]> git.meshlink.io Git - catta/blob - avahi-core/socket.c
autoipd: don't consider packets with out own MAC as source as conflicting. See #265
[catta] / avahi-core / socket.c
1 /***
2   This file is part of avahi.
3
4   avahi is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   avahi is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <inttypes.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/time.h>
31 #include <sys/ioctl.h>
32 #ifdef HAVE_SYS_FILIO_H
33 #include <sys/filio.h>
34 #endif
35 #include <assert.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <net/if.h>
42 #include <sys/uio.h>
43
44 #ifdef IP_RECVIF
45 #include <net/if_dl.h>
46 #endif
47
48 #include "dns.h"
49 #include "fdutil.h"
50 #include "socket.h"
51 #include "log.h"
52 #include "addr-util.h"
53
54 /* this is a portability hack */
55 #ifndef IPV6_ADD_MEMBERSHIP
56 #ifdef  IPV6_JOIN_GROUP
57 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
58 #endif
59 #endif
60
61 #ifndef IPV6_DROP_MEMBERSHIP
62 #ifdef  IPV6_LEAVE_GROUP
63 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
64 #endif
65 #endif
66
67 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
68     assert(ret_sa);
69
70     memset(ret_sa, 0, sizeof(struct sockaddr_in));
71     ret_sa->sin_family = AF_INET;
72     ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
73     inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
74 }
75
76 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
77     assert(ret_sa);
78
79     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
80     ret_sa->sin6_family = AF_INET6;
81     ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
82     inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
83 }
84
85 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) {
86     assert(ret_sa);
87     assert(a);
88     assert(port > 0);
89
90     memset(ret_sa, 0, sizeof(struct sockaddr_in));
91     ret_sa->sin_family = AF_INET;
92     ret_sa->sin_port = htons(port);
93     memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
94 }
95
96 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) {
97     assert(ret_sa);
98     assert(a);
99     assert(port > 0);
100
101     memset(ret_sa, 0, sizeof(struct sockaddr_in6));
102     ret_sa->sin6_family = AF_INET6;
103     ret_sa->sin6_port = htons(port);
104     memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
105 }
106
107 int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) {
108 #ifdef HAVE_STRUCT_IP_MREQN
109     struct ip_mreqn mreq;
110 #else
111     struct ip_mreq mreq;
112 #endif
113     struct sockaddr_in sa;
114
115     assert(fd >= 0);
116     assert(idx >= 0);
117     assert(a);
118
119     memset(&mreq, 0, sizeof(mreq));
120 #ifdef HAVE_STRUCT_IP_MREQN
121     mreq.imr_ifindex = idx;
122     mreq.imr_address.s_addr = a->address;
123 #else
124     mreq.imr_interface.s_addr = a->address;
125 #endif
126     mdns_mcast_group_ipv4(&sa);
127     mreq.imr_multiaddr = sa.sin_addr;
128
129     /* Some network drivers have issues with dropping membership of
130      * mcast groups when the iface is down, but don't allow rejoining
131      * when it comes back up. This is an ugly workaround */
132     if (join)
133         setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
134
135     if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
136         avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno));
137         return -1;
138     }
139
140     return 0;
141 }
142
143 int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) {
144     struct ipv6_mreq mreq6;
145     struct sockaddr_in6 sa6;
146
147     assert(fd >= 0);
148     assert(idx >= 0);
149     assert(a);
150
151     memset(&mreq6, 0, sizeof(mreq6));
152     mdns_mcast_group_ipv6 (&sa6);
153     mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
154     mreq6.ipv6mr_interface = idx;
155
156     if (join)
157         setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
158
159     if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
160         avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno));
161         return -1;
162     }
163
164     return 0;
165 }
166
167 static int reuseaddr(int fd) {
168     int yes;
169
170     yes = 1;
171     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
172         avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
173         return -1;
174     }
175
176 #ifdef SO_REUSEPORT
177     yes = 1;
178     if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) {
179         avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno));
180         return -1;
181     }
182 #endif
183
184     return 0;
185 }
186
187 static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
188
189     assert(fd >= 0);
190     assert(sa);
191     assert(l > 0);
192
193     if (bind(fd, sa, l) < 0) {
194
195         if (errno != EADDRINUSE) {
196             avahi_log_warn("bind() failed: %s", strerror(errno));
197             return -1;
198         }
199
200         avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
201                        sa->sa_family == AF_INET ? "IPv4" : "IPv6");
202
203         /* Try again, this time with SO_REUSEADDR set */
204         if (reuseaddr(fd) < 0)
205             return -1;
206
207         if (bind(fd, sa, l) < 0) {
208             avahi_log_warn("bind() failed: %s", strerror(errno));
209             return -1;
210         }
211     } else {
212
213         /* We enable SO_REUSEADDR afterwards, to make sure that the
214          * user may run other mDNS implementations if he really
215          * wants. */
216
217         if (reuseaddr(fd) < 0)
218             return -1;
219     }
220
221     return 0;
222 }
223
224 static int ipv4_pktinfo(int fd) {
225     int yes;
226
227 #ifdef IP_PKTINFO
228     yes = 1;
229     if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
230         avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
231         return -1;
232     }
233 #else
234
235 #ifdef IP_RECVINTERFACE
236     yes = 1;
237     if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) {
238         avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
239         return -1;
240     }
241 #elif defined(IP_RECVIF)
242     yes = 1;
243     if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) {
244         avahi_log_warn("IP_RECVIF failed: %s", strerror(errno));
245         return -1;
246     }
247 #endif
248
249 #ifdef IP_RECVDSTADDR
250     yes = 1;
251     if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) {
252         avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
253         return -1;
254     }
255 #endif
256
257 #endif /* IP_PKTINFO */
258
259 #ifdef IP_RECVTTL
260     yes = 1;
261     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
262         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
263         return -1;
264     }
265 #endif
266
267     return 0;
268 }
269
270 static int ipv6_pktinfo(int fd) {
271     int yes;
272
273 #ifdef IPV6_RECVPKTINFO
274     yes = 1;
275     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
276         avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
277         return -1;
278     }
279 #elif defined(IPV6_PKTINFO)
280     yes = 1;
281     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
282         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
283         return -1;
284     }
285 #endif
286
287 #ifdef IPV6_RECVHOPS
288     yes = 1;
289     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
290         avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
291         return -1;
292     }
293 #elif defined(IPV6_RECVHOPLIMIT)
294     yes = 1;
295     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
296         avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
297         return -1;
298     }
299 #elif defined(IPV6_HOPLIMIT)
300     yes = 1;
301     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
302         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
303         return -1;
304     }
305 #endif
306
307     return 0;
308 }
309
310 int avahi_open_socket_ipv4(int no_reuse) {
311     struct sockaddr_in local;
312     int fd = -1, r, ittl;
313     uint8_t ttl, cyes;
314
315     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
316         avahi_log_warn("socket() failed: %s", strerror(errno));
317         goto fail;
318     }
319
320     ttl = 255;
321     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
322         avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
323         goto fail;
324     }
325
326     ittl = 255;
327     if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) {
328         avahi_log_warn("IP_TTL failed: %s", strerror(errno));
329         goto fail;
330     }
331
332     cyes = 1;
333     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) {
334         avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
335         goto fail;
336     }
337
338     memset(&local, 0, sizeof(local));
339     local.sin_family = AF_INET;
340     local.sin_port = htons(AVAHI_MDNS_PORT);
341
342     if (no_reuse)
343         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
344     else
345         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
346
347     if (r < 0)
348         goto fail;
349
350     if (ipv4_pktinfo (fd) < 0)
351          goto fail;
352
353     if (avahi_set_cloexec(fd) < 0) {
354         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
355         goto fail;
356     }
357
358     if (avahi_set_nonblock(fd) < 0) {
359         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
360         goto fail;
361     }
362
363     return fd;
364
365 fail:
366     if (fd >= 0)
367         close(fd);
368
369     return -1;
370 }
371
372 int avahi_open_socket_ipv6(int no_reuse) {
373     struct sockaddr_in6 sa, local;
374     int fd = -1, yes, r;
375     int ttl;
376
377     mdns_mcast_group_ipv6(&sa);
378
379     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
380         avahi_log_warn("socket() failed: %s", strerror(errno));
381         goto fail;
382     }
383
384     ttl = 255;
385     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
386         avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
387         goto fail;
388     }
389
390     ttl = 255;
391     if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
392         avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
393         goto fail;
394     }
395
396     yes = 1;
397     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
398         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
399         goto fail;
400     }
401
402     yes = 1;
403     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
404         avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
405         goto fail;
406     }
407
408     memset(&local, 0, sizeof(local));
409     local.sin6_family = AF_INET6;
410     local.sin6_port = htons(AVAHI_MDNS_PORT);
411
412     if (no_reuse)
413         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
414     else
415         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
416
417     if (r < 0)
418         goto fail;
419
420     if (ipv6_pktinfo(fd) < 0)
421         goto fail;
422
423     if (avahi_set_cloexec(fd) < 0) {
424         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
425         goto fail;
426     }
427
428     if (avahi_set_nonblock(fd) < 0) {
429         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
430         goto fail;
431     }
432
433     return fd;
434
435 fail:
436     if (fd >= 0)
437         close(fd);
438
439     return -1;
440 }
441
442 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
443     assert(fd >= 0);
444     assert(msg);
445
446     for (;;) {
447
448         if (sendmsg(fd, msg, flags) >= 0)
449             break;
450
451         if (errno != EAGAIN) {
452             char where[64];
453             struct sockaddr_in *sin = msg->msg_name;
454
455             inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where));
456             avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno));
457             return -1;
458         }
459
460         if (avahi_wait_for_write(fd) < 0)
461             return -1;
462     }
463
464     return 0;
465 }
466
467 int avahi_send_dns_packet_ipv4(
468         int fd,
469         AvahiIfIndex interface,
470         AvahiDnsPacket *p,
471         const AvahiIPv4Address *src_address,
472         const AvahiIPv4Address *dst_address,
473         uint16_t dst_port) {
474
475     struct sockaddr_in sa;
476     struct msghdr msg;
477     struct iovec io;
478 #ifdef IP_PKTINFO
479     struct cmsghdr *cmsg;
480     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
481 #elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR)
482     struct cmsghdr *cmsg;
483     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1];
484 #endif
485
486     assert(fd >= 0);
487     assert(p);
488     assert(avahi_dns_packet_check_valid(p) >= 0);
489     assert(!dst_address || dst_port > 0);
490
491     if (!dst_address)
492         mdns_mcast_group_ipv4(&sa);
493     else
494         ipv4_address_to_sockaddr(&sa, dst_address, dst_port);
495
496     memset(&io, 0, sizeof(io));
497     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
498     io.iov_len = p->size;
499
500     memset(&msg, 0, sizeof(msg));
501     msg.msg_name = &sa;
502     msg.msg_namelen = sizeof(sa);
503     msg.msg_iov = &io;
504     msg.msg_iovlen = 1;
505     msg.msg_flags = 0;
506     msg.msg_control = NULL;
507     msg.msg_controllen = 0;
508
509 #ifdef IP_PKTINFO
510     if (interface > 0 || src_address) {
511         struct in_pktinfo *pkti;
512
513         memset(cmsg_data, 0, sizeof(cmsg_data));
514         msg.msg_control = cmsg_data;
515         msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
516
517         cmsg = CMSG_FIRSTHDR(&msg);
518         cmsg->cmsg_len = msg.msg_controllen;
519         cmsg->cmsg_level = IPPROTO_IP;
520         cmsg->cmsg_type = IP_PKTINFO;
521
522         pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
523
524         if (interface > 0)
525             pkti->ipi_ifindex = interface;
526
527         if (src_address)
528             pkti->ipi_spec_dst.s_addr = src_address->address;
529     }
530 #elif defined(IP_MULTICAST_IF)
531     if (src_address) {
532         struct in_addr any = { INADDR_ANY };
533         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) {
534             avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
535             return -1;
536         }
537     }
538 #elif defined(IP_SENDSRCADDR)
539     if (src_address) {
540         struct in_addr *addr;
541
542         memset(cmsg_data, 0, sizeof(cmsg_data));
543         msg.msg_control = cmsg_data;
544         msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr));
545
546         cmsg = CMSG_FIRSTHDR(&msg);
547         cmsg->cmsg_len = msg.msg_controllen;
548         cmsg->cmsg_level = IPPROTO_IP;
549         cmsg->cmsg_type = IP_SENDSRCADDR;
550
551         addr = (struct in_addr *)CMSG_DATA(cmsg);
552         addr->s_addr =  src_address->address;
553     }
554 #elif defined(__GNUC__)
555 #warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
556 #endif
557
558     return sendmsg_loop(fd, &msg, 0);
559 }
560
561 int avahi_send_dns_packet_ipv6(
562         int fd,
563         AvahiIfIndex interface,
564         AvahiDnsPacket *p,
565         const AvahiIPv6Address *src_address,
566         const AvahiIPv6Address *dst_address,
567         uint16_t dst_port) {
568
569     struct sockaddr_in6 sa;
570     struct msghdr msg;
571     struct iovec io;
572     struct cmsghdr *cmsg;
573     size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
574
575     assert(fd >= 0);
576     assert(p);
577     assert(avahi_dns_packet_check_valid(p) >= 0);
578     assert(!dst_address || dst_port > 0);
579
580     if (!dst_address)
581         mdns_mcast_group_ipv6(&sa);
582     else
583         ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
584
585     memset(&io, 0, sizeof(io));
586     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
587     io.iov_len = p->size;
588
589     memset(&msg, 0, sizeof(msg));
590     msg.msg_name = &sa;
591     msg.msg_namelen = sizeof(sa);
592     msg.msg_iov = &io;
593     msg.msg_iovlen = 1;
594     msg.msg_flags = 0;
595
596     if (interface > 0 || src_address) {
597         struct in6_pktinfo *pkti;
598
599         memset(cmsg_data, 0, sizeof(cmsg_data));
600         msg.msg_control = cmsg_data;
601         msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
602
603         cmsg = CMSG_FIRSTHDR(&msg);
604         cmsg->cmsg_len = msg.msg_controllen;
605         cmsg->cmsg_level = IPPROTO_IPV6;
606         cmsg->cmsg_type = IPV6_PKTINFO;
607
608         pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
609
610         if (interface > 0)
611             pkti->ipi6_ifindex = interface;
612
613         if (src_address)
614             memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
615     } else {
616         msg.msg_control = NULL;
617         msg.msg_controllen = 0;
618     }
619
620     return sendmsg_loop(fd, &msg, 0);
621 }
622
623 AvahiDnsPacket *avahi_recv_dns_packet_ipv4(
624         int fd,
625         AvahiIPv4Address *ret_src_address,
626         uint16_t *ret_src_port,
627         AvahiIPv4Address *ret_dst_address,
628         AvahiIfIndex *ret_iface,
629         uint8_t *ret_ttl) {
630
631     AvahiDnsPacket *p= NULL;
632     struct msghdr msg;
633     struct iovec io;
634     size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
635     ssize_t l;
636     struct cmsghdr *cmsg;
637     int found_addr = 0;
638     int ms;
639     struct sockaddr_in sa;
640
641     assert(fd >= 0);
642
643     if (ioctl(fd, FIONREAD, &ms) < 0) {
644         avahi_log_warn("ioctl(): %s", strerror(errno));
645         goto fail;
646     }
647
648     if (ms < 0) {
649         avahi_log_warn("FIONREAD returned negative value.");
650         goto fail;
651     }
652
653     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297) */
654     if (!ms)
655         goto fail;
656
657     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
658
659     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
660     io.iov_len = p->max_size;
661
662     memset(&msg, 0, sizeof(msg));
663     msg.msg_name = &sa;
664     msg.msg_namelen = sizeof(sa);
665     msg.msg_iov = &io;
666     msg.msg_iovlen = 1;
667     msg.msg_control = aux;
668     msg.msg_controllen = sizeof(aux);
669     msg.msg_flags = 0;
670
671     if ((l = recvmsg(fd, &msg, 0)) < 0) {
672         /* Linux returns EAGAIN when an invalid IP packet has been
673         received. We suppress warnings in this case because this might
674         create quite a bit of log traffic on machines with unstable
675         links. (See #60) */
676
677         if (errno != EAGAIN)
678             avahi_log_warn("recvmsg(): %s", strerror(errno));
679
680         goto fail;
681     }
682
683     if (sa.sin_addr.s_addr == INADDR_ANY) {
684         /* Linux 2.4 behaves very strangely sometimes! */
685         goto fail;
686     }
687
688     assert(!(msg.msg_flags & MSG_CTRUNC));
689     assert(!(msg.msg_flags & MSG_TRUNC));
690
691     p->size = (size_t) l;
692
693     if (ret_src_port)
694         *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
695
696     if (ret_src_address) {
697         AvahiAddress a;
698         avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
699         *ret_src_address = a.data.ipv4;
700     }
701
702     if (ret_ttl)
703         *ret_ttl = 255;
704
705     if (ret_iface)
706         *ret_iface = AVAHI_IF_UNSPEC;
707
708     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
709
710         if (cmsg->cmsg_level == IPPROTO_IP) {
711
712             switch (cmsg->cmsg_type) {
713 #ifdef IP_RECVTTL
714                 case IP_RECVTTL:
715 #endif
716                 case IP_TTL:
717                     if (ret_ttl)
718                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
719
720                     break;
721
722 #ifdef IP_PKTINFO
723                 case IP_PKTINFO: {
724                     struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
725
726                     if (ret_iface)
727                         *ret_iface = (int) i->ipi_ifindex;
728
729                     if (ret_dst_address)
730                         ret_dst_address->address = i->ipi_addr.s_addr;
731
732                     found_addr = 1;
733
734                     break;
735                 }
736 #endif
737
738 #ifdef IP_RECVIF
739                 case IP_RECVIF: {
740                     struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
741
742                     if (ret_iface)
743 #ifdef __sun
744                         *ret_iface = *(uint_t*) sdl;
745 #else
746                         *ret_iface = (int) sdl->sdl_index;
747 #endif
748
749                     break;
750                 }
751 #endif
752
753 #ifdef IP_RECVDSTADDR
754                 case IP_RECVDSTADDR:
755                     if (ret_dst_address)
756                         memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
757
758                     found_addr = 1;
759                     break;
760 #endif
761
762                 default:
763                     avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type);
764                     break;
765             }
766         }
767     }
768
769     assert(found_addr);
770
771     return p;
772
773 fail:
774     if (p)
775         avahi_dns_packet_free(p);
776
777     return NULL;
778 }
779
780 AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
781         int fd,
782         AvahiIPv6Address *ret_src_address,
783         uint16_t *ret_src_port,
784         AvahiIPv6Address *ret_dst_address,
785         AvahiIfIndex *ret_iface,
786         uint8_t *ret_ttl) {
787
788     AvahiDnsPacket *p = NULL;
789     struct msghdr msg;
790     struct iovec io;
791     size_t aux[1024 / sizeof(size_t)];
792     ssize_t l;
793     int ms;
794     struct cmsghdr *cmsg;
795     int found_ttl = 0, found_iface = 0;
796     struct sockaddr_in6 sa;
797
798     assert(fd >= 0);
799
800     if (ioctl(fd, FIONREAD, &ms) < 0) {
801         avahi_log_warn("ioctl(): %s", strerror(errno));
802         goto fail;
803     }
804
805     if (ms < 0) {
806         avahi_log_warn("FIONREAD returned negative value.");
807         goto fail;
808     }
809
810     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297) */
811     if (!ms)
812         goto fail;
813
814     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
815
816     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
817     io.iov_len = p->max_size;
818
819     memset(&msg, 0, sizeof(msg));
820     msg.msg_name = (struct sockaddr*) &sa;
821     msg.msg_namelen = sizeof(sa);
822
823     msg.msg_iov = &io;
824     msg.msg_iovlen = 1;
825     msg.msg_control = aux;
826     msg.msg_controllen = sizeof(aux);
827     msg.msg_flags = 0;
828
829     if ((l = recvmsg(fd, &msg, 0)) < 0) {
830         /* Linux returns EAGAIN when an invalid IP packet has been
831         received. We suppress warnings in this case because this might
832         create quite a bit of log traffic on machines with unstable
833         links. (See #60) */
834
835         if (errno != EAGAIN)
836             avahi_log_warn("recvmsg(): %s", strerror(errno));
837
838         goto fail;
839     }
840
841     assert(!(msg.msg_flags & MSG_CTRUNC));
842     assert(!(msg.msg_flags & MSG_TRUNC));
843
844     p->size = (size_t) l;
845
846     if (ret_src_port)
847         *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
848
849     if (ret_src_address) {
850         AvahiAddress a;
851         avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
852         *ret_src_address = a.data.ipv6;
853     }
854
855     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
856
857         if (cmsg->cmsg_level == IPPROTO_IPV6) {
858
859             switch (cmsg->cmsg_type) {
860
861                 case IPV6_HOPLIMIT:
862
863                     if (ret_ttl)
864                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
865
866                     found_ttl = 1;
867
868                     break;
869
870                 case IPV6_PKTINFO: {
871                     struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
872
873                     if (ret_iface)
874                         *ret_iface = i->ipi6_ifindex;
875
876                     if (ret_dst_address)
877                         memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
878
879                     found_iface = 1;
880                     break;
881                 }
882
883                 default:
884                     avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type);
885                     break;
886             }
887         }
888     }
889
890     assert(found_iface);
891     assert(found_ttl);
892
893     return p;
894
895 fail:
896     if (p)
897         avahi_dns_packet_free(p);
898
899     return NULL;
900 }
901
902 int avahi_open_unicast_socket_ipv4(void) {
903     struct sockaddr_in local;
904     int fd = -1;
905
906     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
907         avahi_log_warn("socket() failed: %s", strerror(errno));
908         goto fail;
909     }
910
911     memset(&local, 0, sizeof(local));
912     local.sin_family = AF_INET;
913
914     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
915         avahi_log_warn("bind() failed: %s", strerror(errno));
916         goto fail;
917     }
918
919     if (ipv4_pktinfo(fd) < 0) {
920          goto fail;
921     }
922
923     if (avahi_set_cloexec(fd) < 0) {
924         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
925         goto fail;
926     }
927
928     if (avahi_set_nonblock(fd) < 0) {
929         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
930         goto fail;
931     }
932
933     return fd;
934
935 fail:
936     if (fd >= 0)
937         close(fd);
938
939     return -1;
940 }
941
942 int avahi_open_unicast_socket_ipv6(void) {
943     struct sockaddr_in6 local;
944     int fd = -1, yes;
945
946     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
947         avahi_log_warn("socket() failed: %s", strerror(errno));
948         goto fail;
949     }
950
951     yes = 1;
952     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
953         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
954         goto fail;
955     }
956
957     memset(&local, 0, sizeof(local));
958     local.sin6_family = AF_INET6;
959
960     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
961         avahi_log_warn("bind() failed: %s", strerror(errno));
962         goto fail;
963     }
964
965     if (ipv6_pktinfo(fd) < 0)
966         goto fail;
967
968     if (avahi_set_cloexec(fd) < 0) {
969         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
970         goto fail;
971     }
972
973     if (avahi_set_nonblock(fd) < 0) {
974         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
975         goto fail;
976     }
977
978     return fd;
979
980 fail:
981     if (fd >= 0)
982         close(fd);
983
984     return -1;
985 }