[asterisk-commits] oej: branch 1.4 r64280 - /branches/1.4/channels/chan_sip.c

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Mon May 14 11:52:09 MST 2007


Author: oej
Date: Mon May 14 13:52:09 2007
New Revision: 64280

URL: http://svn.digium.com/view/asterisk?view=rev&rev=64280
Log:
Handle network errors, like host or network unreachable, in a better way. This means that
calls to hosts or qualify (OPTION) messages will fail quicker if the TCP/IP stack tells us
that there is an issue.

Since this is an unconnected UDP socket, we will not get error messages directly
in most cases, but maybe on the second and third try.

This is already implemented in trunk.

Modified:
    branches/1.4/channels/chan_sip.c

Modified: branches/1.4/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/branches/1.4/channels/chan_sip.c?view=diff&rev=64280&r1=64279&r2=64280
==============================================================================
--- branches/1.4/channels/chan_sip.c (original)
+++ branches/1.4/channels/chan_sip.c Mon May 14 13:52:09 2007
@@ -1751,6 +1751,15 @@
 	const struct sockaddr_in *dst = sip_real_dst(p);
 	res = sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in));
 
+	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: 		/* Inteface down */
+			case ENETUNREACH:	/* Network failure */
+				res = -2;	/* Don't bother with trying to transmit again */
+		}
+	}
 	if (res != len)
 		ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno));
 	return res;
@@ -1852,6 +1861,7 @@
 {
 	struct sip_pkt *pkt = data, *prev, *cur = NULL;
 	int reschedule = DEFAULT_RETRANS;
+	int xmitres = 0;
 
 	/* Lock channel PVT */
 	ast_mutex_lock(&pkt->owner->lock);
@@ -1891,19 +1901,25 @@
 		}
 
 		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data);
-		__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
+		xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
 		ast_mutex_unlock(&pkt->owner->lock);
-		return  reschedule;
+		if (xmitres == -2)
+			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) {
+	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->method == SIP_OPTIONS) && sipdebug)
+	} else if ((pkt->method == SIP_OPTIONS) && sipdebug) {
 			ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid);
 	}
-	append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)");
+	if (xmitres == -2) {
+		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;
 
@@ -1953,6 +1969,7 @@
 {
 	struct sip_pkt *pkt;
 	int siptimer_a = DEFAULT_RETRANS;
+	int xmitres = 0;
 
 	if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
 		return AST_FAILURE;
@@ -1977,13 +1994,20 @@
 		ast_log(LOG_DEBUG, "*** SIP TIMER: Initalizing retransmit timer on packet: Id  #%d\n", pkt->retransid);
 	pkt->next = p->packets;
 	p->packets = pkt;
-
-	__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);	/* Send packet */
 	if (sipmethod == SIP_INVITE) {
 		/* Note this is a pending invite */
 		p->pendinginvite = seqno;
 	}
-	return AST_SUCCESS;
+
+	xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);	/* Send packet */
+
+	if (xmitres == -2) {	/* 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;
+		return AST_FAILURE;
+	} else
+		return AST_SUCCESS;
 }
 
 /*! \brief Kill a SIP dialog (called by scheduler) */
@@ -2175,7 +2199,7 @@
 			(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text);
 	}
 	res = (reliable) ?
-		__sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
+		 __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
 		__sip_xmit(p, req->data, req->len);
 	if (res > 0)
 		return 0;
@@ -2771,7 +2795,7 @@
  *      used from the dial() application      */
 static int sip_call(struct ast_channel *ast, char *dest, int timeout)
 {
-	int res;
+	int res, xmitres = 0;
 	struct sip_pvt *p;
 	struct varshead *headp;
 	struct ast_var_t *current;
@@ -2845,7 +2869,10 @@
 			p->t38.jointcapability = p->t38.capability;
 			if (option_debug > 1)
 				ast_log(LOG_DEBUG,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
-			transmit_invite(p, SIP_INVITE, 1, 2);
+			xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
+			if (xmitres == -2)
+				return -1;	/* Transmission error */
+
 			p->invitestate = INV_CALLING;
 
 			/* Initialize auto-congest time */
@@ -6417,7 +6444,9 @@
 	dst->rlPart2 += offset;
 }
 
-/*! \brief Used for 200 OK and 183 early media */
+/*! \brief Used for 200 OK and 183 early media 
+	\return Will return -2 for network errors.
+*/
 static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
 {
 	struct sip_request resp;
@@ -7510,7 +7539,9 @@
 	return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
 }
 
-/*! \brief Transmit generic SIP request */
+/*! \brief Transmit generic SIP request 
+	returns -2 if transmit failed with a critical error (don't retry)
+*/
 static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
 {
 	struct sip_request resp;
@@ -11587,16 +11618,16 @@
 static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
 {
 	char tmp[BUFSIZ];
-	char *s, *e;
+	char *s, *e, *uri;
 	char *domain;
 
 	ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp));
 	s = get_in_brackets(tmp);
-	s = strsep(&s, ";");	/* strip ; and beyond */
+	uri = ast_strdupa(s);
 	if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) {
 		if (!strncasecmp(s, "sip:", 4))
 			s += 4;
-		e = strchr(s, '/');
+		e = strchr(s, ';');
 		if (e)
 			*e = '\0';
 		if (option_debug)
@@ -11612,14 +11643,19 @@
 			/* No username part */
 			domain = tmp;
 		}
-		e = strchr(tmp, '/');
+		e = strchr(s, ';');	/* Strip of parameters in the username part */
 		if (e)
 			*e = '\0';
+		e = strchr(domain, ';');	/* Strip of parameters in the domain part */
+		if (e)
+			*e = '\0';
+	
 		if (!strncasecmp(s, "sip:", 4))
 			s += 4;
 		if (option_debug > 1)
 			ast_log(LOG_DEBUG, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain);
 		if (p->owner) {
+			pbx_builtin_setvar_helper(p->owner, "SIPREDIRECTURI", uri);
 			pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain);
 			ast_string_field_set(p->owner, call_forward, s);
 		}
@@ -11653,6 +11689,7 @@
 {
 	int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING);
 	int res = 0;
+	int xmitres = 0;
 	int reinvite = (p->owner && p->owner->_state == AST_STATE_UP);
 	struct ast_channel *bridgepeer = NULL;
 	
@@ -11832,13 +11869,13 @@
 		}
 		/* If I understand this right, the branch is different for a non-200 ACK only */
 		p->invitestate = INV_TERMINATED;
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE);
 		check_pendings(p);
 		break;
 	case 407: /* Proxy authentication */
 	case 401: /* Www auth */
 		/* First we ACK */
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->options)
 			p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH);
 
@@ -11861,7 +11898,7 @@
 
 	case 403: /* Forbidden */
 		/* First we ACK */
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From"));
 		if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner)
 			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
@@ -11870,7 +11907,7 @@
 		break;
 
 	case 404: /* Not found */
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
 			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
 		sip_alreadygone(p);
@@ -11879,7 +11916,7 @@
 	case 481: /* Call leg does not exist */
 		/* Could be REFER caused INVITE with replaces */
 		ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid);
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->owner)
 			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@@ -11888,14 +11925,14 @@
 		/* We have sent CANCEL on an outbound INVITE 
 			This transaction is already scheduled to be killed by sip_hangup().
 		*/
-		transmit_request(p, SIP_ACK, seqno, 0, 0);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
 			ast_queue_hangup(p->owner);
 		else if (!ast_test_flag(req, SIP_PKT_IGNORE))
 			update_call_counter(p, DEC_CALL_LIMIT);
 		break;
 	case 488: /* Not acceptable here */
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (reinvite && p->udptl) {
 			/* If this is a T.38 call, we should go back to 
 			   audio. If this is an audio call - something went
@@ -11925,18 +11962,20 @@
 		/* we really should have to wait a while, then retransmit */
 			/* We should support the retry-after at some point */
 		/* At this point, we treat this as a congestion */
-		transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
 			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
 		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
 		break;
 
 	case 501: /* Not implemented */
-		transmit_request(p, SIP_ACK, seqno, 0, 0);
+		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->owner)
 			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
 		break;
 	}
+	if (xmitres == -2)
+		ast_log(LOG_WARNING, "Could not transmit message in dialog %s\n", p->callid);
 }
 
 /* \brief Handle SIP response in REFER transaction
@@ -13078,6 +13117,7 @@
 
 	/* Answer the incoming call and set channel to UP state */
 	transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE);
+		
 	ast_setstate(c, AST_STATE_UP);
 	
 	/* Stop music on hold and other generators */
@@ -15220,6 +15260,7 @@
 static int sip_poke_peer(struct sip_peer *peer)
 {
 	struct sip_pvt *p;
+	int xmitres = 0;
 
 	if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
 		/* IF we have no IP, or this isn't to be monitored, return
@@ -15265,12 +15306,15 @@
 	ast_set_flag(&p->flags[0], SIP_OUTGOING);
 #ifdef VOCAL_DATA_HACK
 	ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
-	transmit_invite(p, SIP_INVITE, 0, 2);
+	xmitres = transmit_invite(p, SIP_INVITE, 0, 2);
 #else
-	transmit_invite(p, SIP_OPTIONS, 0, 2);
+	xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2);
 #endif
 	gettimeofday(&peer->ps, NULL);
-	peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer);
+	if (xmitres == -2)
+		sip_poke_noanswer(peer);	/* Immediately unreachable, network problems */
+	else
+		peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer);
 
 	return 0;
 }



More information about the asterisk-commits mailing list