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 <asterisk/srv.h>
Martin Pycko
committed
#include <asterisk/causes.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#ifdef SIP_MYSQL_FRIENDS
#define MYSQL_FRIENDS
#ifndef IPTOS_MINCOST
#define IPTOS_MINCOST 0x02
#endif
#define DEFAULT_DEFAULT_EXPIRY 120
#define DEFAULT_MAX_EXPIRY 3600
Martin Pycko
committed
#define CALLERID_UNKNOWN "Unknown"
#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_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 */
#ifdef MYSQL_FRIENDS
static ast_mutex_t mysqllock = AST_MUTEX_INITIALIZER;
static MYSQL *mysql;
static char mydbuser[80];
static char mydbpass[80];
static char mydbhost[80];
static char mydbname[80];
#endif
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 */
#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER"
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 char notifymime[AST_MAX_EXTENSION] = "application/simple-message-summary";
static int srvlookup = 0;
static int pedanticsipchecking = 0;
static int autocreatepeer = 0;
static ast_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
/* Protect the interface list (of sip_pvt's) */
static ast_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 ast_mutex_t netlock = AST_MUTEX_INITIALIZER;
static ast_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 = AST_PTHREADT_NULL;
/* Codecs that we support by default: */
static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263;
static int videosupport = 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
#define DEC_IN_USE 0
#define INC_IN_USE 1
#define DEC_OUT_USE 2
#define INC_OUT_USE 3
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];
};
ast_mutex_t lock; /* Channel private lock */
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 jointcapability; /* Supported capability at both ends */
int prefcodec; /* Preferred codec (outbound only) */
int authtries; /* Times we've tried to authenticate */
int ringing; /* Have sent 180 ringing */
int progress; /* Have sent 183 message progress */
int nat; /* Whether to try to support NAT */
int sessionid; /* SDP Session ID */
int sessionversion; /* SDP Session Version */
struct sockaddr_in redirip; /* Where our RTP should be going if not to us */
struct sockaddr_in vredirip; /* Where our Video RTP should be going if not to us */
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 username[256];
char peername[256];
char uri[256]; /* Original requested URI */
char peersecret[256];
Martin Pycko
committed
int restrictcid; /* hide presentation from remote user */
char accountcode[20]; /* Account code */
char our_contact[256]; /* Our contact header */
char realm[256]; /* Authorization realm */
char nonce[256]; /* Authorization nonce */
char domain[256]; /* Authorization nonce */
char lastmsg[256]; /* Last Message sent/received */
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 ast_rtp *vrtp; /* Video RTP session */
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];
unsigned int callgroup;
unsigned int pickupgroup;
int hascallerid;
int amaflags;
int insecure;
int dtmfmode;
int inUse;
int incominglimit;
Martin Pycko
committed
int restrictcid;
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;
static ast_mutex_t sip_reload_lock = AST_MUTEX_INITIALIZER;
static int sip_reloading = 0;
#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 {
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 md5secret[80];
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;
};
static struct ast_user_list {
struct sip_user *users;
ast_mutex_t lock;
} userl = { NULL, AST_MUTEX_INITIALIZER };
static struct ast_peer_list {
struct sip_peer *peers;
ast_mutex_t lock;
} peerl = { NULL, AST_MUTEX_INITIALIZER };
static struct ast_register_list {
struct sip_registry *registrations;
ast_mutex_t lock;
} regl = { NULL, AST_MUTEX_INITIALIZER };
#define REINVITE_INVITE 1
#define REINVITE_UPDATE 2
static int __sip_do_register(struct sip_registry *r);
static int globalcanreinvite = REINVITE_INVITE;
static struct sockaddr_in localnet;
static struct sockaddr_in localmask;
static struct sockaddr_in externip;
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 *authheader, char *vxml_url,char *distinctive_ring, int init);
static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp);
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 transmit_refer(struct sip_pvt *p, char *dest);
static struct sip_peer *temp_peer(char *name);
static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init);
// static char *getsipuri(char *header);
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 find_user(struct sip_pvt *fup, int event);
static int sip_do_reload(void);
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 ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us)
{
/*
check to see if them is contained in our localnet/mask,
if not, use our externip for us, otherwise use the
real internal address in bindaddr
*/
if (localnet.sin_addr.s_addr && externip.sin_addr.s_addr &&
((htonl(them->s_addr) & htonl(localnet.sin_addr.s_addr)) != htonl(localnet.sin_addr.s_addr)))
memcpy(us, &externip.sin_addr, sizeof(struct in_addr));
else if (bindaddr.sin_addr.s_addr)
memcpy(us, &bindaddr.sin_addr, sizeof(struct in_addr));
else
return ast_ouraddrfor(them, us);
return 0;
}
static int retrans_pkt(void *data)
{
struct sip_pkt *pkt=data;
int res = 0;
ast_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;
ast_log(LOG_WARNING, "Maximum retries exceeded on call %s for seqno %d (%s)\n", pkt->owner->callid, pkt->seqno, pkt->resp ? "Response" : "Request");
while(pkt->owner->owner && ast_mutex_trylock(&pkt->owner->owner->lock)) {
ast_mutex_unlock(&pkt->owner->lock);
usleep(1);
ast_mutex_lock(&pkt->owner->lock);
}
if (pkt->owner->owner) {
/* XXX Potential deadlocK?? XXX */
ast_queue_hangup(pkt->owner->owner, 0);
ast_mutex_unlock(&pkt->owner->owner->lock);
} else {
/* If no owner, destroy now */
ast_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, DEFAULT_RETRANS, 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");
static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp)
{
struct sip_pkt *cur;
int res = -1;
cur = p->packets;
while(cur) {
if ((cur->seqno == seqno) && (cur->resp == resp)) {
/* this is our baby */
if (cur->retransid > -1)
ast_sched_del(sched, cur->retransid);
cur->retransid = -1;
res = 0;
break;
}
cur = cur->next;
}
ast_log(LOG_DEBUG, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
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;
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 void mysql_update_peer(char *peer, struct sockaddr_in *sin, char *username, int expiry)
{
if (mysql && (strlen(peer) < 128)) {
char query[512];
char *name;
char *uname;
time_t nowtime;
name = alloca(strlen(peer) * 2 + 1);
uname = alloca(strlen(username) * 2 + 1);
time(&nowtime);
mysql_real_escape_string(mysql, name, peer, strlen(peer));
mysql_real_escape_string(mysql, uname, username, strlen(username));
snprintf(query, sizeof(query), "UPDATE sipfriends SET ipaddr=\"%s\", port=\"%d\", regseconds=\"%ld\", username=\"%s\" WHERE name=\"%s\"",
inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), nowtime + expiry, uname, name);
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
743
744
745
746
747
748
749
750
751
752
ast_mutex_lock(&mysqllock);
if (mysql_real_query(mysql, query, strlen(query)))
ast_log(LOG_WARNING, "Unable to update database\n");
ast_mutex_unlock(&mysqllock);
}
}
static struct sip_peer *mysql_peer(char *peer, struct sockaddr_in *sin)
{
struct sip_peer *p;
int success = 0;
p = malloc(sizeof(struct sip_peer));
memset(p, 0, sizeof(struct sip_peer));
if (mysql && (!peer || (strlen(peer) < 128))) {
char query[512];
char *name = NULL;
int numfields, x;
int port;
time_t regseconds, nowtime;
MYSQL_RES *result;
MYSQL_FIELD *fields;
MYSQL_ROW rowval;
if (peer) {
name = alloca(strlen(peer) * 2 + 1);
mysql_real_escape_string(mysql, name, peer, strlen(peer));
}
if (sin)
snprintf(query, sizeof(query), "SELECT * FROM sipfriends WHERE ipaddr=\"%s\" AND port=\"%d\"", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
else
snprintf(query, sizeof(query), "SELECT * FROM sipfriends WHERE name=\"%s\"", name);
ast_mutex_lock(&mysqllock);
mysql_query(mysql, query);
if ((result = mysql_store_result(mysql))) {
if ((rowval = mysql_fetch_row(result))) {
numfields = mysql_num_fields(result);
fields = mysql_fetch_fields(result);
success = 1;
for (x=0;x<numfields;x++) {
if (rowval[x]) {
if (!strcasecmp(fields[x].name, "secret")) {
strncpy(p->secret, rowval[x], sizeof(p->secret));
} else if (!strcasecmp(fields[x].name, "name")) {
strncpy(p->name, rowval[x], sizeof(p->name) - 1);
} else if (!strcasecmp(fields[x].name, "context")) {
strncpy(p->context, rowval[x], sizeof(p->context) - 1);
} else if (!strcasecmp(fields[x].name, "username")) {
strncpy(p->username, rowval[x], sizeof(p->username) - 1);
} else if (!strcasecmp(fields[x].name, "ipaddr")) {
inet_aton(rowval[x], &p->addr.sin_addr);
} else if (!strcasecmp(fields[x].name, "port")) {
if (sscanf(rowval[x], "%i", &port) != 1)
port = 0;
p->addr.sin_port = htons(port);
} else if (!strcasecmp(fields[x].name, "regseconds")) {
if (sscanf(rowval[x], "%li", ®seconds) != 1)
regseconds = 0;
}
}
}
time(&nowtime);
memset(&p->addr, 0, sizeof(p->addr));
}
mysql_free_result(result);
result = NULL;
}
ast_mutex_unlock(&mysqllock);
}
if (!success) {
free(p);
p = NULL;
} else {
p->dynamic = 1;
p->capability = capability;
p->nat = globalnat;
p->dtmfmode = globaldtmfmode;
p->insecure = 1;
p->expire = -1;
p->temponly = 1;
}
return p;
}
#endif /* MYSQL_FRIENDS */
static int create_addr(struct sip_pvt *r, char *peer)
{
struct hostent *hp;
struct sip_peer *p;
int found=0;
int portno;
char host[256], *hostn;
ast_mutex_lock(&peerl.lock);
if (!strcasecmp(p->name, peer))
break;
p = p->next;
}
#ifdef MYSQL_FRIENDS
if (!p)
p = mysql_peer(peer, NULL);
#endif
if (p) {
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);
}
if (r->vrtp) {
ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", r->nat);
ast_rtp_setnat(r->vrtp, r->nat);
}
strncpy(r->peername, p->username, sizeof(r->peername)-1);
strncpy(r->peersecret, p->secret, sizeof(r->peersecret)-1);
strncpy(r->peermd5secret, p->md5secret, sizeof(r->peermd5secret)-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));
} else {
if (p->temponly)
free(p);
ast_mutex_unlock(&peerl.lock);
if ((port=strchr(peer, ':'))) {
*port='\0';
port++;
}
hostn = peer;
if (port)
portno = atoi(port);
else
portno = DEFAULT_SIP_PORT;
if (srvlookup) {
char service[256];
int tportno;
int ret;
snprintf(service, sizeof(service), "_sip._udp.%s", peer);
ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service);
if (ret > 0) {
hostn = host;
portno = tportno;
}
}
hp = gethostbyname(hostn);
strncpy(r->tohost, peer, sizeof(r->tohost) - 1);
memcpy(&r->sa.sin_addr, hp->h_addr, sizeof(r->sa.sin_addr));
r->sa.sin_port = htons(portno);
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;
static int auto_congest(void *nothing)
{
struct sip_pvt *p = nothing;
ast_mutex_lock(&p->lock);
if (!ast_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_mutex_unlock(&p->owner->lock);
ast_mutex_unlock(&p->lock);
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;
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
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;
formats &= ((AST_FORMAT_MAX_AUDIO << 1) - 1);
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 *distinctive_ring = NULL;
struct varshead *headp;
struct ast_var_t *current;