Mercurial > pidgin.yaz
comparison src/proxy.c @ 10711:00483ba950bf
[gaim-migrate @ 12301]
Add some comments to the proxy code, rename some functions, and shuffle
things around in a way that I think makes it much more readable.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Mon, 21 Mar 2005 02:14:46 +0000 |
parents | cc9922ce3a52 |
children | dcbc56eb3f37 |
comparison
equal
deleted
inserted
replaced
10710:a2c0ce632bec | 10711:00483ba950bf |
---|---|
189 typedef void (*dns_callback_t)(GSList *hosts, gpointer data, | 189 typedef void (*dns_callback_t)(GSList *hosts, gpointer data, |
190 const char *error_message); | 190 const char *error_message); |
191 | 191 |
192 #ifdef __unix__ | 192 #ifdef __unix__ |
193 | 193 |
194 /* This structure represents both a pending DNS request and | 194 /* |
195 * a free child process. | 195 * This structure represents both a pending DNS request and |
196 * a free child process. | |
196 */ | 197 */ |
197 typedef struct { | 198 typedef struct { |
198 char *host; | 199 char *host; |
199 int port; | 200 int port; |
200 dns_callback_t callback; | 201 dns_callback_t callback; |
220 dns_params_t params; | 221 dns_params_t params; |
221 dns_callback_t callback; | 222 dns_callback_t callback; |
222 gpointer data; | 223 gpointer data; |
223 } queued_dns_request_t; | 224 } queued_dns_request_t; |
224 | 225 |
225 static void req_free(pending_dns_request_t *req) | 226 /* |
227 * Begin the DNS resolver child process functions. | |
228 */ | |
229 #ifdef HAVE_SIGNAL_H | |
230 static void | |
231 trap_gdb_bug() | |
232 { | |
233 const char *message = | |
234 "Gaim's DNS child got a SIGTRAP signal. \n" | |
235 "This can be caused by trying to run gaim inside gdb.\n" | |
236 "There is a known gdb bug which prevents this. Supposedly gaim\n" | |
237 "should have detected you were using gdb and used an ugly hack,\n" | |
238 "check cope_with_gdb_brokenness() in proxy.c.\n\n" | |
239 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; | |
240 fputs("\n* * *\n",stderr); | |
241 fputs(message,stderr); | |
242 fputs("* * *\n\n",stderr); | |
243 execlp("xmessage","xmessage","-center", message, NULL); | |
244 _exit(1); | |
245 } | |
246 #endif | |
247 | |
248 static void | |
249 cope_with_gdb_brokenness() | |
250 { | |
251 #ifdef __linux__ | |
252 static gboolean already_done = FALSE; | |
253 char s[256], e[512]; | |
254 int n; | |
255 pid_t ppid; | |
256 | |
257 if(already_done) | |
258 return; | |
259 already_done = TRUE; | |
260 ppid = getppid(); | |
261 snprintf(s, sizeof(s), "/proc/%d/exe", ppid); | |
262 n = readlink(s, e, sizeof(e)); | |
263 if(n < 0) | |
264 return; | |
265 | |
266 e[MIN(n,sizeof(e)-1)] = '\0'; | |
267 | |
268 if(strstr(e,"gdb")) { | |
269 gaim_debug_info("dns", | |
270 "Debugger detected, performing useless query...\n"); | |
271 gethostbyname("x.x.x.x.x"); | |
272 } | |
273 #endif | |
274 } | |
275 | |
276 static void | |
277 gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) | |
278 { | |
279 dns_params_t dns_params; | |
280 const size_t zero = 0; | |
281 int rc; | |
282 #if HAVE_GETADDRINFO | |
283 struct addrinfo hints, *res, *tmp; | |
284 char servname[20]; | |
285 #else | |
286 struct sockaddr_in sin; | |
287 const size_t addrlen = sizeof(sin); | |
288 #endif | |
289 | |
290 #ifdef HAVE_SIGNAL_H | |
291 signal(SIGHUP, SIG_DFL); | |
292 signal(SIGINT, SIG_DFL); | |
293 signal(SIGQUIT, SIG_DFL); | |
294 signal(SIGCHLD, SIG_DFL); | |
295 signal(SIGTERM, SIG_DFL); | |
296 signal(SIGTRAP, trap_gdb_bug); | |
297 #endif | |
298 | |
299 /* | |
300 * We resolve 1 host name for each iteration of this | |
301 * while loop. | |
302 * | |
303 * The top half of this reads in the hostname and port | |
304 * number from the socket with our parent. The bottom | |
305 * half of this resolves the IP (blocking) and sends | |
306 * the result back to our parent, when finished. | |
307 */ | |
308 while (1) { | |
309 const char ch = 'Y'; | |
310 fd_set fds; | |
311 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; | |
312 FD_ZERO(&fds); | |
313 FD_SET(child_in, &fds); | |
314 rc = select(child_in + 1, &fds, NULL, NULL, &tv); | |
315 if (!rc) { | |
316 if (show_debug) | |
317 fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid()); | |
318 break; | |
319 } | |
320 rc = read(child_in, &dns_params, sizeof(dns_params_t)); | |
321 if (rc < 0) { | |
322 perror("read()"); | |
323 break; | |
324 } | |
325 if (rc == 0) { | |
326 if (show_debug) | |
327 fprintf(stderr,"dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); | |
328 _exit(0); | |
329 } | |
330 if (dns_params.hostname[0] == '\0') { | |
331 fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); | |
332 _exit(1); | |
333 } | |
334 /* Tell our parent that we read the data successfully */ | |
335 write(child_out, &ch, sizeof(ch)); | |
336 | |
337 /* We have the hostname and port, now resolve the IP */ | |
338 | |
339 #if HAVE_GETADDRINFO | |
340 g_snprintf(servname, sizeof(servname), "%d", dns_params.port); | |
341 memset(&hints, 0, sizeof(hints)); | |
342 | |
343 /* This is only used to convert a service | |
344 * name to a port number. As we know we are | |
345 * passing a number already, we know this | |
346 * value will not be really used by the C | |
347 * library. | |
348 */ | |
349 hints.ai_socktype = SOCK_STREAM; | |
350 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); | |
351 write(child_out, &rc, sizeof(rc)); | |
352 if (rc != 0) { | |
353 close(child_out); | |
354 if (show_debug) | |
355 fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n", | |
356 getpid(), rc); | |
357 dns_params.hostname[0] = '\0'; | |
358 continue; | |
359 } | |
360 tmp = res; | |
361 while (res) { | |
362 size_t ai_addrlen = res->ai_addrlen; | |
363 write(child_out, &ai_addrlen, sizeof(ai_addrlen)); | |
364 write(child_out, res->ai_addr, res->ai_addrlen); | |
365 res = res->ai_next; | |
366 } | |
367 freeaddrinfo(tmp); | |
368 write(child_out, &zero, sizeof(zero)); | |
369 #else | |
370 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { | |
371 struct hostent *hp; | |
372 if (!(hp = gethostbyname(dns_params.hostname))) { | |
373 write(child_out, &h_errno, sizeof(int)); | |
374 close(child_out); | |
375 if (show_debug) | |
376 fprintf(stderr,"DNS Error: %d\n", h_errno); | |
377 _exit(0); | |
378 } | |
379 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
380 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
381 sin.sin_family = hp->h_addrtype; | |
382 } else | |
383 sin.sin_family = AF_INET; | |
384 | |
385 sin.sin_port = htons(dns_params.port); | |
386 write(child_out, &addrlen, sizeof(addrlen)); | |
387 write(child_out, &sin, addrlen); | |
388 write(child_out, &zero, sizeof(zero)); | |
389 #endif | |
390 dns_params.hostname[0] = '\0'; | |
391 } | |
392 | |
393 close(child_out); | |
394 close(child_in); | |
395 | |
396 _exit(0); | |
397 } | |
398 | |
399 static pending_dns_request_t * | |
400 gaim_dns_new_resolverthread(gboolean show_debug) | |
401 { | |
402 pending_dns_request_t *req; | |
403 int child_out[2], child_in[2]; | |
404 | |
405 /* Create pipes for communicating with the child process */ | |
406 if (pipe(child_out) || pipe(child_in)) { | |
407 gaim_debug_error("dns", | |
408 "Could not create pipes: %s\n", strerror(errno)); | |
409 return NULL; | |
410 } | |
411 | |
412 req = g_new(pending_dns_request_t, 1); | |
413 | |
414 cope_with_gdb_brokenness(); | |
415 | |
416 /* Fork! */ | |
417 req->dns_pid = fork(); | |
418 | |
419 /* If we are the child process... */ | |
420 if (req->dns_pid == 0) { | |
421 /* We should not access the parent's side of the pipes, so close them */ | |
422 close(child_out[0]); | |
423 close(child_in[1]); | |
424 | |
425 gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); | |
426 /* The thread calls _exit() rather than returning, so we never get here */ | |
427 } | |
428 | |
429 /* We should not access the child's side of the pipes, so close them */ | |
430 close(child_out[1]); | |
431 close(child_in[0]); | |
432 if (req->dns_pid == -1) { | |
433 gaim_debug_error("dns", | |
434 "Could not create child process for DNS: %s\n", | |
435 strerror(errno)); | |
436 g_free(req); | |
437 return NULL; | |
438 } | |
439 | |
440 req->fd_out = child_out[0]; | |
441 req->fd_in = child_in[1]; | |
442 number_of_dns_children++; | |
443 gaim_debug_info("dns", | |
444 "Created new DNS child %d, there are now %d children.\n", | |
445 req->dns_pid, number_of_dns_children); | |
446 | |
447 return req; | |
448 } | |
449 /* | |
450 * End the DNS resolver child process functions. | |
451 */ | |
452 | |
453 /* | |
454 * Begin the functions for dealing with the DNS child processes. | |
455 */ | |
456 static void | |
457 req_free(pending_dns_request_t *req) | |
226 { | 458 { |
227 g_return_if_fail(req != NULL); | 459 g_return_if_fail(req != NULL); |
228 | 460 |
229 close(req->fd_in); | 461 close(req->fd_in); |
230 close(req->fd_out); | 462 close(req->fd_out); |
233 g_free(req); | 465 g_free(req); |
234 | 466 |
235 number_of_dns_children--; | 467 number_of_dns_children--; |
236 } | 468 } |
237 | 469 |
238 static int send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) | 470 static int |
471 send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) | |
239 { | 472 { |
240 char ch; | 473 char ch; |
241 int rc; | 474 int rc; |
242 | 475 |
243 /* Are you alive? */ | 476 /* Are you alive? */ |
274 "Successfully sent DNS request to child %d\n", req->dns_pid); | 507 "Successfully sent DNS request to child %d\n", req->dns_pid); |
275 | 508 |
276 return 0; | 509 return 0; |
277 } | 510 } |
278 | 511 |
279 static void host_resolved(gpointer data, gint source, GaimInputCondition cond); | 512 static void |
280 | 513 host_resolved(gpointer data, gint source, GaimInputCondition cond); |
281 static void release_dns_child(pending_dns_request_t *req) | 514 |
515 static void | |
516 release_dns_child(pending_dns_request_t *req) | |
282 { | 517 { |
283 g_free(req->host); | 518 g_free(req->host); |
284 req->host = NULL; | 519 req->host = NULL; |
285 | 520 |
286 if (queued_requests && !g_queue_is_empty(queued_requests)) { | 521 if (queued_requests && !g_queue_is_empty(queued_requests)) { |
313 req->data = NULL; | 548 req->data = NULL; |
314 free_dns_children = g_slist_append(free_dns_children, req); | 549 free_dns_children = g_slist_append(free_dns_children, req); |
315 } | 550 } |
316 } | 551 } |
317 | 552 |
318 static void host_resolved(gpointer data, gint source, GaimInputCondition cond) | 553 static void |
554 host_resolved(gpointer data, gint source, GaimInputCondition cond) | |
319 { | 555 { |
320 pending_dns_request_t *req = (pending_dns_request_t*)data; | 556 pending_dns_request_t *req = (pending_dns_request_t*)data; |
321 int rc, err; | 557 int rc, err; |
322 GSList *hosts = NULL; | 558 GSList *hosts = NULL; |
323 struct sockaddr *addr = NULL; | 559 struct sockaddr *addr = NULL; |
376 | 612 |
377 req->callback(hosts, req->data, NULL); | 613 req->callback(hosts, req->data, NULL); |
378 | 614 |
379 release_dns_child(req); | 615 release_dns_child(req); |
380 } | 616 } |
381 | 617 /* |
382 static void trap_gdb_bug() | 618 * End the functions for dealing with the DNS child processes. |
383 { | 619 */ |
384 const char *message = | 620 |
385 "Gaim's DNS child got a SIGTRAP signal. \n" | 621 int |
386 "This can be caused by trying to run gaim inside gdb.\n" | 622 gaim_gethostbyname_async(const char *hostname, int port, dns_callback_t callback, gpointer data) |
387 "There is a known gdb bug which prevents this. Supposedly gaim\n" | |
388 "should have detected you were using gdb and used an ugly hack,\n" | |
389 "check cope_with_gdb_brokenness() in proxy.c.\n\n" | |
390 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; | |
391 fputs("\n* * *\n",stderr); | |
392 fputs(message,stderr); | |
393 fputs("* * *\n\n",stderr); | |
394 execlp("xmessage","xmessage","-center", message, NULL); | |
395 _exit(1); | |
396 } | |
397 | |
398 static void cope_with_gdb_brokenness() | |
399 { | |
400 #ifdef __linux__ | |
401 static gboolean already_done = FALSE; | |
402 char s[256], e[512]; | |
403 int n; | |
404 pid_t ppid; | |
405 | |
406 if(already_done) | |
407 return; | |
408 already_done = TRUE; | |
409 ppid = getppid(); | |
410 snprintf(s, sizeof(s), "/proc/%d/exe", ppid); | |
411 n = readlink(s, e, sizeof(e)); | |
412 if(n < 0) | |
413 return; | |
414 | |
415 e[MIN(n,sizeof(e)-1)] = '\0'; | |
416 | |
417 if(strstr(e,"gdb")) { | |
418 gaim_debug_info("dns", | |
419 "Debugger detected, performing useless query...\n"); | |
420 gethostbyname("x.x.x.x.x"); | |
421 } | |
422 #endif | |
423 } | |
424 | |
425 static void | |
426 gaim_dns_childthread(int child_out, int child_in, dns_params_t *dns_params, gboolean show_debug) | |
427 { | |
428 const size_t zero = 0; | |
429 int rc; | |
430 #if HAVE_GETADDRINFO | |
431 struct addrinfo hints, *res, *tmp; | |
432 char servname[20]; | |
433 #else | |
434 struct sockaddr_in sin; | |
435 const size_t addrlen = sizeof(sin); | |
436 #endif | |
437 | |
438 #ifdef HAVE_SIGNAL_H | |
439 signal(SIGHUP, SIG_DFL); | |
440 signal(SIGINT, SIG_DFL); | |
441 signal(SIGQUIT, SIG_DFL); | |
442 signal(SIGCHLD, SIG_DFL); | |
443 signal(SIGTERM, SIG_DFL); | |
444 signal(SIGTRAP, trap_gdb_bug); | |
445 #endif | |
446 | |
447 while (1) { | |
448 if (dns_params->hostname[0] == '\0') { | |
449 const char ch = 'Y'; | |
450 fd_set fds; | |
451 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; | |
452 FD_ZERO(&fds); | |
453 FD_SET(child_in, &fds); | |
454 rc = select(child_in + 1, &fds, NULL, NULL, &tv); | |
455 if (!rc) { | |
456 if (show_debug) | |
457 fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid()); | |
458 break; | |
459 } | |
460 rc = read(child_in, dns_params, sizeof(dns_params_t)); | |
461 if (rc < 0) { | |
462 perror("read()"); | |
463 break; | |
464 } | |
465 if (rc == 0) { | |
466 if (show_debug) | |
467 fprintf(stderr,"dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); | |
468 _exit(0); | |
469 } | |
470 if (dns_params->hostname[0] == '\0') { | |
471 fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params->port); | |
472 _exit(1); | |
473 } | |
474 write(child_out, &ch, sizeof(ch)); | |
475 } | |
476 | |
477 #if HAVE_GETADDRINFO | |
478 g_snprintf(servname, sizeof(servname), "%d", dns_params->port); | |
479 memset(&hints, 0, sizeof(hints)); | |
480 | |
481 /* This is only used to convert a service | |
482 * name to a port number. As we know we are | |
483 * passing a number already, we know this | |
484 * value will not be really used by the C | |
485 * library. | |
486 */ | |
487 hints.ai_socktype = SOCK_STREAM; | |
488 rc = getaddrinfo(dns_params->hostname, servname, &hints, &res); | |
489 write(child_out, &rc, sizeof(rc)); | |
490 if (rc != 0) { | |
491 close(child_out); | |
492 if (show_debug) | |
493 fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n", | |
494 getpid(), rc); | |
495 dns_params->hostname[0] = '\0'; | |
496 continue; | |
497 } | |
498 tmp = res; | |
499 while (res) { | |
500 size_t ai_addrlen = res->ai_addrlen; | |
501 write(child_out, &ai_addrlen, sizeof(ai_addrlen)); | |
502 write(child_out, res->ai_addr, res->ai_addrlen); | |
503 res = res->ai_next; | |
504 } | |
505 freeaddrinfo(tmp); | |
506 write(child_out, &zero, sizeof(zero)); | |
507 #else | |
508 if (!inet_aton(dns_params->hostname, &sin.sin_addr)) { | |
509 struct hostent *hp; | |
510 if (!(hp = gethostbyname(dns_params->hostname))) { | |
511 write(child_out, &h_errno, sizeof(int)); | |
512 close(child_out); | |
513 if (show_debug) | |
514 fprintf(stderr,"DNS Error: %d\n", h_errno); | |
515 _exit(0); | |
516 } | |
517 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
518 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
519 sin.sin_family = hp->h_addrtype; | |
520 } else | |
521 sin.sin_family = AF_INET; | |
522 | |
523 sin.sin_port = htons(dns_params->port); | |
524 write(child_out, &addrlen, sizeof(addrlen)); | |
525 write(child_out, &sin, addrlen); | |
526 write(child_out, &zero, sizeof(zero)); | |
527 #endif | |
528 dns_params->hostname[0] = '\0'; | |
529 } | |
530 | |
531 close(child_out); | |
532 close(child_in); | |
533 | |
534 _exit(0); | |
535 } | |
536 | |
537 int gaim_gethostbyname_async(const char *hostname, int port, dns_callback_t callback, gpointer data) | |
538 { | 623 { |
539 pending_dns_request_t *req = NULL; | 624 pending_dns_request_t *req = NULL; |
540 dns_params_t dns_params; | 625 dns_params_t dns_params; |
541 gchar *host_temp; | 626 gchar *host_temp; |
542 gboolean show_debug; | 627 gboolean show_debug; |
567 req = NULL; | 652 req = NULL; |
568 } | 653 } |
569 | 654 |
570 /* We need to create a new DNS request child */ | 655 /* We need to create a new DNS request child */ |
571 if (req == NULL) { | 656 if (req == NULL) { |
572 int child_out[2], child_in[2]; | |
573 | |
574 if (number_of_dns_children >= MAX_DNS_CHILDREN) { | 657 if (number_of_dns_children >= MAX_DNS_CHILDREN) { |
575 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); | 658 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); |
576 memcpy(&(r->params), &dns_params, sizeof(dns_params)); | 659 memcpy(&(r->params), &dns_params, sizeof(dns_params)); |
577 r->callback = callback; | 660 r->callback = callback; |
578 r->data = data; | 661 r->data = data; |
584 "DNS query for '%s' queued\n", dns_params.hostname); | 667 "DNS query for '%s' queued\n", dns_params.hostname); |
585 | 668 |
586 return 0; | 669 return 0; |
587 } | 670 } |
588 | 671 |
589 /* Create pipes for communicating with the child process */ | 672 req = gaim_dns_new_resolverthread(show_debug); |
590 if (pipe(child_out) || pipe(child_in)) { | 673 send_dns_request_to_child(req, &dns_params); |
591 gaim_debug_error("dns", | |
592 "Could not create pipes: %s\n", strerror(errno)); | |
593 return -1; | |
594 } | |
595 | |
596 req = g_new(pending_dns_request_t, 1); | |
597 | |
598 cope_with_gdb_brokenness(); | |
599 | |
600 /* Fork! */ | |
601 req->dns_pid = fork(); | |
602 | |
603 /* If we are the child process... */ | |
604 if (req->dns_pid == 0) { | |
605 /* We should not access the parent's side of the pipe, so close them... */ | |
606 close(child_out[0]); | |
607 close(child_in[1]); | |
608 | |
609 gaim_dns_childthread(child_out[1], child_in[0], &dns_params, show_debug); | |
610 /* The thread calls _exit() rather than returning, so we never get here */ | |
611 } | |
612 | |
613 /* We should not access the child's side of the pipe, so close them... */ | |
614 close(child_out[1]); | |
615 close(child_in[0]); | |
616 if (req->dns_pid == -1) { | |
617 gaim_debug_error("dns", | |
618 "Could not create child process for DNS: %s\n", | |
619 strerror(errno)); | |
620 g_free(req); | |
621 return -1; | |
622 } | |
623 | |
624 req->fd_out = child_out[0]; | |
625 req->fd_in = child_in[1]; | |
626 number_of_dns_children++; | |
627 gaim_debug_info("dns", | |
628 "Created new DNS child %d, there are now %d children.\n", | |
629 req->dns_pid, number_of_dns_children); | |
630 } | 674 } |
631 | 675 |
632 req->host = g_strdup(hostname); | 676 req->host = g_strdup(hostname); |
633 req->port = port; | 677 req->port = port; |
634 req->callback = callback; | 678 req->callback = callback; |
715 /* back to main thread */ | 759 /* back to main thread */ |
716 g_idle_add(dns_main_thread_cb, td); | 760 g_idle_add(dns_main_thread_cb, td); |
717 return 0; | 761 return 0; |
718 } | 762 } |
719 | 763 |
720 int gaim_gethostbyname_async(const char *hostname, int port, | 764 int |
721 dns_callback_t callback, gpointer data) { | 765 gaim_gethostbyname_async(const char *hostname, int port, |
766 dns_callback_t callback, gpointer data) | |
767 { | |
722 dns_tdata *td; | 768 dns_tdata *td; |
723 struct sockaddr_in sin; | 769 struct sockaddr_in sin; |
724 GError* err = NULL; | 770 GError* err = NULL; |
725 | 771 |
726 if(inet_aton(hostname, &sin.sin_addr)) { | 772 if(inet_aton(hostname, &sin.sin_addr)) { |