diff --git a/example/echo2/echo2_test.go b/example/echo2/echo2_test.go index c5e2928..65411bb 100755 --- a/example/echo2/echo2_test.go +++ b/example/echo2/echo2_test.go @@ -14,7 +14,7 @@ import ( ) func TestCallToEchoApp(t *testing.T) { - to := &sip.Addr{Uri: &sip.URI{Host: "127.0.0.1", Port: 5060}} + to := &sip.Addr{Uri: &sip.URI{User: "echo", Host: "127.0.0.1", Port: 5060}} from := &sip.Addr{Uri: &sip.URI{Host: "127.0.0.1"}} // Create the SIP UDP transport layer. @@ -24,13 +24,48 @@ func TestCallToEchoApp(t *testing.T) { } defer tp.Sock.Close() - // Create an RTP socket. + // Create an RTP session. rtpsock, err := net.ListenPacket("udp", "108.61.60.146:0") if err != nil { t.Fatal("rtp listen:", err) } defer rtpsock.Close() rtpaddr := rtpsock.LocalAddr().(*net.UDPAddr) + rrtpaddrChan := make(chan *net.UDPAddr) + go func() { + var rrtpaddr *net.UDPAddr + frameout := make([]byte, rtp.HeaderSize+160) + rtpHeader := rtp.Header{ + PT: sdp.ULAWCodec.PT, + Seq: 666, + TS: 0, + Ssrc: rand.Uint32(), + } + for n := 0; n < 160; n++ { + frameout[rtp.HeaderSize+n] = byte(n) + } + ticker := time.NewTicker(20 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + rtpHeader.Write(frameout) + rtpHeader.TS += 160 + rtpHeader.Seq++ + if rrtpaddr != nil { + _, err := rtpsock.WriteTo(frameout, rrtpaddr) + if err != nil { + t.Fatal("rtp write", err) + } + } + case rrtpaddr = <-rrtpaddrChan: + if rrtpaddr == nil { + return + } + } + } + }() + defer func() { rrtpaddrChan <- nil }() // Send an INVITE message with an SDP. invite := sip.NewRequest(tp, "INVITE", to, from) @@ -41,15 +76,23 @@ func TestCallToEchoApp(t *testing.T) { } // Consume provisional messages until we receive answer. - var rrtpaddr *net.UDPAddr + var msg *sip.Msg for { tp.Sock.SetDeadline(time.Now().Add(time.Second)) - msg := tp.Recv() + msg = tp.Recv() if msg.Error != nil { t.Fatal(msg.Error) } if msg.Status < sip.StatusOK { - log.Printf("Got provisional %d %s", msg.Status, msg.Phrase) + log.Printf("Provisional %d %s", msg.Status, msg.Phrase) + } else if msg.Status == sip.StatusOK { + log.Printf("Answered!") + } + if msg.Status >= sip.StatusOK { + err = tp.Send(sip.NewAck(invite, msg)) + if err != nil { + t.Fatal(err) + } } if msg.Headers["Content-Type"] == "application/sdp" { log.Printf("Establishing media session") @@ -57,71 +100,20 @@ func TestCallToEchoApp(t *testing.T) { if err != nil { t.Fatal("failed to parse sdp", err) } - rrtpaddr = &net.UDPAddr{IP: net.ParseIP(rsdp.Addr), Port: int(rsdp.Audio.Port)} + rrtpaddrChan <- &net.UDPAddr{IP: net.ParseIP(rsdp.Addr), Port: int(rsdp.Audio.Port)} } if msg.Status == sip.StatusOK { - log.Printf("Answered!") break } else if msg.Status > sip.StatusOK { t.Fatalf("Got %d %s", msg.Status, msg.Phrase) - NewAck(invite) } } - // Figure out where they want us to send RTP. - - // Acknowledge the 200 OK to answer the call. - var ack sip.Msg - ack.Request = invite.Request - ack.From = msg.From - ack.To = msg.To - ack.CallID = msg.CallID - ack.Method = "ACK" - ack.CSeq = msg.CSeq - ack.CSeqMethod = "ACK" - ack.Via = msg.Via - b.Reset() - ack.Append(&b) - if amt, err := conn.Write(b.Bytes()); err != nil || amt != b.Len() { - t.Fatal(err) - } - - // Send RTP packets containing junk until we get an echo response. - quit := make(chan bool) - go func() { - frameout := make([]byte, rtp.HeaderSize+160) - rtpHeader := rtp.Header{ - PT: sdp.ULAWCodec.PT, - Seq: 666, - TS: 0, - Ssrc: rand.Uint32(), - } - for n := 0; n < 160; n++ { - frameout[rtp.HeaderSize+n] = byte(n) - } - ticker := time.NewTicker(20 * time.Millisecond) - defer ticker.Stop() - for { - select { - case <-ticker.C: - rtpHeader.Write(frameout) - rtpHeader.TS += 160 - rtpHeader.Seq++ - amt, err = rtpsock.WriteTo(frameout, rrtpaddr) - if err != nil { - t.Fatal("rtp write", err) - } - case <-quit: - return - } - } - }() - defer func() { quit <- true }() - // We're talking to an echo application so they should send us back exactly // the same audio. + memory := make([]byte, 2048) rtpsock.SetDeadline(time.Now().Add(5 * time.Second)) - amt, _, err = rtpsock.ReadFrom(memory) + amt, _, err := rtpsock.ReadFrom(memory) if err != nil { t.Fatal("rtp read", err) } @@ -140,27 +132,18 @@ func TestCallToEchoApp(t *testing.T) { } // Hangup (we'll be lazy and just change up the ack Msg) - ack.Method = "BYE" - ack.CSeqMethod = "BYE" - ack.CSeq++ - b.Reset() - ack.Append(&b) - amt, err = conn.Write(b.Bytes()) - if err != nil || amt != b.Len() { + err = tp.Send(sip.NewBye(invite, msg)) + if err != nil { t.Fatal(err) } // Wait for acknowledgment of hangup. - conn.SetDeadline(time.Now().Add(time.Second)) - amt, err = conn.Read(memory) - if err != nil { - t.Fatal(err) - } - msg, err = sip.ParseMsg(string(memory[0:amt])) - if err != nil { - t.Fatal(err) + tp.Sock.SetDeadline(time.Now().Add(time.Second)) + msg = tp.Recv() + if msg.Error != nil { + t.Fatal(msg.Error) } - if !msg.IsResponse || msg.Status != 200 || msg.Phrase != "OK" { - t.Fatal("wanted bye response 200 ok but got:", msg.Status, msg.Phrase) + if msg.Status != 200 || msg.CSeqMethod != "BYE" { + t.Fatal("Wanted BYE 200:", msg) } } diff --git a/sdp/sdp.go b/sdp/sdp.go index 2858039..fd92536 100755 --- a/sdp/sdp.go +++ b/sdp/sdp.go @@ -267,7 +267,7 @@ func (sdp *SDP) Append(b *bytes.Buffer) error { return errors.New("sdp lonely no media :[") } if sdp.Session == "" { - sdp.Session = "pokémon" + sdp.Session = "my people call themselves dark angels" } if sdp.Time == "" { sdp.Time = "0 0" diff --git a/sdp/sdp_test.go b/sdp/sdp_test.go index 7dc5d5d..64b9ee0 100755 --- a/sdp/sdp_test.go +++ b/sdp/sdp_test.go @@ -372,9 +372,9 @@ func sdpCompareMedia(t *testing.T, name string, correct, media *sdp.Media) { sdpCompareCodecs(t, name, correct.Codecs, media.Codecs) } -func TestParseSDP(t *testing.T) { +func TestParse(t *testing.T) { for _, test := range sdpTests { - sdp, err := sdp.ParseSDP(test.s) + sdp, err := sdp.Parse(test.s) if err != nil { if test.err == nil { t.Errorf("%v", err) diff --git a/sip/messages.go b/sip/messages.go index aecb898..3abc9eb 100644 --- a/sip/messages.go +++ b/sip/messages.go @@ -43,17 +43,17 @@ func NewResponse(msg *Msg, status int) *Msg { } // http://tools.ietf.org/html/rfc3261#section-17.1.1.3 -func NewAck(original, last *Msg) *Msg { +func NewAck(original, msg *Msg) *Msg { return &Msg{ Method: "ACK", Request: original.Request, Via: original.Via.Copy().SetNext(nil), From: original.From, - To: last.To, + To: msg.To, CallID: original.CallID, CSeq: original.CSeq, CSeqMethod: "ACK", - Route: last.RecordRoute.Reversed(), + Route: msg.RecordRoute.Reversed(), Headers: DefaultHeaders(), } } @@ -71,7 +71,7 @@ func NewCancel(invite *Msg) *Msg { CallID: invite.CallID, CSeq: invite.CSeq, CSeqMethod: "CANCEL", - Route: invite.RecordRoute.Reversed(), + Route: invite.Route, Headers: DefaultHeaders(), } } @@ -81,10 +81,10 @@ func NewBye(invite, last *Msg) *Msg { Method: "BYE", Request: last.Contact.Uri, Via: invite.Via, - From: last.From, + From: invite.From, To: last.To, - CallID: last.CallID, - CSeq: last.CSeq + 1, + CallID: invite.CallID, + CSeq: invite.CSeq + 1, CSeqMethod: "BYE", Route: last.RecordRoute.Reversed(), Headers: DefaultHeaders(), diff --git a/sip/transport.go b/sip/transport.go index 80fc3b0..48b9724 100755 --- a/sip/transport.go +++ b/sip/transport.go @@ -16,7 +16,7 @@ import ( ) var ( - tracing = flag.Bool("tracing", false, "Enable SIP message tracing") + tracing = flag.Bool("tracing", true, "Enable SIP message tracing") timestampTagging = flag.Bool("timestampTagging", false, "Add microsecond timestamps to Via tags") ) @@ -43,10 +43,10 @@ type Transport struct { // signalling messages. // // contact is a SIP address, e.g. "", that tells how to bind -// sockets. If contact.Uri.Port is 0, then a port will be selected randomly. -// This value is also used for contact headers which tell other user-agents -// where to send responses and hence should only contain an IP or canonical -// address. +// sockets. If contact.Uri.Port is 0, it'll be mutated with a randomly selected +// port. This value is also used for contact headers which tell other +// 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) sock, err := net.ListenPacket("udp", saddr) @@ -54,6 +54,7 @@ func NewTransport(contact *Addr) (tp *Transport, err error) { return nil, err } addr := sock.LocalAddr().(*net.UDPAddr) + contact.Uri.Port = uint16(addr.Port) contact = contact.Copy() contact.Uri.Port = uint16(addr.Port) contact.Uri.Params["transport"] = addr.Network() @@ -73,7 +74,7 @@ func (tp *Transport) Send(msg *Msg) error { if err != nil { return err } - addr, err := net.ResolveUDPAddr("ip", saddr) + addr, err := net.ResolveUDPAddr("udp", saddr) if err != nil { return err } @@ -130,7 +131,7 @@ func (tp *Transport) Recv() *Msg { func (tp *Transport) trace(dir, pkt string, addr *net.UDPAddr, t time.Time) { size := len(pkt) bar := strings.Repeat("-", 72) - suffix := "\n " + suffix := "\n" if pkt[len(pkt)-1] == '\n' { suffix = "" } @@ -142,7 +143,7 @@ func (tp *Transport) trace(dir, pkt string, addr *net.UDPAddr, t time.Time) { dir, size, addr.Network(), addr.String(), t.Format(time.RFC3339Nano), bar, - strings.Replace(pkt, "\n", "\n ", -1), suffix, + pkt, suffix, bar) } @@ -172,7 +173,8 @@ func (tp *Transport) preprocess(msg *Msg) { log.Printf("Removing our route header: %s", msg.Route) msg.Route = msg.Route.Next } - if _, ok := msg.Request.Params["lr"]; ok && msg.Route != nil && tp.Contact.Uri.Compare(msg.Request) { + + if msg.Request != nil && msg.Request.Params.Has("lr") && msg.Route != nil && tp.Contact.Uri.Compare(msg.Request) { // RFC3261 16.4 Route Information Preprocessing // RFC3261 16.12.1.2: Traversing a Strict-Routing Proxy var oldReq, newReq *URI @@ -223,7 +225,7 @@ func (tp *Transport) route(old *Msg) (msg *Msg, saddr string, err error) { if msg.Method == "REGISTER" { return nil, "", errors.New("Don't loose route register requests") } - if _, ok := msg.Route.Uri.Params["lr"]; ok { + if msg.Route.Uri.Params.Has("lr") { // RFC3261 16.12.1.1 Basic SIP Trapezoid route := msg.Route msg.Route = msg.Route.Next diff --git a/sip/uri.go b/sip/uri.go index 3c024d9..c20c567 100755 --- a/sip/uri.go +++ b/sip/uri.go @@ -200,3 +200,11 @@ func (params Params) Append(b *bytes.Buffer) { } } } + +func (params *Params) Has(k string) bool { + if params == nil { + return false + } + _, ok := (*params)["lr"] + return ok +} diff --git a/sip/via.go b/sip/via.go index d92f078..4a19e7b 100755 --- a/sip/via.go +++ b/sip/via.go @@ -124,9 +124,7 @@ func (via *Via) Compare(other *Via) bool { func (via *Via) CompareAddr(other *Via) bool { if via != nil && other != nil { - if via.Version == other.Version && - via.Proto == other.Proto && - via.Host == other.Host && + if via.Host == other.Host && via.Port == other.Port { return true }