Mercurial > pidgin.yaz
annotate src/protocols/rendezvous/mdns.c @ 8791:407355e05a0a
[gaim-migrate @ 9553]
These files haven't been touched in ages, but someone was only able to
compile Zephyr when <internal.h> was changed to "internal.h", so here we
go.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Sat, 24 Apr 2004 09:00:37 +0000 |
parents | dbbf5470ba05 |
children | 8212661dc3cc |
rev | line source |
---|---|
8487 | 1 /** |
2 * @file mdns.c Multicast DNS connection code used by rendezvous. | |
3 * | |
4 * gaim | |
5 * | |
6 * Gaim is the legal property of its developers, whose names are too numerous | |
7 * to list here. Please refer to the COPYRIGHT file distributed with this | |
8 * source distribution. | |
9 * | |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 * | |
24 */ | |
25 | |
26 /* | |
27 * If you want to understand this, read RFC1035 and | |
8612 | 28 * draft-cheshire-dnsext-multicastdns.txt, and buy |
29 * me a doughnut. thx k bye. | |
8487 | 30 */ |
31 | |
32 /* | |
8636 | 33 * XXX - This entire file could use another pair of eyes to audit for |
8738 | 34 * any possible buffer overflow exploits. It doesn't even HAVE to be |
35 * a pair, neither--one eye would suffice. Oh, snap, somebody call | |
36 * One Eyed Willie. | |
8487 | 37 */ |
38 | |
8546 | 39 #include "internal.h" |
8487 | 40 #include "debug.h" |
41 | |
42 #include "mdns.h" | |
8738 | 43 #include "mdns_cache.h" |
8487 | 44 #include "util.h" |
45 | |
8612 | 46 /******************************************/ |
8636 | 47 /* Functions for freeing a DNS structure */ |
48 /******************************************/ | |
49 | |
50 /** | |
51 * Free the rdata associated with a given resource record. | |
52 */ | |
53 static void | |
54 mdns_free_rr_rdata(unsigned short type, void *rdata) | |
55 { | |
56 if (rdata == NULL) | |
57 return; | |
58 | |
59 switch (type) { | |
60 case RENDEZVOUS_RRTYPE_NULL: | |
61 case RENDEZVOUS_RRTYPE_PTR: | |
62 g_free(rdata); | |
63 break; | |
64 | |
65 case RENDEZVOUS_RRTYPE_TXT: | |
66 g_hash_table_destroy(rdata); | |
67 break; | |
68 | |
69 case RENDEZVOUS_RRTYPE_SRV: | |
70 g_free(((ResourceRecordRDataSRV *)rdata)->target); | |
71 g_free(rdata); | |
72 break; | |
73 } | |
74 } | |
75 | |
76 /** | |
77 * Free a given question | |
78 */ | |
79 static void | |
80 mdns_free_q(Question *q) | |
81 { | |
82 g_free(q->name); | |
83 } | |
84 | |
85 /** | |
86 * Free a given resource record. | |
87 */ | |
8738 | 88 void |
8636 | 89 mdns_free_rr(ResourceRecord *rr) |
90 { | |
91 g_free(rr->name); | |
92 mdns_free_rr_rdata(rr->type, rr->rdata); | |
93 } | |
94 | |
95 void | |
96 mdns_free(DNSPacket *dns) | |
97 { | |
98 int i; | |
99 | |
100 for (i = 0; i < dns->header.numquestions; i++) | |
101 mdns_free_q(&dns->questions[i]); | |
102 for (i = 0; i < dns->header.numanswers; i++) | |
103 mdns_free_rr(&dns->answers[i]); | |
104 for (i = 0; i < dns->header.numauthority; i++) | |
105 mdns_free_rr(&dns->authority[i]); | |
106 for (i = 0; i < dns->header.numadditional; i++) | |
107 mdns_free_rr(&dns->additional[i]); | |
108 | |
109 g_free(dns->questions); | |
110 g_free(dns->answers); | |
111 g_free(dns->authority); | |
112 g_free(dns->additional); | |
113 g_free(dns); | |
114 } | |
115 | |
116 /******************************************/ | |
8612 | 117 /* Functions for connection establishment */ |
118 /******************************************/ | |
119 | |
8487 | 120 int |
121 mdns_establish_socket() | |
122 { | |
123 int fd = -1; | |
124 struct sockaddr_in addr; | |
125 struct ip_mreq mreq; | |
126 unsigned char loop; | |
127 unsigned char ttl; | |
128 int reuseaddr; | |
129 | |
130 gaim_debug_info("mdns", "Establishing multicast socket\n"); | |
131 | |
132 /* What's the difference between AF_INET and PF_INET? */ | |
133 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
134 gaim_debug_error("mdns", "Unable to create socket: %s\n", strerror(errno)); | |
135 return -1; | |
136 } | |
137 | |
138 /* Make the socket non-blocking (although it shouldn't matter) */ | |
139 fcntl(fd, F_SETFL, O_NONBLOCK); | |
140 | |
141 /* Bind the socket to a local IP and port */ | |
142 addr.sin_family = AF_INET; | |
143 addr.sin_port = htons(5353); | |
144 addr.sin_addr.s_addr = INADDR_ANY; | |
145 if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { | |
146 gaim_debug_error("mdns", "Unable to bind socket to interface.\n"); | |
147 close(fd); | |
148 return -1; | |
149 } | |
150 | |
8631 | 151 /* Ensure loopback is enabled (it should be enabled by default, but let's be sure) */ |
8487 | 152 loop = 1; |
153 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(unsigned char)) == -1) { | |
154 gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_LOOP\n"); | |
155 } | |
156 | |
157 /* Set TTL to 255--required by mDNS */ | |
158 ttl = 255; | |
159 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(unsigned char)) == -1) { | |
160 gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_TTL\n"); | |
161 close(fd); | |
162 return -1; | |
163 } | |
164 | |
165 /* Join the .local multicast group */ | |
166 mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); | |
167 mreq.imr_interface.s_addr = htonl(INADDR_ANY); | |
168 if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) { | |
169 gaim_debug_error("mdns", "Error calling setsockopt for IP_ADD_MEMBERSHIP\n"); | |
170 close(fd); | |
171 return -1; | |
172 } | |
173 | |
174 /* Make the local IP re-usable */ | |
175 reuseaddr = 1; | |
176 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1) { | |
177 gaim_debug_error("mdns", "Error calling setsockopt for SO_REUSEADDR: %s\n", strerror(errno)); | |
178 } | |
179 | |
180 return fd; | |
181 } | |
182 | |
8636 | 183 /** |
184 * Multicast raw data over a file descriptor. | |
185 * | |
186 * @param fd A file descriptor that is a socket bound to a UDP port. | |
187 * @param datalen The length of the data you wish to send. | |
188 * @param data The data you wish to send. | |
189 * @return 0 on success, otherwise return -1. | |
190 */ | |
8612 | 191 static int |
192 mdns_send_raw(int fd, unsigned int datalen, unsigned char *data) | |
193 { | |
194 struct sockaddr_in addr; | |
195 int n; | |
196 | |
197 addr.sin_family = AF_INET; | |
198 addr.sin_port = htons(5353); | |
199 addr.sin_addr.s_addr = inet_addr("224.0.0.251"); | |
200 n = sendto(fd, data, datalen, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); | |
201 | |
202 if (n == -1) { | |
203 gaim_debug_error("mdns", "Error sending packet: %d\n", errno); | |
204 return -1; | |
205 } else if (n != datalen) { | |
206 gaim_debug_error("mdns", "Only sent %d of %d bytes of data.\n", n, datalen); | |
207 return -1; | |
208 } | |
209 | |
210 return 0; | |
211 } | |
212 | |
213 /***************************************/ | |
214 /* Functions for sending mDNS messages */ | |
215 /***************************************/ | |
216 | |
217 static int | |
8631 | 218 mdns_getlength_name(const void *name) |
8629 | 219 { |
8631 | 220 return strlen((const char *)name) + 2; |
221 } | |
222 | |
223 static int | |
224 mdns_getlength_RR_rdata(unsigned short type, const void *rdata) | |
225 { | |
8629 | 226 int rdlength = 0; |
227 | |
8631 | 228 switch (type) { |
229 case RENDEZVOUS_RRTYPE_PTR: | |
230 rdlength = mdns_getlength_name(rdata); | |
231 break; | |
232 | |
233 case RENDEZVOUS_RRTYPE_TXT: { | |
234 GSList *cur; | |
235 ResourceRecordRDataTXTNode *node; | |
236 | |
237 for (cur = (GSList *)rdata; cur != NULL; cur = cur->next) { | |
238 node = (ResourceRecordRDataTXTNode *)cur->data; | |
239 rdlength += 1 + strlen(node->name); | |
240 if (node->value != NULL) | |
241 rdlength += 1 + strlen(node->value); | |
242 } | |
243 } break; | |
244 | |
245 case RENDEZVOUS_RRTYPE_SRV: | |
246 rdlength = 6 + mdns_getlength_name(((const ResourceRecordRDataSRV *)rdata)->target); | |
247 break; | |
8629 | 248 } |
249 | |
250 return rdlength; | |
251 } | |
252 | |
253 static int | |
8631 | 254 mdns_getlength_RR(ResourceRecord *rr) |
8612 | 255 { |
8634 | 256 return mdns_getlength_name(rr->name) + 10 + rr->rdlength; |
8612 | 257 } |
258 | |
259 static int | |
8634 | 260 mdns_put_name(char *data, unsigned int datalen, unsigned int offset, const char *name) |
8612 | 261 { |
262 int i = 0; | |
263 char *b, *c; | |
264 | |
265 b = (char *)name; | |
266 while ((c = strchr(b, '.'))) { | |
267 i += util_put8(&data[offset + i], c - b); /* Length of domain-name segment */ | |
268 memcpy(&data[offset + i], b, c - b); /* Domain-name segment */ | |
269 i += c - b; /* Increment the destination pointer */ | |
270 b = c + 1; | |
271 } | |
272 i += util_put8(&data[offset + i], strlen(b)); /* Length of domain-name segment */ | |
273 strcpy(&data[offset + i], b); /* Domain-name segment */ | |
274 i += strlen(b) + 1; /* Increment the destination pointer */ | |
275 | |
276 return i; | |
277 } | |
278 | |
279 static int | |
8634 | 280 mdns_put_RR(char *data, unsigned int datalen, unsigned int offset, const ResourceRecord *rr) |
8612 | 281 { |
282 int i = 0; | |
283 | |
284 i += mdns_put_name(data, datalen, offset + i, rr->name); | |
285 i += util_put16(&data[offset + i], rr->type); | |
286 i += util_put16(&data[offset + i], rr->class); | |
287 i += util_put32(&data[offset + i], rr->ttl); | |
8631 | 288 i += util_put16(&data[offset + i], rr->rdlength); |
8612 | 289 |
290 switch (rr->type) { | |
8636 | 291 case RENDEZVOUS_RRTYPE_NULL: |
292 memcpy(&data[offset + i], rr->rdata, rr->rdlength); | |
293 i += rr->rdlength; | |
294 break; | |
295 | |
8612 | 296 case RENDEZVOUS_RRTYPE_PTR: |
297 i += mdns_put_name(data, datalen, offset + i, (const char *)rr->rdata); | |
298 break; | |
8629 | 299 |
300 case RENDEZVOUS_RRTYPE_TXT: { | |
301 GSList *cur; | |
8631 | 302 ResourceRecordRDataTXTNode *node; |
8629 | 303 int mylength; |
304 | |
305 for (cur = (GSList *)rr->rdata; cur != NULL; cur = cur->next) { | |
8631 | 306 node = (ResourceRecordRDataTXTNode *)cur->data; |
8629 | 307 mylength = 1 + strlen(node->name); |
308 if (node->value) | |
309 mylength += 1 + strlen(node->value); | |
310 i += util_put8(&data[offset + i], mylength - 1); | |
311 memcpy(&data[offset + i], node->name, strlen(node->name)); | |
312 i += strlen(node->name); | |
313 if (node->value) { | |
314 data[offset + i] = '='; | |
315 i++; | |
316 memcpy(&data[offset + i], node->value, strlen(node->value)); | |
317 i += strlen(node->value); | |
318 } | |
319 } | |
320 } break; | |
8631 | 321 |
322 case RENDEZVOUS_RRTYPE_SRV: { | |
323 ResourceRecordRDataSRV *srv = rr->rdata; | |
324 i += util_put16(&data[offset + i], 0); | |
325 i += util_put16(&data[offset + i], 0); | |
326 i += util_put16(&data[offset + i], srv->port); | |
327 i += mdns_put_name(data, datalen, offset + i, srv->target); | |
328 } break; | |
8612 | 329 } |
330 | |
331 return i; | |
332 } | |
333 | |
334 int | |
335 mdns_send_dns(int fd, const DNSPacket *dns) | |
336 { | |
337 int ret; | |
338 unsigned int datalen; | |
339 unsigned char *data; | |
8634 | 340 unsigned int offset; |
8612 | 341 int i; |
342 | |
343 /* Calculate the length of the buffer we'll need to hold the DNS packet */ | |
344 datalen = 0; | |
345 | |
346 /* Header */ | |
347 datalen += 12; | |
348 | |
349 /* Questions */ | |
350 for (i = 0; i < dns->header.numquestions; i++) | |
8631 | 351 datalen += mdns_getlength_name(dns->questions[i].name) + 4; |
8612 | 352 |
353 /* Resource records */ | |
354 for (i = 0; i < dns->header.numanswers; i++) | |
355 datalen += mdns_getlength_RR(&dns->answers[i]); | |
356 for (i = 0; i < dns->header.numauthority; i++) | |
357 datalen += mdns_getlength_RR(&dns->authority[i]); | |
358 for (i = 0; i < dns->header.numadditional; i++) | |
359 datalen += mdns_getlength_RR(&dns->additional[i]); | |
360 | |
361 /* Allocate a buffer */ | |
362 if (!(data = (unsigned char *)g_malloc(datalen))) { | |
363 return -ENOMEM; | |
364 } | |
365 | |
366 /* Construct the datagram */ | |
367 /* Header */ | |
368 offset = 0; | |
369 offset += util_put16(&data[offset], dns->header.id); /* ID */ | |
370 offset += util_put16(&data[offset], dns->header.flags); | |
371 offset += util_put16(&data[offset], dns->header.numquestions); /* QDCOUNT */ | |
372 offset += util_put16(&data[offset], dns->header.numanswers); /* ANCOUNT */ | |
373 offset += util_put16(&data[offset], dns->header.numauthority); /* NSCOUNT */ | |
374 offset += util_put16(&data[offset], dns->header.numadditional); /* ARCOUNT */ | |
375 | |
376 /* Questions */ | |
377 for (i = 0; i < dns->header.numquestions; i++) { | |
378 offset += mdns_put_name(data, datalen, offset, dns->questions[i].name); /* QNAME */ | |
379 offset += util_put16(&data[offset], dns->questions[i].type); /* QTYPE */ | |
380 offset += util_put16(&data[offset], dns->questions[i].class); /* QCLASS */ | |
381 } | |
382 | |
383 /* Resource records */ | |
384 for (i = 0; i < dns->header.numanswers; i++) | |
385 offset += mdns_put_RR(data, datalen, offset, &dns->answers[i]); | |
386 for (i = 0; i < dns->header.numauthority; i++) | |
387 offset += mdns_put_RR(data, datalen, offset, &dns->authority[i]); | |
388 for (i = 0; i < dns->header.numadditional; i++) | |
389 offset += mdns_put_RR(data, datalen, offset, &dns->additional[i]); | |
390 | |
391 /* Send the datagram */ | |
8634 | 392 /* Offset can be shorter than datalen because of name compression */ |
393 ret = mdns_send_raw(fd, offset, data); | |
8612 | 394 |
395 g_free(data); | |
396 | |
397 return ret; | |
398 } | |
399 | |
8487 | 400 int |
8636 | 401 mdns_query(int fd, const char *domain, unsigned short type) |
8487 | 402 { |
8612 | 403 int ret; |
404 DNSPacket *dns; | |
8487 | 405 |
406 if (strlen(domain) > 255) { | |
407 return -EINVAL; | |
408 } | |
409 | |
8612 | 410 dns = (DNSPacket *)g_malloc(sizeof(DNSPacket)); |
411 dns->header.id = 0x0000; | |
412 dns->header.flags = 0x0000; | |
413 dns->header.numquestions = 0x0001; | |
414 dns->header.numanswers = 0x0000; | |
415 dns->header.numauthority = 0x0000; | |
416 dns->header.numadditional = 0x0000; | |
417 | |
418 dns->questions = (Question *)g_malloc(1 * sizeof(Question)); | |
419 dns->questions[0].name = g_strdup(domain); | |
8636 | 420 dns->questions[0].type = type; |
421 dns->questions[0].class = 0x0001; | |
8612 | 422 |
423 dns->answers = NULL; | |
424 dns->authority = NULL; | |
425 dns->additional = NULL; | |
426 | |
427 mdns_send_dns(fd, dns); | |
428 | |
429 mdns_free(dns); | |
430 | |
431 return ret; | |
432 } | |
433 | |
434 int | |
8738 | 435 mdns_send_rr(int fd, ResourceRecord *rr) |
8636 | 436 { |
437 int ret; | |
438 DNSPacket *dns; | |
439 | |
8612 | 440 dns = (DNSPacket *)g_malloc(sizeof(DNSPacket)); |
441 dns->header.id = 0x0000; | |
442 dns->header.flags = 0x8400; | |
443 dns->header.numquestions = 0x0000; | |
444 dns->header.numanswers = 0x0001; | |
445 dns->header.numauthority = 0x0000; | |
446 dns->header.numadditional = 0x0000; | |
447 dns->questions = NULL; | |
8738 | 448 dns->answers = rr; |
8612 | 449 dns->authority = NULL; |
450 dns->additional = NULL; | |
8487 | 451 |
8612 | 452 mdns_send_dns(fd, dns); |
453 | |
8738 | 454 /* The rr should be freed by the caller of this function */ |
455 dns->header.numanswers = 0x0000; | |
456 dns->answers = NULL; | |
457 | |
8612 | 458 mdns_free(dns); |
8487 | 459 |
8612 | 460 return ret; |
461 } | |
8487 | 462 |
8629 | 463 int |
8738 | 464 mdns_advertise_null(int fd, const char *name, const char *rdata, unsigned short rdlength) |
465 { | |
466 int ret; | |
467 ResourceRecord *rr; | |
468 | |
469 if ((strlen(name) > 255)) { | |
470 return -EINVAL; | |
471 } | |
472 | |
473 rr = (ResourceRecord *)g_malloc(sizeof(ResourceRecord)); | |
474 rr->name = g_strdup(name); | |
475 rr->type = RENDEZVOUS_RRTYPE_NULL; | |
476 rr->class = 0x0001; | |
477 rr->ttl = 0x00001c20; | |
478 rr->rdlength = rdlength; | |
479 rr->rdata = (void *)rdata; | |
480 | |
481 mdns_send_rr(fd, rr); | |
482 | |
483 /* The rdata should be freed by the caller of this function */ | |
484 rr->rdata = NULL; | |
485 | |
486 mdns_free_rr(rr); | |
487 | |
488 return ret; | |
489 } | |
490 | |
491 int | |
492 mdns_advertise_ptr(int fd, const char *name, const char *domain) | |
493 { | |
494 int ret; | |
495 ResourceRecord *rr; | |
496 | |
497 if ((strlen(name) > 255) || (strlen(domain) > 255)) { | |
498 return -EINVAL; | |
499 } | |
500 | |
501 rr = (ResourceRecord *)g_malloc(sizeof(ResourceRecord)); | |
502 rr->name = g_strdup(name); | |
503 rr->type = RENDEZVOUS_RRTYPE_PTR; | |
504 rr->class = 0x8001; | |
505 rr->ttl = 0x00001c20; | |
506 rr->rdata = (void *)g_strdup(domain); | |
507 rr->rdlength = mdns_getlength_RR_rdata(rr->type, rr->rdata); | |
508 | |
509 mdns_send_rr(fd, rr); | |
510 | |
511 mdns_free_rr(rr); | |
512 | |
513 return ret; | |
514 } | |
515 | |
516 int | |
8629 | 517 mdns_advertise_txt(int fd, const char *name, const GSList *rdata) |
518 { | |
519 int ret; | |
8738 | 520 ResourceRecord *rr; |
8629 | 521 |
522 if ((strlen(name) > 255)) { | |
523 return -EINVAL; | |
524 } | |
525 | |
8738 | 526 rr = (ResourceRecord *)g_malloc(sizeof(ResourceRecord)); |
527 rr->name = g_strdup(name); | |
528 rr->type = RENDEZVOUS_RRTYPE_TXT; | |
529 rr->class = 0x8001; | |
530 rr->ttl = 0x00001c20; | |
531 rr->rdata = (void *)rdata; | |
532 rr->rdlength = mdns_getlength_RR_rdata(rr->type, rr->rdata); | |
8629 | 533 |
8738 | 534 mdns_send_rr(fd, rr); |
8629 | 535 |
8631 | 536 /* The rdata should be freed by the caller of this function */ |
8738 | 537 rr->rdata = NULL; |
8631 | 538 |
8738 | 539 mdns_free_rr(rr); |
8631 | 540 |
541 return ret; | |
542 } | |
543 | |
544 int | |
545 mdns_advertise_srv(int fd, const char *name, unsigned short port, const char *target) | |
546 { | |
547 int ret; | |
8738 | 548 ResourceRecord *rr; |
8631 | 549 ResourceRecordRDataSRV *rdata; |
550 | |
551 if ((strlen(target) > 255)) { | |
552 return -EINVAL; | |
553 } | |
554 | |
555 rdata = g_malloc(sizeof(ResourceRecordRDataSRV)); | |
556 rdata->port = port; | |
8634 | 557 rdata->target = g_strdup(target); |
8631 | 558 |
8738 | 559 rr = (ResourceRecord *)g_malloc(sizeof(ResourceRecord)); |
560 rr->name = g_strdup(name); | |
561 rr->type = RENDEZVOUS_RRTYPE_SRV; | |
562 rr->class = 0x8001; | |
563 rr->ttl = 0x00001c20; | |
564 rr->rdata = rdata; | |
565 rr->rdlength = mdns_getlength_RR_rdata(rr->type, rr->rdata); | |
8631 | 566 |
8738 | 567 mdns_send_rr(fd, rr); |
8631 | 568 |
8738 | 569 mdns_free_rr(rr); |
8629 | 570 |
571 return ret; | |
572 } | |
573 | |
8612 | 574 /***************************************/ |
575 /* Functions for parsing mDNS messages */ | |
576 /***************************************/ | |
8487 | 577 |
578 /* | |
579 * Read in a domain name from the given buffer starting at the given | |
580 * offset. This handles using domain name compression to jump around | |
581 * the data buffer, if needed. | |
582 * | |
583 * @return A null-terminated string representation of the domain name. | |
584 * This should be g_free'd when no longer needed. | |
585 */ | |
586 static gchar * | |
8634 | 587 mdns_read_name(const char *data, int datalen, unsigned int offset) |
8487 | 588 { |
589 GString *ret = g_string_new(""); | |
8634 | 590 unsigned char tmp, newoffset; |
8487 | 591 |
8634 | 592 while ((offset <= datalen) && ((tmp = util_get8(&data[offset])) != 0)) { |
593 offset++; | |
8487 | 594 |
595 if ((tmp & 0xc0) == 0) { /* First two bits are 00 */ | |
8634 | 596 if (offset + tmp > datalen) |
597 /* Attempt to read past end of data! Bailing! */ | |
598 return g_string_free(ret, TRUE); | |
599 if (*ret->str != '\0') | |
8487 | 600 g_string_append_c(ret, '.'); |
8634 | 601 g_string_append_len(ret, &data[offset], tmp); |
602 offset += tmp; | |
8487 | 603 |
604 } else if ((tmp & 0x40) == 0) { /* First two bits are 10 */ | |
605 /* Reserved for future use */ | |
606 | |
607 } else if ((tmp & 0x80) == 1) { /* First two bits are 01 */ | |
608 /* Reserved for future use */ | |
609 | |
610 } else { /* First two bits are 11 */ | |
611 /* Jump to another position in the data */ | |
8634 | 612 newoffset = util_get8(&data[offset]); |
613 if (newoffset >= offset) | |
614 /* Invalid pointer! Could lead to infinite recursion! Bailing! */ | |
8636 | 615 return g_string_free(ret, TRUE); |
8634 | 616 offset = newoffset; |
8487 | 617 } |
618 } | |
619 | |
8636 | 620 if (offset > datalen) |
621 return g_string_free(ret, TRUE); | |
622 | |
8487 | 623 return g_string_free(ret, FALSE); |
624 } | |
625 | |
626 /* | |
627 * Determine how many bytes long a portion of the domain name is | |
628 * at the given offset. This does NOT jump around the data array | |
629 * in the case of domain name compression. | |
630 * | |
631 * @return The length of the portion of the domain name. | |
632 */ | |
633 static int | |
8634 | 634 mdns_read_name_len(const char *data, unsigned int datalen, unsigned int offset) |
8487 | 635 { |
8634 | 636 int startoffset = offset; |
8487 | 637 unsigned char tmp; |
638 | |
8634 | 639 while ((offset <= datalen) && ((tmp = util_get8(&data[offset])) != 0)) { |
640 offset++; | |
8487 | 641 |
642 if ((tmp & 0xc0) == 0) { /* First two bits are 00 */ | |
8634 | 643 if (offset + tmp > datalen) |
644 /* Attempt to read past end of data! Bailing! */ | |
645 return 0; | |
646 offset += tmp; | |
8487 | 647 |
648 } else if ((tmp & 0x40) == 0) { /* First two bits are 10 */ | |
649 /* Reserved for future use */ | |
650 | |
651 } else if ((tmp & 0x80) == 1) { /* First two bits are 01 */ | |
652 /* Reserved for future use */ | |
653 | |
654 } else { /* First two bits are 11 */ | |
655 /* End of this portion of the domain name */ | |
656 break; | |
657 | |
658 } | |
659 } | |
660 | |
8634 | 661 return offset - startoffset + 1; |
8487 | 662 } |
663 | |
664 /* | |
8636 | 665 * |
8487 | 666 * |
667 */ | |
668 static Question * | |
8634 | 669 mdns_read_questions(int numquestions, const char *data, unsigned int datalen, int *offset) |
8487 | 670 { |
671 Question *ret; | |
672 int i; | |
673 | |
674 ret = (Question *)g_malloc0(numquestions * sizeof(Question)); | |
675 for (i = 0; i < numquestions; i++) { | |
8634 | 676 ret[i].name = mdns_read_name(data, datalen, *offset); |
677 *offset += mdns_read_name_len(data, datalen, *offset); | |
8636 | 678 if (*offset + 4 > datalen) |
679 break; | |
8487 | 680 ret[i].type = util_get16(&data[*offset]); /* QTYPE */ |
681 *offset += 2; | |
682 ret[i].class = util_get16(&data[*offset]); /* QCLASS */ | |
683 *offset += 2; | |
8636 | 684 if (*offset > datalen) |
685 break; | |
686 } | |
687 | |
688 /* Malformed packet check */ | |
689 if (i < numquestions) { | |
690 for (i = 0; i < numquestions; i++) | |
691 g_free(ret[i].name); | |
692 g_free(ret); | |
693 return NULL; | |
8487 | 694 } |
695 | |
696 return ret; | |
697 } | |
698 | |
699 /* | |
700 * Read in a chunk of data, probably a buddy icon. | |
701 * | |
702 */ | |
703 static unsigned char * | |
8634 | 704 mdns_read_rr_rdata_null(const char *data, unsigned int datalen, unsigned int offset, unsigned short rdlength) |
8487 | 705 { |
706 unsigned char *ret = NULL; | |
707 | |
708 if (offset + rdlength > datalen) | |
709 return NULL; | |
710 | |
711 ret = (unsigned char *)g_malloc(rdlength); | |
712 memcpy(ret, &data[offset], rdlength); | |
713 | |
714 return ret; | |
715 } | |
716 | |
717 /* | |
8636 | 718 * Read in a compressed name. |
8487 | 719 * |
720 */ | |
721 static char * | |
8634 | 722 mdns_read_rr_rdata_ptr(const char *data, unsigned int datalen, unsigned int offset) |
8487 | 723 { |
8636 | 724 return mdns_read_name(data, datalen, offset); |
8487 | 725 } |
726 | |
727 /* | |
8636 | 728 * Read in a list of name=value pairs into a GHashTable. |
8487 | 729 * |
730 */ | |
731 static GHashTable * | |
8634 | 732 mdns_read_rr_rdata_txt(const char *data, unsigned int datalen, unsigned int offset, unsigned short rdlength) |
8487 | 733 { |
734 GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
735 int endoffset = offset + rdlength; | |
736 unsigned char tmp; | |
737 char buf[256], *key, *value; | |
738 | |
739 while (offset < endoffset) { | |
740 /* Read in the length of the next name/value pair */ | |
741 tmp = util_get8(&data[offset]); | |
742 offset++; | |
743 | |
8636 | 744 /* Malformed packet check */ |
8487 | 745 if (offset + tmp > endoffset) |
746 break; | |
747 | |
748 /* Read in the next name/value pair */ | |
749 strncpy(buf, &data[offset], tmp); | |
750 offset += tmp; | |
751 | |
752 if (buf[0] == '=') { | |
753 /* Name/value pairs beginning with = are silently ignored */ | |
754 continue; | |
755 } | |
756 | |
757 /* The value is a substring of buf, starting just after the = */ | |
758 buf[tmp] = '\0'; | |
759 value = strchr(buf, '='); | |
760 if (value != NULL) { | |
761 value[0] = '\0'; | |
762 value++; | |
763 } | |
764 | |
765 /* Make the key all lowercase */ | |
766 key = g_utf8_strdown(buf, -1); | |
767 if (!g_hash_table_lookup(ret, key)) | |
768 g_hash_table_insert(ret, key, g_strdup(value)); | |
769 else | |
770 g_free(key); | |
771 } | |
772 | |
8636 | 773 /* Malformed packet check */ |
774 if ((offset > datalen) || (offset != endoffset)) { | |
775 g_hash_table_destroy(ret); | |
776 return NULL; | |
777 } | |
778 | |
8487 | 779 return ret; |
780 } | |
781 | |
782 /* | |
8594 | 783 * |
784 * | |
785 */ | |
786 static ResourceRecordSRV * | |
8634 | 787 mdns_read_rr_rdata_srv(const char *data, unsigned int datalen, unsigned int offset, unsigned short rdlength) |
8594 | 788 { |
789 ResourceRecordSRV *ret = NULL; | |
790 int endoffset = offset + rdlength; | |
791 | |
8636 | 792 /* Malformed packet check */ |
8594 | 793 if (offset + 7 > endoffset) |
794 return NULL; | |
795 | |
796 ret = g_malloc(sizeof(ResourceRecordSRV)); | |
797 | |
798 /* Read in the priority */ | |
799 ret->priority = util_get16(&data[offset]); | |
800 offset += 2; | |
801 | |
802 /* Read in the weight */ | |
803 ret->weight = util_get16(&data[offset]); | |
804 offset += 2; | |
805 | |
806 /* Read in the port */ | |
807 ret->port = util_get16(&data[offset]); | |
808 offset += 2; | |
809 | |
810 /* Read in the target name */ | |
811 /* | |
812 * XXX - RFC2782 says it's not supposed to be an alias... | |
813 * but it was in the packet capture I looked at from iChat. | |
814 */ | |
815 ret->target = mdns_read_name(data, datalen, offset); | |
8636 | 816 offset += mdns_read_name_len(data, datalen, offset); |
817 | |
818 /* Malformed packet check */ | |
819 if ((offset > endoffset) || (ret->target == NULL)) { | |
820 g_free(ret->target); | |
821 g_free(ret); | |
822 return NULL; | |
823 } | |
8594 | 824 |
825 return ret; | |
826 } | |
827 | |
828 /* | |
8636 | 829 * |
8487 | 830 * |
831 */ | |
832 static ResourceRecord * | |
8634 | 833 mdns_read_rr(int numrecords, const char *data, unsigned int datalen, int *offset) |
8487 | 834 { |
835 ResourceRecord *ret; | |
836 int i; | |
837 | |
838 ret = (ResourceRecord *)g_malloc0(numrecords * sizeof(ResourceRecord)); | |
839 for (i = 0; i < numrecords; i++) { | |
8636 | 840 /* NAME */ |
841 ret[i].name = mdns_read_name(data, datalen, *offset); | |
8634 | 842 *offset += mdns_read_name_len(data, datalen, *offset); |
8636 | 843 |
844 /* Malformed packet check */ | |
845 if (*offset + 10 > datalen) | |
846 break; | |
847 | |
848 /* TYPE */ | |
849 ret[i].type = util_get16(&data[*offset]); | |
8487 | 850 *offset += 2; |
8636 | 851 |
852 /* CLASS */ | |
853 ret[i].class = util_get16(&data[*offset]); | |
8487 | 854 *offset += 2; |
8636 | 855 |
856 /* TTL */ | |
857 ret[i].ttl = util_get32(&data[*offset]); | |
8487 | 858 *offset += 4; |
8636 | 859 |
860 /* RDLENGTH */ | |
861 ret[i].rdlength = util_get16(&data[*offset]); | |
8487 | 862 *offset += 2; |
863 | |
864 /* RDATA */ | |
8636 | 865 if (ret[i].type == RENDEZVOUS_RRTYPE_NULL) { |
866 ret[i].rdata = mdns_read_rr_rdata_null(data, datalen, *offset, ret[i].rdlength); | |
867 if (ret[i].rdata == NULL) | |
868 break; | |
8487 | 869 |
8636 | 870 } else if (ret[i].type == RENDEZVOUS_RRTYPE_PTR) { |
871 ret[i].rdata = mdns_read_rr_rdata_ptr(data, datalen, *offset); | |
872 if (ret[i].rdata == NULL) | |
873 break; | |
874 | |
875 } else if (ret[i].type == RENDEZVOUS_RRTYPE_TXT) { | |
876 ret[i].rdata = mdns_read_rr_rdata_txt(data, datalen, *offset, ret[i].rdlength); | |
877 if (ret[i].rdata == NULL) | |
878 break; | |
8487 | 879 |
8636 | 880 } else if (ret[i].type == RENDEZVOUS_RRTYPE_SRV) { |
881 ret[i].rdata = mdns_read_rr_rdata_srv(data, datalen, *offset, ret[i].rdlength); | |
882 if (ret[i].rdata == NULL) | |
883 break; | |
8487 | 884 |
8636 | 885 } |
886 | |
887 /* Malformed packet check */ | |
888 *offset += ret[i].rdlength; | |
889 if (*offset > datalen) | |
8594 | 890 break; |
8636 | 891 } |
8594 | 892 |
8636 | 893 /* Malformed packet check */ |
894 if (i < numrecords) { | |
895 for (i = 0; i < numrecords; i++) { | |
896 g_free(ret[i].name); | |
897 mdns_free_rr_rdata(ret[i].type, ret[i].rdata); | |
8487 | 898 } |
8636 | 899 g_free(ret); |
900 return NULL; | |
8487 | 901 } |
902 | |
903 return ret; | |
904 } | |
905 | |
906 /* | |
8679 | 907 * If invalid data is encountered at any point when parsing data |
8735
92cbf9713795
[gaim-migrate @ 9490]
Christian Hammond <chipx86@chipx86.com>
parents:
8679
diff
changeset
|
908 * then the entire packet is discarded and NULL is returned. |
8487 | 909 * |
910 */ | |
911 DNSPacket * | |
912 mdns_read(int fd) | |
913 { | |
914 DNSPacket *ret = NULL; | |
8738 | 915 int i; |
8636 | 916 int offset; /* Current position in datagram */ |
8612 | 917 /* XXX - Find out what to use as a maximum incoming UDP packet size */ |
918 /* char data[512]; */ | |
8487 | 919 char data[10096]; |
8634 | 920 unsigned int datalen; |
8487 | 921 struct sockaddr_in addr; |
922 socklen_t addrlen; | |
923 | |
924 /* Read in an mDNS packet */ | |
925 addrlen = sizeof(struct sockaddr_in); | |
926 if ((datalen = recvfrom(fd, data, sizeof(data), 0, (struct sockaddr *)&addr, &addrlen)) == -1) { | |
927 gaim_debug_error("mdns", "Error reading packet: %d\n", errno); | |
928 return NULL; | |
929 } | |
930 | |
931 ret = (DNSPacket *)g_malloc0(sizeof(DNSPacket)); | |
932 | |
933 /* Parse the incoming packet, starting from 0 */ | |
8636 | 934 offset = 0; |
935 | |
936 if (offset + 12 > datalen) { | |
937 g_free(ret); | |
938 return NULL; | |
939 } | |
8487 | 940 |
941 /* The header section */ | |
8636 | 942 ret->header.id = util_get16(&data[offset]); /* ID */ |
943 offset += 2; | |
8487 | 944 |
945 /* For the flags, some bits must be 0 and some must be 1, the rest are ignored */ | |
8636 | 946 ret->header.flags = util_get16(&data[offset]); /* Flags (QR, OPCODE, AA, TC, RD, RA, Z, AD, CD, and RCODE */ |
947 offset += 2; | |
8487 | 948 if ((ret->header.flags & 0x8000) == 0) { |
949 /* QR should be 1 */ | |
950 g_free(ret); | |
951 return NULL; | |
952 } | |
953 if ((ret->header.flags & 0x7800) != 0) { | |
954 /* OPCODE should be all 0's */ | |
955 g_free(ret); | |
956 return NULL; | |
957 } | |
958 | |
959 /* Read in the number of other things in the packet */ | |
8636 | 960 ret->header.numquestions = util_get16(&data[offset]); |
961 offset += 2; | |
962 ret->header.numanswers = util_get16(&data[offset]); | |
963 offset += 2; | |
964 ret->header.numauthority = util_get16(&data[offset]); | |
965 offset += 2; | |
966 ret->header.numadditional = util_get16(&data[offset]); | |
967 offset += 2; | |
8487 | 968 |
969 /* Read in all the questions */ | |
8636 | 970 ret->questions = mdns_read_questions(ret->header.numquestions, data, datalen, &offset); |
8487 | 971 |
972 /* Read in all resource records */ | |
8636 | 973 ret->answers = mdns_read_rr(ret->header.numanswers, data, datalen, &offset); |
8487 | 974 |
975 /* Read in all authority records */ | |
8636 | 976 ret->authority = mdns_read_rr(ret->header.numauthority, data, datalen, &offset); |
8487 | 977 |
978 /* Read in all additional records */ | |
8636 | 979 ret->additional = mdns_read_rr(ret->header.numadditional, data, datalen, &offset); |
980 | |
981 /* Malformed packet check */ | |
982 if ((ret->header.numquestions > 0 && ret->questions == NULL) || | |
983 (ret->header.numanswers > 0 && ret->answers == NULL) || | |
984 (ret->header.numauthority > 0 && ret->authority == NULL) || | |
985 (ret->header.numadditional > 0 && ret->additional == NULL)) { | |
986 gaim_debug_error("mdns", "Received an invalid DNS packet.\n"); | |
987 return NULL; | |
988 } | |
8487 | 989 |
990 /* We should be at the end of the packet */ | |
8636 | 991 if (offset != datalen) { |
992 gaim_debug_error("mdns", "Finished parsing before end of DNS packet! Only parsed %d of %d bytes.", offset, datalen); | |
8487 | 993 g_free(ret); |
994 return NULL; | |
995 } | |
996 | |
8738 | 997 #if 0 |
998 for (i = 0; i < ret->header.numanswers; i++) | |
999 mdns_cache_add(&ret->answers[i]); | |
1000 for (i = 0; i < ret->header.numauthority; i++) | |
1001 mdns_cache_add(&ret->authority[i]); | |
1002 for (i = 0; i < ret->header.numadditional; i++) | |
1003 mdns_cache_add(&ret->additional[i]); | |
1004 for (i = 0; i < ret->header.numquestions; i++) | |
1005 mdns_cache_respond(fd, &ret->questions[i]); | |
1006 #endif | |
1007 | |
8487 | 1008 return ret; |
1009 } |