diff options
author | tilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-01-11 15:52:31 +0000 |
---|---|---|
committer | tilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-01-11 15:52:31 +0000 |
commit | 23748da29ef7de0d00c49a4d2fb00496e2317a6e (patch) | |
tree | 57a6565befa69fbac5f787ab26105c458d4e98e8 /channels | |
parent | 83a7a217a2600f21b23c1cf8ba4f0858222cbccc (diff) |
Back out changes from revision 97077, since it wasn't perfect
git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@98164 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 236 |
1 files changed, 89 insertions, 147 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index dce4f799d..92c138910 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -150,7 +150,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/threadstorage.h" #include "asterisk/translate.h" #include "asterisk/dnsmgr.h" -#include "asterisk/astobj2.h" #ifndef FALSE #define FALSE 0 @@ -1008,7 +1007,7 @@ static struct sip_pvt { struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ struct ast_rtp *rtp; /*!< RTP Session */ struct ast_rtp *vrtp; /*!< Video RTP session */ - struct ao2_container *packets; /*!< Packets scheduled for re-transmission */ + struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ struct sip_history_head *history; /*!< History of this SIP dialog */ size_t history_entries; /*!< Number of entires in the history */ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ @@ -1025,6 +1024,7 @@ static struct sip_pvt { /*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission */ struct sip_pkt { + struct sip_pkt *next; /*!< Next packet in linked list */ int retrans; /*!< Retransmission number */ int method; /*!< SIP method for this packet */ int seqno; /*!< Sequence number */ @@ -1852,7 +1852,7 @@ static void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap) if (!(hist = ast_calloc(1, sizeof(*hist) + l))) return; if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) { - ast_free(hist); + free(hist); return; } memcpy(hist->event, buf, l); @@ -1889,15 +1889,12 @@ static void append_history_full(struct sip_pvt *p, const char *fmt, ...) /*! \brief Retransmit SIP message if no answer (Called from scheduler) */ static int retrans_pkt(const void *data) { - struct sip_pkt *pkt = (struct sip_pkt *)data, *prev; + struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL; int reschedule = DEFAULT_RETRANS; int xmitres = 0; - ao2_ref(pkt, 1); /* Make sure this cannot go away while we're using it */ - /* Lock channel PVT */ - if (pkt->owner) - ast_mutex_lock(&pkt->owner->lock); + ast_mutex_lock(&pkt->owner->lock); if (pkt->retrans < MAX_RETRANS) { pkt->retrans++; @@ -1925,7 +1922,7 @@ static int retrans_pkt(const void *data) 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 (pkt->owner && sip_debug_test_pvt(pkt->owner)) { + if (sip_debug_test_pvt(pkt->owner)) { const struct sockaddr_in *dst = sip_real_dst(pkt->owner); ast_verbose("Retransmitting #%d (%s) to %s:%d:\n%s\n---\n", pkt->retrans, sip_nat_mode(pkt->owner), @@ -1935,43 +1932,38 @@ 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); - if (pkt->owner) - ast_mutex_unlock(&pkt->owner->lock); + ast_mutex_unlock(&pkt->owner->lock); if (xmitres == XMIT_ERROR) - ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner ? pkt->owner->callid : "<unknown>"); - else { - ao2_ref(pkt, -1); + ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid); + else 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 */ ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s)\n", pkt->owner->callid, pkt->seqno, (ast_test_flag(pkt, FLAG_FATAL)) ? "Critical" : "Non-critical", (ast_test_flag(pkt, FLAG_RESPONSE)) ? "Response" : "Request"); - } else if (pkt->owner && (pkt->method == SIP_OPTIONS) && sipdebug) { - ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid); + } else if ((pkt->method == SIP_OPTIONS) && sipdebug) { + ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid); } - if (pkt->owner) { - if (xmitres == XMIT_ERROR) { - ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission of transaction in call id %s \n", pkt->owner->callid); - 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)"); - } + if (xmitres == XMIT_ERROR) { + ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission of transaction in call id %s \n", pkt->owner->callid); + 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)) { - while (pkt->owner && pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) { + while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) { ast_mutex_unlock(&pkt->owner->lock); /* SIP_PVT, not channel */ usleep(1); - if (pkt->owner) - ast_mutex_lock(&pkt->owner->lock); + ast_mutex_lock(&pkt->owner->lock); } - if (pkt->owner && 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 && pkt->owner->owner) { + if (pkt->owner->owner) { sip_alreadygone(pkt->owner); ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid); ast_queue_hangup(pkt->owner->owner); @@ -1980,7 +1972,7 @@ static int retrans_pkt(const void *data) /* If no channel owner, destroy now */ /* Let the peerpoke system expire packets when the timer expires for poke_noanswer */ - if (pkt->owner && pkt->method != SIP_OPTIONS) { + if (pkt->method != SIP_OPTIONS) { ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY); sip_alreadygone(pkt->owner); if (option_debug) @@ -1989,7 +1981,7 @@ static int retrans_pkt(const void *data) } } - if (pkt->owner && pkt->method == SIP_BYE) { + if (pkt->method == SIP_BYE) { /* We're not getting answers on SIP BYE's. Tear down the call anyway. */ if (pkt->owner->owner) ast_channel_unlock(pkt->owner->owner); @@ -1998,23 +1990,23 @@ static int retrans_pkt(const void *data) } /* In any case, go ahead and remove the packet */ - if (pkt->owner && (prev = ao2_find(pkt->owner->packets, pkt, OBJ_UNLINK | OBJ_POINTER))) { - /* Destroy the container's reference (inherited) */ - ao2_ref(prev, -1); + for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) { + if (cur == pkt) + break; + } + if (cur) { + if (prev) + prev->next = cur->next; + else + pkt->owner->packets = cur->next; ast_mutex_unlock(&pkt->owner->lock); - /* Now destroy our initial reference */ - ao2_ref(pkt, -1); - /* And destroy the sched ref */ - ao2_ref(pkt, -1); - return 0; - } else { + free(cur); + pkt = NULL; + } else ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n"); - if (pkt->owner) - ast_mutex_unlock(&pkt->owner->lock); - ao2_ref(pkt, -1); /* Initial ref */ - ao2_ref(pkt, -1); /* Sched ref */ - return 0; - } + if (pkt) + ast_mutex_unlock(&pkt->owner->lock); + return 0; } /*! \brief Transmit packet with retransmits @@ -2026,11 +2018,12 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res int siptimer_a = DEFAULT_RETRANS; int xmitres = 0; - if (!(pkt = ao2_alloc(sizeof(*pkt) + len + 1, ast_free))) + if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1))) return AST_FAILURE; memcpy(pkt->data, data, len); pkt->method = sipmethod; pkt->packetlen = len; + pkt->next = p->packets; pkt->owner = p; pkt->seqno = seqno; if (resp) @@ -2042,9 +2035,12 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res if (pkt->timer_t1) siptimer_a = pkt->timer_t1 * 2; + /* Schedule retransmission */ + pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1); if (option_debug > 3 && sipdebug) ast_log(LOG_DEBUG, "*** SIP TIMER: Initializing retransmit timer on packet: Id #%d\n", pkt->retransid); - + pkt->next = p->packets; + p->packets = pkt; if (sipmethod == SIP_INVITE) { /* Note this is a pending invite */ p->pendinginvite = seqno; @@ -2054,25 +2050,11 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */ append_history(pkt->owner, "XmitErr", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); + ast_sched_del(sched, pkt->retransid); /* No more retransmission */ pkt->retransid = -1; - ao2_ref(pkt, -1); /* and deallocate */ return AST_FAILURE; - } else { - /* Add refcount for scheduler pointer */ - ao2_ref(pkt, 1); - /* Schedule retransmission */ - pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1); - /* Link into the list of packets */ - ao2_link(p->packets, pkt); + } else return AST_SUCCESS; - } -} - -static int __deref_ao2_owner_cb(void *obj, void *unused, int flags) -{ - struct sip_pkt *pkt = obj; - pkt->owner = NULL; - return 0; } /*! \brief Kill a SIP dialog (called by scheduler) */ @@ -2086,15 +2068,16 @@ static int __sip_autodestruct(const void *data) p->subscribed = NONE; append_history(p, "Subscribestatus", "timeout"); if (option_debug > 2) - ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>"); + ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP subsription %s\n", p->callid ? p->callid : "<unknown>"); return 10000; /* Reschedule this destruction so that we know that it's gone */ } - /* If there are packets still waiting for delivery, make sure they can't callback to us anymore. */ - if (ao2_container_count(p->packets)) { - ast_mutex_lock(&p->lock); - ao2_callback(p->packets, 0, __deref_ao2_owner_cb, NULL); - ast_mutex_unlock(&p->lock); + /* If there are packets still waiting for delivery, delay the destruction */ + if (p->packets) { + if (option_debug > 2) + ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>"); + append_history(p, "ReliableXmit", "timeout"); + return 10000; } /* If we're destroying a subscription, dereference peer object too */ @@ -2151,8 +2134,7 @@ static void sip_cancel_destroy(struct sip_pvt *p) /*! \brief Acknowledges receipt of a packet and stops retransmission */ static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod) { - struct sip_pkt *cur; - struct ao2_iterator ao2i; + struct sip_pkt *cur, *prev = NULL; /* Just in case... */ char *msg; @@ -2161,8 +2143,7 @@ static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod) msg = sip_methods[sipmethod].text; ast_mutex_lock(&p->lock); - ao2i = ao2_iterator_init(p->packets, 0); - while ((cur = ao2_iterator_next(&ao2i))) { + for (cur = p->packets; cur; prev = cur, cur = cur->next) { if ((cur->seqno == seqno) && ((ast_test_flag(cur, FLAG_RESPONSE)) == resp) && ((ast_test_flag(cur, FLAG_RESPONSE)) || (!strncasecmp(msg, cur->data, strlen(msg)) && (cur->data[strlen(msg)] < 33)))) { @@ -2173,68 +2154,59 @@ static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod) } /* this is our baby */ res = TRUE; + UNLINK(cur, p->packets, prev); if (cur->retransid > -1) { if (sipdebug && option_debug > 3) ast_log(LOG_DEBUG, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid); - if (!ast_sched_del(sched, cur->retransid)) - ao2_ref(cur, -1); /* scheduler deref */ + ast_sched_del(sched, cur->retransid); cur->retransid = -1; } - - /* Remove it from the list */ - ao2_unlink(p->packets, cur); - ao2_ref(cur, -1); /* iterator deref */ + free(cur); break; } - - ao2_ref(cur, -1); /* iterator deref */ } ast_mutex_unlock(&p->lock); if (option_debug) ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: Match %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found"); } -static int __sip_pretend_ack_cb(void *obj, void *vp, int flags) -{ - struct sip_pvt *p = vp; - struct sip_pkt *pkt = obj; - __sip_ack(p, pkt->seqno, ast_test_flag(pkt, FLAG_RESPONSE), pkt->method ? pkt->method : find_sip_method(pkt->data)); - return 0; -} - -/*! \brief Pretend to ack all packets */ +/*! \brief Pretend to ack all packets + * maybe the lock on p is not strictly necessary but there might be a race */ static void __sip_pretend_ack(struct sip_pvt *p) { - ao2_callback(p->packets, 0, __sip_pretend_ack_cb, p); + struct sip_pkt *cur = NULL; + + while (p->packets) { + int method; + if (cur == p->packets) { + ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text); + return; + } + cur = p->packets; + method = (cur->method) ? cur->method : find_sip_method(cur->data); + __sip_ack(p, cur->seqno, ast_test_flag(cur, FLAG_RESPONSE), method); + } } /*! \brief Acks receipt of packet, keep it around (used for provisional responses) */ static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod) { - struct sip_pkt *cur, *found; + struct sip_pkt *cur; int res = -1; - struct ao2_iterator ao2i; - ao2i = ao2_iterator_init(p->packets, 0); - while ((cur = ao2_iterator_next(&ao2i))) { + for (cur = p->packets; cur; cur = cur->next) { if (cur->seqno == seqno && ast_test_flag(cur, FLAG_RESPONSE) == resp && (ast_test_flag(cur, FLAG_RESPONSE) || method_match(sipmethod, cur->data))) { /* this is our baby */ if (cur->retransid > -1) { if (option_debug > 3 && sipdebug) ast_log(LOG_DEBUG, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text); - if (!ast_sched_del(sched, cur->retransid)) - ao2_ref(cur, -1); /* scheduler deref */ + ast_sched_del(sched, cur->retransid); cur->retransid = -1; } res = 0; - /* Now remove it from the packet list. */ - if ((found = ao2_find(p->packets, cur, OBJ_UNLINK | OBJ_POINTER))) - ao2_ref(found, -1); /* container item deref */ - ao2_ref(cur, -1); /* iterator deref */ break; } - ao2_ref(cur, -1); /* iterator deref */ } if (option_debug) ast_log(LOG_DEBUG, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found"); @@ -3080,21 +3052,11 @@ static void sip_registry_destroy(struct sip_registry *reg) } -static int __sip_destroy_packet_cb(void *obj, void *unused, int flags) -{ - struct sip_pkt *pkt = obj; - if (pkt->retransid > -1) { - if (!ast_sched_del(sched, pkt->retransid)) - ao2_ref(pkt, -1); /* scheduler deref */ - } - pkt->owner = NULL; - return 0; -} - /*! \brief Execute destruction of SIP dialog structure, release memory */ static void __sip_destroy(struct sip_pvt *p, int lockowner) { struct sip_pvt *cur, *prev = NULL; + struct sip_pkt *cp; if (sip_debug_test_pvt(p) || option_debug > 2) ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text); @@ -3113,7 +3075,7 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) sip_dump_history(p); if (p->options) - ast_free(p->options); + free(p->options); if (p->stateid > -1) ast_extension_state_del(p->stateid, NULL); @@ -3131,7 +3093,7 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) if (p->udptl) ast_udptl_destroy(p->udptl); if (p->refer) - ast_free(p->refer); + free(p->refer); if (p->route) { free_old_route(p->route); p->route = NULL; @@ -3156,7 +3118,7 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) if (p->history) { struct sip_history *hist; while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) { - ast_free(hist); + free(hist); p->history_entries--; } free(p->history); @@ -3175,8 +3137,12 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) } /* remove all current packets in this dialog */ - ao2_callback(p->packets, 0, __sip_destroy_packet_cb, NULL); - + while((cp = p->packets)) { + p->packets = p->packets->next; + if (cp->retransid > -1) + ast_sched_del(sched, cp->retransid); + free(cp); + } if (p->chanvars) { ast_variables_destroy(p->chanvars); p->chanvars = NULL; @@ -3185,7 +3151,7 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) ast_string_field_free_memory(p); - ast_free(p); + free(p); } /*! \brief update_call_counter: Handle call_limit for SIP users @@ -4425,40 +4391,17 @@ static void make_our_tag(char *tagbuf, size_t len) snprintf(tagbuf, len, "as%08lx", ast_random()); } -static int packet_hash_fn(const void *obj, const int flags) -{ - const struct sip_pkt *pkt = obj; - return pkt->seqno; -} - -static int packet_cmp_fn(void *obj1, void *obj2, int flags) -{ - struct sip_pkt *p1 = obj1, *p2 = obj2; - - if (flags & OBJ_POINTER) - return p1 == p2 ? CMP_MATCH : 0; - else - return p1->seqno == p2->seqno ? CMP_MATCH : 0; -} - /*! \brief Allocate SIP_PVT structure and set defaults */ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method) { struct sip_pvt *p; - struct ao2_container *aoc; - if (!(aoc = ao2_container_alloc(37, packet_hash_fn, packet_cmp_fn))) + if (!(p = ast_calloc(1, sizeof(*p)))) return NULL; - if (!(p = ast_calloc(1, sizeof(*p)))) { - ao2_ref(aoc, -1); - return NULL; - } - if (ast_string_field_init(p, 512)) { - ao2_ref(aoc, -1); - ast_free(p); + free(p); return NULL; } @@ -4471,7 +4414,6 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si p->subscribed = NONE; p->stateid = -1; p->prefs = default_prefs; /* Set default codecs for this call */ - p->packets = aoc; if (intended_method != SIP_OPTIONS) /* Peerpoke has it's own system */ p->timer_t1 = 500; /* Default SIP retransmission timer T1 (RFC 3261) */ @@ -15626,7 +15568,7 @@ restartsearch: } } /* If we have sessions that needs to be destroyed, do it now */ - if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !ao2_container_count(sip->packets) && + if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets && !sip->owner) { ast_mutex_unlock(&sip->lock); __sip_destroy(sip, 1); |