From 4c0c0db318146d10c11e84d9d81c2941fd8a95e6 Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Wed, 27 Oct 2004 13:58:31 +0000 Subject: Preliminary "PRECACHE" / push support... git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4110 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- pbx/pbx_dundi.c | 690 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 525 insertions(+), 165 deletions(-) (limited to 'pbx/pbx_dundi.c') diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c index 625bccc46..ed70de5d7 100755 --- a/pbx/pbx_dundi.c +++ b/pbx/pbx_dundi.c @@ -209,6 +209,8 @@ static struct dundi_peer { 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 @@ static struct dundi_peer { 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 @@ static struct dundi_peer { 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 @@ static int str2tech(char *str) 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 @@ static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct 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 @@ static void *dundi_lookup_thread(void *data) 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 @@ static void *dundi_lookup_thread(void *data) 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 @@ static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies 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;xrespcount;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;xanscount;x++) { + if (trans->parent->respcount < trans->parent->maxcount) { + /* Make sure it's not already there */ + for (z=0;zparent->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 @@ static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies mapcount++; cur = cur->next; } + /* If no maps, return -1 immediately */ if (!mapcount) return -1; @@ -786,6 +962,7 @@ static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies 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 @@ static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies 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;xrespcount;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 @@ static int cache_lookup_internal(time_t now, struct dundi_request *req, char *ke 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 @@ static int handle_command_response(struct dundi_transaction *trans, struct dundi 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 @@ static int handle_command_response(struct dundi_transaction *trans, struct dundi /* 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 @@ static int handle_command_response(struct dundi_transaction *trans, struct dundi } } 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;xparent->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 @@ static int handle_command_response(struct dundi_transaction *trans, struct dundi 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,8 +2262,34 @@ static int dundi_do_lookup(int fd, int argc, char *argv[]) else sort_results(dr, res); for (x=0;x 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; } @@ -2429,6 +2599,12 @@ static char lookup_usage[] = "(or e164 if none is specified). Bypasses cache if 'bypass'\n" "keyword is specified.\n"; +static char precache_usage[] = +"Usage: dundi precache [@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 [@context]\n" " Attempts to retrieve contact information for a specific\n" @@ -2477,6 +2653,9 @@ static struct ast_cli_entry cli_show_peer = 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 @@ static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, i 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 @@ static int dundi_discover(struct dundi_transaction *trans) 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;xparent->number, &trans->us_eid, ouranswers, &hmd); + if (ouranswers < 0) + ouranswers = 0; + for (x=0;xparent->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;xeidcount;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 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 dundi_query(struct dundi_transaction *trans) 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 @@ static void abort_request(struct dundi_request *dr) 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 @@ static void build_transactions(struct dundi_request *dr, int ttl, int order, int } } /* 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 @@ static unsigned long avoid_crc32(dundi_eid *avoid[]) 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 @@ static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct 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 dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *ch 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 @@ static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *d 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 @@ static void build_peer(dundi_eid *eid, struct ast_variable *v) 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 @@ static void build_peer(dundi_eid *eid, struct ast_variable *v) 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 @@ int unload_module(void) 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 @@ int load_module(void) 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"); -- cgit v1.2.3