]> git.meshlink.io Git - catta/blob - avahi-core/socket.c
* preliminary ipv6 support for BSD
[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 #ifdef IP_RECVIF
41 #include <net/if_dl.h>
42 #endif
43
44 #include "dns.h"
45 #include "fdutil.h"
46 #include "socket.h"
47 #include "log.h"
48
49 /* this is a portability hack */
50 #ifndef IPV6_ADD_MEMBERSHIP
51 #ifdef  IPV6_JOIN_GROUP
52 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
53 #endif
54 #endif
55
56 #ifndef IPV6_DROP_MEMBERSHIP
57 #ifdef  IPV6_LEAVE_GROUP
58 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
59 #endif
60 #endif
61
62 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
63     assert(ret_sa);
64
65     memset(ret_sa, 0, sizeof(struct sockaddr_in));
66     ret_sa->sin_family = AF_INET;
67     ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
68     inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
69 }
70
71 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
72     assert(ret_sa);
73
74     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
75     ret_sa->sin6_family = AF_INET6;
76     ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
77     inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
78 }
79
80 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) {
81     assert(ret_sa);
82     assert(a);
83     assert(port > 0);
84
85     memset(ret_sa, 0, sizeof(struct sockaddr_in));
86     ret_sa->sin_family = AF_INET;
87     ret_sa->sin_port = htons(port);
88     memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
89 }
90
91 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) {
92     assert(ret_sa);
93     assert(a);
94     assert(port > 0);
95
96     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
97     ret_sa->sin6_family = AF_INET6;
98     ret_sa->sin6_port = htons(port);
99     memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
100 }
101
102 int avahi_mdns_mcast_join_ipv4(int fd, int idx) {
103 /*  FIXME: sebest : we must factorize this part of the code */
104     struct sockaddr_in sa;
105 #ifdef HAVE_STRUCT_IP_MREQN
106     struct ip_mreqn mreq; 
107  
108     memset(&mreq, 0, sizeof(mreq));
109     mreq.imr_ifindex = idx;
110 #else
111     struct ip_mreq mreq; 
112     struct ifreq ifreq;
113
114     memset(&mreq, 0, sizeof(mreq));
115
116     if ((!if_indextoname(idx, ifreq.ifr_name))) {
117       avahi_log_warn("if_indextoname failed: %s", strerror(errno));
118       return -1;
119     }
120
121     if (ioctl(fd, SIOCGIFADDR, &ifreq) < 0) {
122       avahi_log_warn("SIOCGIFADDR failed: %s", strerror(errno));
123       return -1;
124     }
125
126     memcpy(&mreq.imr_interface,
127            &((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr,
128            sizeof(struct in_addr));
129 #endif
130     mdns_mcast_group_ipv4 (&sa);
131     mreq.imr_multiaddr = sa.sin_addr;
132
133     if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
134         avahi_log_warn("IP_ADD_MEMBERSHIP failed: %s", strerror(errno));
135         return -1;
136     } 
137
138     return 0;
139 }
140
141 int avahi_mdns_mcast_join_ipv6(int fd, int idx) {
142     struct ipv6_mreq mreq6; 
143     struct sockaddr_in6 sa6;
144
145     mdns_mcast_group_ipv6 (&sa6);
146
147     memset(&mreq6, 0, sizeof(mreq6));
148     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
149     mreq6.ipv6mr_interface = idx;
150
151     if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
152         avahi_log_warn("IPV6_ADD_MEMBERSHIP failed: %s", strerror(errno));
153         return -1;
154     }
155
156     return 0;
157 }
158
159 int avahi_mdns_mcast_leave_ipv4(int fd, int idx) {
160 /*  FIXME: sebest : we must factorize this part of the code */
161     struct sockaddr_in sa;
162 #ifdef HAVE_STRUCT_IP_MREQN
163     struct ip_mreqn mreq; 
164  
165     memset(&mreq, 0, sizeof(mreq));
166     mreq.imr_ifindex = idx;
167 #else
168     struct ip_mreq mreq; 
169     struct ifreq ifreq;
170
171     memset(&mreq, 0, sizeof(mreq));
172
173     if ((!if_indextoname(idx, ifreq.ifr_name))) {
174       avahi_log_warn("if_indextoname failed: %s", strerror(errno));
175       return -1;
176     }
177
178     if (ioctl(fd, SIOCGIFADDR, &ifreq) < 0) {
179       avahi_log_warn("SIOCGIFADDR failed: %s", strerror(errno));
180       return -1;
181     }
182
183     memcpy(&mreq.imr_interface,
184            &((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr,
185            sizeof(struct in_addr));
186 #endif
187     mdns_mcast_group_ipv4 (&sa);
188     mreq.imr_multiaddr = sa.sin_addr;
189
190     if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
191         avahi_log_warn("IP_DROP_MEMBERSHIP failed: %s", strerror(errno));
192         return -1;
193     }
194
195     return 0;
196 }
197
198 int avahi_mdns_mcast_leave_ipv6(int fd, int idx) {
199     struct ipv6_mreq mreq6; 
200     struct sockaddr_in6 sa6;
201
202     mdns_mcast_group_ipv6 (&sa6);
203
204     memset(&mreq6, 0, sizeof(mreq6));
205     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
206     mreq6.ipv6mr_interface = idx;
207
208     if (setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
209         avahi_log_warn("IPV6_DROP_MEMBERSHIP failed: %s", strerror(errno));
210         return -1;
211     }
212
213     return 0;
214 }
215
216 static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
217     int yes;
218     
219     assert(fd >= 0);
220     assert(sa);
221     assert(l > 0);
222     
223     if (bind(fd, sa, l) < 0) {
224
225         if (errno != EADDRINUSE) {
226             avahi_log_warn("bind() failed: %s", strerror(errno));
227             return -1;
228         }
229             
230         avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
231                        sa->sa_family == AF_INET ? "IPv4" : "IPv6");
232
233         /* Try again, this time with SO_REUSEADDR set */
234         yes = 1;
235         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
236             avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
237             return -1;
238         }
239
240         if (bind(fd, sa, l) < 0) {
241             avahi_log_warn("bind() failed: %s", strerror(errno));
242             return -1;
243         }
244     } else {
245
246         /* We enable SO_REUSEADDR afterwards, to make sure that the
247          * user may run other mDNS implementations if he really
248          * wants. */
249         
250         yes = 1;
251         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
252             avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
253             return -1;
254         }
255     }
256
257     return 0;
258 }
259
260 static int ip_pktinfo (int fd, int yes)
261 {
262     int ret = -1;
263
264 #ifdef IP_PKTINFO
265     if ((ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes))) < 0) {
266         avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
267     }
268 #else
269 #ifdef IP_RECVINTERFACE
270     if ((ret = setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof (yes))) < 0) {
271         avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
272     }
273 #else
274 #ifdef IP_RECVIF
275     if ((ret = setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof (yes))) < 0) {
276         avahi_log_warn("IP_RECVIF failed: %s", strerror(errno));
277     }
278 #endif /* IP_RECVIF */
279 #endif /* IP_RECVINTERFACE */
280 #if defined (IP_RECVDSTADDR) /* && !defined(solaris) */
281     if ((ret = setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof (yes))) < 0) {
282         avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
283     }
284 #endif /* IP_RECVDSTADDR */
285 #endif /* IP_PKTINFO */
286
287     return (ret);
288 }
289
290 int avahi_open_socket_ipv4(int no_reuse) {
291     struct sockaddr_in local;
292     int fd = -1, ttl, yes, r;
293         
294     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
295         avahi_log_warn("socket() failed: %s", strerror(errno));
296         goto fail;
297     }
298     
299     ttl = 255;
300     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
301         avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
302         goto fail;
303     }
304
305     ttl = 255;
306     if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
307         avahi_log_warn("IP_TTL failed: %s", strerror(errno));
308         goto fail;
309     }
310     
311     yes = 1;
312     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
313         avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
314         goto fail;
315     }
316     
317     memset(&local, 0, sizeof(local));
318     local.sin_family = AF_INET;
319     local.sin_port = htons(AVAHI_MDNS_PORT);
320
321     if (no_reuse)
322         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
323     else
324         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
325
326     if (r < 0)
327         goto fail;
328
329     yes = 1;
330     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
331         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
332         goto fail;
333     }
334
335     yes = 1;
336     if (ip_pktinfo (fd, yes) < 0) {
337          goto fail;
338     }
339
340     if (avahi_set_cloexec(fd) < 0) {
341         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
342         goto fail;
343     }
344     
345     if (avahi_set_nonblock(fd) < 0) {
346         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
347         goto fail;
348     }
349
350     return fd;
351
352 fail:
353     if (fd >= 0)
354         close(fd);
355
356     return -1;
357 }
358
359 int avahi_open_socket_ipv6(int no_reuse) {
360     struct sockaddr_in6 sa, local;
361     int fd = -1, ttl, yes, r;
362
363     mdns_mcast_group_ipv6(&sa);
364         
365     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
366         avahi_log_warn("socket() failed: %s", strerror(errno));
367         goto fail;
368     }
369     
370     ttl = 255;
371     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
372         avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
373         goto fail;
374     }
375
376     ttl = 255;
377     if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
378         avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
379         goto fail;
380     }
381     
382     yes = 1;
383     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
384         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
385         goto fail;
386     }
387
388     yes = 1;
389     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
390         avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
391         goto fail;
392     }
393
394     memset(&local, 0, sizeof(local));
395     local.sin6_family = AF_INET6;
396     local.sin6_port = htons(AVAHI_MDNS_PORT);
397     
398     if (no_reuse)
399         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
400     else
401         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
402
403     if (r < 0)
404         goto fail;
405
406 #ifdef IPV6_RECVHOPS
407     yes = 1;
408     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
409         avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
410         goto fail;
411     }
412 #elif IPV6_RECVHOPLIMIT
413     yes = 1;
414     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
415         avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
416         goto fail;
417     }
418 #elif IPV6_HOPLIMIT
419     yes = 1;
420     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
421         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
422         goto fail;
423     }
424 #endif
425
426 #ifdef IPV6_RECVPKTINFO
427     yes = 1;
428     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
429         avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
430         goto fail;
431     }
432 #elif IPV6_PKTINFO
433     yes = 1;
434     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
435         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
436         goto fail;
437     }
438 #endif
439     
440     if (avahi_set_cloexec(fd) < 0) {
441         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
442         goto fail;
443     }
444     
445     if (avahi_set_nonblock(fd) < 0) {
446         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
447         goto fail;
448     }
449
450     return fd;
451
452 fail:
453     if (fd >= 0)
454         close(fd);
455
456     return -1;
457 }
458
459 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
460     assert(fd >= 0);
461     assert(msg);
462
463     for (;;) {
464     
465         if (sendmsg(fd, msg, flags) >= 0)
466             break;
467         
468         if (errno != EAGAIN) {
469             avahi_log_debug("sendmsg() failed: %s", strerror(errno));
470             return -1;
471         }
472         
473         if (avahi_wait_for_write(fd) < 0)
474             return -1;
475     }
476
477     return 0;
478 }
479
480 int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const AvahiIPv4Address *a, uint16_t port) {
481     struct sockaddr_in sa;
482     struct msghdr msg;
483     struct iovec io;
484     struct cmsghdr *cmsg;
485 #ifdef IP_PKTINFO
486     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
487 #elif IP_RECVIF
488     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct sockaddr_dl)];
489 #elif IP_RECVINTERFACE
490     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(u_short)];
491 #endif
492
493     assert(fd >= 0);
494     assert(p);
495     assert(avahi_dns_packet_check_valid(p) >= 0);
496     assert(!a || port > 0);
497
498     if (!a)
499         mdns_mcast_group_ipv4(&sa);
500     else
501         ipv4_address_to_sockaddr(&sa, a, port);
502
503     memset(&io, 0, sizeof(io));
504     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
505     io.iov_len = p->size;
506
507     memset(&msg, 0, sizeof(msg));
508     msg.msg_name = &sa;
509     msg.msg_namelen = sizeof(sa);
510     msg.msg_iov = &io;
511     msg.msg_iovlen = 1;
512     msg.msg_flags = 0;
513
514     if (interface >= 0) {
515         
516         memset(cmsg_data, 0, sizeof(cmsg_data));
517         cmsg = (struct cmsghdr*) cmsg_data;
518         cmsg->cmsg_len = sizeof(cmsg_data);
519         cmsg->cmsg_level = IPPROTO_IP;
520 #ifdef IP_PKTINFO
521         {
522           struct in_pktinfo *pkti;
523
524           cmsg->cmsg_type = IP_PKTINFO;
525           pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
526           pkti->ipi_ifindex = interface;
527         }
528 #elif IP_RECVIF
529         {
530           struct sockaddr_dl *sdl;
531
532           cmsg->cmsg_type = IP_RECVIF;
533           sdl = (struct sockaddr_dl*) (cmsg_data + sizeof(struct cmsghdr));
534           sdl->sdl_index = interface;
535         }
536 #elif IP_RECVINTERFACE
537         {
538           u_short *i;
539
540           cmsg->cmsg_type = IP_RECVINTERFACE;
541           i = (u_short *) (cmsg_data + sizeof(struct cmsghdr));
542           memcpy(&i, CMSG_DATA (cmsg), sizeof(u_short));
543         }
544 #endif
545         msg.msg_control = cmsg_data;
546         msg.msg_controllen = sizeof(cmsg_data);
547     } else {
548         msg.msg_control = NULL;
549         msg.msg_controllen = 0;
550     }
551
552     return sendmsg_loop(fd, &msg, 0);
553 }
554
555 int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const AvahiIPv6Address *a, uint16_t port) {
556     struct sockaddr_in6 sa;
557     struct msghdr msg;
558     struct iovec io;
559     struct cmsghdr *cmsg;
560     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
561
562     assert(fd >= 0);
563     assert(p);
564     assert(avahi_dns_packet_check_valid(p) >= 0);
565
566     if (!a)
567         mdns_mcast_group_ipv6(&sa);
568     else
569         ipv6_address_to_sockaddr(&sa, a, port);
570
571     memset(&io, 0, sizeof(io));
572     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
573     io.iov_len = p->size;
574
575     
576     memset(&msg, 0, sizeof(msg));
577     msg.msg_name = &sa;
578     msg.msg_namelen = sizeof(sa);
579     msg.msg_iov = &io;
580     msg.msg_iovlen = 1;
581     msg.msg_flags = 0;
582
583     if (interface >= 0) {
584         struct in6_pktinfo *pkti;
585     
586         memset(cmsg_data, 0, sizeof(cmsg_data));
587         cmsg = (struct cmsghdr*) cmsg_data;
588         cmsg->cmsg_len = sizeof(cmsg_data);
589         cmsg->cmsg_level = IPPROTO_IPV6;
590         cmsg->cmsg_type = IPV6_PKTINFO;
591         
592         pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
593         pkti->ipi6_ifindex = interface;
594         
595         msg.msg_control = cmsg_data;
596         msg.msg_controllen = sizeof(cmsg_data);
597     } else {
598         msg.msg_control = NULL;
599         msg.msg_controllen = 0;
600     }
601     
602     return sendmsg_loop(fd, &msg, 0);
603 }
604
605 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
606     AvahiDnsPacket *p= NULL;
607     struct msghdr msg;
608     struct iovec io;
609     uint8_t aux[1024];
610     ssize_t l;
611     struct cmsghdr *cmsg;
612     int found_ttl = 0, found_iface = 0, found_addr = 0;
613     int ms;
614
615     assert(fd >= 0);
616
617     if (ioctl(fd, FIONREAD, &ms) < 0) {
618         avahi_log_warn("ioctl(): %s", strerror(errno));
619         goto fail;
620     }
621
622     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
623
624     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
625     io.iov_len = p->max_size;
626     
627     memset(&msg, 0, sizeof(msg));
628     if (ret_sa) {
629         msg.msg_name = ret_sa;
630         msg.msg_namelen = sizeof(struct sockaddr_in);
631     } else {
632         msg.msg_name = NULL;
633         msg.msg_namelen = 0;
634     }
635     msg.msg_iov = &io;
636     msg.msg_iovlen = 1;
637     msg.msg_control = aux;
638     msg.msg_controllen = sizeof(aux);
639     msg.msg_flags = 0;
640     
641     if ((l = recvmsg(fd, &msg, 0)) < 0) {
642         avahi_log_warn("recvmsg(): %s", strerror(errno));
643         goto fail;
644     }
645
646     if (ret_sa && ret_sa->sin_addr.s_addr == INADDR_ANY) {
647         /* Linux 2.4 behaves very strangely sometimes! */
648
649         /*avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); */
650         goto fail;
651     }
652     
653     assert(!(msg.msg_flags & MSG_CTRUNC));
654     assert(!(msg.msg_flags & MSG_TRUNC));
655     p->size = (size_t) l;
656
657     if (ret_ttl)
658         *ret_ttl = 0;
659
660     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
661
662         if (cmsg->cmsg_level == IPPROTO_IP) {
663           switch (cmsg->cmsg_type) {
664 #ifdef IP_RECVTTL
665           case IP_RECVTTL:
666 #endif
667           case IP_TTL:
668             if (ret_ttl)
669               *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
670             found_ttl = 1;
671             break;
672 #ifdef IP_PKTINFO
673           case IP_PKTINFO:
674             {
675               struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
676               
677               if (ret_iface)
678                 *ret_iface = (int) i->ipi_ifindex;
679               
680               if (ret_dest_address)
681                 ret_dest_address->address = i->ipi_addr.s_addr;
682               found_addr = found_iface = 1;
683             }
684             break;
685 #elif IP_RECVIF
686           case IP_RECVIF:
687             {
688               struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
689
690               if (ret_iface)
691                 *ret_iface = (int) sdl->sdl_index;
692               found_iface = 1;
693             }
694             break;
695 #endif 
696 #ifdef IP_RECVDSTADDR
697           case IP_RECVDSTADDR:
698             if (ret_dest_address)
699               memcpy (&ret_dest_address->address, CMSG_DATA (cmsg), 4);
700             found_addr = 1;
701             break;
702 #endif
703           default:
704             avahi_log_warn("Unhandled cmsg_type : %d",cmsg->cmsg_type);
705             break;
706           }
707         }
708     }
709
710     assert(found_iface);
711     assert(found_addr);
712     assert(found_ttl);
713
714     return p;
715
716 fail:
717     if (p)
718         avahi_dns_packet_free(p);
719
720     return NULL;
721 }
722
723 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa, AvahiIPv6Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
724     AvahiDnsPacket *p = NULL;
725     struct msghdr msg;
726     struct iovec io;
727     uint8_t aux[64];
728     ssize_t l;
729     int ms;
730     
731     struct cmsghdr *cmsg;
732     int found_ttl = 0, found_iface = 0;
733
734     assert(fd >= 0);
735
736     if (ioctl(fd, FIONREAD, &ms) < 0) {
737         avahi_log_warn("ioctl(): %s", strerror(errno));
738         goto fail;
739     }
740     
741     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
742
743     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
744     io.iov_len = p->max_size;
745     
746     memset(&msg, 0, sizeof(msg));
747     if (ret_sa) {
748         msg.msg_name = ret_sa;
749         msg.msg_namelen = sizeof(struct sockaddr_in6);
750     } else {
751         msg.msg_name = NULL;
752         msg.msg_namelen = 0;
753     }
754     
755     msg.msg_iov = &io;
756     msg.msg_iovlen = 1;
757     msg.msg_control = aux;
758     msg.msg_controllen = sizeof(aux);
759     msg.msg_flags = 0;
760     
761     if ((l = recvmsg(fd, &msg, 0)) < 0) {
762         avahi_log_warn("recvmsg(): %s", strerror(errno));
763         goto fail;
764     }
765
766     p->size = (size_t) l;
767
768     if (ret_ttl)
769         *ret_ttl = 0;
770
771     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
772         if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
773
774             if (ret_ttl)
775                 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
776             
777             found_ttl = 1;
778         }
779             
780         if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
781             struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
782
783             if (ret_iface)
784                 *ret_iface = i->ipi6_ifindex;
785
786             if (ret_dest_address)
787                 memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
788             found_iface = 1;
789         }
790     }
791
792     assert(found_iface);
793     assert(found_ttl);
794
795     return p;
796
797 fail:
798     if (p)
799         avahi_dns_packet_free(p);
800
801     return NULL;
802 }
803
804 int avahi_open_unicast_socket_ipv4(void) {
805     struct sockaddr_in local;
806     int fd = -1, yes;
807         
808     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
809         avahi_log_warn("socket() failed: %s", strerror(errno));
810         goto fail;
811     }
812     
813     memset(&local, 0, sizeof(local));
814     local.sin_family = AF_INET;
815     
816     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
817         avahi_log_warn("bind() failed: %s", strerror(errno));
818         goto fail;
819     }
820
821     yes = 1;
822     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
823         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
824         goto fail;
825     }
826
827     yes = 1;
828     if (ip_pktinfo (fd, yes) < 0) {
829          goto fail;
830     }
831
832     if (avahi_set_cloexec(fd) < 0) {
833         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
834         goto fail;
835     }
836     
837     if (avahi_set_nonblock(fd) < 0) {
838         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
839         goto fail;
840     }
841
842     return fd;
843
844 fail:
845     if (fd >= 0)
846         close(fd);
847
848     return -1;
849 }
850
851 int avahi_open_unicast_socket_ipv6(void) {
852     struct sockaddr_in6 local;
853     int fd = -1, yes;
854         
855     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
856         avahi_log_warn("socket() failed: %s", strerror(errno));
857         goto fail;
858     }
859     
860     memset(&local, 0, sizeof(local));
861     local.sin6_family = AF_INET6;
862     
863     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
864         avahi_log_warn("bind() failed: %s", strerror(errno));
865         goto fail;
866     }
867
868 #ifdef IPV6_RECVHOPS
869     yes = 1;
870     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
871         avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
872         goto fail;
873     }
874 #elif IPV6_RECVHOPLIMIT
875     yes = 1;
876     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
877         avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
878         goto fail;
879     }
880 #elif IPV6_HOPLIMIT
881     yes = 1;
882     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
883         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
884         goto fail;
885     }
886 #endif
887
888 #ifdef IPV6_RECVPKTINFO
889     yes = 1;
890     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
891         avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
892         goto fail;
893     }
894 #elif IPV6_PKTINFO
895     yes = 1;
896     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
897         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
898         goto fail;
899     }
900 #endif
901     
902     if (avahi_set_cloexec(fd) < 0) {
903         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
904         goto fail;
905     }
906     
907     if (avahi_set_nonblock(fd) < 0) {
908         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
909         goto fail;
910     }
911
912     return fd;
913
914 fail:
915     if (fd >= 0)
916         close(fd);
917
918     return -1;
919 }