]> git.meshlink.io Git - catta/blob - avahi-core/socket.c
* Update configure.ac to check for the availabilty of more functions
[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
39 #include "dns.h"
40 #include "util.h"
41 #include "socket.h"
42 #include "log.h"
43
44 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
45     g_assert(ret_sa);
46
47     memset(ret_sa, 0, sizeof(struct sockaddr_in));
48     
49     ret_sa->sin_family = AF_INET;
50     ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
51     inet_pton(AF_INET, "224.0.0.251", &ret_sa->sin_addr);
52 }
53
54 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
55
56     g_assert(ret_sa);
57
58     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
59     
60     ret_sa->sin6_family = AF_INET6;
61     ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
62     inet_pton(AF_INET6, "ff02::fb", &ret_sa->sin6_addr);
63 }
64
65 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, guint16 port) {
66     g_assert(ret_sa);
67     g_assert(a);
68     g_assert(port > 0);
69
70     memset(ret_sa, 0, sizeof(struct sockaddr_in));
71     ret_sa->sin_family = AF_INET;
72     ret_sa->sin_port = htons(port);
73     memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
74 }
75
76 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, guint16 port) {
77     g_assert(ret_sa);
78     g_assert(a);
79     g_assert(port > 0);
80
81     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
82     ret_sa->sin6_family = AF_INET6;
83     ret_sa->sin6_port = htons(port);
84     memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
85 }
86
87 int avahi_mdns_mcast_join_ipv4 (int index, int fd) {
88     struct ip_mreqn mreq; 
89     struct sockaddr_in sa;
90
91     mdns_mcast_group_ipv4 (&sa);
92  
93     memset(&mreq, 0, sizeof(mreq));
94     mreq.imr_multiaddr = sa.sin_addr;
95     mreq.imr_ifindex = index;
96  
97     if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
98         avahi_log_warn("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
99         return -1;
100     } 
101
102     return 0;
103 }
104
105 int avahi_mdns_mcast_join_ipv6 (int index, int fd) {
106     struct ipv6_mreq mreq6; 
107     struct sockaddr_in6 sa6;
108
109     mdns_mcast_group_ipv6 (&sa6);
110
111     memset(&mreq6, 0, sizeof(mreq6));
112     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
113     mreq6.ipv6mr_interface = index;
114
115     if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
116         avahi_log_warn("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
117         return -1;
118     }
119
120     return 0;
121 }
122
123 int avahi_mdns_mcast_leave_ipv4 (int index, int fd) {
124     struct ip_mreqn mreq; 
125     struct sockaddr_in sa;
126     
127     mdns_mcast_group_ipv4 (&sa);
128  
129     memset(&mreq, 0, sizeof(mreq));
130     mreq.imr_multiaddr = sa.sin_addr;
131     mreq.imr_ifindex = index;
132  
133     if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
134         avahi_log_warn("IP_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
135         return -1;
136     }
137
138     return 0;
139 }
140
141 int avahi_mdns_mcast_leave_ipv6 (int index, int fd) {
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 = index;
150
151     if (setsockopt(fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
152         avahi_log_warn("IPV6_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
153         return -1;
154     }
155
156     return 0;
157 }
158
159 static gint bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
160     gint yes;
161     
162     g_assert(fd >= 0);
163     g_assert(sa);
164     g_assert(l > 0);
165     
166     if (bind(fd, sa, l) < 0) {
167
168         if (errno != EADDRINUSE) {
169             avahi_log_warn("bind() failed: %s\n", strerror(errno));
170             return -1;
171         }
172             
173         avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
174                        sa->sa_family == AF_INET ? "IPv4" : "IPv6");
175
176         /* Try again, this time with SO_REUSEADDR set */
177         yes = 1;
178         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
179             avahi_log_warn("SO_REUSEADDR failed: %s\n", strerror(errno));
180             return -1;
181         }
182
183         if (bind(fd, sa, l) < 0) {
184             avahi_log_warn("bind() failed: %s\n", strerror(errno));
185             return -1;
186         }
187     } else {
188
189         /* We enable SO_REUSEADDR afterwards, to make sure that the
190          * user may run other mDNS implementations if he really
191          * wants. */
192         
193         yes = 1;
194         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
195             avahi_log_warn("SO_REUSEADDR failed: %s\n", strerror(errno));
196             return -1;
197         }
198     }
199
200     return 0;
201 }
202
203 gint avahi_open_socket_ipv4(void) {
204     struct sockaddr_in local;
205     int fd = -1, ttl, yes;
206         
207     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
208         avahi_log_warn("socket() failed: %s\n", strerror(errno));
209         goto fail;
210     }
211     
212     ttl = 255;
213     if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
214         avahi_log_warn("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
215         goto fail;
216     }
217
218     ttl = 255;
219     if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
220         avahi_log_warn("IP_TTL failed: %s\n", strerror(errno));
221         goto fail;
222     }
223     
224     yes = 1;
225     if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
226         avahi_log_warn("IP_MULTICAST_LOOP failed: %s\n", strerror(errno));
227         goto fail;
228     }
229     
230     memset(&local, 0, sizeof(local));
231     local.sin_family = AF_INET;
232     local.sin_port = htons(AVAHI_MDNS_PORT);
233
234     if (bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)) < 0)
235         goto fail;
236
237     yes = 1;
238     if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
239         avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
240         goto fail;
241     }
242
243     yes = 1;
244     if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
245         avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
246         goto fail;
247     }
248     
249     if (avahi_set_cloexec(fd) < 0) {
250         avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
251         goto fail;
252     }
253     
254     if (avahi_set_nonblock(fd) < 0) {
255         avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
256         goto fail;
257     }
258
259     return fd;
260
261 fail:
262     if (fd >= 0)
263         close(fd);
264
265     return -1;
266 }
267
268 gint avahi_open_socket_ipv6(void) {
269     struct sockaddr_in6 sa, local;
270     int fd = -1, ttl, yes;
271
272     mdns_mcast_group_ipv6(&sa);
273         
274     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
275         avahi_log_warn("socket() failed: %s\n", strerror(errno));
276         goto fail;
277     }
278     
279     ttl = 255;
280     if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
281         avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
282         goto fail;
283     }
284
285     ttl = 255;
286     if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
287         avahi_log_warn("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
288         goto fail;
289     }
290     
291     yes = 1;
292     if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
293         avahi_log_warn("IPV6_V6ONLY failed: %s\n", strerror(errno));
294         goto fail;
295     }
296
297     yes = 1;
298     if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
299         avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno));
300         goto fail;
301     }
302
303     memset(&local, 0, sizeof(local));
304     local.sin6_family = AF_INET6;
305     local.sin6_port = htons(AVAHI_MDNS_PORT);
306     
307     if (bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)) < 0)
308         goto fail;
309
310     yes = 1;
311     if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
312         avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
313         goto fail;
314     }
315
316     yes = 1;
317     if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
318         avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
319         goto fail;
320     }
321     
322     if (avahi_set_cloexec(fd) < 0) {
323         avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
324         goto fail;
325     }
326     
327     if (avahi_set_nonblock(fd) < 0) {
328         avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
329         goto fail;
330     }
331
332     return fd;
333
334 fail:
335     if (fd >= 0)
336         close(fd);
337
338     return -1;
339 }
340
341 static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
342     g_assert(fd >= 0);
343     g_assert(msg);
344
345     for (;;) {
346     
347         if (sendmsg(fd, msg, flags) >= 0)
348             break;
349         
350         if (errno != EAGAIN) {
351             avahi_log_debug("sendmsg() failed: %s\n", strerror(errno));
352             return -1;
353         }
354         
355         if (avahi_wait_for_write(fd) < 0)
356             return -1;
357     }
358
359     return 0;
360 }
361
362 gint avahi_send_dns_packet_ipv4(gint fd, gint interface, AvahiDnsPacket *p, const AvahiIPv4Address *a, guint16 port) {
363     struct sockaddr_in sa;
364     struct msghdr msg;
365     struct iovec io;
366     struct cmsghdr *cmsg;
367     struct in_pktinfo *pkti;
368     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
369
370     g_assert(fd >= 0);
371     g_assert(p);
372     g_assert(avahi_dns_packet_check_valid(p) >= 0);
373     g_assert(!a || port > 0);
374
375     if (!a)
376         mdns_mcast_group_ipv4(&sa);
377     else
378         ipv4_address_to_sockaddr(&sa, a, port);
379
380     memset(&io, 0, sizeof(io));
381     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
382     io.iov_len = p->size;
383
384     memset(cmsg_data, 0, sizeof(cmsg_data));
385     cmsg = (struct cmsghdr*) cmsg_data;
386     cmsg->cmsg_len = sizeof(cmsg_data);
387     cmsg->cmsg_level = IPPROTO_IP;
388     cmsg->cmsg_type = IP_PKTINFO;
389
390     pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
391     pkti->ipi_ifindex = interface;
392     
393     memset(&msg, 0, sizeof(msg));
394     msg.msg_name = &sa;
395     msg.msg_namelen = sizeof(sa);
396     msg.msg_iov = &io;
397     msg.msg_iovlen = 1;
398     msg.msg_control = cmsg_data;
399     msg.msg_controllen = sizeof(cmsg_data);
400     msg.msg_flags = 0;
401
402     return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
403 }
404
405 gint avahi_send_dns_packet_ipv6(gint fd, gint interface, AvahiDnsPacket *p, const AvahiIPv6Address *a, guint16 port) {
406     struct sockaddr_in6 sa;
407     struct msghdr msg;
408     struct iovec io;
409     struct cmsghdr *cmsg;
410     struct in6_pktinfo *pkti;
411     uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
412
413     g_assert(fd >= 0);
414     g_assert(p);
415     g_assert(avahi_dns_packet_check_valid(p) >= 0);
416
417     if (!a)
418         mdns_mcast_group_ipv6(&sa);
419     else
420         ipv6_address_to_sockaddr(&sa, a, port);
421
422     memset(&io, 0, sizeof(io));
423     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
424     io.iov_len = p->size;
425
426     memset(cmsg_data, 0, sizeof(cmsg_data));
427     cmsg = (struct cmsghdr*) cmsg_data;
428     cmsg->cmsg_len = sizeof(cmsg_data);
429     cmsg->cmsg_level = IPPROTO_IPV6;
430     cmsg->cmsg_type = IPV6_PKTINFO;
431
432     pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
433     pkti->ipi6_ifindex = interface;
434     
435     memset(&msg, 0, sizeof(msg));
436     msg.msg_name = &sa;
437     msg.msg_namelen = sizeof(sa);
438     msg.msg_iov = &io;
439     msg.msg_iovlen = 1;
440     msg.msg_control = cmsg_data;
441     msg.msg_controllen = sizeof(cmsg_data);
442     msg.msg_flags = 0;
443
444     return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
445 }
446
447 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
448     AvahiDnsPacket *p= NULL;
449     struct msghdr msg;
450     struct iovec io;
451     uint8_t aux[1024];
452     ssize_t l;
453     struct cmsghdr *cmsg;
454     gboolean found_ttl = FALSE, found_iface = FALSE;
455     gint ms;
456
457     g_assert(fd >= 0);
458     g_assert(ret_sa);
459     g_assert(ret_iface);
460     g_assert(ret_ttl);
461
462     if (ioctl(fd, FIONREAD, &ms) < 0) {
463         avahi_log_warn("ioctl(): %s", strerror(errno));
464         goto fail;
465     }
466
467     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
468
469     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
470     io.iov_len = p->max_size;
471     
472     memset(&msg, 0, sizeof(msg));
473     msg.msg_name = ret_sa;
474     msg.msg_namelen = sizeof(struct sockaddr_in);
475     msg.msg_iov = &io;
476     msg.msg_iovlen = 1;
477     msg.msg_control = aux;
478     msg.msg_controllen = sizeof(aux);
479     msg.msg_flags = 0;
480     
481     if ((l = recvmsg(fd, &msg, 0)) < 0) {
482         avahi_log_warn("recvmsg(): %s", strerror(errno));
483         goto fail;
484     }
485
486     if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
487         /* Linux 2.4 behaves very strangely sometimes! */
488
489         avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); 
490         
491         
492         goto fail;
493     }
494     
495     g_assert(!(msg.msg_flags & MSG_CTRUNC));
496     g_assert(!(msg.msg_flags & MSG_TRUNC));
497     p->size = (size_t) l;
498     
499     *ret_ttl = 0;
500
501 /*     avahi_hexdump(msg.msg_control, msg.msg_controllen); */
502         
503     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
504
505 /*         avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
506         
507         if (cmsg->cmsg_level == SOL_IP) {
508             
509             if (cmsg->cmsg_type == IP_TTL) {
510                 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
511                 found_ttl = TRUE;
512             } else if (cmsg->cmsg_type == IP_PKTINFO) {
513                 *ret_iface = (gint) ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
514                 found_iface = TRUE;
515             }
516         }
517     }
518
519 /*     avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
520
521     g_assert(found_iface);
522     g_assert(found_ttl);
523
524     return p;
525
526 fail:
527     if (p)
528         avahi_dns_packet_free(p);
529
530     return NULL;
531 }
532
533 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
534     AvahiDnsPacket *p = NULL;
535     struct msghdr msg;
536     struct iovec io;
537     uint8_t aux[64];
538     ssize_t l;
539     gint ms;
540     
541     struct cmsghdr *cmsg;
542     gboolean found_ttl = FALSE, found_iface = FALSE;
543
544     g_assert(fd >= 0);
545     g_assert(ret_sa);
546     g_assert(ret_iface);
547     g_assert(ret_ttl);
548
549     if (ioctl(fd, FIONREAD, &ms) < 0) {
550         avahi_log_warn("ioctl(): %s", strerror(errno));
551         goto fail;
552     }
553     
554     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
555
556     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
557     io.iov_len = p->max_size;
558     
559     memset(&msg, 0, sizeof(msg));
560     msg.msg_name = ret_sa;
561     msg.msg_namelen = sizeof(struct sockaddr_in6);
562     msg.msg_iov = &io;
563     msg.msg_iovlen = 1;
564     msg.msg_control = aux;
565     msg.msg_controllen = sizeof(aux);
566     msg.msg_flags = 0;
567     
568     if ((l = recvmsg(fd, &msg, 0)) < 0) {
569         avahi_log_warn("recvmsg(): %s", strerror(errno));
570         goto fail;
571     }
572
573     p->size = (size_t) l;
574     
575     *ret_ttl = 0;
576
577     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
578         if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
579             *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
580             found_ttl = TRUE;
581         }
582             
583         if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
584             *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex;
585             found_iface = TRUE;
586         }
587     }
588
589     g_assert(found_iface);
590     g_assert(found_ttl);
591
592     return p;
593
594 fail:
595     if (p)
596         avahi_dns_packet_free(p);
597
598     return NULL;
599 }
600
601 gint avahi_open_legacy_unicast_socket_ipv4(void) {
602     struct sockaddr_in local;
603     int fd = -1, yes;
604         
605     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
606         avahi_log_warn("socket() failed: %s\n", strerror(errno));
607         goto fail;
608     }
609     
610     memset(&local, 0, sizeof(local));
611     local.sin_family = AF_INET;
612     
613     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
614         avahi_log_warn("bind() failed: %s\n", strerror(errno));
615         goto fail;
616     }
617
618     yes = 1;
619     if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
620         avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
621         goto fail;
622     }
623
624     yes = 1;
625     if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
626         avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
627         goto fail;
628     }
629     
630     if (avahi_set_cloexec(fd) < 0) {
631         avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
632         goto fail;
633     }
634     
635     if (avahi_set_nonblock(fd) < 0) {
636         avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
637         goto fail;
638     }
639
640     return fd;
641
642 fail:
643     if (fd >= 0)
644         close(fd);
645
646     return -1;
647 }
648
649 gint avahi_open_legacy_unicast_socket_ipv6(void) {
650     struct sockaddr_in local;
651     int fd = -1, yes;
652         
653     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
654         avahi_log_warn("socket() failed: %s\n", strerror(errno));
655         goto fail;
656     }
657     
658     memset(&local, 0, sizeof(local));
659     local.sin_family = AF_INET;
660     
661     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
662         avahi_log_warn("bind() failed: %s\n", strerror(errno));
663         goto fail;
664     }
665
666     yes = 1;
667     if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
668         avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
669         goto fail;
670     }
671
672     yes = 1;
673     if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
674         avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
675         goto fail;
676     }
677     
678     if (avahi_set_cloexec(fd) < 0) {
679         avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
680         goto fail;
681     }
682     
683     if (avahi_set_nonblock(fd) < 0) {
684         avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
685         goto fail;
686     }
687
688     return fd;
689
690 fail:
691     if (fd >= 0)
692         close(fd);
693
694     return -1;
695 }