Mercurial > pidgin
comparison libpurple/network.c @ 29741:35a1cf247168
Add network listen functions that accept a family argument (AF_INET(6?)).
These allow code to portably support IPv6 for listeners (mostly file
transfers and Bonjour). Callers should use the purple_socket_speaks_ipv4
to determine whether they need two sockets or just an IPv6 one. I used
GIO's g_socket_speaks_ipv4 as the inspiration for that.
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sat, 17 Apr 2010 01:27:04 +0000 |
parents | ce3f4bd8939b |
children | c91976cf319c |
comparison
equal
deleted
inserted
replaced
29740:5be4137c4a68 | 29741:35a1cf247168 |
---|---|
392 { | 392 { |
393 listen_map_external = map_external; | 393 listen_map_external = map_external; |
394 } | 394 } |
395 | 395 |
396 static PurpleNetworkListenData * | 396 static PurpleNetworkListenData * |
397 purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data) | 397 purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data) |
398 { | 398 { |
399 int listenfd = -1; | 399 int listenfd = -1; |
400 int flags; | 400 int flags; |
401 const int on = 1; | 401 const int on = 1; |
402 PurpleNetworkListenData *listen_data; | 402 PurpleNetworkListenData *listen_data; |
410 * Get a list of addresses on this machine. | 410 * Get a list of addresses on this machine. |
411 */ | 411 */ |
412 g_snprintf(serv, sizeof(serv), "%hu", port); | 412 g_snprintf(serv, sizeof(serv), "%hu", port); |
413 memset(&hints, 0, sizeof(struct addrinfo)); | 413 memset(&hints, 0, sizeof(struct addrinfo)); |
414 hints.ai_flags = AI_PASSIVE; | 414 hints.ai_flags = AI_PASSIVE; |
415 hints.ai_family = AF_UNSPEC; | 415 hints.ai_family = socket_family; |
416 hints.ai_socktype = socket_type; | 416 hints.ai_socktype = socket_type; |
417 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res); | 417 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res); |
418 if (errnum != 0) { | 418 if (errnum != 0) { |
419 #ifndef _WIN32 | 419 #ifndef _WIN32 |
420 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum)); | 420 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum)); |
434 for (next = res; next != NULL; next = next->ai_next) { | 434 for (next = res; next != NULL; next = next->ai_next) { |
435 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol); | 435 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol); |
436 if (listenfd < 0) | 436 if (listenfd < 0) |
437 continue; | 437 continue; |
438 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) | 438 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) |
439 purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno)); | 439 purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno)); |
440 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0) | 440 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0) |
441 break; /* success */ | 441 break; /* success */ |
442 /* XXX - It is unclear to me (datallah) whether we need to be | 442 /* XXX - It is unclear to me (datallah) whether we need to be |
443 using a new socket each time */ | 443 using a new socket each time */ |
444 close(listenfd); | 444 close(listenfd); |
448 | 448 |
449 if (next == NULL) | 449 if (next == NULL) |
450 return NULL; | 450 return NULL; |
451 #else | 451 #else |
452 struct sockaddr_in sockin; | 452 struct sockaddr_in sockin; |
453 | |
454 if (socket_family != AF_INET && socket_family != AF_UNSPEC) { | |
455 purple_debug_warning("network", "Address family %d only " | |
456 "supported when built with getaddrinfo() " | |
457 "support\n", socket_family); | |
458 return NULL; | |
459 } | |
453 | 460 |
454 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) { | 461 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) { |
455 purple_debug_warning("network", "socket: %s\n", g_strerror(errno)); | 462 purple_debug_warning("network", "socket: %s\n", g_strerror(errno)); |
456 return NULL; | 463 return NULL; |
457 } | 464 } |
490 listen_data->retry = TRUE; | 497 listen_data->retry = TRUE; |
491 listen_data->cb = cb; | 498 listen_data->cb = cb; |
492 listen_data->cb_data = cb_data; | 499 listen_data->cb_data = cb_data; |
493 listen_data->socket_type = socket_type; | 500 listen_data->socket_type = socket_type; |
494 | 501 |
495 if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports")) | 502 if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external || |
503 !purple_prefs_get_bool("/purple/network/map_ports")) | |
496 { | 504 { |
497 purple_debug_info("network", "Skipping external port mapping.\n"); | 505 purple_debug_info("network", "Skipping external port mapping.\n"); |
498 /* The pmp_map_cb does what we want to do */ | 506 /* The pmp_map_cb does what we want to do */ |
499 purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data); | 507 purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data); |
500 } | 508 } |
517 | 525 |
518 return listen_data; | 526 return listen_data; |
519 } | 527 } |
520 | 528 |
521 PurpleNetworkListenData * | 529 PurpleNetworkListenData * |
530 purple_network_listen_family(unsigned short port, int socket_family, | |
531 int socket_type, PurpleNetworkListenCallback cb, | |
532 gpointer cb_data) | |
533 { | |
534 g_return_val_if_fail(port != 0, NULL); | |
535 | |
536 return purple_network_do_listen(port, socket_family, socket_type, | |
537 cb, cb_data); | |
538 } | |
539 | |
540 PurpleNetworkListenData * | |
522 purple_network_listen(unsigned short port, int socket_type, | 541 purple_network_listen(unsigned short port, int socket_type, |
523 PurpleNetworkListenCallback cb, gpointer cb_data) | 542 PurpleNetworkListenCallback cb, gpointer cb_data) |
524 { | 543 { |
525 g_return_val_if_fail(port != 0, NULL); | 544 return purple_network_listen_family(port, AF_UNSPEC, socket_type, |
526 | 545 cb, cb_data); |
527 return purple_network_do_listen(port, socket_type, cb, cb_data); | |
528 } | 546 } |
529 | 547 |
530 PurpleNetworkListenData * | 548 PurpleNetworkListenData * |
531 purple_network_listen_range(unsigned short start, unsigned short end, | 549 purple_network_listen_range_family(unsigned short start, unsigned short end, |
532 int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data) | 550 int socket_family, int socket_type, |
551 PurpleNetworkListenCallback cb, | |
552 gpointer cb_data) | |
533 { | 553 { |
534 PurpleNetworkListenData *ret = NULL; | 554 PurpleNetworkListenData *ret = NULL; |
535 | 555 |
536 if (purple_prefs_get_bool("/purple/network/ports_range_use")) { | 556 if (purple_prefs_get_bool("/purple/network/ports_range_use")) { |
537 start = purple_prefs_get_int("/purple/network/ports_range_start"); | 557 start = purple_prefs_get_int("/purple/network/ports_range_start"); |
540 if (end < start) | 560 if (end < start) |
541 end = start; | 561 end = start; |
542 } | 562 } |
543 | 563 |
544 for (; start <= end; start++) { | 564 for (; start <= end; start++) { |
545 ret = purple_network_do_listen(start, socket_type, cb, cb_data); | 565 ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data); |
546 if (ret != NULL) | 566 if (ret != NULL) |
547 break; | 567 break; |
548 } | 568 } |
549 | 569 |
550 return ret; | 570 return ret; |
571 } | |
572 | |
573 PurpleNetworkListenData * | |
574 purple_network_listen_range(unsigned short start, unsigned short end, | |
575 int socket_type, PurpleNetworkListenCallback cb, | |
576 gpointer cb_data) | |
577 { | |
578 return purple_network_listen_range_family(start, end, AF_UNSPEC, | |
579 socket_type, cb, cb_data); | |
551 } | 580 } |
552 | 581 |
553 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data) | 582 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data) |
554 { | 583 { |
555 if (listen_data->mapping_data != NULL) | 584 if (listen_data->mapping_data != NULL) |