]> git.meshlink.io Git - catta/blob - avahi-core/socket.c
improve documentation
[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, AVAHI_IPV4_MCAST_GROUP, &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, AVAHI_IPV6_MCAST_GROUP, &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, 0 /*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, 0 /*MSG_DONTROUTE*/);
445 }
446
447 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, 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_dest_address);
460     g_assert(ret_iface);
461     g_assert(ret_ttl);
462
463     if (ioctl(fd, FIONREAD, &ms) < 0) {
464         avahi_log_warn("ioctl(): %s", strerror(errno));
465         goto fail;
466     }
467
468     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
469
470     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
471     io.iov_len = p->max_size;
472     
473     memset(&msg, 0, sizeof(msg));
474     msg.msg_name = ret_sa;
475     msg.msg_namelen = sizeof(struct sockaddr_in);
476     msg.msg_iov = &io;
477     msg.msg_iovlen = 1;
478     msg.msg_control = aux;
479     msg.msg_controllen = sizeof(aux);
480     msg.msg_flags = 0;
481     
482     if ((l = recvmsg(fd, &msg, 0)) < 0) {
483         avahi_log_warn("recvmsg(): %s", strerror(errno));
484         goto fail;
485     }
486
487     if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
488         /* Linux 2.4 behaves very strangely sometimes! */
489
490         avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); 
491         goto fail;
492     }
493     
494     g_assert(!(msg.msg_flags & MSG_CTRUNC));
495     g_assert(!(msg.msg_flags & MSG_TRUNC));
496     p->size = (size_t) l;
497     
498     *ret_ttl = 0;
499
500 /*     avahi_hexdump(msg.msg_control, msg.msg_controllen); */
501         
502     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
503
504 /*         avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
505         
506         if (cmsg->cmsg_level == SOL_IP) {
507             
508             if (cmsg->cmsg_type == IP_TTL) {
509                 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
510                 found_ttl = TRUE;
511             } else if (cmsg->cmsg_type == IP_PKTINFO) {
512                 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
513                 *ret_iface = (gint) i->ipi_ifindex;
514                 ret_dest_address->address = i->ipi_addr.s_addr;
515                 found_iface = TRUE;
516             }
517         }
518     }
519
520 /*     avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
521
522     g_assert(found_iface);
523     g_assert(found_ttl);
524
525     return p;
526
527 fail:
528     if (p)
529         avahi_dns_packet_free(p);
530
531     return NULL;
532 }
533
534 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, AvahiIPv6Address *ret_dest_address, gint *ret_iface, guint8* ret_ttl) {
535     AvahiDnsPacket *p = NULL;
536     struct msghdr msg;
537     struct iovec io;
538     uint8_t aux[64];
539     ssize_t l;
540     gint ms;
541     
542     struct cmsghdr *cmsg;
543     gboolean found_ttl = FALSE, found_iface = FALSE;
544
545     g_assert(fd >= 0);
546     g_assert(ret_sa);
547     g_assert(ret_dest_address);
548     g_assert(ret_iface);
549     g_assert(ret_ttl);
550
551     if (ioctl(fd, FIONREAD, &ms) < 0) {
552         avahi_log_warn("ioctl(): %s", strerror(errno));
553         goto fail;
554     }
555     
556     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
557
558     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
559     io.iov_len = p->max_size;
560     
561     memset(&msg, 0, sizeof(msg));
562     msg.msg_name = ret_sa;
563     msg.msg_namelen = sizeof(struct sockaddr_in6);
564     msg.msg_iov = &io;
565     msg.msg_iovlen = 1;
566     msg.msg_control = aux;
567     msg.msg_controllen = sizeof(aux);
568     msg.msg_flags = 0;
569     
570     if ((l = recvmsg(fd, &msg, 0)) < 0) {
571         avahi_log_warn("recvmsg(): %s", strerror(errno));
572         goto fail;
573     }
574
575     p->size = (size_t) l;
576     
577     *ret_ttl = 0;
578
579     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
580         if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
581             *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
582             found_ttl = TRUE;
583         }
584             
585         if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
586             struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
587             *ret_iface = i->ipi6_ifindex;
588             memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
589             found_iface = TRUE;
590         }
591     }
592
593     g_assert(found_iface);
594     g_assert(found_ttl);
595
596     return p;
597
598 fail:
599     if (p)
600         avahi_dns_packet_free(p);
601
602     return NULL;
603 }
604
605 gint avahi_open_legacy_unicast_socket_ipv4(void) {
606     struct sockaddr_in local;
607     int fd = -1, yes;
608         
609     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
610         avahi_log_warn("socket() failed: %s\n", strerror(errno));
611         goto fail;
612     }
613     
614     memset(&local, 0, sizeof(local));
615     local.sin_family = AF_INET;
616     
617     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
618         avahi_log_warn("bind() failed: %s\n", strerror(errno));
619         goto fail;
620     }
621
622     yes = 1;
623     if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
624         avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
625         goto fail;
626     }
627
628     yes = 1;
629     if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
630         avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
631         goto fail;
632     }
633     
634     if (avahi_set_cloexec(fd) < 0) {
635         avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
636         goto fail;
637     }
638     
639     if (avahi_set_nonblock(fd) < 0) {
640         avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
641         goto fail;
642     }
643
644     return fd;
645
646 fail:
647     if (fd >= 0)
648         close(fd);
649
650     return -1;
651 }
652
653 gint avahi_open_legacy_unicast_socket_ipv6(void) {
654     struct sockaddr_in local;
655     int fd = -1, yes;
656         
657     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
658         avahi_log_warn("socket() failed: %s\n", strerror(errno));
659         goto fail;
660     }
661     
662     memset(&local, 0, sizeof(local));
663     local.sin_family = AF_INET;
664     
665     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
666         avahi_log_warn("bind() failed: %s\n", strerror(errno));
667         goto fail;
668     }
669
670     yes = 1;
671     if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
672         avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
673         goto fail;
674     }
675
676     yes = 1;
677     if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
678         avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
679         goto fail;
680     }
681     
682     if (avahi_set_cloexec(fd) < 0) {
683         avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
684         goto fail;
685     }
686     
687     if (avahi_set_nonblock(fd) < 0) {
688         avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
689         goto fail;
690     }
691
692     return fd;
693
694 fail:
695     if (fd >= 0)
696         close(fd);
697
698     return -1;
699 }