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 gint avahi_open_socket_ipv4(void) {
160 struct sockaddr_in local;
161 int fd = -1, ttl, yes;
163 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
164 avahi_log_warn("socket() failed: %s\n", strerror(errno));
169 if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
170 avahi_log_warn("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
175 if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
176 avahi_log_warn("IP_TTL failed: %s\n", strerror(errno));
181 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
182 avahi_log_warn("SO_REUSEADDR failed: %s\n", strerror(errno));
187 if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
188 avahi_log_warn("IP_MULTICAST_LOOP failed: %s\n", strerror(errno));
193 memset(&local, 0, sizeof(local));
194 local.sin_family = AF_INET;
195 local.sin_port = htons(AVAHI_MDNS_PORT);
197 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
198 avahi_log_warn("bind() failed: %s\n", strerror(errno));
203 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
204 avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
209 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
210 avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
214 if (avahi_set_cloexec(fd) < 0) {
215 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
219 if (avahi_set_nonblock(fd) < 0) {
220 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
233 gint avahi_open_socket_ipv6(void) {
234 struct sockaddr_in6 sa, local;
235 int fd = -1, ttl, yes;
237 mdns_mcast_group_ipv6(&sa);
239 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
240 avahi_log_warn("socket() failed: %s\n", strerror(errno));
245 if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
246 avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
251 if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
252 avahi_log_warn("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
257 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
258 avahi_log_warn("SO_REUSEADDR failed: %s\n", strerror(errno));
263 if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
264 avahi_log_warn("IPV6_V6ONLY failed: %s\n", strerror(errno));
269 if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
270 avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno));
274 memset(&local, 0, sizeof(local));
275 local.sin6_family = AF_INET6;
276 local.sin6_port = htons(AVAHI_MDNS_PORT);
278 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
279 avahi_log_warn("bind() failed: %s\n", strerror(errno));
284 if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
285 avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
290 if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
291 avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
295 if (avahi_set_cloexec(fd) < 0) {
296 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
300 if (avahi_set_nonblock(fd) < 0) {
301 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
314 static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
320 if (sendmsg(fd, msg, flags) >= 0)
323 if (errno != EAGAIN) {
324 avahi_log_debug("sendmsg() failed: %s\n", strerror(errno));
328 if (avahi_wait_for_write(fd) < 0)
335 gint avahi_send_dns_packet_ipv4(gint fd, gint interface, AvahiDnsPacket *p, const AvahiIPv4Address *a, guint16 port) {
336 struct sockaddr_in sa;
339 struct cmsghdr *cmsg;
340 struct in_pktinfo *pkti;
341 uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
345 g_assert(avahi_dns_packet_check_valid(p) >= 0);
346 g_assert(!a || port > 0);
349 mdns_mcast_group_ipv4(&sa);
351 ipv4_address_to_sockaddr(&sa, a, port);
353 memset(&io, 0, sizeof(io));
354 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
355 io.iov_len = p->size;
357 memset(cmsg_data, 0, sizeof(cmsg_data));
358 cmsg = (struct cmsghdr*) cmsg_data;
359 cmsg->cmsg_len = sizeof(cmsg_data);
360 cmsg->cmsg_level = IPPROTO_IP;
361 cmsg->cmsg_type = IP_PKTINFO;
363 pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
364 pkti->ipi_ifindex = interface;
366 memset(&msg, 0, sizeof(msg));
368 msg.msg_namelen = sizeof(sa);
371 msg.msg_control = cmsg_data;
372 msg.msg_controllen = sizeof(cmsg_data);
375 return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
378 gint avahi_send_dns_packet_ipv6(gint fd, gint interface, AvahiDnsPacket *p, const AvahiIPv6Address *a, guint16 port) {
379 struct sockaddr_in6 sa;
382 struct cmsghdr *cmsg;
383 struct in6_pktinfo *pkti;
384 uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
388 g_assert(avahi_dns_packet_check_valid(p) >= 0);
391 mdns_mcast_group_ipv6(&sa);
393 ipv6_address_to_sockaddr(&sa, a, port);
395 memset(&io, 0, sizeof(io));
396 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
397 io.iov_len = p->size;
399 memset(cmsg_data, 0, sizeof(cmsg_data));
400 cmsg = (struct cmsghdr*) cmsg_data;
401 cmsg->cmsg_len = sizeof(cmsg_data);
402 cmsg->cmsg_level = IPPROTO_IPV6;
403 cmsg->cmsg_type = IPV6_PKTINFO;
405 pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
406 pkti->ipi6_ifindex = interface;
408 memset(&msg, 0, sizeof(msg));
410 msg.msg_namelen = sizeof(sa);
413 msg.msg_control = cmsg_data;
414 msg.msg_controllen = sizeof(cmsg_data);
417 return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
420 AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
421 AvahiDnsPacket *p= NULL;
426 struct cmsghdr *cmsg;
427 gboolean found_ttl = FALSE, found_iface = FALSE;
435 if (ioctl(fd, FIONREAD, &ms) < 0) {
436 avahi_log_warn("ioctl(): %s", strerror(errno));
440 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
442 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
443 io.iov_len = p->max_size;
445 memset(&msg, 0, sizeof(msg));
446 msg.msg_name = ret_sa;
447 msg.msg_namelen = sizeof(struct sockaddr_in);
450 msg.msg_control = aux;
451 msg.msg_controllen = sizeof(aux);
454 if ((l = recvmsg(fd, &msg, 0)) < 0) {
455 avahi_log_warn("recvmsg(): %s", strerror(errno));
459 if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
460 /* Linux 2.4 behaves very strangely sometimes! */
462 avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l);
468 g_assert(!(msg.msg_flags & MSG_CTRUNC));
469 g_assert(!(msg.msg_flags & MSG_TRUNC));
470 p->size = (size_t) l;
474 /* avahi_hexdump(msg.msg_control, msg.msg_controllen); */
476 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
478 /* avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
480 if (cmsg->cmsg_level == SOL_IP) {
482 if (cmsg->cmsg_type == IP_TTL) {
483 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
485 } else if (cmsg->cmsg_type == IP_PKTINFO) {
486 *ret_iface = (gint) ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
492 /* avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
494 g_assert(found_iface);
501 avahi_dns_packet_free(p);
506 AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
507 AvahiDnsPacket *p = NULL;
514 struct cmsghdr *cmsg;
515 gboolean found_ttl = FALSE, found_iface = FALSE;
522 if (ioctl(fd, FIONREAD, &ms) < 0) {
523 avahi_log_warn("ioctl(): %s", strerror(errno));
527 p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
529 io.iov_base = AVAHI_DNS_PACKET_DATA(p);
530 io.iov_len = p->max_size;
532 memset(&msg, 0, sizeof(msg));
533 msg.msg_name = ret_sa;
534 msg.msg_namelen = sizeof(struct sockaddr_in6);
537 msg.msg_control = aux;
538 msg.msg_controllen = sizeof(aux);
541 if ((l = recvmsg(fd, &msg, 0)) < 0) {
542 avahi_log_warn("recvmsg(): %s", strerror(errno));
546 p->size = (size_t) l;
550 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
551 if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
552 *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
556 if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
557 *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex;
562 g_assert(found_iface);
569 avahi_dns_packet_free(p);
574 gint avahi_open_legacy_unicast_socket_ipv4(void) {
575 struct sockaddr_in local;
578 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
579 avahi_log_warn("socket() failed: %s\n", strerror(errno));
583 memset(&local, 0, sizeof(local));
584 local.sin_family = AF_INET;
586 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
587 avahi_log_warn("bind() failed: %s\n", strerror(errno));
592 if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
593 avahi_log_warn("IP_RECVTTL failed: %s\n", strerror(errno));
598 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
599 avahi_log_warn("IP_PKTINFO failed: %s\n", strerror(errno));
603 if (avahi_set_cloexec(fd) < 0) {
604 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
608 if (avahi_set_nonblock(fd) < 0) {
609 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));
622 gint avahi_open_legacy_unicast_socket_ipv6(void) {
623 struct sockaddr_in local;
626 if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
627 avahi_log_warn("socket() failed: %s\n", strerror(errno));
631 memset(&local, 0, sizeof(local));
632 local.sin_family = AF_INET;
634 if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
635 avahi_log_warn("bind() failed: %s\n", strerror(errno));
640 if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
641 avahi_log_warn("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
646 if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
647 avahi_log_warn("IPV6_PKTINFO failed: %s\n", strerror(errno));
651 if (avahi_set_cloexec(fd) < 0) {
652 avahi_log_warn("FD_CLOEXEC failed: %s\n", strerror(errno));
656 if (avahi_set_nonblock(fd) < 0) {
657 avahi_log_warn("O_NONBLOCK failed: %s\n", strerror(errno));