43 #include <sys/ioctl.h>
49 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
50 #include <net/if_dl.h>
75 #include "dundi-parser.h"
153 #define MAX_RESULTS 64
155 #define MAX_PACKET_SIZE 8192
157 #define MAX_WEIGHT 59999
159 #define DUNDI_MODEL_INBOUND (1 << 0)
160 #define DUNDI_MODEL_OUTBOUND (1 << 1)
161 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
164 #define DUNDI_TIMING_HISTORY 10
176 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
179 #define DUNDI_SECRET_TIME 15
181 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
186 static int netsocket = -1;
187 static int netsocket2 = -1;
188 static pthread_t netthreadid = AST_PTHREADT_NULL;
189 static pthread_t precachethreadid = AST_PTHREADT_NULL;
190 static pthread_t clearcachethreadid = AST_PTHREADT_NULL;
191 static unsigned int tos = 0;
192 static int dundidebug = 0;
193 static int authdebug = 0;
197 static int global_autokilltimeout = 0;
199 static int default_expiration = 60;
200 static int global_storehistory = 0;
201 static char dept[80];
203 static char locality[80];
204 static char stateprov[80];
205 static char country[80];
206 static char email[80];
207 static char phone[80];
208 static char secretpath[80];
209 static char cursecret[80];
210 static char ipaddr[80];
211 static int outgoing_sip_tech;
212 static char pjsip_outgoing_endpoint[80];
213 static time_t rotatetime;
214 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
215 static int dundi_shutdown = 0;
230 unsigned char data[0];
234 unsigned short flags;
318 unsigned char txenckey[256];
319 unsigned char rxenckey[256];
320 uint32_t us_keycrc32;
323 uint32_t them_keycrc32;
329 char *lookups[DUNDI_TIMING_HISTORY];
336 unsigned int dynamic:1;
339 struct timeval qualtx;
358 static
void dundi_debug_output(const
char *data)
361 ast_verbose(
"%s", data);
364 static void dundi_error_output(
const char *data)
366 ast_log(LOG_WARNING,
"%s", data);
369 static int has_permission(
struct permissionlist *permlist,
char *cont)
375 if (!strcasecmp(perm->name,
"all") || !strcasecmp(perm->name, cont))
382 static char *tech2str(
int tech)
400 static int str2tech(
const char *str)
402 if (!strcasecmp(str,
"IAX") || !strcasecmp(str,
"IAX2"))
404 else if (!strcasecmp(str,
"SIP"))
406 else if (!strcasecmp(str,
"H323"))
408 else if (!strcasecmp(str,
"PJSIP"))
414 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[]);
415 static int dundi_precache_internal(
const char *context,
const char *number,
int ttl,
dundi_eid *avoids[]);
444 if (!(trans = create_transaction(NULL)))
471 memset(&tmp, 0,
sizeof(tmp));
472 memset(&trans, 0,
sizeof(trans));
474 tmp.hdr.strans = h->
dtrans;
475 tmp.hdr.dtrans = h->
strans;
476 tmp.hdr.iseqno = h->
oseqno;
477 tmp.hdr.oseqno = h->
iseqno;
479 tmp.hdr.cmdflags = 0;
480 tmp.pack.h = (
struct dundi_hdr *)tmp.pack.data;
481 tmp.pack.datalen =
sizeof(
struct dundi_hdr);
482 tmp.pack.parent = &trans;
483 dundi_xmit(&tmp.pack);
486 static int get_trans_id(
void)
489 int stid = (ast_random() % 32766) + 1;
499 tid = (tid % 32766) + 1;
500 }
while (tid != stid);
508 tid = get_trans_id();
539 static void build_iv(
unsigned char *iv)
542 unsigned int *fluffy;
544 fluffy = (
unsigned int *)(iv);
546 fluffy[x] = ast_random();
551 int directs[DUNDI_MAX_STACK + 1];
570 if (map->weightstr) {
572 pbx_substitute_variables_varshead(headp, map->weightstr, buf,
sizeof(buf) - 1);
574 pbx_substitute_variables_helper(NULL, map->weightstr, buf,
sizeof(buf) - 1);
577 if (sscanf(buf,
"%30d", &map->_weight) != 1)
578 map->_weight = MAX_WEIGHT;
588 if (!ast_strlen_zero(map->lcontext)) {
599 if (ast_test_flag(&flags, AST_FLAGS_ALL))
602 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
606 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
609 ast_set_flag(&flags, map->options & 0xffff);
610 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
611 dr[anscnt].techint = map->tech;
612 dr[anscnt].expiration = dundi_cache_time;
613 ast_copy_string(dr[anscnt].tech, tech2str(map->tech),
sizeof(dr[anscnt].tech));
615 ast_eid_to_str(dr[anscnt].eid_str,
sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
618 if ((newvariable = ast_var_assign(
"NUMBER", called_number))) {
621 if ((newvariable = ast_var_assign(
"EID", dr[anscnt].eid_str))) {
624 if ((newvariable = ast_var_assign(
"SECRET", cursecret))) {
627 if ((newvariable = ast_var_assign(
"IPADDR", ipaddr))) {
630 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest,
sizeof(dr[anscnt].dest));
631 dr[anscnt].weight = get_mapping_weight(map, &headp);
633 ast_var_delete(newvariable);
635 dr[anscnt].dest[0] =
'\0';
636 dr[anscnt].weight = get_mapping_weight(map, NULL);
643 for (x = 0; x < (
sizeof(tmp) - 1); x++) {
644 tmp[x] = called_number[x];
650 if (strlen(tmp) > strlen(hmd->exten)) {
663 static void *dundi_lookup_thread(
void *data)
673 int expiration = dundi_cache_time;
675 ast_debug(1,
"Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
676 st->eids[0] ?
ast_eid_to_str(eid_str,
sizeof(eid_str), st->eids[0]) :
"ourselves");
677 memset(&ied, 0,
sizeof(ied));
678 memset(&dr, 0,
sizeof(dr));
679 memset(&hmd, 0,
sizeof(hmd));
682 for (x=0;x<st->nummaps;x++)
683 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->
us_eid, ouranswers, &hmd);
686 for (x=0;x<ouranswers;x++) {
687 if (dr[x].weight < max)
693 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);
698 if ((res < -1) && (!ouranswers))
706 if (ast_test_flag(st->trans,
FLAG_DEAD)) {
707 ast_debug(1,
"Our transaction went away!\n");
709 destroy_trans(st->trans, 0);
711 for (x=0;x<ouranswers;x++) {
713 if (dr[x].expiration && (expiration > dr[x].expiration))
714 expiration = dr[x].expiration;
715 dundi_ie_append_answer(&ied,
DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
717 dundi_ie_append_hint(&ied,
DUNDI_IE_HINT, hmd.flags, hmd.exten);
727 static void *dundi_precache_thread(
void *data)
734 ast_debug(1,
"Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
735 st->eids[0] ?
ast_eid_to_str(eid_str,
sizeof(eid_str), st->eids[0]) :
"ourselves");
736 memset(&ied, 0,
sizeof(ied));
739 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
745 if (ast_test_flag(st->trans,
FLAG_DEAD)) {
746 ast_debug(1,
"Our transaction went away!\n");
748 destroy_trans(st->trans, 0);
760 static void *dundi_query_thread(
void *data)
769 ast_debug(1,
"Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
770 st->eids[0] ?
ast_eid_to_str(eid_str,
sizeof(eid_str), st->eids[0]) :
"ourselves");
771 memset(&ied, 0,
sizeof(ied));
772 memset(&dei, 0,
sizeof(dei));
773 memset(&hmd, 0,
sizeof(hmd));
776 ast_debug(1,
"Neat, someone look for us!\n");
787 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
790 if (ast_test_flag(st->trans,
FLAG_DEAD)) {
791 ast_debug(1,
"Our transaction went away!\n");
793 destroy_trans(st->trans, 0);
803 if (!ast_strlen_zero(dei.ipaddr))
806 dundi_ie_append_hint(&ied,
DUNDI_IE_HINT, hmd.flags, hmd.exten);
823 pthread_t lookupthread;
825 if (ies->eidcount > 1) {
830 if (!
ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
834 totallen += (ies->eidcount - skipfirst) *
sizeof(
dundi_eid);
837 ast_copy_string(st->called_context, ies->called_context,
sizeof(st->called_context));
838 memcpy(&st->reqeid, ies->reqeid,
sizeof(st->reqeid));
840 st->ttl = ies->ttl - 1;
844 for (x=skipfirst;ies->eids[x];x++) {
846 *st->eids[x-skipfirst] = *ies->eids[x];
849 ast_debug(1,
"Answering EID query for '%s@%s'!\n",
ast_eid_to_str(eid_str,
sizeof(eid_str), ies->reqeid), ies->called_context);
852 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
855 ast_log(LOG_WARNING,
"Unable to create thread!\n");
870 static int cache_save_hint(
dundi_eid *eidpeer,
struct dundi_request *req,
struct dundi_hint *hint,
int expiration)
875 char eidpeer_str[20];
876 char eidroot_str[20];
881 expiration = dundi_cache_time;
889 dundi_eid_to_str_short(eidpeer_str,
sizeof(eidpeer_str), eidpeer);
890 dundi_eid_to_str_short(eidroot_str,
sizeof(eidroot_str), &req->root_eid);
891 snprintf(key1,
sizeof(key1),
"hint/%s/%s/%s/e%08x", eidpeer_str, hint->
data, req->dcontext, unaffected ? 0 : req->
crc32);
892 snprintf(key2,
sizeof(key2),
"hint/%s/%s/%s/r%s", eidpeer_str, hint->
data, req->dcontext, eidroot_str);
895 timeout += expiration;
896 snprintf(data,
sizeof(data),
"%ld|", (
long)(timeout));
899 ast_debug(1,
"Caching hint at '%s'\n", key1);
901 ast_debug(1,
"Caching hint at '%s'\n", key2);
905 static int cache_save(
dundi_eid *eidpeer,
struct dundi_request *req,
int start,
int unaffected,
int expiration,
int push)
911 char eidpeer_str[20];
912 char eidroot_str[20];
916 expiration = dundi_cache_time;
925 dundi_eid_to_str_short(eidpeer_str,
sizeof(eidpeer_str), eidpeer);
926 dundi_eid_to_str_short(eidroot_str,
sizeof(eidroot_str), &req->root_eid);
927 snprintf(key1,
sizeof(key1),
"%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->
crc32);
928 snprintf(key2,
sizeof(key2),
"%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
931 timeout += expiration;
932 snprintf(data,
sizeof(data),
"%ld|", (
long)(timeout));
933 for (x=start;x<req->respcount;x++) {
935 if (strchr(req->dr[x].dest,
'|'))
937 snprintf(data + strlen(data),
sizeof(data) - strlen(data),
"%u/%d/%d/%s/%s|",
938 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
939 dundi_eid_to_str_short(eidpeer_str,
sizeof(eidpeer_str), &req->dr[x].eid));
954 struct dundi_request dr;
961 pthread_t lookupthread;
963 memset(&dr2, 0,
sizeof(dr2));
964 memset(&dr, 0,
sizeof(dr));
965 memset(&hmd, 0,
sizeof(hmd));
970 dr.maxcount = MAX_RESULTS;
971 dr.expiration = dundi_cache_time;
973 dr.pfds[0] = dr.pfds[1] = -1;
975 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context :
"e164",
sizeof(dr.dcontext));
978 for (x=0;x<ies->anscount;x++) {
979 if (trans->
parent->respcount < trans->
parent->maxcount) {
981 for (z=0;z<trans->
parent->respcount;z++) {
982 if ((trans->
parent->dr[z].techint == ies->answers[x]->
protocol) &&
983 !strcmp(trans->
parent->dr[z].dest, (
char *)ies->answers[x]->
data))
986 if (z == trans->
parent->respcount) {
988 trans->
parent->dr[trans->
parent->respcount].flags = ntohs(ies->answers[x]->
flags);
991 trans->
parent->dr[trans->
parent->respcount].eid = ies->answers[x]->
eid;
992 if (ies->expiration > 0)
993 trans->
parent->dr[trans->
parent->respcount].expiration = ies->expiration;
995 trans->
parent->dr[trans->
parent->respcount].expiration = dundi_cache_time;
997 sizeof(trans->
parent->dr[trans->
parent->respcount].eid_str),
998 &ies->answers[x]->
eid);
1000 sizeof(trans->
parent->dr[trans->
parent->respcount].dest));
1002 sizeof(trans->
parent->dr[trans->
parent->respcount].tech));
1003 trans->
parent->respcount++;
1005 }
else if (trans->
parent->dr[z].weight > ntohs(ies->answers[x]->
weight)) {
1007 trans->
parent->dr[z].weight = ntohs(ies->answers[x]->
weight);
1010 ast_log(LOG_NOTICE,
"Dropping excessive answers in precache for %s@%s\n",
1015 cache_save(&trans->
them_eid, trans->
parent, 0, 0, ies->expiration, 1);
1017 cache_save_hint(&trans->
them_eid, trans->
parent, ies->hint, ies->expiration);
1023 if (!strcasecmp(cur->dcontext, ccontext))
1031 if (ies->eidcount > 1) {
1036 if (!
ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1042 totallen += (ies->eidcount - skipfirst) *
sizeof(
dundi_eid);
1045 ast_copy_string(st->called_context, dr.dcontext,
sizeof(st->called_context));
1046 ast_copy_string(st->called_number, ies->called_number,
sizeof(st->called_number));
1048 st->ttl = ies->ttl - 1;
1049 st->nocache = ies->cbypass;
1053 for (x=skipfirst;ies->eids[x];x++) {
1055 *st->eids[x-skipfirst] = *ies->eids[x];
1056 st->directs[x-skipfirst] = ies->eid_direct[x];
1063 if (!strcasecmp(cur->dcontext, ccontext)) {
1066 st->maps[x].list.next = NULL;
1071 st->nummaps = mapcount;
1072 ast_debug(1,
"Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1074 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1076 ast_log(LOG_WARNING,
"Unable to create thread!\n");
1078 memset(&ied, 0,
sizeof(ied));
1084 ast_log(LOG_WARNING,
"Out of memory!\n");
1085 memset(&ied, 0,
sizeof(ied));
1104 pthread_t lookupthread;
1108 if (!strcasecmp(cur->dcontext, ccontext))
1115 if (ies->eidcount > 1) {
1120 if (!
ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1125 totallen += (ies->eidcount - skipfirst) *
sizeof(
dundi_eid);
1128 ast_copy_string(st->called_context, ies->called_context,
sizeof(st->called_context));
1129 ast_copy_string(st->called_number, ies->called_number,
sizeof(st->called_number));
1131 st->ttl = ies->ttl - 1;
1132 st->nocache = ies->cbypass;
1136 for (x=skipfirst;ies->eids[x];x++) {
1138 *st->eids[x-skipfirst] = *ies->eids[x];
1139 st->directs[x-skipfirst] = ies->eid_direct[x];
1146 if (!strcasecmp(cur->dcontext, ccontext)) {
1149 st->maps[x].list.next = NULL;
1154 st->nummaps = mapcount;
1155 ast_debug(1,
"Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1157 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1159 ast_log(LOG_WARNING,
"Unable to create thread!\n");
1161 memset(&ied, 0,
sizeof(ied));
1167 ast_log(LOG_WARNING,
"Out of memory!\n");
1168 memset(&ied, 0,
sizeof(ied));
1176 static int cache_lookup_internal(time_t now,
struct dundi_request *req,
char *key,
char *eid_str_full,
int *lowexpiration)
1179 char *ptr, *term, *src;
1188 if (!
ast_db_get(
"dundi/cache", key, data,
sizeof(data))) {
1192 int expiration = timeout - now;
1193 if (expiration > 0) {
1194 ast_debug(1,
"Found cache expiring in %d seconds!\n", expiration);
1196 while((sscanf(ptr,
"%30d/%30d/%30d/%n", (
int *)&(flags.flags), &weight, &tech, &length) == 3)) {
1198 term = strchr(ptr,
'|');
1201 src = strrchr(ptr,
'/');
1207 ast_debug(1,
"Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1208 tech2str(tech), ptr, src, dundi_flags2str(fs,
sizeof(fs), flags.flags), eid_str_full);
1210 for (z=0;z<req->respcount;z++) {
1211 if ((req->dr[z].techint == tech) &&
1212 !strcmp(req->dr[z].dest, ptr))
1215 if (z == req->respcount) {
1217 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1218 req->dr[req->respcount].weight = weight;
1219 req->dr[req->respcount].techint = tech;
1220 req->dr[req->respcount].expiration = expiration;
1221 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1223 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1225 sizeof(req->dr[req->respcount].dest));
1227 sizeof(req->dr[req->respcount].tech));
1230 }
else if (req->dr[z].weight > weight)
1231 req->dr[z].weight = weight;
1236 if (expiration < *lowexpiration)
1237 *lowexpiration = expiration;
1248 static int cache_lookup(
struct dundi_request *req,
dundi_eid *peer_eid, uint32_t crc,
int *lowexpiration)
1251 char eidroot_str[20];
1255 char eid_str_full[20];
1258 char key[
sizeof(eid_str) +
sizeof(tmp) +
sizeof(req->dcontext) +
sizeof(eidroot_str) +
sizeof(
"hint////r")];
1262 dundi_eid_to_str_short(eid_str,
sizeof(eid_str), peer_eid);
1263 dundi_eid_to_str_short(eidroot_str,
sizeof(eidroot_str), &req->root_eid);
1265 snprintf(key,
sizeof(key),
"%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc);
1266 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1267 snprintf(key,
sizeof(key),
"%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, (
unsigned)0);
1268 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1269 snprintf(key,
sizeof(key),
"%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1270 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1272 if (!req->respcount) {
1276 if (!(tmp[x] = req->number[x]))
1280 snprintf(key,
sizeof(key),
"hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc);
1281 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1282 snprintf(key,
sizeof(key),
"hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, (
unsigned)0);
1283 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1284 snprintf(key,
sizeof(key),
"hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1285 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1287 if (strlen(tmp) > strlen(req->hmd->exten)) {
1299 static void qualify_peer(
struct dundi_peer *peer,
int schedonly);
1306 trans->
us_eid = p->us_eid;
1309 if (!ast_strlen_zero(p->inkey))
1333 ast_db_del(
"dundi/dpeers", dundi_eid_to_str_short(eid_str,
sizeof(eid_str), &peer->eid));
1334 peer->registerexpire = -1;
1340 static int update_key(
struct dundi_peer *peer)
1342 unsigned char key[16];
1348 ast_aes_set_encrypt_key(key, &peer->
us_ecx);
1349 ast_aes_set_decrypt_key(key, &peer->
us_dcx);
1352 ast_log(LOG_NOTICE,
"No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1353 peer->inkey,
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
1356 skey =
ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1358 ast_log(LOG_NOTICE,
"No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1359 peer->outkey,
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
1363 ast_log(LOG_NOTICE,
"Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1367 ast_log(LOG_NOTICE,
"Failed to sign key (%d)!\n", res);
1371 peer->sentfullkey = 0;
1379 static int encrypt_memcpy(
unsigned char *dst,
unsigned char *src,
int len,
unsigned char *iv,
ast_aes_encrypt_key *ecx)
1381 unsigned char curblock[16];
1383 memcpy(curblock, iv,
sizeof(curblock));
1386 curblock[x] ^= src[x];
1387 ast_aes_encrypt(curblock, dst, ecx);
1388 memcpy(curblock, dst,
sizeof(curblock));
1395 static int decrypt_memcpy(
unsigned char *dst,
unsigned char *src,
int len,
unsigned char *iv,
ast_aes_decrypt_key *dcx)
1397 unsigned char lastblock[16];
1399 memcpy(lastblock, iv,
sizeof(lastblock));
1401 ast_aes_decrypt(src, dst, dcx);
1403 dst[x] ^= lastblock[x];
1404 memcpy(lastblock, src,
sizeof(lastblock));
1414 int space = *dstlen;
1415 unsigned long bytes;
1417 unsigned char *decrypt_space;
1419 decrypt_memcpy(decrypt_space, src->
encdata, srclen, src->
iv, &trans->
dcx);
1424 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1425 ast_debug(1,
"Ouch, uncompress failed :(\n");
1429 *dstlen = bytes + 6;
1436 unsigned char *compress_space;
1439 unsigned long bytes;
1442 unsigned char iv[16];
1443 len = pack->datalen + pack->datalen / 100 + 42;
1445 memset(compress_space, 0, len);
1448 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1450 ast_debug(1,
"Ouch, compression failed!\n");
1453 memset(&ied, 0,
sizeof(ied));
1459 if (update_key(peer))
1461 if (!peer->sentfullkey)
1476 peer->sentfullkey = 1;
1481 dundi_ie_append_encdata(&ied,
DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1483 if ((ied.pos + bytes) >=
sizeof(ied.buf)) {
1484 ast_log(LOG_NOTICE,
"Final packet too large!\n");
1487 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->
ecx);
1488 ied.pos += ((bytes + 15) / 16) * 16;
1490 pack->datalen =
sizeof(
struct dundi_hdr);
1493 memcpy(pack->h->ies, ied.buf, ied.pos);
1494 pack->datalen += ied.pos;
1498 static int check_key(
struct dundi_peer *peer,
unsigned char *newkey,
unsigned char *newsig, uint32_t keycrc32)
1500 unsigned char dst[128];
1508 }
else if (!newkey || !newsig)
1510 if (!memcmp(peer->
rxenckey, newkey, 128) &&
1511 !memcmp(peer->
rxenckey + 128, newsig, 128)) {
1518 ast_log(LOG_NOTICE,
"Unable to find key '%s' to decode shared key from '%s'\n",
1519 peer->outkey,
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
1525 ast_log(LOG_NOTICE,
"Unable to find key '%s' to verify shared key from '%s'\n",
1526 peer->inkey,
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
1538 ast_log(LOG_NOTICE,
"Weird, key decoded to the wrong size (%d)\n", res);
1542 ast_debug(1,
"Wow, new key combo passed signature and decrypt!\n");
1543 memcpy(peer->
rxenckey, newkey, 128);
1544 memcpy(peer->
rxenckey + 128, newsig, 128);
1546 ast_aes_set_decrypt_key(dst, &peer->
them_dcx);
1547 ast_aes_set_encrypt_key(dst, &peer->
them_ecx);
1555 *peer_dst = *peer_src;
1559 peer_dst->registerid = -1;
1560 peer_dst->qualifyid = -1;
1561 peer_dst->registerexpire = -1;
1566 memset(&peer_dst->lookups, 0,
sizeof(peer_dst->lookups));
1568 memset(&peer_dst->permit, 0,
sizeof(peer_dst->permit));
1569 memset(&peer_dst->include, 0,
sizeof(peer_dst->permit));
1572 if (!(perm =
ast_calloc(1,
sizeof(*perm) + strlen(cur->name) + 1)))
1575 perm->allow = cur->allow;
1576 strcpy(perm->name, cur->name);
1582 if (!(perm =
ast_calloc(1,
sizeof(*perm) + strlen(cur->name) + 1)))
1585 perm->allow = cur->allow;
1586 strcpy(perm->name, cur->name);
1595 int final = hdr->
cmdresp & 0x80;
1596 int cmd = hdr->
cmdresp & 0x7f;
1601 unsigned char *bufcpy;
1625 memcpy(bufcpy, hdr->ies, datalen);
1626 ast_debug(1,
"Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->
oseqno, datalen,
final ?
" (Final)" :
"");
1627 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1628 ast_log(LOG_WARNING,
"Failed to parse DUNDI information elements!\n");
1629 goto return_cleanup;
1646 dundi_send(trans, resp, 0, 1, ied);
1649 trans->
us_eid = peer->us_eid;
1650 if (strlen(peer->inkey)) {
1651 hasauth = encrypted;
1656 if (!ies.called_context)
1657 ies.called_context =
"e164";
1659 res = dundi_answer_entity(trans, &ies, ies.called_context);
1661 if (ast_strlen_zero(ies.called_number)) {
1664 dundi_send(trans, resp, 0, 1, ied);
1666 (peer->
model & DUNDI_MODEL_INBOUND) &&
1667 has_permission(&peer->permit, ies.called_context)) {
1668 res = dundi_answer_query(trans, &ies, ies.called_context);
1672 dundi_send(trans, resp, 0, 1, ied);
1675 (peer->
pcmodel & DUNDI_MODEL_INBOUND) &&
1676 has_permission(&peer->include, ies.called_context)) {
1677 res = dundi_prop_precache(trans, &ies, ies.called_context);
1681 dundi_send(trans, resp, 0, 1, ied);
1686 dundi_send(trans, resp, 0, 1, ied);
1692 dundi_send(trans, resp, 0, 1, ied);
1708 peer->eid = *ies.eids[0];
1716 if (!peer || !peer->
dynamic) {
1721 trans->
us_eid = peer->us_eid;
1722 if (!ast_strlen_zero(peer->inkey)) {
1723 hasauth = encrypted;
1727 int expire = default_expiration;
1733 ast_db_put(
"dundi/dpeers", dundi_eid_to_str_short(eid_str,
sizeof(eid_str), &peer->eid), data);
1735 ast_verb(3,
"Registered DUNDi peer '%s' at '%s'\n",
1745 qualify_peer(peer, 1);
1751 if (ies.cause < 1) {
1753 ast_debug(1,
"Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1755 authpass = encrypted;
1761 y = trans->
parent->respcount;
1762 for (x=0;x<ies.anscount;x++) {
1763 if (trans->
parent->respcount < trans->
parent->maxcount) {
1765 for (z=0;z<trans->
parent->respcount;z++) {
1766 if ((trans->
parent->dr[z].techint == ies.answers[x]->
protocol) &&
1767 !strcmp(trans->
parent->dr[z].dest, (
char *)ies.answers[x]->
data))
1770 if (z == trans->
parent->respcount) {
1772 trans->
parent->dr[trans->
parent->respcount].flags = ntohs(ies.answers[x]->
flags);
1775 trans->
parent->dr[trans->
parent->respcount].eid = ies.answers[x]->
eid;
1776 if (ies.expiration > 0)
1777 trans->
parent->dr[trans->
parent->respcount].expiration = ies.expiration;
1779 trans->
parent->dr[trans->
parent->respcount].expiration = dundi_cache_time;
1781 sizeof(trans->
parent->dr[trans->
parent->respcount].eid_str),
1782 &ies.answers[x]->
eid);
1784 sizeof(trans->
parent->dr[trans->
parent->respcount].dest));
1786 sizeof(trans->
parent->dr[trans->
parent->respcount].tech));
1787 trans->
parent->respcount++;
1789 }
else if (trans->
parent->dr[z].weight > ntohs(ies.answers[x]->
weight)) {
1791 trans->
parent->dr[z].weight = ntohs(ies.answers[x]->
weight);
1794 ast_log(LOG_NOTICE,
"Dropping excessive answers to request for %s@%s\n",
1802 cache_save_hint(&trans->
them_eid, trans->
parent, ies.hint, ies.expiration);
1806 if (strlen((
char *)ies.hint->
data) > strlen(trans->
parent->hmd->exten)) {
1808 sizeof(trans->
parent->hmd->exten));
1814 if (ies.expiration > 0) {
1815 if (trans->
parent->expiration > ies.expiration) {
1816 trans->
parent->expiration = ies.expiration;
1835 if (ies.cause < 1) {
1837 ast_debug(1,
"Looks like success of some sort (%d)\n", ies.cause);
1839 authpass = encrypted;
1844 if (trans->
parent && trans->
parent->dei && ies.q_org) {
1845 if (!trans->
parent->respcount) {
1846 trans->
parent->respcount++;
1853 if (ies.q_stateprov)
1888 if (ies.cause < 1) {
1892 hasauth = encrypted;
1897 ast_log(LOG_NOTICE,
"Reponse to register not authorized!\n");
1933 dundi_ack(trans, hdr->
cmdresp & 0x80);
1936 if (!reset_transaction(trans)) {
1940 memset(&ies, 0,
sizeof(ies));
1943 memset(ied, 0,
sizeof(*ied));
1950 peer->sentfullkey = 1;
1959 if (!ies.eids[0] || !(peer =
find_peer(ies.eids[0])) ||
1960 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1961 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1967 apply_peer(trans, peer);
1972 if (ast_test_flag(trans,
FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1974 unsigned char decoded[MAX_PACKET_SIZE];
1976 ddatalen =
sizeof(decoded);
1977 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1981 dundi_showframe(dhdr, 3, &trans->
addr, ddatalen -
sizeof(
struct dundi_hdr));
1982 handle_command_response(trans, dhdr, ddatalen -
sizeof(
struct dundi_hdr), 1);
1987 ast_debug(1,
"Ouch, decrypt failed :(\n");
2015 static void destroy_packet(
struct dundi_packet *pack,
int needfree);
2016 static void destroy_packets(
struct packetlist *p)
2033 if ((pack->h->
oseqno + 1) % 255 == iseqno) {
2034 destroy_packet(pack, 0);
2036 ast_log(LOG_WARNING,
"Whoa, there was still a last trans?\n");
2051 trans = find_transaction(h, sin);
2053 dundi_reject(h, sin);
2061 destroy_trans(trans, 0);
2067 handle_command_response(trans, h, datalen, 0);
2070 dundi_ack(trans, h->
cmdresp & 0x80);
2077 destroy_trans(trans, 0);
2081 dundi_ack(trans, 0);
2084 ast_debug(1,
"Dropping packet out of window!\n");
2089 static int socket_read(
int *
id,
int fd,
short events,
void *sock)
2094 char buf[MAX_PACKET_SIZE];
2096 res =
ast_recvfrom(*((
int *)sock), buf,
sizeof(buf), 0, &sin);
2098 if (errno != ECONNREFUSED)
2099 ast_log(LOG_WARNING,
"Error: %s\n", strerror(errno));
2103 ast_log(LOG_WARNING,
"midget packet received (%d of %d min)\n", res, (
int)
sizeof(
struct dundi_hdr));
2109 dundi_showframe(h, 1, &sin, res -
sizeof(
struct dundi_hdr));
2116 static void build_secret(
char *secret,
int seclen)
2118 unsigned char tmp[16];
2124 while((s = strchr(secret,
';'))) *s =
'+';
2125 while((s = strchr(secret,
'/'))) *s =
'+';
2126 while((s = strchr(secret,
':'))) *s =
'+';
2127 while((s = strchr(secret,
'@'))) *s =
'+';
2131 static void save_secret(
const char *newkey,
const char *oldkey)
2135 snprintf(tmp,
sizeof(tmp),
"%s;%s", oldkey, newkey);
2137 snprintf(tmp,
sizeof(tmp),
"%s", newkey);
2138 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2140 snprintf(tmp,
sizeof(tmp),
"%d", (
int)rotatetime);
2144 static void load_password(
void)
2151 ast_db_get(secretpath,
"secretexpiry", tmp,
sizeof(tmp));
2153 ast_db_get(secretpath,
"secret", tmp,
sizeof(tmp));
2154 current = strchr(tmp,
';');
2161 if ((time(NULL) - expired) < 0) {
2162 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2163 expired = time(NULL) + DUNDI_SECRET_TIME;
2164 }
else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2175 rotatetime = expired;
2178 build_secret(cursecret,
sizeof(cursecret));
2179 save_secret(cursecret, last);
2190 printf(
"%ld/%ld\n", now, rotatetime);
2192 if ((now - rotatetime) >= 0) {
2195 build_secret(cursecret,
sizeof(cursecret));
2196 save_secret(cursecret, oldsecret);
2200 static void *network_thread(
void *ignore)
2207 int *socket_read_id2 = NULL;
2208 if (netsocket2 >= 0) {
2212 while (!dundi_shutdown) {
2214 if ((res > 1000) || (res < 0))
2227 if (socket_read_id2) {
2234 static void *process_clearcache(
void *ignore)
2237 int striplen =
sizeof(
"/dundi/cache");
2240 while (!dundi_shutdown) {
2241 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2246 for (; db_entry; db_entry = db_entry->next) {
2251 ast_debug(1,
"clearing expired DUNDI cache entry: %s\n", db_entry->key);
2252 ast_db_del(
"dundi/cache", db_entry->key + striplen);
2258 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2259 pthread_testcancel();
2261 pthread_testcancel();
2267 static void *process_precache(
void *ign)
2275 while (!dundi_shutdown) {
2280 if (!qe->expiration) {
2284 }
else if (qe->expiration < now) {
2302 static int start_network_thread(
void)
2304 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2305 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2306 ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
2314 e->
command =
"dundi set debug {on|off}";
2316 "Usage: dundi set debug {on|off}\n"
2317 " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2323 if (a->argc != e->
args) {
2324 return CLI_SHOWUSAGE;
2326 if (!strncasecmp(a->argv[e->
args -1],
"on", 2)) {
2328 ast_cli(a->fd,
"DUNDi Debugging Enabled\n");
2331 ast_cli(a->fd,
"DUNDi Debugging Disabled\n");
2340 e->
command =
"dundi store history {on|off}";
2342 "Usage: dundi store history {on|off}\n"
2343 " Enables/Disables storing of DUNDi requests and times for debugging\n"
2350 if (a->argc != e->
args) {
2351 return CLI_SHOWUSAGE;
2353 if (!strncasecmp(a->argv[e->
args -1],
"on", 2)) {
2354 global_storehistory = 1;
2355 ast_cli(a->fd,
"DUNDi History Storage Enabled\n");
2357 global_storehistory = 0;
2358 ast_cli(a->fd,
"DUNDi History Storage Disabled\n");
2368 e->
command =
"dundi flush [stats]";
2370 "Usage: dundi flush [stats]\n"
2371 " Flushes DUNDi answer cache, used primarily for debug. If\n"
2372 "'stats' is present, clears timer statistics instead of normal\n"
2378 if ((a->argc < 2) || (a->argc > 3)) {
2379 return CLI_SHOWUSAGE;
2382 if (!strcasecmp(a->argv[2],
"stats")) {
2385 return CLI_SHOWUSAGE;
2395 ast_free(p->lookups[x]);
2396 p->lookups[x] = NULL;
2397 p->lookuptimes[x] = 0;
2404 ast_cli(a->fd,
"DUNDi Cache Flushed\n");
2409 static char *model2str(
int model)
2412 case DUNDI_MODEL_INBOUND:
2414 case DUNDI_MODEL_OUTBOUND:
2416 case DUNDI_MODEL_SYMMETRIC:
2423 static char *complete_peer_helper(
const char *line,
const char *word,
int pos,
int state,
int rpos)
2435 const char *s =
ast_eid_to_str(eid_str,
sizeof(eid_str), &p->eid);
2436 if (!strncasecmp(word, s, len) && ++which > state) {
2445 static int rescomp(
const void *a,
const void *b)
2450 if (resa->weight < resb->weight)
2452 if (resa->weight > resb->weight)
2457 static void sort_results(
struct dundi_result *results,
int count)
2459 qsort(results, count,
sizeof(results[0]), rescomp);
2471 struct timeval start;
2476 "Usage: dundi lookup <number>[@context] [bypass]\n"
2477 " Lookup the given number within the given DUNDi context\n"
2478 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2479 "keyword is specified.\n";
2485 if ((a->argc < 3) || (a->argc > 4)) {
2486 return CLI_SHOWUSAGE;
2489 if (!strcasecmp(a->argv[3],
"bypass")) {
2492 return CLI_SHOWUSAGE;
2496 context = strchr(tmp,
'@');
2502 res =
dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2505 ast_cli(a->fd,
"DUNDi lookup returned error.\n");
2507 ast_cli(a->fd,
"DUNDi lookup returned no results.\n");
2509 sort_results(dr, res);
2510 for (x=0;x<res;x++) {
2511 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));
2512 ast_cli(a->fd,
" from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2523 struct timeval start;
2526 e->
command =
"dundi precache";
2528 "Usage: dundi precache <number>[@context]\n"
2529 " Lookup the given number within the given DUNDi context\n"
2530 "(or e164 if none is specified) and precaches the results to any\n"
2531 "upstream DUNDi push servers.\n";
2536 if ((a->argc < 3) || (a->argc > 3)) {
2537 return CLI_SHOWUSAGE;
2540 context = strchr(tmp,
'@');
2549 ast_cli(a->fd,
"DUNDi precache returned error.\n");
2551 ast_cli(a->fd,
"DUNDi precache returned no error.\n");
2567 "Usage: dundi query <entity>[@context]\n"
2568 " Attempts to retrieve contact information for a specific\n"
2569 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2570 "e164 if none is specified).\n";
2575 if ((a->argc < 3) || (a->argc > 3)) {
2576 return CLI_SHOWUSAGE;
2579 ast_cli(a->fd,
"'%s' is not a valid EID!\n", a->argv[2]);
2580 return CLI_SHOWUSAGE;
2583 context = strchr(tmp,
'@');
2590 ast_cli(a->fd,
"DUNDi Query EID returned error.\n");
2592 ast_cli(a->fd,
"DUNDi Query EID returned no results.\n");
2594 ast_cli(a->fd,
"DUNDi Query EID succeeded:\n");
2595 ast_cli(a->fd,
"Department: %s\n", dei.orgunit);
2596 ast_cli(a->fd,
"Organization: %s\n", dei.org);
2597 ast_cli(a->fd,
"City/Locality: %s\n", dei.locality);
2598 ast_cli(a->fd,
"State/Province: %s\n", dei.stateprov);
2599 ast_cli(a->fd,
"Country: %s\n", dei.country);
2600 ast_cli(a->fd,
"E-mail: %s\n", dei.email);
2601 ast_cli(a->fd,
"Phone: %s\n", dei.phone);
2602 ast_cli(a->fd,
"IP Address: %s\n", dei.ipaddr);
2607 static char *dundi_sockaddr_stringify_host(
const struct ast_sockaddr *
addr)
2610 return "(Unspecified)";
2615 static uint16_t dundi_sockaddr_port(
const struct ast_sockaddr *addr)
2636 e->
command =
"dundi show peer";
2638 "Usage: dundi show peer [peer]\n"
2639 " Provide a detailed description of a specifid DUNDi peer.\n";
2642 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2645 return CLI_SHOWUSAGE;
2649 if (!strcasecmp(
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid), a->argv[3]))
2653 switch(peer->order) {
2658 order =
"Secondary";
2664 order =
"Quartiary";
2669 ast_cli(a->fd,
"Peer: %s\n",
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
2670 ast_cli(a->fd,
"Model: %s\n", model2str(peer->
model));
2671 ast_cli(a->fd,
"Order: %s\n", order);
2673 ast_cli(a->fd,
"Port: %d\n", dundi_sockaddr_port(&peer->
addr));
2674 ast_cli(a->fd,
"Dynamic: %s\n", peer->
dynamic ?
"yes" :
"no");
2675 ast_cli(a->fd,
"Reg: %s\n", peer->registerid < 0 ?
"No" :
"Yes");
2676 ast_cli(a->fd,
"In Key: %s\n", ast_strlen_zero(peer->inkey) ?
"<None>" : peer->inkey);
2677 ast_cli(a->fd,
"Out Key: %s\n", ast_strlen_zero(peer->outkey) ?
"<None>" : peer->outkey);
2679 ast_cli(a->fd,
"Include logic%s:\n", peer->
model & DUNDI_MODEL_OUTBOUND ?
"" :
" (IGNORED)");
2681 ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2683 ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2685 ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2688 if (peer->lookups[x]) {
2690 ast_cli(a->fd,
"Last few query times:\n");
2691 ast_cli(a->fd,
"-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2696 ast_cli(a->fd,
"Average query time: %d ms\n", peer->avgms);
2698 ast_cli(a->fd,
"No such peer '%s'\n", a->argv[3]);
2705 #define FORMAT2 "%-20.20s %-41s %-6.6s %-10.10s %-8.8s %-15.15s\n"
2706 #define FORMAT "%-20.20s %-41s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2708 int registeredonly=0;
2711 int online_peers = 0;
2712 int offline_peers = 0;
2713 int unmonitored_peers = 0;
2714 int total_peers = 0;
2717 e->
command =
"dundi show peers [registered|include|exclude|begin]";
2719 "Usage: dundi show peers [registered|include|exclude|begin]\n"
2720 " Lists all known DUNDi peers.\n"
2721 " If 'registered' is present, only registered peers are shown.\n";
2727 if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2728 return CLI_SHOWUSAGE;
2730 if ((a->argc == 4)) {
2731 if (!strcasecmp(a->argv[3],
"registered")) {
2734 return CLI_SHOWUSAGE;
2738 ast_cli(a->fd, FORMAT2,
"EID",
"Host",
"Port",
"Model",
"AvgTime",
"Status");
2741 int print_line = -1;
2750 strcpy(status,
"UNREACHABLE");
2754 snprintf(status,
sizeof(status),
"LAGGED (%d ms)", peer->
lastms);
2758 snprintf(status,
sizeof(status),
"OK (%d ms)", peer->
lastms);
2762 strcpy(status,
"UNKNOWN");
2766 strcpy(status,
"Unmonitored");
2767 unmonitored_peers++;
2770 snprintf(avgms,
sizeof(avgms),
"%d ms", peer->avgms);
2772 strcpy(avgms,
"Unavail");
2773 snprintf(srch,
sizeof(srch), FORMAT,
ast_eid_to_str(eid_str,
sizeof(eid_str),
2774 &peer->eid), dundi_sockaddr_stringify_host(&peer->
addr),
2775 peer->
dynamic ?
"(D)" :
"(S)", dundi_sockaddr_port(&peer->
addr), model2str(peer->
model), avgms, status);
2778 if (!strcasecmp(a->argv[3],
"include") && strstr(srch,a->argv[4])) {
2780 }
else if (!strcasecmp(a->argv[3],
"exclude") && !strstr(srch,a->argv[4])) {
2782 }
else if (!strcasecmp(a->argv[3],
"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2790 ast_cli(a->fd, FORMAT,
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid),
2791 dundi_sockaddr_stringify_host(&peer->
addr),
2792 peer->
dynamic ?
"(D)" :
"(S)", dundi_sockaddr_port(&peer->
addr), model2str(peer->
model), avgms, status);
2795 ast_cli(a->fd,
"%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2804 #define FORMAT2 "%-47s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2805 #define FORMAT "%-41s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2809 e->
command =
"dundi show trans";
2811 "Usage: dundi show trans\n"
2812 " Lists all known DUNDi transactions.\n";
2818 return CLI_SHOWUSAGE;
2821 ast_cli(a->fd, FORMAT2,
"Remote",
"Src",
"Dst",
"Tx",
"Rx",
"Ack");
2838 e->
command =
"dundi show entityid";
2840 "Usage: dundi show entityid\n"
2841 " Displays the global entityid for this host.\n";
2847 return CLI_SHOWUSAGE;
2852 ast_cli(a->fd,
"Global EID for this system is '%s'\n", eid_str);
2858 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2859 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2860 struct dundi_request *req;
2864 e->
command =
"dundi show requests";
2866 "Usage: dundi show requests\n"
2867 " Lists all known pending DUNDi requests.\n";
2873 return CLI_SHOWUSAGE;
2876 ast_cli(a->fd, FORMAT2,
"Number",
"Context",
"Root",
"Max",
"Rsp");
2878 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2879 dundi_eid_zero(&req->root_eid) ?
"<unspecified>" :
ast_eid_to_str(eidstr,
sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2891 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2892 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2898 e->
command =
"dundi show mappings";
2900 "Usage: dundi show mappings\n"
2901 " Lists all known DUNDi mappings.\n";
2907 return CLI_SHOWUSAGE;
2910 ast_cli(a->fd, FORMAT2,
"DUNDi Cntxt",
"Weight",
"Local Cntxt",
"Options",
"Tech",
"Destination");
2912 snprintf(weight,
sizeof(weight),
"%d", get_mapping_weight(map, NULL));
2913 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2914 ast_strlen_zero(map->lcontext) ?
"<none>" : map->lcontext,
2915 dundi_flags2str(fs,
sizeof(fs), map->options), tech2str(map->tech), map->dest);
2925 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2926 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2932 e->
command =
"dundi show precache";
2934 "Usage: dundi show precache\n"
2935 " Lists all known DUNDi scheduled precache updates.\n";
2941 return CLI_SHOWUSAGE;
2944 ast_cli(a->fd, FORMAT2,
"Number",
"Context",
"Expiration");
2947 s = qe->expiration - now;
2952 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2963 #define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s %-7s %s\n"
2964 #define FORMAT "%-12.12s %-16.16s %6d sec %-18s %-7d %s/%s (%s)\n"
2969 char src_eid_str[20];
2970 int expiry, tech, weight;
2974 char *ptr, *term, *src, *number, *context, *dst;
2978 e->
command =
"dundi show cache";
2980 "Usage: dundi show cache\n"
2981 " Lists all DUNDi cache entries.\n";
2988 return CLI_SHOWUSAGE;
2993 ast_cli(a->fd, FORMAT2,
"Number",
"Context",
"Expiration",
"From",
"Weight",
"Destination (Flags)");
2994 for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
2997 if ((strncmp(db_entry->key,
"/dundi/cache/hint/", 18) == 0) ||
ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3007 ptr = db_entry->key +
sizeof(
"/dundi/cache");
3008 strtok_r(ptr,
"/", &rest);
3009 number = strtok_r(NULL,
"/", &rest);
3010 context = strtok_r(NULL,
"/", &rest);
3011 ptr = strtok_r(NULL,
"/", &rest);
3017 ptr = db_entry->data + length + 1;
3019 if ((sscanf(ptr,
"%30u/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
3025 term = strchr(ptr,
'|');
3035 src = strrchr(ptr,
'/');
3036 dundi_eid_zero(&src_eid);
3041 dundi_str_short_to_eid(&src_eid, src);
3045 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs,
sizeof(fs), flags.flags));
3048 ast_cli(a->fd,
"Number of entries: %d\n", cnt);
3058 #define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s\n"
3059 #define FORMAT "%-12.12s %-16.16s %6d sec %-18s\n"
3064 char src_eid_str[20];
3067 char *ptr, *src, *number, *context;
3071 e->
command =
"dundi show hints";
3073 "Usage: dundi show hints\n"
3074 " Lists all DUNDi 'DONTASK' hints in the cache.\n";
3081 return CLI_SHOWUSAGE;
3086 ast_cli(a->fd, FORMAT2,
"Prefix",
"Context",
"Expiration",
"From");
3088 for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3101 ptr = db_entry->key +
sizeof(
"/dundi/cache/hint");
3102 src = strtok_r(ptr,
"/", &rest);
3103 number = strtok_r(NULL,
"/", &rest);
3104 context = strtok_r(NULL,
"/", &rest);
3105 ptr = strtok_r(NULL,
"/", &rest);
3112 dundi_str_short_to_eid(&src_eid, src);
3114 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
3117 ast_cli(a->fd,
"Number of entries: %d\n", cnt);
3126 AST_CLI_DEFINE(dundi_set_debug,
"Enable/Disable DUNDi debugging"),
3127 AST_CLI_DEFINE(dundi_store_history,
"Enable/Disable DUNDi historic records"),
3128 AST_CLI_DEFINE(dundi_flush,
"Flush DUNDi cache"),
3129 AST_CLI_DEFINE(dundi_show_peers,
"Show defined DUNDi peers"),
3130 AST_CLI_DEFINE(dundi_show_trans,
"Show active DUNDi transactions"),
3131 AST_CLI_DEFINE(dundi_show_entityid,
"Display Global Entity ID"),
3132 AST_CLI_DEFINE(dundi_show_mappings,
"Show DUNDi mappings"),
3133 AST_CLI_DEFINE(dundi_show_precache,
"Show DUNDi precache"),
3134 AST_CLI_DEFINE(dundi_show_requests,
"Show DUNDi requests"),
3135 AST_CLI_DEFINE(dundi_show_peer,
"Show info on a specific DUNDi peer"),
3136 AST_CLI_DEFINE(dundi_show_cache,
"Show DUNDi cache"),
3137 AST_CLI_DEFINE(dundi_show_hints,
"Show DUNDi hints in the cache"),
3138 AST_CLI_DEFINE(dundi_do_precache,
"Precache a number in DUNDi"),
3139 AST_CLI_DEFINE(dundi_do_lookup,
"Lookup a number in DUNDi"),
3140 AST_CLI_DEFINE(dundi_do_query,
"Query a DUNDi EID"),
3152 tid = get_trans_id();
3155 if (!(trans =
ast_calloc(1,
sizeof(*trans))))
3158 if (global_storehistory) {
3165 apply_peer(trans, p);
3166 if (!p->sentfullkey)
3179 dundi_showframe(pack->h, 0, &pack->parent->
addr, pack->datalen -
sizeof(
struct dundi_hdr));
3181 if (netsocket2 < 0) {
3182 res =
ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->
addr);
3185 res =
ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->
addr);
3187 res =
ast_sendto(netsocket2, pack->data, pack->datalen, 0, &pack->parent->
addr);
3192 ast_log(LOG_WARNING,
"Failed to transmit to '%s': %s\n",
3200 static void destroy_packet(
struct dundi_packet *pack,
int needfree)
3223 ast_log(LOG_NOTICE,
"Peer '%s' has become UNREACHABLE!\n",
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
3229 if (ms < peer->
maxms) {
3231 ast_log(LOG_NOTICE,
"Peer '%s' has become REACHABLE!\n",
ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid));
3233 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);
3240 if (trans->
parent && !ast_strlen_zero(trans->
parent->number)) {
3244 ast_free(peer->lookups[DUNDI_TIMING_HISTORY - 1]);
3245 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
3246 peer->lookuptimes[x] = peer->lookuptimes[x-1];
3247 peer->lookups[x] = peer->lookups[x-1];
3248 if (peer->lookups[x]) {
3249 peer->avgms += peer->lookuptimes[x];
3255 if (peer->lookups[0]) {
3256 sprintf(peer->lookups[0],
"%s@%s", trans->
parent->number, trans->
parent->dcontext);
3257 peer->avgms += peer->lookuptimes[0];
3272 if (trans->
parent->pfds[1] > -1) {
3273 if (write(trans->
parent->pfds[1],
"killa!", 6) < 0) {
3274 ast_log(LOG_WARNING,
"write() failed: %s\n", strerror(errno));
3281 destroy_packets(&trans->
packets);
3291 static int dundi_rexmit(
const void *data)
3296 if (pack->retrans < 1) {
3297 pack->retransid = -1;
3299 ast_log(LOG_NOTICE,
"Max retries exceeded to host '%s' msg %d on call %d\n",
3302 destroy_trans(pack->parent, 1);
3322 if (ast_test_flag(trans, FLAG_ENCRYPT))
3326 pack->h = (
struct dundi_hdr *)(pack->data);
3327 pack->retransid = -1;
3330 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3333 pack->parent = trans;
3339 pack->datalen =
sizeof(
struct dundi_hdr);
3341 memcpy(pack->h->ies, ied->buf, ied->pos);
3342 pack->datalen += ied->pos;
3366 dundi_showframe(pack->h, 2, &trans->
addr, pack->datalen -
sizeof(
struct dundi_hdr));
3367 res = dundi_encrypt(trans, pack);
3375 res = dundi_xmit(pack);
3377 ast_log(LOG_NOTICE,
"Failed to send packet to '%s'\n",
ast_eid_to_str(eid_str,
sizeof(eid_str), &trans->
them_eid));
3386 static int do_autokill(
const void *data)
3390 ast_log(LOG_NOTICE,
"Transaction to '%s' took too long to ACK, destroying\n",
3393 destroy_trans(trans, 0);
3407 if (has_permission(&p->include, context))
3424 ast_log(LOG_WARNING,
"Tried to discover a transaction with no parent?!?\n");
3427 memset(&ied, 0,
sizeof(ied));
3429 if (!dundi_eid_zero(&trans->
us_eid))
3432 dundi_ie_append_eid_appropriately(&ied, trans->
parent->dcontext, &trans->eids[x], &trans->
us_eid);
3436 if (trans->
parent->cbypass)
3448 int expiration = dundi_cache_time;
3451 int direct[1] = { 0, };
3455 ast_log(LOG_WARNING,
"Tried to discover a transaction with no parent?!?\n");
3458 memset(&hmd, 0,
sizeof(hmd));
3459 memset(&dr, 0,
sizeof(dr));
3461 for (x=0;x<mapcount;x++)
3462 ouranswers = dundi_lookup_local(dr, maps + x, trans->
parent->number, &trans->
us_eid, ouranswers, &hmd);
3465 for (x=0;x<ouranswers;x++) {
3466 if (dr[x].weight < max)
3471 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);
3478 if (ouranswers > 0) {
3479 *foundanswers += ouranswers;
3480 memset(&ied, 0,
sizeof(ied));
3482 if (!dundi_eid_zero(&trans->
us_eid))
3485 dundi_ie_append_eid(&ied,
DUNDI_IE_EID, &trans->eids[x]);
3489 for (x=0;x<ouranswers;x++) {
3491 if (dr[x].expiration && (expiration > dr[x].expiration))
3492 expiration = dr[x].expiration;
3493 dundi_ie_append_answer(&ied,
DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3495 dundi_ie_append_hint(&ied,
DUNDI_IE_HINT, hmd.flags, hmd.exten);
3499 if (expiration < *minexp)
3500 *minexp = expiration;
3504 destroy_trans(trans, 0);
3514 ast_log(LOG_WARNING,
"Tried to query a transaction with no parent?!?\n");
3517 memset(&ied, 0,
sizeof(ied));
3519 if (!dundi_eid_zero(&trans->
us_eid))
3522 dundi_ie_append_eid(&ied,
DUNDI_IE_EID, &trans->eids[x]);
3531 static int discover_transactions(
struct dundi_request *dr)
3536 dundi_discover(trans);
3542 static int precache_transactions(
struct dundi_request *dr,
struct dundi_mapping *maps,
int mapcount,
int *expiration,
int *foundanswers)
3550 ast_log(LOG_WARNING,
"This shouldn't happen, really...\n");
3557 precache_trans(trans, maps, mapcount, expiration, foundanswers);
3565 ast_debug(1,
"Our transaction went away!\n");
3568 destroy_trans(trans, 0);
3577 static int query_transactions(
struct dundi_request *dr)
3590 static int optimize_transactions(
struct dundi_request *dr,
int order)
3604 tmp = trans->eids[--trans->
eidcount];
3614 has_permission(&peer->include, dr->dcontext) &&
3616 (peer->order <= order)) {
3630 if (trans->
eidcount < DUNDI_MAX_STACK - needpush) {
3631 trans->eids[trans->
eidcount++] = peer->eid;
3641 trans->eids[trans->
eidcount++] = tmp;
3648 static int append_transaction(
struct dundi_request *dr,
struct dundi_peer *p,
int ttl,
dundi_eid *avoid[])
3662 if (ast_strlen_zero(dr->number))
3663 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);
3665 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);
3667 trans = create_transaction(p);
3672 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3673 trans->eids[x] = *avoid[x];
3680 static void cancel_request(
struct dundi_request *dr)
3694 static void abort_request(
struct dundi_request *dr)
3701 destroy_trans(trans, 0);
3706 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[])
3716 if (modeselect == 1) {
3718 pass = has_permission(&p->permit, dr->dcontext) && (p->
pcmodel & DUNDI_MODEL_OUTBOUND);
3722 pass = has_permission(&p->include, dr->dcontext);
3723 allowconnect = p->
model & DUNDI_MODEL_OUTBOUND;
3730 if (p->order <= order) {
3734 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->
crc32, &dr->expiration)))) {
3738 for (x=0;avoid[x];x++) {
3741 if (directs && !directs[x])
3748 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3750 append_transaction(dr, p, ttl, avoid);
3757 }
else if (!*skipped || (p->order < *skipped))
3758 *skipped = p->order;
3764 static int register_request(
struct dundi_request *dr,
struct dundi_request **pending)
3766 struct dundi_request *cur;
3771 ast_debug(1,
"Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3772 dr->dcontext, dr->number);
3773 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3774 !strcasecmp(cur->number, dr->number) &&
3776 ast_debug(1,
"Found existing query for '%s@%s' for '%s' crc '%08x'\n",
3777 cur->dcontext, cur->number,
ast_eid_to_str(eid_str,
sizeof(eid_str), &cur->root_eid), cur->
crc32);
3784 ast_debug(1,
"Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
3785 dr->number, dr->dcontext,
ast_eid_to_str(eid_str,
sizeof(eid_str), &dr->root_eid), dr->
crc32);
3794 static void unregister_request(
struct dundi_request *dr)
3801 static int check_request(
struct dundi_request *dr)
3803 struct dundi_request *cur;
3815 static unsigned long avoid_crc32(
dundi_eid *avoid[])
3819 uint32_t acrc32 = 0;
3821 for (x=0;avoid[x];x++) {
3824 acrc32 ^=
crc32(0L, (
unsigned char *)avoid[x],
sizeof(
dundi_eid));
3830 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[])
3833 struct dundi_request dr, *pending;
3842 struct timeval start;
3850 for (x=0;avoid[x];x++)
3853 memset(&dr, 0,
sizeof(dr));
3854 if (pipe(dr.pfds)) {
3855 ast_log(LOG_WARNING,
"pipe failed: %s\n" , strerror(errno));
3860 dr.maxcount = maxret;
3861 dr.expiration = *expiration;
3862 dr.cbypass = cbypass;
3863 dr.
crc32 = avoid_crc32(avoid);
3864 ast_copy_string(dr.dcontext, dcontext ? dcontext :
"e164",
sizeof(dr.dcontext));
3867 dr.root_eid = *rooteid;
3868 res = register_request(&dr, &pending);
3871 if (rooteid && !
ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3874 ast_debug(1,
"Oooh, duplicate request for '%s@%s' for '%s'\n",
3875 dr.number,dr.dcontext,
ast_eid_to_str(eid_str,
sizeof(eid_str), &dr.root_eid));
3881 ast_debug(1,
"Waiting for similar request for '%s@%s' for '%s'\n",
3882 dr.number,dr.dcontext,
ast_eid_to_str(eid_str,
sizeof(eid_str), &pending->root_eid));
3897 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3905 unregister_request(&dr);
3912 optimize_transactions(&dr, order);
3914 discover_transactions(&dr);
3922 ast_debug(1,
"Hrm, '%s' hungup before their query for %s@%s finished\n", ast_channel_name(chan), dr.number, dr.dcontext);
3923 cancel_request(&dr);
3924 unregister_request(&dr);
3926 *expiration = dr.expiration;
3936 int direct[1] = { 0, };
3937 int expiration = dundi_cache_time;
3938 memset(&hmd, 0,
sizeof(hmd));
3940 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3943 static void reschedule_precache(
const char *number,
const char *context,
int expiration)
3949 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3956 int len =
sizeof(*qe);
3957 int num_len = strlen(number) + 1;
3958 int context_len = strlen(context) + 1;
3959 if (!(qe =
ast_calloc(1, len + num_len + context_len))) {
3963 strcpy(qe->number, number);
3964 qe->context = qe->number + num_len + 1;
3967 time(&qe->expiration);
3968 qe->expiration += expiration;
3978 static void dundi_precache_full(
void)
3985 ast_log(LOG_NOTICE,
"Should precache context '%s'\n", cur->dcontext);
3988 while ((con = ast_walk_contexts(con))) {
3989 if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
3994 while ((e = ast_walk_context_extensions(con, e)))
3995 reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
4002 static int dundi_precache_internal(
const char *context,
const char *number,
int ttl,
dundi_eid *avoids[])
4004 struct dundi_request dr;
4007 struct timeval start;
4011 int foundcache, skipped, ttlms, ms;
4014 ast_debug(1,
"Precache internal (%s@%s)!\n", number, context);
4018 if (!strcasecmp(cur->dcontext, context))
4025 if (!strcasecmp(cur->dcontext, context))
4026 maps[nummaps++] = *cur;
4034 memset(&dr2, 0,
sizeof(dr2));
4035 memset(&dr, 0,
sizeof(dr));
4036 memset(&hmd, 0,
sizeof(hmd));
4039 ast_copy_string(dr.dcontext, context ? context :
"e164",
sizeof(dr.dcontext));
4040 dr.maxcount = MAX_RESULTS;
4041 dr.expiration = dundi_cache_time;
4043 dr.pfds[0] = dr.pfds[1] = -1;
4044 if (pipe(dr.pfds) < 0) {
4045 ast_log(LOG_WARNING,
"pipe() failed: %s\n", strerror(errno));
4048 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
4049 optimize_transactions(&dr, 0);
4051 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
4053 if (dr.expiration > 0)
4054 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
4056 ast_log(LOG_NOTICE,
"Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
4060 if (dr.pfds[0] > -1) {
4066 cancel_request(&dr);
4067 if (dr.pfds[0] > -1) {
4077 return dundi_precache_internal(context, number, dundi_ttl, avoid);
4083 struct dundi_request dr;
4089 struct timeval start;
4093 for (x=0;avoid[x];x++)
4096 memset(&dr, 0,
sizeof(dr));
4099 dr.pfds[0] = dr.pfds[1] = -1;
4100 ast_copy_string(dr.dcontext, dcontext ? dcontext :
"e164",
sizeof(dr.dcontext));
4101 memcpy(&dr.query_eid, eid,
sizeof(dr.query_eid));
4103 dr.root_eid = *rooteid;
4105 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
4116 optimize_transactions(&dr, 9999);
4118 query_transactions(&dr);
4131 memset(&hmd, 0,
sizeof(hmd));
4132 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
4136 OPT_BYPASS_CACHE = (1 << 0),
4143 static int dundifunc_read(
struct ast_channel *chan,
const char *cmd,
char *num,
char *buf,
size_t len)
4158 if (ast_strlen_zero(num)) {
4159 ast_log(LOG_WARNING,
"DUNDILOOKUP requires an argument (number)\n");
4167 if (!ast_strlen_zero(args.options)) {
4170 if (ast_strlen_zero(args.context)) {
4171 args.context =
"e164";
4174 results =
dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4176 sort_results(dr, results);
4177 for (x = 0; x < results; x++) {
4179 snprintf(buf, len,
"%s/%s", dr[x].tech, dr[x].dest);
4193 .
name =
"DUNDILOOKUP",
4194 .read = dundifunc_read,
4197 static unsigned int dundi_result_id;
4210 static void drds_destroy_cb(
void *data)
4217 .
type =
"DUNDIQUERY",
4218 .destroy = drds_destroy_cb,
4221 static int dundi_query_read(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
4233 if (ast_strlen_zero(data)) {
4234 ast_log(LOG_WARNING,
"DUNDIQUERY requires an argument (number)\n");
4239 ast_log(LOG_ERROR,
"DUNDIQUERY can not be used without a channel!\n");
4247 if (!ast_strlen_zero(args.options))
4250 if (ast_strlen_zero(args.context))
4251 args.context =
"e164";
4253 if (!(drds =
ast_calloc(1,
sizeof(*drds)))) {
4258 snprintf(buf, len,
"%u", drds->id);
4260 if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
4265 datastore->
data = drds;
4267 drds->num_results =
dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
4268 args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4270 if (drds->num_results > 0)
4271 sort_results(drds->results, drds->num_results);
4273 ast_channel_lock(chan);
4275 ast_channel_unlock(chan);
4281 .
name =
"DUNDIQUERY",
4282 .read = dundi_query_read,
4285 static int dundi_result_read(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
4297 if (ast_strlen_zero(data)) {
4298 ast_log(LOG_WARNING,
"DUNDIRESULT requires an argument (id and resultnum)\n");
4303 ast_log(LOG_ERROR,
"DUNDRESULT can not be used without a channel!\n");
4311 if (ast_strlen_zero(args.id)) {
4312 ast_log(LOG_ERROR,
"A result ID must be provided to DUNDIRESULT\n");
4316 if (ast_strlen_zero(args.resultnum)) {
4317 ast_log(LOG_ERROR,
"A result number must be given to DUNDIRESULT!\n");
4321 ast_channel_lock(chan);
4323 ast_channel_unlock(chan);
4326 ast_log(LOG_WARNING,
"No DUNDi results found for query ID '%s'\n", args.id);
4330 drds = datastore->
data;
4332 if (!strcasecmp(args.resultnum,
"getnum")) {
4333 snprintf(buf, len,
"%d", drds->num_results < 0 ? 0 : drds->num_results);
4338 if (sscanf(args.resultnum,
"%30u", &num) != 1) {
4339 ast_log(LOG_ERROR,
"Invalid value '%s' for resultnum to DUNDIRESULT!\n",
4344 if (num && drds->num_results > 0 && num <= drds->num_results) {
4345 snprintf(buf, len,
"%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
4348 ast_log(LOG_WARNING,
"Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
4355 .
name =
"DUNDIRESULT",
4356 .read = dundi_result_read,
4359 static void mark_peers(
void)
4369 static void mark_mappings(
void)
4380 static void destroy_permissions(
struct permissionlist *permlist)
4388 static void destroy_peer(
struct dundi_peer *peer)
4401 destroy_permissions(&peer->permit);
4402 destroy_permissions(&peer->include);
4405 for (idx = 0; idx < ARRAY_LEN(peer->lookups); ++idx) {
4406 ast_free(peer->lookups[idx]);
4414 ast_free(map->weightstr);
4418 static void prune_peers(
void)
4433 static void prune_mappings(
void)
4448 static void append_permission(
struct permissionlist *permlist,
const char *s,
int allow)
4452 if (!(perm =
ast_calloc(1,
sizeof(*perm) + strlen(s) + 1)))
4455 strcpy(perm->name, s);
4456 perm->allow = allow;
4461 #define MAX_OPTS 128
4463 static void build_mapping(
const char *name,
const char *value)
4465 char *t, *fields[MAX_OPTS];
4474 if (!strcasecmp(map->dcontext, name) &&
4475 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
4476 (!value[strlen(map->lcontext)] ||
4477 (value[strlen(map->lcontext)] ==
','))))
4487 memset(fields, 0,
sizeof(fields));
4489 while (t && x < MAX_OPTS) {
4497 if ((x == 1) && ast_strlen_zero(fields[0])) {
4501 }
else if (x >= 4) {
4504 if ((sscanf(fields[1],
"%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
4506 if ((map->tech = str2tech(fields[2])))
4508 }
else if (!strncmp(fields[1],
"${", 2) && fields[1][strlen(fields[1]) - 1] ==
'}') {
4511 if ((map->tech = str2tech(fields[2])))
4514 ast_log(LOG_WARNING,
"Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
4516 for (y = 4;y < x; y++) {
4517 if (!strcasecmp(fields[y],
"nounsolicited"))
4519 else if (!strcasecmp(fields[y],
"nocomunsolicit"))
4521 else if (!strcasecmp(fields[y],
"residential"))
4523 else if (!strcasecmp(fields[y],
"commercial"))
4525 else if (!strcasecmp(fields[y],
"mobile"))
4527 else if (!strcasecmp(fields[y],
"nopartial"))
4528 map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
4530 ast_log(LOG_WARNING,
"Don't know anything about option '%s'\n", fields[y]);
4533 ast_log(LOG_WARNING,
"Expected at least %d arguments in map, but got only %d\n", 4, x);
4548 peer->
regtrans = create_transaction(peer);
4551 memset(&ied, 0,
sizeof(ied));
4558 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));
4563 static int do_qualify(
const void *data)
4566 peer->qualifyid = -1;
4567 qualify_peer(peer, 0);
4571 static void qualify_peer(
struct dundi_peer *peer,
int schedonly)
4578 if (peer->
maxms > 0) {
4584 peer->qualifyid =
ast_sched_add(sched, when, do_qualify, peer);
4586 peer->
qualtrans = create_transaction(peer);
4601 if (!
ast_db_get(
"dundi/dpeers", eid_str, data,
sizeof(data))) {
4618 if (sscanf(c,
"%5d:%30d", &port, &expire) == 2) {
4650 if (!(peer =
ast_calloc(1,
sizeof(*peer)))) {
4654 peer->registerid = -1;
4655 peer->registerexpire = -1;
4656 peer->qualifyid = -1;
4657 populate_addr(peer, eid);
4662 peer->us_eid = global_eid;
4663 destroy_permissions(&peer->permit);
4664 destroy_permissions(&peer->include);
4666 for (; v; v = v->
next) {
4667 if (!strcasecmp(v->
name,
"inkey")) {
4669 }
else if (!strcasecmp(v->
name,
"outkey")) {
4671 }
else if (!strcasecmp(v->
name,
"port")) {
4672 port = atoi(v->
value);
4673 }
else if (!strcasecmp(v->
name,
"host")) {
4674 if (!strcasecmp(v->
value,
"dynamic")) {
4684 ast_log(LOG_WARNING,
"Unable to find host '%s' at line %d\n", v->
value, v->lineno);
4688 }
else if (!strcasecmp(v->
name,
"ustothem")) {
4690 peer->us_eid = testeid;
4692 ast_log(LOG_WARNING,
"'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->
value, v->lineno);
4693 }
else if (!strcasecmp(v->
name,
"include")) {
4694 append_permission(&peer->include, v->
value, 1);
4695 }
else if (!strcasecmp(v->
name,
"permit")) {
4696 append_permission(&peer->permit, v->
value, 1);
4697 }
else if (!strcasecmp(v->
name,
"noinclude")) {
4698 append_permission(&peer->include, v->
value, 0);
4699 }
else if (!strcasecmp(v->
name,
"deny")) {
4700 append_permission(&peer->permit, v->
value, 0);
4701 }
else if (!strcasecmp(v->
name,
"register")) {
4703 }
else if (!strcasecmp(v->
name,
"order")) {
4704 if (!strcasecmp(v->
value,
"primary"))
4706 else if (!strcasecmp(v->
value,
"secondary"))
4708 else if (!strcasecmp(v->
value,
"tertiary"))
4710 else if (!strcasecmp(v->
value,
"quartiary"))
4713 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);
4715 }
else if (!strcasecmp(v->
name,
"qualify")) {
4716 if (!strcasecmp(v->
value,
"no")) {
4718 }
else if (!strcasecmp(v->
value,
"yes")) {
4719 peer->
maxms = DEFAULT_MAXMS;
4720 }
else if (sscanf(v->
value,
"%30d", &peer->
maxms) != 1) {
4721 ast_log(LOG_WARNING,
"Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4722 ast_eid_to_str(eid_str,
sizeof(eid_str), &peer->eid), v->lineno);
4725 }
else if (!strcasecmp(v->
name,
"model")) {
4726 if (!strcasecmp(v->
value,
"inbound"))
4727 peer->
model = DUNDI_MODEL_INBOUND;
4728 else if (!strcasecmp(v->
value,
"outbound"))
4729 peer->
model = DUNDI_MODEL_OUTBOUND;
4730 else if (!strcasecmp(v->
value,
"symmetric"))
4731 peer->
model = DUNDI_MODEL_SYMMETRIC;
4732 else if (!strcasecmp(v->
value,
"none"))
4735 ast_log(LOG_WARNING,
"Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4736 v->
value, v->lineno);
4738 }
else if (!strcasecmp(v->
name,
"precache")) {
4739 if (!strcasecmp(v->
value,
"inbound"))
4740 peer->
pcmodel = DUNDI_MODEL_INBOUND;
4741 else if (!strcasecmp(v->
value,
"outbound"))
4742 peer->
pcmodel = DUNDI_MODEL_OUTBOUND;
4743 else if (!strcasecmp(v->
value,
"symmetric"))
4744 peer->
pcmodel = DUNDI_MODEL_SYMMETRIC;
4745 else if (!strcasecmp(v->
value,
"none"))
4748 ast_log(LOG_WARNING,
"Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4749 v->
value, v->lineno);
4758 (*globalpcmode) |= peer->
pcmodel;
4760 ast_log(LOG_WARNING,
"Peer '%s' lacks a model or pcmodel, discarding!\n",
4763 }
else if ((peer->
model & DUNDI_MODEL_INBOUND) && (peer->
pcmodel & DUNDI_MODEL_OUTBOUND)) {
4764 ast_log(LOG_WARNING,
"Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4767 }
else if ((peer->
model & DUNDI_MODEL_OUTBOUND) && (peer->
pcmodel & DUNDI_MODEL_INBOUND)) {
4768 ast_log(LOG_WARNING,
"Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4771 }
else if (!
AST_LIST_EMPTY(&peer->include) && !(peer->
model & DUNDI_MODEL_OUTBOUND) && !(peer->
pcmodel & DUNDI_MODEL_INBOUND)) {
4772 ast_log(LOG_WARNING,
"Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4774 }
else if (!
AST_LIST_EMPTY(&peer->permit) && !(peer->
model & DUNDI_MODEL_INBOUND) && !(peer->
pcmodel & DUNDI_MODEL_OUTBOUND)) {
4775 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",
4783 qualify_peer(peer, 1);
4789 static int dundi_helper(
struct ast_channel *chan,
const char *context,
const char *exten,
int priority,
const char *data,
int flag)
4795 if (ast_strlen_zero(data))
4798 res =
dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4799 for (x=0;x<res;x++) {
4800 if (ast_test_flag(results + x, flag))
4803 if (found >= priority)
4808 static int dundi_exists(
struct ast_channel *chan,
const char *context,
const char *exten,
int priority,
const char *callerid,
const char *data)
4813 static int dundi_canmatch(
struct ast_channel *chan,
const char *context,
const char *exten,
int priority,
const char *callerid,
const char *data)
4818 static int dundi_exec(
struct ast_channel *chan,
const char *context,
const char *exten,
int priority,
const char *callerid,
const char *data)
4824 const char *dundiargs;
4826 if (ast_strlen_zero(data))
4829 res =
dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4831 sort_results(results, res);
4832 for (x=0;x<res;x++) {
4846 if (!strcasecmp(results[x].tech,
"SIP") || !strcasecmp(results[x].tech,
"PJSIP")) {
4849 if (strcasecmp(results[x].tech,
"SIP")) {
4850 ast_log(LOG_WARNING,
"%s cannot be specified by DUNDi peers (peer should use SIP for DUNDi lookups instead)\n", results[x].tech);
4853 results[x].techint = outgoing_sip_tech;
4854 ast_copy_string(results[x].tech, tech2str(outgoing_sip_tech),
sizeof(results[x].tech));
4859 if (ast_strlen_zero(pjsip_outgoing_endpoint)) {
4860 ast_log(LOG_WARNING,
"PJSIP calls require an endpoint to be specified explicitly (use the pjsip_outgoing_endpoint option in dundi.conf)\n");
4864 if (ast_strlen_zero(ip)) {
4865 ast_log(LOG_WARNING,
"PJSIP destination is empty?\n");
4868 number = strsep(&ip,
"/");
4869 snprintf(req,
sizeof(req),
"%s/%s/sip:%s@%s,,%s", results[x].tech, pjsip_outgoing_endpoint,
S_OR(number,
""), ip,
S_OR(dundiargs,
""));
4870 ast_debug(1,
"Finalized PJSIP Dial: %s\n", req);
4872 snprintf(req,
sizeof(req),
"%s/%s,,%s", results[x].tech, results[x].dest,
S_OR(dundiargs,
""));
4881 static int dundi_matchmore(
struct ast_channel *chan,
const char *context,
const char *exten,
int priority,
const char *callerid,
const char *data)
4888 .description =
"DUNDi Discovered Dialplan Switch",
4889 .exists = dundi_exists,
4890 .canmatch = dundi_canmatch,
4892 .matchmore = dundi_matchmore,
4895 static int get_ipaddress(
char *ip,
size_t size,
const char *str,
int family)
4911 char hn[MAXHOSTNAMELEN];
4912 struct addrinfo
hints;
4923 if (gethostname(hn,
sizeof(hn) - 1) < 0) {
4924 ast_log(LOG_WARNING,
"Unable to get host name!\n");
4928 get_ipaddress(ipaddr,
sizeof(ipaddr), hn, family);
4938 static int last_port = 0;
4940 int globalpcmodel = 0;
4942 char bind_addr[80]={0,};
4943 char bind_addr2[80]={0,};
4945 if (!(cfg =
ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
4946 ast_log(LOG_ERROR,
"Unable to load config %s\n", config_file);
4958 ast_log(LOG_WARNING,
"Entity ID is not set.\n");
4962 global_storehistory = 0;
4964 v = ast_variable_browse(cfg,
"general");
4966 if (!strcasecmp(v->
name,
"port")){
4967 port = atoi(v->
value);
4968 }
else if (!strcasecmp(v->
name,
"bindaddr")) {
4969 if (get_ipaddress(bind_addr,
sizeof(bind_addr), v->
value, AF_UNSPEC) == 0) {
4971 ast_log(LOG_WARNING,
"Invalid host/IP '%s'\n", v->
value);
4974 }
else if (!strcasecmp(v->
name,
"bindaddr2")) {
4975 if (get_ipaddress(bind_addr2,
sizeof(bind_addr2), v->
value, AF_UNSPEC) == 0) {
4977 ast_log(LOG_WARNING,
"Invalid host/IP '%s'\n", v->
value);
4980 }
else if (!strcasecmp(v->
name,
"authdebug")) {
4982 }
else if (!strcasecmp(v->
name,
"ttl")) {
4986 ast_log(LOG_WARNING,
"'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4989 }
else if (!strcasecmp(v->
name,
"autokill")) {
4990 if (sscanf(v->
value,
"%30d", &x) == 1) {
4992 global_autokilltimeout = x;
4994 ast_log(LOG_NOTICE,
"Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
4996 global_autokilltimeout = DEFAULT_MAXMS;
4998 global_autokilltimeout = 0;
5000 }
else if (!strcasecmp(v->
name,
"entityid")) {
5002 global_eid = testeid;
5004 ast_log(LOG_WARNING,
"Invalid global endpoint identifier '%s' at line %d\n", v->
value, v->lineno);
5005 }
else if (!strcasecmp(v->
name,
"tos")) {
5007 ast_log(LOG_WARNING,
"Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
5008 }
else if (!strcasecmp(v->
name,
"department")) {
5010 }
else if (!strcasecmp(v->
name,
"organization")) {
5012 }
else if (!strcasecmp(v->
name,
"locality")) {
5014 }
else if (!strcasecmp(v->
name,
"stateprov")) {
5016 }
else if (!strcasecmp(v->
name,
"country")) {
5018 }
else if (!strcasecmp(v->
name,
"email")) {
5020 }
else if (!strcasecmp(v->
name,
"phone")) {
5022 }
else if (!strcasecmp(v->
name,
"storehistory")) {
5024 }
else if (!strcasecmp(v->
name,
"outgoing_sip_tech")) {
5025 int outgoing_tech = str2tech(v->
value);
5027 ast_log(LOG_WARNING,
"outgoing_sip_tech must be SIP or PJSIP\n");
5029 outgoing_sip_tech = outgoing_tech;
5031 }
else if (!strcasecmp(v->
name,
"pjsip_outgoing_endpoint")) {
5033 }
else if (!strcasecmp(v->
name,
"cachetime")) {
5034 if ((sscanf(v->
value,
"%30d", &x) == 1)) {
5035 dundi_cache_time = x;
5037 ast_log(LOG_WARNING,
"'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
5049 sprintf(bind_addr,
"0.0.0.0:%d", port);
5055 if (last_port == 0) {
5057 }
else if (last_port != port) {
5058 ast_log(LOG_WARNING,
"change to port ignored until next asterisk re-start\n");
5061 set_host_ipaddr(sin);
5070 v = ast_variable_browse(cfg,
"mappings");
5082 if (strcasecmp(cat,
"general") && strcasecmp(cat,
"mappings")) {
5085 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
5086 else if (!strcasecmp(cat,
"*")) {
5087 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
5090 ast_log(LOG_NOTICE,
"Ignoring invalid EID entry '%s'\n", cat);
5098 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
5099 dundi_precache_full();
5103 static int unload_module(
void)
5113 if (netthreadid != AST_PTHREADT_NULL) {
5114 pthread_kill(netthreadid, SIGURG);
5115 pthread_join(netthreadid, NULL);
5116 netthreadid = AST_PTHREADT_NULL;
5118 if (precachethreadid != AST_PTHREADT_NULL) {
5119 pthread_kill(precachethreadid, SIGURG);
5120 pthread_join(precachethreadid, NULL);
5121 precachethreadid = AST_PTHREADT_NULL;
5123 if (clearcachethreadid != AST_PTHREADT_NULL) {
5124 pthread_cancel(clearcachethreadid);
5125 pthread_join(clearcachethreadid, NULL);
5126 clearcachethreadid = AST_PTHREADT_NULL;
5129 if (netsocket >= 0) {
5133 if (netsocket2 >= 0) {
5142 if (-1 < netsocket) {
5159 static int reload(
void)
5167 if (set_config(
"dundi.conf", &sin, 1, &sin2))
5173 static int load_module(
void)
5178 dundi_set_output(dundi_debug_output);
5179 dundi_set_error(dundi_error_output);
5185 if (!io || !sched) {
5192 if (set_config(
"dundi.conf", &sin, 0, &sin2)) {
5198 ast_log(LOG_ERROR,
"bindaddr & bindaddr2 should be different IP protocols.\n");
5204 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5205 netsocket2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5206 if (netsocket < 0 || netsocket2 < 0) {
5207 ast_log(LOG_ERROR,
"Unable to create network socket: %s\n", strerror(errno));
5212 ast_log(LOG_ERROR,
"Unable to bind to %s : %s\n",
5217 ast_log(LOG_ERROR,
"Unable to bind to %s : %s\n",
5223 ast_log(LOG_ERROR,
"Unable to bind to %s : %s\n",
5228 ast_log(LOG_ERROR,
"Unable to bind to %s : %s\n",
5237 netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5239 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5241 if (netsocket < 0) {
5242 ast_log(LOG_ERROR,
"Unable to create network socket: %s\n", strerror(errno));
5246 ast_log(LOG_ERROR,
"Unable to bind to %s : %s\n",
5253 if (start_network_thread()) {
5254 ast_log(LOG_ERROR,
"Unable to start network thread\n");
5260 ast_log(LOG_ERROR,
"Unable to register DUNDi switch\n");
5276 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
"Distributed Universal Number Discovery (DUNDi)",
5277 .support_level = AST_MODULE_SUPPORT_EXTENDED,
5278 .load = load_module,
5279 .unload = unload_module,
5281 .optional_modules =
"res_crypto",
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
int ast_io_wait(struct io_context *ioc, int howlong)
Waits for IO.
struct ast_variable * next
int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
Retrieve information on a specific EID.
int ast_unlock_context(struct ast_context *con)
#define DUNDI_IE_EXPIRATION
int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks to see if adding anything to this extension might match something. (exists ^ canmatch) ...
Main Channel structure associated with a channel.
int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
Lookup the given number in the given dundi context. Lookup number in a given dundi context (if unspec...
ssize_t ast_sendto(int sockfd, const void *buf, size_t len, int flags, const struct ast_sockaddr *dest_addr)
Wrapper around sendto(2) that uses ast_sockaddr.
#define AST_LIST_LOCK(head)
Locks a list.
#define DUNDI_IE_EID_DIRECT
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
static struct iax2_peer * find_peer(const char *name, int realtime)
Distributed Universal Number Discovery (DUNDi) See also.
struct dundi_request * parent
#define DUNDI_IE_CACHEBYPASS
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
#define DUNDI_COMMAND_ENCREJ
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
static int do_register_expire(const void *data)
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
#define DUNDI_TIMING_HISTORY
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
#define DUNDI_DEFAULT_CACHE_TIME
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define DUNDI_IE_SHAREDKEY
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Provide cryptographic signature routines.
Structure for variables, used for configurations and for channel variables.
unsigned char rxenckey[256]
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
int ast_ignore_pattern(const char *context, const char *pattern)
Checks to see if a number should be ignored.
Structure for a data store type.
int * ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
Adds an IO context.
void ast_unregister_switch(struct ast_switch *sw)
Unregister an alternative switch.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
int ast_rdlock_contexts(void)
Read locks the context list.
#define DUNDI_COMMAND_ENCRYPT
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
Structure for a data store object.
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_str2tos(const char *value, unsigned int *tos)
Convert a string to the appropriate TOS value.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
struct packetlist lasttrans
int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
Compares two ast_sockaddr structures.
I/O Management (derived from Cheops-NG)
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
#define DUNDI_IE_DEPARTMENT
static int do_register(const void *data)
#define DUNDI_COMMAND_ACK
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Socket address structure.
int ast_bind(int sockfd, const struct ast_sockaddr *addr)
Wrapper around bind(2) that uses struct ast_sockaddr.
An Entity ID is essentially a MAC address, brief and unique.
ast_aes_decrypt_key them_dcx
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
struct dundi_request::@421 trans
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
#define AST_LIST_INSERT_AFTER(head, listelm, elm, field)
Inserts a list entry after a given entry.
static void ast_sockaddr_setnull(struct ast_sockaddr *addr)
Sets address addr to null.
int args
This gets set in ast_cli_register()
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
static int ast_sockaddr_isnull(const struct ast_sockaddr *addr)
Checks if the ast_sockaddr is null. "null" in this sense essentially means uninitialized, or having a 0 length.
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Configuration File Parser.
static const char config_file[]
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
Parse a time (integer) string.
static struct iax2_peer * build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
Create peer structure based on configuration.
#define DUNDI_COMMAND_DPRESPONSE
#define DUNDI_IE_LOCALITY
#define ast_config_load(filename, flags)
Load a config file.
#define DUNDI_IE_KEYCRC32
#define DUNDI_IE_SIGNATURE
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Waits for input on an fd.
static struct dundi_peer * any_peer
Wildcard peer.
General Asterisk PBX channel definitions.
#define AST_SCHED_DEL(sched, id)
Remove a scheduler entry.
#define DUNDI_DEFAULT_TTL
void io_context_destroy(struct io_context *ioc)
Destroys a context.
ast_aes_decrypt_key us_dcx
#define ast_strdupa(s)
duplicate a string in memory from the stack
Data structure associated with a custom dialplan function.
Access Control of various sorts.
Global IO variables are now in a struct in order to be made threadsafe.
#define AST_MAX_EXTENSION
Scheduler Routines (derived from cheops)
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Asterisk internal frame definitions.
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static struct stasis_rest_handlers events
REST handler for /api-docs/events.json.
#define ast_malloc(len)
A wrapper for malloc()
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
struct dundi_transaction::@419 parentlist
struct dundi_transaction::packetlist packets
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
static struct ast_custom_function dundi_function
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
ast_aes_encrypt_key them_ecx
Wrapper for network related headers, masking differences between various operating systems...
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
int ast_pbx_exec_application(struct ast_channel *chan, const char *app_name, const char *app_args)
Execute an application.
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
#define DUNDI_COMMAND_UNKNOWN
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
int AST_OPTIONAL_API_NAME() ast_encrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
encrypt a message
int ast_set_qos(int sockfd, int tos, int cos, const char *desc)
Set type of service.
int ast_unlock_contexts(void)
Unlocks contexts.
struct dundi_transaction * qualtrans
#define DUNDI_IE_CALLED_CONTEXT
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
ssize_t ast_recvfrom(int sockfd, void *buf, size_t len, int flags, struct ast_sockaddr *src_addr)
Wrapper around recvfrom(2) that uses struct ast_sockaddr.
#define DUNDI_IE_ORGANIZATION
#define DUNDI_DEFAULT_KEY_EXPIRE
int dundi_precache(const char *context, const char *number)
Pre-cache to push upstream peers.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define ast_calloc(num, len)
A wrapper for calloc()
int AST_OPTIONAL_API_NAME() ast_sign_bin(struct ast_key *key, const char *msg, int msglen, unsigned char *dsig)
signs outgoing message with public key
int ast_register_switch(struct ast_switch *sw)
Register an alternative dialplan switch.
Module could not be loaded properly.
int AST_OPTIONAL_API_NAME() ast_check_signature_bin(struct ast_key *key, const char *msg, int msglen, const unsigned char *dsig)
check signature of a message
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
Structure used to handle boolean flags.
int ast_io_remove(struct io_context *ioc, int *id)
Removes an IO context.
#define DUNDI_IE_CALLED_NUMBER
#define DUNDI_COMMAND_PRECACHERQ
#define DUNDI_COMMAND_PRECACHERP
struct ast_eid ast_eid_default
Global EID.
static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr, struct ast_channel *chan)
Helper function that handles frames.
ast_aes_encrypt_key us_ecx
#define DUNDI_COMMAND_REGREQ
#define DUNDI_COMMAND_FINAL
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
unsigned char txenckey[256]
#define DUNDI_COMMAND_EIDRESPONSE
Standard Command Line Interface.
#define DUNDI_IE_STATE_PROV
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
struct dundi_transaction::@420 all
#define DUNDI_COMMAND_EIDQUERY
int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr)
Determine if the address is an IPv4 address.
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
#define DUNDI_COMMAND_INVALID
struct ast_key *AST_OPTIONAL_API_NAME() ast_key_get(const char *kname, int ktype)
return the ast_key structure for name
struct dundi_transaction * regtrans
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define DUNDI_COMMAND_DPDISCOVER
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Persistent data storage (akin to *doze registry)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
ast_context: An extension context
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
#define ast_custom_function_register(acf)
Register a custom function.
#define DUNDI_COMMAND_NULL
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
Determine if this is an IPv6 address.
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
#define DUNDI_COMMAND_REGRESPONSE
static char * ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only, suitable for a URL (with brack...
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
#define AST_APP_ARG(name)
Define an application argument.
int AST_OPTIONAL_API_NAME() ast_decrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
decrypt a message
#define DUNDI_COMMAND_CANCEL
struct io_context * io_context_create(void)
Creates a context Create a context for I/O operations Basically mallocs an IO structure and sets up s...