1 /*** 2 This file is part of avahi. 3 4 avahi is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 2.1 of the 7 License, or (at your option) any later version. 8 9 avahi is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 12 Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with avahi; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 17 USA. 18 ***/ 19 20 #ifdef HAVE_CONFIG_H 21 #include <config.h> 22 #endif 23 24 #include <inttypes.h> 25 #include <errno.h> 26 #include <string.h> 27 #include <stdio.h> 28 #include <unistd.h> 29 #include <fcntl.h> 30 #include <sys/time.h> 31 #include <sys/ioctl.h> 32 #ifdef HAVE_SYS_FILIO_H 33 #include <sys/filio.h> 34 #endif 35 #include <assert.h> 36 37 #include <sys/types.h> 38 #include <sys/socket.h> 39 #include <netinet/in.h> 40 #include <arpa/inet.h> 41 #include <net/if.h> 42 #include <sys/uio.h> 43 44 #ifdef IP_RECVIF 45 #include <net/if_dl.h> 46 #endif 47 48 #include "dns.h" 49 #include "fdutil.h" 50 #include "socket.h" 51 #include "log.h" 52 #include "addr-util.h" 53 54 /* this is a portability hack */ 55 #ifndef IPV6_ADD_MEMBERSHIP 56 #ifdef IPV6_JOIN_GROUP 57 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP 58 #endif 59 #endif 60 61 #ifndef IPV6_DROP_MEMBERSHIP 62 #ifdef IPV6_LEAVE_GROUP 63 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP 64 #endif 65 #endif 66 67 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { 68 assert(ret_sa); 69 70 memset(ret_sa, 0, sizeof(struct sockaddr_in)); 71 ret_sa->sin_family = AF_INET; 72 ret_sa->sin_port = htons(AVAHI_MDNS_PORT); 73 inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr); 74 } 75 76 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { 77 assert(ret_sa); 78 79 memset(ret_sa, 0, sizeof(struct sockaddr_in6)); 80 ret_sa->sin6_family = AF_INET6; 81 ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); 82 inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr); 83 } 84 85 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) { 86 assert(ret_sa); 87 assert(a); 88 assert(port > 0); 89 90 memset(ret_sa, 0, sizeof(struct sockaddr_in)); 91 ret_sa->sin_family = AF_INET; 92 ret_sa->sin_port = htons(port); 93 memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address)); 94 } 95 96 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) { 97 assert(ret_sa); 98 assert(a); 99 assert(port > 0); 100 101 memset(ret_sa, 0, sizeof(struct sockaddr_in6)); 102 ret_sa->sin6_family = AF_INET6; 103 ret_sa->sin6_port = htons(port); 104 memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address)); 105 } 106 107 int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { 108 #ifdef HAVE_STRUCT_IP_MREQN 109 struct ip_mreqn mreq; 110 #else 111 struct ip_mreq mreq; 112 #endif 113 struct sockaddr_in sa; 114 115 assert(fd >= 0); 116 assert(idx >= 0); 117 assert(a); 118 119 memset(&mreq, 0, sizeof(mreq)); 120 #ifdef HAVE_STRUCT_IP_MREQN 121 mreq.imr_ifindex = idx; 122 mreq.imr_address.s_addr = a->address; 123 #else 124 mreq.imr_interface.s_addr = a->address; 125 #endif 126 mdns_mcast_group_ipv4(&sa); 127 mreq.imr_multiaddr = sa.sin_addr; 128 129 /* Some network drivers have issues with dropping membership of 130 * mcast groups when the iface is down, but don't allow rejoining 131 * when it comes back up. This is an ugly workaround */ 132 if (join) 133 setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); 134 135 if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { 136 avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno)); 137 return -1; 138 } 139 140 return 0; 141 } 142 143 int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) { 144 struct ipv6_mreq mreq6; 145 struct sockaddr_in6 sa6; 146 147 assert(fd >= 0); 148 assert(idx >= 0); 149 assert(a); 150 151 memset(&mreq6, 0, sizeof(mreq6)); 152 mdns_mcast_group_ipv6 (&sa6); 153 mreq6.ipv6mr_multiaddr = sa6.sin6_addr; 154 mreq6.ipv6mr_interface = idx; 155 156 if (join) 157 setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); 158 159 if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { 160 avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno)); 161 return -1; 162 } 163 164 return 0; 165 } 166 167 static int reuseaddr(int fd) { 168 int yes; 169 170 yes = 1; 171 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { 172 avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno)); 173 return -1; 174 } 175 176 #ifdef SO_REUSEPORT 177 yes = 1; 178 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { 179 avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno)); 180 return -1; 181 } 182 #endif 183 184 return 0; 185 } 186 187 static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { 188 189 assert(fd >= 0); 190 assert(sa); 191 assert(l > 0); 192 193 if (bind(fd, sa, l) < 0) { 194 195 if (errno != EADDRINUSE) { 196 avahi_log_warn("bind() failed: %s", strerror(errno)); 197 return -1; 198 } 199 200 avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***", 201 sa->sa_family == AF_INET ? "IPv4" : "IPv6"); 202 203 /* Try again, this time with SO_REUSEADDR set */ 204 if (reuseaddr(fd) < 0) 205 return -1; 206 207 if (bind(fd, sa, l) < 0) { 208 avahi_log_warn("bind() failed: %s", strerror(errno)); 209 return -1; 210 } 211 } else { 212 213 /* We enable SO_REUSEADDR afterwards, to make sure that the 214 * user may run other mDNS implementations if he really 215 * wants. */ 216 217 if (reuseaddr(fd) < 0) 218 return -1; 219 } 220 221 return 0; 222 } 223 224 static int ipv4_pktinfo(int fd) { 225 int yes; 226 227 #ifdef IP_PKTINFO 228 yes = 1; 229 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { 230 avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno)); 231 return -1; 232 } 233 #else 234 235 #ifdef IP_RECVINTERFACE 236 yes = 1; 237 if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) { 238 avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno)); 239 return -1; 240 } 241 #elif defined(IP_RECVIF) 242 yes = 1; 243 if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) { 244 avahi_log_warn("IP_RECVIF failed: %s", strerror(errno)); 245 return -1; 246 } 247 #endif 248 249 #ifdef IP_RECVDSTADDR 250 yes = 1; 251 if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) { 252 avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno)); 253 return -1; 254 } 255 #endif 256 257 #endif /* IP_PKTINFO */ 258 259 #ifdef IP_RECVTTL 260 yes = 1; 261 if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { 262 avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno)); 263 return -1; 264 } 265 #endif 266 267 return 0; 268 } 269 270 static int ipv6_pktinfo(int fd) { 271 int yes; 272 273 #ifdef IPV6_RECVPKTINFO 274 yes = 1; 275 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) { 276 avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno)); 277 return -1; 278 } 279 #elif defined(IPV6_PKTINFO) 280 yes = 1; 281 if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { 282 avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno)); 283 return -1; 284 } 285 #endif 286 287 #ifdef IPV6_RECVHOPS 288 yes = 1; 289 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) { 290 avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno)); 291 return -1; 292 } 293 #elif defined(IPV6_RECVHOPLIMIT) 294 yes = 1; 295 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) { 296 avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno)); 297 return -1; 298 } 299 #elif defined(IPV6_HOPLIMIT) 300 yes = 1; 301 if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { 302 avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno)); 303 return -1; 304 } 305 #endif 306 307 return 0; 308 } 309 310 int avahi_open_socket_ipv4(int no_reuse) { 311 struct sockaddr_in local; 312 int fd = -1, r, ittl; 313 uint8_t ttl, cyes; 314 315 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 316 avahi_log_warn("socket() failed: %s", strerror(errno)); 317 goto fail; 318 } 319 320 ttl = 255; 321 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { 322 avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno)); 323 goto fail; 324 } 325 326 ittl = 255; 327 if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) { 328 avahi_log_warn("IP_TTL failed: %s", strerror(errno)); 329 goto fail; 330 } 331 332 cyes = 1; 333 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) { 334 avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno)); 335 goto fail; 336 } 337 338 memset(&local, 0, sizeof(local)); 339 local.sin_family = AF_INET; 340 local.sin_port = htons(AVAHI_MDNS_PORT); 341 342 if (no_reuse) 343 r = bind(fd, (struct sockaddr*) &local, sizeof(local)); 344 else 345 r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); 346 347 if (r < 0) 348 goto fail; 349 350 if (ipv4_pktinfo (fd) < 0) 351 goto fail; 352 353 if (avahi_set_cloexec(fd) < 0) { 354 avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); 355 goto fail; 356 } 357 358 if (avahi_set_nonblock(fd) < 0) { 359 avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); 360 goto fail; 361 } 362 363 return fd; 364 365 fail: 366 if (fd >= 0) 367 close(fd); 368 369 return -1; 370 } 371 372 int avahi_open_socket_ipv6(int no_reuse) { 373 struct sockaddr_in6 sa, local; 374 int fd = -1, yes, r; 375 int ttl; 376 377 mdns_mcast_group_ipv6(&sa); 378 379 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 380 avahi_log_warn("socket() failed: %s", strerror(errno)); 381 goto fail; 382 } 383 384 ttl = 255; 385 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { 386 avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno)); 387 goto fail; 388 } 389 390 ttl = 255; 391 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { 392 avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno)); 393 goto fail; 394 } 395 396 yes = 1; 397 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { 398 avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno)); 399 goto fail; 400 } 401 402 yes = 1; 403 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { 404 avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno)); 405 goto fail; 406 } 407 408 memset(&local, 0, sizeof(local)); 409 local.sin6_family = AF_INET6; 410 local.sin6_port = htons(AVAHI_MDNS_PORT); 411 412 if (no_reuse) 413 r = bind(fd, (struct sockaddr*) &local, sizeof(local)); 414 else 415 r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); 416 417 if (r < 0) 418 goto fail; 419 420 if (ipv6_pktinfo(fd) < 0) 421 goto fail; 422 423 if (avahi_set_cloexec(fd) < 0) { 424 avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); 425 goto fail; 426 } 427 428 if (avahi_set_nonblock(fd) < 0) { 429 avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); 430 goto fail; 431 } 432 433 return fd; 434 435 fail: 436 if (fd >= 0) 437 close(fd); 438 439 return -1; 440 } 441 442 static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { 443 assert(fd >= 0); 444 assert(msg); 445 446 for (;;) { 447 448 if (sendmsg(fd, msg, flags) >= 0) 449 break; 450 451 if (errno == 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 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 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 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 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 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 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