Newer
Older
/*
* 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 <asterisk/cli.h>
#include <asterisk/md5.h>
#include <asterisk/app.h>
#include <asterisk/parking.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include <netinet/ip.h>
/* #define VOCAL_DATA_HACK */
#define DEFAULT_DEFAULT_EXPIRY 120
#define DEFAULT_MAX_EXPIRY 3600
#define SIP_DTMF_RFC2833 (1 << 0)
#define SIP_DTMF_INBAND (1 << 1)
#define SIP_DTMF_INFO (1 << 2)
static int max_expiry = DEFAULT_MAX_EXPIRY;
static int default_expiry = DEFAULT_DEFAULT_EXPIRY;
#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... */
#define DEFAULT_RETRANS 1000 /* How frequently to retransmit */
#define MAX_RETRANS 5 /* Try only 5 times for retransmissions */
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 char callerid[AST_MAX_EXTENSION] = "asterisk";
Mark Spencer
committed
static char fromdomain[AST_MAX_EXTENSION] = "";
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);
/* Codecs that we support by default: */
static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM;
static int sipdebug = 0;
static int tos = 0;
static int globaldtmfmode = SIP_DTMF_RFC2833;
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
static struct sip_codec_pref {
int codec;
struct sip_codec_pref *next;
} *prefs;
char *rlPart1; /* SIP Method Name or "SIP/2.0" protocol version */
char *rlPart2; /* The Request URI or Response Status */
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];
};
struct sip_route {
struct sip_route *next;
char hop[0];
};
char randdata[80]; /* Random data */
unsigned int ocseq; /* Current outgoing seqno */
unsigned int icseq; /* Current incoming seqno */
unsigned int callgroup;
unsigned int pickupgroup;
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 authtries; /* Times we've tried to authenticate */
int canreinvite; /* Do we support reinvite */
int progress; /* Have sent 183 message progress */
int nat; /* Whether to try to support NAT */
struct sockaddr_in recv; /* Received as */
struct ast_channel *owner; /* Who owns us */
char exten[AST_MAX_EXTENSION]; /* Extention where to start */
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 */
struct sip_pvt *refer_call; /* Call we are referring */
struct sip_route *route; /* Head of linked list of routing steps (fm Record-Route) */
char fromdomain[AST_MAX_EXTENSION]; /* Domain to show in the from field */
Mark Spencer
committed
char fromuser[AST_MAX_EXTENSION]; /* Domain to show in the user field */
char tohost[AST_MAX_EXTENSION]; /* Host we should put in the "to" field */
char theirtag[256]; /* Their tag */
char username[81];
char uri[81]; /* Original requested URI */
char our_contact[256]; /* Our contact header */
char realm[256]; /* Authorization realm */
char nonce[256]; /* Authorization nonce */
int pendinginvite; /* Any pending invite */
int pendingbye; /* Need to send bye after we ack? */
struct sip_request initreq; /* Initial request */
int maxtime; /* Max time for first response */
int initid; /* Auto-congest ID if appropriate */
int subscribed;
int stateid;
int dialogver;
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 */
struct sip_pkt *packets; /* Packets scheduled for re-transmission */
struct sip_pkt {
struct sip_pkt *next; /* Next packet */
int retrans; /* Retransmission number */
int seqno; /* Sequence number */
int resp; /* non-zero if this is a response packet (e.g. 200 OK) */
struct sip_pvt *owner; /* Owner call */
int retransid; /* Retransmission ID */
int packetlen; /* Length of packet */
char data[0];
};
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];
unsigned int callgroup;
unsigned int pickupgroup;
int hascallerid;
int amaflags;
int insecure;
struct ast_ha *ha;
struct sip_user *next;
};
struct sip_peer {
char name[80];
char secret[80];
char context[80]; /* JK02: peers need context too to allow parking etc */
Mark Spencer
committed
char fromuser[80];
char fromdomain[80];
int lastmsgssent;
time_t lastmsgcheck;
unsigned int callgroup;
unsigned int pickupgroup;
struct sockaddr_in addr;
struct in_addr mask;
/* 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 */
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 };
#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]; /* Who we are registering as */
char authuser[80]; /* Who we *authenticate* as */
char secret[80]; /* Password or key name in []'s */
char contact[80]; /* Contact extension */
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 */
unsigned int ocseq; /* Sequence number we got to for REGISTERs for this registry */
struct sockaddr_in us; /* Who the server thinks we are */
struct sip_registry *next;
};
#define REINVITE_INVITE 1
#define REINVITE_UPDATE 2
static int sip_do_register(struct sip_registry *r);
struct sip_registry *registrations;
static struct sockaddr_in bindaddr;
static struct ast_frame *sip_read(struct ast_channel *ast);
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, int retrans);
static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand, int reliable);
static int transmit_request(struct sip_pvt *p, char *msg, int inc, int reliable);
static int transmit_request_with_auth(struct sip_pvt *p, char *msg, int inc, int reliable);
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_info_with_digit(struct sip_pvt *p, char digit);
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);
static void free_old_route(struct sip_route *route);
static int build_reply_digest(struct sip_pvt *p, char *orig_header, char *digest, int digest_len);
static int __sip_xmit(struct sip_pvt *p, char *data, int len)
{
int res;
if (p->nat)
res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->recv, sizeof(struct sockaddr_in));
else
res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_in));
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));
static void sip_destroy(struct sip_pvt *p);
static int retrans_pkt(void *data)
{
struct sip_pkt *pkt=data;
int res = 0;
ast_pthread_mutex_lock(&pkt->owner->lock);
if (pkt->retrans < MAX_RETRANS) {
pkt->retrans++;
if (sipdebug) {
if (pkt->owner->nat)
ast_verbose("Retransmitting #%d (NAT):\n%s\n to %s:%d\n", pkt->retrans, pkt->data, inet_ntoa(pkt->owner->recv.sin_addr), ntohs(pkt->owner->recv.sin_port));
else
ast_verbose("Retransmitting #%d (no NAT):\n%s\n to %s:%d\n", pkt->retrans, pkt->data, inet_ntoa(pkt->owner->sa.sin_addr), ntohs(pkt->owner->sa.sin_port));
}
__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
res = 1;
} else {
ast_log(LOG_WARNING, "Maximum retries exceeded on call %s for seqno %d (%s)\n", pkt->owner->callid, pkt->seqno, pkt->resp ? "Response" : "Request");
pkt->retransid = -1;
if (pkt->owner->owner) {
/* XXX Potential deadlocK?? XXX */
ast_queue_hangup(pkt->owner->owner, 1);
} else {
/* If no owner, destroy now */
ast_pthread_mutex_unlock(&pkt->owner->lock);
/* Don't bother retransmitting. It's about to be killed anyway */
if (pkt->owner->owner) {
/* XXX Potential deadlocK?? XXX */
ast_queue_hangup(pkt->owner->owner, 1);
} else {
/* If no owner, destroy now */
ast_pthread_mutex_unlock(&pkt->owner->lock);
sip_destroy(pkt->owner);
pkt=NULL;
}
if (pkt)
ast_pthread_mutex_unlock(&pkt->owner->lock);
return res;
}
static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len)
{
struct sip_pkt *pkt;
pkt = malloc(sizeof(struct sip_pkt) + len);
if (!pkt)
return -1;
memset(pkt, 0, sizeof(struct sip_pkt));
memcpy(pkt->data, data, len);
pkt->packetlen = len;
pkt->next = p->packets;
pkt->owner = p;
pkt->seqno = seqno;
pkt->resp = resp;
/* Schedule retransmission */
pkt->retransid = ast_sched_add(sched, 1000, retrans_pkt, pkt);
pkt->next = p->packets;
p->packets = pkt;
__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
if (!strncasecmp(pkt->data, "INVITE", 6)) {
/* Note this is a pending invite */
p->pendinginvite = seqno;
}
return 0;
}
static int __sip_autodestruct(void *data)
{
struct sip_pvt *p = data;
p->autokillid = -1;
ast_log(LOG_DEBUG, "Auto destroying call '%s'\n", p->callid);
if (p->owner) {
ast_log(LOG_WARNING, "Autodestruct on call '%s' with owner in place\n", p->callid);
ast_queue_hangup(p->owner, 0);
} else {
sip_destroy(p);
}
return 0;
}
static int sip_scheddestroy(struct sip_pvt *p, int ms)
{
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, p);
return 0;
}
static int sip_cancel_destroy(struct sip_pvt *p)
{
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
p->autokillid = -1;
return 0;
}
static int __sip_ack(struct sip_pvt *p, int seqno, int resp)
{
struct sip_pkt *cur, *prev = NULL;
int res = -1;
int resetinvite = 0;
cur = p->packets;
while(cur) {
if ((cur->seqno == seqno) && (cur->resp == resp)) {
if (!resp && (seqno == p->pendinginvite)) {
ast_log(LOG_DEBUG, "Acked pending invite %d\n", p->pendinginvite);
p->pendinginvite = 0;
resetinvite = 1;
}
/* this is our baby */
if (prev)
prev->next = cur->next;
else
p->packets = cur->next;
if (cur->retransid > -1)
ast_sched_del(sched, cur->retransid);
free(cur);
res = 0;
break;
}
prev = cur;
cur = cur->next;
}
ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
return res;
}
static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno)
ast_verbose("%sTransmitting (NAT):\n%s\n to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
ast_verbose("%sTransmitting (no NAT):\n%s\n to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
if (reliable)
res = __sip_reliable_xmit(p, seqno, 1, req->data, req->len);
else
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 reliable, int seqno)
ast_verbose("%sTransmitting:\n%s (NAT) to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
ast_verbose("%sTransmitting:\n%s (no NAT) to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
if (reliable)
res = __sip_reliable_xmit(p, seqno, 0, req->data, req->len);
else
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_sendtext(struct ast_channel *ast, char *text)
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;
static int create_addr(struct sip_pvt *r, char *peer)
{
struct hostent *hp;
struct sip_peer *p;
int found=0;
ast_pthread_mutex_lock(&peerl.lock);
p = peerl.peers;
while(p) {
if (!strcasecmp(p->name, peer)) {
found++;
r->nat = p->nat;
if (r->rtp) {
ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", r->nat);
ast_rtp_setnat(r->rtp, r->nat);
}
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);
strncpy(r->tohost, p->tohost, sizeof(r->tohost)-1);
if (!strlen(r->tohost)) {
if (p->addr.sin_addr.s_addr)
snprintf(r->tohost, sizeof(r->tohost), inet_ntoa(p->addr.sin_addr));
else
snprintf(r->tohost, sizeof(r->tohost), inet_ntoa(p->defaddr.sin_addr));
}
if (strlen(p->fromdomain))
strncpy(r->fromdomain, p->fromdomain, sizeof(r->fromdomain)-1);
Mark Spencer
committed
if (strlen(p->fromuser))
strncpy(r->fromuser, p->fromuser, sizeof(r->fromuser)-1);
r->insecure = p->insecure;
r->canreinvite = p->canreinvite;
r->maxtime = p->maxms;
r->callgroup = p->callgroup;
r->pickupgroup = p->pickupgroup;
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)))) {
r->sa.sin_addr = p->addr.sin_addr;
r->sa.sin_port = p->addr.sin_port;
r->sa.sin_addr = p->defaddr.sin_addr;
r->sa.sin_port = p->defaddr.sin_port;
memcpy(&r->recv, &r->sa, sizeof(r->recv));
break;
}
}
p = p->next;
}
ast_pthread_mutex_unlock(&peerl.lock);
if (!p && !found) {
if ((port=strchr(peer, ':'))) {
*port='\0';
port++;
}
strncpy(r->tohost, peer, sizeof(r->tohost) - 1);
memcpy(&r->sa.sin_addr, hp->h_addr, sizeof(r->sa.sin_addr));
if (port) {
r->sa.sin_port = htons(atoi(port));
} else {
r->sa.sin_port = htons(DEFAULT_SIP_PORT);
}
memcpy(&r->recv, &r->sa, sizeof(r->recv));
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 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;
}
static void sip_prefs_free(void)
{
struct sip_codec_pref *cur, *next;
cur = prefs;
while(cur) {
next = cur->next;
free(cur);
cur = next;
}
prefs = NULL;
}
static void sip_pref_remove(int format)
{
struct sip_codec_pref *cur, *prev=NULL;
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
cur = prefs;
while(cur) {
if (cur->codec == format) {
if (prev)
prev->next = cur->next;
else
prefs = cur->next;
free(cur);
return;
}
prev = cur;
cur = cur->next;
}
}
static int sip_pref_append(int format)
{
struct sip_codec_pref *cur, *tmp;
sip_pref_remove(format);
tmp = (struct sip_codec_pref *)malloc(sizeof(struct sip_codec_pref));
if (!tmp)
return -1;
memset(tmp, 0, sizeof(struct sip_codec_pref));
tmp->codec = format;
if (prefs) {
cur = prefs;
while(cur->next)
cur = cur->next;
cur->next = tmp;
} else
prefs = tmp;
return 0;
}
static int sip_codec_choose(int formats)
{
struct sip_codec_pref *cur;
cur = prefs;
while(cur) {
if (formats & cur->codec)
return cur->codec;
cur = cur->next;
}
return ast_best_codec(formats);
}
static int sip_call(struct ast_channel *ast, char *dest, int timeout)
{
int res;
struct sip_pvt *p;
char *vxml_url = NULL;
struct varshead *headp;
struct ast_var_t *current;
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;
}
/* 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;
}
}
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);
}
static void __sip_destroy(struct sip_pvt *p, int lockowner)
if (sipdebug)
ast_log(LOG_DEBUG, "Destorying call '%s'\n", p->callid);
if (p->stateid > -1)
ast_extension_state_del(p->stateid, NULL);
if (p->initid > -1)
ast_sched_del(sched, p->initid);
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
if (p->route) {
free_old_route(p->route);
p->route = NULL;
}
/* Unlink us from the owner if we have one */
if (p->owner) {
if (lockowner)
ast_pthread_mutex_lock(&p->owner->lock);
ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
p->owner->pvt->pvt = NULL;
if (lockowner)
ast_pthread_mutex_unlock(&p->owner->lock);
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 {
if (p->initid > -1)
ast_sched_del(sched, p->initid);
while((cp = p->packets)) {
p->packets = p->packets->next;
if (cp->retransid > -1)
ast_sched_del(sched, cp->retransid);
free(cp);
}
}
static void sip_destroy(struct sip_pvt *p)
{
ast_pthread_mutex_lock(&iflock);
/* Interface lookup code courtesy Tilghman of DrunkCoder.com. Thanks! */
struct my_ifreq {
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
struct sockaddr_in ifru_addr;
};
struct in_addr *lookup_iface(char *iface) {
int mysock;
int res;
static struct my_ifreq ifreq;
strncpy(ifreq.ifrn_name,iface,sizeof(ifreq.ifrn_name) - 1);
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.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;
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
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) {
if (sipdebug)
ast_verbose("Interface is %s\n",iface);
if (sipdebug)
ast_verbose("IP Address is %s\n",inet_ntoa(*temp));
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;
}
static int transmit_response_reliable(struct sip_pvt *p, char *msg, struct sip_request *req);
static int sip_hangup(struct ast_channel *ast)
{
struct sip_pvt *p = ast->pvt->pvt;
int needcancel = 0;
int needdestroy = 0;
if (option_debug)
ast_log(LOG_DEBUG, "sip_hangup(%s)\n", ast->name);
if (!ast->pvt->pvt) {
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
ast_pthread_mutex_lock(&p->lock);
/* Determine how to disconnect */
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))
if (p->vad) {
ast_dsp_free(p->vad);
}
/* Start the process if it's not already started */
if (!p->alreadygone && strlen(p->initreq.data)) {
if (p->outgoing) {
transmit_request_with_auth(p, "CANCEL", p->ocseq, 1);
/* Actually don't destroy us yet, wait for the 487 on our original
INVITE, but do set an autodestruct just in case. */
sip_scheddestroy(p, 15000);
} else
transmit_response_reliable(p, "403 Forbidden", &p->initreq);
if (!p->pendinginvite) {
/* Send a hangup */
transmit_request_with_auth(p, "BYE", 0, 1);
} else {
/* Note we will need a BYE when this all settles out
but we can't send one while we have "INVITE" outstanding. */
p->pendingbye = 1;
}
p->needdestroy = needdestroy;
return 0;
}
static int sip_answer(struct ast_channel *ast)
{
codec=pbx_builtin_getvar_helper(p->owner,"SIP_CODEC");
if (codec) {
ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC) variable\n",codec);
fmt=ast_getformatbyname(codec);