]> git.meshlink.io Git - catta/blob - avahi-core/socket.c
* handle cmsg_type IP_RECVTTL
[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("Can't find iface name for iface index %i:%s\n", idx, 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("Can't find iface name for iface index %i:%s\n", idx, 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     yes = 1;
407     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
408         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
409         goto fail;
410     }
411
412     yes = 1;
413     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
414         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
415         goto fail;
416     }
417     
418     if (avahi_set_cloexec(fd) < 0) {
419         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
420         goto fail;
421     }
422     
423     if (avahi_set_nonblock(fd) < 0) {
424         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
425         goto fail;
426     }
427
428     return fd;
429
430 fail:
431     if (fd >= 0)
432         close(fd);
433
434     return -1;
435 }
436
437 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
438     assert(fd >= 0);
439     assert(msg);
440
441     for (;;) {
442     
443         if (sendmsg(fd, msg, flags) >= 0)
444             break;
445         
446         if (errno != EAGAIN) {
447             avahi_log_debug("sendmsg() failed: %s", strerror(errno));
448             return -1;
449         }
450         
451         if (avahi_wait_for_write(fd) < 0)
452             return -1;
453     }
454
455     return 0;
456 }
457
458 int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const AvahiIPv4Address *a, uint16_t port) {
459     struct sockaddr_in sa;
460     struct msghdr msg;
461     struct iovec io;
462     struct cmsghdr *cmsg;
463 #ifdef IP_PKTINFO
464     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
465 #elif IP_RECVIF
466     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct sockaddr_dl)];
467 #elif IP_RECVINTERFACE
468     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(u_short)];
469 #endif
470
471     assert(fd >= 0);
472     assert(p);
473     assert(avahi_dns_packet_check_valid(p) >= 0);
474     assert(!a || port > 0);
475
476     if (!a)
477         mdns_mcast_group_ipv4(&sa);
478     else
479         ipv4_address_to_sockaddr(&sa, a, port);
480
481     memset(&io, 0, sizeof(io));
482     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
483     io.iov_len = p->size;
484
485     memset(&msg, 0, sizeof(msg));
486     msg.msg_name = &sa;
487     msg.msg_namelen = sizeof(sa);
488     msg.msg_iov = &io;
489     msg.msg_iovlen = 1;
490     msg.msg_flags = 0;
491
492     if (interface >= 0) {
493         
494         memset(cmsg_data, 0, sizeof(cmsg_data));
495         cmsg = (struct cmsghdr*) cmsg_data;
496         cmsg->cmsg_len = sizeof(cmsg_data);
497         cmsg->cmsg_level = IPPROTO_IP;
498 #ifdef IP_PKTINFO
499         {
500           struct in_pktinfo *pkti;
501
502           cmsg->cmsg_type = IP_PKTINFO;
503           pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
504           pkti->ipi_ifindex = interface;
505         }
506 #elif IP_RECVIF
507         {
508           struct sockaddr_dl *pkti;
509
510           cmsg->cmsg_type = IP_RECVIF;
511           pkti = (struct sockaddr_dl*) (cmsg_data + sizeof(struct cmsghdr));
512           pkti->sdl_index = interface;
513         }
514 #elif IP_RECVINTERFACE
515         {
516           u_short *i;
517
518           cmsg->cmsg_type = IP_RECVINTERFACE;
519           i = (u_short *) (cmsg_data + sizeof(u_short));
520           memcpy(&i, CMSG_DATA (cmsg), sizeof(u_short));
521         }
522 #endif
523         msg.msg_control = cmsg_data;
524         msg.msg_controllen = sizeof(cmsg_data);
525     } else {
526         msg.msg_control = NULL;
527         msg.msg_controllen = 0;
528     }
529
530     return sendmsg_loop(fd, &msg, 0);
531 }
532
533 int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const AvahiIPv6Address *a, uint16_t port) {
534     struct sockaddr_in6 sa;
535     struct msghdr msg;
536     struct iovec io;
537     struct cmsghdr *cmsg;
538     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
539
540     assert(fd >= 0);
541     assert(p);
542     assert(avahi_dns_packet_check_valid(p) >= 0);
543
544     if (!a)
545         mdns_mcast_group_ipv6(&sa);
546     else
547         ipv6_address_to_sockaddr(&sa, a, port);
548
549     memset(&io, 0, sizeof(io));
550     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
551     io.iov_len = p->size;
552
553     
554     memset(&msg, 0, sizeof(msg));
555     msg.msg_name = &sa;
556     msg.msg_namelen = sizeof(sa);
557     msg.msg_iov = &io;
558     msg.msg_iovlen = 1;
559     msg.msg_flags = 0;
560
561     if (interface >= 0) {
562         struct in6_pktinfo *pkti;
563     
564         memset(cmsg_data, 0, sizeof(cmsg_data));
565         cmsg = (struct cmsghdr*) cmsg_data;
566         cmsg->cmsg_len = sizeof(cmsg_data);
567         cmsg->cmsg_level = IPPROTO_IPV6;
568         cmsg->cmsg_type = IPV6_PKTINFO;
569         
570         pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
571         pkti->ipi6_ifindex = interface;
572         
573         msg.msg_control = cmsg_data;
574         msg.msg_controllen = sizeof(cmsg_data);
575     } else {
576         msg.msg_control = NULL;
577         msg.msg_controllen = 0;
578     }
579     
580     return sendmsg_loop(fd, &msg, 0);
581 }
582
583 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
584     AvahiDnsPacket *p= NULL;
585     struct msghdr msg;
586     struct iovec io;
587     uint8_t aux[1024];
588     ssize_t l;
589     struct cmsghdr *cmsg;
590     int found_ttl = 0, found_iface = 0, found_addr = 0;
591     int ms;
592
593     assert(fd >= 0);
594
595     if (ioctl(fd, FIONREAD, &ms) < 0) {
596         avahi_log_warn("ioctl(): %s", strerror(errno));
597         goto fail;
598     }
599
600     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
601
602     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
603     io.iov_len = p->max_size;
604     
605     memset(&msg, 0, sizeof(msg));
606     if (ret_sa) {
607         msg.msg_name = ret_sa;
608         msg.msg_namelen = sizeof(struct sockaddr_in);
609     } else {
610         msg.msg_name = NULL;
611         msg.msg_namelen = 0;
612     }
613     msg.msg_iov = &io;
614     msg.msg_iovlen = 1;
615     msg.msg_control = aux;
616     msg.msg_controllen = sizeof(aux);
617     msg.msg_flags = 0;
618     
619     if ((l = recvmsg(fd, &msg, 0)) < 0) {
620         avahi_log_warn("recvmsg(): %s", strerror(errno));
621         goto fail;
622     }
623
624     if (ret_sa && ret_sa->sin_addr.s_addr == INADDR_ANY) {
625         /* Linux 2.4 behaves very strangely sometimes! */
626
627         /*avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); */
628         goto fail;
629     }
630     
631     assert(!(msg.msg_flags & MSG_CTRUNC));
632     assert(!(msg.msg_flags & MSG_TRUNC));
633     p->size = (size_t) l;
634
635     if (ret_ttl)
636         *ret_ttl = 0;
637
638     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
639
640         if (cmsg->cmsg_level == IPPROTO_IP) {
641           switch (cmsg->cmsg_type) {
642 #ifdef IP_RECVTTL
643           case IP_RECVTTL:
644 #endif
645           case IP_TTL:
646             if (ret_ttl)
647               *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
648             found_ttl = 1;
649             break;
650 #ifdef IP_PKTINFO
651           case IP_PKTINFO:
652             {
653               struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
654               
655               if (ret_iface)
656                 *ret_iface = (int) i->ipi_ifindex;
657               
658               if (ret_dest_address)
659                 ret_dest_address->address = i->ipi_addr.s_addr;
660               found_addr = found_iface = 1;
661             }
662             break;
663 #elif IP_RECVIF
664           case IP_RECVIF:
665             {
666               struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
667
668               if (ret_iface)
669                 *ret_iface = (int) sdl->sdl_index;
670               found_iface = 1;
671             }
672             break;
673 #endif 
674 #ifdef IP_RECVDSTADDR
675           case IP_RECVDSTADDR:
676             if (ret_dest_address)
677               memcpy (&ret_dest_address->address, CMSG_DATA (cmsg), 4);
678             found_addr = 1;
679             break;
680 #endif
681           default:
682             avahi_log_warn("Unhandled cmsg_type : %d\n",cmsg->cmsg_type);
683             break;
684           }
685         }
686     }
687
688     assert(found_iface);
689     assert(found_addr);
690     assert(found_ttl);
691
692     return p;
693
694 fail:
695     if (p)
696         avahi_dns_packet_free(p);
697
698     return NULL;
699 }
700
701 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa, AvahiIPv6Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
702     AvahiDnsPacket *p = NULL;
703     struct msghdr msg;
704     struct iovec io;
705     uint8_t aux[64];
706     ssize_t l;
707     int ms;
708     
709     struct cmsghdr *cmsg;
710     int found_ttl = 0, found_iface = 0;
711
712     assert(fd >= 0);
713
714     if (ioctl(fd, FIONREAD, &ms) < 0) {
715         avahi_log_warn("ioctl(): %s", strerror(errno));
716         goto fail;
717     }
718     
719     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
720
721     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
722     io.iov_len = p->max_size;
723     
724     memset(&msg, 0, sizeof(msg));
725     if (ret_sa) {
726         msg.msg_name = ret_sa;
727         msg.msg_namelen = sizeof(struct sockaddr_in6);
728     } else {
729         msg.msg_name = NULL;
730         msg.msg_namelen = 0;
731     }
732     
733     msg.msg_iov = &io;
734     msg.msg_iovlen = 1;
735     msg.msg_control = aux;
736     msg.msg_controllen = sizeof(aux);
737     msg.msg_flags = 0;
738     
739     if ((l = recvmsg(fd, &msg, 0)) < 0) {
740         avahi_log_warn("recvmsg(): %s", strerror(errno));
741         goto fail;
742     }
743
744     p->size = (size_t) l;
745
746     if (ret_ttl)
747         *ret_ttl = 0;
748
749     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
750         if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
751
752             if (ret_ttl)
753                 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
754             
755             found_ttl = 1;
756         }
757             
758         if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
759             struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
760
761             if (ret_iface)
762                 *ret_iface = i->ipi6_ifindex;
763
764             if (ret_dest_address)
765                 memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
766             found_iface = 1;
767         }
768     }
769
770     assert(found_iface);
771     assert(found_ttl);
772
773     return p;
774
775 fail:
776     if (p)
777         avahi_dns_packet_free(p);
778
779     return NULL;
780 }
781
782 int avahi_open_unicast_socket_ipv4(void) {
783     struct sockaddr_in local;
784     int fd = -1, yes;
785         
786     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
787         avahi_log_warn("socket() failed: %s", strerror(errno));
788         goto fail;
789     }
790     
791     memset(&local, 0, sizeof(local));
792     local.sin_family = AF_INET;
793     
794     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
795         avahi_log_warn("bind() failed: %s", strerror(errno));
796         goto fail;
797     }
798
799     yes = 1;
800     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
801         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
802         goto fail;
803     }
804
805     yes = 1;
806     if (ip_pktinfo (fd, yes) < 0) {
807          goto fail;
808     }
809
810     if (avahi_set_cloexec(fd) < 0) {
811         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
812         goto fail;
813     }
814     
815     if (avahi_set_nonblock(fd) < 0) {
816         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
817         goto fail;
818     }
819
820     return fd;
821
822 fail:
823     if (fd >= 0)
824         close(fd);
825
826     return -1;
827 }
828
829 int avahi_open_unicast_socket_ipv6(void) {
830     struct sockaddr_in6 local;
831     int fd = -1, yes;
832         
833     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
834         avahi_log_warn("socket() failed: %s", strerror(errno));
835         goto fail;
836     }
837     
838     memset(&local, 0, sizeof(local));
839     local.sin6_family = AF_INET6;
840     
841     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
842         avahi_log_warn("bind() failed: %s", strerror(errno));
843         goto fail;
844     }
845
846     yes = 1;
847     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
848         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
849         goto fail;
850     }
851
852     yes = 1;
853     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
854         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
855         goto fail;
856     }
857     
858     if (avahi_set_cloexec(fd) < 0) {
859         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
860         goto fail;
861     }
862     
863     if (avahi_set_nonblock(fd) < 0) {
864         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
865         goto fail;
866     }
867
868     return fd;
869
870 fail:
871     if (fd >= 0)
872         close(fd);
873
874     return -1;
875 }