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