mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@472 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			1952 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1952 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * 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>
 | |
| #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>
 | |
| #include <sys/socket.h>
 | |
| #include <errno.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include <fcntl.h>
 | |
| #include <netdb.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <sys/signal.h>
 | |
| 
 | |
| #define SIPDUMPER
 | |
| #define DEFAULT_EXPIREY 120
 | |
| #define MAX_EXPIREY     3600
 | |
| 
 | |
| 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 */
 | |
| static int capability = AST_FORMAT_ULAW;
 | |
| 
 | |
| static char ourhost[256];
 | |
| static struct in_addr ourip;
 | |
| static int ourport;
 | |
| 
 | |
| /* 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 {
 | |
| 	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 {
 | |
| 	char callid[80];					/* Global CallID */
 | |
| 	unsigned int cseq;							/* Current seqno */
 | |
| 	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 */
 | |
| 	struct sockaddr_in sa;				/* Our peer */
 | |
| 	struct ast_channel *owner;			/* Who owns us */
 | |
| 	char exten[AST_MAX_EXTENSION];		/* Extention where to start */
 | |
| 	char context[AST_MAX_EXTENSION];
 | |
| 	char language[MAX_LANGUAGE];
 | |
| 	struct sip_request initreq;			/* Initial request */
 | |
| 	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;
 | |
| 	struct ast_ha *ha;
 | |
| 	struct sip_user *next;
 | |
| };
 | |
| 
 | |
| struct sip_peer {
 | |
| 	char name[80];
 | |
| 	char secret[80];
 | |
| 	char methods[80];
 | |
| 	char username[80];
 | |
| 	int dynamic;
 | |
| 	int expire;
 | |
| 	int expirey;
 | |
| 	int capability;
 | |
| 	int insecure;
 | |
| 	struct sockaddr_in addr;
 | |
| 	struct in_addr mask;
 | |
| 	
 | |
| 	struct sockaddr_in defaddr;
 | |
| 	struct ast_ha *ha;
 | |
| 	int delme;
 | |
| 	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 };
 | |
| 
 | |
| 
 | |
| static int sipsock  = -1;
 | |
| 
 | |
| static struct sockaddr_in bindaddr;
 | |
| 
 | |
| #ifdef SIPDUMPER
 | |
| 
 | |
| static void sip_dump_packet(char *data, int len)
 | |
| {
 | |
| 	printf("SIP Packet Dump\n");
 | |
| 	printf("================\n");
 | |
| 	printf("Data: \n%s\n", data);
 | |
| 	fflush(stdout);
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static struct ast_frame  *sip_read(struct ast_channel *ast);
 | |
| static int transmit_response(struct sip_pvt *p, char *msg);
 | |
| static int transmit_response_with_sdp(struct sip_pvt *p, char *msg);
 | |
| static int transmit_request(struct sip_pvt *p, char *msg, int inc);
 | |
| static int transmit_invite_with_sdp(struct sip_pvt *p, char *msg);
 | |
| 
 | |
| 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) {
 | |
| 		ast_log(LOG_WARNING, "sip_xmit returned %d\n", res);
 | |
| 	}
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static int send_response(struct sip_pvt *p, struct sip_request *req)
 | |
| {
 | |
| 	int res;
 | |
| 	printf("Transmitting:\n%s\n", req->data);
 | |
| 	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;
 | |
| 	printf("XXX Need to handle Retransmitting XXX:\n%s\n", req->data);
 | |
| 	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;
 | |
| }
 | |
| 
 | |
| static int sip_digit(struct ast_channel *ast, char digit)
 | |
| {
 | |
| 	printf("SIP digit! (%c)\n", digit);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int create_addr(struct sockaddr_in *sin, int *capability, char *peer, char *username, int *insecure)
 | |
| {
 | |
| 	struct hostent *hp;
 | |
| 	struct sip_peer *p;
 | |
| 	int found=0;
 | |
| 	sin->sin_family = AF_INET;
 | |
| 	ast_pthread_mutex_lock(&peerl.lock);
 | |
| 	p = peerl.peers;
 | |
| 	while(p) {
 | |
| 		if (!strcasecmp(p->name, peer)) {
 | |
| 			found++;
 | |
| 			if (capability)
 | |
| 				*capability = p->capability;
 | |
| 			if (username)
 | |
| 				strncpy(username, p->username, 80);
 | |
| 			if (insecure)
 | |
| 				*insecure = p->insecure;
 | |
| 			if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
 | |
| 				if (p->addr.sin_addr.s_addr) {
 | |
| 					sin->sin_addr = p->addr.sin_addr;
 | |
| 					sin->sin_port = p->addr.sin_port;
 | |
| 				} else {
 | |
| 					sin->sin_addr = p->defaddr.sin_addr;
 | |
| 					sin->sin_port = p->defaddr.sin_port;
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		p = p->next;
 | |
| 	}
 | |
| 	ast_pthread_mutex_unlock(&peerl.lock);
 | |
| 	if (!p && !found) {
 | |
| 		hp = gethostbyname(peer);
 | |
| 		if (hp) {
 | |
| 			memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
 | |
| 			sin->sin_port = htons(DEFAULT_SIP_PORT);
 | |
| 			return 0;
 | |
| 		} else {
 | |
| 			ast_log(LOG_WARNING, "No such host: %s\n", peer);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	} else if (!p)
 | |
| 		return -1;
 | |
| 	else
 | |
| 		return 0;
 | |
| }
 | |
| 
 | |
| static int sip_call(struct ast_channel *ast, char *dest, int timeout)
 | |
| {
 | |
| 	int res;
 | |
| 	struct sip_pvt *p;
 | |
| 	char *ext, *host;
 | |
| 	char tmp[256];
 | |
| 	char username[81];
 | |
| 	strncpy(tmp, dest, sizeof(tmp) - 1);
 | |
| 	
 | |
| 	p = ast->pvt->pvt;
 | |
| 	if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
 | |
| 		ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	host = strchr(tmp, '@');
 | |
| 	if (host) {
 | |
| 		*host = '\0';
 | |
| 		host++;
 | |
| 		ext = tmp;
 | |
| 	} else {
 | |
| 		host = tmp;
 | |
| 		ext = NULL;
 | |
| 	}
 | |
| 	if (create_addr(&p->sa, &p->capability, host, username, &p->insecure)) {
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (!ext && strlen(username))
 | |
| 		ext = username;
 | |
| 	res = 0;
 | |
| 	p->outgoing = 1;
 | |
| 	transmit_invite_with_sdp(p, username);
 | |
| 	printf("Calling extension '%s' at '%s'\n", ext ? ext : "<none>", host);
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static void __sip_destroy(struct sip_pvt *p)
 | |
| {
 | |
| 	struct sip_pvt *cur, *prev = NULL;
 | |
| 	if (p->rtp) {
 | |
| 		ast_rtp_destroy(p->rtp);
 | |
| 	}
 | |
| 	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);
 | |
| 	} else
 | |
| 		free(p);
 | |
| }
 | |
| static void sip_destroy(struct sip_pvt *p)
 | |
| {
 | |
| 	ast_pthread_mutex_lock(&iflock);
 | |
| 	__sip_destroy(p);
 | |
| 	ast_pthread_mutex_unlock(&iflock);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int sip_hangup(struct ast_channel *ast)
 | |
| {
 | |
| 	struct sip_pvt *p;
 | |
| 	if (option_debug)
 | |
| 		ast_log(LOG_DEBUG, "sip_hangup(%s)\n", ast->name);
 | |
| 	if (!ast->pvt->pvt) {
 | |
| 		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	p = ast->pvt->pvt;
 | |
| 	if (!p->alreadygone && strlen(p->initreq.data)) {
 | |
| 		if (!p->owner || p->owner->state != AST_STATE_UP)
 | |
| 			transmit_request(p, "CANCEL", 0);
 | |
| 		else
 | |
| 			/* Send a hangup */
 | |
| 			transmit_request(p, "BYE", 1);
 | |
| 	}
 | |
| 	p->needdestroy = 1;
 | |
| 	p->owner = NULL;
 | |
| 	p->outgoing = 0;
 | |
| 	ast->pvt->pvt = NULL;
 | |
| 	printf("SIP Hangup!\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sip_answer(struct ast_channel *ast)
 | |
| {
 | |
| 	int res = 0;
 | |
| 	struct sip_pvt *p = ast->pvt->pvt;
 | |
| 	if (ast->state != AST_STATE_UP) {
 | |
| 		ast->state = AST_STATE_UP;
 | |
| 		if (option_debug)
 | |
| 			ast_log(LOG_DEBUG, "sip_answer(%s)\n", ast->name);
 | |
| 		res = transmit_response_with_sdp(p, "200 OK");
 | |
| 	}
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static struct ast_frame  *sip_read(struct ast_channel *ast)
 | |
| {
 | |
| 	ast_log(LOG_WARNING, "I should never get called but am on %s!\n", ast->name);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 | |
| {
 | |
| 	struct sip_pvt *p = ast->pvt->pvt;
 | |
| 	if (p->rtp) {
 | |
| 		return ast_rtp_write(p->rtp, frame);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 | |
| {
 | |
| 	struct sip_pvt *p = newchan->pvt->pvt;
 | |
| 	if (p->owner != oldchan) {
 | |
| 		ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	p->owner = newchan;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sip_indicate(struct ast_channel *ast, int condition)
 | |
| {
 | |
| 	struct sip_pvt *p = ast->pvt->pvt;
 | |
| 	switch(condition) {
 | |
| 	case AST_CONTROL_RINGING:
 | |
| 		if (ast->state == AST_STATE_RING) {
 | |
| 			transmit_response(p, "180 Ringing");
 | |
| 		} else {
 | |
| 			ast_log(LOG_WARNING, "XXX Need to send in-band ringtone XXX\n");
 | |
| 		}
 | |
| 		break;
 | |
| 	case AST_CONTROL_BUSY:
 | |
| 		if (ast->state != AST_STATE_UP) {
 | |
| 			transmit_response(p, "600 Busy everywhere");
 | |
| 		} else {
 | |
| 			ast_log(LOG_WARNING, "XXX Need to send in-band busy tone XXX\n");
 | |
| 		}
 | |
| 		break;
 | |
| 	case AST_CONTROL_CONGESTION:
 | |
| 		if (ast->state != AST_STATE_UP) {
 | |
| 			transmit_response(p, "486 Busy here");
 | |
| 		} else {
 | |
| 			ast_log(LOG_WARNING, "XXX Need to send in-band congestion tone XXX\n");
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("Don't know how to indicate condition %d\n", condition);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct ast_channel *sip_new(struct sip_pvt *i, int state)
 | |
| {
 | |
| 	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);
 | |
| 		snprintf(tmp->name, sizeof(tmp->name), "SIP/%s:%d", inet_ntoa(i->sa.sin_addr), ntohs(i->sa.sin_port));
 | |
| 		tmp->type = type;
 | |
| 		tmp->state = state;
 | |
| 		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;
 | |
| 		tmp->pvt->send_digit = sip_digit;
 | |
| 		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;
 | |
| 		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);
 | |
| 		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 "";
 | |
| }
 | |
| 
 | |
| static char *get_header(struct sip_request *req, char *name)
 | |
| {
 | |
| 	int x;
 | |
| 	int len = strlen(name);
 | |
| 	char *r;
 | |
| 	for (x=0;x<req->headers;x++) {
 | |
| 		if (!strncasecmp(req->header[x], name, len) && 
 | |
| 				(req->header[x][len] == ':')) {
 | |
| 					r = req->header[x] + len + 1;
 | |
| 					while(*r && (*r < 33))
 | |
| 							r++;
 | |
| 					return r;
 | |
| 		}
 | |
| 	}
 | |
| 	/* Try aliases */
 | |
| 	for (x=0;x<sizeof(aliases) / sizeof(aliases[0]); x++) 
 | |
| 		if (!strcasecmp(aliases[x].fullname, name)) 
 | |
| 			return get_header(req, aliases[x].shortname);
 | |
| 
 | |
| 	/* Don't return NULL, so get_header is always a valid pointer */
 | |
| 	return "";
 | |
| }
 | |
| 
 | |
| static int rtpready(struct ast_rtp *rtp, struct ast_frame *f, void *data)
 | |
| {
 | |
| 	/* Just deliver the audio directly */
 | |
| 	struct sip_pvt *p = data;
 | |
| 	if (p->owner)
 | |
| 		ast_queue_frame(p->owner, f, 1);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void build_callid(char *callid, int len)
 | |
| {
 | |
| 	int res;
 | |
| 	int val;
 | |
| 	int x;
 | |
| 	for (x=0;x<4;x++) {
 | |
| 		val = rand();
 | |
| 		res = snprintf(callid, len, "%08x", val);
 | |
| 		len -= res;
 | |
| 		callid += res;
 | |
| 	}
 | |
| 	snprintf(callid, len, "@%s", inet_ntoa(ourip));
 | |
| }
 | |
| 
 | |
| static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin)
 | |
| {
 | |
| 	struct sip_pvt *p;
 | |
| 	p = malloc(sizeof(struct sip_pvt));
 | |
| 	if (!p)
 | |
| 		return NULL;
 | |
| 	/* Keep track of stuff */
 | |
| 	memset(p, 0, sizeof(struct sip_pvt));
 | |
| 	if (!callid)
 | |
| 		build_callid(p->callid, sizeof(p->callid));
 | |
| 	else
 | |
| 		strncpy(p->callid, callid, sizeof(p->callid) - 1);
 | |
| 	if (sin)
 | |
| 		memcpy(&p->sa, sin, sizeof(p->sa));
 | |
| 	p->rtp = ast_rtp_new(sched, io);
 | |
| 	ast_rtp_set_data(p->rtp, p);
 | |
| 	ast_rtp_set_callback(p->rtp, rtpready);
 | |
| 	if (!p->rtp) {
 | |
| 		ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno));
 | |
| 		free(p);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	
 | |
| 	/* Add to list */
 | |
| 	ast_pthread_mutex_lock(&iflock);
 | |
| 	p->next = iflist;
 | |
| 	iflist = p;
 | |
| 	ast_pthread_mutex_unlock(&iflock);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin)
 | |
| {
 | |
| 	struct sip_pvt *p;
 | |
| 	char *callid;
 | |
| 	callid = get_header(req, "Call-ID");
 | |
| 	if (!strlen(callid)) {
 | |
| 		ast_log(LOG_WARNING, "Call missing call ID from '%s'\n", inet_ntoa(sin->sin_addr));
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	ast_pthread_mutex_lock(&iflock);
 | |
| 	p = iflist;
 | |
| 	while(p) {
 | |
| 		if (!strcmp(p->callid, callid)) {
 | |
| 			/* Found the call */
 | |
| #if 0
 | |
| 			if (!p->insecure && ((p->sa.sin_addr.s_addr != sin->sin_addr.s_addr) ||
 | |
| 			    (p->sa.sin_port != sin->sin_port))) {
 | |
| 					char orig[80];
 | |
| 					char new[80];
 | |
| 					snprintf(orig, sizeof(orig), "%s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
 | |
| 					snprintf(new, sizeof(new), "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
 | |
| 					ast_log(LOG_WARNING, "Looks like %s is trying to steal call '%s' from %s?\n", new, p->callid, orig);
 | |
| 					ast_pthread_mutex_unlock(&iflock);
 | |
| 					return NULL;
 | |
| 			}
 | |
| #endif
 | |
| 			ast_pthread_mutex_unlock(&iflock);
 | |
| 			return p;
 | |
| 		}
 | |
| 		p = p->next;
 | |
| 	}
 | |
| 	ast_pthread_mutex_unlock(&iflock);
 | |
| 	return sip_alloc(callid, sin);
 | |
| }
 | |
| 
 | |
| static void parse(struct sip_request *req)
 | |
| {
 | |
| 	/* Divide fields by NULL's */
 | |
| 	char *c;
 | |
| 	int f = 0;
 | |
| 	c = req->data;
 | |
| 
 | |
| 	/* First header starts immediately */
 | |
| 	req->header[f] = c;
 | |
| 	while(*c) {
 | |
| 		if (*c == '\n') {
 | |
| 			/* We've got a new header */
 | |
| 			*c = 0;
 | |
| 
 | |
| #if 0
 | |
| 			printf("Header: %s (%d)\n", req->header[f], strlen(req->header[f]));
 | |
| #endif			
 | |
| 			if (!strlen(req->header[f])) {
 | |
| 				/* Line by itself means we're now in content */
 | |
| 				c++;
 | |
| 				break;
 | |
| 			}
 | |
| 			if (f >= SIP_MAX_HEADERS - 1) {
 | |
| 				ast_log(LOG_WARNING, "Too many SIP headers...\n");
 | |
| 			} else
 | |
| 				f++;
 | |
| 			req->header[f] = c + 1;
 | |
| 		} else if (*c == '\r') {
 | |
| 			/* Ignore but eliminate \r's */
 | |
| 			*c = 0;
 | |
| 		}
 | |
| 		c++;
 | |
| 	}
 | |
| 	/* Check for last header */
 | |
| 	if (strlen(req->header[f])) 
 | |
| 		f++;
 | |
| 	req->headers = f;
 | |
| 	/* Now we process any mime content */
 | |
| 	f = 0;
 | |
| 	req->line[f] = c;
 | |
| 	while(*c) {
 | |
| 		if (*c == '\n') {
 | |
| 			/* We've got a new line */
 | |
| 			*c = 0;
 | |
| #if 0
 | |
| 			printf("Line: %s (%d)\n", req->line[f], strlen(req->line[f]));
 | |
| #endif			
 | |
| 			if (f >= SIP_MAX_LINES - 1) {
 | |
| 				ast_log(LOG_WARNING, "Too many SDP lines...\n");
 | |
| 			} else
 | |
| 				f++;
 | |
| 			req->line[f] = c + 1;
 | |
| 		} else if (*c == '\r') {
 | |
| 			/* Ignore and eliminate \r's */
 | |
| 			*c = 0;
 | |
| 		}
 | |
| 		c++;
 | |
| 	}
 | |
| 	/* Check for last line */
 | |
| 	if (strlen(req->line[f])) 
 | |
| 		f++;
 | |
| 	req->lines = f;
 | |
| 	printf("%d headers, %d lines\n", req->headers, req->lines);
 | |
| 	if (*c) 
 | |
| 		ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c);
 | |
| }
 | |
| 
 | |
| static int process_sdp(struct sip_pvt *p, struct sip_request *req)
 | |
| {
 | |
| 	char *m;
 | |
| 	char *c;
 | |
| 	char host[258];
 | |
| 	int len;
 | |
| 	int portno;
 | |
| 	int peercapability;
 | |
| 	struct sockaddr_in sin;
 | |
| 	char *codecs;
 | |
| 	struct hostent *hp;
 | |
| 	int codec;
 | |
| 	/* Get codec and RTP info from SDP */
 | |
| 	if (strcasecmp(get_header(req, "Content-Type"), "application/sdp")) {
 | |
| 		ast_log(LOG_NOTICE, "Content is '%s', not 'application/sdp'\n", get_header(req, "Content-Type"));
 | |
| 		return -1;
 | |
| 	}
 | |
| 	m = get_sdp(req, "m");
 | |
| 	c = get_sdp(req, "c");
 | |
| 	if (!strlen(m) || !strlen(c)) {
 | |
| 		ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (sscanf(c, "IN IP4 %256s", host) != 1) {
 | |
| 		ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	/* XXX This could block for a long time, and block the main thread! XXX */
 | |
| 	hp = gethostbyname(host);
 | |
| 	if (!hp) {
 | |
| 		ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (sscanf(m, "audio %d RTP/AVP %n", &portno, &len) != 1) {
 | |
| 		ast_log(LOG_WARNING, "Unable to determine port number for RTP in '%s'\n", m); 
 | |
| 		return -1;
 | |
| 	}
 | |
| 	sin.sin_family = AF_INET;
 | |
| 	memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
 | |
| 	sin.sin_port = htons(portno);
 | |
| 	ast_rtp_set_peer(p->rtp, &sin);
 | |
| #if 0
 | |
| 	printf("Peer RTP is at port %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
 | |
| #endif	
 | |
| 	peercapability = 0;
 | |
| 	codecs = m + len;
 | |
| 	while(strlen(codecs)) {
 | |
| 		if (sscanf(codecs, "%d %n", &codec, &len) != 1) {
 | |
| 			ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
 | |
| 			return -1;
 | |
| 		}
 | |
| #if 0
 | |
| 		printf("Codec: %d\n", codec);
 | |
| #endif		
 | |
| 		codec = rtp2ast(codec);
 | |
| 		if (codec  > -1)
 | |
| 			peercapability |= codec;
 | |
| 		codecs += len;
 | |
| 	}
 | |
| 	p->capability = capability & peercapability;
 | |
| 	printf("Capabilities: us - %d, them - %d, combined - %d\n",
 | |
| 		capability, peercapability, p->capability);
 | |
| 	if (!p->capability) {
 | |
| 		ast_log(LOG_WARNING, "No compatible codecs!\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return 0;
 | |
| 	
 | |
| }
 | |
| 
 | |
| static int add_header(struct sip_request *req, char *var, char *value)
 | |
| {
 | |
| 	if (req->lines) {
 | |
| 		ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	req->header[req->headers] = req->data + req->len;
 | |
| 	req->len += snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s: %s\r\n", var, value);
 | |
| 	if (req->headers < SIP_MAX_HEADERS)
 | |
| 		req->headers++;
 | |
| 	else {
 | |
| 		ast_log(LOG_WARNING, "Out of header space\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return 0;	
 | |
| }
 | |
| 
 | |
| static int add_blank_header(struct sip_request *req)
 | |
| {
 | |
| 	if (req->lines) {
 | |
| 		ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	req->header[req->headers] = req->data + req->len;
 | |
| 	req->len += snprintf(req->header[req->headers], sizeof(req->data) - req->len, "\r\n");
 | |
| 	if (req->headers < SIP_MAX_HEADERS)
 | |
| 		req->headers++;
 | |
| 	else {
 | |
| 		ast_log(LOG_WARNING, "Out of header space\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return 0;	
 | |
| }
 | |
| 
 | |
| static int add_line(struct sip_request *req, char *line)
 | |
| {
 | |
| 	if (!req->lines) {
 | |
| 		/* Add extra empty return */
 | |
| 		req->len += snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n");
 | |
| 	}
 | |
| 	req->line[req->lines] = req->data + req->len;
 | |
| 	req->len += snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
 | |
| 	if (req->lines < SIP_MAX_LINES)
 | |
| 		req->lines++;
 | |
| 	else {
 | |
| 		ast_log(LOG_WARNING, "Out of line space\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return 0;	
 | |
| }
 | |
| 
 | |
| static int copy_header(struct sip_request *req, struct sip_request *orig, char *field)
 | |
| {
 | |
| 	char *tmp;
 | |
| 	tmp = get_header(orig, field);
 | |
| 	if (strlen(tmp)) {
 | |
| 		/* Add what we're responding to */
 | |
| 		return add_header(req, field, tmp);
 | |
| 	}
 | |
| 	ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int init_resp(struct sip_request *req, char *resp, struct sip_request *orig)
 | |
| {
 | |
| 	/* Initialize a response */
 | |
| 	if (req->headers || req->len) {
 | |
| 		ast_log(LOG_WARNING, "Request already initialized?!?\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	req->header[req->headers] = req->data + req->len;
 | |
| 	req->len += snprintf(req->header[req->headers], sizeof(req->data) - req->len, "SIP/2.0 %s\r\n", resp);
 | |
| 	if (req->headers < SIP_MAX_HEADERS)
 | |
| 		req->headers++;
 | |
| 	else
 | |
| 		ast_log(LOG_WARNING, "Out of header space\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int init_req(struct sip_request *req, char *resp, char *recip)
 | |
| {
 | |
| 	/* Initialize a response */
 | |
| 	if (req->headers || req->len) {
 | |
| 		ast_log(LOG_WARNING, "Request already initialized?!?\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	req->header[req->headers] = req->data + req->len;
 | |
| 	req->len += snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %s SIP/2.0\r\n", resp, recip);
 | |
| 	if (req->headers < SIP_MAX_HEADERS)
 | |
| 		req->headers++;
 | |
| 	else
 | |
| 		ast_log(LOG_WARNING, "Out of header space\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg)
 | |
| {
 | |
| 	struct sip_request *req = &p->initreq;
 | |
| 	memset(resp, 0, sizeof(*resp));
 | |
| 	init_resp(resp, msg, req);
 | |
| 	copy_header(resp, req, "Via");
 | |
| 	copy_header(resp, req, "From");
 | |
| 	copy_header(resp, req, "To");
 | |
| 	copy_header(resp, req, "Call-ID");
 | |
| 	copy_header(resp, req, "CSeq");
 | |
| 	add_header(resp, "User-Agent", "Asterisk PBX");
 | |
| 	if (p->expirey) {
 | |
| 		/* For registration responses, we also need expirey and
 | |
| 		   contact info */
 | |
| 		char tmp[80];
 | |
| 		char contact2[256], *c, contact[256];
 | |
| 		snprintf(tmp, sizeof(tmp), "%d", p->expirey);
 | |
| #if 1
 | |
| 		/* XXX This isn't exactly right and it's implemented
 | |
| 		       very stupidly *sigh* XXX */
 | |
| 		strncpy(contact2, get_header(req, "Contact"), sizeof(contact2)-1);
 | |
| 		c = ditch_braces(contact2);
 | |
| 		snprintf(contact, sizeof(contact), "<%s>", c);
 | |
| #endif
 | |
| 		add_header(resp, "Expires", tmp);
 | |
| 		add_header(resp, "Contact", contact);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int inc)
 | |
| {
 | |
| 	struct sip_request *orig = &p->initreq;
 | |
| 	char stripped[80];
 | |
| 	char tmp[80];
 | |
| 	char *c, *n;
 | |
| 	char *ot, *of;
 | |
| 
 | |
| 	memset(req, 0, sizeof(struct sip_request));
 | |
| 	if (inc)
 | |
| 		p->cseq++;
 | |
| 
 | |
| 	
 | |
| 	if (p->outgoing)
 | |
| 		strncpy(stripped, get_header(orig, "To"), sizeof(stripped) - 1);
 | |
| 	else
 | |
| 		strncpy(stripped, get_header(orig, "From"), sizeof(stripped) - 1);
 | |
| 	
 | |
| 	c = strchr(stripped, '<');
 | |
| 	if (c) 
 | |
| 		c++;
 | |
| 	else
 | |
| 		c = stripped;
 | |
| 	n = strchr(c, '>');
 | |
| 	if (n)
 | |
| 		*n = '\0';
 | |
| 	
 | |
| 	init_req(req, msg, c);
 | |
| 
 | |
| 	snprintf(tmp, sizeof(tmp), "%d %s", p->cseq, msg);
 | |
| 	add_header(req, "CSeq", tmp);
 | |
| 
 | |
| 	copy_header(req, orig, "Via");
 | |
| 
 | |
| 	ot = get_header(orig, "To");
 | |
| 	of = get_header(orig, "From");
 | |
| 	if (p->outgoing) {
 | |
| 		add_header(req, "From", of);
 | |
| 		add_header(req, "To", ot);
 | |
| 	} else {
 | |
| 		add_header(req, "From", ot);
 | |
| 		add_header(req, "To", of);
 | |
| 	}
 | |
| 
 | |
| 	copy_header(req, orig, "Call-ID");
 | |
| 	add_header(req, "User-Agent", "Asterisk PBX");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int transmit_response(struct sip_pvt *p, char *msg)
 | |
| {
 | |
| 	struct sip_request resp;
 | |
| 	respprep(&resp, p, msg);
 | |
| 	add_header(&resp, "Content-Length", "0");
 | |
| 	add_blank_header(&resp);
 | |
| 	return send_response(p, &resp);
 | |
| }
 | |
| 
 | |
| static int add_sdp(struct sip_request *resp, struct sip_pvt *p)
 | |
| {
 | |
| 	int len;
 | |
| 	int codec;
 | |
| 	char costr[80];
 | |
| 	struct sockaddr_in sin;
 | |
| 	char v[256];
 | |
| 	char s[256];
 | |
| 	char o[256];
 | |
| 	char c[256];
 | |
| 	char t[256];
 | |
| 	char m[256];
 | |
| 	int x;
 | |
| 	/* XXX We break with the "recommendation" and send our IP, in order that our
 | |
| 	       peer doesn't have to gethostbyname() us XXX */
 | |
| 	len = 0;
 | |
| 	ast_rtp_get_us(p->rtp, &sin);
 | |
| 	printf("We're at %s port %d\n", inet_ntoa(ourip), ntohs(sin.sin_port));	
 | |
| 	snprintf(v, sizeof(v), "v=0\r\n");
 | |
| 	snprintf(s, sizeof(s), "s=Asterisk Call from %s\r\n", ourhost);
 | |
| 	snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), inet_ntoa(ourip));
 | |
| 	snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", inet_ntoa(ourip));
 | |
| 	snprintf(t, sizeof(t), "t=0 0\r\n");
 | |
| 	snprintf(m, sizeof(m), "m=audio %d RTP/AVP 101", ntohs(sin.sin_port));
 | |
| 	for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
 | |
| 		if (p->capability & x) {
 | |
| 			printf("Answering with capability %d\n", x);
 | |
| 			if ((codec = ast2rtp(x)) > -1) {
 | |
| 				snprintf(costr, sizeof(costr), " %d", codec);
 | |
| 				strcat(m, costr);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	strcat(m, "\r\n");
 | |
| 	len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m);
 | |
| 	snprintf(costr, sizeof(costr), "%d", len);
 | |
| 	add_header(resp, "Content-Type", "application/sdp");
 | |
| 	add_header(resp, "Content-Length", costr);
 | |
| 	add_line(resp, v);
 | |
| 	add_line(resp, s);
 | |
| 	add_line(resp, o);
 | |
| 	add_line(resp, c);
 | |
| 	add_line(resp, t);
 | |
| 	add_line(resp, m);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void copy_request(struct sip_request *dst,struct sip_request *src)
 | |
| {
 | |
| 	long offset;
 | |
| 	int x;
 | |
| 	offset = ((void *)dst) - ((void *)src);
 | |
| 	/* First copy stuff */
 | |
| 	memcpy(dst, src, sizeof(*dst));
 | |
| 	/* Now fix pointer arithmetic */
 | |
| 	for (x=0;x<src->headers;x++)
 | |
| 		dst->header[x] += offset;
 | |
| 	for (x=0;x<src->lines;x++)
 | |
| 		dst->line[x] += offset;
 | |
| }
 | |
| 
 | |
| static int transmit_response_with_sdp(struct sip_pvt *p, char *msg)
 | |
| {
 | |
| 	struct sip_request resp;
 | |
| 	respprep(&resp, p, msg);
 | |
| 	add_sdp(&resp, p);
 | |
| 	return send_response(p, &resp);
 | |
| }
 | |
| 
 | |
| static int transmit_invite_with_sdp(struct sip_pvt *p, char *username)
 | |
| {
 | |
| 	struct sip_request req;
 | |
| 	char from[256];
 | |
| 	char to[256];
 | |
| 	char tmp[80];
 | |
| 	char via[256];
 | |
| 	char cid[256];
 | |
| 	char *l, *n=NULL;
 | |
| 	if (p->owner && p->owner->callerid) {
 | |
| 		strcpy(cid, p->owner->callerid);
 | |
| 		ast_callerid_parse(cid, &n, &l);
 | |
| 		if (!n)
 | |
| 			n = l;
 | |
| 	}
 | |
| 	if (!n)
 | |
| 		n = "";
 | |
| 	snprintf(from, sizeof(from), "\"%s\" <sip:sip@%s>;tag=%08x", n, inet_ntoa(ourip), rand());
 | |
| 	if (strlen(username))
 | |
| 		snprintf(to, sizeof(to), " sip:%s@%s",username, inet_ntoa(p->sa.sin_addr));
 | |
| 	else
 | |
| 		snprintf(to, sizeof(to), " sip:%s", inet_ntoa(p->sa.sin_addr));
 | |
| 	snprintf(via, sizeof(via), "SIP/2.0/UDP %s:%d", inet_ntoa(ourip), ourport);
 | |
| 	memset(&req, 0, sizeof(req));
 | |
| 
 | |
| 	init_req(&req, "INVITE", to);
 | |
| 	snprintf(tmp, sizeof(tmp), "%d %s", ++p->cseq, "INVITE");
 | |
| 	add_header(&req, "CSeq", tmp);
 | |
| 
 | |
| 	add_header(&req, "To", to);
 | |
| 	add_header(&req, "From", from);
 | |
| 	add_header(&req, "Call-ID", p->callid);
 | |
| 	add_header(&req, "Via", via);
 | |
| 	add_header(&req, "User-Agent", "Asterisk PBX");
 | |
| 	add_sdp(&req, p);
 | |
| 	/* Use this as the basis */
 | |
| 	copy_request(&p->initreq, &req);
 | |
| 	parse(&p->initreq);
 | |
| 	return send_request(p, &req);
 | |
| }
 | |
| 
 | |
| static int transmit_request(struct sip_pvt *p, char *msg, int inc)
 | |
| {
 | |
| 	struct sip_request resp;
 | |
| 	reqprep(&resp, p, msg, inc);
 | |
| 	add_header(&resp, "Content-Length", "0");
 | |
| 	add_blank_header(&resp);
 | |
| 	return send_request(p, &resp);
 | |
| }
 | |
| 
 | |
| static int expire_register(void *data)
 | |
| {
 | |
| 	struct sip_peer *p = data;
 | |
| 	memset(&p->addr, 0, sizeof(p->addr));
 | |
| 	p->expire = -1;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req)
 | |
| {
 | |
| 	char contact[80]; 
 | |
| 	char *expires = get_header(req, "Expires");
 | |
| 	int expirey = atoi(expires);
 | |
| 	char *c, *n, *pt;
 | |
| 	int port;
 | |
| 	struct hostent *hp;
 | |
| 	struct sockaddr_in oldsin;
 | |
| 	if (!strlen(expires)) {
 | |
| 		expires = strstr(get_header(req, "Contact"), "expires=");
 | |
| 		if (expires) 
 | |
| 			if (sscanf(expires + 8, "%d;", &expirey) != 1)
 | |
| 				expirey = 0;
 | |
| 	}
 | |
| 	/* Look for brackets */
 | |
| 	strncpy(contact, get_header(req, "Contact"), sizeof(contact) - 1);
 | |
| 	c = contact;
 | |
| 	
 | |
| 	if ((n=strchr(c, '<'))) {
 | |
| 		c = n + 1;
 | |
| 		n = strchr(c, '>');
 | |
| 		/* Lose the part after the > */
 | |
| 		if (n) 
 | |
| 			*n = '\0';
 | |
| 	}
 | |
| 	/* Make sure it's a SIP URL */
 | |
| 	if (strncasecmp(c, "sip:", 4)) {
 | |
| 		ast_log(LOG_NOTICE, "'%s' is not a valid SIP contcact\n", c);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	c += 4;
 | |
| 	/* Ditch q */
 | |
| 	n = strchr(c, ';');
 | |
| 	if (n) 
 | |
| 		*n = '\0';
 | |
| 	/* Grab host */
 | |
| 	n = strchr(c, '@');
 | |
| 	if (!n) {
 | |
| 		n = c;
 | |
| 		c = NULL;
 | |
| 	} else {
 | |
| 		*n = '\0';
 | |
| 		n++;
 | |
| 	}
 | |
| 	pt = strchr(n, ':');
 | |
| 	if (pt) {
 | |
| 		*pt = '\0';
 | |
| 		pt++;
 | |
| 		port = atoi(pt);
 | |
| 	} else
 | |
| 		port = DEFAULT_SIP_PORT;
 | |
| 	/* XXX This could block for a long time XXX */
 | |
| 	hp = gethostbyname(n);
 | |
| 	if (!hp)  {
 | |
| 		ast_log(LOG_WARNING, "Invalid host '%s'\n", n);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	memcpy(&oldsin, &p->addr, sizeof(oldsin));
 | |
| 	p->addr.sin_family = AF_INET;
 | |
| 	memcpy(&p->addr.sin_addr, hp->h_addr, sizeof(p->addr.sin_addr));
 | |
| 	p->addr.sin_port = htons(port);
 | |
| 	if (c)
 | |
| 		strncpy(p->username, c, sizeof(p->username) - 1);
 | |
| 	else
 | |
| 		strcpy(p->username, "");
 | |
| 	if (p->expire > -1)
 | |
| 		ast_sched_del(sched, p->expire);
 | |
| 	if ((expirey < 1) || (expirey > MAX_EXPIREY))
 | |
| 		expirey = DEFAULT_EXPIREY;
 | |
| 	p->expire = ast_sched_add(sched, expirey * 1000, expire_register, p);
 | |
| 	pvt->expirey = expirey;
 | |
| 	if (memcmp(&p->addr, &oldsin, sizeof(oldsin))) {
 | |
| 		if (option_verbose > 2)
 | |
| 			ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s port %d expires %d\n", p->username, inet_ntoa(p->addr.sin_addr), ntohs(p->addr.sin_port), expirey);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req)
 | |
| {
 | |
| 	int res = -1;
 | |
| 	struct sip_peer *peer;
 | |
| 	char tmp[256];
 | |
| 	char *name, *c;
 | |
| 	strncpy(tmp, get_header(req, "To"), sizeof(tmp) - 1);
 | |
| 	c = ditch_braces(tmp);
 | |
| 	if (strncmp(c, "sip:", 4)) {
 | |
| 		ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s\n", tmp, inet_ntoa(sin->sin_addr));
 | |
| 		return -1;
 | |
| 	}
 | |
| 	name = c + 4;
 | |
| 	c = strchr(name, '@');
 | |
| 	if (c) 
 | |
| 		*c = '\0';
 | |
| 	ast_pthread_mutex_lock(&peerl.lock);
 | |
| 	peer = peerl.peers;
 | |
| 	while(peer) {
 | |
| 		if (!strcasecmp(peer->name, name) && peer->dynamic) {
 | |
| 			if (parse_contact(p, peer, req)) 
 | |
| 				ast_log(LOG_WARNING, "Failed to parse contact info\n");
 | |
| 			else
 | |
| 				res = 0;
 | |
| 			
 | |
| 		}	
 | |
| 		peer = peer->next;
 | |
| 	}
 | |
| 	ast_pthread_mutex_unlock(&peerl.lock);
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static int get_destination(struct sip_pvt *p)
 | |
| {
 | |
| 	char tmp[256], *c, *a;
 | |
| 	strncpy(tmp, get_header(&p->initreq, "To"), sizeof(tmp));
 | |
| 	c = ditch_braces(tmp);
 | |
| 	if (strncmp(c, "sip:", 4)) {
 | |
| 		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", c);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	c += 4;
 | |
| 	if ((a = strchr(c, '@')) || (a = strchr(c, ';'))) {
 | |
| 		*a = '\0';
 | |
| 	}
 | |
| 	printf("Looking for %s in %s\n", c, p->context);
 | |
| 	if (ast_exists_extension(NULL, p->context, c, 1, NULL)) {
 | |
| 		strncpy(p->exten, c, sizeof(p->exten));
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int check_via(struct sip_pvt *p, struct sip_request *req)
 | |
| {
 | |
| 	char via[256];
 | |
| 	char *c, *pt;
 | |
| 	struct hostent *hp;
 | |
| 	strncpy(via, get_header(req, "Via"), sizeof(via));
 | |
| 	c = strchr(via, ' ');
 | |
| 	if (c) {
 | |
| 		*c = '\0';
 | |
| 		c++;
 | |
| 		if (strcmp(via, "SIP/2.0/UDP")) {
 | |
| 			ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		pt = strchr(c, ':');
 | |
| 		if (pt) {
 | |
| 			*pt = '\0';
 | |
| 			pt++;
 | |
| 		}
 | |
| 		hp = gethostbyname(c);
 | |
| 		if (!hp) {
 | |
| 			ast_log(LOG_WARNING, "'%s' is not a valid host\n", c);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		memset(&p->sa, 0, sizeof(p->sa));
 | |
| 		p->sa.sin_family = AF_INET;
 | |
| 		p->sa.sin_port = htons(pt ? atoi(pt) : DEFAULT_SIP_PORT);
 | |
| 		memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
 | |
| 		printf("Sending to %s : %d\n", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req)
 | |
| {
 | |
| 	struct ast_frame f;
 | |
| 	memset(&f, 0, sizeof(f));
 | |
| 	if (p->outgoing) {
 | |
| 		switch(resp) {
 | |
| 		case 100:
 | |
| 			/* Not important */
 | |
| 			break;
 | |
| 		case 180:
 | |
| 			if (p->owner) {
 | |
| 				f.frametype = AST_FRAME_CONTROL;
 | |
| 				f.subclass = AST_CONTROL_RINGING;
 | |
| 				ast_queue_frame(p->owner, &f, 1);
 | |
| 				if (p->owner->state != AST_STATE_UP)
 | |
| 					p->owner->state = AST_STATE_RINGING;
 | |
| 			}
 | |
| 			break;
 | |
| 		case 200:
 | |
| 			process_sdp(p, req);
 | |
| 			if (p->owner) {
 | |
| 				if (p->owner->state != AST_STATE_UP) {
 | |
| 					f.frametype = AST_FRAME_CONTROL;
 | |
| 					f.subclass = AST_CONTROL_ANSWER;
 | |
| 					p->owner->state = AST_STATE_UP;
 | |
| 					ast_queue_frame(p->owner, &f, 1);
 | |
| 					transmit_request(p, "ACK", 0);
 | |
| 				}
 | |
| 			}
 | |
| 			break;
 | |
| 		default:
 | |
| 			if ((resp >= 400) && (resp < 700)) {
 | |
| 				if (option_verbose > 2) 
 | |
| 					ast_verbose(VERBOSE_PREFIX_3 "Got SIP response %d \"%s\" back from %s\n", resp, rest, inet_ntoa(p->sa.sin_addr));
 | |
| 				p->alreadygone = 1;
 | |
| 				if (p->rtp) {
 | |
| 					/* Immediately stop RTP */
 | |
| 					ast_rtp_destroy(p->rtp);
 | |
| 					p->rtp = NULL;
 | |
| 				}
 | |
| 				/* Send hangup */	
 | |
| 				if (p->owner)
 | |
| 					ast_queue_hangup(p->owner, 1);
 | |
| 			} else
 | |
| 				ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : inet_ntoa(p->sa.sin_addr));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin)
 | |
| {
 | |
| 	struct sip_request resp;
 | |
| 	char *cmd;
 | |
| 	char *cseq;
 | |
| 	char *e;
 | |
| 	struct ast_channel *c;
 | |
| 	int seqno;
 | |
| 	int len;
 | |
| 	int ignore=0;
 | |
| 	int respid;
 | |
| 	/* Clear out potential response */
 | |
| 	memset(&resp, 0, sizeof(resp));
 | |
| 	/* Get Method and Cseq */
 | |
| 	cseq = get_header(req, "Cseq");
 | |
| 	cmd = req->header[0];
 | |
| 	/* Must have Cseq */
 | |
| 	if (!strlen(cmd) || !strlen(cseq))
 | |
| 			return -1;
 | |
| 	if (sscanf(cseq, "%i%n", &seqno, &len) != 1) {
 | |
| 		ast_log(LOG_DEBUG, "No seqno in '%s'\n", cmd);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (p->cseq && (p->cseq < seqno)) {
 | |
| 		ast_log(LOG_DEBUG, "Ignoring out of order packet %d\n", seqno);
 | |
| 		return -1;
 | |
| 	} else if (p->cseq && (p->cseq != seqno)) {
 | |
| 		/* ignore means "don't do anything with it" but still have to 
 | |
| 		   respond appropriately  */
 | |
| 		ignore=1;
 | |
| 	}
 | |
| 	
 | |
| 	/* Get the command */
 | |
| 	cseq += len;
 | |
| 	while(*cmd && (*cmd < 33))
 | |
| 		cmd++;
 | |
| 	if (!*cmd)
 | |
| 		return -1;
 | |
| 	e = cmd;
 | |
| 	while(*e && (*e > 32))
 | |
| 		e++;
 | |
| 	/* Get the command */
 | |
| 	if (*e) {
 | |
| 		*e = '\0';
 | |
| 		e++;
 | |
| 	}
 | |
| 	if (strcmp(cmd, "SIP/2.0"))
 | |
| 		/* Next should follow monotonically increasing */
 | |
| 		p->cseq = seqno + 1;
 | |
| 
 | |
| 	if (!strcasecmp(cmd, "INVITE")) {
 | |
| 		/* Process the SDP portion */
 | |
| 		if (!ignore) {
 | |
| 			/* Use this as the basis */
 | |
| 			printf("Using latest request as basis request\n");
 | |
| 			copy_request(&p->initreq, req);
 | |
| 			check_via(p, req);
 | |
| 			if (process_sdp(p, req))
 | |
| 				return -1;
 | |
| 		} else
 | |
| 			printf("Ignoring this request\n");
 | |
| 		if (!p->lastinvite) {
 | |
| 			/* Initialize the context if it hasn't been already */
 | |
| 			if (!strlen(p->context))
 | |
| 				strncpy(p->context, context, sizeof(p->context));
 | |
| 			if (get_destination(p)) {
 | |
| 				transmit_response(p, "404 Not Found");
 | |
| 				sip_destroy(p);
 | |
| 				p = NULL;
 | |
| 				c = NULL;
 | |
| 			} else {
 | |
| 				/* If no extension was specified, use the s one */
 | |
| 				if (!strlen(p->exten))
 | |
| 					strncpy(p->exten, "s", sizeof(p->exten));
 | |
| 				/* First invitation */
 | |
| 				c = sip_new(p, AST_STATE_RING);
 | |
| 			}
 | |
| 			
 | |
| 		} else 
 | |
| 			c = p->owner;
 | |
| 		if (!ignore && p)
 | |
| 			p->lastinvite = seqno;
 | |
| 		if (c) {
 | |
| 			switch(c->state) {
 | |
| 			case AST_STATE_RING:
 | |
| 				transmit_response(p, "100 Trying");
 | |
| 				break;
 | |
| 			case AST_STATE_RINGING:
 | |
| 				transmit_response(p, "180 Ringing");
 | |
| 				break;
 | |
| 			case AST_STATE_UP:
 | |
| 				transmit_response_with_sdp(p, "200 OK");
 | |
| 				break;
 | |
| 			default:
 | |
| 				ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->state);
 | |
| 				transmit_response(p, "100 Trying");
 | |
| 			}
 | |
| 		} else {
 | |
| 			if (p) {
 | |
| 				ast_log(LOG_NOTICE, "Unable to create/find channel\n");
 | |
| 				transmit_response(p, "503 Unavailable");
 | |
| 				sip_destroy(p);
 | |
| 			}
 | |
| 		}
 | |
| 	} else if (!strcasecmp(cmd, "CANCEL") || !strcasecmp(cmd, "BYE")) {
 | |
| 		copy_request(&p->initreq, req);
 | |
| 		/* Hangup this channel */
 | |
| 		p->alreadygone = 1;
 | |
| 		if (p->rtp) {
 | |
| 			/* Immediately stop RTP */
 | |
| 			ast_rtp_destroy(p->rtp);
 | |
| 			p->rtp = NULL;
 | |
| 		}
 | |
| 		if (p->owner)
 | |
| 			ast_queue_hangup(p->owner, 1);
 | |
| 		transmit_response(p, "200 OK");
 | |
| 	} else if (!strcasecmp(cmd, "REGISTER")) {
 | |
| 		/* Use this as the basis */
 | |
| 		printf("Using latest request as basis request\n");
 | |
| 		copy_request(&p->initreq, req);
 | |
| 		check_via(p, req);
 | |
| 		transmit_response(p, "100 Trying");
 | |
| 		if (register_verify(p, sin, req)) {
 | |
| 			ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s'\n", get_header(req, "To"), inet_ntoa(sin->sin_addr));
 | |
| 			transmit_response(p, "401 Unauthorized");
 | |
| 		} else {
 | |
| 			transmit_response(p, "200 OK");
 | |
| 		}
 | |
| 		sip_destroy(p);
 | |
| 	} else if (!strcasecmp(cmd, "ACK")) {
 | |
| 		/* Uhm, I haven't figured out the point of the ACK yet.  Are we
 | |
| 		   supposed to retransmit responses until we get an ack? 
 | |
| 		   Make sure this is on a valid call */
 | |
| 		if (!p->lastinvite)
 | |
| 			sip_destroy(p);
 | |
| 	} else if (!strcasecmp(cmd, "SIP/2.0")) {
 | |
| 		while(*e && (*e < 33)) e++;
 | |
| 		if (sscanf(e, "%i %n", &respid, &len) != 1) {
 | |
| 			ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
 | |
| 		} else {
 | |
| 			handle_response(p, respid, e + len, req);
 | |
| 		}
 | |
| 	} else {
 | |
| 		ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", 
 | |
| 			cmd, inet_ntoa(p->sa.sin_addr));
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int sipsock_read(int *id, int fd, short events, void *ignore)
 | |
| {
 | |
| 	struct sip_request req;
 | |
| 	struct sockaddr_in sin;
 | |
| 	struct sip_pvt *p;
 | |
| 	int res;
 | |
| 	int len;
 | |
| 	len = sizeof(sin);
 | |
| 	res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
 | |
| 	if (res < 0) {
 | |
| 		if (errno != ECONNREFUSED)
 | |
| 			ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
 | |
| 		return 1;
 | |
| 	}
 | |
| 	req.data[res] = '\0';
 | |
| 	req.len = res;
 | |
| 	printf("Sip read: \n%s\n", req.data);
 | |
| 	parse(&req);
 | |
| 	if (req.headers < 2) {
 | |
| 		/* Must have at least two headers */
 | |
| 		return 1;
 | |
| 	}
 | |
| 	/* Process request, with iflock held */
 | |
| 	ast_pthread_mutex_lock(&netlock);
 | |
| 	p = find_call(&req, &sin);
 | |
| 	if (p) {
 | |
| 		handle_request(p, &req, &sin);
 | |
| 	}
 | |
| 	ast_pthread_mutex_unlock(&netlock);
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static void *do_monitor(void *data)
 | |
| {
 | |
| 	int res;
 | |
| 	struct sip_pkt *p;
 | |
| 	struct sip_pvt *sip;
 | |
| 	sched = sched_context_create();
 | |
| 	if (!sched) {
 | |
| 		ast_log(LOG_WARNING, "Unable to create schedule context\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	io = io_context_create();
 | |
| 	if (!io) {
 | |
| 		ast_log(LOG_WARNING, "Unable to create I/O context\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	
 | |
| 	/* Add an I/O event to our UDP socket */
 | |
| 	if (sipsock > -1) 
 | |
| 		ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
 | |
| 	
 | |
| 	/* This thread monitors all the frame relay interfaces which are not yet in use
 | |
| 	   (and thus do not have a separate thread) indefinitely */
 | |
| 	/* From here on out, we die whenever asked */
 | |
| 	for(;;) {
 | |
| 		/* Check for interfaces needing to be killed */
 | |
| 		ast_pthread_mutex_lock(&iflock);
 | |
| restartsearch:		
 | |
| 		sip = iflist;
 | |
| 		while(sip) {
 | |
| 			if (sip->needdestroy) {
 | |
| 				__sip_destroy(sip);
 | |
| 				goto restartsearch;
 | |
| 			}
 | |
| 			sip = sip->next;
 | |
| 		}
 | |
| 		ast_pthread_mutex_unlock(&iflock);
 | |
| 		/* Don't let anybody kill us right away.  Nobody should lock the interface list
 | |
| 		   and wait for the monitor list, but the other way around is okay. */
 | |
| 		ast_pthread_mutex_lock(&monlock);
 | |
| 		/* Lock the network interface */
 | |
| 		ast_pthread_mutex_lock(&netlock);
 | |
| 		p = packets;
 | |
| 		while(p) {
 | |
| 			/* Handle any retransmissions */
 | |
| 			p = p->next;
 | |
| 		}
 | |
| 		/* Okay, now that we know what to do, release the network lock */
 | |
| 		ast_pthread_mutex_unlock(&netlock);
 | |
| 		/* And from now on, we're okay to be killed, so release the monitor lock as well */
 | |
| 		ast_pthread_mutex_unlock(&monlock);
 | |
| 		pthread_testcancel();
 | |
| 		/* Wait for sched or io */
 | |
| 		res = ast_sched_wait(sched);
 | |
| 		res = ast_io_wait(io, res);
 | |
| 		ast_pthread_mutex_lock(&monlock);
 | |
| 		if (res >= 0) 
 | |
| 			ast_sched_runq(sched);
 | |
| 		ast_pthread_mutex_unlock(&monlock);
 | |
| 	}
 | |
| 	/* Never reached */
 | |
| 	return NULL;
 | |
| 	
 | |
| }
 | |
| 
 | |
| static int restart_monitor(void)
 | |
| {
 | |
| 	/* If we're supposed to be stopped -- stay stopped */
 | |
| 	if (monitor_thread == -2)
 | |
| 		return 0;
 | |
| 	if (ast_pthread_mutex_lock(&monlock)) {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (monitor_thread == pthread_self()) {
 | |
| 		ast_pthread_mutex_unlock(&monlock);
 | |
| 		ast_log(LOG_WARNING, "Cannot kill myself\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (monitor_thread) {
 | |
| 		/* Wake up the thread */
 | |
| 		pthread_kill(monitor_thread, SIGURG);
 | |
| 	} else {
 | |
| 		/* Start a new monitor */
 | |
| 		if (pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
 | |
| 			ast_pthread_mutex_unlock(&monlock);
 | |
| 			ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 	ast_pthread_mutex_unlock(&monlock);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct ast_channel *sip_request(char *type, int format, void *data)
 | |
| {
 | |
| 	int oldformat;
 | |
| 	struct sip_pvt *p;
 | |
| 	struct ast_channel *tmp = NULL;
 | |
| 	/* We can only support G.723.1 formatted frames, but we should never
 | |
| 	   be asked to support anything else anyway, since we've published
 | |
| 	   our capabilities when we registered. */
 | |
| 	oldformat = format;
 | |
| 	format &= capability;
 | |
| 	if (!format) {
 | |
| 		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	p = sip_alloc(NULL, NULL);
 | |
| 	if (!p) {
 | |
| 		ast_log(LOG_WARNING, "Unable to build sip pvt data for '%s'\n", (char *)data);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	tmp = sip_new(p, AST_STATE_DOWN);
 | |
| 	restart_monitor();
 | |
| 	return tmp;
 | |
| }
 | |
| 
 | |
| static struct sip_user *build_user(char *name, struct ast_variable *v)
 | |
| {
 | |
| 	struct sip_user *user;
 | |
| 	int format;
 | |
| 	user = (struct sip_user *)malloc(sizeof(struct sip_user));
 | |
| 	if (user) {
 | |
| 		memset(user, 0, sizeof(struct sip_user));
 | |
| 		strncpy(user->name, name, sizeof(user->name)-1);
 | |
| 		while(v) {
 | |
| 			if (!strcasecmp(v->name, "context")) {
 | |
| 				strncpy(user->context, v->value, sizeof(user->context));
 | |
| 			} else if (!strcasecmp(v->name, "permit") ||
 | |
| 					   !strcasecmp(v->name, "deny")) {
 | |
| 				user->ha = ast_append_ha(v->name, v->value, user->ha);
 | |
| 			} else if (!strcasecmp(v->name, "auth")) {
 | |
| 				strncpy(user->methods, v->value, sizeof(user->methods)-1);
 | |
| 			} else if (!strcasecmp(v->name, "secret")) {
 | |
| 				strncpy(user->secret, v->value, sizeof(user->secret)-1);
 | |
| 			} else if (!strcasecmp(v->name, "callerid")) {
 | |
| 				strncpy(user->callerid, v->value, sizeof(user->callerid)-1);
 | |
| 				user->hascallerid=1;
 | |
| 			} else if (!strcasecmp(v->name, "accountcode")) {
 | |
| 				strncpy(user->accountcode, v->value, sizeof(user->accountcode)-1);
 | |
| 			} else if (!strcasecmp(v->name, "amaflags")) {
 | |
| 				format = ast_cdr_amaflags2int(v->value);
 | |
| 				if (format < 0) {
 | |
| 					ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
 | |
| 				} else {
 | |
| 					user->amaflags = format;
 | |
| 				}
 | |
| 			} else if (!strcasecmp(v->name, "insecure")) {
 | |
| 				user->insecure = ast_true(v->value);
 | |
| 			}
 | |
| 			v = v->next;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!strlen(user->methods)) {
 | |
| 		if (strlen(user->secret)) 
 | |
| 			strncpy(user->methods, "md5,plaintext", sizeof(user->methods) - 1);
 | |
| 	}
 | |
| 	return user;
 | |
| }
 | |
| 
 | |
| static struct sip_peer *build_peer(char *name, struct ast_variable *v)
 | |
| {
 | |
| 	struct sip_peer *peer;
 | |
| 	struct sip_peer *prev;
 | |
| 	int maskfound=0;
 | |
| 	int format;
 | |
| 	int found=0;
 | |
| 	prev = NULL;
 | |
| 	ast_pthread_mutex_lock(&peerl.lock);
 | |
| 	peer = peerl.peers;
 | |
| 	while(peer) {
 | |
| 		if (!strcasecmp(peer->name, name)) {	
 | |
| 			break;
 | |
| 		}
 | |
| 		prev = peer;
 | |
| 		peer = peer->next;
 | |
| 	}
 | |
| 	if (peer) {
 | |
| 		found++;
 | |
| 		/* Already in the list, remove it and it will be added back (or FREE'd) */
 | |
| 		if (prev) {
 | |
| 			prev->next = peer->next;
 | |
| 		} else {
 | |
| 			peerl.peers = peer->next;
 | |
| 		}
 | |
| 		ast_pthread_mutex_unlock(&peerl.lock);
 | |
|  	} else {
 | |
| 		ast_pthread_mutex_unlock(&peerl.lock);
 | |
| 		peer = malloc(sizeof(struct sip_peer));
 | |
| 		memset(peer, 0, sizeof(struct sip_peer));
 | |
| 		peer->expire = -1;
 | |
| 	}
 | |
| 	if (peer) {
 | |
| 		if (!found) {
 | |
| 			strncpy(peer->name, name, sizeof(peer->name)-1);
 | |
| 			peer->addr.sin_port = htons(DEFAULT_SIP_PORT);
 | |
| 			peer->expirey = expirey;
 | |
| 		}
 | |
| 		peer->capability = capability;
 | |
| 		while(v) {
 | |
| 			if (!strcasecmp(v->name, "secret")) 
 | |
| 				strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
 | |
| 			else if (!strcasecmp(v->name, "auth")) 
 | |
| 				strncpy(peer->methods, v->value, sizeof(peer->methods)-1);
 | |
| 			else if (!strcasecmp(v->name, "host")) {
 | |
| 				if (!strcasecmp(v->value, "dynamic")) {
 | |
| 					/* They'll register with us */
 | |
| 					peer->dynamic = 1;
 | |
| 					if (!found) {
 | |
| 						/* Initialize stuff iff we're not found, otherwise
 | |
| 						   we keep going with what we had */
 | |
| 						memset(&peer->addr.sin_addr, 0, 4);
 | |
| 						if (peer->addr.sin_port) {
 | |
| 							/* If we've already got a port, make it the default rather than absolute */
 | |
| 							peer->defaddr.sin_port = peer->addr.sin_port;
 | |
| 							peer->addr.sin_port = 0;
 | |
| 						}
 | |
| 					}
 | |
| 				} else {
 | |
| 					/* Non-dynamic.  Make sure we become that way if we're not */
 | |
| 					if (peer->expire > -1)
 | |
| 						ast_sched_del(sched, peer->expire);
 | |
| 					peer->expire = -1;
 | |
| 					peer->dynamic = 0;
 | |
| 					if (ast_get_ip(&peer->addr, v->value)) {
 | |
| 						free(peer);
 | |
| 						return NULL;
 | |
| 					}
 | |
| 				}
 | |
| 				if (!maskfound)
 | |
| 					inet_aton("255.255.255.255", &peer->mask);
 | |
| 			} else if (!strcasecmp(v->name, "defaultip")) {
 | |
| 				if (ast_get_ip(&peer->defaddr, v->value)) {
 | |
| 					free(peer);
 | |
| 					return NULL;
 | |
| 				}
 | |
| 			} else if (!strcasecmp(v->name, "permit") ||
 | |
| 					   !strcasecmp(v->name, "deny")) {
 | |
| 				peer->ha = ast_append_ha(v->name, v->value, peer->ha);
 | |
| 			} else if (!strcasecmp(v->name, "mask")) {
 | |
| 				maskfound++;
 | |
| 				inet_aton(v->value, &peer->mask);
 | |
| 			} else if (!strcasecmp(v->name, "port")) {
 | |
| 				if (peer->dynamic)
 | |
| 					peer->defaddr.sin_port = htons(atoi(v->value));
 | |
| 				else
 | |
| 					peer->addr.sin_port = htons(atoi(v->value));
 | |
| 			} else if (!strcasecmp(v->name, "username")) {
 | |
| 				strncpy(peer->username, v->value, sizeof(peer->username)-1);
 | |
| 			} else if (!strcasecmp(v->name, "allow")) {
 | |
| 				format = ast_getformatbyname(v->value);
 | |
| 				if (format < 1) 
 | |
| 					ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
 | |
| 				else
 | |
| 					peer->capability |= format;
 | |
| 			} else if (!strcasecmp(v->name, "disallow")) {
 | |
| 				format = ast_getformatbyname(v->value);
 | |
| 				if (format < 1) 
 | |
| 					ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
 | |
| 				else
 | |
| 					peer->capability &= ~format;
 | |
| 			} else if (!strcasecmp(v->name, "insecure")) {
 | |
| 				peer->insecure = ast_true(v->value);
 | |
| 			}
 | |
| 
 | |
| 			v=v->next;
 | |
| 		}
 | |
| 		if (!strlen(peer->methods))
 | |
| 			strcpy(peer->methods, "md5,plaintext");
 | |
| 		peer->delme = 0;
 | |
| 	}
 | |
| 	return peer;
 | |
| }
 | |
| 
 | |
| int load_module()
 | |
| {
 | |
| 	struct ast_config *cfg;
 | |
| 	struct ast_variable *v;
 | |
| 	struct sip_peer *peer;
 | |
| 	struct sip_user *user;
 | |
| 	char *cat;
 | |
|     char *utype;
 | |
| 	struct hostent *hp;
 | |
| 	
 | |
| 	if (gethostname(ourhost, sizeof(ourhost))) {
 | |
| 		ast_log(LOG_WARNING, "Unable to get hostname, SIP disabled\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	hp = gethostbyname(ourhost);
 | |
| 	if (!hp) {
 | |
| 		ast_log(LOG_WARNING, "Unable to get our IP address, SIP disabled\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	memcpy(&ourip, hp->h_addr, sizeof(ourip));
 | |
| 	cfg = ast_load(config);
 | |
| 
 | |
| 	/* We *must* have a config file otherwise stop immediately */
 | |
| 	if (!cfg) {
 | |
| 		ast_log(LOG_NOTICE, "Unable to load config %s, SIP disabled\n", config);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	memset(&bindaddr, 0, sizeof(bindaddr));
 | |
| 	v = ast_variable_browse(cfg, "general");
 | |
| 	while(v) {
 | |
| 		/* Create the interface list */
 | |
| 		if (!strcasecmp(v->name, "context")) {
 | |
| 			strncpy(context, v->value, sizeof(context)-1);
 | |
| 		} else if (!strcasecmp(v->name, "language")) {
 | |
| 			strncpy(language, v->value, sizeof(language)-1);
 | |
| 		} else if (!strcasecmp(v->name, "bindaddr")) {
 | |
| 			if (!(hp = gethostbyname(v->value))) {
 | |
| 				ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
 | |
| 			} else {
 | |
| 				memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
 | |
| 			}
 | |
| 		} else if (!strcasecmp(v->name, "port")) {
 | |
| 			if (sscanf(v->value, "%i", &ourport) == 1) {
 | |
| 				bindaddr.sin_port = htons(ourport);
 | |
| 			} else {
 | |
| 				ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
 | |
| 			}
 | |
| 		}
 | |
| 		v = v->next;
 | |
| 	}
 | |
| 	
 | |
| 	cat = ast_category_browse(cfg, NULL);
 | |
| 	while(cat) {
 | |
| 		if (strcasecmp(cat, "general")) {
 | |
| 			utype = ast_variable_retrieve(cfg, cat, "type");
 | |
| 			if (utype) {
 | |
| 				if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
 | |
| 					user = build_user(cat, ast_variable_browse(cfg, cat));
 | |
| 					if (user) {
 | |
| 						ast_pthread_mutex_lock(&userl.lock);
 | |
| 						user->next = userl.users;
 | |
| 						userl.users = user;
 | |
| 						ast_pthread_mutex_unlock(&userl.lock);
 | |
| 					}
 | |
| 				}
 | |
| 				if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) {
 | |
| 					peer = build_peer(cat, ast_variable_browse(cfg, cat));
 | |
| 					if (peer) {
 | |
| 						ast_pthread_mutex_lock(&peerl.lock);
 | |
| 						peer->next = peerl.peers;
 | |
| 						peerl.peers = peer;
 | |
| 						ast_pthread_mutex_unlock(&peerl.lock);
 | |
| 					}
 | |
| 				} else if (strcasecmp(utype, "user")) {
 | |
| 					ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf");
 | |
| 				}
 | |
| 			} else
 | |
| 				ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
 | |
| 		}
 | |
| 		cat = ast_category_browse(cfg, cat);
 | |
| 	}
 | |
| 	
 | |
| 	if (!ntohs(bindaddr.sin_port))
 | |
| 		bindaddr.sin_port = ntohs(DEFAULT_SIP_PORT);
 | |
| 	bindaddr.sin_family = AF_INET;
 | |
| 	pthread_mutex_lock(&netlock);
 | |
| 	if (sipsock > -1)
 | |
| 		close(sipsock);
 | |
| 	sipsock = socket(AF_INET, SOCK_DGRAM, 0);
 | |
| 	if (sipsock < 0) {
 | |
| 		ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno));
 | |
| 	} else {
 | |
| 		if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
 | |
| 			ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
 | |
| 					inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
 | |
| 						strerror(errno));
 | |
| 			close(sipsock);
 | |
| 			sipsock = -1;
 | |
| 		} else if (option_verbose > 1)
 | |
| 			ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", 
 | |
| 				inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
 | |
| 	}
 | |
| 	pthread_mutex_unlock(&netlock);
 | |
| 
 | |
| 	/* Make sure we can register our sip channel type */
 | |
| 	if (ast_channel_register(type, tdesc, capability, sip_request)) {
 | |
| 		ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
 | |
| 		ast_destroy(cfg);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	ast_destroy(cfg);
 | |
| 	/* And start the monitor for the first time */
 | |
| 	restart_monitor();
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int unload_module()
 | |
| {
 | |
| 	struct sip_pvt *p, *pl;
 | |
| 	/* First, take us out of the channel loop */
 | |
| 	ast_channel_unregister(type);
 | |
| 	if (!ast_pthread_mutex_lock(&iflock)) {
 | |
| 		/* Hangup all interfaces if they have an owner */
 | |
| 		p = iflist;
 | |
| 		while(p) {
 | |
| 			if (p->owner)
 | |
| 				ast_softhangup(p->owner);
 | |
| 			p = p->next;
 | |
| 		}
 | |
| 		iflist = NULL;
 | |
| 		ast_pthread_mutex_unlock(&iflock);
 | |
| 	} else {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (!ast_pthread_mutex_lock(&monlock)) {
 | |
| 		if (monitor_thread) {
 | |
| 			pthread_cancel(monitor_thread);
 | |
| 			pthread_kill(monitor_thread, SIGURG);
 | |
| 			pthread_join(monitor_thread, NULL);
 | |
| 		}
 | |
| 		monitor_thread = -2;
 | |
| 		ast_pthread_mutex_unlock(&monlock);
 | |
| 	} else {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (!ast_pthread_mutex_lock(&iflock)) {
 | |
| 		/* Destroy all the interfaces and free their memory */
 | |
| 		p = iflist;
 | |
| 		while(p) {
 | |
| 			pl = p;
 | |
| 			p = p->next;
 | |
| 			/* Free associated memory */
 | |
| 			free(pl);
 | |
| 		}
 | |
| 		iflist = NULL;
 | |
| 		ast_pthread_mutex_unlock(&iflock);
 | |
| 	} else {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 		
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int usecount()
 | |
| {
 | |
| 	int res;
 | |
| 	ast_pthread_mutex_lock(&usecnt_lock);
 | |
| 	res = usecnt;
 | |
| 	ast_pthread_mutex_unlock(&usecnt_lock);
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| char *key()
 | |
| {
 | |
| 	return ASTERISK_GPL_KEY;
 | |
| }
 | |
| 
 | |
| char *description()
 | |
| {
 | |
| 	return desc;
 | |
| }
 | |
| 
 |