1/* $Id$ */ 2 3/*** 4 This file is part of avahi. 5 6 avahi is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 avahi is distributed in the hope that it will be useful, but WITHOUT 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 14 Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with avahi; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 19 USA. 20***/ 21 22#ifdef HAVE_CONFIG_H 23#include <config.h> 24#endif 25 26#include <inttypes.h> 27#include <errno.h> 28#include <string.h> 29#include <stdio.h> 30#include <unistd.h> 31#include <fcntl.h> 32#include <sys/time.h> 33#include <sys/ioctl.h> 34#ifdef HAVE_SYS_FILIO_H 35#include <sys/filio.h> 36#endif 37#include <assert.h> 38 39#include <sys/types.h> 40#include <sys/socket.h> 41#include <netinet/in.h> 42#include <arpa/inet.h> 43#include <net/if.h> 44#include <sys/uio.h> 45 46#ifdef IP_RECVIF 47#include <net/if_dl.h> 48#endif 49 50#include "dns.h" 51#include "fdutil.h" 52#include "socket.h" 53#include "log.h" 54#include "addr-util.h" 55 56/* this is a portability hack */ 57#ifndef IPV6_ADD_MEMBERSHIP 58#ifdef IPV6_JOIN_GROUP 59#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP 60#endif 61#endif 62 63#ifndef IPV6_DROP_MEMBERSHIP 64#ifdef IPV6_LEAVE_GROUP 65#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP 66#endif 67#endif 68 69static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) { 70 assert(ret_sa); 71 72 memset(ret_sa, 0, sizeof(struct sockaddr_in)); 73 ret_sa->sin_family = AF_INET; 74 ret_sa->sin_port = htons(AVAHI_MDNS_PORT); 75 inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr); 76} 77 78static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) { 79 assert(ret_sa); 80 81 memset(ret_sa, 0, sizeof(struct sockaddr_in6)); 82 ret_sa->sin6_family = AF_INET6; 83 ret_sa->sin6_port = htons(AVAHI_MDNS_PORT); 84 inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr); 85} 86 87static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) { 88 assert(ret_sa); 89 assert(a); 90 assert(port > 0); 91 92 memset(ret_sa, 0, sizeof(struct sockaddr_in)); 93 ret_sa->sin_family = AF_INET; 94 ret_sa->sin_port = htons(port); 95 memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address)); 96} 97 98static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) { 99 assert(ret_sa); 100 assert(a); 101 assert(port > 0); 102 103 memset(ret_sa, 0, sizeof(struct sockaddr_in6)); 104 ret_sa->sin6_family = AF_INET6; 105 ret_sa->sin6_port = htons(port); 106 memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address)); 107} 108 109int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) { 110#ifdef HAVE_STRUCT_IP_MREQN 111 struct ip_mreqn mreq; 112#else 113 struct ip_mreq mreq; 114#endif 115 struct sockaddr_in sa; 116 117 assert(fd >= 0); 118 assert(idx >= 0); 119 assert(a); 120 121 memset(&mreq, 0, sizeof(mreq)); 122#ifdef HAVE_STRUCT_IP_MREQN 123 mreq.imr_ifindex = idx; 124 mreq.imr_address.s_addr = a->address; 125#else 126 mreq.imr_interface.s_addr = a->address; 127#endif 128 mdns_mcast_group_ipv4(&sa); 129 mreq.imr_multiaddr = sa.sin_addr; 130 131 /* Some network drivers have issues with dropping membership of 132 * mcast groups when the iface is down, but don't allow rejoining 133 * when it comes back up. This is an ugly workaround */ 134 if (join) 135 setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); 136 137 if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { 138 avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno)); 139 return -1; 140 } 141 142 return 0; 143} 144 145int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) { 146 struct ipv6_mreq mreq6; 147 struct sockaddr_in6 sa6; 148 149 assert(fd >= 0); 150 assert(idx >= 0); 151 assert(a); 152 153 memset(&mreq6, 0, sizeof(mreq6)); 154 mdns_mcast_group_ipv6 (&sa6); 155 mreq6.ipv6mr_multiaddr = sa6.sin6_addr; 156 mreq6.ipv6mr_interface = idx; 157 158 if (join) 159 setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); 160 161 if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { 162 avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno)); 163 return -1; 164 } 165 166 return 0; 167} 168 169static int reuseaddr(int fd) { 170 int yes; 171 172 yes = 1; 173 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { 174 avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno)); 175 return -1; 176 } 177 178#ifdef SO_REUSEPORT 179 yes = 1; 180 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) { 181 avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno)); 182 return -1; 183 } 184#endif 185 186 return 0; 187} 188 189static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) { 190 191 assert(fd >= 0); 192 assert(sa); 193 assert(l > 0); 194 195 if (bind(fd, sa, l) < 0) { 196 197 if (errno != EADDRINUSE) { 198 avahi_log_warn("bind() failed: %s", strerror(errno)); 199 return -1; 200 } 201 202 avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***", 203 sa->sa_family == AF_INET ? "IPv4" : "IPv6"); 204 205 /* Try again, this time with SO_REUSEADDR set */ 206 if (reuseaddr(fd) < 0) 207 return -1; 208 209 if (bind(fd, sa, l) < 0) { 210 avahi_log_warn("bind() failed: %s", strerror(errno)); 211 return -1; 212 } 213 } else { 214 215 /* We enable SO_REUSEADDR afterwards, to make sure that the 216 * user may run other mDNS implementations if he really 217 * wants. */ 218 219 if (reuseaddr(fd) < 0) 220 return -1; 221 } 222 223 return 0; 224} 225 226static int ipv4_pktinfo(int fd) { 227 int yes; 228 229#ifdef IP_PKTINFO 230 yes = 1; 231 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) { 232 avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno)); 233 return -1; 234 } 235#else 236 237#ifdef IP_RECVINTERFACE 238 yes = 1; 239 if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) { 240 avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno)); 241 return -1; 242 } 243#elif defined(IP_RECVIF) 244 yes = 1; 245 if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) { 246 avahi_log_warn("IP_RECVIF failed: %s", strerror(errno)); 247 return -1; 248 } 249#endif 250 251#ifdef IP_RECVDSTADDR 252 yes = 1; 253 if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) { 254 avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno)); 255 return -1; 256 } 257#endif 258 259#endif /* IP_PKTINFO */ 260 261#ifdef IP_RECVTTL 262 yes = 1; 263 if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) { 264 avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno)); 265 return -1; 266 } 267#endif 268 269 return 0; 270} 271 272static int ipv6_pktinfo(int fd) { 273 int yes; 274 275#ifdef IPV6_RECVPKTINFO 276 yes = 1; 277 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) { 278 avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno)); 279 return -1; 280 } 281#elif defined(IPV6_PKTINFO) 282 yes = 1; 283 if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) { 284 avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno)); 285 return -1; 286 } 287#endif 288 289#ifdef IPV6_RECVHOPS 290 yes = 1; 291 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) { 292 avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno)); 293 return -1; 294 } 295#elif defined(IPV6_RECVHOPLIMIT) 296 yes = 1; 297 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) { 298 avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno)); 299 return -1; 300 } 301#elif defined(IPV6_HOPLIMIT) 302 yes = 1; 303 if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) { 304 avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno)); 305 return -1; 306 } 307#endif 308 309 return 0; 310} 311 312int avahi_open_socket_ipv4(int no_reuse) { 313 struct sockaddr_in local; 314 int fd = -1, r, ittl; 315 uint8_t ttl, cyes; 316 317 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 318 avahi_log_warn("socket() failed: %s", strerror(errno)); 319 goto fail; 320 } 321 322 ttl = 255; 323 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { 324 avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno)); 325 goto fail; 326 } 327 328 ittl = 255; 329 if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) { 330 avahi_log_warn("IP_TTL failed: %s", strerror(errno)); 331 goto fail; 332 } 333 334 cyes = 1; 335 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) { 336 avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno)); 337 goto fail; 338 } 339 340 memset(&local, 0, sizeof(local)); 341 local.sin_family = AF_INET; 342 local.sin_port = htons(AVAHI_MDNS_PORT); 343 344 if (no_reuse) 345 r = bind(fd, (struct sockaddr*) &local, sizeof(local)); 346 else 347 r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); 348 349 if (r < 0) 350 goto fail; 351 352 if (ipv4_pktinfo (fd) < 0) 353 goto fail; 354 355 if (avahi_set_cloexec(fd) < 0) { 356 avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); 357 goto fail; 358 } 359 360 if (avahi_set_nonblock(fd) < 0) { 361 avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); 362 goto fail; 363 } 364 365 return fd; 366 367fail: 368 if (fd >= 0) 369 close(fd); 370 371 return -1; 372} 373 374int avahi_open_socket_ipv6(int no_reuse) { 375 struct sockaddr_in6 sa, local; 376 int fd = -1, yes, r; 377 int ttl; 378 379 mdns_mcast_group_ipv6(&sa); 380 381 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 382 avahi_log_warn("socket() failed: %s", strerror(errno)); 383 goto fail; 384 } 385 386 ttl = 255; 387 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { 388 avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno)); 389 goto fail; 390 } 391 392 ttl = 255; 393 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) { 394 avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno)); 395 goto fail; 396 } 397 398 yes = 1; 399 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { 400 avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno)); 401 goto fail; 402 } 403 404 yes = 1; 405 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { 406 avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno)); 407 goto fail; 408 } 409 410 memset(&local, 0, sizeof(local)); 411 local.sin6_family = AF_INET6; 412 local.sin6_port = htons(AVAHI_MDNS_PORT); 413 414 if (no_reuse) 415 r = bind(fd, (struct sockaddr*) &local, sizeof(local)); 416 else 417 r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)); 418 419 if (r < 0) 420 goto fail; 421 422 if (ipv6_pktinfo(fd) < 0) 423 goto fail; 424 425 if (avahi_set_cloexec(fd) < 0) { 426 avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); 427 goto fail; 428 } 429 430 if (avahi_set_nonblock(fd) < 0) { 431 avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); 432 goto fail; 433 } 434 435 return fd; 436 437fail: 438 if (fd >= 0) 439 close(fd); 440 441 return -1; 442} 443 444static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { 445 assert(fd >= 0); 446 assert(msg); 447 448 for (;;) { 449 450 if (sendmsg(fd, msg, flags) >= 0) 451 break; 452 453 if (errno != EAGAIN) { 454 char where[64]; 455 struct sockaddr_in *sin = msg->msg_name; 456 457 inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where)); 458 avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno)); 459 return -1; 460 } 461 462 if (avahi_wait_for_write(fd) < 0) 463 return -1; 464 } 465 466 return 0; 467} 468 469int avahi_send_dns_packet_ipv4( 470 int fd, 471 AvahiIfIndex interface, 472 AvahiDnsPacket *p, 473 const AvahiIPv4Address *src_address, 474 const AvahiIPv4Address *dst_address, 475 uint16_t dst_port) { 476 477 struct sockaddr_in sa; 478 struct msghdr msg; 479 struct iovec io; 480#ifdef IP_PKTINFO 481 struct cmsghdr *cmsg; 482 size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; 483#elif !defined(IP_MULTICAST_IF) && defined(IP_SENDSRCADDR) 484 struct cmsghdr *cmsg; 485 size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_addr)) / sizeof(size_t)) + 1]; 486#endif 487 488 assert(fd >= 0); 489 assert(p); 490 assert(avahi_dns_packet_check_valid(p) >= 0); 491 assert(!dst_address || dst_port > 0); 492 493 if (!dst_address) 494 mdns_mcast_group_ipv4(&sa); 495 else 496 ipv4_address_to_sockaddr(&sa, dst_address, dst_port); 497 498 memset(&io, 0, sizeof(io)); 499 io.iov_base = AVAHI_DNS_PACKET_DATA(p); 500 io.iov_len = p->size; 501 502 memset(&msg, 0, sizeof(msg)); 503 msg.msg_name = &sa; 504 msg.msg_namelen = sizeof(sa); 505 msg.msg_iov = &io; 506 msg.msg_iovlen = 1; 507 msg.msg_flags = 0; 508 msg.msg_control = NULL; 509 msg.msg_controllen = 0; 510 511#ifdef IP_PKTINFO 512 if (interface > 0 || src_address) { 513 struct in_pktinfo *pkti; 514 515 memset(cmsg_data, 0, sizeof(cmsg_data)); 516 msg.msg_control = cmsg_data; 517 msg.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); 518 519 cmsg = CMSG_FIRSTHDR(&msg); 520 cmsg->cmsg_len = msg.msg_controllen; 521 cmsg->cmsg_level = IPPROTO_IP; 522 cmsg->cmsg_type = IP_PKTINFO; 523 524 pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); 525 526 if (interface > 0) 527 pkti->ipi_ifindex = interface; 528 529 if (src_address) 530 pkti->ipi_spec_dst.s_addr = src_address->address; 531 } 532#elif defined(IP_MULTICAST_IF) 533 if (src_address) { 534 struct in_addr any = { INADDR_ANY }; 535 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? &src_address->address : &any, sizeof(struct in_addr)) < 0) { 536 avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno)); 537 return -1; 538 } 539 } 540#elif defined(IP_SENDSRCADDR) 541 if (src_address) { 542 struct in_addr *addr; 543 544 memset(cmsg_data, 0, sizeof(cmsg_data)); 545 msg.msg_control = cmsg_data; 546 msg.msg_controllen = CMSG_LEN(sizeof(struct in_addr)); 547 548 cmsg = CMSG_FIRSTHDR(&msg); 549 cmsg->cmsg_len = msg.msg_controllen; 550 cmsg->cmsg_level = IPPROTO_IP; 551 cmsg->cmsg_type = IP_SENDSRCADDR; 552 553 addr = (struct in_addr *)CMSG_DATA(cmsg); 554 addr->s_addr = src_address->address; 555 } 556#elif defined(__GNUC__) 557#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available" 558#endif 559 560 return sendmsg_loop(fd, &msg, 0); 561} 562 563int avahi_send_dns_packet_ipv6( 564 int fd, 565 AvahiIfIndex interface, 566 AvahiDnsPacket *p, 567 const AvahiIPv6Address *src_address, 568 const AvahiIPv6Address *dst_address, 569 uint16_t dst_port) { 570 571 struct sockaddr_in6 sa; 572 struct msghdr msg; 573 struct iovec io; 574 struct cmsghdr *cmsg; 575 size_t cmsg_data[(CMSG_SPACE(sizeof(struct in6_pktinfo))/sizeof(size_t)) + 1]; 576 577 assert(fd >= 0); 578 assert(p); 579 assert(avahi_dns_packet_check_valid(p) >= 0); 580 assert(!dst_address || dst_port > 0); 581 582 if (!dst_address) 583 mdns_mcast_group_ipv6(&sa); 584 else 585 ipv6_address_to_sockaddr(&sa, dst_address, dst_port); 586 587 memset(&io, 0, sizeof(io)); 588 io.iov_base = AVAHI_DNS_PACKET_DATA(p); 589 io.iov_len = p->size; 590 591 memset(&msg, 0, sizeof(msg)); 592 msg.msg_name = &sa; 593 msg.msg_namelen = sizeof(sa); 594 msg.msg_iov = &io; 595 msg.msg_iovlen = 1; 596 msg.msg_flags = 0; 597 598 if (interface > 0 || src_address) { 599 struct in6_pktinfo *pkti; 600 601 memset(cmsg_data, 0, sizeof(cmsg_data)); 602 msg.msg_control = cmsg_data; 603 msg.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); 604 605 cmsg = CMSG_FIRSTHDR(&msg); 606 cmsg->cmsg_len = msg.msg_controllen; 607 cmsg->cmsg_level = IPPROTO_IPV6; 608 cmsg->cmsg_type = IPV6_PKTINFO; 609 610 pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg); 611 612 if (interface > 0) 613 pkti->ipi6_ifindex = interface; 614 615 if (src_address) 616 memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address)); 617 } else { 618 msg.msg_control = NULL; 619 msg.msg_controllen = 0; 620 } 621 622 return sendmsg_loop(fd, &msg, 0); 623} 624 625AvahiDnsPacket *avahi_recv_dns_packet_ipv4( 626 int fd, 627 AvahiIPv4Address *ret_src_address, 628 uint16_t *ret_src_port, 629 AvahiIPv4Address *ret_dst_address, 630 AvahiIfIndex *ret_iface, 631 uint8_t *ret_ttl) { 632 633 AvahiDnsPacket *p= NULL; 634 struct msghdr msg; 635 struct iovec io; 636 size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */ 637 ssize_t l; 638 struct cmsghdr *cmsg; 639 int found_addr = 0; 640 int ms; 641 struct sockaddr_in sa; 642 643 assert(fd >= 0); 644 645 if (ioctl(fd, FIONREAD, &ms) < 0) { 646 avahi_log_warn("ioctl(): %s", strerror(errno)); 647 goto fail; 648 } 649 650 if (ms < 0) { 651 avahi_log_warn("FIONREAD returned negative value."); 652 goto fail; 653 } 654 655 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); 656 657 io.iov_base = AVAHI_DNS_PACKET_DATA(p); 658 io.iov_len = p->max_size; 659 660 memset(&msg, 0, sizeof(msg)); 661 msg.msg_name = &sa; 662 msg.msg_namelen = sizeof(sa); 663 msg.msg_iov = &io; 664 msg.msg_iovlen = 1; 665 msg.msg_control = aux; 666 msg.msg_controllen = sizeof(aux); 667 msg.msg_flags = 0; 668 669 if ((l = recvmsg(fd, &msg, 0)) < 0) { 670 /* Linux returns EAGAIN when an invalid IP packet has been 671 received. We suppress warnings in this case because this might 672 create quite a bit of log traffic on machines with unstable 673 links. (See #60) */ 674 675 if (errno != EAGAIN) 676 avahi_log_warn("recvmsg(): %s", strerror(errno)); 677 678 goto fail; 679 } 680 681 if (sa.sin_addr.s_addr == INADDR_ANY) { 682 /* Linux 2.4 behaves very strangely sometimes! */ 683 goto fail; 684 } 685 686 assert(!(msg.msg_flags & MSG_CTRUNC)); 687 assert(!(msg.msg_flags & MSG_TRUNC)); 688 689 p->size = (size_t) l; 690 691 if (ret_src_port) 692 *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); 693 694 if (ret_src_address) { 695 AvahiAddress a; 696 avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); 697 *ret_src_address = a.data.ipv4; 698 } 699 700 if (ret_ttl) 701 *ret_ttl = 255; 702 703 if (ret_iface) 704 *ret_iface = AVAHI_IF_UNSPEC; 705 706 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 707 708 if (cmsg->cmsg_level == IPPROTO_IP) { 709 710 switch (cmsg->cmsg_type) { 711#ifdef IP_RECVTTL 712 case IP_RECVTTL: 713#endif 714 case IP_TTL: 715 if (ret_ttl) 716 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); 717 718 break; 719 720#ifdef IP_PKTINFO 721 case IP_PKTINFO: { 722 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); 723 724 if (ret_iface) 725 *ret_iface = (int) i->ipi_ifindex; 726 727 if (ret_dst_address) 728 ret_dst_address->address = i->ipi_addr.s_addr; 729 730 found_addr = 1; 731 732 break; 733 } 734#endif 735 736#ifdef IP_RECVIF 737 case IP_RECVIF: { 738 struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg); 739 740 if (ret_iface) 741#ifdef __sun 742 *ret_iface = *(uint_t*) sdl; 743#else 744 *ret_iface = (int) sdl->sdl_index; 745#endif 746 747 break; 748 } 749#endif 750 751#ifdef IP_RECVDSTADDR 752 case IP_RECVDSTADDR: 753 if (ret_dst_address) 754 memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4); 755 756 found_addr = 1; 757 break; 758#endif 759 760 default: 761 avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type); 762 break; 763 } 764 } 765 } 766 767 assert(found_addr); 768 769 return p; 770 771fail: 772 if (p) 773 avahi_dns_packet_free(p); 774 775 return NULL; 776} 777 778AvahiDnsPacket *avahi_recv_dns_packet_ipv6( 779 int fd, 780 AvahiIPv6Address *ret_src_address, 781 uint16_t *ret_src_port, 782 AvahiIPv6Address *ret_dst_address, 783 AvahiIfIndex *ret_iface, 784 uint8_t *ret_ttl) { 785 786 AvahiDnsPacket *p = NULL; 787 struct msghdr msg; 788 struct iovec io; 789 size_t aux[1024 / sizeof(size_t)]; 790 ssize_t l; 791 int ms; 792 struct cmsghdr *cmsg; 793 int found_ttl = 0, found_iface = 0; 794 struct sockaddr_in6 sa; 795 796 assert(fd >= 0); 797 798 if (ioctl(fd, FIONREAD, &ms) < 0) { 799 avahi_log_warn("ioctl(): %s", strerror(errno)); 800 goto fail; 801 } 802 803 if (ms < 0) { 804 avahi_log_warn("FIONREAD returned negative value."); 805 goto fail; 806 } 807 808 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE); 809 810 io.iov_base = AVAHI_DNS_PACKET_DATA(p); 811 io.iov_len = p->max_size; 812 813 memset(&msg, 0, sizeof(msg)); 814 msg.msg_name = (struct sockaddr*) &sa; 815 msg.msg_namelen = sizeof(sa); 816 817 msg.msg_iov = &io; 818 msg.msg_iovlen = 1; 819 msg.msg_control = aux; 820 msg.msg_controllen = sizeof(aux); 821 msg.msg_flags = 0; 822 823 if ((l = recvmsg(fd, &msg, 0)) < 0) { 824 /* Linux returns EAGAIN when an invalid IP packet has been 825 received. We suppress warnings in this case because this might 826 create quite a bit of log traffic on machines with unstable 827 links. (See #60) */ 828 829 if (errno != EAGAIN) 830 avahi_log_warn("recvmsg(): %s", strerror(errno)); 831 832 goto fail; 833 } 834 835 assert(!(msg.msg_flags & MSG_CTRUNC)); 836 assert(!(msg.msg_flags & MSG_TRUNC)); 837 838 p->size = (size_t) l; 839 840 if (ret_src_port) 841 *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa); 842 843 if (ret_src_address) { 844 AvahiAddress a; 845 avahi_address_from_sockaddr((struct sockaddr*) &sa, &a); 846 *ret_src_address = a.data.ipv6; 847 } 848 849 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 850 851 if (cmsg->cmsg_level == IPPROTO_IPV6) { 852 853 switch (cmsg->cmsg_type) { 854 855 case IPV6_HOPLIMIT: 856 857 if (ret_ttl) 858 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg)); 859 860 found_ttl = 1; 861 862 break; 863 864 case IPV6_PKTINFO: { 865 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); 866 867 if (ret_iface) 868 *ret_iface = i->ipi6_ifindex; 869 870 if (ret_dst_address) 871 memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16); 872 873 found_iface = 1; 874 break; 875 } 876 877 default: 878 avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type); 879 break; 880 } 881 } 882 } 883 884 assert(found_iface); 885 assert(found_ttl); 886 887 return p; 888 889fail: 890 if (p) 891 avahi_dns_packet_free(p); 892 893 return NULL; 894} 895 896int avahi_open_unicast_socket_ipv4(void) { 897 struct sockaddr_in local; 898 int fd = -1; 899 900 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 901 avahi_log_warn("socket() failed: %s", strerror(errno)); 902 goto fail; 903 } 904 905 memset(&local, 0, sizeof(local)); 906 local.sin_family = AF_INET; 907 908 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { 909 avahi_log_warn("bind() failed: %s", strerror(errno)); 910 goto fail; 911 } 912 913 if (ipv4_pktinfo(fd) < 0) { 914 goto fail; 915 } 916 917 if (avahi_set_cloexec(fd) < 0) { 918 avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); 919 goto fail; 920 } 921 922 if (avahi_set_nonblock(fd) < 0) { 923 avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); 924 goto fail; 925 } 926 927 return fd; 928 929fail: 930 if (fd >= 0) 931 close(fd); 932 933 return -1; 934} 935 936int avahi_open_unicast_socket_ipv6(void) { 937 struct sockaddr_in6 local; 938 int fd = -1, yes; 939 940 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 941 avahi_log_warn("socket() failed: %s", strerror(errno)); 942 goto fail; 943 } 944 945 yes = 1; 946 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) { 947 avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno)); 948 goto fail; 949 } 950 951 memset(&local, 0, sizeof(local)); 952 local.sin6_family = AF_INET6; 953 954 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { 955 avahi_log_warn("bind() failed: %s", strerror(errno)); 956 goto fail; 957 } 958 959 if (ipv6_pktinfo(fd) < 0) 960 goto fail; 961 962 if (avahi_set_cloexec(fd) < 0) { 963 avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno)); 964 goto fail; 965 } 966 967 if (avahi_set_nonblock(fd) < 0) { 968 avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno)); 969 goto fail; 970 } 971 972 return fd; 973 974fail: 975 if (fd >= 0) 976 close(fd); 977 978 return -1; 979} 980