| 74 gaim_debug_error("mdns", "Unable to bind socket to interface.\n"); |
74 gaim_debug_error("mdns", "Unable to bind socket to interface.\n"); |
| 75 close(fd); |
75 close(fd); |
| 76 return -1; |
76 return -1; |
| 77 } |
77 } |
| 78 |
78 |
| 79 /* Ensure loopback is enabled (it should be enabled by default, by let's be sure) */ |
79 /* Ensure loopback is enabled (it should be enabled by default, but let's be sure) */ |
| 80 loop = 1; |
80 loop = 1; |
| 81 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(unsigned char)) == -1) { |
81 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(unsigned char)) == -1) { |
| 82 gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_LOOP\n"); |
82 gaim_debug_error("mdns", "Error calling setsockopt for IP_MULTICAST_LOOP\n"); |
| 83 } |
83 } |
| 84 |
84 |
| 133 /***************************************/ |
133 /***************************************/ |
| 134 /* Functions for sending mDNS messages */ |
134 /* Functions for sending mDNS messages */ |
| 135 /***************************************/ |
135 /***************************************/ |
| 136 |
136 |
| 137 static int |
137 static int |
| 138 mdns_getlength_RR_txt(void *rdata) |
138 mdns_getlength_name(const void *name) |
| 139 { |
139 { |
| 140 GSList *cur; |
140 return strlen((const char *)name) + 2; |
| 141 ResourceRecordTXTRDataNode *node; |
141 } |
| |
142 |
| |
143 static int |
| |
144 mdns_getlength_RR_rdata(unsigned short type, const void *rdata) |
| |
145 { |
| 142 int rdlength = 0; |
146 int rdlength = 0; |
| 143 |
147 |
| 144 for (cur = (GSList *)rdata; cur != NULL; cur = cur->next) { |
148 switch (type) { |
| 145 node = (ResourceRecordTXTRDataNode *)cur->data; |
149 case RENDEZVOUS_RRTYPE_PTR: |
| 146 rdlength += 1 + strlen(node->name); |
150 rdlength = mdns_getlength_name(rdata); |
| 147 if (node->value != NULL) |
151 break; |
| 148 rdlength += 1 + strlen(node->value); |
152 |
| |
153 case RENDEZVOUS_RRTYPE_TXT: { |
| |
154 GSList *cur; |
| |
155 ResourceRecordRDataTXTNode *node; |
| |
156 |
| |
157 for (cur = (GSList *)rdata; cur != NULL; cur = cur->next) { |
| |
158 node = (ResourceRecordRDataTXTNode *)cur->data; |
| |
159 rdlength += 1 + strlen(node->name); |
| |
160 if (node->value != NULL) |
| |
161 rdlength += 1 + strlen(node->value); |
| |
162 } |
| |
163 } break; |
| |
164 |
| |
165 case RENDEZVOUS_RRTYPE_SRV: |
| |
166 rdlength = 6 + mdns_getlength_name(((const ResourceRecordRDataSRV *)rdata)->target); |
| |
167 break; |
| 149 } |
168 } |
| 150 |
169 |
| 151 return rdlength; |
170 return rdlength; |
| 152 } |
171 } |
| 153 |
172 |
| 154 static int |
173 static int |
| 155 mdns_getlength_RR(const ResourceRecord *rr) |
174 mdns_getlength_RR(ResourceRecord *rr) |
| 156 { |
175 { |
| 157 int ret = 0; |
176 int ret = 0; |
| |
177 |
| |
178 rr->rdlength = mdns_getlength_RR_rdata(rr->type, rr->rdata); |
| 158 |
179 |
| 159 ret += strlen(rr->name) + 2; |
180 ret += strlen(rr->name) + 2; |
| 160 ret += 10; |
181 ret += 10; |
| 161 |
182 ret += rr->rdlength; |
| 162 switch (rr->type) { |
|
| 163 case RENDEZVOUS_RRTYPE_PTR: |
|
| 164 ret += strlen((const char *)rr->rdata) + 2; |
|
| 165 break; |
|
| 166 |
|
| 167 case RENDEZVOUS_RRTYPE_TXT: |
|
| 168 ret += mdns_getlength_RR_txt(rr->rdata); |
|
| 169 break; |
|
| 170 } |
|
| 171 |
183 |
| 172 return ret; |
184 return ret; |
| 173 } |
185 } |
| 174 |
186 |
| 175 static int |
187 static int |
| 199 |
211 |
| 200 i += mdns_put_name(data, datalen, offset + i, rr->name); |
212 i += mdns_put_name(data, datalen, offset + i, rr->name); |
| 201 i += util_put16(&data[offset + i], rr->type); |
213 i += util_put16(&data[offset + i], rr->type); |
| 202 i += util_put16(&data[offset + i], rr->class); |
214 i += util_put16(&data[offset + i], rr->class); |
| 203 i += util_put32(&data[offset + i], rr->ttl); |
215 i += util_put32(&data[offset + i], rr->ttl); |
| |
216 i += util_put16(&data[offset + i], rr->rdlength); |
| 204 |
217 |
| 205 switch (rr->type) { |
218 switch (rr->type) { |
| 206 case RENDEZVOUS_RRTYPE_PTR: |
219 case RENDEZVOUS_RRTYPE_PTR: |
| 207 i += util_put16(&data[offset + i], strlen((const char *)rr->rdata) + 2); |
|
| 208 i += mdns_put_name(data, datalen, offset + i, (const char *)rr->rdata); |
220 i += mdns_put_name(data, datalen, offset + i, (const char *)rr->rdata); |
| 209 break; |
221 break; |
| 210 |
222 |
| 211 case RENDEZVOUS_RRTYPE_TXT: { |
223 case RENDEZVOUS_RRTYPE_TXT: { |
| 212 GSList *cur; |
224 GSList *cur; |
| 213 ResourceRecordTXTRDataNode *node; |
225 ResourceRecordRDataTXTNode *node; |
| 214 int rdlength = mdns_getlength_RR_txt(rr->rdata); |
|
| 215 int mylength; |
226 int mylength; |
| 216 |
227 |
| 217 i += util_put16(&data[offset + i], rdlength); |
|
| 218 for (cur = (GSList *)rr->rdata; cur != NULL; cur = cur->next) { |
228 for (cur = (GSList *)rr->rdata; cur != NULL; cur = cur->next) { |
| 219 node = (ResourceRecordTXTRDataNode *)cur->data; |
229 node = (ResourceRecordRDataTXTNode *)cur->data; |
| 220 mylength = 1 + strlen(node->name); |
230 mylength = 1 + strlen(node->name); |
| 221 if (node->value) |
231 if (node->value) |
| 222 mylength += 1 + strlen(node->value); |
232 mylength += 1 + strlen(node->value); |
| 223 i += util_put8(&data[offset + i], mylength - 1); |
233 i += util_put8(&data[offset + i], mylength - 1); |
| 224 memcpy(&data[offset + i], node->name, strlen(node->name)); |
234 memcpy(&data[offset + i], node->name, strlen(node->name)); |
| 229 memcpy(&data[offset + i], node->value, strlen(node->value)); |
239 memcpy(&data[offset + i], node->value, strlen(node->value)); |
| 230 i += strlen(node->value); |
240 i += strlen(node->value); |
| 231 } |
241 } |
| 232 } |
242 } |
| 233 } break; |
243 } break; |
| |
244 |
| |
245 case RENDEZVOUS_RRTYPE_SRV: { |
| |
246 ResourceRecordRDataSRV *srv = rr->rdata; |
| |
247 i += util_put16(&data[offset + i], 0); |
| |
248 i += util_put16(&data[offset + i], 0); |
| |
249 i += util_put16(&data[offset + i], srv->port); |
| |
250 i += mdns_put_name(data, datalen, offset + i, srv->target); |
| |
251 } break; |
| 234 } |
252 } |
| 235 |
253 |
| 236 return i; |
254 return i; |
| 237 } |
255 } |
| 238 |
256 |
| 251 /* Header */ |
269 /* Header */ |
| 252 datalen += 12; |
270 datalen += 12; |
| 253 |
271 |
| 254 /* Questions */ |
272 /* Questions */ |
| 255 for (i = 0; i < dns->header.numquestions; i++) |
273 for (i = 0; i < dns->header.numquestions; i++) |
| 256 datalen += strlen(dns->questions[i].name) + 2 + 4; |
274 datalen += mdns_getlength_name(dns->questions[i].name) + 4; |
| 257 |
275 |
| 258 /* Resource records */ |
276 /* Resource records */ |
| 259 for (i = 0; i < dns->header.numanswers; i++) |
277 for (i = 0; i < dns->header.numanswers; i++) |
| 260 datalen += mdns_getlength_RR(&dns->answers[i]); |
278 datalen += mdns_getlength_RR(&dns->answers[i]); |
| 261 for (i = 0; i < dns->header.numauthority; i++) |
279 for (i = 0; i < dns->header.numauthority; i++) |
| 355 dns->questions = NULL; |
373 dns->questions = NULL; |
| 356 |
374 |
| 357 dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord)); |
375 dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord)); |
| 358 dns->answers[0].name = g_strdup(name); |
376 dns->answers[0].name = g_strdup(name); |
| 359 dns->answers[0].type = RENDEZVOUS_RRTYPE_PTR; |
377 dns->answers[0].type = RENDEZVOUS_RRTYPE_PTR; |
| 360 dns->answers[0].class = 0x0001; |
378 dns->answers[0].class = 0x8001; |
| 361 dns->answers[0].ttl = 0x00001c20; |
379 dns->answers[0].ttl = 0x00001c20; |
| 362 dns->answers[0].rdlength = 0x0000; /* Set automatically */ |
380 dns->answers[0].rdlength = 0x0000; /* Set automatically */ |
| 363 dns->answers[0].rdata = (void *)g_strdup(domain); |
381 dns->answers[0].rdata = (void *)g_strdup(domain); |
| 364 |
382 |
| 365 dns->authority = NULL; |
383 dns->authority = NULL; |
| 392 dns->questions = NULL; |
410 dns->questions = NULL; |
| 393 |
411 |
| 394 dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord)); |
412 dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord)); |
| 395 dns->answers[0].name = g_strdup(name); |
413 dns->answers[0].name = g_strdup(name); |
| 396 dns->answers[0].type = RENDEZVOUS_RRTYPE_TXT; |
414 dns->answers[0].type = RENDEZVOUS_RRTYPE_TXT; |
| 397 dns->answers[0].class = 0x0001; |
415 dns->answers[0].class = 0x8001; |
| 398 dns->answers[0].ttl = 0x00001c20; |
416 dns->answers[0].ttl = 0x00001c20; |
| 399 dns->answers[0].rdlength = 0x0000; /* Set automatically */ |
417 dns->answers[0].rdlength = 0x0000; /* Set automatically */ |
| 400 dns->answers[0].rdata = (void *)rdata; |
418 dns->answers[0].rdata = (void *)rdata; |
| 401 |
419 |
| 402 dns->authority = NULL; |
420 dns->authority = NULL; |
| 403 dns->additional = NULL; |
421 dns->additional = NULL; |
| 404 |
422 |
| 405 mdns_send_dns(fd, dns); |
423 mdns_send_dns(fd, dns); |
| 406 |
424 |
| 407 /* The hash table should be freed by the caller of this function */ |
425 /* The rdata should be freed by the caller of this function */ |
| |
426 dns->answers[0].rdata = NULL; |
| |
427 |
| |
428 mdns_free(dns); |
| |
429 |
| |
430 return ret; |
| |
431 } |
| |
432 |
| |
433 int |
| |
434 mdns_advertise_srv(int fd, const char *name, unsigned short port, const char *target) |
| |
435 { |
| |
436 int ret; |
| |
437 DNSPacket *dns; |
| |
438 ResourceRecordRDataSRV *rdata; |
| |
439 |
| |
440 if ((strlen(target) > 255)) { |
| |
441 return -EINVAL; |
| |
442 } |
| |
443 |
| |
444 rdata = g_malloc(sizeof(ResourceRecordRDataSRV)); |
| |
445 rdata->port = port; |
| |
446 rdata->target = target; |
| |
447 |
| |
448 dns = (DNSPacket *)g_malloc(sizeof(DNSPacket)); |
| |
449 dns->header.id = 0x0000; |
| |
450 dns->header.flags = 0x8400; |
| |
451 dns->header.numquestions = 0x0000; |
| |
452 dns->header.numanswers = 0x0001; |
| |
453 dns->header.numauthority = 0x0000; |
| |
454 dns->header.numadditional = 0x0000; |
| |
455 dns->questions = NULL; |
| |
456 |
| |
457 dns->answers = (ResourceRecord *)g_malloc(1 * sizeof(ResourceRecord)); |
| |
458 dns->answers[0].name = g_strdup(name); |
| |
459 dns->answers[0].type = RENDEZVOUS_RRTYPE_SRV; |
| |
460 dns->answers[0].class = 0x8001; |
| |
461 dns->answers[0].ttl = 0x00001c20; |
| |
462 dns->answers[0].rdlength = 0x0000; /* Set automatically */ |
| |
463 dns->answers[0].rdata = rdata; |
| |
464 |
| |
465 dns->authority = NULL; |
| |
466 dns->additional = NULL; |
| |
467 |
| |
468 mdns_send_dns(fd, dns); |
| |
469 |
| |
470 g_free(dns->answers[0].rdata); |
| 408 dns->answers[0].rdata = NULL; |
471 dns->answers[0].rdata = NULL; |
| 409 |
472 |
| 410 mdns_free(dns); |
473 mdns_free(dns); |
| 411 |
474 |
| 412 return ret; |
475 return ret; |
| 416 /* Functions for parsing mDNS messages */ |
479 /* Functions for parsing mDNS messages */ |
| 417 /***************************************/ |
480 /***************************************/ |
| 418 |
481 |
| 419 /* |
482 /* |
| 420 * XXX - Needs bounds checking! |
483 * XXX - Needs bounds checking! |
| |
484 * XXX - Also make sure you don't backtrack and infinitely loop. |
| 421 * |
485 * |
| 422 * Read in a domain name from the given buffer starting at the given |
486 * Read in a domain name from the given buffer starting at the given |
| 423 * offset. This handles using domain name compression to jump around |
487 * offset. This handles using domain name compression to jump around |
| 424 * the data buffer, if needed. |
488 * the data buffer, if needed. |
| 425 * |
489 * |