changeset 8636:005c96dab551

[gaim-migrate @ 9388] I finished all my overflow checking and what not. If anyone else wants to look over it feel free... Null resource records (buddy icons) should be getting sent now, too. I'm not really sure why I'm not showing up in my iChat buddy list. Gaim rendezvous people show up in gaim rendezvous buddy lists, I think. Eh, I still have to deal with caching and handling queries. And then messaging. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Mon, 12 Apr 2004 01:17:10 +0000
parents 4aee5a47937d
children dc5f694e1cab
files src/protocols/rendezvous/mdns.c src/protocols/rendezvous/mdns.h src/protocols/rendezvous/rendezvous.c
diffstat 3 files changed, 306 insertions(+), 144 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/rendezvous/mdns.c	Mon Apr 12 01:11:45 2004 +0000
+++ b/src/protocols/rendezvous/mdns.c	Mon Apr 12 01:17:10 2004 +0000
@@ -30,9 +30,8 @@
  */
 
 /*
- * XXX - THIS DOESN'T DO BOUNDS CHECKING!!!  DON'T USE IT ON AN UNTRUSTED
- * NETWORK UNTIL IT DOES!!!  THERE ARE POSSIBLE REMOTE ACCESS VIA BUFFER
- * OVERFLOW SECURITY HOLES!!!
+ * XXX - This entire file could use another pair of eyes to audit for 
+ * any possible buffer overflow exploits.
  */
 
 #include "internal.h"
@@ -42,6 +41,76 @@
 #include "util.h"
 
 /******************************************/
+/* Functions for freeing a DNS structure  */
+/******************************************/
+
+/**
+ * Free the rdata associated with a given resource record.
+ */
+static void
+mdns_free_rr_rdata(unsigned short type, void *rdata)
+{
+	if (rdata == NULL)
+		return;
+
+	switch (type) {
+			case RENDEZVOUS_RRTYPE_NULL:
+			case RENDEZVOUS_RRTYPE_PTR:
+				g_free(rdata);
+			break;
+
+			case RENDEZVOUS_RRTYPE_TXT:
+				g_hash_table_destroy(rdata);
+			break;
+
+			case RENDEZVOUS_RRTYPE_SRV:
+				g_free(((ResourceRecordRDataSRV *)rdata)->target);
+				g_free(rdata);
+			break;
+	}
+}
+
+/**
+ * Free a given question
+ */
+static void
+mdns_free_q(Question *q)
+{
+	g_free(q->name);
+}
+
+/**
+ * Free a given resource record.
+ */
+static void
+mdns_free_rr(ResourceRecord *rr)
+{
+	g_free(rr->name);
+	mdns_free_rr_rdata(rr->type, rr->rdata);
+}
+
+void
+mdns_free(DNSPacket *dns)
+{
+	int i;
+
+	for (i = 0; i < dns->header.numquestions; i++)
+		mdns_free_q(&dns->questions[i]);
+	for (i = 0; i < dns->header.numanswers; i++)
+		mdns_free_rr(&dns->answers[i]);
+	for (i = 0; i < dns->header.numauthority; i++)
+		mdns_free_rr(&dns->authority[i]);
+	for (i = 0; i < dns->header.numadditional; i++)
+		mdns_free_rr(&dns->additional[i]);
+
+	g_free(dns->questions);
+	g_free(dns->answers);
+	g_free(dns->authority);
+	g_free(dns->additional);
+	g_free(dns);
+}
+
+/******************************************/
 /* Functions for connection establishment */
 /******************************************/
 
@@ -108,6 +177,14 @@
 	return fd;
 }
 
+/**
+ * Multicast raw data over a file descriptor.
+ *
+ * @param fd A file descriptor that is a socket bound to a UDP port.
+ * @param datalen The length of the data you wish to send.
+ * @param data The data you wish to send.
+ * @return 0 on success, otherwise return -1.
+ */
 static int
 mdns_send_raw(int fd, unsigned int datalen, unsigned char *data)
 {
@@ -173,8 +250,6 @@
 static int
 mdns_getlength_RR(ResourceRecord *rr)
 {
-	rr->rdlength = mdns_getlength_RR_rdata(rr->type, rr->rdata);
-
 	return mdns_getlength_name(rr->name) + 10 + rr->rdlength;
 }
 
@@ -210,6 +285,11 @@
 	i += util_put16(&data[offset + i], rr->rdlength);
 
 	switch (rr->type) {
+		case RENDEZVOUS_RRTYPE_NULL:
+			memcpy(&data[offset + i], rr->rdata, rr->rdlength);
+			i += rr->rdlength;
+		break;
+
 		case RENDEZVOUS_RRTYPE_PTR:
 			i += mdns_put_name(data, datalen, offset + i, (const char *)rr->rdata);
 		break;
@@ -315,7 +395,7 @@
 }
 
 int
-mdns_query(int fd, const char *domain)
+mdns_query(int fd, const char *domain, unsigned short type)
 {
 	int ret;
 	DNSPacket *dns;
@@ -334,8 +414,8 @@
 
 	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->questions[0].type = type;
+	dns->questions[0].class = 0x0001;
 
 	dns->answers = NULL;
 	dns->authority = NULL;
@@ -349,6 +429,46 @@
 }
 
 int
+mdns_advertise_null(int fd, const char *name, const char *rdata, unsigned short rdlength)
+{
+	int ret;
+	DNSPacket *dns;
+
+	if ((strlen(name) > 255)) {
+		return -EINVAL;
+	}
+
+	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;
+
+	dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord));
+	dns->answers[0].name = g_strdup(name);
+	dns->answers[0].type = RENDEZVOUS_RRTYPE_NULL;
+	dns->answers[0].class = 0x0001;
+	dns->answers[0].ttl = 0x00001c20;
+	dns->answers[0].rdlength = rdlength;
+	dns->answers[0].rdata = (void *)rdata;
+
+	dns->authority = NULL;
+	dns->additional = NULL;
+
+	mdns_send_dns(fd, dns);
+
+	/* The rdata should be freed by the caller of this function */
+	dns->answers[0].rdata = NULL;
+
+	mdns_free(dns);
+
+	return ret;
+}
+
+int
 mdns_advertise_ptr(int fd, const char *name, const char *domain)
 {
 	int ret;
@@ -372,8 +492,8 @@
 	dns->answers[0].type = RENDEZVOUS_RRTYPE_PTR;
 	dns->answers[0].class = 0x8001;
 	dns->answers[0].ttl = 0x00001c20;
-	dns->answers[0].rdlength = 0x0000; /* Set automatically */
 	dns->answers[0].rdata = (void *)g_strdup(domain);
+	dns->answers[0].rdlength = mdns_getlength_RR_rdata(dns->answers[0].type, dns->answers[0].rdata);
 
 	dns->authority = NULL;
 	dns->additional = NULL;
@@ -409,8 +529,8 @@
 	dns->answers[0].type = RENDEZVOUS_RRTYPE_TXT;
 	dns->answers[0].class = 0x8001;
 	dns->answers[0].ttl = 0x00001c20;
-	dns->answers[0].rdlength = 0x0000; /* Set automatically */
 	dns->answers[0].rdata = (void *)rdata;
+	dns->answers[0].rdlength = mdns_getlength_RR_rdata(dns->answers[0].type, dns->answers[0].rdata);
 
 	dns->authority = NULL;
 	dns->additional = NULL;
@@ -444,20 +564,20 @@
 	dns->header.id = 0x0000;
 	dns->header.flags = 0x8400;
 	dns->header.numquestions = 0x0000;
-	dns->header.numanswers = 0x0001;
-	dns->header.numauthority = 0x0000;
+	dns->header.numanswers = 0x0000;
+	dns->header.numauthority = 0x0001;
 	dns->header.numadditional = 0x0000;
 	dns->questions = NULL;
+	dns->answers = NULL;
 
-	dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord));
-	dns->answers[0].name = g_strdup(name);
-	dns->answers[0].type = RENDEZVOUS_RRTYPE_SRV;
-	dns->answers[0].class = 0x8001;
-	dns->answers[0].ttl = 0x00001c20;
-	dns->answers[0].rdlength = 0x0000; /* Set automatically */
-	dns->answers[0].rdata = rdata;
+	dns->authority = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord));
+	dns->authority[0].name = g_strdup(name);
+	dns->authority[0].type = RENDEZVOUS_RRTYPE_SRV;
+	dns->authority[0].class = 0x8001;
+	dns->authority[0].ttl = 0x00001c20;
+	dns->authority[0].rdata = rdata;
+	dns->authority[0].rdlength = mdns_getlength_RR_rdata(dns->authority[0].type, dns->authority[0].rdata);
 
-	dns->authority = NULL;
 	dns->additional = NULL;
 
 	mdns_send_dns(fd, dns);
@@ -508,11 +628,14 @@
 			newoffset = util_get8(&data[offset]);
 			if (newoffset >= offset)
 				/* Invalid pointer!  Could lead to infinite recursion! Bailing! */
-				g_string_free(ret, TRUE);
+				return g_string_free(ret, TRUE);
 			offset = newoffset;
 		}
 	}
 
+	if (offset > datalen)
+		return g_string_free(ret, TRUE);
+
 	return g_string_free(ret, FALSE);
 }
 
@@ -555,7 +678,7 @@
 }
 
 /*
- * XXX - Needs bounds checking!
+ *
  *
  */
 static Question *
@@ -568,10 +691,22 @@
 	for (i = 0; i < numquestions; i++) {
 		ret[i].name = mdns_read_name(data, datalen, *offset);
 		*offset += mdns_read_name_len(data, datalen, *offset);
+		if (*offset + 4 > datalen)
+			break;
 		ret[i].type = util_get16(&data[*offset]); /* QTYPE */
 		*offset += 2;
 		ret[i].class = util_get16(&data[*offset]); /* QCLASS */
 		*offset += 2;
+		if (*offset > datalen)
+			break;
+	}
+
+	/* Malformed packet check */
+	if (i < numquestions) {
+		for (i = 0; i < numquestions; i++)
+			g_free(ret[i].name);
+		g_free(ret);
+		return NULL;
 	}
 
 	return ret;
@@ -596,21 +731,17 @@
 }
 
 /*
- * XXX - Needs bounds checking!
+ * Read in a compressed name.
  *
  */
 static char *
 mdns_read_rr_rdata_ptr(const char *data, unsigned int datalen, unsigned int offset)
 {
-	char *ret = NULL;
-
-	ret = mdns_read_name(data, datalen, offset);
-
-	return ret;
+	return mdns_read_name(data, datalen, offset);
 }
 
 /*
- *
+ * Read in a list of name=value pairs into a GHashTable.
  *
  */
 static GHashTable *
@@ -626,7 +757,7 @@
 		tmp = util_get8(&data[offset]);
 		offset++;
 
-		/* Ensure packet is valid */
+		/* Malformed packet check */
 		if (offset + tmp > endoffset)
 			break;
 
@@ -655,6 +786,12 @@
 			g_free(key);
 	}
 
+	/* Malformed packet check */
+	if ((offset > datalen) || (offset != endoffset)) {
+		g_hash_table_destroy(ret);
+		return NULL;
+	}
+
 	return ret;
 }
 
@@ -668,6 +805,7 @@
 	ResourceRecordSRV *ret = NULL;
 	int endoffset = offset + rdlength;
 
+	/* Malformed packet check */
 	if (offset + 7 > endoffset)
 		return NULL;
 
@@ -691,12 +829,20 @@
 	 * but it was in the packet capture I looked at from iChat.
 	 */
 	ret->target = mdns_read_name(data, datalen, offset);
+	offset += mdns_read_name_len(data, datalen, offset);
+
+	/* Malformed packet check */
+	if ((offset > endoffset) || (ret->target == NULL)) {
+		g_free(ret->target);
+		g_free(ret);
+		return NULL;
+	}
 
 	return ret;
 }
 
 /*
- * XXX - Needs bounds checking!
+ *
  *
  */
 static ResourceRecord *
@@ -707,54 +853,81 @@
 
 	ret = (ResourceRecord *)g_malloc0(numrecords * sizeof(ResourceRecord));
 	for (i = 0; i < numrecords; i++) {
-		ret[i].name = mdns_read_name(data, datalen, *offset); /* NAME */
+		/* NAME */
+		ret[i].name = mdns_read_name(data, datalen, *offset);
 		*offset += mdns_read_name_len(data, datalen, *offset);
-		ret[i].type = util_get16(&data[*offset]); /* TYPE */
+
+		/* Malformed packet check */
+		if (*offset + 10 > datalen)
+			break;
+
+		/* TYPE */
+		ret[i].type = util_get16(&data[*offset]);
 		*offset += 2;
-		ret[i].class = util_get16(&data[*offset]); /* CLASS */
+
+		/* CLASS */
+		ret[i].class = util_get16(&data[*offset]);
 		*offset += 2;
-		ret[i].ttl = util_get32(&data[*offset]); /* TTL */
+
+		/* TTL */
+		ret[i].ttl = util_get32(&data[*offset]);
 		*offset += 4;
-		ret[i].rdlength = util_get16(&data[*offset]); /* RDLENGTH */
+
+		/* RDLENGTH */
+		ret[i].rdlength = util_get16(&data[*offset]);
 		*offset += 2;
 
 		/* RDATA */
-		switch (ret[i].type) {
-			case RENDEZVOUS_RRTYPE_NULL:
-				ret[i].rdata = mdns_read_rr_rdata_null(data, datalen, *offset, ret[i].rdlength);
-			break;
+		if (ret[i].type == RENDEZVOUS_RRTYPE_NULL) {
+			ret[i].rdata = mdns_read_rr_rdata_null(data, datalen, *offset, ret[i].rdlength);
+			if (ret[i].rdata == NULL)
+				break;
 
-			case RENDEZVOUS_RRTYPE_PTR:
-				ret[i].rdata = mdns_read_rr_rdata_ptr(data, datalen, *offset);
-			break;
+		} else if (ret[i].type == RENDEZVOUS_RRTYPE_PTR) {
+			ret[i].rdata = mdns_read_rr_rdata_ptr(data, datalen, *offset);
+			if (ret[i].rdata == NULL)
+				break;
+
+		} else if (ret[i].type == RENDEZVOUS_RRTYPE_TXT) {
+			ret[i].rdata = mdns_read_rr_rdata_txt(data, datalen, *offset, ret[i].rdlength);
+			if (ret[i].rdata == NULL)
+				break;
 
-			case RENDEZVOUS_RRTYPE_TXT:
-				ret[i].rdata = mdns_read_rr_rdata_txt(data, datalen, *offset, ret[i].rdlength);
-			break;
+		} else if (ret[i].type == RENDEZVOUS_RRTYPE_SRV) {
+			ret[i].rdata = mdns_read_rr_rdata_srv(data, datalen, *offset, ret[i].rdlength);
+			if (ret[i].rdata == NULL)
+				break;
 
-			case RENDEZVOUS_RRTYPE_SRV:
-				ret[i].rdata = mdns_read_rr_rdata_srv(data, datalen, *offset, ret[i].rdlength);
+		}
+
+		/* Malformed packet check */
+		*offset += ret[i].rdlength;
+		if (*offset > datalen)
 			break;
+	}
 
-			default:
-				ret[i].rdata = NULL;
-			break;
+	/* Malformed packet check */
+	if (i < numrecords) {
+		for (i = 0; i < numrecords; i++) {
+			g_free(ret[i].name);
+			mdns_free_rr_rdata(ret[i].type, ret[i].rdata);
 		}
-		*offset += ret[i].rdlength;
+		g_free(ret);
+		return NULL;
 	}
 
 	return ret;
 }
 
 /*
- * XXX - Needs bounds checking!
+ *
  *
  */
 DNSPacket *
 mdns_read(int fd)
 {
 	DNSPacket *ret = NULL;
-	int i; /* Current position in datagram */
+	int offset; /* Current position in datagram */
 	/* XXX - Find out what to use as a maximum incoming UDP packet size */
 	/* char data[512]; */
 	char data[10096];
@@ -772,15 +945,20 @@
 	ret = (DNSPacket *)g_malloc0(sizeof(DNSPacket));
 
 	/* Parse the incoming packet, starting from 0 */
-	i = 0;
+	offset = 0;
+
+	if (offset + 12 > datalen) {
+		g_free(ret);
+		return NULL;
+	}
 
 	/* The header section */
-	ret->header.id = util_get16(&data[i]); /* ID */
-	i += 2;
+	ret->header.id = util_get16(&data[offset]); /* ID */
+	offset += 2;
 
 	/* For the flags, some bits must be 0 and some must be 1, the rest are ignored */
-	ret->header.flags = util_get16(&data[i]); /* Flags (QR, OPCODE, AA, TC, RD, RA, Z, AD, CD, and RCODE */
-	i += 2;
+	ret->header.flags = util_get16(&data[offset]); /* Flags (QR, OPCODE, AA, TC, RD, RA, Z, AD, CD, and RCODE */
+	offset += 2;
 	if ((ret->header.flags & 0x8000) == 0) {
 		/* QR should be 1 */
 		g_free(ret);
@@ -793,97 +971,42 @@
 	}
 
 	/* Read in the number of other things in the packet */
-	ret->header.numquestions = util_get16(&data[i]);
-	i += 2;
-	ret->header.numanswers = util_get16(&data[i]);
-	i += 2;
-	ret->header.numauthority = util_get16(&data[i]);
-	i += 2;
-	ret->header.numadditional = util_get16(&data[i]);
-	i += 2;
+	ret->header.numquestions = util_get16(&data[offset]);
+	offset += 2;
+	ret->header.numanswers = util_get16(&data[offset]);
+	offset += 2;
+	ret->header.numauthority = util_get16(&data[offset]);
+	offset += 2;
+	ret->header.numadditional = util_get16(&data[offset]);
+	offset += 2;
 
 	/* Read in all the questions */
-	ret->questions = mdns_read_questions(ret->header.numquestions, data, datalen, &i);
+	ret->questions = mdns_read_questions(ret->header.numquestions, data, datalen, &offset);
 
 	/* Read in all resource records */
-	ret->answers = mdns_read_rr(ret->header.numanswers, data, datalen, &i);
+	ret->answers = mdns_read_rr(ret->header.numanswers, data, datalen, &offset);
 
 	/* Read in all authority records */
-	ret->authority = mdns_read_rr(ret->header.numauthority, data, datalen, &i);
+	ret->authority = mdns_read_rr(ret->header.numauthority, data, datalen, &offset);
 
 	/* Read in all additional records */
-	ret->additional = mdns_read_rr(ret->header.numadditional, data, datalen, &i);
+	ret->additional = mdns_read_rr(ret->header.numadditional, data, datalen, &offset);
+
+	/* Malformed packet check */
+	if ((ret->header.numquestions > 0 && ret->questions == NULL) ||
+		(ret->header.numanswers > 0 && ret->answers == NULL) ||
+		(ret->header.numauthority > 0 && ret->authority == NULL) ||
+		(ret->header.numadditional > 0 && ret->additional == NULL)) {
+		gaim_debug_error("mdns", "Received an invalid DNS packet.\n");
+		return NULL;
+	}
 
 	/* We should be at the end of the packet */
-	if (i != datalen) {
-		gaim_debug_error("mdns", "Finished parsing before end of DNS packet!  Only parsed %d of %d bytes.", i, datalen);
+	if (offset != datalen) {
+		gaim_debug_error("mdns", "Finished parsing before end of DNS packet!  Only parsed %d of %d bytes.", offset, datalen);
 		g_free(ret);
 		return NULL;
 	}
 
 	return ret;
 }
-
-/**
- * Free the rdata associated with a given resource record.
- */
-static void
-mdns_free_rr_rdata(unsigned short type, void *rdata)
-{
-	switch (type) {
-			case RENDEZVOUS_RRTYPE_NULL:
-			case RENDEZVOUS_RRTYPE_PTR:
-				g_free(rdata);
-			break;
-
-			case RENDEZVOUS_RRTYPE_TXT:
-				g_hash_table_destroy(rdata);
-			break;
-
-			case RENDEZVOUS_RRTYPE_SRV:
-				g_free(((ResourceRecordRDataSRV *)rdata)->target);
-				g_free(rdata);
-			break;
-	}
-}
-
-/**
- * Free a given question
- */
-static void
-mdns_free_q(Question *q)
-{
-	g_free(q->name);
-}
-
-/**
- * Free a given resource record.
- */
-static void
-mdns_free_rr(ResourceRecord *rr)
-{
-	g_free(rr->name);
-	if (rr->rdata != NULL)
-		mdns_free_rr_rdata(rr->type, rr->rdata);
-}
-
-void
-mdns_free(DNSPacket *dns)
-{
-	int i;
-
-	for (i = 0; i < dns->header.numquestions; i++)
-		mdns_free_q(&dns->questions[i]);
-	for (i = 0; i < dns->header.numanswers; i++)
-		mdns_free_rr(&dns->answers[i]);
-	for (i = 0; i < dns->header.numauthority; i++)
-		mdns_free_rr(&dns->authority[i]);
-	for (i = 0; i < dns->header.numadditional; i++)
-		mdns_free_rr(&dns->additional[i]);
-
-	g_free(dns->questions);
-	g_free(dns->answers);
-	g_free(dns->authority);
-	g_free(dns->additional);
-	g_free(dns);
-}
--- a/src/protocols/rendezvous/mdns.h	Mon Apr 12 01:11:45 2004 +0000
+++ b/src/protocols/rendezvous/mdns.h	Mon Apr 12 01:17:10 2004 +0000
@@ -61,6 +61,7 @@
 #define RENDEZVOUS_RRTYPE_PTR	12
 #define RENDEZVOUS_RRTYPE_TXT	16
 #define RENDEZVOUS_RRTYPE_SRV	33
+#define RENDEZVOUS_RRTYPE_ALL	255
 
 /*
  * Express for Men's
@@ -153,8 +154,9 @@
  *        be of the format "_presence._tcp.local" for example.
  * @return 0 if sucessful.
  */
-int mdns_query(int fd, const char *domain);
+int mdns_query(int fd, const char *domain, unsigned short type);
 
+int mdns_advertise_null(int fd, const char *name, const char *data, unsigned short rdlength);
 int mdns_advertise_ptr(int fd, const char *name, const char *domain);
 int mdns_advertise_txt(int fd, const char *name, const GSList *txt);
 int mdns_advertise_srv(int fd, const char *name, unsigned short port, const char *target);
--- a/src/protocols/rendezvous/rendezvous.c	Mon Apr 12 01:11:45 2004 +0000
+++ b/src/protocols/rendezvous/rendezvous.c	Mon Apr 12 01:17:10 2004 +0000
@@ -270,6 +270,7 @@
  */
 static void rendezvous_handle_rr(GaimConnection *gc, ResourceRecord *rr)
 {
+	RendezvousData *rd = gc->proto_data;
 	gchar *name;
 
 	gaim_debug_misc("rendezvous", "Parsing resource record with domain name %s\n", rr->name);
@@ -295,10 +296,14 @@
 		case RENDEZVOUS_RRTYPE_PTR: {
 			gchar *rdata = rr->rdata;
 			if ((name = rendezvous_extract_name(rdata)) != NULL) {
-				if (rr->ttl > 0)
+				if (rr->ttl > 0) {
+					/* Add them to our buddy list and request their icon */
 					rendezvous_addtolocal(gc, name, "Rendezvous");
-				else
+					mdns_query(rd->fd, rdata, RENDEZVOUS_RRTYPE_NULL);
+				} else {
+					/* Remove them from our buddy list */
 					rendezvous_removefromlocal(gc, name, "Rendezvous");
+				}
 				g_free(name);
 			}
 		} break;
@@ -415,12 +420,42 @@
 	rd->mytxtdata = g_slist_append(rd->mytxtdata, node);
 }
 
+static void rendezvous_send_icon(GaimConnection *gc)
+{
+	RendezvousData *rd = gc->proto_data;
+	GaimAccount *account = gaim_connection_get_account(gc);
+	const char *iconfile = gaim_account_get_buddy_icon(account);
+	struct stat st;
+	FILE *file;
+	unsigned char *rdata;
+	unsigned short rdlength;
+	gchar *myname;
+
+	if (iconfile == NULL)
+		return;
+
+	if (stat(iconfile, &st))
+		return;
+
+	if (!(file = fopen(iconfile, "rb")))
+		return;
+
+	rdlength = st.st_size;
+	rdata = g_malloc(rdlength);
+	fread(rdata, 1, rdlength, file);
+	fclose(file);
+
+	myname = g_strdup_printf("%s._presence._tcp.local", gaim_account_get_username(account));
+	mdns_advertise_null(rd->fd, myname, rdata, rdlength);
+	g_free(myname);
+}
+
 static void rendezvous_send_online(GaimConnection *gc)
 {
 	RendezvousData *rd = gc->proto_data;
 	GaimAccount *account = gaim_connection_get_account(gc);
-	const char *me;
-	char *myname, *mycomp;
+	const gchar *me;
+	gchar *myname, *mycomp;
 
 	me = gaim_account_get_username(account);
 	myname = g_strdup_printf("%s._presence._tcp.local", me);
@@ -452,6 +487,8 @@
 	rendezvous_add_to_txt(rd, "last", gaim_account_get_string(account, "last", _("User")));
 	mdns_advertise_txt(rd->fd, myname, rd->mytxtdata);
 
+	rendezvous_send_icon(gc);
+
 	g_free(myname);
 	g_free(mycomp);
 }
@@ -478,7 +515,7 @@
 	gc->inpa = gaim_input_add(rd->fd, GAIM_INPUT_READ, rendezvous_callback, gc);
 	gaim_connection_set_state(gc, GAIM_CONNECTED);
 
-	mdns_query(rd->fd, "_presence._tcp.local");
+	mdns_query(rd->fd, "_presence._tcp.local", RENDEZVOUS_RRTYPE_ALL);
 	rendezvous_send_online(gc);
 }
 
@@ -527,7 +564,7 @@
 
 static GaimPluginProtocolInfo prpl_info =
 {
-	OPT_PROTO_NO_PASSWORD,
+	OPT_PROTO_NO_PASSWORD | OPT_PROTO_BUDDY_ICON,
 	NULL,
 	NULL,
 	NULL,