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