Skip to content

Commit

Permalink
Test client now supports INVITE against Twilio
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean-Der committed Nov 20, 2023
1 parent 819608b commit 65fcaad
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 26 deletions.
6 changes: 3 additions & 3 deletions pkg/sip/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,16 @@ func (s *Server) handleInviteAuth(req *sip.Request, tx sip.ServerTransaction, fr
s.inProgressInvites = append(s.inProgressInvites, inviteState)
}

h := req.GetHeader("Authorization")
h := req.GetHeader("Proxy-Authorization")
if h == nil {
inviteState.challenge = digest.Challenge{
Realm: userAgent,
Nonce: fmt.Sprintf("%d", time.Now().UnixMicro()),
Algorithm: "MD5",
}

res := sip.NewResponseFromRequest(req, 401, "Unauthorized", nil)
res.AppendHeader(sip.NewHeader("WWW-Authenticate", inviteState.challenge.String()))
res := sip.NewResponseFromRequest(req, 407, "Unauthorized", nil)
res.AppendHeader(sip.NewHeader("Proxy-Authenticate", inviteState.challenge.String()))
logOnError(tx.Respond(res))
return false
}
Expand Down
68 changes: 45 additions & 23 deletions test/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ import (
)

var (
to = flag.String("to", "+15550100000", "")
from = flag.String("from", "+15550100001", "")
username = flag.String("username", "", "")
password = flag.String("password", "", "")
sipRecipentHost = "example.pstn.twilio.com"
sipServer = flag.String("sip-server", "", "")
to = flag.String("to", "+15550100000", "")
from = flag.String("from", "+15550100001", "")
username = flag.String("username", "", "")
password = flag.String("password", "", "")
sipUri = flag.String("sip-uri", "example.pstn.twilio.com", "")
)

func startMediaListener() *net.UDPConn {
Expand Down Expand Up @@ -74,6 +75,10 @@ func getResponse(tx sip.ClientTransaction) *sip.Response {
case <-tx.Done():
panic("transaction failed to complete")
case res := <-tx.Responses():
if res.StatusCode == 100 || res.StatusCode == 180 || res.StatusCode == 183 {
return getResponse(tx)
}

return res
}
}
Expand Down Expand Up @@ -123,16 +128,18 @@ func createOffer(port int) ([]byte, error) {
return offer.Marshal()
}

func parseAnswer(in []byte) int {
func parseAnswer(in []byte) (string, int) {
offer := sdp.SessionDescription{}
if err := offer.Unmarshal(in); err != nil {
panic(err)
}

return offer.MediaDescriptions[0].MediaName.Port.Value
return offer.ConnectionInformation.Address.Address, offer.MediaDescriptions[0].MediaName.Port.Value
}

func sendAudioPackets(conn *net.UDPConn, port int) {
func sendAudioPackets(conn *net.UDPConn, body []byte) {
ip, port := parseAnswer(body)

r, err := os.Open("audio.mkv")
if err != nil {
panic(err)
Expand Down Expand Up @@ -162,7 +169,7 @@ func sendAudioPackets(conn *net.UDPConn, port int) {
},
}

dstAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", getLocalIP(), port))
dstAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", ip, port))
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -190,13 +197,16 @@ func sendAudioPackets(conn *net.UDPConn, port int) {
}

func attemptInvite(sipClient *sipgo.Client, offer []byte, authorizationHeaderValue string) (*sip.Request, *sip.Response) {
inviteRecipent := &sip.Uri{User: *to, Host: sipRecipentHost}
inviteRecipent := &sip.Uri{User: *to, Host: *sipUri}
inviteRequest := sip.NewRequest(sip.INVITE, inviteRecipent)
inviteRequest.SetDestination(getLocalIP() + ":5060")
inviteRequest.SetDestination(*sipServer)
inviteRequest.SetBody(offer)
inviteRequest.AppendHeader(sip.NewHeader("Content-Type", "application/sdp"))
inviteRequest.AppendHeader(sip.NewHeader("Contact", fmt.Sprintf("<sip:livekit@%s:5060>", getLocalIP())))
inviteRequest.AppendHeader(sip.NewHeader("Allow", "INVITE, ACK, CANCEL, BYE, NOTIFY, REFER, MESSAGE, OPTIONS, INFO, SUBSCRIBE"))

if authorizationHeaderValue != "" {
inviteRequest.AppendHeader(sip.NewHeader("Authorization", authorizationHeaderValue))
inviteRequest.AppendHeader(sip.NewHeader("Proxy-Authorization", authorizationHeaderValue))
}

tx, err := sipClient.TransactionRequest(inviteRequest)
Expand All @@ -211,6 +221,10 @@ func attemptInvite(sipClient *sipgo.Client, offer []byte, authorizationHeaderVal
func main() {
flag.Parse()

if *sipServer == "" {
*sipServer = getLocalIP() + ":5060"
}

mediaConn := startMediaListener()
offer, err := createOffer(mediaConn.LocalAddr().(*net.UDPAddr).Port)
if err != nil {
Expand All @@ -224,7 +238,7 @@ func main() {
log.Fatal(err)
}

sipClient, err := sipgo.NewClient(ua)
sipClient, err := sipgo.NewClient(ua, sipgo.WithClientHostname(getLocalIP()))
if err != nil {
log.Fatal(err)
}
Expand All @@ -238,29 +252,30 @@ func main() {
for {
inviteRequest, inviteResponse = attemptInvite(sipClient, offer, authorizationHeaderValue)

if inviteResponse.StatusCode == 100 {
// Try again
continue
} else if inviteResponse.StatusCode == 401 {
if inviteResponse.StatusCode == 407 {
if *username == "" || *password == "" {
panic("Server responded with 401, but no username or password was provided")
panic("Server responded with 407, but no username or password was provided")
}

wwwAuth := inviteResponse.GetHeader("WWW-Authenticate")
challenge, err := digest.ParseChallenge(wwwAuth.Value())
headerVal := inviteResponse.GetHeader("Proxy-Authenticate")
challenge, err := digest.ParseChallenge(headerVal.Value())
if err != nil {
panic(err)
}

toHeader, ok := inviteResponse.To()
if !ok {
panic("No To Header on Request")
}

cred, _ := digest.Digest(challenge, digest.Options{
Method: inviteRequest.Method.String(),
URI: sipRecipentHost,
URI: toHeader.Address.String(),
Username: *username,
Password: *password,
})

authorizationHeaderValue = cred.String()

// Compute digest and try again
continue
} else if inviteResponse.StatusCode != 200 {
Expand All @@ -270,8 +285,15 @@ func main() {
break
}

if contactHeader, ok := inviteResponse.Contact(); ok {
inviteRequest.Recipient = &contactHeader.Address
inviteRequest.Recipient.Port = 5060
}
sipClient.WriteRequest(sip.NewAckRequest(inviteRequest, inviteResponse, nil))

sendBye := func() {
req := sip.NewByeRequest(inviteRequest, inviteResponse, nil)
inviteRequest.AppendHeader(sip.NewHeader("User-Agent", "LiveKit"))

tx, err := sipClient.TransactionRequest(req)
if err != nil {
Expand All @@ -289,6 +311,6 @@ func main() {
sendBye()
}()

sendAudioPackets(mediaConn, parseAnswer(inviteResponse.Body()))
sendAudioPackets(mediaConn, inviteResponse.Body())
sendBye()
}

0 comments on commit 65fcaad

Please sign in to comment.