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         if (errno != ENOPROTOOPT)
181             return -1;
182     }
183 #endif
184 
185     return 0;
186 }
187 
bind_with_warn(int fd,const struct sockaddr * sa,socklen_t l)188 static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
189 
190     assert(fd >= 0);
191     assert(sa);
192     assert(l > 0);
193 
194     if (bind(fd, sa, l) < 0) {
195 
196         if (errno != EADDRINUSE) {
197             avahi_log_warn("bind() failed: %s", strerror(errno));
198             return -1;
199         }
200 
201         avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
202                        sa->sa_family == AF_INET ? "IPv4" : "IPv6");
203 
204         /* Try again, this time with SO_REUSEADDR set */
205         if (reuseaddr(fd) < 0)
206             return -1;
207 
208         if (bind(fd, sa, l) < 0) {
209             avahi_log_warn("bind() failed: %s", strerror(errno));
210             return -1;
211         }
212     } else {
213 
214         /* We enable SO_REUSEADDR afterwards, to make sure that the
215          * user may run other mDNS implementations if he really
216          * wants. */
217 
218         if (reuseaddr(fd) < 0)
219             return -1;
220     }
221 
222     return 0;
223 }
224 
ipv4_pktinfo(int fd)225 static int ipv4_pktinfo(int fd) {
226     int yes;
227 
228 #ifdef IP_PKTINFO
229     yes = 1;
230     if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
231         avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
232         return -1;
233     }
234 #else
235 
236 #ifdef IP_RECVINTERFACE
237     yes = 1;
238     if (setsockopt(fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) {
239         avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
240         return -1;
241     }
242 #elif defined(IP_RECVIF)
243     yes = 1;
244     if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) {
245         avahi_log_warn("IP_RECVIF failed: %s", strerror(errno));
246         return -1;
247     }
248 #endif
249 
250 #ifdef IP_RECVDSTADDR
251     yes = 1;
252     if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) {
253         avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
254         return -1;
255     }
256 #endif
257 
258 #endif /* IP_PKTINFO */
259 
260 #ifdef IP_RECVTTL
261     yes = 1;
262     if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
263         avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
264         return -1;
265     }
266 #endif
267 
268     return 0;
269 }
270 
ipv6_pktinfo(int fd)271 static int ipv6_pktinfo(int fd) {
272     int yes;
273 
274 #ifdef IPV6_RECVPKTINFO
275     yes = 1;
276     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
277         avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
278         return -1;
279     }
280 #elif defined(IPV6_PKTINFO)
281     yes = 1;
282     if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
283         avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
284         return -1;
285     }
286 #endif
287 
288 #ifdef IPV6_RECVHOPS
289     yes = 1;
290     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
291         avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
292         return -1;
293     }
294 #elif defined(IPV6_RECVHOPLIMIT)
295     yes = 1;
296     if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
297         avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
298         return -1;
299     }
300 #elif defined(IPV6_HOPLIMIT)
301     yes = 1;
302     if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
303         avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
304         return -1;
305     }
306 #endif
307 
308     return 0;
309 }
310 
avahi_open_socket_ipv4(int no_reuse)311 int avahi_open_socket_ipv4(int no_reuse) {
312     struct sockaddr_in local;
313     int fd = -1, r, ittl;
314     uint8_t ttl, cyes;
315 
316     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
317         avahi_log_warn("socket() failed: %s", strerror(errno));
318         goto fail;
319     }
320 
321     ttl = 255;
322     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
323         avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
324         goto fail;
325     }
326 
327     ittl = 255;
328     if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) {
329         avahi_log_warn("IP_TTL failed: %s", strerror(errno));
330         goto fail;
331     }
332 
333     cyes = 1;
334     if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) {
335         avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
336         goto fail;
337     }
338 
339     memset(&local, 0, sizeof(local));
340     local.sin_family = AF_INET;
341     local.sin_port = htons(AVAHI_MDNS_PORT);
342 
343     if (no_reuse)
344         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
345     else
346         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
347 
348     if (r < 0)
349         goto fail;
350 
351     if (ipv4_pktinfo (fd) < 0)
352          goto fail;
353 
354     if (avahi_set_cloexec(fd) < 0) {
355         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
356         goto fail;
357     }
358 
359     if (avahi_set_nonblock(fd) < 0) {
360         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
361         goto fail;
362     }
363 
364     return fd;
365 
366 fail:
367     if (fd >= 0)
368         close(fd);
369 
370     return -1;
371 }
372 
avahi_open_socket_ipv6(int no_reuse)373 int avahi_open_socket_ipv6(int no_reuse) {
374     struct sockaddr_in6 sa, local;
375     int fd = -1, yes, r;
376     int ttl;
377 
378     mdns_mcast_group_ipv6(&sa);
379 
380     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
381         avahi_log_warn("socket() failed: %s", strerror(errno));
382         goto fail;
383     }
384 
385     ttl = 255;
386     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
387         avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
388         goto fail;
389     }
390 
391     ttl = 255;
392     if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
393         avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
394         goto fail;
395     }
396 
397     yes = 1;
398     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
399         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
400         goto fail;
401     }
402 
403     yes = 1;
404     if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
405         avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
406         goto fail;
407     }
408 
409     memset(&local, 0, sizeof(local));
410     local.sin6_family = AF_INET6;
411     local.sin6_port = htons(AVAHI_MDNS_PORT);
412 
413     if (no_reuse)
414         r = bind(fd, (struct sockaddr*) &local, sizeof(local));
415     else
416         r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
417 
418     if (r < 0)
419         goto fail;
420 
421     if (ipv6_pktinfo(fd) < 0)
422         goto fail;
423 
424     if (avahi_set_cloexec(fd) < 0) {
425         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
426         goto fail;
427     }
428 
429     if (avahi_set_nonblock(fd) < 0) {
430         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
431         goto fail;
432     }
433 
434     return fd;
435 
436 fail:
437     if (fd >= 0)
438         close(fd);
439 
440     return -1;
441 }
442 
sendmsg_loop(int fd,struct msghdr * msg,int flags)443 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
444     assert(fd >= 0);
445     assert(msg);
446 
447     for (;;) {
448 
449         if (sendmsg(fd, msg, flags) >= 0)
450             break;
451 
452         if (errno == EINTR)
453             continue;
454 
455         if (errno != EAGAIN) {
456             char where[64];
457             struct sockaddr_storage *ss = msg->msg_name;
458 
459             if (ss->ss_family == PF_INET) {
460                 inet_ntop(ss->ss_family, &((struct sockaddr_in*)ss)->sin_addr, where, sizeof(where));
461             } else if (ss->ss_family == PF_INET6) {
462                 inet_ntop(ss->ss_family, &((struct sockaddr_in6*)ss)->sin6_addr, where, sizeof(where));
463             } else {
464                 where[0] = '\0';
465             }
466 
467             avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno));
468 
469             return -1;
470 
471         }
472 
473         if (avahi_wait_for_write(fd) < 0)
474             return -1;
475     }
476 
477     return 0;
478 }
479 
avahi_send_dns_packet_ipv4(int fd,AvahiIfIndex interface,AvahiDnsPacket * p,const AvahiIPv4Address * src_address,const AvahiIPv4Address * dst_address,uint16_t dst_port)480 int avahi_send_dns_packet_ipv4(
481         int fd,
482         AvahiIfIndex interface,
483         AvahiDnsPacket *p,
484         const AvahiIPv4Address *src_address,
485         const AvahiIPv4Address *dst_address,
486         uint16_t dst_port) {
487 
488     struct sockaddr_in sa;
489     struct msghdr msg;
490     struct iovec io;
491 #ifdef IP_PKTINFO
492     struct cmsghdr *cmsg;
493     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1];
494 #elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR)
495     struct cmsghdr *cmsg;
496     size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1];
497 #endif
498 
499     assert(fd >= 0);
500     assert(p);
501     assert(avahi_dns_packet_check_valid(p) >= 0);
502     assert(!dst_address || dst_port > 0);
503 
504     if (!dst_address)
505         mdns_mcast_group_ipv4(&sa);
506     else
507         ipv4_address_to_sockaddr(&sa, dst_address, dst_port);
508 
509     memset(&io, 0, sizeof(io));
510     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
511     io.iov_len = p->size;
512 
513     memset(&msg, 0, sizeof(msg));
514     msg.msg_name = &sa;
515     msg.msg_namelen = sizeof(sa);
516     msg.msg_iov = &io;
517     msg.msg_iovlen = 1;
518     msg.msg_flags = 0;
519     msg.msg_control = NULL;
520     msg.msg_controllen = 0;
521 
522 #ifdef IP_PKTINFO
523     if (interface > 0 || src_address) {
524         struct in_pktinfo *pkti;
525 
526         memset(cmsg_data, 0, sizeof(cmsg_data));
527         msg.msg_control = cmsg_data;
528         msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
529 
530         cmsg = CMSG_FIRSTHDR(&msg);
531         cmsg->cmsg_len = msg.msg_controllen;
532         cmsg->cmsg_level = IPPROTO_IP;
533         cmsg->cmsg_type = IP_PKTINFO;
534 
535         pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
536 
537         if (interface > 0)
538             pkti->ipi_ifindex = interface;
539 
540         if (src_address)
541             pkti->ipi_spec_dst.s_addr = src_address->address;
542     }
543 #elif defined(IP_MULTICAST_IF)
544     if (src_address) {
545         struct in_addr any = { INADDR_ANY };
546         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) {
547             avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
548             return -1;
549         }
550     }
551 #elif defined(IP_SENDSRCADDR)
552     if (src_address) {
553         struct in_addr *addr;
554 
555         memset(cmsg_data, 0, sizeof(cmsg_data));
556         msg.msg_control = cmsg_data;
557         msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr));
558 
559         cmsg = CMSG_FIRSTHDR(&msg);
560         cmsg->cmsg_len = msg.msg_controllen;
561         cmsg->cmsg_level = IPPROTO_IP;
562         cmsg->cmsg_type = IP_SENDSRCADDR;
563 
564         addr = (struct in_addr *)CMSG_DATA(cmsg);
565         addr->s_addr =  src_address->address;
566     }
567 #elif defined(__GNUC__)
568 #warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
569 #endif
570 
571     return sendmsg_loop(fd, &msg, 0);
572 }
573 
avahi_send_dns_packet_ipv6(int fd,AvahiIfIndex interface,AvahiDnsPacket * p,const AvahiIPv6Address * src_address,const AvahiIPv6Address * dst_address,uint16_t dst_port)574 int avahi_send_dns_packet_ipv6(
575         int fd,
576         AvahiIfIndex interface,
577         AvahiDnsPacket *p,
578         const AvahiIPv6Address *src_address,
579         const AvahiIPv6Address *dst_address,
580         uint16_t dst_port) {
581 
582     struct sockaddr_in6 sa;
583     struct msghdr msg;
584     struct iovec io;
585     struct cmsghdr *cmsg;
586     size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1];
587 
588     assert(fd >= 0);
589     assert(p);
590     assert(avahi_dns_packet_check_valid(p) >= 0);
591     assert(!dst_address || dst_port > 0);
592 
593     if (!dst_address)
594         mdns_mcast_group_ipv6(&sa);
595     else
596         ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
597 
598     memset(&io, 0, sizeof(io));
599     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
600     io.iov_len = p->size;
601 
602     memset(&msg, 0, sizeof(msg));
603     msg.msg_name = &sa;
604     msg.msg_namelen = sizeof(sa);
605     msg.msg_iov = &io;
606     msg.msg_iovlen = 1;
607     msg.msg_flags = 0;
608 
609     if (interface > 0 || src_address) {
610         struct in6_pktinfo *pkti;
611 
612         memset(cmsg_data, 0, sizeof(cmsg_data));
613         msg.msg_control = cmsg_data;
614         msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
615 
616         cmsg = CMSG_FIRSTHDR(&msg);
617         cmsg->cmsg_len = msg.msg_controllen;
618         cmsg->cmsg_level = IPPROTO_IPV6;
619         cmsg->cmsg_type = IPV6_PKTINFO;
620 
621         pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
622 
623         if (interface > 0)
624             pkti->ipi6_ifindex = interface;
625 
626         if (src_address)
627             memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
628     } else {
629         msg.msg_control = NULL;
630         msg.msg_controllen = 0;
631     }
632 
633     return sendmsg_loop(fd, &msg, 0);
634 }
635 
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)636 AvahiDnsPacket *avahi_recv_dns_packet_ipv4(
637         int fd,
638         AvahiIPv4Address *ret_src_address,
639         uint16_t *ret_src_port,
640         AvahiIPv4Address *ret_dst_address,
641         AvahiIfIndex *ret_iface,
642         uint8_t *ret_ttl) {
643 
644     AvahiDnsPacket *p= NULL;
645     struct msghdr msg;
646     struct iovec io;
647     size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
648     ssize_t l;
649     struct cmsghdr *cmsg;
650     int found_addr = 0;
651     int ms;
652     struct sockaddr_in sa;
653 
654     assert(fd >= 0);
655 
656     if (ioctl(fd, FIONREAD, &ms) < 0) {
657         avahi_log_warn("ioctl(): %s", strerror(errno));
658         goto fail;
659     }
660 
661     if (ms < 0) {
662         avahi_log_warn("FIONREAD returned negative value.");
663         goto fail;
664     }
665 
666     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
667 
668     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
669     io.iov_len = p->max_size;
670 
671     memset(&msg, 0, sizeof(msg));
672     msg.msg_name = &sa;
673     msg.msg_namelen = sizeof(sa);
674     msg.msg_iov = &io;
675     msg.msg_iovlen = 1;
676     msg.msg_control = aux;
677     msg.msg_controllen = sizeof(aux);
678     msg.msg_flags = 0;
679 
680     if ((l = recvmsg(fd, &msg, 0)) < 0) {
681         /* Linux returns EAGAIN when an invalid IP packet has been
682         received. We suppress warnings in this case because this might
683         create quite a bit of log traffic on machines with unstable
684         links. (See #60) */
685 
686         if (errno != EAGAIN)
687             avahi_log_warn("recvmsg(): %s", strerror(errno));
688 
689         goto fail;
690     }
691 
692     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
693      * fail after having read them. */
694     if (!ms)
695         goto fail;
696 
697     if (sa.sin_addr.s_addr == INADDR_ANY)
698         /* Linux 2.4 behaves very strangely sometimes! */
699         goto fail;
700 
701     assert(!(msg.msg_flags & MSG_CTRUNC));
702     assert(!(msg.msg_flags & MSG_TRUNC));
703 
704     p->size = (size_t) l;
705 
706     if (ret_src_port)
707         *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
708 
709     if (ret_src_address) {
710         AvahiAddress a;
711         avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
712         *ret_src_address = a.data.ipv4;
713     }
714 
715     if (ret_ttl)
716         *ret_ttl = 255;
717 
718     if (ret_iface)
719         *ret_iface = AVAHI_IF_UNSPEC;
720 
721     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
722 
723         if (cmsg->cmsg_level == IPPROTO_IP) {
724 
725             switch (cmsg->cmsg_type) {
726 #ifdef IP_RECVTTL
727                 case IP_RECVTTL:
728 #endif
729                 case IP_TTL:
730                     if (ret_ttl)
731                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
732 
733                     break;
734 
735 #ifdef IP_PKTINFO
736                 case IP_PKTINFO: {
737                     struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
738 
739                     if (ret_iface && i->ipi_ifindex > 0)
740                         *ret_iface = (int) i->ipi_ifindex;
741 
742                     if (ret_dst_address)
743                         ret_dst_address->address = i->ipi_addr.s_addr;
744 
745                     found_addr = 1;
746 
747                     break;
748                 }
749 #endif
750 
751 #ifdef IP_RECVIF
752                 case IP_RECVIF: {
753                     struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
754 
755                     if (ret_iface) {
756 #ifdef __sun
757                         if (*(uint_t*) sdl > 0)
758                             *ret_iface = *(uint_t*) sdl;
759 #else
760 
761                         if (sdl->sdl_index > 0)
762                             *ret_iface = (int) sdl->sdl_index;
763 #endif
764                     }
765 
766                     break;
767                 }
768 #endif
769 
770 #ifdef IP_RECVDSTADDR
771                 case IP_RECVDSTADDR:
772                     if (ret_dst_address)
773                         memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
774 
775                     found_addr = 1;
776                     break;
777 #endif
778 
779                 default:
780                     avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
781                     break;
782             }
783         }
784     }
785 
786     assert(found_addr);
787 
788     return p;
789 
790 fail:
791     if (p)
792         avahi_dns_packet_free(p);
793 
794     return NULL;
795 }
796 
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)797 AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
798         int fd,
799         AvahiIPv6Address *ret_src_address,
800         uint16_t *ret_src_port,
801         AvahiIPv6Address *ret_dst_address,
802         AvahiIfIndex *ret_iface,
803         uint8_t *ret_ttl) {
804 
805     AvahiDnsPacket *p = NULL;
806     struct msghdr msg;
807     struct iovec io;
808     size_t aux[1024 / sizeof(size_t)];
809     ssize_t l;
810     int ms;
811     struct cmsghdr *cmsg;
812     int found_ttl = 0, found_iface = 0;
813     struct sockaddr_in6 sa;
814 
815     assert(fd >= 0);
816 
817     if (ioctl(fd, FIONREAD, &ms) < 0) {
818         avahi_log_warn("ioctl(): %s", strerror(errno));
819         goto fail;
820     }
821 
822     if (ms < 0) {
823         avahi_log_warn("FIONREAD returned negative value.");
824         goto fail;
825     }
826 
827     p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
828 
829     io.iov_base = AVAHI_DNS_PACKET_DATA(p);
830     io.iov_len = p->max_size;
831 
832     memset(&msg, 0, sizeof(msg));
833     msg.msg_name = (struct sockaddr*) &sa;
834     msg.msg_namelen = sizeof(sa);
835 
836     msg.msg_iov = &io;
837     msg.msg_iovlen = 1;
838     msg.msg_control = aux;
839     msg.msg_controllen = sizeof(aux);
840     msg.msg_flags = 0;
841 
842     if ((l = recvmsg(fd, &msg, 0)) < 0) {
843         /* Linux returns EAGAIN when an invalid IP packet has been
844         received. We suppress warnings in this case because this might
845         create quite a bit of log traffic on machines with unstable
846         links. (See #60) */
847 
848         if (errno != EAGAIN)
849             avahi_log_warn("recvmsg(): %s", strerror(errno));
850 
851         goto fail;
852     }
853 
854     /* For corrupt packets FIONREAD returns zero size (See rhbz #607297). So
855      * fail after having read them. */
856     if (!ms)
857         goto fail;
858 
859     assert(!(msg.msg_flags & MSG_CTRUNC));
860     assert(!(msg.msg_flags & MSG_TRUNC));
861 
862     p->size = (size_t) l;
863 
864     if (ret_src_port)
865         *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
866 
867     if (ret_src_address) {
868         AvahiAddress a;
869         avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
870         *ret_src_address = a.data.ipv6;
871     }
872 
873     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
874 
875         if (cmsg->cmsg_level == IPPROTO_IPV6) {
876 
877             switch (cmsg->cmsg_type) {
878 
879                 case IPV6_HOPLIMIT:
880 
881                     if (ret_ttl)
882                         *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
883 
884                     found_ttl = 1;
885 
886                     break;
887 
888                 case IPV6_PKTINFO: {
889                     struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
890 
891                     if (ret_iface && i->ipi6_ifindex > 0)
892                         *ret_iface = i->ipi6_ifindex;
893 
894                     if (ret_dst_address)
895                         memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
896 
897                     found_iface = 1;
898                     break;
899                 }
900 
901                 default:
902                     avahi_log_warn("Unhandled cmsg_type: %d", cmsg->cmsg_type);
903                     break;
904             }
905         }
906     }
907 
908     assert(found_iface);
909     assert(found_ttl);
910 
911     return p;
912 
913 fail:
914     if (p)
915         avahi_dns_packet_free(p);
916 
917     return NULL;
918 }
919 
avahi_open_unicast_socket_ipv4(void)920 int avahi_open_unicast_socket_ipv4(void) {
921     struct sockaddr_in local;
922     int fd = -1;
923 
924     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
925         avahi_log_warn("socket() failed: %s", strerror(errno));
926         goto fail;
927     }
928 
929     memset(&local, 0, sizeof(local));
930     local.sin_family = AF_INET;
931 
932     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
933         avahi_log_warn("bind() failed: %s", strerror(errno));
934         goto fail;
935     }
936 
937     if (ipv4_pktinfo(fd) < 0) {
938          goto fail;
939     }
940 
941     if (avahi_set_cloexec(fd) < 0) {
942         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
943         goto fail;
944     }
945 
946     if (avahi_set_nonblock(fd) < 0) {
947         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
948         goto fail;
949     }
950 
951     return fd;
952 
953 fail:
954     if (fd >= 0)
955         close(fd);
956 
957     return -1;
958 }
959 
avahi_open_unicast_socket_ipv6(void)960 int avahi_open_unicast_socket_ipv6(void) {
961     struct sockaddr_in6 local;
962     int fd = -1, yes;
963 
964     if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
965         avahi_log_warn("socket() failed: %s", strerror(errno));
966         goto fail;
967     }
968 
969     yes = 1;
970     if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
971         avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
972         goto fail;
973     }
974 
975     memset(&local, 0, sizeof(local));
976     local.sin6_family = AF_INET6;
977 
978     if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
979         avahi_log_warn("bind() failed: %s", strerror(errno));
980         goto fail;
981     }
982 
983     if (ipv6_pktinfo(fd) < 0)
984         goto fail;
985 
986     if (avahi_set_cloexec(fd) < 0) {
987         avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
988         goto fail;
989     }
990 
991     if (avahi_set_nonblock(fd) < 0) {
992         avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
993         goto fail;
994     }
995 
996     return fd;
997 
998 fail:
999     if (fd >= 0)
1000         close(fd);
1001 
1002     return -1;
1003 }
1004