Skip to content
Snippets Groups Projects
chan_sip.c 1.06 MiB
Newer Older
  • Learn to ignore specific revisions
  • 			ast_free(cp->data);
    
    	AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
    
    	AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
    	
    
    	if (dialog->autokillid > -1) {
    
    		AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
    
    	if (dialog->request_queue_sched_id > -1) {
    		AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr"));
    
    	AST_SCHED_DEL_UNREF(sched, dialog->provisional_keepalive_sched_id, dialog_unref(dialog, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
    
    	if (dialog->t38id > -1) {
    		AST_SCHED_DEL_UNREF(sched, dialog->t38id, dialog_unref(dialog, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
    
    	if (dialog->stimer) {
    		stop_session_timer(dialog);
    	}
    
    
    	dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
    
    void *registry_unref(struct sip_registry *reg, char *tag)
    
    	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount - 1);
    	ASTOBJ_UNREF(reg, sip_registry_destroy);
    	return NULL;
    }
    
    /*! \brief Add object reference to SIP registry */
    static struct sip_registry *registry_addref(struct sip_registry *reg, char *tag)
    {
    	ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount + 1);
    	return ASTOBJ_REF(reg);	/* Add pointer to registry in packet */
    }
    
    /*! \brief Interface structure with callbacks used to connect to UDPTL module*/
    static struct ast_udptl_protocol sip_udptl = {
    	type: "SIP",
    	get_udptl_info: sip_get_udptl_peer,
    	set_udptl_peer: sip_set_udptl_peer,
    };
    
    static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
    	__attribute__((format(printf, 2, 3)));
    
    /*! \brief Convert transfer status to string */
    static const char *referstatus2str(enum referstatus rstatus)
    {
    	return map_x_s(referstatusstrings, rstatus, "");
    }
    
    static inline void pvt_set_needdestroy(struct sip_pvt *pvt, const char *reason)
    {
    
    	if (pvt->final_destruction_scheduled) {
    		return; /* This is already scheduled for final destruction, let the scheduler take care of it. */
    	}
    
    	append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason);
    	if (!pvt->needdestroy) {
    		pvt->needdestroy = 1;
    
    		ao2_t_link(dialogs_needdestroy, pvt, "link pvt into dialogs_needdestroy container");
    	}
    
    /*! \brief Initialize the initital request packet in the pvt structure.
    
    	This packet is used for creating replies and future requests in
    
    	a dialog */
    static void initialize_initreq(struct sip_pvt *p, struct sip_request *req)
    
    	if (p->initreq.headers) {
    
    		ast_debug(1, "Initializing already initialized SIP dialog %s (presumably reinvite)\n", p->callid);
    
    		ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
    
    	/* Use this as the basis */
    	copy_request(&p->initreq, req);
    	parse_request(&p->initreq);
    
    	if (req->debug) {
    
    		ast_verbose("Initreq: %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
    
    /*! \brief Encapsulate setting of SIP_ALREADYGONE to be able to trace it with debugging */
    static void sip_alreadygone(struct sip_pvt *dialog)
    {
    	ast_debug(3, "Setting SIP_ALREADYGONE on dialog %s\n", dialog->callid);
    	dialog->alreadygone = 1;
    }
    
    /*! Resolve DNS srv name or host name in a sip_proxy structure */
    static int proxy_update(struct sip_proxy *proxy)
    {
    	/* if it's actually an IP address and not a name,
               there's no need for a managed lookup */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	if (!ast_sockaddr_parse(&proxy->ip, proxy->name, 0)) {
    
    		/* Ok, not an IP address, then let's check if it's a domain or host */
    		/* XXX Todo - if we have proxy port, don't do SRV */
    
    Mark Michelson's avatar
    Mark Michelson committed
    		proxy->ip.ss.ss_family = get_address_family_filter(&bindaddr); /* Filter address family */
    
    		if (ast_get_ip_or_srv(&proxy->ip, proxy->name, sip_cfg.srvlookup ? "_sip._udp" : NULL) < 0) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    				ast_log(LOG_WARNING, "Unable to locate host '%s'\n", proxy->name);
    				return FALSE;
    
    Mark Michelson's avatar
    Mark Michelson committed
    
    	ast_sockaddr_set_port(&proxy->ip, proxy->port);
    
    
    	proxy->last_dnsupdate = time(NULL);
    	return TRUE;
    }
    
    /*! \brief converts ascii port to int representation. If no
     *  pt buffer is provided or the pt has errors when being converted
     *  to an int value, the port provided as the standard is used.
     */
    unsigned int port_str2int(const char *pt, unsigned int standard)
    {
    	int port = standard;
    	if (ast_strlen_zero(pt) || (sscanf(pt, "%30d", &port) != 1) || (port < 1) || (port > 65535)) {
    		port = standard;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    /*! \brief Get default outbound proxy or global proxy */
    static struct sip_proxy *obproxy_get(struct sip_pvt *dialog, struct sip_peer *peer)
    {
    	if (peer && peer->outboundproxy) {
    
    			ast_debug(1, "OBPROXY: Applying peer OBproxy to this call\n");
    
    		append_history(dialog, "OBproxy", "Using peer obproxy %s", peer->outboundproxy->name);
    		return peer->outboundproxy;
    	}
    	if (sip_cfg.outboundproxy.name[0]) {
    
    			ast_debug(1, "OBPROXY: Applying global OBproxy to this call\n");
    
    		append_history(dialog, "OBproxy", "Using global obproxy %s", sip_cfg.outboundproxy.name);
    		return &sip_cfg.outboundproxy;
    	}
    
    		ast_debug(1, "OBPROXY: Not applying OBproxy to this call\n");
    
    /*! \brief returns true if 'name' (with optional trailing whitespace)
     * matches the sip method 'id'.
     * Strictly speaking, SIP methods are case SENSITIVE, but we do
     * a case-insensitive comparison to be more tolerant.
     * following Jon Postel's rule: Be gentle in what you accept, strict with what you send
    
    static int method_match(enum sipmethod id, const char *name)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int len = strlen(sip_methods[id].text);
    	int l_name = name ? strlen(name) : 0;
    	/* true if the string is long enough, and ends with whitespace, and matches */
    	return (l_name >= len && name[len] < 33 &&
    		!strncasecmp(sip_methods[id].text, name, len));
    }
    
    /*! \brief  find_sip_method: Find SIP method from header */
    static int find_sip_method(const char *msg)
    {
    	int i, res = 0;
    	
    
    	if (ast_strlen_zero(msg)) {
    
    	for (i = 1; i < ARRAY_LEN(sip_methods) && !res; i++) {
    
    		if (method_match(i, msg)) {
    
    			res = sip_methods[i].id;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    /*! \brief See if we pass debug IP filter */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static inline int sip_debug_test_addr(const struct ast_sockaddr *addr)
    
    	/* Can't debug if sipdebug is not enabled */
    	if (!sipdebug) {
    		return 0;
    	}
    
    	/* A null debug_addr means we'll debug any address */
    	if (ast_sockaddr_isnull(&debugaddr)) {
    		return 1;
    	}
    
    	/* If no port was specified for a debug address, just compare the
    	 * addresses, otherwise compare the address and port
    	 */
    	if (ast_sockaddr_port(&debugaddr)) {
    		return !ast_sockaddr_cmp(&debugaddr, addr);
    	} else {
    		return !ast_sockaddr_cmp_addr(&debugaddr, addr);
    	}
    
    /*! \brief The real destination address for a write */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static const struct ast_sockaddr *sip_real_dst(const struct sip_pvt *p)
    
    	if (p->outboundproxy) {
    
    		return &p->outboundproxy->ip;
    
    
    	return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT) ? &p->recv : &p->sa;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    /*! \brief Display SIP nat mode */
    static const char *sip_nat_mode(const struct sip_pvt *p)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) ? "NAT" : "no NAT";
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \brief Test PVT for debugging output */
    static inline int sip_debug_test_pvt(struct sip_pvt *p)
    
    	if (!sipdebug) {
    
    	return sip_debug_test_addr(sip_real_dst(p));
    
    /*! \brief Return int representing a bit field of transport types found in const char *transport */
    static int get_transport_str2enum(const char *transport)
    
    	if (ast_strlen_zero(transport)) {
    		return res;
    
    	if (!strcasecmp(transport, "udp")) {
    		res |= SIP_TRANSPORT_UDP;
    	}
    	if (!strcasecmp(transport, "tcp")) {
    		res |= SIP_TRANSPORT_TCP;
    	}
    	if (!strcasecmp(transport, "tls")) {
    		res |= SIP_TRANSPORT_TLS;
    
    /*! \brief Return configuration of transports for a device */
    static inline const char *get_transport_list(unsigned int transports) {
    	switch (transports) {
    		case SIP_TRANSPORT_UDP:
    			return "UDP";
    		case SIP_TRANSPORT_TCP:
    			return "TCP";
    		case SIP_TRANSPORT_TLS:
    			return "TLS";
    		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TCP:
    			return "TCP,UDP";
    		case SIP_TRANSPORT_UDP | SIP_TRANSPORT_TLS:
    			return "TLS,UDP";
    		case SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS:
    			return "TLS,TCP";
    		default:
    			return transports ?
    				"TLS,TCP,UDP" : "UNKNOWN";	
    	}
    
    /*! \brief Return transport as string */
    
    const char *sip_get_transport(enum sip_transport t)
    
    {
    	switch (t) {
    	case SIP_TRANSPORT_UDP:
    		return "UDP";
    	case SIP_TRANSPORT_TCP:
    		return "TCP";
    	case SIP_TRANSPORT_TLS:
    		return "TLS";
    	}
    
    	return "UNKNOWN";
    
    /*! \brief Return protocol string for srv dns query */
    static inline const char *get_srv_protocol(enum sip_transport t)
    {
    	switch (t) {
    	case SIP_TRANSPORT_UDP:
    		return "udp";
    	case SIP_TRANSPORT_TLS:
    	case SIP_TRANSPORT_TCP:
    		return "tcp";
    	}
    
    	return "udp";
    }
    
    /*! \brief Return service string for srv dns query */
    static inline const char *get_srv_service(enum sip_transport t)
    {
    	switch (t) {
    	case SIP_TRANSPORT_TCP:
    	case SIP_TRANSPORT_UDP:
    		return "sip";
    	case SIP_TRANSPORT_TLS:
    		return "sips";
    	}
    	return "sip";
    }
    
    
    /*! \brief Return transport of dialog.
    	\note this is based on a false assumption. We don't always use the
    	outbound proxy for all requests in a dialog. It depends on the
    	"force" parameter. The FIRST request is always sent to the ob proxy.
    	\todo Fix this function to work correctly
    */
    static inline const char *get_transport_pvt(struct sip_pvt *p)
    
    	if (p->outboundproxy && p->outboundproxy->transport) {
    		set_socket_transport(&p->socket, p->outboundproxy->transport);
    	}
    
    	return sip_get_transport(p->socket.type);
    
    /*!
     * \internal
     * \brief Transmit SIP message
     *
     * \details
     * Sends a SIP request or response on a given socket (in the pvt)
     * \note
     * Called by retrans_pkt, send_request, send_response and __sip_reliable_xmit
     *
     * \return length of transmitted message, XMIT_ERROR on known network failures -1 on other failures.
     */
    
    static int __sip_xmit(struct sip_pvt *p, struct ast_str *data)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	const struct ast_sockaddr *dst = sip_real_dst(p);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_debug(2, "Trying to put '%.11s' onto %s socket destined for %s\n", data->str, get_transport_pvt(p), ast_sockaddr_stringify(dst));
    
    	if (sip_prepare_socket(p) < 0) {
    
    		return XMIT_ERROR;
    
    
    	if (p->socket.type == SIP_TRANSPORT_UDP) {
    
    		res = ast_sendto(p->socket.fd, data->str, ast_str_strlen(data), 0, dst);
    
    	} else if (p->socket.tcptls_session) {
    
    		res = sip_tcptls_write(p->socket.tcptls_session, data->str, ast_str_strlen(data));
    
    	} else {
    		ast_debug(2, "Socket type is TCP but no tcptls_session is present to write to\n");
    		return XMIT_ERROR;
    
    	if (res == -1) {
    		switch (errno) {
    
    		case EBADF:		/* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
    		case EHOSTUNREACH:	/* Host can't be reached */
    		case ENETDOWN:		/* Interface down */
    
    		case ENETUNREACH:	/* Network failure */
    		case ECONNREFUSED:      /* ICMP port unreachable */
    			res = XMIT_ERROR;	/* Don't bother with trying to transmit again */
    		}
    
    	if (res != ast_str_strlen(data)) {
    		ast_log(LOG_WARNING, "sip_xmit of %p (len %zu) to %s returned %d: %s\n", data, ast_str_strlen(data), ast_sockaddr_stringify(dst), res, strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Build a Via header for a request */
    static void build_via(struct sip_pvt *p)
    
    	/* Work around buggy UNIDEN UIP200 firmware */
    	const char *rport = (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT) || ast_test_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT)) ? ";rport" : "";
    
    	/* z9hG4bK is a magic cookie.  See RFC 3261 section 8.1.1.7 */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	snprintf(p->via, sizeof(p->via), "SIP/2.0/%s %s;branch=z9hG4bK%08x%s",
    
    		 get_transport_pvt(p),
    
    		 ast_sockaddr_stringify_remote(&p->ourip),
    
    Mark Michelson's avatar
    Mark Michelson committed
    		 (int) p->branch, rport);
    
    /*! \brief NAT fix - decide which IP address to use for Asterisk server?
     *
     * Using the localaddr structure built up with localnet statements in sip.conf
     * apply it to their address to see if we need to substitute our
    
     * externaddr or can get away with our internal bindaddr
    
     * 'us' is always overwritten.
     */
    
    Mark Michelson's avatar
    Mark Michelson committed
    static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p)
    
    Mark Michelson's avatar
    Mark Michelson committed
    	struct ast_sockaddr theirs;
    
    
    	/* Set want_remap to non-zero if we want to remap 'us' to an externally
    	 * reachable IP address and port. This is done if:
    	 * 1. we have a localaddr list (containing 'internal' addresses marked
    	 *    as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
    	 *    and AST_SENSE_ALLOW on 'external' ones);
    
    	 * 2. externaddr is set, so we know what to use as the
    
    	 *    externally visible address;
    	 * 3. the remote address, 'them', is external;
    	 * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
    	 *    when passed to ast_apply_ha() so it does need to be remapped.
    	 *    This fourth condition is checked later.
    	 */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	int want_remap = 0;
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sockaddr_copy(us, &internip); /* starting guess for the internal address */
    
    	/* now ask the system what would it use to talk to 'them' */
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_ouraddrfor(them, us);
    	ast_sockaddr_copy(&theirs, them);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	if (ast_sockaddr_is_ipv6(&theirs)) {
    
    		if (localaddr && !ast_sockaddr_isnull(&externaddr)) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    			ast_log(LOG_WARNING, "Address remapping activated in sip.conf "
    				"but we're using IPv6, which doesn't need it. Please "
    
    				"remove \"localnet\" and/or \"externaddr\" settings.\n");
    
    Mark Michelson's avatar
    Mark Michelson committed
    		}
    	} else {
    		want_remap = localaddr &&
    
    			!ast_sockaddr_isnull(&externaddr) &&
    
    Mark Michelson's avatar
    Mark Michelson committed
    			ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
    
    	if (want_remap &&
    
    	    (!sip_cfg.matchexternaddrlocally || !ast_apply_ha(localaddr, us)) ) {
    
    		/* if we used externhost, see if it is time to refresh the info */
    
    		if (externexpire && time(NULL) >= externexpire) {
    
    			if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
    				ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
    
    			}
    			externexpire = time(NULL) + externrefresh;
    		}
    
    		if (!ast_sockaddr_isnull(&externaddr)) {
    			ast_sockaddr_copy(us, &externaddr);
    
    			switch (p->socket.type) {
    			case SIP_TRANSPORT_TCP:
    
    				if (!externtcpport && ast_sockaddr_port(&externaddr)) {
    					/* for consistency, default to the externaddr port */
    					externtcpport = ast_sockaddr_port(&externaddr);
    
    Mark Michelson's avatar
    Mark Michelson committed
    				}
    				ast_sockaddr_set_port(us, externtcpport);
    
    				break;
    			case SIP_TRANSPORT_TLS:
    
    Mark Michelson's avatar
    Mark Michelson committed
    				ast_sockaddr_set_port(us, externtlsport);
    
    				break;
    			case SIP_TRANSPORT_UDP:
    
    				if (!ast_sockaddr_port(&externaddr)) {
    					ast_sockaddr_set_port(us, ast_sockaddr_port(&bindaddr));
    				}
    				break;
    
    		ast_debug(1, "Target address %s is not local, substituting externaddr\n",
    
    Mark Michelson's avatar
    Mark Michelson committed
    			  ast_sockaddr_stringify(them));
    
    	} else if (p) {
    		/* no remapping, but we bind to a specific address, so use it. */
    		switch (p->socket.type) {
    		case SIP_TRANSPORT_TCP:
    
    Mark Michelson's avatar
    Mark Michelson committed
    			if (!ast_sockaddr_is_any(&sip_tcp_desc.local_address)) {
    				ast_sockaddr_copy(us,
    						  &sip_tcp_desc.local_address);
    
    Mark Michelson's avatar
    Mark Michelson committed
    				ast_sockaddr_set_port(us,
    						      ast_sockaddr_port(&sip_tcp_desc.local_address));
    
    			}
    			break;
    		case SIP_TRANSPORT_TLS:
    
    Mark Michelson's avatar
    Mark Michelson committed
    			if (!ast_sockaddr_is_any(&sip_tls_desc.local_address)) {
    				ast_sockaddr_copy(us,
    						  &sip_tls_desc.local_address);
    
    Mark Michelson's avatar
    Mark Michelson committed
    				ast_sockaddr_set_port(us,
    						      ast_sockaddr_port(&sip_tls_desc.local_address));
    
    Mark Michelson's avatar
    Mark Michelson committed
    			break;
    
    		case SIP_TRANSPORT_UDP:
    			/* fall through on purpose */
    		default:
    
    Mark Michelson's avatar
    Mark Michelson committed
    			if (!ast_sockaddr_is_any(&bindaddr)) {
    				ast_sockaddr_copy(us, &bindaddr);
    
    			if (!ast_sockaddr_port(us)) {
    				ast_sockaddr_set_port(us, ast_sockaddr_port(&bindaddr));
    			}
    
    Mark Michelson's avatar
    Mark Michelson committed
    	} else if (!ast_sockaddr_is_any(&bindaddr)) {
    		ast_sockaddr_copy(us, &bindaddr);
    
    	ast_debug(3, "Setting SIP_TRANSPORT_%s with address %s\n", sip_get_transport(p->socket.type), ast_sockaddr_stringify(us));
    
    /*! \brief Append to SIP dialog history with arg list  */
    static __attribute__((format(printf, 2, 0))) void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
    {
    	char buf[80], *c = buf; /* max history length */
    	struct sip_history *hist;
    	int l;
    
    	vsnprintf(buf, sizeof(buf), fmt, ap);
    	strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
    	l = strlen(buf) + 1;
    
    	if (!(hist = ast_calloc(1, sizeof(*hist) + l))) {
    
    	if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
    		ast_free(hist);
    
    	memcpy(hist->event, buf, l);
    	if (p->history_entries == MAX_HISTORY_ENTRIES) {
    		struct sip_history *oldest;
    		oldest = AST_LIST_REMOVE_HEAD(p->history, list);
    		p->history_entries--;
    		ast_free(oldest);
    
    	AST_LIST_INSERT_TAIL(p->history, hist, list);
    	p->history_entries++;
    
    /*! \brief Append to SIP dialog history with arg list  */
    static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
    
    	if (!p->do_history && !recordhistory && !dumphistory) {
    
    	va_start(ap, fmt);
    	append_history_va(p, fmt, ap);
    	va_end(ap);
    
    /*! \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, *cur = NULL;
    	int reschedule = DEFAULT_RETRANS;
    	int xmitres = 0;
    
    	/* how many ms until retrans timeout is reached */
    	int64_t diff = pkt->retrans_stop_time - ast_tvdiff_ms(ast_tvnow(), pkt->time_sent);
    
    	/* Do not retransmit if time out is reached. This will be negative if the time between
    	 * the first transmission and now is larger than our timeout period. This is a fail safe
    	 * check in case the scheduler gets behind or the clock is changed. */
    	if ((diff <= 0) || (diff > pkt->retrans_stop_time)) {
    		pkt->retrans_stop = 1;
    	}
    
    	/* Lock channel PVT */
    	sip_pvt_lock(pkt->owner);
    
    	if (!pkt->retrans_stop) {
    
    		pkt->retrans++;
    
    		if (!pkt->timer_t1) {	/* Re-schedule using timer_a and timer_t1 */
    			if (sipdebug) {
    
    				ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n",
    					pkt->retransid,
    					sip_methods[pkt->method].text,
    					pkt->method);
    
    			int siptimer_a;
    
    			if (sipdebug) {
    
    				ast_debug(4, "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;
    
    			ast_debug(4, "** 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)) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    			const struct ast_sockaddr *dst = sip_real_dst(pkt->owner);
    			ast_verbose("Retransmitting #%d (%s) to %s:\n%s\n---\n",
    
    				pkt->retrans, sip_nat_mode(pkt->owner),
    
    Mark Michelson's avatar
    Mark Michelson committed
    				ast_sockaddr_stringify(dst),
    				pkt->data->str);
    
    		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data->str);
    
    		xmitres = __sip_xmit(pkt->owner, pkt->data);
    
    
    		/* If there was no error during the network transmission, schedule the next retransmission,
    		 * but if the next retransmission is going to be beyond our timeout period, mark the packet's
    		 * stop_retrans value and set the next retransmit to be the exact time of timeout.  This will
    		 * allow any responses to the packet to be processed before the packet is destroyed on the next
    		 * call to this function by the scheduler. */
    		if (xmitres != XMIT_ERROR) {
    			if (reschedule >= diff) {
    				pkt->retrans_stop = 1;
    				reschedule = diff;
    			}
    
    			sip_pvt_unlock(pkt->owner);
    
    			return  reschedule;
    
    	/* At this point, either the packet's retransmission timed out, or there was a
    	 * transmission error, either way destroy the scheduler item and this packet. */
    
    	pkt->retransid = -1; /* Kill this scheduler item */
    
    
    	if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) {
    
    		if (pkt->is_fatal || sipdebug) { /* Tell us if it's critical or if we're debugging */
    
    			ast_log(LOG_WARNING, "Retransmission timeout reached on transmission %s for seqno %d (%s %s) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions\n"
    
    				"Packet timed out after %dms with no response\n",
    				pkt->owner->callid,
    				pkt->seqno,
    				pkt->is_fatal ? "Critical" : "Non-critical",
    				pkt->is_resp ? "Response" : "Request",
    				(int) ast_tvdiff_ms(ast_tvnow(), pkt->time_sent));
    
    	} else if (pkt->method == SIP_OPTIONS && sipdebug) {
    
    		ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s)  -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions\n", pkt->owner->callid);
    
    	if (xmitres == XMIT_ERROR) {
    		ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid);
    		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
    
    		append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
    
    	if (pkt->is_fatal) {
    		while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
    			sip_pvt_unlock(pkt->owner);	/* SIP_PVT, not channel */
    			usleep(1);
    			sip_pvt_lock(pkt->owner);
    		}
    
    		if (pkt->owner->owner && !pkt->owner->owner->hangupcause) {
    
    			pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE;
    
    		if (pkt->owner->owner) {
    
    			ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions).\n", pkt->owner->callid);
    
    
    			if (pkt->is_resp &&
    				(pkt->response_code >= 200) &&
    				(pkt->response_code < 300) &&
    				pkt->owner->pendinginvite &&
    				ast_test_flag(&pkt->owner->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
    				/* This is a timeout of the 2XX response to a pending INVITE.  In this case terminate the INVITE
    				 * transaction just as if we received the ACK, but immediately hangup with a BYE (sip_hangup
    				 * will send the BYE as long as the dialog is not set as "alreadygone")
    				 * RFC 3261 section 13.3.1.4.
    				 * "If the server retransmits the 2xx response for 64*T1 seconds without receiving
    				 * an ACK, the dialog is confirmed, but the session SHOULD be terminated.  This is
    				 * accomplished with a BYE, as described in Section 15." */
    				pkt->owner->invitestate = INV_TERMINATED;
    				pkt->owner->pendinginvite = 0;
    			} else {
    				/* there is nothing left to do, mark the dialog as gone */
    				sip_alreadygone(pkt->owner);
    			}
    
    			ast_queue_hangup_with_cause(pkt->owner->owner, AST_CAUSE_PROTOCOL_ERROR);
    			ast_channel_unlock(pkt->owner->owner);
    		} else {
    			/* If no channel owner, destroy now */
    
    			/* Let the peerpoke system expire packets when the timer expires for poke_noanswer */
    			if (pkt->method != SIP_OPTIONS && pkt->method != SIP_REGISTER) {
    				pvt_set_needdestroy(pkt->owner, "no response to critical packet");
    				sip_alreadygone(pkt->owner);
    				append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately");
    			}
    		}
    	}
    
    	if (pkt->method == SIP_BYE) {
    		/* We're not getting answers on SIP BYE's.  Tear down the call anyway. */
    
    		sip_alreadygone(pkt->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.");
    		pvt_set_needdestroy(pkt->owner, "no response to BYE");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* Remove the packet */
    	for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
    		if (cur == pkt) {
    			UNLINK(cur, pkt->owner->packets, prev);
    			sip_pvt_unlock(pkt->owner);
    
    				pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
    
    				ast_free(pkt->data);
    
    			pkt->data = NULL;
    			ast_free(pkt);
    			return 0;
    		}
    	}
    	/* error case */
    	ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
    	sip_pvt_unlock(pkt->owner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*!
     * \internal
     * \brief Transmit packet with retransmits
     * \return 0 on success, -1 on failure to allocate packet
     */
    
    static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, struct ast_str *data, int fatal, int sipmethod)
    
    	struct sip_pkt *pkt = NULL;
    	int siptimer_a = DEFAULT_RETRANS;
    	int xmitres = 0;
    	int respid;
    
    	if (sipmethod == SIP_INVITE) {
    		/* Note this is a pending invite */
    		p->pendinginvite = seqno;
    
    	/* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
    	/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
    	/*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
    	if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
    
    		xmitres = __sip_xmit(p, data);	/* Send packet */
    
    		if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
    			append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
    			return AST_FAILURE;
    		} else {
    			return AST_SUCCESS;
    		}
    	}
    
    	if (!(pkt = ast_calloc(1, sizeof(*pkt)))) {
    
    		return AST_FAILURE;
    
    	/* copy data, add a terminator and save length */
    
    	if (!(pkt->data = ast_str_create(ast_str_strlen(data)))) {
    
    		ast_free(pkt);
    		return AST_FAILURE;
    	}
    	ast_str_set(&pkt->data, 0, "%s%s", data->str, "\0");
    	/* copy other parameters from the caller */
    	pkt->method = sipmethod;
    	pkt->seqno = seqno;
    	pkt->is_resp = resp;
    	pkt->is_fatal = fatal;
    	pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
    	pkt->next = p->packets;
    	p->packets = pkt;	/* Add it to the queue */
    	if (resp) {
    		/* Parse out the response code */
    		if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %30u", &respid) == 1) {
    			pkt->response_code = respid;
    		}
    	}
    	pkt->timer_t1 = p->timer_t1;	/* Set SIP timer T1 */
    	pkt->retransid = -1;
    
    	if (pkt->timer_t1) {
    
    	pkt->time_sent = ast_tvnow(); /* time packet was sent */
    	pkt->retrans_stop_time = 64 * (pkt->timer_t1 ? pkt->timer_t1 : DEFAULT_TIMER_T1); /* time in ms after pkt->time_sent to stop retransmission */
    
    
    	/* Schedule retransmission */
    	AST_SCHED_REPLACE_VARIABLE(pkt->retransid, sched, siptimer_a, retrans_pkt, pkt, 1);
    
    		ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id  #%d\n", pkt->retransid);
    
    	xmitres = __sip_xmit(pkt->owner, pkt->data);	/* Send packet */
    
    	if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
    		append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
    		ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
    		AST_SCHED_DEL(sched, pkt->retransid);
    		p->packets = pkt->next;
    		pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
    		ast_free(pkt->data);
    		ast_free(pkt);
    		return AST_FAILURE;
    
    		/* This is odd, but since the retrans timer starts at 500ms and the do_monitor thread
    		 * only wakes up every 1000ms by default, we have to poke the thread here to make
    		 * sure it successfully detects this must be retransmitted in less time than
    		 * it usually sleeps for. Otherwise it might not retransmit this packet for 1000ms. */
    
    		if (monitor_thread != AST_PTHREADT_NULL) {
    			pthread_kill(monitor_thread, SIGURG);
    		}
    
    		return AST_SUCCESS;
    
    /*! \brief Kill a SIP dialog (called only by the scheduler)
     * The scheduler has a reference to this dialog when p->autokillid != -1,
     * and we are called using that reference. So if the event is not
     * rescheduled, we need to call dialog_unref().
     */
    static int __sip_autodestruct(const void *data)
    
    	struct sip_pvt *p = (struct sip_pvt *)data;
    
    	struct ast_channel *owner;
    
    	/* If this is a subscription, tell the phone that we got a timeout */
    	if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) {
    		transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE);	/* Send last notification */
    		p->subscribed = NONE;
    		append_history(p, "Subscribestatus", "timeout");
    		ast_debug(3, "Re-scheduled destruction of SIP subscription %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, delay the destruction */
    	if (p->packets) {
    		if (!p->needdestroy) {
    			char method_str[31];
    			ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
    			append_history(p, "ReliableXmit", "timeout");
    			if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
    				if (method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
    					pvt_set_needdestroy(p, "autodestruct");
    				}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		} else {
    
    			/* They've had their chance to respond. Time to bail */
    			__sip_pretend_ack(p);
    
    Steve Murphy's avatar
    Steve Murphy committed
    		}
    
    	/* Reset schedule ID */
    	p->autokillid = -1;
    
    
    	/*
    	 * Lock both the pvt and the channel safely so that we can queue up a frame.
    	 */
    
    	owner = sip_pvt_lock_full(p);
    	if (owner) {
    
    		ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
    
    		ast_queue_hangup_with_cause(owner, AST_CAUSE_PROTOCOL_ERROR);
    		ast_channel_unlock(owner);
    		ast_channel_unref(owner);
    
    	} else if (p->refer && !p->alreadygone) {
    		ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
    		transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
    		append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
    		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    	} else {
    		append_history(p, "AutoDestroy", "%s", p->callid);
    		ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
    
    		sip_pvt_unlock(p);
    		dialog_unlink_all(p); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
    		sip_pvt_lock(p);
    
    		/* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
    		/* sip_destroy(p); */		/* Go ahead and destroy dialog. All attempts to recover is done */
    		/* sip_destroy also absorbs the reference */
    	}
    
    	dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
    
    /*! \brief Schedule final destruction of SIP dialog.  This can not be canceled.
     *  This function is used to keep a dialog around for a period of time in order
     *  to properly respond to any retransmits. */
    void sip_scheddestroy_final(struct sip_pvt *p, int ms)
    {
    	if (p->final_destruction_scheduled) {
    		return; /* already set final destruction */
    	}
    
    	sip_scheddestroy(p, ms);
    	if (p->autokillid != -1) {
    		p->final_destruction_scheduled = 1;
    	}
    }
    
    
    /*! \brief Schedule destruction of SIP dialog */
    void sip_scheddestroy(struct sip_pvt *p, int ms)
    
    	if (p->final_destruction_scheduled) {
    		return; /* already set final destruction */
    	}
    
    
    	if (ms < 0) {
    		if (p->timer_t1 == 0) {
    			p->timer_t1 = global_t1;	/* Set timer T1 if not set (RFC 3261) */
    		}
    		if (p->timer_b == 0) {
    			p->timer_b = global_timer_b;  /* Set timer B if not set (RFC 3261) */
    		}
    		ms = p->timer_t1 * 64;
    
    	if (sip_debug_test_pvt(p)) {
    
    		ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
    
    	}
    	if (sip_cancel_destroy(p)) {
    
    		ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
    
    	if (p->do_history) {
    
    		append_history(p, "SchedDestroy", "%d ms", ms);
    
    	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
    
    	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0) {
    
    		stop_session_timer(p);
    
    /*! \brief Cancel destruction of SIP dialog.
     * Be careful as this also absorbs the reference - if you call it
     * from within the scheduler, this might be the last reference.
     */
    int sip_cancel_destroy(struct sip_pvt *p)
    
    	if (p->final_destruction_scheduled) {
    
    	if (p->autokillid > -1) {
    
    		append_history(p, "CancelDestroy", "");
    		AST_SCHED_DEL_UNREF(sched, p->autokillid, dialog_unref(p, "remove ref for autokillid"));
    
    /*! \brief Acknowledges receipt of a packet and stops retransmission
     * called with p locked*/
    int __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
    
    	struct sip_pkt *cur, *prev = NULL;
    	const char *msg = "Not Found";	/* used only for debugging */
    	int res = FALSE;
    
    	/* If we have an outbound proxy for this dialog, then delete it now since
    	  the rest of the requests in this dialog needs to follow the routing.
    	  If obforcing is set, we will keep the outbound proxy during the whole
    	  dialog, regardless of what the SIP rfc says
    	*/
    	if (p->outboundproxy && !p->outboundproxy->force){
    		ref_proxy(p, NULL);
    
    	for (cur = p->packets; cur; prev = cur, cur = cur->next) {
    
    		if (cur->seqno != seqno || cur->is_resp != resp) {
    
    		if (cur->is_resp || cur->method == sipmethod) {
    			res = TRUE;
    			msg = "Found";
    			if (!resp && (seqno == p->pendinginvite)) {
    				ast_debug(1, "Acked pending invite %d\n", p->pendinginvite);
    				p->pendinginvite = 0;