Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request req;
    
    	reqprep(&req, p, SIP_MESSAGE, 0, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_text(&req, text);
    
    	return send_request(p, &req, 1, p->ocseq);
    
    /*! \brief Allocate SIP refer structure */
    
    static int sip_refer_allocate(struct sip_pvt *p)
    {
    
    	p->refer = ast_calloc(1, sizeof(struct sip_refer)); 
    	return p->refer ? 1 : 0;
    
    /*! \brief Transmit SIP REFER message (initiated by the transfer() dialplan application
    	\note this is currently broken as we have no way of telling the dialplan
    	engine whether a transfer succeeds or fails.
    	\todo Fix the transfer() dialplan function so that a transfer may fail
    */
    
    static int transmit_refer(struct sip_pvt *p, const char *dest)
    
    	char from[256];
    
    	char referto[256];
    
    	char *ttag, *ftag;
    	char *theirtag = ast_strdupa(p->theirtag);
    
    	if (option_debug || sipdebug)
    		ast_log(LOG_DEBUG, "SIP transfer of %s to %s\n", p->callid, dest);
    
    	/* Are we transfering an inbound or outbound call ? */
    	if (ast_test_flag(&p->flags[0], SIP_OUTGOING))  {
    
    		of = get_header(&p->initreq, "To");
    
    		ttag = theirtag;
    		ftag = p->tag;
    	} else {
    
    		of = get_header(&p->initreq, "From");
    
    	ast_string_field_set(p, from, of);
    
    		ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
    
    		of += 4;
    	/* Get just the username part */
    
    	if ((c = strchr(dest, '@')))
    
    	else if ((c = strchr(of, '@')))
    		*c++ = '\0';
    
    		snprintf(referto, sizeof(referto), "<sip:%s@%s>", dest, c);
    
    		snprintf(referto, sizeof(referto), "<sip:%s>", dest);
    
    
    	add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    
    	/* save in case we get 407 challenge */
    
    	sip_refer_allocate(p);
    	ast_copy_string(p->refer->refer_to, referto, sizeof(p->refer->refer_to));
    	ast_copy_string(p->refer->referred_by, p->our_contact, sizeof(p->refer->referred_by));
    	p->refer->status = REFER_SENT;   /* Set refer status */
    
    	reqprep(&req, p, SIP_REFER, 0, 1);
    	add_header(&req, "Refer-To", referto);
    
    	add_header(&req, "Allow", ALLOWED_METHODS);
    	add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
    
    	if (!ast_strlen_zero(p->our_contact))
    		add_header(&req, "Referred-By", p->our_contact);
    
    	return send_request(p, &req, 1, p->ocseq);
    
    	/* We should propably wait for a NOTIFY here until we ack the transfer */
    	/* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
    
    
    	/*! \todo In theory, we should hang around and wait for a reply, before
    	returning to the dial plan here. Don't know really how that would
    	affect the transfer() app or the pbx, but, well, to make this
    	useful we should have a STATUS code on transfer().
    	*/
    
    /*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
    
    static int transmit_info_with_digit(struct sip_pvt *p, const char digit)
    
    {
    	struct sip_request req;
    
    	reqprep(&req, p, SIP_INFO, 0, 1);
    
    	add_digit(&req, digit);
    
    	return send_request(p, &req, 1, p->ocseq);
    
    /*! \brief Send SIP INFO with video update request */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int transmit_info_with_vidupdate(struct sip_pvt *p)
    {
    	struct sip_request req;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	reqprep(&req, p, SIP_INFO, 0, 1);
    	add_vidupdate(&req);
    	return send_request(p, &req, 1, p->ocseq);
    }
    
    
    /*! \brief Transmit generic SIP request */
    
    static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    	reqprep(&resp, p, sipmethod, seqno, newbranch);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);
    
    /*! \brief Transmit SIP request, auth added */
    
    static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
    
    {
    	struct sip_request resp;
    
    
    	reqprep(&resp, p, sipmethod, seqno, newbranch);
    
    	if (!ast_strlen_zero(p->realm)) {
    
    		char digest[1024];
    
    		memset(digest, 0, sizeof(digest));
    
    		if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) {
    			if (p->options && p->options->auth_type == PROXY_AUTH)
    				add_header(&resp, "Proxy-Authorization", digest);
    			else if (p->options && p->options->auth_type == WWW_AUTH)
    				add_header(&resp, "Authorization", digest);
    			else	/* Default, to be backwards compatible (maybe being too careful, but leaving it for now) */
    				add_header(&resp, "Proxy-Authorization", digest);
    		} else
    
    			ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid);
    
    	/* If we are hanging up and know a cause for that, send it in clear text to make
    		debugging easier. */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (sipmethod == SIP_BYE && p->owner && p->owner->hangupcause)	{
    		char buf[10];
    
    		add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
    		snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
    		add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq);	
    
    /*! \brief Remove registration data from realtime database or AST/DB when registration expires */
    
    static void destroy_association(struct sip_peer *peer)
    {
    
    	if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE)) {
    
    		if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT))
    
    			ast_update_realtime("sippeers", "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "username", "", "regserver", "", NULL);
    
    /*! \brief Expire registration of SIP peer */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int expire_register(void *data)
    {
    
    	struct sip_peer *peer = data;
    
    	
    	if (!peer)		/* Hmmm. We have no peer. Weird. */
    		return 0;
    
    
    	memset(&peer->addr, 0, sizeof(peer->addr));
    
    	destroy_association(peer);	/* remove registration data from storage */
    
    	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
    
    	register_peer_exten(peer, FALSE);	/* Remove regexten */
    
    	peer->expire = -1;
    	ast_device_state_changed("SIP/%s", peer->name);
    
    
    	/* Do we need to release this peer from memory? 
    		Only for realtime peers and autocreated peers
    	*/
    
    	if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT) ||
    	    ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
    
    		peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer);	/* Remove from peer list */
    		ASTOBJ_UNREF(peer, sip_destroy_peer);		/* Remove from memory */
    
    /*! \brief Poke peer (send qualify to check if peer is alive and well) */
    
    static int sip_poke_peer_s(void *data)
    {
    	struct sip_peer *peer = data;
    
    	peer->pokeexpire = -1;
    	sip_poke_peer(peer);
    	return 0;
    }
    
    
    /*! \brief Get registration details from Asterisk DB */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    static void reg_source_db(struct sip_peer *peer)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char data[256];
    
    	struct in_addr in;
    	int expiry;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	int port;
    	char *scan, *addr, *port_str, *expiry_str, *username, *contact;
    
    
    	if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) 
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data)))
    		return;
    
    	scan = data;
    	addr = strsep(&scan, ":");
    	port_str = strsep(&scan, ":");
    	expiry_str = strsep(&scan, ":");
    	username = strsep(&scan, ":");
    
    	contact = scan;	/* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	if (!inet_aton(addr, &in))
    		return;
    
    	if (port_str)
    		port = atoi(port_str);
    	else
    		return;
    
    	if (expiry_str)
    		expiry = atoi(expiry_str);
    	else
    		return;
    
    	if (username)
    
    		ast_copy_string(peer->username, username, sizeof(peer->username));
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (contact)
    
    		ast_copy_string(peer->fullcontact, contact, sizeof(peer->fullcontact));
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n",
    
    			    peer->name, peer->username, ast_inet_ntoa(in), port, expiry);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    	memset(&peer->addr, 0, sizeof(peer->addr));
    	peer->addr.sin_family = AF_INET;
    	peer->addr.sin_addr = in;
    	peer->addr.sin_port = htons(port);
    	if (sipsock < 0) {
    		/* SIP isn't up yet, so schedule a poke only, pretty soon */
    		if (peer->pokeexpire > -1)
    			ast_sched_del(sched, peer->pokeexpire);
    
    		peer->pokeexpire = ast_sched_add(sched, ast_random() % 5000 + 1, sip_poke_peer_s, peer);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	} else
    		sip_poke_peer(peer);
    	if (peer->expire > -1)
    		ast_sched_del(sched, peer->expire);
    	peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
    
    /*! \brief Save contact header for 200 OK on INVITE */
    
    static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
    {
    
    
    	/* Look for brackets */
    
    	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
    
    	c = get_in_brackets(contact);
    
    
    	/* Save full contact to call pvt for later bye or re-invite */
    
    	ast_string_field_set(pvt, fullcontact, c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Save URI for later ACKs, BYE or RE-invites */
    
    	ast_string_field_set(pvt, okcontacturi, c);
    
    
    	/* We should return false for URI:s we can't handle,
    		like sips:, tel:, mailto:,ldap: etc */
    	return TRUE;		
    }
    
    /*! \brief Change the other partys IP address based on given contact */
    static int set_address_from_contact(struct sip_pvt *pvt)
    {
    	struct hostent *hp;
    	struct ast_hostent ahp;
    	int port;
    	char *c, *host, *pt;
    	char *contact;
    
    
    	if (ast_test_flag(&pvt->flags[0], SIP_NAT_ROUTE)) {
    		/* NAT: Don't trust the contact field.  Just use what they came to us
    		   with. */
    		pvt->sa = pvt->recv;
    		return 0;
    	}
    
    
    	/* Work on a copy */
    	contact = ast_strdupa(pvt->fullcontact);
    
    
    	/* XXX this code is repeated all over */
    
    	/* Make sure it's a SIP URL */
    
    	if (strncasecmp(contact, "sip:", 4)) {
    		ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact);
    
    
    	/* Ditch arguments */
    
    	/* XXX this code is replicated also shortly below */
    
    	contact = strsep(&contact, ";");	/* trim ; and beyond */
    
    	host = strchr(contact, '@');
    	if (!host) {	/* No username part */
    		host = contact;
    
    		c = NULL;
    	} else {
    
    		port = atoi(pt);
    	} else
    		port = DEFAULT_SIP_PORT;
    
    
    	/* XXX This could block for a long time XXX */
    	/* We should only do this if it's a name, not an IP */
    	hp = ast_gethostbyname(host, &ahp);
    	if (!hp)  {
    		ast_log(LOG_WARNING, "Invalid host name in Contact: (can't resolve in DNS) : '%s'\n", host);
    		return -1;
    
    	pvt->sa.sin_family = AF_INET;
    	memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr));
    	pvt->sa.sin_port = htons(port);
    
    
    /*! \brief Parse contact header and save registration (peer registration) */
    static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req)
    
    	char contact[BUFSIZ]; 
    	char data[BUFSIZ];
    
    	const char *expires = get_header(req, "Expires");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int expiry = atoi(expires);
    
    	char *curi, *n, *pt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int port;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct hostent *hp;
    
    	struct ast_hostent ahp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sockaddr_in oldsin;
    
    	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
    
    
    	if (ast_strlen_zero(expires)) {	/* No expires header */
    
    		expires = strcasestr(contact, ";expires=");
    
    		if (expires) {
    
    			/* XXX bug here, we overwrite the string */
    
    			expires = strsep((char **) &expires, ";"); /* trim ; and beyond */
    
    			if (sscanf(expires + 9, "%d", &expiry) != 1)
    
    				expiry = default_expiry;
    		} else {
    			/* Nothing has been specified */
    			expiry = default_expiry;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Look for brackets */
    
    	curi = contact;
    
    	if (strchr(contact, '<') == NULL)	/* No <, check for ; and strip it */
    
    		strsep(&curi, ";");	/* This is Header options, not URI options */
    	curi = get_in_brackets(contact);
    
    	/* if they did not specify Contact: or Expires:, they are querying
    	   what we currently have stored as their contact address, so return
    	   it
    	*/
    
    	if (ast_strlen_zero(curi) && ast_strlen_zero(expires)) {
    
    		/* If we have an active registration, tell them when the registration is going to expire */
    
    		if (peer->expire > -1 && !ast_strlen_zero(peer->fullcontact))
    			pvt->expiry = ast_sched_when(sched, peer->expire);
    
    		return PARSE_REGISTER_QUERY;
    
    	} else if (!strcasecmp(curi, "*") || !expiry) {	/* Unregister this peer */
    
    		/* This means remove all registrations and return OK */
    
    		memset(&peer->addr, 0, sizeof(peer->addr));
    		if (peer->expire > -1)
    			ast_sched_del(sched, peer->expire);
    		peer->expire = -1;
    
    		destroy_association(peer);
    
    		register_peer_exten(peer, 0);	/* Add extension from regexten= setting in sip.conf */
    		peer->fullcontact[0] = '\0';
    		peer->useragent[0] = '\0';
    		peer->sipoptions = 0;
    		peer->lastms = 0;
    
    		if (option_verbose > 2)
    
    			ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", peer->name);
    			manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", peer->name);
    
    		return PARSE_REGISTER_UPDATE;
    
    
    	/* Store whatever we got as a contact from the client */
    	ast_copy_string(peer->fullcontact, curi, sizeof(peer->fullcontact));
    
    
    	/* For the 200 OK, we should use the received contact */
    
    	ast_string_field_build(pvt, our_contact, "<%s>", curi);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Make sure it's a SIP URL */
    
    	if (strncasecmp(curi, "sip:", 4)) {
    		ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", curi);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Ditch q */
    
    	curi = strsep(&curi, ";");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Grab host */
    
    	n = strchr(curi, '@');
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!n) {
    
    		n = curi;
    		curi = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pt = strchr(n, ':');
    	if (pt) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		port = atoi(pt);
    	} else
    		port = DEFAULT_SIP_PORT;
    
    	oldsin = peer->addr;
    	if (!ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE)) {
    
    		/* XXX This could block for a long time XXX */
    
    		hp = ast_gethostbyname(n, &ahp);
    
    		if (!hp)  {
    			ast_log(LOG_WARNING, "Invalid host '%s'\n", n);
    
    			return PARSE_REGISTER_FAILED;
    
    		peer->addr.sin_family = AF_INET;
    		memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
    		peer->addr.sin_port = htons(port);
    
    	} else {
    		/* Don't trust the contact field.  Just use what they came to us
    		   with */
    
    		peer->addr = pvt->recv;
    
    	/* Save SIP options profile */
    
    	peer->sipoptions = pvt->sipoptions;
    
    	if (curi)	/* Overwrite the default username from config at registration */
    		ast_copy_string(peer->username, curi, sizeof(peer->username));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    		peer->username[0] = '\0';
    
    	if (peer->expire > -1)
    		ast_sched_del(sched, peer->expire);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		expiry = max_expiry;
    
    	if (expiry < min_expiry)
    		expiry = min_expiry;
    
    	peer->expire = ast_test_flag(&peer->flags[0], SIP_REALTIME) ? -1 :
    
    		ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pvt->expiry = expiry;
    
    	snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expiry, peer->username, peer->fullcontact);
    
    	if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) 
    		ast_db_put("SIP/Registry", peer->name, data);
    	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name);
    
    	/* Is this a new IP address for us? */
    	if (inaddrcmp(&peer->addr, &oldsin)) {
    		sip_poke_peer(peer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_verbose > 2)
    
    			ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s port %d expires %d\n", peer->name, ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expiry);
    
    		register_peer_exten(peer, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	useragent = get_header(req, "User-Agent");
    
    	if (useragent && strcasecmp(useragent, peer->useragent)) {
    		ast_copy_string(peer->useragent, useragent, sizeof(peer->useragent));
    
    			ast_verbose(VERBOSE_PREFIX_3 "Saved useragent \"%s\" for peer %s\n", peer->useragent, peer->name);  
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	return PARSE_REGISTER_UPDATE;
    
    /*! \brief Remove route from route list */
    
    static void free_old_route(struct sip_route *route)
    {
    	struct sip_route *next;
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    	while (route) {
    		next = route->next;
    		free(route);
    		route = next;
    	}
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief List all routes - mostly for debugging */
    
    static void list_route(struct sip_route *route)
    {
    
    		ast_verbose("list_route: no route\n");
    
    	else {
    		for (;route; route = route->next)
    			ast_verbose("list_route: hop: <%s>\n", route->hop);
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Build route list from Record-Route header */
    
    static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards)
    {
    	struct sip_route *thishop, *head, *tail;
    	int start = 0;
    	int len;
    
    	/* Once a persistant route is set, don't fool with it */
    	if (p->route && p->route_persistant) {
    		ast_log(LOG_DEBUG, "build_route: Retaining previous route: <%s>\n", p->route->hop);
    		return;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p->route) {
    		free_old_route(p->route);
    		p->route = NULL;
    	}
    
    	
    	p->route_persistant = backwards;
    	
    
    	/* Build a tailq, then assign it to p->route when done.
    	 * If backwards, we add entries from the head so they end up
    	 * in reverse order. However, we do need to maintain a correct
    	 * tail pointer because the contact is always at the end.
    	 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* 1st we pass through all the hops in any Record-Route headers */
    
    	for (;;) {
    		/* Each Record-Route header */
    		rr = __get_header(req, "Record-Route", &start);
    
    		for (; (rr = strchr(rr, '<')) ; rr += len) { /* Each route entry */
    
    			/* Make a struct route */
    
    			if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
    				/* ast_calloc is not needed because all fields are initialized in this block */
    
    				if (option_debug > 1)
    					ast_log(LOG_DEBUG, "build_route: Record-Route hop: <%s>\n", thishop->hop);
    
    				/* Link in */
    				if (backwards) {
    					/* Link in at head so they end up in reverse order */
    					thishop->next = head;
    					head = thishop;
    					/* If this was the first then it'll be the tail */
    
    					if (!tail)
    						tail = thishop;
    
    					thishop->next = NULL;
    
    					/* Link in at the end */
    					if (tail)
    						tail->next = thishop;
    					else
    						head = thishop;
    					tail = thishop;
    				}
    			}
    		}
    	}
    
    
    	/* Only append the contact if we are dealing with a strict router */
    	if (!head || (!ast_strlen_zero(head->hop) && strstr(head->hop,";lr") == NULL) ) {
    		/* 2nd append the Contact: if there is one */
    		/* Can be multiple Contact headers, comma separated values - we just take the first */
    		contact = get_header(req, "Contact");
    		if (!ast_strlen_zero(contact)) {
    
    			if (option_debug > 1)
    
    				ast_log(LOG_DEBUG, "build_route: Contact hop: %s\n", contact);
    
    			/* Look for <: delimited address */
    			c = strchr(contact, '<');
    			if (c) {
    				/* Take to > */
    				++c;
    				len = strcspn(c, ">") + 1;
    			} else {
    				/* No <> - just take the lot */
    				c = contact;
    				len = strlen(contact) + 1;
    			}
    
    			if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
    				/* ast_calloc is not needed because all fields are initialized in this block */
    
    				ast_copy_string(thishop->hop, c, len);
    
    				thishop->next = NULL;
    				/* Goes at the end */
    				if (tail)
    					tail->next = thishop;
    				else
    					head = thishop;
    			}
    
    	/* Store as new route */
    	p->route = head;
    
    	/* For debugging dump what we ended up with */
    
    /*! \brief  Check user authorization from peer definition 
    	Some actions, like REGISTER and INVITEs from peers require
    
    Olle Johansson's avatar
    Olle Johansson committed
    	authentication (if peer have secret set) 
    
        \return 0 on success, non-zero on error
    
    Olle Johansson's avatar
    Olle Johansson committed
    */
    
    static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
    					 const char *secret, const char *md5secret, int sipmethod,
    					 char *uri, enum xmittype reliable, int ignore)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	const char *response = "407 Proxy Authentication Required";
    	const char *reqheader = "Proxy-Authorization";
    	const char *respheader = "Proxy-Authenticate";
    	const char *authtoken;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	char a1_hash[256];
    	char resp_hash[256]="";
    	char tmp[BUFSIZ * 2];                /* Make a large enough buffer */
    	char *c;
    	int  wrongnonce = FALSE;
    	int  good_response;
    	const char *usednonce = p->randdata;
    
    	/* table of recognised keywords, and their value in the digest */
    	enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST };
    	struct x {
    		const char *key;
    		const char *s;
    	} *i, keys[] = {
    		[K_RESP] = { "response=", "" },
    		[K_URI] = { "uri=", "" },
    		[K_USER] = { "username=", "" },
    		[K_NONCE] = { "nonce=", "" },
    		[K_LAST] = { NULL, NULL}
    	};
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Always OK if no secret */
    
    	if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret))
    
    	if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
    
    		/* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family
    		   of headers -- GO SIP!  Whoo hoo!  Two things that do the same thing but are used in
    		   different circumstances! What a surprise. */
    		response = "401 Unauthorized";
    		reqheader = "Authorization";
    		respheader = "WWW-Authenticate";
    	}
    	authtoken =  get_header(req, reqheader);	
    
    	if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
    
    		/* This is a retransmitted invite/register/etc, don't reconstruct authentication
    		   information */
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (!reliable) {
    			/* Resend message if this was NOT a reliable delivery.   Otherwise the
    			   retransmission should get it */
    			transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
    			/* Schedule auto destroy in 32 seconds (according to RFC 3261) */
    
    			sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
    
    	} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		/* We have no auth, so issue challenge and request authentication */
    
    		ast_string_field_build(p, randdata, "%08lx", ast_random());	/* Create nonce for challenge */
    
    		transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		/* Schedule auto destroy in 32 seconds */
    
    		sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	} 
    
    Olle Johansson's avatar
    Olle Johansson committed
    	/* --- We have auth, so check it */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
       	   an example in the spec of just what it is you're doing a hash on. */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	/* Make a copy of the response and parse it */
    	ast_copy_string(tmp, authtoken, sizeof(tmp));
    	c = tmp;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */
    		for (i = keys; i->key != NULL; i++) {
    			const char *separator = ",";	/* default */
    
    Olle Johansson's avatar
    Olle Johansson committed
    			if (strncasecmp(c, i->key, strlen(i->key)) != 0)
    				continue;
    			/* Found. Skip keyword, take text in quotes or up to the separator. */
    			c += strlen(i->key);
    			if (*c == '"') { /* in quotes. Skip first and look for last */
    				c++;
    				separator = "\"";
    
    Olle Johansson's avatar
    Olle Johansson committed
    			i->s = c;
    			strsep(&c, separator);
    			break;
    		}
    		if (i->key == NULL) /* not found, jump after space or comma */
    			strsep(&c, " ,");
    	}
    
    Olle Johansson's avatar
    Olle Johansson committed
    	/* Verify that digest username matches  the username we auth as */
    	if (strcmp(username, keys[K_USER].s)) {
    		ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
    			username, keys[K_USER].s);
    		/* Oops, we're trying something here */
    		return AUTH_USERNAME_MISMATCH;
    	}
    
    Olle Johansson's avatar
    Olle Johansson committed
    	/* Verify nonce from request matches our nonce.  If not, send 401 with new nonce */
    	if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */
    		wrongnonce = TRUE;
    		usednonce = keys[K_NONCE].s;
    	}
    
    	if (!ast_strlen_zero(md5secret))
    		ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
    	else {
    		char a1[256];
    		snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret);
    		ast_md5_hash(a1_hash, a1);
    	}
    
    	/* compute the expected response to compare with what we received */
    	{
    		char a2[256];
    		char a2_hash[256];
    		char resp[256];
    
    		snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text,
    				S_OR(keys[K_URI].s, uri));
    		ast_md5_hash(a2_hash, a2);
    		snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
    		ast_md5_hash(resp_hash, resp);
    	}
    
    	good_response = keys[K_RESP].s &&
    			!strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash));
    	if (wrongnonce) {
    		ast_string_field_build(p, randdata, "%08lx", ast_random());
    		if (good_response) {
    			if (sipdebug)
    				ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To"));
    			/* We got working auth token, based on stale nonce . */
    			transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 1);
    		} else {
    			/* Everything was wrong, so give the device one more try with a new challenge */
    			if (sipdebug)
    				ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To"));
    			transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
    		}
    
    		/* Schedule auto destroy in 32 seconds */
    
    		sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	} 
    	if (good_response)
    		return AUTH_SUCCESSFUL;
    
    	/* Ok, we have a bad username/secret pair */
    	/* Challenge again, and again, and again */
    	transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
    	sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
    
    	return AUTH_CHALLENGE_SENT;
    
    /*! \brief Change onhold state of a peer using a pvt structure */
    static void sip_peer_hold(struct sip_pvt *p, int hold)
    {
    	struct sip_peer *peer = find_peer(p->peername, NULL, 1);
    
    	if (!peer)
    		return;
    
    	/* If they put someone on hold, increment the value... otherwise decrement it */
    	if (hold)
    		peer->onHold++;
    
    		peer->onHold--;
    
    	/* Request device state update */
    	ast_device_state_changed("SIP/%s", peer->name);
    
    	return;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
    \note	If you add an "hint" priority to the extension in the dial plan,
    	you will get notifications on device state changes */
    
    static int cb_extensionstate(char *context, char* exten, int state, void *data)
    
    	struct sip_pvt *p = data;
    
    	switch(state) {
    	case AST_EXTENSION_DEACTIVATED:	/* Retry after a while */
    	case AST_EXTENSION_REMOVED:	/* Extension is gone */
    
    		if (p->autokillid > -1)
    			sip_cancel_destroy(p);	/* Remove subscription expiry for renewals */
    
    		sip_scheddestroy(p, SIP_TRANS_TIMEOUT);	/* Delete subscription in 32 secs */
    
    		ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
    
    		p->stateid = -1;
    
    		append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
    
    		break;
    	default:	/* Tell user */
    		p->laststate = state;
    		break;
    
    	transmit_state_notify(p, state, 1);
    
    
    	if (option_debug > 1)
    		ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s new state %s for Notify User %s\n", exten, ast_extension_state2str(state), p->username);
    	return 0;
    
    /*! \brief Send a fake 401 Unauthorized response when the administrator
      wants to hide the names of local users/peers from fishers
     */
    static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable)
    {
    	ast_string_field_build(p, randdata, "%08lx", ast_random());	/* Create nonce for challenge */
    	transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0);
    }
    
    
    /*! \brief Verify registration of user 
    	- Registration is done in several steps, first a REGISTER without auth
    	  to get a challenge (nonce) then a second one with auth
    	- Registration requests are only matched with peers that are marked as "dynamic"
     */
    
    static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin,
    					      struct sip_request *req, char *uri)
    
    	enum check_auth_result res = AUTH_NOT_FOUND;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_peer *peer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *name, *c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *t;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Terminate URI */
    	t = uri;
    	while(*t && (*t > 32) && (*t != ';'))
    		t++;
    	*t = '\0';
    	
    
    	ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp));
    
    	if (pedanticsipchecking)
    		ast_uri_decode(tmp);
    
    
    	c = strsep(&c, ";");	/* Ditch ;user=phone */
    
    	if (!strncmp(c, "sip:", 4)) {
    		name = c + 4;
    	} else {
    		name = c;
    
    		ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(sin->sin_addr));
    
    	/* Strip off the domain name */
    
    	if ((c = strchr(name, '@'))) {
    		*c++ = '\0';
    		domain = c;
    		if ((c = strchr(domain, ':')))	/* Remove :port */
    			*c = '\0';
    		if (!AST_LIST_EMPTY(&domain_list)) {
    			if (!check_sip_domain(domain, NULL, 0)) {
    				transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
    
    	ast_string_field_set(p, exten, name);
    
    	build_contact(p);
    
    	peer = find_peer(name, NULL, 1);
    
    	if (!(peer && ast_apply_ha(peer->ha, sin))) {
    
    			ASTOBJ_UNREF(peer, sip_destroy_peer);
    
    	}
    	if (peer) {
    
    		if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)) {
    
    			ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name);
    
    			ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT);
    
    			transmit_response(p, "100 Trying", req);
    
    			if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, XMIT_UNRELIABLE, ast_test_flag(req, SIP_PKT_IGNORE)))) {
    
    				sip_cancel_destroy(p);
    
    
    				/* We have a succesful registration attemp with proper authentication,
    				   now, update the peer */
    
    				switch (parse_register_contact(p, peer, req)) {
    				case PARSE_REGISTER_FAILED:
    
    					ast_log(LOG_WARNING, "Failed to parse contact info\n");
    
    					transmit_response_with_date(p, "400 Bad Request", req);
    					peer->lastmsgssent = -1;
    					res = 0;
    
    					break;
    				case PARSE_REGISTER_QUERY:
    					transmit_response_with_date(p, "200 OK", req);
    					peer->lastmsgssent = -1;
    					res = 0;
    					break;
    				case PARSE_REGISTER_UPDATE:
    
    					update_peer(peer, p->expiry);
    					/* Say OK and ask subsystem to retransmit msg counter */
    					transmit_response_with_date(p, "200 OK", req);
    
    					if (!ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY))
    						peer->lastmsgssent = -1;
    
    	if (!peer && autocreatepeer) {
    		/* Create peer if we have autocreate mode enabled */
    		peer = temp_peer(name);
    		if (peer) {
    
    			sip_cancel_destroy(p);
    
    			switch (parse_register_contact(p, peer, req)) {
    			case PARSE_REGISTER_FAILED:
    
    				ast_log(LOG_WARNING, "Failed to parse contact info\n");
    
    				transmit_response_with_date(p, "400 Bad Request", req);
    				peer->lastmsgssent = -1;
    				res = 0;
    
    				break;
    			case PARSE_REGISTER_QUERY:
    				transmit_response_with_date(p, "200 OK", req);
    				peer->lastmsgssent = -1;
    				res = 0;
    				break;
    			case PARSE_REGISTER_UPDATE:
    
    				/* Say OK and ask subsystem to retransmit msg counter */
    				transmit_response_with_date(p, "200 OK", req);
    
    				manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name);
    
    				peer->lastmsgssent = -1;
    				res = 0;
    
    		ast_device_state_changed("SIP/%s", peer->name);
    
    			/* Wrong password in authentication. Go away, don't try again until you fixed it */
    
    			transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
    
    			/* Username and digest username does not match. 
    			   Asterisk uses the From: username for authentication. We need the
    			   users to use the same authentication user name until we support
    			   proper authentication by digest auth name */
    			transmit_response(p, "403 Authentication user name does not match account name", &p->initreq);
    			break;
    
    		case AUTH_NOT_FOUND:
    			if (global_alwaysauthreject) {
    				transmit_fake_auth_response(p, &p->initreq, 1);
    			} else {
    				/* URI not found */
    				transmit_response(p, "404 Not found", &p->initreq);
    			}
    			break;
    		default:
    
    			const char *reason = "";
    
    			switch (res) {
    			case AUTH_SECRET_FAILED:
    				reason = "Bad password";
    				break;
    			case AUTH_USERNAME_MISMATCH:
    				reason = "Bad digest user";
    				break;
    			case AUTH_NOT_FOUND: