From bbdc6cafb5a4c952b565135ee3470200673b310f Mon Sep 17 00:00:00 2001 From: qwell Date: Thu, 9 Sep 2010 20:06:31 +0000 Subject: Transmit silence when reading DTMF in ast_readstring. Otherwise, you could get issues with DTMF timeouts causing hangups. (closes issue #17370) Reported by: makoto Patches: channel-readstring-silence-generator.patch uploaded by makoto (license 38) git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@285742 f38db490-d61c-443f-a65b-d21fe96a405b --- channels/chan_alsa.c | 37 +++--- channels/chan_dahdi.c | 4 +- channels/chan_local.c | 20 +++ channels/chan_misdn.c | 54 ++++---- channels/chan_oss.c | 32 +++-- channels/chan_phone.c | 76 +++++++----- channels/chan_sip.c | 336 ++++++++++++++++++++++++++++++++++++-------------- 7 files changed, 362 insertions(+), 197 deletions(-) (limited to 'channels') diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c index 3a9a47208..a68c031b1 100644 --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -277,34 +277,25 @@ static int send_sound(void) static void *sound_thread(void *unused) { - fd_set rfds; - fd_set wfds; - int max, res; + struct pollfd pfd[3] = { { .fd = sndcmd[0], .events = POLLIN }, { .fd = writedev }, { .fd = readdev } }; + int res, x; for (;;) { - FD_ZERO(&rfds); - FD_ZERO(&wfds); - max = sndcmd[0]; - FD_SET(sndcmd[0], &rfds); - if (cursound > -1) { - FD_SET(writedev, &wfds); - if (writedev > max) - max = writedev; + for (x = 0; x < 3; x++) { + pfd[x].revents = 0; } + + pfd[1].events = cursound > -1 ? POLLOUT : 0; #ifdef ALSA_MONITOR - if (!alsa.owner) { - FD_SET(readdev, &rfds); - if (readdev > max) - max = readdev; - } + pfd[2].events = !alsa.owner ? POLLIN : 0; #endif - res = ast_select(max + 1, &rfds, &wfds, NULL, NULL); + res = ast_poll(pfd, 3, -1); if (res < 1) { - ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); + ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno)); continue; } #ifdef ALSA_MONITOR - if (FD_ISSET(readdev, &rfds)) { + if (pfd[2].revents & POLLIN) { /* Keep the pipe going with read audio */ snd_pcm_state_t state; short buf[FRAME_SIZE]; @@ -329,7 +320,7 @@ static void *sound_thread(void *unused) alsa_monitor_read((char *) buf, r * 2); } #endif - if (FD_ISSET(sndcmd[0], &rfds)) { + if (pfd[0].revents & POLLIN) { if (read(sndcmd[0], &cursound, sizeof(cursound)) < 0) { ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); } @@ -337,9 +328,11 @@ static void *sound_thread(void *unused) offset = 0; sampsent = 0; } - if (FD_ISSET(writedev, &wfds)) - if (send_sound()) + if (pfd[1].revents & POLLOUT) { + if (send_sound()) { ast_log(LOG_WARNING, "Failed to write sound\n"); + } + } } /* Never reached */ return NULL; diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index a4e549a6f..667ee0313 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -5636,7 +5636,7 @@ static int dahdi_indicate(struct ast_channel *chan, int condition, const void *d ast_log(LOG_DEBUG,"Received AST_CONTROL_PROGRESS on %s\n",chan->name); #ifdef HAVE_PRI p->digital = 0; /* Digital-only calls isn't allows any inband progress messages */ - if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) { + if (!p->progress && !p->alerting && p->sig==SIG_PRI && p->pri && !p->outgoing) { if (p->pri->pri) { if (!pri_grab(p, p->pri)) { pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1); @@ -10066,7 +10066,7 @@ static void *pri_dchannel(void *vpri) } break; case PRI_EVENT_CONFIG_ERR: - ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->trunkgroup, e->err.err); + ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->span, e->err.err); break; case PRI_EVENT_RESTART_ACK: chanpos = pri_find_principle(pri, e->restartack.channel); diff --git a/channels/chan_local.c b/channels/chan_local.c index 81f16eadf..7fa6968cc 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -294,6 +294,26 @@ static void check_bridge(struct local_pvt *p) p->chan->audiohooks = p->owner->audiohooks; p->owner->audiohooks = audiohooks_swapper; } + + /* If any Caller ID was set, preserve it after masquerade like above. We must check + * to see if Caller ID was set because otherwise we'll mistakingly copy info not + * set from the dialplan and will overwrite the real channel Caller ID. The reason + * for this whole preswapping action is because the Caller ID is set on the channel + * thread (which is the to be masqueraded away local channel) before both local + * channels are optimized away. + */ + if (p->owner->cid.cid_dnid || p->owner->cid.cid_num || + p->owner->cid.cid_name || p->owner->cid.cid_ani || + p->owner->cid.cid_rdnis || p->owner->cid.cid_pres || + p->owner->cid.cid_ani2 || p->owner->cid.cid_ton || + p->owner->cid.cid_tns) { + + struct ast_callerid tmpcid; + tmpcid = p->owner->cid; + p->owner->cid = p->chan->_bridge->cid; + p->chan->_bridge->cid = tmpcid; + } + ast_app_group_update(p->chan, p->owner); ast_channel_masquerade(p->owner, p->chan->_bridge); ast_set_flag(p, LOCAL_ALREADY_MASQED); diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 95aff770a..389bd54e2 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -2763,9 +2763,8 @@ static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame static struct ast_frame *misdn_read(struct ast_channel *ast) { struct chan_list *tmp; - fd_set rrfs; - struct timeval tv; int len, t; + struct pollfd pfd = { .fd = -1, .events = POLLIN }; if (!ast) { chan_misdn_log(1, 0, "misdn_read called without ast\n"); @@ -2781,30 +2780,23 @@ static struct ast_frame *misdn_read(struct ast_channel *ast) return NULL; } - tv.tv_sec=0; - tv.tv_usec=20000; + pfd.fd = tmp->pipe[0]; + t = ast_poll(&pfd, 1, 20); - FD_ZERO(&rrfs); - FD_SET(tmp->pipe[0],&rrfs); - - t=select(FD_SETSIZE,&rrfs,NULL, NULL,&tv); - - if (!t) { - chan_misdn_log(3, tmp->bc->port, "read Select Timed out\n"); - len=160; - } - - if (t<0) { - chan_misdn_log(-1, tmp->bc->port, "Select Error (err=%s)\n",strerror(errno)); + if (t < 0) { + chan_misdn_log(-1, tmp->bc->port, "poll() error (err=%s)\n", strerror(errno)); return NULL; } - if (FD_ISSET(tmp->pipe[0],&rrfs)) { - len=read(tmp->pipe[0],tmp->ast_rd_buf,sizeof(tmp->ast_rd_buf)); + if (!t) { + chan_misdn_log(3, tmp->bc->port, "poll() timed out\n"); + len = 160; + } else if (pfd.revents & POLLIN) { + len = read(tmp->pipe[0], tmp->ast_rd_buf, sizeof(tmp->ast_rd_buf)); - if (len<=0) { + if (len <= 0) { /* we hangup here, since our pipe is closed */ - chan_misdn_log(2,tmp->bc->port,"misdn_read: Pipe closed, hanging up\n"); + chan_misdn_log(2, tmp->bc->port, "misdn_read: Pipe closed, hanging up\n"); return NULL; } @@ -4910,26 +4902,22 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) if (ch->ast) ast_queue_frame(ch->ast, &frame); } else { - fd_set wrfs; - struct timeval tv = { 0, 0 }; + struct pollfd pfd = { .fd = ch->pipe[1], .events = POLLOUT }; int t; - FD_ZERO(&wrfs); - FD_SET(ch->pipe[1], &wrfs); + t = ast_poll(&pfd, 1, 0); - t = select(FD_SETSIZE, NULL, &wrfs, NULL, &tv); - - if (!t) { - chan_misdn_log(9, bc->port, "Select Timed out\n"); + if (t < 0) { + chan_misdn_log(-1, bc->port, "poll() error (err=%s)\n", strerror(errno)); break; } - - if (t < 0) { - chan_misdn_log(-1, bc->port, "Select Error (err=%s)\n", strerror(errno)); + + if (!t) { + chan_misdn_log(9, bc->port, "poll() timed out\n"); break; } - - if (FD_ISSET(ch->pipe[1], &wrfs)) { + + if (pfd.revents & POLLOUT) { chan_misdn_log(9, bc->port, "writing %d bytes to asterisk\n", bc->bframe_len); if (write(ch->pipe[1], bc->bframe, bc->bframe_len) <= 0) { chan_misdn_log(0, bc->port, "Write returned <=0 (err=%s) --> hanging up channel\n", strerror(errno)); diff --git a/channels/chan_oss.c b/channels/chan_oss.c index 8f55daa55..f78046780 100644 --- a/channels/chan_oss.c +++ b/channels/chan_oss.c @@ -604,38 +604,32 @@ static void *sound_thread(void *arg) if (read(o->sounddev, ign, sizeof(ign)) < 0) { } for (;;) { - fd_set rfds, wfds; - int maxfd, res; + int res; + struct pollfd pfd[2] = { { .fd = o->sndcmd[0], .events = POLLIN }, { .fd = o->sounddev, .events = 0 } }; pthread_testcancel(); - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_SET(o->sndcmd[0], &rfds); - maxfd = o->sndcmd[0]; /* pipe from the main process */ - if (o->cursound > -1 && o->sounddev < 0) + if (o->cursound > -1 && o->sounddev < 0) { setformat(o, O_RDWR); /* need the channel, try to reopen */ - else if (o->cursound == -1 && o->owner == NULL) + } else if (o->cursound == -1 && o->owner == NULL) { setformat(o, O_CLOSE); /* can close */ + } if (o->sounddev > -1) { if (!o->owner) { /* no one owns the audio, so we must drain it */ - FD_SET(o->sounddev, &rfds); - maxfd = MAX(o->sounddev, maxfd); + pfd[1].events |= POLLIN; } if (o->cursound > -1) { - FD_SET(o->sounddev, &wfds); - maxfd = MAX(o->sounddev, maxfd); + pfd[1].events |= POLLOUT; } } - /* ast_select emulates linux behaviour in terms of timeout handling */ - res = ast_select(maxfd + 1, &rfds, &wfds, NULL, NULL); + res = ast_poll(pfd, 2, -1); pthread_testcancel(); if (res < 1) { - ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); + ast_log(LOG_WARNING, "poll() failed: %s\n", strerror(errno)); sleep(1); continue; } - if (FD_ISSET(o->sndcmd[0], &rfds)) { + if (pfd[0].revents & POLLIN) { /* read which sound to play from the pipe */ int i, what = -1; @@ -656,11 +650,13 @@ static void *sound_thread(void *arg) ast_log(LOG_WARNING, "invalid sound index: %d\n", what); } if (o->sounddev > -1) { - if (FD_ISSET(o->sounddev, &rfds)) /* read and ignore errors */ + if (pfd[1].revents & POLLIN) { /* read and ignore errors */ if (read(o->sounddev, ign, sizeof(ign)) < 0) { } - if (FD_ISSET(o->sounddev, &wfds)) + } + if (pfd[1].revents & POLLOUT) { send_sound(o); + } } } return NULL; /* Never reached */ diff --git a/channels/chan_phone.c b/channels/chan_phone.c index fd202844b..bd13c78bb 100644 --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -997,12 +997,12 @@ static void phone_check_exception(struct phone_pvt *i) static void *do_monitor(void *data) { - fd_set rfds, efds; - int n, res; + struct pollfd *fds = NULL; + int nfds = 0, inuse_fds = 0, res; struct phone_pvt *i; int tonepos = 0; /* The tone we're playing this round */ - struct timeval tv = {0,0}; + struct timeval tv = { 0, 0 }; int dotone; /* This thread monitors all the frame relay interfaces which are not yet in use (and thus do not have a separate thread) indefinitely */ @@ -1016,33 +1016,38 @@ static void *do_monitor(void *data) } /* Build the stuff we're going to select on, that is the socket of every phone_pvt that does not have an associated owner channel */ - n = -1; - FD_ZERO(&rfds); - FD_ZERO(&efds); i = iflist; dotone = 0; - while (i) { - if (FD_ISSET(i->fd, &rfds)) - ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->fd, i->dev); + inuse_fds = 0; + for (i = iflist; i; i = i->next) { if (!i->owner) { /* This needs to be watched, as it lacks an owner */ - FD_SET(i->fd, &rfds); - FD_SET(i->fd, &efds); - if (i->fd > n) - n = i->fd; + if (inuse_fds == nfds) { + void *tmp = ast_realloc(fds, (nfds + 1) * sizeof(*fds)); + if (!tmp) { + /* Avoid leaking */ + continue; + } + fds = tmp; + nfds++; + } + fds[inuse_fds].fd = i->fd; + fds[inuse_fds].events = POLLIN | POLLERR; + fds[inuse_fds].revents = 0; + inuse_fds++; + if (i->dialtone && i->mode != MODE_SIGMA) { /* Remember we're going to have to come back and play more dialtones */ if (ast_tvzero(tv)) { /* If we're due for a dialtone, play one */ - if (write(i->fd, DialTone + tonepos, 240) != 240) + if (write(i->fd, DialTone + tonepos, 240) != 240) { ast_log(LOG_WARNING, "Dial tone write error\n"); + } } dotone++; } } - - i = i->next; } /* Okay, now that we know what to do, release the interface lock */ ast_mutex_unlock(&iflock); @@ -1051,26 +1056,28 @@ static void *do_monitor(void *data) if (dotone && i && i->mode != MODE_SIGMA) { /* If we're ready to recycle the time, set it to 30 ms */ tonepos += 240; - if (tonepos >= sizeof(DialTone)) - tonepos = 0; + if (tonepos >= sizeof(DialTone)) { + tonepos = 0; + } if (ast_tvzero(tv)) { - tv = ast_tv(30000, 0); + tv = ast_tv(0, 30000); } - res = ast_select(n + 1, &rfds, NULL, &efds, &tv); + res = ast_poll2(fds, inuse_fds, &tv); } else { - res = ast_select(n + 1, &rfds, NULL, &efds, NULL); - tv = ast_tv(0,0); + res = ast_poll(fds, inuse_fds, -1); + tv = ast_tv(0, 0); tonepos = 0; } /* Okay, select has finished. Let's see what happened. */ if (res < 0) { - ast_log(LOG_DEBUG, "select return %d: %s\n", res, strerror(errno)); + ast_log(LOG_DEBUG, "poll returned %d: %s\n", res, strerror(errno)); continue; } /* If there are no fd's changed, just continue, it's probably time to play some more dialtones */ - if (!res) + if (!res) { continue; + } /* Alright, lock the interface list again, and let's look and see what has happened */ if (ast_mutex_lock(&iflock)) { @@ -1078,15 +1085,27 @@ static void *do_monitor(void *data) continue; } - i = iflist; - for(; i; i=i->next) { - if (FD_ISSET(i->fd, &rfds)) { + for (i = iflist; i; i = i->next) { + int j; + /* Find the record */ + for (j = 0; j < inuse_fds; j++) { + if (fds[j].fd == i->fd) { + break; + } + } + + /* Not found? */ + if (j == inuse_fds) { + continue; + } + + if (fds[j].revents & POLLIN) { if (i->owner) { continue; } phone_mini_packet(i); } - if (FD_ISSET(i->fd, &efds)) { + if (fds[j].revents & POLLERR) { if (i->owner) { continue; } @@ -1096,7 +1115,6 @@ static void *do_monitor(void *data) ast_mutex_unlock(&iflock); } return NULL; - } static int restart_monitor() diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 631da359d..9063d3fee 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -149,6 +149,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/compiler.h" #include "asterisk/threadstorage.h" #include "asterisk/translate.h" +#include "asterisk/astobj2.h" #ifndef FALSE #define FALSE 0 @@ -855,6 +856,12 @@ static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_ #define sipdebug_config ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG) #define sipdebug_console ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE) +/*! \brief provisional keep alive scheduler item data */ +struct provisional_keepalive_data { + struct sip_pvt *pvt; + int sched_id; +}; + /*! \brief T38 States for a call */ enum t38state { T38_DISABLED = 0, /*!< Not enabled */ @@ -1044,7 +1051,7 @@ static struct sip_pvt { struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */ int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ - int provisional_keepalive_sched_id; /*!< Scheduler ID for provisional responses that need to be sent out to avoid cancellation */ + struct provisional_keepalive_data *provisional_keepalive_data; /*!< Scheduler data for provisional responses that need to be sent out to avoid cancellation */ const char *last_provisional; /*!< The last successfully transmitted provisonal response message */ struct sip_pvt *next; /*!< Next dialog in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ @@ -1969,29 +1976,29 @@ static int retrans_pkt(const void *data) if (pkt->retrans < MAX_RETRANS) { pkt->retrans++; - if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ + if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); + ast_log(LOG_DEBUG, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); } else { - int siptimer_a; - - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); - if (!pkt->timer_a) - pkt->timer_a = 2 ; - else - pkt->timer_a = 2 * pkt->timer_a; - - /* For non-invites, a maximum of 4 secs */ - siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ - if (pkt->method != SIP_INVITE && siptimer_a > 4000) - siptimer_a = 4000; - - /* Reschedule re-transmit */ + int siptimer_a; + + if (sipdebug && option_debug > 3) + ast_log(LOG_DEBUG, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); + if (!pkt->timer_a) + pkt->timer_a = 2 ; + else + pkt->timer_a = 2 * pkt->timer_a; + + /* For non-invites, a maximum of 4 secs */ + siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ + if (pkt->method != SIP_INVITE && siptimer_a > 4000) + siptimer_a = 4000; + + /* Reschedule re-transmit */ reschedule = siptimer_a; - if (option_debug > 3) - ast_log(LOG_DEBUG, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); - } + if (option_debug > 3) + ast_log(LOG_DEBUG, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); + } if (sip_debug_test_pvt(pkt->owner)) { const struct sockaddr_in *dst = sip_real_dst(pkt->owner); @@ -2003,12 +2010,13 @@ static int retrans_pkt(const void *data) append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data); xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); - ast_mutex_unlock(&pkt->owner->lock); - if (xmitres == XMIT_ERROR) + if (xmitres == XMIT_ERROR) { ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid); - else + } else { + ast_mutex_unlock(&pkt->owner->lock); return reschedule; - } + } + } /* Too many retries */ if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) { if (ast_test_flag(pkt, FLAG_FATAL) || sipdebug) /* Tell us if it's critical or if we're debugging */ @@ -2021,7 +2029,7 @@ static int retrans_pkt(const void *data) append_history(pkt->owner, "XmitErr", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); } else append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); - + pkt->retransid = -1; if (ast_test_flag(pkt, FLAG_FATAL)) { @@ -2029,9 +2037,9 @@ static int retrans_pkt(const void *data) DEADLOCK_AVOIDANCE(&pkt->owner->lock); /* SIP_PVT, not channel */ } - if (pkt->owner->owner && !pkt->owner->owner->hangupcause) + if (pkt->owner->owner && !pkt->owner->owner->hangupcause) pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE; - + if (pkt->owner->owner) { sip_alreadygone(pkt->owner); ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see doc/sip-retransmit.txt).\n", pkt->owner->callid); @@ -2042,7 +2050,7 @@ static int retrans_pkt(const void *data) /* Let the peerpoke system expire packets when the timer expires for poke_noanswer */ if (pkt->method != SIP_OPTIONS) { - ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY); + ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY); sip_alreadygone(pkt->owner); if (option_debug) append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately"); @@ -2052,7 +2060,7 @@ static int retrans_pkt(const void *data) if (pkt->method == SIP_BYE) { /* We're not getting answers on SIP BYE's. Tear down the call anyway. */ - if (pkt->owner->owner) + if (pkt->owner->owner) ast_channel_unlock(pkt->owner->owner); append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway."); ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY); @@ -2166,10 +2174,6 @@ static int __sip_autodestruct(const void *data) return 10000; } - /* If we're destroying a subscription, dereference peer object too */ - if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) - ASTOBJ_UNREF(p->relatedpeer,sip_destroy_peer); - /* Reset schedule ID */ p->autokillid = -1; @@ -2335,44 +2339,124 @@ static void add_blank(struct sip_request *req) } } -static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp) +/*! \brief This is called by the scheduler to resend the last provisional message in a dialog */ +static int send_provisional_keepalive_full(struct provisional_keepalive_data *data, int with_sdp) { const char *msg = NULL; + int res = 0; + struct sip_pvt *pvt = data->pvt; - if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) { - msg = "183 Session Progress"; + if (!pvt) { + ao2_ref(data, -1); /* not rescheduling so drop ref. in this case the dialog has already dropped this ref */ + return res; } - if (pvt->invitestate < INV_COMPLETED) { - if (with_sdp) { - transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE); + ast_mutex_lock(&pvt->lock); + while (pvt->owner && ast_channel_trylock(pvt->owner)) { + ast_mutex_unlock(&pvt->lock); + sched_yield(); + if ((pvt = data->pvt)) { + ast_mutex_lock(&pvt->lock); } else { - transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq); + ao2_ref(data, -1); + return res; } - return PROVIS_KEEPALIVE_TIMEOUT; } - return 0; + if (data->sched_id == -1 || pvt->invitestate >= INV_COMPLETED) { + goto provisional_keepalive_cleanup; + } + + if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) { + msg = "183 Session Progress"; + } + + if (with_sdp) { + transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE); + } else { + transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq); + } + + res = PROVIS_KEEPALIVE_TIMEOUT; /* reschedule the keepalive event */ + +provisional_keepalive_cleanup: + if (!res) { /* not rescheduling, so drop ref */ + data->sched_id = -1; /* if we don't re-schedule, make sure to remove the sched id */ + ao2_ref(data, -1); /* release the scheduler's reference to this data */ + } + + if (pvt->owner) { + ast_channel_unlock(pvt->owner); + } + ast_mutex_unlock(&pvt->lock); + + return res; +} + +static int send_provisional_keepalive(const void *data) +{ + struct provisional_keepalive_data *d = (struct provisional_keepalive_data *) data; + + return send_provisional_keepalive_full(d, 0); } -static int send_provisional_keepalive(const void *data) { - struct sip_pvt *pvt = (struct sip_pvt *) data; +static int send_provisional_keepalive_with_sdp(const void *data) +{ + struct provisional_keepalive_data *d = (struct provisional_keepalive_data *) data; - return send_provisional_keepalive_full(pvt, 0); + return send_provisional_keepalive_full(d, 1); } -static int send_provisional_keepalive_with_sdp(const void *data) { - struct sip_pvt *pvt = (void *)data; +static void *unref_provisional_keepalive(struct provisional_keepalive_data *data) +{ + if (data) { + data->sched_id = -1; + data->pvt = NULL; + ao2_ref(data, -1); + } + return NULL; +} - return send_provisional_keepalive_full(pvt, 1); +static void remove_provisional_keepalive_sched(struct sip_pvt *pvt) +{ + int res; + if (!pvt->provisional_keepalive_data) { + return; + } + res = AST_SCHED_DEL(sched, pvt->provisional_keepalive_data->sched_id); + /* If we could not remove this item. remove pvt's reference this data and mark it for removal + * for the next time the scheduler uses it. The scheduler has it's own ref to this data + * and will detect it should not reschedule the event since the sched_id is -1 and pvt == NULL */ + if (res == -1) { + pvt->provisional_keepalive_data = unref_provisional_keepalive(pvt->provisional_keepalive_data); + } } static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp) { - AST_SCHED_DEL(sched, pvt->provisional_keepalive_sched_id); + remove_provisional_keepalive_sched(pvt); + + if (!pvt->provisional_keepalive_data) { + if (!(pvt->provisional_keepalive_data = ao2_alloc(sizeof(*pvt->provisional_keepalive_data), NULL))) { + return; /* alloc error, can not recover */ + } + pvt->provisional_keepalive_data->sched_id = -1; + pvt->provisional_keepalive_data->pvt = pvt; + } + + /* give the scheduler a ref */ + ao2_ref(pvt->provisional_keepalive_data, +1); + + /* schedule the provisional keepalive */ + pvt->provisional_keepalive_data->sched_id = ast_sched_add(sched, + PROVIS_KEEPALIVE_TIMEOUT, + with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, + pvt->provisional_keepalive_data); - pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT, - with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, pvt); + /* if schedule was unsuccessful, remove the scheduler's ref */ + if (pvt->provisional_keepalive_data->sched_id == -1) { + ao2_ref(pvt->provisional_keepalive_data, -1); + } } /*! \brief Transmit response on SIP request*/ @@ -2399,7 +2483,7 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty /* If we are sending a final response to an INVITE, stop retransmitting provisional responses */ if (p->initreq.method == SIP_INVITE && reliable == XMIT_CRITICAL) { - AST_SCHED_DEL(sched, p->provisional_keepalive_sched_id); + remove_provisional_keepalive_sched(p); } res = (reliable) ? @@ -3297,7 +3381,13 @@ static int __sip_destroy(struct sip_pvt *p, int lockowner) AST_SCHED_DEL(sched, p->waitid); AST_SCHED_DEL(sched, p->autokillid); AST_SCHED_DEL(sched, p->request_queue_sched_id); - AST_SCHED_DEL(sched, p->provisional_keepalive_sched_id); + + remove_provisional_keepalive_sched(p); + if (p->provisional_keepalive_data) { + ast_mutex_lock(&p->lock); + p->provisional_keepalive_data = unref_provisional_keepalive(p->provisional_keepalive_data); + ast_mutex_unlock(&p->lock); + } if (p->rtp) { ast_rtp_destroy(p->rtp); @@ -3765,7 +3855,6 @@ static int sip_hangup(struct ast_channel *ast) if (p->invitestate == INV_CALLING) { /* We can't send anything in CALLING state */ ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - __sip_pretend_ack(p); /* Do we need a timer here if we don't hear from them at all? Yes we do or else we will get hung dialogs and those are no fun. */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); append_history(p, "DELAY", "Not sending cancel, waiting for timeout"); @@ -3783,7 +3872,7 @@ static int sip_hangup(struct ast_channel *ast) } } else { /* Incoming call, not up */ const char *res; - AST_SCHED_DEL(sched, p->provisional_keepalive_sched_id); + remove_provisional_keepalive_sched(p); if (p->hangupcause && (res = hangup_cause2sip(p->hangupcause))) transmit_response_reliable(p, res, &p->initreq); else @@ -4681,7 +4770,6 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si p->waitid = -1; p->autokillid = -1; p->request_queue_sched_id = -1; - p->provisional_keepalive_sched_id = -1; p->subscribed = NONE; p->stateid = -1; p->prefs = default_prefs; /* Set default codecs for this call */ @@ -12799,7 +12887,11 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) } } -/*! \brief Check pending actions on SIP call */ +/*! \brief Check pending actions on SIP call + * + * \note both sip_pvt and sip_pvt's owner channel (if present) + * must be locked for this function. + */ static void check_pendings(struct sip_pvt *p) { if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { @@ -12814,6 +12906,9 @@ static void check_pendings(struct sip_pvt *p) if (p->pendinginvite) return; + if (p->owner) { + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + } /* Perhaps there is an SD change INVITE outstanding */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE); } @@ -12841,12 +12936,22 @@ static void check_pendings(struct sip_pvt *p) static int sip_reinvite_retry(const void *data) { struct sip_pvt *p = (struct sip_pvt *) data; + struct ast_channel *owner; ast_mutex_lock(&p->lock); /* called from schedule thread which requires a lock */ + while ((owner = p->owner) && ast_channel_trylock(owner)) { + ast_mutex_unlock(&p->lock); + usleep(1); + ast_mutex_lock(&p->lock); + } ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); p->waitid = -1; check_pendings(p); ast_mutex_unlock(&p->lock); + if (owner) { + ast_channel_unlock(owner); + } + return 0; } @@ -16047,7 +16152,6 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, int firststate = AST_EXTENSION_REMOVED; struct sip_peer *authpeer = NULL; const char *eventheader = get_header(req, "Event"); /* Get Event package name */ - const char *accept = get_header(req, "Accept"); int resubscribe = (p->subscribed != NONE); char *temp, *event; @@ -16174,56 +16278,96 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */ unsigned int pidf_xml; + const char *accept; + int start = 0; + enum subscriptiontype subscribed = NONE; + const char *unknown_acceptheader = NULL; if (authpeer) /* No need for authpeer here */ ASTOBJ_UNREF(authpeer, sip_destroy_peer); /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ + accept = __get_header(req, "Accept", &start); + while ((subscribed == NONE) && !ast_strlen_zero(accept)) { + pidf_xml = strstr(accept, "application/pidf+xml") ? 1 : 0; + + /* Older versions of Polycom firmware will claim pidf+xml, but really + * they only support xpidf+xml. */ + if (pidf_xml && strstr(p->useragent, "Polycom")) { + subscribed = XPIDF_XML; + } else if (pidf_xml) { + subscribed = PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/dialog-info+xml")) { + subscribed = DIALOG_INFO_XML; + /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ + } else if (strstr(accept, "application/cpim-pidf+xml")) { + subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ + } else if (strstr(accept, "application/xpidf+xml")) { + subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ + } else { + unknown_acceptheader = accept; + } + /* check to see if there is another Accept header present */ + accept = __get_header(req, "Accept", &start); + } - pidf_xml = strstr(accept, "application/pidf+xml") ? 1 : 0; - - /* Older versions of Polycom firmware will claim pidf+xml, but really - * they only support xpidf+xml. */ - if (pidf_xml && strstr(p->useragent, "Polycom")) { - p->subscribed = XPIDF_XML; - } else if (pidf_xml) { - p->subscribed = PIDF_XML; /* RFC 3863 format */ - } else if (strstr(accept, "application/dialog-info+xml")) { - p->subscribed = DIALOG_INFO_XML; - /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ - } else if (strstr(accept, "application/cpim-pidf+xml")) { - p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ - } else if (strstr(accept, "application/xpidf+xml")) { - p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ - } else if (ast_strlen_zero(accept)) { + if (!start) { if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */ transmit_response(p, "489 Bad Event", req); - - ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: " + "stateid: %d, laststate: %d, dialogver: %d, subscribecont: " + "'%s', subscribeuri: '%s'\n", + p->stateid, + p->laststate, + p->dialogver, + p->subscribecontext, + p->subscribeuri); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); return 0; } /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least. so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */ - } else { + } else if (subscribed == NONE) { /* Can't find a format for events that we know about */ char mybuf[200]; - snprintf(mybuf,sizeof(mybuf),"489 Bad Event (format %s)", accept); + if (!ast_strlen_zero(unknown_acceptheader)) { + snprintf(mybuf, sizeof(mybuf), "489 Bad Event (format %s)", unknown_acceptheader); + } else { + snprintf(mybuf, sizeof(mybuf), "489 Bad Event"); + } transmit_response(p, mybuf, req); - - ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format: '%s' pvt: subscribed: %d, stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - accept, (int)p->subscribed, p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format:" + "'%s' pvt: subscribed: %d, stateid: %d, laststate: %d," + "dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", + unknown_acceptheader, + (int)p->subscribed, + p->stateid, + p->laststate, + p->dialogver, + p->subscribecontext, + p->subscribeuri); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); return 0; + } else { + p->subscribed = subscribed; + } + } else if (!strcmp(event, "message-summary")) { + int start = 0; + int found_supported = 0; + const char *acceptheader; + + acceptheader = __get_header(req, "Accept", &start); + while (!found_supported && !ast_strlen_zero(acceptheader)) { + found_supported = strcmp(acceptheader, "application/simple-message-summary") ? 0 : 1; + if (!found_supported && (option_debug > 2)) { + ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader); + } + acceptheader = __get_header(req, "Accept", &start); } - } else if (!strcmp(event, "message-summary")) { - if (!ast_strlen_zero(accept) && strcmp(accept, "application/simple-message-summary")) { + if (start && !found_supported) { /* Format requested that we do not support */ transmit_response(p, "406 Not Acceptable", req); - if (option_debug > 1) - ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", accept); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); + ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); if (authpeer) /* No need for authpeer here */ ASTOBJ_UNREF(authpeer, sip_destroy_peer); return 0; @@ -17102,7 +17246,7 @@ static int restart_monitor(void) static int sip_poke_noanswer(const void *data) { struct sip_peer *peer = (struct sip_peer *)data; - + peer->pokeexpire = -1; if (peer->lastms > -1) { ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms); @@ -17114,8 +17258,12 @@ static int sip_poke_noanswer(const void *data) if (peer->call) sip_destroy(peer->call); peer->call = NULL; - peer->lastms = -1; - ast_device_state_changed("SIP/%s", peer->name); + + /* Don't send a devstate change if nothing changed. */ + if (peer->lastms > -1) { + peer->lastms = -1; + ast_device_state_changed("SIP/%s", peer->name); + } /* This function gets called one place outside of the scheduler ... */ if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { @@ -18824,6 +18972,8 @@ static int reload_config(enum channelreloadreason reason) ast_log(LOG_WARNING, "Unable to set SIP TOS to %s\n", ast_tos2str(global_tos_sip)); } } + } else if (setsockopt(sipsock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) { + ast_log(LOG_WARNING, "Unable to set SIP TOS to %s\n", ast_tos2str(global_tos_sip)); } ast_mutex_unlock(&netlock); -- cgit v1.2.3