[Pkg-voip-commits] r7759 - in /asterisk/branches/lenny/debian/patches: AST-2009-001 AST-2009-002 AST-2009-003 AST-2009-006 AST-2009-008

paravoid at alioth.debian.org paravoid at alioth.debian.org
Sat Nov 7 06:55:51 UTC 2009


Author: paravoid
Date: Sat Nov  7 06:55:50 2009
New Revision: 7759

URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=7759
Log:
Commit original Digium patches for ASTs (can't be applied yet)

Added:
    asterisk/branches/lenny/debian/patches/AST-2009-001
    asterisk/branches/lenny/debian/patches/AST-2009-002
    asterisk/branches/lenny/debian/patches/AST-2009-003
    asterisk/branches/lenny/debian/patches/AST-2009-006
    asterisk/branches/lenny/debian/patches/AST-2009-008

Added: asterisk/branches/lenny/debian/patches/AST-2009-001
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/AST-2009-001?rev=7759&op=file
==============================================================================
--- asterisk/branches/lenny/debian/patches/AST-2009-001 (added)
+++ asterisk/branches/lenny/debian/patches/AST-2009-001 Sat Nov  7 06:55:50 2009
@@ -1,0 +1,256 @@
+Index: channels/chan_iax2.c
+===================================================================
+--- a/channels/chan_iax2.c	(revision 166591)
++++ b/channels/chan_iax2.c	(working copy)
+@@ -155,6 +155,7 @@
+ static int authdebug = 1;
+ static int autokill = 0;
+ static int iaxcompat = 0;
++static int last_authmethod = 0;
+ 
+ static int iaxdefaultdpcache=10 * 60;	/* Cache dialplan entries for 10 minutes by default */
+ 
+@@ -228,10 +229,9 @@
+ static ast_cond_t sched_cond;
+ 
+ enum {
+-	IAX_STATE_STARTED = 		(1 << 0),
+-	IAX_STATE_AUTHENTICATED = 	(1 << 1),
+-	IAX_STATE_TBD = 		(1 << 2),
+-	IAX_STATE_UNCHANGED = 		(1 << 3),
++	IAX_STATE_STARTED =			(1 << 0),
++	IAX_STATE_AUTHENTICATED =	(1 << 1),
++	IAX_STATE_TBD =				(1 << 2),
+ } iax2_state;
+ 
+ struct iax2_context {
+@@ -512,7 +512,7 @@
+ 	/*! The jitterbuffer */
+         jitterbuf *jb;
+ 	/*! active jb read scheduler id */
+-        int jbid;                       
++        int jbid;
+ 	/*! LAG */
+ 	int lag;
+ 	/*! Error, as discovered by the manager */
+@@ -568,7 +568,8 @@
+ 		AST_STRING_FIELD(mohinterpret);
+ 		AST_STRING_FIELD(mohsuggest);
+ 	);
+-	
++	/*! AUTHREJ all AUTHREP frames */
++	int authrej;
+ 	/*! permitted authentication methods */
+ 	int authmethods;
+ 	/*! permitted encryption methods */
+@@ -5400,6 +5401,18 @@
+ 			ast_string_field_set(iaxs[callno], secret, user->secret);
+ 		res = 0;
+ 		user = user_unref(user);
++	} else {
++		 /* user was not found, but we should still fake an AUTHREQ.
++		  * Set authmethods to the last known authmethod used by the system
++		  * Set a fake secret, it's not looked at, just required to attempt authentication.
++		  * Set authrej so the AUTHREP is rejected without even looking at its contents */
++		iaxs[callno]->authmethods = last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT);
++		ast_string_field_set(iaxs[callno], secret, "badsecret");
++		iaxs[callno]->authrej = 1;
++		if (!ast_strlen_zero(iaxs[callno]->username)) {
++			/* only send the AUTHREQ if a username was specified. */
++			res = 0;
++		}
+ 	}
+ 	ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK);	
+ 	return res;
+@@ -5506,6 +5519,9 @@
+ 		.name = p->username,	
+ 	};
+ 
++	if (p->authrej) {
++		return res;
++	}
+ 	user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ 	if (user) {
+ 		if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
+@@ -5583,7 +5599,7 @@
+ 	int expire = 0;
+ 	int res = -1;
+ 
+-	ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED);
++	ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
+ 	/* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */
+ 	if (ies->username)
+ 		ast_copy_string(peer, ies->username, sizeof(peer));
+@@ -5606,8 +5622,29 @@
+ 	p = find_peer(peer, 1);
+ 	ast_mutex_lock(&iaxsl[callno]);
+ 	if (!p || !iaxs[callno]) {
++		if (iaxs[callno]) {
++			int plaintext = ((last_authmethod & IAX_AUTH_PLAINTEXT) | (iaxs[callno]->authmethods & IAX_AUTH_PLAINTEXT));
++
++			ast_string_field_set(iaxs[callno], secret, "badsecret");
++
++			/* An AUTHREQ must be sent in response to a REGREQ of an invalid peer unless
++			 * 1. A challenge already exists indicating a AUTHREQ was already sent out.
++			 * 2. A plaintext secret is present in ie as result of a previous AUTHREQ requesting it.
++			 * 3. A plaintext secret is present in the ie and the last_authmethod used by a peer happened
++			 *    to be plaintext, indicating it is an authmethod used by other peers on the system. 
++			 *
++			 * If none of these cases exist, res will be returned as 0 without authentication indicating
++			 * an AUTHREQ needs to be sent out. */
++
++			if (ast_strlen_zero(iaxs[callno]->challenge) &&
++				!(!ast_strlen_zero(secret) && plaintext)) {
++				/* by setting res to 0, an REGAUTH will be sent */
++				res = 0;
++			}
++		}
+ 		if (authdebug && !p)
+ 			ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
++
+ 		goto return_unref;
+ 	}
+ 
+@@ -5622,8 +5659,6 @@
+ 			ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
+ 		goto return_unref;
+ 	}
+-	if (!inaddrcmp(&p->addr, sin))
+-		ast_set_flag(&iaxs[callno]->state, IAX_STATE_UNCHANGED);
+ 	ast_string_field_set(iaxs[callno], secret, p->secret);
+ 	ast_string_field_set(iaxs[callno], inkeys, p->inkeys);
+ 	/* Check secret against what we have on file */
+@@ -5639,7 +5674,7 @@
+ 				if (key && !ast_check_signature(key, iaxs[callno]->challenge, rsasecret)) {
+ 					ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
+ 					break;
+-				} else if (!key) 
++				} else if (!key)
+ 					ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn);
+ 				keyn = strsep(&stringp, ":");
+ 			}
+@@ -5657,7 +5692,7 @@
+ 		struct MD5Context md5;
+ 		unsigned char digest[16];
+ 		char *tmppw, *stringp;
+-		
++
+ 		tmppw = ast_strdupa(p->secret);
+ 		stringp = tmppw;
+ 		while((tmppw = strsep(&stringp, ";"))) {
+@@ -5667,7 +5702,7 @@
+ 			MD5Final(digest, &md5);
+ 			for (x=0;x<16;x++)
+ 				sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
+-			if (!strcasecmp(requeststr, md5secret)) 
++			if (!strcasecmp(requeststr, md5secret))
+ 				break;
+ 		}
+ 		if (tmppw) {
+@@ -5685,24 +5720,25 @@
+ 			goto return_unref;
+ 		} else
+ 			ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
+-	} else if (!ast_strlen_zero(md5secret) || !ast_strlen_zero(secret)) {
+-		if (authdebug)
+-			ast_log(LOG_NOTICE, "Inappropriate authentication received\n");
++	} else if (!ast_strlen_zero(iaxs[callno]->challenge) && ast_strlen_zero(md5secret) && ast_strlen_zero(rsasecret)) {
++		/* if challenge has been sent, but no challenge response if given, reject. */
+ 		goto return_unref;
+ 	}
+-	ast_string_field_set(iaxs[callno], peer, peer);
+-	/* Choose lowest expiry number */
+-	if (expire && (expire < iaxs[callno]->expiry)) 
+-		iaxs[callno]->expiry = expire;
+-
+ 	ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+ 
++	/* either Authentication has taken place, or a REGAUTH must be sent before verifying registration */
+ 	res = 0;
++return_unref:
++	if (iaxs[callno]) {
++		ast_string_field_set(iaxs[callno], peer, peer);
++	}
++	/* Choose lowest expiry number */
++	if (expire && (expire < iaxs[callno]->expiry)) {
++		iaxs[callno]->expiry = expire;
++	}
+ 
+-return_unref:
+ 	if (p)
+ 		peer_unref(p);
+-
+ 	return res;
+ }
+ 
+@@ -6375,24 +6411,33 @@
+ 	struct iax2_peer *p;
+ 	char challenge[10];
+ 	const char *peer_name;
+-	int res = -1;
++	int sentauthmethod;
+ 
+ 	peer_name = ast_strdupa(iaxs[callno]->peer);
+ 
+ 	/* SLD: third call to find_peer in registration */
+ 	ast_mutex_unlock(&iaxsl[callno]);
+-	p = find_peer(peer_name, 1);
++	if ((p = find_peer(peer_name, 1))) {
++		last_authmethod = p->authmethods;
++	}
++
+ 	ast_mutex_lock(&iaxsl[callno]);
+ 	if (!iaxs[callno])
+ 		goto return_unref;
++
++	memset(&ied, 0, sizeof(ied));
++	/* The selection of which delayed reject is sent may leak information,
++	 * if it sets a static response.  For example, if a host is known to only
++	 * use MD5 authentication, then an RSA response would indicate that the
++	 * peer does not exist, and vice-versa.
++	 * Therefore, we use whatever the last peer used (which may vary over the
++	 * course of a server, which should leak minimal information). */
++	sentauthmethod = p ? p->authmethods : last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT);
+ 	if (!p) {
+-		ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
+-		goto return_unref;
++		iaxs[callno]->authmethods = sentauthmethod;
+ 	}
+-	
+-	memset(&ied, 0, sizeof(ied));
+-	iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+-	if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
++	iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, sentauthmethod);
++	if (sentauthmethod & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
+ 		/* Build the challenge */
+ 		snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
+ 		ast_string_field_set(iaxs[callno], challenge, challenge);
+@@ -6400,12 +6445,12 @@
+ 	}
+ 	iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
+ 
+-	res = 0;
+-
+ return_unref:
+-	peer_unref(p);
++	if (p) {
++		peer_unref(p);
++	}
+ 
+-	return res ? res : send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
++	return iaxs[callno] ? send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1) : -1;
+ }
+ 
+ static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
+@@ -8347,8 +8392,9 @@
+ 					ast_mutex_unlock(&iaxsl[fr->callno]);
+ 					return 1;
+ 				}
+-				if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) || 
+-						ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED)) {
++				if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) ||
++						ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED)) {
++
+ 					if (f.subclass == IAX_COMMAND_REGREL)
+ 						memset(&sin, 0, sizeof(sin));
+ 					if (update_registry(&sin, fr->callno, ies.devicetype, fd, ies.refresh))

Added: asterisk/branches/lenny/debian/patches/AST-2009-002
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/AST-2009-002?rev=7759&op=file
==============================================================================
--- asterisk/branches/lenny/debian/patches/AST-2009-002 (added)
+++ asterisk/branches/lenny/debian/patches/AST-2009-002 Sat Nov  7 06:55:50 2009
@@ -1,0 +1,70 @@
+Index: channels/chan_sip.c
+===================================================================
+--- a/channels/chan_sip.c	(revision 174081)
++++ b/channels/chan_sip.c	(revision 174082)
+@@ -14012,19 +14012,32 @@
+  */
+ static int sip_uri_params_cmp(const char *input1, const char *input2) 
+ {
+-	char *params1 = ast_strdupa(input1);
+-	char *params2 = ast_strdupa(input2);
++	char *params1 = NULL;
++	char *params2 = NULL;
+ 	char *pos1;
+ 	char *pos2;
++	int zerolength1 = 0;
++	int zerolength2 = 0;
+ 	int maddrmatch = 0;
+ 	int ttlmatch = 0;
+ 	int usermatch = 0;
+ 	int methodmatch = 0;
+ 
++	if (ast_strlen_zero(input1)) {
++		zerolength1 = 1;
++	} else {
++		params1 = ast_strdupa(input1);
++	}
++	if (ast_strlen_zero(input2)) {
++		zerolength2 = 1;
++	} else {
++		params2 = ast_strdupa(input2);
++	}
++
+ 	/*Quick optimization. If both params are zero-length, then
+ 	 * they match
+ 	 */
+-	if (ast_strlen_zero(params1) && ast_strlen_zero(params2)) {
++	if (zerolength1 && zerolength2) {
+ 		return 0;
+ 	}
+ 
+@@ -14139,13 +14152,25 @@
+  */
+ static int sip_uri_headers_cmp(const char *input1, const char *input2)
+ {
+-	char *headers1 = ast_strdupa(input1);
+-	char *headers2 = ast_strdupa(input2);
+-	int zerolength1 = ast_strlen_zero(headers1);
+-	int zerolength2 = ast_strlen_zero(headers2);
++	char *headers1 = NULL;
++	char *headers2 = NULL;
++	int zerolength1 = 0;
++	int zerolength2 = 0;
+ 	int different = 0;
+ 	char *header1;
+ 
++	if (ast_strlen_zero(input1)) {
++		zerolength1 = 1;
++	} else {
++		headers1 = ast_strdupa(input1);
++	}
++	
++	if (ast_strlen_zero(input2)) {
++		zerolength2 = 1;
++	} else {
++		headers2 = ast_strdupa(input2);
++	}
++
+ 	if ((zerolength1 && !zerolength2) ||
+ 			(zerolength2 && !zerolength1))
+ 		return 1;

Added: asterisk/branches/lenny/debian/patches/AST-2009-003
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/AST-2009-003?rev=7759&op=file
==============================================================================
--- asterisk/branches/lenny/debian/patches/AST-2009-003 (added)
+++ asterisk/branches/lenny/debian/patches/AST-2009-003 Sat Nov  7 06:55:50 2009
@@ -1,0 +1,164 @@
+Index: channels/chan_sip.c
+===================================================================
+--- a/channels/chan_sip.c	(revision 183280)
++++ b/channels/chan_sip.c	(working copy)
+@@ -1266,7 +1266,7 @@
+ static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
+ static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
+ static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
+-static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable);
++static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable);
+ static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
+ static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch);
+ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init);
+@@ -8873,10 +8873,96 @@
+ /*! \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, enum xmittype reliable)
++static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype 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);
++	/* We have to emulate EXACTLY what we'd get with a good peer
++	 * and a bad password, or else we leak information. */
++	const char *response = "407 Proxy Authentication Required";
++	const char *reqheader = "Proxy-Authorization";
++	const char *respheader = "Proxy-Authenticate";
++	const char *authtoken;
++	struct ast_dynamic_str *buf;
++	char *c;
++
++	/* table of recognised keywords, and their value in the digest */
++	enum keys { K_NONCE, K_LAST };
++	struct x {
++		const char *key;
++		const char *s;
++	} *i, keys[] = {
++		[K_NONCE] = { "nonce=", "" },
++		[K_LAST] = { NULL, NULL}
++	};
++
++	if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
++		response = "401 Unauthorized";
++		reqheader = "Authorization";
++		respheader = "WWW-Authenticate";
++	}
++	authtoken = get_header(req, reqheader);
++	if (ast_test_flag(req, SIP_PKT_IGNORE) && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
++		/* This is a retransmitted invite/register/etc, don't reconstruct authentication
++		 * information */
++		transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0);
++		/* Schedule auto destroy in 32 seconds (according to RFC 3261) */
++		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
++		return;
++	} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
++		/* 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, 0, respheader, 0);
++		/* Schedule auto destroy in 32 seconds */
++		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
++		return;
++	}
++
++	if (!(buf = ast_dynamic_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) {
++		transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++		return;
++	}
++
++	/* Make a copy of the response and parse it */
++	if (ast_dynamic_str_thread_set(&buf, 0, &check_auth_buf, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) {
++		transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++		return;
++	}
++
++	c = buf->str;
++
++	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 = "\"";
++			}
++			i->s = c;
++			strsep(&c, separator);
++			break;
++		}
++		if (i->key == NULL) { /* not found, jump after space or comma */
++			strsep(&c, " ,");
++		}
++	}
++
++	/* Verify nonce from request matches our nonce.  If not, send 401 with new nonce */
++	if (strcasecmp(p->randdata, keys[K_NONCE].s)) {
++		if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
++			ast_string_field_build(p, randdata, "%08lx", ast_random());
++		}
++		transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE);
++
++		/* Schedule auto destroy in 32 seconds */
++		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
++	} else {
++		transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
++	}
+ }
+ 
+ /*! \brief Verify registration of user 
+@@ -9010,6 +9096,14 @@
+ 			}
+ 		}
+ 	}
++	if (!peer && global_alwaysauthreject) {
++		/* If we found a peer, we transmit a 100 Trying.  Therefore, if we're
++		 * trying to avoid leaking information, we MUST also transmit the same
++		 * response when we DON'T find a peer. */
++		transmit_response(p, "100 Trying", req);
++		/* Insert a fake delay between the 100 and the subsequent failure. */
++		sched_yield();
++	}
+ 	if (!res) {
+ 		ast_device_state_changed("SIP/%s", peer->name);
+ 	}
+@@ -9020,7 +9114,7 @@
+ 			transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
+ 			break;
+ 		case AUTH_USERNAME_MISMATCH:
+-			/* Username and digest username does not match. 
++			/* 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 */
+@@ -9030,7 +9124,7 @@
+ 		case AUTH_PEER_NOT_DYNAMIC:
+ 		case AUTH_ACL_FAILED:
+ 			if (global_alwaysauthreject) {
+-				transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE);
++				transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE);
+ 			} else {
+ 				/* URI not found */
+ 				if (res == AUTH_PEER_NOT_DYNAMIC)
+@@ -14557,7 +14651,7 @@
+ 		if (res < 0) { /* Something failed in authentication */
+ 			if (res == AUTH_FAKE_AUTH) {
+ 				ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
+-				transmit_fake_auth_response(p, req, XMIT_RELIABLE);
++				transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE);
+ 			} else {
+   				ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
+ 				transmit_response_reliable(p, "403 Forbidden", req);
+@@ -15594,7 +15688,7 @@
+ 	if (res < 0) {
+ 		if (res == AUTH_FAKE_AUTH) {
+ 			ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
+-			transmit_fake_auth_response(p, req, XMIT_UNRELIABLE);
++			transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE);
+ 		} else {
+ 			ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
+ 			transmit_response_reliable(p, "403 Forbidden", req);

Added: asterisk/branches/lenny/debian/patches/AST-2009-006
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/AST-2009-006?rev=7759&op=file
==============================================================================
--- asterisk/branches/lenny/debian/patches/AST-2009-006 (added)
+++ asterisk/branches/lenny/debian/patches/AST-2009-006 Sat Nov  7 06:55:50 2009
@@ -1,0 +1,2195 @@
+Index: channels/iax2-parser.h
+===================================================================
+--- a/channels/iax2-parser.h	(revision 215999)
++++ b/channels/iax2-parser.h	(working copy)
+@@ -74,6 +74,8 @@
+ 	unsigned short rr_delay;
+ 	unsigned int rr_dropped;
+ 	unsigned int rr_ooo;
++	unsigned char calltoken;
++	unsigned char *calltokendata;
+ };
+ 
+ #define DIRECTION_INGRESS 1
+Index: channels/chan_sip.c
+===================================================================
+--- a/channels/chan_sip.c	(revision 215999)
++++ b/channels/chan_sip.c	(working copy)
+@@ -2661,7 +2661,7 @@
+ 					if (!strcasecmp(tmp->name, "host")) {
+ 						struct hostent *hp;
+ 						struct ast_hostent ahp;
+-						if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
++						if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
+ 							/* No match */
+ 							ast_variables_destroy(var);
+ 							var = NULL;
+Index: channels/chan_iax2.c
+===================================================================
+--- a/channels/chan_iax2.c	(revision 215999)
++++ b/channels/chan_iax2.c	(working copy)
+@@ -276,6 +276,20 @@
+ static int reload_config(void);
+ static int iax2_reload(int fd, int argc, char *argv[]);
+ 
++/*!
++ * \brief Call token validation settings.
++ */
++enum calltoken_peer_enum {
++	/*! \brief Default calltoken required unless the ip is in the ignorelist */
++	CALLTOKEN_DEFAULT = 0,
++	/*! \brief Require call token validation. */
++	CALLTOKEN_YES = 1,
++	/*! \brief Require call token validation after a successful registration
++	 *         using call token validation occurs. */
++	CALLTOKEN_AUTO = 2,
++	/*! \brief Do not require call token validation. */
++	CALLTOKEN_NO = 3,
++};
+ 
+ struct iax2_user {
+ 	AST_DECLARE_STRING_FIELDS(
+@@ -303,6 +317,7 @@
+ 	struct ast_ha *ha;
+ 	struct iax2_context *contexts;
+ 	struct ast_variable *vars;
++	enum calltoken_peer_enum calltoken_required;        /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
+ };
+ 
+ struct iax2_peer {
+@@ -353,8 +368,10 @@
+ 	int pokefreqnotok;				/*!< How often to check when the host has been determined to be down */
+ 	int historicms;					/*!< How long recent average responses took */
+ 	int smoothing;					/*!< Sample over how many units to determine historic ms */
++	uint16_t maxcallno;					/*!< Max call number limit for this peer.  Set on registration */
+ 	
+ 	struct ast_ha *ha;
++	enum calltoken_peer_enum calltoken_required;        /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
+ };
+ 
+ #define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
+@@ -502,6 +519,8 @@
+ 	struct ast_codec_pref rprefs;
+ 	/*! Our call number */
+ 	unsigned short callno;
++	/*! Our callno_entry entry */
++	struct callno_entry *callno_entry;
+ 	/*! Peer callno */
+ 	unsigned short peercallno;
+ 	/*! Negotiated format, this is only used to remember what format was
+@@ -634,13 +653,27 @@
+ 	int frames_dropped;
+ 	/*! received frame count: (just for stats) */
+ 	int frames_received;
++	/*! num bytes used for calltoken ie, even an empty ie should contain 2 */
++	unsigned char calltoken_ie_len;
+ };
+ 
++/*! table of available call numbers */
++static struct ao2_container *callno_pool;
++
++/*! table of available trunk call numbers */
++static struct ao2_container *callno_pool_trunk;
++
++static const unsigned int CALLNO_POOL_BUCKETS = 2699;
++
+ static struct ast_iax2_queue {
+ 	AST_LIST_HEAD(, iax_frame) queue;
+ 	int count;
+ } iaxq;
+ 
++static int randomcalltokendata;
++
++static const time_t MAX_CALLTOKEN_DELAY = 10;
++
+ /*!
+  * This module will get much higher performance when doing a lot of
+  * user and peer lookups if the number of buckets is increased from 1.
+@@ -660,6 +693,58 @@
+ #define MAX_USER_BUCKETS MAX_PEER_BUCKETS
+ static struct ao2_container *users;
+ 
++
++/*! Table containing peercnt objects for every ip address consuming a callno */
++static struct ao2_container *peercnts;
++
++/*! Table containing custom callno limit rules for a range of ip addresses. */
++static struct ao2_container *callno_limits;
++
++/*! Table containing ip addresses not requiring calltoken validation */
++static struct ao2_container *calltoken_ignores;
++
++static uint16_t DEFAULT_MAXCALLNO_LIMIT = 2048;
++
++static uint16_t DEFAULT_MAXCALLNO_LIMIT_NONVAL = 8192;
++
++static uint16_t global_maxcallno;
++
++/*! Total num of call numbers allowed to be allocated without calltoken validation */
++static uint16_t global_maxcallno_nonval;
++
++static uint16_t total_nonval_callno_used = 0;
++
++/*! peer connection private, keeps track of all the call numbers
++ *  consumed by a single ip address */
++struct peercnt {
++	/*! ip address consuming call numbers */
++	unsigned long addr;
++	/*! Number of call numbers currently used by this ip address */
++	uint16_t cur;
++	/*! Max call numbers allowed for this ip address */
++	uint16_t limit;
++	/*! Specifies whether limit is set by a registration or not, if so normal
++ 	 *  limit setting rules do not apply to this address. */
++	unsigned char reg;
++};
++
++/*! used by both callno_limits and calltoken_ignores containers */
++struct addr_range {
++	/*! ip address range for custom callno limit rule */
++	struct ast_ha ha;
++	/*! callno limit for this ip address range, only used in callno_limits container */
++	uint16_t limit;
++	/*! delete me marker for reloads */
++	unsigned char delme;
++};
++
++struct callno_entry {
++	/*! callno used for this entry */
++	uint16_t callno;
++	/*! was this callno calltoken validated or not */
++	unsigned char validated;
++};
++
+ static struct ast_firmware_list {
+ 	struct iax_firmware *wares;
+ 	ast_mutex_t lock;
+@@ -698,6 +783,7 @@
+ 
+ static void reg_source_db(struct iax2_peer *p);
+ static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
++static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin);
+ 
+ static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
+ 
+@@ -819,7 +905,6 @@
+ /* XXX We probably should use a mutex when working with this XXX */
+ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
+ static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)];
+-static struct timeval lastused[ARRAY_LEN(iaxs)];
+ 
+ /*!
+  * \brief Another container of iax2_pvt structures
+@@ -882,6 +967,9 @@
+ static int encrypt_frame(aes_encrypt_ctx *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen);
+ static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt);
+ static void build_rand_pad(unsigned char *buf, ssize_t len);
++static struct callno_entry *get_unused_callno(int trunk, int validated);
++static int replace_callno(const void *obj);
++static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
+ 
+ static const struct ast_channel_tech iax2_tech = {
+ 	.type = "IAX2",
+@@ -1346,8 +1434,7 @@
+ 
+ retry:
+ 	pvt = iaxs[callno];
+-	gettimeofday(&lastused[callno], NULL);
+-	
++
+ 	owner = pvt ? pvt->owner : NULL;
+ 
+ 	if (owner) {
+@@ -1427,6 +1514,8 @@
+ 	struct iax_frame *cur = NULL;
+ 
+ 	iax2_destroy_helper(pvt);
++	sched_delay_remove(&pvt->addr, pvt->callno_entry);
++	pvt->callno_entry = NULL;
+ 
+ 	/* Already gone */
+ 	ast_set_flag(pvt, IAX_ALREADYGONE);	
+@@ -1515,9 +1604,19 @@
+ 	return new;
+ }
+ 
+-#define NEW_PREVENT 	0
+-#define NEW_ALLOW 	1
+-#define NEW_FORCE 	2
++/* keep these defined in this order.  They are used in find_callno to
++ * determine whether or not a new call number should be allowed. */
++enum {
++	/* do not allow a new call number, only search ones in use for match */
++	NEW_PREVENT = 0,
++	/* search for match first, then allow a new one to be allocated */
++	NEW_ALLOW = 1,
++	/* do not search for match, force a new call number */
++	NEW_FORCE = 2,
++	/* do not search for match, force a new call number.  Signifies call number
++ 	 * has been calltoken validated */
++	NEW_ALLOW_CALLTOKEN_VALIDATED = 3,
++};
+ 
+ static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur, int check_dcallno)
+ {
+@@ -1557,7 +1656,7 @@
+ {
+ 	int x;
+ 	int res= 0;
+-	struct timeval now;
++	struct callno_entry *callno_entry;
+ 	if (iaxs[callno]->oseqno) {
+ 		ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n");
+ 		return -1;
+@@ -1566,35 +1665,43 @@
+ 		ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
+ 		return -1;
+ 	}
+-	gettimeofday(&now, NULL);
+-	for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) {
+-		ast_mutex_lock(&iaxsl[x]);
+-		if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
+-			/* Update the two timers that should have been started */
+-			/*!
+-			 * \note We delete these before switching the slot, because if
+-			 * they fire in the meantime, they will generate a warning.
+-			 */
+-			AST_SCHED_DEL(sched, iaxs[callno]->pingid);
+-			AST_SCHED_DEL(sched, iaxs[callno]->lagid);
+-			iaxs[x] = iaxs[callno];
+-			iaxs[x]->callno = x;
+-			iaxs[callno] = NULL;
+-			iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
+-			iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
+-			if (locked)
+-				ast_mutex_unlock(&iaxsl[callno]);
+-			res = x;
+-			if (!locked)
+-				ast_mutex_unlock(&iaxsl[x]);
+-			break;
+-		}
+-		ast_mutex_unlock(&iaxsl[x]);
+-	}
+-	if (x >= ARRAY_LEN(iaxs) - 1) {
++	if (!(callno_entry = get_unused_callno(1, iaxs[callno]->callno_entry->validated))) {
+ 		ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
+ 		return -1;
+ 	}
++
++	x = callno_entry->callno;
++	ast_mutex_lock(&iaxsl[x]);
++
++	/*!
++	 * \note We delete these before switching the slot, because if
++	 * they fire in the meantime, they will generate a warning.
++	 */
++	AST_SCHED_DEL(sched, iaxs[callno]->pingid);
++	AST_SCHED_DEL(sched, iaxs[callno]->lagid);
++	iaxs[x] = iaxs[callno];
++	iaxs[x]->callno = x;
++
++	/* since we copied over the pvt from a different callno, make sure the old entry is replaced
++	 * before assigning the new one */
++	if (iaxs[x]->callno_entry) {
++		iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, iaxs[x]->callno_entry);
++	}
++	iaxs[x]->callno_entry = callno_entry;
++
++	iaxs[callno] = NULL;
++	/* Update the two timers that should have been started */
++	iaxs[x]->pingid = iax2_sched_add(sched, 
++		ping_time * 1000, send_ping, (void *)(long)x);
++	iaxs[x]->lagid = iax2_sched_add(sched, 
++		lagrq_time * 1000, send_lagrq, (void *)(long)x);
++
++	if (locked)
++		ast_mutex_unlock(&iaxsl[callno]);
++	res = x;
++	if (!locked)
++		ast_mutex_unlock(&iaxsl[x]);
++
+ 	if (option_debug)
+ 		ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x);
+ 	/* We move this call from a non-trunked to a trunked call */
+@@ -1603,14 +1710,604 @@
+ 	return res;
+ }
+ 
++static int addr_range_delme_cb(void *obj, void *arg, int flags)
++{
++	struct addr_range *lim = obj;
++	lim->delme = 1;
++	return 0;
++}
++
++static int addr_range_hash_cb(const void *obj, const int flags)
++{
++	const struct addr_range *lim = obj;
++	return abs((int) lim->ha.netaddr.s_addr);
++}
++
++static int addr_range_cmp_cb(void *obj, void *arg, int flags)
++{
++	struct addr_range *lim1 = obj, *lim2 = arg;
++	return ((lim1->ha.netaddr.s_addr == lim2->ha.netaddr.s_addr) &&
++		(lim1->ha.netmask.s_addr == lim2->ha.netmask.s_addr)) ?
++		CMP_MATCH | CMP_STOP : 0;
++}
++
++static int peercnt_hash_cb(const void *obj, const int flags)
++{
++	const struct peercnt *peercnt = obj;
++	return abs((int) peercnt->addr);
++}
++
++static int peercnt_cmp_cb(void *obj, void *arg, int flags)
++{
++	struct peercnt *peercnt1 = obj, *peercnt2 = arg;
++	return (peercnt1->addr == peercnt2->addr) ? CMP_MATCH | CMP_STOP : 0;
++}
++
++static int addr_range_match_address_cb(void *obj, void *arg, int flags)
++{
++	struct addr_range *addr_range = obj;
++	struct sockaddr_in *sin = arg;
++
++	if ((sin->sin_addr.s_addr & addr_range->ha.netmask.s_addr) == addr_range->ha.netaddr.s_addr) {
++		return CMP_MATCH | CMP_STOP;
++	}
++	return 0;
++}
++
++/*! 
++ * \internal
++ *
++ * \brief compares sin to calltoken_ignores table to determine if validation is required.
++ */
++static int calltoken_required(struct sockaddr_in *sin, const char *name, int subclass)
++{
++	struct addr_range *addr_range;
++	struct iax2_peer *peer = NULL;
++	struct iax2_user *user = NULL;
++	/* if no username is given, check for guest accounts */
++	const char *find = S_OR(name, "guest");
++	int res = 1;  /* required by default */
++	int optional = 0;
++	enum calltoken_peer_enum calltoken_required = CALLTOKEN_DEFAULT;
++	/* There are only two cases in which calltoken validation is not required.
++	 * Case 1. sin falls within the list of address ranges specified in the calltoken optional table and
++	 *         the peer definition has not set the requirecalltoken option.
++	 * Case 2. Username is a valid peer/user, and that peer has requirecalltoken set either auto or no.
++	 */
++
++	/* ----- Case 1 ----- */
++	if ((addr_range = ao2_callback(calltoken_ignores, 0, addr_range_match_address_cb, sin))) {
++		ao2_ref(addr_range, -1);
++		optional = 1;
++	}
++
++	/* ----- Case 2 ----- */
++	if ((subclass == IAX_COMMAND_NEW) && (user = find_user(find))) {
++		calltoken_required = user->calltoken_required;
++	} else if ((subclass == IAX_COMMAND_NEW) && (user = realtime_user(find, sin))) {
++		calltoken_required = user->calltoken_required;
++	} else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(find, 0))) {
++		calltoken_required = peer->calltoken_required;
++	} else if ((subclass != IAX_COMMAND_NEW) && (peer = realtime_peer(find, sin))) {
++		calltoken_required = peer->calltoken_required;
++	}
++
++	if (peer) {
++		peer_unref(peer);
++	}
++	if (user) {
++		user_unref(user);
++	}
++	if (option_debug) {
++	ast_log(LOG_DEBUG, "Determining if address %s with username %s requires calltoken validation.  Optional = %d  calltoken_required = %d \n", ast_inet_ntoa(sin->sin_addr), name, optional, calltoken_required);
++	}
++	if (((calltoken_required == CALLTOKEN_NO) || (calltoken_required == CALLTOKEN_AUTO)) ||
++		(optional && (calltoken_required == CALLTOKEN_DEFAULT))) {
++		res = 0;
++	}
++
++	return res;
++}
++
++/*! 
++ * \internal
++ *
++ * \brief set peercnt callno limit.
++ *
++ * \details 
++ * First looks in custom definitions. If not found, global limit
++ * is used.  Entries marked as reg already have
++ * a custom limit set by a registration and are not modified.
++ */
++static void set_peercnt_limit(struct peercnt *peercnt)
++{
++	uint16_t limit = global_maxcallno;
++	struct addr_range *addr_range;
++	struct sockaddr_in sin = {
++		.sin_addr.s_addr = peercnt->addr,
++	};
++
++
++	if (peercnt->reg && peercnt->limit) {
++		return; /* this peercnt has a custom limit set by a registration */
++	}
++
++	if ((addr_range = ao2_callback(callno_limits, 0, addr_range_match_address_cb, &sin))) {
++		limit = addr_range->limit;
++		if (option_debug) {
++			ast_log(LOG_NOTICE, "custom addr_range %d found for %s\n", limit, ast_inet_ntoa(sin.sin_addr));
++		}
++		ao2_ref(addr_range, -1);
++	}
++
++	peercnt->limit = limit;
++}
++
++/*! 
++ * \internal
++ * \brief sets limits for all peercnts in table. done on reload to reflect changes in conf.
++ */
++static int set_peercnt_limit_all_cb(void *obj, void *arg, int flags)
++{
++	struct peercnt *peercnt = obj;
++
++	set_peercnt_limit(peercnt);
++	if (option_debug) {
++		ast_log(LOG_NOTICE, "Reset limits for peercnts table\n");
++	}
++	return 0;
++}
++
++/*! 
++ * \internal
++ * \brief returns match if delme is set. 
++ */
++static int prune_addr_range_cb(void *obj, void *arg, int flags)
++{
++	struct addr_range *addr_range = obj;
++
++	return addr_range->delme ? CMP_MATCH : 0;
++}
++
++/*! 
++ * \internal
++ * \brief modifies peercnt entry in peercnts table. Used to set custom limit or mark a registered ip
++ */
++static void peercnt_modify(unsigned char reg, uint16_t limit, struct sockaddr_in *sin)
++{
++	/* this function turns off and on custom callno limits set by peer registration */
++	struct peercnt *peercnt;
++	struct peercnt tmp = {
++		.addr = sin->sin_addr.s_addr,
++	};
++
++	if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
++		peercnt->reg = reg;
++		if (limit) {
++			peercnt->limit = limit;
++		} else {
++			set_peercnt_limit(peercnt);
++		}
++		if (option_debug) {
++			ast_log(LOG_NOTICE, "peercnt entry %s modified limit:%d registered:%d", ast_inet_ntoa(sin->sin_addr), peercnt->limit, peercnt->reg);
++		}
++		ao2_ref(peercnt, -1); /* decrement ref from find */
++	}
++}
++
++/*! 
++ * \internal
++ * \brief adds an ip to the peercnts table, increments connection count if it already exists
++ *
++ * \details First searches for the address in the peercnts table.  If found
++ * the current count is incremented.  If not found a new peercnt is allocated
++ * and linked into the peercnts table with a call number count of 1.
++ */
++static int peercnt_add(struct sockaddr_in *sin)
++{
++	struct peercnt *peercnt;
++	unsigned long addr = sin->sin_addr.s_addr;
++	int res = 0;
++	struct peercnt tmp = {
++		.addr = addr,
++	};
++
++	/* Reasoning for peercnts container lock:  Two identical ip addresses
++	 * could be added by different threads at the "same time". Without the container
++	 * lock, both threads could alloc space for the same object and attempt
++	 * to link to table.  With the lock, one would create the object and link
++	 * to table while the other would find the already created peercnt object
++	 * rather than creating a new one. */
++	ao2_lock(peercnts);
++	if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
++		ao2_lock(peercnt);
++	} else if ((peercnt = ao2_alloc(sizeof(*peercnt), NULL))) {
++		ao2_lock(peercnt);
++		/* create and set defaults */
++		peercnt->addr = addr;
++		set_peercnt_limit(peercnt);
++		/* guarantees it does not go away after unlocking table
++		 * ao2_find automatically adds this */
++		ao2_link(peercnts, peercnt);
++	} else {
++		ao2_unlock(peercnts);
++		return -1;
++	}
++
++	/* check to see if the address has hit its callno limit.  If not increment cur. */
++	if (peercnt->limit > peercnt->cur) {
++		peercnt->cur++;
++		if (option_debug) {
++			ast_log(LOG_NOTICE, "ip callno count incremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin->sin_addr));
++		}
++	} else { /* max num call numbers for this peer has been reached! */
++		ast_log(LOG_ERROR, "maxcallnumber limit of %d for %s has been reached!\n", peercnt->limit, ast_inet_ntoa(sin->sin_addr));
++		res = -1;
++	}
++
++	/* clean up locks and ref count */
++	ao2_unlock(peercnt);
++	ao2_unlock(peercnts);
++	ao2_ref(peercnt, -1); /* decrement ref from find/alloc, only the container ref remains. */
++
++	return res;
++}
++
++/*! 
++ * \internal
++ * \brief decrements a peercnts table entry
++ */
++static void peercnt_remove(struct peercnt *peercnt)
++{
++	struct sockaddr_in sin = {
++		.sin_addr.s_addr = peercnt->addr,
++	};
++
++	if (peercnt) {
++		/* Container locked here since peercnt may be unlinked from list.  If left unlocked,
++		 * peercnt_add could try and grab this entry from the table and modify it at the
++		 * "same time" this thread attemps to unlink it.*/
++		ao2_lock(peercnts);
++		peercnt->cur--;
++		if (option_debug) {
++			ast_log(LOG_NOTICE, "ip callno count decremented to %d for %s\n", peercnt->cur, ast_inet_ntoa(sin.sin_addr));
++		}
++		/* if this was the last connection from the peer remove it from table */
++		if (peercnt->cur == 0) {
++			ao2_unlink(peercnts, peercnt);/* decrements ref from table, last ref is left to scheduler */
++		}
++		ao2_unlock(peercnts);
++	}
++}
++
++/*! 
++ * \internal
++ * \brief called by scheduler to decrement object
++ */
++static int peercnt_remove_cb(const void *obj)
++{
++	struct peercnt *peercnt = (struct peercnt *) obj;
++
++	peercnt_remove(peercnt);
++	ao2_ref(peercnt, -1); /* decrement ref from scheduler */
++
++	return 0;
++}
++
++/*! 
++ * \internal
++ * \brief decrements peercnts connection count, finds by addr
++ */
++static int peercnt_remove_by_addr(struct sockaddr_in *sin)
++{
++	struct peercnt *peercnt;
++	struct peercnt tmp = {
++		.addr = sin->sin_addr.s_addr,
++	};
++
++	if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
++		peercnt_remove(peercnt);
++		ao2_ref(peercnt, -1); /* decrement ref from find */
++	}
++	return 0;
++}
++
++/*! 
++ * \internal
++ * \brief Create callno_limit entry based on configuration
++ */
++static void build_callno_limits(struct ast_variable *v)
++{
++	struct addr_range *addr_range = NULL;
++	struct addr_range tmp;
++	struct ast_ha *ha;
++	int limit;
++	int found;
++
++	for (; v; v = v->next) {
++		limit = -1;
++		found = 0;
++		ha = ast_append_ha("permit", v->name, NULL);
++
++		/* check for valid config information */
++		if (!ha) {
++			ast_log(LOG_ERROR, "Call number limit for %s could not be added, Invalid address range\n.", v->name);
++			continue;
++		} else if ((sscanf(v->value, "%d", &limit) != 1) || (limit < 0)) {
++			ast_log(LOG_ERROR, "Call number limit for %s could not be added. Invalid limit %s\n.", v->name, v->value);
++			ast_free_ha(ha);
++			continue;
++		}
++
++		ast_copy_ha(ha, &tmp.ha);
++		/* find or create the addr_range */
++		if ((addr_range = ao2_find(callno_limits, &tmp, OBJ_POINTER))) {
++			ao2_lock(addr_range);
++			found = 1;
++		} else if (!(addr_range = ao2_alloc(sizeof(*addr_range), NULL))) {
++			ast_free_ha(ha);
++			return; /* out of memory */
++		}
++
++		/* copy over config data into addr_range object */
++		ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible for each limit */
++		ast_free_ha(ha); /* cleanup the tmp ha */
++		addr_range->limit = limit;
++		addr_range->delme = 0;
++
++		/* cleanup */
++		if (found) {
++			ao2_unlock(addr_range);
++		} else {
++			ao2_link(callno_limits, addr_range);
++		}
++		ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */
++	}
++}
++
++/*! 
++ * \internal
++ * \brief Create calltoken_ignores entry based on configuration
++ */
++static int add_calltoken_ignore(const char *addr)
++{
++	struct addr_range tmp;
++	struct addr_range *addr_range = NULL;
++	struct ast_ha *ha = NULL;
++
++	if (ast_strlen_zero(addr)) {
++		ast_log(LOG_WARNING, "invalid calltokenoptional %s\n", addr);
++		return -1;
++	}
++
++	ha = ast_append_ha("permit", addr, NULL);
++
++	/* check for valid config information */
++	if (!ha) {
++		ast_log(LOG_WARNING, "Error creating calltokenoptional entry %s\n", addr);
++		return -1;
++	}
++
++	ast_copy_ha(ha, &tmp.ha);
++	/* find or create the addr_range */
++	if ((addr_range = ao2_find(calltoken_ignores, &tmp, OBJ_POINTER))) {
++		ao2_lock(addr_range);
++		addr_range->delme = 0;
++		ao2_unlock(addr_range);
++	} else if ((addr_range = ao2_alloc(sizeof(*addr_range), NULL))) {
++		/* copy over config data into addr_range object */
++		ast_copy_ha(ha, &addr_range->ha); /* this is safe because only one ha is possible */
++		ao2_link(calltoken_ignores, addr_range);
++	} else {
++		ast_free_ha(ha);
++		return -1;
++	}
++
++	ast_free_ha(ha);
++	ao2_ref(addr_range, -1); /* decrement ref from ao2_find and ao2_alloc, only container ref remains */
++
++	return 0;
++}
++
++static int iax2_show_callnumber_usage(int fd, int argc, char *argv[])
++{
++	struct ao2_iterator i;
++	struct peercnt *peercnt;
++	struct sockaddr_in sin;
++	int found = 0;
++
++	if (argc < 4 || argc > 5)
++		return RESULT_SHOWUSAGE;
++
++	ast_cli(fd, "%-15s %-12s %-12s\n", "Address", "Callno Usage", "Callno Limit");
++	i = ao2_iterator_init(peercnts, 0);
++	while ((peercnt = ao2_iterator_next(&i))) {
++		sin.sin_addr.s_addr = peercnt->addr;
++		if (argc == 5 && (!strcasecmp(argv[4], ast_inet_ntoa(sin.sin_addr)))) {
++				ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
++				found = 1;
++				break;
++		} else {
++			ast_cli(fd, "%-15s %-12d %-12d\n", ast_inet_ntoa(sin.sin_addr), peercnt->cur, peercnt->limit);
++		}
++		ao2_ref(peercnt, -1);
++	}
++	if (argc == 4) {
++		ast_cli(fd, "\nNon-CallToken Validation Limit: %d\nNon-CallToken Validated: %d\n", global_maxcallno_nonval, total_nonval_callno_used);
++	} else if (argc == 5 && !found) {
++		ast_cli(fd, "No callnumber table entries for %s found\n", argv[4] );
++	}
++	return RESULT_SUCCESS;
++}
++
++static struct callno_entry *get_unused_callno(int trunk, int validated)
++{
++	struct callno_entry *callno_entry = NULL;
++	if ((!ao2_container_count(callno_pool) && !trunk) || (!ao2_container_count(callno_pool_trunk) && trunk)) {
++		ast_log(LOG_WARNING, "Out of CallNumbers\n");
++		/* Minor optimization for the extreme case. */
++		return NULL;
++	}
++
++	/* the callno_pool container is locked here primarily to ensure thread
++	 * safety of the total_nonval_callno_used check and increment */
++	ao2_lock(callno_pool);
++
++	/* only a certain number of nonvalidated call numbers should be allocated.
++	 * If there ever is an attack, this separates the calltoken validating
++	 * users from the non calltoken validating users. */
++	if (!validated && (total_nonval_callno_used >= global_maxcallno_nonval)) {
++		ast_log(LOG_WARNING, "NON-CallToken callnumber limit is reached. Current:%d Max:%d\n", total_nonval_callno_used, global_maxcallno_nonval);
++		ao2_unlock(callno_pool);
++		return NULL;
++	}
++
++	/* unlink the object from the container, taking over ownership
++	 * of the reference the container had to the object */
++	callno_entry = ao2_find((trunk ? callno_pool_trunk : callno_pool), NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE);
++
++	if (callno_entry) {
++		callno_entry->validated = validated;
++		if (!validated) {
++			total_nonval_callno_used++;
++		}
++	}
++
++	ao2_unlock(callno_pool);
++	return callno_entry;
++}
++
++static int replace_callno(const void *obj)
++{
++	struct callno_entry *callno_entry = (struct callno_entry *) obj;
++
++	/* the callno_pool container is locked here primarily to ensure thread
++	 * safety of the total_nonval_callno_used check and decrement */
++	ao2_lock(callno_pool);
++
++	if (!callno_entry->validated && (total_nonval_callno_used != 0)) {
++		total_nonval_callno_used--;
++	} else if (!callno_entry->validated && (total_nonval_callno_used == 0)) {
++		ast_log(LOG_ERROR, "Attempted to decrement total non calltoken validated callnumbers below zero... Callno is:%d \n", callno_entry->callno);
++	}
++
++	if (callno_entry->callno < TRUNK_CALL_START) {
++		ao2_link(callno_pool, callno_entry);
++	} else {
++		ao2_link(callno_pool_trunk, callno_entry);
++	}
++	ao2_ref(callno_entry, -1); /* only container ref remains */
++
++	ao2_unlock(callno_pool);
++	return 0;
++}
++
++static int callno_hash(const void *obj, const int flags)
++{
++	return abs(ast_random());
++}
++
++static int create_callno_pools(void)
++{
++	uint16_t i;
++
++	if (!(callno_pool = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
++		return -1;
++	}
++
++	if (!(callno_pool_trunk = ao2_container_alloc(CALLNO_POOL_BUCKETS, callno_hash, NULL))) {
++		return -1;
++	}
++
++	/* start at 2, 0 and 1 are reserved */
++	for (i = 2; i <= IAX_MAX_CALLS; i++) {
++		struct callno_entry *callno_entry;
++
++		if (!(callno_entry = ao2_alloc(sizeof(*callno_entry), NULL))) {
++			return -1;
++		}
++
++		callno_entry->callno = i;
++
++		if (i < TRUNK_CALL_START) {
++			ao2_link(callno_pool, callno_entry);
++		} else {
++			ao2_link(callno_pool_trunk, callno_entry);
++		}
++
++		ao2_ref(callno_entry, -1);
++	}
++
++	return 0;
++}
++
+ /*!
++ * \internal
++ * \brief Schedules delayed removal of iax2_pvt call number data
++ *
++ * \note After MIN_REUSE_TIME has passed for a destroyed iax2_pvt, the callno is
++ * avaliable again, and the address from the previous connection must be decremented
++ * from the peercnts table.  This function schedules these operations to take place.
++ */
++static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry)
++{
++	int i;
++	struct peercnt *peercnt;
++	struct peercnt tmp = {
++		.addr = sin->sin_addr.s_addr,
++	};
++
++	if ((peercnt = ao2_find(peercnts, &tmp, OBJ_POINTER))) {
++		/* refcount is incremented with ao2_find.  keep that ref for the scheduler */
++		if (option_debug) {
++			ast_log(LOG_NOTICE, "schedule decrement of callno used for %s in %d seconds\n", ast_inet_ntoa(sin->sin_addr), MIN_REUSE_TIME);
++		}
++		i = iax2_sched_add(sched, MIN_REUSE_TIME * 1000, peercnt_remove_cb, peercnt);
++		if (i == -1) {
++			ao2_ref(peercnt, -1);
++		}
++	}
++
++	iax2_sched_add(sched, MIN_REUSE_TIME * 1000, replace_callno, callno_entry);
++}
++
++/*! 
++ * \internal
++ * \brief returns whether or not a frame is capable of starting a new IAX2 dialog. 
++ *
++ * \note For this implementation, inbound pokes should _NOT_ be capable of allocating
++ * a new callno.
++ */
++static inline int attribute_pure iax2_allow_new(int frametype, int subclass, int inbound)
++{
++	if (frametype != AST_FRAME_IAX) {
++		return 0;
++	}
++	switch (subclass) {
++	case IAX_COMMAND_NEW:
++	case IAX_COMMAND_REGREQ:
++	case IAX_COMMAND_FWDOWNL:
++	case IAX_COMMAND_REGREL:
++		return 1;
++	case IAX_COMMAND_POKE:
++		if (!inbound) {
++			return 1;
++		}
++		break;
++	}
++	return 0;
++}
++
++/*!
+  * \note Calling this function while holding another pvt lock can cause a deadlock.
+  */
+ static int __find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int return_locked, int check_dcallno)
+ {
+ 	int res = 0;
+ 	int x;
+-	struct timeval now;
++	/* this call is calltoken validated as long as it is either NEW_FORCE
++	 * or NEW_ALLOW_CALLTOKEN_VALIDATED */
++	int validated = (new > NEW_ALLOW) ? 1 : 0;
+ 	char host[80];
+ 
+ 	if (new <= NEW_ALLOW) {
+@@ -1707,8 +2404,7 @@
+ #endif
+ 	}
+ 	if (!res && (new >= NEW_ALLOW)) {
+-		int start, found = 0;
+-
++		struct callno_entry *callno_entry;
+ 		/* It may seem odd that we look through the peer list for a name for
+ 		 * this *incoming* call.  Well, it is weird.  However, users don't
+ 		 * have an IP address/port number that we can match against.  So,
+@@ -1718,36 +2414,28 @@
+ 		if (!iax2_getpeername(*sin, host, sizeof(host)))
+ 			snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
+ 
+-		now = ast_tvnow();
+-		start = 2 + (ast_random() % (TRUNK_CALL_START - 1));
+-		for (x = start; 1; x++) {
+-			if (x == TRUNK_CALL_START) {
+-				x = 1;
+-				continue;
+-			}
++		if (peercnt_add(sin)) {
++			/* This address has hit its callnumber limit.  When the limit
++			 * is reached, the connection is not added to the peercnts table.*/
++			return 0;
++		}
+ 
+-			/* Find first unused call number that hasn't been used in a while */
+-			ast_mutex_lock(&iaxsl[x]);
+-			if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
+-				found = 1;
+-				break;
+-			}
+-			ast_mutex_unlock(&iaxsl[x]);
+-			
+-			if (x == start - 1) {
+-				break;
+-			}
+-		}
+-		/* We've still got lock held if we found a spot */
+-		if (x == start - 1 && !found) {
++		if (!(callno_entry = get_unused_callno(0, validated))) {
++			/* since we ran out of space, remove the peercnt
++			 * entry we added earlier */
++			peercnt_remove_by_addr(sin);
+ 			ast_log(LOG_WARNING, "No more space\n");
+ 			return 0;
+ 		}
++		x = callno_entry->callno;
++		ast_mutex_lock(&iaxsl[x]);
++
+ 		iaxs[x] = new_iax(sin, host);
+ 		update_max_nontrunk();
+ 		if (iaxs[x]) {
+ 			if (option_debug && iaxdebug)
+ 				ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
++			iaxs[x]->callno_entry = callno_entry;
+ 			iaxs[x]->sockfd = sockfd;
+ 			iaxs[x]->addr.sin_port = sin->sin_port;
+ 			iaxs[x]->addr.sin_family = sin->sin_family;
+@@ -1771,6 +2459,7 @@
+ 		} else {
+ 			ast_log(LOG_WARNING, "Out of resources\n");
+ 			ast_mutex_unlock(&iaxsl[x]);
++			replace_callno(callno_entry);
+ 			return 0;
+ 		}
+ 		if (!return_locked)
+@@ -2523,6 +3212,10 @@
+ 		ast_cli(fd, "  Context      : %s\n", peer->context);
+ 		ast_cli(fd, "  Mailbox      : %s\n", peer->mailbox);
+ 		ast_cli(fd, "  Dynamic      : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes":"No");
++		ast_cli(fd, "  Callnum limit: %d\n", peer->maxcallno);
++		ast_cli(fd, "  Calltoken req: %s\n", (peer->calltoken_required == CALLTOKEN_YES) ? "Yes" : ((peer->calltoken_required == CALLTOKEN_AUTO) ? "Auto" : "No"));
++
++
+ 		ast_cli(fd, "  Trunk        : %s\n", ast_test_flag(peer, IAX_TRUNK) ? "Yes" : "No");
+ 		ast_cli(fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
+ 		ast_cli(fd, "  Expire       : %d\n", peer->expire);
+@@ -2998,7 +3691,7 @@
+ 				if (!strcasecmp(tmp->name, "host")) {
+ 					struct ast_hostent ahp;
+ 					struct hostent *hp;
+-					if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
++					if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
+ 						/* No match */
+ 						ast_variables_destroy(var);
+ 						var = NULL;
+@@ -3112,7 +3805,7 @@
+ 				if (!strcasecmp(tmp->name, "host")) {
+ 					struct ast_hostent ahp;
+ 					struct hostent *hp;
+-					if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
++					if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) {
+ 						/* No match */
+ 						ast_variables_destroy(var);
+ 						var = NULL;
+@@ -3334,16 +4027,250 @@
+ 
+ static int send_apathetic_reply(unsigned short callno, unsigned short dcallno,
+ 		struct sockaddr_in *sin, int command, int ts, unsigned char seqno,
+-		int sockfd)
++		int sockfd, struct iax_ie_data *ied)
+ {
+-	struct ast_iax2_full_hdr f = { .scallno = htons(0x8000 | callno), .dcallno = htons(dcallno),
+-		.ts = htonl(ts), .iseqno = seqno, .oseqno = 0, .type = AST_FRAME_IAX,
+-		.csub = compress_subclass(command) };
++	struct {
++		struct ast_iax2_full_hdr f;
++		struct iax_ie_data ied;
++	} data;
++	size_t size = sizeof(struct ast_iax2_full_hdr);
+ 
+-	return sendto(sockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin));
++	if (ied) {
++		size += ied->pos;
++		memcpy(&data.ied, ied->buf, ied->pos);
++	}
++
++	data.f.scallno = htons(0x8000 | callno);
++	data.f.dcallno = htons(dcallno);
++	data.f.ts = htonl(ts);
++	data.f.iseqno = seqno;
++	data.f.oseqno = 0;
++	data.f.type = AST_FRAME_IAX;
++	data.f.csub = compress_subclass(command);
++
++	return sendto(sockfd, &data, size, 0, (struct sockaddr *)sin, sizeof(*sin));
+ }
+ 
++static void add_empty_calltoken_ie(struct chan_iax2_pvt *pvt, struct iax_ie_data *ied)
++{
++	/* first make sure their are two empty bytes left in ied->buf */
++	if (pvt && ied && (2 < ((int) sizeof(ied->buf) - ied->pos))) {
++		ied->buf[ied->pos++] = IAX_IE_CALLTOKEN;  /* type */
++		ied->buf[ied->pos++] = 0;   /* data size,  ZERO in this case */
++		pvt->calltoken_ie_len = 2;
++	}
++}
++
++static void resend_with_token(int callno, struct iax_frame *f, const char *newtoken)
++{
++	struct chan_iax2_pvt *pvt = iaxs[callno];
++	int frametype = f->af.frametype;
++	int subclass = f->af.subclass;
++	struct {
++		struct ast_iax2_full_hdr fh;
++		struct iax_ie_data ied;
++	} data = {
++		.ied.buf = { 0 },
++		.ied.pos = 0,
++	};
++	/* total len - header len gives us the frame's IE len */
++	int ie_data_pos = f->datalen - sizeof(struct ast_iax2_full_hdr);
++
++	if (!pvt) {
++		return;  /* this should not be possible if called from socket_process() */
++	}
++
++	/* 
++	 * Check to make sure last frame sent is valid for call token resend
++	 * 1. Frame should _NOT_ be encrypted since it starts the IAX dialog 
++	 * 2. Frame should _NOT_ already have a destination callno
++	 * 3. Frame must be a valid iax_frame subclass capable of starting dialog
++	 * 4. Pvt must have a calltoken_ie_len which represents the number of
++	 *    bytes at the end of the frame used for the previous calltoken ie.
++	 * 5. Pvt's calltoken_ie_len must be _LESS_ than the total IE length
++	 * 6. Total length of f->data must be _LESS_ than size of our data struct
++	 *    because f->data must be able to fit within data. 
++	 */
++	if (f->encmethods || f->dcallno || !iax2_allow_new(frametype, subclass, 0)
++		|| !pvt->calltoken_ie_len || (pvt->calltoken_ie_len > ie_data_pos) ||
++		(f->datalen > sizeof(data))) {
++
++		return;  /* ignore resend, token was not valid for the dialog */
++	}
++
++	/* token is valid
++	 * 1. Copy frame data over
++	 * 2. Redo calltoken IE, it will always be the last ie in the frame.
++	 *    NOTE: Having the ie always be last is not protocol specified,
++	 *    it is only an implementation choice.  Since we only expect the ie to
++	 *    be last for frames we have sent, this can no way be affected by
++	 *    another end point.
++	 * 3. Remove frame from queue
++	 * 4. Free old frame
++	 * 5. Clear previous seqnos
++	 * 6. Resend with CALLTOKEN ie.
++	 */
++
++	/* ---1.--- */
++	memcpy(&data, f->data, f->datalen);
++	data.ied.pos = ie_data_pos;
++
++	/* ---2.--- */
++	/* move to the beginning of the calltoken ie so we can write over it */
++	data.ied.pos -= pvt->calltoken_ie_len;
++	iax_ie_append_str(&data.ied, IAX_IE_CALLTOKEN, newtoken);
++
++	/* make sure to update token length incase it ever has to be stripped off again */
++	pvt->calltoken_ie_len = data.ied.pos - ie_data_pos; /* new pos minus old pos tells how big token ie is */
++
++	/* ---3.--- */
++	AST_LIST_LOCK(&iaxq.queue);
++	AST_LIST_REMOVE(&iaxq.queue, f, list);
++	AST_LIST_UNLOCK(&iaxq.queue);
++
++	/* ---4.--- */
++	iax2_frame_free(f);
++
++	/* ---5.--- */
++	pvt->oseqno = 0;
++	pvt->rseqno = 0;
++	pvt->iseqno = 0;
++	pvt->aseqno = 0;
++	if (pvt->peercallno) {
++		remove_by_peercallno(pvt);
++		pvt->peercallno = 0;
++	}
++
++	/* ---6.--- */
++	send_command(pvt, AST_FRAME_IAX, subclass, 0, data.ied.buf, data.ied.pos, -1);
++}
++
++static void requirecalltoken_mark_auto(const char *name, int subclass)
++{
++	struct iax2_user *user = NULL;
++	struct iax2_peer *peer = NULL;
++
++	if (ast_strlen_zero(name)) {
++		return; /* no username given */
++	}
++
++	if ((subclass == IAX_COMMAND_NEW) && (user = find_user(name)) && (user->calltoken_required == CALLTOKEN_AUTO)) {
++		user->calltoken_required = CALLTOKEN_YES;
++	} else if ((subclass != IAX_COMMAND_NEW) && (peer = find_peer(name, 1)) && (peer->calltoken_required == CALLTOKEN_AUTO)) {
++		peer->calltoken_required = CALLTOKEN_YES;
++	}
++
++	if (peer) {
++		peer_unref(peer);
++	}
++	if (user) {
++		user_unref(user);
++	}
++}
++
+ /*!
++ * \internal
++ *
++ * \brief handles calltoken logic for a received iax_frame.
++ * 
++ * \note frametype must be AST_FRAME_IAX.
++ * 
++ * \note
++ * Three different cases are possible here.
++ * Case 1. An empty calltoken is provided. This means the client supports
++ *         calltokens but has not yet received one from us.  In this case
++ *         a full calltoken IE is created and sent in a calltoken fullframe.
++ * Case 2. A full calltoken is received and must be checked for validity.
++ * Case 3. No calltoken is received indicating that the client does not
++ *         support calltokens.  In this case it is up to the configuration
++ *         to decide how this should be handled (reject or permit without calltoken)
++ */
++static int handle_call_token(struct ast_iax2_full_hdr *fh, struct iax_ies *ies,
++		struct sockaddr_in *sin, int fd)
++{
++#define CALLTOKEN_HASH_FORMAT "%s%d%u%d"  /* address + port + ts + randomcalldata */
++#define CALLTOKEN_IE_FORMAT   "%u?%s"     /* time + ? + (40 char hash) */
++	char buf[256] = { 0 };
++	time_t t = time(NULL);
++	char hash[41]; /* 40 char sha1 hash */
++	int subclass = uncompress_subclass(fh->csub);
++
++	/* ----- Case 1 ----- */
++	if (ies->calltoken && !ies->calltokendata) {  /* empty calltoken is provided, client supports calltokens */
++		struct iax_ie_data ied = {
++			.buf = { 0 },
++			.pos = 0,
++		};
++
++		/* create the hash with their address data and our timestamp */
++		snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(sin->sin_addr), sin->sin_port, (unsigned int) t, randomcalltokendata);
++		ast_sha1_hash(hash, buf);
++
++		snprintf(buf, sizeof(buf), CALLTOKEN_IE_FORMAT, (unsigned int) t, hash);
++		iax_ie_append_str(&ied, IAX_IE_CALLTOKEN, buf);
++		send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_CALLTOKEN, ntohl(fh->ts), fh->iseqno + 1, fd, &ied);
++
++		return 1;
++
++	/* ----- Case 2 ----- */
++	} else if (ies->calltoken && ies->calltokendata) { /* calltoken received, check to see if it is valid */
++		char *rec_hash = NULL;    /* the received hash, make sure it matches with ours. */
++		char *rec_ts = NULL;      /* received timestamp */
++		unsigned int rec_time;  /* received time_t */
++
++		/* split the timestamp from the hash data */
++		rec_hash = strchr((char *) ies->calltokendata, '?');
++		if (rec_hash) {
++			*rec_hash++ = '\0';
++			rec_ts = (char *) ies->calltokendata;
++		}
++
++		/* check that we have valid data before we do any comparisons */
++		if (!rec_hash || !rec_ts) {
++			goto reject;
++		} else if (sscanf(rec_ts, "%u", &rec_time) != 1) {
++			goto reject;
++		}
++
++		/* create a hash with their address and the _TOKEN'S_ timestamp */
++		snprintf(buf, sizeof(buf), CALLTOKEN_HASH_FORMAT, ast_inet_ntoa(sin->sin_addr), sin->sin_port, (unsigned int) rec_time, randomcalltokendata);
++		ast_sha1_hash(hash, buf);
++
++		/* compare hashes and then check timestamp delay */
++		if (strcmp(hash, rec_hash)) {
++			ast_log(LOG_WARNING, "Address %s failed CallToken hash inspection\n", ast_inet_ntoa(sin->sin_addr));
++			goto reject; /* received hash does not match ours, reject */
++		} else if ((t < rec_time) || ((t - rec_time) >= MAX_CALLTOKEN_DELAY)) {
++			ast_log(LOG_WARNING, "Too much delay in IAX2 calltoken timestamp from address %s\n", ast_inet_ntoa(sin->sin_addr));
++			goto reject; /* too much delay, reject */
++		}
++
++		/* at this point the call token is valid, returning 0 
++		 * will allow socket_process to continue as usual */
++		requirecalltoken_mark_auto(ies->username, subclass);
++		return 0;
++
++	/* ----- Case 3 ----- */
++	} else { /* calltokens are not supported for this client, how do we respond? */
++		if (calltoken_required(sin, ies->username, subclass)) {
++			ast_log(LOG_ERROR, "Call rejected, CallToken Support required. If unexpected, resolve by placing address %s in the calltokenignore list or setting user %s requirecalltoken=no\n", ast_inet_ntoa(sin->sin_addr), ies->username);
++			goto reject;
++		}
++		return 0; /* calltoken is not required for this addr, so permit it. */
++	}
++
++reject:
++	/* received frame has failed calltoken inspection, send apathetic reject messages */
++	if (subclass == IAX_COMMAND_REGREQ || subclass == IAX_COMMAND_REGREL) {
++		send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++	} else {
++		send_apathetic_reply(1, ntohs(fh->scallno), sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++	}
++
++	return 1;
++}
++
++/*!
+  * \brief Parses an IAX dial string into its component parts.
+  * \param data the string to be parsed
+  * \param pds pointer to a \c struct \c parsed_dial_string to be filled in
+@@ -3540,6 +4467,7 @@
+ 	iaxs[callno]->sockfd = cai.sockfd;
+ 
+ 	/* Transmit the string in a "NEW" request */
++	add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ 	send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+ 
+ 	ast_mutex_unlock(&iaxsl[callno]);
+@@ -6167,6 +7095,12 @@
+ 		return -1;
+ 	}
+ 	remove_by_transfercallno(pvt);
++	/* since a transfer has taken place, the address will change.
++	 * This must be accounted for in the peercnts table.  Remove
++	 * the old address and add the new one */
++	peercnt_remove_by_addr(&pvt->addr);
++	peercnt_add(&pvt->transfer);
++	/* now copy over the new address */
+ 	memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr));
+ 	memset(&pvt->transfer, 0, sizeof(pvt->transfer));
+ 	/* Reset sequence numbers */
+@@ -6373,6 +7307,8 @@
+ 	if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
+ 		realtime_update_peer(peer->name, &peer->addr, 0);
+ 	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
++	/* modify entry in peercnts table as _not_ registered */
++	peercnt_modify(0, 0, &peer->addr);
+ 	/* Reset the address */
+ 	memset(&peer->addr, 0, sizeof(peer->addr));
+ 	/* Reset expiry value */
+@@ -6489,8 +7425,13 @@
+ 	if (inaddrcmp(&p->addr, sin)) {
+ 		if (iax2_regfunk)
+ 			iax2_regfunk(p->name, 1);
++
++		/* modify entry in peercnts table as _not_ registered */
++		peercnt_modify(0, 0, &p->addr);
++
+ 		/* Stash the IP address from which they registered */
+ 		memcpy(&p->addr, sin, sizeof(p->addr));
++
+ 		snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry);
+ 		if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) {
+ 			ast_db_put("IAX/Registry", p->name, data);
+@@ -6512,8 +7453,13 @@
+ 		/* Update the host */
+ 		/* Verify that the host is really there */
+ 		iax2_poke_peer(p, callno);
+-	}		
++	}
+ 
++	/* modify entry in peercnts table as registered */
++	if (p->maxcallno) {
++		peercnt_modify(1, p->maxcallno, &p->addr);
++	}
++
+ 	/* Make sure our call still exists, an INVAL at the right point may make it go away */
+ 	if (!iaxs[callno]) {
+ 		res = -1;
+@@ -6666,6 +7612,7 @@
+ 				res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL);
+ 			if (!res) {
+ 				reg->regstate = REG_STATE_AUTHSENT;
++				add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ 				return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+ 			} else
+ 				return -1;
+@@ -7292,6 +8239,7 @@
+ 	int new = NEW_PREVENT;
+ 	void *ptr;
+ 	int dcallno = 0;
++	char decrypted = 0;
+ 	struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)thread->buf;
+ 	struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)thread->buf;
+ 	struct ast_iax2_meta_hdr *meta = (struct ast_iax2_meta_hdr *)thread->buf;
+@@ -7468,6 +8416,25 @@
+ 
+ 		/* Get the destination call number */
+ 		dcallno = ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS;
++
++
++		/* check to make sure this full frame isn't encrypted before we attempt
++ 		 * to look inside of it. If it is encrypted, decrypt it first. Its ok if the
++		 * callno is not found here, that just means one hasn't been allocated for
++		 * this connection yet. */
++		if ((dcallno != 1) && (fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, NEW_PREVENT, fd, 1))) {
++			ast_mutex_lock(&iaxsl[fr->callno]);
++			if (iaxs[fr->callno] && ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) {
++				if (decrypt_frame(fr->callno, fh, &f, &res)) {
++					ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
++					ast_mutex_unlock(&iaxsl[fr->callno]);
++					return 1;
++				}
++				decrypted = 1;
++			}
++			ast_mutex_unlock(&iaxsl[fr->callno]);
++		}
++
+ 		/* Retrieve the type and subclass */
+ 		f.frametype = fh->type;
+ 		if (f.frametype == AST_FRAME_VIDEO) {
+@@ -7479,17 +8446,51 @@
+ 		/* Deal with POKE/PONG without allocating a callno */
+ 		if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) {
+ 			/* Reply back with a PONG, but don't care about the result. */
+-			send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd);
++			send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
+ 			return 1;
+ 		} else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) {
+ 			/* Ignore */
+ 			return 1;
+ 		}
+ 
+-		if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) ||
+-						       (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) ||
+-						       (f.subclass == IAX_COMMAND_REGREL)))
+-			new = NEW_ALLOW;
++		f.datalen = res - sizeof(*fh);
++		if (f.datalen) {
++			if (f.frametype == AST_FRAME_IAX) {
++				if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
++					ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
++					ast_mutex_unlock(&iaxsl[fr->callno]);
++					return 1;
++				}
++				f.data = NULL;
++				f.datalen = 0;
++			} else {
++				f.data = thread->buf + sizeof(*fh);
++				memset(&ies, 0, sizeof(ies));
++			}
++		} else {
++			if (f.frametype == AST_FRAME_IAX)
++				f.data = NULL;
++			else
++				f.data = empty;
++			memset(&ies, 0, sizeof(ies));
++		}
++
++		if (!dcallno && iax2_allow_new(f.frametype, f.subclass, 1)) {
++			/* only set NEW_ALLOW if calltoken checks out */
++			if (handle_call_token(fh, &ies, &sin, fd)) {
++				return 1;
++			}
++
++			if (ies.calltoken && ies.calltokendata) {
++				/* if we've gotten this far, and the calltoken ie data exists,
++				 * then calltoken validation _MUST_ have taken place.  If calltoken
++				 * data is provided, it is always validated reguardless of any
++				 * calltokenoptional or requirecalltoken options */
++				new = NEW_ALLOW_CALLTOKEN_VALIDATED;
++			} else {
++				new = NEW_ALLOW;
++			}
++		}
+ 	} else {
+ 		/* Don't know anything about it yet */
+ 		f.frametype = AST_FRAME_NULL;
+@@ -7515,7 +8516,14 @@
+ 			check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1;
+ 		}
+ 
+-		fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno);
++		if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno))) {
++			if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_NEW) {
++				send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REJECT, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++			} else if (f.frametype == AST_FRAME_IAX && (f.subclass == IAX_COMMAND_REGREQ || f.subclass == IAX_COMMAND_REGREL)) {
++				send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_REGREJ, ntohl(fh->ts), fh->iseqno + 1, fd, NULL);
++			}
++			return 1;
++		}
+ 	}
+ 
+ 	if (fr->callno > 0)
+@@ -7538,17 +8546,19 @@
+ 			ast_mutex_unlock(&iaxsl[fr->callno]);
+ 		return 1;
+ 	}
+-	if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) {
++	if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED) && !decrypted) {
+ 		if (decrypt_frame(fr->callno, fh, &f, &res)) {
+ 			ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
+ 			ast_mutex_unlock(&iaxsl[fr->callno]);
+ 			return 1;
+-		}
++	}
++		decrypted = 1;
++	}
+ #ifdef DEBUG_SUPPORT
+-		else if (iaxdebug)
+-			iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh));
++	if (decrypted && iaxdebug) {
++		iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh));
++	}
+ #endif
+-	}
+ 
+ 	/* count this frame */
+ 	iaxs[fr->callno]->frames_received++;
+@@ -7642,12 +8652,6 @@
+ 			    (f.frametype != AST_FRAME_IAX))
+ 				iaxs[fr->callno]->iseqno++;
+ 		}
+-		/* A full frame */
+-		if (res < sizeof(*fh)) {
+-			ast_log(LOG_WARNING, "midget packet received (%d of %zd min)\n", res, sizeof(*fh));
+-			ast_mutex_unlock(&iaxsl[fr->callno]);
+-			return 1;
+-		}
+ 		/* Ensure text frames are NULL-terminated */
+ 		if (f.frametype == AST_FRAME_TEXT && thread->buf[res - 1] != '\0') {
+ 			if (res < thread->buf_size)
+@@ -7655,7 +8659,6 @@
+ 			else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */
+ 				thread->buf[res - 1] = '\0';
+ 		}
+-		f.datalen = res - sizeof(*fh);
+ 
+ 		/* Handle implicit ACKing unless this is an INVAL, and only if this is 
+ 		   from the real peer, not the transfer peer */
+@@ -7717,25 +8720,6 @@
+ 			return 1;
+ 		}
+ 
+-		if (f.datalen) {
+-			if (f.frametype == AST_FRAME_IAX) {
+-				if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) {
+-					ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr));
+-					ast_mutex_unlock(&iaxsl[fr->callno]);
+-					return 1;
+-				}
+-				f.data = NULL;
+-				f.datalen = 0;
+-			} else
+-				f.data = thread->buf + sizeof(*fh);
+-		} else {
+-			if (f.frametype == AST_FRAME_IAX)
+-				f.data = NULL;
+-			else
+-				f.data = empty;
+-			memset(&ies, 0, sizeof(ies));
+-		}
+-
+ 		/* when we receive the first full frame for a new incoming channel,
+ 		   it is safe to start the PBX on the channel because we have now
+ 		   completed a 3-way handshake with the peer */
+@@ -8749,6 +9733,26 @@
+ 					return 1;
+ 				}
+ 				break;
++			case IAX_COMMAND_CALLTOKEN:
++			{
++				struct iax_frame *cur;
++				AST_LIST_LOCK(&iaxq.queue);
++				AST_LIST_TRAVERSE(&iaxq.queue, cur, list) {
++					/* find the last sent frame in our frame queue for this callno.
++					 * There are many things to take into account before resending this frame.
++					 * All of these are taken care of in resend_with_token() */
++					if (cur->callno == fr->callno) {
++						break;
++					}
++				}
++				AST_LIST_UNLOCK(&iaxq.queue);
++
++				/* find last sent frame */
++				if (cur && ies.calltoken && ies.calltokendata) {
++					resend_with_token(fr->callno, cur, (char *) ies.calltokendata);
++				}
++				break;
++			}
+ 			default:
+ 				if (option_debug)
+ 					ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno);
+@@ -9055,6 +10059,7 @@
+ 	memset(&ied, 0, sizeof(ied));
+ 	iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username);
+ 	iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh);
++	add_empty_calltoken_ie(iaxs[reg->callno], &ied); /* this _MUST_ be the last ie added */
+ 	send_command(iaxs[reg->callno],AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+ 	reg->regstate = REG_STATE_REGSENT;
+ 	return 0;
+@@ -9278,7 +10283,12 @@
+ 	/* And send the poke */
+ 	ast_mutex_lock(&iaxsl[callno]);
+ 	if (iaxs[callno]) {
+-		send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1);
++		struct iax_ie_data ied = {
++			.buf = { 0 },
++			.pos = 0,
++		};
++		add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
++		send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1);
+ 	}
+ 	ast_mutex_unlock(&iaxsl[callno]);
+ 
+@@ -9702,6 +10712,9 @@
+ 			peer->smoothing = 0;
+ 			peer->pokefreqok = DEFAULT_FREQ_OK;
+ 			peer->pokefreqnotok = DEFAULT_FREQ_NOTOK;
++			peer->maxcallno = 0;
++			peercnt_modify(0, 0, &peer->addr);
++			peer->calltoken_required = CALLTOKEN_DEFAULT;
+ 			ast_string_field_set(peer,context,"");
+ 			ast_string_field_set(peer,peercontext,"");
+ 			ast_clear_flag(peer, IAX_HASCALLERID);
+@@ -9855,7 +10868,24 @@
+ 				ast_string_field_set(peer, zonetag, v->value);
+ 			} else if (!strcasecmp(v->name, "adsi")) {
+ 				peer->adsi = ast_true(v->value);
+-			}/* else if (strcasecmp(v->name,"type")) */
++			} else if (!strcasecmp(v->name, "maxcallnumbers")) {
++				if (sscanf(v->value, "%10hu", &peer->maxcallno) != 1) {
++					ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number. %s is not valid at line %d.\n", v->value, v->lineno);
++				} else {
++					peercnt_modify(1, peer->maxcallno, &peer->addr);
++				}
++			} else if (!strcasecmp(v->name, "requirecalltoken")) {
++				/* default is required unless in optional ip list */
++				if (ast_false(v->value)) {
++					peer->calltoken_required = CALLTOKEN_NO;
++				} else if (!strcasecmp(v->value, "auto")) {
++					peer->calltoken_required = CALLTOKEN_AUTO;
++				} else if (ast_true(v->value)) {
++					peer->calltoken_required = CALLTOKEN_YES;
++				} else {
++					ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
++				}
++			} /* else if (strcasecmp(v->name,"type")) */
+ 			/*	ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ 			v = v->next;
+ 			if (!v) {
+@@ -9937,6 +10967,7 @@
+ 			user->capability = iax2_capability;
+ 			user->encmethods = iax2_encryption;
+ 			user->adsi = adsi;
++			user->calltoken_required = CALLTOKEN_DEFAULT;
+ 			ast_string_field_set(user, name, name);
+ 			ast_string_field_set(user, language, language);
+ 			ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP);	
+@@ -10075,7 +11106,18 @@
+ 					user->maxauthreq = 0;
+ 			} else if (!strcasecmp(v->name, "adsi")) {
+ 				user->adsi = ast_true(v->value);
+-			}/* else if (strcasecmp(v->name,"type")) */
++			} else if (!strcasecmp(v->name, "requirecalltoken")) {
++				/* default is required unless in optional ip list */
++				if (ast_false(v->value)) {
++					user->calltoken_required = CALLTOKEN_NO;
++				} else if (!strcasecmp(v->value, "auto")) {
++					user->calltoken_required = CALLTOKEN_AUTO;
++				} else if (ast_true(v->value)) {
++					user->calltoken_required = CALLTOKEN_YES;
++				} else {
++					ast_log(LOG_WARNING, "requirecalltoken must be set to a valid value. at line %d\n", v->lineno);
++				}
++			} /* else if (strcasecmp(v->name,"type")) */
+ 			/*	ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ 			v = v->next;
+ 			if (!v) {
+@@ -10206,10 +11248,12 @@
+ 	ast_clear_flag((&globalflags), IAX_USEJITTERBUF);	
+ 	ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF);	
+ 	delete_users();
++	ao2_callback(callno_limits, OBJ_NODATA, addr_range_delme_cb, NULL);
++	ao2_callback(calltoken_ignores, OBJ_NODATA, addr_range_delme_cb, NULL);
+ }
+ 
+ /*! \brief Load configuration */
+-static int set_config(char *config_file, int reload)
++static int set_config(const char *config_file, int reload)
+ {
+ 	struct ast_config *cfg, *ucfg;
+ 	int capability=iax2_capability;
+@@ -10251,6 +11295,8 @@
+ 
+ 	min_reg_expire = IAX_DEFAULT_REG_EXPIRE;
+ 	max_reg_expire = IAX_DEFAULT_REG_EXPIRE;
++	global_maxcallno = DEFAULT_MAXCALLNO_LIMIT;
++	global_maxcallno_nonval = DEFAULT_MAXCALLNO_LIMIT_NONVAL;
+ 
+ 	maxauthreq = 3;
+ 
+@@ -10448,7 +11494,19 @@
+ 				maxauthreq = 0;
+ 		} else if (!strcasecmp(v->name, "adsi")) {
+ 			adsi = ast_true(v->value);
+-		} /*else if (strcasecmp(v->name,"type")) */
++		} else if (!strcasecmp(v->name, "maxcallnumbers")) {
++			if (sscanf(v->value, "%10hu", &global_maxcallno) != 1) {
++				ast_log(LOG_WARNING, "maxcallnumbers must be set to a valid number.  %s is not valid at line %d\n", v->value, v->lineno);
++			}
++		} else if (!strcasecmp(v->name, "maxcallnumbers_nonvalidated")) {
++			if (sscanf(v->value, "%10hu", &global_maxcallno_nonval) != 1) {
++				ast_log(LOG_WARNING, "maxcallnumbers_nonvalidated must be set to a valid number.  %s is not valid at line %d.\n", v->value, v->lineno);
++			}
++		} else if(!strcasecmp(v->name, "calltokenoptional")) {
++			if (add_calltoken_ignore(v->value)) {
++				ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
++			}
++		}/*else if (strcasecmp(v->name,"type")) */
+ 		/*	ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
+ 		v = v->next;
+ 	}
+@@ -10539,7 +11597,9 @@
+ 	while(cat) {
+ 		if (strcasecmp(cat, "general")) {
+ 			utype = ast_variable_retrieve(cfg, cat, "type");
+-			if (utype) {
++			if (!strcasecmp(cat, "callnumberlimits")) {
++				build_callno_limits(ast_variable_browse(cfg, cat));
++			} else if (utype) {
+ 				if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
+ 					user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
+ 					if (user) {
+@@ -10581,12 +11641,15 @@
+ }
+ static int reload_config(void)
+ {
+-	char *config = "iax.conf";
++	static const char config[] = "iax.conf";
+ 	struct iax2_registry *reg;
+ 
+ 	if (set_config(config, 1) > 0) {
+ 		prune_peers();
+ 		prune_users();
++		ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
++		ao2_callback(calltoken_ignores, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
++		ao2_callback(peercnts, OBJ_NODATA, set_peercnt_limit_all_cb, NULL);
+ 		AST_LIST_LOCK(&registrations);
+ 		AST_LIST_TRAVERSE(&registrations, reg, entry)
+ 			iax2_do_register(reg);
+@@ -10678,6 +11741,7 @@
+ 	if (pds.key)
+ 		ast_string_field_set(iaxs[callno], outkey, pds.key);
+ 	/* Start the call going */
++	add_empty_calltoken_ie(iaxs[callno], &ied); /* this _MUST_ be the last ie added */
+ 	send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
+ 
+ 	return callno;
+@@ -11112,6 +12176,10 @@
+ "Usage: iax2 show peer <name>\n"
+ "       Display details on specific IAX peer\n";
+ 
++static char show_callnumber_usage[] =
++"Usage: iax2 show callnumber usage <ip optional>\n"
++"       Show current entries in the ip Call Number Limit table.\n";
++
+ static char prune_realtime_usage[] =
+ "Usage: iax2 prune realtime [<peername>|all]\n"
+ "       Prunes object(s) from the cache\n";
+@@ -11274,6 +12342,10 @@
+ 	iax2_show_peer, "Show details on specific IAX peer",
+ 	show_peer_usage, complete_iax2_show_peer },
+ 
++	{ { "iax2", "show", "callnumber", "usage", NULL },
++	iax2_show_callnumber_usage, "Show current entries in ip Call number limit table",
++	show_callnumber_usage, NULL },
++
+ 	{ { "iax2", "set", "debug", NULL },
+ 	iax2_do_debug, "Enable IAX debugging",
+ 	debug_usage },
+@@ -11406,6 +12478,11 @@
+ 	ao2_ref(users, -1);
+ 	ao2_ref(iax_peercallno_pvts, -1);
+ 	ao2_ref(iax_transfercallno_pvts, -1);	
++	ao2_ref(peercnts, -1);
++	ao2_ref(callno_limits, -1);
++	ao2_ref(calltoken_ignores, -1);
++	ao2_ref(callno_pool, -1);
++	ao2_ref(callno_pool_trunk, -1);
+ 
+ 	return 0;
+ }
+@@ -11461,35 +12538,77 @@
+ 	return match(&pvt2->transfer, pvt2->transfercallno, pvt2->callno, pvt,
+ 		pvt2->frames_received) ? CMP_MATCH | CMP_STOP : 0;
+ }
++
++
++static int load_objects(void)
++{
++	peers = users = iax_peercallno_pvts = iax_transfercallno_pvts = NULL;
++	peercnts = callno_limits = calltoken_ignores = callno_pool = callno_pool_trunk = NULL;
++
++	if (!(peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb))) {
++		goto container_fail;
++	} else if (!(users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb))) {
++		goto container_fail;
++	} else if (!(iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb))) {
++		goto container_fail;
++	} else if (!(iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb))) {
++		goto container_fail;
++	} else if (!(peercnts = ao2_container_alloc(MAX_PEER_BUCKETS, peercnt_hash_cb, peercnt_cmp_cb))) {
++		goto container_fail;
++	} else if (!(callno_limits = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
++		goto container_fail;
++	} else if (!(calltoken_ignores = ao2_container_alloc(MAX_PEER_BUCKETS, addr_range_hash_cb, addr_range_cmp_cb))) {
++		goto container_fail;
++	} else if (create_callno_pools()) {
++		goto container_fail;
++	}
++
++	return 0;
++
++container_fail:
++	if (peers) {
++		ao2_ref(peers, -1);
++	}
++	if (users) {
++		ao2_ref(users, -1);
++	}
++	if (iax_peercallno_pvts) {
++		ao2_ref(iax_peercallno_pvts, -1);
++	}
++	if (iax_transfercallno_pvts) {
++		ao2_ref(iax_transfercallno_pvts, -1);
++	}
++	if (peercnts) {
++		ao2_ref(peercnts, -1);
++	}
++	if (callno_limits) {
++		ao2_ref(callno_limits, -1);
++	}
++	if (calltoken_ignores) {
++		ao2_ref(calltoken_ignores, -1);
++	}
++	if (callno_pool) {
++		ao2_ref(callno_pool, -1);
++	}
++	if (callno_pool_trunk) {
++		ao2_ref(callno_pool_trunk, -1);
++	}
++	return AST_MODULE_LOAD_FAILURE;
++}
++
+ /*! \brief Load IAX2 module, load configuraiton ---*/
+ static int load_module(void)
+ {
+-	char *config = "iax.conf";
++	static const char config[] = "iax.conf";
+ 	int res = 0;
+ 	int x;
+ 	struct iax2_registry *reg = NULL;
+ 
+-	peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb);
+-	if (!peers)
++	if (load_objects()) {
+ 		return AST_MODULE_LOAD_FAILURE;
+-	users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb);
+-	if (!users) {
+-		ao2_ref(peers, -1);
+-		return AST_MODULE_LOAD_FAILURE;
+ 	}
+-	iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb);
+-	if (!iax_peercallno_pvts) {
+-		ao2_ref(peers, -1);
+-		ao2_ref(users, -1);
+-		return AST_MODULE_LOAD_FAILURE;
+-	}
+-	iax_transfercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, transfercallno_pvt_hash_cb, transfercallno_pvt_cmp_cb);
+-	if (!iax_transfercallno_pvts) {
+-		ao2_ref(peers, -1);
+-		ao2_ref(users, -1);
+-		ao2_ref(iax_peercallno_pvts, -1);
+-		return AST_MODULE_LOAD_FAILURE;
+-	}
++
++	randomcalltokendata = ast_random();
+ 	ast_custom_function_register(&iaxpeer_function);
+ 
+ 	iax_set_output(iax_debug_output);
+Index: channels/iax2.h
+===================================================================
+--- a/channels/iax2.h	(revision 215999)
++++ b/channels/iax2.h	(working copy)
+@@ -73,7 +73,9 @@
+ #define IAX_COMMAND_FWDOWNL	36	/* Download firmware */
+ #define IAX_COMMAND_FWDATA	37	/* Firmware Data */
+ #define IAX_COMMAND_TXMEDIA 38  /* Transfer media only */
++#define IAX_COMMAND_CALLTOKEN 40  /*! Call number token */
+ 
++
+ #define IAX_DEFAULT_REG_EXPIRE  60	/* By default require re-registration once per minute */
+ 
+ #define IAX_LINGER_TIMEOUT		10 /* How long to wait before closing bridged call */
+@@ -133,6 +135,7 @@
+ #define IAX_IE_RR_DELAY				49		/* Max playout delay for received frames (in ms) u16 */
+ #define IAX_IE_RR_DROPPED			50		/* Dropped frames (presumably by jitterbuf) u32 */
+ #define IAX_IE_RR_OOO				51		/* Frames received Out of Order u32 */
++#define IAX_IE_CALLTOKEN			54		/* Call number security token */
+ 
+ 
+ #define IAX_AUTH_PLAINTEXT			(1 << 0)
+Index: channels/iax2-parser.c
+===================================================================
+--- a/channels/iax2-parser.c	(revision 215999)
++++ b/channels/iax2-parser.c	(working copy)
+@@ -442,7 +442,9 @@
+ 		"PROVISN",
+ 		"FWDWNLD",
+ 		"FWDATA ",
+-		"TXMEDIA"
++		"TXMEDIA",
++		"RTKEY  ",
++		"CTOKEN ",
+ 	};
+ 	if ((copylen > len) || !subclass || (subclass < 0)) {
+ 		str[0] = '\0';
+@@ -959,6 +961,12 @@
+ 				ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2));
+ 			}
+ 			break;
++		case IAX_IE_CALLTOKEN:
++			if (len) {
++				ies->calltokendata = (unsigned char *) data + 2;
++			}
++			ies->calltoken = 1;
++			break;
+ 		default:
+ 			snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
+ 			outputf(tmp);
+Index: include/asterisk/acl.h
+===================================================================
+--- a/include/asterisk/acl.h	(revision 215999)
++++ b/include/asterisk/acl.h	(working copy)
+@@ -35,11 +35,17 @@
+ #define AST_SENSE_ALLOW                 1
+ 
+ /* Host based access control */
++struct ast_ha {
++	/* Host access rule */
++	struct in_addr netaddr;
++	struct in_addr netmask;
++	int sense;
++	struct ast_ha *next;
++};
+ 
+-struct ast_ha;
+-
+ void ast_free_ha(struct ast_ha *ha);
+-struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path);
++void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to);
++struct ast_ha *ast_append_ha(char *sense, const char *stuff, struct ast_ha *path);
+ int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin);
+ int ast_get_ip(struct sockaddr_in *sin, const char *value);
+ int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service);
+Index: include/asterisk/astobj2.h
+===================================================================
+--- a/include/asterisk/astobj2.h	(revision 215999)
++++ b/include/asterisk/astobj2.h	(working copy)
+@@ -290,6 +290,16 @@
+ 	 *  This implies that it can be passed to the object's hash function
+ 	 *  for optimized searching. */
+ 	OBJ_POINTER	 = (1 << 3),
++	/*! 
++	 * \brief Continue if a match is not found in the hashed out bucket
++	 *
++	 * This flag is to be used in combination with OBJ_POINTER.  This tells
++	 * the ao2_callback() core to keep searching through the rest of the
++	 * buckets if a match is not found in the starting bucket defined by
++	 * the hash value on the argument.
++	 */
++	OBJ_CONTINUE = (1 << 4),
++
+ };
+ 
+ /*!
+Index: main/acl.c
+===================================================================
+--- a/main/acl.c	(revision 215999)
++++ b/main/acl.c	(working copy)
+@@ -72,14 +72,6 @@
+ #include "asterisk/lock.h"
+ #include "asterisk/srv.h"
+ 
+-struct ast_ha {
+-	/* Host access rule */
+-	struct in_addr netaddr;
+-	struct in_addr netmask;
+-	int sense;
+-	struct ast_ha *next;
+-};
+-
+ /* Default IP - if not otherwise set, don't breathe garbage */
+ static struct in_addr __ourip = { .s_addr = 0x00000000, };
+ 
+@@ -261,7 +253,7 @@
+ }
+ 
+ /* Copy HA structure */
+-static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
++void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to)
+ {
+ 	memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
+ 	memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
+@@ -303,7 +295,7 @@
+ 	return ret;    			/* Return start of list */
+ }
+ 
+-struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
++struct ast_ha *ast_append_ha(char *sense, const char *stuff, struct ast_ha *path)
+ {
+ 	struct ast_ha *ha;
+ 	char *nm = "255.255.255.255";
+Index: main/astobj2.c
+===================================================================
+--- a/main/astobj2.c	(revision 215999)
++++ b/main/astobj2.c	(working copy)
+@@ -453,7 +453,7 @@
+ 	const enum search_flags flags,
+ 	ao2_callback_fn cb_fn, void *arg)
+ {
+-	int i, last;	/* search boundaries */
++	int i, start, last;	/* search boundaries */
+ 	void *ret = NULL;
+ 
+ 	if (INTERNAL_OBJ(c) == NULL)	/* safety check on the argument */
+@@ -483,14 +483,16 @@
+ 	 * (this only for the time being. We need to optimize this.)
+ 	 */
+ 	if ((flags & OBJ_POINTER))	/* we know hash can handle this case */
+-		i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
++		start = i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
+ 	else			/* don't know, let's scan all buckets */
+ 		i = -1;		/* XXX this must be fixed later. */
+ 
+ 	/* determine the search boundaries: i..last-1 */
+ 	if (i < 0) {
+-		i = 0;
++		start = i = 0;
+ 		last = c->n_buckets;
++	} else if ((flags & OBJ_CONTINUE)) {
++		last = c->n_buckets;
+ 	} else {
+ 		last = i + 1;
+ 	}
+@@ -545,6 +547,17 @@
+ 			}
+ 		}
+ 		AST_LIST_TRAVERSE_SAFE_END
++
++		if (ret) {
++			/* This assumes OBJ_MULTIPLE with !OBJ_NODATA is still not implemented */
++			break;
++		}
++
++		if (i == c->n_buckets - 1 && (flags & OBJ_POINTER) && (flags & OBJ_CONTINUE)) {
++			/* Move to the beginning to ensure we check every bucket */
++			i = -1;
++			last = start;
++		}
+ 	}
+ 	ao2_unlock(c);
+ 	return ret;
+Index: configs/iax.conf.sample
+===================================================================
+--- a/configs/iax.conf.sample	(revision 215999)
++++ b/configs/iax.conf.sample	(working copy)
+@@ -268,6 +268,62 @@
+ 			; has expired based on its registration interval, used the stored
+ 			; address information regardless. (yes|no)
+ 
++;
++; The following two options are used to disable call token validation for the
++; purposes of interoperability with IAX2 endpoints that do not yet support it.
++;
++; Call token validation can be set as optional for a single IP address or IP
++; address range by using the 'calltokenoptional' option. 'calltokenoptional' is
++; only a global option.  
++;
++;calltokenoptional=209.16.236.73/255.255.255.0
++;
++; In a peer/user/friend definition, the 'requirecalltoken' option may be used.
++; By setting 'requirecalltoken=no', call token validation becomes optional for
++; that peer/user.  By setting 'requirecalltoken=auto', call token validation 
++; is optional until a call token supporting peer registers successfully using
++; call token validation.  This is used as an indication that from now on, we
++; can require it from this peer.  So, requirecalltoken is internally set to yes.
++; By default, 'requirecalltoken=yes'.
++;
++;requirecalltoken=no
++;
++
++;
++; These options are used to limit the amount of call numbers allocated to a
++; single IP address.  Before changing any of these values, it is highly encouraged
++; to read the user guide associated with these options first.  In most cases, the
++; default values for these options are sufficient.
++;
++; The 'maxcallnumbers' option limits the amount of call numbers allowed for each
++; individual remote IP address.  Once an IP address reaches it's call number
++; limit, no more new connections are allowed until the previous ones close.  This
++; option can be used in a peer definition as well, but only takes effect for
++; the IP of a dynamic peer after it completes registration.
++;
++;maxcallnumbers=512
++;
++; The 'maxcallnumbers_nonvalidated' is used to set the combined number of call
++; numbers that can be allocated for connections where call token  validation
++; has been disabled.  Unlike the 'maxcallnumbers' option, this limit is not
++; separate for each individual IP address.  Any connection resulting in a
++; non-call token validated call number being allocated contributes to this
++; limit.  For use cases, see the call token user guide.  This option's 
++; default value of 8192 should be sufficient in most cases.
++;
++;maxcallnumbers_nonvalidated=1024
++;
++; The [callnumberlimits] section allows custom call number limits to be set
++; for specific IP addresses and IP address ranges.  These limits take precedence
++; over the global 'maxcallnumbers' option, but may still be overridden by a
++; peer defined 'maxcallnumbers' entry.  Note that these limits take effect
++; for every individual address within the range, not the range as a whole. 
++;
++;[callnumberlimits]
++;10.1.1.0/255.255.255.0 = 24
++;10.1.2.0/255.255.255.0 = 32
++;
++
+ ; Guest sections for unauthenticated connection attempts.  Just specify an
+ ; empty secret, or provide no secret section.
+ ;
+Index: contrib/scripts/iax-friends.sql
+===================================================================
+--- a/contrib/scripts/iax-friends.sql	(revision 215999)
++++ b/contrib/scripts/iax-friends.sql	(working copy)
+@@ -4,51 +4,54 @@
+ 
+ CREATE TABLE `iaxfriends` (
+   `name` varchar(40) NOT NULL default '',
+-  `username` varchar(40) NOT NULL default '',
+-  `secret` varchar(40) NOT NULL default '',
+-  `dbsecret` varchar(40) NOT NULL default '',
+-  `context` varchar(40) NOT NULL default '',
+-  `regcontext` varchar(40) NOT NULL default '',
+-  `host` varchar(40) NOT NULL default 'dynamic',
+-  `ipaddr` varchar(20) NOT NULL default '',
+-  `port` int(6) NOT NULL default '0',
+-  `defaultip` varchar(20) NOT NULL default '',
+-  `sourceaddress` varchar(20) NOT NULL default '',
+-  `mask` varchar(20) NOT NULL default '',
+-  `regexten` varchar(40) NOT NULL default '',
+-  `regseconds` int(11) NOT NULL default '0',
+-  `accountcode` varchar(20) NOT NULL default '', 
+-  `mohinterpret` varchar(20) NOT NULL default '', 
+-  `mohsuggest` varchar(20) NOT NULL default '', 
+-  `inkeys` varchar(40) NOT NULL default '', 
+-  `outkey` varchar(40) NOT NULL default '', 
+-  `language` varchar(10) NOT NULL default '', 
+-  `callerid` varchar(40) NOT NULL default '', 
+-  `cid_number` varchar(40) NOT NULL default '', 
+-  `sendani` varchar(10) NOT NULL default '', 
+-  `fullname` varchar(40) NOT NULL default '', 
+-  `trunk` varchar(10) NOT NULL default '', 
+-  `auth` varchar(20) NOT NULL default '', 
+-  `maxauthreq` varchar(15) NOT NULL default '', 
+-  `encryption` varchar(20) NOT NULL default '', 
+-  `transfer` varchar(10) NOT NULL default '', 
+-  `jitterbuffer` varchar(10) NOT NULL default '', 
+-  `forcejitterbuffer` varchar(10) NOT NULL default '', 
+-  `disallow` varchar(40) NOT NULL default 'all', 
+-  `allow` varchar(40) NOT NULL default '', 
+-  `codecpriority` varchar(40) NOT NULL default '', 
+-  `qualify` varchar(10) NOT NULL default '', 
+-  `qualifysmoothing` varchar(10) NOT NULL default '', 
+-  `qualifyfreqok` varchar(10) NOT NULL default '', 
+-  `qualifyfreqnotok` varchar(10) NOT NULL default '', 
+-  `timezone` varchar(20) NOT NULL default '', 
+-  `adsi` varchar(10) NOT NULL default '', 
+-  `amaflags` varchar(20) NOT NULL default '', 
+-  `setvar` varchar(200) NOT NULL default '', 
++  `type` varchar(10) NOT NULL default 'friend', -- friend/user/peer
++  `username` varchar(40) NULL, -- username to send as peer
++  `mailbox` varchar(40) NULL, -- mailbox at context
++  `secret` varchar(40) NULL,
++  `dbsecret` varchar(40) NULL, -- In AstDB, location to store/retrieve secret
++  `context` varchar(40) NULL,
++  `regcontext` varchar(40) NULL,
++  `host` varchar(40) NULL default 'dynamic',
++  `ipaddr` varchar(20) NULL, -- Must be updateable by Asterisk user
++  `port` int(5) NULL, -- Must be updateable by Asterisk user
++  `defaultip` varchar(20) NULL,
++  `sourceaddress` varchar(20) NULL,
++  `mask` varchar(20) NULL,
++  `regexten` varchar(40) NULL,
++  `regseconds` int(11) NULL, -- Must be updateable by Asterisk user
++  `accountcode` varchar(20) NULL, 
++  `mohinterpret` varchar(20) NULL, 
++  `mohsuggest` varchar(20) NULL, 
++  `inkeys` varchar(40) NULL, 
++  `outkey` varchar(40) NULL, 
++  `language` varchar(10) NULL, 
++  `callerid` varchar(100) NULL, -- The whole callerid string, or broken down in the next 3 fields
++  `cid_number` varchar(40) NULL, -- The number portion of the callerid
++  `sendani` varchar(10) NULL, -- yes/no
++  `fullname` varchar(40) NULL, -- The name portion of the callerid
++  `trunk` varchar(3) NULL, -- Yes/no
++  `auth` varchar(20) NULL, -- RSA/md5/plaintext
++  `maxauthreq` varchar(5) NULL, -- Maximum outstanding AUTHREQ calls {1-32767}
++  `requirecalltoken` varchar(4) NULL, -- yes/no/auto
++  `encryption` varchar(20) NULL, -- aes128/yes/no
++  `transfer` varchar(10) NULL, -- mediaonly/yes/no
++  `jitterbuffer` varchar(3) NULL, -- yes/no
++  `forcejitterbuffer` varchar(3) NULL, -- yes/no
++  `disallow` varchar(40) NULL, -- all/{list-of-codecs}
++  `allow` varchar(40) NULL, -- all/{list-of-codecs}
++  `codecpriority` varchar(40) NULL, 
++  `qualify` varchar(10) NULL, -- yes/no/{number of milliseconds}
++  `qualifysmoothing` varchar(10) NULL, -- yes/no
++  `qualifyfreqok` varchar(10) NULL, -- {number of milliseconds}|60000
++  `qualifyfreqnotok` varchar(10) NULL, -- {number of milliseconds}|10000
++  `timezone` varchar(20) NULL, 
++  `adsi` varchar(10) NULL, -- yes/no
++  `amaflags` varchar(20) NULL, 
++  `setvar` varchar(200) NULL, 
+   PRIMARY KEY  (`name`),
+   INDEX name (name, host),
+   INDEX name2 (name, ipaddr, port),
+   INDEX ipaddr (ipaddr, port),
+-  INDEX host (host, port),
+-) TYPE=MyISAM;
++  INDEX host (host, port)
++);
+ 

Added: asterisk/branches/lenny/debian/patches/AST-2009-008
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/AST-2009-008?rev=7759&op=file
==============================================================================
--- asterisk/branches/lenny/debian/patches/AST-2009-008 (added)
+++ asterisk/branches/lenny/debian/patches/AST-2009-008 Sat Nov  7 06:55:50 2009
@@ -1,0 +1,13 @@
+Index: channels/chan_sip.c
+===================================================================
+--- a/channels/chan_sip.c	(revision 226137)
++++ b/channels/chan_sip.c	(working copy)
+@@ -9346,8 +9346,6 @@
+ 			   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:
+ 		case AUTH_PEER_NOT_DYNAMIC:
+ 		case AUTH_ACL_FAILED:




More information about the Pkg-voip-commits mailing list