diff options
author | (no author) <(no author)@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-06-29 21:01:20 +0000 |
---|---|---|
committer | (no author) <(no author)@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-06-29 21:01:20 +0000 |
commit | 2e18788335055aabf777ff1f36592946c750014d (patch) | |
tree | 6bd35f88d11c0d434f75eb1129e38b2e83b144c4 /channels/chan_sip.c | |
parent | 27a9c96742202c8188b53a0173649de479256b69 (diff) |
This commit was manufactured by cvs2svn to create tag 'v1-0-9'.
git-svn-id: http://svn.digium.com/svn/asterisk/tags/v1-0-9@6013 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_sip.c')
-rwxr-xr-x | channels/chan_sip.c | 1100 |
1 files changed, 661 insertions, 439 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index be81665c8..b746d6309 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -129,7 +129,9 @@ 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 */ +/* From RFC2543, this should be 1500, so it's up to the ability of + remote devices as to whether it will be accepted when more than that*/ +#define SIP_MAX_PACKET 4096 #define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER" @@ -168,6 +170,7 @@ static int global_ospauth = 0; static int usecnt =0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); + /* Protect the interface list (of sip_pvt's) */ AST_MUTEX_DEFINE_STATIC(iflock); @@ -187,7 +190,6 @@ static int restart_monitor(void); static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263; static int noncodeccapability = AST_RTP_DTMF; -static char ourhost[256]; static struct in_addr __ourip; static int ourport; @@ -203,7 +205,7 @@ static int recordhistory = 0; static int global_promiscredir; static char global_musicclass[MAX_LANGUAGE] = ""; /* Global music on hold class */ -static char global_realm[AST_MAX_EXTENSION] = "asterisk"; /* Default realm */ +static char global_realm[MAXHOSTNAMELEN] = "asterisk"; /* Default realm */ static char regcontext[AST_MAX_EXTENSION] = ""; /* Expire slowly */ @@ -222,10 +224,8 @@ static struct io_context *io; #define DEC_OUT_USE 2 #define INC_OUT_USE 3 -static struct sip_codec_pref { - int codec; - struct sip_codec_pref *next; -} *prefs; +static struct ast_codec_pref prefs; + /* sip_request: The data grabbed from the UDP socket */ struct sip_request { @@ -256,6 +256,7 @@ static struct sip_pvt { ast_mutex_t lock; /* Channel private lock */ char callid[80]; /* Global CallID */ char randdata[80]; /* Random data */ + struct ast_codec_pref prefs; /* codec prefs */ unsigned int ocseq; /* Current outgoing seqno */ unsigned int icseq; /* Current incoming seqno */ unsigned int callgroup; /* Call group */ @@ -298,9 +299,10 @@ static struct sip_pvt { char from[256]; /* The From: header */ char useragent[256]; /* User agent in SIP request */ char context[AST_MAX_EXTENSION]; /* Context for this call */ - char fromdomain[AST_MAX_EXTENSION]; /* Domain to show in the from field */ - 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 fromdomain[MAXHOSTNAMELEN]; /* Domain to show in the from field */ + char fromuser[AST_MAX_EXTENSION]; /* User to show in the user field */ + char fromname[AST_MAX_EXTENSION]; /* Name to show in the user field */ + char tohost[MAXHOSTNAMELEN]; /* Host we should put in the "to" field */ char language[MAX_LANGUAGE]; /* Default language for this call */ char musicclass[MAX_LANGUAGE]; /* Music on Hold class */ char rdnis[256]; /* Referring DNIS */ @@ -314,14 +316,14 @@ static struct sip_pvt { char callerid[256]; /* Caller*ID */ int restrictcid; /* hide presentation from remote user */ char via[256]; - char fullcontact[128]; /* Extra parameters to go in the "To" header */ + char fullcontact[128]; /* Extra parameters to go in the "To" header */ char accountcode[20]; /* Account code */ char our_contact[256]; /* Our contact header */ - char realm[256]; /* Authorization realm */ + char realm[MAXHOSTNAMELEN]; /* Authorization realm */ char nonce[256]; /* Authorization nonce */ char opaque[256]; /* Opaque nonsense */ char qop[80]; /* Quality of Protection, since SIP wasn't complicated enough yet. */ - char domain[256]; /* Authorization nonce */ + char domain[MAXHOSTNAMELEN]; /* Authorization domain */ char lastmsg[256]; /* Last Message sent/received */ int amaflags; /* AMA Flags */ int pendinginvite; /* Any pending invite */ @@ -383,7 +385,7 @@ struct sip_user { char name[80]; char secret[80]; char md5secret[80]; - char context[80]; + char context[AST_MAX_EXTENSION]; char callerid[80]; char accountcode[20]; char language[MAX_LANGUAGE]; @@ -391,6 +393,7 @@ struct sip_user { char useragent[256]; /* User agent in SIP request */ unsigned int callgroup; unsigned int pickupgroup; + struct ast_codec_pref prefs; /* codec prefs */ int nat; int hascallerid; int amaflags; @@ -421,17 +424,18 @@ struct sip_peer { char name[80]; char secret[80]; char md5secret[80]; - char context[80]; /* JK02: peers need context too to allow parking etc */ + char context[AST_MAX_EXTENSION]; /* JK02: peers need context too to allow parking etc */ char username[80]; - char tohost[80]; + char tohost[MAXHOSTNAMELEN]; char regexten[AST_MAX_EXTENSION]; /* Extension to register (if regcontext is used) */ char fromuser[80]; - char fromdomain[80]; - char fullcontact[128]; + char fromdomain[MAXHOSTNAMELEN]; + char fullcontact[256]; char mailbox[AST_MAX_EXTENSION]; char language[MAX_LANGUAGE]; char musicclass[MAX_LANGUAGE]; /* Music on Hold class */ char useragent[256]; /* User agent in SIP request */ + struct ast_codec_pref prefs; /* codec prefs */ int lastmsgssent; time_t lastmsgcheck; int dynamic; @@ -492,10 +496,10 @@ struct sip_registry { int portno; /* Optional port override */ char username[80]; /* Who we are registering as */ char authuser[80]; /* Who we *authenticate* as */ - char hostname[80]; + char hostname[MAXHOSTNAMELEN]; char secret[80]; /* Password or key name in []'s */ char md5secret[80]; - char contact[80]; /* Contact extension */ + char contact[256]; /* Contact extension */ char random[80]; int expire; /* Sched ID of expiration */ int timeout; /* sched id of sip_reg_timeout */ @@ -506,6 +510,15 @@ struct sip_registry { 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 */ + + /* Saved headers */ + char realm[MAXHOSTNAMELEN]; /* Authorization realm */ + char nonce[256]; /* Authorization nonce */ + char domain[MAXHOSTNAMELEN]; /* Authorization domain */ + char opaque[256]; /* Opaque nonsense */ + char qop[80]; /* Quality of Protection. */ + + char lastmsg[256]; /* Last Message sent/received */ struct sip_registry *next; }; @@ -590,6 +603,7 @@ static int __sip_xmit(struct sip_pvt *p, char *data, int len) { int res; char iabuf[INET_ADDRSTRLEN]; +// ast_log(LOG_WARNING, "__sip_xmit from '%s' to '%s'\n", "", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr)); if (p->nat & SIP_NAT_ROUTE) res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->recv, sizeof(struct sockaddr_in)); else @@ -688,6 +702,7 @@ static int retrans_pkt(void *data) ast_mutex_lock(&pkt->owner->lock); } if (pkt->owner->owner) { + pkt->owner->alreadygone=1; ast_queue_hangup(pkt->owner->owner); ast_mutex_unlock(&pkt->owner->owner->lock); } else { @@ -827,6 +842,15 @@ static int __sip_ack(struct sip_pvt *p, int seqno, int resp, const char *msg) return res; } +/* Pretend to ack all packets */ +static int __sip_pretend_ack(struct sip_pvt *p) +{ + while(p->packets) { + __sip_ack(p, p->packets->seqno, (p->packets->flags & FLAG_RESPONSE), p->packets->data); + } + return 0; +} + /*--- __sip_semi_ack: Acks receipt of packet, keep it around (used for provisional responses) ---*/ static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, const char *msg) { @@ -1049,7 +1073,7 @@ static struct sip_user *mysql_user(char *user) if (sscanf(rowval[x], "%li", ®seconds) != 1) regseconds = 0; } else if (!strcasecmp(fields[x].name, "restrictcid")) { - u->restrictcid = 1; + u->restrictcid = *(rowval[x])-'0'; } else if (!strcasecmp(fields[x].name, "callerid")) { strncpy(u->callerid, rowval[x], sizeof(u->callerid) - 1); u->hascallerid=1; @@ -1265,7 +1289,7 @@ static int create_addr(struct sip_pvt *r, char *opeer) char *port; char *callhost; int portno; - char host[256], *hostn; + char host[MAXHOSTNAMELEN], *hostn; char peer[256]=""; strncpy(peer, opeer, sizeof(peer) - 1); @@ -1326,8 +1350,10 @@ static int create_addr(struct sip_pvt *r, char *opeer) } r->promiscredir = p->promiscredir; strncpy(r->context, p->context,sizeof(r->context)-1); + r->rtptimeout = p->rtptimeout; + r->rtpholdtimeout = p->rtpholdtimeout; if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && - (!p->maxms || ((p->lastms > 0) && (p->lastms <= p->maxms)))) { + (!p->maxms || ((p->lastms >= 0) && (p->lastms <= p->maxms)))) { if (p->addr.sin_addr.s_addr) { r->sa.sin_addr = p->addr.sin_addr; r->sa.sin_port = p->addr.sin_port; @@ -1354,7 +1380,7 @@ static int create_addr(struct sip_pvt *r, char *opeer) else portno = DEFAULT_SIP_PORT; if (srvlookup) { - char service[256]; + char service[MAXHOSTNAMELEN]; int tportno; int ret; snprintf(service, sizeof(service), "_sip._udp.%s", peer); @@ -1405,71 +1431,8 @@ static int auto_congest(void *nothing) return 0; } -/*--- sip_prefs_free: Free codec list in preference structure ---*/ -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; -} - -/*--- sip_pref_remove: Remove codec from pref list ---*/ -static void sip_pref_remove(int format) -{ - struct sip_codec_pref *cur, *prev=NULL; - 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; - } -} -/*--- sip_pref_append: Append codec to list ---*/ -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; -} -/*--- sip_codec_choose: Pick a codec ---*/ -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); -} /*--- sip_call: Initiate SIP call from PBX ---*/ /* used from the dial() application */ @@ -1682,6 +1645,10 @@ static int update_user_counter(struct sip_pvt *fup, int event) ast_log(LOG_ERROR, "update_user_counter(%s,%d) called with no event!\n",u->name,event); } ast_mutex_unlock(&userl.lock); +#ifdef MYSQL_USERS + if (u && u->temponly) + free(u); +#endif return 0; } @@ -1842,7 +1809,11 @@ static int sip_answer(struct ast_channel *ast) fmt=ast_getformatbyname(codec); if (fmt) { ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC) variable\n",codec); - p->jointcapability=fmt; + if (p->jointcapability & fmt) { + p->jointcapability &= fmt; + p->capability &= fmt; + } else + ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because it is not shared by both ends.\n"); } else ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized/not configured codec (check allow/disallow in sip.conf): %s\n",codec); } @@ -2002,6 +1973,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title) { struct ast_channel *tmp; int fmt; + ast_mutex_unlock(&i->lock); /* Don't hold a sip pvt lock while we allocate a channel */ tmp = ast_channel_alloc(1); @@ -2009,12 +1981,14 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title) if (tmp) { /* Select our native format based on codec preference until we receive something from another device to the contrary. */ + ast_mutex_lock(&i->lock); if (i->jointcapability) - tmp->nativeformats = sip_codec_choose(i->jointcapability); + tmp->nativeformats = ast_codec_choose(&i->prefs, i->jointcapability, 1); else if (i->capability) - tmp->nativeformats = sip_codec_choose(i->capability); + tmp->nativeformats = ast_codec_choose(&i->prefs, i->capability, 1); else - tmp->nativeformats = sip_codec_choose(global_capability); + tmp->nativeformats = ast_codec_choose(&i->prefs, global_capability, 1); + ast_mutex_unlock(&i->lock); fmt = ast_best_codec(tmp->nativeformats); if (title) snprintf(tmp->name, sizeof(tmp->name), "SIP/%s-%04x", title, rand() & 0xffff); @@ -2122,6 +2096,7 @@ static struct cfalias { { "To", "t" }, { "Supported", "k" }, { "Refer-To", "r" }, + { "Referred-By", "b" }, { "Allow-Events", "u" }, { "Event", "o" }, { "Via", "v" }, @@ -2259,7 +2234,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p } if ((p->dtmfmode & SIP_DTMF_INBAND) && p->vad) { f = ast_dsp_process(p->owner,p->vad,f); - if (f && (f->frametype == AST_FRAME_DTMF)) + if (f && (f->frametype == AST_FRAME_DTMF)) ast_log(LOG_DEBUG, "Detected DTMF '%c'\n", f->subclass); } } @@ -2311,9 +2286,11 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg /* Keep track of stuff */ memset(p, 0, sizeof(struct sip_pvt)); ast_mutex_init(&p->lock); + p->initid = -1; p->autokillid = -1; p->stateid = -1; + p->prefs = prefs; #ifdef OSP_SUPPORT p->osphandle = -1; #endif @@ -2418,7 +2395,7 @@ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *si strncpy(tmp, get_header(req, "To"), sizeof(tmp) - 1); else strncpy(tmp, get_header(req, "From"), sizeof(tmp) - 1); - tag = strstr(tmp, "tag="); + tag = ast_strcasestr(tmp, "tag="); if (tag) { tag += 4; c = strchr(tag, ';'); @@ -2540,33 +2517,32 @@ static int lws2sws(char *msgbuf, int len) } /* Check for end-of-line */ if (msgbuf[h] == '\n') { - /* Check for end-of-message */ + /* Check for end-of-message */ if (h + 1 == len) - break; - /* Check for a continuation line */ - if (msgbuf[h + 1] == ' ') { - /* Merge continuation line */ - h++; + break; + /* Check for a continuation line */ + if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') { + /* Merge continuation line */ + h++; + continue; + } + /* Propagate LF and start new line */ + msgbuf[t++] = msgbuf[h++]; + lws = 0; continue; } - /* Propagate LF and start new line */ - msgbuf[t++] = msgbuf[h++]; - lws = 0; - continue; - } - - if (msgbuf[h] == ' ' || msgbuf[h] == '\t') { - if (lws) { - h++; + if (msgbuf[h] == ' ' || msgbuf[h] == '\t') { + if (lws) { + h++; + continue; + } + msgbuf[t++] = msgbuf[h++]; + lws = 1; continue; } msgbuf[t++] = msgbuf[h++]; - lws = 1; - continue; - } - msgbuf[t++] = msgbuf[h++]; - if (lws) - lws = 0; + if (lws) + lws = 0; } msgbuf[t] = '\0'; return t; @@ -2648,8 +2624,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) char host[258]; char iabuf[INET_ADDRSTRLEN]; int len = -1; - int portno=0; - int vportno=0; + int portno = -1; + int vportno = -1; int peercapability, peernoncodeccapability; int vpeercapability=0, vpeernoncodeccapability=0; struct sockaddr_in sin; @@ -2660,6 +2636,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) int iterator; int sendonly = 0; int x; + int found; int debug=sip_debug_test_pvt(p); /* Update our last rtprx when we receive an SDP, too */ @@ -2689,7 +2666,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) sdpLineNum_iterator_init(&iterator); p->novideo = 1; while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { + found = 0; if ((sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) { + found = 1; portno = x; /* Scan through the RTP payload types specified in a "m=" line: */ ast_rtp_pt_clear(p->rtp); @@ -2711,6 +2690,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_rtp_pt_clear(p->vrtp); /* Must be cleared in case no m=video line exists */ if (p->vrtp && (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) { + found = 1; p->novideo = 0; vportno = x; /* Scan through the RTP payload types specified in a "m=" line: */ @@ -2728,6 +2708,12 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) while(*codecs && (*codecs < 33)) codecs++; } } + if (!found) + ast_log(LOG_WARNING, "Unknown SDP media type in offer %s\n", m); + } + if (portno == -1 && vportno == -1) { + /* No acceptable offer found in SDP */ + return -2; } /* RTP addresses and ports for audio and video */ @@ -2758,21 +2744,22 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) */ sdpLineNum_iterator_init(&iterator); while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { - char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */ - if (!strcasecmp(a, "sendonly")) { - sendonly=1; - continue; - } - if (!strcasecmp(a, "sendrecv")) { - sendonly=0; - } - if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue; - if (debug) - ast_verbose("Found description format %s\n", mimeSubtype); - /* Note: should really look at the 'freq' and '#chans' params too */ - ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype); - if (p->vrtp) - ast_rtp_set_rtpmap_type(p->vrtp, codec, "video", mimeSubtype); + char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */ + if (!strcasecmp(a, "sendonly")) { + sendonly=1; + continue; + } + if (!strcasecmp(a, "sendrecv")) { + sendonly=0; + } + if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) + continue; + if (debug) + ast_verbose("Found description format %s\n", mimeSubtype); + /* Note: should really look at the 'freq' and '#chans' params too */ + ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype); + if (p->vrtp) + ast_rtp_set_rtpmap_type(p->vrtp, codec, "video", mimeSubtype); } /* Now gather all of the codecs that were asked for: */ @@ -2786,7 +2773,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) p->noncodeccapability = noncodeccapability & peernoncodeccapability; if (debug) { - const unsigned slen=80; + /* shame on whoever coded this.... */ + const unsigned slen=512; char s1[slen], s2[slen], s3[slen], s4[slen]; ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n", @@ -2801,17 +2789,17 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_getformatname_multiple(s3, slen, p->noncodeccapability)); } if (!p->jointcapability) { - ast_log(LOG_WARNING, "No compatible codecs!\n"); + ast_log(LOG_NOTICE, "No compatible codecs!\n"); return -1; } if (p->owner) { if (!(p->owner->nativeformats & p->jointcapability)) { - const unsigned slen=80; + const unsigned slen=512; char s1[slen], s2[slen]; ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %s and not %s\n", ast_getformatname_multiple(s1, slen, p->jointcapability), ast_getformatname_multiple(s2, slen, p->owner->nativeformats)); - p->owner->nativeformats = sip_codec_choose(p->jointcapability); + p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1); ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } @@ -2821,6 +2809,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_moh_stop(p->owner->bridge); } else { ast_moh_start(p->owner->bridge, NULL); + if (sendonly) + ast_rtp_stop(p->rtp); } } } @@ -2831,54 +2821,52 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) /*--- add_header: Add header to SIP message */ static int add_header(struct sip_request *req, char *var, char *value) { - if (req->len >= sizeof(req->data) - 4) { - ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value); + if (req->headers == SIP_MAX_HEADERS) { + ast_log(LOG_WARNING, "Out of SIP header space\n"); return -1; } if (req->lines) { ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n"); return -1; } + if (req->len >= sizeof(req->data) - 4) { + ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value); + return -1; + } req->header[req->headers] = req->data + req->len; snprintf(req->header[req->headers], sizeof(req->data) - req->len - 4, "%s: %s\r\n", var, value); req->len += strlen(req->header[req->headers]); - if (req->headers < SIP_MAX_HEADERS) - req->headers++; - else { - ast_log(LOG_WARNING, "Out of header space\n"); - return -1; - } + req->headers++; return 0; } /*--- add_blank_header: Add blank header to SIP message */ static int add_blank_header(struct sip_request *req) { - if (req->len >= sizeof(req->data) - 4) { - ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); + if (req->headers == SIP_MAX_HEADERS) { + ast_log(LOG_WARNING, "Out of SIP header space\n"); return -1; } if (req->lines) { ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n"); return -1; } + if (req->len >= sizeof(req->data) - 4) { + ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); + return -1; + } req->header[req->headers] = req->data + req->len; snprintf(req->header[req->headers], sizeof(req->data) - req->len, "\r\n"); req->len += strlen(req->header[req->headers]); - if (req->headers < SIP_MAX_HEADERS) - req->headers++; - else { - ast_log(LOG_WARNING, "Out of header space\n"); - return -1; - } + req->headers++; return 0; } /*--- add_line: Add content (not header) to SIP message */ static int add_line(struct sip_request *req, char *line) { - if (req->len >= sizeof(req->data) - 4) { - ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); + if (req->lines == SIP_MAX_LINES) { + ast_log(LOG_WARNING, "Out of line space\n"); return -1; } if (!req->lines) { @@ -2886,15 +2874,14 @@ static int add_line(struct sip_request *req, char *line) snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n"); req->len += strlen(req->data + req->len); } + if (req->len >= sizeof(req->data) - 4) { + ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); + return -1; + } req->line[req->lines] = req->data + req->len; snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line); req->len += strlen(req->line[req->lines]); - if (req->lines < SIP_MAX_LINES) - req->lines++; - else { - ast_log(LOG_WARNING, "Out of line space\n"); - return -1; - } + req->lines++; return 0; } @@ -3070,10 +3057,7 @@ static int init_resp(struct sip_request *req, char *resp, struct sip_request *or req->header[req->headers] = req->data + req->len; snprintf(req->header[req->headers], sizeof(req->data) - req->len, "SIP/2.0 %s\r\n", resp); req->len += strlen(req->header[req->headers]); - if (req->headers < SIP_MAX_HEADERS) - req->headers++; - else - ast_log(LOG_WARNING, "Out of header space\n"); + req->headers++; return 0; } @@ -3088,10 +3072,7 @@ static int init_req(struct sip_request *req, char *resp, char *recip) req->header[req->headers] = req->data + req->len; snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %s SIP/2.0\r\n", resp, recip); req->len += strlen(req->header[req->headers]); - if (req->headers < SIP_MAX_HEADERS) - req->headers++; - else - ast_log(LOG_WARNING, "Out of header space\n"); + req->headers++; return 0; } @@ -3106,7 +3087,7 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru if (msg[0] == '2') copy_all_header(resp, req, "Record-Route"); copy_header(resp, req, "From"); ot = get_header(req, "To"); - if (!strstr(ot, "tag=")) { + if (!ast_strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) { /* Add the proper tag if we don't have it already. If they have specified their tag, use it. Otherwise, use our own tag */ if (!ast_strlen_zero(p->theirtag) && p->outgoing) @@ -3139,6 +3120,7 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, stru return 0; } +/*--- reqprep: Initialize a SIP request packet ---*/ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int seqno, int newbranch) { struct sip_request *orig = &p->initreq; @@ -3165,12 +3147,14 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int se else /* Some implementations (e.g. Uniden UIP200) can't handle rport being in the message!! */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); } - if (!strcasecmp(msg, "CANCEL") || !strcasecmp(msg, "ACK")) { - /* MUST use original URI */ + if (!strcasecmp(msg, "CANCEL")) { + c = p->initreq.rlPart2; /* Use original URI */ + } else if (!strcasecmp(msg, "ACK")) { c = p->initreq.rlPart2; } else if (!ast_strlen_zero(p->uri)) { c = p->uri; } else { + /* We have no URI, use To: or From: header as URI (depending on direction) */ if (p->outgoing) strncpy(stripped, get_header(orig, "To"), sizeof(stripped) - 1); else @@ -3203,7 +3187,7 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, char *msg, int se /* Add tag *unless* this is a CANCEL, in which case we need to send it exactly as our original request, including tag (or presumably lack thereof) */ - if (!strstr(ot, "tag=") && strcasecmp(msg, "CANCEL")) { + if (!ast_strcasestr(ot, "tag=") && strcasecmp(msg, "CANCEL")) { /* Add the proper tag if we don't have it already. If they have specified their tag, use it. Otherwise, use our own tag */ if (p->outgoing && !ast_strlen_zero(p->theirtag)) @@ -3340,13 +3324,13 @@ static int add_digit(struct sip_request *req, char digit) /*--- add_sdp: Add Session Description Protocol message ---*/ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) { - int len; - int codec; + int len = 0; + int codec = 0; + int pref_codec = 0; int alreadysent = 0; char costr[80]; struct sockaddr_in sin; struct sockaddr_in vsin; - struct sip_codec_pref *cur; char v[256] = ""; char s[256] = ""; char o[256] = ""; @@ -3357,11 +3341,13 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) char a[1024] = ""; char a2[1024] = ""; char iabuf[INET_ADDRSTRLEN]; - int x; - int capability; + int x = 0; + int capability = 0 ; struct sockaddr_in dest; struct sockaddr_in vdest = { 0, }; - int debug=sip_debug_test_pvt(p); + int debug=0; + + debug = sip_debug_test_pvt(p); /* XXX We break with the "recommendation" and send our IP, in order that our peer doesn't have to ast_gethostbyname() us XXX */ @@ -3413,9 +3399,10 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) snprintf(t, sizeof(t), "t=0 0\r\n"); snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port)); snprintf(m2, sizeof(m2), "m=video %d RTP/AVP", ntohs(vdest.sin_port)); + /* Prefer the codec we were requested to use, first, no matter what */ if (capability & p->prefcodec) { if (debug) - ast_verbose("Answering/Requesting with root capability %d\n", p->prefcodec); + ast_verbose("Answering/Requesting with root capability 0x%x (%s)\n", p->prefcodec, ast_getformatname(p->prefcodec)); codec = ast_rtp_lookup_code(p->rtp, 1, p->prefcodec); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); @@ -3432,33 +3419,34 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) alreadysent |= p->prefcodec; } /* Start by sending our preferred codecs */ - cur = prefs; - while(cur) { - if ((capability & cur->codec) && !(alreadysent & cur->codec)) { + for (x = 0 ; x < 32 ; x++) { + if(!(pref_codec = ast_codec_pref_index(&p->prefs,x))) + break; + if ((capability & pref_codec) && !(alreadysent & pref_codec)) { if (debug) - ast_verbose("Answering with preferred capability 0x%x(%s)\n", cur->codec, ast_getformatname(cur->codec)); - codec = ast_rtp_lookup_code(p->rtp, 1, cur->codec); + ast_verbose("Answering with preferred capability 0x%x (%s)\n", pref_codec, ast_getformatname(pref_codec)); + codec = ast_rtp_lookup_code(p->rtp, 1, pref_codec); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); - if (cur->codec <= AST_FORMAT_MAX_AUDIO) { + if (pref_codec <= AST_FORMAT_MAX_AUDIO) { strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, pref_codec)); strncat(a, costr, sizeof(a) - strlen(a) - 1); } else { strncat(m2, costr, sizeof(m2) - strlen(m2) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec)); + snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, pref_codec)); strncat(a2, costr, sizeof(a2) - strlen(a) - 1); } } } - alreadysent |= cur->codec; - cur = cur->next; + alreadysent |= pref_codec; } + /* Now send any other common codecs, and non-codec formats: */ for (x = 1; x <= ((videosupport && p->vrtp) ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) { if ((capability & x) && !(alreadysent & x)) { if (debug) - ast_verbose("Answering with capability 0x%x(%s)\n", x, ast_getformatname(x)); + ast_verbose("Answering with capability 0x%x (%s)\n", x, ast_getformatname(x)); codec = ast_rtp_lookup_code(p->rtp, 1, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); @@ -3477,7 +3465,7 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) for (x = 1; x <= AST_RTP_MAX; x <<= 1) { if (p->noncodeccapability & x) { if (debug) - ast_verbose("Answering with non-codec capability 0x%x(%s)\n", x, ast_getformatname(x)); + ast_verbose("Answering with non-codec capability 0x%x (%s)\n", x, ast_rtp_lookup_mime_subtype(0, x)); codec = ast_rtp_lookup_code(p->rtp, 0, x); if (codec > -1) { snprintf(costr, sizeof(costr), " %d", codec); @@ -3485,7 +3473,7 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p) snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x)); strncat(a, costr, sizeof(a) - strlen(a) - 1); if (x == AST_RTP_DTMF) { - /* Indicate we support DTMF... Not sure about 16, but MSN supports it so dang it, we will too... */ + /* Indicate we support DTMF and FLASH... */ snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n", codec); strncat(a, costr, sizeof(a) - strlen(a) - 1); @@ -3598,7 +3586,7 @@ static int determine_firstline_parts( struct sip_request *req ) { e++; if( !*e ) { return -1; } } - req->rlPart2= e; + req->rlPart2= e; /* URI */ if( ( e= strrchr( req->rlPart2, 'S' ) ) == NULL ) { return -1; } @@ -3612,9 +3600,12 @@ static int determine_firstline_parts( struct sip_request *req ) { return 1; } -/* transmit_reinvite_with_sdp: Transmit reinvite with SDP :-) ---*/ -/* A re-invite is basically a new INVITE with the same CALL-ID and TAG as the - INVITE that opened the SIP dialogue */ +/*--- transmit_reinvite_with_sdp: Transmit reinvite with SDP :-) ---*/ +/* A re-invite is basically a new INVITE with the same CALL-ID and TAG as the + INVITE that opened the SIP dialogue + We reinvite so that the audio stream (RTP) go directly between + the SIP UAs. SIP Signalling stays with * in the path. +*/ static int transmit_reinvite_with_sdp(struct sip_pvt *p) { struct sip_request req; @@ -3688,7 +3679,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, char *cmd, c ast_callerid_parse(cid, &n, &l); if (l) ast_shrink_phone_number(l); - if (!l || !ast_isphonenumber(l)) + if (!l || (!ast_isphonenumber(l) && default_callerid[0])) l = default_callerid; } /* if user want's his callerid restricted */ @@ -3701,15 +3692,25 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, char *cmd, c /* Allow user to be overridden */ if (!ast_strlen_zero(p->fromuser)) l = p->fromuser; + else /* Save for any further attempts */ + strncpy(p->fromuser, l, sizeof(p->fromuser) - 1); + + /* Allow user to be overridden */ + if (!ast_strlen_zero(p->fromname)) + n = p->fromname; + else /* Save for any further attempts */ + strncpy(p->fromname, n, sizeof(p->fromname) - 1); if ((ourport != 5060) && ast_strlen_zero(p->fromdomain)) snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=as%08x", n, l, ast_strlen_zero(p->fromdomain) ? ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip) : p->fromdomain, ourport, p->tag); else snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=as%08x", n, l, ast_strlen_zero(p->fromdomain) ? ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip) : p->fromdomain, p->tag); + /* If we're calling a registred SIP peer, use the fullcontact to dial to the peer */ if (!ast_strlen_zero(p->fullcontact)) { /* If we have full contact, trust it */ strncpy(invite, p->fullcontact, sizeof(invite) - 1); + /* Otherwise, use the username while waiting for registration */ } else if (!ast_strlen_zero(p->username)) { if (ntohs(p->sa.sin_port) != DEFAULT_SIP_PORT) { snprintf(invite, sizeof(invite), "sip:%s@%s:%d",p->username, p->tohost, ntohs(p->sa.sin_port)); @@ -3782,7 +3783,7 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch #endif if (distinctive_ring && !ast_strlen_zero(distinctive_ring)) { - add_header(&req, "Alert-info",distinctive_ring); + add_header(&req, "Alert-Info",distinctive_ring); } add_header(&req, "Allow", ALLOWED_METHODS); if (sdp) { @@ -3846,6 +3847,8 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full) } mto = c; + add_header(&req, "Event", "presence"); + add_header(&req, "Subscription-State", "active"); add_header(&req, "Content-Type", "application/xpidf+xml"); if ((state==AST_EXTENSION_UNAVAILABLE) || (state==AST_EXTENSION_BUSY)) @@ -3872,7 +3875,7 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full) bytes = snprintf(t, maxbytes, "<atom id=\"%s\">\n", p->exten); t += bytes; maxbytes -= bytes; - bytes = snprintf(t, maxbytes, "<address uri=\"%s;user=ip\" priority=\"0,800000\">\n", mto); + bytes = snprintf(t, maxbytes, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto); t += bytes; maxbytes -= bytes; bytes = snprintf(t, maxbytes, "<status status=\"%s\" />\n", !state ? "open" : (state==1) ? "inuse" : "closed"); @@ -3928,8 +3931,8 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs) add_header(&req, "Event", "message-summary"); add_header(&req, "Content-Type", notifymime); - snprintf(tmp, sizeof(tmp), "Messages-Waiting: %s\n", newmsgs ? "yes" : "no"); - snprintf(tmp2, sizeof(tmp2), "Voicemail: %d/%d\n", newmsgs, oldmsgs); + snprintf(tmp, sizeof(tmp), "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); + snprintf(tmp2, sizeof(tmp2), "Voice-Message: %d/%d\r\n", newmsgs, oldmsgs); snprintf(clen, sizeof(clen), "%d", (int)(strlen(tmp) + strlen(tmp2))); add_header(&req, "Content-Length", clen); add_line(&req, tmp); @@ -3957,7 +3960,7 @@ static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq) struct sip_request req; char tmp[256]; char clen[20]; - initreqprep(&req, p, "NOTIFY", NULL); + reqprep(&req, p, "NOTIFY", 0, 1); snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq); add_header(&req, "Event", tmp); add_header(&req, "Subscription-state", "terminated;reason=noresource"); @@ -4009,6 +4012,10 @@ static int sip_reregister(void *data) { /* if we are here, we know that we need to reregister. */ struct sip_registry *r=(struct sip_registry *)data; + + if (sipdebug) + ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname); + ast_mutex_lock(®l.lock); r->expire = -1; __sip_do_register(r); @@ -4027,12 +4034,15 @@ static int __sip_do_register(struct sip_registry *r) /*--- sip_reg_timeout: Registration timeout, register again */ static int sip_reg_timeout(void *data) { + /* if we are here, our registration timed out, so we'll just do it over */ struct sip_registry *r=data; struct sip_pvt *p; int res; + ast_mutex_lock(®l.lock); - ast_log(LOG_NOTICE, "Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); + + ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); if (r->call) { /* Unlink us, destroy old call. Locking is not relevent here because all this happens in the single SIP manager thread. */ @@ -4040,16 +4050,18 @@ static int sip_reg_timeout(void *data) p->registry = NULL; r->call = NULL; p->needdestroy = 1; + /* Pretend to ACK anything just in case */ + __sip_pretend_ack(p); } r->regstate=REG_STATE_UNREGISTERED; - manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nDomain: %s\r\nStatus: %s\r\n", r->hostname, regstate2str(r->regstate)); + manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nUser: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate)); r->timeout = -1; res=transmit_register(r, "REGISTER", NULL, NULL); ast_mutex_unlock(®l.lock); return 0; } -/*--- transmit_register: Transmit register to SIP proxy ---*/ +/*--- transmit_register: Transmit register to SIP proxy or UA ---*/ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char *authheader) { struct sip_request req; @@ -4074,41 +4086,57 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char } else p = r->call; } else { + /* Build callid for registration if we haven't registred before */ if (!r->callid_valid) { build_callid(r->callid, sizeof(r->callid), __ourip, default_fromdomain); r->callid_valid = 1; } + /* Allocate SIP packet for registration */ p=sip_alloc( r->callid, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to allocate registration call\n"); return 0; } + /* Find address to hostname */ if (create_addr(p,r->hostname)) { - sip_destroy(p); + /* we have what we hope is a temporary network error, + * probably DNS. We need to reschedule a registration try */ + sip_destroy(p); + if (r->timeout > -1) { + ast_log(LOG_WARNING, "Still have a registration timeout (create_addr() error), %d\n", r->timeout); + ast_sched_del(sched, r->timeout); + } + r->timeout = ast_sched_add(sched, 20*1000, sip_reg_timeout, r); + return 0; } + /* Copy back Call-ID in case create_addr changed it */ strncpy(r->callid, p->callid, sizeof(r->callid) - 1); if (r->portno) p->sa.sin_port = htons(r->portno); - p->outgoing = 1; - r->call=p; - p->registry=r; - if (!ast_strlen_zero(r->secret)) + p->outgoing = 1; /* Registration is outgoing call */ + r->call=p; /* Save pointer to SIP packet */ + p->registry=r; /* Add pointer to registry in packet */ + if (!ast_strlen_zero(r->secret)) /* Secret (password) */ strncpy(p->peersecret, r->secret, sizeof(p->peersecret)-1); if (!ast_strlen_zero(r->md5secret)) strncpy(p->peermd5secret, r->md5secret, sizeof(p->peermd5secret)-1); - if (!ast_strlen_zero(r->authuser)) { + /* User name in this realm + - if authuser is set, use that, otherwise use username */ + if (!ast_strlen_zero(r->authuser)) { strncpy(p->peername, r->authuser, sizeof(p->peername)-1); strncpy(p->authname, r->authuser, sizeof(p->authname)-1); } else { if (!ast_strlen_zero(r->username)) { strncpy(p->peername, r->username, sizeof(p->peername)-1); strncpy(p->authname, r->username, sizeof(p->authname)-1); + strncpy(p->fromuser, r->username, sizeof(p->fromuser)-1); } } if (!ast_strlen_zero(r->username)) strncpy(p->username, r->username, sizeof(p->username)-1); + /* Save extension in packet */ strncpy(p->exten, r->contact, sizeof(p->exten) - 1); /* @@ -4124,11 +4152,11 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char /* set up a timeout */ if (auth==NULL) { if (r->timeout > -1) { - ast_log(LOG_WARNING, "Still have a timeout, %d\n", r->timeout); + ast_log(LOG_WARNING, "Still have a registration timeout, %d\n", r->timeout); ast_sched_del(sched, r->timeout); } r->timeout = ast_sched_add(sched, 20*1000, sip_reg_timeout, r); - ast_log(LOG_DEBUG, "Scheduled a timeout # %d\n", r->timeout); + ast_log(LOG_DEBUG, "Scheduled a registration timeout # %d\n", r->timeout); } if (strchr(r->username, '@')) { @@ -4153,6 +4181,7 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char memset(&req, 0, sizeof(req)); init_req(&req, cmd, addr); + /* Add to CSEQ */ snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, cmd); p->ocseq = r->ocseq; @@ -4167,8 +4196,27 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char add_header(&req, "Call-ID", p->callid); add_header(&req, "CSeq", tmp); add_header(&req, "User-Agent", default_useragent); - if (auth) + + + if (auth) /* Add auth header */ add_header(&req, authheader, auth); + else if ( !ast_strlen_zero(r->nonce) ) { + char digest[1024]; + + /* We have auth data to reuse, build a digest header! */ + if (sipdebug) + ast_log(LOG_DEBUG, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname); + strncpy(p->realm, r->realm, sizeof(p->realm)-1); + strncpy(p->nonce, r->nonce, sizeof(p->nonce)-1); + strncpy(p->domain, r->domain, sizeof(p->domain)-1); + strncpy(p->opaque, r->opaque, sizeof(p->opaque)-1); + strncpy(p->qop, r->qop, sizeof(p->qop)-1); + + memset(digest,0,sizeof(digest)); + build_reply_digest(p, "REGISTER", digest, sizeof(digest)); + add_header(&req, "Authorization", digest); + + } snprintf(tmp, sizeof(tmp), "%d", default_expiry); add_header(&req, "Expires", tmp); @@ -4263,6 +4311,7 @@ static int transmit_request_with_auth(struct sip_pvt *p, char *msg, int seqno, i if (*p->realm) { char digest[1024]; + memset(digest,0,sizeof(digest)); build_reply_digest(p, msg, digest, sizeof(digest)); add_header(&resp, "Proxy-Authorization", digest); @@ -4347,8 +4396,9 @@ static void reg_source_db(struct sip_peer *p) strncpy(p->username, u, sizeof(p->username) - 1); } - ast_verbose(VERBOSE_PREFIX_3 "SIP Seeding '%s' at %s@%s:%d for %d\n", p->name, - p->username, ast_inet_ntoa(iabuf, sizeof(iabuf), in), atoi(c), atoi(d)); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "SIP Seeding '%s' at %s@%s:%d for %d\n", p->name, + p->username, ast_inet_ntoa(iabuf, sizeof(iabuf), in), atoi(c), atoi(d)); expiry = atoi(d); memset(&p->addr, 0, sizeof(p->addr)); p->addr.sin_family = AF_INET; @@ -4372,6 +4422,7 @@ static void reg_source_db(struct sip_peer *p) } } + /*--- parse_contact: Parse contact header and save registration ---*/ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req) { @@ -4524,7 +4575,7 @@ static void list_route(struct sip_route *route) } } -/*--- build_route: Build route headers ---*/ +/*--- build_route: Build route list from Record-Route header ---*/ static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards) { struct sip_route *thishop, *head, *tail; @@ -4716,8 +4767,9 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata char tmp[256] = ""; char *c; char *z; - char *response =""; + char *ua_hash =""; char *resp_uri =""; + char *nonce = ""; /* Find their response among the mess that we'r sent for comparison */ strncpy(tmp, authtoken, sizeof(tmp) - 1); @@ -4730,12 +4782,12 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata if (!strncasecmp(c, "response=", strlen("response="))) { c+= strlen("response="); if ((*c == '\"')) { - response=++c; + ua_hash=++c; if((c = strchr(c,'\"'))) *c = '\0'; } else { - response=c; + ua_hash=c; if((c = strchr(c,','))) *c = '\0'; } @@ -4752,31 +4804,52 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata *c = '\0'; } + } else if (!strncasecmp(c, "nonce=", strlen("nonce="))) +{ + c+= strlen("nonce="); + if ((*c == '\"')) { + nonce=++c; + if ((c = strchr(c,'\"'))) + *c = '\0'; + } else { + nonce=c; + if ((c = strchr(c,','))) + *c = '\0'; + } } else if ((z = strchr(c,' ')) || (z = strchr(c,','))) c=z; if (c) c++; } - snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret); - if(!ast_strlen_zero(resp_uri)) - snprintf(a2, sizeof(a2), "%s:%s", method, resp_uri); - else - snprintf(a2, sizeof(a2), "%s:%s", method, uri); - if (!ast_strlen_zero(md5secret)) - snprintf(a1_hash, sizeof(a1_hash), "%s", md5secret); - else - md5_hash(a1_hash, a1); - md5_hash(a2_hash, a2); - snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, randdata, a2_hash); - md5_hash(resp_hash, resp); - - /* resp_hash now has the expected response, compare the two */ + /* Check if the nonce the client sends is the same as we are testing authentication with */ + if (strncasecmp(randdata, nonce, randlen)) { + ast_log(LOG_WARNING, "Stale nonce received from '%s'\n", get_header(req, "To")); + snprintf(randdata, randlen, "%08x", rand()); + transmit_response_with_auth(p, response, req, randdata, reliable, respheader); + /* Schedule auto destroy in 15 seconds */ + sip_scheddestroy(p, 15000); + return 1; + } else { + snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret); + if(!ast_strlen_zero(resp_uri)) + snprintf(a2, sizeof(a2), "%s:%s", method, resp_uri); + else + snprintf(a2, sizeof(a2), "%s:%s", method, uri); + if (!ast_strlen_zero(md5secret)) + snprintf(a1_hash, sizeof(a1_hash), "%s", md5secret); + else + md5_hash(a1_hash, a1); + md5_hash(a2_hash, a2); + snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, randdata, a2_hash); + md5_hash(resp_hash, resp); + + /* resp_hash now has the expected response, compare the two */ - if (response && !strncasecmp(response, resp_hash, strlen(resp_hash))) { - /* Auth is OK */ - res = 0; + if (ua_hash && !strncasecmp(ua_hash, resp_hash, strlen(resp_hash))) { + /* Auth is OK */ + return 0; + } } - /* Assume success ;-) */ } return res; } @@ -5110,9 +5183,13 @@ static int get_refer_info(struct sip_pvt *p, struct sip_request *oreq) p2 = p2->next; } ast_mutex_unlock(&iflock); - if (p->refer_call) - return 0; - else + if (p->refer_call) { + if (p->refer_call == p) { + ast_log(LOG_NOTICE, "Supervised transfer attempted to transfer into same call id (%s == %s)!\n", tmp5, p->callid); + p->refer_call = NULL; + } else + return 0; + } else ast_log(LOG_NOTICE, "Supervised transfer requested, but unable to find callid '%s'\n", tmp5); } else if (ast_exists_extension(NULL, p->context, c, 1, NULL) || !strcmp(c, ast_parking_ext())) { /* This is an unsupervised transfer */ @@ -5193,7 +5270,7 @@ static int check_via(struct sip_pvt *p, struct sip_request *req) c++; while(*c && (*c < 33)) c++; - if (strcmp(via, "SIP/2.0/UDP")) { + if (strcasecmp(via, "SIP/2.0/UDP")) { ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via); return -1; } @@ -5292,7 +5369,8 @@ static int get_rpid_num(char *input,char *output, int maxlen) end = strchr(output,'@'); if (end) *end = '\0'; - + else + output[0] = '\0'; if(strstr(input,"privacy=full") || strstr(input,"privacy=uri")) return 1; @@ -5358,7 +5436,8 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd ast_mutex_lock(&userl.lock); user = find_user(of); /* Find user based on user name in the from header */ - if (user && ast_apply_ha(user->ha, sin)) { + if (!mailbox && user && ast_apply_ha(user->ha, sin)) { + p->prefs = user->prefs; p->nat = user->nat; #ifdef OSP_SUPPORT p->ospauth = user->ospauth; @@ -5414,7 +5493,7 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd if (user && debug) ast_verbose("Found user '%s'\n", user->name); } else { - if (user && debug) + if (!mailbox && user && debug) ast_verbose("Found user '%s', but fails host access\n", user->name); user = NULL; } @@ -5480,6 +5559,7 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd strncpy(p->context, peer->context, sizeof(p->context) - 1); strncpy(p->peersecret, peer->secret, sizeof(p->peersecret) - 1); strncpy(p->peermd5secret, peer->md5secret, sizeof(p->peermd5secret) - 1); + strncpy(p->language, peer->language, sizeof(p->language) -1); p->callgroup = peer->callgroup; p->pickupgroup = peer->pickupgroup; p->capability = peer->capability; @@ -5726,6 +5806,9 @@ static int sip_show_peer(int fd, int argc, char *argv[]) char status[30] = ""; char iabuf[INET_ADDRSTRLEN]; struct sip_peer *peer; + char codec_buf[512]; + struct ast_codec_pref *pref; + int x = 0, codec = 0; if (argc != 4) return RESULT_SHOWUSAGE; @@ -5747,7 +5830,7 @@ static int sip_show_peer(int fd, int argc, char *argv[]) ast_cli(fd, " Mailbox : %s\n", peer->mailbox); ast_cli(fd, " LastMsgsSent : %d\n", peer->lastmsgssent); ast_cli(fd, " Dynamic : %s\n", (peer->dynamic?"Yes":"No")); - ast_cli(fd, " Expire : %d\n", peer->expire); + ast_cli(fd, " Expire : %ld seconds\n", ast_sched_when(sched,peer->expire)); ast_cli(fd, " Expiry : %d\n", peer->expiry); ast_cli(fd, " Insecure : %s\n", (peer->insecure?((peer->insecure == 2)?"Very":"Yes"):"No") ); ast_cli(fd, " Nat : %s\n", nat2str(peer->nat)); @@ -5770,38 +5853,23 @@ static int sip_show_peer(int fd, int argc, char *argv[]) ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); ast_cli(fd, " Username : %s\n", peer->username); ast_cli(fd, " Codecs : "); - /* This should really be a function in frame.c */ - if (peer->capability & AST_FORMAT_G723_1) - ast_cli(fd, "G723 "); - if (peer->capability & AST_FORMAT_GSM) - ast_cli(fd, "GSM "); - if (peer->capability & AST_FORMAT_ULAW) - ast_cli(fd, "ULAW "); - if (peer->capability & AST_FORMAT_ALAW) - ast_cli(fd, "ALAW "); - if (peer->capability & AST_FORMAT_G726) - ast_cli(fd, "G.726 "); - if (peer->capability & AST_FORMAT_SLINEAR) - ast_cli(fd, "SLINR "); - if (peer->capability & AST_FORMAT_LPC10) - ast_cli(fd, "LPC10 "); - if (peer->capability & AST_FORMAT_ADPCM) - ast_cli(fd, "ADPCM "); - if (peer->capability & AST_FORMAT_G729A) - ast_cli(fd, "G.729A "); - if (peer->capability & AST_FORMAT_SPEEX) - ast_cli(fd, "SPEEX "); - if (peer->capability & AST_FORMAT_ILBC) - ast_cli(fd, "ILBC "); - if (peer->capability & AST_FORMAT_JPEG) - ast_cli(fd, "JPEG "); - if (peer->capability & AST_FORMAT_PNG) - ast_cli(fd, "PNG "); - if (peer->capability & AST_FORMAT_H261) - ast_cli(fd, "H.261 "); - if (peer->capability & AST_FORMAT_H263) - ast_cli(fd, "H.263 "); - ast_cli(fd, "\n"); + ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); + ast_cli(fd, "%s\n", codec_buf); + ast_cli(fd, " Codec Order : ("); + pref = &peer->prefs; + for(x = 0; x < 32 ; x++) { + codec = ast_codec_pref_index(pref,x); + if(!codec) + break; + ast_cli(fd, "%s", ast_getformatname(codec)); + if(x < 31 && ast_codec_pref_index(pref,x+1)) + ast_cli(fd, "|"); + } + + if (!x) + ast_cli(fd, "none"); + ast_cli(fd, ")\n"); + ast_cli(fd, " Status : "); if (peer->lastms < 0) strncpy(status, "UNREACHABLE", sizeof(status) - 1); @@ -6042,7 +6110,8 @@ static void receive_info(struct sip_pvt *p, struct sip_request *req) char *c; /* Need to check the media/type */ - if (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay")) { + if ( (!strcasecmp(get_header(req, "Content-Type"), "application/vnd.nortelnetworks.digits")) || + (!strcasecmp(get_header(req, "Content-Type"), "application/dtmf-relay")) ) { /* Try getting the "signal=" part */ if (ast_strlen_zero(c = get_sdp(req, "Signal")) && ast_strlen_zero(c = get_sdp(req, "d"))) { @@ -6061,6 +6130,8 @@ static void receive_info(struct sip_pvt *p, struct sip_request *req) event = 10; else if (buf[0] == '#') event = 11; + else if ((buf[0] >= 'A') && (buf[0] <= 'D')) + event = 12 + buf[0] - 'A'; else event = atoi(buf); if (event < 10) { @@ -6210,27 +6281,34 @@ static int sip_no_debug(int fd, int argc, char *argv[]) static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *digest, int digest_len); -/*--- do_register_auth: Challenge for registration ---*/ +/*--- do_register_auth: Authenticate for outbound registration ---*/ static int do_register_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader) { char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, "REGISTER", digest, sizeof(digest))) { + if (reply_digest(p, req, header, "REGISTER", digest, sizeof(digest))) { /* There's nothing to use for authentication */ + /* No digest challenge in request */ + if (sip_debug_test_pvt(p)) + ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname); + /* No old challenge */ return -1; } + if (sip_debug_test_pvt(p)) + ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname); return transmit_register(p->registry,"REGISTER",digest, respheader); } -/*--- do_proxy_auth: Challenge user ---*/ +/*--- do_proxy_auth: Add authentication on outbound SIP packet ---*/ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init) { char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, msg, digest, sizeof(digest) )) { + if (reply_digest(p, req, header, msg, digest, sizeof(digest) )) { /* No way to authenticate */ return -1; } + /* Now we have a reply digest */ return transmit_invite(p,msg,!strcasecmp(msg, "INVITE"),digest, respheader, NULL,NULL,NULL, init); } @@ -6326,6 +6404,17 @@ static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header strncpy(p->domain, domain, sizeof(p->domain)-1); strncpy(p->opaque, opaque, sizeof(p->opaque)-1); strncpy(p->qop, qop, sizeof(p->qop)-1); + + /* Save auth data for following registrations */ + if (p->registry) { + struct sip_registry *r = p->registry; + + strncpy(r->realm, p->realm, sizeof(r->realm)-1); + strncpy(r->nonce, p->nonce, sizeof(r->nonce)-1); + strncpy(r->domain, p->domain, sizeof(r->domain)-1); + strncpy(r->opaque, p->opaque, sizeof(r->opaque)-1); + strncpy(r->qop, p->qop, sizeof(r->qop)-1); + } build_reply_digest(p, orig_header, digest, digest_len); return 0; } @@ -6432,7 +6521,7 @@ static char no_history_usage[] = static char history_usage[] = "Usage: sip history\n" " Enables recording of SIP dialog history for debugging purposes.\n" -"Use 'sip show hitory' to view the history of a call number.\n"; +"Use 'sip show history' to view the history of a call number.\n"; static char sip_reload_usage[] = "Usage: sip reload\n" @@ -6487,6 +6576,9 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) char *s, *e; strncpy(tmp, get_header(req, "Contact"), sizeof(tmp) - 1); s = ditch_braces(tmp); + e = strchr(s, ';'); + if (e) + *e = '\0'; if (p->promiscredir) { if (!strncasecmp(s, "sip:", 4)) s += 4; @@ -6511,6 +6603,7 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) } } +/*--- check_pendings: Check pending actions on SIP call ---*/ static void check_pendings(struct sip_pvt *p) { /* Go ahead and send bye at this point */ @@ -6553,7 +6646,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ __sip_ack(p, seqno, 0, msg); /* Get their tag if we haven't already */ to = get_header(req, "To"); - to = strstr(to, "tag="); + to = ast_strcasestr(to, "tag="); if (to) { to += 4; strncpy(p->theirtag, to, sizeof(p->theirtag) - 1); @@ -6660,6 +6753,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ sip_cancel_destroy(p); if (!ast_strlen_zero(get_header(req, "Content-Type"))) process_sdp(p, req); + /* Save Record-Route for any later requests we make on this dialogue */ build_route(p, req, 1); if (p->owner) { @@ -6727,24 +6821,30 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ if (!expires) expires=atoi(get_header(req, "expires")); if (!expires) expires=default_expiry; + expires_ms = expires * 1000; if (expires <= EXPIRY_GUARD_LIMIT) expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT),EXPIRY_GUARD_MIN); else expires_ms -= EXPIRY_GUARD_SECS * 1000; + if (sipdebug) + ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d ms)\n", r->hostname, expires, expires_ms); r->refresh= (int) expires_ms / 1000; + + /* Schedule re-registration before we expire */ r->expire=ast_sched_add(sched, expires_ms, sip_reregister, r); } else ast_log(LOG_WARNING, "Got 200 OK on REGISTER that isn't a register\n"); } break; - case 401: /* Not authorized on REGISTER */ + case 401: /* Not www-authorized on REGISTER */ if (!strcasecmp(msg, "INVITE")) { /* First we ACK */ transmit_request(p, "ACK", seqno, 0, 0); /* Then we AUTH */ + p->theirtag[0]='\0'; /* forget their old tag, so we don't match tags when getting response */ if ((p->authtries > 1) || do_proxy_auth(p, req, "WWW-Authenticate", "Authorization", "INVITE", 1)) { ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From")); p->needdestroy = 1; @@ -6757,6 +6857,21 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ } else p->needdestroy = 1; break; + case 403: /* Forbidden - we failed authentication */ + if (!strcasecmp(msg, "INVITE")) { + /* First we ACK */ + transmit_request(p, "ACK", seqno, 0, 0); + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for INVITE to '%s'\n", get_header(&p->initreq, "From")); + if (owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + p->needdestroy = 1; + } else if (p->registry && !strcasecmp(msg, "REGISTER")) { + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname); + p->needdestroy = 1; + } else { + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for %s\n", msg); + } + break; case 407: if (!strcasecmp(msg, "INVITE")) { /* First we ACK */ @@ -6764,6 +6879,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ /* Then we AUTH */ /* But only if the packet wasn't marked as ignore in handle_request */ if(!ignore){ + p->theirtag[0]='\0'; /* forget their old tag, so we don't match tags when getting response */ if ((p->authtries > 1) || do_proxy_auth(p, req, "Proxy-Authenticate", "Proxy-Authorization", "INVITE", 1)) { ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From")); p->needdestroy = 1; @@ -6860,8 +6976,18 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ p->alreadygone = 1; if (!p->owner) p->needdestroy = 1; + } else if ((resp >= 100) && (resp < 200)) { + if (!strcasecmp(msg, "INVITE")) { + sip_cancel_destroy(p); + if (!ast_strlen_zero(get_header(req, "Content-Type"))) + process_sdp(p, req); + if (p->owner) { + /* Queue a progress frame */ + ast_queue_control(p->owner, AST_CONTROL_PROGRESS); + } + } } else - ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); + ast_log(LOG_NOTICE, "Don't know anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); } } else { if (sip_debug_test_pvt(p)) @@ -6973,38 +7099,73 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct return -1; } +static void ast_quiet_chan(struct ast_channel *chan) { + if(chan && chan->_state == AST_STATE_UP) { + if(chan->generatordata) + ast_deactivate_generator(chan); + } +} /*--- attempt_transfer: Attempt transfer of SIP call ---*/ static int attempt_transfer(struct sip_pvt *p1, struct sip_pvt *p2) { + int res = 0; + struct ast_channel + *chana = NULL, + *chanb = NULL, + *bridgea = NULL, + *bridgeb = NULL, + *peera = NULL, + *peerb = NULL, + *peerc = NULL, + *peerd = NULL; + if (!p1->owner || !p2->owner) { ast_log(LOG_WARNING, "Transfer attempted without dual ownership?\n"); return -1; } - if (p1->owner->bridge) { - if (p2->owner->bridge) - ast_moh_stop(p2->owner->bridge); - ast_moh_stop(p1->owner->bridge); - ast_moh_stop(p1->owner); - ast_moh_stop(p2->owner); - if (ast_channel_masquerade(p2->owner, p1->owner->bridge)) { - ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", p2->owner->name, p1->owner->bridge->name); - return -1; - } - } else if (p2->owner->bridge) { - ast_moh_stop(p2->owner->bridge); - ast_moh_stop(p2->owner); - ast_moh_stop(p1->owner); - if (ast_channel_masquerade(p1->owner, p2->owner->bridge)) { - ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", p1->owner->name, p2->owner->bridge->name); - return -1; - } + chana = p1->owner; + chanb = p2->owner; + bridgea = p1->owner->bridge; + bridgeb = p2->owner->bridge; + + if (bridgea) { + peera = chana; + peerb = chanb; + peerc = bridgea; + peerd = bridgeb; + } else if(bridgeb) { + peera = chanb; + peerb = chana; + peerc = bridgeb; + peerd = bridgea; + } + + if(peera && peerb && peerc && (peerb != peerc)) { + ast_quiet_chan(peera); + ast_quiet_chan(peerb); + ast_quiet_chan(peerc); + ast_quiet_chan(peerd); + + if (peera->cdr && peerb->cdr) { + peerb->cdr = ast_cdr_append(peerb->cdr, peera->cdr); + } else if(peera->cdr) { + peerb->cdr = peera->cdr; + } + peera->cdr = NULL; + + if (peerb->cdr && peerc->cdr) { + peerb->cdr = ast_cdr_append(peerb->cdr, peerc->cdr); + } else if(peerc->cdr) { + peerb->cdr = peerc->cdr; + } + peerc->cdr = NULL; + if (ast_channel_masquerade(peerb, peerc)) { + ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name); + res = -1; + } + return res; } else { - ast_log(LOG_NOTICE, "Transfer attempted with no bridged calls to transfer\n"); - if (p1->owner) - ast_softhangup_nolock(p1->owner, AST_SOFTHANGUP_DEV); - if (p2->owner) - ast_softhangup_nolock(p2->owner, AST_SOFTHANGUP_DEV); return -1; } return 0; @@ -7066,7 +7227,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (p->icseq && (p->icseq > seqno)) { ast_log(LOG_DEBUG, "Ignoring too old packet packet %d (expecting >= %d)\n", seqno, p->icseq); return -1; - } else if (p->icseq && (p->icseq == seqno)) { + } else if (p->icseq && (p->icseq == seqno) && (strcasecmp(cmd, "CANCEL") || p->alreadygone)) { /* ignore means "don't do anything with it" but still have to respond appropriately. We do this if we receive a repeat of the last sequence number */ @@ -7074,7 +7235,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc } if (ast_strlen_zero(p->theirtag)) { from = get_header(req, "From"); - from = strstr(from, "tag="); + from = ast_strcasestr(from, "tag="); if (from) { from += 4; strncpy(p->theirtag, from, sizeof(p->theirtag) - 1); @@ -7132,7 +7293,6 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc /* We do NOT destroy p here, so that our response will be accepted */ return 0; } - /* Process the SDP portion */ if (!ignore) { /* Use this as the basis */ if (debug) @@ -7144,19 +7304,22 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc p->pendinginvite = seqno; copy_request(&p->initreq, req); check_via(p, req); - if (!ast_strlen_zero(get_header(req, "Content-Type"))) { - if (process_sdp(p, req)) - return -1; - } else { - p->jointcapability = p->capability; - ast_log(LOG_DEBUG, "Hm.... No sdp for the moment\n"); + if (p->owner) { + /* Handle SDP here if we already have an owner */ + if (!ast_strlen_zero(get_header(req, "Content-Type"))) { + if (process_sdp(p, req)) { + transmit_response(p, "488 Not acceptable here", req); + p->needdestroy = 1; + return -1; + } + } else { + p->jointcapability = p->capability; + ast_log(LOG_DEBUG, "Hm.... No sdp for the moment\n"); + } } - /* Queue NULL frame to prod ast_rtp_bridge if appropriate */ - if (p->owner) - ast_queue_frame(p->owner, &af); } else if (debug) ast_verbose("Ignoring this request\n"); - if (!p->lastinvite) { + if (!p->lastinvite && !ignore && !p->owner) { /* Handle authentication if this is our first invite */ res = check_user(p, req, cmd, e, 1, sin, ignore); if (res) { @@ -7170,6 +7333,20 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc } return 0; } + /* Process the SDP portion */ + if (!ast_strlen_zero(get_header(req, "Content-Type"))) { + if (process_sdp(p, req)) { + transmit_response(p, "488 Not acceptable here", req); + p->needdestroy = 1; + return -1; + } + } else { + p->jointcapability = p->capability; + ast_log(LOG_DEBUG, "Hm.... No sdp for the moment\n"); + } + /* Queue NULL frame to prod ast_rtp_bridge if appropriate */ + if (p->owner) + ast_queue_frame(p->owner, &af); /* Initialize the context if it hasn't been already */ if (ast_strlen_zero(p->context)) strncpy(p->context, default_context, sizeof(p->context) - 1); @@ -7282,12 +7459,20 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc } } else { if (p && !p->needdestroy) { - ast_log(LOG_NOTICE, "Unable to create/find channel\n"); - if (ignore) - transmit_response(p, "503 Unavailable", req); - else - transmit_response_reliable(p, "503 Unavailable", req, 1); - p->needdestroy = 1; + if (!p->jointcapability) { + if (ignore) + transmit_response(p, "488 Not Acceptable Here", req); + else + transmit_response_reliable(p, "488 Not Acceptable Here", req, 1); + p->needdestroy = 1; + } else { + ast_log(LOG_NOTICE, "Unable to create/find channel\n"); + if (ignore) + transmit_response(p, "503 Unavailable", req); + else + transmit_response_reliable(p, "503 Unavailable", req, 1); + p->needdestroy = 1; + } } } } else if (!strcasecmp(cmd, "REFER")) { @@ -7407,12 +7592,17 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc p->needdestroy = 1; transmit_response(p, "200 OK", req); } else if (!strcasecmp(cmd, "MESSAGE")) { - if (!ignore) { - if (debug) - ast_verbose("Receiving message!\n"); - receive_message(p, req); + if (p->lastinvite) { + if (!ignore) { + if (debug) + ast_verbose("Receiving message!\n"); + receive_message(p, req); + } + transmit_response(p, "200 OK", req); + } else { + transmit_response(p, "405 Method Not Allowed", req); + p->needdestroy = 1; } - transmit_response(p, "200 OK", req); } else if (!strcasecmp(cmd, "SUBSCRIBE")) { if (!ignore) { /* Use this as the basis */ @@ -7427,8 +7617,8 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (!p->lastinvite) { char mailbox[256]=""; - char rbox[256]; int found = 0; + /* Handle authentication if this is our first subscribe */ res = check_user_full(p, req, cmd, e, 0, sin, ignore, mailbox, sizeof(mailbox)); if (res) { @@ -7457,21 +7647,33 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc p->subscribed = 2; else if (!strcmp(get_header(req, "Accept"), "application/simple-message-summary")) { /* Looks like they actually want a mailbox */ - snprintf(rbox, sizeof(rbox), ",%s@%s,", p->exten, p->context); - if (strstr(mailbox, rbox)) + + /* At this point, we should check if they subscribe to a mailbox that + has the same extension as the peer or the mailbox id. If we configure + the context to be the same as a SIP domain, we could check mailbox + context as well. To be able to securely accept subscribes on mailbox + IDs, not extensions, we need to check the digest auth user to make + sure that the user has access to the mailbox. + + Since we do not act on this subscribe anyway, we might as well + accept any authenticated peer with a mailbox definition in their + config section. + + */ + if (!ast_strlen_zero(mailbox)) { found++; - if (!found) { - snprintf(rbox, sizeof(rbox), ",%s,", p->exten); - if (strstr(mailbox, rbox)) - found++; } - if (found) + + if (found) { transmit_response(p, "200 OK", req); - else { + p->needdestroy = 1; + } else { transmit_response(p, "403 Forbidden", req); p->needdestroy = 1; } - + + return 0; + } else p->subscribed = 1; if (p->subscribed) @@ -7484,19 +7686,29 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (!ignore && p) p->lastinvite = seqno; if (p && !p->needdestroy) { - if (!(p->expiry = atoi(get_header(req, "Expires")))) { + if (!(p->expiry = atoi(get_header(req, "Expires")))) { + transmit_response(p, "200 OK", req); + p->needdestroy = 1; + return 0; + } + /* The next line can be removed if the SNOM200 Expires bug is fixed */ + if (p->subscribed == 1) { + if (p->expiry>max_expiry) { + p->expiry = max_expiry; + } + } + /* Go ahead and free RTP port */ + if (p->rtp) { + ast_rtp_destroy(p->rtp); + p->rtp = NULL; + } + if (p->vrtp) { + ast_rtp_destroy(p->vrtp); + p->vrtp = NULL; + } transmit_response(p, "200 OK", req); - p->needdestroy = 1; - return 0; - } - /* The next line can be removed if the SNOM200 Expires bug is fixed */ - if (p->subscribed == 1) { - if (p->expiry>max_expiry) - p->expiry = max_expiry; - } - transmit_response(p, "200 OK", req); - sip_scheddestroy(p, (p->expiry+10)*1000); - transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); + sip_scheddestroy(p, (p->expiry+10)*1000); + transmit_state_notify(p, ast_extension_state(NULL, p->context, p->exten),1); } } else if (!strcasecmp(cmd, "INFO")) { if (!ignore) { @@ -7520,6 +7732,15 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if ((res = register_verify(p, sin, req, e, ignore)) < 0) ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s'\n", get_header(req, "To"), ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); if (res < 1) { + /* Go ahead and free RTP port */ + if (p->rtp) { + ast_rtp_destroy(p->rtp); + p->rtp = NULL; + } + if (p->vrtp) { + ast_rtp_destroy(p->vrtp); + p->vrtp = NULL; + } /* Destroy the session, but keep us around for just a bit in case they don't get our 200 OK */ sip_scheddestroy(p, 15*1000); @@ -7584,8 +7805,7 @@ static int sipsock_read(int *id, int fd, short events, void *ignore) debug = sip_debug_test_addr(&sin); if (debug) ast_verbose("\n\nSip read: \n%s\n", req.data); - if (pedanticsipchecking) - req.len = lws2sws(req.data, req.len); + req.len = lws2sws(req.data, req.len); parse(&req); if (debug) ast_verbose("%d headers, %d lines\n", req.headers, req.lines); @@ -7850,6 +8070,8 @@ static int sip_poke_noanswer(void *data) } /*--- sip_poke_peer: Check availability of peer, also keep NAT open ---*/ +/* This is done with the interval in qualify= option in sip.conf */ +/* Default is 2 seconds */ static int sip_poke_peer(struct sip_peer *peer) { struct sip_pvt *p; @@ -7875,6 +8097,12 @@ static int sip_poke_peer(struct sip_peer *peer) } memcpy(&p->sa, &peer->addr, sizeof(p->sa)); memcpy(&p->recv, &peer->addr, sizeof(p->sa)); + + /* Send options to peer's fullcontact */ + if (!ast_strlen_zero(peer->fullcontact)) { + strncpy (p->fullcontact, peer->fullcontact, sizeof(p->fullcontact)); + } + if (!ast_strlen_zero(p->tohost)) strncpy(p->tohost, peer->tohost, sizeof(p->tohost) - 1); else @@ -8011,8 +8239,15 @@ static struct ast_channel *sip_request(char *type, int format, void *data) else /* UNIDEN bug */ snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch); build_callid(p->callid, sizeof(p->callid), p->ourip, p->fromdomain); - if (ext) + + /* We have an extension to call, don't use the full contact here */ + /* This to enable dialling registred peers with extension dialling, + like SIP/peername/extension + SIP/peername will still use the full contact */ + if (ext) { strncpy(p->username, ext, sizeof(p->username) - 1); + p->fullcontact[0] = 0; + } #if 0 printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host); #endif @@ -8047,6 +8282,8 @@ static struct sip_user *build_user(char *name, struct ast_variable *v) user->trustrpid = global_trustrpid; user->dtmfmode = global_dtmfmode; user->progressinband = global_progressinband; + user->nat = global_nat; + user->prefs = prefs; #ifdef OSP_SUPPORT user->ospauth = global_ospauth; #endif @@ -8120,17 +8357,9 @@ static struct sip_user *build_user(char *name, struct ast_variable *v) user->amaflags = format; } } 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 - user->capability |= format; + ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1); } 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 - user->capability &= ~format; + ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 0); } else if (!strcasecmp(v->name, "insecure")) { user->insecure = ast_true(v->value); } else if (!strcasecmp(v->name, "restrictcid")) { @@ -8165,6 +8394,9 @@ static struct sip_peer *temp_peer(char *name) { struct sip_peer *peer; peer = malloc(sizeof(struct sip_peer)); + if (!peer) + return NULL; + memset(peer, 0, sizeof(struct sip_peer)); peer->expire = -1; peer->pokeexpire = -1; @@ -8187,6 +8419,7 @@ static struct sip_peer *temp_peer(char *name) peer->dynamic = 1; peer->trustrpid = global_trustrpid; peer->progressinband = global_progressinband; + peer->prefs = prefs; #ifdef OSP_SUPPORT peer->ospauth = global_ospauth; #endif @@ -8201,7 +8434,6 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) struct sip_peer *prev; struct ast_ha *oldha = NULL; int maskfound=0; - int format; int found=0; prev = NULL; ast_mutex_lock(&peerl.lock); @@ -8225,22 +8457,34 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) } else { ast_mutex_unlock(&peerl.lock); peer = malloc(sizeof(struct sip_peer)); - memset(peer, 0, sizeof(struct sip_peer)); - peer->expire = -1; - peer->pokeexpire = -1; + if (peer) { + memset(peer, 0, sizeof(struct sip_peer)); + peer->expire = -1; + peer->pokeexpire = -1; + } } - peer->lastmsgssent = -1; if (peer) { + peer->lastmsgssent = -1; if (!found) { strncpy(peer->name, name, sizeof(peer->name)-1); - strncpy(peer->context, default_context, sizeof(peer->context)-1); - strncpy(peer->language, default_language, sizeof(peer->language)-1); - strncpy(peer->musicclass, global_musicclass, sizeof(peer->musicclass)-1); - peer->addr.sin_port = htons(DEFAULT_SIP_PORT); + peer->addr.sin_port = htons(DEFAULT_SIP_PORT); peer->addr.sin_family = AF_INET; peer->defaddr.sin_family = AF_INET; peer->expiry = expiry; } + strncpy(peer->context, default_context, sizeof(peer->context)-1); + strncpy(peer->language, default_language, sizeof(peer->language)-1); + strncpy(peer->musicclass, global_musicclass, sizeof(peer->musicclass)-1); + peer->secret[0] = '\0'; + peer->md5secret[0] = '\0'; + peer->fromdomain[0] = '\0'; + peer->fromuser[0] = '\0'; + peer->regexten[0] = '\0'; + peer->mailbox[0] = '\0'; + peer->callgroup = 0; + peer->pickupgroup = 0; + peer->maxms = 0; + peer->prefs = prefs; oldha = peer->ha; peer->ha = NULL; peer->addr.sin_family = AF_INET; @@ -8353,17 +8597,9 @@ static struct sip_peer *build_peer(char *name, struct ast_variable *v) } else if (!strcasecmp(v->name, "pickupgroup")) { peer->pickupgroup = ast_get_group(v->value); } 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; + ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1); } 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; + ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0); } else if (!strcasecmp(v->name, "insecure")) { if (!strcasecmp(v->value, "very")) { peer->insecure = 2; @@ -8427,7 +8663,7 @@ static int reload_config(void) struct sip_user *user; struct ast_hostent ahp; char *cat; - char *utype; + char *utype; struct hostent *hp; int format; int oldport = ntohs(bindaddr.sin_port); @@ -8436,10 +8672,6 @@ static int reload_config(void) global_dtmfmode = SIP_DTMF_RFC2833; global_promiscredir = 0; - if (gethostname(ourhost, sizeof(ourhost))) { - ast_log(LOG_WARNING, "Unable to get hostname, SIP disabled\n"); - return 0; - } cfg = ast_load(config); /* We *must* have a config file otherwise stop immediately */ @@ -8450,11 +8682,10 @@ static int reload_config(void) global_nat = SIP_NAT_RFC3581; - sip_prefs_free(); - memset(&bindaddr, 0, sizeof(bindaddr)); memset(&localaddr, 0, sizeof(localaddr)); memset(&externip, 0, sizeof(externip)); + memset(&prefs, 0 , sizeof(struct ast_codec_pref)); /* Initialize some reasonable defaults */ strncpy(default_context, "default", sizeof(default_context) - 1); @@ -8585,21 +8816,9 @@ static int reload_config(void) else memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); } 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 { - global_capability |= format; - sip_pref_append(format); - } + ast_parse_allow_disallow(&prefs, &global_capability, v->value, 1); } 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 { - global_capability &= ~format; - sip_pref_remove(format); - } + ast_parse_allow_disallow(&prefs, &global_capability, v->value, 0); } else if (!strcasecmp(v->name, "register")) { sip_register(v->value, v->lineno); } else if (!strcasecmp(v->name, "recordhistory")) { @@ -8673,19 +8892,11 @@ static int reload_config(void) cat = ast_category_browse(cfg, cat); } - if (ntohl(bindaddr.sin_addr.s_addr)) { - memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip)); - } else { - hp = ast_gethostbyname(ourhost, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "Unable to get IP address for %s, SIP disabled\n", ourhost); - if (!__ourip.s_addr) { - ast_destroy(cfg); - return 0; - } - } else - memcpy(&__ourip, hp->h_addr, sizeof(__ourip)); - } + if (ast_find_ourip(&__ourip, bindaddr)) { + ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n"); + return 0; + } + if (!ntohs(bindaddr.sin_port)) bindaddr.sin_port = ntohs(DEFAULT_SIP_PORT); bindaddr.sin_family = AF_INET; @@ -8771,6 +8982,7 @@ static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan) return rtp; } +/*--- sip_set_rtp_peer: Set the RTP peer for this call ---*/ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs) { struct sip_pvt *p; @@ -8942,20 +9154,31 @@ static void prune_peers(void) ast_mutex_unlock(&peerl.lock); } +/* Send all of our registrations, spaced out to avoid suicide*/ +static void sip_send_all_registers(void) +{ + struct sip_registry *reg; + int ms; + ast_mutex_lock(®l.lock); + for (reg = regl.registrations; reg; reg = reg->next) { + if (reg->expire > -1) + ast_sched_del(sched, reg->expire); + ms = (rand() >> 12) & 0x1fff; + reg->expire = ast_sched_add(sched, ms, sip_reregister, reg); + } + ast_mutex_unlock(®l.lock); +} + /*--- sip_do_reload: Reload module */ static int sip_do_reload(void) { - struct sip_registry *reg; struct sip_peer *peer; delete_users(); reload_config(); - prune_peers(); - /* And start the monitor for the first time */ - ast_mutex_lock(®l.lock); - for (reg = regl.registrations; reg; reg = reg->next) - __sip_do_register(reg); - ast_mutex_unlock(®l.lock); + + sip_send_all_registers(); + ast_mutex_lock(&peerl.lock); for (peer = peerl.peers; peer; peer = peer->next) sip_poke_peer(peer); @@ -8991,11 +9214,10 @@ int load_module() { int res; struct sip_peer *peer; - struct sip_registry *reg; - ast_mutex_init(&userl.lock); - ast_mutex_init(&peerl.lock); - ast_mutex_init(®l.lock); + ast_mutex_init(&userl.lock); + ast_mutex_init(&peerl.lock); + ast_mutex_init(®l.lock); sched = sched_context_create(); if (!sched) { ast_log(LOG_WARNING, "Unable to create schedule context\n"); @@ -9004,7 +9226,8 @@ int load_module() if (!io) { ast_log(LOG_WARNING, "Unable to create I/O context\n"); } - + + res = reload_config(); if (!res) { /* Make sure we can register our sip channel type */ @@ -9039,11 +9262,8 @@ int load_module() sip_poke_peer(peer); ast_mutex_unlock(&peerl.lock); - ast_mutex_lock(®l.lock); - for (reg = regl.registrations; reg; reg = reg->next) - __sip_do_register(reg); - ast_mutex_unlock(®l.lock); - + sip_send_all_registers(); + /* And start the monitor for the first time */ restart_monitor(); } @@ -9127,7 +9347,9 @@ int unload_module() ast_mutex_destroy(&userl.lock); ast_mutex_destroy(&peerl.lock); ast_mutex_destroy(®l.lock); - + + close(sipsock); + return 0; } |