├── transport ├── transport_tls.go ├── transport_udp.go ├── transport_fv_test.go ├── transport_tcp.go ├── transport.go ├── connection.go ├── conntable_test.go └── conntable.go ├── transaction ├── server_test.go ├── manager_test.go ├── transaction.go ├── client_test.go ├── server.go ├── manager.go └── client.go ├── .gitignore ├── README.md ├── utils ├── utils.go ├── semaphore.go └── elasticchan.go ├── testutils └── dummyconn.go ├── parser ├── parserbuffer.go └── parser.go ├── timing └── timing.go ├── log └── log.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /transaction/manager_test.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import "testing" 4 | 5 | // Tests we can start/stop a transaction manager repeatedly on the same port. 6 | func TestStop(t *testing.T) { 7 | loops := 5 8 | for i := 0; i < loops; i++ { 9 | m, err := NewManager("udp", "localhost:12345") 10 | if err != nil { 11 | t.Fatalf("Failed to start manager on loop %v: %v\n", i, err) 12 | } 13 | 14 | m.Stop() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 struct { 18 | notifier 19 | transport transport 20 | } 21 | 22 | type transport interface { 23 | IsStreamed() bool 24 | Listen(address string) error 25 | Send(addr string, message base.SipMessage) error 26 | Stop() 27 | } 28 | 29 | func NewManager(transportType string) (manager *Manager, err error) { 30 | err = fmt.Errorf("Unknown transport type '%s'", transportType) 31 | 32 | var n notifier 33 | n.init() 34 | 35 | var transport transport 36 | switch strings.ToLower(transportType) { 37 | case "udp": 38 | transport, err = NewUdp(n.inputs) 39 | case "tcp": 40 | transport, err = NewTcp(n.inputs) 41 | case "tls": 42 | // TODO 43 | } 44 | 45 | if transport != nil && err == nil { 46 | manager = &Manager{notifier: n, transport: transport} 47 | } else { 48 | // Close the input chan in order to stop the notifier; this prevents 49 | // us leaking it. 50 | close(n.inputs) 51 | } 52 | 53 | return 54 | } 55 | 56 | func (manager *Manager) Listen(address string) error { 57 | return manager.transport.Listen(address) 58 | } 59 | 60 | func (manager *Manager) Send(addr string, message base.SipMessage) error { 61 | return manager.transport.Send(addr, message) 62 | } 63 | 64 | func (manager *Manager) Stop() { 65 | manager.transport.Stop() 66 | manager.notifier.stop() 67 | } 68 | 69 | type notifier struct { 70 | listeners map[listener]bool 71 | listenerLock sync.Mutex 72 | inputs chan base.SipMessage 73 | } 74 | 75 | func (n *notifier) init() { 76 | n.listeners = make(map[listener]bool) 77 | n.inputs = make(chan base.SipMessage) 78 | go n.forward() 79 | } 80 | 81 | func (n *notifier) register(l listener) { 82 | log.Debug("Notifier %p has new listener %p", n, l) 83 | if n.listeners == nil { 84 | n.listeners = make(map[listener]bool) 85 | } 86 | n.listenerLock.Lock() 87 | n.listeners[l] = true 88 | n.listenerLock.Unlock() 89 | } 90 | 91 | func (n *notifier) GetChannel() (l listener) { 92 | c := make(chan base.SipMessage, c_LISTENER_QUEUE_SIZE) 93 | n.register(c) 94 | return c 95 | } 96 | 97 | func (n *notifier) forward() { 98 | for msg := range n.inputs { 99 | deadListeners := make([]chan base.SipMessage, 0) 100 | n.listenerLock.Lock() 101 | log.Debug(fmt.Sprintf("Notify %d listeners of message", len(n.listeners))) 102 | for listener := range n.listeners { 103 | sent := listener.notify(msg) 104 | if !sent { 105 | deadListeners = append(deadListeners, listener) 106 | } 107 | } 108 | for _, deadListener := range deadListeners { 109 | log.Debug(fmt.Sprintf("Expiring listener %#v", deadListener)) 110 | delete(n.listeners, deadListener) 111 | } 112 | n.listenerLock.Unlock() 113 | } 114 | } 115 | 116 | func (n *notifier) stop() { 117 | n.listenerLock.Lock() 118 | for c, _ := range n.listeners { 119 | close(c) 120 | } 121 | n.listeners = nil 122 | n.listenerLock.Unlock() 123 | } 124 | 125 | type listener chan base.SipMessage 126 | 127 | // notify tries to send a message to the listener. 128 | // If the underlying channel has been closed by the receiver, return 'false'; 129 | // otherwise, return true. 130 | func (c listener) notify(message base.SipMessage) (ok bool) { 131 | defer func() { recover() }() 132 | c <- message 133 | return true 134 | } 135 | -------------------------------------------------------------------------------- /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/conntable_test.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | import ( 9 | "github.com/stefankopieczek/gossip/base" 10 | "github.com/stefankopieczek/gossip/log" 11 | "github.com/stefankopieczek/gossip/parser" 12 | "github.com/stefankopieczek/gossip/testutils" 13 | "github.com/stefankopieczek/gossip/timing" 14 | ) 15 | 16 | var c_LOG_LEVEL = log.WARN 17 | 18 | func TestAAAAA(t *testing.T) { 19 | timing.MockMode = true 20 | log.SetDefaultLogLevel(c_LOG_LEVEL) 21 | } 22 | 23 | // Test that we can store and retrieve a connection. 24 | func TestBasicStorage(t *testing.T) { 25 | var table connTable 26 | table.Init() 27 | defer table.Stop() 28 | 29 | conn := makeTestConn() 30 | table.Notify("foo", conn) 31 | 32 | if table.GetConn("foo") != conn { 33 | t.FailNow() 34 | } 35 | } 36 | 37 | // Test that after the configured expiry time, a stored connection expires and 38 | // is nulled out. 39 | func TestBasicExpiry(t *testing.T) { 40 | var table connTable 41 | table.Init() 42 | defer table.Stop() // This currently panics due to issue #13. 43 | 44 | table.Notify("bar", makeTestConn()) 45 | timing.Elapse(c_SOCKET_EXPIRY) 46 | timing.Elapse(time.Nanosecond) 47 | 48 | if table.GetConn("bar") != nil { 49 | t.FailNow() 50 | } 51 | } 52 | 53 | // Test that two different connections can be stored at the same time. 54 | func TestDoubleStorage(t *testing.T) { 55 | var table connTable 56 | table.Init() 57 | defer table.Stop() 58 | 59 | conn1 := makeTestConn() 60 | table.Notify("foo", conn1) 61 | conn2 := makeTestConn() 62 | table.Notify("bar", conn2) 63 | 64 | if table.GetConn("foo") != conn1 { 65 | t.FailNow() 66 | } else if table.GetConn("bar") != conn2 { 67 | t.FailNow() 68 | } 69 | } 70 | 71 | // Test that we can push an update to a stored connection, and that when we 72 | // retrieve the connection we get the updated value. 73 | func TestUpdate(t *testing.T) { 74 | var table connTable 75 | table.Init() 76 | defer table.Stop() 77 | table.Notify("foo", makeTestConn()) 78 | conn2 := makeTestConn() 79 | table.Notify("foo", conn2) 80 | 81 | if table.GetConn("foo") != conn2 { 82 | t.FailNow() 83 | } 84 | } 85 | 86 | // Test that if a connection is allowed to expire, we can store the same connection 87 | // again with the same key and retrieve it. 88 | func TestReuse1(t *testing.T) { 89 | var table connTable 90 | table.Init() 91 | defer table.Stop() // This currently panics due to issue #13. 92 | 93 | conn := makeTestConn() 94 | table.Notify("foo", conn) 95 | timing.Elapse(c_SOCKET_EXPIRY) 96 | timing.Elapse(time.Nanosecond) 97 | 98 | table.Notify("foo", conn) 99 | if table.GetConn("foo") != conn { 100 | t.FailNow() 101 | } 102 | } 103 | 104 | // Test that if a connection is allowed to expire, we can store a different connection 105 | // with the same key and then retrieve it. 106 | func TestReuse2(t *testing.T) { 107 | var table connTable 108 | table.Init() 109 | defer table.Stop() // This currently panics due to issue #13. 110 | 111 | table.Notify("foo", makeTestConn()) 112 | timing.Elapse(c_SOCKET_EXPIRY) 113 | timing.Elapse(time.Nanosecond) 114 | 115 | conn2 := makeTestConn() 116 | table.Notify("foo", conn2) 117 | if table.GetConn("foo") != conn2 { 118 | t.FailNow() 119 | } 120 | } 121 | 122 | // Construct a dummy connection object to use to populate the connTable for tests. 123 | func makeTestConn() *connection { 124 | parsedMessages := make(chan base.SipMessage) 125 | errors := make(chan error) 126 | streamed := true 127 | return &connection{ 128 | &testutils.DummyConn{}, 129 | true, 130 | parser.NewParser(parsedMessages, errors, streamed), 131 | parsedMessages, 132 | errors, 133 | make(chan base.SipMessage), 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /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 | } 50 | 51 | func (t *mockTimer) C() <-chan time.Time { 52 | return t.Chan 53 | } 54 | 55 | func (t *mockTimer) Reset(d time.Duration) bool { 56 | wasActive := removeMockTimer(t) 57 | 58 | t.EndTime = currentTimeMock.Add(d) 59 | if d > 0 { 60 | mockTimers = append(mockTimers, t) 61 | } else { 62 | // The new timer has an expiry time of 0. 63 | // Fire it right away, and don't bother tracking it. 64 | t.Chan <- currentTimeMock 65 | } 66 | 67 | 68 | return wasActive 69 | } 70 | 71 | func (t *mockTimer) Stop() bool { 72 | return removeMockTimer(t) 73 | } 74 | 75 | // Creates a new Timer; either a wrapper around a standard Go time.Timer, or a mocked-out Timer, 76 | // depending on whether MockMode is set. 77 | func NewTimer(d time.Duration) Timer { 78 | if MockMode { 79 | t := mockTimer{currentTimeMock.Add(d), make(chan time.Time)} 80 | if d == 0 { 81 | t.Chan <- currentTimeMock 82 | } else { 83 | mockTimers = append(mockTimers, &t) 84 | } 85 | return &t 86 | } else { 87 | return &realTimer{time.NewTimer(d)} 88 | } 89 | } 90 | 91 | // See built-in time.After() function. 92 | func After(d time.Duration) <-chan time.Time { 93 | return NewTimer(d).C() 94 | } 95 | 96 | // See built-in time.Sleep() function. 97 | func Sleep(d time.Duration) { 98 | <-After(d) 99 | } 100 | 101 | // Increment the current time by the given Duration. 102 | // This function can only be called in Mock Mode, otherwise we will panic. 103 | func Elapse(d time.Duration) { 104 | requireMockMode() 105 | fired := make([]*mockTimer, 0) 106 | currentTimeMock = currentTimeMock.Add(d) 107 | 108 | // Fire any timers whose time has come up. 109 | for _, t := range mockTimers { 110 | if currentTimeMock.After(t.EndTime) { 111 | t.Chan <- currentTimeMock 112 | fired = append(fired, t) 113 | } 114 | } 115 | 116 | // Stop tracking any fired timers. 117 | remainingTimers := make([]*mockTimer, 0) 118 | loop: 119 | for _, t := range mockTimers { 120 | for _, fired_t := range fired { 121 | if t == fired_t { 122 | continue loop 123 | } 124 | } 125 | 126 | remainingTimers = append(remainingTimers, t) 127 | } 128 | 129 | mockTimers = remainingTimers 130 | } 131 | 132 | // Returns the current time. 133 | // If Mock Mode is set, this will be the sum of all Durations passed into Elapse calls; 134 | // otherwise it will be the true system time. 135 | func Now() time.Time { 136 | if MockMode { 137 | return currentTimeMock 138 | } else { 139 | return time.Now() 140 | } 141 | } 142 | 143 | // Shortcut method to enforce that Mock Mode is enabled. 144 | func requireMockMode() { 145 | if !MockMode { 146 | panic("This method requires MockMode to be enabled") 147 | } 148 | } 149 | 150 | // Utility method to remove a mockTimer from the list of outstanding timers. 151 | func removeMockTimer(t *mockTimer) bool { 152 | // First, find the index of the timer in our list. 153 | found := false 154 | var idx int 155 | var elt *mockTimer 156 | for idx, elt = range mockTimers { 157 | if elt == t { 158 | found = true 159 | break 160 | } 161 | } 162 | 163 | if found { 164 | // We found the given timer. Remove it. 165 | if idx == len(mockTimers)-1 { 166 | mockTimers = mockTimers[:idx] 167 | } else { 168 | mockTimers = append(mockTimers[:idx], mockTimers[idx+1]) 169 | } 170 | return true 171 | } else { 172 | // The timer was not present, indicating that it was already expired. 173 | return false 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /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/transport" 11 | ) 12 | 13 | // Generic Client Transaction 14 | 15 | const ( 16 | T1 = 500 * time.Millisecond 17 | T2 = 4 * time.Second 18 | ) 19 | 20 | type Transaction interface { 21 | Receive(m base.SipMessage) 22 | Origin() *base.Request 23 | Destination() string 24 | Transport() *transport.Manager 25 | Delete() 26 | } 27 | 28 | type transaction struct { 29 | fsm *fsm.FSM // FSM which governs the behavior of this transaction. 30 | origin *base.Request // Request that started this transaction. 31 | lastResp *base.Response // Most recently received message. 32 | dest string // Of the form hostname:port 33 | transport *transport.Manager 34 | tm *Manager 35 | } 36 | 37 | func (tx *transaction) Origin() *base.Request { 38 | return tx.origin 39 | } 40 | 41 | func (tx *transaction) Destination() string { 42 | return tx.dest 43 | } 44 | 45 | func (tx *transaction) Transport() *transport.Manager { 46 | return tx.transport 47 | } 48 | 49 | func (tx *ServerTransaction) Delete() { 50 | tx.tm.delTx(tx) 51 | } 52 | 53 | func (tx *ClientTransaction) Delete() { 54 | log.Warn("Tx: %p, tm: %p", tx, tx.tm) 55 | tx.tm.delTx(tx) 56 | } 57 | 58 | type ClientTransaction struct { 59 | transaction 60 | 61 | tu chan *base.Response // Channel to transaction user. 62 | tu_err chan error // Channel to report up errors to TU. 63 | timer_a_time time.Duration // Current duration of timer A. 64 | timer_a *time.Timer 65 | timer_b *time.Timer 66 | timer_d_time time.Duration // Current duration of timer A. 67 | timer_d *time.Timer 68 | } 69 | 70 | type ServerTransaction struct { 71 | transaction 72 | 73 | tu chan *base.Response // Channel to transaction user. 74 | tu_err chan error // Channel to report up errors to TU. 75 | ack chan *base.Request // Channel we send the ACK up on. 76 | timer_g *time.Timer 77 | timer_h *time.Timer 78 | timer_i *time.Timer 79 | } 80 | 81 | func (tx *ServerTransaction) Receive(m base.SipMessage) { 82 | r, ok := m.(*base.Request) 83 | if !ok { 84 | log.Warn("Client transaction received request") 85 | } 86 | 87 | var input fsm.Input = fsm.NO_INPUT 88 | switch { 89 | case r.Method == tx.origin.Method: 90 | input = server_input_request 91 | case r.Method == base.ACK: 92 | input = server_input_ack 93 | tx.ack <- r 94 | default: 95 | log.Warn("Invalid message correlated to server transaction.") 96 | } 97 | 98 | tx.fsm.Spin(input) 99 | } 100 | 101 | func (tx *ServerTransaction) Respond(r *base.Response) { 102 | tx.lastResp = r 103 | 104 | var input fsm.Input 105 | switch { 106 | case r.StatusCode < 200: 107 | input = server_input_user_1xx 108 | case r.StatusCode < 300: 109 | input = server_input_user_2xx 110 | default: 111 | input = server_input_user_300_plus 112 | } 113 | 114 | tx.fsm.Spin(input) 115 | } 116 | 117 | func (tx *ServerTransaction) Ack() <-chan *base.Request { 118 | return (<-chan *base.Request)(tx.ack) 119 | } 120 | 121 | func (tx *ClientTransaction) Receive(m base.SipMessage) { 122 | r, ok := m.(*base.Response) 123 | if !ok { 124 | log.Warn("Client transaction received request") 125 | } 126 | 127 | tx.lastResp = r 128 | 129 | var input fsm.Input 130 | switch { 131 | case r.StatusCode < 200: 132 | input = client_input_1xx 133 | case r.StatusCode < 300: 134 | input = client_input_2xx 135 | default: 136 | input = client_input_300_plus 137 | } 138 | 139 | tx.fsm.Spin(input) 140 | } 141 | 142 | // Resend the originating request. 143 | func (tx *ClientTransaction) resend() { 144 | err := tx.transport.Send(tx.dest, tx.origin) 145 | if err != nil { 146 | tx.fsm.Spin(client_input_transport_err) 147 | } 148 | } 149 | 150 | // Pass up the most recently received response to the TU. 151 | func (tx *ClientTransaction) passUp() { 152 | tx.tu <- tx.lastResp 153 | } 154 | 155 | // Send an error to the TU. 156 | func (tx *ClientTransaction) transportError() { 157 | tx.tu_err <- errors.New("failed to send message.") 158 | } 159 | 160 | // Inform the TU that the transaction timed out. 161 | func (tx *ClientTransaction) timeoutError() { 162 | tx.tu_err <- errors.New("transaction timed out.") 163 | } 164 | 165 | // Send an automatic ACK. 166 | func (tx *ClientTransaction) Ack() { 167 | ack := base.NewRequest(base.ACK, 168 | tx.origin.Recipient, 169 | tx.origin.SipVersion, 170 | []base.SipHeader{}, 171 | "") 172 | 173 | // Copy headers from original request. 174 | // TODO: Safety 175 | base.CopyHeaders("From", tx.origin, ack) 176 | base.CopyHeaders("Call-Id", tx.origin, ack) 177 | base.CopyHeaders("Route", tx.origin, ack) 178 | cseq := tx.origin.Headers("CSeq")[0].Copy() 179 | cseq.(*base.CSeq).MethodName = base.ACK 180 | ack.AddHeader(cseq) 181 | via := tx.origin.Headers("Via")[0].Copy() 182 | ack.AddHeader(via) 183 | 184 | // Copy headers from response. 185 | base.CopyHeaders("To", tx.lastResp, ack) 186 | 187 | // Send the ACK. 188 | tx.transport.Send(tx.dest, ack) 189 | } 190 | 191 | // Return the channel we send responses on. 192 | func (tx *ClientTransaction) Responses() <-chan *base.Response { 193 | return (<-chan *base.Response)(tx.tu) 194 | } 195 | 196 | // Return the channel we send errors on. 197 | func (tx *ClientTransaction) Errors() <-chan error { 198 | return (<-chan error)(tx.tu_err) 199 | } 200 | -------------------------------------------------------------------------------- /transaction/client_test.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stefankopieczek/gossip/base" 11 | "github.com/stefankopieczek/gossip/log" 12 | "github.com/stefankopieczek/gossip/parser" 13 | "github.com/stefankopieczek/gossip/transport" 14 | ) 15 | 16 | var c_SERVER string = "localhost:5060" 17 | var c_CLIENT string = "localhost:5061" 18 | 19 | var testLog *log.Logger = log.New(os.Stderr, ">>> ", 0) 20 | 21 | func TestAAAASetup(t *testing.T) { 22 | log.SetDefaultLogLevel(log.WARN) 23 | testLog.Level = log.DEBUG 24 | } 25 | 26 | func TestSendInviteUDP(t *testing.T) { 27 | invite, err := request([]string{ 28 | "INVITE sip:joe@bloggs.com SIP/2.0", 29 | "Via: SIP/2.0/UDP " + c_CLIENT + ";branch=z9hG4bK776asdhds", 30 | "", 31 | "", 32 | }) 33 | assertNoError(t, err) 34 | 35 | test := transactionTest{actions: []action{ 36 | &clientSend{invite}, 37 | &serverRecv{invite}, 38 | }} 39 | test.Execute(t) 40 | } 41 | 42 | func TestReceiveOKUDP(t *testing.T) { 43 | invite, err := request([]string{ 44 | "INVITE sip:joe@bloggs.com SIP/2.0", 45 | "CSeq: 1 INVITE", 46 | "Via: SIP/2.0/UDP " + c_CLIENT + ";branch=z9hG4bK776asdhds", 47 | "", 48 | "", 49 | }) 50 | assertNoError(t, err) 51 | 52 | ok, err := response([]string{ 53 | "SIP/2.0 200 OK", 54 | "CSeq: 1 INVITE", 55 | "Via: SIP/2.0/UDP " + c_SERVER + ";branch=z9hG4bK776asdhds", 56 | "", 57 | "", 58 | }) 59 | assertNoError(t, err) 60 | 61 | test := transactionTest{actions: []action{ 62 | &clientSend{invite}, 63 | &serverRecv{invite}, 64 | &serverSend{ok}, 65 | &clientRecv{ok}, 66 | }} 67 | test.Execute(t) 68 | } 69 | 70 | type action interface { 71 | Act(test *transactionTest) error 72 | } 73 | 74 | type transactionTest struct { 75 | actions []action 76 | client *Manager 77 | server *transport.Manager 78 | serverRecv chan base.SipMessage 79 | lastTx *ClientTransaction 80 | } 81 | 82 | func (test *transactionTest) Execute(t *testing.T) { 83 | defer func() { <-time.After(time.Millisecond * 200) }() 84 | var err error 85 | test.client, err = NewManager("udp", c_CLIENT) 86 | assertNoError(t, err) 87 | defer test.client.Stop() 88 | 89 | test.server, err = transport.NewManager("udp") 90 | assertNoError(t, err) 91 | defer test.server.Stop() 92 | test.serverRecv = test.server.GetChannel() 93 | test.server.Listen(c_SERVER) 94 | 95 | for _, actn := range test.actions { 96 | testLog.Debug("Performing action %v", actn) 97 | assertNoError(t, actn.Act(test)) 98 | } 99 | } 100 | 101 | type clientSend struct { 102 | msg *base.Request 103 | } 104 | 105 | func (actn *clientSend) Act(test *transactionTest) error { 106 | testLog.Debug("Client sending message\n%v", actn.msg.String()) 107 | test.lastTx = test.client.Send(actn.msg, c_SERVER) 108 | return nil 109 | } 110 | 111 | type serverSend struct { 112 | msg base.SipMessage 113 | } 114 | 115 | func (actn *serverSend) Act(test *transactionTest) error { 116 | testLog.Debug("Server sending message\n%v", actn.msg.String()) 117 | return test.server.Send(c_CLIENT, actn.msg) 118 | } 119 | 120 | type clientRecv struct { 121 | expected *base.Response 122 | } 123 | 124 | func (actn *clientRecv) Act(test *transactionTest) error { 125 | responses := test.lastTx.Responses() 126 | select { 127 | case response, ok := <-responses: 128 | if !ok { 129 | return fmt.Errorf("Response channel prematurely closed") 130 | } else if response.String() != actn.expected.String() { 131 | return fmt.Errorf("Unexpected response:\n%s", response.String()) 132 | } else { 133 | testLog.Debug("Client received correct message\n%v", response.String()) 134 | return nil 135 | } 136 | case <-time.After(time.Second): 137 | return fmt.Errorf("Timed out waiting for response") 138 | } 139 | } 140 | 141 | type serverRecv struct { 142 | expected base.SipMessage 143 | } 144 | 145 | func (actn *serverRecv) Act(test *transactionTest) error { 146 | select { 147 | case msg, ok := <-test.serverRecv: 148 | if !ok { 149 | return fmt.Errorf("Server receive channel prematurely closed") 150 | } else if msg.String() != actn.expected.String() { 151 | return fmt.Errorf("Unexpected message arrived at server:\n%s", msg.String()) 152 | } else { 153 | testLog.Debug("Server received correct message\n %v", msg.String()) 154 | return nil 155 | } 156 | case <-time.After(time.Second): 157 | return fmt.Errorf("Timed out waiting for message on server") 158 | } 159 | } 160 | 161 | func assert(t *testing.T, b bool, msg string) { 162 | if !b { 163 | t.Errorf(msg) 164 | } 165 | } 166 | 167 | func assertNoError(t *testing.T, err error) { 168 | if err != nil { 169 | t.Fatalf("Unexpected error: %s", err.Error()) 170 | } 171 | } 172 | 173 | func message(rawMsg []string) (base.SipMessage, error) { 174 | return parser.ParseMessage([]byte(strings.Join(rawMsg, "\r\n"))) 175 | } 176 | 177 | func request(rawMsg []string) (*base.Request, error) { 178 | msg, err := message(rawMsg) 179 | 180 | if err != nil { 181 | return nil, err 182 | } 183 | 184 | switch msg.(type) { 185 | case *base.Request: 186 | return msg.(*base.Request), nil 187 | default: 188 | return nil, fmt.Errorf("%s is not a request", msg.Short) 189 | } 190 | } 191 | 192 | func response(rawMsg []string) (*base.Response, error) { 193 | msg, err := message(rawMsg) 194 | 195 | if err != nil { 196 | return nil, err 197 | } 198 | 199 | switch msg.(type) { 200 | case *base.Response: 201 | return msg.(*base.Response), nil 202 | default: 203 | return nil, fmt.Errorf("%s is not a response", msg.Short) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /transaction/server.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 | ) 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 = time.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/transport" 12 | ) 13 | 14 | var ( 15 | global *Manager = &Manager{ 16 | txs: map[key]Transaction{}, 17 | } 18 | ) 19 | 20 | type Manager struct { 21 | txs map[key]Transaction 22 | transport *transport.Manager 23 | requests chan *ServerTransaction 24 | txLock *sync.RWMutex 25 | } 26 | 27 | // Transactions are identified by the branch parameter in the top Via header, and the method. (RFC 3261 17.1.3) 28 | type key struct { 29 | branch string 30 | method string 31 | } 32 | 33 | func NewManager(trans, addr string) (*Manager, error) { 34 | t, err := transport.NewManager(trans) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | mng := &Manager{ 40 | txs: map[key]Transaction{}, 41 | txLock: &sync.RWMutex{}, 42 | transport: t, 43 | } 44 | 45 | mng.requests = make(chan *ServerTransaction, 5) 46 | 47 | // Spin up a goroutine to pull messages up from the depths. 48 | c := mng.transport.GetChannel() 49 | go func() { 50 | for msg := range c { 51 | go mng.handle(msg) 52 | } 53 | }() 54 | 55 | err = mng.transport.Listen(addr) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return mng, nil 61 | } 62 | 63 | // Stop the manager and close down all processing on it, losing all transactions in progress. 64 | func (mng *Manager) Stop() { 65 | // Stop the transport layer. 66 | mng.transport.Stop() 67 | } 68 | 69 | func (mng *Manager) Requests() <-chan *ServerTransaction { 70 | return (<-chan *ServerTransaction)(mng.requests) 71 | } 72 | 73 | func (mng *Manager) putTx(tx Transaction) { 74 | viaHeaders := tx.Origin().Headers("Via") 75 | if len(viaHeaders) == 0 { 76 | log.Warn("No Via header on new transaction. Transaction will be dropped.") 77 | return 78 | } 79 | 80 | via, ok := viaHeaders[0].(*base.ViaHeader) 81 | if !ok { 82 | // TODO: Handle this better. 83 | panic(errors.New("Headers('Via') returned non-Via header!")) 84 | } 85 | 86 | branch, ok := (*via)[0].Params.Get("branch") 87 | if !ok { 88 | log.Warn("No branch parameter on top Via header. Transaction will be dropped.") 89 | return 90 | } 91 | 92 | var k key 93 | switch branch := branch.(type) { 94 | case base.String: 95 | k = key{branch.String(), string(tx.Origin().Method)} 96 | case base.NoString: 97 | log.Warn("Empty branch parameter on top Via header. Transaction will be dropped.") 98 | return 99 | default: 100 | log.Warn("Unexpected type of branch value on top Via header: %T", branch) 101 | return 102 | } 103 | mng.txLock.Lock() 104 | mng.txs[k] = tx 105 | mng.txLock.Unlock() 106 | } 107 | 108 | func (mng *Manager) makeKey(s base.SipMessage) (key, bool) { 109 | viaHeaders := s.Headers("Via") 110 | via, ok := viaHeaders[0].(*base.ViaHeader) 111 | if !ok { 112 | panic(errors.New("Headers('Via') returned non-Via header!")) 113 | } 114 | 115 | b, ok := (*via)[0].Params.Get("branch") 116 | if !ok { 117 | return key{}, false 118 | } 119 | 120 | branch, ok := b.(base.String) 121 | if !ok { 122 | return key{}, false 123 | } 124 | 125 | var method string 126 | switch s := s.(type) { 127 | case *base.Request: 128 | // Correlate an ACK request to the related INVITE. 129 | if s.Method == base.ACK { 130 | method = string(base.INVITE) 131 | } else { 132 | method = string(s.Method) 133 | } 134 | case *base.Response: 135 | cseqs := s.Headers("CSeq") 136 | if len(cseqs) == 0 { 137 | // TODO - Handle non-existent CSeq 138 | panic("No CSeq on response!") 139 | } 140 | 141 | cseq, _ := s.Headers("CSeq")[0].(*base.CSeq) 142 | method = string(cseq.MethodName) 143 | } 144 | 145 | return key{branch.String(), method}, true 146 | } 147 | 148 | // Gets a transaction from the transaction store. 149 | // Should only be called inside the storage handling goroutine to ensure concurrency safety. 150 | func (mng *Manager) getTx(s base.SipMessage) (Transaction, bool) { 151 | key, ok := mng.makeKey(s) 152 | if !ok { 153 | // TODO: Here we should initiate more intense searching as specified in RFC3261 section 17 154 | log.Warn("Could not correlate message to transaction by branch/method. Dropping.") 155 | return nil, false 156 | } 157 | 158 | mng.txLock.RLock() 159 | tx, ok := mng.txs[key] 160 | mng.txLock.RUnlock() 161 | 162 | return tx, ok 163 | } 164 | 165 | // Deletes a transaction from the transaction store. 166 | // Should only be called inside the storage handling goroutine to ensure concurrency safety. 167 | func (mng *Manager) delTx(t Transaction) { 168 | key, ok := mng.makeKey(t.Origin()) 169 | if !ok { 170 | log.Debug("Could not build lookup key for transaction. Is it missing a branch parameter?") 171 | } 172 | 173 | mng.txLock.Lock() 174 | delete(mng.txs, key) 175 | mng.txLock.Unlock() 176 | } 177 | 178 | func (mng *Manager) handle(msg base.SipMessage) { 179 | log.Info("Received message: %s", msg.Short()) 180 | switch m := msg.(type) { 181 | case *base.Request: 182 | mng.request(m) 183 | case *base.Response: 184 | mng.correlate(m) 185 | default: 186 | // TODO: Error 187 | } 188 | } 189 | 190 | // Create Client transaction. 191 | func (mng *Manager) Send(r *base.Request, dest string) *ClientTransaction { 192 | log.Debug("Sending to %v: %v", dest, r.String()) 193 | 194 | tx := &ClientTransaction{} 195 | tx.origin = r 196 | tx.dest = dest 197 | tx.transport = mng.transport 198 | tx.tm = mng 199 | 200 | tx.initFSM() 201 | 202 | tx.tu = make(chan *base.Response, 3) 203 | tx.tu_err = make(chan error, 1) 204 | 205 | tx.timer_a_time = T1 206 | tx.timer_a = time.AfterFunc(tx.timer_a_time, func() { 207 | tx.fsm.Spin(client_input_timer_a) 208 | }) 209 | tx.timer_b = time.AfterFunc(64*T1, func() { 210 | tx.fsm.Spin(client_input_timer_b) 211 | }) 212 | 213 | // Timer D is set to 32 seconds for unreliable transports, and 0 seconds otherwise. 214 | tx.timer_d_time = 32 * time.Second 215 | 216 | err := mng.transport.Send(dest, r) 217 | if err != nil { 218 | log.Warn("Failed to send message: %s", err.Error()) 219 | tx.fsm.Spin(client_input_transport_err) 220 | } 221 | 222 | mng.putTx(tx) 223 | 224 | return tx 225 | } 226 | 227 | // Give a received response to the correct transaction. 228 | func (mng *Manager) correlate(r *base.Response) { 229 | tx, ok := mng.getTx(r) 230 | if !ok { 231 | // TODO: Something 232 | log.Warn("Failed to correlate response to active transaction. Dropping it.") 233 | return 234 | } 235 | 236 | tx.Receive(r) 237 | } 238 | 239 | // Handle a request. 240 | func (mng *Manager) request(r *base.Request) { 241 | t, ok := mng.getTx(r) 242 | if ok { 243 | t.Receive(r) 244 | return 245 | } 246 | 247 | // If we failed to correlate an ACK, just drop it. 248 | if r.Method == base.ACK { 249 | log.Warn("Couldn't correlate ACK to an open transaction. Dropping it.") 250 | return 251 | } 252 | 253 | // Create a new transaction 254 | tx := &ServerTransaction{} 255 | tx.tm = mng 256 | tx.origin = r 257 | tx.transport = mng.transport 258 | 259 | // Use the remote address in the top Via header. This is not correct behaviour. 260 | viaHeaders := tx.Origin().Headers("Via") 261 | if len(viaHeaders) == 0 { 262 | log.Warn("No Via header on new transaction. Transaction will be dropped.") 263 | return 264 | } 265 | 266 | via, ok := viaHeaders[0].(*base.ViaHeader) 267 | if !ok { 268 | panic(errors.New("Headers('Via') returned non-Via header!")) 269 | } 270 | 271 | if len(*via) == 0 { 272 | log.Warn("Via header contained no hops! Transaction will be dropped.") 273 | return 274 | } 275 | 276 | hop := (*via)[0] 277 | 278 | port := uint16(5060) 279 | 280 | if hop.Port != nil { 281 | port = *hop.Port 282 | } 283 | 284 | tx.dest = fmt.Sprintf("%s:%d", hop.Host, port) 285 | tx.transport = mng.transport 286 | 287 | tx.initFSM() 288 | 289 | tx.tu = make(chan *base.Response, 3) 290 | tx.tu_err = make(chan error, 1) 291 | tx.ack = make(chan *base.Request, 1) 292 | 293 | // Send a 100 Trying immediately. 294 | // Technically we shouldn't do this if we trustthe user to do it within 200ms, 295 | // but I'm not sure how to handle that situation right now. 296 | 297 | // Pretend the user sent us a 100 to send. 298 | trying := base.NewResponse( 299 | "SIP/2.0", 300 | 100, 301 | "Trying", 302 | []base.SipHeader{}, 303 | "", 304 | ) 305 | 306 | base.CopyHeaders("Via", tx.origin, trying) 307 | base.CopyHeaders("From", tx.origin, trying) 308 | base.CopyHeaders("To", tx.origin, trying) 309 | base.CopyHeaders("Call-Id", tx.origin, trying) 310 | base.CopyHeaders("CSeq", tx.origin, trying) 311 | 312 | tx.lastResp = trying 313 | tx.fsm.Spin(server_input_user_1xx) 314 | 315 | mng.requests <- tx 316 | } 317 | -------------------------------------------------------------------------------- /transaction/client.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/discoviking/fsm" 7 | "github.com/stefankopieczek/gossip/base" 8 | "github.com/stefankopieczek/gossip/log" 9 | ) 10 | 11 | // SIP Client Transaction FSM 12 | // Implements the behaviour described in RFC 3261 section 17.1 13 | 14 | // FSM States 15 | const ( 16 | client_state_calling = iota 17 | client_state_proceeding 18 | client_state_completed 19 | client_state_terminated 20 | ) 21 | 22 | // FSM Inputs 23 | const ( 24 | client_input_1xx fsm.Input = iota 25 | client_input_2xx 26 | client_input_300_plus 27 | client_input_timer_a 28 | client_input_timer_b 29 | client_input_timer_d 30 | client_input_transport_err 31 | client_input_delete 32 | ) 33 | 34 | // Initialises the correct kind of FSM based on request method. 35 | func (tx *ClientTransaction) initFSM() { 36 | if tx.origin.Method == base.INVITE { 37 | tx.initInviteFSM() 38 | } else { 39 | tx.initNonInviteFSM() 40 | } 41 | } 42 | 43 | func (tx *ClientTransaction) initInviteFSM() { 44 | // Define Actions 45 | 46 | // Resend the request. 47 | act_resend := func() fsm.Input { 48 | tx.timer_a_time *= 2 49 | tx.timer_a.Reset(tx.timer_a_time) 50 | tx.resend() 51 | return fsm.NO_INPUT 52 | } 53 | 54 | // Just pass up the latest response. 55 | act_passup := func() fsm.Input { 56 | tx.passUp() 57 | return fsm.NO_INPUT 58 | } 59 | 60 | // Handle 300+ responses. 61 | // Pass up response and send ACK, start timer D. 62 | act_300 := func() fsm.Input { 63 | tx.passUp() 64 | tx.Ack() 65 | if tx.timer_d != nil { 66 | tx.timer_d.Stop() 67 | } 68 | tx.timer_d = time.AfterFunc(tx.timer_d_time, func() { 69 | tx.fsm.Spin(client_input_timer_d) 70 | }) 71 | return fsm.NO_INPUT 72 | } 73 | 74 | // Send an ACK. 75 | act_ack := func() fsm.Input { 76 | tx.Ack() 77 | return fsm.NO_INPUT 78 | } 79 | 80 | // Send up transport failure error. 81 | act_trans_err := func() fsm.Input { 82 | tx.transportError() 83 | return client_input_delete 84 | } 85 | 86 | // Send up timeout error. 87 | act_timeout := func() fsm.Input { 88 | tx.timeoutError() 89 | return client_input_delete 90 | } 91 | 92 | // Pass up the response and delete the transaction. 93 | act_passup_delete := func() fsm.Input { 94 | tx.passUp() 95 | tx.Delete() 96 | return fsm.NO_INPUT 97 | } 98 | 99 | // Just delete the transaction. 100 | act_delete := func() fsm.Input { 101 | tx.Delete() 102 | return fsm.NO_INPUT 103 | } 104 | 105 | // Define States 106 | 107 | // Calling 108 | client_state_def_calling := fsm.State{ 109 | Index: client_state_calling, 110 | Outcomes: map[fsm.Input]fsm.Outcome{ 111 | client_input_1xx: {client_state_proceeding, act_passup}, 112 | client_input_2xx: {client_state_terminated, act_passup_delete}, 113 | client_input_300_plus: {client_state_completed, act_300}, 114 | client_input_timer_a: {client_state_calling, act_resend}, 115 | client_input_timer_b: {client_state_terminated, act_timeout}, 116 | client_input_transport_err: {client_state_terminated, act_trans_err}, 117 | }, 118 | } 119 | 120 | // Proceeding 121 | client_state_def_proceeding := fsm.State{ 122 | Index: client_state_proceeding, 123 | Outcomes: map[fsm.Input]fsm.Outcome{ 124 | client_input_1xx: {client_state_proceeding, act_passup}, 125 | client_input_2xx: {client_state_terminated, act_passup_delete}, 126 | client_input_300_plus: {client_state_completed, act_300}, 127 | client_input_timer_a: {client_state_proceeding, fsm.NO_ACTION}, 128 | client_input_timer_b: {client_state_proceeding, fsm.NO_ACTION}, 129 | }, 130 | } 131 | 132 | // Completed 133 | client_state_def_completed := fsm.State{ 134 | Index: client_state_completed, 135 | Outcomes: map[fsm.Input]fsm.Outcome{ 136 | client_input_1xx: {client_state_completed, fsm.NO_ACTION}, 137 | client_input_2xx: {client_state_completed, fsm.NO_ACTION}, 138 | client_input_300_plus: {client_state_completed, act_ack}, 139 | client_input_timer_d: {client_state_terminated, act_delete}, 140 | client_input_transport_err: {client_state_terminated, act_trans_err}, 141 | client_input_timer_a: {client_state_completed, fsm.NO_ACTION}, 142 | client_input_timer_b: {client_state_completed, fsm.NO_ACTION}, 143 | }, 144 | } 145 | 146 | // Terminated 147 | client_state_def_terminated := fsm.State{ 148 | Index: client_state_terminated, 149 | Outcomes: map[fsm.Input]fsm.Outcome{ 150 | client_input_1xx: {client_state_terminated, fsm.NO_ACTION}, 151 | client_input_2xx: {client_state_terminated, fsm.NO_ACTION}, 152 | client_input_300_plus: {client_state_terminated, fsm.NO_ACTION}, 153 | client_input_timer_a: {client_state_terminated, fsm.NO_ACTION}, 154 | client_input_timer_b: {client_state_terminated, fsm.NO_ACTION}, 155 | client_input_delete: {client_state_terminated, act_delete}, 156 | }, 157 | } 158 | 159 | fsm, err := fsm.Define( 160 | client_state_def_calling, 161 | client_state_def_proceeding, 162 | client_state_def_completed, 163 | client_state_def_terminated, 164 | ) 165 | 166 | if err != nil { 167 | log.Severe("Failure to define INVITE client transaction fsm: %s", err.Error()) 168 | } 169 | 170 | tx.fsm = fsm 171 | } 172 | 173 | func (tx *ClientTransaction) initNonInviteFSM() { 174 | // Define Actions 175 | 176 | // Resend the request. 177 | act_resend := func() fsm.Input { 178 | tx.timer_a_time *= 2 179 | // For non-INVITE, cap timer A at T2 seconds. 180 | if tx.timer_a_time > T2 { 181 | tx.timer_a_time = T2 182 | } 183 | 184 | tx.timer_a.Reset(tx.timer_a_time) 185 | tx.resend() 186 | return fsm.NO_INPUT 187 | } 188 | 189 | // Just pass up the latest response. 190 | act_passup := func() fsm.Input { 191 | tx.passUp() 192 | return fsm.NO_INPUT 193 | } 194 | 195 | // Handle a final response. 196 | act_final := func() fsm.Input { 197 | tx.passUp() 198 | if tx.timer_d != nil { 199 | tx.timer_d.Stop() 200 | } 201 | tx.timer_d = time.AfterFunc(tx.timer_d_time, func() { 202 | tx.fsm.Spin(client_input_timer_d) 203 | }) 204 | return fsm.NO_INPUT 205 | } 206 | 207 | // Send up transport failure error. 208 | act_trans_err := func() fsm.Input { 209 | tx.transportError() 210 | return client_input_delete 211 | } 212 | 213 | // Send up timeout error. 214 | act_timeout := func() fsm.Input { 215 | tx.timeoutError() 216 | return client_input_delete 217 | } 218 | 219 | // Just delete the transaction. 220 | act_delete := func() fsm.Input { 221 | tx.Delete() 222 | return fsm.NO_INPUT 223 | } 224 | 225 | // Define States 226 | 227 | // "Trying" 228 | client_state_def_calling := fsm.State{ 229 | Index: client_state_calling, 230 | Outcomes: map[fsm.Input]fsm.Outcome{ 231 | client_input_1xx: {client_state_proceeding, act_passup}, 232 | client_input_2xx: {client_state_completed, act_final}, 233 | client_input_300_plus: {client_state_completed, act_final}, 234 | client_input_timer_a: {client_state_calling, act_resend}, 235 | client_input_timer_b: {client_state_terminated, act_timeout}, 236 | client_input_transport_err: {client_state_terminated, act_trans_err}, 237 | }, 238 | } 239 | 240 | // Proceeding 241 | client_state_def_proceeding := fsm.State{ 242 | Index: client_state_proceeding, 243 | Outcomes: map[fsm.Input]fsm.Outcome{ 244 | client_input_1xx: {client_state_proceeding, act_passup}, 245 | client_input_2xx: {client_state_completed, act_final}, 246 | client_input_300_plus: {client_state_completed, act_final}, 247 | client_input_timer_a: {client_state_proceeding, act_resend}, 248 | client_input_timer_b: {client_state_terminated, act_timeout}, 249 | client_input_transport_err: {client_state_terminated, act_trans_err}, 250 | }, 251 | } 252 | 253 | // Completed 254 | client_state_def_completed := fsm.State{ 255 | Index: client_state_completed, 256 | Outcomes: map[fsm.Input]fsm.Outcome{ 257 | client_input_1xx: {client_state_completed, fsm.NO_ACTION}, 258 | client_input_2xx: {client_state_completed, fsm.NO_ACTION}, 259 | client_input_300_plus: {client_state_completed, fsm.NO_ACTION}, 260 | client_input_timer_d: {client_state_terminated, act_delete}, 261 | client_input_timer_a: {client_state_completed, fsm.NO_ACTION}, 262 | client_input_timer_b: {client_state_completed, fsm.NO_ACTION}, 263 | }, 264 | } 265 | 266 | // Terminated 267 | client_state_def_terminated := fsm.State{ 268 | Index: client_state_terminated, 269 | Outcomes: map[fsm.Input]fsm.Outcome{ 270 | client_input_1xx: {client_state_terminated, fsm.NO_ACTION}, 271 | client_input_2xx: {client_state_terminated, fsm.NO_ACTION}, 272 | client_input_300_plus: {client_state_terminated, fsm.NO_ACTION}, 273 | client_input_timer_a: {client_state_terminated, fsm.NO_ACTION}, 274 | client_input_timer_b: {client_state_terminated, fsm.NO_ACTION}, 275 | client_input_timer_d: {client_state_terminated, fsm.NO_ACTION}, 276 | client_input_delete: {client_state_terminated, act_delete}, 277 | }, 278 | } 279 | 280 | fsm, err := fsm.Define( 281 | client_state_def_calling, 282 | client_state_def_proceeding, 283 | client_state_def_completed, 284 | client_state_def_terminated, 285 | ) 286 | 287 | if err != nil { 288 | log.Severe("Failure to define INVITE client transaction fsm: %s", err.Error()) 289 | } 290 | 291 | tx.fsm = fsm 292 | } 293 | -------------------------------------------------------------------------------- /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 | // Gets some headers. 112 | func (hs *headers) Headers(name string) []SipHeader { 113 | if hs.headers == nil { 114 | hs.headers = map[string][]SipHeader{} 115 | hs.headerOrder = []string{} 116 | } 117 | if headers, ok := hs.headers[name]; ok { 118 | return headers 119 | } else { 120 | return []SipHeader{} 121 | } 122 | } 123 | 124 | // Copy all headers of one type from one message to another. 125 | // Appending to any headers that were already there. 126 | func CopyHeaders(name string, from, to SipMessage) { 127 | for _, h := range from.Headers(name) { 128 | to.AddHeader(h.Copy()) 129 | } 130 | 131 | } 132 | 133 | // A SIP request (c.f. RFC 3261 section 7.1). 134 | type Request struct { 135 | // Which method this request is, e.g. an INVITE or a REGISTER. 136 | Method Method 137 | 138 | // The Request URI. This indicates the user to whom this request is being addressed. 139 | Recipient Uri 140 | 141 | // The version of SIP used in this message, e.g. "SIP/2.0". 142 | SipVersion string 143 | 144 | // A Request has headers. 145 | headers 146 | 147 | // The order the headers should be displayed in. 148 | headerOrder []string 149 | 150 | // The application data of the message. 151 | Body string 152 | } 153 | 154 | func NewRequest(method Method, recipient Uri, sipVersion string, headers []SipHeader, body string) (request *Request) { 155 | request = new(Request) 156 | request.Method = method 157 | request.SipVersion = sipVersion 158 | request.Recipient = recipient 159 | request.headers = newHeaders() 160 | request.Body = body 161 | 162 | for _, header := range headers { 163 | request.AddHeader(header) 164 | } 165 | 166 | return 167 | } 168 | 169 | func (request *Request) String() string { 170 | var buffer bytes.Buffer 171 | 172 | // Every SIP request starts with a Request Line - RFC 2361 7.1. 173 | buffer.WriteString(fmt.Sprintf("%s %s %s\r\n", 174 | (string)(request.Method), 175 | request.Recipient.String(), 176 | request.SipVersion)) 177 | 178 | buffer.WriteString(request.headers.String()) 179 | 180 | // If the request has a message body, add it. 181 | buffer.WriteString("\r\n" + request.Body) 182 | 183 | return buffer.String() 184 | } 185 | 186 | func (request *Request) Short() string { 187 | var buffer bytes.Buffer 188 | 189 | buffer.WriteString(fmt.Sprintf("%s %s %s", 190 | (string)(request.Method), 191 | request.Recipient.String(), 192 | request.SipVersion)) 193 | 194 | cseqs := request.Headers("CSeq") 195 | if len(cseqs) > 0 { 196 | buffer.WriteString(fmt.Sprintf(" (CSeq: %s)", (cseqs[0].(*CSeq)).String())) 197 | } 198 | 199 | return buffer.String() 200 | } 201 | 202 | func (request *Request) AllHeaders() []SipHeader { 203 | allHeaders := make([]SipHeader, 0) 204 | for _, key := range request.headers.headerOrder { 205 | allHeaders = append(allHeaders, request.headers.headers[key]...) 206 | } 207 | 208 | return allHeaders 209 | } 210 | 211 | func (request *Request) RemoveHeader(header SipHeader) error { 212 | errNoMatch := fmt.Errorf("cannot remove header '%s' from request '%s' as it is not present", 213 | header.String(), request.Short()) 214 | name := header.Name() 215 | 216 | headersOfSameType, isMatch := request.headers.headers[name] 217 | 218 | if !isMatch || len(headersOfSameType) == 0 { 219 | return errNoMatch 220 | } 221 | 222 | found := false 223 | for idx, hdr := range headersOfSameType { 224 | if hdr == header { 225 | request.headers.headers[name] = append(headersOfSameType[:idx], headersOfSameType[idx+1:]...) 226 | found = true 227 | break 228 | } 229 | } 230 | if !found { 231 | return errNoMatch 232 | } 233 | 234 | if len(request.headers.headers[name]) == 0 { 235 | // The header we removed was the only one of its type. 236 | // Tidy up the header structure by removing the empty list value from the header map, 237 | // and removing the entry from the headerOrder list. 238 | delete(request.headers.headers, name) 239 | 240 | for idx, entry := range request.headerOrder { 241 | if entry == name { 242 | request.headers.headerOrder = append(request.headerOrder[:idx], request.headerOrder[idx+1:]...) 243 | } 244 | } 245 | } 246 | 247 | return nil 248 | } 249 | 250 | func (request *Request) GetBody() string { 251 | return request.Body 252 | } 253 | 254 | func (request *Request) SetBody(body string) { 255 | request.Body = body 256 | } 257 | 258 | // A SIP response object (c.f. RFC 3261 section 7.2). 259 | type Response struct { 260 | // The version of SIP used in this message, e.g. "SIP/2.0". 261 | SipVersion string 262 | 263 | // The response code, e.g. 200, 401 or 500. 264 | // This indicates the outcome of the originating request. 265 | StatusCode uint16 266 | 267 | // The reason string provides additional, human-readable information used to provide 268 | // clarification or explanation of the status code. 269 | // This will vary between different SIP UAs, and should not be interpreted by the receiving UA. 270 | Reason string 271 | 272 | // A response has headers. 273 | headers 274 | 275 | // The application data of the message. 276 | Body string 277 | } 278 | 279 | func NewResponse(sipVersion string, statusCode uint16, reason string, headers []SipHeader, body string) (response *Response) { 280 | response = new(Response) 281 | response.SipVersion = sipVersion 282 | response.StatusCode = statusCode 283 | response.Reason = reason 284 | response.Body = body 285 | response.headers = newHeaders() 286 | response.headerOrder = make([]string, 0) 287 | 288 | for _, header := range headers { 289 | response.AddHeader(header) 290 | } 291 | 292 | return 293 | } 294 | 295 | func (response *Response) String() string { 296 | var buffer bytes.Buffer 297 | 298 | // Every SIP response starts with a Status Line - RFC 2361 7.2. 299 | buffer.WriteString(fmt.Sprintf("%s %d %s\r\n", 300 | response.SipVersion, 301 | response.StatusCode, 302 | response.Reason)) 303 | 304 | // Write the headers. 305 | buffer.WriteString(response.headers.String()) 306 | 307 | // If the request has a message body, add it. 308 | buffer.WriteString("\r\n" + response.Body) 309 | 310 | return buffer.String() 311 | } 312 | 313 | func (response *Response) Short() string { 314 | var buffer bytes.Buffer 315 | 316 | buffer.WriteString(fmt.Sprintf("%s %d %s\r\n", 317 | response.SipVersion, 318 | response.StatusCode, 319 | response.Reason)) 320 | 321 | cseqs := response.Headers("CSeq") 322 | if len(cseqs) > 0 { 323 | buffer.WriteString(fmt.Sprintf(" (CSeq: %s)", (cseqs[0].(*CSeq)).String())) 324 | } 325 | 326 | return buffer.String() 327 | } 328 | 329 | func (response *Response) AllHeaders() []SipHeader { 330 | allHeaders := make([]SipHeader, 0) 331 | for _, key := range response.headers.headerOrder { 332 | allHeaders = append(allHeaders, response.headers.headers[key]...) 333 | } 334 | 335 | return allHeaders 336 | } 337 | 338 | func (response *Response) RemoveHeader(header SipHeader) error { 339 | errNoMatch := fmt.Errorf("cannot remove header '%s' from response '%s' as it is not present", 340 | header.String(), response.Short()) 341 | name := header.Name() 342 | 343 | headersOfSameType, isMatch := response.headers.headers[name] 344 | 345 | if !isMatch || len(headersOfSameType) == 0 { 346 | return errNoMatch 347 | } 348 | 349 | found := false 350 | for idx, hdr := range headersOfSameType { 351 | if hdr == header { 352 | response.headers.headers[name] = append(headersOfSameType[:idx], headersOfSameType[idx+1:]...) 353 | found = true 354 | break 355 | } 356 | } 357 | if !found { 358 | return errNoMatch 359 | } 360 | 361 | if len(response.headers.headers[name]) == 0 { 362 | // The header we removed was the only one of its type. 363 | // Tidy up the header structure by removing the empty list value from the header map, 364 | // and removing the entry from the headerOrder list. 365 | delete(response.headers.headers, name) 366 | 367 | for idx, entry := range response.headers.headerOrder { 368 | if entry == name { 369 | response.headers.headerOrder = append(response.headers.headerOrder[:idx], response.headers.headerOrder[idx+1:]...) 370 | } 371 | } 372 | } 373 | 374 | return nil 375 | } 376 | 377 | func (response *Response) GetBody() string { 378 | return response.Body 379 | } 380 | 381 | func (response *Response) SetBody(body string) { 382 | response.Body = body 383 | } 384 | -------------------------------------------------------------------------------- /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 | // Copy the Sip URI. 108 | func (uri *SipUri) Copy() Uri { 109 | var port *uint16 110 | if uri.Port != nil { 111 | temp := *uri.Port 112 | port = &temp 113 | } 114 | 115 | return &SipUri{ 116 | uri.IsEncrypted, 117 | uri.User, 118 | uri.Password, 119 | uri.Host, 120 | port, 121 | uri.UriParams.Copy(), 122 | uri.Headers.Copy(), 123 | } 124 | } 125 | 126 | // IsWildcard() always returns 'false' for SIP URIs as they are not equal to the wildcard '*' URI. 127 | // This method is required since SIP URIs are valid in Contact: headers. 128 | func (uri *SipUri) IsWildcard() bool { 129 | return false 130 | } 131 | 132 | // Determine if the SIP URI is equal to the specified URI according to the rules laid down in RFC 3261 s. 19.1.4. 133 | // TODO: The Equals method is not currently RFC-compliant; fix this! 134 | func (uri *SipUri) Equals(otherUri Uri) bool { 135 | otherPtr, ok := otherUri.(*SipUri) 136 | if !ok { 137 | return false 138 | } 139 | 140 | other := *otherPtr 141 | result := uri.IsEncrypted == other.IsEncrypted && 142 | uri.User == other.User && 143 | uri.Password == other.Password && 144 | uri.Host == other.Host && 145 | utils.Uint16PtrEq(uri.Port, other.Port) 146 | 147 | if !result { 148 | return false 149 | } 150 | 151 | if !uri.UriParams.Equals(other.UriParams) { 152 | return false 153 | } 154 | 155 | if !uri.Headers.Equals(other.Headers) { 156 | return false 157 | } 158 | 159 | return true 160 | } 161 | 162 | // Generates the string representation of a SipUri struct. 163 | func (uri *SipUri) String() string { 164 | var buffer bytes.Buffer 165 | 166 | // Compulsory protocol identifier. 167 | if uri.IsEncrypted { 168 | buffer.WriteString("sips") 169 | buffer.WriteString(":") 170 | } else { 171 | buffer.WriteString("sip") 172 | buffer.WriteString(":") 173 | } 174 | 175 | // Optional userinfo part. 176 | switch user := uri.User.(type) { 177 | case String: 178 | buffer.WriteString(user.String()) 179 | switch pw := uri.Password.(type) { 180 | case String: 181 | buffer.WriteString(":") 182 | buffer.WriteString(pw.String()) 183 | } 184 | buffer.WriteString("@") 185 | } 186 | 187 | // Compulsory hostname. 188 | buffer.WriteString(uri.Host) 189 | 190 | // Optional port number. 191 | if uri.Port != nil { 192 | buffer.WriteString(":") 193 | buffer.WriteString(strconv.Itoa(int(*uri.Port))) 194 | } 195 | 196 | if uri.UriParams.Length() > 0 { 197 | buffer.WriteString(";") 198 | buffer.WriteString(uri.UriParams.ToString(';')) 199 | } 200 | 201 | if uri.Headers.Length() > 0 { 202 | buffer.WriteString("?") 203 | buffer.WriteString(uri.Headers.ToString('&')) 204 | } 205 | 206 | return buffer.String() 207 | } 208 | 209 | // The special wildcard URI used in Contact: headers in REGISTER requests when expiring all registrations. 210 | type WildcardUri struct{} 211 | 212 | // Copy the wildcard URI. Not hard! 213 | func (uri WildcardUri) Copy() Uri { return uri } 214 | 215 | // Always returns 'true'. 216 | func (uri WildcardUri) IsWildcard() bool { 217 | return true 218 | } 219 | 220 | // Always returns '*' - the representation of a wildcard URI in a SIP message. 221 | func (uri WildcardUri) String() string { 222 | return "*" 223 | } 224 | 225 | // Determines if this wildcard URI equals the specified other URI. 226 | // This is true if and only if the other URI is also a wildcard URI. 227 | func (uri WildcardUri) Equals(other Uri) bool { 228 | switch other.(type) { 229 | case WildcardUri: 230 | return true 231 | default: 232 | return false 233 | } 234 | } 235 | 236 | // Generic list of parameters on a header. 237 | type Params interface { 238 | Get(k string) (MaybeString, bool) 239 | Add(k string, v MaybeString) Params 240 | Copy() Params 241 | Equals(p Params) bool 242 | ToString(sep uint8) string 243 | Length() int 244 | Items() map[string]MaybeString 245 | Keys() []string 246 | } 247 | 248 | type params struct { 249 | params map[string]MaybeString 250 | paramOrder []string 251 | } 252 | 253 | // Create an empty set of parameters. 254 | func NewParams() Params { 255 | return ¶ms{map[string]MaybeString{}, []string{}} 256 | } 257 | 258 | // Returns the entire parameter map. 259 | func (p *params) Items() map[string]MaybeString { 260 | return p.params 261 | } 262 | 263 | // Returns a slice of keys, in order. 264 | func (p *params) Keys() []string { 265 | return p.paramOrder 266 | } 267 | 268 | // Returns the requested parameter value. 269 | func (p *params) Get(k string) (MaybeString, bool) { 270 | v, ok := p.params[k] 271 | return v, ok 272 | } 273 | 274 | // Add a new parameter. 275 | func (p *params) Add(k string, v MaybeString) Params { 276 | // Add param to order list if new. 277 | if _, ok := p.params[k]; !ok { 278 | p.paramOrder = append(p.paramOrder, k) 279 | } 280 | 281 | // Set param value. 282 | p.params[k] = v 283 | 284 | // Return the params so calls can be chained. 285 | return p 286 | } 287 | 288 | // Copy a list of params. 289 | func (p *params) Copy() Params { 290 | dup := NewParams() 291 | for _, k := range p.Keys() { 292 | if v, ok := p.Get(k); ok { 293 | dup.Add(k, v) 294 | } else { 295 | log.Severe("Internal consistency error. Key %v present in param.Keys() but failed to Get()!", k) 296 | } 297 | } 298 | 299 | return dup 300 | } 301 | 302 | // Render params to a string. 303 | // Note that this does not escape special characters, this should already have been done before calling this method. 304 | func (p *params) ToString(sep uint8) string { 305 | var buffer bytes.Buffer 306 | first := true 307 | 308 | for _, k := range p.Keys() { 309 | v, ok := p.Get(k) 310 | if !ok { 311 | log.Severe("Internal consistency error. Key %v present in param.Keys() but failed to Get()!", k) 312 | continue 313 | } 314 | 315 | if !first { 316 | buffer.WriteString(fmt.Sprintf("%c", sep)) 317 | } 318 | first = false 319 | 320 | buffer.WriteString(fmt.Sprintf("%s", k)) 321 | 322 | switch v := v.(type) { 323 | case String: 324 | if strings.ContainsAny(v.String(), c_ABNF_WS) { 325 | buffer.WriteString(fmt.Sprintf("=\"%s\"", v.String())) 326 | } else { 327 | buffer.WriteString(fmt.Sprintf("=%s", v.String())) 328 | } 329 | } 330 | } 331 | 332 | return buffer.String() 333 | } 334 | 335 | // Returns number of params. 336 | func (p *params) Length() int { 337 | return len(p.params) 338 | } 339 | 340 | // Check if two maps of parameters are equal in the sense of having the same keys with the same values. 341 | // This does not rely on any ordering of the keys of the map in memory. 342 | func (p *params) Equals(q Params) bool { 343 | if p.Length() == 0 && q.Length() == 0 { 344 | return true 345 | } 346 | 347 | if p.Length() != q.Length() { 348 | return false 349 | } 350 | 351 | for k, p_val := range p.Items() { 352 | q_val, ok := q.Get(k) 353 | if !ok { 354 | return false 355 | } 356 | if p_val != q_val { 357 | return false 358 | } 359 | } 360 | 361 | return true 362 | } 363 | 364 | // Encapsulates a header that gossip does not natively support. 365 | // This allows header data that is not understood to be parsed by gossip and relayed to the parent application. 366 | type GenericHeader struct { 367 | // The name of the header. 368 | HeaderName string 369 | 370 | // The contents of the header, including any parameters. 371 | // This is transparent data that is not natively understood by gossip. 372 | Contents string 373 | } 374 | 375 | // Convert the header to a flat string representation. 376 | func (header *GenericHeader) String() string { 377 | return header.HeaderName + ": " + header.Contents 378 | } 379 | 380 | // Pull out the header name. 381 | func (h *GenericHeader) Name() string { 382 | return h.HeaderName 383 | } 384 | 385 | // Copy the header. 386 | func (h *GenericHeader) Copy() SipHeader { 387 | return &GenericHeader{h.HeaderName, h.Contents} 388 | } 389 | 390 | type ToHeader struct { 391 | // The display name from the header, may be omitted. 392 | DisplayName MaybeString 393 | 394 | Address Uri 395 | 396 | // Any parameters present in the header. 397 | Params Params 398 | } 399 | 400 | func (to *ToHeader) String() string { 401 | var buffer bytes.Buffer 402 | buffer.WriteString("To: ") 403 | 404 | switch s := to.DisplayName.(type) { 405 | case String: 406 | buffer.WriteString(fmt.Sprintf("\"%s\" ", s.String())) 407 | } 408 | 409 | buffer.WriteString(fmt.Sprintf("<%s>", to.Address)) 410 | 411 | if to.Params.Length() > 0 { 412 | buffer.WriteString(";") 413 | buffer.WriteString(to.Params.ToString(';')) 414 | } 415 | 416 | return buffer.String() 417 | } 418 | 419 | func (h *ToHeader) Name() string { return "To" } 420 | 421 | // Copy the header. 422 | func (h *ToHeader) Copy() SipHeader { 423 | return &ToHeader{h.DisplayName, h.Address.Copy(), h.Params.Copy()} 424 | } 425 | 426 | type FromHeader struct { 427 | // The display name from the header, may be omitted. 428 | DisplayName MaybeString 429 | 430 | Address Uri 431 | 432 | // Any parameters present in the header. 433 | Params Params 434 | } 435 | 436 | func (from *FromHeader) String() string { 437 | var buffer bytes.Buffer 438 | buffer.WriteString("From: ") 439 | 440 | switch s := from.DisplayName.(type) { 441 | case String: 442 | buffer.WriteString(fmt.Sprintf("\"%s\" ", s.String())) 443 | } 444 | 445 | buffer.WriteString(fmt.Sprintf("<%s>", from.Address)) 446 | if from.Params.Length() > 0 { 447 | buffer.WriteString(";") 448 | buffer.WriteString(from.Params.ToString(';')) 449 | } 450 | 451 | return buffer.String() 452 | } 453 | 454 | func (h *FromHeader) Name() string { return "From" } 455 | 456 | // Copy the header. 457 | func (h *FromHeader) Copy() SipHeader { 458 | return &FromHeader{h.DisplayName, h.Address.Copy(), h.Params.Copy()} 459 | } 460 | 461 | type ContactHeader struct { 462 | // The display name from the header, may be omitted. 463 | DisplayName MaybeString 464 | 465 | Address ContactUri 466 | 467 | // Any parameters present in the header. 468 | Params Params 469 | } 470 | 471 | func (contact *ContactHeader) String() string { 472 | var buffer bytes.Buffer 473 | buffer.WriteString("Contact: ") 474 | 475 | switch s := contact.DisplayName.(type) { 476 | case String: 477 | buffer.WriteString(fmt.Sprintf("\"%s\" ", s.String())) 478 | } 479 | 480 | switch contact.Address.(type) { 481 | case *WildcardUri: 482 | // Treat the Wildcard URI separately as it must not be contained in < > angle brackets. 483 | buffer.WriteString("*") 484 | default: 485 | buffer.WriteString(fmt.Sprintf("<%s>", contact.Address.String())) 486 | } 487 | 488 | if contact.Params.Length() > 0 { 489 | buffer.WriteString(";") 490 | buffer.WriteString(contact.Params.ToString(';')) 491 | } 492 | 493 | return buffer.String() 494 | } 495 | 496 | func (h *ContactHeader) Name() string { return "Contact" } 497 | 498 | // Copy the header. 499 | func (h *ContactHeader) Copy() SipHeader { 500 | return &ContactHeader{h.DisplayName, h.Address.Copy().(ContactUri), h.Params.Copy()} 501 | } 502 | 503 | type CallId string 504 | 505 | func (callId CallId) String() string { 506 | return "Call-Id: " + (string)(callId) 507 | } 508 | 509 | func (h *CallId) Name() string { return "Call-Id" } 510 | 511 | func (h *CallId) Copy() SipHeader { 512 | temp := *h 513 | return &temp 514 | } 515 | 516 | type CSeq struct { 517 | SeqNo uint32 518 | MethodName Method 519 | } 520 | 521 | func (cseq *CSeq) String() string { 522 | return fmt.Sprintf("CSeq: %d %s", cseq.SeqNo, cseq.MethodName) 523 | } 524 | 525 | func (h *CSeq) Name() string { return "CSeq" } 526 | 527 | func (h *CSeq) Copy() SipHeader { return &CSeq{h.SeqNo, h.MethodName} } 528 | 529 | type MaxForwards uint32 530 | 531 | func (maxForwards MaxForwards) String() string { 532 | return fmt.Sprintf("Max-Forwards: %d", ((int)(maxForwards))) 533 | } 534 | 535 | func (h MaxForwards) Name() string { return "Max-Forwards" } 536 | 537 | func (h MaxForwards) Copy() SipHeader { return h } 538 | 539 | type ContentLength uint32 540 | 541 | func (contentLength ContentLength) String() string { 542 | return fmt.Sprintf("Content-Length: %d", ((int)(contentLength))) 543 | } 544 | 545 | func (h ContentLength) Name() string { return "Content-Length" } 546 | 547 | func (h ContentLength) Copy() SipHeader { return h } 548 | 549 | type ViaHeader []*ViaHop 550 | 551 | // A single component in a Via header. 552 | // Via headers are composed of several segments of the same structure, added by successive nodes in a routing chain. 553 | type ViaHop struct { 554 | // E.g. 'SIP'. 555 | ProtocolName string 556 | 557 | // E.g. '2.0'. 558 | ProtocolVersion string 559 | Transport string 560 | Host string 561 | 562 | // The port for this via hop. This is stored as a pointer type, since it is an optional field. 563 | Port *uint16 564 | 565 | Params Params 566 | } 567 | 568 | func (hop *ViaHop) String() string { 569 | var buffer bytes.Buffer 570 | buffer.WriteString(fmt.Sprintf("%s/%s/%s %s", 571 | hop.ProtocolName, hop.ProtocolVersion, 572 | hop.Transport, 573 | hop.Host)) 574 | if hop.Port != nil { 575 | buffer.WriteString(fmt.Sprintf(":%d", *hop.Port)) 576 | } 577 | 578 | if hop.Params.Length() > 0 { 579 | buffer.WriteString(";") 580 | buffer.WriteString(hop.Params.ToString(';')) 581 | } 582 | 583 | return buffer.String() 584 | } 585 | 586 | // Return an exact copy of this ViaHop. 587 | func (hop *ViaHop) Copy() *ViaHop { 588 | var port *uint16 = nil 589 | if hop.Port != nil { 590 | temp := *hop.Port 591 | port = &temp 592 | } 593 | return &ViaHop{ 594 | hop.ProtocolName, 595 | hop.ProtocolVersion, 596 | hop.Transport, 597 | hop.Host, 598 | port, 599 | hop.Params.Copy(), 600 | } 601 | } 602 | 603 | func (via ViaHeader) String() string { 604 | var buffer bytes.Buffer 605 | buffer.WriteString("Via: ") 606 | for idx, hop := range via { 607 | buffer.WriteString(hop.String()) 608 | if idx != len(via)-1 { 609 | buffer.WriteString(", ") 610 | } 611 | } 612 | 613 | return buffer.String() 614 | } 615 | 616 | func (h ViaHeader) Name() string { return "Via" } 617 | 618 | func (h ViaHeader) Copy() SipHeader { 619 | dup := make([]*ViaHop, 0, len(h)) 620 | for _, hop := range h { 621 | dup = append(dup, hop.Copy()) 622 | } 623 | return ViaHeader(dup) 624 | } 625 | 626 | type RequireHeader struct { 627 | Options []string 628 | } 629 | 630 | func (header *RequireHeader) String() string { 631 | return fmt.Sprintf("Require: %s", 632 | strings.Join(header.Options, ", ")) 633 | } 634 | 635 | func (h *RequireHeader) Name() string { return "Require" } 636 | 637 | func (h *RequireHeader) Copy() SipHeader { 638 | dup := make([]string, len(h.Options)) 639 | copy(h.Options, dup) 640 | return &RequireHeader{dup} 641 | } 642 | 643 | type SupportedHeader struct { 644 | Options []string 645 | } 646 | 647 | func (header *SupportedHeader) String() string { 648 | return fmt.Sprintf("Supported: %s", 649 | strings.Join(header.Options, ", ")) 650 | } 651 | 652 | func (h *SupportedHeader) Name() string { return "Supported" } 653 | 654 | func (h *SupportedHeader) Copy() SipHeader { 655 | dup := make([]string, len(h.Options)) 656 | copy(h.Options, dup) 657 | return &SupportedHeader{dup} 658 | } 659 | 660 | type ProxyRequireHeader struct { 661 | Options []string 662 | } 663 | 664 | func (header *ProxyRequireHeader) String() string { 665 | return fmt.Sprintf("Proxy-Require: %s", 666 | strings.Join(header.Options, ", ")) 667 | } 668 | 669 | func (h *ProxyRequireHeader) Name() string { return "Proxy-Require" } 670 | 671 | func (h *ProxyRequireHeader) Copy() SipHeader { 672 | dup := make([]string, len(h.Options)) 673 | copy(h.Options, dup) 674 | return &ProxyRequireHeader{dup} 675 | } 676 | 677 | // 'Unsupported:' is a SIP header type - this doesn't indicate that the 678 | // header itself is not supported by gossip! 679 | type UnsupportedHeader struct { 680 | Options []string 681 | } 682 | 683 | func (header *UnsupportedHeader) String() string { 684 | return fmt.Sprintf("Unsupported: %s", 685 | strings.Join(header.Options, ", ")) 686 | } 687 | 688 | func (h *UnsupportedHeader) Name() string { return "Unsupported" } 689 | 690 | func (h *UnsupportedHeader) Copy() SipHeader { 691 | dup := make([]string, len(h.Options)) 692 | copy(h.Options, dup) 693 | return &UnsupportedHeader{dup} 694 | } 695 | -------------------------------------------------------------------------------- /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.ToLower(strings.TrimSpace(headerText[:colonIdx])) 727 | fieldText := strings.TrimSpace(headerText[colonIdx+1:]) 728 | if headerParser, ok := p.headerParsers[fieldName]; ok { 729 | // We have a registered parser for this header type - use it. 730 | headers, err = headerParser(fieldName, fieldText) 731 | } else { 732 | // We have no registered parser for this header type, 733 | // so we encapsulate the header data in a GenericHeader struct. 734 | log.Debug("Parser %p has no parser for header type %s", p, fieldName) 735 | header := base.GenericHeader{fieldName, fieldText} 736 | headers = []base.SipHeader{&header} 737 | } 738 | 739 | return 740 | } 741 | 742 | // Parse a To, From or Contact header line, producing one or more logical SipHeaders. 743 | func parseAddressHeader(headerName string, headerText string) ( 744 | headers []base.SipHeader, err error) { 745 | switch headerName { 746 | case "to", "from", "contact", "t", "f", "m": 747 | var displayNames []base.MaybeString 748 | var uris []base.Uri 749 | var paramSets []base.Params 750 | 751 | // Perform the actual parsing. The rest of this method is just typeclass bookkeeping. 752 | displayNames, uris, paramSets, err = parseAddressValues(headerText) 753 | 754 | if err != nil { 755 | return 756 | } 757 | if len(displayNames) != len(uris) || len(uris) != len(paramSets) { 758 | // This shouldn't happen unless parseAddressValues is bugged. 759 | err = fmt.Errorf("internal parser error: parsed param mismatch. "+ 760 | "%d display names, %d uris and %d param sets "+ 761 | "in %s.", 762 | len(displayNames), len(uris), len(paramSets), 763 | headerText) 764 | return 765 | } 766 | 767 | // Build a slice of headers of the appropriate kind, populating them with the values parsed above. 768 | // It is assumed that all headers returned by parseAddressValues are of the same kind, 769 | // although we do not check for this below. 770 | for idx := 0; idx < len(displayNames); idx++ { 771 | var header base.SipHeader 772 | if headerName == "to" || headerName == "t" { 773 | if idx > 0 { 774 | // Only a single To header is permitted in a SIP message. 775 | return nil, 776 | fmt.Errorf("Multiple to: headers in message:\n%s: %s", 777 | headerName, headerText) 778 | } 779 | switch uris[idx].(type) { 780 | case base.WildcardUri: 781 | // The Wildcard '*' URI is only permitted in Contact headers. 782 | err = fmt.Errorf("wildcard uri not permitted in to: "+ 783 | "header: %s", headerText) 784 | return 785 | default: 786 | toHeader := base.ToHeader{displayNames[idx], 787 | uris[idx], 788 | paramSets[idx]} 789 | header = &toHeader 790 | } 791 | } else if headerName == "from" || headerName == "f" { 792 | if idx > 0 { 793 | // Only a single From header is permitted in a SIP message. 794 | return nil, 795 | fmt.Errorf("Multiple from: headers in message:\n%s: %s", 796 | headerName, headerText) 797 | } 798 | switch uris[idx].(type) { 799 | case base.WildcardUri: 800 | // The Wildcard '*' URI is only permitted in Contact headers. 801 | err = fmt.Errorf("wildcard uri not permitted in from: "+ 802 | "header: %s", headerText) 803 | return 804 | default: 805 | fromHeader := base.FromHeader{displayNames[idx], 806 | uris[idx], 807 | paramSets[idx]} 808 | header = &fromHeader 809 | } 810 | } else if headerName == "contact" || headerName == "m" { 811 | switch uris[idx].(type) { 812 | case base.ContactUri: 813 | if uris[idx].(base.ContactUri).IsWildcard() { 814 | if paramSets[idx].Length() > 0 { 815 | // Wildcard headers do not contain parameters. 816 | err = fmt.Errorf("wildcard contact header should contain no parameters: '%s", 817 | headerText) 818 | return 819 | } 820 | if _, ok := displayNames[idx].(base.String); ok { 821 | // Wildcard headers do not contain display names. 822 | err = fmt.Errorf("wildcard contact header should contain no display name %s", 823 | headerText) 824 | return 825 | } 826 | } 827 | contactHeader := base.ContactHeader{displayNames[idx], 828 | uris[idx].(base.ContactUri), 829 | paramSets[idx]} 830 | header = &contactHeader 831 | default: 832 | // URIs in contact headers are restricted to being either SIP URIs or 'Contact: *'. 833 | return nil, 834 | fmt.Errorf("Uri %s not valid in Contact header. Must be SIP uri or '*'", uris[idx].String()) 835 | } 836 | } 837 | 838 | headers = append(headers, header) 839 | } 840 | } 841 | 842 | return 843 | } 844 | 845 | // Parse a string representation of a CSeq header, returning a slice of at most one CSeq. 846 | func parseCSeq(headerName string, headerText string) ( 847 | headers []base.SipHeader, err error) { 848 | var cseq base.CSeq 849 | 850 | parts := splitByWhitespace(headerText) 851 | if len(parts) != 2 { 852 | err = fmt.Errorf("CSeq field should have precisely one whitespace section: '%s'", 853 | headerText) 854 | return 855 | } 856 | 857 | var seqno uint64 858 | seqno, err = strconv.ParseUint(parts[0], 10, 32) 859 | if err != nil { 860 | return 861 | } 862 | 863 | if seqno > MAX_CSEQ { 864 | err = fmt.Errorf("invalid CSeq %d: exceeds maximum permitted value "+ 865 | "2**31 - 1", seqno) 866 | return 867 | } 868 | 869 | cseq.SeqNo = uint32(seqno) 870 | cseq.MethodName = base.Method(strings.TrimSpace(parts[1])) 871 | 872 | if strings.Contains(string(cseq.MethodName), ";") { 873 | err = fmt.Errorf("unexpected ';' in CSeq body: %s", headerText) 874 | return 875 | } 876 | 877 | headers = []base.SipHeader{&cseq} 878 | 879 | return 880 | } 881 | 882 | // Parse a string representation of a Call-Id header, returning a slice of at most one CallId. 883 | func parseCallId(headerName string, headerText string) ( 884 | headers []base.SipHeader, err error) { 885 | headerText = strings.TrimSpace(headerText) 886 | var callId base.CallId = base.CallId(headerText) 887 | 888 | if strings.ContainsAny(string(callId), c_ABNF_WS) { 889 | err = fmt.Errorf("unexpected whitespace in CallId header body '%s'", headerText) 890 | return 891 | } 892 | if strings.Contains(string(callId), ";") { 893 | err = fmt.Errorf("unexpected semicolon in CallId header body '%s'", headerText) 894 | return 895 | } 896 | if len(string(callId)) == 0 { 897 | err = fmt.Errorf("empty Call-Id body") 898 | return 899 | } 900 | 901 | headers = []base.SipHeader{&callId} 902 | 903 | return 904 | } 905 | 906 | // Parse a string representation of a Via header, returning a slice of at most one ViaHeader. 907 | // Note that although Via headers may contain a comma-separated list, RFC 3261 makes it clear that 908 | // these should not be treated as separate logical Via headers, but as multiple values on a single 909 | // Via header. 910 | func parseViaHeader(headerName string, headerText string) ( 911 | headers []base.SipHeader, err error) { 912 | sections := strings.Split(headerText, ",") 913 | var via base.ViaHeader = base.ViaHeader{} 914 | for _, section := range sections { 915 | var hop base.ViaHop 916 | parts := strings.Split(section, "/") 917 | 918 | if len(parts) < 3 { 919 | err = fmt.Errorf("not enough protocol parts in via header: '%s'", 920 | parts) 921 | return 922 | } 923 | 924 | parts[2] = strings.Join(parts[2:], "/") 925 | 926 | // The transport part ends when whitespace is reached, but may also start with 927 | // whitespace. 928 | // So the end of the transport part is the first whitespace char following the 929 | // first non-whitespace char. 930 | initialSpaces := len(parts[2]) - len(strings.TrimLeft(parts[2], c_ABNF_WS)) 931 | sentByIdx := strings.IndexAny(parts[2][initialSpaces:], c_ABNF_WS) + initialSpaces + 1 932 | if sentByIdx == 0 { 933 | err = fmt.Errorf("expected whitespace after sent-protocol part "+ 934 | "in via header '%s'", section) 935 | return 936 | } else if sentByIdx == 1 { 937 | err = fmt.Errorf("empty transport field in via header '%s'", section) 938 | return 939 | } 940 | 941 | hop.ProtocolName = strings.TrimSpace(parts[0]) 942 | hop.ProtocolVersion = strings.TrimSpace(parts[1]) 943 | hop.Transport = strings.TrimSpace(parts[2][:sentByIdx-1]) 944 | 945 | if len(hop.ProtocolName) == 0 { 946 | err = fmt.Errorf("no protocol name provided in via header '%s'", section) 947 | } else if len(hop.ProtocolVersion) == 0 { 948 | err = fmt.Errorf("no version provided in via header '%s'", section) 949 | } else if len(hop.Transport) == 0 { 950 | err = fmt.Errorf("no transport provided in via header '%s'", section) 951 | } 952 | if err != nil { 953 | return 954 | } 955 | 956 | viaBody := parts[2][sentByIdx:] 957 | 958 | paramsIdx := strings.Index(viaBody, ";") 959 | var host string 960 | var port *uint16 961 | if paramsIdx == -1 { 962 | // There are no header parameters, so the rest of the Via body is part of the host[:post]. 963 | host, port, err = parseHostPort(viaBody) 964 | hop.Host = host 965 | hop.Port = port 966 | if err != nil { 967 | return 968 | } 969 | hop.Params = base.NewParams() 970 | } else { 971 | host, port, err = parseHostPort(viaBody[:paramsIdx]) 972 | if err != nil { 973 | return 974 | } 975 | hop.Host = host 976 | hop.Port = port 977 | 978 | hop.Params, _, err = parseParams(viaBody[paramsIdx:], 979 | ';', ';', 0, true, true) 980 | } 981 | via = append(via, &hop) 982 | } 983 | 984 | headers = []base.SipHeader{&via} 985 | return 986 | } 987 | 988 | // Parse a string representation of a Max-Forwards header into a slice of at most one MaxForwards header object. 989 | func parseMaxForwards(headerName string, headerText string) ( 990 | headers []base.SipHeader, err error) { 991 | var maxForwards base.MaxForwards 992 | var value uint64 993 | value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32) 994 | maxForwards = base.MaxForwards(value) 995 | 996 | headers = []base.SipHeader{&maxForwards} 997 | return 998 | } 999 | 1000 | // Parse a string representation of a Content-Length header into a slice of at most one ContentLength header object. 1001 | func parseContentLength(headerName string, headerText string) ( 1002 | headers []base.SipHeader, err error) { 1003 | var contentLength base.ContentLength 1004 | var value uint64 1005 | value, err = strconv.ParseUint(strings.TrimSpace(headerText), 10, 32) 1006 | contentLength = base.ContentLength(value) 1007 | 1008 | headers = []base.SipHeader{&contentLength} 1009 | return 1010 | } 1011 | 1012 | // parseAddressValues parses a comma-separated list of addresses, returning 1013 | // any display names and header params, as well as the SIP URIs themselves. 1014 | // parseAddressValues is aware of < > bracketing and quoting, and will not 1015 | // break on commas within these structures. 1016 | func parseAddressValues(addresses string) ( 1017 | displayNames []base.MaybeString, uris []base.Uri, 1018 | headerParams []base.Params, err error) { 1019 | 1020 | prevIdx := 0 1021 | inBrackets := false 1022 | inQuotes := false 1023 | 1024 | // Append a comma to simplify the parsing code; we split address sections 1025 | // on commas, so use a comma to signify the end of the final address section. 1026 | addresses = addresses + "," 1027 | 1028 | for idx, char := range addresses { 1029 | if char == '<' && !inQuotes { 1030 | inBrackets = true 1031 | } else if char == '>' && !inQuotes { 1032 | inBrackets = false 1033 | } else if char == '"' { 1034 | inQuotes = !inQuotes 1035 | } else if !inQuotes && !inBrackets && char == ',' { 1036 | var displayName base.MaybeString 1037 | var uri base.Uri 1038 | var params base.Params 1039 | displayName, uri, params, err = 1040 | parseAddressValue(addresses[prevIdx:idx]) 1041 | if err != nil { 1042 | return 1043 | } 1044 | prevIdx = idx + 1 1045 | 1046 | displayNames = append(displayNames, displayName) 1047 | uris = append(uris, uri) 1048 | headerParams = append(headerParams, params) 1049 | } 1050 | } 1051 | 1052 | return 1053 | } 1054 | 1055 | // parseAddressValue parses an address - such as from a From, To, or 1056 | // Contact header. It returns: 1057 | // - a MaybeString containing the display name (or not) 1058 | // - a parsed SipUri object 1059 | // - a map containing any header parameters present 1060 | // - the error object 1061 | // See RFC 3261 section 20.10 for details on parsing an address. 1062 | // Note that this method will not accept a comma-separated list of addresses; 1063 | // addresses in that form should be handled by parseAddressValues. 1064 | func parseAddressValue(addressText string) ( 1065 | displayName base.MaybeString, uri base.Uri, 1066 | headerParams base.Params, err error) { 1067 | 1068 | headerParams = base.NewParams() 1069 | 1070 | if len(addressText) == 0 { 1071 | err = fmt.Errorf("address-type header has empty body") 1072 | return 1073 | } 1074 | 1075 | addressTextCopy := addressText 1076 | addressText = strings.TrimSpace(addressText) 1077 | 1078 | firstAngleBracket := findUnescaped(addressText, '<', quotes_delim) 1079 | firstSpace := findAnyUnescaped(addressText, c_ABNF_WS, quotes_delim, angles_delim) 1080 | displayName = base.NoString{} 1081 | if firstAngleBracket != -1 && firstSpace != -1 && 1082 | firstSpace < firstAngleBracket { 1083 | // There is a display name present. Let's parse it. 1084 | if addressText[0] == '"' { 1085 | // The display name is within quotations. 1086 | addressText = addressText[1:] 1087 | nextQuote := strings.Index(addressText, "\"") 1088 | 1089 | if nextQuote == -1 { 1090 | // Unclosed quotes - parse error. 1091 | err = fmt.Errorf("Unclosed quotes in header text: %s", 1092 | addressTextCopy) 1093 | return 1094 | } 1095 | 1096 | nameField := addressText[:nextQuote] 1097 | displayName = base.String{nameField} 1098 | addressText = addressText[nextQuote+1:] 1099 | } else { 1100 | // The display name is unquoted, so match until the next whitespace 1101 | // character. 1102 | nameField := addressText[:firstSpace] 1103 | displayName = base.String{nameField} 1104 | addressText = addressText[firstSpace+1:] 1105 | } 1106 | } 1107 | 1108 | // Work out where the SIP URI starts and ends. 1109 | addressText = strings.TrimSpace(addressText) 1110 | var endOfUri int 1111 | var startOfParams int 1112 | if addressText[0] != '<' { 1113 | switch displayName.(type) { 1114 | case base.String: 1115 | // The address must be in if a display name is 1116 | // present, so this is an invalid address line. 1117 | err = fmt.Errorf("Invalid character '%c' following display "+ 1118 | "name in address line; expected '<': %s", 1119 | addressText[0], addressTextCopy) 1120 | return 1121 | } 1122 | 1123 | endOfUri = strings.Index(addressText, ";") 1124 | if endOfUri == -1 { 1125 | endOfUri = len(addressText) 1126 | } 1127 | startOfParams = endOfUri 1128 | 1129 | } else { 1130 | addressText = addressText[1:] 1131 | endOfUri = strings.Index(addressText, ">") 1132 | if endOfUri == 0 { 1133 | err = fmt.Errorf("'<' without closing '>' in address %s", 1134 | addressTextCopy) 1135 | return 1136 | } 1137 | startOfParams = endOfUri + 1 1138 | 1139 | } 1140 | 1141 | // Now parse the SIP URI. 1142 | uri, err = ParseUri(addressText[:endOfUri]) 1143 | if err != nil { 1144 | return 1145 | } 1146 | 1147 | if startOfParams >= len(addressText) { 1148 | return 1149 | } 1150 | 1151 | // Finally, parse any header parameters and then return. 1152 | addressText = addressText[startOfParams:] 1153 | headerParams, _, err = parseParams(addressText, ';', ';', ',', true, true) 1154 | return 1155 | } 1156 | 1157 | // Extract the next logical header line from the message. 1158 | // This may run over several actual lines; lines that start with whitespace are 1159 | // a continuation of the previous line. 1160 | // Therefore also return how many lines we consumed so the parent parser can 1161 | // keep track of progress through the message. 1162 | func getNextHeaderLine(contents []string) (headerText string, consumed int) { 1163 | if len(contents) == 0 { 1164 | return 1165 | } 1166 | if len(contents[0]) == 0 { 1167 | return 1168 | } 1169 | 1170 | var buffer bytes.Buffer 1171 | buffer.WriteString(contents[0]) 1172 | 1173 | for consumed = 1; consumed < len(contents); consumed++ { 1174 | firstChar, _ := utf8.DecodeRuneInString(contents[consumed]) 1175 | if !unicode.IsSpace(firstChar) { 1176 | break 1177 | } else if len(contents[consumed]) == 0 { 1178 | break 1179 | } 1180 | 1181 | buffer.WriteString(" " + strings.TrimSpace(contents[consumed])) 1182 | } 1183 | 1184 | headerText = buffer.String() 1185 | return 1186 | } 1187 | 1188 | // A delimiter is any pair of characters used for quoting text (i.e. bulk escaping literals). 1189 | type delimiter struct { 1190 | start uint8 1191 | end uint8 1192 | } 1193 | 1194 | // Define common quote characters needed in parsing. 1195 | var quotes_delim = delimiter{'"', '"'} 1196 | var angles_delim = delimiter{'<', '>'} 1197 | 1198 | // Find the first instance of the target in the given text which is not enclosed in any delimiters 1199 | // from the list provided. 1200 | func findUnescaped(text string, target uint8, delims ...delimiter) int { 1201 | return findAnyUnescaped(text, string(target), delims...) 1202 | } 1203 | 1204 | // Find the first instance of any of the targets in the given text that are not enclosed in any delimiters 1205 | // from the list provided. 1206 | func findAnyUnescaped(text string, targets string, delims ...delimiter) int { 1207 | escaped := false 1208 | var endEscape uint8 = 0 1209 | 1210 | endChars := make(map[uint8]uint8) 1211 | for _, delim := range delims { 1212 | endChars[delim.start] = delim.end 1213 | } 1214 | 1215 | for idx := 0; idx < len(text); idx++ { 1216 | if !escaped && strings.Contains(targets, string(text[idx])) { 1217 | return idx 1218 | } 1219 | 1220 | if escaped { 1221 | escaped = (text[idx] != endEscape) 1222 | continue 1223 | } else { 1224 | endEscape, escaped = endChars[text[idx]] 1225 | } 1226 | } 1227 | 1228 | return -1 1229 | } 1230 | 1231 | // Splits the given string into sections, separated by one or more characters 1232 | // from c_ABNF_WS. 1233 | func splitByWhitespace(text string) []string { 1234 | var buffer bytes.Buffer 1235 | var inString bool = true 1236 | result := make([]string, 0) 1237 | 1238 | for _, char := range text { 1239 | s := string(char) 1240 | if strings.Contains(c_ABNF_WS, s) { 1241 | if inString { 1242 | // First whitespace char following text; flush buffer to the results array. 1243 | result = append(result, buffer.String()) 1244 | buffer.Reset() 1245 | } 1246 | inString = false 1247 | } else { 1248 | buffer.WriteString(s) 1249 | inString = true 1250 | } 1251 | } 1252 | 1253 | if buffer.Len() > 0 { 1254 | result = append(result, buffer.String()) 1255 | } 1256 | 1257 | return result 1258 | } 1259 | --------------------------------------------------------------------------------