[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