loudmouth/lm-socket.c
changeset 271 52ea4e0b897a
parent 257 d2df6b962601
child 272 00c0890d19c0
--- a/loudmouth/lm-socket.c	Wed Sep 05 15:06:09 2007 +0100
+++ b/loudmouth/lm-socket.c	Wed Sep 05 15:41:15 2007 +0100
@@ -37,6 +37,11 @@
 #include "lm-sock.h"
 #include "lm-error.h"
 
+#ifdef HAVE_ASYNCNS
+#include <asyncns.h>
+#define freeaddrinfo(x) asyncns_freeaddrinfo(x)
+#endif
+
 #define IN_BUFFER_SIZE 1024
 #define MIN_PORT 1
 #define MAX_PORT 65536
@@ -78,6 +83,13 @@
 	gpointer         user_data;
 
 	guint          ref_count;
+
+#ifdef HAVE_ASYNCNS
+	GSource		*watch_resolv;
+	asyncns_query_t *resolv_query;
+	asyncns_t	*asyncns_ctx;
+	GIOChannel	*resolv_channel;
+#endif
 }; 
 
 static void         socket_free               (LmSocket       *socket);
@@ -701,6 +713,206 @@
 	return FALSE;
 }
 
+static void
+_lm_socket_create_phase1 (LmSocket *socket, unsigned char *srv_ans, int len);
+static void
+_lm_socket_create_phase2 (LmSocket *socket, struct addrinfo *ans);
+
+#ifdef HAVE_ASYNCNS
+#define PHASE_1 0
+#define PHASE_2 1
+
+static gboolean
+_lm_socket_resolver_done (GSource *source,
+    			  GIOCondition condition,
+			  gpointer data);
+
+
+void _asyncns_cancel (LmSocket *socket)
+{
+	if (socket == NULL)
+		return;
+	if (socket->asyncns_ctx) {
+		if (socket->watch_resolv) {
+			g_source_destroy (socket->watch_resolv);
+			socket->watch_resolv = NULL;
+		}
+		if (socket->resolv_channel) {
+			g_io_channel_close (socket->resolv_channel);
+			socket->resolv_channel = NULL;
+		}
+		asyncns_cancel (socket->asyncns_ctx, socket->resolv_query);
+		asyncns_free (socket->asyncns_ctx);
+		socket->asyncns_ctx = NULL;
+	}
+}
+
+static gboolean
+_asyncns_prep (LmSocket *socket, GError **error)
+{
+	if (socket->asyncns_ctx) {
+		return TRUE;
+	}
+
+	socket->asyncns_ctx = asyncns_new (1);
+	if (socket->asyncns_ctx == NULL) {
+		g_set_error (error,
+				LM_ERROR,                 
+				LM_ERROR_CONNECTION_FAILED,   
+				"can't initialise libasyncns");
+		return FALSE;
+	}
+
+	socket->resolv_channel =
+		g_io_channel_unix_new (asyncns_fd (socket->asyncns_ctx));
+
+	socket->watch_resolv = 
+		lm_misc_add_io_watch (socket->context,
+				      socket->resolv_channel,
+				      G_IO_IN,
+				      (GIOFunc) _lm_socket_resolver_done,
+				      socket);
+
+	return TRUE;
+}
+
+static void
+_asyncns_done (LmSocket *socket)
+{
+	if (socket->resolv_channel == NULL)
+		return;
+
+	g_io_channel_unref (socket->resolv_channel);
+	socket->resolv_channel = NULL;
+	asyncns_free (socket->asyncns_ctx);
+	socket->asyncns_ctx = NULL;
+	socket->watch_resolv = NULL;
+	socket->resolv_query = NULL;
+}
+
+static gboolean
+_lm_socket_resolver_done (GSource *source,
+    			  GIOCondition condition,
+			  gpointer data)
+{
+	LmSocket	*socket = (LmSocket *) data;
+	struct addrinfo	*ans;
+	unsigned char   *srv_ans;
+	int 		 err;
+
+	/* process pending data */
+	asyncns_wait (socket->asyncns_ctx, FALSE);
+
+	if (!asyncns_isdone (socket->asyncns_ctx, socket->resolv_query)) {
+		return TRUE;
+	}
+
+	switch ((guint) asyncns_getuserdata (socket->asyncns_ctx, socket->resolv_query)) {
+	case PHASE_1:
+		err = asyncns_res_done (socket->asyncns_ctx, socket->resolv_query, &srv_ans);
+		_lm_socket_create_phase1 (socket, (err) ? NULL : srv_ans, err);
+		return TRUE;
+		break;
+	case PHASE_2:
+		err = asyncns_getaddrinfo_done (socket->asyncns_ctx, socket->resolv_query, &ans);
+		_lm_socket_create_phase2 (socket, (err) ? NULL : ans);
+		_asyncns_done (socket);
+		return FALSE;
+	}
+
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+#endif
+
+static void
+_lm_socket_create_phase1 (LmSocket *socket,
+			  unsigned char *srv_ans,
+			  int len)
+{
+	const char          *remote_addr;
+	LmConnectData       *data;
+	struct addrinfo      req;
+#ifndef HAVE_ASYNCNS
+	struct addrinfo *ans;
+	int              err;
+#endif
+
+	if (srv_ans != NULL) {
+		gchar    *new_server;
+		guint     new_port;
+		gboolean  result;
+		result = socket_parse_srv_response (srv_ans, len, 
+						    &new_server, 
+						    &new_port);
+		if (result == TRUE) {
+			g_free (socket->server);
+			socket->server = new_server;
+			if (!socket->port) socket->port = new_port;
+		}
+	}
+
+	if (socket->proxy) {
+		remote_addr = lm_proxy_get_server (socket->proxy);
+	} else {
+		remote_addr = socket->server;
+	}
+
+	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+	       "Going to connect to %s%s:%u\n", (socket->proxy) ? "proxy " : "",
+	       remote_addr, socket->port);
+
+	data = g_new0 (LmConnectData, 1);
+
+	data->socket        = socket;
+	data->connection    = socket->connection;
+	data->fd            = -1;
+	socket->connect_data = data;
+
+	memset (&req, 0, sizeof(req));
+	req.ai_family   = AF_UNSPEC;
+	req.ai_socktype = SOCK_STREAM;
+	req.ai_protocol = IPPROTO_TCP;
+
+#ifdef HAVE_ASYNCNS
+	if (!_asyncns_prep (socket, NULL))
+		return;
+
+	socket->resolv_query =
+	  	asyncns_getaddrinfo (socket->asyncns_ctx,
+		    		     remote_addr,
+				     NULL,
+				     &req);
+
+	asyncns_setuserdata (socket->asyncns_ctx,
+			     socket->resolv_query,
+			     (gpointer) PHASE_2);
+#else
+	err = getaddrinfo (remote_addr, NULL, &req, &ans);
+	_lm_socket_create_phase2 (socket, (err) ? NULL : ans);
+	if (err != 0)
+		return;
+#endif
+}
+
+static void
+_lm_socket_create_phase2 (LmSocket *socket, struct addrinfo *ans)
+{
+	if (ans == NULL) {
+		lm_verbose ("error while resolving, bailing out");
+		(socket->connect_func) (socket, FALSE, socket->user_data);
+		g_free (socket->connect_data);
+		socket->connect_data = NULL;
+		return;
+	}
+
+	socket->connect_data->resolved_addrs = ans;
+	socket->connect_data->current_addr   = ans;
+
+	socket_do_connect (socket->connect_data);
+}
+
 LmSocket *
 lm_socket_create (GMainContext      *context,
 		  IncomingDataFunc   data_func,
@@ -717,11 +929,11 @@
 		  GError           **error)
 {
 	LmSocket        *socket;
-	struct addrinfo  req;
-	struct addrinfo *ans;
-	const char      *remote_addr;
-	LmConnectData   *data;
+
+#ifndef HAVE_ASYNCNS
+	unsigned char    srv_ans[SRV_LEN];
 	int              err;
+#endif
 	
 	g_return_val_if_fail (server != NULL, NULL);
 	g_return_val_if_fail ((port >= MIN_PORT && port <= MAX_PORT), NULL);
@@ -731,12 +943,6 @@
 
 	socket = g_new0 (LmSocket, 1);
 
-	memset (&req, 0, sizeof(req));
-
-	req.ai_family   = AF_UNSPEC;
-	req.ai_socktype = SOCK_STREAM;
-	req.ai_protocol = IPPROTO_TCP;
-
 	socket->ref_count = 1;
 
 	socket->connection = connection;
@@ -752,61 +958,12 @@
 	socket->connect_func = connect_func;
 	socket->user_data = user_data;
 
-	if (use_srv) {
-		char          *srv;
-		unsigned char  srv_ans[SRV_LEN];
-
-		res_init ();
-
-
-		srv = g_strdup_printf ("_xmpp-client._tcp.%s", socket->server);
-		lm_verbose ("Performing a SRV lookup for %s\n", srv);
-
-		err = res_query (srv, C_IN, T_SRV, srv_ans, SRV_LEN);
-		if (err > 0) {
-			gchar    *new_server;
-			guint     new_port;
-			gboolean  result;
-			
-			result = socket_parse_srv_response (srv_ans, err, 
-							    &new_server, 
-							    &new_port);
-			if (result == TRUE) {
-				g_free (socket->server);
-				socket->server = new_server;
-				socket->port = new_port;
-			}
-		}
-		g_free (srv);
-	} else {
-		lm_verbose ("SRV lookup disabled for %s\n", socket->server);
-	}
-
 	if (context) {
 		socket->context = g_main_context_ref (context);
 	}
 
 	if (proxy) {
 		socket->proxy = lm_proxy_ref (proxy);
-		remote_addr = lm_proxy_get_server (socket->proxy);
-	} else {
-	    	remote_addr = socket->server;
-	}
-
-	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
-	       "Going to connect to %s%s:%u\n", (proxy) ? "proxy " : "",
-	       remote_addr, socket->port);
-
-	err = getaddrinfo (remote_addr, NULL, &req, &ans);
-	if (err != 0) {
-		const char *str;
-
-		str = _lm_sock_addrinfo_get_error_str (err);
-		g_set_error (error,
-			     LM_ERROR,                 
-			     LM_ERROR_CONNECTION_FAILED,   
-			     str);
-		return NULL;
 	}
 
 	if (ssl) {
@@ -814,24 +971,29 @@
 		_lm_ssl_initialize (socket->ssl);
 	}
 
-	/* Prepare and do the nonblocking connection */
-	data = g_new (LmConnectData, 1);
+
+	if (use_srv) {
+		char          *srv;
+		srv = g_strdup_printf ("_xmpp-client._tcp.%s", socket->server);
+		lm_verbose ("Performing a SRV lookup for %s\n", srv);
 
-	data->socket         = socket;
-	data->connection     = connection;
-	data->resolved_addrs = ans;
-	data->current_addr   = ans;
-	data->io_channel     = NULL;
-	data->fd             = -1;
+#ifdef HAVE_ASYNCNS
+		if (!_asyncns_prep (socket, error))
+			return NULL;
+		
+		socket->resolv_query =
+			asyncns_res_query (socket->asyncns_ctx, srv, C_IN, T_SRV);
+		asyncns_setuserdata (socket->asyncns_ctx, socket->resolv_query, (gpointer) PHASE_1);
+#else
+		res_init ();
 
-	socket->connect_data = data;
-
-	if (!socket_do_connect (data)) {
-		g_set_error (error,
-			LM_ERROR,
-			LM_ERROR_CONNECTION_FAILED,
-			"unable to connect");
-		return NULL;
+		err = res_query (srv, C_IN, T_SRV, srv_ans, SRV_LEN);
+		_lm_socket_create_phase1 (socket, (err) ? NULL : srv_ans, err);
+		g_free (srv);
+#endif
+	} else {
+		lm_verbose ("SRV lookup disabled for %s\n", socket->server);
+		_lm_socket_create_phase1 (socket, NULL, 0);
 	}
 
 	return socket;