From 80b7b8f1d619d626fbb9a3fb2b5beda8d3cb1890 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 18 Aug 2003 15:59:05 +0000 Subject: [PATCH] Make this more like a struct sockaddr again (like it used to be). In * libsoup/soup-address.c (SoupAddressPrivate): Make this more like a struct sockaddr again (like it used to be). In particular, add back the "port" field. Add a bunch of macros to try (and fail) to simplify some of the code. (soup_address_new): Now returns a SoupAddress directly rather than a random handle, and the caller can just use g_object_unref to cancel the lookup. Also, the callback now uses a SoupKnownErrorCode rather than a special-purpose address-lookup error code. (soup_address_new_cancel): No longer needed. (soup_address_new_sync): Removed (soup_address_new_any): Replaces soup_address_ipv4_any and soup_address_ipv6_any. (soup_address_get_name, etc): Gone. Use soup_address_resolve() now. (soup_address_get_physical): Renamed from soup_address_get_canonical_name. (soup_address_get_sockaddr): Replaces soup_address_make_sockaddr() * libsoup/soup-socket.c: Update for SoupAddress changes and make similar changes here. (soup_socket_new): Just creates a generic SoupSocket now. (soup_socket_connect): Client setup (soup_socket_listen): Server setup. Now also sets up an iochannel listening for connects and emits a "new_connection" signal as they come in. (soup_socket_start_ssl): Turns on SSL. (soup_socket_client_new, soup_socket_server_new): Utility functions that wrap the above. (soup_socket_new_cancel, soup_socket_new_sync): Gone (soup_socket_server_accept, soup_socket_server_try_accept): No longer needed. (soup_socket_get_iochannel): No longer adds a ref when returning the iochannel. Also, we set it to "close_on_unref" so that if a caller adds a ref to it, the connection will actually remain open even after the SoupSocket is destroyed. (soup_socket_get_local_address, soup_socket_get_remote_address): Let the caller get both of these. * libsoup/soup-connection.c: Don't keep a private copy of the socket's iochannel. (soup_connection_new): Don't need to set socket options here. SoupSocket does it. (soup_connection_start_ssl): Just call soup_socket_start_ssl. (soup_connection_get_iochannel): Just return the socket's iochannel (and don't ref it) * libsoup/soup-error.c: add SOUP_ERROR_CANT_RESOLVE and SOUP_ERROR_CANT_RESOLVE_PROXY * libsoup/soup-dns.c (soup_ntop): Make the address arg const. Remove the "FIXME add a CANT_RESOLVE error" and return SOUP_ERROR_CANT_RESOLVE instead. * libsoup/soup-server.c: Update for socket/address changes. Don't poke into SoupSocket's private fields. (soup_server_run_async): Just connect to the socket's "new_connection" signal. * libsoup/soup-context.c (try_create_connection, soup_context_connect_cb): Update for socket changes. Replace SOUP_CONNECT_ERROR codes with plain SOUP_ERROR codes. * libsoup/soup-misc.c (soup_signal_connect_once): Utility function to connect to a signal handler and connect another function to clean up the first signal handler after its first invocation. (Lets us use signals to replace one-off callbacks.) * libsoup/soup-private.h: Remove SoupSocketPrivate since it is actually private now. (struct _SoupServer): Remove accept_tag. * libsoup/soup-queue.c (soup_queue_read_done_cb, start_request): Don't unref the iochannel. (soup_queue_connect_cb): Takes a SoupKnownErrorCode now. * libsoup/soup-socks.c: Update for socket/address changes * tests/simple-httpd.c (main): s/SOUP_SERVER_ANY_PORT/SOUP_ADDRESS_ANY_PORT/ * tests/simple-proxy.c (main): Likewise * tests/timeserver.c: Update for SoupSocket's "new_connection" signal, and for SoupAddress changes. --- ChangeLog | 87 +++++ libsoup/soup-address.c | 641 +++++++++++++--------------------- libsoup/soup-address.h | 85 ++--- libsoup/soup-connection.c | 40 +-- libsoup/soup-context.c | 70 ++-- libsoup/soup-context.h | 9 +- libsoup/soup-dns.c | 5 +- libsoup/soup-dns.h | 2 +- libsoup/soup-error.c | 2 + libsoup/soup-error.h | 2 + libsoup/soup-marshal.list | 2 + libsoup/soup-misc.c | 58 ++++ libsoup/soup-misc.h | 7 + libsoup/soup-private.h | 8 - libsoup/soup-queue.c | 36 +- libsoup/soup-queue.h | 2 +- libsoup/soup-server.c | 111 ++---- libsoup/soup-server.h | 2 +- libsoup/soup-socket.c | 853 +++++++++++++++++++--------------------------- libsoup/soup-socket.h | 83 ++--- libsoup/soup-socks.c | 48 +-- tests/simple-httpd.c | 4 +- tests/simple-proxy.c | 2 +- tests/timeserver.c | 87 +++-- 24 files changed, 950 insertions(+), 1296 deletions(-) diff --git a/ChangeLog b/ChangeLog index 34f1250..f120400 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,90 @@ +2003-08-18 Dan Winship + + * libsoup/soup-address.c (SoupAddressPrivate): Make this more like + a struct sockaddr again (like it used to be). In particular, add + back the "port" field. Add a bunch of macros to try (and fail) to + simplify some of the code. + (soup_address_new): Now returns a SoupAddress directly rather than + a random handle, and the caller can just use g_object_unref to + cancel the lookup. Also, the callback now uses a + SoupKnownErrorCode rather than a special-purpose address-lookup + error code. + (soup_address_new_cancel): No longer needed. + (soup_address_new_sync): Removed + (soup_address_new_any): Replaces soup_address_ipv4_any and + soup_address_ipv6_any. + (soup_address_get_name, etc): Gone. Use soup_address_resolve() + now. + (soup_address_get_physical): Renamed from + soup_address_get_canonical_name. + (soup_address_get_sockaddr): Replaces soup_address_make_sockaddr() + + * libsoup/soup-socket.c: Update for SoupAddress changes and make + similar changes here. + (soup_socket_new): Just creates a generic SoupSocket now. + (soup_socket_connect): Client setup + (soup_socket_listen): Server setup. Now also sets up an iochannel + listening for connects and emits a "new_connection" signal as they + come in. + (soup_socket_start_ssl): Turns on SSL. + (soup_socket_client_new, soup_socket_server_new): Utility + functions that wrap the above. + (soup_socket_new_cancel, soup_socket_new_sync): Gone + (soup_socket_server_accept, soup_socket_server_try_accept): No + longer needed. + (soup_socket_get_iochannel): No longer adds a ref when returning + the iochannel. Also, we set it to "close_on_unref" so that if a + caller adds a ref to it, the connection will actually remain open + even after the SoupSocket is destroyed. + (soup_socket_get_local_address, soup_socket_get_remote_address): + Let the caller get both of these. + + * libsoup/soup-connection.c: Don't keep a private copy of the + socket's iochannel. + (soup_connection_new): Don't need to set socket options here. + SoupSocket does it. + (soup_connection_start_ssl): Just call soup_socket_start_ssl. + (soup_connection_get_iochannel): Just return the socket's + iochannel (and don't ref it) + + * libsoup/soup-error.c: add SOUP_ERROR_CANT_RESOLVE and + SOUP_ERROR_CANT_RESOLVE_PROXY + + * libsoup/soup-dns.c (soup_ntop): Make the address arg const. + Remove the "FIXME add a CANT_RESOLVE error" and return + SOUP_ERROR_CANT_RESOLVE instead. + + * libsoup/soup-server.c: Update for socket/address changes. Don't + poke into SoupSocket's private fields. + (soup_server_run_async): Just connect to the socket's + "new_connection" signal. + + * libsoup/soup-context.c (try_create_connection, + soup_context_connect_cb): Update for socket changes. Replace + SOUP_CONNECT_ERROR codes with plain SOUP_ERROR codes. + + * libsoup/soup-misc.c (soup_signal_connect_once): Utility function + to connect to a signal handler and connect another function to + clean up the first signal handler after its first invocation. + (Lets us use signals to replace one-off callbacks.) + + * libsoup/soup-private.h: Remove SoupSocketPrivate since it is + actually private now. + (struct _SoupServer): Remove accept_tag. + + * libsoup/soup-queue.c (soup_queue_read_done_cb, start_request): + Don't unref the iochannel. + (soup_queue_connect_cb): Takes a SoupKnownErrorCode now. + + * libsoup/soup-socks.c: Update for socket/address changes + + * tests/simple-httpd.c (main): + s/SOUP_SERVER_ANY_PORT/SOUP_ADDRESS_ANY_PORT/ + * tests/simple-proxy.c (main): Likewise + + * tests/timeserver.c: Update for SoupSocket's "new_connection" + signal, and for SoupAddress changes. + 2003-08-14 Dan Winship * libsoup/soup-connection.c: New, split out from soup-context and diff --git a/libsoup/soup-address.c b/libsoup/soup-address.c index b94e81f..66db876 100644 --- a/libsoup/soup-address.c +++ b/libsoup/soup-address.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -25,17 +24,7 @@ #include "soup-private.h" #include "soup-address.h" #include "soup-dns.h" - -struct SoupAddressPrivate { - char *name; - int family; - union { - struct in_addr in; -#ifdef HAVE_IPV6 - struct in6_addr in6; -#endif - } addr; -}; +#include "soup-marshal.h" #include #ifndef socklen_t @@ -51,6 +40,61 @@ struct SoupAddressPrivate { #define INADDR_NONE -1 #endif +struct SoupAddressPrivate { + struct sockaddr *sockaddr; + + char *name, *physical; + guint port; + + SoupDNSHandle lookup; + guint idle_id; +}; + +#define SOUP_ADDRESS_PORT_IS_VALID(port) (port >= 0 && port <= 65535) +#define SOUP_ADDRESS_FAMILY(addr) (addr->priv->sockaddr->sa_family) + +#define SOUP_SIN(addr) ((struct sockaddr_in *)addr->priv->sockaddr) + +#ifdef HAVE_IPV6 + +# define SOUP_SIN6(addr) ((struct sockaddr_in6 *)addr->priv->sockaddr) + +# define SOUP_ADDRESS_FAMILY_IS_VALID(family) \ + (family == AF_INET || family == AF_INET6) +# define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) \ + (family == AF_INET ? sizeof (struct sockaddr_in) : \ + sizeof (struct sockaddr_in6)) +# define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) \ + (family == AF_INET ? sizeof (struct in_addr) : \ + sizeof (struct in6_addr)) + +# define SOUP_ADDRESS_DATA(addr) \ + (addr->priv->sockaddr->sa_family == AF_INET ? \ + (gpointer)&SOUP_SIN(addr)->sin_addr : \ + (gpointer)&SOUP_SIN6(addr)->sin6_addr) +# define SOUP_ADDRESS_PORT(addr) \ + (addr->priv->sockaddr->sa_family == AF_INET ? \ + SOUP_SIN(addr)->sin_port : \ + SOUP_SIN6(addr)->sin6_port) + +#else + +# define SOUP_ADDRESS_FAMILY_IS_VALID(family) (family == AF_INET6) +# define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) sizeof (struct sockaddr_in) +# define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) sizeof (struct in_addr) + +# define SOUP_ADDRESS_DATA(addr) ((gpointer)&SOUP_SIN(addr)->sin_addr) +# define SOUP_ADDRESS_PORT(addr) (SOUP_SIN(addr)->sin_port) + +#endif + +enum { + DNS_RESULT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + #define PARENT_TYPE G_TYPE_OBJECT static GObjectClass *parent_class; @@ -67,8 +111,17 @@ finalize (GObject *object) { SoupAddress *addr = SOUP_ADDRESS (object); + if (addr->priv->sockaddr) + g_free (addr->priv->sockaddr); if (addr->priv->name) g_free (addr->priv->name); + if (addr->priv->physical) + g_free (addr->priv->physical); + + if (addr->priv->lookup) + soup_gethostby_cancel (addr->priv->lookup); + if (addr->priv->idle_id) + g_source_remove (addr->priv->idle_id); g_free (addr->priv); @@ -82,68 +135,50 @@ class_init (GObjectClass *object_class) /* virtual method override */ object_class->finalize = finalize; + + /* signals */ + signals[DNS_RESULT] = + g_signal_new ("dns_result", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SoupAddressClass, dns_result), + NULL, NULL, + soup_marshal_NONE__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); } SOUP_MAKE_TYPE (soup_address, SoupAddress, class_init, init, PARENT_TYPE) -static void -soup_address_new_sync_cb (SoupAddress *addr, - SoupAddressStatus status, - gpointer user_data) -{ - SoupAddress **ret = user_data; - *ret = addr; -} /** - * soup_address_new_sync: - * @name: a hostname, as with soup_address_new() + * soup_address_new: + * @name: a hostname or physical address + * @port: a port number + * + * Creates a #SoupAddress from @name and @port. The #SoupAddress's IP + * address will not be available right away; the caller can listen for + * a #dns_result signal to know when @name has been resolved. * - * Return value: a #SoupAddress, or %NULL if the lookup fails. + * The DNS lookup can be cancelled by destroying the address object. + * + * Return value: a #SoupAddress **/ SoupAddress * -soup_address_new_sync (const char *name) -{ - SoupAddress *ret = (SoupAddress *) 0xdeadbeef; - - soup_address_new (name, soup_address_new_sync_cb, &ret); - - while (1) { - g_main_iteration (TRUE); - if (ret != (SoupAddress *) 0xdeadbeef) return ret; - } - - return ret; -} - -static SoupAddress * -new_address (const char *name, int family, gpointer addr_data) +soup_address_new (const char *name, guint port) { SoupAddress *addr; - addr = g_object_new (SOUP_TYPE_ADDRESS, NULL); - if (name) - addr->priv->name = g_strdup (name); - addr->priv->family = family; - - switch (family) { - case AF_INET: - memcpy (&addr->priv->addr.in, addr_data, - sizeof (addr->priv->addr.in)); - break; + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL); -#ifdef HAVE_IPV6 - case AF_INET6: - memcpy (&addr->priv->addr.in6, addr_data, - sizeof (addr->priv->addr.in6)); - break; -#endif + addr = g_object_new (SOUP_TYPE_ADDRESS, NULL); + addr->priv->name = g_strdup (name); + addr->priv->port = port; - default: - g_object_unref (addr); - addr = NULL; - } + /* Start a lookup */ + soup_address_resolve (addr, NULL, NULL); return addr; } @@ -151,432 +186,222 @@ new_address (const char *name, int family, gpointer addr_data) /** * soup_address_new_from_sockaddr: * @sa: a pointer to a sockaddr - * @port: pointer to a variable to store @sa's port number in - * - * This parses @sa and returns its address as a #SoupAddress - * and its port in @port. @sa can point to a #sockaddr_in or - * (if soup was compiled with IPv6 support) a #sockaddr_in6. - * - * Return value: a #SoupAddress, or %NULL if the lookup fails. - **/ -SoupAddress * -soup_address_new_from_sockaddr (struct sockaddr *sa, guint *port) -{ - switch (sa->sa_family) { - case AF_INET: - { - struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; - - if (port) - *port = g_ntohs (sa_in->sin_port); - return new_address (NULL, AF_INET, &sa_in->sin_addr); - } - -#ifdef HAVE_IPV6 - case AF_INET6: - { - struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; - - if (port) - *port = g_ntohs (sa_in6->sin6_port); - return new_address (NULL, AF_INET6, &sa_in6->sin6_addr); - } -#endif - - default: - return NULL; - } -} - -/** - * soup_address_ipv4_any: + * @len: size of @sa * - * Return value: a #SoupAddress corresponding to %INADDR_ANY, suitable - * for passing to soup_socket_server_new(). + * Return value: a #SoupAddress equivalent to @sa (or %NULL if @sa's + * address family isn't supported) **/ SoupAddress * -soup_address_ipv4_any (void) +soup_address_new_from_sockaddr (struct sockaddr *sa, int len) { - static SoupAddress *ipv4_any = NULL; - - if (!ipv4_any) { - struct sockaddr_in sa_in; + SoupAddress *addr; - sa_in.sin_family = AF_INET; - sa_in.sin_addr.s_addr = INADDR_ANY; - ipv4_any = soup_address_new_from_sockaddr ((struct sockaddr *)&sa_in, NULL); - } + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (sa->sa_family), NULL); + g_return_val_if_fail (len == SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (sa->sa_family), NULL); - g_object_ref (ipv4_any); - return ipv4_any; + addr = g_object_new (SOUP_TYPE_ADDRESS, NULL); + addr->priv->sockaddr = g_memdup (sa, len); + addr->priv->port = ntohs (SOUP_ADDRESS_PORT (addr)); + return addr; } /** - * soup_address_ipv6_any: + * soup_address_new_any: + * @family: the address family + * @port: the port number (usually 0, meaning "any port") * - * Return value: If soup was compiled without IPv6 support, %NULL. - * Otherwise, a #SoupAddress corresponding to the IPv6 address "::", - * suitable for passing to soup_socket_server_new(). + * Return value: a #SoupAddress corresponding to the "any" address + * for @family (or %NULL if @family isn't supported), suitable for + * passing to soup_socket_server_new(). **/ SoupAddress * -soup_address_ipv6_any (void) +soup_address_new_any (SoupAddressFamily family, guint port) { - static SoupAddress *ipv6_any = NULL; + SoupAddress *addr; -#ifdef HAVE_IPV6 - if (!ipv6_any) { - struct sockaddr_in6 sa_in6; + g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (family), NULL); + g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL); - sa_in6.sin6_family = AF_INET6; - sa_in6.sin6_addr = in6addr_any; - ipv6_any = soup_address_new_from_sockaddr ((struct sockaddr *)&sa_in6, NULL); - } + addr = g_object_new (SOUP_TYPE_ADDRESS, NULL); + addr->priv->port = port; - g_object_ref (ipv6_any); -#endif - return ipv6_any; -} + addr->priv->sockaddr = + g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (family)); + SOUP_ADDRESS_FAMILY (addr) = family; + SOUP_ADDRESS_PORT (addr) = htons (port); -static void -soup_address_get_name_sync_cb (SoupAddress *addr, - SoupAddressStatus status, - const char *name, - gpointer user_data) -{ - const char **ret = user_data; - *ret = name; + return addr; } /** - * soup_address_get_name_sync: + * soup_address_get_name: * @addr: a #SoupAddress * - * Return value: the hostname associated with @addr, as with - * soup_address_get_name(). + * Return value: the hostname associated with @addr, or %NULL if + * it is not known. **/ const char * -soup_address_get_name_sync (SoupAddress *addr) +soup_address_get_name (SoupAddress *addr) { - const char *ret = (const char *) 0xdeadbeef; - - soup_address_get_name (addr, soup_address_get_name_sync_cb, &ret); - - while (1) { - g_main_iteration (TRUE); - if (ret != (const char *) 0xdeadbeef) return ret; - } + g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL); - return ret; + return addr->priv->name; } /** - * soup_address_get_canonical_name: - * @addr: Address to get the canonical name of. + * soup_address_get_sockaddr: + * @addr: a #SoupAddress + * @len: return location for sockaddr length * - * Get the "canonical" name of an address (eg, for IP4 the dotted - * decimal name 141.213.8.59). + * Returns the sockaddr associated with @addr, with its length in + * *@len. If the sockaddr is not yet know, returns %NULL. * - * Returns: %NULL if there was an error. The caller is responsible - * for deleting the returned string. + * Return value: the sockaddr, or %NULL **/ -char * -soup_address_get_canonical_name (SoupAddress *addr) +struct sockaddr * +soup_address_get_sockaddr (SoupAddress *addr, int *len) { - switch (addr->priv->family) { - case AF_INET: - { -#ifdef HAVE_INET_NTOP - char buffer[INET_ADDRSTRLEN]; - - inet_ntop (addr->priv->family, &addr->priv->addr.in, - buffer, sizeof (buffer)); - return g_strdup (buffer); -#else - return g_strdup (inet_ntoa (addr->priv->addr.in)); -#endif - } - -#ifdef HAVE_IPV6 - case AF_INET6: - { - char buffer[INET6_ADDRSTRLEN]; - - inet_ntop (addr->priv->family, &addr->priv->addr.in6, - buffer, sizeof (buffer)); - return g_strdup (buffer); - } -#endif - - default: - return NULL; - } -} + g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL); -/** - * soup_address_make_sockaddr: - * @addr: The %SoupAddress. - * @port: The port number - * @sa: Pointer to struct sockaddr * to output the sockaddr into - * @len: Pointer to int to return the size of the sockaddr into - * - * This creates an appropriate struct sockaddr for @addr and @port - * and outputs it into *@sa. The caller must free *@sa with g_free(). - **/ -void -soup_address_make_sockaddr (SoupAddress *addr, guint port, - struct sockaddr **sa, int *len) -{ - switch (addr->priv->family) { - case AF_INET: - { - struct sockaddr_in sa_in; - - memset (&sa_in, 0, sizeof (sa_in)); - sa_in.sin_family = AF_INET; - memcpy (&sa_in.sin_addr, &addr->priv->addr.in, - sizeof (sa_in.sin_addr)); - sa_in.sin_port = g_htons (port); - - *sa = g_memdup (&sa_in, sizeof (sa_in)); - *len = sizeof (sa_in); - break; - } + if (addr->priv->sockaddr && len) + *len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_FAMILY (addr)); -#ifdef HAVE_IPV6 - case AF_INET6: - { - struct sockaddr_in6 sa_in6; - - memset (&sa_in6, 0, sizeof (sa_in6)); - sa_in6.sin6_family = AF_INET6; - memcpy (&sa_in6.sin6_addr, &addr->priv->addr.in6, - sizeof (sa_in6.sin6_addr)); - sa_in6.sin6_port = g_htons (port); - - *sa = g_memdup (&sa_in6, sizeof (sa_in6)); - *len = sizeof (sa_in6); - break; - } -#endif - default: - *sa = NULL; - *len = 0; - } + return addr->priv->sockaddr; } /** - * soup_address_hash: - * @p: Pointer to an #SoupAddress. + * soup_address_get_physical: + * @addr: a #SoupAddress * - * Hash the address. This is useful for glib containers. + * Returns the physical address associated with @addr as a string. + * (Eg, "127.0.0.1"). If the address is not yet known, returns %NULL. * - * Returns: hash value. + * Return value: the physical address, or %NULL **/ -guint -soup_address_hash (const gpointer p) +const char * +soup_address_get_physical (SoupAddress *addr) { - const SoupAddress *addr; - - g_return_val_if_fail (p != NULL, 0); - - addr = (const SoupAddress*) p; + g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL); - /* This isn't network byte-order transparent... (Not sure how - * that works in the v6 case.) - */ + if (!addr->priv->sockaddr) + return NULL; - switch (addr->priv->family) { - case AF_INET: - return addr->priv->addr.in.s_addr; -#ifdef HAVE_IPV6 - case AF_INET6: - { - guint32 *bytes = (guint32 *)&(addr->priv->addr.in6.s6_addr); - return (bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3]); - } -#endif - default: - return 0; + if (!addr->priv->physical) { + addr->priv->physical = soup_ntop (SOUP_ADDRESS_DATA (addr), + SOUP_ADDRESS_FAMILY (addr)); } + + return addr->priv->physical; } /** - * soup_address_equal: - * @p1: Pointer to first #SoupAddress. - * @p2: Pointer to second #SoupAddress. - * - * Compare two #SoupAddress structures. + * soup_address_get_port: + * @addr: a #SoupAddress * - * Returns: 1 if they are the same; 0 otherwise. + * Return value: the port associated with @addr **/ -gint -soup_address_equal (const gpointer p1, const gpointer p2) +guint +soup_address_get_port (SoupAddress *addr) { - const SoupAddress *addr1 = (const SoupAddress*) p1; - const SoupAddress *addr2 = (const SoupAddress*) p2; - - g_return_val_if_fail (p1 != NULL && p2 != NULL, TRUE); + g_return_val_if_fail (SOUP_IS_ADDRESS (addr), 0); - /* Note network byte order doesn't matter */ - return memcmp (&addr1->priv->addr, &addr2->priv->addr, - sizeof (addr1->priv->addr)) == 0; + return addr->priv->port; } -typedef struct { - SoupDNSHandle handle; - SoupAddressNewFn func; - gpointer data; -} SoupAddressLookupState; - -typedef struct { - SoupAddress *addr; - SoupDNSHandle handle; - SoupAddressGetNameFn func; - gpointer data; -} SoupAddressReverseState; static void -soup_address_new_cb (SoupDNSHandle handle, SoupKnownErrorCode status, - struct hostent *h, gpointer data) +got_addr (SoupDNSHandle handle, SoupKnownErrorCode status, + struct hostent *h, gpointer data) { - SoupAddressLookupState *state = (SoupAddressLookupState*) data; - SoupAddress *addr = NULL; - - if (status == SOUP_ERROR_OK) - addr = new_address (h->h_name, h->h_addrtype, h->h_addr); + SoupAddress *addr = data; + + addr->priv->lookup = NULL; + + if (status == SOUP_ERROR_OK) { + if (!SOUP_ADDRESS_FAMILY_IS_VALID (h->h_addrtype)) { + status = SOUP_ERROR_CANT_RESOLVE; + goto done; + } + if (SOUP_ADDRESS_FAMILY_DATA_SIZE (h->h_addrtype) != h->h_length) { + status = SOUP_ERROR_MALFORMED; + goto done; + } + + addr->priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (h->h_addrtype)); + SOUP_ADDRESS_FAMILY (addr) = h->h_addrtype; + SOUP_ADDRESS_PORT (addr) = htons (addr->priv->port); + memcpy (SOUP_ADDRESS_DATA (addr), h->h_addr, h->h_length); + } - state->func (addr, - addr ? SOUP_ADDRESS_STATUS_OK : SOUP_ADDRESS_STATUS_ERROR, - state->data); - g_free (state); + done: + g_signal_emit (addr, signals[DNS_RESULT], 0, status); } -/** - * soup_address_new: - * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name - * (eg, 141.213.8.59). - * @func: Callback function. - * @data: User data passed when callback function is called. - * - * Create a SoupAddress from a name asynchronously. Once the structure - * is created, it will call the callback. It will call the callback if - * there is a failure. - * - * Currently this routine forks and does the lookup, which can cause - * some problems. In general, this will work ok for most programs most - * of the time. It will be slow or even fail when using operating - * systems that copy the entire process when forking. - * - * If you need to lookup a lot of addresses, you should call - * g_main_iteration(FALSE) between calls. This will help prevent an - * explosion of processes. - * - * Returns: ID of the lookup which can be used with - * soup_address_new_cancel() to cancel it; - **/ -SoupAddressNewId -soup_address_new (const char *name, SoupAddressNewFn func, gpointer data) +static void +got_name (SoupDNSHandle handle, SoupKnownErrorCode status, + struct hostent *h, gpointer data) { - SoupAddressLookupState *state; - - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (func != NULL, NULL); - - state = g_new0 (SoupAddressLookupState, 1); - state->func = func; - state->data = data; - state->handle = soup_gethostbyname (name, soup_address_new_cb, state); + SoupAddress *addr = data; - return state; -} + addr->priv->lookup = NULL; -/** - * soup_address_new_cancel: - * @id: ID of the lookup - * - * Cancel an asynchronous SoupAddress creation that was started with - * soup_address_new(). The lookup's callback will not be called. - */ -void -soup_address_new_cancel (SoupAddressNewId id) -{ - SoupAddressLookupState *state = id; + if (status == SOUP_ERROR_OK) + addr->priv->name = g_strdup (h->h_name); - soup_gethostby_cancel (state->handle); - g_free (state); + g_signal_emit (addr, signals[DNS_RESULT], 0, status); } -static void -soup_address_get_name_cb (SoupDNSHandle handle, SoupKnownErrorCode status, - struct hostent *h, gpointer data) +static gboolean +idle_dns_result (gpointer user_data) { - SoupAddressReverseState *state = data; - - if (status == SOUP_ERROR_OK && !state->addr->priv->name) - state->addr->priv->name = g_strdup (h->h_name); - - state->func (state->addr, - state->addr->priv->name ? SOUP_ADDRESS_STATUS_OK : SOUP_ADDRESS_STATUS_ERROR, - state->addr->priv->name, - state->data); + SoupAddress *addr = user_data; - g_object_unref (state->addr); - g_free (state); + addr->priv->idle_id = 0; + g_signal_emit (addr, signals[DNS_RESULT], 0, SOUP_ERROR_OK); + return FALSE; } /** - * soup_address_get_name: - * @addr: Address to get the name of. - * @func: Callback function. - * @data: User data passed when callback function is called. - * - * Get the nice name of the address (eg, "mofo.eecs.umich.edu"). - * This function will use the callback once it knows the nice name - * or if there is an error. - * - * As with soup_address_new(), this forks to do the lookup. + * soup_address_resolve: + * @addr: a #SoupAddress + * @callback: callback to call with the result + * @user_data: data for @callback * - * Returns: ID of the lookup which can be used with - * soup_address_get_name_cancel() to cancel it; + * Asynchronously resolves the missing half of @addr. (It's IP address + * if it was created with soup_address_new(), or it's hostname if it + * was created with soup_address_new_from_sockaddr() or + * soup_address_new_any().) @callback will be called when the + * resolution finishes (successfully or not). **/ -SoupAddressGetNameId -soup_address_get_name (SoupAddress *addr, - SoupAddressGetNameFn func, - gpointer data) +void +soup_address_resolve (SoupAddress *addr, + SoupAddressCallback callback, gpointer user_data) { - SoupAddressReverseState *state; + g_return_if_fail (SOUP_IS_ADDRESS (addr)); - g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL); - g_return_val_if_fail (func != NULL, NULL); - - state = g_new0 (SoupAddressReverseState, 1); - state->addr = g_object_ref (addr); - state->func = func; - state->data = data; - state->handle = soup_gethostbyaddr (&addr->priv->addr, - addr->priv->family, - soup_address_get_name_cb, - state); - - return state; -} + if (callback) { + soup_signal_connect_once (addr, "dns_result", + G_CALLBACK (callback), user_data); + } -/** - * soup_address_get_name_cancel: - * @id: ID of the lookup - * - * Cancel an asynchronous nice name lookup that was started with - * soup_address_get_name(). - */ -void -soup_address_get_name_cancel (SoupAddressGetNameId id) -{ - SoupAddressReverseState *state = id; + if (addr->priv->lookup) + return; - g_return_if_fail (state != NULL); + if (addr->priv->name && addr->priv->sockaddr) { + addr->priv->idle_id = g_idle_add (idle_dns_result, addr); + return; + } - soup_gethostby_cancel (state->handle); - g_object_unref (state->addr); - g_free(state); + if (addr->priv->name) { + addr->priv->lookup = + soup_gethostbyname (addr->priv->name, + got_addr, addr); + } else { + addr->priv->lookup = + soup_gethostbyaddr (SOUP_ADDRESS_DATA (addr), + SOUP_ADDRESS_FAMILY (addr), + got_name, addr); + } } diff --git a/libsoup/soup-address.h b/libsoup/soup-address.h index 1c2b9f8..5de2b09 100644 --- a/libsoup/soup-address.h +++ b/libsoup/soup-address.h @@ -10,6 +10,8 @@ #include #include +#include + #define SOUP_TYPE_ADDRESS (soup_address_get_type ()) #define SOUP_ADDRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_ADDRESS, SoupAddress)) #define SOUP_ADDRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_ADDRESS, SoupAddressClass)) @@ -28,64 +30,41 @@ typedef struct { typedef struct { GObjectClass parent_class; + /* signals */ + void (*dns_result) (SoupAddress *addr, SoupKnownErrorCode); } SoupAddressClass; -GType soup_address_get_type (void); - - -typedef gpointer SoupAddressNewId; - typedef enum { - SOUP_ADDRESS_STATUS_OK, - SOUP_ADDRESS_STATUS_ERROR -} SoupAddressStatus; - -typedef void (*SoupAddressNewFn) (SoupAddress *inetaddr, - SoupAddressStatus status, - gpointer user_data); - -SoupAddressNewId soup_address_new (const char *name, - SoupAddressNewFn func, - gpointer data); - -void soup_address_new_cancel (SoupAddressNewId id); - -SoupAddress *soup_address_new_sync (const char *name); - -SoupAddress *soup_address_ipv4_any (void); -SoupAddress *soup_address_ipv6_any (void); + SOUP_ADDRESS_FAMILY_IPV4 = AF_INET, +#ifdef AF_INET6 + SOUP_ADDRESS_FAMILY_IPV6 = AF_INET6 +#else + SOUP_ADDRESS_FAMILY_IPV6 = -1 +#endif +} SoupAddressFamily; +#define SOUP_ADDRESS_ANY_PORT 0 -typedef gpointer SoupAddressGetNameId; - -typedef void (*SoupAddressGetNameFn) (SoupAddress *inetaddr, - SoupAddressStatus status, - const char *name, - gpointer user_data); - -SoupAddressGetNameId - soup_address_get_name (SoupAddress *addr, - SoupAddressGetNameFn func, - gpointer data); - -void soup_address_get_name_cancel (SoupAddressGetNameId id); - -const char *soup_address_get_name_sync (SoupAddress *addr); - -char *soup_address_get_canonical_name (SoupAddress *addr); - - -SoupAddress *soup_address_new_from_sockaddr (struct sockaddr *sa, - guint *port); - -void soup_address_make_sockaddr (SoupAddress *addr, - guint port, - struct sockaddr **sa, - int *len); - -guint soup_address_hash (const gpointer p); +GType soup_address_get_type (void); -gint soup_address_equal (const gpointer p1, - const gpointer p2); +SoupAddress *soup_address_new (const char *name, + guint port); +SoupAddress *soup_address_new_from_sockaddr (struct sockaddr *sa, + int len); +SoupAddress *soup_address_new_any (SoupAddressFamily family, + guint port); + +typedef void (*SoupAddressCallback) (SoupAddress *addr, + SoupKnownErrorCode status, + gpointer data); +void soup_address_resolve (SoupAddress *addr, + SoupAddressCallback cb, + gpointer data); + +const char *soup_address_get_name (SoupAddress *addr); +struct sockaddr *soup_address_get_sockaddr (SoupAddress *addr, + int *len); +const char *soup_address_get_physical (SoupAddress *addr); +guint soup_address_get_port (SoupAddress *addr); #endif /* SOUP_ADDRESS_H */ diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index 571eafe..d5f1f19 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -30,7 +30,6 @@ struct SoupConnectionPrivate { SoupSocket *socket; - GIOChannel *raw_chan, *cooked_chan; gboolean in_use, new; time_t last_used; guint death_tag; @@ -44,7 +43,8 @@ enum { LAST_SIGNAL }; -guint signals[LAST_SIGNAL] = { 0 }; +static guint signals[LAST_SIGNAL] = { 0 }; + static void init (GObject *object) { @@ -100,17 +100,10 @@ SoupConnection * soup_connection_new (SoupSocket *sock) { SoupConnection *conn; - int yes = 1, flags = 0, fd; conn = g_object_new (SOUP_TYPE_CONNECTION, NULL); conn->priv->socket = g_object_ref (sock); - conn->priv->raw_chan = soup_socket_get_iochannel (sock); - fd = g_io_channel_unix_get_fd (conn->priv->raw_chan); - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof (yes)); - flags = fcntl (fd, F_GETFL, 0); - fcntl (fd, F_SETFL, flags | O_NONBLOCK); - return conn; } @@ -126,10 +119,7 @@ soup_connection_start_ssl (SoupConnection *conn) g_return_if_fail (SOUP_IS_CONNECTION (conn)); g_return_if_fail (conn->priv->socket != NULL); - if (conn->priv->cooked_chan) - g_io_channel_unref (conn->priv->cooked_chan); - conn->priv->cooked_chan = - soup_ssl_get_iochannel (conn->priv->raw_chan); + soup_socket_start_ssl (conn->priv->socket); } /** @@ -149,15 +139,6 @@ soup_connection_disconnect (SoupConnection *conn) conn->priv->death_tag = 0; } - if (conn->priv->raw_chan) { - g_io_channel_unref (conn->priv->raw_chan); - conn->priv->raw_chan = NULL; - } - if (conn->priv->cooked_chan) { - g_io_channel_unref (conn->priv->cooked_chan); - conn->priv->cooked_chan = NULL; - } - if (conn->priv->socket) { g_object_unref (conn->priv->socket); conn->priv->socket = NULL; @@ -180,8 +161,7 @@ soup_connection_is_connected (SoupConnection *conn) * Returns a #GIOChannel used for IO operations on the network * connection represented by @conn. * - * Return value: a pointer to the #GIOChannel used for IO on @conn, - * which the caller must unref. + * Return value: a pointer to the #GIOChannel used for IO on @conn */ GIOChannel * soup_connection_get_iochannel (SoupConnection *conn) @@ -191,13 +171,7 @@ soup_connection_get_iochannel (SoupConnection *conn) if (!conn->priv->socket) return NULL; - if (!conn->priv->cooked_chan) { - conn->priv->cooked_chan = conn->priv->raw_chan; - g_io_channel_ref (conn->priv->cooked_chan); - } - - g_io_channel_ref (conn->priv->cooked_chan); - return conn->priv->cooked_chan; + return soup_socket_get_iochannel (conn->priv->socket); } @@ -231,10 +205,8 @@ soup_connection_set_in_use (SoupConnection *conn, gboolean in_use) conn->priv->in_use = in_use; if (!conn->priv->in_use) { - if (!conn->priv->cooked_chan) - soup_connection_get_iochannel (conn); conn->priv->death_tag = - g_io_add_watch (conn->priv->cooked_chan, + g_io_add_watch (soup_socket_get_iochannel (conn->priv->socket), G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, connection_died, conn); diff --git a/libsoup/soup-context.c b/libsoup/soup-context.c index 14f4cf9..92bd592 100644 --- a/libsoup/soup-context.c +++ b/libsoup/soup-context.c @@ -286,7 +286,7 @@ struct SoupConnectData { gpointer user_data; guint timeout_tag; - gpointer connect_tag; + SoupSocket *sock; }; static void @@ -329,20 +329,17 @@ prune_least_used_connection (void) static gboolean retry_connect_timeout_cb (struct SoupConnectData *data); static void -soup_context_connect_cb (SoupSocket *socket, - SoupSocketConnectStatus status, - gpointer user_data) +soup_context_connect_cb (SoupSocket *socket, + SoupKnownErrorCode status, + gpointer user_data) { struct SoupConnectData *data = user_data; SoupContext *ctx = data->ctx; - SoupConnection *new_conn; + SoupConnection *new_conn = NULL; switch (status) { - case SOUP_SOCKET_CONNECT_ERROR_NONE: + case SOUP_ERROR_OK: new_conn = soup_connection_new (socket); - g_object_unref (socket); - if (ctx->priv->uri->protocol == SOUP_PROTOCOL_HTTPS) - soup_connection_start_ssl (new_conn); g_signal_connect (new_conn, "disconnected", G_CALLBACK (connection_disconnected), @@ -355,20 +352,13 @@ soup_context_connect_cb (SoupSocket *socket, ctx->priv->server->connections = g_slist_prepend (ctx->priv->server->connections, new_conn); - (*data->cb) (ctx, - SOUP_CONNECT_ERROR_NONE, - new_conn, - data->user_data); break; - case SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE: - connection_count--; - (*data->cb) (ctx, - SOUP_CONNECT_ERROR_ADDR_RESOLVE, - NULL, - data->user_data); + case SOUP_ERROR_CANT_RESOLVE: + connection_count--; break; - case SOUP_SOCKET_CONNECT_ERROR_NETWORK: + + default: connection_count--; /* @@ -384,14 +374,12 @@ soup_context_connect_cb (SoupSocket *socket, return; } - (*data->cb) (ctx, - SOUP_CONNECT_ERROR_NETWORK, - NULL, - data->user_data); break; } + (*data->cb) (ctx, status, new_conn, data->user_data); g_object_unref (ctx); + g_object_unref (data->sock); g_free (data); } @@ -412,7 +400,7 @@ try_existing_connections (SoupContext *ctx, soup_connection_set_in_use (conn, TRUE); /* Issue success callback */ - (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data); + (*cb) (ctx, SOUP_ERROR_OK, conn, user_data); return TRUE; } @@ -423,11 +411,10 @@ try_existing_connections (SoupContext *ctx, } static gboolean -try_create_connection (struct SoupConnectData **dataptr) +try_create_connection (struct SoupConnectData *data) { - struct SoupConnectData *data = *dataptr; int conn_limit = soup_get_connection_limit (); - gpointer connect_tag; + SoupUri *uri; /* * Check if we are allowed to create a new connection, otherwise wait @@ -436,26 +423,17 @@ try_create_connection (struct SoupConnectData **dataptr) if (conn_limit && connection_count >= conn_limit && !prune_least_used_connection ()) { - data->connect_tag = 0; + data->sock = NULL; return FALSE; } connection_count++; data->timeout_tag = 0; - connect_tag = soup_socket_connect (data->ctx->priv->uri->host, - data->ctx->priv->uri->port, - soup_context_connect_cb, - data); - /* - * NOTE: soup_socket_connect can fail immediately and call our - * callback which will delete the state. - */ - if (connect_tag) - data->connect_tag = connect_tag; - else - *dataptr = NULL; - + uri = data->ctx->priv->uri; + data->sock = soup_socket_client_new (uri->host, uri->port, + uri->protocol == SOUP_PROTOCOL_HTTPS, + soup_context_connect_cb, data); return TRUE; } @@ -470,7 +448,7 @@ retry_connect_timeout_cb (struct SoupConnectData *data) return FALSE; } - return try_create_connection (&data) == FALSE; + return try_create_connection (data) == FALSE; } /** @@ -514,7 +492,7 @@ soup_context_get_connection (SoupContext *ctx, data->ctx = g_object_ref (ctx); - if (!try_create_connection (&data)) { + if (!try_create_connection (data)) { data->timeout_tag = g_timeout_add (150, (GSourceFunc) retry_connect_timeout_cb, @@ -540,9 +518,9 @@ soup_context_cancel_connect (SoupConnectId tag) if (data->timeout_tag) g_source_remove (data->timeout_tag); - else if (data->connect_tag) { + else if (data->sock) { connection_count--; - soup_socket_connect_cancel (data->connect_tag); + g_object_unref (data->sock); } g_free (data); diff --git a/libsoup/soup-context.h b/libsoup/soup-context.h index 5521f7a..1a730b1 100644 --- a/libsoup/soup-context.h +++ b/libsoup/soup-context.h @@ -33,15 +33,8 @@ typedef struct { GType soup_context_get_type (void); - -typedef enum { - SOUP_CONNECT_ERROR_NONE, - SOUP_CONNECT_ERROR_ADDR_RESOLVE, - SOUP_CONNECT_ERROR_NETWORK -} SoupConnectErrorCode; - typedef void (*SoupConnectCallbackFn) (SoupContext *ctx, - SoupConnectErrorCode err, + SoupKnownErrorCode err, SoupConnection *conn, gpointer user_data); diff --git a/libsoup/soup-dns.c b/libsoup/soup-dns.c index 52464ed..017f36b 100644 --- a/libsoup/soup-dns.c +++ b/libsoup/soup-dns.c @@ -123,7 +123,7 @@ new_hostent_from_phys (const char *addr) } char * -soup_ntop (gpointer addr, int family) +soup_ntop (gconstpointer addr, int family) { switch (family) { case AF_INET: @@ -422,8 +422,7 @@ soup_gothost (gpointer user_data) info = entry->lookups->data; entry->lookups = g_slist_remove (entry->lookups, info); - /* FIXME: add a CANT_RESOLVE error */ - (*info->func) (info, entry->h ? SOUP_ERROR_OK : SOUP_ERROR_CANT_CONNECT, entry->h, info->data); + (*info->func) (info, entry->h ? SOUP_ERROR_OK : SOUP_ERROR_CANT_RESOLVE, entry->h, info->data); g_free (info); } diff --git a/libsoup/soup-dns.h b/libsoup/soup-dns.h index 4c5c420..c7ba32e 100644 --- a/libsoup/soup-dns.h +++ b/libsoup/soup-dns.h @@ -30,7 +30,7 @@ SoupDNSHandle soup_gethostbyaddr (gpointer addr, void soup_gethostby_cancel (SoupDNSHandle id); -char *soup_ntop (gpointer addr, +char *soup_ntop (gconstpointer addr, int family); #endif /* SOUP_DNS_H */ diff --git a/libsoup/soup-error.c b/libsoup/soup-error.c index d73ad36..efd8e95 100644 --- a/libsoup/soup-error.c +++ b/libsoup/soup-error.c @@ -20,6 +20,8 @@ struct { * SOUP_ERROR_CLASS_TRANSPORT */ { SOUP_ERROR_CANCELLED, "Cancelled" }, + { SOUP_ERROR_CANT_RESOLVE, "Cannot resolve hostname" }, + { SOUP_ERROR_CANT_RESOLVE_PROXY, "Cannot resolve proxy hostname" }, { SOUP_ERROR_CANT_CONNECT, "Cannot connect to destination" }, { SOUP_ERROR_CANT_CONNECT_PROXY, "Cannot connect to proxy" }, { SOUP_ERROR_IO, "Connection terminated " diff --git a/libsoup/soup-error.h b/libsoup/soup-error.h index dd19c3c..51803b6 100644 --- a/libsoup/soup-error.h +++ b/libsoup/soup-error.h @@ -35,6 +35,8 @@ typedef enum { * Transport Errors */ SOUP_ERROR_CANCELLED = 1, + SOUP_ERROR_CANT_RESOLVE = 2, + SOUP_ERROR_CANT_RESOLVE_PROXY = 3, SOUP_ERROR_CANT_CONNECT = 2, SOUP_ERROR_CANT_CONNECT_PROXY = 3, SOUP_ERROR_IO = 4, diff --git a/libsoup/soup-marshal.list b/libsoup/soup-marshal.list index fa33740..1b091eb 100644 --- a/libsoup/soup-marshal.list +++ b/libsoup/soup-marshal.list @@ -1 +1,3 @@ NONE:NONE +NONE:INT +NONE:OBJECT diff --git a/libsoup/soup-misc.c b/libsoup/soup-misc.c index 025e85a..ee89cb4 100644 --- a/libsoup/soup-misc.c +++ b/libsoup/soup-misc.c @@ -804,3 +804,61 @@ soup_set_authorize_callback (SoupAuthorizeFn authfn, soup_auth_fn = authfn; soup_auth_fn_user_data = user_data; } + + +typedef struct { + gpointer instance; + guint real_id, self_id; +} SoupSignalOnceData; + +static void +signal_once_destroy (gpointer ssod, GClosure *closure) +{ + g_free (ssod); +} + +static void +signal_once_cb (gpointer user_data, ...) +{ + SoupSignalOnceData *ssod = user_data; + + if (g_signal_handler_is_connected (ssod->instance, ssod->real_id)) + g_signal_handler_disconnect (ssod->instance, ssod->real_id); + g_signal_handler_disconnect (ssod->instance, ssod->self_id); +} + +/** + * soup_signal_connect_once: + * @instance: an object + * @detailed_signal: "signal-name" or "signal-name::detail" to connect to + * @c_handler: the #GCallback to connect + * @data: data to pass to @c_handler calls + * + * Connects a #GCallback function to a signal as with + * g_signal_connect(), but automatically removes the signal handler + * after its first invocation. + * + * Return value: the signal handler id + **/ +guint +soup_signal_connect_once (gpointer instance, const char *detailed_signal, + GCallback c_handler, gpointer data) +{ + SoupSignalOnceData *ssod; + + g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0); + g_return_val_if_fail (detailed_signal != NULL, 0); + g_return_val_if_fail (c_handler != NULL, 0); + + ssod = g_new0 (SoupSignalOnceData, 1); + ssod->instance = instance; + ssod->real_id = + g_signal_connect (instance, detailed_signal, c_handler, data); + ssod->self_id = + g_signal_connect_data (instance, detailed_signal, + G_CALLBACK (signal_once_cb), ssod, + signal_once_destroy, + G_CONNECT_SWAPPED); + + return ssod->real_id; +} diff --git a/libsoup/soup-misc.h b/libsoup/soup-misc.h index cb97b8b..3df12ce 100644 --- a/libsoup/soup-misc.h +++ b/libsoup/soup-misc.h @@ -92,6 +92,13 @@ int soup_base64_decode_step (const guchar *in, int *state, guint *save); +/* Misc utils */ + +guint soup_signal_connect_once (gpointer instance, + const char * signal, + GCallback c_handler, + gpointer data); + /* Useful debugging routines */ void soup_debug_print_headers (SoupMessage *req); diff --git a/libsoup/soup-private.h b/libsoup/soup-private.h index 52ac68d..ae8b58e 100644 --- a/libsoup/soup-private.h +++ b/libsoup/soup-private.h @@ -55,13 +55,6 @@ typedef struct { GHashTable *ntlm_auths; /* KEY: SoupConnection, VALUE: SoupAuth */ } SoupHost; -struct SoupSocketPrivate { - int sockfd; - SoupAddress *addr; - guint port; - GIOChannel *iochannel; -}; - #ifdef HAVE_IPV6 #define soup_sockaddr_max sockaddr_in6 #else @@ -75,7 +68,6 @@ struct _SoupServer { guint refcnt; GMainLoop *loop; - guint accept_tag; SoupSocket *listen_sock; GIOChannel *cgi_read_chan; diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c index 2386efa..53a6557 100644 --- a/libsoup/soup-queue.c +++ b/libsoup/soup-queue.c @@ -262,8 +262,6 @@ soup_queue_read_done_cb (const SoupDataBuffer *data, soup_queue_read_done_cb, soup_queue_error_cb, req); - - g_io_channel_unref (channel); } else { req->status = SOUP_STATUS_FINISHED; @@ -502,8 +500,6 @@ start_request (SoupContext *ctx, SoupMessage *req) soup_queue_error_cb, req); - g_io_channel_unref (channel); - req->status = SOUP_STATUS_SENDING_REQUEST; } @@ -587,7 +583,7 @@ proxy_connect (SoupContext *ctx, SoupMessage *req, SoupConnection *conn) void soup_queue_connect_cb (SoupContext *ctx, - SoupConnectErrorCode err, + SoupKnownErrorCode err, SoupConnection *conn, gpointer user_data) { @@ -600,7 +596,7 @@ soup_queue_connect_cb (SoupContext *ctx, req->connection = conn; switch (err) { - case SOUP_CONNECT_ERROR_NONE: + case SOUP_ERROR_OK: /* * NOTE: proxy_connect will either set an error or call us * again after proxy negotiation. @@ -611,29 +607,19 @@ soup_queue_connect_cb (SoupContext *ctx, start_request (ctx, req); break; - case SOUP_CONNECT_ERROR_ADDR_RESOLVE: - if (ctx != req->context) - soup_message_set_error_full ( - req, - SOUP_ERROR_CANT_CONNECT_PROXY, - "Unable to resolve proxy hostname"); - else - soup_message_set_error_full ( - req, - SOUP_ERROR_CANT_CONNECT, - "Unable to resolve hostname"); - + case SOUP_ERROR_CANT_RESOLVE: + if (ctx == req->context) + soup_message_set_error (req, SOUP_ERROR_CANT_RESOLVE); + else + soup_message_set_error (req, SOUP_ERROR_CANT_RESOLVE_PROXY); soup_message_issue_callback (req); break; - case SOUP_CONNECT_ERROR_NETWORK: - if (ctx != req->context) - soup_message_set_error (req, - SOUP_ERROR_CANT_CONNECT_PROXY); + default: + if (ctx == req->context) + soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT); else - soup_message_set_error (req, - SOUP_ERROR_CANT_CONNECT); - + soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT_PROXY); soup_message_issue_callback (req); break; } diff --git a/libsoup/soup-queue.h b/libsoup/soup-queue.h index acd938a..5368326 100644 --- a/libsoup/soup-queue.h +++ b/libsoup/soup-queue.h @@ -20,7 +20,7 @@ void soup_queue_message (SoupMessage *req, gpointer user_data); void soup_queue_connect_cb (SoupContext *ctx, - SoupConnectErrorCode err, + SoupKnownErrorCode err, SoupConnection *conn, gpointer user_data); diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c index fdd152d..f57a494 100644 --- a/libsoup/soup-server.c +++ b/libsoup/soup-server.c @@ -49,11 +49,12 @@ struct _SoupServerMessage { }; static SoupServer * -new_server (SoupAddress *address, SoupProtocol proto, guint port) +new_server (SoupAddress *address, SoupProtocol proto) { SoupServer *serv; SoupSocket *sock = NULL; GIOChannel *read_chan = NULL, *write_chan = NULL; + guint port = 0; g_return_val_if_fail (address, NULL); @@ -68,11 +69,14 @@ new_server (SoupAddress *address, SoupProtocol proto, guint port) return NULL; } } else { - sock = soup_socket_server_new (address, port); + sock = soup_socket_server_new (address, + proto == SOUP_PROTOCOL_HTTPS, + NULL, NULL); if (!sock) return NULL; - port = soup_socket_get_port (sock); + address = soup_socket_get_local_address (sock); + port = soup_address_get_port (address); } serv = g_new0 (SoupServer, 1); @@ -89,7 +93,7 @@ new_server (SoupAddress *address, SoupProtocol proto, guint port) SoupServer * soup_server_new (SoupProtocol proto, guint port) { - return new_server (soup_address_ipv4_any (), proto, port); + return new_server (soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, port), proto); } SoupServer * @@ -97,12 +101,12 @@ soup_server_new_with_host (const char *host, SoupProtocol proto, guint port) { SoupAddress *address; - address = soup_address_new_sync (host); + address = soup_address_new (host, port); if (!address) return NULL; - return new_server (address, proto, port); + return new_server (address, proto); } SoupServer * @@ -155,9 +159,6 @@ soup_server_unref (SoupServer *serv) --serv->refcnt; if (serv->refcnt == 0) { - if (serv->accept_tag) - g_source_remove (serv->accept_tag); - if (serv->listen_sock) g_object_unref (serv->listen_sock); @@ -280,8 +281,6 @@ destroy_message (SoupMessage *msg) start_another_request, data); } - - g_io_channel_unref (chan); } if (server_msg) { @@ -441,28 +440,6 @@ read_headers_cgi (SoupMessage *msg, return SOUP_TRANSFER_END; } -#define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s)) - -static gchar * -get_server_sockname (gint fd) -{ - struct sockaddr name; - int namelen; - gchar *host = NULL; - guchar *p; - - if (getsockname (fd, &name, &namelen) == 0) { - p = (guchar*) &(SOUP_SOCKADDR_IN(name).sin_addr); - host = g_strdup_printf ("%d.%d.%d.%d", - p [0], - p [1], - p [2], - p [3]); - } - - return host; -} - static void write_header (gchar *key, gchar *value, GString *ret) { @@ -532,7 +509,6 @@ issue_bad_request (SoupMessage *msg) SOUP_TRANSFER_CONTENT_LENGTH); channel = soup_socket_get_iochannel (msg->priv->server_sock); - msg->priv->write_tag = soup_transfer_write_simple (channel, header, @@ -540,9 +516,7 @@ issue_bad_request (SoupMessage *msg) write_done_cb, error_cb, msg); - - g_io_channel_unref (channel); -} /* issue_bad_request */ +} static void read_headers_cb (const GString *headers, @@ -631,9 +605,9 @@ read_headers_cb (const GString *headers, * No Host header, no AbsoluteUri */ SoupSocket *server_sock = msg->priv->server_sock; - gchar *host; + SoupAddress *addr = soup_socket_get_local_address (server_sock); + const char *host = soup_address_get_physical (addr); - host = get_server_sockname (server_sock->priv->sockfd); url = g_strdup_printf ( "%s%s:%d%s", @@ -908,8 +882,6 @@ read_done_cb (const SoupDataBuffer *data, req); } - g_io_channel_unref (channel); - return; } @@ -968,43 +940,27 @@ start_another_request (GIOChannel *serv_chan, return FALSE; } -static gboolean -conn_accept (GIOChannel *serv_chan, - GIOCondition condition, - gpointer user_data) +static void +new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data) { SoupServer *server = user_data; SoupMessage *msg; - GIOChannel *chan; - SoupSocket *sock; - - sock = soup_socket_server_try_accept (server->listen_sock); - if (!sock) return TRUE; msg = message_new (server); if (!msg) { g_warning ("Unable to create new incoming message\n"); - return TRUE; + return; } - chan = soup_socket_get_iochannel (sock); - - if (server->proto == SOUP_PROTOCOL_HTTPS) - sock->priv->iochannel = soup_ssl_get_server_iochannel (chan); - - msg->priv->server_sock = sock; + msg->priv->server_sock = g_object_ref (sock); msg->priv->read_tag = - soup_transfer_read (sock->priv->iochannel, + soup_transfer_read (soup_socket_get_iochannel (sock), FALSE, read_headers_cb, NULL, read_done_cb, error_cb, msg); - - g_io_channel_unref (chan); - - return TRUE; } typedef struct { @@ -1109,25 +1065,11 @@ soup_server_run_async (SoupServer *server) read_done_cgi_cb (&buf, msg); } } else { - GIOChannel *chan; - if (!server->listen_sock) goto START_ERROR; - /* - * Listen for new connections (if not already) - */ - if (!server->accept_tag) { - chan = soup_socket_get_iochannel (server->listen_sock); - - server->accept_tag = - g_io_add_watch (chan, - G_IO_IN, - (GIOFunc) conn_accept, - server); - - g_io_channel_unref (chan); - } + g_signal_connect (server->listen_sock, "new_connection", + G_CALLBACK (new_connection), server); } soup_server_ref (server); @@ -1220,27 +1162,20 @@ SoupAddress * soup_server_context_get_client_address (SoupServerContext *context) { SoupSocket *socket; - SoupAddress *address; g_return_val_if_fail (context != NULL, NULL); socket = context->msg->priv->server_sock; - address = soup_socket_get_address (socket); - - return address; + return soup_socket_get_remote_address (socket); } -gchar * +const char * soup_server_context_get_client_host (SoupServerContext *context) { - gchar *host; SoupAddress *address; address = soup_server_context_get_client_address (context); - host = g_strdup (soup_address_get_canonical_name (address)); - g_object_unref (address); - - return host; + return soup_address_get_physical (address); } static SoupServerAuthContext * diff --git a/libsoup/soup-server.h b/libsoup/soup-server.h index 8060786..3ef1938 100644 --- a/libsoup/soup-server.h +++ b/libsoup/soup-server.h @@ -91,7 +91,7 @@ GSList *soup_server_list_handlers (SoupServer *serv); SoupAddress *soup_server_context_get_client_address (SoupServerContext *context); -gchar *soup_server_context_get_client_host (SoupServerContext *context); +const char *soup_server_context_get_client_host (SoupServerContext *context); /* * Apache/soup-httpd module initializtion diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index ec6652a..6c24e8f 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -14,20 +14,37 @@ #include #include -#include #include +#include #include "soup-private.h" #include "soup-socket.h" +#include "soup-marshal.h" +#include "soup-ssl.h" -#include -#ifndef socklen_t -# define socklen_t size_t -#endif +#include +#include #define PARENT_TYPE G_TYPE_OBJECT static GObjectClass *parent_class; +enum { + CONNECT_RESULT, + NEW_CONNECTION, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct SoupSocketPrivate { + int sockfd; + SoupAddress *local_addr, *remote_addr; + GIOChannel *iochannel; + + guint watch; + gboolean server, ssl; +}; + static void init (GObject *object) { @@ -42,15 +59,17 @@ finalize (GObject *object) { SoupSocket *sock = SOUP_SOCKET (object); - if (sock->priv->sockfd != -1) - close (sock->priv->sockfd); - - if (sock->priv->addr) - g_object_unref (sock->priv->addr); + if (sock->priv->local_addr) + g_object_unref (sock->priv->local_addr); + if (sock->priv->remote_addr) + g_object_unref (sock->priv->remote_addr); if (sock->priv->iochannel) g_io_channel_unref (sock->priv->iochannel); + if (sock->priv->watch) + g_source_remove (sock->priv->watch); + g_free (sock->priv); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -63,601 +82,435 @@ class_init (GObjectClass *object_class) /* virtual method override */ object_class->finalize = finalize; -} - -SOUP_MAKE_TYPE (soup_socket, SoupSocket, class_init, init, PARENT_TYPE) - -typedef struct { - SoupSocketConnectFn func; - gpointer data; - guint port; - - gpointer inetaddr_id; - gpointer tcp_id; -} SoupSocketConnectState; - -static void -soup_socket_connect_tcp_cb (SoupSocket *socket, - SoupSocketConnectStatus status, - gpointer data) -{ - SoupSocketConnectState *state = (SoupSocketConnectState*) data; - SoupSocketConnectFn func = state->func; - gpointer user_data = state->data; - - if (status == SOUP_SOCKET_NEW_STATUS_OK) - (*func) (socket, - SOUP_SOCKET_CONNECT_ERROR_NONE, - user_data); - else - (*func) (NULL, - SOUP_SOCKET_CONNECT_ERROR_NETWORK, - user_data); - - if (state->tcp_id) - g_free (state); + /* signals */ + signals[CONNECT_RESULT] = + g_signal_new ("connect_result", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SoupSocketClass, connect_result), + NULL, NULL, + soup_marshal_NONE__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + signals[NEW_CONNECTION] = + g_signal_new ("new_connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SoupSocketClass, new_connection), + NULL, NULL, + soup_marshal_NONE__OBJECT, + G_TYPE_NONE, 1, + SOUP_TYPE_SOCKET); } -static void -soup_socket_connect_inetaddr_cb (SoupAddress *inetaddr, - SoupAddressStatus status, - gpointer data) -{ - SoupSocketConnectState *state = (SoupSocketConnectState*) data; - - if (status == SOUP_ADDRESS_STATUS_OK) { - state->tcp_id = soup_socket_new (inetaddr, state->port, - soup_socket_connect_tcp_cb, - state); - g_object_unref (inetaddr); - } else { - SoupSocketConnectFn func = state->func; - gpointer user_data = state->data; - - (*func) (NULL, - SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE, - user_data); - } +SOUP_MAKE_TYPE (soup_socket, SoupSocket, class_init, init, PARENT_TYPE) - if (state->inetaddr_id && !state->tcp_id) - g_free (state); - else - state->inetaddr_id = NULL; -} /** - * soup_socket_connect: - * @hostname: Name of host to connect to - * @port: Port to connect to - * @func: Callback function - * @data: User data passed when callback function is called. - * - * A quick and easy non-blocking #SoupSocket constructor. This - * connects to the specified address and port and then calls the - * callback with the data. Use this function when you're a client - * connecting to a server and you don't want to block or mess with - * #SoupAddress. It may call the callback before the function - * returns. It will call the callback if there is a failure. + * soup_socket_new: * - * Returns: ID of the connection which can be used with - * soup_socket_connect_cancel() to cancel it; %NULL if it succeeds or - * fails immediately. + * Return value: a new (disconnected) socket **/ -SoupSocketConnectId -soup_socket_connect (const char *hostname, - guint port, - SoupSocketConnectFn func, - gpointer data) +SoupSocket * +soup_socket_new (void) { - SoupSocketConnectState *state; - - g_return_val_if_fail (hostname != NULL, NULL); - g_return_val_if_fail (func != NULL, NULL); - - state = g_new0 (SoupSocketConnectState, 1); - state->func = func; - state->data = data; - state->port = port; - - state->inetaddr_id = soup_address_new (hostname, - soup_socket_connect_inetaddr_cb, - state); - /* NOTE: soup_address_new could succeed immediately - * and call our callback, in which case state->inetaddr_id - * will be NULL but state->tcp_id may be set. - */ - - if (state->tcp_id || state->inetaddr_id) - return state; - else { - g_free (state); - return NULL; - } + return g_object_new (SOUP_TYPE_SOCKET, NULL); } -/** - * soup_socket_connect_cancel: - * @id: Id of the connection. - * - * Cancel an asynchronous connection that was started with - * soup_socket_connect(). - */ -void -soup_socket_connect_cancel (SoupSocketConnectId id) -{ - SoupSocketConnectState *state = (SoupSocketConnectState*) id; - - g_return_if_fail (state != NULL); - - if (state->inetaddr_id) - soup_address_new_cancel (state->inetaddr_id); - else if (state->tcp_id) - soup_socket_new_cancel (state->tcp_id); - - g_free (state); -} +#define SOUP_SOCKET_NONBLOCKING (1<<0) +#define SOUP_SOCKET_NONBUFFERED (1<<1) +#define SOUP_SOCKET_REUSEADDR (1<<2) static void -soup_socket_connect_sync_cb (SoupSocket *socket, - SoupSocketConnectStatus status, - gpointer data) +soup_set_sockopts (int sockfd, int opts) { - SoupSocket **ret = data; - *ret = socket; -} - -SoupSocket * -soup_socket_connect_sync (const char *name, guint port) -{ - SoupSocket *ret = (SoupSocket *) 0xdeadbeef; - - soup_socket_connect (name, port, soup_socket_connect_sync_cb, &ret); + int flags; - while (1) { - g_main_iteration (TRUE); - if (ret != (SoupSocket *) 0xdeadbeef) return ret; + if (opts & SOUP_SOCKET_NONBLOCKING) { + flags = fcntl (sockfd, F_GETFL, 0); + if (flags != -1) + fcntl (sockfd, F_SETFL, flags | O_NONBLOCK); } - return ret; -} + if (opts & SOUP_SOCKET_NONBUFFERED) { + flags = 1; + setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, + &flags, sizeof (flags)); + } -static void -soup_socket_new_sync_cb (SoupSocket *socket, - SoupSocketNewStatus status, - gpointer data) -{ - SoupSocket **ret = data; - *ret = socket; + if (opts & SOUP_SOCKET_REUSEADDR) { + flags = 1; + setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, + &flags, sizeof (flags)); + } } -SoupSocket * -soup_socket_new_sync (SoupAddress *addr, guint port) +static gboolean +connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data) { - SoupSocket *ret = (SoupSocket *) 0xdeadbeef; + SoupSocket *sock = data; + int error = 0; + int len = sizeof (error); - soup_socket_new (addr, port, soup_socket_new_sync_cb, &ret); + /* Remove the watch now in case we don't return immediately */ + g_source_remove (sock->priv->watch); + sock->priv->watch = 0; - while (1) { - g_main_iteration (TRUE); - if (ret != (SoupSocket *) 0xdeadbeef) return ret; - } + if (condition & ~(G_IO_IN | G_IO_OUT)) + goto cant_connect; - return ret; -} + if (getsockopt (sock->priv->sockfd, SOL_SOCKET, SO_ERROR, + &error, &len) != 0) + goto cant_connect; + if (error) + goto cant_connect; -/** - * soup_socket_get_iochannel: - * @socket: #SoupSocket to get #GIOChannel from. - * - * Get the #GIOChannel for the #SoupSocket. - * - * For a client socket, the #GIOChannel represents the data stream. - * Use it like you would any other #GIOChannel. - * - * For a server socket however, the #GIOChannel represents incoming - * connections. If you can read from it, there's a connection - * waiting. - * - * There is one channel for every socket. This function refs the - * channel before returning it. You should unref the channel when - * you are done with it. However, you should not close the channel - - * this is done when you delete the socket. - * - * Returns: A #GIOChannel; %NULL on failure. - * - **/ -GIOChannel* -soup_socket_get_iochannel (SoupSocket *socket) -{ - g_return_val_if_fail (SOUP_IS_SOCKET (socket), NULL); + if (sock->priv->ssl) + soup_socket_start_ssl (sock); - if (socket->priv->iochannel == NULL) { - socket->priv->iochannel = g_io_channel_unix_new (socket->priv->sockfd); - g_io_channel_set_encoding (socket->priv->iochannel, NULL, NULL); - g_io_channel_set_buffered (socket->priv->iochannel, FALSE); - } - - g_io_channel_ref (socket->priv->iochannel); + g_signal_emit (sock, signals[CONNECT_RESULT], 0, SOUP_ERROR_OK); + return FALSE; - return socket->priv->iochannel; + cant_connect: + g_signal_emit (sock, signals[CONNECT_RESULT], 0, SOUP_ERROR_CANT_CONNECT); + return FALSE; } -/** - * soup_socket_get_address: - * @socket: #SoupSocket to get address of. - * - * Get the address of the socket. If the socket is connected to a - * remote machine (as a client or server), the address will be the - * address of that machine. If it is a listening socket, the address - * will be %NULL. - * - * Returns: #SoupAddress of socket; %NULL on failure. - **/ -SoupAddress * -soup_socket_get_address (const SoupSocket *socket) +static gboolean +idle_connect_result (gpointer user_data) { - g_return_val_if_fail (SOUP_IS_SOCKET (socket), NULL); - g_return_val_if_fail (socket->priv->addr != NULL, NULL); + SoupSocket *sock = user_data; + + sock->priv->watch = 0; - return g_object_ref (socket->priv->addr); + g_signal_emit (sock, signals[CONNECT_RESULT], 0, + sock->priv->sockfd != -1 ? SOUP_ERROR_OK : SOUP_ERROR_CANT_CONNECT); + return FALSE; } -/** - * soup_socket_get_port: - * @socket: #SoupSocket to get the port number of. - * - * Get the port number the socket is bound to. - * - * Returns: Port number of the socket. - **/ -guint -soup_socket_get_port (const SoupSocket *socket) +static void +got_address (SoupAddress *addr, SoupKnownErrorCode status, gpointer user_data) { - g_return_val_if_fail (SOUP_IS_SOCKET (socket), 0); + SoupSocket *sock = user_data; + + if (!SOUP_ERROR_IS_SUCCESSFUL (status)) { + g_signal_emit (sock, signals[CONNECT_RESULT], 0, status); + return; + } - return socket->priv->port; + soup_socket_connect (sock, addr); + /* soup_socket_connect re-reffed addr */ + g_object_unref (addr); } /** - * soup_socket_server_new: - * @local_addr: Local address to bind to. (soup_address_ipv4_any() to - * accept connections on any local IPv4 address) - * @local_port: Port number for the socket (%SOUP_SERVER_ANY_PORT if you - * don't care). - * - * Create and open a new #SoupSocket listening on the specified - * address and port. Use this sort of socket when your are a server - * and you know what the port number should be (or pass 0 if you don't - * care what the port is). + * soup_socket_connect: + * @sock: a #SoupSocket (which must not be connected or listening) + * @remote_addr: address to connect to * - * Returns: a new #SoupSocket, or %NULL if there was a failure. + * Starts connecting to the indicated remote address and port. The + * socket will emit %connect_result when it succeeds or fails (but + * not before returning from this function). **/ -SoupSocket * -soup_socket_server_new (SoupAddress *local_addr, guint local_port) +void +soup_socket_connect (SoupSocket *sock, SoupAddress *remote_addr) { - SoupSocket *s; struct sockaddr *sa = NULL; - struct soup_sockaddr_max bound_sa; - int sa_len; - const int on = 1; - int flags; - - g_return_val_if_fail (local_addr != NULL, NULL); - - /* Create an appropriate sockaddr */ - soup_address_make_sockaddr (local_addr, local_port, &sa, &sa_len); - - /* Create socket */ - s = g_object_new (SOUP_TYPE_SOCKET, NULL); - - if ((s->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0)) < 0) { - g_object_unref (s); - g_free (sa); - return NULL; + int len, status; + + g_return_if_fail (SOUP_IS_SOCKET (sock)); + g_return_if_fail (SOUP_IS_ADDRESS (remote_addr)); + g_return_if_fail (sock->priv->sockfd == -1); + + sock->priv->remote_addr = g_object_ref (remote_addr); + sa = soup_address_get_sockaddr (sock->priv->remote_addr, &len); + if (!sa) { + soup_address_resolve (sock->priv->remote_addr, + got_address, sock); + return; } - /* Set REUSEADDR so we can reuse the port */ - if (setsockopt (s->priv->sockfd, - SOL_SOCKET, - SO_REUSEADDR, - &on, - sizeof (on)) != 0) - g_warning("Can't set reuse on tcp socket\n"); - - /* Get the flags (should all be 0?) */ - flags = fcntl (s->priv->sockfd, F_GETFL, 0); - if (flags == -1) goto SETUP_ERROR; - - /* Make the socket non-blocking */ - if (fcntl (s->priv->sockfd, F_SETFL, flags | O_NONBLOCK) == -1) - goto SETUP_ERROR; + sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0); + if (sock->priv->sockfd < 0) + goto cant_connect; + soup_set_sockopts (sock->priv->sockfd, + SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_NONBUFFERED); - /* Bind */ - if (bind (s->priv->sockfd, sa, sa_len) != 0) - goto SETUP_ERROR; + /* Connect (non-blocking) */ + status = connect (sock->priv->sockfd, sa, len); g_free (sa); + sa = NULL; - sa_len = sizeof (bound_sa); - getsockname (s->priv->sockfd, (struct sockaddr *)&bound_sa, &sa_len); - s->priv->addr = - soup_address_new_from_sockaddr ((struct sockaddr *)&bound_sa, - &s->priv->port); - - /* Listen */ - if (listen (s->priv->sockfd, 10) != 0) goto SETUP_ERROR; - - return s; - - SETUP_ERROR: - g_object_unref (s); - g_free (sa); - return NULL; + if (status == 0) { + /* Connect already succeeded */ + sock->priv->watch = g_idle_add (idle_connect_result, sock); + return; + } + if (errno != EINPROGRESS) + goto cant_connect; + + soup_socket_get_iochannel (sock); + sock->priv->watch = g_io_add_watch (sock->priv->iochannel, + (G_IO_IN | G_IO_OUT | G_IO_PRI | + G_IO_ERR | G_IO_HUP | G_IO_NVAL), + connect_watch, sock); + return; + + cant_connect: + if (sa) + g_free (sa); + if (sock->priv->sockfd != -1) { + close (sock->priv->sockfd); + sock->priv->sockfd = -1; + } + sock->priv->watch = g_idle_add (idle_connect_result, sock); } - -#define SOUP_ANY_IO_CONDITION (G_IO_IN | G_IO_OUT | G_IO_PRI | \ - G_IO_ERR | G_IO_HUP | G_IO_NVAL) - -typedef struct { - int sockfd; - SoupAddress *addr; - guint port; - SoupSocketNewFn func; - gpointer data; - int flags; - guint connect_watch; -} SoupSocketState; - static gboolean -soup_socket_new_cb (GIOChannel *iochannel, - GIOCondition condition, - gpointer data) +listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data) { - SoupSocketState *state = (SoupSocketState*) data; - SoupSocket *s; - int error = 0; - int len = sizeof (int); - - /* Remove the watch now in case we don't return immediately */ - g_source_remove (state->connect_watch); - - if (condition & ~(G_IO_IN | G_IO_OUT)) goto ERROR; - - errno = 0; - if (getsockopt (state->sockfd, - SOL_SOCKET, - SO_ERROR, - &error, - &len) != 0) goto ERROR; - - if (error) goto ERROR; + SoupSocket *sock = data, *new; + struct soup_sockaddr_max sa; + int sa_len, sockfd; - if (fcntl (state->sockfd, F_SETFL, state->flags) != 0) - goto ERROR; + if (condition & (G_IO_HUP | G_IO_ERR)) { + g_source_remove (sock->priv->watch); + sock->priv->watch = 0; + return FALSE; + } - s = g_object_new (SOUP_TYPE_SOCKET, NULL); - s->priv->sockfd = state->sockfd; - s->priv->addr = state->addr; - s->priv->port = state->port; + sa_len = sizeof (sa); + sockfd = accept (sock->priv->sockfd, (struct sockaddr *)&sa, &sa_len); + if (sockfd == -1) + return TRUE; - (*state->func) (s, SOUP_SOCKET_NEW_STATUS_OK, state->data); + soup_set_sockopts (sockfd, + SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_NONBUFFERED); - g_free (state); + new = soup_socket_new (); + new->priv->sockfd = sockfd; + new->priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len); - return FALSE; + new->priv->server = TRUE; + if (sock->priv->ssl) { + new->priv->ssl = TRUE; + soup_socket_start_ssl (new); + } - ERROR: - g_object_unref (state->addr); - (*state->func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, state->data); - g_free (state); + g_signal_emit (sock, signals[NEW_CONNECTION], 0, new); + g_object_unref (new); - return FALSE; + return TRUE; } /** - * soup_socket_new: - * @addr: Address to connect to. - * @port: Port to connect to - * @func: Callback function. - * @data: User data passed when callback function is called. + * soup_socket_listen: + * @sock: a #SoupSocket (which must not be connected or listening) + * @local_addr: Local address to bind to. * - * Connect to a specifed address asynchronously. When the connection - * is complete or there is an error, it will call the callback. It - * may call the callback before the function returns. It will call - * the callback if there is a failure. + * Makes @sock start listening on the given interface and port. When + * connections come in, @sock will emit %new_connection. * - * Returns: ID of the connection which can be used with - * soup_socket_connect_cancel() to cancel it; %NULL on - * failure. + * Return value: whether or not @sock is now listening. **/ -SoupSocketNewId -soup_socket_new (SoupAddress *addr, - guint port, - SoupSocketNewFn func, - gpointer data) +gboolean +soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr) { - int sockfd; - int flags; - SoupSocketState *state; - GIOChannel *chan; struct sockaddr *sa; - int len; + int sa_len; - g_return_val_if_fail(addr != NULL, NULL); - g_return_val_if_fail(func != NULL, NULL); + g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE); + g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), FALSE); + g_return_val_if_fail (sock->priv->sockfd == -1, FALSE); - /* Create socket */ - soup_address_make_sockaddr (addr, port, &sa, &len); - sockfd = socket (sa->sa_family, SOCK_STREAM, 0); - if (sockfd < 0) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - g_free (sa); - return NULL; - } - - /* Get the flags (should all be 0?) */ - flags = fcntl (sockfd, F_GETFL, 0); - if (flags == -1) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - g_free (sa); - return NULL; - } + /* @local_addr may have its port set to 0. So we intentionally + * don't store it in sock->priv->local_addr, so that if the + * caller calls soup_socket_get_local_address() later, we'll + * have to make a new addr by calling getsockname(), which + * will have the right port number. + */ + sa = soup_address_get_sockaddr (local_addr, &sa_len); + g_return_val_if_fail (sa != NULL, FALSE); - if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - g_free (sa); - return NULL; - } + sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0); + if (sock->priv->sockfd < 0) + goto cant_listen; + soup_set_sockopts (sock->priv->sockfd, + SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_REUSEADDR); - errno = 0; + /* Bind */ + if (bind (sock->priv->sockfd, sa, sa_len) != 0) + goto cant_listen; - /* Connect (but non-blocking!) */ - if (connect (sockfd, sa, len) < 0 && errno != EINPROGRESS) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - g_free (sa); - return NULL; - } - g_free (sa); + /* Listen */ + if (listen (sock->priv->sockfd, 10) != 0) + goto cant_listen; - /* Unref in soup_socket_new_cb if failure */ - g_object_ref (addr); + sock->priv->server = TRUE; - /* Connect succeeded, return immediately */ - if (!errno) { - SoupSocket *s = g_object_new (SOUP_TYPE_SOCKET, NULL); - s->priv->sockfd = sockfd; - s->priv->addr = addr; + soup_socket_get_iochannel (sock); + sock->priv->watch = g_io_add_watch (sock->priv->iochannel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + listen_watch, sock); + return TRUE; - (*func) (s, SOUP_SOCKET_NEW_STATUS_OK, data); - return NULL; + cant_listen: + if (sock->priv->sockfd != -1) { + close (sock->priv->sockfd); + sock->priv->sockfd = -1; } - - chan = g_io_channel_unix_new (sockfd); - - /* Wait for the connection */ - state = g_new0 (SoupSocketState, 1); - state->sockfd = sockfd; - state->addr = addr; - state->port = port; - state->func = func; - state->data = data; - state->flags = flags; - state->connect_watch = g_io_add_watch (chan, - SOUP_ANY_IO_CONDITION, - soup_socket_new_cb, - state); - - g_io_channel_unref (chan); - - return state; + if (sa) + g_free (sa); + return FALSE; } /** - * soup_socket_new_cancel: - * @id: ID of the connection. + * soup_socket_start_ssl: + * @socket: the socket * - * Cancel an asynchronous connection that was started with - * soup_socket_new(). + * Starts using SSL on @socket. **/ void -soup_socket_new_cancel (SoupSocketNewId id) +soup_socket_start_ssl (SoupSocket *sock) { - SoupSocketState *state = (SoupSocketState*) id; + GIOChannel *chan; - g_source_remove (state->connect_watch); - g_object_unref (state->addr); - g_free (state); + chan = soup_socket_get_iochannel (sock); + sock->priv->iochannel = sock->priv->server ? + soup_ssl_get_server_iochannel (chan) : + soup_ssl_get_iochannel (chan); + sock->priv->ssl = TRUE; } + -static SoupSocket * -server_accept_internal (SoupSocket *socket, gboolean block) +/** + * soup_socket_client_new: + * @hostname: remote machine to connect to + * @port: remote port to connect to + * @ssl: whether or not to use SSL + * @callback: callback to call when the socket is connected + * @user_data: data for @callback + * + * Creates a connection to @uri. @callback will be called when the + * connection completes (or fails). + * + * Return value: the new socket (not yet ready for use). + **/ +SoupSocket * +soup_socket_client_new (const char *hostname, guint port, gboolean ssl, + SoupSocketCallback callback, gpointer user_data) { - int sockfd; - int flags; - struct soup_sockaddr_max sa; - socklen_t n; - fd_set fdset; - SoupSocket *s; + SoupSocket *sock; - g_return_val_if_fail (SOUP_IS_SOCKET (socket), NULL); + g_return_val_if_fail (hostname != NULL, NULL); - try_again: - FD_ZERO (&fdset); - FD_SET (socket->priv->sockfd, &fdset); + sock = soup_socket_new (); + sock->priv->ssl = ssl; + soup_socket_connect (sock, soup_address_new (hostname, port)); - if (select (socket->priv->sockfd + 1, &fdset, NULL, NULL, NULL) == -1) { - if (errno == EINTR) goto try_again; - return NULL; + if (callback) { + soup_signal_connect_once (sock, "connect_result", + G_CALLBACK (callback), user_data); } + return sock; +} - n = sizeof(sa); - - if ((sockfd = accept (socket->priv->sockfd, (struct sockaddr *)&sa, &n)) == -1) { - if (!block) - return NULL; - if (errno == EWOULDBLOCK || - errno == ECONNABORTED || -#ifdef EPROTO /* OpenBSD does not have EPROTO */ - errno == EPROTO || -#endif - errno == EINTR) - goto try_again; +/** + * soup_socket_server_new: + * @local_addr: Local address to bind to. (Use soup_address_any_new() to + * accept connections on any local address) + * @ssl: Whether or not this is an SSL server. + * @callback: Callback to call when a client connects + * @user_data: data to pass to @callback. + * + * Create and open a new #SoupSocket listening on the specified + * address. @callback will be called each time a client connects, + * with a new #SoupSocket. + * + * Returns: a new #SoupSocket, or NULL if there was a failure. + **/ +SoupSocket * +soup_socket_server_new (SoupAddress *local_addr, gboolean ssl, + SoupSocketListenerCallback callback, + gpointer user_data) +{ + SoupSocket *sock; - return NULL; - } + g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), NULL); - /* Get the flags (should all be 0?) */ - flags = fcntl (sockfd, F_GETFL, 0); - if (flags == -1) return NULL; + sock = soup_socket_new (); + sock->priv->ssl = ssl; - /* Make the socket non-blocking */ - if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) + if (!soup_socket_listen (sock, local_addr)) { + g_object_unref (sock); return NULL; + } - s = g_object_new (SOUP_TYPE_SOCKET, NULL); - s->priv->sockfd = sockfd; - s->priv->addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, - &s->priv->port); + if (callback) { + g_signal_connect (sock, "new_connection", + G_CALLBACK (callback), user_data); + } - return s; + return sock; } + /** - * soup_socket_server_accept: - * @socket: #SoupSocket to accept connections from. + * soup_socket_get_iochannel: + * @sock: #SoupSocket to get #GIOChannel from. + * + * Get the #GIOChannel for the #SoupSocket. * - * Accept a connection from the socket. The socket must have been - * created using soup_socket_server_new(). This function will - * block (use soup_socket_server_try_accept() if you don't - * want to block). If the socket's #GIOChannel is readable, it DOES - * NOT mean that this function will not block. + * If you ref the iochannel, it will remain valid after @sock is + * destroyed. * - * Returns: a new #SoupSocket if there is another connect, or %NULL if - * there's an error. + * Returns: A #GIOChannel; %NULL on failure. **/ -SoupSocket * -soup_socket_server_accept (SoupSocket *socket) +GIOChannel * +soup_socket_get_iochannel (SoupSocket *sock) { - return server_accept_internal (socket, TRUE); + g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); + + if (!sock->priv->iochannel) { + sock->priv->iochannel = + g_io_channel_unix_new (sock->priv->sockfd); + g_io_channel_set_close_on_unref (sock->priv->iochannel, TRUE); + } + return sock->priv->iochannel; } -/** - * soup_socket_server_try_accept: - * @socket: SoupSocket to accept connections from. - * - * Accept a connection from the socket without blocking. The socket - * must have been created using soup_socket_server_new(). This - * function is best used with the sockets #GIOChannel. If the - * channel is readable, then you PROBABLY have a connection. It is - * possible for the connection to close by the time you call this, so - * it may return %NULL even if the channel was readable. - * - * Returns a new SoupSocket if there is another connect, or %NULL - * otherwise. - **/ -SoupSocket * -soup_socket_server_try_accept (SoupSocket *socket) + +SoupAddress * +soup_socket_get_local_address (SoupSocket *sock) { - return server_accept_internal (socket, FALSE); + g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); + + if (!sock->priv->local_addr) { + struct soup_sockaddr_max bound_sa; + int sa_len; + + sa_len = sizeof (bound_sa); + getsockname (sock->priv->sockfd, (struct sockaddr *)&bound_sa, &sa_len); + sock->priv->local_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&bound_sa, sa_len); + } + + return sock->priv->local_addr; +} + +SoupAddress * +soup_socket_get_remote_address (SoupSocket *sock) +{ + g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); + + if (!sock->priv->local_addr) { + struct soup_sockaddr_max bound_sa; + int sa_len; + + sa_len = sizeof (bound_sa); + getpeername (sock->priv->sockfd, (struct sockaddr *)&bound_sa, &sa_len); + sock->priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&bound_sa, sa_len); + } + + return sock->priv->remote_addr; } diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h index c82cfc4..036d52f 100644 --- a/libsoup/soup-socket.h +++ b/libsoup/soup-socket.h @@ -8,6 +8,7 @@ #include #include +#include #define SOUP_TYPE_SOCKET (soup_socket_get_type ()) #define SOUP_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SOCKET, SoupSocket)) @@ -27,70 +28,44 @@ typedef struct { typedef struct { GObjectClass parent_class; + /* signals */ + void (*connect_result) (SoupSocket *, SoupKnownErrorCode); + + void (*new_connection) (SoupSocket *, SoupSocket *); } SoupSocketClass; GType soup_socket_get_type (void); +SoupSocket *soup_socket_new (void); -typedef gpointer SoupSocketConnectId; - -typedef enum { - SOUP_SOCKET_CONNECT_ERROR_NONE, - SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE, - SOUP_SOCKET_CONNECT_ERROR_NETWORK -} SoupSocketConnectStatus; - -typedef void (*SoupSocketConnectFn) (SoupSocket *socket, - SoupSocketConnectStatus status, - gpointer data); - -SoupSocketConnectId soup_socket_connect (const char *hostname, - guint port, - SoupSocketConnectFn func, - gpointer data); - -void soup_socket_connect_cancel (SoupSocketConnectId id); - -SoupSocket *soup_socket_connect_sync (const char *hostname, - guint port); - - -typedef gpointer SoupSocketNewId; - -typedef enum { - SOUP_SOCKET_NEW_STATUS_OK, - SOUP_SOCKET_NEW_STATUS_ERROR -} SoupSocketNewStatus; - -typedef void (*SoupSocketNewFn) (SoupSocket* socket, - SoupSocketNewStatus status, - gpointer data); - -SoupSocketNewId soup_socket_new (SoupAddress *addr, - guint port, - SoupSocketNewFn func, - gpointer data); - -void soup_socket_new_cancel (SoupSocketNewId id); - -SoupSocket *soup_socket_new_sync (SoupAddress *addr, - guint port); - - -GIOChannel *soup_socket_get_iochannel (SoupSocket* socket); - -SoupAddress *soup_socket_get_address (const SoupSocket* socket); +void soup_socket_connect (SoupSocket *sock, + SoupAddress *rem_addr); +gboolean soup_socket_listen (SoupSocket *sock, + SoupAddress *local_addr); +void soup_socket_start_ssl (SoupSocket *sock); -guint soup_socket_get_port (const SoupSocket* socket); +typedef void (*SoupSocketCallback) (SoupSocket *sock, + SoupKnownErrorCode status, + gpointer user_data); +typedef void (*SoupSocketListenerCallback) (SoupSocket *listener, + SoupSocket *sock, + gpointer user_data); -#define SOUP_SERVER_ANY_PORT 0 +SoupSocket *soup_socket_client_new (const char *hostname, + guint port, + gboolean ssl, + SoupSocketCallback callback, + gpointer user_data); +SoupSocket *soup_socket_server_new (SoupAddress *local_addr, + gboolean ssl, + SoupSocketListenerCallback, + gpointer user_data); -SoupSocket *soup_socket_server_new (SoupAddress *local_addr, - guint local_port); -SoupSocket *soup_socket_server_accept (SoupSocket *socket); +GIOChannel *soup_socket_get_iochannel (SoupSocket *sock); -SoupSocket *soup_socket_server_try_accept (SoupSocket *socket); +SoupAddress *soup_socket_get_local_address (SoupSocket *sock); +SoupAddress *soup_socket_get_remote_address (SoupSocket *sock); #endif /* SOUP_SOCKET_H */ diff --git a/libsoup/soup-socks.c b/libsoup/soup-socks.c index 19b9949..c461e0e 100644 --- a/libsoup/soup-socks.c +++ b/libsoup/soup-socks.c @@ -98,8 +98,7 @@ soup_socks_write (GIOChannel* iochannel, buf[len++] = 0x04; buf[len++] = 0x01; WSHORT (buf, &len, (gushort) dest_uri->port); - soup_address_make_sockaddr (sd->dest_addr, dest_uri->port, - &sa, &sa_len); + sa = soup_address_get_sockaddr (sd->dest_addr, &sa_len); memcpy (&buf [len], &((struct sockaddr_in *) sa)->sin_addr, 4); @@ -161,10 +160,7 @@ soup_socks_write (GIOChannel* iochannel, return !finished; CONNECT_ERROR: - (*sd->cb) (sd->dest_ctx, - SOUP_CONNECT_ERROR_NETWORK, - NULL, - sd->user_data); + (*sd->cb) (sd->dest_ctx, SOUP_ERROR_CANT_CONNECT, NULL, sd->user_data); socks_data_free (sd); return FALSE; } @@ -220,18 +216,12 @@ soup_socks_read (GIOChannel* iochannel, return TRUE; CONNECT_OK: - (*sd->cb) (sd->dest_ctx, - SOUP_CONNECT_ERROR_NONE, - sd->src_conn, - sd->user_data); + (*sd->cb) (sd->dest_ctx, SOUP_ERROR_OK, sd->src_conn, sd->user_data); socks_data_free (sd); return FALSE; CONNECT_ERROR: - (*sd->cb) (sd->dest_ctx, - SOUP_CONNECT_ERROR_NETWORK, - NULL, - sd->user_data); + (*sd->cb) (sd->dest_ctx, SOUP_ERROR_CANT_CONNECT, NULL, sd->user_data); socks_data_free (sd); return FALSE; } @@ -241,33 +231,25 @@ soup_socks_error (GIOChannel* iochannel, GIOCondition condition, SoupSocksData *sd) { - (*sd->cb) (sd->dest_ctx, - SOUP_CONNECT_ERROR_NETWORK, - NULL, - sd->user_data); - + (*sd->cb) (sd->dest_ctx, SOUP_ERROR_CANT_CONNECT, NULL, sd->user_data); socks_data_free (sd); return FALSE; } static void -soup_lookup_dest_addr_cb (SoupAddress* inetaddr, - SoupAddressStatus status, +soup_lookup_dest_addr_cb (SoupAddress *addr, + SoupKnownErrorCode status, gpointer data) { SoupSocksData *sd = data; GIOChannel *channel; - if (status != SOUP_ADDRESS_STATUS_OK) { - (*sd->cb) (sd->dest_ctx, - SOUP_CONNECT_ERROR_ADDR_RESOLVE, - NULL, - sd->user_data); + if (status != SOUP_ERROR_OK) { + (*sd->cb) (sd->dest_ctx, status, NULL, sd->user_data); g_free (sd); return; } - sd->dest_addr = inetaddr; sd->phase = SOCKS_4_SEND_DEST_ADDR; channel = soup_connection_get_iochannel (sd->src_conn); @@ -277,7 +259,6 @@ soup_lookup_dest_addr_cb (SoupAddress* inetaddr, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) soup_socks_error, sd); - g_io_channel_unref (channel); } void @@ -304,9 +285,11 @@ soup_connect_socks_proxy (SoupConnection *conn, proxy_uri = soup_context_get_uri (proxy_ctx); if (proxy_uri->protocol == SOUP_PROTOCOL_SOCKS4) { - soup_address_new (dest_uri->host, - soup_lookup_dest_addr_cb, - sd); + sd->dest_addr = soup_address_new (dest_uri->host, + dest_uri->port); + soup_address_resolve (sd->dest_addr, + soup_lookup_dest_addr_cb, + sd); sd->phase = SOCKS_4_DEST_ADDR_LOOKUP; } else if (proxy_uri->protocol == SOUP_PROTOCOL_SOCKS5) { channel = soup_connection_get_iochannel (conn); @@ -322,7 +305,6 @@ soup_connect_socks_proxy (SoupConnection *conn, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) soup_socks_error, sd); - g_io_channel_unref (channel); sd->phase = SOCKS_5_SEND_INIT; } else @@ -331,6 +313,6 @@ soup_connect_socks_proxy (SoupConnection *conn, return; CONNECT_SUCCESS: - (*cb) (dest_ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data); + (*cb) (dest_ctx, SOUP_ERROR_OK, conn, user_data); g_free (sd); } diff --git a/tests/simple-httpd.c b/tests/simple-httpd.c index 9f549e5..027ff82 100644 --- a/tests/simple-httpd.c +++ b/tests/simple-httpd.c @@ -119,8 +119,8 @@ main (int argc, char **argv) GMainLoop *loop; SoupServer *server, *ssl_server; int opt; - int port = SOUP_SERVER_ANY_PORT; - int ssl_port = SOUP_SERVER_ANY_PORT; + int port = SOUP_ADDRESS_ANY_PORT; + int ssl_port = SOUP_ADDRESS_ANY_PORT; g_type_init (); signal (SIGINT, quit); diff --git a/tests/simple-proxy.c b/tests/simple-proxy.c index 92a2967..255c6a6 100644 --- a/tests/simple-proxy.c +++ b/tests/simple-proxy.c @@ -50,7 +50,7 @@ main (int argc, char **argv) { GMainLoop *loop; int opt; - int port = SOUP_SERVER_ANY_PORT; + int port = SOUP_ADDRESS_ANY_PORT; SoupServer *server; g_type_init (); diff --git a/tests/timeserver.c b/tests/timeserver.c index 9ea8d9b..766c322 100644 --- a/tests/timeserver.c +++ b/tests/timeserver.c @@ -8,29 +8,60 @@ #include #include -int -main (int argc, char **argv) +static void +got_address (SoupAddress *addr, SoupKnownErrorCode status, gpointer user_data) { - SoupSocket *listener, *client; - SoupAddress *addr; - guint port; + SoupSocket *client = user_data; + const char *name, *phys; time_t now; char *timebuf; GIOChannel *chan; gsize wrote; + name = soup_address_get_name (addr); + phys = soup_address_get_physical (addr); + + printf ("got connection from %s (%s) port %d\n", + name ? name : "?", phys, + soup_address_get_port (addr)); + + now = time (NULL); + timebuf = ctime (&now); + + chan = soup_socket_get_iochannel (client); + g_io_channel_write (chan, timebuf, strlen (timebuf), &wrote); + g_io_channel_unref (chan); + + g_object_unref (client); +} + +static void +new_connection (SoupSocket *listener, SoupSocket *client, gpointer user_data) +{ + SoupAddress *addr; + + g_object_ref (client); + addr = soup_socket_get_remote_address (client); + soup_address_resolve (addr, got_address, client); +} + +int +main (int argc, char **argv) +{ + SoupSocket *listener; + SoupAddressFamily family; + SoupAddress *addr; + guint port; + GMainLoop *loop; + g_type_init (); if (argc >=2 && !strcmp (argv[1], "-6")) { - addr = soup_address_ipv6_any (); - if (!addr) { - fprintf (stderr, "No IPv6 support\n"); - exit (1); - } + family = SOUP_ADDRESS_FAMILY_IPV6; argc--; argv++; } else - addr = soup_address_ipv4_any (); + family = SOUP_ADDRESS_FAMILY_IPV4; if (argc > 2) { fprintf (stderr, "Usage: %s [-6] [port]\n", argv[0]); @@ -40,31 +71,27 @@ main (int argc, char **argv) if (argc == 2) port = atoi (argv[1]); else - port = SOUP_SERVER_ANY_PORT; - listener = soup_socket_server_new (addr, port); + port = SOUP_ADDRESS_ANY_PORT; + + addr = soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, port); + if (!addr) { + fprintf (stderr, "Could not create listener address\n"); + exit (1); + } + + listener = soup_socket_server_new (addr, port, + new_connection, NULL); g_object_unref (addr); if (!listener) { fprintf (stderr, "Could not create listening socket\n"); exit (1); } - printf ("Listening on port %d\n", soup_socket_get_port (listener)); - - while ((client = soup_socket_server_accept (listener))) { - addr = soup_socket_get_address (client); - printf ("got connection from %s port %d\n", - soup_address_get_name_sync (addr), - soup_socket_get_port (client)); - g_object_unref (addr); + printf ("Listening on port %d\n", + soup_address_get_port ( + soup_socket_get_local_address (listener))); - now = time (NULL); - timebuf = ctime (&now); - - chan = soup_socket_get_iochannel (client); - g_io_channel_write (chan, timebuf, strlen (timebuf), &wrote); - g_io_channel_unref (chan); - - g_object_unref (client); - } + loop = g_main_loop_new (NULL, TRUE); + g_main_loop_run (loop); g_object_unref (listener); return 0; -- 2.7.4