Mercurial > pidgin.yaz
annotate libgaim/dnssrv.c @ 15296:7d6bd7a4994f
[gaim-migrate @ 18086]
core-ify some code...and someone tell me why my gvim and svn aren't getting along and I have to type out this damn message on the command line
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Mon, 08 Jan 2007 04:26:16 +0000 |
parents | ef05f400817f |
children |
rev | line source |
---|---|
14192 | 1 /** |
2 * @file dnssrv.c | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 */ | |
22 | |
23 #include "internal.h" | |
24 | |
25 #ifndef _WIN32 | |
26 #include <resolv.h> | |
27 #include <arpa/nameser.h> | |
28 #ifdef HAVE_ARPA_NAMESER_COMPAT_H | |
29 #include <arpa/nameser_compat.h> | |
30 #endif | |
31 #ifndef T_SRV | |
32 #define T_SRV 33 | |
33 #endif | |
34 #else | |
35 #include <windns.h> | |
36 /* Missing from the mingw headers */ | |
37 #ifndef DNS_TYPE_SRV | |
38 # define DNS_TYPE_SRV 33 | |
39 #endif | |
40 #endif | |
41 | |
42 #include "dnssrv.h" | |
43 #include "eventloop.h" | |
44 #include "debug.h" | |
45 | |
46 #ifndef _WIN32 | |
47 typedef union { | |
48 HEADER hdr; | |
49 u_char buf[1024]; | |
50 } queryans; | |
51 #else | |
52 static DNS_STATUS WINAPI (*MyDnsQuery_UTF8) ( | |
53 PCSTR lpstrName, WORD wType, DWORD fOptions, | |
54 PIP4_ARRAY aipServers, PDNS_RECORD* ppQueryResultsSet, | |
55 PVOID* pReserved) = NULL; | |
56 static void WINAPI (*MyDnsRecordListFree) (PDNS_RECORD pRecordList, | |
57 DNS_FREE_TYPE FreeType) = NULL; | |
58 #endif | |
59 | |
14308 | 60 struct _GaimSrvQueryData { |
61 GaimSrvCallback cb; | |
14192 | 62 gpointer extradata; |
63 guint handle; | |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
64 #ifdef _WIN32 |
14308 | 65 GThread *resolver; |
14192 | 66 char *query; |
14308 | 67 char *error_message; |
14192 | 68 GSList *results; |
69 #endif | |
70 }; | |
71 | |
14308 | 72 static gint |
73 responsecompare(gconstpointer ar, gconstpointer br) | |
74 { | |
14192 | 75 GaimSrvResponse *a = (GaimSrvResponse*)ar; |
76 GaimSrvResponse *b = (GaimSrvResponse*)br; | |
77 | |
78 if(a->pref == b->pref) { | |
79 if(a->weight == b->weight) | |
80 return 0; | |
81 if(a->weight < b->weight) | |
82 return -1; | |
83 return 1; | |
84 } | |
85 if(a->pref < b->pref) | |
86 return -1; | |
87 return 1; | |
88 } | |
14308 | 89 |
14192 | 90 #ifndef _WIN32 |
14308 | 91 |
92 static void | |
93 resolve(int in, int out) | |
94 { | |
14192 | 95 GList *ret = NULL; |
96 GaimSrvResponse *srvres; | |
97 queryans answer; | |
98 int size; | |
99 int qdcount; | |
100 int ancount; | |
101 guchar *end; | |
102 guchar *cp; | |
103 gchar name[256]; | |
104 guint16 type, dlen, pref, weight, port; | |
105 gchar query[256]; | |
106 | |
14308 | 107 if (read(in, query, 256) <= 0) |
14192 | 108 _exit(0); |
14308 | 109 |
14192 | 110 size = res_query( query, C_IN, T_SRV, (u_char*)&answer, sizeof( answer)); |
111 | |
112 qdcount = ntohs(answer.hdr.qdcount); | |
113 ancount = ntohs(answer.hdr.ancount); | |
114 | |
115 cp = (guchar*)&answer + sizeof(HEADER); | |
116 end = (guchar*)&answer + size; | |
117 | |
118 /* skip over unwanted stuff */ | |
119 while (qdcount-- > 0 && cp < end) { | |
120 size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); | |
121 if(size < 0) goto end; | |
122 cp += size + QFIXEDSZ; | |
123 } | |
124 | |
125 while (ancount-- > 0 && cp < end) { | |
126 size = dn_expand((unsigned char*)&answer, end, cp, name, 256); | |
127 if(size < 0) | |
128 goto end; | |
129 | |
130 cp += size; | |
131 | |
132 GETSHORT(type,cp); | |
133 | |
134 /* skip ttl and class since we already know it */ | |
135 cp += 6; | |
136 | |
137 GETSHORT(dlen,cp); | |
138 | |
139 if (type == T_SRV) { | |
140 GETSHORT(pref,cp); | |
141 | |
142 GETSHORT(weight,cp); | |
143 | |
144 GETSHORT(port,cp); | |
145 | |
146 size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); | |
147 if(size < 0 ) | |
148 goto end; | |
149 | |
150 cp += size; | |
151 | |
152 srvres = g_new0(GaimSrvResponse, 1); | |
153 strcpy(srvres->hostname, name); | |
154 srvres->pref = pref; | |
155 srvres->port = port; | |
156 srvres->weight = weight; | |
157 | |
158 ret = g_list_insert_sorted(ret, srvres, responsecompare); | |
159 } else { | |
160 cp += dlen; | |
161 } | |
162 } | |
14308 | 163 |
164 end: | |
165 size = g_list_length(ret); | |
14192 | 166 write(out, &size, sizeof(int)); |
14308 | 167 while (ret != NULL) |
168 { | |
169 write(out, ret->data, sizeof(GaimSrvResponse)); | |
170 g_free(ret->data); | |
171 ret = g_list_remove(ret, ret->data); | |
14192 | 172 } |
173 | |
174 _exit(0); | |
175 } | |
176 | |
14308 | 177 static void |
178 resolved(gpointer data, gint source, GaimInputCondition cond) | |
179 { | |
14192 | 180 int size; |
14308 | 181 GaimSrvQueryData *query_data = (GaimSrvQueryData*)data; |
14192 | 182 GaimSrvResponse *res; |
183 GaimSrvResponse *tmp; | |
184 int i; | |
14308 | 185 GaimSrvCallback cb = query_data->cb; |
14192 | 186 |
187 read(source, &size, sizeof(int)); | |
14308 | 188 gaim_debug_info("dnssrv","found %d SRV entries\n", size); |
14192 | 189 tmp = res = g_new0(GaimSrvResponse, size); |
14308 | 190 for (i = 0; i < size; i++) { |
14192 | 191 read(source, tmp++, sizeof(GaimSrvResponse)); |
192 } | |
14308 | 193 |
194 cb(res, size, query_data->extradata); | |
195 | |
196 gaim_srv_cancel(query_data); | |
14192 | 197 } |
198 | |
199 #else /* _WIN32 */ | |
200 | |
201 /** The Jabber Server code was inspiration for parts of this. */ | |
202 | |
14308 | 203 static gboolean |
204 res_main_thread_cb(gpointer data) | |
205 { | |
14192 | 206 GaimSrvResponse *srvres = NULL; |
207 int size = 0; | |
14308 | 208 GaimSrvQueryData *query_data = data; |
14192 | 209 |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
210 if(query_data->error_message != NULL) |
14308 | 211 gaim_debug_error("dnssrv", query_data->error_message); |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
212 else { |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
213 GaimSrvResponse *srvres_tmp = NULL; |
14308 | 214 GSList *lst = query_data->results; |
14192 | 215 |
14308 | 216 size = g_slist_length(query_data->results); |
14192 | 217 |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
218 if(query_data->cb) |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
219 srvres_tmp = srvres = g_new0(GaimSrvResponse, size); |
14192 | 220 while (lst) { |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
221 if(query_data->cb) |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
222 memcpy(srvres_tmp++, lst->data, sizeof(GaimSrvResponse)); |
14192 | 223 g_free(lst->data); |
224 lst = g_slist_remove(lst, lst->data); | |
225 } | |
226 | |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
227 query_data->results = NULL; |
14192 | 228 } |
229 | |
14308 | 230 gaim_debug_info("dnssrv", "found %d SRV entries\n", size); |
14192 | 231 |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
232 if(query_data->cb) |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
233 query_data->cb(srvres, size, query_data->extradata); |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
234 |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
235 query_data->resolver = NULL; |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
236 query_data->handle = 0; |
14192 | 237 |
14308 | 238 gaim_srv_cancel(query_data); |
14192 | 239 |
240 return FALSE; | |
241 } | |
242 | |
14308 | 243 static gpointer |
244 res_thread(gpointer data) | |
245 { | |
14192 | 246 PDNS_RECORD dr = NULL; |
247 int type = DNS_TYPE_SRV; | |
248 DNS_STATUS ds; | |
14308 | 249 GaimSrvQueryData *query_data = data; |
14192 | 250 |
14308 | 251 ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); |
14192 | 252 if (ds != ERROR_SUCCESS) { |
253 gchar *msg = g_win32_error_message(ds); | |
14308 | 254 query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); |
14192 | 255 g_free(msg); |
256 } else { | |
257 PDNS_RECORD dr_tmp; | |
258 GSList *lst = NULL; | |
259 DNS_SRV_DATA *srv_data; | |
260 GaimSrvResponse *srvres; | |
261 | |
262 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { | |
263 /* Discard any incorrect entries. I'm not sure if this is necessary */ | |
14308 | 264 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { |
14192 | 265 continue; |
266 } | |
267 | |
268 srv_data = &dr_tmp->Data.SRV; | |
269 srvres = g_new0(GaimSrvResponse, 1); | |
270 strncpy(srvres->hostname, srv_data->pNameTarget, 255); | |
271 srvres->hostname[255] = '\0'; | |
272 srvres->pref = srv_data->wPriority; | |
273 srvres->port = srv_data->wPort; | |
274 srvres->weight = srv_data->wWeight; | |
275 | |
276 lst = g_slist_insert_sorted(lst, srvres, responsecompare); | |
277 } | |
278 | |
279 MyDnsRecordListFree(dr, DnsFreeRecordList); | |
14308 | 280 query_data->results = lst; |
14192 | 281 } |
282 | |
283 /* back to main thread */ | |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
284 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ |
14308 | 285 g_idle_add(res_main_thread_cb, query_data); |
14192 | 286 |
287 g_thread_exit(NULL); | |
288 return NULL; | |
289 } | |
290 | |
291 #endif | |
292 | |
14308 | 293 GaimSrvQueryData * |
294 gaim_srv_resolve(const char *protocol, const char *transport, const char *domain, GaimSrvCallback cb, gpointer extradata) | |
295 { | |
296 char *query; | |
297 GaimSrvQueryData *query_data; | |
14192 | 298 #ifndef _WIN32 |
299 int in[2], out[2]; | |
300 int pid; | |
14308 | 301 #else |
302 GError* err = NULL; | |
303 static gboolean initialized = FALSE; | |
304 #endif | |
305 | |
306 query = g_strdup_printf("_%s._%s.%s", protocol, transport, domain); | |
307 gaim_debug_info("dnssrv","querying SRV record for %s\n", query); | |
308 | |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
309 #ifndef _WIN32 |
14192 | 310 if(pipe(in) || pipe(out)) { |
14308 | 311 gaim_debug_error("dnssrv", "Could not create pipe\n"); |
14192 | 312 g_free(query); |
313 cb(NULL, 0, extradata); | |
14308 | 314 return NULL; |
14192 | 315 } |
316 | |
317 pid = fork(); | |
14308 | 318 if (pid == -1) { |
319 gaim_debug_error("dnssrv", "Could not create process!\n"); | |
14192 | 320 cb(NULL, 0, extradata); |
321 g_free(query); | |
14308 | 322 return NULL; |
14192 | 323 } |
14308 | 324 |
14192 | 325 /* Child */ |
14308 | 326 if (pid == 0) |
327 { | |
14192 | 328 close(out[0]); |
329 close(in[1]); | |
330 resolve(in[0], out[1]); | |
331 } | |
332 | |
333 close(out[1]); | |
334 close(in[0]); | |
335 | |
14308 | 336 if (write(in[1], query, strlen(query)+1) < 0) |
337 gaim_debug_error("dnssrv", "Could not write to SRV resolver\n"); | |
338 | |
339 query_data = g_new0(GaimSrvQueryData, 1); | |
340 query_data->cb = cb; | |
341 query_data->extradata = extradata; | |
342 query_data->handle = gaim_input_add(out[0], GAIM_INPUT_READ, resolved, query_data); | |
14192 | 343 |
344 g_free(query); | |
14308 | 345 |
346 return query_data; | |
14192 | 347 #else |
348 if (!initialized) { | |
349 MyDnsQuery_UTF8 = (void*) wgaim_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8"); | |
350 MyDnsRecordListFree = (void*) wgaim_find_and_loadproc( | |
351 "dnsapi.dll", "DnsRecordListFree"); | |
352 initialized = TRUE; | |
353 } | |
354 | |
14308 | 355 query_data = g_new0(GaimSrvQueryData, 1); |
356 query_data->cb = cb; | |
357 query_data->query = query; | |
358 query_data->extradata = extradata; | |
14192 | 359 |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
360 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) { |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
361 query_data->error_message = g_strdup_printf("System missing DNS API (Requires W2K+)\n"); |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
362 |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
363 /* Asynchronously call the callback since stuff may not expect |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
364 * the callback to be called before this returns */ |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
365 query_data->handle = g_idle_add(res_main_thread_cb, query_data); |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
366 |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
367 return query_data; |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
368 } |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
369 |
14308 | 370 query_data->resolver = g_thread_create(res_thread, query_data, FALSE, &err); |
371 if (query_data->resolver == NULL) | |
372 { | |
373 query_data->error_message = g_strdup_printf("SRV thread create failure: %s\n", err ? err->message : ""); | |
14192 | 374 g_error_free(err); |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
375 |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
376 /* Asynchronously call the callback since stuff may not expect |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
377 * the callback to be called before this returns */ |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
378 query_data->handle = g_idle_add(res_main_thread_cb, query_data); |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
379 |
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
380 return query_data; |
14192 | 381 } |
14308 | 382 |
383 return query_data; | |
14192 | 384 #endif |
385 } | |
386 | |
14308 | 387 void |
388 gaim_srv_cancel(GaimSrvQueryData *query_data) | |
389 { | |
390 if (query_data->handle > 0) | |
391 gaim_input_remove(query_data->handle); | |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
392 #ifdef _WIN32 |
14308 | 393 if (query_data->resolver != NULL) |
394 { | |
395 /* | |
396 * It's not really possible to kill a thread. So instead we | |
397 * just set the callback to NULL and let the DNS lookup | |
398 * finish. | |
399 */ | |
14312
ef05f400817f
[gaim-migrate @ 17002]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14308
diff
changeset
|
400 query_data->cb = NULL; |
14308 | 401 return; |
402 } | |
403 g_free(query_data->query); | |
404 g_free(query_data->error_message); | |
405 #endif | |
406 g_free(query_data); | |
407 } |