Skip to content
Snippets Groups Projects
chan_sip.c 482 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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;
    	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 and 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", "", NULL);
    		} else {
    			ast_db_del("SIP/Registry", peer->name);
    		}
    	}
    }
    
    
    /*! \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];
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	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(iabuf, sizeof(iabuf), in), port, expiry);
    
    	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 */
    
    	host = strchr(contact, ';');
    	if (host) 
    		*host = '\0';
    
    	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);
    
    
    enum parse_register_result {
    	PARSE_REGISTER_FAILED,
    	PARSE_REGISTER_UPDATE,
    	PARSE_REGISTER_QUERY,
    };
    
    
    /*! \brief Parse contact header and save registration */
    
    static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req)
    
    	char contact[BUFSIZ]; 
    	char data[BUFSIZ];
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	const char *expires = get_header(req, "Expires");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int expiry = atoi(expires);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *c, *n, *pt;
    	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;
    
    
    	if (ast_strlen_zero(expires)) {	/* No expires header */
    
    		expires = strcasestr(get_header(req, "Contact"), ";expires=");
    
    		if (expires) {
    
    			char *ptr;
    			if ((ptr = strchr(expires, ';')))
    				*ptr = '\0';
    			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 */
    
    	ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact));
    
    	if (strchr(contact, '<') == NULL) {	/* No <, check for ; and strip it */
    		char *ptr = strchr(contact, ';');	/* This is Header options, not URI options */
    		if (ptr)
    			*ptr = '\0';
    	}
    
    	/* 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(c) && ast_strlen_zero(expires)) {
    
    		/* If we have an active registration, tell them when the registration is going to expire */
    
    		if (p->expire > -1 && !ast_strlen_zero(p->fullcontact))
    
    			pvt->expiry = ast_sched_when(sched, p->expire);
    
    		return PARSE_REGISTER_QUERY;
    
    	} else if (!strcasecmp(c, "*") || !expiry) {	/* Unregister this peer */
    
    		/* This means remove all registrations and return OK */
    		memset(&p->addr, 0, sizeof(p->addr));
    		if (p->expire > -1)
    			ast_sched_del(sched, p->expire);
    		p->expire = -1;
    
    		p->fullcontact[0] = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    		p->lastms = 0;
    
    		if (option_verbose > 2)
    
    			ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", p->name);
    
    			manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", p->name);
    
    		return PARSE_REGISTER_UPDATE;
    
    	ast_copy_string(p->fullcontact, c, sizeof(p->fullcontact));
    
    	/* For the 200 OK, we should use the received contact */
    
    	ast_string_field_build(pvt, our_contact, "<%s>", c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Make sure it's a SIP URL */
    	if (strncasecmp(c, "sip:", 4)) {
    
    		ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", c);
    	} else
    		c += 4;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Ditch q */
    	n = strchr(c, ';');
    
    Mark Spencer's avatar
    Mark Spencer committed
    		*n = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Grab host */
    	n = strchr(c, '@');
    	if (!n) {
    		n = c;
    		c = 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;
    
    	if (!ast_test_flag(&p->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;
    
    		}
    		p->addr.sin_family = AF_INET;
    		memcpy(&p->addr.sin_addr, hp->h_addr, sizeof(p->addr.sin_addr));
    		p->addr.sin_port = htons(port);
    	} else {
    		/* Don't trust the contact field.  Just use what they came to us
    		   with */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (c)	/* Overwrite the default username from config at registration */
    
    		ast_copy_string(p->username, c, sizeof(p->username));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p->expire > -1)
    		ast_sched_del(sched, p->expire);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		expiry = max_expiry;
    
    	if (expiry < min_expiry)
    		expiry = min_expiry;
    
    	p->expire = ast_test_flag(&p->flags[0], SIP_REALTIME) ? -1 :
    		ast_sched_add(sched, (expiry + 10) * 1000, expire_register, p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pvt->expiry = expiry;
    
    	snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry, p->username, p->fullcontact);
    
    	if (!ast_test_flag(&p->flags[1], SIP_PAGE2_RT_FROMCONTACT)) 
    
    		ast_db_put("SIP/Registry", p->name, data);
    
    	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", p->name);
    
    	if (inaddrcmp(&p->addr, &oldsin)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sip_poke_peer(p);
    
    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", p->name, ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry);
    
    	
    	/* Save SIP options profile */
    	p->sipoptions = pvt->sipoptions;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	useragent = get_header(req, "User-Agent");
    
    	if (useragent && strcasecmp(useragent, p->useragent)) {
    
    		ast_copy_string(p->useragent, useragent, sizeof(p->useragent));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_verbose(VERBOSE_PREFIX_3 "Saved useragent \"%s\" for peer %s\n",p->useragent,p->name);  
    	}
    
    	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;
    	
    
    	/* We build up head, then assign it to p->route when we're done */
    
    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 (;;) {
    			/* Each route entry */
    			/* Find < */
    			rr = strchr(rr, '<');
    
    			if (!rr) 
    				break; /* No more hops */
    
    			/* Make a struct route */
    
    			if ((thishop = ast_malloc(sizeof(*thishop) + len))) {
    				/* ast_calloc is not needed because all fields are initialized in this block */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				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;
    				} else {
    
    					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)) {
    			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 -1 on Error, 0 on success, 1 on challenge sent
    	
    */
    
    static int 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;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Always OK if no secret */
    
    	if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    	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);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		return 1;	/* Auth sent */
    
    	} 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
    		return 1;	/* Auth sent */
    	} else {	/* We have auth, so check it */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
    
    Olle Johansson's avatar
    Olle Johansson committed
    	   	an example in the spec of just what it is you're doing a hash on. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char a1_hash[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char *c;
    
    		int  wrongnonce = FALSE;
    
    Olle Johansson's avatar
    Olle Johansson committed
    		int  good_response;
    		const char *usednonce = p->randdata; /* XXX check */
    
    		/* 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}
    		};
    
    		/* Make a copy of the response and parse it */
    
    		ast_copy_string(tmp, authtoken, sizeof(tmp));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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 */
    
    				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, " ,");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* Verify that digest username matches  the username we auth as */
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (strcmp(username, keys[K_USER].s)) {
    			ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
    				username, keys[K_USER].s);
    
    		/* Verify nonce from request matches our nonce.  If not, send 401 with new nonce */
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */
    
    			wrongnonce = TRUE;
    
    Olle Johansson's avatar
    Olle Johansson committed
    			usednonce = keys[K_NONCE].s;
    
    		if (!ast_strlen_zero(md5secret))
    
    Olle Johansson's avatar
    Olle Johansson committed
    			ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
    		else {
    			char a1[256];
    			snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		}
    
    Olle Johansson's avatar
    Olle Johansson committed
    		/* 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));
    
    Olle Johansson's avatar
    Olle Johansson committed
    			ast_md5_hash(a2_hash, a2);
    			snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
    			ast_md5_hash(resp_hash, resp);
    		}
    
    Olle Johansson's avatar
    Olle Johansson committed
    		good_response = keys[K_RESP].s &&
    				!strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash));
    
    			ast_string_field_build(p, randdata, "%08lx", ast_random());
    
    Olle Johansson's avatar
    Olle Johansson committed
    			if (good_response) {
    
    					ast_log(LOG_NOTICE, "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);
    
    				/* Everything was wrong, so give the device one more try with a new challenge */
    
    					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);
    
    Olle Johansson's avatar
    Olle Johansson committed
    			/* Schedule auto destroy in 32 seconds */
    
    			sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
    
    			return 1;	/* XXX should it be -1 ? */
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (good_response) /* Auth is OK */
    			return 0;
    
    		/* 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);
    
    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;
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Verify registration of user */
    
    static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req, char *uri)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_peer *peer;
    
    	char iabuf[INET_ADDRSTRLEN];
    
    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);
    
    
    	/* Ditch ;user=phone */
    	name = strchr(c, ';');
    	if (name)
    		*name = '\0';
    
    
    	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(iabuf, sizeof(iabuf), 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);
    				return -3;
    			}
    		}
    	}
    
    
    	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);
    
    				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);
    
    	if (res < 0) {
    		switch (res) {
    		case -1:
    			/* Wrong password in authentication. Go away, don't try again until you fixed it */
    
    			transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
    
    			break;
    		case -2:
    			/* 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 -3:
    			/* URI not found */
    			transmit_response(p, "404 Not found", &p->initreq);
    
    			/* Set res back to -2 because we don't want to return an invalid domain message. That check already happened up above. */
    			res = -2;
    
    			break;
    		}
    		if (option_debug > 1) {
    			ast_log(LOG_DEBUG, "SIP REGISTER attempt failed for %s : %s\n",
    				peer->name,
    				(res == -1) ? "Bad password" : ((res == -2 ) ? "Bad digest user" : "Peer not found"));
    
    		ASTOBJ_UNREF(peer, sip_destroy_peer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Get referring dnis */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_request *req;
    	
    	req = oreq;
    	if (!req)
    		req = &p->initreq;
    
    	ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp));
    
    	if (ast_strlen_zero(tmp))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (strncmp(c, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Huh?  Not an RDNIS SIP header (%s)?\n", c);
    		return -1;
    	}
    	c += 4;
    
    	a = c;
    	strsep(&a, "@;");	/* trim anything after @ or ; */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose("RDNIS is %s\n", c);
    
    	ast_string_field_set(p, rdnis, c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    /*! \brief Find out who the call is for 
    	We use the INVITE uri to find out
    */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
    
    	char tmp[256] = "", *uri, *a;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_request *req;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	req = oreq;
    	if (!req)
    		req = &p->initreq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (req->rlPart2)
    
    		ast_copy_string(tmp, req->rlPart2, sizeof(tmp));
    
    	if (strncmp(uri, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", uri);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    
    	/* Now find the From: caller ID and name */
    	ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf));
    	if (!ast_strlen_zero(tmpf)) {
    		if (pedanticsipchecking)
    			ast_uri_decode(tmpf);
    		from = get_in_brackets(tmpf);
    	} else {
    		from = NULL;
    	}
    	
    
    	if (!ast_strlen_zero(from)) {
    		if (strncmp(from, "sip:", 4)) {
    			ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", from);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    
    		if ((a = strchr(from, ';')))
    			*a = '\0';
    		if ((a = strchr(from, '@'))) {
    			*a = '\0';
    			ast_string_field_set(p, fromdomain, a + 1);
    		} else
    			ast_string_field_set(p, fromdomain, from);
    
    	/* Skip any options and find the domain */
    
    	if ((a = strchr(uri, ';')))
    
    	/* Get the target domain */
    	if ((a = strchr(uri, '@'))) {
    
    		colon = strchr(a, ':'); /* Remove :port */
    		if (colon)
    			*colon = '\0';
    
    	} else {	/* No username part */
    		a = uri;
    		uri = "s";	/* Set extension to "s" */
    
    	colon = strchr(a, ':'); /* Remove :port */
    	if (colon)
    		*colon = '\0';
    
    	ast_string_field_set(p, domain, a);
    
    
    	if (!AST_LIST_EMPTY(&domain_list)) {
    		char domain_context[AST_MAX_EXTENSION];
    
    		domain_context[0] = '\0';
    		if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) {
    
    			if (!allow_external_domains && (req->method == SIP_INVITE || req->method == SIP_REFER)) {
    
    				ast_log(LOG_DEBUG, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain);
    				return -2;
    			}
    		}
    		/* If we have a context defined, overwrite the original context */
    		if (!ast_strlen_zero(domain_context))
    
    			ast_string_field_set(p, context, domain_context);
    
    		ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain);
    
    
    	/* Check the dialplan for the username part of the request URI,
    	   the domain will be stored in the SIPDOMAIN variable
    		Return 0 if we have a matching extension */
    
    	if (ast_exists_extension(NULL, p->context, uri, 1, from) ||
    		!strcmp(uri, ast_pickup_ext())) {
    
    			ast_string_field_set(p, exten, uri);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}