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 
mdns_mcast_group_ipv4(struct sockaddr_in * ret_sa)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 
mdns_mcast_group_ipv6(struct sockaddr_in6 * ret_sa)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 
ipv4_address_to_sockaddr(struct sockaddr_in * ret_sa,const AvahiIPv4Address * a,uint16_t port)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 
ipv6_address_to_sockaddr(struct sockaddr_in6 * ret_sa,const AvahiIPv6Address * a,uint16_t port)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 
avahi_mdns_mcast_join_ipv4(int fd,const AvahiIPv4Address * a,int idx,int join)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 
avahi_mdns_mcast_join_ipv6(int fd,const AvahiIPv6Address * a,int idx,int join)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 
reuseaddr(int fd)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 
bind_with_warn(int fd,const struct sockaddr * sa,socklen_t l)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 
ipv4_pktinfo(int fd)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 
ipv6_pktinfo(int fd)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 
avahi_open_socket_ipv4(int no_reuse)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 
avahi_open_socket_ipv6(int no_reuse)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 
sendmsg_loop(int fd,struct msghdr * msg,int flags)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 == EINTR)
452             continue;
453 
454         if (errno != EAGAIN) {
455             char where[64];
456             struct sockaddr_in *sin = msg->msg_name;
457 
458             inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where));
459             avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno));
460             return -1;
461         }
462 
463         if (avahi_wait_for_write(fd) < 0)
464             return -1;
465     }
466 
467     return 0;
468 }
469 
avahi_send_dns_packet_ipv4(int fd,AvahiIfIndex interface,AvahiDnsPacket * p,const AvahiIPv4Address * src_address,const AvahiIPv4Address * dst_address,uint16_t dst_port)470 int avahi_send_dns_packet_ipv4(
471         int fd,
472         AvahiIfIndex interface,
473         AvahiDnsPacket *p,
474         const AvahiIPv4Address *src_address,
475         const AvahiIPv4Address *dst_address,
476         uint16_t dst_port) {
477 
478     struct sockaddr_in sa;
479     struct msghdr msg;
480     struct iovec io;
481 #ifdef IP_PKTINFO
482     struct cmsghdr *cmsg;
483     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
484 #elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR)
485     struct cmsghdr *cmsg;
486     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1];
487 #endif
488 
489     assert(fd >= 0);
490     assert(p);
491     assert(avahi_dns_packet_check_valid(p) >= 0);
492     assert(!dst_address || dst_port > 0);
493 
494     if (!dst_address)
495         mdns_mcast_group_ipv4(&sa);
496     else
497         ipv4_address_to_sockaddr(&sa, dst_address, dst_port);
498 
499     memset(&io, 0, sizeof(io));
500     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
501     io.iov_len = p->size;
502 
503     memset(&msg, 0, sizeof(msg));
504     msg.msg_name = &sa;
505     msg.msg_namelen = sizeof(sa);
506     msg.msg_iov = &io;
507     msg.msg_iovlen = 1;
508     msg.msg_flags = 0;
509     msg.msg_control = NULL;
510     msg.msg_controllen = 0;
511 
512 #ifdef IP_PKTINFO
513     if (interface > 0 || src_address) {
514         struct in_pktinfo *pkti;
515 
516         memset(cmsg_data, 0, sizeof(cmsg_data));
517         msg.msg_control = cmsg_data;
518         msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
519 
520         cmsg = CMSG_FIRSTHDR(&msg);
521         cmsg->cmsg_len = msg.msg_controllen;
522         cmsg->cmsg_level = IPPROTO_IP;
523         cmsg->cmsg_type = IP_PKTINFO;
524 
525         pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
526 
527         if (interface > 0)
528             pkti->ipi_ifindex = interface;
529 
530         if (src_address)
531             pkti->ipi_spec_dst.s_addr = src_address->address;
532     }
533 #elif defined(IP_MULTICAST_IF)
534     if (src_address) {
535         struct in_addr any = { INADDR_ANY };
536         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) {
537             avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
538             return -1;
539         }
540     }
541 #elif defined(IP_SENDSRCADDR)
542     if (src_address) {
543         struct in_addr *addr;
544 
545         memset(cmsg_data, 0, sizeof(cmsg_data));
546         msg.msg_control = cmsg_data;
547         msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr));
548 
549         cmsg = CMSG_FIRSTHDR(&msg);
550         cmsg->cmsg_len = msg.msg_controllen;
551         cmsg->cmsg_level = IPPROTO_IP;
552         cmsg->cmsg_type = IP_SENDSRCADDR;
553 
554         addr = (struct in_addr *)CMSG_DATA(cmsg);
555         addr->s_addr =  src_address->address;
556     }
557 #elif defined(__GNUC__)
558 #warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
559 #endif
560 
561     return sendmsg_loop(fd, &msg, 0);
562 }
563 
avahi_send_dns_packet_ipv6(int fd,AvahiIfIndex interface,AvahiDnsPacket * p,const AvahiIPv6Address * src_address,const AvahiIPv6Address * dst_address,uint16_t dst_port)564 int avahi_send_dns_packet_ipv6(
565         int fd,
566         AvahiIfIndex interface,
567         AvahiDnsPacket *p,
568         const AvahiIPv6Address *src_address,
569         const AvahiIPv6Address *dst_address,
570         uint16_t dst_port) {
571 
572     struct sockaddr_in6 sa;
573     struct msghdr msg;
574     struct iovec io;
575     struct cmsghdr *cmsg;
576     size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
577 
578     assert(fd >= 0);
579     assert(p);
580     assert(avahi_dns_packet_check_valid(p) >= 0);
581     assert(!dst_address || dst_port > 0);
582 
583     if (!dst_address)
584         mdns_mcast_group_ipv6(&sa);
585     else
586         ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
587 
588     memset(&io, 0, sizeof(io));
589     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
590     io.iov_len = p->size;
591 
592     memset(&msg, 0, sizeof(msg));
593     msg.msg_name = &sa;
594     msg.msg_namelen = sizeof(sa);
595     msg.msg_iov = &io;
596     msg.msg_iovlen = 1;
597     msg.msg_flags = 0;
598 
599     if (interface > 0 || src_address) {
600         struct in6_pktinfo *pkti;
601 
602         memset(cmsg_data, 0, sizeof(cmsg_data));
603         msg.msg_control = cmsg_data;
604         msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
605 
606         cmsg = CMSG_FIRSTHDR(&msg);
607         cmsg->cmsg_len = msg.msg_controllen;
608         cmsg->cmsg_level = IPPROTO_IPV6;
609         cmsg->cmsg_type = IPV6_PKTINFO;
610 
611         pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
612 
613         if (interface > 0)
614             pkti->ipi6_ifindex = interface;
615 
616         if (src_address)
617             memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
618     } else {
619         msg.msg_control = NULL;
620         msg.msg_controllen = 0;
621     }
622 
623     return sendmsg_loop(fd, &msg, 0);
624 }
625 
avahi_recv_dns_packet_ipv4(int fd,AvahiIPv4Address * ret_src_address,uint16_t * ret_src_port,AvahiIPv4Address * ret_dst_address,AvahiIfIndex * ret_iface,uint8_t * ret_ttl)626 AvahiDnsPacket *avahi_recv_dns_packet_ipv4(
627         int fd,
628         AvahiIPv4Address *ret_src_address,
629         uint16_t *ret_src_port,
630         AvahiIPv4Address *ret_dst_address,
631         AvahiIfIndex *ret_iface,
632         uint8_t *ret_ttl) {
633 
634     AvahiDnsPacket *p= NULL;
635     struct msghdr msg;
636     struct iovec io;
637     size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
638     ssize_t l;
639     struct cmsghdr *cmsg;
640     int found_addr = 0;
641     int ms;
642     struct sockaddr_in sa;
643 
644     assert(fd >= 0);
645 
646     if (ioctl(fd, FIONREAD, &ms) < 0) {
647         avahi_log_warn("ioctl(): %s", strerror(errno));
648         goto fail;
649     }
650 
651     if (ms < 0) {
652         avahi_log_warn("FIONREAD returned negative value.");
653         goto fail;
654     }
655 
656     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
657 
658     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
659     io.iov_len = p->max_size;
660 
661     memset(&msg, 0, sizeof(msg));
662     msg.msg_name = &sa;
663     msg.msg_namelen = sizeof(sa);
664     msg.msg_iov = &io;
665     msg.msg_iovlen = 1;
666     msg.msg_control = aux;
667     msg.msg_controllen = sizeof(aux);
668     msg.msg_flags = 0;
669 
670     if ((l = recvmsg(fd, &msg, 0)) < 0) {
671         /* Linux returns EAGAIN when an invalid IP packet has been
672         received. We suppress warnings in this case because this might
673         create quite a bit of log traffic on machines with unstable
674         links. (See #60) */
675 
676         if (errno != EAGAIN)
677             avahi_log_warn("recvmsg(): %s", strerror(errno));
678 
679         goto fail;
680     }
681 
682     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
683      * fail after having read them. */
684     if (!ms)
685         goto fail;
686 
687     if (sa.sin_addr.s_addr == INADDR_ANY)
688         /* Linux 2.4 behaves very strangely sometimes! */
689         goto fail;
690 
691     assert(!(msg.msg_flags & MSG_CTRUNC));
692     assert(!(msg.msg_flags & MSG_TRUNC));
693 
694     p->size = (size_t) l;
695 
696     if (ret_src_port)
697         *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
698 
699     if (ret_src_address) {
700         AvahiAddress a;
701         avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
702         *ret_src_address = a.data.ipv4;
703     }
704 
705     if (ret_ttl)
706         *ret_ttl = 255;
707 
708     if (ret_iface)
709         *ret_iface = AVAHI_IF_UNSPEC;
710 
711     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
712 
713         if (cmsg->cmsg_level == IPPROTO_IP) {
714 
715             switch (cmsg->cmsg_type) {
716 #ifdef IP_RECVTTL
717                 case IP_RECVTTL:
718 #endif
719                 case IP_TTL:
720                     if (ret_ttl)
721                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
722 
723                     break;
724 
725 #ifdef IP_PKTINFO
726                 case IP_PKTINFO: {
727                     struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
728 
729                     if (ret_iface && i->ipi_ifindex > 0)
730                         *ret_iface = (int) i->ipi_ifindex;
731 
732                     if (ret_dst_address)
733                         ret_dst_address->address = i->ipi_addr.s_addr;
734 
735                     found_addr = 1;
736 
737                     break;
738                 }
739 #endif
740 
741 #ifdef IP_RECVIF
742                 case IP_RECVIF: {
743                     struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
744 
745                     if (ret_iface) {
746 #ifdef __sun
747                         if (*(uint_t*) sdl > 0)
748                             *ret_iface = *(uint_t*) sdl;
749 #else
750 
751                         if (sdl->sdl_index > 0)
752                             *ret_iface = (int) sdl->sdl_index;
753 #endif
754                     }
755 
756                     break;
757                 }
758 #endif
759 
760 #ifdef IP_RECVDSTADDR
761                 case IP_RECVDSTADDR:
762                     if (ret_dst_address)
763                         memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
764 
765                     found_addr = 1;
766                     break;
767 #endif
768 
769                 default:
770                     avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
771                     break;
772             }
773         }
774     }
775 
776     assert(found_addr);
777 
778     return p;
779 
780 fail:
781     if (p)
782         avahi_dns_packet_free(p);
783 
784     return NULL;
785 }
786 
avahi_recv_dns_packet_ipv6(int fd,AvahiIPv6Address * ret_src_address,uint16_t * ret_src_port,AvahiIPv6Address * ret_dst_address,AvahiIfIndex * ret_iface,uint8_t * ret_ttl)787 AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
788         int fd,
789         AvahiIPv6Address *ret_src_address,
790         uint16_t *ret_src_port,
791         AvahiIPv6Address *ret_dst_address,
792         AvahiIfIndex *ret_iface,
793         uint8_t *ret_ttl) {
794 
795     AvahiDnsPacket *p = NULL;
796     struct msghdr msg;
797     struct iovec io;
798     size_t aux[1024 / sizeof(size_t)];
799     ssize_t l;
800     int ms;
801     struct cmsghdr *cmsg;
802     int found_ttl = 0, found_iface = 0;
803     struct sockaddr_in6 sa;
804 
805     assert(fd >= 0);
806 
807     if (ioctl(fd, FIONREAD, &ms) < 0) {
808         avahi_log_warn("ioctl(): %s", strerror(errno));
809         goto fail;
810     }
811 
812     if (ms < 0) {
813         avahi_log_warn("FIONREAD returned negative value.");
814         goto fail;
815     }
816 
817     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
818 
819     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
820     io.iov_len = p->max_size;
821 
822     memset(&msg, 0, sizeof(msg));
823     msg.msg_name = (struct sockaddr*) &sa;
824     msg.msg_namelen = sizeof(sa);
825 
826     msg.msg_iov = &io;
827     msg.msg_iovlen = 1;
828     msg.msg_control = aux;
829     msg.msg_controllen = sizeof(aux);
830     msg.msg_flags = 0;
831 
832     if ((l = recvmsg(fd, &msg, 0)) < 0) {
833         /* Linux returns EAGAIN when an invalid IP packet has been
834         received. We suppress warnings in this case because this might
835         create quite a bit of log traffic on machines with unstable
836         links. (See #60) */
837 
838         if (errno != EAGAIN)
839             avahi_log_warn("recvmsg(): %s", strerror(errno));
840 
841         goto fail;
842     }
843 
844     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
845      * fail after having read them. */
846     if (!ms)
847         goto fail;
848 
849     assert(!(msg.msg_flags & MSG_CTRUNC));
850     assert(!(msg.msg_flags & MSG_TRUNC));
851 
852     p->size = (size_t) l;
853 
854     if (ret_src_port)
855         *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
856 
857     if (ret_src_address) {
858         AvahiAddress a;
859         avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
860         *ret_src_address = a.data.ipv6;
861     }
862 
863     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
864 
865         if (cmsg->cmsg_level == IPPROTO_IPV6) {
866 
867             switch (cmsg->cmsg_type) {
868 
869                 case IPV6_HOPLIMIT:
870 
871                     if (ret_ttl)
872                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
873 
874                     found_ttl = 1;
875 
876                     break;
877 
878                 case IPV6_PKTINFO: {
879                     struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
880 
881                     if (ret_iface && i->ipi6_ifindex > 0)
882                         *ret_iface = i->ipi6_ifindex;
883 
884                     if (ret_dst_address)
885                         memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
886 
887                     found_iface = 1;
888                     break;
889                 }
890 
891                 default:
892                     avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
893                     break;
894             }
895         }
896     }
897 
898     assert(found_iface);
899     assert(found_ttl);
900 
901     return p;
902 
903 fail:
904     if (p)
905         avahi_dns_packet_free(p);
906 
907     return NULL;
908 }
909 
avahi_open_unicast_socket_ipv4(void)910 int avahi_open_unicast_socket_ipv4(void) {
911     struct sockaddr_in local;
912     int fd = -1;
913 
914     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
915         avahi_log_warn("socket() failed: %s", strerror(errno));
916         goto fail;
917     }
918 
919     memset(&local, 0, sizeof(local));
920     local.sin_family = AF_INET;
921 
922     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
923         avahi_log_warn("bind() failed: %s", strerror(errno));
924         goto fail;
925     }
926 
927     if (ipv4_pktinfo(fd) < 0) {
928          goto fail;
929     }
930 
931     if (avahi_set_cloexec(fd) < 0) {
932         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
933         goto fail;
934     }
935 
936     if (avahi_set_nonblock(fd) < 0) {
937         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
938         goto fail;
939     }
940 
941     return fd;
942 
943 fail:
944     if (fd >= 0)
945         close(fd);
946 
947     return -1;
948 }
949 
avahi_open_unicast_socket_ipv6(void)950 int avahi_open_unicast_socket_ipv6(void) {
951     struct sockaddr_in6 local;
952     int fd = -1, yes;
953 
954     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
955         avahi_log_warn("socket() failed: %s", strerror(errno));
956         goto fail;
957     }
958 
959     yes = 1;
960     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
961         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
962         goto fail;
963     }
964 
965     memset(&local, 0, sizeof(local));
966     local.sin6_family = AF_INET6;
967 
968     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
969         avahi_log_warn("bind() failed: %s", strerror(errno));
970         goto fail;
971     }
972 
973     if (ipv6_pktinfo(fd) < 0)
974         goto fail;
975 
976     if (avahi_set_cloexec(fd) < 0) {
977         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
978         goto fail;
979     }
980 
981     if (avahi_set_nonblock(fd) < 0) {
982         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
983         goto fail;
984     }
985 
986     return fd;
987 
988 fail:
989     if (fd >= 0)
990         close(fd);
991 
992     return -1;
993 }
994