Mercurial > pidgin
comparison libpurple/dnssrv.c @ 15373:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | 61b42cf81aa4 |
comparison
equal
deleted
inserted
replaced
15372:f79e0f4df793 | 15373:5fe8042783c1 |
---|---|
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 | |
60 struct _GaimSrvQueryData { | |
61 GaimSrvCallback cb; | |
62 gpointer extradata; | |
63 guint handle; | |
64 #ifdef _WIN32 | |
65 GThread *resolver; | |
66 char *query; | |
67 char *error_message; | |
68 GSList *results; | |
69 #endif | |
70 }; | |
71 | |
72 static gint | |
73 responsecompare(gconstpointer ar, gconstpointer br) | |
74 { | |
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 } | |
89 | |
90 #ifndef _WIN32 | |
91 | |
92 static void | |
93 resolve(int in, int out) | |
94 { | |
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 | |
107 if (read(in, query, 256) <= 0) | |
108 _exit(0); | |
109 | |
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 } | |
163 | |
164 end: | |
165 size = g_list_length(ret); | |
166 write(out, &size, sizeof(int)); | |
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); | |
172 } | |
173 | |
174 _exit(0); | |
175 } | |
176 | |
177 static void | |
178 resolved(gpointer data, gint source, GaimInputCondition cond) | |
179 { | |
180 int size; | |
181 GaimSrvQueryData *query_data = (GaimSrvQueryData*)data; | |
182 GaimSrvResponse *res; | |
183 GaimSrvResponse *tmp; | |
184 int i; | |
185 GaimSrvCallback cb = query_data->cb; | |
186 | |
187 read(source, &size, sizeof(int)); | |
188 gaim_debug_info("dnssrv","found %d SRV entries\n", size); | |
189 tmp = res = g_new0(GaimSrvResponse, size); | |
190 for (i = 0; i < size; i++) { | |
191 read(source, tmp++, sizeof(GaimSrvResponse)); | |
192 } | |
193 | |
194 cb(res, size, query_data->extradata); | |
195 | |
196 gaim_srv_cancel(query_data); | |
197 } | |
198 | |
199 #else /* _WIN32 */ | |
200 | |
201 /** The Jabber Server code was inspiration for parts of this. */ | |
202 | |
203 static gboolean | |
204 res_main_thread_cb(gpointer data) | |
205 { | |
206 GaimSrvResponse *srvres = NULL; | |
207 int size = 0; | |
208 GaimSrvQueryData *query_data = data; | |
209 | |
210 if(query_data->error_message != NULL) | |
211 gaim_debug_error("dnssrv", query_data->error_message); | |
212 else { | |
213 GaimSrvResponse *srvres_tmp = NULL; | |
214 GSList *lst = query_data->results; | |
215 | |
216 size = g_slist_length(query_data->results); | |
217 | |
218 if(query_data->cb) | |
219 srvres_tmp = srvres = g_new0(GaimSrvResponse, size); | |
220 while (lst) { | |
221 if(query_data->cb) | |
222 memcpy(srvres_tmp++, lst->data, sizeof(GaimSrvResponse)); | |
223 g_free(lst->data); | |
224 lst = g_slist_remove(lst, lst->data); | |
225 } | |
226 | |
227 query_data->results = NULL; | |
228 } | |
229 | |
230 gaim_debug_info("dnssrv", "found %d SRV entries\n", size); | |
231 | |
232 if(query_data->cb) | |
233 query_data->cb(srvres, size, query_data->extradata); | |
234 | |
235 query_data->resolver = NULL; | |
236 query_data->handle = 0; | |
237 | |
238 gaim_srv_cancel(query_data); | |
239 | |
240 return FALSE; | |
241 } | |
242 | |
243 static gpointer | |
244 res_thread(gpointer data) | |
245 { | |
246 PDNS_RECORD dr = NULL; | |
247 int type = DNS_TYPE_SRV; | |
248 DNS_STATUS ds; | |
249 GaimSrvQueryData *query_data = data; | |
250 | |
251 ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); | |
252 if (ds != ERROR_SUCCESS) { | |
253 gchar *msg = g_win32_error_message(ds); | |
254 query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); | |
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 */ | |
264 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { | |
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); | |
280 query_data->results = lst; | |
281 } | |
282 | |
283 /* back to main thread */ | |
284 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ | |
285 g_idle_add(res_main_thread_cb, query_data); | |
286 | |
287 g_thread_exit(NULL); | |
288 return NULL; | |
289 } | |
290 | |
291 #endif | |
292 | |
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; | |
298 #ifndef _WIN32 | |
299 int in[2], out[2]; | |
300 int pid; | |
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 | |
309 #ifndef _WIN32 | |
310 if(pipe(in) || pipe(out)) { | |
311 gaim_debug_error("dnssrv", "Could not create pipe\n"); | |
312 g_free(query); | |
313 cb(NULL, 0, extradata); | |
314 return NULL; | |
315 } | |
316 | |
317 pid = fork(); | |
318 if (pid == -1) { | |
319 gaim_debug_error("dnssrv", "Could not create process!\n"); | |
320 cb(NULL, 0, extradata); | |
321 g_free(query); | |
322 return NULL; | |
323 } | |
324 | |
325 /* Child */ | |
326 if (pid == 0) | |
327 { | |
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 | |
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); | |
343 | |
344 g_free(query); | |
345 | |
346 return query_data; | |
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 | |
355 query_data = g_new0(GaimSrvQueryData, 1); | |
356 query_data->cb = cb; | |
357 query_data->query = query; | |
358 query_data->extradata = extradata; | |
359 | |
360 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) { | |
361 query_data->error_message = g_strdup_printf("System missing DNS API (Requires W2K+)\n"); | |
362 | |
363 /* Asynchronously call the callback since stuff may not expect | |
364 * the callback to be called before this returns */ | |
365 query_data->handle = g_idle_add(res_main_thread_cb, query_data); | |
366 | |
367 return query_data; | |
368 } | |
369 | |
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 : ""); | |
374 g_error_free(err); | |
375 | |
376 /* Asynchronously call the callback since stuff may not expect | |
377 * the callback to be called before this returns */ | |
378 query_data->handle = g_idle_add(res_main_thread_cb, query_data); | |
379 | |
380 return query_data; | |
381 } | |
382 | |
383 return query_data; | |
384 #endif | |
385 } | |
386 | |
387 void | |
388 gaim_srv_cancel(GaimSrvQueryData *query_data) | |
389 { | |
390 if (query_data->handle > 0) | |
391 gaim_input_remove(query_data->handle); | |
392 #ifdef _WIN32 | |
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 */ | |
400 query_data->cb = NULL; | |
401 return; | |
402 } | |
403 g_free(query_data->query); | |
404 g_free(query_data->error_message); | |
405 #endif | |
406 g_free(query_data); | |
407 } |