4 This file is part of avahi.
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.
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.
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
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
37 #include <sys/ioctl.h>
44 static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
47 memset(ret_sa, 0, sizeof(struct sockaddr_in));
49 ret_sa->sin_family = AF_INET;
50 ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
51 inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
54 static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
58 memset(ret_sa, 0, sizeof(struct sockaddr_in6));
60 ret_sa->sin6_family = AF_INET6;
61 ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
62 inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
65 static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, guint16 port) {
70 memset(ret_sa, 0, sizeof(struct sockaddr_in));
71 ret_sa->sin_family = AF_INET;
72 ret_sa->sin_port = htons(port);
73 memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
76 static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, guint16 port) {
81 memset(ret_sa, 0, sizeof(struct sockaddr_in6));
82 ret_sa->sin6_family = AF_INET6;
83 ret_sa->sin6_port = htons(port);
84 memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
87 int avahi_mdns_mcast_join_ipv4 (int index, int fd) {
89 struct sockaddr_in sa;
91 mdns_mcast_group_ipv4 (&sa);
93 memset(&mreq, 0, sizeof(mreq));
94 mreq.imr_multiaddr = sa.sin_addr;
95 mreq.imr_ifindex = index;
97 if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
98 avahi_log_warn("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
105 int avahi_mdns_mcast_join_ipv6 (int index, int fd) {
106 struct ipv6_mreq mreq6;
107 struct sockaddr_in6 sa6;
109 mdns_mcast_group_ipv6 (&sa6);
111 memset(&mreq6, 0, sizeof(mreq6));
112 mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
113 mreq6.ipv6mr_interface = index;
115 if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
116 avahi_log_warn("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
123 int avahi_mdns_mcast_leave_ipv4 (int index, int fd) {
124 struct ip_mreqn mreq;
125 struct sockaddr_in sa;
127 mdns_mcast_group_ipv4 (&sa);
129 memset(&mreq, 0, sizeof(mreq));
130 mreq.imr_multiaddr = sa.sin_addr;
131 mreq.imr_ifindex = index;
133 if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
134 avahi_log_warn("IP_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
141 int avahi_mdns_mcast_leave_ipv6 (int index, int fd) {
142 struct ipv6_mreq mreq6;
143 struct sockaddr_in6 sa6;
145 mdns_mcast_group_ipv6 (&sa6);
147 memset(&mreq6, 0, sizeof(mreq6));
148 mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
149 mreq6.ipv6mr_interface = index;
151 if (setsockopt(fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
152 avahi_log_warn("IPV6_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
159 static gint bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
166 if (bind(fd, sa, l) < 0) {
168 if (errno != EADDRINUSE) {
169 avahi_log_warn("bind() failed: %s\n", strerror(errno));
173 avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
174 sa->sa_family == AF_INET ? "IPv4" : "IPv6");
176 /* Try again, this time with SO_REUSEADDR set */
178 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
179 avahi_log_warn("SO_REUSEADDR failed: %s\n", strerror(errno));
183 if (bind(fd, sa, l) < 0) {
184 avahi_log_warn("bind() failed: %s\n", strerror(errno));
189 /* We enable SO_REUSEADDR afterwards, to make sure that the
190 * user may run other mDNS implementations if he really
194 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
195 avahi_log_warn("SO_REUSEADDR failed: %s\n", strerror(errno));
203 gint avahi_open_socket_ipv4(void) {
204 struct sockaddr_in local;
205 int fd = -1, ttl, yes;
207 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
208 avahi_log_warn("socket() failed: %s\n", strerror(errno));
213 if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
214 avahi_log_warn("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
219 if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
220 avahi_log_warn("IP_TTL failed: %s\n", strerror(errno));
225 if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
226 avahi_log_warn("IP_MULTICAST_LOOP failed: %s\n", strerror(errno));
230 memset(&local, 0, sizeof(local));
231 local.sin_family = AF_INET;
232 local.sin_port = htons(AVAHI_MDNS_PORT);
234 if (bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)) < 0)
238 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
239 avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
244 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
245 avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
249 if (avahi_set_cloexec(fd) < 0) {
250 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
254 if (avahi_set_nonblock(fd) < 0) {
255 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
268 gint avahi_open_socket_ipv6(void) {
269 struct sockaddr_in6 sa, local;
270 int fd = -1, ttl, yes;
272 mdns_mcast_group_ipv6(&sa);
274 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
275 avahi_log_warn("socket() failed: %s\n", strerror(errno));
280 if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
281 avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
286 if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
287 avahi_log_warn("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
292 if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
293 avahi_log_warn("IPV6_V6ONLY failed: %s\n", strerror(errno));
298 if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
299 avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno));
303 memset(&local, 0, sizeof(local));
304 local.sin6_family = AF_INET6;
305 local.sin6_port = htons(AVAHI_MDNS_PORT);
307 if (bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local)) < 0)
311 if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
312 avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
317 if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
318 avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
322 if (avahi_set_cloexec(fd) < 0) {
323 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
327 if (avahi_set_nonblock(fd) < 0) {
328 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
341 static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
347 if (sendmsg(fd, msg, flags) >= 0)
350 if (errno != EAGAIN) {
351 avahi_log_debug("sendmsg() failed: %s\n", strerror(errno));
355 if (avahi_wait_for_write(fd) < 0)
362 gint avahi_send_dns_packet_ipv4(gint fd, gint interface, AvahiDnsPacket *p, const AvahiIPv4Address *a, guint16 port) {
363 struct sockaddr_in sa;
366 struct cmsghdr *cmsg;
367 struct in_pktinfo *pkti;
368 uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
372 g_assert(avahi_dns_packet_check_valid(p) >= 0);
373 g_assert(!a || port > 0);
376 mdns_mcast_group_ipv4(&sa);
378 ipv4_address_to_sockaddr(&sa, a, port);
380 memset(&io, 0, sizeof(io));
381 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
382 io.iov_len = p->size;
384 memset(cmsg_data, 0, sizeof(cmsg_data));
385 cmsg = (struct cmsghdr*) cmsg_data;
386 cmsg->cmsg_len = sizeof(cmsg_data);
387 cmsg->cmsg_level = IPPROTO_IP;
388 cmsg->cmsg_type = IP_PKTINFO;
390 pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
391 pkti->ipi_ifindex = interface;
393 memset(&msg, 0, sizeof(msg));
395 msg.msg_namelen = sizeof(sa);
398 msg.msg_control = cmsg_data;
399 msg.msg_controllen = sizeof(cmsg_data);
402 return sendmsg_loop(fd, &msg, 0 /*MSG_DONTROUTE*/);
405 gint avahi_send_dns_packet_ipv6(gint fd, gint interface, AvahiDnsPacket *p, const AvahiIPv6Address *a, guint16 port) {
406 struct sockaddr_in6 sa;
409 struct cmsghdr *cmsg;
410 struct in6_pktinfo *pkti;
411 uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
415 g_assert(avahi_dns_packet_check_valid(p) >= 0);
418 mdns_mcast_group_ipv6(&sa);
420 ipv6_address_to_sockaddr(&sa, a, port);
422 memset(&io, 0, sizeof(io));
423 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
424 io.iov_len = p->size;
426 memset(cmsg_data, 0, sizeof(cmsg_data));
427 cmsg = (struct cmsghdr*) cmsg_data;
428 cmsg->cmsg_len = sizeof(cmsg_data);
429 cmsg->cmsg_level = IPPROTO_IPV6;
430 cmsg->cmsg_type = IPV6_PKTINFO;
432 pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
433 pkti->ipi6_ifindex = interface;
435 memset(&msg, 0, sizeof(msg));
437 msg.msg_namelen = sizeof(sa);
440 msg.msg_control = cmsg_data;
441 msg.msg_controllen = sizeof(cmsg_data);
444 return sendmsg_loop(fd, &msg, 0 /*MSG_DONTROUTE*/);
447 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, gint *ret_iface, guint8* ret_ttl) {
448 AvahiDnsPacket *p= NULL;
453 struct cmsghdr *cmsg;
454 gboolean found_ttl = FALSE, found_iface = FALSE;
459 g_assert(ret_dest_address);
463 if (ioctl(fd, FIONREAD, &ms) < 0) {
464 avahi_log_warn("ioctl(): %s", strerror(errno));
468 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
470 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
471 io.iov_len = p->max_size;
473 memset(&msg, 0, sizeof(msg));
474 msg.msg_name = ret_sa;
475 msg.msg_namelen = sizeof(struct sockaddr_in);
478 msg.msg_control = aux;
479 msg.msg_controllen = sizeof(aux);
482 if ((l = recvmsg(fd, &msg, 0)) < 0) {
483 avahi_log_warn("recvmsg(): %s", strerror(errno));
487 if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
488 /* Linux 2.4 behaves very strangely sometimes! */
490 avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l);
494 g_assert(!(msg.msg_flags & MSG_CTRUNC));
495 g_assert(!(msg.msg_flags & MSG_TRUNC));
496 p->size = (size_t) l;
500 /* avahi_hexdump(msg.msg_control, msg.msg_controllen); */
502 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
504 /* avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
506 if (cmsg->cmsg_level == SOL_IP) {
508 if (cmsg->cmsg_type == IP_TTL) {
509 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
511 } else if (cmsg->cmsg_type == IP_PKTINFO) {
512 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
513 *ret_iface = (gint) i->ipi_ifindex;
514 ret_dest_address->address = i->ipi_addr.s_addr;
520 /* avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
522 g_assert(found_iface);
529 avahi_dns_packet_free(p);
534 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, AvahiIPv6Address *ret_dest_address, gint *ret_iface, guint8* ret_ttl) {
535 AvahiDnsPacket *p = NULL;
542 struct cmsghdr *cmsg;
543 gboolean found_ttl = FALSE, found_iface = FALSE;
547 g_assert(ret_dest_address);
551 if (ioctl(fd, FIONREAD, &ms) < 0) {
552 avahi_log_warn("ioctl(): %s", strerror(errno));
556 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
558 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
559 io.iov_len = p->max_size;
561 memset(&msg, 0, sizeof(msg));
562 msg.msg_name = ret_sa;
563 msg.msg_namelen = sizeof(struct sockaddr_in6);
566 msg.msg_control = aux;
567 msg.msg_controllen = sizeof(aux);
570 if ((l = recvmsg(fd, &msg, 0)) < 0) {
571 avahi_log_warn("recvmsg(): %s", strerror(errno));
575 p->size = (size_t) l;
579 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
580 if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
581 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
585 if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
586 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
587 *ret_iface = i->ipi6_ifindex;
588 memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
593 g_assert(found_iface);
600 avahi_dns_packet_free(p);
605 gint avahi_open_legacy_unicast_socket_ipv4(void) {
606 struct sockaddr_in local;
609 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
610 avahi_log_warn("socket() failed: %s\n", strerror(errno));
614 memset(&local, 0, sizeof(local));
615 local.sin_family = AF_INET;
617 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
618 avahi_log_warn("bind() failed: %s\n", strerror(errno));
623 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
624 avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
629 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
630 avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
634 if (avahi_set_cloexec(fd) < 0) {
635 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
639 if (avahi_set_nonblock(fd) < 0) {
640 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
653 gint avahi_open_legacy_unicast_socket_ipv6(void) {
654 struct sockaddr_in local;
657 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
658 avahi_log_warn("socket() failed: %s\n", strerror(errno));
662 memset(&local, 0, sizeof(local));
663 local.sin_family = AF_INET;
665 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
666 avahi_log_warn("bind() failed: %s\n", strerror(errno));
671 if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
672 avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
677 if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
678 avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
682 if (avahi_set_cloexec(fd) < 0) {
683 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
687 if (avahi_set_nonblock(fd) < 0) {
688 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));