Skip to content
Snippets Groups Projects
chan_sip.c 107 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Implementation of Session Initiation Protocol
     * 
     * Copyright (C) 1999, Mark Spencer
     *
     * Mark Spencer <markster@linux-support.net>
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    #include <stdio.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <ctype.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <pthread.h>
    #include <string.h>
    #include <asterisk/lock.h>
    #include <asterisk/channel.h>
    #include <asterisk/channel_pvt.h>
    #include <asterisk/config.h>
    #include <asterisk/logger.h>
    #include <asterisk/module.h>
    #include <asterisk/pbx.h>
    #include <asterisk/options.h>
    #include <asterisk/lock.h>
    #include <asterisk/sched.h>
    #include <asterisk/io.h>
    #include <asterisk/rtp.h>
    #include <asterisk/acl.h>
    #include <asterisk/callerid.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/cli.h>
    #include <asterisk/md5.h>
    #include <asterisk/app.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/socket.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/ioctl.h>
    #include <net/if.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <errno.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <sys/signal.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <netinet/ip.h>
    
    /* #define VOCAL_DATA_HACK */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    #define SIPDUMPER
    #define DEFAULT_EXPIREY 120
    #define MAX_EXPIREY     3600
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define DEFAULT_MAXMS		2000		/* Must be faster than 2 seconds by default */
    
    #define DEFAULT_MAXMS		2000		/* Must be faster than 2 seconds by default */
    #define DEFAULT_FREQ_OK		60 * 1000		/* How often to check for the host to be up */
    #define DEFAULT_FREQ_NOTOK	10 * 1000		/* How often to check, if the host is down... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char *desc = "Session Initiation Protocol (SIP)";
    static char *type = "sip";
    static char *tdesc = "Session Initiation Protocol (SIP)";
    static char *config = "sip.conf";
    
    #define DEFAULT_SIP_PORT	5060	/* From RFC 2543 */
    #define SIP_MAX_PACKET	1500		/* Also from RFC 2543, should sub headers tho */
    
    static char context[AST_MAX_EXTENSION] = "default";
    
    static char language[MAX_LANGUAGE] = "";
    
    static int usecnt =0;
    static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
    
    /* Protect the interface list (of sip_pvt's) */
    static pthread_mutex_t iflock = AST_MUTEX_INITIALIZER;
    
    /* Protect the monitoring thread, so only one process can kill or start it, and not
       when it's doing something critical. */
    static pthread_mutex_t netlock = AST_MUTEX_INITIALIZER;
    
    static pthread_mutex_t monlock = AST_MUTEX_INITIALIZER;
    
    /* This is the thread for the monitor which checks for input on the channels
       which are not currently in use.  */
    static pthread_t monitor_thread = 0;
    
    static int restart_monitor(void);
    
    /* Just about everybody seems to support ulaw, so make it a nice default */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char ourhost[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct in_addr __ourip;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int ourport;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sipdebug = 0;
    
    static int tos = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* Expire slowly */
    static int expirey = 900;
    
    static struct sched_context *sched;
    static struct io_context *io;
    /* The private structures of the  sip channels are linked for
       selecting outgoing channels */
       
    #define SIP_MAX_HEADERS		64
    #define SIP_MAX_LINES 		64
    
    struct sip_request {
    
    Mark Spencer's avatar
    Mark Spencer committed
      char *rlPart1; /* SIP Method Name or "SIP/2.0" protocol version */
      char *rlPart2; /* The Request URI or Response Status */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int len;
    	int headers;					/* SIP Headers */
    	char *header[SIP_MAX_HEADERS];
    	int lines;						/* SDP Content */
    	char *line[SIP_MAX_LINES];
    	char data[SIP_MAX_PACKET];
    };
    
    static struct sip_pvt {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_mutex_t lock;				/* Channel private lock */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char callid[80];					/* Global CallID */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char randdata[80];	/* Random data */
    	unsigned int ocseq;					/* Current outgoing seqno */
    	unsigned int icseq;					/* Current incoming seqno */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int lastinvite;						/* Last Cseq of invite */
    	int alreadygone;					/* Whether or not we've already been destroyed by or peer */
    	int needdestroy;					/* if we need to be destroyed */
    	int capability;						/* Special capability */
    	int outgoing;						/* Outgoing or incoming call? */
    	int insecure;						/* Don't check source port/ip */
    	int expirey;						/* How long we take to expire */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int branch;							/* One random number */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int canreinvite;					/* Do we support reinvite */
    	int progress;						/* Have sent 183 message progress */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int tag;							/* Another random number */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sockaddr_in sa;				/* Our peer */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct in_addr ourip;				/* Our IP */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *owner;			/* Who owns us */
    	char exten[AST_MAX_EXTENSION];		/* Extention where to start */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char refer_to[AST_MAX_EXTENSION];	/* Place to store REFER-TO extension */
    	char referred_by[AST_MAX_EXTENSION];/* Place to store REFERRED-BY extension */
    	char refer_contact[AST_MAX_EXTENSION];/* Place to store Contact info from a REFER extension */
    	char record_route[256];
    	char record_route_info[256];
    	char remote_party_id[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char context[AST_MAX_EXTENSION];
    	char language[MAX_LANGUAGE];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char theirtag[256];				/* Their tag */
    	char username[81];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char peername[81];
    	char peersecret[81];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char callerid[256];					/* Caller*ID */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char via[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char accountcode[256];				/* Account code */
    	int amaflags;						/* AMA Flags */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_request initreq;			/* Initial request */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	int maxtime;						/* Max time for first response */
    	int initid;							/* Auto-congest ID if appropriate */
    	
    	struct sip_peer *peerpoke;			/* If this calls is to poke a peer, which one */
    	struct sip_registry *registry;			/* If this is a REGISTER call, to which registry */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_rtp *rtp;				/* RTP Session */
    	struct sip_pvt *next;
    } *iflist = NULL;
    
    static struct sip_pkt {
    	int retrans;
    	struct sip_pvt *owner;
    	int packetlen;
    	char data[SIP_MAX_PACKET];
    	struct sip_pkt *next;
    } *packets = NULL;	
    
    struct sip_user {
    	/* Users who can access various contexts */
    	char name[80];
    	char secret[80];
    	char context[80];
    	char callerid[80];
    	char methods[80];
    	char accountcode[80];
    	int hascallerid;
    	int amaflags;
    	int insecure;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int canreinvite;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_ha *ha;
    	struct sip_user *next;
    };
    
    struct sip_peer {
    	char name[80];
    	char secret[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char context[80];		/* JK02: peers need context too to allow parking etc */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char methods[80];
    	char username[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char mailbox[AST_MAX_EXTENSION];
    
    	int lastmsgssent;
    	time_t	lastmsgcheck;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int dynamic;
    	int expire;
    	int expirey;
    	int capability;
    	int insecure;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int canreinvite;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sockaddr_in addr;
    	struct in_addr mask;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Qualification */
    	struct sip_pvt *call;		/* Call pointer */
    	int pokeexpire;				/* When to expire poke */
    	int lastms;					/* How long last response took (in ms), or -1 for no response */
    	int maxms;					/* Max ms we will accept for the host to be up, 0 to not monitor */
    	struct timeval ps;			/* Ping send time */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	struct sockaddr_in defaddr;
    	struct ast_ha *ha;
    	int delme;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int lastmsg;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_peer *next;
    };
    
    static struct ast_user_list {
    	struct sip_user *users;
    	pthread_mutex_t lock;
    } userl = { NULL, AST_MUTEX_INITIALIZER };
    
    static struct ast_peer_list {
    	struct sip_peer *peers;
    	pthread_mutex_t lock;
    } peerl = { NULL, AST_MUTEX_INITIALIZER };
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define REG_STATE_UNREGISTERED 0
    #define REG_STATE_REGSENT	   1
    #define REG_STATE_AUTHSENT 	   2
    #define REG_STATE_REGISTERED   3
    #define REG_STATE_REJECTED	   4
    #define REG_STATE_TIMEOUT	   5
    #define REG_STATE_NOAUTH	   6
    
    struct sip_registry {
    	pthread_mutex_t lock;				/* Channel private lock */
    	struct sockaddr_in addr;		/* Who we connect to for registration purposes */
    	char username[80];
    	char secret[80];			/* Password or key name in []'s */
    	char random[80];
    	int expire;					/* Sched ID of expiration */
    	int timeout; 					/* sched id of sip_reg_timeout */
    	int refresh;					/* How often to refresh */
    	struct sip_pvt *call;				/* create a sip_pvt structure for each outbound "registration call" in progress */
    	int regstate;
    	int callid_valid;		/* 0 means we haven't chosen callid for this registry yet. */
    	char callid[80];		/* Global CallID for this registry */
    	struct sockaddr_in us;			/* Who the server thinks we are */
    	struct sip_registry *next;
    };
    
    static int sip_do_register(struct sip_registry *r);
    struct sip_registry *registrations;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sipsock  = -1;
    
    static struct sockaddr_in bindaddr;
    
    static struct ast_frame  *sip_read(struct ast_channel *ast);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req);
    static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_request(struct sip_pvt *p, char *msg, int inc);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_invite(struct sip_pvt *p, char *msg, int sendsdp, char *auth, char *vxml_url);
    static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp);
    static int transmit_message_with_text(struct sip_pvt *p, char *text);
    static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int __sip_xmit(struct sip_pvt *p, char *data, int len)
    {
    	int res;
        res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_in));
    	if (res != len) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s returned %d: %s\n", data, len, inet_ntoa(p->sa.sin_addr), res, strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	return res;
    }
    
    static int send_response(struct sip_pvt *p, struct sip_request *req)
    {
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sipdebug)
    		ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = __sip_xmit(p, req->data, req->len);
    	if (res > 0)
    		res = 0;
    	return res;
    }
    
    static int send_request(struct sip_pvt *p, struct sip_request *req)
    {
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sipdebug)
    		ast_verbose("XXX Need to handle Retransmitting XXX:\n%s to %s:%d\n", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = __sip_xmit(p, req->data, req->len);
    	return res;
    }
    
    static char *ditch_braces(char *tmp)
    {
    	char *c = tmp;
    	char *n;
    	c = tmp;
    	if ((n = strchr(tmp, '<')) ) {
    		c = n + 1;
    		while(*c && *c != '>') c++;
    		if (*c != '>') {
    			ast_log(LOG_WARNING, "No closing brace in '%s'\n", tmp);
    		} else {
    			*c = '\0';
    		}
    		return n+1;
    	}
    	return c;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_sendtext(struct ast_channel *ast, char *text)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_pvt *p = ast->pvt->pvt;
    	if (sipdebug) 
    		ast_verbose("Sending text %s on %s\n", text, ast->name);
    	if (!p)
    		return -1;
    	if (!text || !strlen(text))
    		return 0;
    	if (sipdebug)
    		ast_verbose("Really sending text %s on %s\n", text, ast->name);
    	transmit_message_with_text(p, text);
    	return 0;	
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int create_addr(struct sip_pvt *r, char *peer)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct hostent *hp;
    	struct sip_peer *p;
    	int found=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	r->sa.sin_family = AF_INET;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&peerl.lock);
    	p = peerl.peers;
    	while(p) {
    		if (!strcasecmp(p->name, peer)) {
    			found++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			r->capability = p->capability;
    			strncpy(r->peername, p->username, sizeof(r->peername)-1);
    			strncpy(r->peersecret, p->secret, sizeof(r->peersecret)-1);
    			strncpy(r->username, p->username, sizeof(r->username)-1);
    			r->insecure = p->insecure;
    			r->canreinvite = p->canreinvite;
    			r->maxtime = p->maxms;
    			strncpy(r->context, p->context,sizeof(r->context)-1);
    			if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) &&
    				(!p->maxms || ((p->lastms > 0)  && (p->lastms <= p->maxms)))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (p->addr.sin_addr.s_addr) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					r->sa.sin_addr = p->addr.sin_addr;
    					r->sa.sin_port = p->addr.sin_port;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					r->sa.sin_addr = p->defaddr.sin_addr;
    					r->sa.sin_port = p->defaddr.sin_port;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    				break;
    			}
    		}
    		p = p->next;
    	}
    	ast_pthread_mutex_unlock(&peerl.lock);
    	if (!p && !found) {
    		hp = gethostbyname(peer);
    		if (hp) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			memcpy(&r->sa.sin_addr, hp->h_addr, sizeof(r->sa.sin_addr));
    			r->sa.sin_port = htons(DEFAULT_SIP_PORT);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		} else {
    			ast_log(LOG_WARNING, "No such host: %s\n", peer);
    			return -1;
    		}
    	} else if (!p)
    		return -1;
    	else
    		return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int auto_congest(void *nothing)
    {
    	struct sip_pvt *p = nothing;
    	ast_pthread_mutex_lock(&p->lock);
    	p->initid = -1;
    	if (p->owner) {
    		if (!pthread_mutex_trylock(&p->owner->lock)) {
    			ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
    			ast_queue_control(p->owner, AST_CONTROL_CONGESTION, 0);
    			ast_pthread_mutex_unlock(&p->owner->lock);
    		}
    	}
    	ast_pthread_mutex_unlock(&p->lock);
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_call(struct ast_channel *ast, char *dest, int timeout)
    {
    	int res;
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *vxml_url = NULL;
    	struct varshead *headp;
    	struct ast_var_t *current;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	p = ast->pvt->pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check whether there is a VXML_URL variable */
    	headp=&ast->varshead;
    	AST_LIST_TRAVERSE(headp,current,entries) {
    		if (strcasecmp(ast_var_name(current),"VXML_URL")==0)
    	        {
    			vxml_url = ast_var_value(current);
    			break;
    		}
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = 0;
    	p->outgoing = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	transmit_invite(p, "INVITE", 1, NULL, vxml_url);
    	if (p->maxtime) {
    		/* Initialize auto-congest time */
    		p->initid = ast_sched_add(sched, p->maxtime * 2, auto_congest, p);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void __sip_destroy(struct sip_pvt *p, int lockowner)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *cur, *prev = NULL;
    	if (p->rtp) {
    		ast_rtp_destroy(p->rtp);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Unlink us from the owner if we have one */
    	if (p->owner) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (lockowner)
    			ast_pthread_mutex_lock(&p->owner->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
    		p->owner->pvt->pvt = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (lockowner)
    			ast_pthread_mutex_unlock(&p->owner->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	cur = iflist;
    	while(cur) {
    		if (cur == p) {
    			if (prev)
    				prev->next = cur->next;
    			else
    				iflist = cur->next;
    			break;
    		}
    		prev = cur;
    		cur = cur->next;
    	}
    	if (!cur) {
    		ast_log(LOG_WARNING, "%p is not in list?!?! \n", cur);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		if (p->initid > -1)
    			ast_sched_del(sched, p->initid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		free(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    static void sip_destroy(struct sip_pvt *p)
    {
    	ast_pthread_mutex_lock(&iflock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	__sip_destroy(p, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&iflock);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* Interface lookup code courtesy Tilghman of DrunkCoder.com.  Thanks! */
    
    struct my_ifreq {
        union
          {
    	char ifrn_name[IFNAMSIZ];	/* Interface name, e.g. "en0".  */
          } ifr_ifrn;
    
        union
          {
    	struct sockaddr_in ifru_addr;
    	char ifru_data[512];
          } ifr_ifru;
    };
    
    struct in_addr *lookup_iface(char *iface) {
    	int mysock;
    	int res;
    	static struct  my_ifreq ifreq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(&ifreq, 0, sizeof(ifreq));
    	strncpy(ifreq.ifr_ifrn.ifrn_name,iface,sizeof(ifreq.ifr_ifrn.ifrn_name) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	mysock = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
    	res = ioctl(mysock,SIOCGIFADDR,&ifreq);
    	
    	close(mysock);
    	if (res < 0) {
    		ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
    		return &__ourip;
    	}
    	return( (struct in_addr *) &ifreq.ifr_ifru.ifru_addr.sin_addr );
    }
    
    static struct in_addr *myaddrfor(struct in_addr *them)
    {
    	FILE *PROC;
    	struct in_addr *temp = NULL;
    	unsigned int remote_ip;
    	char line[256];
    	remote_ip = them->s_addr;
    	
    	PROC = fopen("/proc/net/route","r");
    	if (!PROC) {
    		/* If /proc/net/route doesn't exist, fall back to the old method */
    		return &__ourip;
    	}
    	/* First line contains headers */
    	fgets(line,sizeof(line),PROC);
    
    	while (!feof(PROC)) {
    		char iface[8];
    		unsigned int dest, gateway, mask;
    		int i,aoffset;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char *fields[40];
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    		fgets(line,sizeof(line),PROC);
    
    		aoffset = 0;
    		for (i=0;i<sizeof(line);i++) {
    			char *boffset;
    
    			fields[aoffset++] = line + i;
    			boffset = strchr(line + i,'\t');
    			if (boffset == NULL) {
    				/* Exit loop */
    				break;
    			} else {
    				*boffset = '\0';
    				i = boffset - line;
    			}
    		}
    
    		sscanf(fields[0],"%s",iface);
    		sscanf(fields[1],"%x",&dest);
    		sscanf(fields[2],"%x",&gateway);
    		sscanf(fields[7],"%x",&mask);
    #if 0
    		printf("Addr: %s %08x Dest: %08x Mask: %08x\n", inet_ntoa(*them), remote_ip, dest, mask);
    #endif		
    		if (((remote_ip & mask) ^ dest) == 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (sipdebug)
    				ast_verbose("Interface is %s\n",iface);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			temp = lookup_iface(iface);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (sipdebug)
    				ast_verbose("IP Address is %s\n",inet_ntoa(*temp));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    		}
    	}
    	fclose(PROC);
    	if (!temp) {
    		ast_log(LOG_WARNING, "Couldn't figure out how to get to %s.  Using default\n", inet_ntoa(*them));
     		temp = &__ourip;
     	}
    	return temp;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int sip_hangup(struct ast_channel *ast)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_pvt *p = ast->pvt->pvt;
    	int needcancel = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_debug)
    		ast_log(LOG_DEBUG, "sip_hangup(%s)\n", ast->name);
    	if (!ast->pvt->pvt) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&p->lock);
    	/* Determine how to disconnect */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p->owner != ast) {
    		ast_log(LOG_WARNING, "Huh?  We aren't the owner?\n");
    		ast_pthread_mutex_unlock(&p->lock);
    		return 0;
    	}
    	if (!ast || (ast->_state != AST_STATE_UP))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		needcancel = 1;
    	/* Disconnect */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p = ast->pvt->pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->owner = NULL;
    	ast->pvt->pvt = NULL;
    
    	p->needdestroy = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 0
    	/* Invert sense of outgoing */
    	p->outgoing = 1 - p->outgoing;
    #endif	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Start the process if it's not already started */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!p->alreadygone && strlen(p->initreq.data)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (needcancel) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			transmit_request(p, "CANCEL", 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Send a hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			transmit_request(p, "BYE", p->outgoing);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 0
    	/* Restore sense of outgoing */
    	p->outgoing = 1 - p->outgoing;
    #endif	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&p->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    static int sip_answer(struct ast_channel *ast)
    {
    	int res = 0;
    	struct sip_pvt *p = ast->pvt->pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ast->_state != AST_STATE_UP) {
    		ast_setstate(ast, AST_STATE_UP);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_debug)
    			ast_log(LOG_DEBUG, "sip_answer(%s)\n", ast->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = transmit_response_with_sdp(p, "200 OK", &p->initreq);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	return res;
    }
    
    static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
    {
    	struct sip_pvt *p = ast->pvt->pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = 0;
    	if (frame->frametype != AST_FRAME_VOICE) {
    		if (frame->frametype == AST_FRAME_IMAGE)
    			return 0;
    		else {
    			ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
    			return 0;
    		}
    	} else {
    		if (!(frame->subclass & ast->nativeformats)) {
    			ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
    				frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
    			return -1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p) {
    		ast_pthread_mutex_lock(&p->lock);
    		if (p->rtp) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) {
    				transmit_response_with_sdp(p, "183 Session Progress", &p->initreq);
    				p->progress = 1;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res =  ast_rtp_write(p->rtp, frame);
    		}
    		ast_pthread_mutex_unlock(&p->lock);
    	}
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
    {
    	struct sip_pvt *p = newchan->pvt->pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&p->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p->owner != oldchan) {
    		ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_pthread_mutex_unlock(&p->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	p->owner = newchan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&p->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_senddigit(struct ast_channel *ast, char digit)
    {
    	struct sip_pvt *p = ast->pvt->pvt;
    	if (p && p->rtp) {
    		ast_rtp_senddigit(p->rtp, digit);
    		return 0;
    	}
    	return -1;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_indicate(struct ast_channel *ast, int condition)
    {
    	struct sip_pvt *p = ast->pvt->pvt;
    	switch(condition) {
    	case AST_CONTROL_RINGING:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast->_state == AST_STATE_RING) {
    			transmit_response(p, "180 Ringing", &p->initreq);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_CONTROL_BUSY:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast->_state != AST_STATE_UP) {
    			transmit_response(p, "600 Busy everywhere", &p->initreq);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			p->alreadygone = 1;
    			ast_softhangup(ast, AST_SOFTHANGUP_DEV);
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_CONTROL_CONGESTION:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast->_state != AST_STATE_UP) {
    			transmit_response(p, "486 Busy here", &p->initreq);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			p->alreadygone = 1;
    			ast_softhangup(ast, AST_SOFTHANGUP_DEV);
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	case -1:
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition);
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int sip_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
    {
    	struct sip_pvt *p0, *p1;
    	struct ast_frame *f;
    	struct ast_channel *who, *cs[3];
    	int to;
    
    	/* if need DTMF, cant native bridge */
    	if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
    		return -2;
    	ast_pthread_mutex_lock(&c0->lock);
    	ast_pthread_mutex_lock(&c1->lock);
    	p0 = c0->pvt->pvt;
    	p1 = c1->pvt->pvt;
    	if (!p0->canreinvite || !p1->canreinvite) {
    		/* Not gonna support reinvite */
    		ast_pthread_mutex_unlock(&c0->lock);
    		ast_pthread_mutex_unlock(&c1->lock);
    		return -2;
    	}
    	transmit_reinvite_with_sdp(p0, p1->rtp);
    	transmit_reinvite_with_sdp(p1, p0->rtp);
    	ast_pthread_mutex_unlock(&c0->lock);
    	ast_pthread_mutex_unlock(&c1->lock);
    	cs[0] = c0;
    	cs[1] = c1;
    	cs[2] = NULL;
    	for (;;) {
    		if ((c0->pvt->pvt != p0)  ||
    			(c1->pvt->pvt != p1) ||
    			(c0->masq || c0->masqr || c1->masq || c1->masqr)) {
    				ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
    				if (c0->pvt->pvt == p0)
    					transmit_reinvite_with_sdp(p0, NULL);
    				if (c1->pvt->pvt == p1)
    					transmit_reinvite_with_sdp(p1, NULL);
    				/* Tell it to try again later */
    				return -3;
    		}
    		to = -1;
    		who = ast_waitfor_n(cs, 2, &to);
    		if (!who) {
    			ast_log(LOG_DEBUG, "Ooh, empty read...\n");
    			continue;
    		}
    		f = ast_read(who);
    		if (!f || ((f->frametype == AST_FRAME_DTMF) &&
    				   (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || 
    			       ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
    			*fo = f;
    			*rc = who;
    			ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup");
    			if (c0->pvt->pvt == p0 && !c0->_softhangup)
    				transmit_reinvite_with_sdp(p0, NULL);
    			if (c1->pvt->pvt == p1 && !c1->_softhangup)
    				transmit_reinvite_with_sdp(p1, NULL);
    			/* That's all we needed */
    			return 0;
    		} else 
    			ast_frfree(f);
    		/* Swap priority not that it's a big deal at this point */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    		
    	}
    	return -1;
    }
    
    
    static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *tmp;
    	int fmt;
    	tmp = ast_channel_alloc(1);
    	if (tmp) {
    		tmp->nativeformats = i->capability;
    		if (!tmp->nativeformats)
    			tmp->nativeformats = capability;
    		fmt = ast_best_codec(tmp->nativeformats);
    
    		if (title)
    			snprintf(tmp->name, sizeof(tmp->name), "SIP/%s", title);
    		else
    			snprintf(tmp->name, sizeof(tmp->name), "SIP/%s:%d", inet_ntoa(i->sa.sin_addr), ntohs(i->sa.sin_port));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->type = type;
    
    		tmp->fds[0] = ast_rtp_fd(i->rtp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_setstate(tmp, state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (state == AST_STATE_RING)
    			tmp->rings = 1;
    		tmp->writeformat = fmt;
    		tmp->pvt->rawwriteformat = fmt;
    		tmp->readformat = fmt;
    		tmp->pvt->rawreadformat = fmt;
    		tmp->pvt->pvt = i;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->pvt->send_text = sip_sendtext;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->pvt->call = sip_call;
    		tmp->pvt->hangup = sip_hangup;
    		tmp->pvt->answer = sip_answer;
    		tmp->pvt->read = sip_read;
    		tmp->pvt->write = sip_write;
    		tmp->pvt->indicate = sip_indicate;
    		tmp->pvt->fixup = sip_fixup;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->pvt->send_digit = sip_senddigit;
    		tmp->pvt->bridge = sip_bridge;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strlen(i->language))
    			strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
    		i->owner = tmp;
    		ast_pthread_mutex_lock(&usecnt_lock);
    		usecnt++;
    		ast_pthread_mutex_unlock(&usecnt_lock);
    		ast_update_use_count();
    		strncpy(tmp->context, i->context, sizeof(tmp->context)-1);
    		strncpy(tmp->exten, i->exten, sizeof(tmp->exten)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strlen(i->callerid))
    			tmp->callerid = strdup(i->callerid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->priority = 1;
    		if (state != AST_STATE_DOWN) {
    			if (ast_pbx_start(tmp)) {
    				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
    				ast_hangup(tmp);
    				tmp = NULL;
    			}
    		}
    	} else
    		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
    	return tmp;
    }
    
    static struct cfalias {
    	char *fullname;
    	char *shortname;
    } aliases[] = {
    	{ "Content-Type", "c" },
    	{ "Content-Encoding", "e" },
    	{ "From", "f" },
    	{ "Call-ID", "i" },
    	{ "Contact", "m" },
    	{ "Content-Length", "l" },
    	{ "Subject", "s" },
    	{ "To", "t" },
    	{ "Via", "v" },
    };
    
    static char *get_sdp(struct sip_request *req, char *name)
    {
    	int x;
    	int len = strlen(name);
    	char *r;
    	for (x=0;x<req->lines;x++) {
    		if (!strncasecmp(req->line[x], name, len) && 
    				(req->line[x][len] == '=')) {
    					r = req->line[x] + len + 1;
    					while(*r && (*r < 33))
    							r++;
    					return r;
    		}
    	}
    	return "";
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *__get_header(struct sip_request *req, char *name, int *start)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int x;
    	int len = strlen(name);
    	char *r;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=*start;x<req->headers;x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strncasecmp(req->header[x], name, len) && 
    				(req->header[x][len] == ':')) {
    					r = req->header[x] + len + 1;
    					while(*r && (*r < 33))
    							r++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					*start = x+1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					return r;
    		}
    	}
    	/* Try aliases */
    	for (x=0;x<sizeof(aliases) / sizeof(aliases[0]); x++) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(aliases[x].fullname, name))
    			return __get_header(req, aliases[x].shortname, start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Don't return NULL, so get_header is always a valid pointer */
    	return "";
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *get_header(struct sip_request *req, char *name)
    {
    	int start = 0;
    	return __get_header(req, name, &start);
    }
    
    
    static struct ast_frame *sip_rtp_read(struct sip_pvt *p)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* Retrieve audio/etc from channel.  Assumes p->lock is already held. */
    	struct ast_frame *f;
    	f = ast_rtp_read(p->rtp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p->owner) {
    
    		/* We already hold the channel lock */
    		if (f->frametype == AST_FRAME_VOICE) {
    			if (f->subclass != p->owner->nativeformats) {
    				ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
    				p->owner->nativeformats = f->subclass;
    				ast_set_read_format(p->owner, p->owner->readformat);
    				ast_set_write_format(p->owner, p->owner->writeformat);
    
    	return f;
    }
    
    static struct ast_frame *sip_read(struct ast_channel *ast)
    {
    	struct ast_frame *fr;
    	struct sip_pvt *p = ast->pvt->pvt;
    	ast_pthread_mutex_lock(&p->lock);
    	fr = sip_rtp_read(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&p->lock);
    
    	return fr;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void build_callid(char *callid, int len, struct in_addr ourip)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    	int val;
    	int x;
    	for (x=0;x<4;x++) {
    		val = rand();
    		res = snprintf(callid, len, "%08x", val);
    		len -= res;
    		callid += res;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* It's not important that we really use our right IP here... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(callid, len, "@%s", inet_ntoa(ourip));
    }
    
    static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin)
    {
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p = malloc(sizeof(struct sip_pvt));
    	if (!p)
    		return NULL;
    	/* Keep track of stuff */
    	memset(p, 0, sizeof(struct sip_pvt));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->initid = -1;
    
    	p->rtp = ast_rtp_new(NULL, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->branch = rand();	
    	p->tag = rand();
    	/* Start with 101 instead of 1 */
    	p->ocseq = 101;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!p->rtp) {
    		ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno));
    		free(p);
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_rtp_settos(p->rtp, tos);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_init(&p->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_rtp_set_data(p->rtp, p);
    	ast_rtp_set_callback(p->rtp, rtpready);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sin) {
    		memcpy(&p->sa, sin, sizeof(p->sa));
    		memcpy(&p->ourip, myaddrfor(&p->sa.sin_addr), sizeof(p->ourip));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		memcpy(&p->ourip, &__ourip, sizeof(p->ourip));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=%08x", inet_ntoa(p->ourip), ourport, p->branch);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!callid)
    		build_callid(p->callid, sizeof(p->callid), p->ourip);
    	else
    		strncpy(p->callid, callid, sizeof(p->callid) - 1);