[Asterisk-cvs] asterisk/pbx dundi-parser.c, 1.3, 1.4 pbx_dundi.c, 1.11, 1.12

markster at lists.digium.com markster at lists.digium.com
Wed Oct 27 09:55:12 CDT 2004


Update of /usr/cvsroot/asterisk/pbx
In directory mongoose.digium.com:/tmp/cvs-serv8127/pbx

Modified Files:
	dundi-parser.c pbx_dundi.c 
Log Message:
Preliminary "PRECACHE"  / push support...


Index: dundi-parser.c
===================================================================
RCS file: /usr/cvsroot/asterisk/pbx/dundi-parser.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- dundi-parser.c	24 Oct 2004 05:51:57 -0000	1.3
+++ dundi-parser.c	27 Oct 2004 13:58:31 -0000	1.4
@@ -439,8 +439,8 @@
 		"DPRESPONSE  ",
 		"EIDQUERY    ",
 		"EIDRESPONSE ",
-		"UNKNOWN (5)?",
-		"UNKNOWN (6)?",
+		"PRECACHERQ  ",
+		"PRECACHERP  ",
 		"INVALID     ",
 		"UNKNOWN CMD ",
 		"NULL        ",
@@ -448,8 +448,7 @@
 		"REGRESPONSE ",
 		"CANCEL      ",
 		"ENCRYPT     ",
-		"ENCREJ      ",
-		"PRECACHE    " };
+		"ENCREJ      " };
 	char class2[20];
 	char *class;
 	char subclass2[20];

Index: pbx_dundi.c
===================================================================
RCS file: /usr/cvsroot/asterisk/pbx/pbx_dundi.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- pbx_dundi.c	24 Oct 2004 17:53:10 -0000	1.11
+++ pbx_dundi.c	27 Oct 2004 13:58:31 -0000	1.12
@@ -209,6 +209,8 @@
 	struct sockaddr_in addr;	/* Address of DUNDi peer */
 	struct permission *permit;
 	struct permission *include;
+	struct permission *precachesend;
+	struct permission *precachereceive;
 	dundi_eid us_eid;
 	char inkey[80];
 	char outkey[80];
@@ -216,7 +218,6 @@
 	int registerid;
 	int qualifyid;
 	int sentfullkey;
-	int canprecache;
 	int order;
 	unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
 	unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
@@ -234,7 +235,8 @@
 	struct dundi_transaction *regtrans;	/* Registration transaction */
 	struct dundi_transaction *qualtrans;	/* Qualify transaction */
 	struct dundi_transaction *keypending;
-	int model;
+	int model;					/* Pull model */
+	int pcmodel;				/* Push/precache model */
 	int dynamic;				/* Are we dynamic? */
 	int lastms;					/* Last measured latency */
 	int maxms;					/* Max permissible latency */
@@ -296,7 +298,8 @@
 		return -1;
 }
 
-static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, dundi_eid *avoid[], int direct[]);
+static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
+static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
 {
@@ -317,6 +320,7 @@
 		switch(hdr->cmdresp & 0x7f) {
 		case DUNDI_COMMAND_DPDISCOVER:
 		case DUNDI_COMMAND_EIDQUERY:
+		case DUNDI_COMMAND_PRECACHERQ:
 		case DUNDI_COMMAND_REGREQ:
 		case DUNDI_COMMAND_NULL:
 		case DUNDI_COMMAND_ENCRYPT:
@@ -595,7 +599,7 @@
 		
 	if (max) {
 		/* If we do not have a canonical result, keep looking */
-		res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, st->eids, st->directs);
+		res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
 		if (res > 0) {
 			/* Append answer in result */
 			ouranswers += res;
@@ -629,6 +633,38 @@
 	return NULL;	
 }
 
+static void *dundi_precache_thread(void *data)
+{
+	struct dundi_query_state *st = data;
+	struct dundi_ie_data ied;
+	struct dundi_hint_metadata hmd;
+	char eid_str[20];
+
+	ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
+		st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
+	memset(&ied, 0, sizeof(ied));
+
+	/* Now produce precache */
+	dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
+
+	ast_mutex_lock(&peerlock);
+	ast_log(LOG_WARNING, "XXX We should schedule retransmission here XXX\n");
+	/* Truncate if "don't ask" isn't present */
+	if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
+		hmd.exten[0] = '\0';
+	if (st->trans->flags & FLAG_DEAD) {
+		ast_log(LOG_DEBUG, "Our transaction went away!\n");
+		st->trans->thread = 0;
+		destroy_trans(st->trans, 0);
+	} else {
+		dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
+		st->trans->thread = 0;
+	}
+	ast_mutex_unlock(&peerlock);
+	free(st);
+	return NULL;	
+}
+
 static inline int calc_ms(struct timeval *start)
 {
 	struct timeval tv;
@@ -751,19 +787,158 @@
 	return 0;
 }
 
-static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
+static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
+{
+	int unaffected;
+	char key1[256];
+	char key2[256];
+	char eidpeer_str[20];
+	char eidroot_str[20];
+	char data[80]="";
+	time_t timeout;
+
+	if (expiration < 0)
+		expiration = DUNDI_DEFAULT_CACHE_TIME;
+
+	/* Only cache hint if "don't ask" is there... */
+	if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
+		return 0;
+
+	unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
+
+	dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
+	dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+	snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
+	snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
+
+	time(&timeout);
+	timeout += expiration;
+	snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+	
+	ast_db_put("dundi/cache", key1, data);
+	ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
+	ast_db_put("dundi/cache", key2, data);
+	ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
+	return 0;
+}
+
+static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration)
+{
+	int x;
+	char key1[256];
+	char key2[256];
+	char data[1024]="";
+	char eidpeer_str[20];
+	char eidroot_str[20];
+	time_t timeout;
+
+	if (expiration < 1)	
+		expiration = DUNDI_DEFAULT_CACHE_TIME;
+	dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
+	dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+	snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
+	snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
+	/* Build request string */
+	time(&timeout);
+	timeout += expiration;
+	snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+	for (x=start;x<req->respcount;x++) {
+		/* Skip anything with an illegal pipe in it */
+		if (strchr(req->dr[x].dest, '|'))
+			continue;
+		snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
+			req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
+			dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
+	}
+	ast_db_put("dundi/cache", key1, data);
+	ast_db_put("dundi/cache", key2, data);
+	return 0;
+}
+
+static void dundi_precache_full(void)
+{
+	struct dundi_mapping *cur;
+	cur = mappings;
+	while(cur) {
+		ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
+		cur = cur->next;
+	}
+}
+
+static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
 {
 	struct dundi_query_state *st;
 	int totallen;
-	int x;
+	int x,z;
 	struct dundi_ie_data ied;
 	char *s;
+	struct dundi_result dr2[MAX_RESULTS];
+	struct dundi_request dr;
+	struct dundi_hint_metadata hmd;
+
 	struct dundi_mapping *cur;
 	int mapcount;
 	int skipfirst = 0;
 	
 	pthread_t lookupthread;
 	pthread_attr_t attr;
+
+	memset(&dr2, 0, sizeof(dr2));
+	memset(&dr, 0, sizeof(dr));
+	memset(&hmd, 0, sizeof(hmd));
+	
+	/* Forge request structure to hold answers for cache */
+	hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
+	dr.dr = dr2;
+	dr.maxcount = MAX_RESULTS;
+	dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
+	dr.hmd = &hmd;
+	dr.pfds[0] = dr.pfds[1] = -1;
+	trans->parent = &dr;
+	strncpy(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
+	strncpy(dr.number, ies->called_number, sizeof(dr.number) - 1);
+	
+	for (x=0;x<ies->anscount;x++) {
+		if (trans->parent->respcount < trans->parent->maxcount) {
+			/* Make sure it's not already there */
+			for (z=0;z<trans->parent->respcount;z++) {
+				if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
+				    !strcmp(trans->parent->dr[z].dest, ies->answers[x]->data)) 
+						break;
+			}
+			if (z == trans->parent->respcount) {
+				/* Copy into parent responses */
+				trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
+				trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
+				trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
+				trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
+				if (ies->expiration > 0)
+					trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
+				else
+					trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
+				dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
+					sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
+					&ies->answers[x]->eid);
+				strncpy(trans->parent->dr[trans->parent->respcount].dest, ies->answers[x]->data,
+					sizeof(trans->parent->dr[trans->parent->respcount].dest));
+					strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
+					sizeof(trans->parent->dr[trans->parent->respcount].tech));
+				trans->parent->respcount++;
+				trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
+			} else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
+				/* Update weight if appropriate */
+				trans->parent->dr[z].weight = ies->answers[x]->weight;
+			}
+		} else
+			ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
+				trans->parent->number, trans->parent->dcontext);
+
+	}
+	/* Save all the results (if any) we had.  Even if no results, still cache lookup. */
+	cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration);
+	if (ies->hint)
+		cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
+
 	totallen = sizeof(struct dundi_query_state);
 	/* Count matching map entries */
 	mapcount = 0;
@@ -773,6 +948,7 @@
 			mapcount++;
 		cur = cur->next;
 	}
+	
 	/* If no maps, return -1 immediately */
 	if (!mapcount)
 		return -1;
@@ -786,6 +962,7 @@
 			skipfirst = 1;
 	}
 
+	/* Prepare to run a query and then propagate that as necessary */
 	totallen += mapcount * sizeof(struct dundi_mapping);
 	totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
 	st = malloc(totallen);
@@ -820,94 +997,118 @@
 			cur = cur->next;
 		}
 		st->nummaps = mapcount;
-		ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
+		ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
 		pthread_attr_init(&attr);
 		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 		trans->thread = 1;
-		if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
+		if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
 			trans->thread = 0;
 			ast_log(LOG_WARNING, "Unable to create thread!\n");
 			free(st);
 			memset(&ied, 0, sizeof(ied));
 			dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
-			dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+			dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
 			return -1;
 		}
 	} else {
 		ast_log(LOG_WARNING, "Out of memory!\n");
 		memset(&ied, 0, sizeof(ied));
 		dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
-		dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+		dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
 		return -1;
 	}
 	return 0;
 }
 
-static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
+static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
 {
-	int unaffected;
-	char key1[256];
-	char key2[256];
-	char eidpeer_str[20];
-	char eidroot_str[20];
-	char data[80]="";
-	time_t timeout;
-
-	if (expiration < 0)
-		expiration = DUNDI_DEFAULT_CACHE_TIME;
-
-	/* Only cache hint if "don't ask" is there... */
-	if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
-		return 0;
-
-	unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
-
-	dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
-	dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
-	snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
-	snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
-
-	time(&timeout);
-	timeout += expiration;
-	snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+	struct dundi_query_state *st;
+	int totallen;
+	int x;
+	struct dundi_ie_data ied;
+	char *s;
+	struct dundi_mapping *cur;
+	int mapcount;
+	int skipfirst = 0;
 	
-	ast_db_put("dundi/cache", key1, data);
-	ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
-	ast_db_put("dundi/cache", key2, data);
-	ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
-	return 0;
-}
+	pthread_t lookupthread;
+	pthread_attr_t attr;
+	totallen = sizeof(struct dundi_query_state);
+	/* Count matching map entries */
+	mapcount = 0;
+	cur = mappings;
+	while(cur) {
+		if (!strcasecmp(cur->dcontext, ccontext))
+			mapcount++;
+		cur = cur->next;
+	}
+	/* If no maps, return -1 immediately */
+	if (!mapcount)
+		return -1;
 
-static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration)
-{
-	int x;
-	char key1[256];
-	char key2[256];
-	char data[1024]="";
-	char eidpeer_str[20];
-	char eidroot_str[20];
-	time_t timeout;
+	if (ies->eidcount > 1) {
+		/* Since it is a requirement that the first EID is the authenticating host
+		   and the last EID is the root, it is permissible that the first and last EID
+		   could be the same.  In that case, we should go ahead copy only the "root" section
+		   since we will not need it for authentication. */
+		if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
+			skipfirst = 1;
+	}
 
-	if (expiration < 1)	
-		expiration = DUNDI_DEFAULT_CACHE_TIME;
-	dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
-	dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
-	snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
-	snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
-	/* Build request string */
-	time(&timeout);
-	timeout += expiration;
-	snprintf(data, sizeof(data), "%ld|", (long)(timeout));
-	for (x=start;x<req->respcount;x++) {
-		/* Skip anything with an illegal pipe in it */
-		if (strchr(req->dr[x].dest, '|'))
-			continue;
-		snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
-			req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
-			dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
+	totallen += mapcount * sizeof(struct dundi_mapping);
+	totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
+	st = malloc(totallen);
+	if (st) {
+		memset(st, 0, totallen);
+		strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
+		strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
+		st->trans = trans;
+		st->ttl = ies->ttl - 1;
+		st->nocache = ies->cbypass;
+		if (st->ttl < 0)
+			st->ttl = 0;
+		s = st->fluffy;
+		for (x=skipfirst;ies->eids[x];x++) {
+			st->eids[x-skipfirst] = (dundi_eid *)s;
+			*st->eids[x-skipfirst] = *ies->eids[x];
+			st->directs[x-skipfirst] = ies->eid_direct[x];
+			s += sizeof(dundi_eid);
+		}
+		/* Append mappings */
+		x = 0;
+		st->maps = (struct dundi_mapping *)s;
+		cur = mappings;
+		while(cur) {
+			if (!strcasecmp(cur->dcontext, ccontext)) {
+				if (x < mapcount) {
+					st->maps[x] = *cur;
+					st->maps[x].next = NULL;
+					x++;
+				}
+			}
+			cur = cur->next;
+		}
+		st->nummaps = mapcount;
+		ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
+		pthread_attr_init(&attr);
+		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+		trans->thread = 1;
+		if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
+			trans->thread = 0;
+			ast_log(LOG_WARNING, "Unable to create thread!\n");
+			free(st);
+			memset(&ied, 0, sizeof(ied));
+			dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
+			dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+			return -1;
+		}
+	} else {
+		ast_log(LOG_WARNING, "Out of memory!\n");
+		memset(&ied, 0, sizeof(ied));
+		dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
+		dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+		return -1;
 	}
-	ast_db_put("dundi/cache", key1, data);
-	ast_db_put("dundi/cache", key2, data);
 	return 0;
 }
 
@@ -957,6 +1158,8 @@
 							req->dr[req->respcount].techint = tech;
 							req->dr[req->respcount].expiration = expiration;
 							dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
+							dundi_eid_to_str(req->dr[req->respcount].eid_str, 
+								sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
 							strncpy(req->dr[req->respcount].dest, ptr,
 								sizeof(req->dr[req->respcount].dest));
 							strncpy(req->dr[req->respcount].tech, tech2str(tech),
@@ -1317,8 +1520,11 @@
 	switch(cmd) {
 	case DUNDI_COMMAND_DPDISCOVER:
 	case DUNDI_COMMAND_EIDQUERY:
+	case DUNDI_COMMAND_PRECACHERQ:
 		if (cmd == DUNDI_COMMAND_EIDQUERY)
 			resp = DUNDI_COMMAND_EIDRESPONSE;
+		else if (cmd == DUNDI_COMMAND_PRECACHERQ)
+			resp = DUNDI_COMMAND_PRECACHERP;
 		else
 			resp = DUNDI_COMMAND_DPRESPONSE;
 		/* A dialplan or entity discover -- qualify by highest level entity */
@@ -1344,14 +1550,24 @@
 						/* They're not permitted to access that context */
 						dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
 						dundi_send(trans, resp, 0, 1, &ied);
-					} else if (has_permission(peer->permit, ies.called_context)) {
+					} else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 
+					           (peer->model & DUNDI_MODEL_INBOUND) && 
+							   has_permission(peer->permit, ies.called_context)) {
 						res = dundi_answer_query(trans, &ies, ies.called_context);
 						if (res < 0) {
 							/* There is no such dundi context */
 							dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
 							dundi_send(trans, resp, 0, 1, &ied);
 						}
-				
+					} else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 
+					           (peer->pcmodel & DUNDI_MODEL_INBOUND) && 
+							   has_permission(peer->include, ies.called_context)) {
+						res = dundi_prop_precache(trans, &ies, ies.called_context);
+						if (res < 0) {
+							/* There is no such dundi context */
+							dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
+							dundi_send(trans, resp, 0, 1, &ied);
+						}
 					} else {
 						/* They're not permitted to access that context */
 						dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
@@ -1403,79 +1619,6 @@
 			}
 		}
 		break;
-	case DUNDI_COMMAND_PRECACHE:
-		/* Success of some sort */
-		ast_log(LOG_DEBUG, "Looks like a precache with %d answers\n", ies.anscount);
-		/* A dialplan or entity discover -- qualify by highest level entity */
-		peer = find_peer(ies.eids[0]);
-		if (peer && ies.called_number) {
-			struct dundi_request dr;
-			struct dundi_result dr2[1];
-			memset(&dr, 0, sizeof(dr));
-			memset(&dr2, 0, sizeof(dr2));
-			/* Build placeholder Dundi Request */
-			trans->us_eid = peer->us_eid;
-			if (!ies.called_context)
-				ies.called_context = "e164";
-			strncpy(dr.dcontext, ies.called_context, sizeof(dr.dcontext) - 1);
-			strncpy(dr.number, ies.called_number, sizeof(dr.number) - 1);
-			dr.dr = dr2;
-			trans->parent = &dr;
-			
-			/* Make sure we have all the proper auths */
-			if (strlen(peer->inkey)) {
-				authpass = encrypted;
-			} else 
-				authpass = 1;
-			authpass &= has_permission(peer->include, ies.called_context);
-			authpass &= peer->canprecache;
-			if (authpass) {
-				/* Okay we're authentiated and all, now we check if they're authorized */
-				for (x=0;x<ies.anscount;x++) {
-					/* Copy into parent responses */
-					trans->parent->dr[0].flags = ntohs(ies.answers[x]->flags);
-					trans->parent->dr[0].techint = ies.answers[x]->protocol;
-					trans->parent->dr[0].weight = ntohs(ies.answers[x]->weight);
-					trans->parent->dr[0].eid = ies.answers[x]->eid;
-					if (ies.expiration > 0)
-						trans->parent->dr[0].expiration = ies.expiration;
-					else
-						trans->parent->dr[0].expiration = DUNDI_DEFAULT_CACHE_TIME;
-					dundi_eid_to_str(trans->parent->dr[0].eid_str, 
-						sizeof(trans->parent->dr[0].eid_str),
-						&ies.answers[x]->eid);
-					strncpy(trans->parent->dr[0].dest, ies.answers[x]->data,
-						sizeof(trans->parent->dr[0].dest));
-					strncpy(trans->parent->dr[0].tech, tech2str(ies.answers[x]->protocol),
-						sizeof(trans->parent->dr[0].tech));
-					trans->parent->respcount=1;
-					/* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
-					   the cache know if this request was unaffected by our entity list. */
-					cache_save(&trans->them_eid, trans->parent, 0, 
-						ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration);
-				}
-				if (ies.hint) {
-					cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
-					if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
-						trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
-					if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) { 
-						if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
-							strncpy(trans->parent->hmd->exten, ies.hint->data, 
-								sizeof(trans->parent->hmd->exten) - 1);
-						}
-					} else {
-						trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
-					}
-				}
-			}
-		}
-		if (!authpass && ies.eids[0])
-			ast_log(LOG_NOTICE, "Peer '%s' does not have permission to pre-cache!\n", 
-				dundi_eid_to_str(eid_str, sizeof(eid_str), ies.eids[0]));
-		/* Close connection if not final */
-		if (!final) 
-			dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
-		break;
 	case DUNDI_COMMAND_DPRESPONSE:
 		/* A dialplan response, lets see what we got... */
 		if (ies.cause < 1) {
@@ -1646,6 +1789,7 @@
 		break;
 	case DUNDI_COMMAND_INVALID:
 	case DUNDI_COMMAND_NULL:
+	case DUNDI_COMMAND_PRECACHERP:
 		/* Do nothing special */
 		if (!final) 
 			dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
@@ -2118,12 +2262,38 @@
 	else
 		sort_results(dr, res);
 	for (x=0;x<res;x++) {
-		ast_cli(fd, "%d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
+		ast_cli(fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
+		ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
 	}
 	ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
 	return RESULT_SUCCESS;
 }
 
+static int dundi_do_precache(int fd, int argc, char *argv[])
+{
+	int res;
+	char tmp[256] = "";
+	char *context;
+	struct timeval start;
+	if ((argc < 3) || (argc > 3))
+		return RESULT_SHOWUSAGE;
+	strncpy(tmp, argv[2], sizeof(tmp) - 1);
+	context = strchr(tmp, '@');
+	if (context) {
+		*context = '\0';
+		context++;
+	}
+	gettimeofday(&start, NULL);
+	res = dundi_precache(context, tmp);
+	
+	if (res < 0) 
+		ast_cli(fd, "DUNDi precache returned error.\n");
+	else if (!res) 
+		ast_cli(fd, "DUNDi precache returned no error.\n");
+	ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
+	return RESULT_SUCCESS;
+}
+
 static int dundi_do_query(int fd, int argc, char *argv[])
 {
 	int res;
@@ -2429,6 +2599,12 @@
 "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
 "keyword is specified.\n";
 
+static char precache_usage[] =
+"Usage: dundi precache <number>[@context]\n"
+"       Lookup the given number within the given DUNDi context\n"
+"(or e164 if none is specified) and precaches the results to any\n"
+"upstream DUNDi push servers.\n";
+
 static char query_usage[] =
 "Usage: dundi query <entity>[@context]\n"
 "       Attempts to retrieve contact information for a specific\n"
@@ -2477,6 +2653,9 @@
 static struct ast_cli_entry  cli_lookup =
 	{ { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
 
+static struct ast_cli_entry  cli_precache =
+	{ { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
+
 static struct ast_cli_entry  cli_queryeid =
 	{ { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
 
@@ -2752,6 +2931,8 @@
 			case DUNDI_COMMAND_DPRESPONSE:
 			case DUNDI_COMMAND_EIDQUERY:
 			case DUNDI_COMMAND_EIDRESPONSE:
+			case DUNDI_COMMAND_PRECACHERQ:
+			case DUNDI_COMMAND_PRECACHERP:
 				if (dundidebug)
 					dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
 				res = dundi_encrypt(trans, pack);
@@ -2832,6 +3013,63 @@
 	return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
 }
 
+static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount)
+{
+	struct dundi_ie_data ied;
+	int x, res;
+	int max = 999999;
+	int expiration = DUNDI_DEFAULT_CACHE_TIME;
+	int ouranswers=0;
+	dundi_eid *avoid[1] = { NULL, };
+	int direct[1] = { 0, };
+	struct dundi_result dr[MAX_RESULTS];
+	struct dundi_hint_metadata hmd;
+	if (!trans->parent) {
+		ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
+		return -1;
+	}
+	memset(&hmd, 0, sizeof(hmd));
+	memset(&dr, 0, sizeof(dr));
+	/* Look up the answers we're going to include */
+	for (x=0;x<mapcount;x++)
+		ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
+	if (ouranswers < 0)
+		ouranswers = 0;
+	for (x=0;x<ouranswers;x++) {
+		if (dr[x].weight < max)
+			max = dr[x].weight;
+	}
+	if (max) {
+		/* If we do not have a canonical result, keep looking */
+		res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
+		if (res > 0) {
+			/* Append answer in result */
+			ouranswers += res;
+		}
+	}
+	
+	memset(&ied, 0, sizeof(ied));
+	dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+	if (!dundi_eid_zero(&trans->us_eid))
+		dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+	for (x=0;x<trans->eidcount;x++)
+		dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
+	dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
+	dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
+	dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+	for (x=0;x<ouranswers;x++) {
+		/* Add answers */
+		if (dr[x].expiration && (expiration > dr[x].expiration))
+			expiration = dr[x].expiration;
+		dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
+	}
+	dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
+	dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
+	if (trans->autokilltimeout)
+		trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
+	return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
+}
+
 static int dundi_query(struct dundi_transaction *trans)
 {
 	struct dundi_ie_data ied;
@@ -2857,13 +3095,22 @@
 static int discover_transactions(struct dundi_request *dr)
 {
 	struct dundi_transaction *trans;
-	ast_mutex_lock(&peerlock);
 	trans = dr->trans;
 	while(trans) {
 		dundi_discover(trans);
 		trans = trans->next;
 	}
-	ast_mutex_unlock(&peerlock);
+	return 0;
+}
+
+static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount)
+{
+	struct dundi_transaction *trans;
+	trans = dr->trans;
+	while(trans) {
+		precache_trans(trans, maps, mapcount);
+		trans = trans->next;
+	}
 	return 0;
 }
 
@@ -2993,21 +3240,36 @@
 	ast_mutex_unlock(&peerlock);
 }
 
-static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, dundi_eid *avoid[], int directs[])
+static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
 {
 	struct dundi_peer *p;
 	int x;
 	int res;
+	int pass;
+	int allowconnect;
 	char eid_str[20];
 	ast_mutex_lock(&peerlock);
 	p = peers;
 	while(p) {
-		if (has_permission(p->include, dr->dcontext)) {
+		if (modeselect == 1) {
+			/* Send the precache to push upstreams only! */
+			pass = has_permission(p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
+			allowconnect = 1;
+		} else {
+			/* Normal lookup / EID query */
+			pass = has_permission(p->include, dr->dcontext);
+			allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
+		}
+		if (skip) {
+			if (!dundi_eid_cmp(skip, &p->eid))
+				pass = 0;
+		}
+		if (pass) {
 			if (p->order <= order) {
 				/* Check order first, then check cache, regardless of
 				   omissions, this gets us more likely to not have an
 				   affected answer. */
-				if(nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration))) {
+				if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
 					res = 0;
 					/* Make sure we haven't already seen it and that it won't
 					   affect our answer */
@@ -3020,11 +3282,13 @@
 						}
 					}
 					/* Make sure we can ask */
-					if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
-						/* Check for a matching or 0 cache entry */
-						append_transaction(dr, p, ttl, avoid);
-					} else
-						ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
+					if (allowconnect) {
+						if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
+							/* Check for a matching or 0 cache entry */
+							append_transaction(dr, p, ttl, avoid);
+						} else
+							ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
+					}
 				}
 				*foundcache |= res;
 			} else if (!*skipped || (p->order < *skipped))
@@ -3121,7 +3385,7 @@
 	return acrc32;
 }
 
-static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *hmd, int *expiration, int cbypass, dundi_eid *avoid[], int direct[])
+static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
 {
 	int res;
 	struct dundi_request dr, *pending;
@@ -3187,7 +3451,7 @@
 		order = skipped;
 		skipped = 0;
 		foundcache = 0;
-		build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, avoid, direct);
+		build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
 	} while (skipped && !foundcache && !dr.trans);
 	/* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
 	   do this earlier because we didn't know if we were going to have transactions
@@ -3230,7 +3494,80 @@
 	int expiration = DUNDI_DEFAULT_CACHE_TIME;
 	memset(&hmd, 0, sizeof(hmd));
 	hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
-	return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, avoid, direct);
+	return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
+}
+
+static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
+{
+	struct dundi_request dr;
+	struct dundi_hint_metadata hmd;
+	struct dundi_result dr2[MAX_RESULTS];
+	struct timeval start;
+	struct dundi_mapping *maps=NULL, *cur;
+	int nummaps;
+	int foundcache, skipped, ttlms, ms;
+	if (!context)
+		context = "e164";
+	ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
+
+	ast_mutex_lock(&peerlock);
+	nummaps = 0;
+	cur = mappings;
+	while(cur) {
+		if (!strcasecmp(cur->dcontext, context))
+			nummaps++;
+		cur = cur->next;
+	}
+	if (nummaps) {
+		maps = alloca(nummaps * sizeof(struct dundi_mapping));
+		nummaps = 0;
+		if (maps) {
+			cur = mappings;
+			while(cur) {
+				if (!strcasecmp(cur->dcontext, context))
+					maps[nummaps++] = *cur;
+				cur = cur->next;
+			}
+		}
+	}
+	ast_mutex_unlock(&peerlock);
+	if (!nummaps || !maps)
+		return -1;
+	ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
+	memset(&dr2, 0, sizeof(dr2));
+	memset(&dr, 0, sizeof(dr));
+	memset(&hmd, 0, sizeof(hmd));
+	dr.dr = dr2;
+	strncpy(dr.number, number, sizeof(dr.number) - 1);
+	strncpy(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext) - 1);
+	dr.maxcount = MAX_RESULTS;
+	dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
+	dr.hmd = &hmd;
+	pipe(dr.pfds);
+	dr.pfds[0] = dr.pfds[1] = -1;
+	build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
+	optimize_transactions(&dr, 0);
+	precache_transactions(&dr, maps, nummaps);
+	gettimeofday(&start, NULL);
+	while(dr.trans && (calc_ms(&start) < ttlms)) {
+		if (dr.pfds[0] > -1) {
+			ms = 100;
+			ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
+		} else
+			usleep(1);
+	}
+	cancel_request(&dr);
+	if (dr.pfds[0] > -1) {
+		close(dr.pfds[0]);
+		close(dr.pfds[1]);
+	}
+	return 0;
+}
+
+int dundi_precache(const char *context, const char *number)
+{
+	dundi_eid *avoid[1] = { NULL, };
+	return dundi_precache_internal(context, number, dundi_ttl, avoid);
 }
 
 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[])
@@ -3257,7 +3594,7 @@
 	if (rooteid)
 		dr.root_eid = *rooteid;
 	/* Create transactions */
-	build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, avoid, NULL);
+	build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
 
 	/* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
 	   do this earlier because we didn't know if we were going to have transactions
@@ -3653,8 +3990,6 @@
 				strncpy(peer->inkey, v->value, sizeof(peer->inkey) - 1);
 			} else if (!strcasecmp(v->name, "outkey")) {
 				strncpy(peer->outkey, v->value, sizeof(peer->outkey) - 1);
-			} else if (!strcasecmp(v->name, "canprecache")) {
-				peer->canprecache = ast_true(v->value);
 			} else if (!strcasecmp(v->name, "host")) {
 				if (!strcasecmp(v->value, "dynamic")) {
 					peer->dynamic = 1;
@@ -3712,15 +4047,38 @@
 					peer->model = DUNDI_MODEL_OUTBOUND;
 				else if (!strcasecmp(v->value, "symmetric"))
 					peer->model = DUNDI_MODEL_SYMMETRIC;
+				else if (!strcasecmp(v->value, "none"))
+					peer->model = 0;
 				else {
-					ast_log(LOG_WARNING, "Unknown model '%s', should be 'outbound', 'inbound', or 'symmetric' at line %d\n", 
+					ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", 
+						v->value, v->lineno);
+				}
+			} else if (!strcasecmp(v->name, "precache")) {
+				if (!strcasecmp(v->value, "inbound"))
+					peer->pcmodel = DUNDI_MODEL_INBOUND;
+				else if (!strcasecmp(v->value, "outbound")) 
+					peer->pcmodel = DUNDI_MODEL_OUTBOUND;
+				else if (!strcasecmp(v->value, "symmetric"))
+					peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
+				else if (!strcasecmp(v->value, "none"))
+					peer->pcmodel = 0;
+				else {
+					ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", 
 						v->value, v->lineno);
 				}
 			}
 			v = v->next;
 		}
-		if (!peer->model) {
-			ast_log(LOG_WARNING, "Peer '%s' lacks a model, discarding!\n", 
+		if (!peer->model && !peer->pcmodel) {
+			ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n", 
+				dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+			peer->dead = 1;
+		} else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
+			ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n", 
+				dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+			peer->dead = 1;
+		} else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
+			ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n", 
 				dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
 			peer->dead = 1;
 		} else if (peer->include && !(peer->model & DUNDI_MODEL_OUTBOUND)) {
@@ -4014,6 +4372,7 @@
 	ast_cli_unregister(&cli_show_mappings);
 	ast_cli_unregister(&cli_show_peer);
 	ast_cli_unregister(&cli_lookup);
+	ast_cli_unregister(&cli_precache);
 	ast_cli_unregister(&cli_queryeid);
 	ast_unregister_switch(&dundi_switch);
 	res = ast_unregister_application(app);
@@ -4064,6 +4423,7 @@
 	ast_cli_register(&cli_show_mappings);
 	ast_cli_register(&cli_show_peer);
 	ast_cli_register(&cli_lookup);
+	ast_cli_register(&cli_precache);
 	ast_cli_register(&cli_queryeid);
 	if (ast_register_switch(&dundi_switch))
 		ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");




More information about the svn-commits mailing list