]> git.meshlink.io Git - catta/blob - avahi-core/socket.c
a555eafba1a0f039ed22d236ebda045d848af820
[catta] / avahi-core / socket.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <inttypes.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/time.h>
36 #include <net/if.h>
37 #include <sys/ioctl.h>
38 #include <assert.h>
39
40 #include "dns.h"
41 #include "fdutil.h"
42 #include "socket.h"
43 #include "log.h"
44
45 /* this is a portability hack */
46 #ifndef IPV6_ADD_MEMBERSHIP
47 #ifdef  IPV6_JOIN_GROUP
48 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
49 #endif
50 #endif
51
52 #ifndef IPV6_DROP_MEMBERSHIP
53 #ifdef  IPV6_LEAVE_GROUP
54 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
55 #endif
56 #endif
57
58 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
59     assert(ret_sa);
60
61     memset(ret_sa, 0, sizeof(struct sockaddr_in));
62     ret_sa->sin_family = AF_INET;
63     ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
64     inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
65 }
66
67 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
68     assert(ret_sa);
69
70     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
71     ret_sa->sin6_family = AF_INET6;
72     ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
73     inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
74 }
75
76 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) {
77     assert(ret_sa);
78     assert(a);
79     assert(port > 0);
80
81     memset(ret_sa, 0, sizeof(struct sockaddr_in));
82     ret_sa->sin_family = AF_INET;
83     ret_sa->sin_port = htons(port);
84     memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
85 }
86
87 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) {
88     assert(ret_sa);
89     assert(a);
90     assert(port > 0);
91
92     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
93     ret_sa->sin6_family = AF_INET6;
94     ret_sa->sin6_port = htons(port);
95     memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
96 }
97
98 int avahi_mdns_mcast_join_ipv4(int fd, int idx) {
99     struct ip_mreqn mreq; 
100     struct sockaddr_in sa;
101
102     mdns_mcast_group_ipv4 (&sa);
103  
104     memset(&mreq, 0, sizeof(mreq));
105     mreq.imr_multiaddr = sa.sin_addr;
106     mreq.imr_ifindex = idx;
107  
108     if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
109         avahi_log_warn("IP_ADD_MEMBERSHIP failed: %s", strerror(errno));
110         return -1;
111     } 
112
113     return 0;
114 }
115
116 int avahi_mdns_mcast_join_ipv6(int fd, int idx) {
117     struct ipv6_mreq mreq6; 
118     struct sockaddr_in6 sa6;
119
120     mdns_mcast_group_ipv6 (&sa6);
121
122     memset(&mreq6, 0, sizeof(mreq6));
123     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
124     mreq6.ipv6mr_interface = idx;
125
126     if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
127         avahi_log_warn("IPV6_ADD_MEMBERSHIP failed: %s", strerror(errno));
128         return -1;
129     }
130
131     return 0;
132 }
133
134 int avahi_mdns_mcast_leave_ipv4(int fd, int idx) {
135     struct ip_mreqn mreq; 
136     struct sockaddr_in sa;
137     
138     mdns_mcast_group_ipv4 (&sa);
139  
140     memset(&mreq, 0, sizeof(mreq));
141     mreq.imr_multiaddr = sa.sin_addr;
142     mreq.imr_ifindex = idx;
143  
144     if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
145         avahi_log_warn("IP_DROP_MEMBERSHIP failed: %s", strerror(errno));
146         return -1;
147     }
148
149     return 0;
150 }
151
152 int avahi_mdns_mcast_leave_ipv6(int fd, int idx) {
153     struct ipv6_mreq mreq6; 
154     struct sockaddr_in6 sa6;
155
156     mdns_mcast_group_ipv6 (&sa6);
157
158     memset(&mreq6, 0, sizeof(mreq6));
159     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
160     mreq6.ipv6mr_interface = idx;
161
162     if (setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
163         avahi_log_warn("IPV6_DROP_MEMBERSHIP failed: %s", strerror(errno));
164         return -1;
165     }
166
167     return 0;
168 }
169
170 static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
171     int yes;
172     
173     assert(fd >= 0);
174     assert(sa);
175     assert(l > 0);
176     
177     if (bind(fd, sa, l) < 0) {
178
179         if (errno != EADDRINUSE) {
180             avahi_log_warn("bind() failed: %s", strerror(errno));
181             return -1;
182         }
183             
184         avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
185                        sa->sa_family == AF_INET ? "IPv4" : "IPv6");
186
187         /* Try again, this time with SO_REUSEADDR set */
188         yes = 1;
189         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
190             avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
191             return -1;
192         }
193
194         if (bind(fd, sa, l) < 0) {
195             avahi_log_warn("bind() failed: %s", strerror(errno));
196             return -1;
197         }
198     } else {
199
200         /* We enable SO_REUSEADDR afterwards, to make sure that the
201          * user may run other mDNS implementations if he really
202          * wants. */
203         
204         yes = 1;
205         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
206             avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
207             return -1;
208         }
209     }
210
211     return 0;
212 }
213
214 static int ip_pktinfo (int fd, int yes)
215 {
216     int ret = -1;
217
218 #if IP_PKTINFO
219     if ((ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes))) < 0) {
220         avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
221     }
222 #else
223 #ifdef IP_RECVINTERFACE
224     if ((ret = setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof (yes))) < 0) {
225         avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
226     }
227 #else
228 #ifdef IP_RECVIF
229     if ((ret = setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof (yes))) < 0) {
230         avahi_log_warn("IP_RECVIF failed: %s", strerror(errno));
231     }
232 #endif /* IP_RECVIF */
233 #endif /* IP_RECVINTERFACE */
234 #if defined (IP_RECVDSTADDR) /* && !defined(solaris) */
235     if ((ret = setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof (yes))) < 0) {
236         avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
237     }
238 #endif /* IP_RECVDSTADDR */
239 #endif /* IP_PKTINFO */
240
241     return (ret);
242 }
243
244 int avahi_open_socket_ipv4(int no_reuse) {
245     struct sockaddr_in local;
246     int fd = -1, ttl, yes, r;
247         
248     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
249         avahi_log_warn("socket() failed: %s", strerror(errno));
250         goto fail;
251     }
252     
253     ttl = 255;
254     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
255         avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
256         goto fail;
257     }
258
259     ttl = 255;
260     if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
261         avahi_log_warn("IP_TTL failed: %s", strerror(errno));
262         goto fail;
263     }
264     
265     yes = 1;
266     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
267         avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
268         goto fail;
269     }
270     
271     memset(&local, 0, sizeof(local));
272     local.sin_family = AF_INET;
273     local.sin_port = htons(AVAHI_MDNS_PORT);
274
275     if (no_reuse)
276         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
277     else
278         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
279
280     if (r < 0)
281         goto fail;
282
283     yes = 1;
284     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
285         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
286         goto fail;
287     }
288
289     yes = 1;
290     if (ip_pktinfo (fd, yes) < 0) {
291          goto fail;
292     }
293
294     if (avahi_set_cloexec(fd) < 0) {
295         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
296         goto fail;
297     }
298     
299     if (avahi_set_nonblock(fd) < 0) {
300         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
301         goto fail;
302     }
303
304     return fd;
305
306 fail:
307     if (fd >= 0)
308         close(fd);
309
310     return -1;
311 }
312
313 int avahi_open_socket_ipv6(int no_reuse) {
314     struct sockaddr_in6 sa, local;
315     int fd = -1, ttl, yes, r;
316
317     mdns_mcast_group_ipv6(&sa);
318         
319     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
320         avahi_log_warn("socket() failed: %s", strerror(errno));
321         goto fail;
322     }
323     
324     ttl = 255;
325     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
326         avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
327         goto fail;
328     }
329
330     ttl = 255;
331     if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
332         avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
333         goto fail;
334     }
335     
336     yes = 1;
337     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
338         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
339         goto fail;
340     }
341
342     yes = 1;
343     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
344         avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
345         goto fail;
346     }
347
348     memset(&local, 0, sizeof(local));
349     local.sin6_family = AF_INET6;
350     local.sin6_port = htons(AVAHI_MDNS_PORT);
351     
352     if (no_reuse)
353         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
354     else
355         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
356
357     if (r < 0)
358         goto fail;
359
360     yes = 1;
361     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
362         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
363         goto fail;
364     }
365
366     yes = 1;
367     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
368         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
369         goto fail;
370     }
371     
372     if (avahi_set_cloexec(fd) < 0) {
373         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
374         goto fail;
375     }
376     
377     if (avahi_set_nonblock(fd) < 0) {
378         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
379         goto fail;
380     }
381
382     return fd;
383
384 fail:
385     if (fd >= 0)
386         close(fd);
387
388     return -1;
389 }
390
391 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
392     assert(fd >= 0);
393     assert(msg);
394
395     for (;;) {
396     
397         if (sendmsg(fd, msg, flags) >= 0)
398             break;
399         
400         if (errno != EAGAIN) {
401             avahi_log_debug("sendmsg() failed: %s", strerror(errno));
402             return -1;
403         }
404         
405         if (avahi_wait_for_write(fd) < 0)
406             return -1;
407     }
408
409     return 0;
410 }
411
412 int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const AvahiIPv4Address *a, uint16_t port) {
413     struct sockaddr_in sa;
414     struct msghdr msg;
415     struct iovec io;
416     struct cmsghdr *cmsg;
417     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
418
419     assert(fd >= 0);
420     assert(p);
421     assert(avahi_dns_packet_check_valid(p) >= 0);
422     assert(!a || port > 0);
423
424     if (!a)
425         mdns_mcast_group_ipv4(&sa);
426     else
427         ipv4_address_to_sockaddr(&sa, a, port);
428
429     memset(&io, 0, sizeof(io));
430     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
431     io.iov_len = p->size;
432
433     memset(&msg, 0, sizeof(msg));
434     msg.msg_name = &sa;
435     msg.msg_namelen = sizeof(sa);
436     msg.msg_iov = &io;
437     msg.msg_iovlen = 1;
438     msg.msg_flags = 0;
439
440     if (interface >= 0) {
441         struct in_pktinfo *pkti;
442         
443         memset(cmsg_data, 0, sizeof(cmsg_data));
444         cmsg = (struct cmsghdr*) cmsg_data;
445         cmsg->cmsg_len = sizeof(cmsg_data);
446         cmsg->cmsg_level = IPPROTO_IP;
447         cmsg->cmsg_type = IP_PKTINFO;
448         
449         pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
450         pkti->ipi_ifindex = interface;
451
452         msg.msg_control = cmsg_data;
453         msg.msg_controllen = sizeof(cmsg_data);
454     } else {
455         msg.msg_control = NULL;
456         msg.msg_controllen = 0;
457     }
458
459     return sendmsg_loop(fd, &msg, 0);
460 }
461
462 int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const AvahiIPv6Address *a, uint16_t port) {
463     struct sockaddr_in6 sa;
464     struct msghdr msg;
465     struct iovec io;
466     struct cmsghdr *cmsg;
467     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
468
469     assert(fd >= 0);
470     assert(p);
471     assert(avahi_dns_packet_check_valid(p) >= 0);
472
473     if (!a)
474         mdns_mcast_group_ipv6(&sa);
475     else
476         ipv6_address_to_sockaddr(&sa, a, port);
477
478     memset(&io, 0, sizeof(io));
479     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
480     io.iov_len = p->size;
481
482     
483     memset(&msg, 0, sizeof(msg));
484     msg.msg_name = &sa;
485     msg.msg_namelen = sizeof(sa);
486     msg.msg_iov = &io;
487     msg.msg_iovlen = 1;
488     msg.msg_flags = 0;
489
490     if (interface >= 0) {
491         struct in6_pktinfo *pkti;
492     
493         memset(cmsg_data, 0, sizeof(cmsg_data));
494         cmsg = (struct cmsghdr*) cmsg_data;
495         cmsg->cmsg_len = sizeof(cmsg_data);
496         cmsg->cmsg_level = IPPROTO_IPV6;
497         cmsg->cmsg_type = IPV6_PKTINFO;
498         
499         pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
500         pkti->ipi6_ifindex = interface;
501         
502         msg.msg_control = cmsg_data;
503         msg.msg_controllen = sizeof(cmsg_data);
504     } else {
505         msg.msg_control = NULL;
506         msg.msg_controllen = 0;
507     }
508     
509     return sendmsg_loop(fd, &msg, 0);
510 }
511
512 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
513     AvahiDnsPacket *p= NULL;
514     struct msghdr msg;
515     struct iovec io;
516     uint8_t aux[1024];
517     ssize_t l;
518     struct cmsghdr *cmsg;
519     int found_ttl = 0, found_iface = 0;
520     int ms;
521
522     assert(fd >= 0);
523
524     if (ioctl(fd, FIONREAD, &ms) < 0) {
525         avahi_log_warn("ioctl(): %s", strerror(errno));
526         goto fail;
527     }
528
529     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
530
531     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
532     io.iov_len = p->max_size;
533     
534     memset(&msg, 0, sizeof(msg));
535     if (ret_sa) {
536         msg.msg_name = ret_sa;
537         msg.msg_namelen = sizeof(struct sockaddr_in);
538     } else {
539         msg.msg_name = NULL;
540         msg.msg_namelen = 0;
541     }
542     msg.msg_iov = &io;
543     msg.msg_iovlen = 1;
544     msg.msg_control = aux;
545     msg.msg_controllen = sizeof(aux);
546     msg.msg_flags = 0;
547     
548     if ((l = recvmsg(fd, &msg, 0)) < 0) {
549         avahi_log_warn("recvmsg(): %s", strerror(errno));
550         goto fail;
551     }
552
553     if (ret_sa && ret_sa->sin_addr.s_addr == INADDR_ANY) {
554         /* Linux 2.4 behaves very strangely sometimes! */
555
556         /*avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); */
557         goto fail;
558     }
559     
560     assert(!(msg.msg_flags & MSG_CTRUNC));
561     assert(!(msg.msg_flags & MSG_TRUNC));
562     p->size = (size_t) l;
563
564     if (ret_ttl)
565         *ret_ttl = 0;
566
567     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
568
569         if (cmsg->cmsg_level == IPPROTO_IP) {
570             
571             if (cmsg->cmsg_type == IP_TTL) {
572                 if (ret_ttl)
573                     *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
574                 found_ttl = 1;
575             } else if (cmsg->cmsg_type == IP_PKTINFO) {
576                 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
577
578                 if (ret_iface)
579                     *ret_iface = (int) i->ipi_ifindex;
580
581                 if (ret_dest_address)
582                     ret_dest_address->address = i->ipi_addr.s_addr;
583                 found_iface = 1;
584             }
585         }
586     }
587
588     assert(found_iface);
589     assert(found_ttl);
590
591     return p;
592
593 fail:
594     if (p)
595         avahi_dns_packet_free(p);
596
597     return NULL;
598 }
599
600 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa, AvahiIPv6Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
601     AvahiDnsPacket *p = NULL;
602     struct msghdr msg;
603     struct iovec io;
604     uint8_t aux[64];
605     ssize_t l;
606     int ms;
607     
608     struct cmsghdr *cmsg;
609     int found_ttl = 0, found_iface = 0;
610
611     assert(fd >= 0);
612
613     if (ioctl(fd, FIONREAD, &ms) < 0) {
614         avahi_log_warn("ioctl(): %s", strerror(errno));
615         goto fail;
616     }
617     
618     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
619
620     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
621     io.iov_len = p->max_size;
622     
623     memset(&msg, 0, sizeof(msg));
624     if (ret_sa) {
625         msg.msg_name = ret_sa;
626         msg.msg_namelen = sizeof(struct sockaddr_in6);
627     } else {
628         msg.msg_name = NULL;
629         msg.msg_namelen = 0;
630     }
631     
632     msg.msg_iov = &io;
633     msg.msg_iovlen = 1;
634     msg.msg_control = aux;
635     msg.msg_controllen = sizeof(aux);
636     msg.msg_flags = 0;
637     
638     if ((l = recvmsg(fd, &msg, 0)) < 0) {
639         avahi_log_warn("recvmsg(): %s", strerror(errno));
640         goto fail;
641     }
642
643     p->size = (size_t) l;
644
645     if (ret_ttl)
646         *ret_ttl = 0;
647
648     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
649         if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
650
651             if (ret_ttl)
652                 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
653             
654             found_ttl = 1;
655         }
656             
657         if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
658             struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
659
660             if (ret_iface)
661                 *ret_iface = i->ipi6_ifindex;
662
663             if (ret_dest_address)
664                 memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
665             found_iface = 1;
666         }
667     }
668
669     assert(found_iface);
670     assert(found_ttl);
671
672     return p;
673
674 fail:
675     if (p)
676         avahi_dns_packet_free(p);
677
678     return NULL;
679 }
680
681 int avahi_open_unicast_socket_ipv4(void) {
682     struct sockaddr_in local;
683     int fd = -1, yes;
684         
685     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
686         avahi_log_warn("socket() failed: %s", strerror(errno));
687         goto fail;
688     }
689     
690     memset(&local, 0, sizeof(local));
691     local.sin_family = AF_INET;
692     
693     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
694         avahi_log_warn("bind() failed: %s", strerror(errno));
695         goto fail;
696     }
697
698     yes = 1;
699     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
700         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
701         goto fail;
702     }
703
704     yes = 1;
705     if (ip_pktinfo (fd, yes) < 0) {
706          goto fail;
707     }
708
709     if (avahi_set_cloexec(fd) < 0) {
710         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
711         goto fail;
712     }
713     
714     if (avahi_set_nonblock(fd) < 0) {
715         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
716         goto fail;
717     }
718
719     return fd;
720
721 fail:
722     if (fd >= 0)
723         close(fd);
724
725     return -1;
726 }
727
728 int avahi_open_unicast_socket_ipv6(void) {
729     struct sockaddr_in6 local;
730     int fd = -1, yes;
731         
732     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
733         avahi_log_warn("socket() failed: %s", strerror(errno));
734         goto fail;
735     }
736     
737     memset(&local, 0, sizeof(local));
738     local.sin6_family = AF_INET6;
739     
740     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
741         avahi_log_warn("bind() failed: %s", strerror(errno));
742         goto fail;
743     }
744
745     yes = 1;
746     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
747         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
748         goto fail;
749     }
750
751     yes = 1;
752     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
753         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
754         goto fail;
755     }
756     
757     if (avahi_set_cloexec(fd) < 0) {
758         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
759         goto fail;
760     }
761     
762     if (avahi_set_nonblock(fd) < 0) {
763         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
764         goto fail;
765     }
766
767     return fd;
768
769 fail:
770     if (fd >= 0)
771         close(fd);
772
773     return -1;
774 }