Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		append_history(p, "Xfer", "Refer succeeded.");
    		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
    		/* Do not hangup call, the other side do that when we say 200 OK */
    		/* We could possibly implement a timer here, auto congestion */
    		res = 0;
    	} else {
    		ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);	/* Don't delay hangup */
    
    		ast_debug(3, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind");
    
    		append_history(p, "Xfer", "Refer failed.");
    		/* Failure of some kind */
    		p->refer->status = REFER_FAILED;
    		transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE);
    		ast_clear_flag(&p->flags[0], SIP_GOTREFER);	
    		res = -1;
    
    /*! \brief Handle incoming CANCEL request */
    
    static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
    
    Olle Johansson's avatar
    Olle Johansson committed
    	sip_alreadygone(p);
    
    
    	/* At this point, we could have cancelled the invite at the same time
    	   as the other side sends a CANCEL. Our final reply with error code
    	   might not have been received by the other side before the CANCEL
    	   was sent, so let's just give up retransmissions and waiting for
    	   ACK on our error code. The call is hanging up any way. */
    	if (p->invitestate == INV_TERMINATED)
    		__sip_pretend_ack(p);
    	else
    		p->invitestate = INV_CANCELLED;
    
    	
    	if (p->owner && p->owner->_state == AST_STATE_UP) {
    		/* This call is up, cancel is ignored, we need a bye */
    		transmit_response(p, "200 OK", req);
    
    		ast_debug(1, "Got CANCEL on an answered call. Ignoring... \n");
    
    	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) 
    
    		update_call_counter(p, DEC_CALL_LIMIT);
    
    
    	stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
    
    	if (p->owner)
    		ast_queue_hangup(p->owner);
    	else
    
    		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    
    	if (p->initreq.len > 0) {
    
    		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
    
    		transmit_response(p, "200 OK", req);
    		return 1;
    	} else {
    		transmit_response(p, "481 Call Leg Does Not Exist", req);
    		return 0;
    	}
    }
    
    
    static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen)
    
    {
    	struct sip_pvt *p = chan->tech_pvt;
    
    	char *all = "", *parse = ast_strdupa(preparse);
    
    	AST_DECLARE_APP_ARGS(args,
    
    		AST_APP_ARG(param);
    
    		AST_APP_ARG(type);
    		AST_APP_ARG(field);
    	);
    	AST_STANDARD_APP_ARGS(args, parse);
    
    	if (!IS_SIP_TECH(chan->tech)) {
    
    		ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
    
    	if (!strcasecmp(args.param, "rtpdest")) {
    		struct sockaddr_in sin;
    
    		if (ast_strlen_zero(args.type))
    			args.type = "audio";
    
    		if (!strcasecmp(args.type, "audio"))
    			ast_rtp_get_peer(p->rtp, &sin);
    		else if (!strcasecmp(args.type, "video"))
    			ast_rtp_get_peer(p->vrtp, &sin);
    		else if (!strcasecmp(args.type, "text"))
    			ast_rtp_get_peer(p->trtp, &sin);
    
    		snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
    	} else if (!strcasecmp(args.param, "rtpqos")) {
    		struct ast_rtp_quality qos;
    		
    		memset(&qos, 0, sizeof(qos));
    
    		if (ast_strlen_zero(args.type))
    			args.type = "audio";
    		if (ast_strlen_zero(args.field))
    			args.field = "all";
    		
    		if (strcasecmp(args.type, "AUDIO") == 0) {
    			all = ast_rtp_get_quality(p->rtp, &qos);
    		} else if (strcasecmp(args.type, "VIDEO") == 0) {
    			all = ast_rtp_get_quality(p->vrtp, &qos);
    		} else if (strcasecmp(args.type, "TEXT") == 0) {
    			all = ast_rtp_get_quality(p->trtp, &qos);
    		}
    		
    		if (strcasecmp(args.field, "local_ssrc") == 0)
    			snprintf(buf, buflen, "%u", qos.local_ssrc);
    		else if (strcasecmp(args.field, "local_lostpackets") == 0)
    			snprintf(buf, buflen, "%u", qos.local_lostpackets);
    		else if (strcasecmp(args.field, "local_jitter") == 0)
    
    			snprintf(buf, buflen, "%.0f", qos.local_jitter * 1000.0);
    
    		else if (strcasecmp(args.field, "local_count") == 0)
    			snprintf(buf, buflen, "%u", qos.local_count);
    		else if (strcasecmp(args.field, "remote_ssrc") == 0)
    			snprintf(buf, buflen, "%u", qos.remote_ssrc);
    		else if (strcasecmp(args.field, "remote_lostpackets") == 0)
    			snprintf(buf, buflen, "%u", qos.remote_lostpackets);
    		else if (strcasecmp(args.field, "remote_jitter") == 0)
    
    			snprintf(buf, buflen, "%.0f", qos.remote_jitter * 1000.0);
    
    		else if (strcasecmp(args.field, "remote_count") == 0)
    			snprintf(buf, buflen, "%u", qos.remote_count);
    		else if (strcasecmp(args.field, "rtt") == 0)
    
    			snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0);
    
    		else if (strcasecmp(args.field, "all") == 0)
    			ast_copy_string(buf, all, buflen);
    		else {
    			ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
    			return -1;
    		}
    	} else {
    		res = -1;
    
    /*! \brief Handle incoming BYE request */
    
    static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
    
    {
    	struct ast_channel *c=NULL;
    	int res;
    	struct ast_channel *bridged_to;
    
    	/* If we have an INCOMING invite that we haven't answered, terminate that transaction */
    
    	if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore && !p->owner) 
    
    		transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
    
    	copy_request(&p->initreq, req);
    
    	if (sipdebug)
    		ast_debug(1, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	sip_alreadygone(p);
    
    
    	/* Get RTCP quality before end of call */
    
    	if (p->do_history || p->owner) {
    
    		char *audioqos, *videoqos, *textqos;
    
    			audioqos = ast_rtp_get_quality(p->rtp, NULL);
    
    				append_history(p, "RTCPaudio", "Quality:%s", audioqos);
    			if (p->owner)
    				pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
    		}
    		if (p->vrtp) {
    
    			videoqos = ast_rtp_get_quality(p->vrtp, NULL);
    
    				append_history(p, "RTCPvideo", "Quality:%s", videoqos);
    			if (p->owner)
    				pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
    		}
    
    			textqos = ast_rtp_get_quality(p->trtp, NULL);
    
    				append_history(p, "RTCPtext", "Quality:%s", textqos);
    			if (p->owner)
    				pbx_builtin_setvar_helper(p->owner, "RTPTEXTQOS", textqos);
    		}
    
    	stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
    
    	if (!ast_strlen_zero(get_header(req, "Also"))) {
    		ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method.  Ask vendor to support REFER instead\n",
    
    		if (ast_strlen_zero(p->context))
    
    			ast_string_field_set(p, context, default_context);
    
    		res = get_also_info(p, req);
    		if (!res) {
    			c = p->owner;
    			if (c) {
    				bridged_to = ast_bridged_channel(c);
    				if (bridged_to) {
    					/* Don't actually hangup here... */
    
    					ast_queue_control(c, AST_CONTROL_UNHOLD);
    
    					ast_async_goto(bridged_to, p->context, p->refer->refer_to,1);
    
    				} else
    					ast_queue_hangup(p->owner);
    			}
    		} else {
    
    			ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
    
    			if (p->owner)
    				ast_queue_hangup(p->owner);
    
    		ast_queue_hangup(p->owner);
    
    		ast_debug(3, "Received bye, issuing owner hangup\n");
    
    		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    
    			ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
    
    	transmit_response(p, "200 OK", req);
    
    	return 1;
    }
    
    
    /*! \brief Handle incoming MESSAGE request */
    
    static int handle_request_message(struct sip_pvt *p, struct sip_request *req)
    
    	if (!req->ignore) {
    		if (req->debug)
    
    			ast_verbose("Receiving message!\n");
    		receive_message(p, req);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	} else
    		transmit_response(p, "202 Accepted", req);
    
    static void add_peer_mwi_subs(struct sip_peer *peer)
    {
    	struct sip_mailbox *mailbox;
    
    	AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
    		mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, peer,
    			AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
    			AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
    			AST_EVENT_IE_END);
    	}
    }
    
    
    /*! \brief  Handle incoming SUBSCRIBE request */
    static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e)
    
    	const char *eventheader = get_header(req, "Event");	/* Get Event package name */
    
    	const char *accept = get_header(req, "Accept");
    
    	int resubscribe = (p->subscribed != NONE);
    
    		/* We already have a dialog */
    		if (p->initreq.method != SIP_SUBSCRIBE) {
    			/* This is a SUBSCRIBE within another SIP dialog, which we do not support */
    			/* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */
     			transmit_response(p, "403 Forbidden (within dialog)", req);
    			/* Do not destroy session, since we will break the call if we do */
    
    			ast_debug(1, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text);
    
    			if (resubscribe)
    				ast_debug(1, "Got a re-subscribe on existing subscription %s\n", p->callid);
    			else
    				ast_debug(1, "Got a new subscription %s (possibly with auth)\n", p->callid);
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    	/* Check if we have a global disallow setting on subscriptions. 
    		if so, we don't have to check peer/user settings after auth, which saves a lot of processing
    	*/
    	if (!global_allowsubscribe) {
     		transmit_response(p, "403 Forbidden (policy)", req);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		return 0;
    	}
    
    
    	if (!req->ignore && !resubscribe) {	/* Set up dialog, new subscription */
    
    		/* Use this as the basis */
    
    		copy_request(&p->initreq, req);
    
    		if (sipdebug)
    			ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
    
    	} else if (req->debug && req->ignore)
    
    		ast_verbose("Ignoring this SUBSCRIBE request\n");
    
    
    	/* Find parameters to Event: header value and remove them for now */
    
    	if (ast_strlen_zero(eventheader)) {
    		transmit_response(p, "489 Bad Event", req);
    
    		ast_debug(2, "Received SIP subscribe for unknown event package: <none>\n");
    
    		return 0;
    	}
    
    	if ( (strchr(eventheader, ';'))) {
    		event = ast_strdupa(eventheader);	/* Since eventheader is a const, we can't change it */
    		temp = strchr(event, ';'); 		
    		*temp = '\0';				/* Remove any options for now */
    							/* We might need to use them later :-) */
    	} else
    		event = (char *) eventheader;		/* XXX is this legal ? */
    
    	res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, sin, &authpeer);
    
    	/* if an authentication response was sent, we are done here */
    
    	if (res == AUTH_CHALLENGE_SENT)	/* authpeer = NULL here */
    
    		return 0;
    	if (res < 0) {
    		if (res == AUTH_FAKE_AUTH) {
    			ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
    			transmit_fake_auth_response(p, req, 1);
    		} else {
    
    			ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
    
    			transmit_response_reliable(p, "403 Forbidden", req);
    
    	/* At this point, authpeer cannot be NULL. Remember we hold a reference,
    	 * so we must release it when done.
    	 * XXX must remove all the checks for authpeer == NULL.
    	 */
    
    
    	/* Check if this user/peer is allowed to subscribe at all */
    	if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
    		transmit_response(p, "403 Forbidden (policy)", req);
    
    	/* Get destination right away */
    	gotdest = get_destination(p, NULL);
    
    
    	/* Get full contact header - this needs to be used as a request URI in NOTIFY's */
    	parse_ok_contact(p, req);
    
    
    	build_contact(p);
    	if (gotdest) {
    		transmit_response(p, "404 Not Found", req);
    
    	/* Initialize tag for new subscriptions */	
    	if (ast_strlen_zero(p->tag))
    		make_our_tag(p->tag, sizeof(p->tag));
    
    	if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
    
    		if (authpeer)	/* We do not need the authpeer any more */
    
    		/* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
    		/* Polycom phones only handle xpidf+xml, even if they say they can
    		   handle pidf+xml as well
    		*/
    		if (strstr(p->useragent, "Polycom")) {
    			p->subscribed = XPIDF_XML;
    		} else if (strstr(accept, "application/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 (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);
    
    				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 {
    			/* Can't find a format for events that we know about */
    
    			char mybuf[200];
    			snprintf(mybuf,sizeof(mybuf),"489 Bad Event (format %s)", accept);
    			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);
    
    			return 0;
    		}
    	} else if (!strcmp(event, "message-summary")) { 
    		if (!ast_strlen_zero(accept) && strcmp(accept, "application/simple-message-summary")) {
    			/* Format requested that we do not support */
    			transmit_response(p, "406 Not Acceptable", req);
    
    			ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", accept);
    
    		/* Looks like they actually want a mailbox status 
    		  This version of Asterisk supports mailbox subscriptions
    		  The subscribed URI needs to exist in the dial plan
    		  In most devices, this is configurable to the voicemailmain extension you use
    		*/
    
    		if (!authpeer || AST_LIST_EMPTY(&authpeer->mailboxes)) {
    
    			transmit_response(p, "404 Not found (no mailbox)", req);
    
    			ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name);
    
    			return 0;
    		}
    
    		p->subscribed = MWI_NOTIFICATION;
    
    		if (ast_test_flag(&authpeer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY)) {
    
    		if (authpeer->mwipvt && authpeer->mwipvt != p)	/* Destroy old PVT if this is a new one */
    			/* We only allow one subscription per peer */
    			sip_destroy(authpeer->mwipvt);
    		authpeer->mwipvt = p;		/* Link from peer to pvt */
    		p->relatedpeer = authpeer;	/* Link from pvt to peer */
    
    		/* Do not release authpeer here */
    
    	} else { /* At this point, Asterisk does not understand the specified event */
    		transmit_response(p, "489 Bad Event", req);
    
    		ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
    
    	/* Add subscription for extension state from the PBX core */
    
    	if (p->subscribed != MWI_NOTIFICATION && !resubscribe) {
    		if (p->stateid > -1)
    			ast_extension_state_del(p->stateid, cb_extensionstate);
    
    		p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
    
    		p->expiry = atoi(get_header(req, "Expires"));
    
    
    		/* check if the requested expiry-time is within the approved limits from sip.conf */
    		if (p->expiry > max_expiry)
    			p->expiry = max_expiry;
    
    		if (p->expiry < min_expiry && p->expiry > 0)
    
    			if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer)
    
    				ast_debug(2, "Adding subscription for mailbox notification - peer %s\n", p->relatedpeer->name);
    
    				ast_debug(2, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username);
    
    		if (p->autokillid > -1)
    			sip_cancel_destroy(p);	/* Remove subscription expiry for renewals */
    
    		if (p->expiry > 0)
    			sip_scheddestroy(p, (p->expiry + 10) * 1000);	/* Set timer for destruction of call at expiration */
    
    			transmit_response(p, "200 OK", req);
    
    			if (p->relatedpeer) {	/* Send first notification */
    				ASTOBJ_WRLOCK(p->relatedpeer);
    
    				sip_send_mwi_to_peer(p->relatedpeer, NULL, 0);
    
    			struct sip_pvt *p_old;
    
    
    			if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) {
    
    				ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_inet_ntoa(p->sa.sin_addr));
    
    			}
    
    			transmit_response(p, "200 OK", req);
    
    			transmit_state_notify(p, firststate, 1, FALSE);	/* Send first notification */
    
    			append_history(p, "Subscribestatus", "%s", ast_extension_state2str(firststate));
    			/* hide the 'complete' exten/context in the refer_to field for later display */
    			ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context);
    
    			/* remove any old subscription from this peer for the same exten/context,
    			as the peer has obviously forgotten about it and it's wasteful to wait
    			for it to expire and send NOTIFY messages to the peer only to have them
    			ignored (or generate errors)
    			*/
    
    			dialoglist_lock();
    
    			for (p_old = dialoglist; p_old; p_old = p_old->next) {
    
    				if (p_old == p)
    					continue;
    				if (p_old->initreq.method != SIP_SUBSCRIBE)
    					continue;
    				if (p_old->subscribed == NONE)
    					continue;
    
    				if (!strcmp(p_old->username, p->username)) {
    					if (!strcmp(p_old->exten, p->exten) &&
    					    !strcmp(p_old->context, p->context)) {
    
    						p_old->needdestroy = 1;
    
    			dialoglist_unlock();
    
    /*! \brief Handle incoming REGISTER request */
    
    static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e)
    
    
    	/* Use this as the basis */
    	copy_request(&p->initreq, req);
    
    	if (sipdebug)
    		ast_debug(4, "Initializing initreq for method %s - callid %s\n", sip_methods[req->method].text, p->callid);
    
    	if ((res = register_verify(p, sin, req, e)) < 0) {
    
    		const char *reason;
    
    
    		switch (res) {
    		case AUTH_SECRET_FAILED:
    			reason = "Wrong password";
    			break;
    		case AUTH_USERNAME_MISMATCH:
    			reason = "Username/auth name mismatch";
    			break;
    		case AUTH_NOT_FOUND:
    			reason = "No matching peer found";
    			break;
    		case AUTH_UNKNOWN_DOMAIN:
    			reason = "Not a local domain";
    			break;
    
    		case AUTH_PEER_NOT_DYNAMIC:
    			reason = "Peer is not supposed to register";
    			break;
    		case AUTH_ACL_FAILED:
    			reason = "Device does not match ACL";
    			break;
    
    			reason = "Unknown failure";
    
    			break;
    		}
    		ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n",
    
    			get_header(req, "To"), ast_inet_ntoa(sin->sin_addr),
    
    		append_history(p, "RegRequest", "Failed : Account %s : %s", get_header(req, "To"), reason);
    	} else
    		append_history(p, "RegRequest", "Succeeded : Account %s", get_header(req, "To"));
    
    
    	if (res < 1) {
    		/* Destroy the session, but keep us around for just a bit in case they don't
    		   get our 200 OK */
    
    		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    
    /*! \brief Handle incoming SIP requests (methods) 
    \note	This is where all incoming requests go first   */
    
    /* called with p and p->owner locked */
    
    static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Called with p->lock held, as well as p->owner->lock if appropriate, keeping things
    	   relatively static */
    
    	const char *cmd;
    	const char *cseq;
    	const char *useragent;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int seqno;
    	int len;
    	int respid;
    
    	int debug = sip_debug_test_pvt(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Get Method and Cseq */
    	cseq = get_header(req, "Cseq");
    	cmd = req->header[0];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Must have Cseq */
    
    	if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq)) {
    		ast_log(LOG_ERROR, "Missing Cseq. Dropping this SIP message, it's incomplete.\n");
    		error = 1;
    	}
    	if (!error && sscanf(cseq, "%d%n", &seqno, &len) != 1) {
    		ast_log(LOG_ERROR, "No seqno in '%s'. Dropping incomplete message.\n", cmd);
    		error = 1;
    	}
    	if (error) {
    
    		if (!p->initreq.headers)	/* New call */
    
    			p->needdestroy = 1; /* Make sure we destroy this dialog */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	cmd = req->rlPart1;
    	e = req->rlPart2;
    
    
    	/* Save useragent of the client */
    	useragent = get_header(req, "User-Agent");
    
    		ast_string_field_set(p, useragent, useragent);
    
    	/* Find out SIP method for incoming request */
    
    	if (req->method == SIP_RESPONSE) {	/* Response to our request */
    
    		/* When we get here, we know this is a SIP dialog where we've sent
    
    		 * a request and have a response, or at least get a response
    		 * within an existing dialog. Do some sanity checks, then
    		 * possibly process the request. In all cases, there function
    		 * terminates at the end of this block
    		 */
    		int ret = 0;
    
    
    		if (p->ocseq < seqno && seqno != p->lastnoninvite) {
    
    			ast_debug(1, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq);
    
    		} else if (p->ocseq != seqno && seqno != p->lastnoninvite) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* ignore means "don't do anything with it" but still have to 
    
    			 * respond appropriately.
    			 * But in this case this is a response already, so we really
    			 * have nothing to do with this message, and even setting the
    			 * ignore flag is pointless.
    			 */
    
    			append_history(p, "Ignore", "Ignoring this retransmit\n");
    
    		} else if (e) {
    			e = ast_skip_blanks(e);
    			if (sscanf(e, "%d %n", &respid, &len) != 1) {
    				ast_log(LOG_WARNING, "Invalid response: '%s'\n", e);
    
    				/* XXX maybe should do ret = -1; */
    			} else if (respid <= 0) {
    				ast_log(LOG_WARNING, "Invalid SIP response code: '%d'\n", respid);
    				/* XXX maybe should do ret = -1; */
    			} else { /* finally, something worth processing */
    
    				/* More SIP ridiculousness, we have to ignore bogus contacts in 100 etc responses */
    				if ((respid == 200) || ((respid >= 300) && (respid <= 399)))
    					extract_uri(p, req);
    				handle_response(p, respid, e + len, req, seqno);
    
    	/* New SIP request coming in 
    	   (could be new request in existing SIP dialog as well...) 
    	 */			
    
    	p->method = req->method;	/* Find out which SIP method they are using */
    
    	ast_debug(4, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd); 
    
    	if (p->icseq && (p->icseq > seqno)) {
    
    		ast_debug(1, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq);
    
    		if (req->method != SIP_ACK)
    			transmit_response(p, "503 Server error", req);	/* We must respond according to RFC 3261 sec 12.2 */
    
    	} else if (p->icseq &&
    		   p->icseq == seqno &&
    		   req->method != SIP_ACK &&
    
    		   (p->method != SIP_CANCEL || p->alreadygone)) {
    
    		/* ignore means "don't do anything with it" but still have to 
    		   respond appropriately.  We do this if we receive a repeat of
    		   the last sequence number  */
    
    		ast_debug(3, "Ignoring SIP message because of retransmit (%s Seqno %d, ours %d)\n", sip_methods[p->method].text, p->icseq, seqno);
    
    	if (seqno >= p->icseq)
    		/* Next should follow monotonically (but not necessarily 
    		   incrementally -- thanks again to the genius authors of SIP --
    		   increasing */
    		p->icseq = seqno;
    
    
    	/* Find their tag if we haven't got it */
    	if (ast_strlen_zero(p->theirtag)) {
    
    		char tag[128];
    
    		gettag(req, "From", tag, sizeof(tag));
    		ast_string_field_set(p, theirtag, tag);
    
    	}
    	snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd);
    
    
    	if (pedanticsipchecking) {
    		/* If this is a request packet without a from tag, it's not
    			correct according to RFC 3261  */
    		/* Check if this a new request in a new dialog with a totag already attached to it,
    			RFC 3261 - section 12.2 - and we don't want to mess with recovery  */
    
    		if (!p->initreq.headers && req->has_to_tag) {
    
    			/* If this is a first request and it got a to-tag, it is not for us */
    
    			if (!req->ignore && req->method == SIP_INVITE) {
    
    				transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req);
    
    				/* Will cease to exist after ACK */
    
    Olle Johansson's avatar
    Olle Johansson committed
    			} else if (req->method != SIP_ACK) {
    
    				transmit_response(p, "481 Call/Transaction Does Not Exist", req);
    
    				sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    
    				ast_debug(1, "Got ACK for unknown dialog... strange.\n");
    
    	if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) {
    
    		transmit_response(p, "400 Bad request", req);
    
    		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    		return -1;
    	}
    
    
    	/* Handle various incoming SIP methods in requests */
    	switch (p->method) {
    	case SIP_OPTIONS:
    
    		res = handle_request_options(p, req);
    
    		res = handle_request_invite(p, req, debug, seqno, sin, recount, e, nounlock);
    
    		res = handle_request_refer(p, req, debug, seqno, nounlock);
    
    		res = handle_request_cancel(p, req);
    
    		res = handle_request_bye(p, req);
    
    		res = handle_request_message(p, req);
    
    		res = handle_request_subscribe(p, req, sin, seqno, e);
    
    		res = handle_request_register(p, req, sin, e);
    
    			ast_verbose("Receiving INFO!\n");
    
    			handle_request_info(p, req);
    
    		else  /* if ignoring, transmit response */
    
    		res = handle_request_notify(p, req, sin, seqno, e);
    
    		/* Make sure we don't ignore this */
    		if (seqno == p->pendinginvite) {
    
    			p->invitestate = INV_TERMINATED;
    
    			__sip_ack(p, seqno, 1 /* response */, 0);
    
    				if (process_sdp(p, req))
    					return -1;
    			} 
    			check_pendings(p);
    		}
    
    		/* Got an ACK that we did not match. Ignore silently */
    
    		if (!p->lastinvite && ast_strlen_zero(p->randdata))
    
    		transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", 
    
    		/* If this is some new method, and we don't have a call, destroy it now */
    
    /*! \brief Read data from SIP socket
    
    \note sipsock_read locks the owner channel while we are processing the SIP message
    
    \return 1 on error, 0 on success
    
    \note Successful messages is connected to SIP call and forwarded to handle_incoming() 
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sipsock_read(int *id, int fd, short events, void *ignore)
    {
    	struct sip_request req;
    
    	struct sockaddr_in sin = { 0, };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_pvt *p;
    	int res;
    
    	socklen_t len = sizeof(sin);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int recount = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(&req, 0, sizeof(req));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
    	if (res < 0) {
    
    #if !defined(__FreeBSD__)
    
    		if (errno == EAGAIN)
    			ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
    
    		else 
    #endif
    		if (errno != ECONNREFUSED)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
    		return 1;
    	}
    
    	if (res == sizeof(req.data)) {
    		ast_debug(1, "Received packet exceeds buffer. Data is possibly lost\n");
    
    		req.data[sizeof(req.data) - 1] = '\0';
    	} else
    		req.data[res] = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req.len = res;
    
    	if(sip_debug_test_addr(&sin))	/* Set the debug flag early on packet level */
    
    		req.len = lws2sws(req.data, req.len);	/* Fix multiline headers */
    
    		ast_verbose("\n<--- SIP read from %s:%d --->\n%s\n<------------->\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data);
    
    	parse_request(&req);
    	req.method = find_sip_method(req.rlPart1);
    
    		ast_verbose("--- (%d headers %d lines)%s ---\n", req.headers, req.lines, (req.headers + req.lines == 0) ? " Nat keepalive" : "");
    
    	if (req.headers < 2)	/* Must have at least two headers */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 1;
    
    	/* Process request, with netlock held, and with usual deadlock avoidance */
    	for (lockretry = 100; lockretry > 0; lockretry--) {
    		ast_mutex_lock(&netlock);
    
    		/* Find the active SIP dialog or create a new one */
    		p = find_call(&req, &sin, req.method);	/* returns p locked */
    		if (p == NULL) {
    
    			ast_debug(1, "Invalid SIP message - rejected , no callid, len %d\n", req.len);
    
    			ast_mutex_unlock(&netlock);
    
    		/* Go ahead and lock the owner if it has one -- we may need it */
    		/* becaues this is deadlock-prone, we need to try and unlock if failed */
    		if (!p->owner || !ast_channel_trylock(p->owner))
    			break;	/* locking succeeded */
    
    		ast_debug(1, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid);
    
    		ast_mutex_unlock(&netlock);
    		/* Sleep for a very short amount of time */
    		usleep(1);
    	}
    	p->recv = sin;
    
    
    	if (p->do_history) /* This is a request or response, note what it was for */
    
    		append_history(p, "Rx", "%s / %s / %s", req.data, get_header(&req, "CSeq"), req.rlPart2);
    
    	if (!lockretry) {
    
    		if (p->owner)
    			ast_log(LOG_ERROR, "We could NOT get the channel lock for %s! \n", S_OR(p->owner->name, "- no channel name ??? - "));
    
    		ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (req.method != SIP_ACK)
    			transmit_response(p, "503 Server error", &req);	/* We must respond according to RFC 3261 sec 12.2 */
    
    		/* XXX We could add retry-after to make sure they come back */
    		append_history(p, "LockFail", "Owner lock failed, transaction failed.");
    		return 1;
    
    	if (handle_incoming(p, &req, &sin, &recount, &nounlock) == -1) {
    
    		ast_debug(1, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
    
    	}
    		
    	if (p->owner && !nounlock)
    		ast_channel_unlock(p->owner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (recount)
    		ast_update_use_count();
    
    
    /*!
     * \brief Get cached MWI info
     * \retval 0 At least one message is waiting
     * \retval 1 no messages waiting
     */
    static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
    {
    	struct sip_mailbox *mailbox;
    
    	AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
    		struct ast_event *event;
    		event = ast_event_get_cached(AST_EVENT_MWI,
    			AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
    
    			AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
    
    			AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
    			AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
    			AST_EVENT_IE_END);
    		if (!event)
    			continue;
    		*new += ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
    		*old += ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
    		ast_event_destroy(event);
    	}
    
    	return (*new || *old) ? 0 : 1;
    }
    
    
    /*! \brief Send message waiting indication to alert peer that they've got voicemail */
    
    static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *event, int cache_only)
    
    {
    	/* Called with peerl lock, but releases it */
    	struct sip_pvt *p;
    
    	int newmsgs = 0, oldmsgs = 0;
    
    	if (ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY) && !peer->mwipvt)
    
    	/* Do we have an IP address? If not, skip this peer */
    	if (!peer->addr.sin_addr.s_addr && !peer->defaddr.sin_addr.s_addr) 
    		return 0;
    
    
    	if (event) {
    		newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
    		oldmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_OLDMSGS);
    
    	} else if (!get_cached_mwi(peer, &newmsgs, &oldmsgs)) {
    		/* got it!  Don't keep looking. */
    
    	} else if (cache_only) {
    		return 0;
    
    	} else { /* Fall back to manually checking the mailbox */
    		struct ast_str *mailbox_str = ast_str_alloca(512);
    		peer_mailboxes_to_str(&mailbox_str, peer);
    		ast_app_inboxcount(mailbox_str->str, &newmsgs, &oldmsgs);
    	}
    
    	if (peer->mwipvt) {
    		/* Base message on subscription */
    
    	} else {
    		/* Build temporary dialog for this message */
    
    		if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) 
    
    			return -1;
    		if (create_addr_from_peer(p, peer)) {
    			/* Maybe they're not registered, etc. */
    			sip_destroy(p);
    			return 0;
    		}
    		/* Recalculate our side, and recalculate Call ID */
    
    		ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
    
    		build_via(p);
    		build_callid_pvt(p);
    		/* Destroy this session after 32 secs */
    
    		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
    
    	/* Send MWI */
    
    	ast_set_flag(&p->flags[0], SIP_OUTGOING);
    
    	transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
    
    /*! \brief helper function for the monitoring thread */
    
    static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
    {
    	/* If we have no RTP or no active owner, no need to check timers */
    	if (!dialog->rtp || !dialog->owner)
    		return;
    	/* If the call is not in UP state or redirected outside Asterisk, no need to check timers */
    	if (dialog->owner->_state != AST_STATE_UP || dialog->redirip.sin_addr.s_addr)
    		return;
    
    
    	/* If the call is involved in a T38 fax session do not check RTP timeout */
    	if (dialog->t38.state == T38_ENABLED)
    		return;
    
    
    	/* If we have no timers set, return now */
    
    	if ((ast_rtp_get_rtpkeepalive(dialog->rtp) == 0) && (ast_rtp_get_rtptimeout(dialog->rtp) == 0) && (ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0))