changeset 8612:219e9638e8f3

[gaim-migrate @ 9363] Make sending mDNS datagrams more object oriented. And allow for advertising PTR records. And some other changes. Just wanted to commit this to the public archives in case my computer it seized in a hostile buy-out by kopete. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Thu, 08 Apr 2004 01:23:49 +0000
parents 05bc76d8e1b7
children b0ceb2a9e122
files src/protocols/rendezvous/mdns.c src/protocols/rendezvous/mdns.h src/protocols/rendezvous/rendezvous.c
diffstat 3 files changed, 261 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/rendezvous/mdns.c	Thu Apr 08 00:54:05 2004 +0000
+++ b/src/protocols/rendezvous/mdns.c	Thu Apr 08 01:23:49 2004 +0000
@@ -25,7 +25,8 @@
 
 /*
  * If you want to understand this, read RFC1035 and 
- * draft-cheshire-dnsext-multicastdns.txt
+ * draft-cheshire-dnsext-multicastdns.txt, and buy
+ * me a doughnut.  thx k bye.
  */
 
 /*
@@ -40,6 +41,10 @@
 #include "mdns.h"
 #include "util.h"
 
+/******************************************/
+/* Functions for connection establishment */
+/******************************************/
+
 int
 mdns_establish_socket()
 {
@@ -103,67 +108,228 @@
 	return fd;
 }
 
+static int
+mdns_send_raw(int fd, unsigned int datalen, unsigned char *data)
+{
+	struct sockaddr_in addr;
+	int n;
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(5353);
+	addr.sin_addr.s_addr = inet_addr("224.0.0.251");
+	n = sendto(fd, data, datalen, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+
+	if (n == -1) {
+		gaim_debug_error("mdns", "Error sending packet: %d\n", errno);
+		return -1;
+	} else if (n != datalen) {
+		gaim_debug_error("mdns", "Only sent %d of %d bytes of data.\n", n, datalen);
+		return -1;
+	}
+
+	return 0;
+}
+
+/***************************************/
+/* Functions for sending mDNS messages */
+/***************************************/
+
+static int
+mdns_getlength_RR(const ResourceRecord *rr)
+{
+	int ret = 0;
+
+	ret += strlen(rr->name) + 2;
+	ret += 10;
+
+	switch (rr->type) {
+		case RENDEZVOUS_RRTYPE_PTR:
+			ret += strlen((const char *)rr->rdata) + 2;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+mdns_put_name(char *data, int datalen, int offset, const char *name)
+{
+	int i = 0;
+	char *b, *c;
+
+	b = (char *)name;
+	while ((c = strchr(b, '.'))) {
+		i += util_put8(&data[offset + i], c - b); /* Length of domain-name segment */
+		memcpy(&data[offset + i], b, c - b); /* Domain-name segment */
+		i += c - b; /* Increment the destination pointer */
+		b = c + 1;
+	}
+	i += util_put8(&data[offset + i], strlen(b)); /* Length of domain-name segment */
+	strcpy(&data[offset + i], b); /* Domain-name segment */
+	i += strlen(b) + 1; /* Increment the destination pointer */
+
+	return i;
+}
+
+static int
+mdns_put_RR(char *data, int datalen, int offset, const ResourceRecord *rr)
+{
+	int i = 0;
+
+	i += mdns_put_name(data, datalen, offset + i, rr->name);
+	i += util_put16(&data[offset + i], rr->type);
+	i += util_put16(&data[offset + i], rr->class);
+	i += util_put32(&data[offset + i], rr->ttl);
+	i += util_put16(&data[offset + i], rr->rdlength);
+
+	switch (rr->type) {
+		case RENDEZVOUS_RRTYPE_PTR:
+			i += mdns_put_name(data, datalen, offset + i, (const char *)rr->rdata);
+		break;
+	}
+
+	return i;
+}
+
+int
+mdns_send_dns(int fd, const DNSPacket *dns)
+{
+	int ret;
+	unsigned int datalen;
+	unsigned char *data;
+	int offset;
+	int i;
+
+	/* Calculate the length of the buffer we'll need to hold the DNS packet */
+	datalen = 0;
+
+	/* Header */
+	datalen += 12;
+
+	/* Questions */
+	for (i = 0; i < dns->header.numquestions; i++)
+		datalen += strlen(dns->questions[i].name) + 2 + 4;
+
+	/* Resource records */
+	for (i = 0; i < dns->header.numanswers; i++)
+		datalen += mdns_getlength_RR(&dns->answers[i]);
+	for (i = 0; i < dns->header.numauthority; i++)
+		datalen += mdns_getlength_RR(&dns->authority[i]);
+	for (i = 0; i < dns->header.numadditional; i++)
+		datalen += mdns_getlength_RR(&dns->additional[i]);
+
+	/* Allocate a buffer */
+	if (!(data = (unsigned char *)g_malloc(datalen))) {
+		return -ENOMEM;
+	}
+
+	/* Construct the datagram */
+	/* Header */
+	offset = 0;
+	offset += util_put16(&data[offset], dns->header.id); /* ID */
+	offset += util_put16(&data[offset], dns->header.flags);
+	offset += util_put16(&data[offset], dns->header.numquestions); /* QDCOUNT */
+	offset += util_put16(&data[offset], dns->header.numanswers); /* ANCOUNT */
+	offset += util_put16(&data[offset], dns->header.numauthority); /* NSCOUNT */
+	offset += util_put16(&data[offset], dns->header.numadditional); /* ARCOUNT */
+
+	/* Questions */
+	for (i = 0; i < dns->header.numquestions; i++) {
+		offset += mdns_put_name(data, datalen, offset, dns->questions[i].name); /* QNAME */
+		offset += util_put16(&data[offset], dns->questions[i].type); /* QTYPE */
+		offset += util_put16(&data[offset], dns->questions[i].class); /* QCLASS */
+	}
+
+	/* Resource records */
+	for (i = 0; i < dns->header.numanswers; i++)
+		offset += mdns_put_RR(data, datalen, offset, &dns->answers[i]);
+	for (i = 0; i < dns->header.numauthority; i++)
+		offset += mdns_put_RR(data, datalen, offset, &dns->authority[i]);
+	for (i = 0; i < dns->header.numadditional; i++)
+		offset += mdns_put_RR(data, datalen, offset, &dns->additional[i]);
+
+	/* Send the datagram */
+	ret = mdns_send_raw(fd, datalen, data);
+
+	g_free(data);
+
+	return ret;
+}
+
 int
 mdns_query(int fd, const char *domain)
 {
-	struct sockaddr_in addr;
-	unsigned int querylen;
-	unsigned char *query;
-	char *b, *c;
-	int i, n;
+	int ret;
+	DNSPacket *dns;
 
 	if (strlen(domain) > 255) {
 		return -EINVAL;
 	}
 
-	/*
-	 * Build the outgoing query packet.  It is made of the header with a
-	 * query made up of the given domain.  The header is 12 bytes.
-	 */
-	querylen = 12 + strlen(domain) + 2 + 4;
-	if (!(query = (unsigned char *)g_malloc(querylen))) {
-		return -ENOMEM;
+	dns = (DNSPacket *)g_malloc(sizeof(DNSPacket));
+	dns->header.id = 0x0000;
+	dns->header.flags = 0x0000;
+	dns->header.numquestions = 0x0001;
+	dns->header.numanswers = 0x0000;
+	dns->header.numauthority = 0x0000;
+	dns->header.numadditional = 0x0000;
+
+	dns->questions = (Question *)g_malloc(1 * sizeof(Question));
+	dns->questions[0].name = g_strdup(domain);
+	dns->questions[0].type = RENDEZVOUS_RRTYPE_PTR;
+	dns->questions[0].class = 0x8001;
+
+	dns->answers = NULL;
+	dns->authority = NULL;
+	dns->additional = NULL;
+
+	mdns_send_dns(fd, dns);
+
+	mdns_free(dns);
+
+	return ret;
+}
+
+int
+mdns_advertise_ptr(int fd, const char *name, const char *domain)
+{
+	int ret;
+	DNSPacket *dns;
+
+	if ((strlen(name) > 255) || (strlen(domain) > 255)) {
+		return -EINVAL;
 	}
 
-	/* The header section */
-	util_put32(&query[0], 0); /* The first 32 bits of the header are all 0's in mDNS */
-	util_put16(&query[4], 1); /* QDCOUNT */
-	util_put16(&query[6], 0); /* ANCOUNT */
-	util_put16(&query[8], 0); /* NSCOUNT */
-	util_put16(&query[10], 0); /* ARCOUNT */
+	dns = (DNSPacket *)g_malloc(sizeof(DNSPacket));
+	dns->header.id = 0x0000;
+	dns->header.flags = 0x8400;
+	dns->header.numquestions = 0x0000;
+	dns->header.numanswers = 0x0001;
+	dns->header.numauthority = 0x0000;
+	dns->header.numadditional = 0x0000;
+	dns->questions = NULL;
 
-	/* The question section */
-	i = 12; /* Destination in query */
-	b = (char *)domain;
-	while ((c = strchr(b, '.'))) {
-		i += util_put8(&query[i], c - b); /* Length of domain-name segment */
-		memcpy(&query[i], b, c - b); /* Domain-name segment */
-		i += c - b; /* Increment the destination pointer */
-		b = c + 1;
-	}
-	i += util_put8(&query[i], strlen(b)); /* Length of domain-name segment */
-	strcpy(&query[i], b); /* Domain-name segment */
-	i += strlen(b) + 1; /* Increment the destination pointer */
-	i += util_put16(&query[i], 0x000c); /* QTYPE */
-	i += util_put16(&query[i], 0x8001); /* QCLASS */
+	dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord));
+	dns->answers[0].name = g_strdup(name);
+	dns->answers[0].type = RENDEZVOUS_RRTYPE_PTR;
+	dns->answers[0].class = 0x0001;
+	dns->answers[0].ttl = 0x00001c20;
+	dns->answers[0].rdlength = strlen(domain) + 2;
+	dns->answers[0].rdata = (void *)g_strdup(domain);
+
+	dns->authority = NULL;
+	dns->additional = NULL;
 
-	/* Actually send the DNS query */
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(5353);
-	addr.sin_addr.s_addr = inet_addr("224.0.0.251");
-	n = sendto(fd, query, querylen, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
-	g_free(query);
+	mdns_send_dns(fd, dns);
+
+	mdns_free(dns);
 
-	if (n == -1) {
-		gaim_debug_error("mdns", "Error sending packet: %d\n", errno);
-		return -1;
-	} else if (n != querylen) {
-		gaim_debug_error("mdns", "Only sent %d of %d bytes of query.\n", n, querylen);
-		return -1;
-	}
+	return ret;
+}
 
-	return 0;
-}
+/***************************************/
+/* Functions for parsing mDNS messages */
+/***************************************/
 
 /*
  * XXX - Needs bounds checking!
@@ -444,7 +610,8 @@
 {
 	DNSPacket *ret = NULL;
 	int i; /* Current position in datagram */
-	/* char data[512]; */ /* XXX - Find out what to use as a maximum incoming UDP packet size */
+	/* XXX - Find out what to use as a maximum incoming UDP packet size */
+	/* char data[512]; */
 	char data[10096];
 	int datalen;
 	struct sockaddr_in addr;
@@ -527,6 +694,11 @@
 			case RENDEZVOUS_RRTYPE_TXT:
 				g_hash_table_destroy(rdata);
 			break;
+
+			case RENDEZVOUS_RRTYPE_SRV:
+				g_free(((ResourceRecordSRV *)rdata)->target);
+				g_free(rdata);
+			break;
 	}
 }
 
--- a/src/protocols/rendezvous/mdns.h	Thu Apr 08 00:54:05 2004 +0000
+++ b/src/protocols/rendezvous/mdns.h	Thu Apr 08 01:23:49 2004 +0000
@@ -120,18 +120,33 @@
  */
 int mdns_establish_socket();
 
+
+/**
+ * Sends a multicast DNS datagram.  Generally this is called
+ * by other convenience functions such as mdns_query(), however
+ * a client CAN construct its own DNSPacket if it wishes.
+ *
+ * @param fd The file descriptor of a pre-established socket to
+ *        be used for sending the outgoing mDNS datagram.
+ * @param dns The DNS datagram you wish to send.
+ * @return 0 on success, otherwise return the error number.
+ */
+int mdns_send_dns(int fd, const DNSPacket *dns);
+
 /**
  * Send a multicast DNS query for the given domain across the given
  * socket.
  *
  * @param fd The file descriptor of a pre-established socket to
- *        be used for sending the outgoing mDNS query.
+ *        be used for sending the outgoing mDNS datagram.
  * @param domain This is the domain name you wish to query.  It should 
  *        be of the format "_presence._tcp.local" for example.
  * @return 0 if sucessful.
  */
 int mdns_query(int fd, const char *domain);
 
+int mdns_advertise_ptr(int fd, const char *name, const char *domain);
+
 /**
  * Read a UDP packet from the given file descriptor and parse it
  * into a DNSPacket.
--- a/src/protocols/rendezvous/rendezvous.c	Thu Apr 08 00:54:05 2004 +0000
+++ b/src/protocols/rendezvous/rendezvous.c	Thu Apr 08 01:23:49 2004 +0000
@@ -39,6 +39,9 @@
 } RendezvousData;
 
 typedef struct _RendezvousBuddy {
+#if 0
+	guint ttltimer;
+#endif
 	gchar *firstandlast;
 	gchar *aim;
 	int p2pjport;
@@ -87,6 +90,7 @@
 /****************************/
 /* Buddy List Functions     */
 /****************************/
+
 static void rendezvous_addtolocal(GaimConnection *gc, const char *name, const char *domain)
 {
 	GaimAccount *account = gaim_connection_get_account(gc);
@@ -106,6 +110,19 @@
 	b = gaim_buddy_new(account, name, NULL);
 	gaim_blist_add_buddy(b, NULL, g, NULL);
 	serv_got_update(gc, b->name, 1, 0, 0, 0, 0);
+
+#if 0
+	RendezvousBuddy *rb;
+	rb = g_hash_table_lookup(rd->buddies, name);
+	if (rb == NULL) {
+		rb = g_malloc0(sizeof(RendezvousBuddy));
+		g_hash_table_insert(rd->buddies, g_strdup(name), rb);
+	}
+	rb->ttltimer = gaim_timeout_add(time * 10000, rendezvous_buddy_timeout, gc);
+
+	gaim_timeout_remove(rb->ttltimer);
+	rb->ttltimer = 0;
+#endif
 }
 
 static void rendezvous_removefromlocal(GaimConnection *gc, const char *name, const char *domain)
@@ -125,6 +142,11 @@
 	serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
 	gaim_blist_remove_buddy(b);
 	/* XXX - This results in incorrect group counts--needs to be fixed in the core */
+
+	/*
+	 * XXX - Instead of removing immediately, wait 10 seconds and THEN remove
+	 * them.  If you do it immediately you don't see the door close icon.
+	 */
 }
 
 static void rendezvous_removeallfromlocal(GaimConnection *gc)
@@ -209,7 +231,7 @@
 			/* Idle */
 			tmp2 = g_hash_table_lookup(rdata, "away");
 			rb->idle = atoi(tmp2);
-			gaim_debug_error("XXX", "User has been idle for %d\n", rb->idle);
+			gaim_debug_error("XXX", "User has been idle since %d\n", rb->idle);
 			rb->status = UC_IDLE;
 		} else if (!strcmp(tmp1, "avail")) {
 			/* Away */
@@ -276,7 +298,6 @@
 					rendezvous_addtolocal(gc, name, "Rendezvous");
 				else
 					rendezvous_removefromlocal(gc, name, "Rendezvous");
-
 				g_free(name);
 			}
 		} break;
@@ -407,6 +428,7 @@
 	gaim_connection_set_state(gc, GAIM_CONNECTED);
 
 	mdns_query(rd->fd, "_presence._tcp.local");
+	/* mdns_advertise_ptr(rd->fd, "_presence._tcp.local", "mark@diverge._presence._tcp.local"); */
 
 #if 0
 	text_record_add("txtvers", "1");