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