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