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: 168523 $")
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 AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries;
00068 };
00069
00070 static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
00071 {
00072 struct srv {
00073 unsigned short priority;
00074 unsigned short weight;
00075 unsigned short port;
00076 } __attribute__((__packed__)) *srv = (struct srv *) answer;
00077
00078 int res = 0;
00079 char repl[256] = "";
00080 struct srv_entry *entry;
00081
00082 if (len < sizeof(*srv))
00083 return -1;
00084
00085 answer += sizeof(*srv);
00086 len -= sizeof(*srv);
00087
00088 if ((res = dn_expand(msg, answer + len, answer, repl, sizeof(repl) - 1)) <= 0) {
00089 ast_log(LOG_WARNING, "Failed to expand hostname\n");
00090 return -1;
00091 }
00092
00093
00094
00095 if (!strcmp(repl, "."))
00096 return -1;
00097
00098 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(repl))))
00099 return -1;
00100
00101 entry->priority = ntohs(srv->priority);
00102 entry->weight = ntohs(srv->weight);
00103 entry->port = ntohs(srv->port);
00104 strcpy(entry->host, repl);
00105
00106 *result = entry;
00107
00108 return 0;
00109 }
00110
00111 static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
00112 {
00113 struct srv_context *c = (struct srv_context *) context;
00114 struct srv_entry *entry = NULL;
00115 struct srv_entry *current;
00116
00117 if (parse_srv(answer, len, fullanswer, &entry))
00118 return -1;
00119
00120 if (entry->weight)
00121 c->have_weights = 1;
00122
00123 AST_LIST_TRAVERSE_SAFE_BEGIN(&c->entries, current, list) {
00124
00125
00126 if (current->priority <= entry->priority)
00127 continue;
00128
00129 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
00130 entry = NULL;
00131 break;
00132 }
00133 AST_LIST_TRAVERSE_SAFE_END;
00134
00135
00136
00137 if (entry)
00138 AST_LIST_INSERT_TAIL(&c->entries, entry, list);
00139
00140 return 0;
00141 }
00142
00143
00144
00145
00146
00147
00148 static void process_weights(struct srv_context *context)
00149 {
00150 struct srv_entry *current;
00151 struct srv_entries newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00152
00153 while (AST_LIST_FIRST(&context->entries)) {
00154 unsigned int random_weight;
00155 unsigned int weight_sum;
00156 unsigned short cur_priority = AST_LIST_FIRST(&context->entries)->priority;
00157 struct srv_entries temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
00158 weight_sum = 0;
00159
00160 AST_LIST_TRAVERSE_SAFE_BEGIN(&context->entries, current, list) {
00161 if (current->priority != cur_priority)
00162 break;
00163
00164 AST_LIST_MOVE_CURRENT(&temp_list, list);
00165 }
00166 AST_LIST_TRAVERSE_SAFE_END;
00167
00168 while (AST_LIST_FIRST(&temp_list)) {
00169 weight_sum = 0;
00170 AST_LIST_TRAVERSE(&temp_list, current, list)
00171 current->weight_sum = weight_sum += current->weight;
00172
00173
00174
00175 if (weight_sum == 0) {
00176 AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
00177 break;
00178 }
00179
00180 random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
00181
00182 AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
00183 if (current->weight < random_weight)
00184 continue;
00185
00186 AST_LIST_MOVE_CURRENT(&newlist, list);
00187 break;
00188 }
00189 AST_LIST_TRAVERSE_SAFE_END;
00190 }
00191
00192 }
00193
00194
00195
00196
00197 AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
00198 }
00199
00200 int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
00201 {
00202 struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
00203 struct srv_entry *current;
00204 int ret;
00205
00206 if (chan && ast_autoservice_start(chan) < 0)
00207 return -1;
00208
00209 ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
00210
00211 if (context.have_weights)
00212 process_weights(&context);
00213
00214 if (chan)
00215 ret |= ast_autoservice_stop(chan);
00216
00217
00218
00219
00220
00221
00222
00223 if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
00224 ast_copy_string(host, current->host, hostlen);
00225 *port = current->port;
00226 ast_free(current);
00227 ast_verb(4, "ast_get_srv: SRV lookup for '%s' mapped to host %s, port %d\n",
00228 service, host, *port);
00229 } else {
00230 host[0] = '\0';
00231 *port = -1;
00232 }
00233
00234 while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list)))
00235 ast_free(current);
00236
00237 return ret;
00238 }