]> git.meshlink.io Git - catta/commitdiff
add infrastrtcur for creating and sending DNS packets
authorLennart Poettering <lennart@poettering.net>
Fri, 21 Jan 2005 22:08:15 +0000 (22:08 +0000)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Jan 2005 22:08:15 +0000 (22:08 +0000)
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@10 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe

Makefile
dns.c [new file with mode: 0644]
dns.h [new file with mode: 0644]
iface.c
main.c
server.c
server.h
socket.c [new file with mode: 0644]
socket.h
util.c
util.h

index 543fb6c31bcd1ac6a122b0241fa74a6ae10bfbc9..6dac8af1dd5730cee315f756a95369ac48465eb1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ LIBS=$(shell pkg-config --libs glib-2.0)
 
 all: flexmdns prioq-test
 
-flexmdns: timeeventq.o main.o iface.o netlink.o server.o address.o util.o prioq.o cache.o rr.o
+flexmdns: timeeventq.o main.o iface.o netlink.o server.o address.o util.o prioq.o cache.o rr.o dns.o socket.o
        $(CC) -o $@ $^ $(LIBS)
 
 #test-llist: test-llist.o
diff --git a/dns.c b/dns.c
new file mode 100644 (file)
index 0000000..1ac7e6a
--- /dev/null
+++ b/dns.c
@@ -0,0 +1,269 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "dns.h"
+
+flxDnsPacket* flx_dns_packet_new(void) {
+    flxDnsPacket *p;
+    p = g_new(flxDnsPacket, 1);
+    p->size = p->rindex = 2*6;
+    memset(p->data, 0, p->size);
+    return p;
+}
+
+void flx_dns_packet_free(flxDnsPacket *p) {
+    g_assert(p);
+    g_free(p);
+}
+
+void flx_dns_packet_set_field(flxDnsPacket *p, guint index, guint16 v) {
+    g_assert(p);
+    g_assert(index < 2*6);
+    
+    ((guint16*) p->data)[index] = g_htons(v);
+}
+
+guint16 flx_dns_packet_get_field(flxDnsPacket *p, guint index) {
+    g_assert(p);
+    g_assert(index < 2*6);
+
+    return g_ntohs(((guint16*) p->data)[index]);
+}
+
+guint8* flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name) {
+    guint8 *d, *f = NULL;
+    
+    g_assert(p);
+    g_assert(name);
+
+    for (;;) {
+        guint n = strcspn(name, ".");
+        if (!n || n > 63)
+            return NULL;
+        
+        d = flx_dns_packet_extend(p, n+1);
+        if (!f)
+            f = d;
+        d[0] = n;
+        memcpy(d+1, name, n);
+
+        name += n;
+
+        /* no trailing dot */
+        if (!*name)
+            break;
+
+        name ++;
+
+        /* trailing dot */
+        if (!*name)
+            break;
+    }
+
+    d = flx_dns_packet_extend(p, 1);
+    d[0] = 0;
+
+    return f;
+}
+
+guint8* flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v) {
+    guint8 *d;
+    
+    g_assert(p);
+    
+    d = flx_dns_packet_extend(p, sizeof(guint16));
+    *((guint16*) d) = g_htons(v);
+    
+    return d;
+}
+
+guint8 *flx_dns_packet_extend(flxDnsPacket *p, guint l) {
+    guint8 *d;
+    
+    g_assert(p);
+    g_assert(p->size+l <= sizeof(p->data));
+
+    d = p->data + p->size;
+    p->size += l;
+    
+    return d;
+}
+
+guint8 *flx_dns_packet_append_name_compressed(flxDnsPacket *p, const gchar *name, guint8 *prev) {
+    guint16 *d;
+    signed long k;
+    g_assert(p);
+
+    if (!prev)
+        return flx_dns_packet_append_name(p, name);
+    
+    k = prev - p->data;
+    if (k < 0 || k >= 0x4000 || (guint) k >= p->size)
+        return flx_dns_packet_append_name(p, name);
+
+    d = (guint16*) flx_dns_packet_extend(p, sizeof(guint16));
+    *d = g_htons((0xC000 | k));
+    
+    return prev;
+}
+
+gint flx_dns_packet_check_valid(flxDnsPacket *p) {
+    guint16 flags;
+    g_assert(p);
+
+    if (p->size < 12)
+        return -1;
+
+    flags = flx_dns_packet_get_field(p, DNS_FIELD_FLAGS);
+
+    if (flags & DNS_FLAG_OPCODE || flags & DNS_FLAG_RCODE)
+        return -1;
+
+    return 0;
+}
+
+gint flx_dns_packet_check_valid_response(flxDnsPacket *p) {
+    guint16 flags;
+    g_assert(p);
+    
+    if (flx_dns_packet_check_valid(p) < 0)
+        return -1;
+
+    flags = flx_dns_packet_get_field(p, DNS_FIELD_FLAGS);
+
+    if (!(flags & DNS_FLAG_QR))
+        return -1;
+
+    if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) > 0)
+        return -1;
+
+    return 0;
+}
+
+static gint consume_labels(flxDnsPacket *p, guint index, gchar *ret_name, guint l) {
+    gint ret = 0;
+    int compressed = 0;
+    int first_label = 1;
+    g_assert(p && ret_name && l);
+    
+    for (;;) {
+        guint8 n;
+
+        if (index+1 > p->size)
+            return -1;
+
+        n = p->data[index];
+
+        if (!n) {
+            index++;
+            if (!compressed)
+                ret++;
+
+            if (l < 1)
+                return -1;
+            *ret_name = 0;
+            
+            return ret;
+            
+        } else if (n <= 63) {
+            /* Uncompressed label */
+            index++;
+            if (!compressed)
+                ret++;
+        
+            if (index + n > p->size)
+                return -1;
+
+            if ((guint) n + 1 > l)
+                return -1;
+
+            if (!first_label) {
+                *(ret_name++) = '.';
+                l--;
+            } else
+                first_label = 0;
+
+            memcpy(ret_name, p->data + index, n);
+            index += n;
+            ret_name += n;
+            l -= n;
+            
+            if (!compressed)
+                ret += n;
+        } else if ((n & 0xC0) == 0xC0) {
+            /* Compressed label */
+
+            if (index+2 > p->size)
+                return -1;
+
+            index = ((guint) (p->data[index] & ~0xC0)) << 8 | p->data[index+1];
+
+            if (!compressed)
+                ret += 2;
+            
+            compressed = 1;
+        } else
+            return -1;
+    }
+}
+
+gint flx_dns_packet_consume_name(flxDnsPacket *p, gchar *ret_name, guint l) {
+    gint r;
+    
+    if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
+        return -1;
+
+    p->rindex += r;
+    return 0;
+}
+
+gint flx_dns_packet_consume_uint16(flxDnsPacket *p, guint16 *ret_v) {
+    g_assert(p);
+    g_assert(ret_v);
+
+    if (p->rindex + sizeof(guint16) > p->size)
+        return -1;
+
+    *ret_v = g_ntohs(*((guint16*) (p->data + p->rindex)));
+    p->rindex += sizeof(guint16);
+
+    return 0;
+}
+
+gint flx_dns_packet_consume_uint32(flxDnsPacket *p, guint32 *ret_v) {
+    g_assert(p);
+    g_assert(ret_v);
+
+    if (p->rindex + sizeof(guint32) > p->size)
+        return -1;
+
+    *ret_v = g_ntohl(*((guint32*) (p->data + p->rindex)));
+    p->rindex += sizeof(guint32);
+    
+    return 0;
+}
+
+gint flx_dns_packet_consume_bytes(flxDnsPacket *p, gpointer ret_data, guint l) {
+    g_assert(p);
+    g_assert(ret_data);
+    g_assert(l > 0);
+    
+    if (p->rindex + l > p->size)
+        return -1;
+
+    memcpy(ret_data, p->data + p->rindex, l);
+    p->rindex += l;
+
+    return 0;
+}
+
+gint flx_dns_packet_skip(flxDnsPacket *p, guint length) {
+    g_assert(p);
+
+    if (p->rindex + length > p->size)
+        return -1;
+
+    p->rindex += length;
+    return 0;
+}
diff --git a/dns.h b/dns.h
new file mode 100644 (file)
index 0000000..5815a9a
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,55 @@
+#ifndef foodnshfoo
+#define foodnshfoo
+
+#include <glib.h>
+
+#define FLX_DNS_MAX_PACKET_SIZE 9000
+
+typedef struct _flxDnsPacket {
+    guint size, rindex;
+    guint8 data[FLX_DNS_MAX_PACKET_SIZE];
+} flxDnsPacket;
+
+flxDnsPacket* flx_dns_packet_new(void);
+void flx_dns_packet_free(flxDnsPacket *p);
+void flx_dns_packet_set_field(flxDnsPacket *p, guint index, guint16 v);
+guint16 flx_dns_packet_get_field(flxDnsPacket *p, guint index);
+
+guint8 *flx_dns_packet_append_uint16(flxDnsPacket *p, guint16 v);
+guint8 *flx_dns_packet_append_name(flxDnsPacket *p, const gchar *name);
+guint8 *flx_dns_packet_append_name_compressed(flxDnsPacket *p, const gchar *name, guint8 *prev);
+guint8 *flx_dns_packet_extend(flxDnsPacket *p, guint l);
+gint flx_dns_packet_check_valid_response(flxDnsPacket *p);
+gint flx_dns_packet_check_valid(flxDnsPacket *p);
+
+gint flx_dns_packet_consume_name(flxDnsPacket *p, gchar *ret_name, guint l);
+gint flx_dns_packet_consume_uint16(flxDnsPacket *p, guint16 *ret_v);
+gint flx_dns_packet_consume_uint32(flxDnsPacket *p, guint32 *ret_v);
+gint flx_dns_packet_consume_bytes(flxDnsPacket *p, gpointer ret_data, guint l);
+gint flx_dns_packet_skip(flxDnsPacket *p, guint length);
+
+#define DNS_FIELD_ID 0
+#define DNS_FIELD_FLAGS 1
+#define DNS_FIELD_QDCOUNT 2
+#define DNS_FIELD_ANCOUNT 3
+#define DNS_FIELD_NSCOUNT 4
+#define DNS_FIELD_ARCOUNT 5
+
+#define DNS_FLAG_QR (1 << 15)
+#define DNS_FLAG_OPCODE (15 << 11)
+#define DNS_FLAG_RCODE (15)
+
+#define DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \
+        (((guint16) !!qr << 15) |  \
+         ((guint16) (opcode & 15) << 11) | \
+         ((guint16) !!aa << 10) | \
+         ((guint16) !!tc << 9) | \
+         ((guint16) !!rd << 8) | \
+         ((guint16) !!ra << 7) | \
+         ((guint16) !!ad << 5) | \
+         ((guint16) !!cd << 4) | \
+         ((guint16) (rd & 15)))
+         
+
+#endif
+
diff --git a/iface.c b/iface.c
index 6ea7054e38439ad4b2270e8f7b5a56635ec91983..d3f47196f9886b65faa328da0ebf63db0a295707 100644 (file)
--- a/iface.c
+++ b/iface.c
@@ -8,6 +8,8 @@
 
 #include "iface.h"
 #include "netlink.h"
+#include "dns.h"
+#include "socket.h"
 
 static void update_address_rr(flxInterfaceMonitor *m, flxInterfaceAddress *a, int remove) {
     g_assert(m);
@@ -343,6 +345,28 @@ int flx_address_is_relevant(flxInterfaceAddress *a) {
 }
 
 void flx_interface_send_query(flxInterface *i, guchar protocol, flxKey *k) {
+    flxDnsPacket *p;
     g_assert(i);
     g_assert(k);
+
+    p = flx_dns_packet_new();
+    flx_dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+    flx_dns_packet_append_name(p, k->name);
+    flx_dns_packet_append_uint16(p, k->type);
+    flx_dns_packet_append_uint16(p, k->class);
+
+    flx_dns_packet_set_field(p, DNS_FIELD_QDCOUNT, 1);
+
+    if ((protocol == AF_INET || protocol == AF_UNSPEC) && i->n_ipv4_addrs > 0 && flx_interface_is_relevant(i)) {
+        g_message("sending on '%s':IPv4", i->name);
+        flx_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->index, p);
+    }
+
+    if ((protocol == AF_INET6 || protocol == AF_UNSPEC) && i->n_ipv6_addrs > 0 && flx_interface_is_relevant(i)) {
+        g_message("sending on '%s':IPv6", i->name);
+        flx_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->index, p);
+    }
+    
+    flx_dns_packet_free(p);
 }
diff --git a/main.c b/main.c
index d8c2065ab6e4e73db543045b60779c1371c9cbca..b366eb5e293a54370579c51c9f49ef17935a4036 100644 (file)
--- a/main.c
+++ b/main.c
@@ -20,15 +20,16 @@ int main(int argc, char *argv[]) {
 
     flx_server_add_text(flx, 0, 0, AF_UNSPEC, FALSE, NULL, "hallo");
 
-    k = flx_key_new("cocaine.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A);
-    flx_server_send_query(flx, 0, AF_UNSPEC, k);
-    flx_key_unref(k);
-
     loop = g_main_loop_new(NULL, FALSE);
     
-    g_timeout_add(5000, timeout, loop);
+    g_timeout_add(1000, timeout, loop);
     
     g_main_loop_run(loop);
+
+    k = flx_key_new("cocaine.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A);
+    flx_server_send_query(flx, 0, AF_UNSPEC, k);
+    flx_key_unref(k);
+
     g_main_loop_unref(loop);
 
     flx_server_dump(flx, stdout);
index 79c017fe11cd57d280583e78ed60ae77611c0de6..f85de456636193bceeed7982ab57eb3cb5d58ac7 100644 (file)
--- a/server.c
+++ b/server.c
@@ -2,10 +2,12 @@
 #include <arpa/inet.h>
 #include <string.h>
 #include <sys/utsname.h>
+#include <unistd.h>
 
 #include "server.h"
 #include "util.h"
 #include "iface.h"
+#include "socket.h"
 
 static void add_default_entries(flxServer *s) {
     gint length = 0;
@@ -38,6 +40,20 @@ flxServer *flx_server_new(GMainContext *c) {
 
     s = g_new(flxServer, 1);
 
+    s->fd_ipv4 = flx_open_socket_ipv4();
+    s->fd_ipv6 = flx_open_socket_ipv6();
+    
+    if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
+        g_critical("Failed to create sockets.\n");
+        g_free(s);
+        return NULL;
+    }
+
+    if (s->fd_ipv4 < 0)
+        g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
+    else if (s->fd_ipv6 < 0)
+        g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
+    
     if (c)
         g_main_context_ref(s->context = c);
     else
@@ -77,6 +93,11 @@ void flx_server_free(flxServer* s) {
 
     flx_time_event_queue_free(s->time_event_queue);
     g_main_context_unref(s->context);
+
+    if (s->fd_ipv4 >= 0)
+        close(s->fd_ipv4);
+    if (s->fd_ipv6 >= 0)
+        close(s->fd_ipv6);
     
     g_free(s->hostname);
     g_free(s);
index 982143b038bf48d56222b76aa3a38efd5fb49c2d..b1cc57bbad2dfdc6a464091abb99e4985f4ea325 100644 (file)
--- a/server.h
+++ b/server.h
@@ -43,6 +43,8 @@ struct _flxServer {
     flxTimeEventQueue *time_event_queue;
     
     gchar *hostname;
+
+    gint fd_ipv4, fd_ipv6;
 };
 
 
diff --git a/socket.c b/socket.c
new file mode 100644 (file)
index 0000000..a77f631
--- /dev/null
+++ b/socket.c
@@ -0,0 +1,415 @@
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "dns.h"
+#include "util.h"
+
+#define MDNS_PORT 5353
+
+static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
+    g_assert(ret_sa);
+
+    memset(ret_sa, 0, sizeof(struct sockaddr_in));
+    
+    ret_sa->sin_family = AF_INET;
+    ret_sa->sin_port = htons(MDNS_PORT);
+    inet_pton(AF_INET, "224.0.0.251", &ret_sa->sin_addr);
+}
+
+gint flx_open_socket_ipv4(void) {
+    struct ip_mreqn mreq;
+    struct sockaddr_in sa, local;
+    int fd = -1, ttl, yes;
+
+    mdns_mcast_group_ipv4(&sa);
+        
+    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        g_warning("socket() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    ttl = 255;
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
+        g_warning("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    ttl = 255;
+    if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
+        g_warning("IP_TTL failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    yes = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+        g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    memset(&local, 0, sizeof(local));
+    local.sin_family = AF_INET;
+    local.sin_port = htons(MDNS_PORT);
+    
+    if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
+        g_warning("bind() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    memset(&mreq, 0, sizeof(mreq));
+    mreq.imr_multiaddr = sa.sin_addr;
+    mreq.imr_address.s_addr = htonl(INADDR_ANY);
+    mreq.imr_ifindex = 0;
+    
+    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+        g_warning("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    yes = 1;
+    if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
+        g_warning("IP_RECVTTL failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    yes = 1;
+    if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
+        g_warning("IP_PKTINFO failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    if (flx_set_cloexec(fd) < 0) {
+        g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    if (flx_set_nonblock(fd) < 0) {
+        g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        close(fd);
+
+    return -1;
+}
+
+static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
+    g_assert(ret_sa);
+
+    memset(ret_sa, 0, sizeof(struct sockaddr_in6));
+    
+    ret_sa->sin6_family = AF_INET6;
+    ret_sa->sin6_port = htons(MDNS_PORT);
+    inet_pton(AF_INET6, "ff02::fb", &ret_sa->sin6_addr);
+}
+
+
+gint flx_open_socket_ipv6(void) {
+    struct ipv6_mreq mreq;
+    struct sockaddr_in6 sa, local;
+    int fd = -1, ttl, yes;
+
+    mdns_mcast_group_ipv6(&sa);
+        
+    if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+        g_warning("socket() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    ttl = 255;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
+        g_warning("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    ttl = 255;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
+        g_warning("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    yes = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+        g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    memset(&local, 0, sizeof(local));
+    local.sin6_family = AF_INET6;
+    local.sin6_port = htons(MDNS_PORT);
+    
+    if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
+        g_warning("bind() failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    memset(&mreq, 0, sizeof(mreq));
+    mreq.ipv6mr_multiaddr = sa.sin6_addr;
+    mreq.ipv6mr_interface = 0;
+    
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+        g_warning("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    yes = 1;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
+        g_warning("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    yes = 1;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
+        g_warning("IPV6_PKTINFO failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    if (flx_set_cloexec(fd) < 0) {
+        g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
+        goto fail;
+    }
+    
+    if (flx_set_nonblock(fd) < 0) {
+        g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    return fd;
+
+fail:
+    if (fd >= 0)
+        close(fd);
+
+    return -1;
+}
+
+static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
+    g_assert(fd >= 0);
+    g_assert(msg);
+
+    for (;;) {
+    
+        if (sendmsg(fd, msg, flags) >= 0)
+            break;
+        
+        if (errno != EAGAIN) {
+            g_message("sendmsg() failed: %s\n", strerror(errno));
+            return -1;
+        }
+        
+        if (flx_wait_for_write(fd) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+gint flx_send_dns_packet_ipv4(gint fd, gint interface, flxDnsPacket *p) {
+    struct sockaddr_in sa;
+    struct msghdr msg;
+    struct iovec io;
+    struct cmsghdr *cmsg;
+    struct in_pktinfo *pkti;
+    uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
+    int i, n;
+
+    g_assert(fd >= 0);
+    g_assert(p);
+    g_assert(flx_dns_packet_check_valid(p) >= 0);
+
+    mdns_mcast_group_ipv4(&sa);
+
+    memset(&io, 0, sizeof(io));
+    io.iov_base = p->data;
+    io.iov_len = p->size;
+
+    memset(cmsg_data, 0, sizeof(cmsg_data));
+    cmsg = (struct cmsghdr*) cmsg_data;
+    cmsg->cmsg_len = sizeof(cmsg_data);
+    cmsg->cmsg_level = IPPROTO_IP;
+    cmsg->cmsg_type = IP_PKTINFO;
+
+    pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
+    pkti->ipi_ifindex = interface;
+    
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = &sa;
+    msg.msg_namelen = sizeof(sa);
+    msg.msg_iov = &io;
+    msg.msg_iovlen = 1;
+    msg.msg_control = cmsg_data;
+    msg.msg_controllen = sizeof(cmsg_data);
+    msg.msg_flags = 0;
+
+    return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
+}
+
+gint flx_send_dns_packet_ipv6(gint fd, gint interface, flxDnsPacket *p) {
+    struct sockaddr_in6 sa;
+    struct msghdr msg;
+    struct iovec io;
+    struct cmsghdr *cmsg;
+    struct in6_pktinfo *pkti;
+    uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
+    int i, n;
+
+    g_assert(fd >= 0);
+    g_assert(p);
+    g_assert(flx_dns_packet_check_valid(p) >= 0);
+
+    mdns_mcast_group_ipv6(&sa);
+
+    memset(&io, 0, sizeof(io));
+    io.iov_base = p->data;
+    io.iov_len = p->size;
+
+    memset(cmsg_data, 0, sizeof(cmsg_data));
+    cmsg = (struct cmsghdr*) cmsg_data;
+    cmsg->cmsg_len = sizeof(cmsg_data);
+    cmsg->cmsg_level = IPPROTO_IPV6;
+    cmsg->cmsg_type = IPV6_PKTINFO;
+
+    pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
+    pkti->ipi6_ifindex = interface;
+    
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = &sa;
+    msg.msg_namelen = sizeof(sa);
+    msg.msg_iov = &io;
+    msg.msg_iovlen = 1;
+    msg.msg_control = cmsg_data;
+    msg.msg_controllen = sizeof(cmsg_data);
+    msg.msg_flags = 0;
+
+    return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
+}
+
+flxDnsPacket* flx_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
+    flxDnsPacket *p= NULL;
+    struct msghdr msg;
+    struct iovec io;
+    uint8_t aux[64];
+    ssize_t l;
+    struct cmsghdr *cmsg;
+    gboolean found_ttl = FALSE, found_iface = FALSE;
+
+    g_assert(fd >= 0);
+    g_assert(ret_sa);
+    g_assert(ret_iface);
+    g_assert(ret_ttl);
+
+    p = flx_dns_packet_new();
+
+    io.iov_base = p->data;
+    io.iov_len = sizeof(p->data);
+    
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = ret_sa;
+    msg.msg_namelen = sizeof(struct sockaddr_in);
+    msg.msg_iov = &io;
+    msg.msg_iovlen = 1;
+    msg.msg_control = aux;
+    msg.msg_controllen = sizeof(aux);
+    msg.msg_flags = 0;
+    
+    if ((l = recvmsg(fd, &msg, 0)) < 0)
+        goto fail;
+
+    p->size = (size_t) l;
+    
+    *ret_ttl = 0;
+        
+    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
+        if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) {
+            *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
+            found_ttl = TRUE;
+        }
+            
+        if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) {
+            *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
+            found_iface = TRUE;
+        }
+    }
+
+    g_assert(found_iface && found_ttl);
+
+    return p;
+
+fail:
+    if (p)
+        flx_dns_packet_free(p);
+
+    return NULL;
+}
+
+flxDnsPacket* flx_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
+    flxDnsPacket *p= NULL;
+    struct msghdr msg;
+    struct iovec io;
+    uint8_t aux[64];
+    ssize_t l;
+    struct cmsghdr *cmsg;
+    gboolean found_ttl = FALSE, found_iface = FALSE;
+
+    g_assert(fd >= 0);
+    g_assert(ret_sa);
+    g_assert(ret_iface);
+    g_assert(ret_ttl);
+
+    p = flx_dns_packet_new();
+
+    io.iov_base = p->data;
+    io.iov_len = sizeof(p->data);
+    
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = ret_sa;
+    msg.msg_namelen = sizeof(struct sockaddr_in6);
+    msg.msg_iov = &io;
+    msg.msg_iovlen = 1;
+    msg.msg_control = aux;
+    msg.msg_controllen = sizeof(aux);
+    msg.msg_flags = 0;
+    
+    if ((l = recvmsg(fd, &msg, 0)) < 0)
+        goto fail;
+
+    p->size = (size_t) l;
+    
+    *ret_ttl = 0;
+        
+    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IPV6_HOPLIMIT) {
+            *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
+            found_ttl = TRUE;
+        }
+            
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IPV6_PKTINFO) {
+            *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
+            found_iface = TRUE;
+        }
+    }
+
+    g_assert(found_iface && found_ttl);
+
+    return p;
+
+fail:
+    if (p)
+        flx_dns_packet_free(p);
+
+    return NULL;
+}
+
index 8d8b1289273f099dccf65e4fdec2ec56cb83f707..472b1150920ed861d22f6ba6b20238c0c2ae15b0 100644 (file)
--- a/socket.h
+++ b/socket.h
@@ -1,11 +1,17 @@
 #ifndef foosockethfoo
 #define foosockethfoo
 
-int flx_open_socket(int iface);
+#include <netinet/in.h>
 
+#include "dns.h"
 
-int flx_send_packet(int fd, int iface, struct flx_dns_packet *p);
+gint flx_open_socket_ipv4(void);
+gint flx_open_socket_ipv6(void);
 
+gint flx_send_dns_packet_ipv4(gint fd, gint iface, flxDnsPacket *p);
+gint flx_send_dns_packet_ipv6(gint fd, gint iface, flxDnsPacket *p);
 
+flxDnsPacket *flx_recv_dns_packet_ipv4(gint fd, struct sockaddr_in*ret_sa, gint *ret_iface, gint *ret_ttl);
+flxDnsPacket *flx_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6*ret_sa, gint *ret_iface, gint *ret_ttl);
 
 #endif
diff --git a/util.c b/util.c
index 62574ba524d83920bf9b8f448ef3f90deeae3987..faf1ac068e03aa89f6b4e53981ac426e19b79b39 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,5 +1,7 @@
 #include <string.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include "util.h"
 
@@ -50,3 +52,50 @@ glong flx_timeval_diff(const GTimeVal *a, const GTimeVal *b) {
 
     return (a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec;
 }
+
+
+gint flx_set_cloexec(gint fd) {
+    gint n;
+
+    g_assert(fd >= 0);
+    
+    if ((n = fcntl(fd, F_GETFD)) < 0)
+        return -1;
+
+    if (n & FD_CLOEXEC)
+        return 0;
+
+    return fcntl(fd, F_SETFD, n|FD_CLOEXEC);
+}
+
+gint flx_set_nonblock(gint fd) {
+    gint n;
+
+    g_assert(fd >= 0);
+
+    if ((n = fcntl(fd, F_GETFL)) < 0)
+        return -1;
+
+    if (n & O_NONBLOCK)
+        return 0;
+
+    return fcntl(fd, F_SETFL, n|O_NONBLOCK);
+}
+
+gint flx_wait_for_write(gint fd) {
+    fd_set fds;
+    gint r;
+    
+    FD_ZERO(&fds);
+    FD_SET(fd, &fds);
+    
+    if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) {
+        g_message("select() failed: %s", strerror(errno));
+
+        return -1;
+    }
+    
+    g_assert(r > 0);
+
+    return 0;
+}
diff --git a/util.h b/util.h
index 9c3edd17a379930abf77f5be2666d78f153da10f..78f86d2e196d457ee38cbd013b5a35d23fcd9029 100644 (file)
--- a/util.h
+++ b/util.h
@@ -9,4 +9,8 @@ gchar *flx_get_host_name(void); /* g_free() the result! */
 gint flx_timeval_compare(const GTimeVal *a, const GTimeVal *b);
 glong flx_timeval_diff(const GTimeVal *a, const GTimeVal *b);
 
+gint flx_set_cloexec(gint fd);
+gint flx_set_nonblock(gint fd);
+gint flx_wait_for_write(gint fd);
+
 #endif