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