Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) {         /* Video */
    
    				if(ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0) != -1) {
    					if (debug)
    						ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec);
    					found_rtpmap_codecs[last_rtpmap_codec] = codec;
    					last_rtpmap_codec++;
    				} else {
    					ast_rtp_unset_m_type(newvideortp, codec);
    					if (debug) 
    						ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
    				}
    
    			} else if (!strncasecmp(mimeSubtype, "T140",4)) { /* Text */
    				if (p->trtp) {
    					/* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */
    					ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0);
    				}
    			} else {                                          /* Must be audio?? */
    
    				if(ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
    						ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0) != -1) {
    					if (debug)
    						ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec);
    					found_rtpmap_codecs[last_rtpmap_codec] = codec;
    					last_rtpmap_codec++;
    				} else {
    					ast_rtp_unset_m_type(newaudiortp, codec);
    					if (debug) 
    						ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
    				}
    
    	
    	if (udptlportno != -1) {
    		int found = 0, x;
    		
    		old = 0;
    		
    		/* Scan trough the a= lines for T38 attributes and set apropriate fileds */
    		iterator = req->sdp_start;
    		while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
    			if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) {
    				found = 1;
    
    				ast_debug(3, "MaxBufferSize:%d\n",x);
    
    			} else if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
    
    				ast_debug(3,"T38MaxBitRate: %d\n",x);
    
    				switch (x) {
    				case 14400:
    					peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 12000:
    					peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 9600:
    					peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 7200:
    					peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 4800:
    					peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 2400:
    					peert38capability |= T38FAX_RATE_2400;
    					break;
    				}
    
    			} else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) {
    
    				if (x == 0)
    					peert38capability |= T38FAX_VERSION_0;
    				else if (x == 1)
    					peert38capability |= T38FAX_VERSION_1;
    
    			} else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1)) {
    
    				ast_debug(3, "FaxMaxDatagram: %d\n",x);
    
    				ast_udptl_set_far_max_datagram(p->udptl, x);
    				ast_udptl_set_local_max_datagram(p->udptl, x);
    
    			} else if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) {
    
    				ast_debug(3, "FillBitRemoval: %d\n",x);
    
    				if (x == 1)
    					peert38capability |= T38FAX_FILL_BIT_REMOVAL;
    
    			} else if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) {
    
    				ast_debug(3, "Transcoding MMR: %d\n",x);
    
    				if (x == 1)
    					peert38capability |= T38FAX_TRANSCODING_MMR;
    			}
    			if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) {
    				found = 1;
    
    				ast_debug(3, "Transcoding JBIG: %d\n",x);
    
    				if (x == 1)
    					peert38capability |= T38FAX_TRANSCODING_JBIG;
    
    			} else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) {
    
    				ast_debug(3, "RateManagement: %s\n", s);
    
    				if (!strcasecmp(s, "localTCF"))
    					peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
    				else if (!strcasecmp(s, "transferredTCF"))
    					peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
    
    			} else if ((sscanf(a, "T38FaxUdpEC:%255s", s) == 1)) {
    
    				if (!strcasecmp(s, "t38UDPRedundancy")) {
    					peert38capability |= T38FAX_UDP_EC_REDUNDANCY;
    					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
    				} else if (!strcasecmp(s, "t38UDPFEC")) {
    					peert38capability |= T38FAX_UDP_EC_FEC;
    					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
    				} else {
    					peert38capability |= T38FAX_UDP_EC_NONE;
    					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
    				}
    			}
    		}
    		if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */
    			p->t38.peercapability = peert38capability;
    			p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */
    			peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400);
    			p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */
    		}
    		if (debug)
    
    			ast_debug(1, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n",
    
    				p->t38.capability,
    				p->t38.peercapability,
    				p->t38.jointcapability);
    	} else {
    		p->t38.state = T38_DISABLED;
    
    		ast_debug(3, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
    
    
    	/* Now gather all of the codecs that we are asked for: */
    
    	ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability);
    	ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability);
    
    	ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability);
     
    	newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability);
    	newpeercapability = (peercapability | vpeercapability | tpeercapability);
    
    	newnoncodeccapability = p->noncodeccapability & peernoncodeccapability;
    
    		char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ], s5[BUFSIZ];
    
    		ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
    
    			    ast_getformatname_multiple(s1, BUFSIZ, p->capability),
    
    			    ast_getformatname_multiple(s2, BUFSIZ, peercapability),
    
    			    ast_getformatname_multiple(s3, BUFSIZ, vpeercapability),
    
    			    ast_getformatname_multiple(s4, BUFSIZ, tpeercapability),
    			    ast_getformatname_multiple(s5, BUFSIZ, newjointcapability));
    
    
    		ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
    
    			    ast_rtp_lookup_mime_multiple(s1, BUFSIZ, p->noncodeccapability, 0, 0),
    
    			    ast_rtp_lookup_mime_multiple(s2, BUFSIZ, peernoncodeccapability, 0, 0),
    			    ast_rtp_lookup_mime_multiple(s3, BUFSIZ, newnoncodeccapability, 0, 0));
    
    		/* If T.38 was not negotiated either, totally bail out... */
    		if (!p->t38.jointcapability) {
    			ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
    			/* Do NOT Change current setting */
    			return -1;
    		} else {
    
    			ast_debug(3, "Have T.38 but no audio codecs, accepting offer anyway\n");
    
    	}
    
    	/* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
    		they are acceptable */
    
    	p->jointcapability = newjointcapability;	        /* Our joint codec profile for this call */
    	p->peercapability = newpeercapability;		        /* The other sides capability in latest offer */
    	p->jointnoncodeccapability = newnoncodeccapability;	/* DTMF capabilities */
    
    	ast_rtp_pt_copy(p->rtp, newaudiortp);
    	if (p->vrtp)
    		ast_rtp_pt_copy(p->vrtp, newvideortp);
    
    	if (p->trtp)
    		ast_rtp_pt_copy(p->trtp, newtextrtp);
    
    	if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
    		ast_clear_flag(&p->flags[0], SIP_DTMF);
    
    		if (newnoncodeccapability & AST_RTP_DTMF) {
    
    			/* XXX Would it be reasonable to drop the DSP at this point? XXX */
    
    			ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
    
    			/* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */
    			ast_rtp_setdtmf(p->rtp, 1);
    			ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
    
    			ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
    
    	/* Setup audio port number */
    	if (p->rtp && sin.sin_port) {
    		ast_rtp_set_peer(p->rtp, &sin);
    		if (debug)
    
    			ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
    
    	}
    
    	/* Setup video port number */
    	if (p->vrtp && vsin.sin_port) {
    		ast_rtp_set_peer(p->vrtp, &vsin);
    		if (debug) 
    
    			ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port));
    
    	/* Setup text port number */
    	if (p->trtp && tsin.sin_port) {
    		ast_rtp_set_peer(p->trtp, &tsin);
    		if (debug) 
    			ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port));
    	}
    
    
    	/* Ok, we're going with this offer */
    
    	ast_debug(2, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, BUFSIZ, p->jointcapability));
    
    	if (!p->owner) 	/* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
    
    	ast_debug(4, "We have an owner, now see if we need to change this call\n");
    
    	if (!(p->owner->nativeformats & p->jointcapability) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
    
    			ast_debug(1, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", 
    
    				ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability),
    				ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats));
    		}
    
    		p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability) | (p->capability & tpeercapability);
    
    		ast_set_read_format(p->owner, p->owner->readformat);
    		ast_set_write_format(p->owner, p->owner->writeformat);
    	}
    
    	if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) {
    
    		ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
    		/* Activate a re-invite */
    		ast_queue_frame(p->owner, &ast_null_frame);
    
    		/* Queue Manager Unhold event */
    		append_history(p, "Unhold", "%s", req->data);
    		if (global_callevents)
    
    			manager_event(EVENT_FLAG_CALL, "Hold",
    				      "Status: Off\r\n"
    
    				      "Channel: %s\r\n"
    				      "Uniqueid: %s\r\n",
    				      p->owner->name,
    				      p->owner->uniqueid);
    		if (global_notifyhold)
    			sip_peer_hold(p, FALSE);
    		ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
    	} else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) {
    
    		int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD);
    
    		ast_queue_control_data(p->owner, AST_CONTROL_HOLD, 
    				       S_OR(p->mohsuggest, NULL),
    				       !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
    		if (sendonly)
    			ast_rtp_stop(p->rtp);
    		/* RTCP needs to go ahead, even if we're on hold!!! */
    		/* Activate a re-invite */
    		ast_queue_frame(p->owner, &ast_null_frame);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		append_history(p, "Hold", "%s", req->data);
    
    		if (global_callevents && !ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
    
    				      "Channel: %s\r\n"
    				      "Uniqueid: %s\r\n",
    				      p->owner->name, 
    				      p->owner->uniqueid);
    
    		if (sendonly == 1)	/* One directional hold (sendonly/recvonly) */
    
    			ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR);
    		else if (sendonly == 2)	/* Inactive stream */
    			ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE);
    
    			ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ACTIVE);
    
    		if (global_notifyhold && !already_on_hold)
    
    /*! \brief Add header to SIP message */
    
    static int add_header(struct sip_request *req, const char *var, const char *value)
    
    	int maxlen = sizeof(req->data) - 4 - req->len; /* 4 bytes are for two \r\n ? */
    
    
    	if (req->headers == SIP_MAX_HEADERS) {
    		ast_log(LOG_WARNING, "Out of SIP header space\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (req->lines) {
    		ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
    		return -1;
    	}
    
    	if (maxlen <= 0) {
    
    		ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value);
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->header[req->headers] = req->data + req->len;
    
    	if (compactheaders)
    		var = find_alias(var, var);
    
    	snprintf(req->header[req->headers], maxlen, "%s: %s\r\n", var, value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->len += strlen(req->header[req->headers]);
    
    	req->headers++;
    
    /*! \brief Add 'Content-Length' header to SIP message */
    
    static int add_header_contentLength(struct sip_request *req, int len)
    {
    	char clen[10];
    
    	snprintf(clen, sizeof(clen), "%d", len);
    	return add_header(req, "Content-Length", clen);
    }
    
    
    /*! \brief Add content (not header) to SIP message */
    
    static int add_line(struct sip_request *req, const char *line)
    
    	if (req->lines == SIP_MAX_LINES)  {
    		ast_log(LOG_WARNING, "Out of SIP line space\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!req->lines) {
    		/* Add extra empty return */
    
    		ast_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		req->len += strlen(req->data + req->len);
    
    	if (req->len >= sizeof(req->data) - 4) {
    		ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->line[req->lines] = req->data + req->len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
    	req->len += strlen(req->line[req->lines]);
    
    	req->lines++;
    
    /*! \brief Copy one header field from one request to another */
    
    static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field)
    
    	const char *tmp = get_header(orig, field);
    
    	if (!ast_strlen_zero(tmp)) /* Add what we're responding to */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return add_header(req, field, tmp);
    	ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
    	return -1;
    }
    
    
    /*! \brief Copy all headers from one request to another */
    
    static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int start = 0;
    	int copied = 0;
    	for (;;) {
    
    		const char *tmp = __get_header(orig, field, &start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    		/* Add what we're responding to */
    		add_header(req, field, tmp);
    		copied++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	return copied ? 0 : -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \brief Copy SIP VIA Headers from the request to the response
    \note	If the client indicates that it wishes to know the port we received from,
    
    	it adds ;rport without an argument to the topmost via header. We need to
    	add the port number (from our point of view) to that parameter.
    
    	We always add ;received=<ip address> to the topmost via header.
    
    	Received: RFC 3261, rport RFC 3581 */
    
    static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field)
    
    {
    	int copied = 0;
    
    	for (;;) {
    
    		const char *oh = __get_header(orig, field, &start);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (!copied) {	/* Only check for empty rport in topmost via header */
    
    			char leftmost[256], *others, *rport;
    
    			/* Only work on leftmost value */
    			ast_copy_string(leftmost, oh, sizeof(leftmost));
    			others = strchr(leftmost, ',');
    			if (others)
    			    *others++ = '\0';
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    			/* Find ;rport;  (empty request) */
    
    			rport = strstr(leftmost, ";rport");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			if (rport && *(rport+6) == '=') 
    				rport = NULL;		/* We already have a parameter to rport */
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    			/* Check rport if NAT=yes or NAT=rfc3581 (which is the default setting)  */
    			if (rport && ((ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_ALWAYS) || (ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_RFC3581))) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				/* We need to add received port - rport */
    
    				rport = strstr(leftmost, ";rport");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    				if (rport) {
    					end = strchr(rport + 1, ';');
    					if (end)
    						memmove(rport, end, strlen(end) + 1);
    					else
    						*rport = '\0';
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    				/* Add rport to first VIA header if requested */
    
    				snprintf(new, sizeof(new), "%s;received=%s;rport=%d%s%s",
    					leftmost, ast_inet_ntoa(p->recv.sin_addr),
    					ntohs(p->recv.sin_port),
    					others ? "," : "", others ? others : "");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			} else {
    				/* We should *always* add a received to the topmost via */
    
    				snprintf(new, sizeof(new), "%s;received=%s%s%s",
    					leftmost, ast_inet_ntoa(p->recv.sin_addr),
    					others ? "," : "", others ? others : "");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			}
    			oh = new;	/* the header to copy */
    		}  /* else add the following via headers untouched */
    		add_header(req, field, oh);
    		copied++;
    
    	}
    	if (!copied) {
    
    		ast_log(LOG_NOTICE, "No header field '%s' present to copy\n", field);
    
    		return -1;
    	}
    	return 0;
    }
    
    /*! \brief Add route header into request per learned route */
    
    static void add_route(struct sip_request *req, struct sip_route *route)
    {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (!route)
    		return;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (;route ; route = route->next) {
    
    		n = strlen(route->hop);
    
    		if (rem < n+3) /* we need room for ",<route>" */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			break;
    
    		if (p != r) {	/* add a separator after fist route */
    
    			*p++ = ',';
    			--rem;
    		}
    		*p++ = '<';
    
    		ast_copy_string(p, route->hop, rem); /* cannot fail */
    		p += n;
    
    		*p++ = '>';
    		rem -= (n+2);
    	}
    	*p = '\0';
    	add_header(req, "Route", r);
    }
    
    
    /*! \brief Set destination from SIP URI */
    
    static void set_destination(struct sip_pvt *p, char *uri)
    {
    
    	char *h, *maddr, hostname[256];
    
    	int port, hn;
    	struct hostent *hp;
    
    	struct ast_hostent ahp;
    
    	int debug=sip_debug_test_pvt(p);
    
    
    	/* Parse uri to h (host) and port - uri is already just the part inside the <> */
    
    	/* general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...] */
    
    	if (debug)
    
    		ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Find and parse hostname */
    
    	h = strchr(uri, '@');
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (h)
    		++h;
    	else {
    		h = uri;
    
    		if (strncasecmp(h, "sip:", 4) == 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			h += 4;
    
    		else if (strncasecmp(h, "sips:", 5) == 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			h += 5;
    
    	if (hn > sizeof(hostname)) 
    		hn = sizeof(hostname);
    
    	/* XXX bug here if string has been trimmed to sizeof(hostname) */
    
    	/* Is "port" present? if not default to STANDARD_SIP_PORT */
    
    	if (*h == ':') {
    		/* Parse port */
    		++h;
    		port = strtol(h, &h, 10);
    	}
    	else
    
    		port = STANDARD_SIP_PORT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Got the hostname:port - but maybe there's a "maddr=" to override address? */
    	maddr = strstr(h, "maddr=");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		maddr += 6;
    
    		if (hn > sizeof(hostname))
    			hn = sizeof(hostname);
    
    		ast_copy_string(hostname, maddr, hn);
    
    	hp = ast_gethostbyname(hostname, &ahp);
    
    	if (hp == NULL)  {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname);
    
    		return;
    	}
    	p->sa.sin_family = AF_INET;
    	memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
    	p->sa.sin_port = htons(port);
    
    	if (debug)
    
    		ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(p->sa.sin_addr), port);
    
    /*! \brief Initialize SIP response, based on SIP request */
    
    static int init_resp(struct sip_request *resp, const char *msg)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Initialize a response */
    
    	memset(resp, 0, sizeof(*resp));
    	resp->method = SIP_RESPONSE;
    	resp->header[0] = resp->data;
    	snprintf(resp->header[0], sizeof(resp->data), "SIP/2.0 %s\r\n", msg);
    	resp->len = strlen(resp->header[0]);
    	resp->headers++;
    
    /*! \brief Initialize SIP request */
    
    static int init_req(struct sip_request *req, int sipmethod, const char *recip)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* Initialize a request */
    	memset(req, 0, sizeof(*req));
            req->method = sipmethod;
    	req->header[0] = req->data;
    	snprintf(req->header[0], sizeof(req->data), "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
    	req->len = strlen(req->header[0]);
    
    	req->headers++;
    
    /*! \brief Prepare SIP response packet */
    
    static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req)
    
    	copy_via_headers(p, resp, req, "Via");
    
    	if (msg[0] == '1' || msg[0] == '2')
    
    Mark Spencer's avatar
    Mark Spencer committed
    		copy_all_header(resp, req, "Record-Route");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	copy_header(resp, req, "From");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ot = get_header(req, "To");
    
    	if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Add the proper tag if we don't have it already.  If they have specified
    		   their tag, use it.  Otherwise, use our own tag */
    
    		if (!ast_strlen_zero(p->theirtag) && ast_test_flag(&p->flags[0], SIP_OUTGOING))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
    
    		else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING))
    
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ot = newto;
    	}
    	add_header(resp, "To", ot);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	copy_header(resp, req, "Call-ID");
    	copy_header(resp, req, "CSeq");
    
    	if (!ast_strlen_zero(global_useragent))
    		add_header(resp, "User-Agent", global_useragent);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(resp, "Allow", ALLOWED_METHODS);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	add_header(resp, "Supported", SUPPORTED_EXTENSIONS);
    
    	if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* For registration responses, we also need expiry and
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   contact info */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char tmp[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(tmp, sizeof(tmp), "%d", p->expiry);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(resp, "Expires", tmp);
    
    		if (p->expiry) {	/* Only add contact if we have an expiry time */
    
    			char contact[BUFSIZ];
    
    			snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry);
    			add_header(resp, "Contact", contact);	/* Not when we unregister */
    		}
    
    	} else if (msg[0] != '4' && !ast_strlen_zero(p->our_contact)) {
    
    		add_header(resp, "Contact", p->our_contact);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(resp, "Access-URL", p->url);
    
    		ast_string_field_set(p, url, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    /*! \brief Initialize a SIP request message (not the initial one in a dialog) */
    
    static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request *orig = &p->initreq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char tmp[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char newto[256];
    
    	int is_strict = FALSE;		/*!< Strict routing flag */
    
    	int is_outbound = ast_test_flag(&p->flags[0], SIP_OUTGOING);	/* Session direction */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	memset(req, 0, sizeof(struct sip_request));
    
    	snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		p->ocseq++;
    
    
    	/* Check for strict or loose router */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop,";lr") == NULL) {
    
    		is_strict = TRUE;
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (sipdebug)
    
    			ast_debug(1, "Strict routing enforced for session %s\n", p->callid);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	if (sipmethod == SIP_CANCEL)
    
    		c = p->initreq.rlPart2;	/* Use original URI */
    
    	else if (sipmethod == SIP_ACK) {
    
    		/* Use URI from Contact: in 200 OK (if INVITE) 
    		(we only have the contacturi on INVITEs) */
    		if (!ast_strlen_zero(p->okcontacturi))
    
    			c = is_strict ? p->route->hop : p->okcontacturi;
     		else
     			c = p->initreq.rlPart2;
    
    	} else if (!ast_strlen_zero(p->okcontacturi)) 
    
    Olle Johansson's avatar
    Olle Johansson committed
    		c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
    
    	else if (!ast_strlen_zero(p->uri)) 
    
    		c = p->uri;
    
    		/* We have no URI, use To: or From:  header as URI (depending on direction) */
    
    		ast_copy_string(stripped, get_header(orig, is_outbound ? "To" : "From"),
    
    	init_req(req, sipmethod, c);
    
    	snprintf(tmp, sizeof(tmp), "%d %s", seqno, sip_methods[sipmethod].text);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(req, "Via", p->via);
    
    	if (p->route) {
    		set_destination(p, p->route->hop);
    
    		add_route(req, is_strict ? p->route->next : p->route);
    
    	add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	ot = get_header(orig, "To");
    	of = get_header(orig, "From");
    
    	/* Add tag *unless* this is a CANCEL, in which case we need to send it exactly
    	   as our original request, including tag (or presumably lack thereof) */
    
    	if (!strcasestr(ot, "tag=") && sipmethod != SIP_CANCEL) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Add the proper tag if we don't have it already.  If they have specified
    		   their tag, use it.  Otherwise, use our own tag */
    
    		if (is_outbound && !ast_strlen_zero(p->theirtag))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
    
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
    
    		else
    			snprintf(newto, sizeof(newto), "%s", ot);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ot = newto;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(req, "From", of);
    		add_header(req, "To", ot);
    	} else {
    		add_header(req, "From", ot);
    		add_header(req, "To", of);
    	}
    
    	/* Do not add Contact for MESSAGE, BYE and Cancel requests */
    	if (sipmethod != SIP_BYE && sipmethod != SIP_CANCEL && sipmethod != SIP_MESSAGE)
    
    		add_header(req, "Contact", p->our_contact);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	copy_header(req, orig, "Call-ID");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(req, "CSeq", tmp);
    
    
    	if (!ast_strlen_zero(global_useragent))
    		add_header(req, "User-Agent", global_useragent);
    
    	if (!ast_strlen_zero(p->rpid))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(req, "Access-URL", p->url);
    
    		ast_string_field_set(p, url, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    /*! \brief Base transmit response function */
    
    static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int seqno = 0;
    
    	if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	respprep(&resp, p, msg, req);
    
    	add_header_contentLength(&resp, 0);
    
    	/* If we are cancelling an incoming invite for some reason, add information
    		about the reason why we are doing this in clear text */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) {
    		char buf[10];
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
    
    Olle Johansson's avatar
    Olle Johansson committed
    		snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
    		add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return send_response(p, &resp, reliable, seqno);
    }
    
    
    static int temp_pvt_init(void *data)
    {
    	struct sip_pvt *p = data;
    
    
    	p->do_history = 0;	/* XXX do we need it ? isn't already all 0 ? */
    
    static void temp_pvt_cleanup(void *data)
    {
    	struct sip_pvt *p = data;
    
    
    	ast_string_field_free_memory(p);
    
    /*! \brief Transmit response, no retransmits, using a temporary pvt structure */
    static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg)
    {
    	struct sip_pvt *p = NULL;
    
    	if (!(p = ast_threadstorage_get(&ts_temp_pvt, sizeof(*p)))) {
    		ast_log(LOG_NOTICE, "Failed to get temporary pvt\n");
    		return -1;
    	}
    
    
    	/* XXX the structure may be dirty from previous usage.
    	 * Here we should state clearly how we should reinitialize it
    	 * before using it.
    	 * E.g. certainly the threadstorage should be left alone,
    	 * but other thihngs such as flags etc. maybe need cleanup ?
    	 */
    	 
    
    	/* Initialize the bare minimum */
    	p->method = intended_method;
    
    
    	if (!sin)
    		p->ourip = internip;
    	else {
    
    		ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
    
    
    	p->branch = ast_random();
    	make_our_tag(p->tag, sizeof(p->tag));
    	p->ocseq = INITIAL_CSEQ;
    
    	if (useglobal_nat && sin) {
    		ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT);
    		p->recv = *sin;
    		do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
    	}
    
    	ast_string_field_set(p, fromdomain, default_fromdomain);
    	build_via(p);
    	ast_string_field_set(p, callid, callid);
    
    	/* Use this temporary pvt structure to send the message */
    	__transmit_response(p, msg, req, XMIT_UNRELIABLE);
    
    
    	/* Free the string fields, but not the pool space */
    
    	ast_string_field_init(p, 0);
    
    /*! \brief Transmit response, no retransmits */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return __transmit_response(p, msg, req, XMIT_UNRELIABLE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \brief Transmit response, no retransmits */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported) 
    
    {
    	struct sip_request resp;
    	respprep(&resp, p, msg, req);
    	append_date(&resp);
    	add_header(&resp, "Unsupported", unsupported);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_response(p, &resp, XMIT_UNRELIABLE, 0);
    
    /*! \brief Transmit response, Make sure you get an ACK
    	This is only used for responses to INVITEs, where we need to make sure we get an ACK
    */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return __transmit_response(p, msg, req, XMIT_CRITICAL);
    
    /*! \brief Append date to SIP message */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void append_date(struct sip_request *req)
    {
    	char tmpdat[256];
    	struct tm tm;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	gmtime_r(&t, &tm);
    	strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T GMT", &tm);
    	add_header(req, "Date", tmpdat);
    }
    
    
    /*! \brief Append date and content length before transmitting response */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    	respprep(&resp, p, msg, req);
    	append_date(&resp);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_response(p, &resp, XMIT_UNRELIABLE, 0);
    
    /*! \brief Append Accept header, content length before transmitting response */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    	respprep(&resp, p, msg, req);
    	add_header(&resp, "Accept", "application/sdp");
    
    	add_header_contentLength(&resp, 0);
    
    	return send_response(p, &resp, reliable, 0);
    
    /*! \brief Respond with authorization request */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    	int seqno = 0;
    
    	if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
    
    		ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
    		return -1;
    	}
    
    	/* Stale means that they sent us correct authentication, but 
    	   based it on an old challenge (nonce) */
    
    	snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	respprep(&resp, p, msg, req);
    
    	add_header_contentLength(&resp, 0);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	append_history(p, "AuthChal", "Auth challenge sent for %s - nc %d", p->username, p->noncecount);
    
    	return send_response(p, &resp, reliable, seqno);
    
    /*! \brief Add text body to SIP message */
    
    static int add_text(struct sip_request *req, const char *text)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* XXX Convert \n's to \r\n's XXX */
    	add_header(req, "Content-Type", "text/plain");
    
    	add_header_contentLength(req, strlen(text));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_line(req, text);
    	return 0;
    }
    
    
    /*! \brief Add DTMF INFO tone to sip message 
    	Mode = 	0 for application/dtmf-relay (Cisco)
    		1 for application/dtmf
    */
    static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode)
    
    {
    	char tmp[256];
    
    	int event;
    	if (mode) {
    		/* Application/dtmf short version used by some implementations */
    		if (digit == '*')
    			event = 10;
    		else if (digit == '#')
    			event = 11;
    		else if ((digit >= 'A') && (digit <= 'D'))
    			event = 12 + digit - 'A';
    		else
    			event = atoi(&digit);
    		snprintf(tmp, sizeof(tmp), "%d\r\n", event);
    		add_header(req, "Content-Type", "application/dtmf");
    		add_header_contentLength(req, strlen(tmp));
    		add_line(req, tmp);
    	} else {
    		/* Application/dtmf-relay as documented by Cisco */
    		snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=%u\r\n", digit, duration);
    		add_header(req, "Content-Type", "application/dtmf-relay");
    		add_header_contentLength(req, strlen(tmp));
    		add_line(req, tmp);
    	}
    
    /*! \brief add XML encoded media control with update 
    	\note XML: The only way to turn 0 bits of information into a few hundred. (markster) */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int add_vidupdate(struct sip_request *req)
    {
    	const char *xml_is_a_huge_waste_of_space =
    		"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n"
    		" <media_control>\r\n"
    		"  <vc_primitive>\r\n"
    		"   <to_encoder>\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    		"    </picture_fast_update>\r\n"
    		"   </to_encoder>\r\n"
    		"  </vc_primitive>\r\n"
    		" </media_control>\r\n";
    	add_header(req, "Content-Type", "application/media_control+xml");
    	add_header_contentLength(req, strlen(xml_is_a_huge_waste_of_space));
    	add_line(req, xml_is_a_huge_waste_of_space);
    	return 0;
    }
    
    
    /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
    
    static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
    
    			     struct ast_str **m_buf, struct ast_str **a_buf,
    
    			     int debug, int *min_packet_size)
    
    
    	if (debug)
    		ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
    	if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1)
    		return;
    
    
    	if (p->rtp) {
    		struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
    		fmt = ast_codec_pref_getsize(pref, codec);
    	} else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */
    		return;
    
    	ast_str_append(m_buf, 0, " %d", rtp_code);
    	ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
    
    			 ast_rtp_lookup_mime_subtype(1, codec,
    						     ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0),
    
    	if (codec == AST_FORMAT_G729A) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		/* Indicate that we don't support VAD (G.729 annex B) */
    
    		ast_str_append(a_buf, 0, "a=fmtp:%d annexb=no\r\n", rtp_code);
    
    	} else if (codec == AST_FORMAT_G723_1) {
    		/* Indicate that we don't support VAD (G.723.1 annex A) */
    
    		ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code);
    
    	} else if (codec == AST_FORMAT_ILBC) {
    
    		/* Add information about us using only 20/30 ms packetization */
    
    		ast_str_append(a_buf, 0, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms);