00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 376657 $")
00033
00034 #include "asterisk/network.h"
00035 #include <sys/ioctl.h>
00036 #include <zlib.h>
00037 #include <sys/signal.h>
00038 #include <pthread.h>
00039 #include <net/if.h>
00040
00041 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
00042 #include <net/if_dl.h>
00043 #include <ifaddrs.h>
00044 #include <signal.h>
00045 #endif
00046
00047 #include "asterisk/file.h"
00048 #include "asterisk/logger.h"
00049 #include "asterisk/channel.h"
00050 #include "asterisk/config.h"
00051 #include "asterisk/pbx.h"
00052 #include "asterisk/module.h"
00053 #include "asterisk/frame.h"
00054 #include "asterisk/cli.h"
00055 #include "asterisk/lock.h"
00056 #include "asterisk/md5.h"
00057 #include "asterisk/dundi.h"
00058 #include "asterisk/sched.h"
00059 #include "asterisk/io.h"
00060 #include "asterisk/utils.h"
00061 #include "asterisk/netsock.h"
00062 #include "asterisk/crypto.h"
00063 #include "asterisk/astdb.h"
00064 #include "asterisk/acl.h"
00065 #include "asterisk/app.h"
00066
00067 #include "dundi-parser.h"
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145 #define MAX_RESULTS 64
00146
00147 #define MAX_PACKET_SIZE 8192
00148
00149 #define MAX_WEIGHT 59999
00150
00151 #define DUNDI_MODEL_INBOUND (1 << 0)
00152 #define DUNDI_MODEL_OUTBOUND (1 << 1)
00153 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
00154
00155
00156 #define DUNDI_TIMING_HISTORY 10
00157
00158 enum {
00159 FLAG_ISREG = (1 << 0),
00160 FLAG_DEAD = (1 << 1),
00161 FLAG_FINAL = (1 << 2),
00162 FLAG_ISQUAL = (1 << 3),
00163 FLAG_ENCRYPT = (1 << 4),
00164 FLAG_SENDFULLKEY = (1 << 5),
00165 FLAG_STOREHIST = (1 << 6),
00166 };
00167
00168 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
00169
00170 #if 0
00171 #define DUNDI_SECRET_TIME 15
00172 #else
00173 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
00174 #endif
00175
00176 static struct io_context *io;
00177 static struct sched_context *sched;
00178 static int netsocket = -1;
00179 static pthread_t netthreadid = AST_PTHREADT_NULL;
00180 static pthread_t precachethreadid = AST_PTHREADT_NULL;
00181 static pthread_t clearcachethreadid = AST_PTHREADT_NULL;
00182 static unsigned int tos = 0;
00183 static int dundidebug = 0;
00184 static int authdebug = 0;
00185 static int dundi_ttl = DUNDI_DEFAULT_TTL;
00186 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
00187 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
00188 static int global_autokilltimeout = 0;
00189 static dundi_eid global_eid;
00190 static int default_expiration = 60;
00191 static int global_storehistory = 0;
00192 static char dept[80];
00193 static char org[80];
00194 static char locality[80];
00195 static char stateprov[80];
00196 static char country[80];
00197 static char email[80];
00198 static char phone[80];
00199 static char secretpath[80];
00200 static char cursecret[80];
00201 static char ipaddr[80];
00202 static time_t rotatetime;
00203 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
00204 static int dundi_shutdown = 0;
00205
00206 struct permission {
00207 AST_LIST_ENTRY(permission) list;
00208 int allow;
00209 char name[0];
00210 };
00211
00212 struct dundi_packet {
00213 AST_LIST_ENTRY(dundi_packet) list;
00214 struct dundi_hdr *h;
00215 int datalen;
00216 struct dundi_transaction *parent;
00217 int retransid;
00218 int retrans;
00219 unsigned char data[0];
00220 };
00221
00222 struct dundi_hint_metadata {
00223 unsigned short flags;
00224 char exten[AST_MAX_EXTENSION];
00225 };
00226
00227 struct dundi_precache_queue {
00228 AST_LIST_ENTRY(dundi_precache_queue) list;
00229 char *context;
00230 time_t expiration;
00231 char number[0];
00232 };
00233
00234 struct dundi_request;
00235
00236 struct dundi_transaction {
00237 struct sockaddr_in addr;
00238 struct timeval start;
00239 dundi_eid eids[DUNDI_MAX_STACK + 1];
00240 int eidcount;
00241 dundi_eid us_eid;
00242 dundi_eid them_eid;
00243 ast_aes_encrypt_key ecx;
00244 ast_aes_decrypt_key dcx;
00245 unsigned int flags;
00246 int ttl;
00247 int thread;
00248 int retranstimer;
00249 int autokillid;
00250 int autokilltimeout;
00251 unsigned short strans;
00252 unsigned short dtrans;
00253 unsigned char iseqno;
00254 unsigned char oiseqno;
00255 unsigned char oseqno;
00256 unsigned char aseqno;
00257 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;
00258 struct packetlist lasttrans;
00259 struct dundi_request *parent;
00260 AST_LIST_ENTRY(dundi_transaction) parentlist;
00261 AST_LIST_ENTRY(dundi_transaction) all;
00262 };
00263
00264 struct dundi_request {
00265 char dcontext[AST_MAX_EXTENSION];
00266 char number[AST_MAX_EXTENSION];
00267 dundi_eid query_eid;
00268 dundi_eid root_eid;
00269 struct dundi_result *dr;
00270 struct dundi_entity_info *dei;
00271 struct dundi_hint_metadata *hmd;
00272 int maxcount;
00273 int respcount;
00274 int expiration;
00275 int cbypass;
00276 int pfds[2];
00277 uint32_t crc32;
00278 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;
00279 AST_LIST_ENTRY(dundi_request) list;
00280 };
00281
00282 struct dundi_mapping {
00283 char dcontext[AST_MAX_EXTENSION];
00284 char lcontext[AST_MAX_EXTENSION];
00285 int _weight;
00286 char *weightstr;
00287 int options;
00288 int tech;
00289 int dead;
00290 char dest[512];
00291 AST_LIST_ENTRY(dundi_mapping) list;
00292 };
00293
00294 struct dundi_peer {
00295 dundi_eid eid;
00296 struct sockaddr_in addr;
00297 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
00298 struct permissionlist include;
00299 dundi_eid us_eid;
00300 char inkey[80];
00301 char outkey[80];
00302 int dead;
00303 int registerid;
00304 int qualifyid;
00305 int sentfullkey;
00306 int order;
00307 unsigned char txenckey[256];
00308 unsigned char rxenckey[256];
00309 uint32_t us_keycrc32;
00310 ast_aes_encrypt_key us_ecx;
00311 ast_aes_decrypt_key us_dcx;
00312 uint32_t them_keycrc32;
00313 ast_aes_encrypt_key them_ecx;
00314 ast_aes_decrypt_key them_dcx;
00315 time_t keyexpire;
00316 int registerexpire;
00317 int lookuptimes[DUNDI_TIMING_HISTORY];
00318 char *lookups[DUNDI_TIMING_HISTORY];
00319 int avgms;
00320 struct dundi_transaction *regtrans;
00321 struct dundi_transaction *qualtrans;
00322 int model;
00323 int pcmodel;
00324
00325 unsigned int dynamic:1;
00326 int lastms;
00327 int maxms;
00328 struct timeval qualtx;
00329 AST_LIST_ENTRY(dundi_peer) list;
00330 };
00331
00332 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
00333 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
00334 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
00335 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
00336 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
00337
00338
00339
00340
00341
00342
00343 static struct dundi_peer *any_peer;
00344
00345 static int dundi_xmit(struct dundi_packet *pack);
00346
00347 static void dundi_debug_output(const char *data)
00348 {
00349 if (dundidebug)
00350 ast_verbose("%s", data);
00351 }
00352
00353 static void dundi_error_output(const char *data)
00354 {
00355 ast_log(LOG_WARNING, "%s", data);
00356 }
00357
00358 static int has_permission(struct permissionlist *permlist, char *cont)
00359 {
00360 struct permission *perm;
00361 int res = 0;
00362
00363 AST_LIST_TRAVERSE(permlist, perm, list) {
00364 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
00365 res = perm->allow;
00366 }
00367
00368 return res;
00369 }
00370
00371 static char *tech2str(int tech)
00372 {
00373 switch(tech) {
00374 case DUNDI_PROTO_NONE:
00375 return "None";
00376 case DUNDI_PROTO_IAX:
00377 return "IAX2";
00378 case DUNDI_PROTO_SIP:
00379 return "SIP";
00380 case DUNDI_PROTO_H323:
00381 return "H323";
00382 default:
00383 return "Unknown";
00384 }
00385 }
00386
00387 static int str2tech(char *str)
00388 {
00389 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
00390 return DUNDI_PROTO_IAX;
00391 else if (!strcasecmp(str, "SIP"))
00392 return DUNDI_PROTO_SIP;
00393 else if (!strcasecmp(str, "H323"))
00394 return DUNDI_PROTO_H323;
00395 else
00396 return -1;
00397 }
00398
00399 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[]);
00400 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
00401 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
00402 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
00403 {
00404 struct dundi_transaction *trans;
00405
00406
00407 AST_LIST_TRAVERSE(&alltrans, trans, all) {
00408 if (!inaddrcmp(&trans->addr, sin) &&
00409 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) ||
00410 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) ) {
00411 if (hdr->strans)
00412 trans->dtrans = ntohs(hdr->strans) & 32767;
00413 return trans;
00414 }
00415 }
00416
00417 switch(hdr->cmdresp & 0x7f) {
00418 case DUNDI_COMMAND_DPDISCOVER:
00419 case DUNDI_COMMAND_EIDQUERY:
00420 case DUNDI_COMMAND_PRECACHERQ:
00421 case DUNDI_COMMAND_REGREQ:
00422 case DUNDI_COMMAND_NULL:
00423 case DUNDI_COMMAND_ENCRYPT:
00424 if (!hdr->strans)
00425 break;
00426
00427 if (!(trans = create_transaction(NULL)))
00428 break;
00429 memcpy(&trans->addr, sin, sizeof(trans->addr));
00430 trans->dtrans = ntohs(hdr->strans) & 32767;
00431 default:
00432 break;
00433 }
00434
00435 return trans;
00436 }
00437
00438 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
00439
00440 static int dundi_ack(struct dundi_transaction *trans, int final)
00441 {
00442 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
00443 }
00444 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
00445 {
00446 struct {
00447 struct dundi_packet pack;
00448 struct dundi_hdr hdr;
00449 } tmp;
00450 struct dundi_transaction trans;
00451
00452 if (h->cmdresp == DUNDI_COMMAND_INVALID)
00453 return;
00454 memset(&tmp, 0, sizeof(tmp));
00455 memset(&trans, 0, sizeof(trans));
00456 memcpy(&trans.addr, sin, sizeof(trans.addr));
00457 tmp.hdr.strans = h->dtrans;
00458 tmp.hdr.dtrans = h->strans;
00459 tmp.hdr.iseqno = h->oseqno;
00460 tmp.hdr.oseqno = h->iseqno;
00461 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
00462 tmp.hdr.cmdflags = 0;
00463 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
00464 tmp.pack.datalen = sizeof(struct dundi_hdr);
00465 tmp.pack.parent = &trans;
00466 dundi_xmit(&tmp.pack);
00467 }
00468
00469 static int get_trans_id(void)
00470 {
00471 struct dundi_transaction *t;
00472 int stid = (ast_random() % 32766) + 1;
00473 int tid = stid;
00474
00475 do {
00476 AST_LIST_TRAVERSE(&alltrans, t, all) {
00477 if (t->strans == tid)
00478 break;
00479 }
00480 if (!t)
00481 return tid;
00482 tid = (tid % 32766) + 1;
00483 } while (tid != stid);
00484
00485 return 0;
00486 }
00487
00488 static int reset_transaction(struct dundi_transaction *trans)
00489 {
00490 int tid;
00491 tid = get_trans_id();
00492 if (tid < 1)
00493 return -1;
00494 trans->strans = tid;
00495 trans->dtrans = 0;
00496 trans->iseqno = 0;
00497 trans->oiseqno = 0;
00498 trans->oseqno = 0;
00499 trans->aseqno = 0;
00500 ast_clear_flag(trans, FLAG_FINAL);
00501 return 0;
00502 }
00503
00504 static struct dundi_peer *find_peer(dundi_eid *eid)
00505 {
00506 struct dundi_peer *cur = NULL;
00507
00508 if (!eid)
00509 eid = &empty_eid;
00510
00511 AST_LIST_TRAVERSE(&peers, cur, list) {
00512 if (!ast_eid_cmp(&cur->eid,eid))
00513 break;
00514 }
00515
00516 if (!cur && any_peer)
00517 cur = any_peer;
00518
00519 return cur;
00520 }
00521
00522 static void build_iv(unsigned char *iv)
00523 {
00524
00525 unsigned int *fluffy;
00526 int x;
00527 fluffy = (unsigned int *)(iv);
00528 for (x=0;x<4;x++)
00529 fluffy[x] = ast_random();
00530 }
00531
00532 struct dundi_query_state {
00533 dundi_eid *eids[DUNDI_MAX_STACK + 1];
00534 int directs[DUNDI_MAX_STACK + 1];
00535 dundi_eid reqeid;
00536 char called_context[AST_MAX_EXTENSION];
00537 char called_number[AST_MAX_EXTENSION];
00538 struct dundi_mapping *maps;
00539 int nummaps;
00540 int nocache;
00541 struct dundi_transaction *trans;
00542 void *chal;
00543 int challen;
00544 int ttl;
00545 char fluffy[0];
00546 };
00547
00548 static int get_mapping_weight(struct dundi_mapping *map)
00549 {
00550 char buf[32];
00551
00552 buf[0] = 0;
00553 if (map->weightstr) {
00554 pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
00555 if (sscanf(buf, "%30d", &map->_weight) != 1)
00556 map->_weight = MAX_WEIGHT;
00557 }
00558
00559 return map->_weight;
00560 }
00561
00562 static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
00563 {
00564 struct ast_flags flags = {0};
00565 int x;
00566 if (!ast_strlen_zero(map->lcontext)) {
00567 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
00568 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
00569 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
00570 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
00571 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
00572 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
00573 if (ast_ignore_pattern(map->lcontext, called_number))
00574 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
00575
00576
00577 if (ast_test_flag(&flags, AST_FLAGS_ALL))
00578 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
00579
00580 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
00581
00582 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
00583 }
00584 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
00585 struct varshead headp;
00586 struct ast_var_t *newvariable;
00587 ast_set_flag(&flags, map->options & 0xffff);
00588 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
00589 dr[anscnt].techint = map->tech;
00590 dr[anscnt].weight = get_mapping_weight(map);
00591 dr[anscnt].expiration = dundi_cache_time;
00592 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
00593 dr[anscnt].eid = *us_eid;
00594 ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
00595 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
00596 AST_LIST_HEAD_INIT_NOLOCK(&headp);
00597 newvariable = ast_var_assign("NUMBER", called_number);
00598 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00599 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
00600 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00601 newvariable = ast_var_assign("SECRET", cursecret);
00602 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00603 newvariable = ast_var_assign("IPADDR", ipaddr);
00604 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00605 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
00606 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
00607 ast_var_delete(newvariable);
00608 } else
00609 dr[anscnt].dest[0] = '\0';
00610 anscnt++;
00611 } else {
00612
00613
00614 char tmp[AST_MAX_EXTENSION + 1] = "";
00615 for (x = 0; x < (sizeof(tmp) - 1); x++) {
00616 tmp[x] = called_number[x];
00617 if (!tmp[x])
00618 break;
00619 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
00620
00621
00622 if (strlen(tmp) > strlen(hmd->exten)) {
00623 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
00624 }
00625 break;
00626 }
00627 }
00628 }
00629 }
00630 return anscnt;
00631 }
00632
00633 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
00634
00635 static void *dundi_lookup_thread(void *data)
00636 {
00637 struct dundi_query_state *st = data;
00638 struct dundi_result dr[MAX_RESULTS];
00639 struct dundi_ie_data ied;
00640 struct dundi_hint_metadata hmd;
00641 char eid_str[20];
00642 int res, x;
00643 int ouranswers=0;
00644 int max = 999999;
00645 int expiration = dundi_cache_time;
00646
00647 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
00648 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
00649 memset(&ied, 0, sizeof(ied));
00650 memset(&dr, 0, sizeof(dr));
00651 memset(&hmd, 0, sizeof(hmd));
00652
00653 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00654 for (x=0;x<st->nummaps;x++)
00655 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
00656 if (ouranswers < 0)
00657 ouranswers = 0;
00658 for (x=0;x<ouranswers;x++) {
00659 if (dr[x].weight < max)
00660 max = dr[x].weight;
00661 }
00662
00663 if (max) {
00664
00665 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);
00666 if (res > 0) {
00667
00668 ouranswers += res;
00669 } else {
00670 if ((res < -1) && (!ouranswers))
00671 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
00672 }
00673 }
00674 AST_LIST_LOCK(&peers);
00675
00676 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00677 hmd.exten[0] = '\0';
00678 if (ast_test_flag(st->trans, FLAG_DEAD)) {
00679 ast_debug(1, "Our transaction went away!\n");
00680 st->trans->thread = 0;
00681 destroy_trans(st->trans, 0);
00682 } else {
00683 for (x=0;x<ouranswers;x++) {
00684
00685 if (dr[x].expiration && (expiration > dr[x].expiration))
00686 expiration = dr[x].expiration;
00687 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
00688 }
00689 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00690 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
00691 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
00692 st->trans->thread = 0;
00693 }
00694 AST_LIST_UNLOCK(&peers);
00695 ast_free(st);
00696 return NULL;
00697 }
00698
00699 static void *dundi_precache_thread(void *data)
00700 {
00701 struct dundi_query_state *st = data;
00702 struct dundi_ie_data ied;
00703 struct dundi_hint_metadata hmd;
00704 char eid_str[20];
00705
00706 ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
00707 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
00708 memset(&ied, 0, sizeof(ied));
00709
00710
00711 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
00712
00713 AST_LIST_LOCK(&peers);
00714
00715 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00716 hmd.exten[0] = '\0';
00717 if (ast_test_flag(st->trans, FLAG_DEAD)) {
00718 ast_debug(1, "Our transaction went away!\n");
00719 st->trans->thread = 0;
00720 destroy_trans(st->trans, 0);
00721 } else {
00722 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
00723 st->trans->thread = 0;
00724 }
00725 AST_LIST_UNLOCK(&peers);
00726 ast_free(st);
00727 return NULL;
00728 }
00729
00730 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[]);
00731
00732 static void *dundi_query_thread(void *data)
00733 {
00734 struct dundi_query_state *st = data;
00735 struct dundi_entity_info dei;
00736 struct dundi_ie_data ied;
00737 struct dundi_hint_metadata hmd;
00738 char eid_str[20];
00739 int res;
00740
00741 ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
00742 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
00743 memset(&ied, 0, sizeof(ied));
00744 memset(&dei, 0, sizeof(dei));
00745 memset(&hmd, 0, sizeof(hmd));
00746 if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
00747
00748 ast_debug(1, "Neat, someone look for us!\n");
00749 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
00750 ast_copy_string(dei.org, org, sizeof(dei.org));
00751 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
00752 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
00753 ast_copy_string(dei.country, country, sizeof(dei.country));
00754 ast_copy_string(dei.email, email, sizeof(dei.email));
00755 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
00756 res = 1;
00757 } else {
00758
00759 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
00760 }
00761 AST_LIST_LOCK(&peers);
00762 if (ast_test_flag(st->trans, FLAG_DEAD)) {
00763 ast_debug(1, "Our transaction went away!\n");
00764 st->trans->thread = 0;
00765 destroy_trans(st->trans, 0);
00766 } else {
00767 if (res) {
00768 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
00769 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
00770 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
00771 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
00772 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
00773 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
00774 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
00775 if (!ast_strlen_zero(dei.ipaddr))
00776 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
00777 }
00778 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00779 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00780 st->trans->thread = 0;
00781 }
00782 AST_LIST_UNLOCK(&peers);
00783 ast_free(st);
00784 return NULL;
00785 }
00786
00787 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00788 {
00789 struct dundi_query_state *st;
00790 int totallen;
00791 int x;
00792 int skipfirst=0;
00793 char eid_str[20];
00794 char *s;
00795 pthread_t lookupthread;
00796
00797 if (ies->eidcount > 1) {
00798
00799
00800
00801
00802 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00803 skipfirst = 1;
00804 }
00805 totallen = sizeof(struct dundi_query_state);
00806 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00807 st = ast_calloc(1, totallen);
00808 if (st) {
00809 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00810 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
00811 st->trans = trans;
00812 st->ttl = ies->ttl - 1;
00813 if (st->ttl < 0)
00814 st->ttl = 0;
00815 s = st->fluffy;
00816 for (x=skipfirst;ies->eids[x];x++) {
00817 st->eids[x-skipfirst] = (dundi_eid *)s;
00818 *st->eids[x-skipfirst] = *ies->eids[x];
00819 s += sizeof(dundi_eid);
00820 }
00821 ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
00822
00823 trans->thread = 1;
00824 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
00825 struct dundi_ie_data ied = { 0, };
00826 trans->thread = 0;
00827 ast_log(LOG_WARNING, "Unable to create thread!\n");
00828 ast_free(st);
00829 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
00830 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00831 return -1;
00832 }
00833 } else {
00834 struct dundi_ie_data ied = { 0, };
00835 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
00836 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00837 return -1;
00838 }
00839 return 0;
00840 }
00841
00842 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
00843 {
00844 int unaffected;
00845 char key1[256];
00846 char key2[256];
00847 char eidpeer_str[20];
00848 char eidroot_str[20];
00849 char data[80];
00850 time_t timeout;
00851
00852 if (expiration < 0)
00853 expiration = dundi_cache_time;
00854
00855
00856 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
00857 return 0;
00858
00859 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
00860
00861 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00862 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00863 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
00864 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
00865
00866 time(&timeout);
00867 timeout += expiration;
00868 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00869
00870 ast_db_put("dundi/cache", key1, data);
00871 ast_debug(1, "Caching hint at '%s'\n", key1);
00872 ast_db_put("dundi/cache", key2, data);
00873 ast_debug(1, "Caching hint at '%s'\n", key2);
00874 return 0;
00875 }
00876
00877 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
00878 {
00879 int x;
00880 char key1[256];
00881 char key2[256];
00882 char data[1024];
00883 char eidpeer_str[20];
00884 char eidroot_str[20];
00885 time_t timeout;
00886
00887 if (expiration < 1)
00888 expiration = dundi_cache_time;
00889
00890
00891 if (push)
00892 expiration += 10;
00893 else
00894 expiration -= 10;
00895 if (expiration < 1)
00896 expiration = 1;
00897 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00898 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00899 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
00900 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
00901
00902 time(&timeout);
00903 timeout += expiration;
00904 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00905 for (x=start;x<req->respcount;x++) {
00906
00907 if (strchr(req->dr[x].dest, '|'))
00908 continue;
00909 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
00910 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
00911 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
00912 }
00913 ast_db_put("dundi/cache", key1, data);
00914 ast_db_put("dundi/cache", key2, data);
00915 return 0;
00916 }
00917
00918 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00919 {
00920 struct dundi_query_state *st;
00921 int totallen;
00922 int x,z;
00923 struct dundi_ie_data ied;
00924 char *s;
00925 struct dundi_result dr2[MAX_RESULTS];
00926 struct dundi_request dr;
00927 struct dundi_hint_metadata hmd;
00928
00929 struct dundi_mapping *cur;
00930 int mapcount;
00931 int skipfirst = 0;
00932
00933 pthread_t lookupthread;
00934
00935 memset(&dr2, 0, sizeof(dr2));
00936 memset(&dr, 0, sizeof(dr));
00937 memset(&hmd, 0, sizeof(hmd));
00938
00939
00940 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00941 dr.dr = dr2;
00942 dr.maxcount = MAX_RESULTS;
00943 dr.expiration = dundi_cache_time;
00944 dr.hmd = &hmd;
00945 dr.pfds[0] = dr.pfds[1] = -1;
00946 trans->parent = &dr;
00947 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
00948 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
00949
00950 for (x=0;x<ies->anscount;x++) {
00951 if (trans->parent->respcount < trans->parent->maxcount) {
00952
00953 for (z=0;z<trans->parent->respcount;z++) {
00954 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
00955 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
00956 break;
00957 }
00958 if (z == trans->parent->respcount) {
00959
00960 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
00961 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
00962 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
00963 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
00964 if (ies->expiration > 0)
00965 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
00966 else
00967 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
00968 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
00969 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
00970 &ies->answers[x]->eid);
00971 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
00972 sizeof(trans->parent->dr[trans->parent->respcount].dest));
00973 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
00974 sizeof(trans->parent->dr[trans->parent->respcount].tech));
00975 trans->parent->respcount++;
00976 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
00977 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
00978
00979 trans->parent->dr[z].weight = ies->answers[x]->weight;
00980 }
00981 } else
00982 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
00983 trans->parent->number, trans->parent->dcontext);
00984
00985 }
00986
00987 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
00988 if (ies->hint)
00989 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
00990
00991 totallen = sizeof(struct dundi_query_state);
00992
00993 mapcount = 0;
00994 AST_LIST_TRAVERSE(&mappings, cur, list) {
00995 if (!strcasecmp(cur->dcontext, ccontext))
00996 mapcount++;
00997 }
00998
00999
01000 if (!mapcount)
01001 return -1;
01002
01003 if (ies->eidcount > 1) {
01004
01005
01006
01007
01008 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01009 skipfirst = 1;
01010 }
01011
01012
01013 totallen += mapcount * sizeof(struct dundi_mapping);
01014 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01015 st = ast_calloc(1, totallen);
01016 if (st) {
01017 ast_copy_string(st->called_context, dr.dcontext, sizeof(st->called_context));
01018 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01019 st->trans = trans;
01020 st->ttl = ies->ttl - 1;
01021 st->nocache = ies->cbypass;
01022 if (st->ttl < 0)
01023 st->ttl = 0;
01024 s = st->fluffy;
01025 for (x=skipfirst;ies->eids[x];x++) {
01026 st->eids[x-skipfirst] = (dundi_eid *)s;
01027 *st->eids[x-skipfirst] = *ies->eids[x];
01028 st->directs[x-skipfirst] = ies->eid_direct[x];
01029 s += sizeof(dundi_eid);
01030 }
01031
01032 x = 0;
01033 st->maps = (struct dundi_mapping *)s;
01034 AST_LIST_TRAVERSE(&mappings, cur, list) {
01035 if (!strcasecmp(cur->dcontext, ccontext)) {
01036 if (x < mapcount) {
01037 st->maps[x] = *cur;
01038 st->maps[x].list.next = NULL;
01039 x++;
01040 }
01041 }
01042 }
01043 st->nummaps = mapcount;
01044 ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
01045 trans->thread = 1;
01046 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
01047 trans->thread = 0;
01048 ast_log(LOG_WARNING, "Unable to create thread!\n");
01049 ast_free(st);
01050 memset(&ied, 0, sizeof(ied));
01051 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01052 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01053 return -1;
01054 }
01055 } else {
01056 ast_log(LOG_WARNING, "Out of memory!\n");
01057 memset(&ied, 0, sizeof(ied));
01058 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01059 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01060 return -1;
01061 }
01062 return 0;
01063 }
01064
01065 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
01066 {
01067 struct dundi_query_state *st;
01068 int totallen;
01069 int x;
01070 struct dundi_ie_data ied;
01071 char *s;
01072 struct dundi_mapping *cur;
01073 int mapcount = 0;
01074 int skipfirst = 0;
01075
01076 pthread_t lookupthread;
01077 totallen = sizeof(struct dundi_query_state);
01078
01079 AST_LIST_TRAVERSE(&mappings, cur, list) {
01080 if (!strcasecmp(cur->dcontext, ccontext))
01081 mapcount++;
01082 }
01083
01084 if (!mapcount)
01085 return -1;
01086
01087 if (ies->eidcount > 1) {
01088
01089
01090
01091
01092 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01093 skipfirst = 1;
01094 }
01095
01096 totallen += mapcount * sizeof(struct dundi_mapping);
01097 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01098 st = ast_calloc(1, totallen);
01099 if (st) {
01100 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
01101 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01102 st->trans = trans;
01103 st->ttl = ies->ttl - 1;
01104 st->nocache = ies->cbypass;
01105 if (st->ttl < 0)
01106 st->ttl = 0;
01107 s = st->fluffy;
01108 for (x=skipfirst;ies->eids[x];x++) {
01109 st->eids[x-skipfirst] = (dundi_eid *)s;
01110 *st->eids[x-skipfirst] = *ies->eids[x];
01111 st->directs[x-skipfirst] = ies->eid_direct[x];
01112 s += sizeof(dundi_eid);
01113 }
01114
01115 x = 0;
01116 st->maps = (struct dundi_mapping *)s;
01117 AST_LIST_TRAVERSE(&mappings, cur, list) {
01118 if (!strcasecmp(cur->dcontext, ccontext)) {
01119 if (x < mapcount) {
01120 st->maps[x] = *cur;
01121 st->maps[x].list.next = NULL;
01122 x++;
01123 }
01124 }
01125 }
01126 st->nummaps = mapcount;
01127 ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
01128 trans->thread = 1;
01129 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
01130 trans->thread = 0;
01131 ast_log(LOG_WARNING, "Unable to create thread!\n");
01132 ast_free(st);
01133 memset(&ied, 0, sizeof(ied));
01134 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01135 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01136 return -1;
01137 }
01138 } else {
01139 ast_log(LOG_WARNING, "Out of memory!\n");
01140 memset(&ied, 0, sizeof(ied));
01141 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01142 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01143 return -1;
01144 }
01145 return 0;
01146 }
01147
01148 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
01149 {
01150 char data[1024];
01151 char *ptr, *term, *src;
01152 int tech;
01153 struct ast_flags flags;
01154 int weight;
01155 int length;
01156 int z;
01157 char fs[256];
01158
01159
01160 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
01161 time_t timeout;
01162 ptr = data;
01163 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
01164 int expiration = timeout - now;
01165 if (expiration > 0) {
01166 ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
01167 ptr += length + 1;
01168 while((sscanf(ptr, "%30d/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
01169 ptr += length;
01170 term = strchr(ptr, '|');
01171 if (term) {
01172 *term = '\0';
01173 src = strrchr(ptr, '/');
01174 if (src) {
01175 *src = '\0';
01176 src++;
01177 } else
01178 src = "";
01179 ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
01180 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
01181
01182 for (z=0;z<req->respcount;z++) {
01183 if ((req->dr[z].techint == tech) &&
01184 !strcmp(req->dr[z].dest, ptr))
01185 break;
01186 }
01187 if (z == req->respcount) {
01188
01189 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
01190 req->dr[req->respcount].weight = weight;
01191 req->dr[req->respcount].techint = tech;
01192 req->dr[req->respcount].expiration = expiration;
01193 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
01194 ast_eid_to_str(req->dr[req->respcount].eid_str,
01195 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
01196 ast_copy_string(req->dr[req->respcount].dest, ptr,
01197 sizeof(req->dr[req->respcount].dest));
01198 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
01199 sizeof(req->dr[req->respcount].tech));
01200 req->respcount++;
01201 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
01202 } else if (req->dr[z].weight > weight)
01203 req->dr[z].weight = weight;
01204 ptr = term + 1;
01205 }
01206 }
01207
01208 if (expiration < *lowexpiration)
01209 *lowexpiration = expiration;
01210 return 1;
01211 } else
01212 ast_db_del("dundi/cache", key);
01213 } else
01214 ast_db_del("dundi/cache", key);
01215 }
01216
01217 return 0;
01218 }
01219
01220 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc32, int *lowexpiration)
01221 {
01222 char key[256];
01223 char eid_str[20];
01224 char eidroot_str[20];
01225 time_t now;
01226 int res=0;
01227 int res2=0;
01228 char eid_str_full[20];
01229 char tmp[256]="";
01230 int x;
01231
01232 time(&now);
01233 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
01234 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
01235 ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
01236 snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc32);
01237 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01238 snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, 0);
01239 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01240 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
01241 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01242 x = 0;
01243 if (!req->respcount) {
01244 while(!res2) {
01245
01246
01247 if (!(tmp[x] = req->number[x]))
01248 break;
01249 x++;
01250
01251 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc32);
01252 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01253 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, 0);
01254 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01255 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
01256 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01257 if (res2) {
01258 if (strlen(tmp) > strlen(req->hmd->exten)) {
01259
01260 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
01261 }
01262 }
01263 }
01264 res |= res2;
01265 }
01266
01267 return res;
01268 }
01269
01270 static void qualify_peer(struct dundi_peer *peer, int schedonly);
01271
01272 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
01273 {
01274 if (!trans->addr.sin_addr.s_addr)
01275 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
01276 trans->us_eid = p->us_eid;
01277 trans->them_eid = p->eid;
01278
01279 if (!ast_strlen_zero(p->inkey))
01280 ast_set_flag(trans, FLAG_ENCRYPT);
01281 if (p->maxms) {
01282 trans->autokilltimeout = p->maxms;
01283 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01284 if (p->lastms > 1) {
01285 trans->retranstimer = p->lastms * 2;
01286
01287 if (trans->retranstimer < 150)
01288 trans->retranstimer = 150;
01289 }
01290 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
01291 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01292 } else
01293 trans->autokilltimeout = global_autokilltimeout;
01294 }
01295
01296
01297 static int do_register_expire(const void *data)
01298 {
01299 struct dundi_peer *peer = (struct dundi_peer *)data;
01300 char eid_str[20];
01301 ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01302 peer->registerexpire = -1;
01303 peer->lastms = 0;
01304 memset(&peer->addr, 0, sizeof(peer->addr));
01305 return 0;
01306 }
01307
01308 static int update_key(struct dundi_peer *peer)
01309 {
01310 unsigned char key[16];
01311 struct ast_key *ekey, *skey;
01312 char eid_str[20];
01313 int res;
01314 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
01315 build_iv(key);
01316 ast_aes_set_encrypt_key(key, &peer->us_ecx);
01317 ast_aes_set_decrypt_key(key, &peer->us_dcx);
01318 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01319 if (!ekey) {
01320 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
01321 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01322 return -1;
01323 }
01324 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01325 if (!skey) {
01326 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
01327 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01328 return -1;
01329 }
01330 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
01331 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
01332 return -1;
01333 }
01334 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
01335 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
01336 return -1;
01337 }
01338 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
01339 peer->sentfullkey = 0;
01340
01341 time(&peer->keyexpire);
01342 peer->keyexpire += dundi_key_ttl;
01343 }
01344 return 0;
01345 }
01346
01347 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
01348 {
01349 unsigned char curblock[16];
01350 int x;
01351 memcpy(curblock, iv, sizeof(curblock));
01352 while(len > 0) {
01353 for (x=0;x<16;x++)
01354 curblock[x] ^= src[x];
01355 ast_aes_encrypt(curblock, dst, ecx);
01356 memcpy(curblock, dst, sizeof(curblock));
01357 dst += 16;
01358 src += 16;
01359 len -= 16;
01360 }
01361 return 0;
01362 }
01363 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
01364 {
01365 unsigned char lastblock[16];
01366 int x;
01367 memcpy(lastblock, iv, sizeof(lastblock));
01368 while(len > 0) {
01369 ast_aes_decrypt(src, dst, dcx);
01370 for (x=0;x<16;x++)
01371 dst[x] ^= lastblock[x];
01372 memcpy(lastblock, src, sizeof(lastblock));
01373 dst += 16;
01374 src += 16;
01375 len -= 16;
01376 }
01377 return 0;
01378 }
01379
01380 static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
01381 {
01382 int space = *dstlen;
01383 unsigned long bytes;
01384 struct dundi_hdr *h;
01385 unsigned char *decrypt_space;
01386 decrypt_space = ast_alloca(srclen);
01387 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
01388
01389 h = (struct dundi_hdr *)dst;
01390 *h = *ohdr;
01391 bytes = space - 6;
01392 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
01393 ast_debug(1, "Ouch, uncompress failed :(\n");
01394 return NULL;
01395 }
01396
01397 *dstlen = bytes + 6;
01398
01399 return h;
01400 }
01401
01402 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
01403 {
01404 unsigned char *compress_space;
01405 int len;
01406 int res;
01407 unsigned long bytes;
01408 struct dundi_ie_data ied;
01409 struct dundi_peer *peer;
01410 unsigned char iv[16];
01411 len = pack->datalen + pack->datalen / 100 + 42;
01412 compress_space = ast_alloca(len);
01413 memset(compress_space, 0, len);
01414
01415 bytes = len;
01416 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
01417 if (res != Z_OK) {
01418 ast_debug(1, "Ouch, compression failed!\n");
01419 return -1;
01420 }
01421 memset(&ied, 0, sizeof(ied));
01422
01423 if (!pack->h->iseqno && !pack->h->oseqno) {
01424
01425 if (!(peer = find_peer(&trans->them_eid)))
01426 return -1;
01427 if (update_key(peer))
01428 return -1;
01429 if (!peer->sentfullkey)
01430 ast_set_flag(trans, FLAG_SENDFULLKEY);
01431
01432 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01433 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
01434 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01435 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01436 } else {
01437 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
01438 }
01439
01440 trans->ecx = peer->us_ecx;
01441 trans->dcx = peer->us_dcx;
01442
01443
01444 peer->sentfullkey = 1;
01445 }
01446
01447 build_iv(iv);
01448
01449 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
01450
01451 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
01452 ast_log(LOG_NOTICE, "Final packet too large!\n");
01453 return -1;
01454 }
01455 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
01456 ied.pos += ((bytes + 15) / 16) * 16;
01457
01458 pack->datalen = sizeof(struct dundi_hdr);
01459 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
01460 pack->h->cmdflags = 0;
01461 memcpy(pack->h->ies, ied.buf, ied.pos);
01462 pack->datalen += ied.pos;
01463 return 0;
01464 }
01465
01466 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
01467 {
01468 unsigned char dst[128];
01469 int res;
01470 struct ast_key *key, *skey;
01471 char eid_str[20];
01472 ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
01473 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
01474
01475 return 1;
01476 } else if (!newkey || !newsig)
01477 return 0;
01478 if (!memcmp(peer->rxenckey, newkey, 128) &&
01479 !memcmp(peer->rxenckey + 128, newsig, 128)) {
01480
01481 return 1;
01482 }
01483
01484 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01485 if (!key) {
01486 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
01487 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01488 return -1;
01489 }
01490
01491 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01492 if (!skey) {
01493 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
01494 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01495 return -1;
01496 }
01497
01498
01499 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
01500 if (res)
01501 return 0;
01502
01503 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
01504 if (res != 16) {
01505 if (res >= 0)
01506 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
01507 return 0;
01508 }
01509
01510 ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
01511 memcpy(peer->rxenckey, newkey, 128);
01512 memcpy(peer->rxenckey + 128, newsig, 128);
01513 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
01514 ast_aes_set_decrypt_key(dst, &peer->them_dcx);
01515 ast_aes_set_encrypt_key(dst, &peer->them_ecx);
01516 return 1;
01517 }
01518
01519 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
01520 {
01521 struct permission *cur, *perm;
01522
01523 memcpy(peer_dst, peer_src, sizeof(*peer_dst));
01524
01525 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
01526 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
01527
01528 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
01529 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01530 continue;
01531
01532 perm->allow = cur->allow;
01533 strcpy(perm->name, cur->name);
01534
01535 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
01536 }
01537
01538 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
01539 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01540 continue;
01541
01542 perm->allow = cur->allow;
01543 strcpy(perm->name, cur->name);
01544
01545 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
01546 }
01547 }
01548
01549 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
01550 {
01551
01552 int final = hdr->cmdresp & 0x80;
01553 int cmd = hdr->cmdresp & 0x7f;
01554 int x,y,z;
01555 int resp;
01556 int res;
01557 int authpass=0;
01558 unsigned char *bufcpy;
01559 #ifdef LOW_MEMORY
01560 struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
01561 #else
01562 struct dundi_ie_data _ied = {
01563 .pos = 0,
01564 };
01565 struct dundi_ie_data *ied = &_ied;
01566 #endif
01567 struct dundi_ies ies = {
01568 .eidcount = 0,
01569 };
01570 struct dundi_peer *peer = NULL;
01571 char eid_str[20];
01572 char eid_str2[20];
01573 int retval = -1;
01574
01575 if (!ied) {
01576 return -1;
01577 }
01578
01579 if (datalen) {
01580 bufcpy = ast_alloca(datalen);
01581
01582 memcpy(bufcpy, hdr->ies, datalen);
01583 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
01584 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
01585 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
01586 goto return_cleanup;
01587 }
01588 }
01589 switch(cmd) {
01590 case DUNDI_COMMAND_DPDISCOVER:
01591 case DUNDI_COMMAND_EIDQUERY:
01592 case DUNDI_COMMAND_PRECACHERQ:
01593 if (cmd == DUNDI_COMMAND_EIDQUERY)
01594 resp = DUNDI_COMMAND_EIDRESPONSE;
01595 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
01596 resp = DUNDI_COMMAND_PRECACHERP;
01597 else
01598 resp = DUNDI_COMMAND_DPRESPONSE;
01599
01600 peer = find_peer(ies.eids[0]);
01601 if (!peer) {
01602 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01603 dundi_send(trans, resp, 0, 1, ied);
01604 } else {
01605 int hasauth = 0;
01606 trans->us_eid = peer->us_eid;
01607 if (strlen(peer->inkey)) {
01608 hasauth = encrypted;
01609 } else
01610 hasauth = 1;
01611 if (hasauth) {
01612
01613 if (!ies.called_context)
01614 ies.called_context = "e164";
01615 if (cmd == DUNDI_COMMAND_EIDQUERY) {
01616 res = dundi_answer_entity(trans, &ies, ies.called_context);
01617 } else {
01618 if (ast_strlen_zero(ies.called_number)) {
01619
01620 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
01621 dundi_send(trans, resp, 0, 1, ied);
01622 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
01623 (peer->model & DUNDI_MODEL_INBOUND) &&
01624 has_permission(&peer->permit, ies.called_context)) {
01625 res = dundi_answer_query(trans, &ies, ies.called_context);
01626 if (res < 0) {
01627
01628 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01629 dundi_send(trans, resp, 0, 1, ied);
01630 }
01631 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
01632 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
01633 has_permission(&peer->include, ies.called_context)) {
01634 res = dundi_prop_precache(trans, &ies, ies.called_context);
01635 if (res < 0) {
01636
01637 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01638 dundi_send(trans, resp, 0, 1, ied);
01639 }
01640 } else {
01641
01642 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
01643 dundi_send(trans, resp, 0, 1, ied);
01644 }
01645 }
01646 } else {
01647
01648 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
01649 dundi_send(trans, resp, 0, 1, ied);
01650 }
01651 }
01652 break;
01653 case DUNDI_COMMAND_REGREQ:
01654
01655 peer = find_peer(ies.eids[0]);
01656
01657
01658 if (any_peer && peer == any_peer) {
01659
01660 peer = ast_calloc(1, sizeof(*peer));
01661 if (peer) {
01662 deep_copy_peer(peer, any_peer);
01663
01664
01665 peer->eid = *ies.eids[0];
01666
01667 AST_LIST_LOCK(&peers);
01668 AST_LIST_INSERT_HEAD(&peers, peer, list);
01669 AST_LIST_UNLOCK(&peers);
01670 }
01671 }
01672
01673 if (!peer || !peer->dynamic) {
01674 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01675 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
01676 } else {
01677 int hasauth = 0;
01678 trans->us_eid = peer->us_eid;
01679 if (!ast_strlen_zero(peer->inkey)) {
01680 hasauth = encrypted;
01681 } else
01682 hasauth = 1;
01683 if (hasauth) {
01684 int expire = default_expiration;
01685 char data[256];
01686 int needqual = 0;
01687 AST_SCHED_DEL(sched, peer->registerexpire);
01688 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
01689 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
01690 ntohs(trans->addr.sin_port), expire);
01691 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
01692 if (inaddrcmp(&peer->addr, &trans->addr)) {
01693 ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
01694 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
01695 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
01696 needqual = 1;
01697 }
01698
01699 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
01700 dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
01701 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
01702 if (needqual)
01703 qualify_peer(peer, 1);
01704 }
01705 }
01706 break;
01707 case DUNDI_COMMAND_DPRESPONSE:
01708
01709 if (ies.cause < 1) {
01710
01711 ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
01712 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01713 authpass = encrypted;
01714 } else
01715 authpass = 1;
01716 if (authpass) {
01717
01718 if (trans->parent && trans->parent->dr) {
01719 y = trans->parent->respcount;
01720 for (x=0;x<ies.anscount;x++) {
01721 if (trans->parent->respcount < trans->parent->maxcount) {
01722
01723 for (z=0;z<trans->parent->respcount;z++) {
01724 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
01725 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
01726 break;
01727 }
01728 if (z == trans->parent->respcount) {
01729
01730 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
01731 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
01732 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
01733 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
01734 if (ies.expiration > 0)
01735 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
01736 else
01737 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
01738 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
01739 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
01740 &ies.answers[x]->eid);
01741 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
01742 sizeof(trans->parent->dr[trans->parent->respcount].dest));
01743 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
01744 sizeof(trans->parent->dr[trans->parent->respcount].tech));
01745 trans->parent->respcount++;
01746 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01747 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
01748
01749 trans->parent->dr[z].weight = ies.answers[x]->weight;
01750 }
01751 } else
01752 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
01753 trans->parent->number, trans->parent->dcontext);
01754 }
01755
01756
01757 cache_save(&trans->them_eid, trans->parent, y,
01758 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
01759 if (ies.hint) {
01760 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
01761 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01762 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01763 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
01764 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
01765 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
01766 sizeof(trans->parent->hmd->exten));
01767 }
01768 } else {
01769 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01770 }
01771 }
01772 if (ies.expiration > 0) {
01773 if (trans->parent->expiration > ies.expiration) {
01774 trans->parent->expiration = ies.expiration;
01775 }
01776 }
01777 }
01778
01779 if (!final)
01780 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01781 }
01782
01783 } else {
01784
01785 if (!final) {
01786
01787 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01788 }
01789 }
01790 break;
01791 case DUNDI_COMMAND_EIDRESPONSE:
01792
01793 if (ies.cause < 1) {
01794
01795 ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
01796 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01797 authpass = encrypted;
01798 } else
01799 authpass = 1;
01800 if (authpass) {
01801
01802 if (trans->parent && trans->parent->dei && ies.q_org) {
01803 if (!trans->parent->respcount) {
01804 trans->parent->respcount++;
01805 if (ies.q_dept)
01806 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
01807 if (ies.q_org)
01808 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
01809 if (ies.q_locality)
01810 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
01811 if (ies.q_stateprov)
01812 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
01813 if (ies.q_country)
01814 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
01815 if (ies.q_email)
01816 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
01817 if (ies.q_phone)
01818 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
01819 if (ies.q_ipaddr)
01820 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
01821 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
01822
01823 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
01824 }
01825 }
01826 if (ies.hint) {
01827 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01828 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01829 }
01830 }
01831
01832 if (!final)
01833 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01834 }
01835
01836 } else {
01837
01838 if (!final) {
01839
01840 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01841 }
01842 }
01843 break;
01844 case DUNDI_COMMAND_REGRESPONSE:
01845
01846 if (ies.cause < 1) {
01847 int hasauth;
01848
01849 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01850 hasauth = encrypted;
01851 } else
01852 hasauth = 1;
01853
01854 if (!hasauth) {
01855 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
01856 if (!final) {
01857 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
01858 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
01859 }
01860 } else {
01861 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
01862 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
01863
01864 if (!final)
01865 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01866 }
01867 } else {
01868
01869 if (!final) {
01870 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01871 }
01872 }
01873 break;
01874 case DUNDI_COMMAND_INVALID:
01875 case DUNDI_COMMAND_NULL:
01876 case DUNDI_COMMAND_PRECACHERP:
01877
01878 if (!final)
01879 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01880 break;
01881 case DUNDI_COMMAND_ENCREJ:
01882 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
01883
01884 if (!final)
01885 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01886 } else {
01887
01888 ast_set_flag(trans, FLAG_SENDFULLKEY);
01889 if (final) {
01890
01891 dundi_ack(trans, hdr->cmdresp & 0x80);
01892 trans->aseqno = trans->iseqno;
01893
01894 if (!reset_transaction(trans)) {
01895
01896 hdr->cmdresp &= 0x7f;
01897
01898 memset(&ies, 0, sizeof(ies));
01899 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
01900
01901 memset(ied, 0, sizeof(*ied));
01902 dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
01903 dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01904 dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01905 if (ies.encblock)
01906 dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
01907 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
01908 peer->sentfullkey = 1;
01909 }
01910 }
01911 }
01912 break;
01913 case DUNDI_COMMAND_ENCRYPT:
01914 if (!encrypted) {
01915
01916 if ((trans->iseqno == 1) && !trans->oseqno) {
01917 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
01918 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
01919 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
01920 if (!final) {
01921 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01922 }
01923 break;
01924 }
01925 apply_peer(trans, peer);
01926
01927 trans->ecx = peer->them_ecx;
01928 trans->dcx = peer->them_dcx;
01929 }
01930 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
01931 struct dundi_hdr *dhdr;
01932 unsigned char decoded[MAX_PACKET_SIZE];
01933 int ddatalen;
01934 ddatalen = sizeof(decoded);
01935 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
01936 if (dhdr) {
01937
01938 if (dundidebug)
01939 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
01940 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
01941
01942 hdr->cmdresp |= dhdr->cmdresp & 0x80;
01943 break;
01944 } else {
01945 ast_debug(1, "Ouch, decrypt failed :(\n");
01946 }
01947 }
01948 }
01949 if (!final) {
01950
01951 ast_clear_flag(trans, FLAG_ENCRYPT);
01952 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01953 }
01954 break;
01955 default:
01956
01957
01958 if (!final) {
01959 dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
01960 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
01961 }
01962 }
01963
01964 retval = 0;
01965
01966 return_cleanup:
01967 #ifdef LOW_MEMORY
01968 ast_free(ied);
01969 #endif
01970 return retval;
01971 }
01972
01973 static void destroy_packet(struct dundi_packet *pack, int needfree);
01974 static void destroy_packets(struct packetlist *p)
01975 {
01976 struct dundi_packet *pack;
01977
01978 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
01979 AST_SCHED_DEL(sched, pack->retransid);
01980 ast_free(pack);
01981 }
01982 }
01983
01984
01985 static int ack_trans(struct dundi_transaction *trans, int iseqno)
01986 {
01987 struct dundi_packet *pack;
01988
01989
01990 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
01991 if ((pack->h->oseqno + 1) % 255 == iseqno) {
01992 destroy_packet(pack, 0);
01993 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
01994 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
01995 destroy_packets(&trans->lasttrans);
01996 }
01997 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
01998 AST_SCHED_DEL(sched, trans->autokillid);
01999 return 1;
02000 }
02001 }
02002
02003 return 0;
02004 }
02005
02006 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
02007 {
02008 struct dundi_transaction *trans;
02009 trans = find_transaction(h, sin);
02010 if (!trans) {
02011 dundi_reject(h, sin);
02012 return 0;
02013 }
02014
02015 if (h->oseqno == trans->iseqno) {
02016
02017 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
02018
02019 destroy_trans(trans, 0);
02020 return 0;
02021 }
02022 if (h->cmdresp != DUNDI_COMMAND_ACK) {
02023 trans->oiseqno = trans->iseqno;
02024 trans->iseqno++;
02025 handle_command_response(trans, h, datalen, 0);
02026 }
02027 if (trans->aseqno != trans->iseqno) {
02028 dundi_ack(trans, h->cmdresp & 0x80);
02029 trans->aseqno = trans->iseqno;
02030 }
02031
02032 destroy_packets(&trans->lasttrans);
02033 if (h->cmdresp & 0x80) {
02034
02035 destroy_trans(trans, 0);
02036 }
02037 } else if (h->oseqno == trans->oiseqno) {
02038
02039 dundi_ack(trans, 0);
02040 } else {
02041
02042 ast_debug(1, "Dropping packet out of window!\n");
02043 }
02044 return 0;
02045 }
02046
02047 static int socket_read(int *id, int fd, short events, void *cbdata)
02048 {
02049 struct sockaddr_in sin;
02050 int res;
02051 struct dundi_hdr *h;
02052 char buf[MAX_PACKET_SIZE];
02053 socklen_t len = sizeof(sin);
02054
02055 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
02056 if (res < 0) {
02057 if (errno != ECONNREFUSED)
02058 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
02059 return 1;
02060 }
02061 if (res < sizeof(struct dundi_hdr)) {
02062 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
02063 return 1;
02064 }
02065 buf[res] = '\0';
02066 h = (struct dundi_hdr *) buf;
02067 if (dundidebug)
02068 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
02069 AST_LIST_LOCK(&peers);
02070 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
02071 AST_LIST_UNLOCK(&peers);
02072 return 1;
02073 }
02074
02075 static void build_secret(char *secret, int seclen)
02076 {
02077 unsigned char tmp[16];
02078 char *s;
02079 build_iv(tmp);
02080 secret[0] = '\0';
02081 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
02082
02083 while((s = strchr(secret, ';'))) *s = '+';
02084 while((s = strchr(secret, '/'))) *s = '+';
02085 while((s = strchr(secret, ':'))) *s = '+';
02086 while((s = strchr(secret, '@'))) *s = '+';
02087 }
02088
02089
02090 static void save_secret(const char *newkey, const char *oldkey)
02091 {
02092 char tmp[256];
02093 if (oldkey)
02094 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
02095 else
02096 snprintf(tmp, sizeof(tmp), "%s", newkey);
02097 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
02098 ast_db_put(secretpath, "secret", tmp);
02099 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
02100 ast_db_put(secretpath, "secretexpiry", tmp);
02101 }
02102
02103 static void load_password(void)
02104 {
02105 char *current=NULL;
02106 char *last=NULL;
02107 char tmp[256];
02108 time_t expired;
02109
02110 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
02111 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
02112 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
02113 current = strchr(tmp, ';');
02114 if (!current)
02115 current = tmp;
02116 else {
02117 *current = '\0';
02118 current++;
02119 };
02120 if ((time(NULL) - expired) < 0) {
02121 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
02122 expired = time(NULL) + DUNDI_SECRET_TIME;
02123 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
02124 last = current;
02125 current = NULL;
02126 } else {
02127 last = NULL;
02128 current = NULL;
02129 }
02130 }
02131 if (current) {
02132
02133 ast_copy_string(cursecret, current, sizeof(cursecret));
02134 rotatetime = expired;
02135 } else {
02136
02137 build_secret(cursecret, sizeof(cursecret));
02138 save_secret(cursecret, last);
02139 }
02140 }
02141
02142 static void check_password(void)
02143 {
02144 char oldsecret[80];
02145 time_t now;
02146
02147 time(&now);
02148 #if 0
02149 printf("%ld/%ld\n", now, rotatetime);
02150 #endif
02151 if ((now - rotatetime) >= 0) {
02152
02153 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
02154 build_secret(cursecret, sizeof(cursecret));
02155 save_secret(cursecret, oldsecret);
02156 }
02157 }
02158
02159 static void *network_thread(void *ignore)
02160 {
02161
02162
02163 int res;
02164
02165 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
02166
02167 while (!dundi_shutdown) {
02168 res = ast_sched_wait(sched);
02169 if ((res > 1000) || (res < 0))
02170 res = 1000;
02171 res = ast_io_wait(io, res);
02172 if (res >= 0) {
02173 AST_LIST_LOCK(&peers);
02174 ast_sched_runq(sched);
02175 AST_LIST_UNLOCK(&peers);
02176 }
02177 check_password();
02178 }
02179
02180 netthreadid = AST_PTHREADT_NULL;
02181
02182 return NULL;
02183 }
02184
02185 static void *process_clearcache(void *ignore)
02186 {
02187 struct ast_db_entry *db_entry, *db_tree;
02188 int striplen = sizeof("/dundi/cache");
02189 time_t now;
02190
02191 while (!dundi_shutdown) {
02192 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
02193
02194 time(&now);
02195
02196 db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
02197 for (; db_entry; db_entry = db_entry->next) {
02198 time_t expiry;
02199
02200 if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
02201 if (expiry < now) {
02202 ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
02203 ast_db_del("dundi/cache", db_entry->key + striplen);
02204 }
02205 }
02206 }
02207 ast_db_freetree(db_tree);
02208
02209 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
02210 pthread_testcancel();
02211 sleep(60);
02212 pthread_testcancel();
02213 }
02214
02215 clearcachethreadid = AST_PTHREADT_NULL;
02216 return NULL;
02217 }
02218
02219 static void *process_precache(void *ign)
02220 {
02221 struct dundi_precache_queue *qe;
02222 time_t now;
02223 char context[256];
02224 char number[256];
02225 int run;
02226
02227 while (!dundi_shutdown) {
02228 time(&now);
02229 run = 0;
02230 AST_LIST_LOCK(&pcq);
02231 if ((qe = AST_LIST_FIRST(&pcq))) {
02232 if (!qe->expiration) {
02233
02234 AST_LIST_REMOVE_HEAD(&pcq, list);
02235 ast_free(qe);
02236 } else if (qe->expiration < now) {
02237
02238 qe->expiration = 0;
02239 ast_copy_string(context, qe->context, sizeof(context));
02240 ast_copy_string(number, qe->number, sizeof(number));
02241 run = 1;
02242 }
02243 }
02244 AST_LIST_UNLOCK(&pcq);
02245 if (run) {
02246 dundi_precache(context, number);
02247 } else
02248 sleep(1);
02249 }
02250
02251 precachethreadid = AST_PTHREADT_NULL;
02252
02253 return NULL;
02254 }
02255
02256 static int start_network_thread(void)
02257 {
02258 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
02259 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
02260 ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
02261 return 0;
02262 }
02263
02264 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02265 {
02266 switch (cmd) {
02267 case CLI_INIT:
02268 e->command = "dundi set debug {on|off}";
02269 e->usage =
02270 "Usage: dundi set debug {on|off}\n"
02271 " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
02272 return NULL;
02273 case CLI_GENERATE:
02274 return NULL;
02275 }
02276
02277 if (a->argc != e->args)
02278 return CLI_SHOWUSAGE;
02279
02280 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
02281 dundidebug = 1;
02282 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
02283 } else {
02284 dundidebug = 0;
02285 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
02286 }
02287 return CLI_SUCCESS;
02288 }
02289
02290 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02291 {
02292 switch (cmd) {
02293 case CLI_INIT:
02294 e->command = "dundi store history {on|off}";
02295 e->usage =
02296 "Usage: dundi store history {on|off}\n"
02297 " Enables/Disables storing of DUNDi requests and times for debugging\n"
02298 "purposes\n";
02299 return NULL;
02300 case CLI_GENERATE:
02301 return NULL;
02302 }
02303
02304 if (a->argc != e->args)
02305 return CLI_SHOWUSAGE;
02306
02307 if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
02308 global_storehistory = 1;
02309 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
02310 } else {
02311 global_storehistory = 0;
02312 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
02313 }
02314 return CLI_SUCCESS;
02315 }
02316
02317 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02318 {
02319 int stats = 0;
02320 switch (cmd) {
02321 case CLI_INIT:
02322 e->command = "dundi flush [stats]";
02323 e->usage =
02324 "Usage: dundi flush [stats]\n"
02325 " Flushes DUNDi answer cache, used primarily for debug. If\n"
02326 "'stats' is present, clears timer statistics instead of normal\n"
02327 "operation.\n";
02328 return NULL;
02329 case CLI_GENERATE:
02330 return NULL;
02331 }
02332 if ((a->argc < 2) || (a->argc > 3))
02333 return CLI_SHOWUSAGE;
02334 if (a->argc > 2) {
02335 if (!strcasecmp(a->argv[2], "stats"))
02336 stats = 1;
02337 else
02338 return CLI_SHOWUSAGE;
02339 }
02340 if (stats) {
02341
02342 struct dundi_peer *p;
02343 int x;
02344 AST_LIST_LOCK(&peers);
02345 AST_LIST_TRAVERSE(&peers, p, list) {
02346 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02347 if (p->lookups[x])
02348 ast_free(p->lookups[x]);
02349 p->lookups[x] = NULL;
02350 p->lookuptimes[x] = 0;
02351 }
02352 p->avgms = 0;
02353 }
02354 AST_LIST_UNLOCK(&peers);
02355 } else {
02356 ast_db_deltree("dundi/cache", NULL);
02357 ast_cli(a->fd, "DUNDi Cache Flushed\n");
02358 }
02359 return CLI_SUCCESS;
02360 }
02361
02362 static char *model2str(int model)
02363 {
02364 switch(model) {
02365 case DUNDI_MODEL_INBOUND:
02366 return "Inbound";
02367 case DUNDI_MODEL_OUTBOUND:
02368 return "Outbound";
02369 case DUNDI_MODEL_SYMMETRIC:
02370 return "Symmetric";
02371 default:
02372 return "Unknown";
02373 }
02374 }
02375
02376 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
02377 {
02378 int which=0, len;
02379 char *ret = NULL;
02380 struct dundi_peer *p;
02381 char eid_str[20];
02382
02383 if (pos != rpos)
02384 return NULL;
02385 AST_LIST_LOCK(&peers);
02386 len = strlen(word);
02387 AST_LIST_TRAVERSE(&peers, p, list) {
02388 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
02389 if (!strncasecmp(word, s, len) && ++which > state) {
02390 ret = ast_strdup(s);
02391 break;
02392 }
02393 }
02394 AST_LIST_UNLOCK(&peers);
02395 return ret;
02396 }
02397
02398 static int rescomp(const void *a, const void *b)
02399 {
02400 const struct dundi_result *resa, *resb;
02401 resa = a;
02402 resb = b;
02403 if (resa->weight < resb->weight)
02404 return -1;
02405 if (resa->weight > resb->weight)
02406 return 1;
02407 return 0;
02408 }
02409
02410 static void sort_results(struct dundi_result *results, int count)
02411 {
02412 qsort(results, count, sizeof(results[0]), rescomp);
02413 }
02414
02415 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02416 {
02417 int res;
02418 char tmp[256];
02419 char fs[80] = "";
02420 char *context;
02421 int x;
02422 int bypass = 0;
02423 struct dundi_result dr[MAX_RESULTS];
02424 struct timeval start;
02425 switch (cmd) {
02426 case CLI_INIT:
02427 e->command = "dundi lookup";
02428 e->usage =
02429 "Usage: dundi lookup <number>[@context] [bypass]\n"
02430 " Lookup the given number within the given DUNDi context\n"
02431 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
02432 "keyword is specified.\n";
02433 return NULL;
02434 case CLI_GENERATE:
02435 return NULL;
02436 }
02437
02438 if ((a->argc < 3) || (a->argc > 4))
02439 return CLI_SHOWUSAGE;
02440 if (a->argc > 3) {
02441 if (!strcasecmp(a->argv[3], "bypass"))
02442 bypass=1;
02443 else
02444 return CLI_SHOWUSAGE;
02445 }
02446 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02447 context = strchr(tmp, '@');
02448 if (context) {
02449 *context = '\0';
02450 context++;
02451 }
02452 start = ast_tvnow();
02453 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
02454
02455 if (res < 0)
02456 ast_cli(a->fd, "DUNDi lookup returned error.\n");
02457 else if (!res)
02458 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
02459 else
02460 sort_results(dr, res);
02461 for (x=0;x<res;x++) {
02462 ast_cli(a->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));
02463 ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
02464 }
02465 ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02466 return CLI_SUCCESS;
02467 }
02468
02469 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02470 {
02471 int res;
02472 char tmp[256];
02473 char *context;
02474 struct timeval start;
02475 switch (cmd) {
02476 case CLI_INIT:
02477 e->command = "dundi precache";
02478 e->usage =
02479 "Usage: dundi precache <number>[@context]\n"
02480 " Lookup the given number within the given DUNDi context\n"
02481 "(or e164 if none is specified) and precaches the results to any\n"
02482 "upstream DUNDi push servers.\n";
02483 return NULL;
02484 case CLI_GENERATE:
02485 return NULL;
02486 }
02487 if ((a->argc < 3) || (a->argc > 3))
02488 return CLI_SHOWUSAGE;
02489 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02490 context = strchr(tmp, '@');
02491 if (context) {
02492 *context = '\0';
02493 context++;
02494 }
02495 start = ast_tvnow();
02496 res = dundi_precache(context, tmp);
02497
02498 if (res < 0)
02499 ast_cli(a->fd, "DUNDi precache returned error.\n");
02500 else if (!res)
02501 ast_cli(a->fd, "DUNDi precache returned no error.\n");
02502 ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02503 return CLI_SUCCESS;
02504 }
02505
02506 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02507 {
02508 int res;
02509 char tmp[256];
02510 char *context;
02511 dundi_eid eid;
02512 struct dundi_entity_info dei;
02513 switch (cmd) {
02514 case CLI_INIT:
02515 e->command = "dundi query";
02516 e->usage =
02517 "Usage: dundi query <entity>[@context]\n"
02518 " Attempts to retrieve contact information for a specific\n"
02519 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
02520 "e164 if none is specified).\n";
02521 return NULL;
02522 case CLI_GENERATE:
02523 return NULL;
02524 }
02525 if ((a->argc < 3) || (a->argc > 3))
02526 return CLI_SHOWUSAGE;
02527 if (ast_str_to_eid(&eid, a->argv[2])) {
02528 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
02529 return CLI_SHOWUSAGE;
02530 }
02531 ast_copy_string(tmp, a->argv[2], sizeof(tmp));
02532 context = strchr(tmp, '@');
02533 if (context) {
02534 *context = '\0';
02535 context++;
02536 }
02537 res = dundi_query_eid(&dei, context, eid);
02538 if (res < 0)
02539 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
02540 else if (!res)
02541 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
02542 else {
02543 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
02544 ast_cli(a->fd, "Department: %s\n", dei.orgunit);
02545 ast_cli(a->fd, "Organization: %s\n", dei.org);
02546 ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
02547 ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
02548 ast_cli(a->fd, "Country: %s\n", dei.country);
02549 ast_cli(a->fd, "E-mail: %s\n", dei.email);
02550 ast_cli(a->fd, "Phone: %s\n", dei.phone);
02551 ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
02552 }
02553 return CLI_SUCCESS;
02554 }
02555
02556 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02557 {
02558 struct dundi_peer *peer;
02559 struct permission *p;
02560 char *order;
02561 char eid_str[20];
02562 int x, cnt;
02563 switch (cmd) {
02564 case CLI_INIT:
02565 e->command = "dundi show peer";
02566 e->usage =
02567 "Usage: dundi show peer [peer]\n"
02568 " Provide a detailed description of a specifid DUNDi peer.\n";
02569 return NULL;
02570 case CLI_GENERATE:
02571 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
02572 }
02573 if (a->argc != 4)
02574 return CLI_SHOWUSAGE;
02575 AST_LIST_LOCK(&peers);
02576 AST_LIST_TRAVERSE(&peers, peer, list) {
02577 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
02578 break;
02579 }
02580 if (peer) {
02581 switch(peer->order) {
02582 case 0:
02583 order = "Primary";
02584 break;
02585 case 1:
02586 order = "Secondary";
02587 break;
02588 case 2:
02589 order = "Tertiary";
02590 break;
02591 case 3:
02592 order = "Quartiary";
02593 break;
02594 default:
02595 order = "Unknown";
02596 }
02597 ast_cli(a->fd, "Peer: %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02598 ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
02599 ast_cli(a->fd, "Order: %s\n", order);
02600 ast_cli(a->fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
02601 ast_cli(a->fd, "Port: %d\n", ntohs(peer->addr.sin_port));
02602 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
02603 ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
02604 ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
02605 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
02606 if (!AST_LIST_EMPTY(&peer->include))
02607 ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
02608 AST_LIST_TRAVERSE(&peer->include, p, list)
02609 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
02610 if (!AST_LIST_EMPTY(&peer->permit))
02611 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
02612 AST_LIST_TRAVERSE(&peer->permit, p, list)
02613 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
02614 cnt = 0;
02615 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02616 if (peer->lookups[x]) {
02617 if (!cnt)
02618 ast_cli(a->fd, "Last few query times:\n");
02619 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
02620 cnt++;
02621 }
02622 }
02623 if (cnt)
02624 ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
02625 } else
02626 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
02627 AST_LIST_UNLOCK(&peers);
02628 return CLI_SUCCESS;
02629 }
02630
02631 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02632 {
02633 #define FORMAT2 "%-20.20s %-15.15s %-6.6s %-10.10s %-8.8s %-15.15s\n"
02634 #define FORMAT "%-20.20s %-15.15s %s %-6d %-10.10s %-8.8s %-15.15s\n"
02635 struct dundi_peer *peer;
02636 int registeredonly=0;
02637 char avgms[20];
02638 char eid_str[20];
02639 int online_peers = 0;
02640 int offline_peers = 0;
02641 int unmonitored_peers = 0;
02642 int total_peers = 0;
02643 switch (cmd) {
02644 case CLI_INIT:
02645 e->command = "dundi show peers [registered|include|exclude|begin]";
02646 e->usage =
02647 "Usage: dundi show peers [registered|include|exclude|begin]\n"
02648 " Lists all known DUNDi peers.\n"
02649 " If 'registered' is present, only registered peers are shown.\n";
02650 return NULL;
02651 case CLI_GENERATE:
02652 return NULL;
02653 }
02654
02655 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5))
02656 return CLI_SHOWUSAGE;
02657 if ((a->argc == 4)) {
02658 if (!strcasecmp(a->argv[3], "registered")) {
02659 registeredonly = 1;
02660 } else
02661 return CLI_SHOWUSAGE;
02662 }
02663 AST_LIST_LOCK(&peers);
02664 ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
02665 AST_LIST_TRAVERSE(&peers, peer, list) {
02666 char status[20];
02667 int print_line = -1;
02668 char srch[2000];
02669 total_peers++;
02670 if (registeredonly && !peer->addr.sin_addr.s_addr)
02671 continue;
02672 if (peer->maxms) {
02673 if (peer->lastms < 0) {
02674 strcpy(status, "UNREACHABLE");
02675 offline_peers++;
02676 }
02677 else if (peer->lastms > peer->maxms) {
02678 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
02679 offline_peers++;
02680 }
02681 else if (peer->lastms) {
02682 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
02683 online_peers++;
02684 }
02685 else {
02686 strcpy(status, "UNKNOWN");
02687 offline_peers++;
02688 }
02689 } else {
02690 strcpy(status, "Unmonitored");
02691 unmonitored_peers++;
02692 }
02693 if (peer->avgms)
02694 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
02695 else
02696 strcpy(avgms, "Unavail");
02697 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
02698 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02699 peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
02700
02701 if (a->argc == 5) {
02702 if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
02703 print_line = -1;
02704 } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
02705 print_line = 1;
02706 } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
02707 print_line = -1;
02708 } else {
02709 print_line = 0;
02710 }
02711 }
02712
02713 if (print_line) {
02714 ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
02715 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02716 peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
02717 }
02718 }
02719 ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
02720 AST_LIST_UNLOCK(&peers);
02721 return CLI_SUCCESS;
02722 #undef FORMAT
02723 #undef FORMAT2
02724 }
02725
02726 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02727 {
02728 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
02729 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
02730 struct dundi_transaction *trans;
02731 switch (cmd) {
02732 case CLI_INIT:
02733 e->command = "dundi show trans";
02734 e->usage =
02735 "Usage: dundi show trans\n"
02736 " Lists all known DUNDi transactions.\n";
02737 return NULL;
02738 case CLI_GENERATE:
02739 return NULL;
02740 }
02741 if (a->argc != 3)
02742 return CLI_SHOWUSAGE;
02743 AST_LIST_LOCK(&peers);
02744 ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
02745 AST_LIST_TRAVERSE(&alltrans, trans, all) {
02746 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
02747 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
02748 }
02749 AST_LIST_UNLOCK(&peers);
02750 return CLI_SUCCESS;
02751 #undef FORMAT
02752 #undef FORMAT2
02753 }
02754
02755 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02756 {
02757 char eid_str[20];
02758 switch (cmd) {
02759 case CLI_INIT:
02760 e->command = "dundi show entityid";
02761 e->usage =
02762 "Usage: dundi show entityid\n"
02763 " Displays the global entityid for this host.\n";
02764 return NULL;
02765 case CLI_GENERATE:
02766 return NULL;
02767 }
02768 if (a->argc != 3)
02769 return CLI_SHOWUSAGE;
02770 AST_LIST_LOCK(&peers);
02771 ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
02772 AST_LIST_UNLOCK(&peers);
02773 ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
02774 return CLI_SUCCESS;
02775 }
02776
02777 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02778 {
02779 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
02780 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
02781 struct dundi_request *req;
02782 char eidstr[20];
02783 switch (cmd) {
02784 case CLI_INIT:
02785 e->command = "dundi show requests";
02786 e->usage =
02787 "Usage: dundi show requests\n"
02788 " Lists all known pending DUNDi requests.\n";
02789 return NULL;
02790 case CLI_GENERATE:
02791 return NULL;
02792 }
02793 if (a->argc != 3)
02794 return CLI_SHOWUSAGE;
02795 AST_LIST_LOCK(&peers);
02796 ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
02797 AST_LIST_TRAVERSE(&requests, req, list) {
02798 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
02799 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
02800 }
02801 AST_LIST_UNLOCK(&peers);
02802 return CLI_SUCCESS;
02803 #undef FORMAT
02804 #undef FORMAT2
02805 }
02806
02807
02808
02809 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02810 {
02811 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02812 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02813 struct dundi_mapping *map;
02814 char fs[256];
02815 char weight[8];
02816 switch (cmd) {
02817 case CLI_INIT:
02818 e->command = "dundi show mappings";
02819 e->usage =
02820 "Usage: dundi show mappings\n"
02821 " Lists all known DUNDi mappings.\n";
02822 return NULL;
02823 case CLI_GENERATE:
02824 return NULL;
02825 }
02826 if (a->argc != 3)
02827 return CLI_SHOWUSAGE;
02828 AST_LIST_LOCK(&peers);
02829 ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
02830 AST_LIST_TRAVERSE(&mappings, map, list) {
02831 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map));
02832 ast_cli(a->fd, FORMAT, map->dcontext, weight,
02833 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
02834 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
02835 }
02836 AST_LIST_UNLOCK(&peers);
02837 return CLI_SUCCESS;
02838 #undef FORMAT
02839 #undef FORMAT2
02840 }
02841
02842 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02843 {
02844 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
02845 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
02846 struct dundi_precache_queue *qe;
02847 int h,m,s;
02848 time_t now;
02849 switch (cmd) {
02850 case CLI_INIT:
02851 e->command = "dundi show precache";
02852 e->usage =
02853 "Usage: dundi show precache\n"
02854 " Lists all known DUNDi scheduled precache updates.\n";
02855 return NULL;
02856 case CLI_GENERATE:
02857 return NULL;
02858 }
02859 if (a->argc != 3)
02860 return CLI_SHOWUSAGE;
02861 time(&now);
02862 ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
02863 AST_LIST_LOCK(&pcq);
02864 AST_LIST_TRAVERSE(&pcq, qe, list) {
02865 s = qe->expiration - now;
02866 h = s / 3600;
02867 s = s % 3600;
02868 m = s / 60;
02869 s = s % 60;
02870 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
02871 }
02872 AST_LIST_UNLOCK(&pcq);
02873
02874 return CLI_SUCCESS;
02875 #undef FORMAT
02876 #undef FORMAT2
02877 }
02878
02879 static struct ast_cli_entry cli_dundi[] = {
02880 AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
02881 AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
02882 AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
02883 AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
02884 AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
02885 AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
02886 AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
02887 AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
02888 AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
02889 AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
02890 AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
02891 AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
02892 AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
02893 };
02894
02895 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
02896 {
02897 struct dundi_transaction *trans;
02898 int tid;
02899
02900
02901 if (p && !p->addr.sin_addr.s_addr)
02902 return NULL;
02903 tid = get_trans_id();
02904 if (tid < 1)
02905 return NULL;
02906 if (!(trans = ast_calloc(1, sizeof(*trans))))
02907 return NULL;
02908
02909 if (global_storehistory) {
02910 trans->start = ast_tvnow();
02911 ast_set_flag(trans, FLAG_STOREHIST);
02912 }
02913 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
02914 trans->autokillid = -1;
02915 if (p) {
02916 apply_peer(trans, p);
02917 if (!p->sentfullkey)
02918 ast_set_flag(trans, FLAG_SENDFULLKEY);
02919 }
02920 trans->strans = tid;
02921 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
02922
02923 return trans;
02924 }
02925
02926 static int dundi_xmit(struct dundi_packet *pack)
02927 {
02928 int res;
02929 if (dundidebug)
02930 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
02931 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
02932 if (res < 0) {
02933 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
02934 ast_inet_ntoa(pack->parent->addr.sin_addr),
02935 ntohs(pack->parent->addr.sin_port), strerror(errno));
02936 }
02937 if (res > 0)
02938 res = 0;
02939 return res;
02940 }
02941
02942 static void destroy_packet(struct dundi_packet *pack, int needfree)
02943 {
02944 if (pack->parent)
02945 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
02946 AST_SCHED_DEL(sched, pack->retransid);
02947 if (needfree)
02948 ast_free(pack);
02949 }
02950
02951 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
02952 {
02953 struct dundi_peer *peer;
02954 int ms;
02955 int x;
02956 int cnt;
02957 char eid_str[20];
02958 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
02959 AST_LIST_TRAVERSE(&peers, peer, list) {
02960 if (peer->regtrans == trans)
02961 peer->regtrans = NULL;
02962 if (peer->qualtrans == trans) {
02963 if (fromtimeout) {
02964 if (peer->lastms > -1)
02965 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02966 peer->lastms = -1;
02967 } else {
02968 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
02969 if (ms < 1)
02970 ms = 1;
02971 if (ms < peer->maxms) {
02972 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
02973 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02974 } else if (peer->lastms < peer->maxms) {
02975 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
02976 }
02977 peer->lastms = ms;
02978 }
02979 peer->qualtrans = NULL;
02980 }
02981 if (ast_test_flag(trans, FLAG_STOREHIST)) {
02982 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
02983 if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
02984 peer->avgms = 0;
02985 cnt = 0;
02986 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
02987 ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
02988 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
02989 peer->lookuptimes[x] = peer->lookuptimes[x-1];
02990 peer->lookups[x] = peer->lookups[x-1];
02991 if (peer->lookups[x]) {
02992 peer->avgms += peer->lookuptimes[x];
02993 cnt++;
02994 }
02995 }
02996 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
02997 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
02998 if (peer->lookups[0]) {
02999 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
03000 peer->avgms += peer->lookuptimes[0];
03001 cnt++;
03002 }
03003 if (cnt)
03004 peer->avgms /= cnt;
03005 }
03006 }
03007 }
03008 }
03009 }
03010 if (trans->parent) {
03011
03012 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
03013 if (AST_LIST_EMPTY(&trans->parent->trans)) {
03014
03015 if (trans->parent->pfds[1] > -1) {
03016 if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
03017 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
03018 }
03019 }
03020 }
03021 }
03022
03023 AST_LIST_REMOVE(&alltrans, trans, all);
03024 destroy_packets(&trans->packets);
03025 destroy_packets(&trans->lasttrans);
03026 AST_SCHED_DEL(sched, trans->autokillid);
03027 if (trans->thread) {
03028
03029 ast_set_flag(trans, FLAG_DEAD);
03030 } else
03031 ast_free(trans);
03032 }
03033
03034 static int dundi_rexmit(const void *data)
03035 {
03036 struct dundi_packet *pack = (struct dundi_packet *)data;
03037 int res;
03038 AST_LIST_LOCK(&peers);
03039 if (pack->retrans < 1) {
03040 pack->retransid = -1;
03041 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
03042 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
03043 ast_inet_ntoa(pack->parent->addr.sin_addr),
03044 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
03045 destroy_trans(pack->parent, 1);
03046 res = 0;
03047 } else {
03048
03049 pack->retrans--;
03050 dundi_xmit(pack);
03051 res = 1;
03052 }
03053 AST_LIST_UNLOCK(&peers);
03054 return res;
03055 }
03056
03057 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
03058 {
03059 struct dundi_packet *pack;
03060 int res;
03061 int len;
03062 char eid_str[20];
03063 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
03064
03065 if (ast_test_flag(trans, FLAG_ENCRYPT))
03066 len += 384;
03067 pack = ast_calloc(1, len);
03068 if (pack) {
03069 pack->h = (struct dundi_hdr *)(pack->data);
03070 if (cmdresp != DUNDI_COMMAND_ACK) {
03071 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
03072 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
03073 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
03074 }
03075 pack->parent = trans;
03076 pack->h->strans = htons(trans->strans);
03077 pack->h->dtrans = htons(trans->dtrans);
03078 pack->h->iseqno = trans->iseqno;
03079 pack->h->oseqno = trans->oseqno;
03080 pack->h->cmdresp = cmdresp;
03081 pack->datalen = sizeof(struct dundi_hdr);
03082 if (ied) {
03083 memcpy(pack->h->ies, ied->buf, ied->pos);
03084 pack->datalen += ied->pos;
03085 }
03086 if (final) {
03087 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
03088 ast_set_flag(trans, FLAG_FINAL);
03089 }
03090 pack->h->cmdflags = flags;
03091 if (cmdresp != DUNDI_COMMAND_ACK) {
03092 trans->oseqno++;
03093 trans->oseqno = trans->oseqno % 256;
03094 }
03095 trans->aseqno = trans->iseqno;
03096
03097 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
03098 switch(cmdresp) {
03099 case DUNDI_COMMAND_REGREQ:
03100 case DUNDI_COMMAND_REGRESPONSE:
03101 case DUNDI_COMMAND_DPDISCOVER:
03102 case DUNDI_COMMAND_DPRESPONSE:
03103 case DUNDI_COMMAND_EIDQUERY:
03104 case DUNDI_COMMAND_EIDRESPONSE:
03105 case DUNDI_COMMAND_PRECACHERQ:
03106 case DUNDI_COMMAND_PRECACHERP:
03107 if (dundidebug)
03108 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
03109 res = dundi_encrypt(trans, pack);
03110 break;
03111 default:
03112 res = 0;
03113 }
03114 } else
03115 res = 0;
03116 if (!res)
03117 res = dundi_xmit(pack);
03118 if (res)
03119 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03120
03121 if (cmdresp == DUNDI_COMMAND_ACK)
03122 ast_free(pack);
03123 return res;
03124 }
03125 return -1;
03126 }
03127
03128 static int do_autokill(const void *data)
03129 {
03130 struct dundi_transaction *trans = (struct dundi_transaction *)data;
03131 char eid_str[20];
03132 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
03133 ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03134 trans->autokillid = -1;
03135 destroy_trans(trans, 0);
03136 return 0;
03137 }
03138
03139 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
03140 {
03141 struct dundi_peer *p;
03142 if (!ast_eid_cmp(eid, us)) {
03143 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03144 return;
03145 }
03146 AST_LIST_LOCK(&peers);
03147 AST_LIST_TRAVERSE(&peers, p, list) {
03148 if (!ast_eid_cmp(&p->eid, eid)) {
03149 if (has_permission(&p->include, context))
03150 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03151 else
03152 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03153 break;
03154 }
03155 }
03156 if (!p)
03157 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03158 AST_LIST_UNLOCK(&peers);
03159 }
03160
03161 static int dundi_discover(struct dundi_transaction *trans)
03162 {
03163 struct dundi_ie_data ied;
03164 int x;
03165 if (!trans->parent) {
03166 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03167 return -1;
03168 }
03169 memset(&ied, 0, sizeof(ied));
03170 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03171 if (!dundi_eid_zero(&trans->us_eid))
03172 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
03173 for (x=0;x<trans->eidcount;x++)
03174 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
03175 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03176 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03177 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03178 if (trans->parent->cbypass)
03179 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
03180 if (trans->autokilltimeout)
03181 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03182 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
03183 }
03184
03185 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
03186 {
03187 struct dundi_ie_data ied;
03188 int x, res;
03189 int max = 999999;
03190 int expiration = dundi_cache_time;
03191 int ouranswers=0;
03192 dundi_eid *avoid[1] = { NULL, };
03193 int direct[1] = { 0, };
03194 struct dundi_result dr[MAX_RESULTS];
03195 struct dundi_hint_metadata hmd;
03196 if (!trans->parent) {
03197 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03198 return -1;
03199 }
03200 memset(&hmd, 0, sizeof(hmd));
03201 memset(&dr, 0, sizeof(dr));
03202
03203 for (x=0;x<mapcount;x++)
03204 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
03205 if (ouranswers < 0)
03206 ouranswers = 0;
03207 for (x=0;x<ouranswers;x++) {
03208 if (dr[x].weight < max)
03209 max = dr[x].weight;
03210 }
03211 if (max) {
03212
03213 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);
03214 if (res > 0) {
03215
03216 ouranswers += res;
03217 }
03218 }
03219
03220 if (ouranswers > 0) {
03221 *foundanswers += ouranswers;
03222 memset(&ied, 0, sizeof(ied));
03223 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03224 if (!dundi_eid_zero(&trans->us_eid))
03225 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03226 for (x=0;x<trans->eidcount;x++)
03227 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03228 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03229 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03230 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03231 for (x=0;x<ouranswers;x++) {
03232
03233 if (dr[x].expiration && (expiration > dr[x].expiration))
03234 expiration = dr[x].expiration;
03235 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
03236 }
03237 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
03238 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
03239 if (trans->autokilltimeout)
03240 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03241 if (expiration < *minexp)
03242 *minexp = expiration;
03243 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
03244 } else {
03245
03246 destroy_trans(trans, 0);
03247 return 0;
03248 }
03249 }
03250
03251 static int dundi_query(struct dundi_transaction *trans)
03252 {
03253 struct dundi_ie_data ied;
03254 int x;
03255 if (!trans->parent) {
03256 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
03257 return -1;
03258 }
03259 memset(&ied, 0, sizeof(ied));
03260 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03261 if (!dundi_eid_zero(&trans->us_eid))
03262 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03263 for (x=0;x<trans->eidcount;x++)
03264 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03265 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
03266 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03267 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03268 if (trans->autokilltimeout)
03269 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03270 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
03271 }
03272
03273 static int discover_transactions(struct dundi_request *dr)
03274 {
03275 struct dundi_transaction *trans;
03276 AST_LIST_LOCK(&peers);
03277 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03278 dundi_discover(trans);
03279 }
03280 AST_LIST_UNLOCK(&peers);
03281 return 0;
03282 }
03283
03284 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
03285 {
03286 struct dundi_transaction *trans;
03287
03288
03289 AST_LIST_LOCK(&peers);
03290 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03291 if (trans->thread)
03292 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
03293 trans->thread = 1;
03294 }
03295 AST_LIST_UNLOCK(&peers);
03296
03297 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03298 if (!ast_test_flag(trans, FLAG_DEAD))
03299 precache_trans(trans, maps, mapcount, expiration, foundanswers);
03300 }
03301
03302
03303 AST_LIST_LOCK(&peers);
03304 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
03305 trans->thread = 0;
03306 if (ast_test_flag(trans, FLAG_DEAD)) {
03307 ast_debug(1, "Our transaction went away!\n");
03308
03309
03310 destroy_trans(trans, 0);
03311 }
03312 }
03313 AST_LIST_TRAVERSE_SAFE_END
03314 AST_LIST_UNLOCK(&peers);
03315
03316 return 0;
03317 }
03318
03319 static int query_transactions(struct dundi_request *dr)
03320 {
03321 struct dundi_transaction *trans;
03322
03323 AST_LIST_LOCK(&peers);
03324 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03325 dundi_query(trans);
03326 }
03327 AST_LIST_UNLOCK(&peers);
03328
03329 return 0;
03330 }
03331
03332 static int optimize_transactions(struct dundi_request *dr, int order)
03333 {
03334
03335
03336 struct dundi_transaction *trans;
03337 struct dundi_peer *peer;
03338 dundi_eid tmp;
03339 int x;
03340 int needpush;
03341
03342 AST_LIST_LOCK(&peers);
03343 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03344
03345 if (trans->eidcount) {
03346 tmp = trans->eids[--trans->eidcount];
03347 needpush = 1;
03348 } else {
03349 tmp = trans->us_eid;
03350 needpush = 0;
03351 }
03352
03353 AST_LIST_TRAVERSE(&peers, peer, list) {
03354 if (ast_eid_cmp(&peer->eid, &empty_eid) &&
03355 (peer->lastms > -1) &&
03356 has_permission(&peer->include, dr->dcontext) &&
03357 ast_eid_cmp(&peer->eid, &trans->them_eid) &&
03358 (peer->order <= order)) {
03359
03360
03361
03362 if (!ast_eid_cmp(&tmp, &peer->eid))
03363 x = -1;
03364 else {
03365 for (x=0;x<trans->eidcount;x++) {
03366 if (!ast_eid_cmp(&trans->eids[x], &peer->eid))
03367 break;
03368 }
03369 }
03370 if (x == trans->eidcount) {
03371
03372 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
03373 trans->eids[trans->eidcount++] = peer->eid;
03374
03375
03376 needpush = 1;
03377 }
03378 }
03379 }
03380 }
03381
03382 if (needpush)
03383 trans->eids[trans->eidcount++] = tmp;
03384 }
03385 AST_LIST_UNLOCK(&peers);
03386
03387 return 0;
03388 }
03389
03390 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
03391 {
03392 struct dundi_transaction *trans;
03393 int x;
03394 char eid_str[20];
03395 char eid_str2[20];
03396
03397
03398 if (!p->addr.sin_addr.s_addr)
03399 return 0;
03400 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
03401 return 0;
03402
03403 if (ast_strlen_zero(dr->number))
03404 ast_debug(1, "Will query peer '%s' for '%s' (context '%s')\n", ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid), ast_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
03405 else
03406 ast_debug(1, "Will query peer '%s' for '%s@%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
03407
03408 trans = create_transaction(p);
03409 if (!trans)
03410 return -1;
03411 trans->parent = dr;
03412 trans->ttl = ttl;
03413 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
03414 trans->eids[x] = *avoid[x];
03415 trans->eidcount = x;
03416 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
03417
03418 return 0;
03419 }
03420
03421 static void cancel_request(struct dundi_request *dr)
03422 {
03423 struct dundi_transaction *trans;
03424
03425 AST_LIST_LOCK(&peers);
03426 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
03427
03428 trans->parent = NULL;
03429
03430 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
03431 }
03432 AST_LIST_UNLOCK(&peers);
03433 }
03434
03435 static void abort_request(struct dundi_request *dr)
03436 {
03437 struct dundi_transaction *trans;
03438
03439 AST_LIST_LOCK(&peers);
03440 while ((trans = AST_LIST_FIRST(&dr->trans))) {
03441
03442 destroy_trans(trans, 0);
03443 }
03444 AST_LIST_UNLOCK(&peers);
03445 }
03446
03447 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[])
03448 {
03449 struct dundi_peer *p;
03450 int x;
03451 int res;
03452 int pass;
03453 int allowconnect;
03454 char eid_str[20];
03455 AST_LIST_LOCK(&peers);
03456 AST_LIST_TRAVERSE(&peers, p, list) {
03457 if (modeselect == 1) {
03458
03459 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
03460 allowconnect = 1;
03461 } else {
03462
03463 pass = has_permission(&p->include, dr->dcontext);
03464 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
03465 }
03466 if (skip) {
03467 if (!ast_eid_cmp(skip, &p->eid))
03468 pass = 0;
03469 }
03470 if (pass) {
03471 if (p->order <= order) {
03472
03473
03474
03475 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
03476 res = 0;
03477
03478
03479 for (x=0;avoid[x];x++) {
03480 if (!ast_eid_cmp(avoid[x], &p->eid) || !ast_eid_cmp(avoid[x], &p->us_eid)) {
03481
03482 if (directs && !directs[x])
03483 ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
03484 break;
03485 }
03486 }
03487
03488 if (allowconnect) {
03489 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
03490
03491 append_transaction(dr, p, ttl, avoid);
03492 } else {
03493 ast_debug(1, "Avoiding '%s' in transaction\n", ast_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
03494 }
03495 }
03496 }
03497 *foundcache |= res;
03498 } else if (!*skipped || (p->order < *skipped))
03499 *skipped = p->order;
03500 }
03501 }
03502 AST_LIST_UNLOCK(&peers);
03503 }
03504
03505 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
03506 {
03507 struct dundi_request *cur;
03508 int res=0;
03509 char eid_str[20];
03510 AST_LIST_LOCK(&peers);
03511 AST_LIST_TRAVERSE(&requests, cur, list) {
03512 ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
03513 dr->dcontext, dr->number);
03514 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
03515 !strcasecmp(cur->number, dr->number) &&
03516 (!ast_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
03517 ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08x'\n",
03518 cur->dcontext, cur->number, ast_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
03519 *pending = cur;
03520 res = 1;
03521 break;
03522 }
03523 }
03524 if (!res) {
03525 ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
03526 dr->number, dr->dcontext, ast_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
03527
03528 AST_LIST_INSERT_HEAD(&requests, dr, list);
03529 *pending = NULL;
03530 }
03531 AST_LIST_UNLOCK(&peers);
03532 return res;
03533 }
03534
03535 static void unregister_request(struct dundi_request *dr)
03536 {
03537 AST_LIST_LOCK(&peers);
03538 AST_LIST_REMOVE(&requests, dr, list);
03539 AST_LIST_UNLOCK(&peers);
03540 }
03541
03542 static int check_request(struct dundi_request *dr)
03543 {
03544 struct dundi_request *cur;
03545
03546 AST_LIST_LOCK(&peers);
03547 AST_LIST_TRAVERSE(&requests, cur, list) {
03548 if (cur == dr)
03549 break;
03550 }
03551 AST_LIST_UNLOCK(&peers);
03552
03553 return cur ? 1 : 0;
03554 }
03555
03556 static unsigned long avoid_crc32(dundi_eid *avoid[])
03557 {
03558
03559
03560 uint32_t acrc32 = 0;
03561 int x;
03562 for (x=0;avoid[x];x++) {
03563
03564 if (avoid[x+1]) {
03565 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
03566 }
03567 }
03568 return acrc32;
03569 }
03570
03571 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[])
03572 {
03573 int res;
03574 struct dundi_request dr, *pending;
03575 dundi_eid *rooteid=NULL;
03576 int x;
03577 int ttlms;
03578 int ms;
03579 int foundcache;
03580 int skipped=0;
03581 int order=0;
03582 char eid_str[20];
03583 struct timeval start;
03584
03585
03586 if (chan && ast_check_hangup(chan))
03587 return 0;
03588
03589 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03590
03591 for (x=0;avoid[x];x++)
03592 rooteid = avoid[x];
03593
03594 memset(&dr, 0, sizeof(dr));
03595 if (pipe(dr.pfds)) {
03596 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
03597 return -1;
03598 }
03599 dr.dr = result;
03600 dr.hmd = hmd;
03601 dr.maxcount = maxret;
03602 dr.expiration = *expiration;
03603 dr.cbypass = cbypass;
03604 dr.crc32 = avoid_crc32(avoid);
03605 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03606 ast_copy_string(dr.number, number, sizeof(dr.number));
03607 if (rooteid)
03608 dr.root_eid = *rooteid;
03609 res = register_request(&dr, &pending);
03610 if (res) {
03611
03612 if (rooteid && !ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
03613
03614
03615 ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
03616 dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
03617 close(dr.pfds[0]);
03618 close(dr.pfds[1]);
03619 return -2;
03620 } else {
03621
03622 ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
03623 dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
03624 start = ast_tvnow();
03625 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
03626
03627
03628 usleep(1);
03629 }
03630
03631 }
03632 }
03633
03634 do {
03635 order = skipped;
03636 skipped = 0;
03637 foundcache = 0;
03638 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
03639 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
03640
03641
03642
03643 if (!ttl) {
03644 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03645 abort_request(&dr);
03646 unregister_request(&dr);
03647 close(dr.pfds[0]);
03648 close(dr.pfds[1]);
03649 return 0;
03650 }
03651
03652
03653 optimize_transactions(&dr, order);
03654
03655 discover_transactions(&dr);
03656
03657 start = ast_tvnow();
03658 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
03659 ms = 100;
03660 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03661 }
03662 if (chan && ast_check_hangup(chan))
03663 ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
03664 cancel_request(&dr);
03665 unregister_request(&dr);
03666 res = dr.respcount;
03667 *expiration = dr.expiration;
03668 close(dr.pfds[0]);
03669 close(dr.pfds[1]);
03670 return res;
03671 }
03672
03673 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
03674 {
03675 struct dundi_hint_metadata hmd;
03676 dundi_eid *avoid[1] = { NULL, };
03677 int direct[1] = { 0, };
03678 int expiration = dundi_cache_time;
03679 memset(&hmd, 0, sizeof(hmd));
03680 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
03681 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
03682 }
03683
03684 static void reschedule_precache(const char *number, const char *context, int expiration)
03685 {
03686 int len;
03687 struct dundi_precache_queue *qe, *prev;
03688
03689 AST_LIST_LOCK(&pcq);
03690 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
03691 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
03692 AST_LIST_REMOVE_CURRENT(list);
03693 break;
03694 }
03695 }
03696 AST_LIST_TRAVERSE_SAFE_END;
03697 if (!qe) {
03698 len = sizeof(*qe);
03699 len += strlen(number) + 1;
03700 len += strlen(context) + 1;
03701 if (!(qe = ast_calloc(1, len))) {
03702 AST_LIST_UNLOCK(&pcq);
03703 return;
03704 }
03705 strcpy(qe->number, number);
03706 qe->context = qe->number + strlen(number) + 1;
03707 strcpy(qe->context, context);
03708 }
03709 time(&qe->expiration);
03710 qe->expiration += expiration;
03711 if ((prev = AST_LIST_FIRST(&pcq))) {
03712 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
03713 prev = AST_LIST_NEXT(prev, list);
03714 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
03715 } else
03716 AST_LIST_INSERT_HEAD(&pcq, qe, list);
03717 AST_LIST_UNLOCK(&pcq);
03718 }
03719
03720 static void dundi_precache_full(void)
03721 {
03722 struct dundi_mapping *cur;
03723 struct ast_context *con;
03724 struct ast_exten *e;
03725
03726 AST_LIST_TRAVERSE(&mappings, cur, list) {
03727 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
03728 ast_rdlock_contexts();
03729 con = NULL;
03730 while ((con = ast_walk_contexts(con))) {
03731 if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
03732 continue;
03733
03734 ast_rdlock_context(con);
03735 e = NULL;
03736 while ((e = ast_walk_context_extensions(con, e)))
03737 reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
03738 ast_unlock_context(con);
03739 }
03740 ast_unlock_contexts();
03741 }
03742 }
03743
03744 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
03745 {
03746 struct dundi_request dr;
03747 struct dundi_hint_metadata hmd;
03748 struct dundi_result dr2[MAX_RESULTS];
03749 struct timeval start;
03750 struct dundi_mapping *maps = NULL, *cur;
03751 int nummaps = 0;
03752 int foundanswers;
03753 int foundcache, skipped, ttlms, ms;
03754 if (!context)
03755 context = "e164";
03756 ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
03757
03758 AST_LIST_LOCK(&peers);
03759 AST_LIST_TRAVERSE(&mappings, cur, list) {
03760 if (!strcasecmp(cur->dcontext, context))
03761 nummaps++;
03762 }
03763 if (nummaps) {
03764 maps = ast_alloca(nummaps * sizeof(*maps));
03765 nummaps = 0;
03766 AST_LIST_TRAVERSE(&mappings, cur, list) {
03767 if (!strcasecmp(cur->dcontext, context))
03768 maps[nummaps++] = *cur;
03769 }
03770 }
03771 AST_LIST_UNLOCK(&peers);
03772 if (!nummaps) {
03773 return -1;
03774 }
03775 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03776 memset(&dr2, 0, sizeof(dr2));
03777 memset(&dr, 0, sizeof(dr));
03778 memset(&hmd, 0, sizeof(hmd));
03779 dr.dr = dr2;
03780 ast_copy_string(dr.number, number, sizeof(dr.number));
03781 ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
03782 dr.maxcount = MAX_RESULTS;
03783 dr.expiration = dundi_cache_time;
03784 dr.hmd = &hmd;
03785 dr.pfds[0] = dr.pfds[1] = -1;
03786 if (pipe(dr.pfds) < 0) {
03787 ast_log(LOG_WARNING, "pipe() failed: %s\n", strerror(errno));
03788 return -1;
03789 }
03790 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
03791 optimize_transactions(&dr, 0);
03792 foundanswers = 0;
03793 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
03794 if (foundanswers) {
03795 if (dr.expiration > 0)
03796 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
03797 else
03798 ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
03799 }
03800 start = ast_tvnow();
03801 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
03802 if (dr.pfds[0] > -1) {
03803 ms = 100;
03804 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03805 } else
03806 usleep(1);
03807 }
03808 cancel_request(&dr);
03809 if (dr.pfds[0] > -1) {
03810 close(dr.pfds[0]);
03811 close(dr.pfds[1]);
03812 }
03813 return 0;
03814 }
03815
03816 int dundi_precache(const char *context, const char *number)
03817 {
03818 dundi_eid *avoid[1] = { NULL, };
03819 return dundi_precache_internal(context, number, dundi_ttl, avoid);
03820 }
03821
03822 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[])
03823 {
03824 int res;
03825 struct dundi_request dr;
03826 dundi_eid *rooteid=NULL;
03827 int x;
03828 int ttlms;
03829 int skipped=0;
03830 int foundcache=0;
03831 struct timeval start;
03832
03833 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03834
03835 for (x=0;avoid[x];x++)
03836 rooteid = avoid[x];
03837
03838 memset(&dr, 0, sizeof(dr));
03839 dr.hmd = hmd;
03840 dr.dei = dei;
03841 dr.pfds[0] = dr.pfds[1] = -1;
03842 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03843 memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
03844 if (rooteid)
03845 dr.root_eid = *rooteid;
03846
03847 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
03848
03849
03850
03851
03852 if (!ttl) {
03853 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03854 return 0;
03855 }
03856
03857
03858 optimize_transactions(&dr, 9999);
03859
03860 query_transactions(&dr);
03861
03862 start = ast_tvnow();
03863 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
03864 usleep(1);
03865 res = dr.respcount;
03866 return res;
03867 }
03868
03869 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
03870 {
03871 dundi_eid *avoid[1] = { NULL, };
03872 struct dundi_hint_metadata hmd;
03873 memset(&hmd, 0, sizeof(hmd));
03874 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
03875 }
03876
03877 enum {
03878 OPT_BYPASS_CACHE = (1 << 0),
03879 };
03880
03881 AST_APP_OPTIONS(dundi_query_opts, BEGIN_OPTIONS
03882 AST_APP_OPTION('b', OPT_BYPASS_CACHE),
03883 END_OPTIONS );
03884
03885 static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
03886 {
03887 int results;
03888 int x;
03889 struct dundi_result dr[MAX_RESULTS];
03890 AST_DECLARE_APP_ARGS(args,
03891 AST_APP_ARG(number);
03892 AST_APP_ARG(context);
03893 AST_APP_ARG(options);
03894 );
03895 char *parse;
03896 struct ast_flags opts = { 0, };
03897
03898 buf[0] = '\0';
03899
03900 if (ast_strlen_zero(num)) {
03901 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
03902 return -1;
03903 }
03904
03905 parse = ast_strdupa(num);
03906
03907 AST_STANDARD_APP_ARGS(args, parse);
03908
03909 if (!ast_strlen_zero(args.options)) {
03910 ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
03911 }
03912 if (ast_strlen_zero(args.context)) {
03913 args.context = "e164";
03914 }
03915
03916 results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
03917 if (results > 0) {
03918 sort_results(dr, results);
03919 for (x = 0; x < results; x++) {
03920 if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
03921 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
03922 break;
03923 }
03924 }
03925 }
03926
03927 return 0;
03928 }
03929
03930
03931
03932
03933
03934 static struct ast_custom_function dundi_function = {
03935 .name = "DUNDILOOKUP",
03936 .read = dundifunc_read,
03937 };
03938
03939 static unsigned int dundi_result_id;
03940
03941 struct dundi_result_datastore {
03942 struct dundi_result results[MAX_RESULTS];
03943 unsigned int num_results;
03944 unsigned int id;
03945 };
03946
03947 static void drds_destroy(struct dundi_result_datastore *drds)
03948 {
03949 ast_free(drds);
03950 }
03951
03952 static void drds_destroy_cb(void *data)
03953 {
03954 struct dundi_result_datastore *drds = data;
03955 drds_destroy(drds);
03956 }
03957
03958 static const struct ast_datastore_info dundi_result_datastore_info = {
03959 .type = "DUNDIQUERY",
03960 .destroy = drds_destroy_cb,
03961 };
03962
03963 static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03964 {
03965 AST_DECLARE_APP_ARGS(args,
03966 AST_APP_ARG(number);
03967 AST_APP_ARG(context);
03968 AST_APP_ARG(options);
03969 );
03970 struct ast_flags opts = { 0, };
03971 char *parse;
03972 struct dundi_result_datastore *drds;
03973 struct ast_datastore *datastore;
03974
03975 if (ast_strlen_zero(data)) {
03976 ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
03977 return -1;
03978 }
03979
03980 if (!chan) {
03981 ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
03982 return -1;
03983 }
03984
03985 parse = ast_strdupa(data);
03986
03987 AST_STANDARD_APP_ARGS(args, parse);
03988
03989 if (!ast_strlen_zero(args.options))
03990 ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
03991
03992 if (ast_strlen_zero(args.context))
03993 args.context = "e164";
03994
03995 if (!(drds = ast_calloc(1, sizeof(*drds)))) {
03996 return -1;
03997 }
03998
03999 drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
04000 snprintf(buf, len, "%u", drds->id);
04001
04002 if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
04003 drds_destroy(drds);
04004 return -1;
04005 }
04006
04007 datastore->data = drds;
04008
04009 drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
04010 args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
04011
04012 if (drds->num_results > 0)
04013 sort_results(drds->results, drds->num_results);
04014
04015 ast_channel_lock(chan);
04016 ast_channel_datastore_add(chan, datastore);
04017 ast_channel_unlock(chan);
04018
04019 return 0;
04020 }
04021
04022 static struct ast_custom_function dundi_query_function = {
04023 .name = "DUNDIQUERY",
04024 .read = dundi_query_read,
04025 };
04026
04027 static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
04028 {
04029 AST_DECLARE_APP_ARGS(args,
04030 AST_APP_ARG(id);
04031 AST_APP_ARG(resultnum);
04032 );
04033 char *parse;
04034 unsigned int num;
04035 struct dundi_result_datastore *drds;
04036 struct ast_datastore *datastore;
04037 int res = -1;
04038
04039 if (ast_strlen_zero(data)) {
04040 ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
04041 goto finish;
04042 }
04043
04044 if (!chan) {
04045 ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
04046 goto finish;
04047 }
04048
04049 parse = ast_strdupa(data);
04050
04051 AST_STANDARD_APP_ARGS(args, parse);
04052
04053 if (ast_strlen_zero(args.id)) {
04054 ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
04055 goto finish;
04056 }
04057
04058 if (ast_strlen_zero(args.resultnum)) {
04059 ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
04060 goto finish;
04061 }
04062
04063 ast_channel_lock(chan);
04064 datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id);
04065 ast_channel_unlock(chan);
04066
04067 if (!datastore) {
04068 ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
04069 goto finish;
04070 }
04071
04072 drds = datastore->data;
04073
04074 if (!strcasecmp(args.resultnum, "getnum")) {
04075 snprintf(buf, len, "%u", drds->num_results);
04076 res = 0;
04077 goto finish;
04078 }
04079
04080 if (sscanf(args.resultnum, "%30u", &num) != 1) {
04081 ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
04082 args.resultnum);
04083 goto finish;
04084 }
04085
04086 if (num && num <= drds->num_results) {
04087 snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
04088 res = 0;
04089 } else
04090 ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
04091
04092 finish:
04093 return res;
04094 }
04095
04096 static struct ast_custom_function dundi_result_function = {
04097 .name = "DUNDIRESULT",
04098 .read = dundi_result_read,
04099 };
04100
04101 static void mark_peers(void)
04102 {
04103 struct dundi_peer *peer;
04104 AST_LIST_LOCK(&peers);
04105 AST_LIST_TRAVERSE(&peers, peer, list) {
04106 peer->dead = 1;
04107 }
04108 AST_LIST_UNLOCK(&peers);
04109 }
04110
04111 static void mark_mappings(void)
04112 {
04113 struct dundi_mapping *map;
04114
04115 AST_LIST_LOCK(&peers);
04116 AST_LIST_TRAVERSE(&mappings, map, list) {
04117 map->dead = 1;
04118 }
04119 AST_LIST_UNLOCK(&peers);
04120 }
04121
04122 static void destroy_permissions(struct permissionlist *permlist)
04123 {
04124 struct permission *perm;
04125
04126 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
04127 ast_free(perm);
04128 }
04129
04130 static void destroy_peer(struct dundi_peer *peer)
04131 {
04132 AST_SCHED_DEL(sched, peer->registerid);
04133 if (peer->regtrans)
04134 destroy_trans(peer->regtrans, 0);
04135 AST_SCHED_DEL(sched, peer->qualifyid);
04136 destroy_permissions(&peer->permit);
04137 destroy_permissions(&peer->include);
04138 ast_free(peer);
04139 }
04140
04141 static void destroy_map(struct dundi_mapping *map)
04142 {
04143 if (map->weightstr)
04144 ast_free(map->weightstr);
04145 ast_free(map);
04146 }
04147
04148 static void prune_peers(void)
04149 {
04150 struct dundi_peer *peer;
04151
04152 AST_LIST_LOCK(&peers);
04153 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
04154 if (peer->dead) {
04155 AST_LIST_REMOVE_CURRENT(list);
04156 destroy_peer(peer);
04157 }
04158 }
04159 AST_LIST_TRAVERSE_SAFE_END;
04160 AST_LIST_UNLOCK(&peers);
04161 }
04162
04163 static void prune_mappings(void)
04164 {
04165 struct dundi_mapping *map;
04166
04167 AST_LIST_LOCK(&peers);
04168 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
04169 if (map->dead) {
04170 AST_LIST_REMOVE_CURRENT(list);
04171 destroy_map(map);
04172 }
04173 }
04174 AST_LIST_TRAVERSE_SAFE_END;
04175 AST_LIST_UNLOCK(&peers);
04176 }
04177
04178 static void append_permission(struct permissionlist *permlist, const char *s, int allow)
04179 {
04180 struct permission *perm;
04181
04182 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
04183 return;
04184
04185 strcpy(perm->name, s);
04186 perm->allow = allow;
04187
04188 AST_LIST_INSERT_TAIL(permlist, perm, list);
04189 }
04190
04191 #define MAX_OPTS 128
04192
04193 static void build_mapping(const char *name, const char *value)
04194 {
04195 char *t, *fields[MAX_OPTS];
04196 struct dundi_mapping *map;
04197 int x;
04198 int y;
04199
04200 t = ast_strdupa(value);
04201
04202 AST_LIST_TRAVERSE(&mappings, map, list) {
04203
04204 if (!strcasecmp(map->dcontext, name) &&
04205 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
04206 (!value[strlen(map->lcontext)] ||
04207 (value[strlen(map->lcontext)] == ','))))
04208 break;
04209 }
04210 if (!map) {
04211 if (!(map = ast_calloc(1, sizeof(*map))))
04212 return;
04213 AST_LIST_INSERT_HEAD(&mappings, map, list);
04214 map->dead = 1;
04215 }
04216 map->options = 0;
04217 memset(fields, 0, sizeof(fields));
04218 x = 0;
04219 while (t && x < MAX_OPTS) {
04220 fields[x++] = t;
04221 t = strchr(t, ',');
04222 if (t) {
04223 *t = '\0';
04224 t++;
04225 }
04226 }
04227 if ((x == 1) && ast_strlen_zero(fields[0])) {
04228
04229 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04230 map->dead = 0;
04231 } else if (x >= 4) {
04232 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04233 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
04234 if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
04235 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04236 if ((map->tech = str2tech(fields[2])))
04237 map->dead = 0;
04238 } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
04239 map->weightstr = ast_strdup(fields[1]);
04240 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04241 if ((map->tech = str2tech(fields[2])))
04242 map->dead = 0;
04243 } else {
04244 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
04245 }
04246 for (y = 4;y < x; y++) {
04247 if (!strcasecmp(fields[y], "nounsolicited"))
04248 map->options |= DUNDI_FLAG_NOUNSOLICITED;
04249 else if (!strcasecmp(fields[y], "nocomunsolicit"))
04250 map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
04251 else if (!strcasecmp(fields[y], "residential"))
04252 map->options |= DUNDI_FLAG_RESIDENTIAL;
04253 else if (!strcasecmp(fields[y], "commercial"))
04254 map->options |= DUNDI_FLAG_COMMERCIAL;
04255 else if (!strcasecmp(fields[y], "mobile"))
04256 map->options |= DUNDI_FLAG_MOBILE;
04257 else if (!strcasecmp(fields[y], "nopartial"))
04258 map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
04259 else
04260 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
04261 }
04262 } else
04263 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
04264 }
04265
04266
04267 static int do_register(const void *data)
04268 {
04269 struct dundi_ie_data ied;
04270 struct dundi_peer *peer = (struct dundi_peer *)data;
04271 char eid_str[20];
04272 char eid_str2[20];
04273 ast_debug(1, "Register us as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), ast_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
04274 peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
04275
04276 if (peer->regtrans)
04277 destroy_trans(peer->regtrans, 0);
04278 peer->regtrans = create_transaction(peer);
04279 if (peer->regtrans) {
04280 ast_set_flag(peer->regtrans, FLAG_ISREG);
04281 memset(&ied, 0, sizeof(ied));
04282 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
04283 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
04284 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
04285 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
04286
04287 } else
04288 ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04289
04290 return 0;
04291 }
04292
04293 static int do_qualify(const void *data)
04294 {
04295 struct dundi_peer *peer = (struct dundi_peer *)data;
04296 peer->qualifyid = -1;
04297 qualify_peer(peer, 0);
04298 return 0;
04299 }
04300
04301 static void qualify_peer(struct dundi_peer *peer, int schedonly)
04302 {
04303 int when;
04304 AST_SCHED_DEL(sched, peer->qualifyid);
04305 if (peer->qualtrans)
04306 destroy_trans(peer->qualtrans, 0);
04307 peer->qualtrans = NULL;
04308 if (peer->maxms > 0) {
04309 when = 60000;
04310 if (peer->lastms < 0)
04311 when = 10000;
04312 if (schedonly)
04313 when = 5000;
04314 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
04315 if (!schedonly)
04316 peer->qualtrans = create_transaction(peer);
04317 if (peer->qualtrans) {
04318 peer->qualtx = ast_tvnow();
04319 ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
04320 dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
04321 }
04322 }
04323 }
04324 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
04325 {
04326 char data[256];
04327 char *c;
04328 int port, expire;
04329 char eid_str[20];
04330 ast_eid_to_str(eid_str, sizeof(eid_str), eid);
04331 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
04332 c = strchr(data, ':');
04333 if (c) {
04334 *c = '\0';
04335 c++;
04336 if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
04337
04338 inet_aton(data, &peer->addr.sin_addr);
04339 peer->addr.sin_family = AF_INET;
04340 peer->addr.sin_port = htons(port);
04341 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
04342 }
04343 }
04344 }
04345 }
04346
04347
04348 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
04349 {
04350 struct dundi_peer *peer;
04351 struct ast_hostent he;
04352 struct hostent *hp;
04353 dundi_eid testeid;
04354 int needregister=0;
04355 char eid_str[20];
04356
04357 AST_LIST_LOCK(&peers);
04358 AST_LIST_TRAVERSE(&peers, peer, list) {
04359 if (!ast_eid_cmp(&peer->eid, eid)) {
04360 break;
04361 }
04362 }
04363 if (!peer) {
04364
04365 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
04366 AST_LIST_UNLOCK(&peers);
04367 return;
04368 }
04369 peer->registerid = -1;
04370 peer->registerexpire = -1;
04371 peer->qualifyid = -1;
04372 peer->addr.sin_family = AF_INET;
04373 peer->addr.sin_port = htons(DUNDI_PORT);
04374 populate_addr(peer, eid);
04375 AST_LIST_INSERT_HEAD(&peers, peer, list);
04376 }
04377 peer->dead = 0;
04378 peer->eid = *eid;
04379 peer->us_eid = global_eid;
04380 destroy_permissions(&peer->permit);
04381 destroy_permissions(&peer->include);
04382 AST_SCHED_DEL(sched, peer->registerid);
04383 for (; v; v = v->next) {
04384 if (!strcasecmp(v->name, "inkey")) {
04385 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
04386 } else if (!strcasecmp(v->name, "outkey")) {
04387 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
04388 } else if (!strcasecmp(v->name, "port")) {
04389 peer->addr.sin_port = htons(atoi(v->value));
04390 } else if (!strcasecmp(v->name, "host")) {
04391 if (!strcasecmp(v->value, "dynamic")) {
04392 peer->dynamic = 1;
04393 } else {
04394 hp = ast_gethostbyname(v->value, &he);
04395 if (hp) {
04396 memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
04397 peer->dynamic = 0;
04398 } else {
04399 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
04400 peer->dead = 1;
04401 }
04402 }
04403 } else if (!strcasecmp(v->name, "ustothem")) {
04404 if (!ast_str_to_eid(&testeid, v->value))
04405 peer->us_eid = testeid;
04406 else
04407 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
04408 } else if (!strcasecmp(v->name, "include")) {
04409 append_permission(&peer->include, v->value, 1);
04410 } else if (!strcasecmp(v->name, "permit")) {
04411 append_permission(&peer->permit, v->value, 1);
04412 } else if (!strcasecmp(v->name, "noinclude")) {
04413 append_permission(&peer->include, v->value, 0);
04414 } else if (!strcasecmp(v->name, "deny")) {
04415 append_permission(&peer->permit, v->value, 0);
04416 } else if (!strcasecmp(v->name, "register")) {
04417 needregister = ast_true(v->value);
04418 } else if (!strcasecmp(v->name, "order")) {
04419 if (!strcasecmp(v->value, "primary"))
04420 peer->order = 0;
04421 else if (!strcasecmp(v->value, "secondary"))
04422 peer->order = 1;
04423 else if (!strcasecmp(v->value, "tertiary"))
04424 peer->order = 2;
04425 else if (!strcasecmp(v->value, "quartiary"))
04426 peer->order = 3;
04427 else {
04428 ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
04429 }
04430 } else if (!strcasecmp(v->name, "qualify")) {
04431 if (!strcasecmp(v->value, "no")) {
04432 peer->maxms = 0;
04433 } else if (!strcasecmp(v->value, "yes")) {
04434 peer->maxms = DEFAULT_MAXMS;
04435 } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
04436 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
04437 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
04438 peer->maxms = 0;
04439 }
04440 } else if (!strcasecmp(v->name, "model")) {
04441 if (!strcasecmp(v->value, "inbound"))
04442 peer->model = DUNDI_MODEL_INBOUND;
04443 else if (!strcasecmp(v->value, "outbound"))
04444 peer->model = DUNDI_MODEL_OUTBOUND;
04445 else if (!strcasecmp(v->value, "symmetric"))
04446 peer->model = DUNDI_MODEL_SYMMETRIC;
04447 else if (!strcasecmp(v->value, "none"))
04448 peer->model = 0;
04449 else {
04450 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04451 v->value, v->lineno);
04452 }
04453 } else if (!strcasecmp(v->name, "precache")) {
04454 if (!strcasecmp(v->value, "inbound"))
04455 peer->pcmodel = DUNDI_MODEL_INBOUND;
04456 else if (!strcasecmp(v->value, "outbound"))
04457 peer->pcmodel = DUNDI_MODEL_OUTBOUND;
04458 else if (!strcasecmp(v->value, "symmetric"))
04459 peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
04460 else if (!strcasecmp(v->value, "none"))
04461 peer->pcmodel = 0;
04462 else {
04463 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04464 v->value, v->lineno);
04465 }
04466 }
04467 }
04468 (*globalpcmode) |= peer->pcmodel;
04469 if (!peer->model && !peer->pcmodel) {
04470 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
04471 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04472 peer->dead = 1;
04473 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04474 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
04475 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04476 peer->dead = 1;
04477 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04478 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
04479 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04480 peer->dead = 1;
04481 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04482 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
04483 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04484 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04485 ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
04486 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04487 } else {
04488 if (needregister) {
04489 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
04490 }
04491 if (ast_eid_cmp(&peer->eid, &empty_eid)) {
04492 qualify_peer(peer, 1);
04493 }
04494 }
04495 AST_LIST_UNLOCK(&peers);
04496 }
04497
04498 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
04499 {
04500 struct dundi_result results[MAX_RESULTS];
04501 int res;
04502 int x;
04503 int found = 0;
04504 if (!strncasecmp(context, "macro-", 6)) {
04505 if (!chan) {
04506 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04507 return -1;
04508 }
04509
04510 if (!strcasecmp(exten, "s")) {
04511 exten = pbx_builtin_getvar_helper(chan, "ARG1");
04512 if (ast_strlen_zero(exten))
04513 exten = chan->macroexten;
04514 if (ast_strlen_zero(exten))
04515 exten = chan->exten;
04516 if (ast_strlen_zero(exten)) {
04517 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04518 return -1;
04519 }
04520 }
04521 if (ast_strlen_zero(data))
04522 data = "e164";
04523 } else {
04524 if (ast_strlen_zero(data))
04525 data = context;
04526 }
04527 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04528 for (x=0;x<res;x++) {
04529 if (ast_test_flag(results + x, flag))
04530 found++;
04531 }
04532 if (found >= priority)
04533 return 1;
04534 return 0;
04535 }
04536
04537 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04538 {
04539 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
04540 }
04541
04542 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04543 {
04544 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
04545 }
04546
04547 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04548 {
04549 struct dundi_result results[MAX_RESULTS];
04550 int res;
04551 int x=0;
04552 char req[1024];
04553 const char *dundiargs;
04554 struct ast_app *dial;
04555
04556 if (!strncasecmp(context, "macro-", 6)) {
04557 if (!chan) {
04558 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04559 return -1;
04560 }
04561
04562 if (!strcasecmp(exten, "s")) {
04563 exten = pbx_builtin_getvar_helper(chan, "ARG1");
04564 if (ast_strlen_zero(exten))
04565 exten = chan->macroexten;
04566 if (ast_strlen_zero(exten))
04567 exten = chan->exten;
04568 if (ast_strlen_zero(exten)) {
04569 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04570 return -1;
04571 }
04572 }
04573 if (ast_strlen_zero(data))
04574 data = "e164";
04575 } else {
04576 if (ast_strlen_zero(data))
04577 data = context;
04578 }
04579 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04580 if (res > 0) {
04581 sort_results(results, res);
04582 for (x=0;x<res;x++) {
04583 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
04584 if (!--priority)
04585 break;
04586 }
04587 }
04588 }
04589 if (x < res) {
04590
04591 dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
04592 snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest,
04593 S_OR(dundiargs, ""));
04594 dial = pbx_findapp("Dial");
04595 if (dial)
04596 res = pbx_exec(chan, dial, req);
04597 } else
04598 res = -1;
04599 return res;
04600 }
04601
04602 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04603 {
04604 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
04605 }
04606
04607 static struct ast_switch dundi_switch = {
04608 .name = "DUNDi",
04609 .description = "DUNDi Discovered Dialplan Switch",
04610 .exists = dundi_exists,
04611 .canmatch = dundi_canmatch,
04612 .exec = dundi_exec,
04613 .matchmore = dundi_matchmore,
04614 };
04615
04616 static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
04617 {
04618 struct ast_config *cfg;
04619 struct ast_variable *v;
04620 char *cat;
04621 int x;
04622 struct ast_flags config_flags = { 0 };
04623 char hn[MAXHOSTNAMELEN] = "";
04624 struct ast_hostent he;
04625 struct hostent *hp;
04626 struct sockaddr_in sin2;
04627 static int last_port = 0;
04628 int globalpcmodel = 0;
04629 dundi_eid testeid;
04630
04631 if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
04632 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
04633 return -1;
04634 }
04635
04636 dundi_ttl = DUNDI_DEFAULT_TTL;
04637 dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
04638 any_peer = NULL;
04639
04640 ipaddr[0] = '\0';
04641 if (!gethostname(hn, sizeof(hn)-1)) {
04642 hp = ast_gethostbyname(hn, &he);
04643 if (hp) {
04644 memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
04645 ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
04646 } else
04647 ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
04648 } else
04649 ast_log(LOG_WARNING, "Unable to get host name!\n");
04650 AST_LIST_LOCK(&peers);
04651
04652 memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
04653
04654 global_storehistory = 0;
04655 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
04656 v = ast_variable_browse(cfg, "general");
04657 while(v) {
04658 if (!strcasecmp(v->name, "port")){
04659 sin->sin_port = htons(atoi(v->value));
04660 if(last_port==0){
04661 last_port=sin->sin_port;
04662 } else if(sin->sin_port != last_port)
04663 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
04664 } else if (!strcasecmp(v->name, "bindaddr")) {
04665 struct hostent *hep;
04666 struct ast_hostent hent;
04667 hep = ast_gethostbyname(v->value, &hent);
04668 if (hep) {
04669 memcpy(&sin->sin_addr, hep->h_addr, sizeof(sin->sin_addr));
04670 } else
04671 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
04672 } else if (!strcasecmp(v->name, "authdebug")) {
04673 authdebug = ast_true(v->value);
04674 } else if (!strcasecmp(v->name, "ttl")) {
04675 if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
04676 dundi_ttl = x;
04677 } else {
04678 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
04679 v->value, v->lineno, DUNDI_DEFAULT_TTL);
04680 }
04681 } else if (!strcasecmp(v->name, "autokill")) {
04682 if (sscanf(v->value, "%30d", &x) == 1) {
04683 if (x >= 0)
04684 global_autokilltimeout = x;
04685 else
04686 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
04687 } else if (ast_true(v->value)) {
04688 global_autokilltimeout = DEFAULT_MAXMS;
04689 } else {
04690 global_autokilltimeout = 0;
04691 }
04692 } else if (!strcasecmp(v->name, "entityid")) {
04693 if (!ast_str_to_eid(&testeid, v->value))
04694 global_eid = testeid;
04695 else
04696 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
04697 } else if (!strcasecmp(v->name, "tos")) {
04698 if (ast_str2tos(v->value, &tos))
04699 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
04700 } else if (!strcasecmp(v->name, "department")) {
04701 ast_copy_string(dept, v->value, sizeof(dept));
04702 } else if (!strcasecmp(v->name, "organization")) {
04703 ast_copy_string(org, v->value, sizeof(org));
04704 } else if (!strcasecmp(v->name, "locality")) {
04705 ast_copy_string(locality, v->value, sizeof(locality));
04706 } else if (!strcasecmp(v->name, "stateprov")) {
04707 ast_copy_string(stateprov, v->value, sizeof(stateprov));
04708 } else if (!strcasecmp(v->name, "country")) {
04709 ast_copy_string(country, v->value, sizeof(country));
04710 } else if (!strcasecmp(v->name, "email")) {
04711 ast_copy_string(email, v->value, sizeof(email));
04712 } else if (!strcasecmp(v->name, "phone")) {
04713 ast_copy_string(phone, v->value, sizeof(phone));
04714 } else if (!strcasecmp(v->name, "storehistory")) {
04715 global_storehistory = ast_true(v->value);
04716 } else if (!strcasecmp(v->name, "cachetime")) {
04717 if ((sscanf(v->value, "%30d", &x) == 1)) {
04718 dundi_cache_time = x;
04719 } else {
04720 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
04721 v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
04722 }
04723 }
04724 v = v->next;
04725 }
04726 AST_LIST_UNLOCK(&peers);
04727 mark_mappings();
04728 v = ast_variable_browse(cfg, "mappings");
04729 while(v) {
04730 build_mapping(v->name, v->value);
04731 v = v->next;
04732 }
04733 prune_mappings();
04734 mark_peers();
04735 cat = ast_category_browse(cfg, NULL);
04736 while(cat) {
04737 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
04738
04739 if (!ast_str_to_eid(&testeid, cat))
04740 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
04741 else if (!strcasecmp(cat, "*")) {
04742 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
04743 any_peer = find_peer(NULL);
04744 } else
04745 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
04746 }
04747 cat = ast_category_browse(cfg, cat);
04748 }
04749 prune_peers();
04750 ast_config_destroy(cfg);
04751 load_password();
04752 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
04753 dundi_precache_full();
04754 return 0;
04755 }
04756
04757 static int unload_module(void)
04758 {
04759 pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid, previous_clearcachethreadid = clearcachethreadid;
04760
04761 ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
04762 ast_unregister_switch(&dundi_switch);
04763 ast_custom_function_unregister(&dundi_function);
04764 ast_custom_function_unregister(&dundi_query_function);
04765 ast_custom_function_unregister(&dundi_result_function);
04766
04767
04768 dundi_shutdown = 1;
04769 if (previous_netthreadid != AST_PTHREADT_NULL) {
04770 pthread_kill(previous_netthreadid, SIGURG);
04771 pthread_join(previous_netthreadid, NULL);
04772 }
04773 if (previous_precachethreadid != AST_PTHREADT_NULL) {
04774 pthread_kill(previous_precachethreadid, SIGURG);
04775 pthread_join(previous_precachethreadid, NULL);
04776 }
04777 if (previous_clearcachethreadid != AST_PTHREADT_NULL) {
04778 pthread_cancel(previous_clearcachethreadid);
04779 pthread_join(previous_clearcachethreadid, NULL);
04780 }
04781
04782 close(netsocket);
04783 io_context_destroy(io);
04784 sched_context_destroy(sched);
04785
04786 mark_mappings();
04787 prune_mappings();
04788 mark_peers();
04789 prune_peers();
04790
04791 return 0;
04792 }
04793
04794 static int reload(void)
04795 {
04796 struct sockaddr_in sin;
04797
04798 if (set_config("dundi.conf", &sin, 1))
04799 return AST_MODULE_LOAD_FAILURE;
04800
04801 return AST_MODULE_LOAD_SUCCESS;
04802 }
04803
04804 static int load_module(void)
04805 {
04806 struct sockaddr_in sin;
04807
04808 dundi_set_output(dundi_debug_output);
04809 dundi_set_error(dundi_error_output);
04810
04811 sin.sin_family = AF_INET;
04812 sin.sin_port = htons(DUNDI_PORT);
04813 sin.sin_addr.s_addr = INADDR_ANY;
04814
04815
04816 io = io_context_create();
04817 sched = sched_context_create();
04818
04819 if (!io || !sched)
04820 return AST_MODULE_LOAD_DECLINE;
04821
04822 if (set_config("dundi.conf", &sin, 0))
04823 return AST_MODULE_LOAD_DECLINE;
04824
04825 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
04826
04827 if (netsocket < 0) {
04828 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
04829 return AST_MODULE_LOAD_DECLINE;
04830 }
04831 if (bind(netsocket, (struct sockaddr *) &sin, sizeof(sin))) {
04832 ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n",
04833 ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
04834 return AST_MODULE_LOAD_DECLINE;
04835 }
04836
04837 ast_netsock_set_qos(netsocket, tos, 0, "DUNDi");
04838
04839 if (start_network_thread()) {
04840 ast_log(LOG_ERROR, "Unable to start network thread\n");
04841 close(netsocket);
04842 return AST_MODULE_LOAD_DECLINE;
04843 }
04844
04845 ast_cli_register_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
04846 if (ast_register_switch(&dundi_switch))
04847 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
04848 ast_custom_function_register(&dundi_function);
04849 ast_custom_function_register(&dundi_query_function);
04850 ast_custom_function_register(&dundi_result_function);
04851
04852 ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
04853
04854 return AST_MODULE_LOAD_SUCCESS;
04855 }
04856
04857 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
04858 .load = load_module,
04859 .unload = unload_module,
04860 .reload = reload,
04861 .nonoptreq = "res_crypto",
04862 );
04863