Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	/* New SIP request coming in 
    	   (could be new request in existing SIP dialog as well...) 
    	 */			
    
    	p->method = req->method;	/* Find out which SIP method they are using */
    
    		ast_log(LOG_DEBUG, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd); 
    
    	if (p->icseq && (p->icseq > seqno)) {
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq);
    		if (req->method != SIP_ACK)
    			transmit_response(p, "503 Server error", req);	/* We must respond according to RFC 3261 sec 12.2 */
    
    	} else if (p->icseq &&
    		   p->icseq == seqno &&
    		   req->method != SIP_ACK &&
    		   (p->method != SIP_CANCEL || ast_test_flag(&p->flags[0], SIP_ALREADYGONE))) {
    
    		/* ignore means "don't do anything with it" but still have to 
    		   respond appropriately.  We do this if we receive a repeat of
    		   the last sequence number  */
    
    		ignore = 2;
    		ast_set_flag(req, SIP_PKT_IGNORE);
    		ast_set_flag(req, SIP_PKT_IGNORE_REQ);
    
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "Ignoring SIP message because of retransmit (%s Seqno %d, ours %d)\n", sip_methods[p->method].text, p->icseq, seqno);
    
    	if (seqno >= p->icseq)
    		/* Next should follow monotonically (but not necessarily 
    		   incrementally -- thanks again to the genius authors of SIP --
    		   increasing */
    		p->icseq = seqno;
    
    
    	/* Find their tag if we haven't got it */
    	if (ast_strlen_zero(p->theirtag)) {
    
    		char tag[128];
    
    		gettag(req, "From", tag, sizeof(tag));
    		ast_string_field_set(p, theirtag, tag);
    
    	}
    	snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd);
    
    
    	if (pedanticsipchecking) {
    		/* If this is a request packet without a from tag, it's not
    			correct according to RFC 3261  */
    		/* Check if this a new request in a new dialog with a totag already attached to it,
    			RFC 3261 - section 12.2 - and we don't want to mess with recovery  */
    		if (!p->initreq.headers && ast_test_flag(req, SIP_PKT_WITH_TOTAG)) {
    			/* If this is a first request and it got a to-tag, it is not for us */
    
    			if (!ast_test_flag(req, SIP_PKT_IGNORE) && req->method == SIP_INVITE) {
    
    				transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req);
    
    				/* Will cease to exist after ACK */
    
    Olle Johansson's avatar
    Olle Johansson committed
    			} else if (req->method != SIP_ACK) {
    
    				transmit_response(p, "481 Call/Transaction Does Not Exist", req);
    
    				ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    
    	/* Handle various incoming SIP methods in requests */
    	switch (p->method) {
    	case SIP_OPTIONS:
    
    		res = handle_request_options(p, req);
    
    		res = handle_request_invite(p, req, debug, seqno, sin, recount, e);
    
    		break;
    	case SIP_REFER:
    		res = handle_request_refer(p, req, debug, ignore, seqno, nounlock);
    		break;
    	case SIP_CANCEL:
    
    		res = handle_request_cancel(p, req);
    
    		res = handle_request_bye(p, req);
    
    		res = handle_request_message(p, req);
    
    		res = handle_request_subscribe(p, req, sin, seqno, e);
    
    		res = handle_request_register(p, req, sin, e);
    
    		if (ast_test_flag(req, SIP_PKT_DEBUG))
    			ast_verbose("Receiving INFO!\n");
    		if (!ignore) 
    
    			handle_request_info(p, req);
    
    		else  /* if ignoring, transmit response */
    
    		res = handle_request_notify(p, req, sin, seqno, e);
    
    		/* Make sure we don't ignore this */
    		if (seqno == p->pendinginvite) {
    			p->pendinginvite = 0;
    
    			__sip_ack(p, seqno, FLAG_RESPONSE, 0, FALSE);
    
    				if (process_sdp(p, req))
    					return -1;
    			} 
    			check_pendings(p);
    		}
    
    		if (!p->lastinvite && ast_strlen_zero(p->randdata))
    
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    		transmit_response_with_allow(p, "501 Method Not Implemented", req, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", 
    
    		/* If this is some new method, and we don't have a call, destroy it now */
    
    			ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);	
    
    /*! \brief Read data from SIP socket
    
    \note sipsock_read locks the owner channel while we are processing the SIP message
    
    \return 1 on error, 0 on success
    \note Successful messages is connected to SIP call and forwarded to handle_request() 
    */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sipsock_read(int *id, int fd, short events, void *ignore)
    {
    	struct sip_request req;
    
    	struct sockaddr_in sin = { 0, };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_pvt *p;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int recount = 0;
    
    	unsigned int lockretry = 100;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	len = sizeof(sin);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(&req, 0, sizeof(req));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len);
    	if (res < 0) {
    
    #if !defined(__FreeBSD__)
    
    		if (errno == EAGAIN)
    			ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n");
    
    		else 
    #endif
    		if (errno != ECONNREFUSED)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno));
    		return 1;
    	}
    
    	if (option_debug && res == sizeof(req.data)) {
    
    		ast_log(LOG_DEBUG, "Received packet exceeds buffer. Data is possibly lost\n");
    
    		req.data[sizeof(req.data) - 1] = '\0';
    	} else
    		req.data[res] = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req.len = res;
    
    	if(sip_debug_test_addr(&sin))	/* Set the debug flag early on packet level */
    
    		ast_set_flag(&req, SIP_PKT_DEBUG);
    
    		req.len = lws2sws(req.data, req.len);	/* Fix multiline headers */
    
    	if (ast_test_flag(&req, SIP_PKT_DEBUG))
    
    		ast_verbose("\n<-- SIP read from %s:%d: \n%s\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data);
    
    	parse_request(&req);
    	req.method = find_sip_method(req.rlPart1);
    
    	if (ast_test_flag(&req, SIP_PKT_DEBUG)) {
    
    		ast_verbose("--- (%d headers %d lines)", req.headers, req.lines);
    		if (req.headers + req.lines == 0) 
    			ast_verbose(" Nat keepalive ");
    		ast_verbose("---\n");
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (req.headers < 2) {
    		/* Must have at least two headers */
    		return 1;
    	}
    
    	/* Process request, with netlock held */
    
    retrylock:
    
    
    	/* Find the active SIP dialog or create a new one */
    
    	p = find_call(&req, &sin, req.method);	/* returns p locked */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Go ahead and lock the owner if it has one -- we may need it */
    
    		/* becaues this is deadlock-prone, we need to try and unlock if failed */
    
    		if (p->owner && ast_channel_trylock(p->owner)) {
    
    				ast_log(LOG_DEBUG, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid);
    
    			ast_mutex_unlock(&netlock);
    
    			/* Sleep for a very short amount of time */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			usleep(1);
    
    		if (recordhistory) /* This is a request or response, note what it was for */
    
    			append_history(p, "Rx", "%s / %s / %s", req.data, get_header(&req, "CSeq"), req.rlPart2);
    
    
    		if (!lockretry) {
    			ast_log(LOG_ERROR, "We could NOT get the channel lock for %s! \n", p->owner->name ? p->owner->name : "- no channel name ??? - ");
    			ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid);
    			transmit_response(p, "503 Server error", &req);	/* We must respond according to RFC 3261 sec 12.2 */
    					/* XXX We could add retry-after to make sure they come back */
    			append_history(p, "LockFail", "Owner lock failed, transaction failed.");
    			return 1;
    		}
    
    		if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) {
    			/* Request failed */
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
    
    			ast_channel_unlock(p->owner);
    
    	} else {
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Invalid SIP message - rejected , bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (recount)
    		ast_update_use_count();
    
    
    /*! \brief Send message waiting indication to alert peer that they've got voicemail */
    
    static int sip_send_mwi_to_peer(struct sip_peer *peer)
    {
    	/* Called with peerl lock, but releases it */
    	struct sip_pvt *p;
    
    	int newmsgs, oldmsgs;
    
    	/* Check for messages */
    
    	ast_app_inboxcount(peer->mailbox, &newmsgs, &oldmsgs);
    
    	peer->lastmsgcheck = time(NULL);
    
    	
    	/* Return now if it's the same thing we told them last time */
    
    	if (((newmsgs << 8) | (oldmsgs)) == peer->lastmsgssent) {
    
    	peer->lastmsgssent = ((newmsgs << 8) | (oldmsgs));
    
    
    	if (peer->mwipvt) {
    		/* Base message on subscription */
    		p = peer->mwipvt;
    	} else {
    		/* Build temporary dialog for this message */
    
    		if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) 
    
    			return -1;
    		if (create_addr_from_peer(p, peer)) {
    			/* Maybe they're not registered, etc. */
    			sip_destroy(p);
    			return 0;
    		}
    		/* Recalculate our side, and recalculate Call ID */
    		if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
    
    		build_via(p);
    		build_callid_pvt(p);
    		/* Destroy this session after 32 secs */
    
    		sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
    
    	}
    	/* Send MWI */
    
    	ast_set_flag(&p->flags[0], SIP_OUTGOING);
    
    	transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
    
    /*! \brief Check whether peer needs a new MWI notification check */
    static int does_peer_need_mwi(struct sip_peer *peer)
    {
    
    
    	if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) &&
    	    !peer->mwipvt) {	/* We don't have a subscription */
    
    		peer->lastmsgcheck = t;	/* Reset timer */
    
    	if (!ast_strlen_zero(peer->mailbox) && (t - peer->lastmsgcheck) > global_mwitime)
    
    /*! \brief The SIP monitoring thread 
    \note	This thread monitors all the SIP sessions and peers that needs notification of mwi
    	(and thus do not have a separate thread) indefinitely 
    */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void *do_monitor(void *data)
    {
    	int res;
    	struct sip_pvt *sip;
    
    	struct sip_peer *peer = NULL;
    
    	int fastrestart = FALSE;
    
    	int lastpeernum = -1;
    	int curpeernum;
    
    	/* Add an I/O event to our SIP UDP socket */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sipsock > -1) 
    		ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
    	
    	/* From here on out, we die whenever asked */
    	for(;;) {
    
    		/* Check for a reload request */
    		ast_mutex_lock(&sip_reload_lock);
    		reloading = sip_reloading;
    
    		sip_reloading = FALSE;
    
    		ast_mutex_unlock(&sip_reload_lock);
    		if (reloading) {
    			if (option_verbose > 0)
    				ast_verbose(VERBOSE_PREFIX_1 "Reloading SIP\n");
    
    			sip_do_reload(sip_reloadreason);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Check for interfaces needing to be killed */
    
    Mark Spencer's avatar
    Mark Spencer committed
    restartsearch:		
    
    		/* don't scan the interface list if it hasn't been a reasonable period
    		   of time since the last time we did it (when MWI is being sent, we can
    		   get back to this point every millisecond or less)
    		*/
    		for (sip = iflist; !fastrestart && sip; sip = sip->next) {
    
    			/* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
    
    			if (sip->rtp && sip->owner &&
    			    (sip->owner->_state == AST_STATE_UP) &&
    			    !sip->redirip.sin_addr.s_addr) {
    				if (sip->lastrtptx &&
    				    sip->rtpkeepalive &&
    				    (t > sip->lastrtptx + sip->rtpkeepalive)) {
    
    					/* Need to send an empty RTP packet */
    
    					ast_rtp_sendcng(sip->rtp, 0);
    				}
    
    				if (sip->lastrtprx &&
    				    (sip->rtptimeout || sip->rtpholdtimeout) &&
    				    (t > sip->lastrtprx + sip->rtptimeout)) {
    
    					/* Might be a timeout now -- see if we're on hold */
    					struct sockaddr_in sin;
    					ast_rtp_get_peer(sip->rtp, &sin);
    					if (sin.sin_addr.s_addr || 
    
    					    (sip->rtpholdtimeout && 
    					     (t > sip->lastrtprx + sip->rtpholdtimeout))) {
    
    						/* Needs a hangup */
    						if (sip->rtptimeout) {
    
    							while (sip->owner && ast_channel_trylock(sip->owner)) {
    
    								ast_mutex_unlock(&sip->lock);
    								usleep(1);
    								ast_mutex_lock(&sip->lock);
    							}
    							if (sip->owner) {
    
    								if (!(ast_rtp_get_bridged(sip->rtp))) {
    									ast_log(LOG_NOTICE,
    										"Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
    										sip->owner->name,
    										(long) (t - sip->lastrtprx));
    									/* Issue a softhangup */
    									ast_softhangup_nolock(sip->owner, AST_SOFTHANGUP_DEV);
    								} else
    									ast_log(LOG_NOTICE, "'%s' will not be disconnected in %ld seconds because it is directly bridged to another RTP stream\n", sip->owner->name, (long) (t - sip->lastrtprx));
    
    								ast_channel_unlock(sip->owner);
    
    								/* forget the timeouts for this call, since a hangup
    								   has already been requested and we don't want to
    								   repeatedly request hangups
    								*/
    								sip->rtptimeout = 0;
    								sip->rtpholdtimeout = 0;
    
    			/* If we have sessions that needs to be destroyed, do it now */
    
    			if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets &&
    			    !sip->owner) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				__sip_destroy(sip, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				goto restartsearch;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_testcancel();
    		/* Wait for sched or io */
    		res = ast_sched_wait(sched);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((res < 0) || (res > 1000))
    			res = 1000;
    
    		/* If we might need to send more mailboxes, don't wait long at all.*/
    		if (fastrestart)
    			res = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ast_io_wait(io, res);
    
    		if (option_debug && res > 20)
    
    Russell Bryant's avatar
    Russell Bryant committed
    			ast_log(LOG_DEBUG, "chan_sip: ast_io_wait ran %d all at once\n", res);
    
    		if (res >= 0)  {
    			res = ast_sched_runq(sched);
    
    			if (option_debug && res >= 20)
    
    Russell Bryant's avatar
    Russell Bryant committed
    				ast_log(LOG_DEBUG, "chan_sip: ast_sched_runq ran %d all at once\n", res);
    
    		/* Send MWI notifications to peers - static and cached realtime peers */
    
    		fastrestart = FALSE;
    
    		/* Find next peer that needs mwi */
    
    		ASTOBJ_CONTAINER_TRAVERSE(&peerl, !peer, do {
    
    			if ((curpeernum > lastpeernum) && does_peer_need_mwi(iterator)) {
    
    				fastrestart = TRUE;
    
    				lastpeernum = curpeernum;
    
    				peer = ASTOBJ_REF(iterator);
    			};
    			curpeernum++;
    		} while (0)
    
    		/* Send MWI to the peer */
    
    			ASTOBJ_WRLOCK(peer);
    
    			ASTOBJ_UNLOCK(peer);
    
    			ASTOBJ_UNREF(peer,sip_destroy_peer);
    		} else {
    
    			/* Reset where we come from */
    			lastpeernum = -1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	/* Never reached */
    	return NULL;
    	
    }
    
    
    /*! \brief Start the channel monitor thread */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int restart_monitor(void)
    {
    	/* If we're supposed to be stopped -- stay stopped */
    
    	if (monitor_thread == AST_PTHREADT_STOP)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (monitor_thread == pthread_self()) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Cannot kill myself\n");
    		return -1;
    	}
    
    	if (monitor_thread != AST_PTHREADT_NULL) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Wake up the thread */
    		pthread_kill(monitor_thread, SIGURG);
    	} else {
    		/* Start a new monitor */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
    			return -1;
    		}
    	}
    
    /*! \brief React to lack of answer to Qualify poke */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_poke_noanswer(void *data)
    {
    	struct sip_peer *peer = data;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	peer->pokeexpire = -1;
    
    	if (peer->lastms > -1) {
    
    		ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!  Last qualify: %d\n", peer->name, peer->lastms);
    
    		manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (peer->call)
    		sip_destroy(peer->call);
    	peer->call = NULL;
    	peer->lastms = -1;
    
    	ast_device_state_changed("SIP/%s", peer->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Try again quickly */
    	peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer);
    	return 0;
    }
    
    
    /*! \brief Check availability of peer, also keep NAT open
    \note	This is done with the interval in qualify= configuration option
    	Default is 2 seconds */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_poke_peer(struct sip_peer *peer)
    {
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
    		/* IF we have no IP, or this isn't to be monitored, return
    		  imeediately after clearing things out */
    
    		if (peer->pokeexpire > -1)
    			ast_sched_del(sched, peer->pokeexpire);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		peer->lastms = 0;
    		peer->pokeexpire = -1;
    		peer->call = NULL;
    		return 0;
    	}
    	if (peer->call > 0) {
    
    			ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sip_destroy(peer->call);
    	}
    
    	if (!(p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	p->sa = peer->addr;
    	p->recv = peer->addr;
    
    	ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    	/* Send OPTIONs to peer's fullcontact */
    
    	if (!ast_strlen_zero(peer->fullcontact))
    		ast_string_field_set(p, fullcontact, peer->fullcontact);
    
    	if (!ast_strlen_zero(peer->tohost))
    
    		ast_string_field_set(p, tohost, peer->tohost);
    
    	else
    		ast_string_field_set(p, tohost, ast_inet_ntoa(peer->addr.sin_addr));
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* Recalculate our side, and recalculate Call ID */
    
    	if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (peer->pokeexpire > -1)
    		ast_sched_del(sched, peer->pokeexpire);
    
    	ast_set_flag(&p->flags[0], SIP_OUTGOING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef VOCAL_DATA_HACK
    
    	ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
    
    	transmit_invite(p, SIP_INVITE, 0, 2);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #else
    
    	transmit_invite(p, SIP_OPTIONS, 0, 2);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    	gettimeofday(&peer->ps, NULL);
    	peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer);
    
    	return 0;
    }
    
    
    /*! \brief Part of PBX channel interface
    \note
    \par	Return values:---
    
    
    	If we have qualify on and the device is not reachable, regardless of registration
    	state we return AST_DEVICE_UNAVAILABLE
    
    	For peers with call limit:
    
    		- not registered			AST_DEVICE_UNAVAILABLE
    
    		- registered, no call			AST_DEVICE_NOT_INUSE
    		- registered, active calls		AST_DEVICE_INUSE
    
    		- registered, call limit reached	AST_DEVICE_BUSY
    
    		- not registered			AST_DEVICE_UNAVAILABLE
    
    		- registered				AST_DEVICE_NOT_INUSE
    		- fixed IP (!dynamic)			AST_DEVICE_NOT_INUSE
    
    	If we return AST_DEVICE_UNKNOWN, the device state engine will try to find
    	out a state by walking the channel list.
    
    static int sip_devicestate(void *data)
    {
    
    	struct ast_hostent ahp;
    
    	struct sip_peer *p;
    
    	int res = AST_DEVICE_INVALID;
    
    
    	host = ast_strdupa(data);
    	if ((tmp = strchr(host, '@')))
    		host = tmp + 1;
    
    		ast_log(LOG_DEBUG, "Checking device state for peer %s\n", host);
    
    
    	if ((p = find_peer(host, NULL, 1))) {
    		if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) {
    			/* we have an address for the peer */
    			/* if qualify is turned on, check the status */
    			if (p->maxms && (p->lastms > p->maxms)) {
    				res = AST_DEVICE_UNAVAILABLE;
    			} else {
    				/* qualify is not on, or the peer is responding properly */
    				/* check call limit */
    
    				if (p->call_limit && (p->inUse == p->call_limit))
    
    				else if (p->call_limit && p->inUse)
    					res = AST_DEVICE_INUSE;
    
    				if (p->onHold)
    					res = AST_DEVICE_ONHOLD;
    				else if (p->inRinging) {
    
    					if (p->inRinging == p->inUse)
    						res = AST_DEVICE_RINGING;
    					else
    						res = AST_DEVICE_RINGINUSE;
    				}
    
    		} else {
    			/* there is no address, it's unavailable */
    			res = AST_DEVICE_UNAVAILABLE;
    
    		ASTOBJ_UNREF(p,sip_destroy_peer);
    	} else {
    
    		hp = ast_gethostbyname(host, &ahp);
    
    /*! \brief PBX interface function -build SIP pvt structure */
    
    /* SIP calls initiated by the PBX arrive here */
    
    static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int oldformat;
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *tmpc = NULL;
    	char *ext, *host;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *dest = data;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	oldformat = format;
    
    	if (!(format &= ((AST_FORMAT_MAX_AUDIO << 1) - 1))) {
    
    		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n", ast_getformatname(oldformat), ast_getformatname(global_capability));
    
    		*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;	/* Can't find codec to connect to host */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat));
    
    
    	if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) {
    
    		ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", (char *)data);
    
    		*cause = AST_CAUSE_SWITCH_CONGESTION;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    
    	if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
    		sip_destroy(p);
    		ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
    
    		*cause = AST_CAUSE_SWITCH_CONGESTION;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	host = strchr(tmp, '@');
    	if (host) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ext = tmp;
    	} else {
    
    		ext = strchr(tmp, '/');
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (ext) 
    
    			*ext++ = '\0';
    
    Olle Johansson's avatar
    Olle Johansson committed
    		host = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (create_addr(p, host)) {
    
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "Cant create SIP call - target device not registred\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sip_destroy(p);
    		return NULL;
    	}
    
    	if (ast_strlen_zero(p->peername) && ext)
    
    		ast_string_field_set(p, peername, ext);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Recalculate our side, and recalculate Call ID */
    
    	if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
    
    	
    	/* We have an extension to call, don't use the full contact here */
    
    	/* This to enable dialing registered peers with extension dialling,
    
    	   like SIP/peername/extension 	
    	   SIP/peername will still use the full contact */
    	if (ext) {
    
    		ast_string_field_set(p, username, ext);
    		ast_string_field_free(p, fullcontact);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #if 0
    
    Mark Spencer's avatar
    Mark Spencer committed
    	printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    	p->prefcodec = oldformat;				/* Format for this call */
    
    	tmpc = sip_new(p, AST_STATE_DOWN, host);	/* Place the call */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!tmpc)
    		sip_destroy(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_update_use_count();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	restart_monitor();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return tmpc;
    
      \brief Handle flag-type options common to configuration of devices - users and peers
    
      \param flags array of two struct ast_flags
      \param mask array of two struct ast_flags
      \param v linked list of config variables to process
      \returns non-zero if any config options were handled, zero otherwise
    */
    
    static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v)
    {
    	int res = 0;
    
    	static int dep_insecure_very = 0;
    	static int dep_insecure_yes = 0;
    
    
    	if (!strcasecmp(v->name, "trustrpid")) {
    
    		ast_set_flag(&mask[0], SIP_TRUSTRPID);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID);
    
    	} else if (!strcasecmp(v->name, "sendrpid")) {
    
    		ast_set_flag(&mask[0], SIP_SENDRPID);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID);
    
    	} else if (!strcasecmp(v->name, "g726nonstandard")) {
    		ast_set_flag(&mask[0], SIP_G726_NONSTANDARD);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD);
    		res = 1;
    
    	} else if (!strcasecmp(v->name, "useclientcode")) {
    
    		ast_set_flag(&mask[0], SIP_USECLIENTCODE);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE);
    
    		res = 1;
    	} else if (!strcasecmp(v->name, "dtmfmode")) {
    
    		ast_set_flag(&mask[0], SIP_DTMF);
    		ast_clear_flag(&flags[0], SIP_DTMF);
    
    		if (!strcasecmp(v->value, "inband"))
    
    			ast_set_flag(&flags[0], SIP_DTMF_INBAND);
    
    		else if (!strcasecmp(v->value, "rfc2833"))
    
    			ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
    
    		else if (!strcasecmp(v->value, "info"))
    
    			ast_set_flag(&flags[0], SIP_DTMF_INFO);
    
    			ast_set_flag(&flags[0], SIP_DTMF_AUTO);
    
    		else {
    			ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno);
    
    			ast_set_flag(&flags[0], SIP_DTMF_RFC2833);
    
    		}
    	} else if (!strcasecmp(v->name, "nat")) {
    
    		ast_set_flag(&mask[0], SIP_NAT);
    		ast_clear_flag(&flags[0], SIP_NAT);
    
    		if (!strcasecmp(v->value, "never"))
    
    			ast_set_flag(&flags[0], SIP_NAT_NEVER);
    
    		else if (!strcasecmp(v->value, "route"))
    
    			ast_set_flag(&flags[0], SIP_NAT_ROUTE);
    
    		else if (ast_true(v->value))
    
    			ast_set_flag(&flags[0], SIP_NAT_ALWAYS);
    
    			ast_set_flag(&flags[0], SIP_NAT_RFC3581);
    
    	} else if (!strcasecmp(v->name, "canreinvite")) {
    
    		ast_set_flag(&mask[0], SIP_REINVITE);
    		ast_clear_flag(&flags[0], SIP_REINVITE);
    
    		if (ast_true(v->value)) {
    			ast_set_flag(&flags[0], SIP_CAN_REINVITE | SIP_CAN_REINVITE_NAT);
    		} else if (!ast_false(v->value)) {
    			char buf[64];
    			char *word, *next = buf;
    
    			ast_copy_string(buf, v->value, sizeof(buf));
    			while ((word = strsep(&next, ","))) {
    				if (!strcasecmp(word, "update")) {
    					ast_set_flag(&flags[0], SIP_REINVITE_UPDATE | SIP_CAN_REINVITE);
    				} else if (!strcasecmp(word, "nonat")) {
    					ast_set_flag(&flags[0], SIP_CAN_REINVITE);
    					ast_clear_flag(&flags[0], SIP_CAN_REINVITE_NAT);
    				} else {
    					ast_log(LOG_WARNING, "Unknown canreinvite mode '%s' on line %d\n", v->value, v->lineno);
    				}
    			}
    		}
    
    	} else if (!strcasecmp(v->name, "insecure")) {
    
    		ast_set_flag(&mask[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE);
    		ast_clear_flag(&flags[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE);
    
    			ast_set_flag(&flags[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE);
    
    			if (!dep_insecure_very) {
    				ast_log(LOG_WARNING, "insecure=very at line %d is deprecated; use insecure=port,invite instead\n", v->lineno);
    				dep_insecure_very = 1;
    			}
    		}
    		else if (ast_true(v->value)) {
    
    			ast_set_flag(&flags[0], SIP_INSECURE_PORT);
    
    			if (!dep_insecure_yes) {
    				ast_log(LOG_WARNING, "insecure=%s at line %d is deprecated; use insecure=port instead\n", v->value, v->lineno);
    				dep_insecure_yes = 1;
    			}
    		}
    
    		else if (!ast_false(v->value)) {
    			char buf[64];
    			char *word, *next;
    
    
    			ast_copy_string(buf, v->value, sizeof(buf));
    
    			next = buf;
    			while ((word = strsep(&next, ","))) {
    				if (!strcasecmp(word, "port"))
    
    					ast_set_flag(&flags[0], SIP_INSECURE_PORT);
    
    				else if (!strcasecmp(word, "invite"))
    
    					ast_set_flag(&flags[0], SIP_INSECURE_INVITE);
    
    				else
    					ast_log(LOG_WARNING, "Unknown insecure mode '%s' on line %d\n", v->value, v->lineno);
    			}
    		}
    
    	} else if (!strcasecmp(v->name, "progressinband")) {
    
    		ast_set_flag(&mask[0], SIP_PROG_INBAND);
    		ast_clear_flag(&flags[0], SIP_PROG_INBAND);
    
    			ast_set_flag(&flags[0], SIP_PROG_INBAND_YES);
    
    		else if (strcasecmp(v->value, "never"))
    
    			ast_set_flag(&flags[0], SIP_PROG_INBAND_NO);
    
    Mark Spencer's avatar
    Mark Spencer committed
      	} else if (!strcasecmp(v->name, "allowguest")) {
    
    		global_allowguest = ast_true(v->value) ? 1 : 0;
    
    	} else if (!strcasecmp(v->name, "promiscredir")) {
    
    		ast_set_flag(&mask[0], SIP_PROMISCREDIR);
    		ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR);
    
    	} else if (!strcasecmp(v->name, "videosupport")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT);
    	} else if (!strcasecmp(v->name, "allowoverlap")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP);
    	} else if (!strcasecmp(v->name, "allowsubscribe")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
    
    	} else if (!strcasecmp(v->name, "t38pt_udptl")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL);
    	} else if (!strcasecmp(v->name, "t38pt_rtp")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP);
    	} else if (!strcasecmp(v->name, "t38pt_tcp")) {
    		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP);
    		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP);
    
    /*! \brief Add SIP domain to list of domains we are responsible for */
    
    static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context)
    {
    	struct domain *d;
    
    
    		ast_log(LOG_WARNING, "Zero length domain.\n");
    		return 1;
    	}
    
    
    	if (!(d = ast_calloc(1, sizeof(*d))))
    
    		return 0;
    
    	ast_copy_string(d->domain, domain, sizeof(d->domain));
    
    
    		ast_copy_string(d->context, context, sizeof(d->context));
    
    	d->mode = mode;
    
    	AST_LIST_LOCK(&domain_list);
    	AST_LIST_INSERT_TAIL(&domain_list, d, list);
    	AST_LIST_UNLOCK(&domain_list);
    
    
    		ast_log(LOG_DEBUG, "Added local SIP domain '%s'\n", domain);
    
    	return 1;
    }
    
    
    /*! \brief  check_sip_domain: Check if domain part of uri is local to our server */
    
    static int check_sip_domain(const char *domain, char *context, size_t len)
    {
    	struct domain *d;
    	int result = 0;
    
    	AST_LIST_LOCK(&domain_list);
    	AST_LIST_TRAVERSE(&domain_list, d, list) {
    		if (strcasecmp(d->domain, domain))
    			continue;
    
    		if (len && !ast_strlen_zero(d->context))
    			ast_copy_string(context, d->context, len);
    		
    		result = 1;
    		break;
    	}
    	AST_LIST_UNLOCK(&domain_list);
    
    	return result;
    }
    
    
    /*! \brief Clear our domain list (at reload) */
    
    static void clear_sip_domains(void)
    {
    	struct domain *d;
    
    	AST_LIST_LOCK(&domain_list);
    	while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list)))
    		free(d);
    	AST_LIST_UNLOCK(&domain_list);
    }
    
    
    
    /*! \brief Add realm authentication in list */
    
    static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno)
    {
    
    	char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
    
    	char *stringp;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	struct sip_auth *a, *b, *auth;
    
    
    	ast_log(LOG_DEBUG, "Auth config ::  %s\n", configuration);
    
    
    	ast_copy_string(authcopy, configuration, sizeof(authcopy));
    	stringp = authcopy;
    
    	username = stringp;
    	realm = strrchr(stringp, '@');
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (realm)
    		*realm++ = '\0';
    
    	if (ast_strlen_zero(username) || ast_strlen_zero(realm)) {
    
    		ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno);
    		return authlist;
    	}
    	stringp = username;
    	username = strsep(&stringp, ":");
    	if (username) {
    		secret = strsep(&stringp, ":");
    
    		if (!secret) {
    
    			stringp = username;
    
    			md5secret = strsep(&stringp,"#");
    		}
    
    	if (!(auth = ast_calloc(1, sizeof(*auth))))
    
    
    	ast_copy_string(auth->realm, realm, sizeof(auth->realm));
    	ast_copy_string(auth->username, username, sizeof(auth->username));
    	if (secret)
    		ast_copy_string(auth->secret, secret, sizeof(auth->secret));
    	if (md5secret)
    		ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret));
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* find the end of the list */
    	for (b = NULL, a = authlist; a ; b = a, a = a->next)
    		;
    	if (b)
    		b->next = auth;	/* Add structure add end of list */
    	else
    		authlist = auth;
    
    
    	if (option_verbose > 2)
    		ast_verbose("Added authentication for realm %s\n", realm);
    
    
    /*! \brief Clear realm authentication list (at reload) */
    
    static int clear_realm_authentication(struct sip_auth *authlist)
    {
    	struct sip_auth *a = authlist;
    	struct sip_auth *b;
    
    
    	while (a) {
    		b = a;
    		a = a->next;
    		free(b);
    	}
    
    /*! \brief Find authentication for a specific realm */
    
    static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm)