Mercurial > pidgin.yaz
annotate libgaim/dnssrv.c @ 15222:3043806ad900
[gaim-migrate @ 18011]
I think this'll fix an occasional "invalid read of size 1 bytes"
message from valgrind. I'm not sure when it happens... it seems
like it would only happen for invalid packets (ones that don't
end in 0xc0 80 or whatever it is)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 17 Dec 2006 04:55:12 +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 } |