int i_enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service) { char *user_s; int user_len, i, j; char name[MAX_DOMAIN_SIZE]; char apex[MAX_COMPONENT_SIZE + 1]; char separator[MAX_COMPONENT_SIZE + 1]; int sdl = 0; /* subdomain location: infrastructure enum offset */ int cc_len; struct rdata* head; char string[17]; str *suffix, *service; suffix = (str*)_suffix; service = (str*)_service; if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("Parsing of R-URI failed\n"); return -1; } if (is_e164(&(_msg->parsed_uri.user)) == -1) { LM_ERR("R-URI user is not an E164 number\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; /* make sure we don't run out of space in strings */ if (( 2*user_len + MAX_COMPONENT_SIZE + MAX_COMPONENT_SIZE + 4) > MAX_DOMAIN_SIZE) { LM_ERR("Strings too long\n"); return -1; } if ( i_branchlabel.len > MAX_COMPONENT_SIZE ) { LM_ERR("i_branchlabel too long\n"); return -1; } if ( suffix->len > MAX_COMPONENT_SIZE ) { LM_ERR("Suffix too long\n"); return -1; } memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; /* Set up parameters as for user-enum */ memcpy(apex, suffix->s , suffix->len); apex[suffix->len] = (char)0; sdl = 0; /* where to insert i-enum separator */ separator[0] = 0; /* don't insert anything */ cc_len = cclen(string + 1); if (!strncasecmp(i_bl_alg.s,"ebl",i_bl_alg.len)) { sdl = cc_len; /* default */ j = 0; memcpy(name, i_branchlabel.s, i_branchlabel.len); j += i_branchlabel.len; name[j++] = '.'; for (i = cc_len ; i > 0; i--) { name[j++] = user_s[i]; name[j++] = '.'; } memcpy(name + j, suffix->s, suffix->len + 1); LM_DBG("Looking for EBL record for %s.\n", name); head = get_record(name, T_EBL); if (head == 0) { LM_DBG("No EBL found for %s. Defaulting to user ENUM.\n",name); } else { struct ebl_rdata* ebl; ebl = (struct ebl_rdata *) head->rdata; LM_DBG("EBL record for %s is %d / %.*s / %.*s.\n", name, ebl->position, (int)ebl->separator_len, ebl->separator,(int)ebl->apex_len, ebl->apex); if ((ebl->apex_len > MAX_COMPONENT_SIZE) || (ebl->separator_len > MAX_COMPONENT_SIZE)) { LM_ERR("EBL strings too long\n"); return -1; } if (ebl->position > 15) { LM_ERR("EBL position too large (%d)\n", ebl->position); return -1; } sdl = ebl->position; memcpy(separator, ebl->separator, ebl->separator_len); separator[ebl->separator_len] = 0; memcpy(apex, ebl->apex, ebl->apex_len); apex[ebl->apex_len] = 0; free_rdata_list(head); } } else if (!strncasecmp(i_bl_alg.s,"txt",i_bl_alg.len)) { sdl = cc_len; /* default */ memcpy(separator, i_branchlabel.s, i_branchlabel.len); separator[i_branchlabel.len] = 0; /* no change to apex */ j = 0; memcpy(name, i_branchlabel.s, i_branchlabel.len); j += i_branchlabel.len; name[j++] = '.'; for (i = cc_len ; i > 0; i--) { name[j++] = user_s[i]; name[j++] = '.'; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_TXT); if (head == 0) { LM_DBG("TXT found for %s. Defaulting to %d\n", name, cc_len); } else { sdl = atoi(((struct txt_rdata*)head->rdata)->txt); LM_DBG("TXT record for %s is %d.\n", name, sdl); if ((sdl < 0) || (sdl > 10)) { LM_ERR("Sdl %d out of bounds. Set back to cc_len.\n", sdl); sdl = cc_len; } free_rdata_list(head); } } else { /* defaults to CC */ sdl = cc_len; memcpy(separator, i_branchlabel.s, i_branchlabel.len); separator[i_branchlabel.len] = 0; /* no change to apex */ } j = 0; sdl++; /* to avoid comparing i to (sdl+1) */ for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; if (separator[0] && (i == sdl)) { /* insert the I-ENUM separator here? */ strcpy(name + j, separator); /* we've checked string sizes. */ j += strlen(separator); name[j++] = '.'; } } memcpy(name + j, apex, strlen(apex)+1); return do_query(_msg, string, name, service); }
/* ENUM lookup */ int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options, unsigned int record, struct enum_context **argcontext) { struct enum_context *context; char tmp[512]; char domain[256]; char left[128]; char middle[128]; char naptrinput[128]; char apex[128] = ""; int ret = -1; /* for ISN rewrite */ char *p1 = NULL; char *p2 = NULL; char *p3 = NULL; int k = 0; int i = 0; int z = 0; int spaceleft = 0; struct timeval time_start, time_end; if (ast_strlen_zero(suffix)) { ast_log(LOG_WARNING, "ast_get_enum need a suffix parameter now.\n"); return -1; } ast_debug(2, "num='%s', tech='%s', suffix='%s', options='%s', record=%u\n", number, tech, suffix, options, record); /* We don't need that any more, that "n" preceding the number has been replaced by a flag in the options paramter. ast_copy_string(naptrinput, number, sizeof(naptrinput)); */ /* * The "number" parameter includes a leading '+' if it's a full E.164 number (and not ISN) * We need to preserve that as the regex inside NAPTRs expect the +. * * But for the domain generation, the '+' is a nuissance, so we get rid of it. */ ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput)); if (number[0] == '+') { number++; } if (!(context = ast_calloc(1, sizeof(*context)))) { return -1; } if ((p3 = strchr(naptrinput, '*'))) { *p3='\0'; } context->naptrinput = naptrinput; /* The number */ context->dst = dst; /* Return string */ context->dstlen = dstlen; context->tech = tech; context->techlen = techlen; context->options = 0; context->position = record > 0 ? record : 1; context->count = 0; context->naptr_rrs = NULL; context->naptr_rrs_count = 0; /* * Process options: * * c Return count, not URI * i Use infrastructure ENUM * s Do ISN transformation * d Direct DNS query: no reversing. * */ if (options != NULL) { if (strchr(options,'s')) { context->options |= ENUMLOOKUP_OPTIONS_ISN; } else if (strchr(options,'i')) { context->options |= ENUMLOOKUP_OPTIONS_IENUM; } else if (strchr(options,'d')) { context->options |= ENUMLOOKUP_OPTIONS_DIRECT; } if (strchr(options,'c')) { context->options |= ENUMLOOKUP_OPTIONS_COUNT; } if (strchr(number,'*')) { context->options |= ENUMLOOKUP_OPTIONS_ISN; } } ast_debug(2, "ENUM options(%s): pos=%d, options='%d'\n", options, context->position, context->options); ast_debug(1, "n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n", number, tech, suffix, context->options, context->position); /* * This code does more than simple RFC3261 ENUM. All these rewriting * schemes have in common that they build the FQDN for the NAPTR lookup * by concatenating * - a number which needs be flipped and "."-seperated (left) * - some fixed string (middle) * - an Apex. (apex) * * The RFC3261 ENUM is: left=full number, middle="", apex=from args. * ISN: number = "middle*left", apex=from args * I-ENUM: EBL parameters build the split, can change apex * Direct: left="", middle=argument, apex=from args * */ /* default: the whole number will be flipped, no middle domain component */ ast_copy_string(left, number, sizeof(left)); middle[0] = '\0'; /* * I-ENUM can change the apex, thus we copy it */ ast_copy_string(apex, suffix, sizeof(apex)); /* ISN rewrite */ if ((context->options & ENUMLOOKUP_OPTIONS_ISN) && (p1 = strchr(number, '*'))) { *p1++ = '\0'; ast_copy_string(left, number, sizeof(left)); ast_copy_string(middle, p1, sizeof(middle) - 1); strcat(middle, "."); ast_debug(2, "ISN ENUM: left=%s, middle='%s'\n", left, middle); /* Direct DNS lookup rewrite */ } else if (context->options & ENUMLOOKUP_OPTIONS_DIRECT) { left[0] = 0; /* nothing to flip around */ ast_copy_string(middle, number, sizeof(middle) - 1); strcat(middle, "."); ast_debug(2, "DIRECT ENUM: middle='%s'\n", middle); /* Infrastructure ENUM rewrite */ } else if (context->options & ENUMLOOKUP_OPTIONS_IENUM) { int sdl = 0; char cc[8]; char sep[256], n_apex[256]; int cc_len = cclen(number); sdl = cc_len; ast_mutex_lock(&enumlock); ast_copy_string(sep, ienum_branchlabel, sizeof(sep)); /* default */ ast_mutex_unlock(&enumlock); switch (ebl_alg) { case ENUMLOOKUP_BLR_EBL: ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */ sdl = blr_ebl(cc, suffix, sep, sizeof(sep) - 1, n_apex, sizeof(n_apex) - 1); if (sdl >= 0) { ast_copy_string(apex, n_apex, sizeof(apex)); ast_debug(2, "EBL ENUM: sep=%s, apex='%s'\n", sep, n_apex); } else { sdl = cc_len; } break; case ENUMLOOKUP_BLR_TXT: ast_copy_string(cc, number, cc_len); /* cclen() never returns more than 3 */ sdl = blr_txt(cc, suffix); if (sdl < 0) { sdl = cc_len; } break; case ENUMLOOKUP_BLR_CC: /* BLR is at the country-code level */ default: sdl = cc_len; break; } if (sdl > strlen(number)) { /* Number too short for this sdl? */ ast_log(LOG_WARNING, "I-ENUM: subdomain location %d behind number %s\n", sdl, number); ast_free(context); return 0; } ast_copy_string(left, number + sdl, sizeof(left)); ast_mutex_lock(&enumlock); ast_copy_string(middle, sep, sizeof(middle) - 1); strcat(middle, "."); ast_mutex_unlock(&enumlock); /* check the space we need for middle */ if ((sdl * 2 + strlen(middle) + 2) > sizeof(middle)) { ast_log(LOG_WARNING, "ast_get_enum: not enough space for I-ENUM rewrite.\n"); ast_free(context); return -1; } p1 = middle + strlen(middle); for (p2 = (char *) number + sdl - 1; p2 >= number; p2--) { if (isdigit(*p2)) { *p1++ = *p2; *p1++ = '.'; } } *p1 = '\0'; ast_debug(2, "I-ENUM: cclen=%d, left=%s, middle='%s', apex='%s'\n", cc_len, left, middle, apex); } if (strlen(left) * 2 + 2 > sizeof(domain)) { ast_log(LOG_WARNING, "string to long in ast_get_enum\n"); ast_free(context); return -1; } /* flip left into domain */ p1 = domain; for (p2 = left + strlen(left); p2 >= left; p2--) { if (isdigit(*p2)) { *p1++ = *p2; *p1++ = '.'; } } *p1 = '\0'; if (chan && ast_autoservice_start(chan) < 0) { ast_free(context); return -1; } spaceleft = sizeof(tmp) - 2; ast_copy_string(tmp, domain, spaceleft); spaceleft -= strlen(domain); if (*middle) { strncat(tmp, middle, spaceleft); spaceleft -= strlen(middle); } strncat(tmp,apex,spaceleft); time_start = ast_tvnow(); ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback); time_end = ast_tvnow(); ast_debug(2, "profiling: %s, %s, %" PRIi64 " ms\n", (ret == 0) ? "OK" : "FAIL", tmp, ast_tvdiff_ms(time_end, time_start)); if (ret < 0) { ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno)); context->naptr_rrs_count = -1; strcpy(dst, "0"); ret = 0; } if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) { /* sort array by NAPTR order/preference */ for (k = 0; k < context->naptr_rrs_count; k++) { for (i = 0; i < context->naptr_rrs_count; i++) { /* use order first and then preference to compare */ if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order) && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos) || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order) && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) { z = context->naptr_rrs[k].sort_pos; context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos; context->naptr_rrs[i].sort_pos = z; continue; } if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) { if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref) && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos) || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref) && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)) { z = context->naptr_rrs[k].sort_pos; context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos; context->naptr_rrs[i].sort_pos = z; } } } } for (k = 0; k < context->naptr_rrs_count; k++) { if (context->naptr_rrs[k].sort_pos == context->position - 1) { ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen); ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen); break; } } } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) { context->dst[0] = 0; } else if ((context->options & ENUMLOOKUP_OPTIONS_COUNT)) { snprintf(context->dst, context->dstlen, "%d", context->naptr_rrs_count + context->count); } if (chan) { ret |= ast_autoservice_stop(chan); } if (!argcontext) { for (k = 0; k < context->naptr_rrs_count; k++) { ast_free(context->naptr_rrs[k].result); ast_free(context->naptr_rrs[k].tech); } ast_free(context->naptr_rrs); ast_free(context); } else { *argcontext = context; } return ret; }