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