├── transport ├── transport_tls.go ├── transport_udp.go ├── transport_fv_test.go ├── transport_tcp.go ├── connection.go ├── transport.go ├── conntable_test.go └── conntable.go ├── transaction ├── server_test.go ├── client_test.go ├── transaction.go ├── transaction_test.go ├── server.go ├── manager.go └── client.go ├── testutils ├── asserts.go └── dummyconn.go ├── .gitignore ├── README.md ├── test └── manager_test.go ├── utils ├── utils.go ├── semaphore.go └── elasticchan.go ├── parser ├── parserbuffer.go └── parser.go ├── log └── log.go ├── timing ├── timing_test.go └── timing.go ├── base ├── stringify_test.go ├── messages.go └── headers.go └── LICENSE /transport/transport_tls.go: -------------------------------------------------------------------------------- 1 | package transport 2 | -------------------------------------------------------------------------------- /transaction/server_test.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import "testing" 4 | 5 | func TestResendInviteOK(t *testing.T) { 6 | } 7 | -------------------------------------------------------------------------------- /testutils/asserts.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Runs f at 10ms intervals until it returns True. 8 | // Gives up after 100 attempts. 9 | func Eventually(f func() bool) bool { 10 | attempts := 0 11 | for !f() { 12 | if attempts++; attempts >= 100 { 13 | return false 14 | } 15 | time.Sleep(10 * time.Millisecond) 16 | } 17 | return true 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Editor swap files 11 | *.swp 12 | *.swo 13 | 14 | # Architecture specific extensions/prefixes 15 | *.[568vq] 16 | [568vq].out 17 | 18 | *.cgo1.go 19 | *.cgo2.c 20 | _cgo_defun.c 21 | _cgo_gotypes.go 22 | _cgo_export.* 23 | 24 | _testmain.go 25 | 26 | *.exe 27 | *.test 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gossip 2 | ====== 3 | 4 | SIP stack in Golang, for use by stateful SIP UAs, whether client, server, or proxy. 5 | 6 | 7 | Project status 8 | -------------- 9 | 10 | **Version: V0.2** 11 | 12 | Gossip is now capable of basic SIP 2.0 transactions over UDP and TCP and has been live tested with real softphones. 13 | It is still missing some compatibility features, isn't well tested and has a few known bugs. 14 | 15 | **APIs will change without warning until V1.0.** 16 | 17 | This readme will be updated as work progresses. 18 | -------------------------------------------------------------------------------- /test/manager_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/stefankopieczek/gossip/transaction" 5 | "github.com/stefankopieczek/gossip/transport" 6 | "testing" 7 | ) 8 | 9 | // Tests we can start/stop a transaction manager repeatedly on the same port. 10 | func TestStop(t *testing.T) { 11 | loops := 5 12 | for i := 0; i < loops; i++ { 13 | transport, err := transport.NewManager("udp") 14 | if err != nil { 15 | t.Fatalf("Failed to start transport manager on loop %v: %v\n", i, err) 16 | } 17 | 18 | m, err := transaction.NewManager(transport, "localhost:12345") 19 | if err != nil { 20 | t.Fatalf("Failed to start transaction manager on loop %v: %v\n", i, err) 21 | } 22 | 23 | m.Stop() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // Check two string pointers for equality as follows: 4 | // - If neither pointer is nil, check equality of the underlying strings. 5 | // - If either pointer is nil, return true if and only if they both are. 6 | func StrPtrEq(a *string, b *string) bool { 7 | if a == nil || b == nil { 8 | return a == b 9 | } 10 | 11 | return *a == *b 12 | } 13 | 14 | // Check two uint16 pointers for equality as follows: 15 | // - If neither pointer is nil, check equality of the underlying uint16s. 16 | // - If either pointer is nil, return true if and only if they both are. 17 | func Uint16PtrEq(a *uint16, b *uint16) bool { 18 | if a == nil || b == nil { 19 | return a == b 20 | } 21 | 22 | return *a == *b 23 | } 24 | -------------------------------------------------------------------------------- /testutils/dummyconn.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import "net" 4 | import "time" 5 | 6 | type DummyConn struct{} 7 | 8 | func (c *DummyConn) Write(b []byte) (n int, err error) { 9 | return 0, nil 10 | } 11 | 12 | func (c *DummyConn) Read(b []byte) (n int, err error) { 13 | return 0, nil 14 | } 15 | 16 | func (c *DummyConn) Close() error { 17 | return nil 18 | } 19 | 20 | func (c *DummyConn) LocalAddr() net.Addr { 21 | return nil 22 | } 23 | 24 | func (c *DummyConn) RemoteAddr() net.Addr { 25 | return nil 26 | } 27 | 28 | func (c *DummyConn) SetDeadline(t time.Time) error { 29 | return nil 30 | } 31 | 32 | func (c *DummyConn) SetReadDeadline(t time.Time) error { 33 | return nil 34 | } 35 | 36 | func (c *DummyConn) SetWriteDeadline(t time.Time) error { 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /utils/semaphore.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "sync" 4 | 5 | // Simple semaphore implementation. 6 | // Any number of calls to Acquire() can be made; these will not block. 7 | // If the semaphore has been acquired more times than it has been released, it is called 'blocked'. 8 | // Otherwise, it is called 'free'. 9 | type Semaphore interface { 10 | // Take a semaphore lock. 11 | Acquire() 12 | 13 | // Release an acquired semaphore lock. 14 | // This should only be called when the semaphore is blocked, otherwise behaviour is undefined 15 | Release() 16 | 17 | // Block execution until the semaphore is free. 18 | Wait() 19 | 20 | // Clean up the semaphore object. 21 | Dispose() 22 | } 23 | 24 | func NewSemaphore() Semaphore { 25 | sem := new(semaphore) 26 | sem.cond = sync.NewCond(&sync.Mutex{}) 27 | go func(s *semaphore) { 28 | select { 29 | case <- s.stop: 30 | return 31 | case <- s.acquired: 32 | s.locks += 1 33 | case <- s.released: 34 | s.locks -= 1 35 | if s.locks == 0 { 36 | s.cond.Broadcast() 37 | } 38 | } 39 | } (sem) 40 | return sem 41 | } 42 | 43 | // Concrete implementation of Semaphore. 44 | type semaphore struct { 45 | held bool 46 | locks int 47 | acquired chan bool 48 | released chan bool 49 | stop chan bool 50 | cond *sync.Cond 51 | } 52 | 53 | // Implements Semaphore.Acquire() 54 | func (sem *semaphore) Acquire() { 55 | sem.acquired <- true 56 | } 57 | 58 | // Implements Semaphore.Release() 59 | func (sem *semaphore) Release() { 60 | sem.released <- true 61 | } 62 | 63 | // Implements Semaphore.Wait() 64 | func (sem *semaphore) Wait() { 65 | sem.cond.L.Lock() 66 | for sem.locks != 0 { 67 | sem.cond.Wait() 68 | } 69 | } 70 | 71 | // Implements Semaphore.Dispose() 72 | func (sem *semaphore) Dispose() { 73 | sem.stop <- true 74 | } 75 | -------------------------------------------------------------------------------- /transaction/client_test.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | var c_SERVER string = "localhost:5060" 9 | var c_CLIENT string = "localhost:5061" 10 | 11 | func TestSendInvite(t *testing.T) { 12 | invite, err := request([]string{ 13 | "INVITE sip:joe@bloggs.com SIP/2.0", 14 | "Via: SIP/2.0/UDP " + c_CLIENT + ";branch=z9hG4bK776asdhds", 15 | "", 16 | "", 17 | }) 18 | assertNoError(t, err) 19 | 20 | test := transactionTest{t: t, 21 | actions: []action{ 22 | &userSend{invite}, 23 | &transportRecv{invite}, 24 | }} 25 | test.Execute() 26 | } 27 | 28 | func TestInviteTimeout(t *testing.T) { 29 | invite, err := request([]string{ 30 | "INVITE sip:joe@bloggs.com SIP/2.0", 31 | "Via: SIP/2.0/UDP " + c_CLIENT + ";branch=z9hG4bK776asdhds", 32 | "", 33 | "", 34 | }) 35 | assertNoError(t, err) 36 | 37 | test := transactionTest{t: t, 38 | actions: []action{ 39 | &userSend{invite}, 40 | &transportRecv{invite}, 41 | &wait{500 * time.Millisecond}, 42 | &transportRecv{invite}, 43 | &wait{1000 * time.Millisecond}, 44 | &transportRecv{invite}, 45 | &wait{2000 * time.Millisecond}, 46 | &transportRecv{invite}, 47 | &wait{4000 * time.Millisecond}, 48 | &transportRecv{invite}, 49 | &wait{8000 * time.Millisecond}, 50 | &transportRecv{invite}, 51 | &wait{16000 * time.Millisecond}, 52 | &transportRecv{invite}, 53 | }} 54 | test.Execute() 55 | } 56 | 57 | func TestReceiveOK(t *testing.T) { 58 | invite, err := request([]string{ 59 | "INVITE sip:joe@bloggs.com SIP/2.0", 60 | "CSeq: 1 INVITE", 61 | "Via: SIP/2.0/UDP " + c_CLIENT + ";branch=z9hG4bK776asdhds", 62 | "", 63 | "", 64 | }) 65 | assertNoError(t, err) 66 | 67 | ok, err := response([]string{ 68 | "SIP/2.0 200 OK", 69 | "CSeq: 1 INVITE", 70 | "Via: SIP/2.0/UDP " + c_SERVER + ";branch=z9hG4bK776asdhds", 71 | "", 72 | "", 73 | }) 74 | assertNoError(t, err) 75 | 76 | test := transactionTest{t: t, 77 | actions: []action{ 78 | &userSend{invite}, 79 | &transportRecv{invite}, 80 | &transportSend{ok}, 81 | &userRecv{ok}, 82 | }} 83 | test.Execute() 84 | } 85 | -------------------------------------------------------------------------------- /utils/elasticchan.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "github.com/stefankopieczek/gossip/log" 4 | 5 | // The buffer size of the primitive input and output chans. 6 | const c_ELASTIC_CHANSIZE = 3 7 | 8 | // A dynamic channel that does not block on send, but has an unlimited buffer capacity. 9 | // ElasticChan uses a dynamic slice to buffer signals received on the input channel until 10 | // the output channel is ready to process them. 11 | type ElasticChan struct { 12 | In chan interface{} 13 | Out chan interface{} 14 | buffer []interface{} 15 | stopped bool 16 | } 17 | 18 | // Initialise the Elastic channel, and start the management goroutine. 19 | func (c *ElasticChan) Init() { 20 | c.In = make(chan interface{}, c_ELASTIC_CHANSIZE) 21 | c.Out = make(chan interface{}, c_ELASTIC_CHANSIZE) 22 | c.buffer = make([]interface{}, 0) 23 | 24 | go c.manage() 25 | } 26 | 27 | // Poll for input from one end of the channel and add it to the buffer. 28 | // Also poll sending buffered signals out over the output chan. 29 | func (c *ElasticChan) manage() { 30 | for { 31 | if len(c.buffer) > 0 { 32 | // The buffer has something in it, so try to send as well as 33 | // receive. 34 | // (Receive first in order to minimize blocked Send() calls). 35 | select { 36 | case in, ok := <-c.In: 37 | if !ok { 38 | log.Debug("Chan %p will dispose", c) 39 | break 40 | } 41 | log.Debug("Chan %p gets '%v'", c, in) 42 | c.buffer = append(c.buffer, in) 43 | case c.Out <- c.buffer[0]: 44 | log.Debug("Chan %p sends '%v'", c, c.buffer[0]) 45 | c.buffer = c.buffer[1:] 46 | } 47 | } else { 48 | // The buffer is empty, so there's nothing to send. 49 | // Just wait to receive. 50 | in, ok := <-c.In 51 | if !ok { 52 | log.Debug("Chan %p will dispose", c) 53 | break 54 | } 55 | log.Debug("Chan %p gets '%v'", c, in) 56 | c.buffer = append(c.buffer, in) 57 | } 58 | } 59 | 60 | c.dispose() 61 | } 62 | 63 | func (c *ElasticChan) dispose() { 64 | log.Debug("Chan %p disposing...", c) 65 | for len(c.buffer) > 0 { 66 | c.Out <- c.buffer[0] 67 | c.buffer = c.buffer[1:] 68 | } 69 | log.Debug("Chan %p disposed", c) 70 | } 71 | -------------------------------------------------------------------------------- /transport/transport_udp.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "github.com/stefankopieczek/gossip/base" 5 | "github.com/stefankopieczek/gossip/log" 6 | "github.com/stefankopieczek/gossip/parser" 7 | ) 8 | 9 | import ( 10 | "net" 11 | ) 12 | 13 | type Udp struct { 14 | listeningPoints []*net.UDPConn 15 | output chan base.SipMessage 16 | stop bool 17 | } 18 | 19 | func NewUdp(output chan base.SipMessage) (*Udp, error) { 20 | newUdp := Udp{listeningPoints: make([]*net.UDPConn, 0), output: output} 21 | return &newUdp, nil 22 | } 23 | 24 | func (udp *Udp) Listen(address string) error { 25 | addr, err := net.ResolveUDPAddr("udp", address) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | lp, err := net.ListenUDP("udp", addr) 31 | 32 | if err == nil { 33 | udp.listeningPoints = append(udp.listeningPoints, lp) 34 | go udp.listen(lp) 35 | } 36 | 37 | return err 38 | } 39 | 40 | func (udp *Udp) IsStreamed() bool { 41 | return false 42 | } 43 | 44 | func (udp *Udp) Send(addr string, msg base.SipMessage) error { 45 | log.Debug("Sending message %s to %s", msg.Short(), addr) 46 | raddr, err := net.ResolveUDPAddr("udp", addr) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | var conn *net.UDPConn 52 | conn, err = net.DialUDP("udp", nil, raddr) 53 | if err != nil { 54 | return err 55 | } 56 | defer conn.Close() 57 | 58 | _, err = conn.Write([]byte(msg.String())) 59 | 60 | return err 61 | } 62 | 63 | func (udp *Udp) listen(conn *net.UDPConn) { 64 | log.Info("Begin listening for UDP on address %s", conn.LocalAddr()) 65 | 66 | buffer := make([]byte, c_BUFSIZE) 67 | for { 68 | num, _, err := conn.ReadFromUDP(buffer) 69 | if err != nil { 70 | if udp.stop { 71 | log.Info("Stopped listening for UDP on %s", conn.LocalAddr) 72 | break 73 | } else { 74 | log.Severe("Failed to read from UDP buffer: " + err.Error()) 75 | continue 76 | } 77 | } 78 | 79 | pkt := append([]byte(nil), buffer[:num]...) 80 | go func() { 81 | msg, err := parser.ParseMessage(pkt) 82 | if err != nil { 83 | log.Warn("Failed to parse SIP message: %s", err.Error()) 84 | } else { 85 | udp.output <- msg 86 | } 87 | }() 88 | } 89 | } 90 | 91 | func (udp *Udp) Stop() { 92 | udp.stop = true 93 | for _, lp := range udp.listeningPoints { 94 | lp.Close() 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /parser/parserbuffer.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | 8 | "github.com/stefankopieczek/gossip/log" 9 | ) 10 | 11 | // parserBuffer is a specialized buffer for use in the parser package. 12 | // It is written to via the non-blocking Write. 13 | // It exposes various blocking read methods, which wait until the requested 14 | // data is avaiable, and then return it. 15 | type parserBuffer struct { 16 | io.Writer 17 | buffer bytes.Buffer 18 | 19 | // Wraps parserBuffer.pipeReader 20 | reader *bufio.Reader 21 | 22 | // Don't access this directly except when closing. 23 | pipeReader *io.PipeReader 24 | } 25 | 26 | // Create a new parserBuffer object (see struct comment for object details). 27 | // Note that resources owned by the parserBuffer may not be able to be GCed 28 | // until the Dispose() method is called. 29 | func newParserBuffer() *parserBuffer { 30 | var pb parserBuffer 31 | pb.pipeReader, pb.Writer = io.Pipe() 32 | pb.reader = bufio.NewReader(pb.pipeReader) 33 | return &pb 34 | } 35 | 36 | // Block until the buffer contains at least one CRLF-terminated line. 37 | // Return the line, excluding the terminal CRLF, and delete it from the buffer. 38 | // Returns an error if the parserbuffer has been stopped. 39 | func (pb *parserBuffer) NextLine() (response string, err error) { 40 | var buffer bytes.Buffer 41 | var data string 42 | var b byte 43 | 44 | // There has to be a better way! 45 | for { 46 | data, err = pb.reader.ReadString('\r') 47 | if err != nil { 48 | return 49 | } 50 | 51 | buffer.WriteString(data) 52 | 53 | b, err = pb.reader.ReadByte() 54 | if err != nil { 55 | return 56 | } 57 | 58 | buffer.WriteByte(b) 59 | if b == '\n' { 60 | response = buffer.String() 61 | response = response[:len(response)-2] 62 | log.Debug("Parser buffer returns line '%s'", response) 63 | return 64 | } 65 | } 66 | } 67 | 68 | // Block until the buffer contains at least n characters. 69 | // Return precisely those n characters, then delete them from the buffer. 70 | func (pb *parserBuffer) NextChunk(n int) (response string, err error) { 71 | var data []byte = make([]byte, n) 72 | 73 | var read int 74 | for total := 0; total < n; { 75 | read, err = pb.reader.Read(data[total:]) 76 | total += read 77 | if err != nil { 78 | return 79 | } 80 | } 81 | 82 | response = string(data) 83 | log.Debug("Parser buffer returns chunk '%s'", response) 84 | return 85 | } 86 | 87 | // Stop the parser buffer. 88 | func (pb *parserBuffer) Stop() { 89 | pb.pipeReader.Close() 90 | } 91 | -------------------------------------------------------------------------------- /transport/transport_fv_test.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stefankopieczek/gossip/base" 10 | ) 11 | 12 | type endpoint struct { 13 | host string 14 | port uint16 15 | } 16 | 17 | var alice endpoint = endpoint{"127.0.0.1", 10862} 18 | var bob endpoint = endpoint{"127.0.0.1", 10863} 19 | 20 | func TestMassUDP(t *testing.T) { 21 | NUM_MSGS := 10000 22 | from, _ := NewManager("udp") 23 | to, _ := NewManager("udp") 24 | to.Listen(fmt.Sprintf("%s:%d", alice.host, alice.port)) 25 | receiver := to.GetChannel() 26 | 27 | receivedIDs := make([]int, 0) 28 | result := make(chan bool) 29 | 30 | go func() { 31 | recvloop: 32 | for { 33 | select { 34 | case msg, ok := <-receiver: 35 | if !ok { 36 | break recvloop 37 | } 38 | id, _ := strconv.ParseInt(msg.GetBody(), 10, 32) 39 | receivedIDs = append(receivedIDs, int(id)) 40 | if len(receivedIDs) >= NUM_MSGS { 41 | break recvloop 42 | } 43 | case <-time.After(time.Second / 10): 44 | break recvloop 45 | } 46 | } 47 | 48 | if !(len(receivedIDs) == NUM_MSGS) { 49 | t.Errorf("Error - received an unexpected number of messages: %d", len(receivedIDs)) 50 | } else { 51 | seenSet := make(map[int]bool) 52 | for _, val := range receivedIDs { 53 | if _, match := seenSet[val]; match { 54 | t.Errorf("Duplicate message received: %d", val) 55 | } 56 | seenSet[val] = true 57 | } 58 | if len(seenSet) != NUM_MSGS { 59 | t.Errorf("Unexpected number of messages received: %d", len(seenSet)) 60 | } 61 | } 62 | result <- true 63 | }() 64 | 65 | go func() { 66 | uri := base.SipUri{User: base.String{"alice"}, Host: "127.0.0.1", Port: nil, UriParams: base.NewParams(), Headers: base.NewParams()} 67 | for ii := 1; ii <= NUM_MSGS; ii++ { 68 | from.Send(fmt.Sprintf("%s:%d", alice.host, alice.port), 69 | base.NewRequest(base.ACK, &uri, "SIP/2.0", 70 | []base.SipHeader{base.ContentLength(len(fmt.Sprintf("%d", ii)))}, 71 | fmt.Sprintf("%d", ii))) 72 | 73 | if ii%100 == 0 { 74 | <-time.After(time.Millisecond) 75 | } 76 | } 77 | }() 78 | 79 | _ = <-result 80 | return 81 | } 82 | 83 | func sendAndCheckReceipt(from Manager, to string, 84 | receiver chan base.SipMessage, 85 | msg base.SipMessage, timeout time.Duration) bool { 86 | from.Send(to, msg) 87 | 88 | select { 89 | case msgIn, ok := <-receiver: 90 | if !ok { 91 | return false 92 | } 93 | 94 | return msgIn.String() == msg.String() 95 | 96 | case <-time.After(timeout): 97 | return false 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /transport/transport_tcp.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "github.com/stefankopieczek/gossip/base" 5 | "github.com/stefankopieczek/gossip/log" 6 | "github.com/stefankopieczek/gossip/parser" 7 | ) 8 | 9 | import "net" 10 | 11 | type Tcp struct { 12 | connTable 13 | listeningPoints []*net.TCPListener 14 | parser *parser.Parser 15 | output chan base.SipMessage 16 | stop bool 17 | } 18 | 19 | func NewTcp(output chan base.SipMessage) (*Tcp, error) { 20 | tcp := Tcp{output: output} 21 | tcp.listeningPoints = make([]*net.TCPListener, 0) 22 | tcp.connTable.Init() 23 | return &tcp, nil 24 | } 25 | 26 | func (tcp *Tcp) Listen(address string) error { 27 | var err error = nil 28 | addr, err := net.ResolveTCPAddr("tcp", address) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | lp, err := net.ListenTCP("tcp", addr) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | tcp.listeningPoints = append(tcp.listeningPoints, lp) 39 | go tcp.serve(lp) 40 | 41 | // At this point, err should be nil but let's be defensive. 42 | return err 43 | } 44 | 45 | func (tcp *Tcp) IsStreamed() bool { 46 | return true 47 | } 48 | 49 | func (tcp *Tcp) getConnection(addr string) (*connection, error) { 50 | conn := tcp.connTable.GetConn(addr) 51 | 52 | if conn == nil { 53 | log.Debug("No stored connection for address %s; generate a new one", addr) 54 | raddr, err := net.ResolveTCPAddr("tcp", addr) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | baseConn, err := net.DialTCP("tcp", nil, raddr) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | conn = NewConn(baseConn, tcp.output) 65 | } else { 66 | conn = tcp.connTable.GetConn(addr) 67 | } 68 | 69 | tcp.connTable.Notify(addr, conn) 70 | return conn, nil 71 | } 72 | 73 | func (tcp *Tcp) Send(addr string, msg base.SipMessage) error { 74 | conn, err := tcp.getConnection(addr) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | err = conn.Send(msg) 80 | return err 81 | } 82 | 83 | func (tcp *Tcp) serve(listeningPoint *net.TCPListener) { 84 | log.Info("Begin serving TCP on address " + listeningPoint.Addr().String()) 85 | 86 | for { 87 | baseConn, err := listeningPoint.Accept() 88 | if err != nil { 89 | log.Severe("Failed to accept TCP conn on address " + listeningPoint.Addr().String() + "; " + err.Error()) 90 | continue 91 | } 92 | 93 | conn := NewConn(baseConn, tcp.output) 94 | log.Debug("Accepted new TCP conn %p from %s on address %s", &conn, conn.baseConn.RemoteAddr(), conn.baseConn.LocalAddr()) 95 | tcp.connTable.Notify(baseConn.RemoteAddr().String(), conn) 96 | } 97 | } 98 | 99 | func (tcp *Tcp) Stop() { 100 | tcp.connTable.Stop() 101 | tcp.stop = true 102 | for _, lp := range tcp.listeningPoints { 103 | lp.Close() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /transport/connection.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net" 7 | 8 | "github.com/stefankopieczek/gossip/base" 9 | "github.com/stefankopieczek/gossip/log" 10 | "github.com/stefankopieczek/gossip/parser" 11 | ) 12 | 13 | type connection struct { 14 | baseConn net.Conn 15 | isStreamed bool 16 | parser parser.Parser 17 | parsedMessages chan base.SipMessage 18 | parserErrors chan error 19 | output chan base.SipMessage 20 | } 21 | 22 | func NewConn(baseConn net.Conn, output chan base.SipMessage) *connection { 23 | var isStreamed bool 24 | switch baseConn.(type) { 25 | case *net.UDPConn: 26 | isStreamed = false 27 | case *net.TCPConn: 28 | isStreamed = true 29 | case *tls.Conn: 30 | isStreamed = true 31 | default: 32 | log.Severe("Conn object %v is not a known connection type. Assume it's a streamed protocol, but this may cause messages to be rejected") 33 | } 34 | connection := connection{baseConn: baseConn, isStreamed: isStreamed} 35 | 36 | connection.parsedMessages = make(chan base.SipMessage) 37 | connection.parserErrors = make(chan error) 38 | connection.output = output 39 | connection.parser = parser.NewParser(connection.parsedMessages, 40 | connection.parserErrors, 41 | connection.isStreamed) 42 | 43 | go connection.read() 44 | go connection.pipeOutput() 45 | 46 | return &connection 47 | } 48 | 49 | func (connection *connection) Send(msg base.SipMessage) (err error) { 50 | log.Debug("Sending message over connection %p: %s", connection, msg.Short()) 51 | msgData := msg.String() 52 | n, err := connection.baseConn.Write([]byte(msgData)) 53 | 54 | if err != nil { 55 | return 56 | } 57 | 58 | if n != len(msgData) { 59 | return fmt.Errorf("not all data was sent when dispatching '%s' to %s", 60 | msg.Short(), connection.baseConn.RemoteAddr()) 61 | } 62 | 63 | return 64 | } 65 | 66 | func (connection *connection) Close() error { 67 | connection.parser.Stop() 68 | return connection.baseConn.Close() 69 | } 70 | 71 | func (connection *connection) read() { 72 | buffer := make([]byte, c_BUFSIZE) 73 | for { 74 | log.Debug("Connection %p waiting for new data on sock", connection) 75 | num, err := connection.baseConn.Read(buffer) 76 | if err != nil { 77 | // If connections are broken, just let them drop. 78 | log.Debug("Lost connection to %s on %s", 79 | connection.baseConn.RemoteAddr().String(), 80 | connection.baseConn.LocalAddr().String()) 81 | return 82 | } 83 | 84 | log.Debug("Connection %p received %d bytes", connection, num) 85 | pkt := append([]byte(nil), buffer[:num]...) 86 | connection.parser.Write(pkt) 87 | } 88 | } 89 | 90 | func (connection *connection) pipeOutput() { 91 | for { 92 | select { 93 | case message, ok := <-connection.parsedMessages: 94 | if ok { 95 | log.Debug("Connection %p from %s to %s received message over the wire: %s", 96 | connection, 97 | connection.baseConn.RemoteAddr(), 98 | connection.baseConn.LocalAddr(), 99 | message.Short()) 100 | connection.output <- message 101 | } else { 102 | break 103 | } 104 | case err, ok := <-connection.parserErrors: 105 | if ok { 106 | // The parser has hit a terminal error. We need to restart it. 107 | log.Warn("Failed to parse SIP message: %s", err.Error()) 108 | connection.parser = parser.NewParser(connection.parsedMessages, 109 | connection.parserErrors, connection.isStreamed) 110 | } else { 111 | break 112 | } 113 | } 114 | } 115 | 116 | log.Info("Parser stopped in ConnWrapper %v (local addr %s; remote addr %s); stopping listening", 117 | connection, connection.baseConn.LocalAddr(), connection.baseConn.RemoteAddr()) 118 | } 119 | -------------------------------------------------------------------------------- /transport/transport.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "sync" 7 | "time" 8 | 9 | "github.com/stefankopieczek/gossip/base" 10 | "github.com/stefankopieczek/gossip/log" 11 | ) 12 | 13 | const c_BUFSIZE int = 65507 14 | const c_LISTENER_QUEUE_SIZE int = 1000 15 | const c_SOCKET_EXPIRY time.Duration = time.Hour 16 | 17 | type Manager interface { 18 | Listen(address string) error 19 | Send(addr string, message base.SipMessage) error 20 | Stop() 21 | GetChannel() Listener 22 | } 23 | 24 | type manager struct { 25 | notifier 26 | transport transport 27 | } 28 | 29 | type transport interface { 30 | IsStreamed() bool 31 | Listen(address string) error 32 | Send(addr string, message base.SipMessage) error 33 | Stop() 34 | } 35 | 36 | func NewManager(transportType string) (m Manager, err error) { 37 | err = fmt.Errorf("Unknown transport type '%s'", transportType) 38 | 39 | var n notifier 40 | n.init() 41 | 42 | var transport transport 43 | switch strings.ToLower(transportType) { 44 | case "udp": 45 | transport, err = NewUdp(n.inputs) 46 | case "tcp": 47 | transport, err = NewTcp(n.inputs) 48 | case "tls": 49 | // TODO 50 | } 51 | 52 | if transport != nil && err == nil { 53 | m = &manager{notifier: n, transport: transport} 54 | } else { 55 | // Close the input chan in order to stop the notifier; this prevents 56 | // us leaking it. 57 | close(n.inputs) 58 | } 59 | 60 | return 61 | } 62 | 63 | func (manager *manager) Listen(address string) error { 64 | return manager.transport.Listen(address) 65 | } 66 | 67 | func (manager *manager) Send(addr string, message base.SipMessage) error { 68 | return manager.transport.Send(addr, message) 69 | } 70 | 71 | func (manager *manager) Stop() { 72 | manager.transport.Stop() 73 | manager.notifier.stop() 74 | } 75 | 76 | type notifier struct { 77 | listeners map[Listener]bool 78 | listenerLock sync.Mutex 79 | inputs chan base.SipMessage 80 | } 81 | 82 | func (n *notifier) init() { 83 | n.listeners = make(map[Listener]bool) 84 | n.inputs = make(chan base.SipMessage) 85 | go n.forward() 86 | } 87 | 88 | func (n *notifier) register(l Listener) { 89 | log.Debug("Notifier %p has new listener %p", n, l) 90 | if n.listeners == nil { 91 | n.listeners = make(map[Listener]bool) 92 | } 93 | n.listenerLock.Lock() 94 | n.listeners[l] = true 95 | n.listenerLock.Unlock() 96 | } 97 | 98 | func (n *notifier) GetChannel() (l Listener) { 99 | c := make(chan base.SipMessage, c_LISTENER_QUEUE_SIZE) 100 | n.register(c) 101 | return c 102 | } 103 | 104 | func (n *notifier) forward() { 105 | for msg := range n.inputs { 106 | deadListeners := make([]chan base.SipMessage, 0) 107 | n.listenerLock.Lock() 108 | log.Debug(fmt.Sprintf("Notify %d listeners of message", len(n.listeners))) 109 | for listener := range n.listeners { 110 | sent := listener.notify(msg) 111 | if !sent { 112 | deadListeners = append(deadListeners, listener) 113 | } 114 | } 115 | for _, deadListener := range deadListeners { 116 | log.Debug(fmt.Sprintf("Expiring listener %#v", deadListener)) 117 | delete(n.listeners, deadListener) 118 | } 119 | n.listenerLock.Unlock() 120 | } 121 | } 122 | 123 | func (n *notifier) stop() { 124 | n.listenerLock.Lock() 125 | for c, _ := range n.listeners { 126 | close(c) 127 | } 128 | n.listeners = nil 129 | n.listenerLock.Unlock() 130 | } 131 | 132 | type Listener chan base.SipMessage 133 | 134 | // notify tries to send a message to the listener. 135 | // If the underlying channel has been closed by the receiver, return 'false'; 136 | // otherwise, return true. 137 | func (c Listener) notify(message base.SipMessage) (ok bool) { 138 | defer func() { recover() }() 139 | c <- message 140 | return true 141 | } 142 | -------------------------------------------------------------------------------- /transport/conntable_test.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | import ( 10 | "github.com/stefankopieczek/gossip/base" 11 | "github.com/stefankopieczek/gossip/log" 12 | "github.com/stefankopieczek/gossip/parser" 13 | "github.com/stefankopieczek/gossip/testutils" 14 | "github.com/stefankopieczek/gossip/timing" 15 | ) 16 | 17 | var c_LOG_LEVEL = log.WARN 18 | 19 | func TestMain(m *testing.M) { 20 | timing.MockMode = true 21 | log.SetDefaultLogLevel(c_LOG_LEVEL) 22 | os.Exit(m.Run()) 23 | } 24 | 25 | // Test that we can store and retrieve a connection. 26 | func TestBasicStorage(t *testing.T) { 27 | var table connTable 28 | table.Init() 29 | defer table.Stop() 30 | 31 | conn := makeTestConn() 32 | table.Notify("foo", conn) 33 | 34 | if table.GetConn("foo") != conn { 35 | t.FailNow() 36 | } 37 | } 38 | 39 | // Test that after the configured expiry time, a stored connection expires and 40 | // is nulled out. 41 | func TestBasicExpiry(t *testing.T) { 42 | var table connTable 43 | table.Init() 44 | defer table.Stop() // This currently panics due to issue #13. 45 | 46 | table.Notify("bar", makeTestConn()) 47 | timing.Elapse(c_SOCKET_EXPIRY) 48 | timing.Elapse(time.Nanosecond) 49 | 50 | if !testutils.Eventually(func() bool { return table.GetConn("bar") != nil }) { 51 | t.FailNow() 52 | } 53 | } 54 | 55 | // Test that two different connections can be stored at the same time. 56 | func TestDoubleStorage(t *testing.T) { 57 | var table connTable 58 | table.Init() 59 | defer table.Stop() 60 | 61 | conn1 := makeTestConn() 62 | table.Notify("foo", conn1) 63 | conn2 := makeTestConn() 64 | table.Notify("bar", conn2) 65 | 66 | if table.GetConn("foo") != conn1 { 67 | t.FailNow() 68 | } else if table.GetConn("bar") != conn2 { 69 | t.FailNow() 70 | } 71 | } 72 | 73 | // Test that we can push an update to a stored connection, and that when we 74 | // retrieve the connection we get the updated value. 75 | func TestUpdate(t *testing.T) { 76 | var table connTable 77 | table.Init() 78 | defer table.Stop() 79 | table.Notify("foo", makeTestConn()) 80 | conn2 := makeTestConn() 81 | table.Notify("foo", conn2) 82 | 83 | if table.GetConn("foo") != conn2 { 84 | t.FailNow() 85 | } 86 | } 87 | 88 | // Test that if a connection is allowed to expire, we can store the same connection 89 | // again with the same key and retrieve it. 90 | func TestReuse1(t *testing.T) { 91 | var table connTable 92 | table.Init() 93 | defer table.Stop() // This currently panics due to issue #13. 94 | 95 | conn := makeTestConn() 96 | table.Notify("foo", conn) 97 | timing.Elapse(c_SOCKET_EXPIRY) 98 | timing.Elapse(time.Nanosecond) 99 | 100 | // Wait for connection to definitely expire. 101 | if !testutils.Eventually(func() bool { return table.GetConn("foo") == nil }) { 102 | t.FailNow() 103 | } 104 | 105 | // Re-store and retrieve. 106 | table.Notify("foo", conn) 107 | if table.GetConn("foo") != conn { 108 | t.FailNow() 109 | } 110 | } 111 | 112 | // Test that if a connection is allowed to expire, we can store a different connection 113 | // with the same key and then retrieve it. 114 | func TestReuse2(t *testing.T) { 115 | var table connTable 116 | table.Init() 117 | defer table.Stop() 118 | 119 | table.Notify("foo", makeTestConn()) 120 | timing.Elapse(c_SOCKET_EXPIRY) 121 | timing.Elapse(time.Nanosecond) 122 | 123 | // Wait for connection to definitely expire. 124 | if !testutils.Eventually(func() bool { return table.GetConn("foo") == nil }) { 125 | t.FailNow() 126 | } 127 | 128 | conn2 := makeTestConn() 129 | table.Notify("foo", conn2) 130 | if table.GetConn("foo") != conn2 { 131 | t.FailNow() 132 | } 133 | } 134 | 135 | // Construct a dummy connection object to use to populate the connTable for tests. 136 | func makeTestConn() *connection { 137 | parsedMessages := make(chan base.SipMessage) 138 | errors := make(chan error) 139 | streamed := true 140 | return &connection{ 141 | &testutils.DummyConn{}, 142 | true, 143 | parser.NewParser(parsedMessages, errors, streamed), 144 | parsedMessages, 145 | errors, 146 | make(chan base.SipMessage), 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "path" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | const c_STACK_BUFFER_SIZE int = 8192 17 | 18 | // Number of frames to search up the stack for a function not in the log package. 19 | // Used to find the function name of the entry point. 20 | const c_NUM_STACK_FRAMES int = 5 21 | 22 | // Format for stack info. 23 | // The lengths of these two format strings should add up so that logs line up correctly. 24 | // c_STACK_INFO_FMT takes three parameters: filename, line number, function name. 25 | // c_NO_STACK_FMT takes one parameter: [Unidentified Location] 26 | const c_STACK_INFO_FMT string = "%20.20s %04d %40.40s" 27 | const c_NO_STACK_FMT string = "%-66s" 28 | 29 | // Format for log level. ie. [FINE ] 30 | const c_LOG_LEVEL_FMT string = "[%-5.5s]" 31 | 32 | // Format for timestamps. 33 | const c_TIMESTAMP_FMT string = "2006-01-02 15:04:05.000" 34 | 35 | type Level struct { 36 | Name string 37 | Level int 38 | } 39 | 40 | var ( 41 | unspecified = Level{"NIL", 0} // Discard first value so 0 can be a placeholder. 42 | DEBUG = Level{"DEBUG", 1} 43 | FINE = Level{"FINE", 2} 44 | INFO = Level{"INFO", 3} 45 | WARN = Level{"WARN", 4} 46 | SEVERE = Level{"SEVERE", 5} 47 | ) 48 | 49 | var c_DEFAULT_LOGGING_LEVEL = INFO 50 | var c_DEFAULT_STACKTRACE_LEVEL = SEVERE 51 | 52 | type Logger struct { 53 | *log.Logger 54 | Level Level 55 | StackTraceLevel Level 56 | } 57 | 58 | var defaultLogger *Logger 59 | 60 | func New(out io.Writer, prefix string, flags int) *Logger { 61 | var logger Logger 62 | logger.Logger = log.New(out, prefix, flags) 63 | logger.Level = c_DEFAULT_LOGGING_LEVEL 64 | logger.StackTraceLevel = c_DEFAULT_STACKTRACE_LEVEL 65 | return &logger 66 | } 67 | 68 | func (l *Logger) Log(level Level, msg string, args ...interface{}) { 69 | if level.Level < l.Level.Level { 70 | return 71 | } 72 | 73 | // Get current time. 74 | now := time.Now() 75 | 76 | // Get information about the stack. 77 | // Try and find the first stack frame outside the logging package. 78 | // Only search up a few frames, it should never be very far. 79 | stackInfo := fmt.Sprintf(c_NO_STACK_FMT, "[Unidentified Location]") 80 | for depth := 0; depth < c_NUM_STACK_FRAMES; depth++ { 81 | if pc, file, line, ok := runtime.Caller(depth); ok { 82 | funcName := runtime.FuncForPC(pc).Name() 83 | funcName = path.Base(funcName) 84 | 85 | // Go up another stack frame if this function is in the logging package. 86 | isLog := strings.HasPrefix(funcName, "log.") 87 | if isLog { 88 | continue 89 | } 90 | 91 | // Now generate the string. 92 | stackInfo = fmt.Sprintf(c_STACK_INFO_FMT, 93 | filepath.Base(file), 94 | line, 95 | funcName) 96 | break 97 | } 98 | 99 | // If we get here, we failed to retrieve the stack information. 100 | // Just give up. 101 | break 102 | } 103 | 104 | // Write all the data into a buffer. 105 | // Format is: 106 | // [LEVEL] - 107 | var buffer bytes.Buffer 108 | buffer.WriteString(fmt.Sprintf(c_LOG_LEVEL_FMT, level.Name)) 109 | buffer.WriteString(now.Format(c_TIMESTAMP_FMT)) 110 | buffer.WriteString(" ") 111 | buffer.WriteString(stackInfo) 112 | buffer.WriteString(" - ") 113 | buffer.WriteString(fmt.Sprintf(msg, args...)) 114 | buffer.WriteString("\n") 115 | 116 | if level.Level >= l.StackTraceLevel.Level { 117 | buffer.WriteString("--- BEGIN stacktrace: ---\n") 118 | buffer.WriteString(stackTrace()) 119 | buffer.WriteString("--- END stacktrace ---\n\n") 120 | } 121 | 122 | l.Logger.Printf(buffer.String()) 123 | } 124 | 125 | func (l *Logger) Debug(msg string, args ...interface{}) { 126 | l.Log(DEBUG, msg, args...) 127 | } 128 | 129 | func (l *Logger) Fine(msg string, args ...interface{}) { 130 | l.Log(FINE, msg, args...) 131 | } 132 | 133 | func (l *Logger) Info(msg string, args ...interface{}) { 134 | l.Log(INFO, msg, args...) 135 | } 136 | 137 | func (l *Logger) Warn(msg string, args ...interface{}) { 138 | l.Log(WARN, msg, args...) 139 | } 140 | 141 | func (l *Logger) Severe(msg string, args ...interface{}) { 142 | l.Log(SEVERE, msg, args...) 143 | } 144 | 145 | func (l *Logger) PrintStack() { 146 | l.Logger.Printf(stackTrace()) 147 | } 148 | 149 | func stackTrace() string { 150 | trace := make([]byte, c_STACK_BUFFER_SIZE) 151 | count := runtime.Stack(trace, true) 152 | return string(trace[:count]) 153 | } 154 | 155 | func Debug(msg string, args ...interface{}) { 156 | if defaultLogger == nil { 157 | defaultLogger = New(os.Stderr, "", 0) 158 | } 159 | defaultLogger.Debug(msg, args...) 160 | } 161 | 162 | func Fine(msg string, args ...interface{}) { 163 | if defaultLogger == nil { 164 | defaultLogger = New(os.Stderr, "", 0) 165 | } 166 | defaultLogger.Fine(msg, args...) 167 | } 168 | 169 | func Info(msg string, args ...interface{}) { 170 | if defaultLogger == nil { 171 | defaultLogger = New(os.Stderr, "", 0) 172 | } 173 | defaultLogger.Info(msg, args...) 174 | } 175 | 176 | func Warn(msg string, args ...interface{}) { 177 | if defaultLogger == nil { 178 | defaultLogger = New(os.Stderr, "", 0) 179 | } 180 | defaultLogger.Warn(msg, args...) 181 | } 182 | 183 | func Severe(msg string, args ...interface{}) { 184 | if defaultLogger == nil { 185 | defaultLogger = New(os.Stderr, "", 0) 186 | } 187 | defaultLogger.Severe(msg, args...) 188 | } 189 | 190 | func SetDefaultLogLevel(level Level) { 191 | if defaultLogger == nil { 192 | defaultLogger = New(os.Stderr, "", 0) 193 | } 194 | defaultLogger.Level = level 195 | } 196 | -------------------------------------------------------------------------------- /transport/conntable.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/stefankopieczek/gossip/log" 7 | "github.com/stefankopieczek/gossip/timing" 8 | ) 9 | 10 | // Fields of connTable should only be modified by the dedicated goroutine called by Init(). 11 | // All other callers should use connTable's associated public methods to access it. 12 | type connTable struct { 13 | conns map[string]*connWatcher 14 | connRequests chan *connRequest 15 | updates chan *connUpdate 16 | expiries chan string 17 | stop chan bool 18 | stopped bool 19 | } 20 | 21 | type connWatcher struct { 22 | addr string 23 | conn *connection 24 | timer timing.Timer 25 | expiryTime time.Time 26 | expiry chan<- string 27 | stop chan bool 28 | } 29 | 30 | // Create a new connection table. 31 | func (t *connTable) Init() { 32 | log.Info("Init conntable %p", t) 33 | t.conns = make(map[string]*connWatcher) 34 | t.connRequests = make(chan *connRequest) 35 | t.updates = make(chan *connUpdate) 36 | t.expiries = make(chan string) 37 | t.stop = make(chan bool) 38 | go t.manage() 39 | } 40 | 41 | // Management loop for the connTable. 42 | // Handles notifications of connection updates, expiries of connections, and 43 | // the termination of the routine. 44 | func (t *connTable) manage() { 45 | for { 46 | select { 47 | case request := <-t.connRequests: 48 | watcher := t.conns[request.addr] 49 | if watcher != nil { 50 | request.responseChan <- watcher.conn 51 | } else { 52 | request.responseChan <- nil 53 | } 54 | case update := <-t.updates: 55 | t.handleUpdate(update) 56 | case addr := <-t.expiries: 57 | if t.conns[addr].expiryTime.Before(time.Now()) { 58 | log.Debug("Conntable %p notified that the watcher for address %s has expired. Remove it.", t, addr) 59 | t.conns[addr].stop <- true 60 | t.conns[addr].conn.Close() 61 | delete(t.conns, addr) 62 | } else { 63 | // Due to a race condition, the socket has been updated since this expiry happened. 64 | // Ignore the expiry since we already have a new socket for this address. 65 | log.Warn("Ignored spurious expiry for address %s in conntable %p", t, addr) 66 | } 67 | case <-t.stop: 68 | log.Info("Conntable %p stopped") 69 | t.stopped = true 70 | for _, watcher := range t.conns { 71 | watcher.stop <- true 72 | watcher.conn.Close() 73 | } 74 | break 75 | } 76 | } 77 | } 78 | 79 | // Push a connection to the connection table, registered under a specific address. 80 | // If it is a new connection, start the socket expiry timer. 81 | // If it is a known connection, restart the timer. 82 | func (t *connTable) Notify(addr string, conn *connection) { 83 | if t.stopped { 84 | log.Debug("Ignoring conn notification for address %s after table stop.", addr) 85 | return 86 | } 87 | 88 | t.updates <- &connUpdate{addr, conn} 89 | } 90 | 91 | func (t *connTable) handleUpdate(update *connUpdate) { 92 | log.Debug("Update received in connTable %p for address %s", t, update.addr) 93 | watcher, entry_exists := t.conns[update.addr] 94 | if !entry_exists { 95 | log.Debug("No connection watcher registered for %s; spawn one", update.addr) 96 | watcher = &connWatcher{update.addr, update.conn, timing.NewTimer(c_SOCKET_EXPIRY), timing.Now().Add(c_SOCKET_EXPIRY), t.expiries, make(chan bool)} 97 | t.conns[update.addr] = watcher 98 | go watcher.loop() 99 | } 100 | 101 | watcher.Update(update.conn) 102 | } 103 | 104 | // Return an existing open socket for the given address, or nil if no such socket 105 | // exists. 106 | func (t *connTable) GetConn(addr string) *connection { 107 | responseChan := make(chan *connection) 108 | t.connRequests <- &connRequest{addr, responseChan} 109 | conn := <-responseChan 110 | 111 | log.Debug("Query connection for address %s returns %p", conn) 112 | return conn 113 | } 114 | 115 | // Close all sockets and stop socket management. 116 | // The table cannot be restarted after Stop() has been called, and GetConn() will return nil. 117 | func (t *connTable) Stop() { 118 | t.stop <- true 119 | } 120 | 121 | // Update the connection associated with a given connWatcher, and reset the 122 | // timeout timer. 123 | // Must only be called from the connTable goroutine (and in particular, must 124 | // *not* be called from the connWatcher goroutine). 125 | func (watcher *connWatcher) Update(c *connection) { 126 | watcher.expiryTime = timing.Now().Add(c_SOCKET_EXPIRY) 127 | watcher.timer.Reset(c_SOCKET_EXPIRY) 128 | watcher.conn = c 129 | } 130 | 131 | // connWatcher main loop. Waits for the connection to expire, and notifies the connTable 132 | // when it does. 133 | func (watcher *connWatcher) loop() { 134 | // We expect to close off connections explicitly, but let's be safe and clean up 135 | // if we close unexpectedly. 136 | defer func(c *connection) { 137 | if c != nil { 138 | c.Close() 139 | } 140 | }(watcher.conn) 141 | 142 | for { 143 | select { 144 | case <-watcher.timer.C(): 145 | // Socket expiry timer has run out. Close the connection. 146 | log.Debug("Socket %p (%s) inactive for too long; close it", watcher.conn, watcher.addr) 147 | watcher.expiry <- watcher.addr 148 | 149 | case stop := <-watcher.stop: 150 | // We've received a termination signal; stop managing this connection. 151 | if stop { 152 | log.Info("Connection watcher for address %s got the kill signal. Stopping.", watcher.addr) 153 | watcher.timer.Stop() 154 | break 155 | } 156 | } 157 | } 158 | } 159 | 160 | type connUpdate struct { 161 | addr string 162 | conn *connection 163 | } 164 | 165 | type connRequest struct { 166 | addr string 167 | responseChan chan *connection 168 | } 169 | -------------------------------------------------------------------------------- /timing/timing_test.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | // Tests for the mock timing module. 4 | 5 | import ( 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestTimer(t *testing.T) { 11 | MockMode = true 12 | timer := NewTimer(5 * time.Second) 13 | done := make(chan struct{}) 14 | 15 | go func() { 16 | <-timer.C() 17 | done <- struct{}{} 18 | }() 19 | 20 | Elapse(5 * time.Second) 21 | <-done 22 | } 23 | 24 | func TestTwoTimers(t *testing.T) { 25 | MockMode = true 26 | timer1 := NewTimer(5 * time.Second) 27 | done1 := make(chan struct{}) 28 | 29 | timer2 := NewTimer(5 * time.Millisecond) 30 | done2 := make(chan struct{}) 31 | 32 | go func() { 33 | <-timer1.C() 34 | done1 <- struct{}{} 35 | }() 36 | 37 | go func() { 38 | <-timer2.C() 39 | done2 <- struct{}{} 40 | }() 41 | 42 | Elapse(5 * time.Millisecond) 43 | <-done2 44 | 45 | Elapse(9995 * time.Millisecond) 46 | <-done1 47 | } 48 | 49 | func TestAfter(t *testing.T) { 50 | MockMode = true 51 | done := make(chan struct{}) 52 | c := After(5 * time.Second) 53 | 54 | go func() { 55 | <-c 56 | done <- struct{}{} 57 | }() 58 | 59 | Elapse(5 * time.Second) 60 | <-done 61 | } 62 | 63 | func TestAfterFunc(t *testing.T) { 64 | MockMode = true 65 | done := make(chan struct{}) 66 | AfterFunc(5*time.Second, 67 | func() { 68 | done <- struct{}{} 69 | }) 70 | 71 | Elapse(5 * time.Second) 72 | <-done 73 | } 74 | 75 | func TestAfterFuncReset(t *testing.T) { 76 | MockMode = true 77 | done := make(chan struct{}) 78 | timer := AfterFunc(5*time.Second, 79 | func() { 80 | done <- struct{}{} 81 | }) 82 | 83 | Elapse(3 * time.Second) 84 | timer.Reset(5 * time.Second) 85 | Elapse(2 * time.Second) 86 | 87 | select { 88 | case <-done: 89 | t.Fatal("AfterFunc fired at its old end time after being reset.") 90 | case <-time.After(50 * time.Millisecond): 91 | t.Log("AfterFunc correctly didn't fire at its old end time after being reset.") 92 | } 93 | 94 | Elapse(3 * time.Second) 95 | select { 96 | case <-done: 97 | t.Log("AfterFunc correctly fired at its new end time after being reset.") 98 | case <-time.After(50 * time.Millisecond): 99 | t.Fatal("AfterFunc didn't fire at its new end time after being reset.") 100 | } 101 | } 102 | 103 | func TestAfterFuncExpiredReset(t *testing.T) { 104 | MockMode = true 105 | done := make(chan struct{}) 106 | timer := AfterFunc(5*time.Second, 107 | func() { 108 | done <- struct{}{} 109 | }) 110 | 111 | Elapse(5 * time.Second) 112 | 113 | select { 114 | case <-done: 115 | t.Log("AfterFunc correctly fired at its end time.") 116 | case <-time.After(50 * time.Millisecond): 117 | t.Fatal("AfterFunc didn't fire at its end time") 118 | } 119 | 120 | timer.Reset(5 * time.Second) 121 | Elapse(5 * time.Second) 122 | select { 123 | case <-done: 124 | t.Log("AfterFunc correctly fired at its new end time after being reset.") 125 | case <-time.After(50 * time.Millisecond): 126 | t.Fatal("AfterFunc didn't fire at its new end time after being reset.") 127 | } 128 | } 129 | 130 | func TestExpiredReset(t *testing.T) { 131 | MockMode = true 132 | timer := NewTimer(5 * time.Second) 133 | done := make(chan struct{}) 134 | 135 | go func() { 136 | <-timer.C() 137 | done <- struct{}{} 138 | }() 139 | 140 | Elapse(5 * time.Second) 141 | <-done 142 | 143 | timer.Reset(3 * time.Second) 144 | go func() { 145 | <-timer.C() 146 | done <- struct{}{} 147 | }() 148 | 149 | Elapse(2 * time.Second) 150 | select { 151 | case <-done: 152 | t.Fatal("Timer fired at its old end time after being reset.") 153 | case <-time.After(50 * time.Millisecond): 154 | t.Log("Timer correctly didn't fire at its old end time after being reset.") 155 | } 156 | 157 | Elapse(1 * time.Second) 158 | select { 159 | case <-done: 160 | t.Log("Timer correctly fired at its new end time after being reset.") 161 | case <-time.After(50 * time.Millisecond): 162 | t.Fatal("Timer didn't fire at its new end time after being reset.") 163 | } 164 | } 165 | 166 | func TestNotExpiredReset(t *testing.T) { 167 | MockMode = true 168 | timer := NewTimer(5 * time.Second) 169 | done := make(chan struct{}) 170 | 171 | go func() { 172 | <-timer.C() 173 | done <- struct{}{} 174 | }() 175 | 176 | Elapse(4 * time.Second) 177 | timer.Reset(5 * time.Second) 178 | Elapse(1 * time.Second) 179 | 180 | select { 181 | case <-done: 182 | t.Fatal("Timer fired at its old end time after being reset.") 183 | case <-time.After(50 * time.Millisecond): 184 | t.Log("Timer correctly didn't fire at its old end time after being reset.") 185 | } 186 | 187 | Elapse(4 * time.Second) 188 | select { 189 | case <-done: 190 | t.Log("Timer correctly fired at its new end time after being reset.") 191 | case <-time.After(50 * time.Millisecond): 192 | t.Fatal("Timer didn't fire at its new end time after being reset.") 193 | } 194 | } 195 | 196 | // This is a regression test for a bug where: 197 | // - Create 3 timers. 198 | // - Reset() the first one. 199 | // - The third timer is now no longer tracked and won't fire. 200 | func TestThreeTimersWithReset(t *testing.T) { 201 | MockMode = true 202 | timer1 := NewTimer(1 * time.Second) 203 | done1 := make(chan struct{}) 204 | 205 | timer2 := NewTimer(2 * time.Second) 206 | done2 := make(chan struct{}) 207 | 208 | timer3 := NewTimer(3 * time.Second) 209 | done3 := make(chan struct{}) 210 | 211 | go func() { 212 | <-timer1.C() 213 | done1 <- struct{}{} 214 | }() 215 | 216 | go func() { 217 | <-timer2.C() 218 | done2 <- struct{}{} 219 | }() 220 | 221 | go func() { 222 | <-timer3.C() 223 | done3 <- struct{}{} 224 | }() 225 | 226 | timer1.Reset(4 * time.Second) 227 | 228 | Elapse(2 * time.Second) 229 | <-done2 230 | 231 | Elapse(1 * time.Second) 232 | // Panic here if bug exists. 233 | <-done3 234 | } 235 | -------------------------------------------------------------------------------- /timing/timing.go: -------------------------------------------------------------------------------- 1 | package timing 2 | 3 | import "time" 4 | 5 | // Controls whether library calls should be mocked, or whether we should use the standard Go time library. 6 | // If we're in Mock Mode, then time does not pass as normal, but only progresses when Elapse is called. 7 | // False by default, indicating that we just call through to standard Go functions. 8 | var MockMode bool = false 9 | var currentTimeMock time.Time = time.Unix(0, 0) 10 | var mockTimers []*mockTimer = make([]*mockTimer, 0) 11 | 12 | // Interface over Golang's built-in Timers, allowing them to be swapped out for mocked timers. 13 | type Timer interface { 14 | // Returns a channel which sends the current time immediately when the timer expires. 15 | // Equivalent to time.Timer.C; however, we have to use a method here instead of a member since this is an interface. 16 | C() <-chan time.Time 17 | 18 | // Resets the timer such that it will expire in duration 'd' after the current time. 19 | // Returns true if the timer had been active, and false if it had expired or been stopped. 20 | Reset(d time.Duration) bool 21 | 22 | // Stops the timer, preventing it from firing. 23 | // Returns true if the timer had been active, and false if it had expired or been stopped. 24 | Stop() bool 25 | } 26 | 27 | // Implementation of Timer that just wraps time.Timer. 28 | type realTimer struct { 29 | *time.Timer 30 | } 31 | 32 | func (t *realTimer) C() <-chan time.Time { 33 | return t.Timer.C 34 | } 35 | 36 | func (t *realTimer) Reset(d time.Duration) bool { 37 | return t.Timer.Reset(d) 38 | } 39 | 40 | func (t *realTimer) Stop() bool { 41 | return t.Timer.Stop() 42 | } 43 | 44 | // Implementation of Timer that mocks time.Timer, firing when the total elapsed time (as controlled by Elapse) 45 | // exceeds the duration specified when the timer was constructed. 46 | type mockTimer struct { 47 | EndTime time.Time 48 | Chan chan time.Time 49 | fired bool 50 | toRun func() 51 | } 52 | 53 | func (t *mockTimer) C() <-chan time.Time { 54 | return t.Chan 55 | } 56 | 57 | func (t *mockTimer) Reset(d time.Duration) bool { 58 | wasActive := removeMockTimer(t) 59 | 60 | t.EndTime = currentTimeMock.Add(d) 61 | if d > 0 { 62 | mockTimers = append(mockTimers, t) 63 | } else { 64 | // The new timer has an expiry time of 0. 65 | // Fire it right away, and don't bother tracking it. 66 | t.Chan <- currentTimeMock 67 | } 68 | 69 | return wasActive 70 | } 71 | 72 | func (t *mockTimer) Stop() bool { 73 | return removeMockTimer(t) 74 | } 75 | 76 | // Creates a new Timer; either a wrapper around a standard Go time.Timer, or a mocked-out Timer, 77 | // depending on whether MockMode is set. 78 | func NewTimer(d time.Duration) Timer { 79 | if MockMode { 80 | t := mockTimer{currentTimeMock.Add(d), make(chan time.Time, 1), false, nil} 81 | if d == 0 { 82 | t.Chan <- currentTimeMock 83 | } else { 84 | mockTimers = append(mockTimers, &t) 85 | } 86 | return &t 87 | } else { 88 | return &realTimer{time.NewTimer(d)} 89 | } 90 | } 91 | 92 | // See built-in time.After() function. 93 | func After(d time.Duration) <-chan time.Time { 94 | return NewTimer(d).C() 95 | } 96 | 97 | // See built-in time.AfterFunc() function. 98 | func AfterFunc(d time.Duration, f func()) Timer { 99 | if MockMode { 100 | t := mockTimer{currentTimeMock.Add(d), make(chan time.Time, 1), false, f} 101 | if d == 0 { 102 | go f() 103 | t.Chan <- currentTimeMock 104 | } else { 105 | mockTimers = append(mockTimers, &t) 106 | } 107 | return &t 108 | } else { 109 | return &realTimer{time.AfterFunc(d, f)} 110 | } 111 | } 112 | 113 | // See built-in time.Sleep() function. 114 | func Sleep(d time.Duration) { 115 | <-After(d) 116 | } 117 | 118 | // Increment the current time by the given Duration. 119 | // This function can only be called in Mock Mode, otherwise we will panic. 120 | func Elapse(d time.Duration) { 121 | requireMockMode() 122 | currentTimeMock = currentTimeMock.Add(d) 123 | 124 | // Fire any timers whose time has come up. 125 | for _, t := range mockTimers { 126 | t.fired = false 127 | if !t.EndTime.After(currentTimeMock) { 128 | if t.toRun != nil { 129 | go t.toRun() 130 | } 131 | 132 | // Clear the channel if something is already in it. 133 | select { 134 | case <-t.Chan: 135 | default: 136 | } 137 | 138 | t.Chan <- currentTimeMock 139 | t.fired = true 140 | } 141 | } 142 | 143 | // Stop tracking any fired timers. 144 | remainingTimers := make([]*mockTimer, 0) 145 | for _, t := range mockTimers { 146 | if !t.fired { 147 | remainingTimers = append(remainingTimers, t) 148 | } 149 | } 150 | 151 | mockTimers = remainingTimers 152 | } 153 | 154 | // Returns the current time. 155 | // If Mock Mode is set, this will be the sum of all Durations passed into Elapse calls; 156 | // otherwise it will be the true system time. 157 | func Now() time.Time { 158 | if MockMode { 159 | return currentTimeMock 160 | } else { 161 | return time.Now() 162 | } 163 | } 164 | 165 | // Shortcut method to enforce that Mock Mode is enabled. 166 | func requireMockMode() { 167 | if !MockMode { 168 | panic("This method requires MockMode to be enabled") 169 | } 170 | } 171 | 172 | // Utility method to remove a mockTimer from the list of outstanding timers. 173 | func removeMockTimer(t *mockTimer) bool { 174 | // First, find the index of the timer in our list. 175 | found := false 176 | var idx int 177 | var elt *mockTimer 178 | for idx, elt = range mockTimers { 179 | if elt == t { 180 | found = true 181 | break 182 | } 183 | } 184 | 185 | if found { 186 | // We found the given timer. Remove it. 187 | mockTimers = append(mockTimers[:idx], mockTimers[idx+1:]...) 188 | return true 189 | } else { 190 | // The timer was not present, indicating that it was already expired. 191 | return false 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /transaction/transaction.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/discoviking/fsm" 8 | "github.com/stefankopieczek/gossip/base" 9 | "github.com/stefankopieczek/gossip/log" 10 | "github.com/stefankopieczek/gossip/timing" 11 | "github.com/stefankopieczek/gossip/transport" 12 | ) 13 | 14 | // Generic Client Transaction 15 | 16 | const ( 17 | T1 = 500 * time.Millisecond 18 | T2 = 4 * time.Second 19 | ) 20 | 21 | type Transaction interface { 22 | Receive(m base.SipMessage) 23 | Origin() *base.Request 24 | Destination() string 25 | Transport() transport.Manager 26 | Delete() 27 | } 28 | 29 | type transaction struct { 30 | fsm *fsm.FSM // FSM which governs the behavior of this transaction. 31 | origin *base.Request // Request that started this transaction. 32 | lastResp *base.Response // Most recently received message. 33 | dest string // Of the form hostname:port 34 | transport transport.Manager 35 | tm *Manager 36 | } 37 | 38 | func (tx *transaction) Origin() *base.Request { 39 | return tx.origin 40 | } 41 | 42 | func (tx *transaction) Destination() string { 43 | return tx.dest 44 | } 45 | 46 | func (tx *transaction) Transport() transport.Manager { 47 | return tx.transport 48 | } 49 | 50 | func (tx *ServerTransaction) Delete() { 51 | tx.tm.delTx(tx) 52 | } 53 | 54 | func (tx *ClientTransaction) Delete() { 55 | log.Warn("Tx: %p, tm: %p", tx, tx.tm) 56 | tx.tm.delTx(tx) 57 | } 58 | 59 | type ClientTransaction struct { 60 | transaction 61 | 62 | tu chan *base.Response // Channel to transaction user. 63 | tu_err chan error // Channel to report up errors to TU. 64 | timer_a_time time.Duration // Current duration of timer A. 65 | timer_a timing.Timer 66 | timer_b timing.Timer 67 | timer_d_time time.Duration // Current duration of timer A. 68 | timer_d timing.Timer 69 | } 70 | 71 | type ServerTransaction struct { 72 | transaction 73 | 74 | tu chan *base.Response // Channel to transaction user. 75 | tu_err chan error // Channel to report up errors to TU. 76 | ack chan *base.Request // Channel we send the ACK up on. 77 | timer_g timing.Timer 78 | timer_h timing.Timer 79 | timer_i timing.Timer 80 | } 81 | 82 | func (tx *ServerTransaction) Receive(m base.SipMessage) { 83 | r, ok := m.(*base.Request) 84 | if !ok { 85 | log.Warn("Client transaction received request") 86 | } 87 | 88 | var input fsm.Input = fsm.NO_INPUT 89 | switch { 90 | case r.Method == tx.origin.Method: 91 | input = server_input_request 92 | case r.Method == base.ACK: 93 | input = server_input_ack 94 | tx.ack <- r 95 | default: 96 | log.Warn("Invalid message correlated to server transaction.") 97 | } 98 | 99 | tx.fsm.Spin(input) 100 | } 101 | 102 | func (tx *ServerTransaction) Respond(r *base.Response) { 103 | tx.lastResp = r 104 | 105 | var input fsm.Input 106 | switch { 107 | case r.StatusCode < 200: 108 | input = server_input_user_1xx 109 | case r.StatusCode < 300: 110 | input = server_input_user_2xx 111 | default: 112 | input = server_input_user_300_plus 113 | } 114 | 115 | tx.fsm.Spin(input) 116 | } 117 | 118 | func (tx *ServerTransaction) Ack() <-chan *base.Request { 119 | return (<-chan *base.Request)(tx.ack) 120 | } 121 | 122 | func (tx *ClientTransaction) Receive(m base.SipMessage) { 123 | r, ok := m.(*base.Response) 124 | if !ok { 125 | log.Warn("Client transaction received request") 126 | } 127 | 128 | tx.lastResp = r 129 | 130 | var input fsm.Input 131 | switch { 132 | case r.StatusCode < 200: 133 | input = client_input_1xx 134 | case r.StatusCode < 300: 135 | input = client_input_2xx 136 | default: 137 | input = client_input_300_plus 138 | } 139 | 140 | tx.fsm.Spin(input) 141 | } 142 | 143 | // Resend the originating request. 144 | func (tx *ClientTransaction) resend() { 145 | log.Info("Client transaction %p resending request: %v", tx, tx.origin.Short()) 146 | err := tx.transport.Send(tx.dest, tx.origin) 147 | if err != nil { 148 | tx.fsm.Spin(client_input_transport_err) 149 | } 150 | } 151 | 152 | // Pass up the most recently received response to the TU. 153 | func (tx *ClientTransaction) passUp() { 154 | log.Info("Client transaction %p passing up response: %v", tx, tx.lastResp.Short()) 155 | tx.tu <- tx.lastResp 156 | } 157 | 158 | // Send an error to the TU. 159 | func (tx *ClientTransaction) transportError() { 160 | log.Info("Client transaction %p had a transport-level error", tx) 161 | tx.tu_err <- errors.New("failed to send message.") 162 | } 163 | 164 | // Inform the TU that the transaction timed out. 165 | func (tx *ClientTransaction) timeoutError() { 166 | log.Info("Client transaction %p timed out", tx) 167 | tx.tu_err <- errors.New("transaction timed out.") 168 | } 169 | 170 | // Send an automatic ACK. 171 | func (tx *ClientTransaction) Ack() { 172 | ack := base.NewRequest(base.ACK, 173 | tx.origin.Recipient, 174 | tx.origin.SipVersion, 175 | []base.SipHeader{}, 176 | "") 177 | 178 | // Copy headers from original request. 179 | // TODO: Safety 180 | base.CopyHeaders("From", tx.origin, ack) 181 | base.CopyHeaders("Call-Id", tx.origin, ack) 182 | base.CopyHeaders("Route", tx.origin, ack) 183 | cseq := tx.origin.Headers("CSeq")[0].Copy() 184 | cseq.(*base.CSeq).MethodName = base.ACK 185 | ack.AddHeader(cseq) 186 | via := tx.origin.Headers("Via")[0].Copy() 187 | ack.AddHeader(via) 188 | 189 | // Copy headers from response. 190 | base.CopyHeaders("To", tx.lastResp, ack) 191 | 192 | // Send the ACK. 193 | tx.transport.Send(tx.dest, ack) 194 | } 195 | 196 | // Return the channel we send responses on. 197 | func (tx *ClientTransaction) Responses() <-chan *base.Response { 198 | return (<-chan *base.Response)(tx.tu) 199 | } 200 | 201 | // Return the channel we send errors on. 202 | func (tx *ClientTransaction) Errors() <-chan error { 203 | return (<-chan error)(tx.tu_err) 204 | } 205 | -------------------------------------------------------------------------------- /transaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stefankopieczek/gossip/base" 6 | "github.com/stefankopieczek/gossip/log" 7 | "github.com/stefankopieczek/gossip/parser" 8 | "github.com/stefankopieczek/gossip/timing" 9 | "github.com/stefankopieczek/gossip/transport" 10 | "strings" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | // UTs for transaction layer. 16 | 17 | // Dummy transport manager. 18 | type dummyTransport struct { 19 | listenReqs chan string 20 | messages chan sentMessage 21 | toTM chan base.SipMessage 22 | } 23 | 24 | type sentMessage struct { 25 | addr string 26 | msg base.SipMessage 27 | } 28 | 29 | func newDummyTransport() *dummyTransport { 30 | return &dummyTransport{ 31 | listenReqs: make(chan string, 5), 32 | messages: make(chan sentMessage, 5), 33 | toTM: make(chan base.SipMessage, 5), 34 | } 35 | } 36 | 37 | // Implement transport.Manager interface. 38 | func (t *dummyTransport) Listen(address string) error { 39 | t.listenReqs <- address 40 | return nil 41 | } 42 | 43 | func (t *dummyTransport) Send(addr string, message base.SipMessage) error { 44 | t.messages <- sentMessage{addr, message} 45 | return nil 46 | } 47 | 48 | func (t *dummyTransport) Stop() {} 49 | 50 | func (t *dummyTransport) GetChannel() transport.Listener { 51 | return t.toTM 52 | } 53 | 54 | // Test infra. 55 | type action interface { 56 | Act(test *transactionTest) error 57 | } 58 | 59 | type transactionTest struct { 60 | t *testing.T 61 | actions []action 62 | tm *Manager 63 | transport *dummyTransport 64 | lastTx *ClientTransaction 65 | } 66 | 67 | func (test *transactionTest) Execute() { 68 | var err error 69 | timing.MockMode = true 70 | log.SetDefaultLogLevel(log.DEBUG) 71 | transport := newDummyTransport() 72 | test.tm, err = NewManager(transport, c_CLIENT) 73 | assertNoError(test.t, err) 74 | defer test.tm.Stop() 75 | 76 | test.transport = transport 77 | 78 | for _, actn := range test.actions { 79 | test.t.Logf("Performing action %v", actn) 80 | assertNoError(test.t, actn.Act(test)) 81 | } 82 | } 83 | 84 | type userSend struct { 85 | msg *base.Request 86 | } 87 | 88 | func (actn *userSend) Act(test *transactionTest) error { 89 | test.t.Logf("Transaction User sending message:\n%v", actn.msg.String()) 90 | test.lastTx = test.tm.Send(actn.msg, c_SERVER) 91 | return nil 92 | } 93 | 94 | type transportSend struct { 95 | msg base.SipMessage 96 | } 97 | 98 | func (actn *transportSend) Act(test *transactionTest) error { 99 | test.t.Logf("Transport Layer sending message\n%v", actn.msg.String()) 100 | test.transport.toTM <- actn.msg 101 | return nil 102 | } 103 | 104 | type userRecv struct { 105 | expected *base.Response 106 | } 107 | 108 | func (actn *userRecv) Act(test *transactionTest) error { 109 | responses := test.lastTx.Responses() 110 | select { 111 | case response, ok := <-responses: 112 | if !ok { 113 | return fmt.Errorf("Response channel prematurely closed") 114 | } else if response.String() != actn.expected.String() { 115 | return fmt.Errorf("Unexpected response:\n%s", response.String()) 116 | } else { 117 | test.t.Logf("Transaction User received correct message\n%v", response.String()) 118 | return nil 119 | } 120 | case <-time.After(time.Second): 121 | return fmt.Errorf("Timed out waiting for response") 122 | } 123 | } 124 | 125 | type transportRecv struct { 126 | expected base.SipMessage 127 | } 128 | 129 | func (actn *transportRecv) Act(test *transactionTest) error { 130 | select { 131 | case msg, ok := <-test.transport.messages: 132 | if !ok { 133 | return fmt.Errorf("Transport layer receive channel prematurely closed") 134 | } else if msg.msg.String() != actn.expected.String() { 135 | return fmt.Errorf("Unexpected message arrived at transport:\n%s", msg.msg.String()) 136 | } else { 137 | test.t.Logf("Transport received correct message\n %v", msg.msg.String()) 138 | return nil 139 | } 140 | case <-time.After(time.Second): 141 | return fmt.Errorf("Timed out waiting for message at transport") 142 | } 143 | } 144 | 145 | type wait struct { 146 | d time.Duration 147 | } 148 | 149 | func (actn *wait) Act(test *transactionTest) error { 150 | test.t.Logf("Elapsing time by %v", actn.d) 151 | timing.Elapse(actn.d) 152 | return nil 153 | } 154 | 155 | func assert(t *testing.T, b bool, msg string) { 156 | if !b { 157 | t.Errorf(msg) 158 | } 159 | } 160 | 161 | func assertNoError(t *testing.T, err error) { 162 | if err != nil { 163 | t.Fatalf("Unexpected error: %s", err.Error()) 164 | } 165 | } 166 | 167 | func message(rawMsg []string) (base.SipMessage, error) { 168 | return parser.ParseMessage([]byte(strings.Join(rawMsg, "\r\n"))) 169 | } 170 | 171 | func request(rawMsg []string) (*base.Request, error) { 172 | msg, err := message(rawMsg) 173 | 174 | if err != nil { 175 | return nil, err 176 | } 177 | 178 | switch msg.(type) { 179 | case *base.Request: 180 | return msg.(*base.Request), nil 181 | default: 182 | return nil, fmt.Errorf("%s is not a request", msg.Short) 183 | } 184 | } 185 | 186 | func response(rawMsg []string) (*base.Response, error) { 187 | msg, err := message(rawMsg) 188 | 189 | if err != nil { 190 | return nil, err 191 | } 192 | 193 | switch msg.(type) { 194 | case *base.Response: 195 | return msg.(*base.Response), nil 196 | default: 197 | return nil, fmt.Errorf("%s is not a response", msg.Short) 198 | } 199 | } 200 | 201 | // Confirm transaction manager requests for transport to listen. 202 | func TestListenRequest(t *testing.T) { 203 | trans := newDummyTransport() 204 | m, err := NewManager(trans, "1.1.1.1") 205 | if err != nil { 206 | t.Fatalf("Error creating TM: %v", err) 207 | } 208 | 209 | addr := <-trans.listenReqs 210 | if addr != "1.1.1.1" { 211 | t.Fatalf("Created TM with addr 1.1.1.1 but were asked to listen on %v", addr) 212 | } 213 | 214 | m.Stop() 215 | } 216 | -------------------------------------------------------------------------------- /transaction/server.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/discoviking/fsm" 7 | "github.com/stefankopieczek/gossip/base" 8 | "github.com/stefankopieczek/gossip/log" 9 | "github.com/stefankopieczek/gossip/timing" 10 | ) 11 | 12 | // SIP Server Transaction FSM 13 | // Implements the behaviour described in RFC 3261 section 17.2 14 | 15 | // FSM States 16 | const ( 17 | server_state_trying = iota 18 | server_state_proceeding 19 | server_state_completed 20 | server_state_confirmed 21 | server_state_terminated 22 | ) 23 | 24 | // FSM Inputs 25 | const ( 26 | server_input_request fsm.Input = iota 27 | server_input_ack 28 | server_input_user_1xx 29 | server_input_user_2xx 30 | server_input_user_300_plus 31 | server_input_timer_g 32 | server_input_timer_h 33 | server_input_timer_i 34 | server_input_transport_err 35 | server_input_delete 36 | ) 37 | 38 | // Define actions. 39 | 40 | // Send response 41 | func (tx *ServerTransaction) act_respond() fsm.Input { 42 | err := tx.transport.Send(tx.dest, tx.lastResp) 43 | if err != nil { 44 | return server_input_transport_err 45 | } 46 | 47 | return fsm.NO_INPUT 48 | } 49 | 50 | // Send final response 51 | func (tx *ServerTransaction) act_final() fsm.Input { 52 | err := tx.transport.Send(tx.dest, tx.lastResp) 53 | if err != nil { 54 | return server_input_transport_err 55 | } 56 | 57 | // Start timer J (we just reuse timer h) 58 | tx.timer_h = timing.AfterFunc(64*T1, func() { 59 | tx.fsm.Spin(server_input_timer_h) 60 | }) 61 | 62 | return fsm.NO_INPUT 63 | } 64 | 65 | // Inform user of transport error 66 | func (tx *ServerTransaction) act_trans_err() fsm.Input { 67 | tx.tu_err <- errors.New("failed to send response") 68 | return server_input_delete 69 | } 70 | 71 | // Inform user of timeout error 72 | func (tx *ServerTransaction) act_timeout() fsm.Input { 73 | tx.tu_err <- errors.New("transaction timed out") 74 | return server_input_delete 75 | } 76 | 77 | // Just delete the transaction. 78 | func (tx *ServerTransaction) act_delete() fsm.Input { 79 | tx.Delete() 80 | return fsm.NO_INPUT 81 | } 82 | 83 | // Send response and delete the transaction. 84 | func (tx *ServerTransaction) act_respond_delete() fsm.Input { 85 | tx.Delete() 86 | 87 | err := tx.transport.Send(tx.dest, tx.lastResp) 88 | if err != nil { 89 | return server_input_transport_err 90 | } 91 | return fsm.NO_INPUT 92 | } 93 | 94 | // Choose the right FSM init function depending on request method. 95 | func (tx *ServerTransaction) initFSM() { 96 | if tx.origin.Method == base.INVITE { 97 | tx.initInviteFSM() 98 | } else { 99 | tx.initNonInviteFSM() 100 | } 101 | } 102 | 103 | func (tx *ServerTransaction) initInviteFSM() { 104 | // Define States 105 | 106 | // Proceeding 107 | server_state_def_proceeding := fsm.State{ 108 | Index: server_state_proceeding, 109 | Outcomes: map[fsm.Input]fsm.Outcome{ 110 | server_input_request: {server_state_proceeding, tx.act_respond}, 111 | server_input_user_1xx: {server_state_proceeding, tx.act_respond}, 112 | server_input_user_2xx: {server_state_terminated, tx.act_respond_delete}, 113 | server_input_user_300_plus: {server_state_completed, tx.act_respond}, 114 | server_input_transport_err: {server_state_terminated, tx.act_trans_err}, 115 | }, 116 | } 117 | 118 | // Completed 119 | server_state_def_completed := fsm.State{ 120 | Index: server_state_completed, 121 | Outcomes: map[fsm.Input]fsm.Outcome{ 122 | server_input_request: {server_state_completed, tx.act_respond}, 123 | server_input_ack: {server_state_confirmed, fsm.NO_ACTION}, 124 | server_input_user_1xx: {server_state_completed, fsm.NO_ACTION}, 125 | server_input_user_2xx: {server_state_completed, fsm.NO_ACTION}, 126 | server_input_user_300_plus: {server_state_completed, fsm.NO_ACTION}, 127 | server_input_timer_g: {server_state_completed, tx.act_respond}, 128 | server_input_timer_h: {server_state_terminated, tx.act_timeout}, 129 | server_input_transport_err: {server_state_terminated, tx.act_trans_err}, 130 | }, 131 | } 132 | 133 | // Confirmed 134 | server_state_def_confirmed := fsm.State{ 135 | Index: server_state_confirmed, 136 | Outcomes: map[fsm.Input]fsm.Outcome{ 137 | server_input_request: {server_state_confirmed, fsm.NO_ACTION}, 138 | server_input_user_1xx: {server_state_confirmed, fsm.NO_ACTION}, 139 | server_input_user_2xx: {server_state_confirmed, fsm.NO_ACTION}, 140 | server_input_user_300_plus: {server_state_confirmed, fsm.NO_ACTION}, 141 | server_input_timer_i: {server_state_terminated, tx.act_delete}, 142 | }, 143 | } 144 | 145 | // Terminated 146 | server_state_def_terminated := fsm.State{ 147 | Index: server_state_terminated, 148 | Outcomes: map[fsm.Input]fsm.Outcome{ 149 | server_input_request: {server_state_terminated, fsm.NO_ACTION}, 150 | server_input_ack: {server_state_terminated, fsm.NO_ACTION}, 151 | server_input_user_1xx: {server_state_terminated, fsm.NO_ACTION}, 152 | server_input_user_2xx: {server_state_terminated, fsm.NO_ACTION}, 153 | server_input_user_300_plus: {server_state_terminated, fsm.NO_ACTION}, 154 | server_input_delete: {server_state_terminated, tx.act_delete}, 155 | }, 156 | } 157 | 158 | // Define FSM 159 | fsm, err := fsm.Define( 160 | server_state_def_proceeding, 161 | server_state_def_completed, 162 | server_state_def_confirmed, 163 | server_state_def_terminated, 164 | ) 165 | if err != nil { 166 | log.Severe("Failed to define transaction FSM. Transaction will be dropped.") 167 | return 168 | } 169 | 170 | tx.fsm = fsm 171 | } 172 | 173 | func (tx *ServerTransaction) initNonInviteFSM() { 174 | // Define States 175 | 176 | // Trying 177 | server_state_def_trying := fsm.State{ 178 | Index: server_state_trying, 179 | Outcomes: map[fsm.Input]fsm.Outcome{ 180 | server_input_request: {server_state_trying, fsm.NO_ACTION}, 181 | server_input_user_1xx: {server_state_proceeding, tx.act_respond}, 182 | server_input_user_2xx: {server_state_completed, tx.act_respond}, 183 | server_input_user_300_plus: {server_state_completed, tx.act_respond}, 184 | }, 185 | } 186 | 187 | // Proceeding 188 | server_state_def_proceeding := fsm.State{ 189 | Index: server_state_proceeding, 190 | Outcomes: map[fsm.Input]fsm.Outcome{ 191 | server_input_request: {server_state_proceeding, tx.act_respond}, 192 | server_input_user_1xx: {server_state_proceeding, tx.act_respond}, 193 | server_input_user_2xx: {server_state_completed, tx.act_final}, 194 | server_input_user_300_plus: {server_state_completed, tx.act_final}, 195 | server_input_transport_err: {server_state_terminated, tx.act_trans_err}, 196 | }, 197 | } 198 | 199 | // Completed 200 | server_state_def_completed := fsm.State{ 201 | Index: server_state_completed, 202 | Outcomes: map[fsm.Input]fsm.Outcome{ 203 | server_input_request: {server_state_completed, tx.act_respond}, 204 | server_input_user_1xx: {server_state_completed, fsm.NO_ACTION}, 205 | server_input_user_2xx: {server_state_completed, fsm.NO_ACTION}, 206 | server_input_user_300_plus: {server_state_completed, fsm.NO_ACTION}, 207 | server_input_timer_h: {server_state_terminated, tx.act_timeout}, 208 | server_input_transport_err: {server_state_terminated, tx.act_trans_err}, 209 | }, 210 | } 211 | 212 | // Terminated 213 | server_state_def_terminated := fsm.State{ 214 | Index: server_state_terminated, 215 | Outcomes: map[fsm.Input]fsm.Outcome{ 216 | server_input_request: {server_state_terminated, fsm.NO_ACTION}, 217 | server_input_user_1xx: {server_state_terminated, fsm.NO_ACTION}, 218 | server_input_user_2xx: {server_state_terminated, fsm.NO_ACTION}, 219 | server_input_user_300_plus: {server_state_terminated, fsm.NO_ACTION}, 220 | server_input_timer_h: {server_state_terminated, fsm.NO_ACTION}, 221 | server_input_delete: {server_state_terminated, tx.act_delete}, 222 | }, 223 | } 224 | 225 | // Define FSM 226 | fsm, err := fsm.Define( 227 | server_state_def_trying, 228 | server_state_def_proceeding, 229 | server_state_def_completed, 230 | server_state_def_terminated, 231 | ) 232 | if err != nil { 233 | log.Severe("Failed to define transaction FSM. Transaction will be dropped.") 234 | return 235 | } 236 | 237 | tx.fsm = fsm 238 | } 239 | -------------------------------------------------------------------------------- /transaction/manager.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/stefankopieczek/gossip/base" 10 | "github.com/stefankopieczek/gossip/log" 11 | "github.com/stefankopieczek/gossip/timing" 12 | "github.com/stefankopieczek/gossip/transport" 13 | ) 14 | 15 | var ( 16 | global *Manager = &Manager{ 17 | txs: map[key]Transaction{}, 18 | } 19 | ) 20 | 21 | type Manager struct { 22 | txs map[key]Transaction 23 | transport transport.Manager 24 | requests chan *ServerTransaction 25 | txLock *sync.RWMutex 26 | } 27 | 28 | // Transactions are identified by the branch parameter in the top Via header, and the method. (RFC 3261 17.1.3) 29 | type key struct { 30 | branch string 31 | method string 32 | } 33 | 34 | func NewManager(t transport.Manager, addr string) (*Manager, error) { 35 | mng := &Manager{ 36 | txs: map[key]Transaction{}, 37 | txLock: &sync.RWMutex{}, 38 | transport: t, 39 | } 40 | 41 | mng.requests = make(chan *ServerTransaction, 5) 42 | 43 | // Spin up a goroutine to pull messages up from the depths. 44 | c := mng.transport.GetChannel() 45 | go func() { 46 | for msg := range c { 47 | go mng.handle(msg) 48 | } 49 | }() 50 | 51 | err := mng.transport.Listen(addr) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return mng, nil 57 | } 58 | 59 | // Stop the manager and close down all processing on it, losing all transactions in progress. 60 | func (mng *Manager) Stop() { 61 | // Stop the transport layer. 62 | mng.transport.Stop() 63 | } 64 | 65 | func (mng *Manager) Requests() <-chan *ServerTransaction { 66 | return (<-chan *ServerTransaction)(mng.requests) 67 | } 68 | 69 | func (mng *Manager) putTx(tx Transaction) { 70 | viaHeaders := tx.Origin().Headers("Via") 71 | if len(viaHeaders) == 0 { 72 | log.Warn("No Via header on new transaction. Transaction will be dropped.") 73 | return 74 | } 75 | 76 | via, ok := viaHeaders[0].(*base.ViaHeader) 77 | if !ok { 78 | // TODO: Handle this better. 79 | panic(errors.New("Headers('Via') returned non-Via header!")) 80 | } 81 | 82 | branch, ok := (*via)[0].Params.Get("branch") 83 | if !ok { 84 | log.Warn("No branch parameter on top Via header. Transaction will be dropped.") 85 | return 86 | } 87 | 88 | var k key 89 | switch branch := branch.(type) { 90 | case base.String: 91 | k = key{branch.String(), string(tx.Origin().Method)} 92 | case base.NoString: 93 | log.Warn("Empty branch parameter on top Via header. Transaction will be dropped.") 94 | return 95 | default: 96 | log.Warn("Unexpected type of branch value on top Via header: %T", branch) 97 | return 98 | } 99 | mng.txLock.Lock() 100 | mng.txs[k] = tx 101 | mng.txLock.Unlock() 102 | } 103 | 104 | func (mng *Manager) makeKey(s base.SipMessage) (key, bool) { 105 | viaHeaders := s.Headers("Via") 106 | via, ok := viaHeaders[0].(*base.ViaHeader) 107 | if !ok { 108 | panic(errors.New("Headers('Via') returned non-Via header!")) 109 | } 110 | 111 | b, ok := (*via)[0].Params.Get("branch") 112 | if !ok { 113 | return key{}, false 114 | } 115 | 116 | branch, ok := b.(base.String) 117 | if !ok { 118 | return key{}, false 119 | } 120 | 121 | var method string 122 | switch s := s.(type) { 123 | case *base.Request: 124 | // Correlate an ACK request to the related INVITE. 125 | if s.Method == base.ACK { 126 | method = string(base.INVITE) 127 | } else { 128 | method = string(s.Method) 129 | } 130 | case *base.Response: 131 | cseqs := s.Headers("CSeq") 132 | if len(cseqs) == 0 { 133 | // TODO - Handle non-existent CSeq 134 | panic("No CSeq on response!") 135 | } 136 | 137 | cseq, _ := s.Headers("CSeq")[0].(*base.CSeq) 138 | method = string(cseq.MethodName) 139 | } 140 | 141 | return key{branch.String(), method}, true 142 | } 143 | 144 | // Gets a transaction from the transaction store. 145 | // Should only be called inside the storage handling goroutine to ensure concurrency safety. 146 | func (mng *Manager) getTx(s base.SipMessage) (Transaction, bool) { 147 | key, ok := mng.makeKey(s) 148 | if !ok { 149 | // TODO: Here we should initiate more intense searching as specified in RFC3261 section 17 150 | log.Warn("Could not correlate message to transaction by branch/method. Dropping.") 151 | return nil, false 152 | } 153 | 154 | mng.txLock.RLock() 155 | tx, ok := mng.txs[key] 156 | mng.txLock.RUnlock() 157 | 158 | return tx, ok 159 | } 160 | 161 | // Deletes a transaction from the transaction store. 162 | // Should only be called inside the storage handling goroutine to ensure concurrency safety. 163 | func (mng *Manager) delTx(t Transaction) { 164 | key, ok := mng.makeKey(t.Origin()) 165 | if !ok { 166 | log.Debug("Could not build lookup key for transaction. Is it missing a branch parameter?") 167 | } 168 | 169 | mng.txLock.Lock() 170 | delete(mng.txs, key) 171 | mng.txLock.Unlock() 172 | } 173 | 174 | func (mng *Manager) handle(msg base.SipMessage) { 175 | log.Info("Received message: %s", msg.Short()) 176 | switch m := msg.(type) { 177 | case *base.Request: 178 | mng.request(m) 179 | case *base.Response: 180 | mng.correlate(m) 181 | default: 182 | // TODO: Error 183 | } 184 | } 185 | 186 | // Create Client transaction. 187 | func (mng *Manager) Send(r *base.Request, dest string) *ClientTransaction { 188 | log.Debug("Sending to %v: %v", dest, r.String()) 189 | 190 | tx := &ClientTransaction{} 191 | tx.origin = r 192 | tx.dest = dest 193 | tx.transport = mng.transport 194 | tx.tm = mng 195 | 196 | tx.initFSM() 197 | 198 | tx.tu = make(chan *base.Response, 3) 199 | tx.tu_err = make(chan error, 1) 200 | 201 | tx.timer_a_time = T1 202 | tx.timer_a = timing.AfterFunc(tx.timer_a_time, func() { 203 | tx.fsm.Spin(client_input_timer_a) 204 | }) 205 | log.Debug("Client transaction %p, timer_b set to %v!", tx, 64*T1) 206 | tx.timer_b = timing.AfterFunc(64*T1, func() { 207 | log.Debug("Client transaction %p, timer_b fired!", tx) 208 | tx.fsm.Spin(client_input_timer_b) 209 | }) 210 | 211 | // Timer D is set to 32 seconds for unreliable transports, and 0 seconds otherwise. 212 | tx.timer_d_time = 32 * time.Second 213 | 214 | err := mng.transport.Send(dest, r) 215 | if err != nil { 216 | log.Warn("Failed to send message: %s", err.Error()) 217 | tx.fsm.Spin(client_input_transport_err) 218 | } 219 | 220 | mng.putTx(tx) 221 | 222 | return tx 223 | } 224 | 225 | // Give a received response to the correct transaction. 226 | func (mng *Manager) correlate(r *base.Response) { 227 | tx, ok := mng.getTx(r) 228 | if !ok { 229 | // TODO: Something 230 | log.Warn("Failed to correlate response to active transaction. Dropping it.") 231 | return 232 | } 233 | 234 | tx.Receive(r) 235 | } 236 | 237 | // Handle a request. 238 | func (mng *Manager) request(r *base.Request) { 239 | t, ok := mng.getTx(r) 240 | if ok { 241 | t.Receive(r) 242 | return 243 | } 244 | 245 | // If we failed to correlate an ACK, just drop it. 246 | if r.Method == base.ACK { 247 | log.Warn("Couldn't correlate ACK to an open transaction. Dropping it.") 248 | return 249 | } 250 | 251 | // Create a new transaction 252 | tx := &ServerTransaction{} 253 | tx.tm = mng 254 | tx.origin = r 255 | tx.transport = mng.transport 256 | 257 | // Use the remote address in the top Via header. This is not correct behaviour. 258 | viaHeaders := tx.Origin().Headers("Via") 259 | if len(viaHeaders) == 0 { 260 | log.Warn("No Via header on new transaction. Transaction will be dropped.") 261 | return 262 | } 263 | 264 | via, ok := viaHeaders[0].(*base.ViaHeader) 265 | if !ok { 266 | panic(errors.New("Headers('Via') returned non-Via header!")) 267 | } 268 | 269 | if len(*via) == 0 { 270 | log.Warn("Via header contained no hops! Transaction will be dropped.") 271 | return 272 | } 273 | 274 | hop := (*via)[0] 275 | 276 | port := uint16(5060) 277 | 278 | if hop.Port != nil { 279 | port = *hop.Port 280 | } 281 | 282 | tx.dest = fmt.Sprintf("%s:%d", hop.Host, port) 283 | tx.transport = mng.transport 284 | 285 | tx.initFSM() 286 | 287 | tx.tu = make(chan *base.Response, 3) 288 | tx.tu_err = make(chan error, 1) 289 | tx.ack = make(chan *base.Request, 1) 290 | 291 | // Send a 100 Trying immediately. 292 | // Technically we shouldn't do this if we trustthe user to do it within 200ms, 293 | // but I'm not sure how to handle that situation right now. 294 | 295 | // Pretend the user sent us a 100 to send. 296 | trying := base.NewResponse( 297 | "SIP/2.0", 298 | 100, 299 | "Trying", 300 | []base.SipHeader{}, 301 | "", 302 | ) 303 | 304 | base.CopyHeaders("Via", tx.origin, trying) 305 | base.CopyHeaders("From", tx.origin, trying) 306 | base.CopyHeaders("To", tx.origin, trying) 307 | base.CopyHeaders("Call-Id", tx.origin, trying) 308 | base.CopyHeaders("CSeq", tx.origin, trying) 309 | 310 | tx.lastResp = trying 311 | tx.fsm.Spin(server_input_user_1xx) 312 | 313 | mng.requests <- tx 314 | } 315 | -------------------------------------------------------------------------------- /transaction/client.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "github.com/discoviking/fsm" 5 | "github.com/stefankopieczek/gossip/base" 6 | "github.com/stefankopieczek/gossip/log" 7 | "github.com/stefankopieczek/gossip/timing" 8 | ) 9 | 10 | // SIP Client Transaction FSM 11 | // Implements the behaviour described in RFC 3261 section 17.1 12 | 13 | // FSM States 14 | const ( 15 | client_state_calling = iota 16 | client_state_proceeding 17 | client_state_completed 18 | client_state_terminated 19 | ) 20 | 21 | // FSM Inputs 22 | const ( 23 | client_input_1xx fsm.Input = iota 24 | client_input_2xx 25 | client_input_300_plus 26 | client_input_timer_a 27 | client_input_timer_b 28 | client_input_timer_d 29 | client_input_transport_err 30 | client_input_delete 31 | ) 32 | 33 | // Initialises the correct kind of FSM based on request method. 34 | func (tx *ClientTransaction) initFSM() { 35 | if tx.origin.Method == base.INVITE { 36 | tx.initInviteFSM() 37 | } else { 38 | tx.initNonInviteFSM() 39 | } 40 | } 41 | 42 | func (tx *ClientTransaction) initInviteFSM() { 43 | log.Debug("Initialising client INVITE transaction FSM") 44 | 45 | // Define Actions 46 | 47 | // Resend the request. 48 | act_resend := func() fsm.Input { 49 | log.Debug("Client transaction %p, act_resend", tx) 50 | tx.timer_a_time *= 2 51 | tx.timer_a.Reset(tx.timer_a_time) 52 | tx.resend() 53 | return fsm.NO_INPUT 54 | } 55 | 56 | // Just pass up the latest response. 57 | act_passup := func() fsm.Input { 58 | log.Debug("Client transaction %p, act_passup", tx) 59 | tx.passUp() 60 | return fsm.NO_INPUT 61 | } 62 | 63 | // Handle 300+ responses. 64 | // Pass up response and send ACK, start timer D. 65 | act_300 := func() fsm.Input { 66 | log.Debug("Client transaction %p, act_300", tx) 67 | tx.passUp() 68 | tx.Ack() 69 | if tx.timer_d != nil { 70 | tx.timer_d.Stop() 71 | } 72 | tx.timer_d = timing.AfterFunc(tx.timer_d_time, func() { 73 | tx.fsm.Spin(client_input_timer_d) 74 | }) 75 | return fsm.NO_INPUT 76 | } 77 | 78 | // Send an ACK. 79 | act_ack := func() fsm.Input { 80 | log.Debug("Client transaction %p, act_ack", tx) 81 | tx.Ack() 82 | return fsm.NO_INPUT 83 | } 84 | 85 | // Send up transport failure error. 86 | act_trans_err := func() fsm.Input { 87 | log.Debug("Client transaction %p, act_trans_err", tx) 88 | tx.transportError() 89 | return client_input_delete 90 | } 91 | 92 | // Send up timeout error. 93 | act_timeout := func() fsm.Input { 94 | log.Debug("Client transaction %p, act_timeout", tx) 95 | tx.timeoutError() 96 | return client_input_delete 97 | } 98 | 99 | // Pass up the response and delete the transaction. 100 | act_passup_delete := func() fsm.Input { 101 | log.Debug("Client transaction %p, act_passup_delete", tx) 102 | tx.passUp() 103 | tx.Delete() 104 | return fsm.NO_INPUT 105 | } 106 | 107 | // Just delete the transaction. 108 | act_delete := func() fsm.Input { 109 | log.Debug("Client transaction %p, act_delete", tx) 110 | tx.Delete() 111 | return fsm.NO_INPUT 112 | } 113 | 114 | // Define States 115 | 116 | // Calling 117 | client_state_def_calling := fsm.State{ 118 | Index: client_state_calling, 119 | Outcomes: map[fsm.Input]fsm.Outcome{ 120 | client_input_1xx: {client_state_proceeding, act_passup}, 121 | client_input_2xx: {client_state_terminated, act_passup_delete}, 122 | client_input_300_plus: {client_state_completed, act_300}, 123 | client_input_timer_a: {client_state_calling, act_resend}, 124 | client_input_timer_b: {client_state_terminated, act_timeout}, 125 | client_input_transport_err: {client_state_terminated, act_trans_err}, 126 | }, 127 | } 128 | 129 | // Proceeding 130 | client_state_def_proceeding := fsm.State{ 131 | Index: client_state_proceeding, 132 | Outcomes: map[fsm.Input]fsm.Outcome{ 133 | client_input_1xx: {client_state_proceeding, act_passup}, 134 | client_input_2xx: {client_state_terminated, act_passup_delete}, 135 | client_input_300_plus: {client_state_completed, act_300}, 136 | client_input_timer_a: {client_state_proceeding, fsm.NO_ACTION}, 137 | client_input_timer_b: {client_state_proceeding, fsm.NO_ACTION}, 138 | }, 139 | } 140 | 141 | // Completed 142 | client_state_def_completed := fsm.State{ 143 | Index: client_state_completed, 144 | Outcomes: map[fsm.Input]fsm.Outcome{ 145 | client_input_1xx: {client_state_completed, fsm.NO_ACTION}, 146 | client_input_2xx: {client_state_completed, fsm.NO_ACTION}, 147 | client_input_300_plus: {client_state_completed, act_ack}, 148 | client_input_timer_d: {client_state_terminated, act_delete}, 149 | client_input_transport_err: {client_state_terminated, act_trans_err}, 150 | client_input_timer_a: {client_state_completed, fsm.NO_ACTION}, 151 | client_input_timer_b: {client_state_completed, fsm.NO_ACTION}, 152 | }, 153 | } 154 | 155 | // Terminated 156 | client_state_def_terminated := fsm.State{ 157 | Index: client_state_terminated, 158 | Outcomes: map[fsm.Input]fsm.Outcome{ 159 | client_input_1xx: {client_state_terminated, fsm.NO_ACTION}, 160 | client_input_2xx: {client_state_terminated, fsm.NO_ACTION}, 161 | client_input_300_plus: {client_state_terminated, fsm.NO_ACTION}, 162 | client_input_timer_a: {client_state_terminated, fsm.NO_ACTION}, 163 | client_input_timer_b: {client_state_terminated, fsm.NO_ACTION}, 164 | client_input_delete: {client_state_terminated, act_delete}, 165 | }, 166 | } 167 | 168 | fsm, err := fsm.Define( 169 | client_state_def_calling, 170 | client_state_def_proceeding, 171 | client_state_def_completed, 172 | client_state_def_terminated, 173 | ) 174 | 175 | if err != nil { 176 | log.Severe("Failure to define INVITE client transaction fsm: %s", err.Error()) 177 | } 178 | 179 | tx.fsm = fsm 180 | } 181 | 182 | func (tx *ClientTransaction) initNonInviteFSM() { 183 | log.Debug("Initialising client non-INVITE transaction FSM") 184 | 185 | // Define Actions 186 | 187 | // Resend the request. 188 | act_resend := func() fsm.Input { 189 | tx.timer_a_time *= 2 190 | // For non-INVITE, cap timer A at T2 seconds. 191 | if tx.timer_a_time > T2 { 192 | tx.timer_a_time = T2 193 | } 194 | 195 | tx.timer_a.Reset(tx.timer_a_time) 196 | tx.resend() 197 | return fsm.NO_INPUT 198 | } 199 | 200 | // Just pass up the latest response. 201 | act_passup := func() fsm.Input { 202 | tx.passUp() 203 | return fsm.NO_INPUT 204 | } 205 | 206 | // Handle a final response. 207 | act_final := func() fsm.Input { 208 | tx.passUp() 209 | if tx.timer_d != nil { 210 | tx.timer_d.Stop() 211 | } 212 | tx.timer_d = timing.AfterFunc(tx.timer_d_time, func() { 213 | tx.fsm.Spin(client_input_timer_d) 214 | }) 215 | return fsm.NO_INPUT 216 | } 217 | 218 | // Send up transport failure error. 219 | act_trans_err := func() fsm.Input { 220 | tx.transportError() 221 | return client_input_delete 222 | } 223 | 224 | // Send up timeout error. 225 | act_timeout := func() fsm.Input { 226 | tx.timeoutError() 227 | return client_input_delete 228 | } 229 | 230 | // Just delete the transaction. 231 | act_delete := func() fsm.Input { 232 | tx.Delete() 233 | return fsm.NO_INPUT 234 | } 235 | 236 | // Define States 237 | 238 | // "Trying" 239 | client_state_def_calling := fsm.State{ 240 | Index: client_state_calling, 241 | Outcomes: map[fsm.Input]fsm.Outcome{ 242 | client_input_1xx: {client_state_proceeding, act_passup}, 243 | client_input_2xx: {client_state_completed, act_final}, 244 | client_input_300_plus: {client_state_completed, act_final}, 245 | client_input_timer_a: {client_state_calling, act_resend}, 246 | client_input_timer_b: {client_state_terminated, act_timeout}, 247 | client_input_transport_err: {client_state_terminated, act_trans_err}, 248 | }, 249 | } 250 | 251 | // Proceeding 252 | client_state_def_proceeding := fsm.State{ 253 | Index: client_state_proceeding, 254 | Outcomes: map[fsm.Input]fsm.Outcome{ 255 | client_input_1xx: {client_state_proceeding, act_passup}, 256 | client_input_2xx: {client_state_completed, act_final}, 257 | client_input_300_plus: {client_state_completed, act_final}, 258 | client_input_timer_a: {client_state_proceeding, act_resend}, 259 | client_input_timer_b: {client_state_terminated, act_timeout}, 260 | client_input_transport_err: {client_state_terminated, act_trans_err}, 261 | }, 262 | } 263 | 264 | // Completed 265 | client_state_def_completed := fsm.State{ 266 | Index: client_state_completed, 267 | Outcomes: map[fsm.Input]fsm.Outcome{ 268 | client_input_1xx: {client_state_completed, fsm.NO_ACTION}, 269 | client_input_2xx: {client_state_completed, fsm.NO_ACTION}, 270 | client_input_300_plus: {client_state_completed, fsm.NO_ACTION}, 271 | client_input_timer_d: {client_state_terminated, act_delete}, 272 | client_input_timer_a: {client_state_completed, fsm.NO_ACTION}, 273 | client_input_timer_b: {client_state_completed, fsm.NO_ACTION}, 274 | }, 275 | } 276 | 277 | // Terminated 278 | client_state_def_terminated := fsm.State{ 279 | Index: client_state_terminated, 280 | Outcomes: map[fsm.Input]fsm.Outcome{ 281 | client_input_1xx: {client_state_terminated, fsm.NO_ACTION}, 282 | client_input_2xx: {client_state_terminated, fsm.NO_ACTION}, 283 | client_input_300_plus: {client_state_terminated, fsm.NO_ACTION}, 284 | client_input_timer_a: {client_state_terminated, fsm.NO_ACTION}, 285 | client_input_timer_b: {client_state_terminated, fsm.NO_ACTION}, 286 | client_input_timer_d: {client_state_terminated, fsm.NO_ACTION}, 287 | client_input_delete: {client_state_terminated, act_delete}, 288 | }, 289 | } 290 | 291 | fsm, err := fsm.Define( 292 | client_state_def_calling, 293 | client_state_def_proceeding, 294 | client_state_def_completed, 295 | client_state_def_terminated, 296 | ) 297 | 298 | if err != nil { 299 | log.Severe("Failure to define INVITE client transaction fsm: %s", err.Error()) 300 | } 301 | 302 | tx.fsm = fsm 303 | } 304 | -------------------------------------------------------------------------------- /base/stringify_test.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | // These tests confirm that our various structures stringify correctly. 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | // Generic test for testing anything with a String() method. 11 | type stringTest struct { 12 | description string 13 | input fmt.Stringer 14 | expected string 15 | } 16 | 17 | func doTests(tests []stringTest, t *testing.T) { 18 | passed := 0 19 | for _, test := range tests { 20 | if test.input.String() != test.expected { 21 | t.Errorf("[FAIL] %v: Expected: \"%v\", Got: \"%v\"", 22 | test.description, 23 | test.expected, 24 | test.input.String(), 25 | ) 26 | } else { 27 | passed++ 28 | } 29 | } 30 | t.Logf("Passed %v/%v tests", passed, len(tests)) 31 | } 32 | 33 | // Some global ports to use since port is still a pointer. 34 | var port5060 uint16 = 5060 35 | var port6060 uint16 = 6060 36 | var noParams = NewParams() 37 | 38 | func TestSipUri(t *testing.T) { 39 | doTests([]stringTest{ 40 | {"Basic SIP URI", 41 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 42 | "sip:alice@wonderland.com"}, 43 | {"SIP URI with no user", 44 | &SipUri{User: NoString{}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 45 | "sip:wonderland.com"}, 46 | {"SIP URI with password", 47 | &SipUri{User: String{"alice"}, Password: String{"hunter2"}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 48 | "sip:alice:hunter2@wonderland.com"}, 49 | {"SIP URI with explicit port 5060", 50 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", Port: &port5060, UriParams: noParams, Headers: noParams}, 51 | "sip:alice@wonderland.com:5060"}, 52 | {"SIP URI with other port", 53 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", Port: &port6060, UriParams: noParams, Headers: noParams}, 54 | "sip:alice@wonderland.com:6060"}, 55 | {"Basic SIPS URI", 56 | &SipUri{IsEncrypted: true, User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 57 | "sips:alice@wonderland.com"}, 58 | {"SIP URI with one parameter", 59 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", 60 | UriParams: NewParams().Add("food", String{"cake"}), 61 | Headers: noParams}, 62 | "sip:alice@wonderland.com;food=cake"}, 63 | {"SIP URI with one no-value parameter", 64 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", 65 | UriParams: NewParams().Add("something", NoString{}), 66 | Headers: noParams}, 67 | "sip:alice@wonderland.com;something"}, 68 | {"SIP URI with three parameters", 69 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", 70 | UriParams: NewParams().Add("food", String{"cake"}).Add("something", NoString{}).Add("drink", String{"tea"}), 71 | Headers: noParams}, 72 | "sip:alice@wonderland.com;food=cake;something;drink=tea"}, 73 | {"SIP URI with one header", 74 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", 75 | UriParams: noParams, 76 | Headers: NewParams().Add("CakeLocation", String{"Tea Party"})}, 77 | "sip:alice@wonderland.com?CakeLocation=\"Tea Party\""}, 78 | {"SIP URI with three headers", 79 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", 80 | UriParams: noParams, 81 | Headers: NewParams().Add("CakeLocation", String{"Tea Party"}).Add("Identity", String{"Mad Hatter"}).Add("OtherHeader", String{"Some value"})}, 82 | "sip:alice@wonderland.com?CakeLocation=\"Tea Party\"&Identity=\"Mad Hatter\"&OtherHeader=\"Some value\""}, 83 | {"SIP URI with parameter and header", 84 | &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", 85 | UriParams: NewParams().Add("food", String{"cake"}), 86 | Headers: NewParams().Add("CakeLocation", String{"Tea Party"})}, 87 | "sip:alice@wonderland.com;food=cake?CakeLocation=\"Tea Party\""}, 88 | {"Wildcard URI", &WildcardUri{}, "*"}, 89 | }, t) 90 | } 91 | 92 | func TestHeaders(t *testing.T) { 93 | doTests([]stringTest{ 94 | // To Headers. 95 | {"Basic To Header", 96 | &ToHeader{DisplayName: NoString{}, 97 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 98 | Params: noParams}, 99 | "To: "}, 100 | {"To Header with display name", 101 | &ToHeader{DisplayName: String{"Alice Liddell"}, 102 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 103 | Params: noParams}, 104 | "To: \"Alice Liddell\" "}, 105 | {"To Header with parameters", 106 | &ToHeader{DisplayName: NoString{}, 107 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 108 | Params: NewParams().Add("food", String{"cake"})}, 109 | "To: ;food=cake"}, 110 | 111 | // From Headers. 112 | {"Basic From Header", 113 | &FromHeader{DisplayName: NoString{}, 114 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 115 | Params: noParams}, 116 | "From: "}, 117 | {"From Header with display name", 118 | &FromHeader{DisplayName: String{"Alice Liddell"}, 119 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 120 | Params: noParams}, 121 | "From: \"Alice Liddell\" "}, 122 | {"From Header with parameters", 123 | &FromHeader{DisplayName: NoString{}, 124 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 125 | Params: NewParams().Add("food", String{"cake"})}, 126 | "From: ;food=cake"}, 127 | 128 | // Contact Headers 129 | {"Basic Contact Header", 130 | &ContactHeader{DisplayName: NoString{}, 131 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 132 | Params: noParams}, 133 | "Contact: "}, 134 | {"Contact Header with display name", 135 | &ContactHeader{DisplayName: String{"Alice Liddell"}, 136 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 137 | Params: noParams}, 138 | "Contact: \"Alice Liddell\" "}, 139 | {"Contact Header with parameters", 140 | &ContactHeader{DisplayName: NoString{}, 141 | Address: &SipUri{User: String{"alice"}, Password: NoString{}, Host: "wonderland.com", UriParams: noParams, Headers: noParams}, 142 | Params: NewParams().Add("food", String{"cake"})}, 143 | "Contact: ;food=cake"}, 144 | {"Contact Header with Wildcard URI", 145 | &ContactHeader{DisplayName: NoString{}, Address: &WildcardUri{}, Params: noParams}, 146 | "Contact: *"}, 147 | {"Contact Header with display name and Wildcard URI", 148 | &ContactHeader{DisplayName: String{"Mad Hatter"}, Address: &WildcardUri{}, Params: noParams}, 149 | "Contact: \"Mad Hatter\" *"}, 150 | {"Contact Header with Wildcard URI and parameters", 151 | &ContactHeader{DisplayName: NoString{}, Address: &WildcardUri{}, Params: NewParams().Add("food", String{"cake"})}, 152 | "Contact: *;food=cake"}, 153 | 154 | // Via Headers. 155 | {"Basic Via Header", ViaHeader{&ViaHop{"SIP", "2.0", "UDP", "wonderland.com", nil, NewParams()}}, "Via: SIP/2.0/UDP wonderland.com"}, 156 | {"Via Header with port", ViaHeader{&ViaHop{"SIP", "2.0", "UDP", "wonderland.com", &port6060, NewParams()}}, "Via: SIP/2.0/UDP wonderland.com:6060"}, 157 | {"Via Header with params", ViaHeader{ 158 | &ViaHop{"SIP", "2.0", "UDP", "wonderland.com", &port6060, NewParams().Add("food", String{"cake"}).Add("delicious", NoString{})}}, 159 | "Via: SIP/2.0/UDP wonderland.com:6060;food=cake;delicious"}, 160 | {"Via Header with 3 simple hops", ViaHeader{ 161 | &ViaHop{"SIP", "2.0", "UDP", "wonderland.com", nil, NewParams()}, 162 | &ViaHop{"SIP", "2.0", "TCP", "looking-glass.net", nil, NewParams()}, 163 | &ViaHop{"SIP", "2.0", "UDP", "oxford.co.uk", nil, NewParams()}, 164 | }, "Via: SIP/2.0/UDP wonderland.com, SIP/2.0/TCP looking-glass.net, SIP/2.0/UDP oxford.co.uk"}, 165 | {"Via Header with 3 complex hops", ViaHeader{ 166 | &ViaHop{"SIP", "2.0", "UDP", "wonderland.com", &port5060, NewParams()}, 167 | &ViaHop{"SIP", "2.0", "TCP", "looking-glass.net", &port6060, NewParams().Add("food", String{"cake"})}, 168 | &ViaHop{"SIP", "2.0", "UDP", "oxford.co.uk", nil, NewParams().Add("delicious", NoString{})}, 169 | }, "Via: SIP/2.0/UDP wonderland.com:5060, SIP/2.0/TCP looking-glass.net:6060;food=cake, SIP/2.0/UDP oxford.co.uk;delicious"}, 170 | 171 | // Require Headers. 172 | {"Require Header (empty)", &RequireHeader{[]string{}}, "Require: "}, 173 | {"Require Header (one option)", &RequireHeader{[]string{"NewFeature1"}}, "Require: NewFeature1"}, 174 | {"Require Header (three options)", &RequireHeader{[]string{"NewFeature1", "FunkyExtension", "UnnecessaryAddition"}}, "Require: NewFeature1, FunkyExtension, UnnecessaryAddition"}, 175 | 176 | // Supported Headers. 177 | {"Supported Header (empty)", &SupportedHeader{[]string{}}, "Supported: "}, 178 | {"Supported Header (one option)", &SupportedHeader{[]string{"NewFeature1"}}, "Supported: NewFeature1"}, 179 | {"Supported Header (three options)", &SupportedHeader{[]string{"NewFeature1", "FunkyExtension", "UnnecessaryAddition"}}, "Supported: NewFeature1, FunkyExtension, UnnecessaryAddition"}, 180 | 181 | // Proxy-Require Headers. 182 | {"Proxy-Require Header (empty)", &ProxyRequireHeader{[]string{}}, "Proxy-Require: "}, 183 | {"Proxy-Require Header (one option)", &ProxyRequireHeader{[]string{"NewFeature1"}}, "Proxy-Require: NewFeature1"}, 184 | {"Proxy-Require Header (three options)", &ProxyRequireHeader{[]string{"NewFeature1", "FunkyExtension", "UnnecessaryAddition"}}, "Proxy-Require: NewFeature1, FunkyExtension, UnnecessaryAddition"}, 185 | 186 | // Unsupported Headers. 187 | {"Unsupported Header (empty)", &UnsupportedHeader{[]string{}}, "Unsupported: "}, 188 | {"Unsupported Header (one option)", &UnsupportedHeader{[]string{"NewFeature1"}}, "Unsupported: NewFeature1"}, 189 | {"Unsupported Header (three options)", &UnsupportedHeader{[]string{"NewFeature1", "FunkyExtension", "UnnecessaryAddition"}}, "Unsupported: NewFeature1, FunkyExtension, UnnecessaryAddition"}, 190 | 191 | // Various simple headers. 192 | {"Call-Id Header", CallId("call-id-1"), "Call-Id: call-id-1"}, 193 | {"CSeq Header", &CSeq{1234, "INVITE"}, "CSeq: 1234 INVITE"}, 194 | {"Max Forwards Header", MaxForwards(70), "Max-Forwards: 70"}, 195 | {"Content Length Header", ContentLength(70), "Content-Length: 70"}, 196 | }, t) 197 | } 198 | -------------------------------------------------------------------------------- /base/messages.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // A representation of a SIP method. 10 | // This is syntactic sugar around the string type, so make sure to use 11 | // the Equals method rather than built-in equality, or you'll fall foul of case differences. 12 | // If you're defining your own Method, uppercase is preferred but not compulsory. 13 | type Method string 14 | 15 | // Determine if the given method equals some other given method. 16 | // This is syntactic sugar for case insensitive equality checking. 17 | func (method *Method) Equals(other *Method) bool { 18 | if method != nil && other != nil { 19 | return strings.EqualFold(string(*method), string(*other)) 20 | } else { 21 | return method == other 22 | } 23 | } 24 | 25 | // It's nicer to avoid using raw strings to represent methods, so the following standard 26 | // method names are defined here as constants for convenience. 27 | const ( 28 | INVITE Method = "INVITE" 29 | ACK Method = "ACK" 30 | CANCEL Method = "CANCEL" 31 | BYE Method = "BYE" 32 | REGISTER Method = "REGISTER" 33 | OPTIONS Method = "OPTIONS" 34 | SUBSCRIBE Method = "SUBSCRIBE" 35 | NOTIFY Method = "NOTIFY" 36 | REFER Method = "REFER" 37 | ) 38 | 39 | // Internal representation of a SIP message - either a Request or a Response. 40 | type SipMessage interface { 41 | // Yields a flat, string representation of the SIP message suitable for sending out over the wire. 42 | String() string 43 | 44 | // Adds a header to this message. 45 | AddHeader(h SipHeader) 46 | 47 | // Returns a slice of all headers of the given type. 48 | // If there are no headers of the requested type, returns an empty slice. 49 | Headers(name string) []SipHeader 50 | 51 | // Return all headers attached to the message, as a slice. 52 | AllHeaders() []SipHeader 53 | 54 | // Yields a short string representation of the message useful for logging. 55 | Short() string 56 | 57 | // Remove the specified header from the message. 58 | RemoveHeader(header SipHeader) error 59 | 60 | // Get the body of the message, as a string. 61 | GetBody() string 62 | 63 | // Set the body of the message. 64 | SetBody(body string) 65 | } 66 | 67 | // A shared type for holding headers and their ordering. 68 | type headers struct { 69 | // The logical SIP headers attached to this message. 70 | headers map[string][]SipHeader 71 | 72 | // The order the headers should be displayed in. 73 | headerOrder []string 74 | } 75 | 76 | func newHeaders() (result headers) { 77 | result.headers = make(map[string][]SipHeader) 78 | return result 79 | } 80 | 81 | func (h headers) String() string { 82 | buffer := bytes.Buffer{} 83 | // Construct each header in turn and add it to the message. 84 | for typeIdx, name := range h.headerOrder { 85 | headers := h.headers[name] 86 | for idx, header := range headers { 87 | buffer.WriteString(header.String()) 88 | if typeIdx < len(h.headerOrder) || idx < len(headers) { 89 | buffer.WriteString("\r\n") 90 | } 91 | } 92 | } 93 | return buffer.String() 94 | } 95 | 96 | // Add the given header. 97 | func (hs *headers) AddHeader(h SipHeader) { 98 | if hs.headers == nil { 99 | hs.headers = map[string][]SipHeader{} 100 | hs.headerOrder = []string{} 101 | } 102 | name := h.Name() 103 | if _, ok := hs.headers[name]; ok { 104 | hs.headers[name] = append(hs.headers[name], h) 105 | } else { 106 | hs.headers[name] = []SipHeader{h} 107 | hs.headerOrder = append(hs.headerOrder, name) 108 | } 109 | } 110 | 111 | // AddFrontHeader adds header to the front of header list 112 | // if there is no header has h's name, add h to the tail of all headers 113 | // if there are some headers have h's name, add h to front of the sublist 114 | func (hs *headers) AddFrontHeader(h SipHeader) { 115 | if hs.headers == nil { 116 | hs.headers = map[string][]SipHeader{} 117 | hs.headerOrder = []string{} 118 | } 119 | name := h.Name() 120 | if hdrs, ok := hs.headers[name]; ok { 121 | newHdrs := make([]SipHeader, 1, len(hdrs)+1) 122 | newHdrs[0] = h 123 | hs.headers[name] = append(newHdrs, hdrs...) 124 | } else { 125 | hs.headers[name] = []SipHeader{h} 126 | hs.headerOrder = append(hs.headerOrder, name) 127 | } 128 | } 129 | 130 | // Gets some headers. 131 | func (hs *headers) Headers(name string) []SipHeader { 132 | if hs.headers == nil { 133 | hs.headers = map[string][]SipHeader{} 134 | hs.headerOrder = []string{} 135 | } 136 | if headers, ok := hs.headers[name]; ok { 137 | return headers 138 | } else { 139 | return []SipHeader{} 140 | } 141 | } 142 | 143 | // Copy all headers of one type from one message to another. 144 | // Appending to any headers that were already there. 145 | func CopyHeaders(name string, from, to SipMessage) { 146 | for _, h := range from.Headers(name) { 147 | to.AddHeader(h.Copy()) 148 | } 149 | 150 | } 151 | 152 | // A SIP request (c.f. RFC 3261 section 7.1). 153 | type Request struct { 154 | // Which method this request is, e.g. an INVITE or a REGISTER. 155 | Method Method 156 | 157 | // The Request URI. This indicates the user to whom this request is being addressed. 158 | Recipient Uri 159 | 160 | // The version of SIP used in this message, e.g. "SIP/2.0". 161 | SipVersion string 162 | 163 | // A Request has headers. 164 | headers 165 | 166 | // The order the headers should be displayed in. 167 | headerOrder []string 168 | 169 | // The application data of the message. 170 | Body string 171 | } 172 | 173 | func NewRequest(method Method, recipient Uri, sipVersion string, headers []SipHeader, body string) (request *Request) { 174 | request = new(Request) 175 | request.Method = method 176 | request.SipVersion = sipVersion 177 | request.Recipient = recipient 178 | request.headers = newHeaders() 179 | request.Body = body 180 | 181 | for _, header := range headers { 182 | request.AddHeader(header) 183 | } 184 | 185 | return 186 | } 187 | 188 | func (request *Request) String() string { 189 | var buffer bytes.Buffer 190 | 191 | // Every SIP request starts with a Request Line - RFC 2361 7.1. 192 | buffer.WriteString(fmt.Sprintf("%s %s %s\r\n", 193 | (string)(request.Method), 194 | request.Recipient.String(), 195 | request.SipVersion)) 196 | 197 | buffer.WriteString(request.headers.String()) 198 | 199 | // If the request has a message body, add it. 200 | buffer.WriteString("\r\n" + request.Body) 201 | 202 | return buffer.String() 203 | } 204 | 205 | func (request *Request) Short() string { 206 | var buffer bytes.Buffer 207 | 208 | buffer.WriteString(fmt.Sprintf("%s %s %s", 209 | (string)(request.Method), 210 | request.Recipient.String(), 211 | request.SipVersion)) 212 | 213 | cseqs := request.Headers("CSeq") 214 | if len(cseqs) > 0 { 215 | buffer.WriteString(fmt.Sprintf(" (CSeq: %s)", (cseqs[0].(*CSeq)).String())) 216 | } 217 | 218 | return buffer.String() 219 | } 220 | 221 | func (request *Request) AllHeaders() []SipHeader { 222 | allHeaders := make([]SipHeader, 0) 223 | for _, key := range request.headers.headerOrder { 224 | allHeaders = append(allHeaders, request.headers.headers[key]...) 225 | } 226 | 227 | return allHeaders 228 | } 229 | 230 | func (request *Request) RemoveHeader(header SipHeader) error { 231 | errNoMatch := fmt.Errorf("cannot remove header '%s' from request '%s' as it is not present", 232 | header.String(), request.Short()) 233 | name := header.Name() 234 | 235 | headersOfSameType, isMatch := request.headers.headers[name] 236 | 237 | if !isMatch || len(headersOfSameType) == 0 { 238 | return errNoMatch 239 | } 240 | 241 | found := false 242 | for idx, hdr := range headersOfSameType { 243 | if hdr == header { 244 | request.headers.headers[name] = append(headersOfSameType[:idx], headersOfSameType[idx+1:]...) 245 | found = true 246 | break 247 | } 248 | } 249 | if !found { 250 | return errNoMatch 251 | } 252 | 253 | if len(request.headers.headers[name]) == 0 { 254 | // The header we removed was the only one of its type. 255 | // Tidy up the header structure by removing the empty list value from the header map, 256 | // and removing the entry from the headerOrder list. 257 | delete(request.headers.headers, name) 258 | 259 | for idx, entry := range request.headerOrder { 260 | if entry == name { 261 | request.headers.headerOrder = append(request.headerOrder[:idx], request.headerOrder[idx+1:]...) 262 | } 263 | } 264 | } 265 | 266 | return nil 267 | } 268 | 269 | func (request *Request) GetBody() string { 270 | return request.Body 271 | } 272 | 273 | func (request *Request) SetBody(body string) { 274 | request.Body = body 275 | hdrs := request.Headers("Content-Length") 276 | if len(hdrs) == 0 { 277 | length := ContentLength(len(body)) 278 | request.AddHeader(length) 279 | } else { 280 | hdrs[0] = ContentLength(len(body)) 281 | } 282 | } 283 | 284 | // A SIP response object (c.f. RFC 3261 section 7.2). 285 | type Response struct { 286 | // The version of SIP used in this message, e.g. "SIP/2.0". 287 | SipVersion string 288 | 289 | // The response code, e.g. 200, 401 or 500. 290 | // This indicates the outcome of the originating request. 291 | StatusCode uint16 292 | 293 | // The reason string provides additional, human-readable information used to provide 294 | // clarification or explanation of the status code. 295 | // This will vary between different SIP UAs, and should not be interpreted by the receiving UA. 296 | Reason string 297 | 298 | // A response has headers. 299 | headers 300 | 301 | // The application data of the message. 302 | Body string 303 | } 304 | 305 | func NewResponse(sipVersion string, statusCode uint16, reason string, headers []SipHeader, body string) (response *Response) { 306 | response = new(Response) 307 | response.SipVersion = sipVersion 308 | response.StatusCode = statusCode 309 | response.Reason = reason 310 | response.Body = body 311 | response.headers = newHeaders() 312 | response.headerOrder = make([]string, 0) 313 | 314 | for _, header := range headers { 315 | response.AddHeader(header) 316 | } 317 | 318 | return 319 | } 320 | 321 | func (response *Response) String() string { 322 | var buffer bytes.Buffer 323 | 324 | // Every SIP response starts with a Status Line - RFC 2361 7.2. 325 | buffer.WriteString(fmt.Sprintf("%s %d %s\r\n", 326 | response.SipVersion, 327 | response.StatusCode, 328 | response.Reason)) 329 | 330 | // Write the headers. 331 | buffer.WriteString(response.headers.String()) 332 | 333 | // If the request has a message body, add it. 334 | buffer.WriteString("\r\n" + response.Body) 335 | 336 | return buffer.String() 337 | } 338 | 339 | func (response *Response) Short() string { 340 | var buffer bytes.Buffer 341 | 342 | buffer.WriteString(fmt.Sprintf("%s %d %s\r\n", 343 | response.SipVersion, 344 | response.StatusCode, 345 | response.Reason)) 346 | 347 | cseqs := response.Headers("CSeq") 348 | if len(cseqs) > 0 { 349 | buffer.WriteString(fmt.Sprintf(" (CSeq: %s)", (cseqs[0].(*CSeq)).String())) 350 | } 351 | 352 | return buffer.String() 353 | } 354 | 355 | func (response *Response) AllHeaders() []SipHeader { 356 | allHeaders := make([]SipHeader, 0) 357 | for _, key := range response.headers.headerOrder { 358 | allHeaders = append(allHeaders, response.headers.headers[key]...) 359 | } 360 | 361 | return allHeaders 362 | } 363 | 364 | func (response *Response) RemoveHeader(header SipHeader) error { 365 | errNoMatch := fmt.Errorf("cannot remove header '%s' from response '%s' as it is not present", 366 | header.String(), response.Short()) 367 | name := header.Name() 368 | 369 | headersOfSameType, isMatch := response.headers.headers[name] 370 | 371 | if !isMatch || len(headersOfSameType) == 0 { 372 | return errNoMatch 373 | } 374 | 375 | found := false 376 | for idx, hdr := range headersOfSameType { 377 | if hdr == header { 378 | response.headers.headers[name] = append(headersOfSameType[:idx], headersOfSameType[idx+1:]...) 379 | found = true 380 | break 381 | } 382 | } 383 | if !found { 384 | return errNoMatch 385 | } 386 | 387 | if len(response.headers.headers[name]) == 0 { 388 | // The header we removed was the only one of its type. 389 | // Tidy up the header structure by removing the empty list value from the header map, 390 | // and removing the entry from the headerOrder list. 391 | delete(response.headers.headers, name) 392 | 393 | for idx, entry := range response.headers.headerOrder { 394 | if entry == name { 395 | response.headers.headerOrder = append(response.headers.headerOrder[:idx], response.headers.headerOrder[idx+1:]...) 396 | } 397 | } 398 | } 399 | 400 | return nil 401 | } 402 | 403 | func (response *Response) GetBody() string { 404 | return response.Body 405 | } 406 | 407 | func (response *Response) SetBody(body string) { 408 | response.Body = body 409 | hdrs := response.Headers("Content-Length") 410 | if len(hdrs) == 0 { 411 | length := ContentLength(len(body)) 412 | response.AddHeader(length) 413 | } else { 414 | hdrs[0] = ContentLength(len(body)) 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /base/headers.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "github.com/stefankopieczek/gossip/log" 5 | "github.com/stefankopieczek/gossip/utils" 6 | ) 7 | 8 | import "bytes" 9 | import "fmt" 10 | import "strconv" 11 | import "strings" 12 | 13 | // Whitespace recognised by SIP protocol. 14 | const c_ABNF_WS = " \t" 15 | 16 | // Maybestring contains a string, or nil. 17 | type MaybeString interface { 18 | implementsMaybeString() 19 | } 20 | 21 | // NoString represents the absence of a string. 22 | type NoString struct{} 23 | 24 | func (n NoString) implementsMaybeString() {} 25 | 26 | // String represents an actual string. 27 | type String struct { 28 | S string 29 | } 30 | 31 | func (s String) implementsMaybeString() {} 32 | 33 | func (s String) String() string { 34 | return s.S 35 | } 36 | 37 | // A single logical header from a SIP message. 38 | type SipHeader interface { 39 | // Produce the string representation of the header. 40 | String() string 41 | 42 | // Produce the name of the header (e.g. "To", "Via") 43 | Name() string 44 | 45 | // Produce an exact copy of this header. 46 | Copy() SipHeader 47 | } 48 | 49 | // A URI from any schema (e.g. sip:, tel:, callto:) 50 | type Uri interface { 51 | // Determine if the two URIs are equal according to the rules in RFC 3261 s. 19.1.4. 52 | Equals(other Uri) bool 53 | 54 | // Produce the string representation of the URI. 55 | String() string 56 | 57 | // Produce an exact copy of this URI. 58 | Copy() Uri 59 | } 60 | 61 | // A URI from a schema suitable for inclusion in a Contact: header. 62 | // The only such URIs are sip/sips URIs and the special wildcard URI '*'. 63 | type ContactUri interface { 64 | Uri 65 | 66 | // Return true if and only if the URI is the special wildcard URI '*'; that is, if it is 67 | // a WildcardUri struct. 68 | IsWildcard() bool 69 | } 70 | 71 | // A SIP or SIPS URI, including all params and URI header params. 72 | type SipUri struct { 73 | // True if and only if the URI is a SIPS URI. 74 | IsEncrypted bool 75 | 76 | // The user part of the URI: the 'joe' in sip:joe@bloggs.com 77 | // This is a pointer, so that URIs without a user part can have 'nil'. 78 | User MaybeString 79 | 80 | // The password field of the URI. This is represented in the URI as joe:hunter2@bloggs.com. 81 | // Note that if a URI has a password field, it *must* have a user field as well. 82 | // This is a pointer, so that URIs without a password field can have 'nil'. 83 | // Note that RFC 3261 strongly recommends against the use of password fields in SIP URIs, 84 | // as they are fundamentally insecure. 85 | Password MaybeString 86 | 87 | // The host part of the URI. This can be a domain, or a string representation of an IP address. 88 | Host string 89 | 90 | // The port part of the URI. This is optional, and so is represented here as a pointer type. 91 | Port *uint16 92 | 93 | // Any parameters associated with the URI. 94 | // These are used to provide information about requests that may be constructed from the URI. 95 | // (For more details, see RFC 3261 section 19.1.1). 96 | // These appear as a semicolon-separated list of key=value pairs following the host[:port] part. 97 | UriParams Params 98 | 99 | // Any headers to be included on requests constructed from this URI. 100 | // These appear as a '&'-separated list at the end of the URI, introduced by '?'. 101 | // Although the values of the map are MaybeStrings, they will never be NoString in practice as the parser 102 | // guarantees to not return blank values for header elements in SIP URIs. 103 | // You should not set the values of headers to NoString. 104 | Headers Params 105 | } 106 | 107 | func copyWithNil(params Params) Params { 108 | if (params == nil) { 109 | return NewParams() 110 | } 111 | return params.Copy() 112 | } 113 | 114 | // Copy the Sip URI. 115 | func (uri *SipUri) Copy() Uri { 116 | var port *uint16 117 | if uri.Port != nil { 118 | temp := *uri.Port 119 | port = &temp 120 | } 121 | 122 | return &SipUri{ 123 | uri.IsEncrypted, 124 | uri.User, 125 | uri.Password, 126 | uri.Host, 127 | port, 128 | copyWithNil(uri.UriParams), 129 | copyWithNil(uri.Headers), 130 | } 131 | } 132 | 133 | // IsWildcard() always returns 'false' for SIP URIs as they are not equal to the wildcard '*' URI. 134 | // This method is required since SIP URIs are valid in Contact: headers. 135 | func (uri *SipUri) IsWildcard() bool { 136 | return false 137 | } 138 | 139 | // Determine if the SIP URI is equal to the specified URI according to the rules laid down in RFC 3261 s. 19.1.4. 140 | // TODO: The Equals method is not currently RFC-compliant; fix this! 141 | func (uri *SipUri) Equals(otherUri Uri) bool { 142 | otherPtr, ok := otherUri.(*SipUri) 143 | if !ok { 144 | return false 145 | } 146 | 147 | other := *otherPtr 148 | result := uri.IsEncrypted == other.IsEncrypted && 149 | uri.User == other.User && 150 | uri.Password == other.Password && 151 | uri.Host == other.Host && 152 | utils.Uint16PtrEq(uri.Port, other.Port) 153 | 154 | if !result { 155 | return false 156 | } 157 | 158 | if !uri.UriParams.Equals(other.UriParams) { 159 | return false 160 | } 161 | 162 | if !uri.Headers.Equals(other.Headers) { 163 | return false 164 | } 165 | 166 | return true 167 | } 168 | 169 | // Generates the string representation of a SipUri struct. 170 | func (uri *SipUri) String() string { 171 | var buffer bytes.Buffer 172 | 173 | // Compulsory protocol identifier. 174 | if uri.IsEncrypted { 175 | buffer.WriteString("sips") 176 | buffer.WriteString(":") 177 | } else { 178 | buffer.WriteString("sip") 179 | buffer.WriteString(":") 180 | } 181 | 182 | // Optional userinfo part. 183 | switch user := uri.User.(type) { 184 | case String: 185 | buffer.WriteString(user.String()) 186 | switch pw := uri.Password.(type) { 187 | case String: 188 | buffer.WriteString(":") 189 | buffer.WriteString(pw.String()) 190 | } 191 | buffer.WriteString("@") 192 | } 193 | 194 | // Compulsory hostname. 195 | buffer.WriteString(uri.Host) 196 | 197 | // Optional port number. 198 | if uri.Port != nil { 199 | buffer.WriteString(":") 200 | buffer.WriteString(strconv.Itoa(int(*uri.Port))) 201 | } 202 | 203 | if (uri.UriParams != nil) && uri.UriParams.Length() > 0 { 204 | buffer.WriteString(";") 205 | buffer.WriteString(uri.UriParams.ToString(';')) 206 | } 207 | 208 | if (uri.Headers != nil) && uri.Headers.Length() > 0 { 209 | buffer.WriteString("?") 210 | buffer.WriteString(uri.Headers.ToString('&')) 211 | } 212 | 213 | return buffer.String() 214 | } 215 | 216 | // The special wildcard URI used in Contact: headers in REGISTER requests when expiring all registrations. 217 | type WildcardUri struct{} 218 | 219 | // Copy the wildcard URI. Not hard! 220 | func (uri WildcardUri) Copy() Uri { return uri } 221 | 222 | // Always returns 'true'. 223 | func (uri WildcardUri) IsWildcard() bool { 224 | return true 225 | } 226 | 227 | // Always returns '*' - the representation of a wildcard URI in a SIP message. 228 | func (uri WildcardUri) String() string { 229 | return "*" 230 | } 231 | 232 | // Determines if this wildcard URI equals the specified other URI. 233 | // This is true if and only if the other URI is also a wildcard URI. 234 | func (uri WildcardUri) Equals(other Uri) bool { 235 | switch other.(type) { 236 | case WildcardUri: 237 | return true 238 | default: 239 | return false 240 | } 241 | } 242 | 243 | // Generic list of parameters on a header. 244 | type Params interface { 245 | Get(k string) (MaybeString, bool) 246 | Add(k string, v MaybeString) Params 247 | Copy() Params 248 | Equals(p Params) bool 249 | ToString(sep uint8) string 250 | Length() int 251 | Items() map[string]MaybeString 252 | Keys() []string 253 | } 254 | 255 | type params struct { 256 | params map[string]MaybeString 257 | paramOrder []string 258 | } 259 | 260 | // Create an empty set of parameters. 261 | func NewParams() Params { 262 | return ¶ms{map[string]MaybeString{}, []string{}} 263 | } 264 | 265 | // Returns the entire parameter map. 266 | func (p *params) Items() map[string]MaybeString { 267 | return p.params 268 | } 269 | 270 | // Returns a slice of keys, in order. 271 | func (p *params) Keys() []string { 272 | return p.paramOrder 273 | } 274 | 275 | // Returns the requested parameter value. 276 | func (p *params) Get(k string) (MaybeString, bool) { 277 | v, ok := p.params[k] 278 | return v, ok 279 | } 280 | 281 | // Add a new parameter. 282 | func (p *params) Add(k string, v MaybeString) Params { 283 | // Add param to order list if new. 284 | if _, ok := p.params[k]; !ok { 285 | p.paramOrder = append(p.paramOrder, k) 286 | } 287 | 288 | // Set param value. 289 | p.params[k] = v 290 | 291 | // Return the params so calls can be chained. 292 | return p 293 | } 294 | 295 | // Copy a list of params. 296 | func (p *params) Copy() Params { 297 | dup := NewParams() 298 | for _, k := range p.Keys() { 299 | if v, ok := p.Get(k); ok { 300 | dup.Add(k, v) 301 | } else { 302 | log.Severe("Internal consistency error. Key %v present in param.Keys() but failed to Get()!", k) 303 | } 304 | } 305 | 306 | return dup 307 | } 308 | 309 | // Render params to a string. 310 | // Note that this does not escape special characters, this should already have been done before calling this method. 311 | func (p *params) ToString(sep uint8) string { 312 | var buffer bytes.Buffer 313 | first := true 314 | 315 | for _, k := range p.Keys() { 316 | v, ok := p.Get(k) 317 | if !ok { 318 | log.Severe("Internal consistency error. Key %v present in param.Keys() but failed to Get()!", k) 319 | continue 320 | } 321 | 322 | if !first { 323 | buffer.WriteString(fmt.Sprintf("%c", sep)) 324 | } 325 | first = false 326 | 327 | buffer.WriteString(fmt.Sprintf("%s", k)) 328 | 329 | switch v := v.(type) { 330 | case String: 331 | if strings.ContainsAny(v.String(), c_ABNF_WS) { 332 | buffer.WriteString(fmt.Sprintf("=\"%s\"", v.String())) 333 | } else { 334 | buffer.WriteString(fmt.Sprintf("=%s", v.String())) 335 | } 336 | } 337 | } 338 | 339 | return buffer.String() 340 | } 341 | 342 | // Returns number of params. 343 | func (p *params) Length() int { 344 | return len(p.params) 345 | } 346 | 347 | // Check if two maps of parameters are equal in the sense of having the same keys with the same values. 348 | // This does not rely on any ordering of the keys of the map in memory. 349 | func (p *params) Equals(q Params) bool { 350 | if p.Length() == 0 && q.Length() == 0 { 351 | return true 352 | } 353 | 354 | if p.Length() != q.Length() { 355 | return false 356 | } 357 | 358 | for k, p_val := range p.Items() { 359 | q_val, ok := q.Get(k) 360 | if !ok { 361 | return false 362 | } 363 | if p_val != q_val { 364 | return false 365 | } 366 | } 367 | 368 | return true 369 | } 370 | 371 | // Encapsulates a header that gossip does not natively support. 372 | // This allows header data that is not understood to be parsed by gossip and relayed to the parent application. 373 | type GenericHeader struct { 374 | // The name of the header. 375 | HeaderName string 376 | 377 | // The contents of the header, including any parameters. 378 | // This is transparent data that is not natively understood by gossip. 379 | Contents string 380 | } 381 | 382 | // Convert the header to a flat string representation. 383 | func (header *GenericHeader) String() string { 384 | return header.HeaderName + ": " + header.Contents 385 | } 386 | 387 | // Pull out the header name. 388 | func (h *GenericHeader) Name() string { 389 | return h.HeaderName 390 | } 391 | 392 | // Copy the header. 393 | func (h *GenericHeader) Copy() SipHeader { 394 | return &GenericHeader{h.HeaderName, h.Contents} 395 | } 396 | 397 | type ToHeader struct { 398 | // The display name from the header, may be omitted. 399 | DisplayName MaybeString 400 | 401 | Address Uri 402 | 403 | // Any parameters present in the header. 404 | Params Params 405 | } 406 | 407 | func (to *ToHeader) String() string { 408 | var buffer bytes.Buffer 409 | buffer.WriteString("To: ") 410 | 411 | switch s := to.DisplayName.(type) { 412 | case String: 413 | buffer.WriteString(fmt.Sprintf("\"%s\" ", s.String())) 414 | } 415 | 416 | buffer.WriteString(fmt.Sprintf("<%s>", to.Address)) 417 | 418 | if to.Params.Length() > 0 { 419 | buffer.WriteString(";") 420 | buffer.WriteString(to.Params.ToString(';')) 421 | } 422 | 423 | return buffer.String() 424 | } 425 | 426 | func (h *ToHeader) Name() string { return "To" } 427 | 428 | // Copy the header. 429 | func (h *ToHeader) Copy() SipHeader { 430 | return &ToHeader{h.DisplayName, h.Address.Copy(), h.Params.Copy()} 431 | } 432 | 433 | type FromHeader struct { 434 | // The display name from the header, may be omitted. 435 | DisplayName MaybeString 436 | 437 | Address Uri 438 | 439 | // Any parameters present in the header. 440 | Params Params 441 | } 442 | 443 | func (from *FromHeader) String() string { 444 | var buffer bytes.Buffer 445 | buffer.WriteString("From: ") 446 | 447 | switch s := from.DisplayName.(type) { 448 | case String: 449 | buffer.WriteString(fmt.Sprintf("\"%s\" ", s.String())) 450 | } 451 | 452 | buffer.WriteString(fmt.Sprintf("<%s>", from.Address)) 453 | if from.Params.Length() > 0 { 454 | buffer.WriteString(";") 455 | buffer.WriteString(from.Params.ToString(';')) 456 | } 457 | 458 | return buffer.String() 459 | } 460 | 461 | func (h *FromHeader) Name() string { return "From" } 462 | 463 | // Copy the header. 464 | func (h *FromHeader) Copy() SipHeader { 465 | return &FromHeader{h.DisplayName, h.Address.Copy(), h.Params.Copy()} 466 | } 467 | 468 | type ContactHeader struct { 469 | // The display name from the header, may be omitted. 470 | DisplayName MaybeString 471 | 472 | Address ContactUri 473 | 474 | // Any parameters present in the header. 475 | Params Params 476 | } 477 | 478 | func (contact *ContactHeader) String() string { 479 | var buffer bytes.Buffer 480 | buffer.WriteString("Contact: ") 481 | 482 | switch s := contact.DisplayName.(type) { 483 | case String: 484 | buffer.WriteString(fmt.Sprintf("\"%s\" ", s.String())) 485 | } 486 | 487 | switch contact.Address.(type) { 488 | case *WildcardUri: 489 | // Treat the Wildcard URI separately as it must not be contained in < > angle brackets. 490 | buffer.WriteString("*") 491 | default: 492 | buffer.WriteString(fmt.Sprintf("<%s>", contact.Address.String())) 493 | } 494 | 495 | if (contact.Params != nil) && (contact.Params.Length() > 0) { 496 | buffer.WriteString(";") 497 | buffer.WriteString(contact.Params.ToString(';')) 498 | } 499 | 500 | return buffer.String() 501 | } 502 | 503 | func (h *ContactHeader) Name() string { return "Contact" } 504 | 505 | // Copy the header. 506 | func (h *ContactHeader) Copy() SipHeader { 507 | return &ContactHeader{h.DisplayName, h.Address.Copy().(ContactUri), h.Params.Copy()} 508 | } 509 | 510 | type CallId string 511 | 512 | func (callId CallId) String() string { 513 | return "Call-Id: " + (string)(callId) 514 | } 515 | 516 | func (h *CallId) Name() string { return "Call-Id" } 517 | 518 | func (h *CallId) Copy() SipHeader { 519 | temp := *h 520 | return &temp 521 | } 522 | 523 | type CSeq struct { 524 | SeqNo uint32 525 | MethodName Method 526 | } 527 | 528 | func (cseq *CSeq) String() string { 529 | return fmt.Sprintf("CSeq: %d %s", cseq.SeqNo, cseq.MethodName) 530 | } 531 | 532 | func (h *CSeq) Name() string { return "CSeq" } 533 | 534 | func (h *CSeq) Copy() SipHeader { return &CSeq{h.SeqNo, h.MethodName} } 535 | 536 | type MaxForwards uint32 537 | 538 | func (maxForwards MaxForwards) String() string { 539 | return fmt.Sprintf("Max-Forwards: %d", ((int)(maxForwards))) 540 | } 541 | 542 | func (h MaxForwards) Name() string { return "Max-Forwards" } 543 | 544 | func (h MaxForwards) Copy() SipHeader { return h } 545 | 546 | type ContentLength uint32 547 | 548 | func (contentLength ContentLength) String() string { 549 | return fmt.Sprintf("Content-Length: %d", ((int)(contentLength))) 550 | } 551 | 552 | func (h ContentLength) Name() string { return "Content-Length" } 553 | 554 | func (h ContentLength) Copy() SipHeader { return h } 555 | 556 | type ViaHeader []*ViaHop 557 | 558 | // A single component in a Via header. 559 | // Via headers are composed of several segments of the same structure, added by successive nodes in a routing chain. 560 | type ViaHop struct { 561 | // E.g. 'SIP'. 562 | ProtocolName string 563 | 564 | // E.g. '2.0'. 565 | ProtocolVersion string 566 | Transport string 567 | Host string 568 | 569 | // The port for this via hop. This is stored as a pointer type, since it is an optional field. 570 | Port *uint16 571 | 572 | Params Params 573 | } 574 | 575 | func (hop *ViaHop) String() string { 576 | var buffer bytes.Buffer 577 | buffer.WriteString(fmt.Sprintf("%s/%s/%s %s", 578 | hop.ProtocolName, hop.ProtocolVersion, 579 | hop.Transport, 580 | hop.Host)) 581 | if hop.Port != nil { 582 | buffer.WriteString(fmt.Sprintf(":%d", *hop.Port)) 583 | } 584 | 585 | if hop.Params.Length() > 0 { 586 | buffer.WriteString(";") 587 | buffer.WriteString(hop.Params.ToString(';')) 588 | } 589 | 590 | return buffer.String() 591 | } 592 | 593 | // Return an exact copy of this ViaHop. 594 | func (hop *ViaHop) Copy() *ViaHop { 595 | var port *uint16 = nil 596 | if hop.Port != nil { 597 | temp := *hop.Port 598 | port = &temp 599 | } 600 | return &ViaHop{ 601 | hop.ProtocolName, 602 | hop.ProtocolVersion, 603 | hop.Transport, 604 | hop.Host, 605 | port, 606 | hop.Params.Copy(), 607 | } 608 | } 609 | 610 | func (via ViaHeader) String() string { 611 | var buffer bytes.Buffer 612 | buffer.WriteString("Via: ") 613 | for idx, hop := range via { 614 | buffer.WriteString(hop.String()) 615 | if idx != len(via)-1 { 616 | buffer.WriteString(", ") 617 | } 618 | } 619 | 620 | return buffer.String() 621 | } 622 | 623 | func (h ViaHeader) Name() string { return "Via" } 624 | 625 | func (h ViaHeader) Copy() SipHeader { 626 | dup := make([]*ViaHop, 0, len(h)) 627 | for _, hop := range h { 628 | dup = append(dup, hop.Copy()) 629 | } 630 | return ViaHeader(dup) 631 | } 632 | 633 | type RequireHeader struct { 634 | Options []string 635 | } 636 | 637 | func (header *RequireHeader) String() string { 638 | return fmt.Sprintf("Require: %s", 639 | strings.Join(header.Options, ", ")) 640 | } 641 | 642 | func (h *RequireHeader) Name() string { return "Require" } 643 | 644 | func (h *RequireHeader) Copy() SipHeader { 645 | dup := make([]string, len(h.Options)) 646 | copy(h.Options, dup) 647 | return &RequireHeader{dup} 648 | } 649 | 650 | type SupportedHeader struct { 651 | Options []string 652 | } 653 | 654 | func (header *SupportedHeader) String() string { 655 | return fmt.Sprintf("Supported: %s", 656 | strings.Join(header.Options, ", ")) 657 | } 658 | 659 | func (h *SupportedHeader) Name() string { return "Supported" } 660 | 661 | func (h *SupportedHeader) Copy() SipHeader { 662 | dup := make([]string, len(h.Options)) 663 | copy(h.Options, dup) 664 | return &SupportedHeader{dup} 665 | } 666 | 667 | type ProxyRequireHeader struct { 668 | Options []string 669 | } 670 | 671 | func (header *ProxyRequireHeader) String() string { 672 | return fmt.Sprintf("Proxy-Require: %s", 673 | strings.Join(header.Options, ", ")) 674 | } 675 | 676 | func (h *ProxyRequireHeader) Name() string { return "Proxy-Require" } 677 | 678 | func (h *ProxyRequireHeader) Copy() SipHeader { 679 | dup := make([]string, len(h.Options)) 680 | copy(h.Options, dup) 681 | return &ProxyRequireHeader{dup} 682 | } 683 | 684 | // 'Unsupported:' is a SIP header type - this doesn't indicate that the 685 | // header itself is not supported by gossip! 686 | type UnsupportedHeader struct { 687 | Options []string 688 | } 689 | 690 | func (header *UnsupportedHeader) String() string { 691 | return fmt.Sprintf("Unsupported: %s", 692 | strings.Join(header.Options, ", ")) 693 | } 694 | 695 | func (h *UnsupportedHeader) Name() string { return "Unsupported" } 696 | 697 | func (h *UnsupportedHeader) Copy() SipHeader { 698 | dup := make([]string, len(h.Options)) 699 | copy(h.Options, dup) 700 | return &UnsupportedHeader{dup} 701 | } 702 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | (This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.) 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | {description} 474 | Copyright (C) {year} {fullname} 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | {signature of Ty Coon}, 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/stefankopieczek/gossip/base" 5 | "github.com/stefankopieczek/gossip/log" 6 | "github.com/stefankopieczek/gossip/utils" 7 | ) 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "strconv" 13 | "strings" 14 | "unicode" 15 | "unicode/utf8" 16 | ) 17 | 18 | // The whitespace characters recognised by the Augmented Backus-Naur Form syntax 19 | // that SIP uses (RFC 3261 S.25). 20 | const c_ABNF_WS = " \t" 21 | 22 | // The maximum permissible CSeq number in a SIP message (2**31 - 1). 23 | // C.f. RFC 3261 S. 8.1.1.5. 24 | const MAX_CSEQ = 2147483647 25 | 26 | // The buffer size of the parser input channel. 27 | const c_INPUT_CHAN_SIZE = 10 28 | 29 | // A Parser converts the raw bytes of SIP messages into base.SipMessage objects. 30 | // It allows 31 | type Parser interface { 32 | // Implements io.Writer. Queues the given bytes to be parsed. 33 | // If the parser has terminated due to a previous fatal error, it will return n=0 and an appropriate error. 34 | // Otherwise, it will return n=len(p) and err=nil. 35 | // Note that err=nil does not indicate that the data provided is valid - simply that the data was successfully queued for parsing. 36 | Write(p []byte) (n int, err error) 37 | 38 | // Register a custom header parser for a particular header type. 39 | // This will overwrite any existing registered parser for that header type. 40 | // If a parser is not available for a header type in a message, the parser will produce a base.GenericHeader struct. 41 | SetHeaderParser(headerName string, headerParser HeaderParser) 42 | 43 | Stop() 44 | } 45 | 46 | // A HeaderParser is any function that turns raw header data into one or more SipHeader objects. 47 | // The HeaderParser will receive arguments of the form ("max-forwards", "70"). 48 | // It should return a slice of headers, which should have length > 1 unless it also returns an error. 49 | type HeaderParser func(headerName string, headerData string) ( 50 | headers []base.SipHeader, err error) 51 | 52 | func defaultHeaderParsers() map[string]HeaderParser { 53 | return map[string]HeaderParser{ 54 | "to": parseAddressHeader, 55 | "t": parseAddressHeader, 56 | "from": parseAddressHeader, 57 | "f": parseAddressHeader, 58 | "contact": parseAddressHeader, 59 | "m": parseAddressHeader, 60 | "call-id": parseCallId, 61 | "cseq": parseCSeq, 62 | "via": parseViaHeader, 63 | "v": parseViaHeader, 64 | "max-forwards": parseMaxForwards, 65 | "content-length": parseContentLength, 66 | "l": parseContentLength, 67 | } 68 | } 69 | 70 | // Parse a SIP message by creating a parser on the fly. 71 | // This is more costly than reusing a parser, but is necessary when we do not 72 | // have a guarantee that all messages coming over a connection are from the 73 | // same endpoint (e.g. UDP). 74 | func ParseMessage(msgData []byte) (base.SipMessage, error) { 75 | output := make(chan base.SipMessage, 0) 76 | errors := make(chan error, 0) 77 | parser := NewParser(output, errors, false) 78 | defer parser.Stop() 79 | 80 | parser.Write(msgData) 81 | select { 82 | case msg := <-output: 83 | return msg, nil 84 | case err := <-errors: 85 | return nil, err 86 | } 87 | } 88 | 89 | // Create a new Parser. 90 | // 91 | // Parsed SIP messages will be sent down the 'output' chan provided. 92 | // Any errors which force the parser to terminate will be sent down the 'errs' chan provided. 93 | // 94 | // If streamed=false, each Write call to the parser should contain data for one complete SIP message. 95 | 96 | // If streamed=true, Write calls can contain a portion of a full SIP message. 97 | // The end of one message and the start of the next may be provided in a single call to Write. 98 | // When streamed=true, all SIP messages provided must have a Content-Length header. 99 | // SIP messages without a Content-Length will cause the parser to permanently stop, and will result in an error on the errs chan. 100 | 101 | // 'streamed' should be set to true whenever the caller cannot reliably identify the starts and ends of messages from the transport frames, 102 | // e.g. when using streamed protocols such as TCP. 103 | func NewParser(output chan<- base.SipMessage, errs chan<- error, streamed bool) Parser { 104 | p := parser{streamed: streamed} 105 | 106 | // Configure the parser with the standard set of header parsers. 107 | p.headerParsers = make(map[string]HeaderParser) 108 | for headerName, headerParser := range defaultHeaderParsers() { 109 | p.SetHeaderParser(headerName, headerParser) 110 | } 111 | 112 | p.output = output 113 | p.errs = errs 114 | 115 | if !streamed { 116 | // If we're not in streaming mode, set up a channel so the Write method can pass calculated body lengths to the parser. 117 | p.bodyLengths.Init() 118 | } 119 | 120 | // Create a managed buffer to allow message data to be asynchronously provided to the parser, and 121 | // to allow the parser to block until enough data is available to parse. 122 | p.input = newParserBuffer() 123 | 124 | // Wait for input a line at a time, and produce SipMessages to send down p.output. 125 | go p.parse(streamed) 126 | 127 | return &p 128 | } 129 | 130 | type parser struct { 131 | headerParsers map[string]HeaderParser 132 | streamed bool 133 | input *parserBuffer 134 | bodyLengths utils.ElasticChan 135 | output chan<- base.SipMessage 136 | errs chan<- error 137 | terminalErr error 138 | stopped bool 139 | } 140 | 141 | func (p *parser) Write(data []byte) (n int, err error) { 142 | if p.terminalErr != nil { 143 | // The parser has stopped due to a terminal error. Return it. 144 | log.Fine("Parser %p ignores %d new bytes due to previous terminal error: %s", p, len(data), p.terminalErr.Error()) 145 | return 0, p.terminalErr 146 | } else if p.stopped { 147 | return 0, fmt.Errorf("Cannot write data to stopped parser %p", p) 148 | } 149 | 150 | if !p.streamed { 151 | l := getBodyLength(data) 152 | p.bodyLengths.In <- l 153 | } 154 | 155 | p.input.Write(data) 156 | return len(data), nil 157 | } 158 | 159 | // Stop parser processing, and allow all resources to be garbage collected. 160 | // The parser will not release its resources until Stop() is called, 161 | // even if the parser object itself is garbage collected. 162 | func (p *parser) Stop() { 163 | log.Debug("Stopping parser %p", p) 164 | p.stopped = true 165 | p.input.Stop() 166 | log.Debug("Parser %p stopped", p) 167 | } 168 | 169 | // Consume input lines one at a time, producing base.SipMessage objects and sending them down p.output. 170 | func (p *parser) parse(requireContentLength bool) { 171 | var message base.SipMessage 172 | 173 | for { 174 | // Parse the StartLine. 175 | startLine, err := p.input.NextLine() 176 | 177 | if err != nil { 178 | log.Debug("Parser %p stopped", p) 179 | break 180 | } 181 | 182 | if isRequest(startLine) { 183 | method, recipient, sipVersion, err := parseRequestLine(startLine) 184 | message = base.NewRequest(method, recipient, sipVersion, []base.SipHeader{}, "") 185 | p.terminalErr = err 186 | } else if isResponse(startLine) { 187 | sipVersion, statusCode, reason, err := parseStatusLine(startLine) 188 | message = base.NewResponse(sipVersion, statusCode, reason, []base.SipHeader{}, "") 189 | p.terminalErr = err 190 | } else { 191 | p.terminalErr = fmt.Errorf("transmission beginning '%s' is not a SIP message", startLine) 192 | } 193 | 194 | if p.terminalErr != nil { 195 | p.terminalErr = fmt.Errorf("failed to parse first line of message: %s", p.terminalErr.Error()) 196 | p.errs <- p.terminalErr 197 | break 198 | } 199 | 200 | // Parse the header section. 201 | // Headers can be split across lines (marked by whitespace at the start of subsequent lines), 202 | // so store lines into a buffer, and then flush and parse it when we hit the end of the header. 203 | var buffer bytes.Buffer 204 | headers := make([]base.SipHeader, 0) 205 | 206 | flushBuffer := func() { 207 | if buffer.Len() > 0 { 208 | newHeaders, err := p.parseHeader(buffer.String()) 209 | if err == nil { 210 | headers = append(headers, newHeaders...) 211 | } else { 212 | log.Debug("Skipping header '%s' due to error: %s", buffer.String(), err.Error()) 213 | } 214 | buffer.Reset() 215 | } 216 | } 217 | 218 | for { 219 | line, err := p.input.NextLine() 220 | 221 | if err != nil { 222 | log.Debug("Parser %p stopped", p) 223 | break 224 | } 225 | 226 | if len(line) == 0 { 227 | // We've hit the end of the header section. 228 | // Parse anything remaining in the buffer, then break out. 229 | flushBuffer() 230 | break 231 | } 232 | 233 | if !strings.Contains(c_ABNF_WS, string(line[0])) { 234 | // This line starts a new header. 235 | // Parse anything currently in the buffer, then store the new header line in the buffer. 236 | flushBuffer() 237 | buffer.WriteString(line) 238 | } else if buffer.Len() > 0 { 239 | // This is a continuation line, so just add it to the buffer. 240 | buffer.WriteString(" ") 241 | buffer.WriteString(line) 242 | } else { 243 | // This is a continuation line, but also the first line of the whole header section. 244 | // Discard it and log. 245 | log.Debug("Discarded unexpected continuation line '%s' at start of header block in message '%s'", 246 | line, 247 | message.Short()) 248 | } 249 | } 250 | 251 | // Store the headers in the message object. 252 | for _, header := range headers { 253 | message.AddHeader(header) 254 | } 255 | 256 | var contentLength int 257 | 258 | // Determine the length of the body, so we know when to stop parsing this message. 259 | if p.streamed { 260 | // Use the content-length header to identify the end of the message. 261 | contentLengthHeaders := message.Headers("Content-Length") 262 | if len(contentLengthHeaders) == 0 { 263 | p.terminalErr = fmt.Errorf("Missing required content-length header on message %s", message.Short()) 264 | p.errs <- p.terminalErr 265 | break 266 | } else if len(contentLengthHeaders) > 1 { 267 | var errbuf bytes.Buffer 268 | errbuf.WriteString("Multiple content-length headers on message ") 269 | errbuf.WriteString(message.Short()) 270 | errbuf.WriteString(":\n") 271 | for _, header := range contentLengthHeaders { 272 | errbuf.WriteString("\t") 273 | errbuf.WriteString(header.String()) 274 | } 275 | p.terminalErr = fmt.Errorf(errbuf.String()) 276 | p.errs <- p.terminalErr 277 | break 278 | } 279 | 280 | contentLength = int(*(contentLengthHeaders[0].(*base.ContentLength))) 281 | } else { 282 | // We're not in streaming mode, so the Write method should have calculated the length of the body for us. 283 | contentLength = (<-p.bodyLengths.Out).(int) 284 | } 285 | 286 | // Extract the message body. 287 | body, err := p.input.NextChunk(contentLength) 288 | 289 | if err != nil { 290 | log.Debug("Parsed %p stopped", p) 291 | break 292 | } 293 | 294 | switch message.(type) { 295 | case *base.Request: 296 | message.(*base.Request).Body = body 297 | case *base.Response: 298 | message.(*base.Response).Body = body 299 | default: 300 | log.Severe("Internal error - message %s is neither a request type nor a response type", message.Short()) 301 | } 302 | p.output <- message 303 | } 304 | 305 | if !p.streamed { 306 | // We're in unstreamed mode, so we created a bodyLengths ElasticChan which 307 | // needs to be disposed. 308 | close(p.bodyLengths.In) 309 | } 310 | return 311 | } 312 | 313 | // Implements ParserFactory.SetHeaderParser. 314 | func (p *parser) SetHeaderParser(headerName string, headerParser HeaderParser) { 315 | headerName = strings.ToLower(headerName) 316 | p.headerParsers[headerName] = headerParser 317 | } 318 | 319 | // Calculate the size of a SIP message's body, given the entire contents of the message as a byte array. 320 | func getBodyLength(data []byte) int { 321 | s := string(data) 322 | 323 | // Body starts with first character following a double-CRLF. 324 | bodyStart := strings.Index(s, "\r\n\r\n") + 4 325 | 326 | return len(s) - bodyStart 327 | } 328 | 329 | // Heuristic to determine if the given transmission looks like a SIP request. 330 | // It is guaranteed that any RFC3261-compliant request will pass this test, 331 | // but invalid messages may not necessarily be rejected. 332 | func isRequest(startLine string) bool { 333 | // SIP request lines contain precisely two spaces. 334 | if strings.Count(startLine, " ") != 2 { 335 | return false 336 | } 337 | 338 | // Check that the version string starts with SIP. 339 | parts := strings.Split(startLine, " ") 340 | if len(parts) < 3 { 341 | return false 342 | } else if len(parts[2]) < 3 { 343 | return false 344 | } else { 345 | return strings.ToUpper(parts[2][:3]) == "SIP" 346 | } 347 | } 348 | 349 | // Heuristic to determine if the given transmission looks like a SIP response. 350 | // It is guaranteed that any RFC3261-compliant response will pass this test, 351 | // but invalid messages may not necessarily be rejected. 352 | func isResponse(startLine string) bool { 353 | // SIP status lines contain at least two spaces. 354 | if strings.Count(startLine, " ") < 2 { 355 | return false 356 | } 357 | 358 | // Check that the version string starts with SIP. 359 | parts := strings.Split(startLine, " ") 360 | if len(parts) < 3 { 361 | return false 362 | } else if len(parts[0]) < 3 { 363 | return false 364 | } else { 365 | return strings.ToUpper(parts[0][:3]) == "SIP" 366 | } 367 | } 368 | 369 | // Parse the first line of a SIP request, e.g: 370 | // INVITE bob@example.com SIP/2.0 371 | // REGISTER jane@telco.com SIP/1.0 372 | func parseRequestLine(requestLine string) ( 373 | method base.Method, recipient base.Uri, sipVersion string, err error) { 374 | parts := strings.Split(requestLine, " ") 375 | if len(parts) != 3 { 376 | err = fmt.Errorf("request line should have 2 spaces: '%s'", requestLine) 377 | return 378 | } 379 | 380 | method = base.Method(strings.ToUpper(parts[0])) 381 | recipient, err = ParseUri(parts[1]) 382 | sipVersion = parts[2] 383 | 384 | switch recipient.(type) { 385 | case *base.WildcardUri: 386 | err = fmt.Errorf("wildcard URI '*' not permitted in request line: '%s'", requestLine) 387 | } 388 | 389 | return 390 | } 391 | 392 | // Parse the first line of a SIP response, e.g: 393 | // SIP/2.0 200 OK 394 | // SIP/1.0 403 Forbidden 395 | func parseStatusLine(statusLine string) ( 396 | sipVersion string, statusCode uint16, reasonPhrase string, err error) { 397 | parts := strings.Split(statusLine, " ") 398 | if len(parts) < 3 { 399 | err = fmt.Errorf("status line has too few spaces: '%s'", statusLine) 400 | return 401 | } 402 | 403 | sipVersion = parts[0] 404 | statusCodeRaw, err := strconv.ParseUint(parts[1], 10, 16) 405 | statusCode = uint16(statusCodeRaw) 406 | reasonPhrase = strings.Join(parts[2:], "") 407 | 408 | return 409 | } 410 | 411 | // parseUri converts a string representation of a URI into a Uri object. 412 | // If the URI is malformed, or the URI schema is not recognised, an error is returned. 413 | // URIs have the general form of schema:address. 414 | func ParseUri(uriStr string) (uri base.Uri, err error) { 415 | if strings.TrimSpace(uriStr) == "*" { 416 | // Wildcard '*' URI used in the Contact headers of REGISTERs when unregistering. 417 | return base.WildcardUri{}, nil 418 | } 419 | 420 | colonIdx := strings.Index(uriStr, ":") 421 | if colonIdx == -1 { 422 | err = fmt.Errorf("no ':' in URI %s", uriStr) 423 | return 424 | } 425 | 426 | switch strings.ToLower(uriStr[:colonIdx]) { 427 | case "sip": 428 | var sipUri base.SipUri 429 | sipUri, err = ParseSipUri(uriStr) 430 | uri = &sipUri 431 | case "sips": 432 | // SIPS URIs have the same form as SIP uris, so we use the same parser. 433 | var sipUri base.SipUri 434 | sipUri, err = ParseSipUri(uriStr) 435 | uri = &sipUri 436 | default: 437 | err = fmt.Errorf("Unsupported URI schema %s", uriStr[:colonIdx]) 438 | } 439 | 440 | return 441 | } 442 | 443 | // ParseSipUri converts a string representation of a SIP or SIPS URI into a SipUri object. 444 | func ParseSipUri(uriStr string) (uri base.SipUri, err error) { 445 | // Store off the original URI in case we need to print it in an error. 446 | uriStrCopy := uriStr 447 | 448 | // URI should start 'sip' or 'sips'. Check the first 3 chars. 449 | if strings.ToLower(uriStr[:3]) != "sip" { 450 | err = fmt.Errorf("invalid SIP uri protocol name in '%s'", uriStrCopy) 451 | return 452 | } 453 | uriStr = uriStr[3:] 454 | 455 | if strings.ToLower(uriStr[0:1]) == "s" { 456 | // URI started 'sips', so it's encrypted. 457 | uri.IsEncrypted = true 458 | uriStr = uriStr[1:] 459 | } 460 | 461 | // The 'sip' or 'sips' protocol name should be followed by a ':' character. 462 | if uriStr[0] != ':' { 463 | err = fmt.Errorf("no ':' after protocol name in SIP uri '%s'", uriStrCopy) 464 | return 465 | } 466 | uriStr = uriStr[1:] 467 | 468 | // SIP URIs may contain a user-info part, ending in a '@'. 469 | // This is the only place '@' may occur, so we can use it to check for the 470 | // existence of a user-info part. 471 | uri.User = base.NoString{} 472 | uri.Password = base.NoString{} 473 | endOfUserInfoPart := strings.Index(uriStr, "@") 474 | if endOfUserInfoPart != -1 { 475 | // A user-info part is present. These take the form: 476 | // user [ ":" password ] "@" 477 | endOfUsernamePart := strings.Index(uriStr, ":") 478 | if endOfUsernamePart > endOfUserInfoPart { 479 | endOfUsernamePart = -1 480 | } 481 | 482 | if endOfUsernamePart == -1 { 483 | // No password component; the whole of the user-info part before 484 | // the '@' is a username. 485 | user := uriStr[:endOfUserInfoPart] 486 | uri.User = base.String{user} 487 | } else { 488 | user := uriStr[:endOfUsernamePart] 489 | pwd := uriStr[endOfUsernamePart+1 : endOfUserInfoPart] 490 | uri.User = base.String{user} 491 | uri.Password = base.String{pwd} 492 | } 493 | uriStr = uriStr[endOfUserInfoPart+1:] 494 | } 495 | 496 | // A ';' indicates the beginning of a URI params section, and the end of the URI itself. 497 | endOfUriPart := strings.Index(uriStr, ";") 498 | if endOfUriPart == -1 { 499 | // There are no URI parameters, but there might be header parameters (introduced by '?'). 500 | endOfUriPart = strings.Index(uriStr, "?") 501 | } 502 | if endOfUriPart == -1 { 503 | // There are no parameters at all. The URI ends after the host[:port] part. 504 | endOfUriPart = len(uriStr) 505 | } 506 | 507 | uri.Host, uri.Port, err = parseHostPort(uriStr[:endOfUriPart]) 508 | uriStr = uriStr[endOfUriPart:] 509 | if err != nil { 510 | return 511 | } else if len(uriStr) == 0 { 512 | uri.UriParams = base.NewParams() 513 | uri.Headers = base.NewParams() 514 | return 515 | } 516 | 517 | // Now parse any URI parameters. 518 | // These are key-value pairs separated by ';'. 519 | // They end at the end of the URI, or at the start of any URI headers 520 | // which may be present (denoted by an initial '?'). 521 | var uriParams base.Params 522 | var n int 523 | if uriStr[0] == ';' { 524 | uriParams, n, err = parseParams(uriStr, ';', ';', '?', true, true) 525 | if err != nil { 526 | return 527 | } 528 | } else { 529 | uriParams, n = base.NewParams(), 0 530 | } 531 | uri.UriParams = uriParams 532 | uriStr = uriStr[n:] 533 | 534 | // Finally parse any URI headers. 535 | // These are key-value pairs, starting with a '?' and separated by '&'. 536 | var headers base.Params 537 | headers, n, err = parseParams(uriStr, '?', '&', 0, true, false) 538 | if err != nil { 539 | return 540 | } 541 | uri.Headers = headers 542 | uriStr = uriStr[n:] 543 | if len(uriStr) > 0 { 544 | err = fmt.Errorf("internal error: parse of SIP uri ended early! '%s'", 545 | uriStrCopy) 546 | return // Defensive return 547 | } 548 | 549 | return 550 | } 551 | 552 | // Parse a text representation of a host[:port] pair. 553 | // The port may or may not be present, so we represent it with a *uint16, 554 | // and return 'nil' if no port was present. 555 | func parseHostPort(rawText string) (host string, port *uint16, err error) { 556 | colonIdx := strings.Index(rawText, ":") 557 | if colonIdx == -1 { 558 | host = rawText 559 | return 560 | } 561 | 562 | // Surely there must be a better way..! 563 | var portRaw64 uint64 564 | var portRaw16 uint16 565 | host = rawText[:colonIdx] 566 | portRaw64, err = strconv.ParseUint(rawText[colonIdx+1:], 10, 16) 567 | portRaw16 = uint16(portRaw64) 568 | port = &portRaw16 569 | 570 | return 571 | } 572 | 573 | // General utility method for parsing 'key=value' parameters. 574 | // Takes a string (source), ensures that it begins with the 'start' character provided, 575 | // and then parses successive key/value pairs separated with 'sep', 576 | // until either 'end' is reached or there are no characters remaining. 577 | // A map of keys to values will be returned, along with the number of characters consumed. 578 | // Provide 0 for start or end to indicate that there is no starting/ending delimiter. 579 | // If quoteValues is true, values can be enclosed in double-quotes which will be validated by the 580 | // parser and omitted from the returned map. 581 | // If permitSingletons is true, keys with no values are permitted. 582 | // These will result in a nil value in the returned map. 583 | func parseParams(source string, 584 | start uint8, sep uint8, end uint8, 585 | quoteValues bool, permitSingletons bool) ( 586 | params base.Params, consumed int, err error) { 587 | 588 | params = base.NewParams() 589 | 590 | if len(source) == 0 { 591 | // Key-value section is completely empty; return defaults. 592 | return 593 | } 594 | 595 | // Ensure the starting character is correct. 596 | if start != 0 { 597 | if source[0] != start { 598 | err = fmt.Errorf("expected %c at start of key-value section; got %c. section was %s", 599 | start, source[0], source) 600 | return 601 | } 602 | consumed++ 603 | } 604 | 605 | // Statefully parse the given string one character at a time. 606 | var buffer bytes.Buffer 607 | var key string 608 | parsingKey := true // false implies we are parsing a value 609 | inQuotes := false 610 | parseLoop: 611 | for ; consumed < len(source); consumed++ { 612 | switch source[consumed] { 613 | case end: 614 | if inQuotes { 615 | // We read an end character, but since we're inside quotations we should 616 | // treat it as a literal part of the value. 617 | buffer.WriteString(string(end)) 618 | continue 619 | } 620 | 621 | break parseLoop 622 | 623 | case sep: 624 | if inQuotes { 625 | // We read a separator character, but since we're inside quotations 626 | // we should treat it as a literal part of the value. 627 | buffer.WriteString(string(sep)) 628 | continue 629 | } 630 | if parsingKey && permitSingletons { 631 | params.Add(buffer.String(), base.NoString{}) 632 | } else if parsingKey { 633 | err = fmt.Errorf("Singleton param '%s' when parsing params which disallow singletons: \"%s\"", 634 | buffer.String(), source) 635 | return 636 | } else { 637 | value := buffer.String() 638 | params.Add(key, base.String{value}) 639 | } 640 | buffer.Reset() 641 | parsingKey = true 642 | 643 | case '"': 644 | if !quoteValues { 645 | // We hit a quote character, but since quoting is turned off we treat it as a literal. 646 | buffer.WriteString("\"") 647 | continue 648 | } 649 | 650 | if parsingKey { 651 | // Quotes are never allowed in keys. 652 | err = fmt.Errorf("Unexpected '\"' in parameter key in params \"%s\"", source) 653 | return 654 | } 655 | 656 | if !inQuotes && buffer.Len() != 0 { 657 | // We hit an initial quote midway through a value; that's not allowed. 658 | err = fmt.Errorf("unexpected '\"' in params \"%s\"", source) 659 | return 660 | } 661 | 662 | if inQuotes && 663 | consumed != len(source)-1 && 664 | source[consumed+1] != sep { 665 | // We hit an end-quote midway through a value; that's not allowed. 666 | err = fmt.Errorf("unexpected character %c after quoted param in \"%s\"", 667 | source[consumed+1], source) 668 | 669 | return 670 | } 671 | 672 | inQuotes = !inQuotes 673 | 674 | case '=': 675 | if buffer.Len() == 0 { 676 | err = fmt.Errorf("Key of length 0 in params \"%s\"", source) 677 | return 678 | } 679 | if !parsingKey { 680 | err = fmt.Errorf("Unexpected '=' char in value token: \"%s\"", source) 681 | return 682 | } 683 | key = buffer.String() 684 | buffer.Reset() 685 | parsingKey = false 686 | 687 | default: 688 | if !inQuotes && strings.Contains(c_ABNF_WS, string(source[consumed])) { 689 | // Skip unquoted whitespace. 690 | continue 691 | } 692 | 693 | buffer.WriteString(string(source[consumed])) 694 | } 695 | } 696 | 697 | // The param string has ended. Check that it ended in a valid place, and then store off the 698 | // contents of the buffer. 699 | if inQuotes { 700 | err = fmt.Errorf("Unclosed quotes in parameter string: %s", source) 701 | } else if parsingKey && permitSingletons { 702 | params.Add(buffer.String(), base.NoString{}) 703 | } else if parsingKey { 704 | err = fmt.Errorf("Singleton param '%s' when parsing params which disallow singletons: \"%s\"", 705 | buffer.String(), source) 706 | } else { 707 | value := buffer.String() 708 | params.Add(key, base.String{value}) 709 | } 710 | return 711 | } 712 | 713 | // Parse a header string, producing one or more SipHeader objects. 714 | // (SIP messages containing multiple headers of the same type can express them as a 715 | // single header containing a comma-separated argument list). 716 | func (p *parser) parseHeader(headerText string) (headers []base.SipHeader, err error) { 717 | log.Debug("Parser %p parsing header \"%s\"", p, headerText) 718 | headers = make([]base.SipHeader, 0) 719 | 720 | colonIdx := strings.Index(headerText, ":") 721 | if colonIdx == -1 { 722 | err = fmt.Errorf("Field name with no value in header: %s", headerText) 723 | return 724 | } 725 | 726 | fieldName := strings.TrimSpace(headerText[:colonIdx]) 727 | lowerFieldName := strings.ToLower(fieldName) 728 | fieldText := strings.TrimSpace(headerText[colonIdx+1:]) 729 | if headerParser, ok := p.headerParsers[lowerFieldName]; ok { 730 | // We have a registered parser for this header type - use it. 731 | headers, err = headerParser(lowerFieldName, fieldText) 732 | } else { 733 | // We have no registered parser for this header type, 734 | // so we encapsulate the header data in a GenericHeader struct. 735 | log.Debug("Parser %p has no parser for header type %s", p, fieldName) 736 | header := base.GenericHeader{fieldName, fieldText} 737 | headers = []base.SipHeader{&header} 738 | } 739 | 740 | return 741 | } 742 | 743 | // Parse a To, From or Contact header line, producing one or more logical SipHeaders. 744 | func parseAddressHeader(headerName string, headerText string) ( 745 | headers []base.SipHeader, err error) { 746 | switch headerName { 747 | case "to", "from", "contact", "t", "f", "m": 748 | var displayNames []base.MaybeString 749 | var uris []base.Uri 750 | var paramSets []base.Params 751 | 752 | // Perform the actual parsing. The rest of this method is just typeclass bookkeeping. 753 | displayNames, uris, paramSets, err = parseAddressValues(headerText) 754 | 755 | if err != nil { 756 | return 757 | } 758 | if len(displayNames) != len(uris) || len(uris) != len(paramSets) { 759 | // This shouldn't happen unless parseAddressValues is bugged. 760 | err = fmt.Errorf("internal parser error: parsed param mismatch. "+ 761 | "%d display names, %d uris and %d param sets "+ 762 | "in %s.", 763 | len(displayNames), len(uris), len(paramSets), 764 | headerText) 765 | return 766 | } 767 | 768 | // Build a slice of headers of the appropriate kind, populating them with the values parsed above. 769 | // It is assumed that all headers returned by parseAddressValues are of the same kind, 770 | // although we do not check for this below. 771 | for idx := 0; idx < len(displayNames); idx++ { 772 | var header base.SipHeader 773 | if headerName == "to" || headerName == "t" { 774 | if idx > 0 { 775 | // Only a single To header is permitted in a SIP message. 776 | return nil, 777 | fmt.Errorf("Multiple to: headers in message:\n%s: %s", 778 | headerName, headerText) 779 | } 780 | switch uris[idx].(type) { 781 | case base.WildcardUri: 782 | // The Wildcard '*' URI is only permitted in Contact headers. 783 | err = fmt.Errorf("wildcard uri not permitted in to: "+ 784 | "header: %s", headerText) 785 | return 786 | default: 787 | toHeader := base.ToHeader{displayNames[idx], 788 | uris[idx], 789 | paramSets[idx]} 790 | header = &toHeader 791 | } 792 | } else if headerName == "from" || headerName == "f" { 793 | if idx > 0 { 794 | // Only a single From header is permitted in a SIP message. 795 | return nil, 796 | fmt.Errorf("Multiple from: headers in message:\n%s: %s", 797 | headerName, headerText) 798 | } 799 | switch uris[idx].(type) { 800 | case base.WildcardUri: 801 | // The Wildcard '*' URI is only permitted in Contact headers. 802 | err = fmt.Errorf("wildcard uri not permitted in from: "+ 803 | "header: %s", headerText) 804 | return 805 | default: 806 | fromHeader := base.FromHeader{displayNames[idx], 807 | uris[idx], 808 | paramSets[idx]} 809 | header = &fromHeader 810 | } 811 | } else if headerName == "contact" || headerName == "m" { 812 | switch uris[idx].(type) { 813 | case base.ContactUri: 814 | if uris[idx].(base.ContactUri).IsWildcard() { 815 | if paramSets[idx].Length() > 0 { 816 | // Wildcard headers do not contain parameters. 817 | err = fmt.Errorf("wildcard contact header should contain no parameters: '%s", 818 | headerText) 819 | return 820 | } 821 | if _, ok := displayNames[idx].(base.String); ok { 822 | // Wildcard headers do not contain display names. 823 | err = fmt.Errorf("wildcard contact header should contain no display name %s", 824 | headerText) 825 | return 826 | } 827 | } 828 | contactHeader := base.ContactHeader{displayNames[idx], 829 | uris[idx].(base.ContactUri), 830 | paramSets[idx]} 831 | header = &contactHeader 832 | default: 833 | // URIs in contact headers are restricted to being either SIP URIs or 'Contact: *'. 834 | return nil, 835 | fmt.Errorf("Uri %s not valid in Contact header. Must be SIP uri or '*'", uris[idx].String()) 836 | } 837 | } 838 | 839 | headers = append(headers, header) 840 | } 841 | } 842 | 843 | return 844 | } 845 | 846 | // Parse a string representation of a CSeq header, returning a slice of at most one CSeq. 847 | func parseCSeq(headerName string, headerText string) ( 848 | headers []base.SipHeader, err error) { 849 | var cseq base.CSeq 850 | 851 | parts := splitByWhitespace(headerText) 852 | if len(parts) != 2 { 853 | err = fmt.Errorf("CSeq field should have precisely one whitespace section: '%s'", 854 | headerText) 855 | return 856 | } 857 | 858 | var seqno uint64 859 | seqno, err = strconv.ParseUint(parts[0], 10, 32) 860 | if err != nil { 861 | return 862 | } 863 | 864 | if seqno > MAX_CSEQ { 865 | err = fmt.Errorf("invalid CSeq %d: exceeds maximum permitted value "+ 866 | "2**31 - 1", seqno) 867 | return 868 | } 869 | 870 | cseq.SeqNo = uint32(seqno) 871 | cseq.MethodName = base.Method(strings.TrimSpace(parts[1])) 872 | 873 | if strings.Contains(string(cseq.MethodName), ";") { 874 | err = fmt.Errorf("unexpected ';' in CSeq body: %s", headerText) 875 | return 876 | } 877 | 878 | headers = []base.SipHeader{&cseq} 879 | 880 | return 881 | } 882 | 883 | // Parse a string representation of a Call-Id header, returning a slice of at most one CallId. 884 | func parseCallId(headerName string, headerText string) ( 885 | headers []base.SipHeader, err error) { 886 | headerText = strings.TrimSpace(headerText) 887 | var callId base.CallId = base.CallId(headerText) 888 | 889 | if strings.ContainsAny(string(callId), c_ABNF_WS) { 890 | err = fmt.Errorf("unexpected whitespace in CallId header body '%s'", headerText) 891 | return 892 | } 893 | if strings.Contains(string(callId), ";") { 894 | err = fmt.Errorf("unexpected semicolon in CallId header body '%s'", headerText) 895 | return 896 | } 897 | if len(string(callId)) == 0 { 898 | err = fmt.Errorf("empty Call-Id body") 899 | return 900 | } 901 | 902 | headers = []base.SipHeader{&callId} 903 | 904 | return 905 | } 906 | 907 | // Parse a string representation of a Via header, returning a slice of at most one ViaHeader. 908 | // Note that although Via headers may contain a comma-separated list, RFC 3261 makes it clear that 909 | // these should not be treated as separate logical Via headers, but as multiple values on a single 910 | // Via header. 911 | func parseViaHeader(headerName string, headerText string) ( 912 | headers []base.SipHeader, err error) { 913 | sections := strings.Split(headerText, ",") 914 | var via base.ViaHeader = base.ViaHeader{} 915 | for _, section := range sections { 916 | var hop base.ViaHop 917 | parts := strings.Split(section, "/") 918 | 919 | if len(parts) < 3 { 920 | err = fmt.Errorf("not enough protocol parts in via header: '%s'", 921 | parts) 922 | return 923 | } 924 | 925 | parts[2] = strings.Join(parts[2:], "/") 926 | 927 | // The transport part ends when whitespace is reached, but may also start with 928 | // whitespace. 929 | // So the end of the transport part is the first whitespace char following the 930 | // first non-whitespace char. 931 | initialSpaces := len(parts[2]) - len(strings.TrimLeft(parts[2], c_ABNF_WS)) 932 | sentByIdx := strings.IndexAny(parts[2][initialSpaces:], c_ABNF_WS) + initialSpaces + 1 933 | if sentByIdx == 0 { 934 | err = fmt.Errorf("expected whitespace after sent-protocol part "+ 935 | "in via header '%s'", section) 936 | return 937 | } else if sentByIdx == 1 { 938 | err = fmt.Errorf("empty transport field in via header '%s'", section) 939 | return 940 | } 941 | 942 | hop.ProtocolName = strings.TrimSpace(parts[0]) 943 | hop.ProtocolVersion = strings.TrimSpace(parts[1]) 944 | hop.Transport = strings.TrimSpace(parts[2][:sentByIdx-1]) 945 | 946 | if len(hop.ProtocolName) == 0 { 947 | err = fmt.Errorf("no protocol name provided in via header '%s'", section) 948 | } else if len(hop.ProtocolVersion) == 0 { 949 | err = fmt.Errorf("no version provided in via header '%s'", section) 950 | } else if len(hop.Transport) == 0 { 951 | err = fmt.Errorf("no transport provided in via header '%s'", section) 952 | } 953 | if err != nil { 954 | return 955 | } 956 | 957 | viaBody := parts[2][sentByIdx:] 958 | 959 | paramsIdx := strings.Index(viaBody, ";") 960 | var host string 961 | var port *uint16 962 | if paramsIdx == -1 { 963 | // There are no header parameters, so the rest of the Via body is part of the host[:post]. 964 | host, port, err = parseHostPort(viaBody) 965 | hop.Host = host 966 | hop.Port = port 967 | if err != nil { 968 | return 969 | } 970 | hop.Params = base.NewParams() 971 | } else { 972 | host, port, err = parseHostPort(viaBody[:paramsIdx]) 973 | if err != nil { 974 | return 975 | } 976 | hop.Host = host 977 | hop.Port = port 978 | 979 | hop.Params, _, err = parseParams(viaBody[paramsIdx:], 980 | ';', ';', 0, true, true) 981 | } 982 | via = append(via, &hop) 983 | } 984 | 985 | headers = []base.SipHeader{&via} 986 | return 987 | } 988 | 989 | // Parse a string representation of a Max-Forwards header into a slice of at most one MaxForwards header object. 990 | func parseMaxForwards(headerName string, headerText string) ( 991 | headers []base.SipHeader, err error) { 992 | var maxForwards base.MaxForwards 993 | var value uint64 994 | value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32) 995 | maxForwards = base.MaxForwards(value) 996 | 997 | headers = []base.SipHeader{&maxForwards} 998 | return 999 | } 1000 | 1001 | // Parse a string representation of a Content-Length header into a slice of at most one ContentLength header object. 1002 | func parseContentLength(headerName string, headerText string) ( 1003 | headers []base.SipHeader, err error) { 1004 | var contentLength base.ContentLength 1005 | var value uint64 1006 | value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32) 1007 | contentLength = base.ContentLength(value) 1008 | 1009 | headers = []base.SipHeader{&contentLength} 1010 | return 1011 | } 1012 | 1013 | // parseAddressValues parses a comma-separated list of addresses, returning 1014 | // any display names and header params, as well as the SIP URIs themselves. 1015 | // parseAddressValues is aware of < > bracketing and quoting, and will not 1016 | // break on commas within these structures. 1017 | func parseAddressValues(addresses string) ( 1018 | displayNames []base.MaybeString, uris []base.Uri, 1019 | headerParams []base.Params, err error) { 1020 | 1021 | prevIdx := 0 1022 | inBrackets := false 1023 | inQuotes := false 1024 | 1025 | // Append a comma to simplify the parsing code; we split address sections 1026 | // on commas, so use a comma to signify the end of the final address section. 1027 | addresses = addresses + "," 1028 | 1029 | for idx, char := range addresses { 1030 | if char == '<' && !inQuotes { 1031 | inBrackets = true 1032 | } else if char == '>' && !inQuotes { 1033 | inBrackets = false 1034 | } else if char == '"' { 1035 | inQuotes = !inQuotes 1036 | } else if !inQuotes && !inBrackets && char == ',' { 1037 | var displayName base.MaybeString 1038 | var uri base.Uri 1039 | var params base.Params 1040 | displayName, uri, params, err = 1041 | parseAddressValue(addresses[prevIdx:idx]) 1042 | if err != nil { 1043 | return 1044 | } 1045 | prevIdx = idx + 1 1046 | 1047 | displayNames = append(displayNames, displayName) 1048 | uris = append(uris, uri) 1049 | headerParams = append(headerParams, params) 1050 | } 1051 | } 1052 | 1053 | return 1054 | } 1055 | 1056 | // parseAddressValue parses an address - such as from a From, To, or 1057 | // Contact header. It returns: 1058 | // - a MaybeString containing the display name (or not) 1059 | // - a parsed SipUri object 1060 | // - a map containing any header parameters present 1061 | // - the error object 1062 | // See RFC 3261 section 20.10 for details on parsing an address. 1063 | // Note that this method will not accept a comma-separated list of addresses; 1064 | // addresses in that form should be handled by parseAddressValues. 1065 | func parseAddressValue(addressText string) ( 1066 | displayName base.MaybeString, uri base.Uri, 1067 | headerParams base.Params, err error) { 1068 | 1069 | headerParams = base.NewParams() 1070 | 1071 | if len(addressText) == 0 { 1072 | err = fmt.Errorf("address-type header has empty body") 1073 | return 1074 | } 1075 | 1076 | addressTextCopy := addressText 1077 | addressText = strings.TrimSpace(addressText) 1078 | 1079 | firstAngleBracket := findUnescaped(addressText, '<', quotes_delim) 1080 | displayName = base.NoString{} 1081 | if firstAngleBracket > 0 { 1082 | // We have an angle bracket, and it's not the first character. 1083 | // Since we have just trimmed whitespace, this means there must 1084 | // be a display name. 1085 | if addressText[0] == '"' { 1086 | // The display name is within quotations. 1087 | // So it is comprised of all text until the closing quote. 1088 | addressText = addressText[1:] 1089 | nextQuote := strings.Index(addressText, "\"") 1090 | 1091 | if nextQuote == -1 { 1092 | // Unclosed quotes - parse error. 1093 | err = fmt.Errorf("Unclosed quotes in header text: %s", 1094 | addressTextCopy) 1095 | return 1096 | } 1097 | 1098 | nameField := addressText[:nextQuote] 1099 | displayName = base.String{nameField} 1100 | addressText = addressText[nextQuote+1:] 1101 | } else { 1102 | // The display name is unquoted, so it is comprised of 1103 | // all text until the opening angle bracket, except surrounding whitespace. 1104 | // According to the ABNF grammar: display-name = *(token LWS)/ quoted-string 1105 | // there are certain characters the display name cannot contain unless it's quoted, 1106 | // however we don't check for them here since it doesn't impact parsing. 1107 | // May as well be lenient. 1108 | nameField := addressText[:firstAngleBracket] 1109 | displayName = base.String{strings.TrimSpace(nameField)} 1110 | addressText = addressText[firstAngleBracket:] 1111 | } 1112 | } 1113 | 1114 | // Work out where the SIP URI starts and ends. 1115 | addressText = strings.TrimSpace(addressText) 1116 | var endOfUri int 1117 | var startOfParams int 1118 | if addressText[0] != '<' { 1119 | switch displayName.(type) { 1120 | case base.String: 1121 | // The address must be in if a display name is 1122 | // present, so this is an invalid address line. 1123 | err = fmt.Errorf("Invalid character '%c' following display "+ 1124 | "name in address line; expected '<': %s", 1125 | addressText[0], addressTextCopy) 1126 | return 1127 | } 1128 | 1129 | endOfUri = strings.Index(addressText, ";") 1130 | if endOfUri == -1 { 1131 | endOfUri = len(addressText) 1132 | } 1133 | startOfParams = endOfUri 1134 | 1135 | } else { 1136 | addressText = addressText[1:] 1137 | endOfUri = strings.Index(addressText, ">") 1138 | if endOfUri == 0 { 1139 | err = fmt.Errorf("'<' without closing '>' in address %s", 1140 | addressTextCopy) 1141 | return 1142 | } 1143 | startOfParams = endOfUri + 1 1144 | 1145 | } 1146 | 1147 | // Now parse the SIP URI. 1148 | uri, err = ParseUri(addressText[:endOfUri]) 1149 | if err != nil { 1150 | return 1151 | } 1152 | 1153 | if startOfParams >= len(addressText) { 1154 | return 1155 | } 1156 | 1157 | // Finally, parse any header parameters and then return. 1158 | addressText = addressText[startOfParams:] 1159 | headerParams, _, err = parseParams(addressText, ';', ';', ',', true, true) 1160 | return 1161 | } 1162 | 1163 | // Extract the next logical header line from the message. 1164 | // This may run over several actual lines; lines that start with whitespace are 1165 | // a continuation of the previous line. 1166 | // Therefore also return how many lines we consumed so the parent parser can 1167 | // keep track of progress through the message. 1168 | func getNextHeaderLine(contents []string) (headerText string, consumed int) { 1169 | if len(contents) == 0 { 1170 | return 1171 | } 1172 | if len(contents[0]) == 0 { 1173 | return 1174 | } 1175 | 1176 | var buffer bytes.Buffer 1177 | buffer.WriteString(contents[0]) 1178 | 1179 | for consumed = 1; consumed < len(contents); consumed++ { 1180 | firstChar, _ := utf8.DecodeRuneInString(contents[consumed]) 1181 | if !unicode.IsSpace(firstChar) { 1182 | break 1183 | } else if len(contents[consumed]) == 0 { 1184 | break 1185 | } 1186 | 1187 | buffer.WriteString(" " + strings.TrimSpace(contents[consumed])) 1188 | } 1189 | 1190 | headerText = buffer.String() 1191 | return 1192 | } 1193 | 1194 | // A delimiter is any pair of characters used for quoting text (i.e. bulk escaping literals). 1195 | type delimiter struct { 1196 | start uint8 1197 | end uint8 1198 | } 1199 | 1200 | // Define common quote characters needed in parsing. 1201 | var quotes_delim = delimiter{'"', '"'} 1202 | var angles_delim = delimiter{'<', '>'} 1203 | 1204 | // Find the first instance of the target in the given text which is not enclosed in any delimiters 1205 | // from the list provided. 1206 | func findUnescaped(text string, target uint8, delims ...delimiter) int { 1207 | return findAnyUnescaped(text, string(target), delims...) 1208 | } 1209 | 1210 | // Find the first instance of any of the targets in the given text that are not enclosed in any delimiters 1211 | // from the list provided. 1212 | func findAnyUnescaped(text string, targets string, delims ...delimiter) int { 1213 | escaped := false 1214 | var endEscape uint8 = 0 1215 | 1216 | endChars := make(map[uint8]uint8) 1217 | for _, delim := range delims { 1218 | endChars[delim.start] = delim.end 1219 | } 1220 | 1221 | for idx := 0; idx < len(text); idx++ { 1222 | if !escaped && strings.Contains(targets, string(text[idx])) { 1223 | return idx 1224 | } 1225 | 1226 | if escaped { 1227 | escaped = (text[idx] != endEscape) 1228 | continue 1229 | } else { 1230 | endEscape, escaped = endChars[text[idx]] 1231 | } 1232 | } 1233 | 1234 | return -1 1235 | } 1236 | 1237 | // Splits the given string into sections, separated by one or more characters 1238 | // from c_ABNF_WS. 1239 | func splitByWhitespace(text string) []string { 1240 | var buffer bytes.Buffer 1241 | var inString bool = true 1242 | result := make([]string, 0) 1243 | 1244 | for _, char := range text { 1245 | s := string(char) 1246 | if strings.Contains(c_ABNF_WS, s) { 1247 | if inString { 1248 | // First whitespace char following text; flush buffer to the results array. 1249 | result = append(result, buffer.String()) 1250 | buffer.Reset() 1251 | } 1252 | inString = false 1253 | } else { 1254 | buffer.WriteString(s) 1255 | inString = true 1256 | } 1257 | } 1258 | 1259 | if buffer.Len() > 0 { 1260 | result = append(result, buffer.String()) 1261 | } 1262 | 1263 | return result 1264 | } 1265 | --------------------------------------------------------------------------------