Mercurial > pidgin
comparison libgaim/dnsquery.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | f189327b9968 |
comparison
equal
deleted
inserted
replaced
14191:009db0b357b5 | 14192:60b1bc8dbf37 |
---|---|
1 /** | |
2 * @file dnsquery.c DNS query API | |
3 * @ingroup core | |
4 * | |
5 * gaim | |
6 * | |
7 * Gaim is the legal property of its developers, whose names are too numerous | |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | |
9 * source distribution. | |
10 * | |
11 * This program is free software; you can redistribute it and/or modify | |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
24 * | |
25 */ | |
26 | |
27 #include "internal.h" | |
28 #include "debug.h" | |
29 #include "notify.h" | |
30 #include "prefs.h" | |
31 #include "dnsquery.h" | |
32 #include "util.h" | |
33 | |
34 /************************************************************************** | |
35 * DNS query API | |
36 **************************************************************************/ | |
37 | |
38 struct _GaimDnsQueryData { | |
39 }; | |
40 | |
41 #if defined(__unix__) || defined(__APPLE__) | |
42 | |
43 #define MAX_DNS_CHILDREN 4 | |
44 | |
45 /* | |
46 * This structure represents both a pending DNS request and | |
47 * a free child process. | |
48 */ | |
49 typedef struct { | |
50 char *host; | |
51 int port; | |
52 GaimDnsQueryConnectFunction callback; | |
53 gpointer data; | |
54 guint inpa; | |
55 int fd_in, fd_out; | |
56 pid_t dns_pid; | |
57 } pending_dns_request_t; | |
58 | |
59 static GSList *free_dns_children = NULL; | |
60 static GQueue *queued_requests = NULL; | |
61 | |
62 static int number_of_dns_children = 0; | |
63 | |
64 typedef struct { | |
65 char hostname[512]; | |
66 int port; | |
67 } dns_params_t; | |
68 | |
69 typedef struct { | |
70 dns_params_t params; | |
71 GaimDnsQueryConnectFunction callback; | |
72 gpointer data; | |
73 } queued_dns_request_t; | |
74 | |
75 /* | |
76 * Begin the DNS resolver child process functions. | |
77 */ | |
78 #ifdef HAVE_SIGNAL_H | |
79 static void | |
80 trap_gdb_bug() | |
81 { | |
82 const char *message = | |
83 "Gaim's DNS child got a SIGTRAP signal.\n" | |
84 "This can be caused by trying to run gaim inside gdb.\n" | |
85 "There is a known gdb bug which prevents this. Supposedly gaim\n" | |
86 "should have detected you were using gdb and used an ugly hack,\n" | |
87 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" | |
88 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; | |
89 fputs("\n* * *\n",stderr); | |
90 fputs(message,stderr); | |
91 fputs("* * *\n\n",stderr); | |
92 execlp("xmessage","xmessage","-center", message, NULL); | |
93 _exit(1); | |
94 } | |
95 #endif | |
96 | |
97 static void | |
98 cope_with_gdb_brokenness() | |
99 { | |
100 #ifdef __linux__ | |
101 static gboolean already_done = FALSE; | |
102 char s[256], e[512]; | |
103 int n; | |
104 pid_t ppid; | |
105 | |
106 if(already_done) | |
107 return; | |
108 already_done = TRUE; | |
109 ppid = getppid(); | |
110 snprintf(s, sizeof(s), "/proc/%d/exe", ppid); | |
111 n = readlink(s, e, sizeof(e)); | |
112 if(n < 0) | |
113 return; | |
114 | |
115 e[MIN(n,sizeof(e)-1)] = '\0'; | |
116 | |
117 if(strstr(e,"gdb")) { | |
118 gaim_debug_info("dns", | |
119 "Debugger detected, performing useless query...\n"); | |
120 gethostbyname("x.x.x.x.x"); | |
121 } | |
122 #endif | |
123 } | |
124 | |
125 static void | |
126 gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) | |
127 { | |
128 dns_params_t dns_params; | |
129 const size_t zero = 0; | |
130 int rc; | |
131 #ifdef HAVE_GETADDRINFO | |
132 struct addrinfo hints, *res, *tmp; | |
133 char servname[20]; | |
134 #else | |
135 struct sockaddr_in sin; | |
136 const size_t addrlen = sizeof(sin); | |
137 #endif | |
138 | |
139 #ifdef HAVE_SIGNAL_H | |
140 signal(SIGHUP, SIG_DFL); | |
141 signal(SIGINT, SIG_DFL); | |
142 signal(SIGQUIT, SIG_DFL); | |
143 signal(SIGCHLD, SIG_DFL); | |
144 signal(SIGTERM, SIG_DFL); | |
145 signal(SIGTRAP, trap_gdb_bug); | |
146 #endif | |
147 | |
148 /* | |
149 * We resolve 1 host name for each iteration of this | |
150 * while loop. | |
151 * | |
152 * The top half of this reads in the hostname and port | |
153 * number from the socket with our parent. The bottom | |
154 * half of this resolves the IP (blocking) and sends | |
155 * the result back to our parent, when finished. | |
156 */ | |
157 while (1) { | |
158 const char ch = 'Y'; | |
159 fd_set fds; | |
160 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; | |
161 FD_ZERO(&fds); | |
162 FD_SET(child_in, &fds); | |
163 rc = select(child_in + 1, &fds, NULL, NULL, &tv); | |
164 if (!rc) { | |
165 if (show_debug) | |
166 printf("dns[%d]: nobody needs me... =(\n", getpid()); | |
167 break; | |
168 } | |
169 rc = read(child_in, &dns_params, sizeof(dns_params_t)); | |
170 if (rc < 0) { | |
171 perror("read()"); | |
172 break; | |
173 } | |
174 if (rc == 0) { | |
175 if (show_debug) | |
176 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); | |
177 _exit(0); | |
178 } | |
179 if (dns_params.hostname[0] == '\0') { | |
180 printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); | |
181 _exit(1); | |
182 } | |
183 /* Tell our parent that we read the data successfully */ | |
184 write(child_out, &ch, sizeof(ch)); | |
185 | |
186 /* We have the hostname and port, now resolve the IP */ | |
187 | |
188 #ifdef HAVE_GETADDRINFO | |
189 g_snprintf(servname, sizeof(servname), "%d", dns_params.port); | |
190 memset(&hints, 0, sizeof(hints)); | |
191 | |
192 /* This is only used to convert a service | |
193 * name to a port number. As we know we are | |
194 * passing a number already, we know this | |
195 * value will not be really used by the C | |
196 * library. | |
197 */ | |
198 hints.ai_socktype = SOCK_STREAM; | |
199 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); | |
200 write(child_out, &rc, sizeof(rc)); | |
201 if (rc != 0) { | |
202 close(child_out); | |
203 if (show_debug) | |
204 printf("dns[%d] Error: getaddrinfo returned %d\n", | |
205 getpid(), rc); | |
206 dns_params.hostname[0] = '\0'; | |
207 continue; | |
208 } | |
209 tmp = res; | |
210 while (res) { | |
211 size_t ai_addrlen = res->ai_addrlen; | |
212 write(child_out, &ai_addrlen, sizeof(ai_addrlen)); | |
213 write(child_out, res->ai_addr, res->ai_addrlen); | |
214 res = res->ai_next; | |
215 } | |
216 freeaddrinfo(tmp); | |
217 write(child_out, &zero, sizeof(zero)); | |
218 #else | |
219 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { | |
220 struct hostent *hp; | |
221 if (!(hp = gethostbyname(dns_params.hostname))) { | |
222 write(child_out, &h_errno, sizeof(int)); | |
223 close(child_out); | |
224 if (show_debug) | |
225 printf("DNS Error: %d\n", h_errno); | |
226 _exit(0); | |
227 } | |
228 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
229 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
230 sin.sin_family = hp->h_addrtype; | |
231 } else | |
232 sin.sin_family = AF_INET; | |
233 | |
234 sin.sin_port = htons(dns_params.port); | |
235 write(child_out, &addrlen, sizeof(addrlen)); | |
236 write(child_out, &sin, addrlen); | |
237 write(child_out, &zero, sizeof(zero)); | |
238 #endif | |
239 dns_params.hostname[0] = '\0'; | |
240 } | |
241 | |
242 close(child_out); | |
243 close(child_in); | |
244 | |
245 _exit(0); | |
246 } | |
247 | |
248 static pending_dns_request_t * | |
249 gaim_dns_new_resolverthread(gboolean show_debug) | |
250 { | |
251 pending_dns_request_t *req; | |
252 int child_out[2], child_in[2]; | |
253 | |
254 /* Create pipes for communicating with the child process */ | |
255 if (pipe(child_out) || pipe(child_in)) { | |
256 gaim_debug_error("dns", | |
257 "Could not create pipes: %s\n", strerror(errno)); | |
258 return NULL; | |
259 } | |
260 | |
261 req = g_new(pending_dns_request_t, 1); | |
262 | |
263 cope_with_gdb_brokenness(); | |
264 | |
265 /* Fork! */ | |
266 req->dns_pid = fork(); | |
267 | |
268 /* If we are the child process... */ | |
269 if (req->dns_pid == 0) { | |
270 /* We should not access the parent's side of the pipes, so close them */ | |
271 close(child_out[0]); | |
272 close(child_in[1]); | |
273 | |
274 gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); | |
275 /* The thread calls _exit() rather than returning, so we never get here */ | |
276 } | |
277 | |
278 /* We should not access the child's side of the pipes, so close them */ | |
279 close(child_out[1]); | |
280 close(child_in[0]); | |
281 if (req->dns_pid == -1) { | |
282 gaim_debug_error("dns", | |
283 "Could not create child process for DNS: %s\n", | |
284 strerror(errno)); | |
285 g_free(req); | |
286 return NULL; | |
287 } | |
288 | |
289 req->fd_out = child_out[0]; | |
290 req->fd_in = child_in[1]; | |
291 number_of_dns_children++; | |
292 gaim_debug_info("dns", | |
293 "Created new DNS child %d, there are now %d children.\n", | |
294 req->dns_pid, number_of_dns_children); | |
295 | |
296 return req; | |
297 } | |
298 /* | |
299 * End the DNS resolver child process functions. | |
300 */ | |
301 | |
302 /* | |
303 * Begin the functions for dealing with the DNS child processes. | |
304 */ | |
305 static void | |
306 req_free(pending_dns_request_t *req) | |
307 { | |
308 g_return_if_fail(req != NULL); | |
309 | |
310 close(req->fd_in); | |
311 close(req->fd_out); | |
312 | |
313 g_free(req->host); | |
314 g_free(req); | |
315 | |
316 number_of_dns_children--; | |
317 } | |
318 | |
319 static int | |
320 send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) | |
321 { | |
322 char ch; | |
323 int rc; | |
324 pid_t pid; | |
325 | |
326 /* This waitpid might return the child's PID if it has recently | |
327 * exited, or it might return an error if it exited "long | |
328 * enough" ago that it has already been reaped; in either | |
329 * instance, we can't use it. */ | |
330 if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { | |
331 gaim_debug_warning("dns", | |
332 "DNS child %d no longer exists\n", req->dns_pid); | |
333 return -1; | |
334 } else if (pid < 0) { | |
335 gaim_debug_warning("dns", | |
336 "Wait for DNS child %d failed: %s\n", | |
337 req->dns_pid, strerror(errno)); | |
338 return -1; | |
339 } | |
340 | |
341 /* Let's contact this lost child! */ | |
342 rc = write(req->fd_in, dns_params, sizeof(*dns_params)); | |
343 if (rc < 0) { | |
344 gaim_debug_error("dns", | |
345 "Unable to write to DNS child %d: %d\n", | |
346 req->dns_pid, strerror(errno)); | |
347 close(req->fd_in); | |
348 return -1; | |
349 } | |
350 | |
351 g_return_val_if_fail(rc == sizeof(*dns_params), -1); | |
352 | |
353 /* Did you hear me? (This avoids some race conditions) */ | |
354 rc = read(req->fd_out, &ch, sizeof(ch)); | |
355 if (rc != 1 || ch != 'Y') | |
356 { | |
357 gaim_debug_warning("dns", | |
358 "DNS child %d not responding. Killing it!\n", | |
359 req->dns_pid); | |
360 kill(req->dns_pid, SIGKILL); | |
361 return -1; | |
362 } | |
363 | |
364 gaim_debug_info("dns", | |
365 "Successfully sent DNS request to child %d\n", req->dns_pid); | |
366 | |
367 return 0; | |
368 } | |
369 | |
370 static void | |
371 host_resolved(gpointer data, gint source, GaimInputCondition cond); | |
372 | |
373 static void | |
374 release_dns_child(pending_dns_request_t *req) | |
375 { | |
376 g_free(req->host); | |
377 req->host = NULL; | |
378 | |
379 if (queued_requests && !g_queue_is_empty(queued_requests)) { | |
380 queued_dns_request_t *r = g_queue_pop_head(queued_requests); | |
381 req->host = g_strdup(r->params.hostname); | |
382 req->port = r->params.port; | |
383 req->callback = r->callback; | |
384 req->data = r->data; | |
385 | |
386 gaim_debug_info("dns", | |
387 "Processing queued DNS query for '%s' with child %d\n", | |
388 req->host, req->dns_pid); | |
389 | |
390 if (send_dns_request_to_child(req, &(r->params)) != 0) { | |
391 req_free(req); | |
392 req = NULL; | |
393 | |
394 gaim_debug_warning("dns", | |
395 "Intent of process queued query of '%s' failed, " | |
396 "requeueing...\n", r->params.hostname); | |
397 g_queue_push_head(queued_requests, r); | |
398 } else { | |
399 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); | |
400 g_free(r); | |
401 } | |
402 | |
403 } else { | |
404 req->host = NULL; | |
405 req->callback = NULL; | |
406 req->data = NULL; | |
407 free_dns_children = g_slist_append(free_dns_children, req); | |
408 } | |
409 } | |
410 | |
411 static void | |
412 host_resolved(gpointer data, gint source, GaimInputCondition cond) | |
413 { | |
414 pending_dns_request_t *req = (pending_dns_request_t*)data; | |
415 int rc, err; | |
416 GSList *hosts = NULL; | |
417 struct sockaddr *addr = NULL; | |
418 size_t addrlen; | |
419 | |
420 gaim_debug_info("dns", "Got response for '%s'\n", req->host); | |
421 gaim_input_remove(req->inpa); | |
422 | |
423 rc = read(req->fd_out, &err, sizeof(err)); | |
424 if ((rc == 4) && (err != 0)) | |
425 { | |
426 char message[1024]; | |
427 #ifdef HAVE_GETADDRINFO | |
428 g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", | |
429 gai_strerror(err), req->dns_pid); | |
430 #else | |
431 g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", | |
432 err, req->dns_pid); | |
433 #endif | |
434 gaim_debug_error("dns", "%s\n", message); | |
435 req->callback(NULL, req->data, message); | |
436 release_dns_child(req); | |
437 return; | |
438 } | |
439 if (rc > 0) | |
440 { | |
441 while (rc > 0) { | |
442 rc = read(req->fd_out, &addrlen, sizeof(addrlen)); | |
443 if (rc > 0 && addrlen > 0) { | |
444 addr = g_malloc(addrlen); | |
445 rc = read(req->fd_out, addr, addrlen); | |
446 hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); | |
447 hosts = g_slist_append(hosts, addr); | |
448 } else { | |
449 break; | |
450 } | |
451 } | |
452 } else if (rc == -1) { | |
453 char message[1024]; | |
454 g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); | |
455 gaim_debug_error("dns", "%s\n", message); | |
456 req->callback(NULL, req->data, message); | |
457 req_free(req); | |
458 return; | |
459 } else if (rc == 0) { | |
460 char message[1024]; | |
461 g_snprintf(message, sizeof(message), "EOF reading from DNS child"); | |
462 close(req->fd_out); | |
463 gaim_debug_error("dns", "%s\n", message); | |
464 req->callback(NULL, req->data, message); | |
465 req_free(req); | |
466 return; | |
467 } | |
468 | |
469 /* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ | |
470 | |
471 req->callback(hosts, req->data, NULL); | |
472 | |
473 release_dns_child(req); | |
474 } | |
475 /* | |
476 * End the functions for dealing with the DNS child processes. | |
477 */ | |
478 | |
479 GaimDnsQueryData * | |
480 gaim_dnsquery_a(const char *hostname, int port, GaimDnsQueryConnectFunction callback, gpointer data) | |
481 { | |
482 pending_dns_request_t *req = NULL; | |
483 dns_params_t dns_params; | |
484 gchar *host_temp; | |
485 gboolean show_debug; | |
486 | |
487 show_debug = gaim_debug_is_enabled(); | |
488 | |
489 host_temp = g_strstrip(g_strdup(hostname)); | |
490 strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); | |
491 g_free(host_temp); | |
492 dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; | |
493 dns_params.port = port; | |
494 | |
495 /* | |
496 * If we have any children, attempt to have them perform the DNS | |
497 * query. If we're able to send the query to a child, then req | |
498 * will be set to the pending_dns_request_t. Otherwise, req will | |
499 * be NULL and we'll need to create a new DNS request child. | |
500 */ | |
501 while (free_dns_children != NULL) { | |
502 req = free_dns_children->data; | |
503 free_dns_children = g_slist_remove(free_dns_children, req); | |
504 | |
505 if (send_dns_request_to_child(req, &dns_params) == 0) | |
506 /* We found an acceptable child, yay */ | |
507 break; | |
508 | |
509 req_free(req); | |
510 req = NULL; | |
511 } | |
512 | |
513 /* We need to create a new DNS request child */ | |
514 if (req == NULL) { | |
515 if (number_of_dns_children >= MAX_DNS_CHILDREN) { | |
516 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); | |
517 memcpy(&(r->params), &dns_params, sizeof(dns_params)); | |
518 r->callback = callback; | |
519 r->data = data; | |
520 if (!queued_requests) | |
521 queued_requests = g_queue_new(); | |
522 g_queue_push_tail(queued_requests, r); | |
523 | |
524 gaim_debug_info("dns", | |
525 "DNS query for '%s' queued\n", dns_params.hostname); | |
526 | |
527 return (GaimDnsQueryData *)1; | |
528 } | |
529 | |
530 req = gaim_dns_new_resolverthread(show_debug); | |
531 if (req == NULL) | |
532 { | |
533 gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); | |
534 return NULL; | |
535 } | |
536 send_dns_request_to_child(req, &dns_params); | |
537 } | |
538 | |
539 req->host = g_strdup(hostname); | |
540 req->port = port; | |
541 req->callback = callback; | |
542 req->data = data; | |
543 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); | |
544 | |
545 return (GaimDnsQueryData *)1; | |
546 } | |
547 | |
548 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ | |
549 | |
550 typedef struct _dns_tdata { | |
551 char *hostname; | |
552 int port; | |
553 GaimDnsQueryConnectFunction callback; | |
554 gpointer data; | |
555 GSList *hosts; | |
556 char *errmsg; | |
557 } dns_tdata; | |
558 | |
559 static gboolean dns_main_thread_cb(gpointer data) { | |
560 dns_tdata *td = (dns_tdata*)data; | |
561 if (td->errmsg != NULL) { | |
562 gaim_debug_info("dns", "%s\n", td->errmsg); | |
563 } | |
564 td->callback(td->hosts, td->data, td->errmsg); | |
565 g_free(td->hostname); | |
566 g_free(td->errmsg); | |
567 g_free(td); | |
568 return FALSE; | |
569 } | |
570 | |
571 static gpointer dns_thread(gpointer data) { | |
572 | |
573 #ifdef HAVE_GETADDRINFO | |
574 int rc; | |
575 struct addrinfo hints, *res, *tmp; | |
576 char servname[20]; | |
577 #else | |
578 struct sockaddr_in sin; | |
579 struct hostent *hp; | |
580 #endif | |
581 dns_tdata *td = (dns_tdata*)data; | |
582 | |
583 #ifdef HAVE_GETADDRINFO | |
584 g_snprintf(servname, sizeof(servname), "%d", td->port); | |
585 memset(&hints,0,sizeof(hints)); | |
586 | |
587 /* This is only used to convert a service | |
588 * name to a port number. As we know we are | |
589 * passing a number already, we know this | |
590 * value will not be really used by the C | |
591 * library. | |
592 */ | |
593 hints.ai_socktype = SOCK_STREAM; | |
594 if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { | |
595 tmp = res; | |
596 while(res) { | |
597 td->hosts = g_slist_append(td->hosts, | |
598 GSIZE_TO_POINTER(res->ai_addrlen)); | |
599 td->hosts = g_slist_append(td->hosts, | |
600 g_memdup(res->ai_addr, res->ai_addrlen)); | |
601 res = res->ai_next; | |
602 } | |
603 freeaddrinfo(tmp); | |
604 } else { | |
605 td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); | |
606 } | |
607 #else | |
608 if ((hp = gethostbyname(td->hostname))) { | |
609 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
610 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
611 sin.sin_family = hp->h_addrtype; | |
612 sin.sin_port = htons(td->port); | |
613 | |
614 td->hosts = g_slist_append(td->hosts, | |
615 GSIZE_TO_POINTER(sizeof(sin))); | |
616 td->hosts = g_slist_append(td->hosts, | |
617 g_memdup(&sin, sizeof(sin))); | |
618 } else { | |
619 td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); | |
620 } | |
621 #endif | |
622 /* back to main thread */ | |
623 g_idle_add(dns_main_thread_cb, td); | |
624 return 0; | |
625 } | |
626 | |
627 GaimDnsQueryData * | |
628 gaim_dnsquery_a(const char *hostname, int port, | |
629 GaimDnsQueryConnectFunction callback, gpointer data) | |
630 { | |
631 dns_tdata *td; | |
632 struct sockaddr_in sin; | |
633 GError* err = NULL; | |
634 | |
635 if(inet_aton(hostname, &sin.sin_addr)) { | |
636 GSList *hosts = NULL; | |
637 sin.sin_family = AF_INET; | |
638 sin.sin_port = htons(port); | |
639 hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); | |
640 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); | |
641 callback(hosts, data, NULL); | |
642 return (GaimDnsQueryData *)1; | |
643 } | |
644 | |
645 gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); | |
646 td = g_new0(dns_tdata, 1); | |
647 td->hostname = g_strdup(hostname); | |
648 td->port = port; | |
649 td->callback = callback; | |
650 td->data = data; | |
651 | |
652 if(!g_thread_create(dns_thread, td, FALSE, &err)) { | |
653 gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); | |
654 g_error_free(err); | |
655 g_free(td->hostname); | |
656 g_free(td); | |
657 return NULL; | |
658 } | |
659 return (GaimDnsQueryData *)1; | |
660 } | |
661 | |
662 #else /* not __unix__ or __APPLE__ or _WIN32 */ | |
663 | |
664 typedef struct { | |
665 gpointer data; | |
666 size_t addrlen; | |
667 struct sockaddr *addr; | |
668 GaimDnsQueryConnectFunction callback; | |
669 } pending_dns_request_t; | |
670 | |
671 static gboolean host_resolved(gpointer data) | |
672 { | |
673 pending_dns_request_t *req = (pending_dns_request_t*)data; | |
674 GSList *hosts = NULL; | |
675 hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); | |
676 hosts = g_slist_append(hosts, req->addr); | |
677 req->callback(hosts, req->data, NULL); | |
678 g_free(req); | |
679 return FALSE; | |
680 } | |
681 | |
682 GaimDnsQueryData * | |
683 gaim_dnsquery_a(const char *hostname, int port, | |
684 GaimDnsQueryConnectFunction callback, gpointer data) | |
685 { | |
686 struct sockaddr_in sin; | |
687 pending_dns_request_t *req; | |
688 | |
689 if (!inet_aton(hostname, &sin.sin_addr)) { | |
690 struct hostent *hp; | |
691 if(!(hp = gethostbyname(hostname))) { | |
692 gaim_debug_error("dns", | |
693 "gaim_gethostbyname(\"%s\", %d) failed: %d\n", | |
694 hostname, port, h_errno); | |
695 return NULL; | |
696 } | |
697 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
698 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
699 sin.sin_family = hp->h_addrtype; | |
700 } else | |
701 sin.sin_family = AF_INET; | |
702 sin.sin_port = htons(port); | |
703 | |
704 req = g_new(pending_dns_request_t, 1); | |
705 req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); | |
706 req->addrlen = sizeof(sin); | |
707 req->data = data; | |
708 req->callback = callback; | |
709 gaim_timeout_add(10, host_resolved, req); | |
710 return (GaimDnsQueryData *)1; | |
711 } | |
712 | |
713 #endif /* not __unix__ or __APPLE__ or _WIN32 */ |