Mercurial > pidgin
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,