00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 321392 $")
00035
00036 #include <netinet/in.h>
00037 #include <arpa/nameser.h>
00038 #ifdef __APPLE__
00039 #if __APPLE_CC__ >= 1495
00040 #include <arpa/nameser_compat.h>
00041 #endif
00042 #endif
00043 #include <resolv.h>
00044
00045 #include "asterisk/channel.h"
00046 #include "asterisk/srv.h"
00047 #include "asterisk/dns.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/linkedlists.h"
00050
00051 #ifdef __APPLE__
00052 #undef T_SRV
00053 #define T_SRV 33
00054 #endif
00055
00056 struct srv_entry {
00057 unsigned short priority;
00058 unsigned short weight;
00059 unsigned short port;
00060 unsigned int weight_sum;
00061 AST_LIST_ENTRY(srv_entry) list;
00062 char host[1];
00063 };
00064
00065 struct srv_context {
00066 unsigned int have_weights:1;
00067 struct srv_entry *prev;
00068 unsigned int num_records;
00069 AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries;
00070 };
00071
00072 static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
00073 {
00074 struct srv {
00075 unsigned short priority;
00076 unsigned short weight;
00077 unsigned short port;
00078 } __attribute__((__packed__)) *srv = (struct srv *) answer;
00079
00080 int res = 0;
00081 char repl[256] = "";
00082 struct srv_entry *entry;
00083
00084 if (len < sizeof(*srv))
00085 return -1;
00086
00087 answer += sizeof(*srv);
00088 len -= sizeof(*srv);
00089
00090 if ((res = dn_expand(msg, answer + len, answer, repl, sizeof(repl) - 1)) <= 0) {
00091 ast_log(LOG_WARNING, "Failed to expand hostname\n");
00092 return -1;
00093 }
00094
00095
00096
00097 if (!strcmp(repl, "."))
00098 return -1;
00099
00100 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(repl))))
00101 return -1;
00102
00103 entry->priority = ntohs(srv->priority);
00104 entry->weight = ntohs(srv->weight);
00105 entry->port = ntohs(srv->port);
00106 strcpy(entry->host, repl);
00107
00108 *result = entry;
00109
00110 return 0;
00111 }
00112
00113 static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
00114 {
00115 struct srv_context *c = (struct srv_context *) context;
00116 struct srv_entry *entry = NULL;
00117 struct srv_entry *current;
00118
00119 if (parse_srv(answer, len, fullanswer, &entry))
00120 return -1;
00121
00122 if (entry->weight)
00123 c->have_weights = 1;
00124
00125 AST_LIST_TRAVERSE_SAFE_BEGIN(&c->entries, current, list) {
00126
00127
00128 if (current->priority <= entry->priority)
00129 continue;
00130
00131 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
00132 entry = NULL;
00133 break;
00134 }
00135 AST_LIST_TRAVERSE_SAFE_END;
00136
00137
00138
00139 if (entry)
00140 AST_LIST_INSERT_TAIL(&c->entries, entry, list);
00141
00142 return 0;
00143 }
00144
00145
00146
00147
00148
00149
00150 static void process_weights(struct srv_context *context)
00151 {
00152 struct srv_entry *current;
00153 struct srv_entries newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00154
00155 while (AST_LIST_FIRST(&context->entries)) {
00156 unsigned int random_weight;
00157 unsigned int weight_sum;
00158 unsigned short cur_priority = AST_LIST_FIRST(&context->entries)->priority;
00159 struct srv_entries temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00160 weight_sum = 0;
00161
00162 AST_LIST_TRAVERSE_SAFE_BEGIN(&context->entries, current, list) {
00163 if (current->priority != cur_priority)
00164 break;
00165
00166 AST_LIST_MOVE_CURRENT(&temp_list, list);
00167 }
00168 AST_LIST_TRAVERSE_SAFE_END;
00169
00170 while (AST_LIST_FIRST(&temp_list)) {
00171 weight_sum = 0;
00172 AST_LIST_TRAVERSE(&temp_list, current, list)
00173 current->weight_sum = weight_sum += current->weight;
00174
00175
00176
00177 if (weight_sum == 0) {
00178 AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
00179 break;
00180 }
00181
00182 random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
00183
00184 AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
00185 if (current->weight < random_weight)
00186 continue;
00187
00188 AST_LIST_MOVE_CURRENT(&newlist, list);
00189 break;
00190 }
00191 AST_LIST_TRAVERSE_SAFE_END;
00192 }
00193
00194 }
00195
00196
00197
00198
00199 AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
00200 }
00201
00202 int ast_srv_lookup(struct srv_context **context, const char *service, const char **host, unsigned short *port)
00203 {
00204 struct srv_entry *cur;
00205
00206 if (*context == NULL) {
00207 if (!(*context = ast_calloc(1, sizeof(struct srv_context)))) {
00208 return -1;
00209 }
00210 AST_LIST_HEAD_INIT_NOLOCK(&(*context)->entries);
00211
00212 if ((ast_search_dns(*context, service, C_IN, T_SRV, srv_callback)) < 0) {
00213 ast_free(*context);
00214 *context = NULL;
00215 return -1;
00216 }
00217
00218 if ((*context)->have_weights) {
00219 process_weights(*context);
00220 }
00221
00222 (*context)->prev = AST_LIST_FIRST(&(*context)->entries);
00223 *host = (*context)->prev->host;
00224 *port = (*context)->prev->port;
00225 AST_LIST_TRAVERSE(&(*context)->entries, cur, list) {
00226 ++((*context)->num_records);
00227 }
00228 return 0;
00229 }
00230
00231 if (((*context)->prev = AST_LIST_NEXT((*context)->prev, list))) {
00232
00233 *host = (*context)->prev->host;
00234 *port = (*context)->prev->port;
00235 return 0;
00236 } else {
00237
00238 while ((cur = AST_LIST_REMOVE_HEAD(&(*context)->entries, list))) {
00239 ast_free(cur);
00240 }
00241 ast_free(*context);
00242 *context = NULL;
00243 return 1;
00244 }
00245 }
00246
00247 void ast_srv_cleanup(struct srv_context **context)
00248 {
00249 const char *host;
00250 unsigned short port;
00251
00252 if (*context) {
00253
00254 while (!(ast_srv_lookup(context, NULL, &host, &port))) {
00255 }
00256 }
00257 }
00258
00259 int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
00260 {
00261 struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
00262 struct srv_entry *current;
00263 int ret;
00264
00265 if (chan && ast_autoservice_start(chan) < 0) {
00266 return -1;
00267 }
00268
00269 ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
00270
00271 if (context.have_weights) {
00272 process_weights(&context);
00273 }
00274
00275 if (chan) {
00276 ret |= ast_autoservice_stop(chan);
00277 }
00278
00279
00280
00281
00282
00283
00284
00285 if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
00286 ast_copy_string(host, current->host, hostlen);
00287 *port = current->port;
00288 ast_free(current);
00289 ast_verb(4, "ast_get_srv: SRV lookup for '%s' mapped to host %s, port %d\n",
00290 service, host, *port);
00291 } else {
00292 host[0] = '\0';
00293 *port = -1;
00294 }
00295
00296 while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
00297 ast_free(current);
00298 }
00299
00300 return ret;
00301 }
00302
00303 unsigned int ast_srv_get_record_count(struct srv_context *context)
00304 {
00305 return context->num_records;
00306 }
00307
00308 int ast_srv_get_nth_record(struct srv_context *context, int record_num, const char **host,
00309 unsigned short *port, unsigned short *priority, unsigned short *weight)
00310 {
00311 int i = 1;
00312 int res = -1;
00313 struct srv_entry *entry;
00314
00315 if (record_num < 1 || record_num > context->num_records) {
00316 return res;
00317 }
00318
00319 AST_LIST_TRAVERSE(&context->entries, entry, list) {
00320 if (i == record_num) {
00321 *host = entry->host;
00322 *port = entry->port;
00323 *priority = entry->priority;
00324 *weight = entry->weight;
00325 res = 0;
00326 break;
00327 }
00328 ++i;
00329 }
00330
00331 return res;
00332 }