/*--- ast_get_txt: Get TXT record from DNS. Really has nothing to do with enum, but anyway... */ int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *txt, int txtlen) { struct enum_context context; char tmp[259 + 512]; char naptrinput[512] = "+"; int pos = strlen(number) - 1; int newpos = 0; int ret = -1; struct enum_search *s = NULL; int version = -1; strncat(naptrinput, number, sizeof(naptrinput) - 2); context.naptrinput = naptrinput; context.dst = dst; context.dstlen = dstlen; context.tech = tech; context.techlen = techlen; context.txt = txt; context.txtlen = txtlen; if (pos > 128) pos = 128; while (pos >= 0) { tmp[newpos++] = number[pos--]; tmp[newpos++] = '.'; } if (chan && ast_autoservice_start(chan) < 0) return -1; for (;;) { ast_mutex_lock(&enumlock); if (version != enumver) { /* Ooh, a reload... */ s = toplevs; version = enumver; } else { s = s->next; } if (s) { strncpy(tmp + newpos, s->toplev, sizeof(tmp) - newpos - 1); } ast_mutex_unlock(&enumlock); if (!s) break; ret = ast_search_dns(&context, tmp, C_IN, T_TXT, txt_callback); if (ret > 0) break; } if (ret < 0) { ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno)); ret = 0; } if (chan) ret |= ast_autoservice_stop(chan); return ret; }
/* * Input: CC code * * Output: number of digits in the number before the i-enum branch * * Algorithm: Build <ienum_branchlabel>.c.c.<suffix> and look for a TXT lookup. * Return atoi(TXT-record). * Return -1 on not found. * */ static int blr_txt(const char *cc, const char *suffix) { struct txt_context context; char domain[128] = ""; char *p1, *p2; int ret; ast_mutex_lock(&enumlock); ast_verb(4, "blr_txt() cc='%s', suffix='%s', c_bl='%s'\n", cc, suffix, ienum_branchlabel); if (sizeof(domain) < (strlen(cc) * 2 + strlen(ienum_branchlabel) + strlen(suffix) + 2)) { ast_mutex_unlock(&enumlock); ast_log(LOG_WARNING, "ERROR: string sizing in blr_txt.\n"); return -1; } p1 = domain + snprintf(domain, sizeof(domain), "%s.", ienum_branchlabel); ast_mutex_unlock(&enumlock); for (p2 = (char *) cc + strlen(cc) - 1; p2 >= cc; p2--) { if (isdigit(*p2)) { *p1++ = *p2; *p1++ = '.'; } } strcat(p1, suffix); ast_verb(4, "blr_txt() FQDN for TXT record: %s, cc was %s\n", domain, cc); ret = ast_search_dns(&context, domain, C_IN, T_TXT, txt_callback); if (ret > 0) { ret = atoi(context.txt); if ((ret >= 0) && (ret < 20)) { ast_verb(3, "blr_txt() BLR TXT record for %s is %d (apex: %s)\n", cc, ret, suffix); return ret; } } ast_verb(3, "blr_txt() BLR TXT record for %s not found (apex: %s)\n", cc, suffix); return -1; }
int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service) { struct srv_context context; int ret; context.host = host; context.hostlen = hostlen; context.port = port; if (chan && ast_autoservice_start(chan) < 0) return -1; ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback); if (chan) ret |= ast_autoservice_stop(chan); if (ret <= 0) { host[0] = '\0'; *port = -1; return ret; } return ret; }
/* 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; }
/*--- ast_get_enum: 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) { struct enum_context context; char tmp[259 + 512]; char naptrinput[512]; int pos = strlen(number) - 1; int newpos = 0; int ret = -1; struct enum_search *s = NULL; int version = -1; /* for ISN rewrite */ char *p1 = NULL; char *p2 = NULL; int k = 0; int i = 0; int z = 0; if (number[0] == 'n') { strncpy(naptrinput, number+1, sizeof(naptrinput)); } else { strncpy(naptrinput, number, sizeof(naptrinput)); } context.naptrinput = naptrinput; /* The number */ context.dst = dst; /* Return string */ context.dstlen = dstlen; context.tech = tech; context.techlen = techlen; context.options = 0; context.position = 1; context.naptr_rrs = NULL; context.naptr_rrs_count = 0; if (options != NULL){ if (*options == 'c'){ context.options = ENUMLOOKUP_OPTIONS_COUNT; context.position = 0; } else { context.position = atoi(options); if (context.position < 1) context.position = 1; } } if (pos > 128) pos = 128; /* ISN rewrite */ p1 = strchr(number, '*'); if (number[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */ p1 = NULL; k = 1; /* strip 'n' from number */ } if (p1 != NULL) { p2 = p1+1; while (p1 > number){ p1--; tmp[newpos++] = *p1; tmp[newpos++] = '.'; } if (*p2) { while(*p2 && newpos < 128){ tmp[newpos++] = *p2; p2++; } tmp[newpos++] = '.'; } } else { while (pos >= k) { if (isdigit(number[pos])) { tmp[newpos++] = number[pos]; tmp[newpos++] = '.'; } pos--; } } if (chan && ast_autoservice_start(chan) < 0) return -1; for (;;) { ast_mutex_lock(&enumlock); if (version != enumver) { /* Ooh, a reload... */ s = toplevs; version = enumver; } else { s = s->next; } if (suffix != NULL) { strncpy(tmp + newpos, suffix, sizeof(tmp) - newpos - 1); } else if (s) { strncpy(tmp + newpos, s->toplev, sizeof(tmp) - newpos - 1); } ast_mutex_unlock(&enumlock); if (!s) break; ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback); if (ret > 0) break; if (suffix != NULL) break; } if (ret < 0) { ast_log(LOG_DEBUG, "No such number found: %s (%s)\n", tmp, strerror(errno)); ret = 0; } if (context.naptr_rrs_count >= context.position && ! (context.options & ENUMLOOKUP_OPTIONS_COUNT)) { /* sort array by NAPTR order/preference/tech */ for (k = 0; k < context.naptr_rrs_count; k++) { for (i = 0; i < context.naptr_rrs_count; i++) { /* Compare by order first. */ 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; } else if (ntohs(context.naptr_rrs[k].naptr.order) == ntohs(context.naptr_rrs[i].naptr.order)) { /* Order is the same, so sort by preference next */ if (ntohs(context.naptr_rrs[k].naptr.pref) == ntohs(context.naptr_rrs[i].naptr.pref)) { /* Preference is the same, so sort by tech */ if ((strcmp(context.naptr_rrs[k].tech, context.naptr_rrs[i].tech) < 0 && context.naptr_rrs[k].sort_pos > context.naptr_rrs[i].sort_pos) || (strcmp(context.naptr_rrs[k].tech, context.naptr_rrs[i].tech) > 0 && 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; } } else 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; } if (chan) ret |= ast_autoservice_stop(chan); for (k=0; k<context.naptr_rrs_count; k++) { free(context.naptr_rrs[k].result); free(context.naptr_rrs[k].tech); } free(context.naptr_rrs); return ret; }
/* 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[259 + 512]; char naptrinput[512]; int pos = strlen(number) - 1; int newpos = 0; int ret = -1; struct enum_search *s = NULL; int version = -1; /* for ISN rewrite */ char *p1 = NULL; char *p2 = NULL; int k = 0; int i = 0; int z = 0; if (!(context = ast_calloc(1, sizeof(*context)))) return -1; ast_copy_string(naptrinput, number[0] == 'n' ? number + 1 : number, sizeof(naptrinput)); 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; context->naptr_rrs = NULL; context->naptr_rrs_count = 0; if (options != NULL) { if (*options == 'c') { context->options = ENUMLOOKUP_OPTIONS_COUNT; context->position = 0; } } ast_debug(1, "ast_get_enum(): n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n", number, tech, suffix, context->options, context->position); if (pos > 128) pos = 128; /* ISN rewrite */ p1 = strchr(number, '*'); if (number[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */ p1 = NULL; k = 1; /* strip 'n' from number */ } if (p1 != NULL) { p2 = p1 + 1; while (p1 > number){ p1--; tmp[newpos++] = *p1; tmp[newpos++] = '.'; } if (*p2) { while (*p2 && newpos < 128){ tmp[newpos++] = *p2; p2++; } tmp[newpos++] = '.'; } } else { while (pos >= k) { if (isdigit(number[pos])) { tmp[newpos++] = number[pos]; tmp[newpos++] = '.'; } pos--; } } if (chan && ast_autoservice_start(chan) < 0) { ast_free(context); return -1; } if (suffix) { ast_copy_string(tmp + newpos, suffix, sizeof(tmp) - newpos); ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback); ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret); } else { ret = -1; /* this is actually dead code since the demise of app_enum.c */ for (;;) { ast_mutex_lock(&enumlock); if (version != enumver) { /* Ooh, a reload... */ s = toplevs; version = enumver; } else { s = s->next; } ast_mutex_unlock(&enumlock); if (!s) break; ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos); ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback); ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret); if (ret > 0) break; } } if (ret < 0) { ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno)); 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; } 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; }