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