func (mng *Manager) putTx(tx Transaction) { viaHeaders := tx.Origin().Headers("Via") if len(viaHeaders) == 0 { log.Warn("No Via header on new transaction. Transaction will be dropped.") return } via, ok := viaHeaders[0].(*base.ViaHeader) if !ok { // TODO: Handle this better. panic(errors.New("Headers('Via') returned non-Via header!")) } branch, ok := (*via)[0].Params.Get("branch") if !ok { log.Warn("No branch parameter on top Via header. Transaction will be dropped.") return } var k key switch branch := branch.(type) { case base.String: k = key{branch.String(), string(tx.Origin().Method)} case base.NoString: log.Warn("Empty branch parameter on top Via header. Transaction will be dropped.") return default: log.Warn("Unexpected type of branch value on top Via header: %T", branch) return } mng.txLock.Lock() mng.txs[k] = tx mng.txLock.Unlock() }
// Management loop for the connTable. // Handles notifications of connection updates, expiries of connections, and // the termination of the routine. func (t *connTable) manage() { for { select { case request := <-t.connRequests: watcher := t.conns[request.addr] if watcher != nil { request.responseChan <- watcher.conn } else { request.responseChan <- nil } case update := <-t.updates: t.handleUpdate(update) case addr := <-t.expiries: if t.conns[addr].expiryTime.Before(time.Now()) { log.Debug("Conntable %p notified that the watcher for address %s has expired. Remove it.", t, addr) t.conns[addr].stop <- true t.conns[addr].conn.Close() delete(t.conns, addr) } else { // Due to a race condition, the socket has been updated since this expiry happened. // Ignore the expiry since we already have a new socket for this address. log.Warn("Ignored spurious expiry for address %s in conntable %p", t, addr) } case <-t.stop: log.Info("Conntable %p stopped") t.stopped = true for _, watcher := range t.conns { watcher.stop <- true watcher.conn.Close() } break } } }
// Create Client transaction. func (mng *Manager) Send(r *base.Request, dest string) *ClientTransaction { log.Debug("Sending to %v: %v", dest, r.String()) tx := &ClientTransaction{} tx.origin = r tx.dest = dest tx.transport = mng.transport tx.tm = mng tx.initFSM() tx.tu = make(chan *base.Response, 3) tx.tu_err = make(chan error, 1) tx.timer_a_time = T1 tx.timer_a = time.AfterFunc(tx.timer_a_time, func() { tx.fsm.Spin(client_input_timer_a) }) tx.timer_b = time.AfterFunc(64*T1, func() { tx.fsm.Spin(client_input_timer_b) }) // Timer D is set to 32 seconds for unreliable transports, and 0 seconds otherwise. tx.timer_d_time = 32 * time.Second err := mng.transport.Send(dest, r) if err != nil { log.Warn("Failed to send message: %s", err.Error()) tx.fsm.Spin(client_input_transport_err) } mng.putTx(tx) return tx }
func (connection *connection) pipeOutput() { for { select { case message, ok := <-connection.parsedMessages: if ok { log.Debug("Connection %p from %s to %s received message over the wire: %s", connection, connection.baseConn.RemoteAddr(), connection.baseConn.LocalAddr(), message.Short()) connection.output <- message } else { break } case err, ok := <-connection.parserErrors: if ok { // The parser has hit a terminal error. We need to restart it. log.Warn("Failed to parse SIP message: %s", err.Error()) connection.parser = parser.NewParser(connection.parsedMessages, connection.parserErrors, connection.isStreamed) } else { break } } } log.Info("Parser stopped in ConnWrapper %v (local addr %s; remote addr %s); stopping listening", connection, connection.baseConn.LocalAddr(), connection.baseConn.RemoteAddr()) }
func (caller *endpoint) Invite(callee *endpoint) error { // Starting a dialog. callid := "thisisacall" + string(caller.dialogIx) tagString := fmt.Sprintf("tag.%s.%s", caller.username, caller.host) branch := "z9hG4bK.callbranch.INVITE" caller.dialog.callId = callid caller.dialog.from_tag = base.String{tagString} caller.dialog.currentTx = txInfo{} caller.dialog.currentTx.branch = branch invite := base.NewRequest( base.INVITE, &base.SipUri{ User: &callee.username, Host: callee.host, }, "SIP/2.0", []base.SipHeader{ Via(caller, branch), To(callee, caller.dialog.to_tag), From(caller, caller.dialog.from_tag), Contact(caller), CSeq(caller.dialog.cseq, base.INVITE), CallId(callid), ContentLength(0), }, "", ) caller.dialog.cseq += 1 log.Info("Sending: %v", invite.Short()) tx := caller.tm.Send(invite, fmt.Sprintf("%v:%v", callee.host, callee.port)) caller.dialog.currentTx.tx = transaction.Transaction(tx) for { select { case r := <-tx.Responses(): log.Info("Received response: %v", r.Short()) log.Debug("Full form:\n%v\n", r.String()) // Get To tag if present. tag, ok := r.Headers("To")[0].(*base.ToHeader).Params.Get("tag") if ok { caller.dialog.to_tag = tag.(base.String) } switch { case r.StatusCode >= 300: // Call setup failed. return fmt.Errorf("callee sent negative response code %v.", r.StatusCode) case r.StatusCode >= 200: // Ack 200s manually. log.Info("Sending Ack") tx.Ack() return nil } case e := <-tx.Errors(): log.Warn(e.Error()) return e } } }
func (udp *Udp) listen(conn *net.UDPConn) { log.Info("Begin listening for UDP on address %s", conn.LocalAddr()) buffer := make([]byte, c_BUFSIZE) for { num, _, err := conn.ReadFromUDP(buffer) if err != nil { if udp.stop { log.Info("Stopped listening for UDP on %s", conn.LocalAddr) break } else { log.Severe("Failed to read from UDP buffer: " + err.Error()) continue } } pkt := append([]byte(nil), buffer[:num]...) go func() { msg, err := parser.ParseMessage(pkt) if err != nil { log.Warn("Failed to parse SIP message: %s", err.Error()) } else { udp.output <- msg } }() } }
// Give a received response to the correct transaction. func (mng *Manager) correlate(r *base.Response) { tx, ok := mng.getTx(r) if !ok { // TODO: Something log.Warn("Failed to correlate response to active transaction. Dropping it.") return } tx.Receive(r) }
func (tx *ServerTransaction) Receive(m base.SipMessage) { r, ok := m.(*base.Request) if !ok { log.Warn("Client transaction received request") } var input fsm.Input = fsm.NO_INPUT switch { case r.Method == tx.origin.Method: input = server_input_request case r.Method == base.ACK: input = server_input_ack tx.ack <- r default: log.Warn("Invalid message correlated to server transaction.") } tx.fsm.Spin(input) }
// Gets a transaction from the transaction store. // Should only be called inside the storage handling goroutine to ensure concurrency safety. func (mng *Manager) getTx(s base.SipMessage) (Transaction, bool) { key, ok := mng.makeKey(s) if !ok { // TODO: Here we should initiate more intense searching as specified in RFC3261 section 17 log.Warn("Could not correlate message to transaction by branch/method. Dropping.") return nil, false } mng.txLock.RLock() tx, ok := mng.txs[key] mng.txLock.RUnlock() return tx, ok }
func main() { log.SetDefaultLogLevel(log.DEBUG) err := caller.Start() if err != nil { log.Warn("Failed to start caller: %v", err) return } // Receive an incoming call. caller.ServeInvite() <-time.After(2 * time.Second) // Send the BYE caller.Bye(callee) }
func (caller *endpoint) nonInvite(callee *endpoint, method base.Method) error { caller.dialog.currentTx.branch = "z9hG4bK.callbranch." + string(method) request := base.NewRequest( method, &base.SipUri{ User: &callee.username, Host: callee.host, }, "SIP/2.0", []base.SipHeader{ Via(caller, caller.dialog.currentTx.branch), To(callee, caller.dialog.to_tag), From(caller, caller.dialog.from_tag), Contact(caller), CSeq(caller.dialog.cseq, method), CallId(caller.dialog.callId), ContentLength(0), }, "", ) caller.dialog.cseq += 1 log.Info("Sending: %v", request.Short()) tx := caller.tm.Send(request, fmt.Sprintf("%v:%v", callee.host, callee.port)) caller.dialog.currentTx.tx = transaction.Transaction(tx) for { select { case r := <-tx.Responses(): log.Info("Received response: %v", r.Short()) log.Debug("Full form:\n%v\n", r.String()) switch { case r.StatusCode >= 300: // Failure (or redirect). return fmt.Errorf("callee sent negative response code %v.", r.StatusCode) case r.StatusCode >= 200: // Success. log.Info("Successful transaction") return nil } case e := <-tx.Errors(): log.Warn(e.Error()) return e } } }
func (tx *ClientTransaction) Receive(m base.SipMessage) { r, ok := m.(*base.Response) if !ok { log.Warn("Client transaction received request") } tx.lastResp = r var input fsm.Input switch { case r.StatusCode < 200: input = client_input_1xx case r.StatusCode < 300: input = client_input_2xx default: input = client_input_300_plus } tx.fsm.Spin(input) }
// Handle a request. func (mng *Manager) request(r *base.Request) { t, ok := mng.getTx(r) if ok { t.Receive(r) return } // If we failed to correlate an ACK, just drop it. if r.Method == base.ACK { log.Warn("Couldn't correlate ACK to an open transaction. Dropping it.") return } // Create a new transaction tx := &ServerTransaction{} tx.tm = mng tx.origin = r tx.transport = mng.transport // Use the remote address in the top Via header. This is not correct behaviour. viaHeaders := tx.Origin().Headers("Via") if len(viaHeaders) == 0 { log.Warn("No Via header on new transaction. Transaction will be dropped.") return } via, ok := viaHeaders[0].(*base.ViaHeader) if !ok { panic(errors.New("Headers('Via') returned non-Via header!")) } if len(*via) == 0 { log.Warn("Via header contained no hops! Transaction will be dropped.") return } hop := (*via)[0] port := uint16(5060) if hop.Port != nil { port = *hop.Port } tx.dest = fmt.Sprintf("%s:%d", hop.Host, port) tx.transport = mng.transport tx.initFSM() tx.tu = make(chan *base.Response, 3) tx.tu_err = make(chan error, 1) tx.ack = make(chan *base.Request, 1) // Send a 100 Trying immediately. // Technically we shouldn't do this if we trustthe user to do it within 200ms, // but I'm not sure how to handle that situation right now. // Pretend the user sent us a 100 to send. trying := base.NewResponse( "SIP/2.0", 100, "Trying", []base.SipHeader{}, "", ) base.CopyHeaders("Via", tx.origin, trying) base.CopyHeaders("From", tx.origin, trying) base.CopyHeaders("To", tx.origin, trying) base.CopyHeaders("Call-Id", tx.origin, trying) base.CopyHeaders("CSeq", tx.origin, trying) tx.lastResp = trying tx.fsm.Spin(server_input_user_1xx) mng.requests <- tx }
func (tx *ClientTransaction) Delete() { log.Warn("Tx: %p, tm: %p", tx, tx.tm) tx.tm.delTx(tx) }