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