Add IPv4 multicast queries
authorKai Blin <kai@samba.org>
Sun, 22 May 2011 18:25:11 +0000 (20:25 +0200)
committerKai Blin <kai@samba.org>
Sun, 22 May 2011 18:25:11 +0000 (20:25 +0200)
listener.c
sender.c

index 2165566..36c29a6 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdbool.h>
+#include <errno.h>
 
-int main(int argc, char **argv)
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+#define V6_MULTICAST_ADDR "ff02::1:3"
+#define V4_MULTICAST_ADDR "224.0.0.252"
+#define IFACE_NAME "br0"
+#define LLMNR_PORT 5355
+
+static int setup_v6_socket(const char *multicast_addr,
+                           const char *iface_name,
+                           int port)
 {
     int err;
     int sock6 = -1;
+    int yes = 1;
     struct sockaddr_in6 name6;
     ssize_t len6 = sizeof(name6);
-    struct sockaddr_storage peer_addr;
-    struct ipv6_mreq mreq;
-    uint8_t buf[1024];
-    ssize_t msg_len;
-    socklen_t peer_addr_len;
-    char host[NI_MAXHOST], service[NI_MAXSERV];
+    struct ipv6_mreq mreq6;
 
     sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
     if (sock6 == -1) {
@@ -31,18 +37,24 @@ int main(int argc, char **argv)
         goto die;
     }
 
-    memset(&mreq, 0, sizeof(struct ipv6_mreq));
-    err = inet_pton(AF_INET6, "ff02::1:3", &mreq.ipv6mr_multiaddr);
+    err = setsockopt(sock6, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int));
+    if (err != 0) {
+        perror("setting IPV6_ONLY on sock6");
+        goto die;
+    }
+
+    memset(&mreq6, 0, sizeof(struct ipv6_mreq));
+    err = inet_pton(AF_INET6, multicast_addr, &mreq6.ipv6mr_multiaddr);
     if (err < 1) {
-        fprintf(stderr, "failed to convert ff02::1:3 to an address\n");
+        fprintf(stderr, "failed to convert %s to an address\n", multicast_addr);
         goto die;
     }
-    mreq.ipv6mr_interface = if_nametoindex("br0");
+    mreq6.ipv6mr_interface = if_nametoindex(iface_name);
 
-    err = setsockopt(sock6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+    err = setsockopt(sock6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
                      sizeof(struct ipv6_mreq));
     if (err != 0) {
-        perror("joining multicast group");
+        perror("joining v6 multicast group");
         goto die;
     }
 
@@ -50,16 +62,124 @@ int main(int argc, char **argv)
 
     name6.sin6_family = PF_INET6;
     name6.sin6_addr = in6addr_any;
-    name6.sin6_port = htons(5355);
+    name6.sin6_port = htons(port);
 
     if (bind(sock6, (struct sockaddr *) &name6, len6) == -1) {
         perror("binding to v6 socket");
         goto die;
     }
 
+    return sock6;
+
+die:
+    if (sock6 != -1) {
+        close(sock6);
+    }
+    return -1;
+}
+
+static int setup_v4_socket(const char *multicast_addr,
+                           const char *iface_name,
+                           int port)
+{
+    int err;
+    int sock4 = -1;
+    struct sockaddr_in name4;
+    ssize_t len4 = sizeof(name4);
+    struct ip_mreqn mreq4;
+
+    sock4 = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock4 == -1) {
+        perror("creating v4 socket");
+        goto die;
+    }
+
+    memset(&name4, 0, len4);
+
+    name4.sin_family = PF_INET;
+    name4.sin_addr.s_addr = inet_addr(multicast_addr);
+    name4.sin_port = htons(port);
+
+    memset(&mreq4, 0, sizeof(struct ip_mreqn));
+    mreq4.imr_multiaddr = name4.sin_addr;
+    mreq4.imr_ifindex = if_nametoindex(iface_name);
+
+    err = setsockopt(sock4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq4,
+                     sizeof(struct ip_mreqn));
+    if (err != 0) {
+        perror("joining v4 multicast group");
+        goto die;
+    }
+
+    if (bind(sock4, (struct sockaddr *) &name4, len4) == -1) {
+        perror("binding to v4 socket");
+        goto die;
+    }
+
+    return sock4;
+
+die:
+    if (sock4 != -1) {
+        close(sock4);
+    }
+    return -1;
+}
+
+int main(int argc, char **argv)
+{
+    int err;
+    int sock6 = -1;
+    int sock4 = -1;
+    struct sockaddr_storage peer_addr;
+    uint8_t buf[1024];
+    ssize_t msg_len;
+    socklen_t peer_addr_len;
+    char host[NI_MAXHOST], service[NI_MAXSERV];
+    fd_set read_fds;
+
+    sock6 = setup_v6_socket(V6_MULTICAST_ADDR, IFACE_NAME, LLMNR_PORT);
+    if (sock6 == -1) {
+        fprintf(stderr, "Failed to set up v6 socket");
+        goto die;
+    }
+
+    sock4 = setup_v4_socket(V4_MULTICAST_ADDR, IFACE_NAME, LLMNR_PORT);
+    if (sock4 == -1) {
+        fprintf(stderr, "Failed to set up v4 socket");
+        goto die;
+    }
+
     while(true) {
+        int nfds = 0;
+        int tmp_sock;
+
+        FD_ZERO(&read_fds);
+
+        FD_SET(sock6, &read_fds);
+        nfds = max(sock6, nfds);
+
+        FD_SET(sock4, &read_fds);
+        nfds = max(sock4, nfds);
+
+        err = select(nfds + 1, &read_fds, NULL, NULL, NULL);
+
+        if (err == -1 && errno == EINTR) {
+            continue;
+        }
+
+        if (err == -1) {
+            perror("select");
+            goto die;
+        }
+
+        if (FD_ISSET(sock6, &read_fds)) {
+            tmp_sock = sock6;
+        } else {
+            tmp_sock = sock4;
+        }
+
         peer_addr_len = sizeof(struct sockaddr_storage);
-        msg_len = recvfrom(sock6, buf, 1024, 0,
+        msg_len = recvfrom(tmp_sock, buf, 1024, 0,
                            (struct sockaddr *)&peer_addr, &peer_addr_len);
         if (msg_len == -1) {
             perror("receiving data");
@@ -79,7 +199,7 @@ int main(int argc, char **argv)
             continue;
         }
 
-        if(sendto(sock6, buf, msg_len, 0,
+        if(sendto(tmp_sock, buf, msg_len, 0,
                   (struct sockaddr *)&peer_addr, peer_addr_len) != msg_len) {
             perror("sending reply");
             goto die;
@@ -89,6 +209,7 @@ int main(int argc, char **argv)
 
     }
 
+    close(sock4);
     close(sock6);
     return 0;
 
@@ -97,6 +218,9 @@ die:
     if (sock6 != -1) {
         close(sock6);
     }
+    if (sock4 != -1) {
+        close(sock4);
+    }
     exit(1);
 }
 
index 1d54519..fada8e4 100644 (file)
--- a/sender.c
+++ b/sender.c
@@ -3,6 +3,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-int main(int argc, char **argv)
+#define V6_MULTICAST_ADDR "ff02::1:3"
+#define V4_MULTICAST_ADDR "224.0.0.252"
+#define IFACE_NAME "br0"
+#define LLMNR_PORT "5355"
+
+int do_request(const char *multicast_addr,
+               const char *iface_name,
+               const char *port,
+               const uint8_t *data,
+               size_t data_len)
 {
     int err;
-    int sock6;
-    int idx = if_nametoindex("br0");
+    int sock = -1;
+    int idx = if_nametoindex(iface_name);
     struct addrinfo hints, *ai, *pai;
-    uint8_t data[] = {
-        0x17, 0x2a, /* id */
-        0x00, 0x00, /* flags */
-        0x00, 0x01, /* 1 question (big endian) */
-        0x00, 0x00, /* 0 answers */
-        0x00, 0x00, /* 0 authority records */
-        0x00, 0x00, /* 0 additional records */
-        0x06, /* size of string 'franky' */
-        'f', 'r', 'a', 'n', 'k', 'y', 0x00, /* string with terminator */
-        0x00, 0x1c, /* AAAA record */
-        0x00, 0x01 /* IN class */
-    };
-    uint8_t reply[1024];
     ssize_t nread;
+    uint8_t reply[1024];
 
     memset(&hints, 0, sizeof(struct addrinfo));
-    hints.ai_family = AF_INET6;
+    hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_DGRAM;
     hints.ai_flags = 0;
     hints.ai_protocol = 0;
 
-    err = getaddrinfo("ff02::1:3", "5355", &hints, &ai);
+    err = getaddrinfo(multicast_addr, port, &hints, &ai);
     if (err != 0) {
         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
-        exit(1);
+        return -1;
     }
 
     for (pai = ai; pai != NULL; pai = pai->ai_next) {
-        sock6 = socket(pai->ai_family, pai->ai_socktype, pai->ai_protocol);
-        if (sock6 == -1) {
+        sock = socket(pai->ai_family, pai->ai_socktype, pai->ai_protocol);
+        if (sock == -1) {
             continue;
         }
 
-        err = setsockopt(sock6, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx));
+        if (pai->ai_family == AF_INET6) {
+            err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx));
+        } else {
+            struct ip_mreqn mreq;
+            memset(&mreq, 0, sizeof(struct ip_mreqn));
+
+            mreq.imr_multiaddr.s_addr = inet_addr(multicast_addr);
+            mreq.imr_ifindex = idx;
+
+            err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq,
+                             sizeof(struct ip_mreqn));
+        }
         if (err != 0) {
-            close(sock6);
+            close(sock);
             continue;
         }
 
-        if (sendto(sock6, data, sizeof(data), 0,
-                   pai->ai_addr, pai->ai_addrlen) == sizeof(data)) {
+        if (sendto(sock, data, data_len, 0,
+                   pai->ai_addr, pai->ai_addrlen) == data_len) {
             break; /* success, break out of loop */
         }
 
         perror("writing data");
-        close(sock6);
+        close(sock);
     }
 
     freeaddrinfo(ai);
 
-    nread = recvfrom(sock6, reply, 1024, 0, NULL, NULL);
+    nread = recvfrom(sock, reply, 1024, 0, NULL, NULL);
     if (nread == -1) {
         perror("reading reply");
+        return -1;;
+    }
+
+    close(sock);
+    return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+    int err;
+    uint8_t data[] = {
+        0x17, 0x2a, /* id */
+        0x00, 0x00, /* flags */
+        0x00, 0x01, /* 1 question (big endian) */
+        0x00, 0x00, /* 0 answers */
+        0x00, 0x00, /* 0 authority records */
+        0x00, 0x00, /* 0 additional records */
+        0x06, /* size of string 'franky' */
+        'f', 'r', 'a', 'n', 'k', 'y', 0x00, /* string with terminator */
+        0x00, 0x1c, /* AAAA record */
+        0x00, 0x01 /* IN class */
+    };
+
+    err = do_request(V6_MULTICAST_ADDR, IFACE_NAME, LLMNR_PORT, data,
+                     sizeof(data));
+    if (err != 0) {
+        fprintf(stderr, "Failed to sent v6 multicast query\n");
+        exit(1);
+    }
+
+    err = do_request(V4_MULTICAST_ADDR, IFACE_NAME, LLMNR_PORT, data,
+                     sizeof(data));
+    if (err != 0) {
+        fprintf(stderr, "Failed to sent v4 multicast query\n");
         exit(1);
     }
 
-    close(sock6);
     return 0;
 }