Implement SRV lookups, patch from Senko Rasic
authorMikael Hallendal <micke@imendio.com>
Sat, 24 Feb 2007 16:02:34 +0100
changeset 243 fa0794ef3570
parent 242 b0b17385a143
child 244 8a05b79bad24
Implement SRV lookups, patch from Senko Rasic
loudmouth/lm-socket.c
--- a/loudmouth/lm-socket.c	Sat Feb 24 16:02:34 2007 +0100
+++ b/loudmouth/lm-socket.c	Sat Feb 24 16:02:34 2007 +0100
@@ -1,6 +1,8 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2006 Imendio AB
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2007 Collabora Ltd.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
@@ -21,6 +23,8 @@
 #include <config.h>
 
 #include <string.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
 
 #include "lm-debug.h"
 #include "lm-internals.h"
@@ -612,6 +616,59 @@
 	return TRUE;
 }
 
+static gboolean
+_parse_srv_response (unsigned char *srv, int srv_len, gchar **out_server, guint *out_port)
+{
+	int qdcount;
+	int ancount;
+	int len;
+	const unsigned char *pos = srv + sizeof(HEADER);
+	unsigned char *end = srv + srv_len;
+	HEADER *head = (HEADER *)srv;
+	char name[256];
+	char pref_name[256];
+	guint pref_port = 0;
+	guint pref_prio = 9999;
+
+	pref_name[0] = 0;
+
+	qdcount = ntohs (head->qdcount);
+	ancount = ntohs (head->ancount);
+
+	/* Ignore the questions */
+	while (qdcount-- > 0 && (len = dn_expand (srv, end, pos, name, 255)) >= 0) {
+		g_assert (len >= 0);
+		pos += len + QFIXEDSZ;
+	}
+
+	/* Parse the answers */
+	while (ancount-- > 0 && (len = dn_expand (srv, end, pos, name, 255)) >= 0) {
+		/* Ignore the initial string */
+		uint16_t pref, weight, port;
+		g_assert (len >= 0);
+		pos += len;
+		/* Ignore type, ttl, class and dlen */
+		pos += 10;
+		GETSHORT (pref, pos);
+		GETSHORT (weight, pos);
+		GETSHORT (port, pos);
+		len = dn_expand (srv, end, pos, name, 255);
+		if (pref < pref_prio) {
+			pref_prio = pref;
+			strcpy (pref_name, name);
+			pref_port = port;
+		}
+		pos += len;
+	}
+
+	if (pref_name[0]) {
+		*out_server = g_strdup (pref_name);
+		*out_port = pref_port;
+		return TRUE;
+	} 
+	return FALSE;
+}
+
 LmSocket *
 lm_socket_create (GMainContext      *context,
 		  IncomingDataFunc   func,
@@ -627,7 +684,12 @@
 	LmSocket        *socket;
 	struct addrinfo  req;
 	struct addrinfo *ans;
+	const char      *remote_addr;
 	LmConnectData   *data;
+	int              err;
+	char            *srv;
+#define SRV_LEN 8192
+	unsigned char    srv_ans[SRV_LEN];
 
 	g_return_val_if_fail (server != NULL, NULL);
 	g_return_val_if_fail ((port >= MIN_PORT && port <= MAX_PORT), NULL);
@@ -653,53 +715,39 @@
 	socket->func = func;
 	socket->user_data = user_data;
 
+	res_init ();
+	srv = g_strdup_printf ("_xmpp-client._tcp.%s", socket->server);
+	err = res_query (srv, C_IN, T_SRV, srv_ans, SRV_LEN);
+	if (err > 0) {
+		_parse_srv_response (srv_ans, err, &(socket->server), &(socket->port));
+	}
+	g_free (srv);
+
 	if (context) {
 		socket->context = g_main_context_ref (context);
 	}
 
 	if (proxy) {
-		int          err;
-		const gchar *proxy_server;
-
 		socket->proxy = lm_proxy_ref (proxy);
-
-		proxy_server = lm_proxy_get_server (socket->proxy);
+		remote_addr = lm_proxy_get_server (socket->proxy);
+	} else {
+	    	remote_addr = socket->server;
+	}
 
-		/* Connect through proxy */
-		g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
-		       "Going to connect to proxy %s\n", proxy_server);
-
-		err = getaddrinfo (proxy_server, NULL, &req, &ans);
-		if (err != 0) {
-			const char *str;
+	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+	       "Going to connect to %s%s:%u\n", (proxy) ? "proxy " : "",
+	       remote_addr, socket->port);
 
-			str = _lm_sock_addrinfo_get_error_str (err);
-			g_set_error (error,
-				     LM_ERROR,                 
-				     LM_ERROR_CONNECTION_FAILED,   
-				     str);
-			return NULL;
-		}
-	} else { 
-		int err;
+	err = getaddrinfo (remote_addr, NULL, &req, &ans);
+	if (err != 0) {
+		const char *str;
 
-		/* Connect directly */
-		g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
-		       "Going to connect to %s\n", 
-		       socket->server);
-
-		err = getaddrinfo (socket->server, 
-				   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;
-		}
+		str = _lm_sock_addrinfo_get_error_str (err);
+		g_set_error (error,
+			     LM_ERROR,                 
+			     LM_ERROR_CONNECTION_FAILED,   
+			     str);
+		return NULL;
 	}
 
 	if (ssl) {