diff --git a/README.md b/README.md index 5f4ae18..05b75eb 100755 --- a/README.md +++ b/README.md @@ -67,3 +67,4 @@ This is what a sip stack looks like: - [RTP (RFC 3550)](https://tools.ietf.org/html/rfc3550) - [RTP DTMF (RFC 4733)](https://tools.ietf.org/html/rfc4733) - [SIP 100rel (RFC 3262)](https://tools.ietf.org/html/rfc3262) +- [SIP: Locating a Server (RFC 3263)](https://tools.ietf.org/html/rfc3263) diff --git a/dsp/dsp_amd64.s b/dsp/dsp_amd64.s index 5ead1e5..dd16653 100755 --- a/dsp/dsp_amd64.s +++ b/dsp/dsp_amd64.s @@ -1,6 +1,7 @@ #define ULAW_BIAS $0x84 // func L16MixSat160(dst, src *int16) +// Explanation: http://i.imgur.com/nejgQ41.jpg TEXT ·L16MixSat160(SB),4,$0-16 MOVQ dst+0(FP), AX MOVQ src+8(FP), BX diff --git a/example/echo2/echo2_test.go b/example/echo2/echo2_test.go index 784fb24..3338d44 100755 --- a/example/echo2/echo2_test.go +++ b/example/echo2/echo2_test.go @@ -42,7 +42,7 @@ func TestCallToEchoApp(t *testing.T) { // We're going to send white noise every 20ms. var frame rtp.Frame - awgn := dsp.NewAWGN(-25.0) + awgn := dsp.NewAWGN(-45.0) ticker := time.NewTicker(20 * time.Millisecond) defer ticker.Stop() @@ -130,14 +130,13 @@ loop: if resends == 2 { t.Fatal("Failed to send", resend.Method) } - resends++ err = tp.Send(resend) if err != nil { t.Fatal("SIP send failed:", err) } - case <-deathTimer: - resends = 0 + resends++ resendTimer = time.After(resendInterval) + case <-deathTimer: if answered { resend = sip.NewBye(invite, msg) } else { @@ -147,6 +146,8 @@ loop: if err != nil { t.Error("SIP send failed:", err) } + resends = 0 + resendTimer = time.After(resendInterval) } } diff --git a/rtp/session.go b/rtp/session.go index c8eff8a..7a8f808 100644 --- a/rtp/session.go +++ b/rtp/session.go @@ -7,9 +7,9 @@ import ( "fmt" "github.com/jart/gosip/dsp" "github.com/jart/gosip/sdp" - "github.com/jart/gosip/util" "math/rand" "net" + "strconv" "strings" ) @@ -125,8 +125,8 @@ func (rs *Session) recv() (frame *Frame, err error) { func listenRTP(host string) (sock net.PacketConn, err error) { for i := 0; i < rtpBindMaxAttempts; i++ { - port := rtpBindPortMin + rand.Int()%(rtpBindPortMax-rtpBindPortMin+1) - saddr := util.HostPortToString(host, uint16(port)) + port := rtpBindPortMin + rand.Int63()%(rtpBindPortMax-rtpBindPortMin+1) + saddr := net.JoinHostPort(host, strconv.FormatInt(port, 10)) sock, err = net.ListenPacket("udp", saddr) if err != nil { if !strings.Contains(err.Error(), "address already in use") { diff --git a/sip/addr.go b/sip/addr.go index eae9907..c72d5ea 100755 --- a/sip/addr.go +++ b/sip/addr.go @@ -137,7 +137,6 @@ func (addr *Addr) Or(other *Addr) *Addr { // Sets newly generated tag ID and returns self. func (addr *Addr) Tag() *Addr { - addr = addr.Copy() addr.Params["tag"] = util.GenerateTag() return addr } @@ -172,11 +171,11 @@ func (addr *Addr) Copy() *Addr { return res } -// Returns true if the host and port match. If a username is present in -// `addr`, then the username is `other` must also match. -func (addr *Addr) Compare(other *Addr) bool { +func (addr *Addr) CompareHostPort(other *Addr) bool { if addr != nil && other != nil { - return addr.Uri.Compare(other.Uri) + if addr.Uri.CompareHostPort(other.Uri) { + return true + } } return false } diff --git a/sip/addr_test.go b/sip/addr_test.go index 79d424b..49c4bb9 100755 --- a/sip/addr_test.go +++ b/sip/addr_test.go @@ -8,7 +8,6 @@ import ( type addrTest struct { s string - s2 string addr sip.Addr err error } @@ -16,13 +15,11 @@ type addrTest struct { var addrTests = []addrTest{ addrTest{ - s: "", - s2: "", + s: "", addr: sip.Addr{ Uri: &sip.URI{ Scheme: "sip", - Host: "pokémon.net", - Port: 5060, + Host: "pokemon.net", }, }, }, @@ -34,7 +31,6 @@ var addrTests = []addrTest{ Scheme: "sip", User: "brave", Host: "toaster.net", - Port: 5060, Params: sip.Params{ "isup-oli": "29", }, @@ -46,12 +42,11 @@ var addrTests = []addrTest{ }, addrTest{ - s: `, "Ditto" `, + s: `,"Ditto" `, addr: sip.Addr{ Uri: &sip.URI{ Scheme: "sip", Host: "pokemon.com", - Port: 5060, }, Next: &sip.Addr{ Display: "Ditto", @@ -59,67 +54,42 @@ var addrTests = []addrTest{ Scheme: "sip", User: "ditto", Host: "pokemon.com", - Port: 5060, }, }, }, }, addrTest{ - s: `, , `, + s: `,,`, addr: sip.Addr{ Uri: &sip.URI{ Scheme: "sip", Host: "1.2.3.4", - Port: 5060, }, Next: &sip.Addr{ Uri: &sip.URI{ Scheme: "sip", Host: "1.2.3.5", - Port: 5060, }, Next: &sip.Addr{ Uri: &sip.URI{ Scheme: "sip", Host: "666::dead:beef", - Port: 5060, }, }, }, }, }, - addrTest{ - s: " hello kitty ;tag=deadbeef", - s2: "\"hello kitty\" ;tag=deadbeef", - addr: sip.Addr{ - Display: "hello kitty", - Uri: &sip.URI{ - Scheme: "sip", - User: "jtunney", - Host: "bsdtelecom.net", - Port: 5060, - Params: sip.Params{ - "isup-oli": "29", - }, - }, - Params: sip.Params{ - "tag": "deadbeef", - }, - }, - }, - addrTest{ s: "\"\\\"\\\"Justine \\\\Tunney \" " + - ";tag=deadbeef", + ";tag=deadbeef", addr: sip.Addr{ Display: "\"\"Justine \\Tunney ", Uri: &sip.URI{ Scheme: "sip", - User: "jtunney", - Host: "bsdtelecom.net", - Port: 5060, + User: "jart", + Host: "google.com", Params: sip.Params{ "isup-oli": "29", }, @@ -143,7 +113,7 @@ func TestParseAddr(t *testing.T) { } } if !reflect.DeepEqual(&test.addr, addr) { - t.Errorf("%#v != %#v", &test.addr, addr) + t.Errorf("%#v != %#v\n%#v != %#v", &test.addr, addr, test.addr.Uri, addr.Uri) } } } @@ -151,14 +121,8 @@ func TestParseAddr(t *testing.T) { func TestAddrString(t *testing.T) { for _, test := range addrTests { addr := test.addr.String() - var s string - if test.s2 != "" { - s = test.s2 - } else { - s = test.s - } - if s != addr { - t.Error(s, "!=", addr) + if test.s != addr { + t.Error(test.s, "!=", addr) } } } diff --git a/sip/messages.go b/sip/messages.go index 23be348..2c488cb 100644 --- a/sip/messages.go +++ b/sip/messages.go @@ -14,10 +14,10 @@ const ( func NewRequest(tp *Transport, method string, to, from *Addr) *Msg { return &Msg{ Method: method, - Request: to.Uri, + Request: to.Uri.Copy(), Via: tp.Via.Copy().Branch(), - From: from.Or(tp.Contact).Tag(), - To: to, + From: from.Or(tp.Contact).Copy().Tag(), + To: to.Copy(), CallID: util.GenerateCallID(), CSeq: util.GenerateCSeq(), CSeqMethod: method, @@ -96,7 +96,7 @@ func ResponseMatch(msg, resp *Msg) bool { return (resp.IsResponse && resp.CSeq == msg.CSeq && resp.CSeqMethod == msg.Method && - resp.Via.Last().Compare(msg.Via)) + resp.Via.Last().CompareHostPort(msg.Via)) } // Returns true if `ack` can be considered an appropriate response to `msg`. @@ -107,7 +107,7 @@ func AckMatch(msg, ack *Msg) bool { ack.Method == MethodAck && ack.CSeq == msg.CSeq && ack.CSeqMethod == MethodAck && - ack.Via.Last().CompareAddr(msg.Via)) + ack.Via.Last().CompareHostPort(msg.Via)) } func AttachSDP(msg *Msg, ms *sdp.SDP) { diff --git a/sip/msg_test.go b/sip/msg_test.go index 6d5ac92..e7daeee 100755 --- a/sip/msg_test.go +++ b/sip/msg_test.go @@ -115,7 +115,6 @@ var msgTests = []msgTest{ Request: &sip.URI{ Scheme: "sip", Host: "10.11.34.37", - Port: 5060, }, Via: &sip.Via{ Version: "2.0", @@ -128,7 +127,6 @@ var msgTests = []msgTest{ Uri: &sip.URI{ Scheme: "sip", Host: "10.11.34.37", - Port: 5060, }, }, From: &sip.Addr{ diff --git a/sip/transport.go b/sip/transport.go index 6c04df0..fe1bd22 100755 --- a/sip/transport.go +++ b/sip/transport.go @@ -7,7 +7,6 @@ import ( "bytes" "errors" "flag" - "github.com/jart/gosip/util" "log" "net" "strconv" @@ -52,7 +51,7 @@ type Transport struct { // user-agents where to send responses and hence should only contain an IP or // canonical address. func NewTransport(contact *Addr) (tp *Transport, err error) { - saddr := util.HostPortToString(contact.Uri.Host, contact.Uri.Port) + saddr := net.JoinHostPort(contact.Uri.Host, portstr(contact.Uri.Port)) sock, err := net.ListenPacket("udp", saddr) if err != nil { return nil, err @@ -173,12 +172,12 @@ func (tp *Transport) sanityCheck(msg *Msg) error { // Perform some ingress message mangling. func (tp *Transport) preprocess(msg *Msg) { - if tp.Contact.Compare(msg.Route) { + if tp.Contact.CompareHostPort(msg.Route) { log.Printf("Removing our route header: %s", msg.Route) msg.Route = msg.Route.Next } - if msg.Request != nil && msg.Request.Params.Has("lr") && msg.Route != nil && tp.Contact.Uri.Compare(msg.Request) { + if msg.Request != nil && msg.Request.Params.Has("lr") && msg.Route != nil && tp.Contact.Uri.CompareHostPort(msg.Request) { // RFC3261 16.4 Route Information Preprocessing // RFC3261 16.12.1.2: Traversing a Strict-Routing Proxy var oldReq, newReq *URI @@ -208,8 +207,8 @@ func (tp *Transport) route(old *Msg) (msg *Msg, dest string, err error) { msg.Contact = tp.Contact } if msg.IsResponse { - if msg.Via.CompareAddr(tp.Via) { - // In proxy scenarios we have to remove our own Via. + if msg.Via.CompareHostPort(tp.Via) { + // In proxy scenarios, we have to remove our own Via. msg.Via = msg.Via.Next } if msg.Via == nil { @@ -224,8 +223,12 @@ func (tp *Transport) route(old *Msg) (msg *Msg, dest string, err error) { if msg.Request == nil { return nil, "", errors.New("Missing request URI") } - if !msg.Via.CompareAddr(tp.Via) { - return nil, "", errors.New("You forgot to say: msg.Via = tp.Via(msg.Via)") + if !msg.Via.CompareHostPort(tp.Via) { + return nil, "", errors.New("Set our Via should come first when sending messages") + } + if msg.Route.CompareHostPort(tp.Contact) { + // In proxy scenarios, we have to remove our own Route. + msg.Route = msg.Route.Next } if msg.Route != nil { if msg.Method == "REGISTER" { @@ -233,23 +236,20 @@ func (tp *Transport) route(old *Msg) (msg *Msg, dest string, err error) { } if msg.Route.Uri.Params.Has("lr") { // RFC3261 16.12.1.1 Basic SIP Trapezoid - route := msg.Route - // TODO(jart): Remove if same as Contact. - // msg.Route = msg.Route.Next - host, port = route.Uri.Host, route.Uri.Port + host, port = msg.Route.Uri.Host, msg.Route.Uri.GetPort() } else { // RFC3261 16.12.1.2: Traversing a Strict-Routing Proxy msg.Route = old.Route.Copy() msg.Route.Last().Next = &Addr{Uri: msg.Request} msg.Request = msg.Route.Uri msg.Route = msg.Route.Next - host, port = msg.Request.Host, msg.Request.Port + host, port = msg.Request.Host, msg.Request.GetPort() } } else { - host, port = msg.Request.Host, msg.Request.Port + host, port = msg.Request.Host, msg.Request.GetPort() } } - dest = util.HostPortToString(host, port) + dest = net.JoinHostPort(host, portstr(port)) return } diff --git a/sip/uri.go b/sip/uri.go index e094816..216950b 100755 --- a/sip/uri.go +++ b/sip/uri.go @@ -24,7 +24,6 @@ import ( "bytes" "errors" "github.com/jart/gosip/util" - "strconv" "strings" ) @@ -156,30 +155,32 @@ func (uri *URI) Append(b *bytes.Buffer) { b.WriteString(util.URLEscape(uri.Host, false)) } if uri.Port > 0 { - b.WriteString(":" + strconv.FormatInt(int64(uri.Port), 10)) + b.WriteString(":" + portstr((uri.Port))) } uri.Params.Append(b) } -// Returns true if scheme, host, and port match. if a username is present in -// `addr`, then the username is `other` must also match. -func (uri *URI) Compare(other *URI) bool { +func (uri *URI) CompareHostPort(other *URI) bool { if uri != nil && other != nil { - if uri.Scheme == other.Scheme && - uri.Host == other.Host && - uri.Port == other.Port { - if uri.User != "" { - if uri.User == other.User { - return true - } - } else { - return true - } + if uri.Host == other.Host && uri.GetPort() == other.GetPort() { + return true } } return false } +func (uri *URI) GetPort() uint16 { + if uri.Port == 0 { + if uri.Scheme == "sips" { + return 5061 + } else { + return 5060 + } + } else { + return uri.Port + } +} + func (params Params) Copy() Params { res := make(Params, len(params)) for k, v := range params { @@ -201,10 +202,7 @@ func (params Params) Append(b *bytes.Buffer) { } } -func (params *Params) Has(k string) bool { - if params == nil { - return false - } - _, ok := (*params)["lr"] +func (params Params) Has(k string) bool { + _, ok := params["lr"] return ok } diff --git a/sip/uri_test.go b/sip/uri_test.go index de063e8..618e055 100755 --- a/sip/uri_test.go +++ b/sip/uri_test.go @@ -8,7 +8,6 @@ import ( type uriTest struct { s string // user input we want to convert - s2 string // non-blank if 's' changes after we parse/format it uri sip.URI // what 's' should become after parsing err error // if we expect parsing to fail } @@ -16,75 +15,67 @@ type uriTest struct { var uriTests = []uriTest{ uriTest{ - s: "sip:bsdtelecom.net", + s: "sip:google.com", uri: sip.URI{ Scheme: "sip", - Host: "bsdtelecom.net", - Port: 5060, + Host: "google.com", }, }, uriTest{ - s: "sip:bsdtelecom.net:666", + s: "sip:jart@google.com", uri: sip.URI{ Scheme: "sip", - Host: "bsdtelecom.net", - Port: 666, + User: "jart", + Host: "google.com", }, }, uriTest{ - s: "sip:jtunney@bsdtelecom.net", + s: "sip:jart@google.com:5060", uri: sip.URI{ Scheme: "sip", - User: "jtunney", - Host: "bsdtelecom.net", + User: "jart", + Host: "google.com", Port: 5060, }, }, uriTest{ - s: "sip:+12125650666@cat.lol", + s: "sip:google.com:666", uri: sip.URI{ Scheme: "sip", - User: "+12125650666", - Host: "cat.lol", - Port: 5060, + Host: "google.com", + Port: 666, }, }, uriTest{ - s: "sip:jtunney@bsdtelecom.net:5060", - s2: "sip:jtunney@bsdtelecom.net", + s: "sip:+12125650666@cat.lol", uri: sip.URI{ Scheme: "sip", - User: "jtunney", - Host: "bsdtelecom.net", - Port: 5060, + User: "+12125650666", + Host: "cat.lol", }, }, uriTest{ - s: "sip:jtunney:lawl@bsdtelecom.net:5060", - s2: "sip:jtunney:lawl@bsdtelecom.net", + s: "sip:jart:lawl@google.com", uri: sip.URI{ Scheme: "sip", - User: "jtunney", + User: "jart", Pass: "lawl", - Host: "bsdtelecom.net", - Port: 5060, + Host: "google.com", }, }, uriTest{ - s: "sip:jtunney:lawl@bsdtelecom.net:5060;isup-oli=00;omg;lol=cat", - s2: "sip:jtunney:lawl@bsdtelecom.net;isup-oli=00;omg;lol=cat", + s: "sip:jart:lawl@google.com;isup-oli=00;omg;lol=cat", uri: sip.URI{ Scheme: "sip", - User: "jtunney", + User: "jart", Pass: "lawl", - Host: "bsdtelecom.net", - Port: 5060, + Host: "google.com", Params: sip.Params{ "isup-oli": "00", "omg": "", @@ -94,12 +85,11 @@ var uriTests = []uriTest{ }, uriTest{ - s: "sip:jtunney@bsdtelecom.net;isup-oli=00;omg;lol=cat", + s: "sip:jart@google.com;isup-oli=00;omg;lol=cat", uri: sip.URI{ Scheme: "sip", - User: "jtunney", - Host: "bsdtelecom.net", - Port: 5060, + User: "jart", + Host: "google.com", Params: sip.Params{ "isup-oli": "00", "omg": "", @@ -113,15 +103,13 @@ var uriTests = []uriTest{ uri: sip.URI{ Scheme: "sip", Host: "dead:beef::666", - Port: 5060, }, }, uriTest{ - s: "sip:[dead:beef::666]:5060", - s2: "sip:[dead:beef::666]", + s: "sips:[dead:beef::666]:5060", uri: sip.URI{ - Scheme: "sip", + Scheme: "sips", Host: "dead:beef::666", Port: 5060, }, @@ -155,13 +143,13 @@ var uriTests = []uriTest{ }, uriTest{ - s: "sip:jtunney%3e:la%3ewl@bsdtelecom%3e.net:65535" + + s: "sip:jart%3e:la%3ewl@google%3e.net:65535" + ";isup%3e-oli=00%3e;%3eomg;omg;lol=cat", uri: sip.URI{ Scheme: "sip", - User: "jtunney>", + User: "jart>", Pass: "la>wl", - Host: "bsdtelecom>.net", + Host: "google>.net", Port: 65535, Params: sip.Params{ "isup>-oli": "00>", @@ -193,11 +181,7 @@ func TestParse(t *testing.T) { func TestFormat(t *testing.T) { for _, test := range uriTests { uri := test.uri.String() - s := test.s - if test.s2 != "" { - s = test.s2 - } - if s != uri { + if test.s != uri { t.Error(test.s, "!=", uri) } } diff --git a/sip/util.go b/sip/util.go index 042ade7..6cfce03 100644 --- a/sip/util.go +++ b/sip/util.go @@ -6,8 +6,11 @@ import ( "strings" ) +func portstr(port uint16) string { + return strconv.FormatInt(int64(port), 10) +} + func extractHostPort(s string) (s2, host string, port uint16, err error) { - port = 5060 if s == "" { err = URIMissingHost } else { diff --git a/sip/via.go b/sip/via.go index 4a19e7b..b8ef0cc 100755 --- a/sip/via.go +++ b/sip/via.go @@ -117,12 +117,7 @@ func (via *Via) Last() *Via { return via } -// returns true if version, proto, ip, port, and branch match -func (via *Via) Compare(other *Via) bool { - return (via.CompareAddr(other) && via.CompareBranch(other)) -} - -func (via *Via) CompareAddr(other *Via) bool { +func (via *Via) CompareHostPort(other *Via) bool { if via != nil && other != nil { if via.Host == other.Host && via.Port == other.Port { diff --git a/util/util.go b/util/util.go index 787b569..54b6899 100755 --- a/util/util.go +++ b/util/util.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "math/rand" "net" - "strconv" "strings" ) @@ -49,16 +48,6 @@ func IsIPPrivate(ip net.IP) bool { return false } -func HostPortToString(host string, port uint16) (saddr string) { - sport := strconv.FormatInt(int64(port), 10) - if IsIPv6(host) { - saddr = "[" + host + "]:" + sport - } else { - saddr = host + ":" + sport - } - return saddr -} - // Generates a secure random number between 0 and 50,000. func GenerateCSeq() int { return rand.Int() % 50000