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, "224.0.0.251", &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, "ff02::fb", &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, 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, MSG_DONTROUTE);
447 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
448 AvahiDnsPacket *p= NULL;
453 struct cmsghdr *cmsg;
454 gboolean found_ttl = FALSE, found_iface = FALSE;
462 if (ioctl(fd, FIONREAD, &ms) < 0) {
463 avahi_log_warn("ioctl(): %s", strerror(errno));
467 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
469 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
470 io.iov_len = p->max_size;
472 memset(&msg, 0, sizeof(msg));
473 msg.msg_name = ret_sa;
474 msg.msg_namelen = sizeof(struct sockaddr_in);
477 msg.msg_control = aux;
478 msg.msg_controllen = sizeof(aux);
481 if ((l = recvmsg(fd, &msg, 0)) < 0) {
482 avahi_log_warn("recvmsg(): %s", strerror(errno));
486 if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
487 /* Linux 2.4 behaves very strangely sometimes! */
489 avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l);
495 g_assert(!(msg.msg_flags & MSG_CTRUNC));
496 g_assert(!(msg.msg_flags & MSG_TRUNC));
497 p->size = (size_t) l;
501 /* avahi_hexdump(msg.msg_control, msg.msg_controllen); */
503 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
505 /* avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
507 if (cmsg->cmsg_level == SOL_IP) {
509 if (cmsg->cmsg_type == IP_TTL) {
510 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
512 } else if (cmsg->cmsg_type == IP_PKTINFO) {
513 *ret_iface = (gint) ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
519 /* avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
521 g_assert(found_iface);
528 avahi_dns_packet_free(p);
533 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
534 AvahiDnsPacket *p = NULL;
541 struct cmsghdr *cmsg;
542 gboolean found_ttl = FALSE, found_iface = FALSE;
549 if (ioctl(fd, FIONREAD, &ms) < 0) {
550 avahi_log_warn("ioctl(): %s", strerror(errno));
554 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
556 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
557 io.iov_len = p->max_size;
559 memset(&msg, 0, sizeof(msg));
560 msg.msg_name = ret_sa;
561 msg.msg_namelen = sizeof(struct sockaddr_in6);
564 msg.msg_control = aux;
565 msg.msg_controllen = sizeof(aux);
568 if ((l = recvmsg(fd, &msg, 0)) < 0) {
569 avahi_log_warn("recvmsg(): %s", strerror(errno));
573 p->size = (size_t) l;
577 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
578 if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
579 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
583 if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
584 *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex;
589 g_assert(found_iface);
596 avahi_dns_packet_free(p);
601 gint avahi_open_legacy_unicast_socket_ipv4(void) {
602 struct sockaddr_in local;
605 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
606 avahi_log_warn("socket() failed: %s\n", strerror(errno));
610 memset(&local, 0, sizeof(local));
611 local.sin_family = AF_INET;
613 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
614 avahi_log_warn("bind() failed: %s\n", strerror(errno));
619 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
620 avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
625 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
626 avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
630 if (avahi_set_cloexec(fd) < 0) {
631 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
635 if (avahi_set_nonblock(fd) < 0) {
636 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
649 gint avahi_open_legacy_unicast_socket_ipv6(void) {
650 struct sockaddr_in local;
653 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
654 avahi_log_warn("socket() failed: %s\n", strerror(errno));
658 memset(&local, 0, sizeof(local));
659 local.sin_family = AF_INET;
661 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
662 avahi_log_warn("bind() failed: %s\n", strerror(errno));
667 if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
668 avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
673 if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
674 avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
678 if (avahi_set_cloexec(fd) < 0) {
679 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
683 if (avahi_set_nonblock(fd) < 0) {
684 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));