pidgin: 5bc899d1: Add network listen functions that accept...
darkrain42 at pidgin.im
darkrain42 at pidgin.im
Sun Apr 18 17:49:58 EDT 2010
-----------------------------------------------------------------
Revision: 5bc899d1d49d1b8701e547f4e4d76519e471d339
Ancestor: 68383ecd23cb8302b7e9036767d15f8fa23f8b04
Author: darkrain42 at pidgin.im
Date: 2010-04-17T01:27:04
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/5bc899d1d49d1b8701e547f4e4d76519e471d339
Modified files:
ChangeLog.API configure.ac libpurple/network.c
libpurple/network.h libpurple/util.c libpurple/util.h
ChangeLog:
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.
-------------- next part --------------
============================================================
--- ChangeLog.API 0f5f0089ca95e07b311d9eef31188f0d40027843
+++ ChangeLog.API ddac86771ebd64dafc63151ec5d0889e34c53f53
@@ -19,7 +19,15 @@ version 2.7.0 (??/??/????):
IPs on the system. On systems with the getifaddrs() function,
this will return both IPv4 and IPv6 addresses (excluding link-local
and loopback addresses). On others, it returns just IPv4 addresses.
+ * purple_network_listen_family and
+ purple_network_listen_range_family. These will replace the
+ versions without _family in 3.0.0 and allow the caller to
+ specifically request either an IPv4 or IPv6 socket. IPv6 is
+ only supported if the getaddrinfo() function is available
+ at build-time (not the case on Windows, currently).
* purple_prpl_got_media_caps
+ * purple_socket_get_family
+ * purple_socket_speaks_ipv4
* purple_unescape_text
* purple_uuid_random
* media_caps to the PurpleBuddy struct
============================================================
--- configure.ac b787c0bf7ca07c1e82e4e74b786b580544609c3e
+++ configure.ac 6d3d072a324d229cd8692310a8d44fc9009bddec
@@ -184,6 +184,12 @@ AC_CHECK_MEMBER([struct sockaddr.sa_len]
[Define if struct sockaddr has an sa_len member])],[:],
[#include <sys/socket.h>])
+dnl Check for v6-only sockets
+AC_CHECK_DECL([IPV6_V6ONLY],
+ [AC_DEFINE([HAVE_IPV6_V6ONLY],[1],
+ [Define if the IPV6_V6ONLY setsockopt option exists])],
+ [], [#include <netinet/in.h>])
+
dnl to prevent the g_stat()/g_unlink() crash,
dnl (09:50:07) Robot101: LSchiere2: it's easy. +LC_SYS_LARGEFILE somewhere in configure.ac
AC_SYS_LARGEFILE
============================================================
--- libpurple/network.c 6bee34054352355a434b132b0249009000f81cf8
+++ libpurple/network.c a1c5a75efbfba4fdf74b08087cb18a2971d1c021
@@ -394,7 +394,7 @@ static PurpleNetworkListenData *
}
static PurpleNetworkListenData *
-purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
{
int listenfd = -1;
int flags;
@@ -412,7 +412,7 @@ purple_network_do_listen(unsigned short
g_snprintf(serv, sizeof(serv), "%hu", port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = socket_family;
hints.ai_socktype = socket_type;
errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
if (errnum != 0) {
@@ -436,7 +436,7 @@ purple_network_do_listen(unsigned short
if (listenfd < 0)
continue;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
- purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
+ purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
break; /* success */
/* XXX - It is unclear to me (datallah) whether we need to be
@@ -451,6 +451,13 @@ purple_network_do_listen(unsigned short
#else
struct sockaddr_in sockin;
+ if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
+ purple_debug_warning("network", "Address family %d only "
+ "supported when built with getaddrinfo() "
+ "support\n", socket_family);
+ return NULL;
+ }
+
if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
return NULL;
@@ -492,7 +499,8 @@ purple_network_do_listen(unsigned short
listen_data->cb_data = cb_data;
listen_data->socket_type = socket_type;
- if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports"))
+ if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external ||
+ !purple_prefs_get_bool("/purple/network/map_ports"))
{
purple_debug_info("network", "Skipping external port mapping.\n");
/* The pmp_map_cb does what we want to do */
@@ -519,17 +527,29 @@ PurpleNetworkListenData *
}
PurpleNetworkListenData *
+purple_network_listen_family(unsigned short port, int socket_family,
+ int socket_type, PurpleNetworkListenCallback cb,
+ gpointer cb_data)
+{
+ g_return_val_if_fail(port != 0, NULL);
+
+ return purple_network_do_listen(port, socket_family, socket_type,
+ cb, cb_data);
+}
+
+PurpleNetworkListenData *
purple_network_listen(unsigned short port, int socket_type,
PurpleNetworkListenCallback cb, gpointer cb_data)
{
- g_return_val_if_fail(port != 0, NULL);
-
- return purple_network_do_listen(port, socket_type, cb, cb_data);
+ return purple_network_listen_family(port, AF_UNSPEC, socket_type,
+ cb, cb_data);
}
PurpleNetworkListenData *
-purple_network_listen_range(unsigned short start, unsigned short end,
- int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_listen_range_family(unsigned short start, unsigned short end,
+ int socket_family, int socket_type,
+ PurpleNetworkListenCallback cb,
+ gpointer cb_data)
{
PurpleNetworkListenData *ret = NULL;
@@ -542,7 +562,7 @@ purple_network_listen_range(unsigned sho
}
for (; start <= end; start++) {
- ret = purple_network_do_listen(start, socket_type, cb, cb_data);
+ ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data);
if (ret != NULL)
break;
}
@@ -550,6 +570,15 @@ purple_network_listen_range(unsigned sho
return ret;
}
+PurpleNetworkListenData *
+purple_network_listen_range(unsigned short start, unsigned short end,
+ int socket_type, PurpleNetworkListenCallback cb,
+ gpointer cb_data)
+{
+ return purple_network_listen_range_family(start, end, AF_UNSPEC,
+ socket_type, cb, cb_data);
+}
+
void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
{
if (listen_data->mapping_data != NULL)
============================================================
--- libpurple/network.h 61b4fe11283f86cd94bd18d1a119c9e77263e151
+++ libpurple/network.h 88072fc86992ea299997cb30e760b635c735a6c6
@@ -138,8 +138,8 @@ void purple_network_listen_map_external(
*
* This opens a listening port. The caller will want to set up a watcher
* of type PURPLE_INPUT_READ on the fd returned in cb. It will probably call
- * accept in the watcher callback, and then possibly remove the watcher and close
- * the listening socket, and add a new watcher on the new socket accept
+ * accept in the watcher callback, and then possibly remove the watcher and
+ * close the listening socket, and add a new watcher on the new socket accept
* returned.
*
* @param port The port number to bind to. Must be greater than 0.
@@ -158,6 +158,27 @@ PurpleNetworkListenData *purple_network_
int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
/**
+ * \copydoc purple_network_listen
+ *
+ * Libpurple does not currently do any port mapping (stateful firewall hole
+ * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
+ * addresses, a mapping is done).
+ *
+ * @param socket_family The protocol family of the socket. This should be
+ * AF_INET for IPv4 or AF_INET6 for IPv6. IPv6 sockets
+ * may or may not be able to accept IPv4 connections
+ * based on the system configuration (use
+ * purple_socket_speaks_ipv4 to check). If an IPv6
+ * socket doesn't accept V4-mapped addresses, you will
+ * need a second listener to support both v4 and v6.
+ * @since 2.7.0
+ * @deprecated This function will be renamed to purple_network_listen in 3.0.0.
+ */
+PurpleNetworkListenData *purple_network_listen_family(unsigned short port,
+ int socket_family, int socket_type, PurpleNetworkListenCallback cb,
+ gpointer cb_data);
+
+/**
* Opens a listening port selected from a range of ports. The range of
* ports used is chosen in the following manner:
* If a range is specified in preferences, these values are used.
@@ -192,6 +213,28 @@ PurpleNetworkListenData *purple_network_
PurpleNetworkListenCallback cb, gpointer cb_data);
/**
+ * \copydoc purple_network_listen_range
+ *
+ * Libpurple does not currently do any port mapping (stateful firewall hole
+ * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
+ * addresses, a mapping is done).
+ *
+ * @param socket_family The protocol family of the socket. This should be
+ * AF_INET for IPv4 or AF_INET6 for IPv6. IPv6 sockets
+ * may or may not be able to accept IPv4 connections
+ * based on the system configuration (use
+ * purple_socket_speaks_ipv4 to check). If an IPv6
+ * socket doesn't accept V4-mapped addresses, you will
+ * need a second listener to support both v4 and v6.
+ * @since 2.7.0
+ * @deprecated This function will be renamed to purple_network_listen_range
+ * in 3.0.0.
+ */
+PurpleNetworkListenData *purple_network_listen_range_family(
+ unsigned short start, unsigned short end, int socket_family,
+ int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
+
+/**
* This can be used to cancel any in-progress listener connection
* by passing in the return value from either purple_network_listen()
* or purple_network_listen_range().
============================================================
--- libpurple/util.c a16567f56409f472cb48ca40090a3a8721f157e0
+++ libpurple/util.c 0f58098fd34ed3b543da8daa4216477f943a2732
@@ -3002,7 +3002,48 @@ purple_fd_get_ip(int fd)
return NULL;
}
+int
+purple_socket_get_family(int fd)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ g_return_val_if_fail(fd >= 0, -1);
+
+ if (getsockname(fd, (struct sockaddr *)&addr, &len))
+ return -1;
+
+ return ((struct sockaddr *)&addr)->sa_family;
+}
+
+gboolean
+purple_socket_speaks_ipv4(int fd)
+{
+ int family;
+
+ g_return_val_if_fail(fd >= 0, FALSE);
+
+ family = purple_socket_get_family(fd);
+
+ switch (family) {
+ case AF_INET:
+ return TRUE;
+#if defined(IPV6_V6ONLY)
+ case AF_INET6:
+ {
+ int val = 0;
+ guint len = sizeof(val);
+
+ if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, &len) != 0)
+ return FALSE;
+ return !val;
+ }
+#endif
+ default:
+ return FALSE;
+ }
+}
+
/**************************************************************************
* String Functions
**************************************************************************/
============================================================
--- libpurple/util.h 3477af852f4f1a6d002efb39cb88318c76f0f10d
+++ libpurple/util.h b5e9d8f317f8318d87695608ba6660e7cccd0f5a
@@ -819,6 +819,28 @@ char *purple_fd_get_ip(int fd);
*/
char *purple_fd_get_ip(int fd);
+/**
+ * Returns the address family of a socket.
+ *
+ * @param fd The socket file descriptor.
+ *
+ * @return The address family of the socket (AF_INET, AF_INET6, etc)
+ * @since 2.7.0
+ */
+int purple_socket_get_family(int fd);
+
+/**
+ * Returns TRUE if a socket is capable of speaking IPv4.
+ *
+ * This is the case for IPv4 sockets and, on some systems, IPv6 sockets
+ * (due to the IPv4-mapped address functionality).
+ *
+ * @param fd The socket file descriptor
+ * @return TRUE if a socket can speak IPv4.
+ * @since 2.7.0
+ */
+gboolean purple_socket_speaks_ipv4(int fd);
+
/*@}*/
More information about the Commits
mailing list