Open sujit-baniya opened 3 weeks ago
@sujit-baniya I will guess you are running this in browser as static. Read about CSP. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
If this is not issue, then you need to provide some logs with debug level, but first check are you even reaching go websocket
@emiago In client.go, I tried adding the CSP
package main
import (
"context"
"crypto/rand"
"encoding/base64"
"flag"
"fmt"
"html/template"
"net/http"
"os"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/emiago/sipgo"
"github.com/emiago/sipgo/sip"
"github.com/emiago/sipgo/utils"
"github.com/icholy/digest"
)
// Static files directory
const staticDir = "./static"
// generateNonce generates a random nonce
func generateNonce() (string, error) {
nonce := make([]byte, 16)
_, err := rand.Read(nonce)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(nonce), nil
}
// cspMiddleware is a middleware function that adds a CSP header to the response
func cspMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
nonce, err := generateNonce()
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Add CSP header with nonce for both scripts and styles
csp := "default-src 'self' 'unsafe-inline'; script-src 'self' https://cdnjs.cloudflare.com 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' ws://192.168.18.8:8080 ws://192.168.18.8:5060;"
w.Header().Set("Content-Security-Policy", csp)
// Store the nonce in the request context
ctx := context.WithValue(r.Context(), "nonce", nonce)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// nonceHandler is a helper function to get the nonce from the request context
func nonceHandler(r *http.Request) string {
if nonce, ok := r.Context().Value("nonce").(string); ok {
return nonce
}
return ""
}
// HTML template
var tmpl = template.Must(template.ParseFiles("./static/index.html"))
// indexHandler serves the main page
func indexHandler(w http.ResponseWriter, r *http.Request) {
nonce := nonceHandler(r)
data := struct {
Nonce string
}{
Nonce: nonce,
}
tmpl.Execute(w, data)
}
func runWeb() {
// Serve static files from the defined directory
fs := http.FileServer(http.Dir(staticDir))
// Wrap the file server with the CSP middleware
http.Handle("/", cspMiddleware(fs))
// Handle the main page
http.HandleFunc("/index", indexHandler)
// Start the HTTP server
http.ListenAndServe(":8080", nil)
}
func main() {
defIP := utils.LocalIp()
dst := flag.String("ip", defIP+":5060", "My external ip")
inter := flag.String("h", "localhost", "My interface ip or hostname")
tran := flag.String("t", "ws", "Transport")
username := flag.String("u", "alice", "SIP Username")
password := flag.String("p", "alice", "Password")
flag.Parse()
// Make SIP Debugging available
sip.SIPDebug = os.Getenv("SIP_DEBUG") != ""
zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMicro
log.Logger = zerolog.New(zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: time.StampMicro,
}).With().Timestamp().Logger().Level(zerolog.InfoLevel)
if lvl, err := zerolog.ParseLevel(os.Getenv("LOG_LEVEL")); err == nil && lvl != zerolog.NoLevel {
log.Logger = log.Logger.Level(lvl)
}
// Setup UAC
ua, err := sipgo.NewUA(
sipgo.WithUserAgent(*username),
)
if err != nil {
log.Fatal().Err(err).Msg("Fail to setup user agent")
}
client, err := sipgo.NewClient(ua, sipgo.WithClientHostname(*inter))
if err != nil {
log.Fatal().Err(err).Msg("Fail to setup client handle")
}
defer client.Close()
// Create basic REGISTER request structure
recipient := &sip.Uri{}
sip.ParseUri(fmt.Sprintf("sip:%s@%s", *username, *dst), recipient)
req := sip.NewRequest(sip.REGISTER, *recipient)
req.AppendHeader(
sip.NewHeader("Contact", fmt.Sprintf("<sip:%s@%s>", *username, *inter)),
)
req.SetTransport(strings.ToUpper(*tran))
// Send request and parse response
// req.SetDestination(*dst)
log.Info().Msg(req.StartLine())
ctx := context.Background()
tx, err := client.TransactionRequest(ctx, req)
if err != nil {
log.Fatal().Err(err).Msg("Fail to create transaction")
}
defer tx.Terminate()
res, err := getResponse(tx)
if err != nil {
log.Fatal().Err(err).Msg("Fail to get response")
}
log.Info().Int("status", int(res.StatusCode)).Msg("Received status")
if res.StatusCode == 401 {
// Get WwW-Authenticate
wwwAuth := res.GetHeader("WWW-Authenticate")
chal, err := digest.ParseChallenge(wwwAuth.Value())
if err != nil {
log.Fatal().Str("wwwauth", wwwAuth.Value()).Err(err).Msg("Fail to parse challenge")
}
// Reply with digest
cred, _ := digest.Digest(chal, digest.Options{
Method: req.Method.String(),
URI: recipient.Host,
Username: *username,
Password: *password,
})
newReq := req.Clone()
newReq.RemoveHeader("Via") // Must be regenerated by tranport layer
newReq.AppendHeader(sip.NewHeader("Authorization", cred.String()))
ctx := context.Background()
tx, err := client.TransactionRequest(ctx, newReq, sipgo.ClientRequestAddVia)
if err != nil {
log.Fatal().Err(err).Msg("Fail to create transaction")
}
defer tx.Terminate()
res, err = getResponse(tx)
if err != nil {
log.Fatal().Err(err).Msg("Fail to get response")
}
}
if res.StatusCode != 200 {
log.Fatal().Msg("Fail to register")
}
log.Info().Msg("Client registered")
runWeb()
}
func getResponse(tx sip.ClientTransaction) (*sip.Response, error) {
select {
case <-tx.Done():
return nil, fmt.Errorf("transaction died")
case res := <-tx.Responses():
return res, nil
}
}
But it didn't connect to server. Do I need to set CSP on server as well?
@sujit-baniya OK you got me confused with web client. Fine for code, but I would need some logs. I am not sure how you are executing.
Set sip.SIPDebug = true
and zerolog logger log = log.SetLevel(...)
to debug
I suggest running tcpdump on your ports and maybe take some more simple example, just to have connection working.
I'm trying to use following code
server.go
client.go
index.html
But I'm not able to connect to
ws
server. I would really appreciate if you could point out the issue or anything I'm missing?