summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2005-01-21 22:08:15 +0000
committerLennart Poettering <lennart@poettering.net>2005-01-21 22:08:15 +0000
commit4ba1a4b0b4488e5058af67b789187735b529075e (patch)
treed1d0f8ee6c36659b552fc371cad67678ae90b7c8
parentb25580915c7223c276348c39d5e7ed496a58a26e (diff)
add infrastrtcur for creating and sending DNS packets
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@10 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
-rw-r--r--Makefile2
-rw-r--r--dns.c269
-rw-r--r--dns.h55
-rw-r--r--iface.c24
-rw-r--r--main.c11
-rw-r--r--server.c21
-rw-r--r--server.h2
-rw-r--r--socket.c415
-rw-r--r--socket.h10
-rw-r--r--util.c49
-rw-r--r--util.h4
11 files changed, 854 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index 543fb6c..6dac8af 100644
--- 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
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
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 6ea7054..d3f4719 100644
--- 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 d8c2065..b366eb5 100644
--- 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);
diff --git a/server.c b/server.c
index 79c017f..f85de45 100644
--- 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);
diff --git a/server.h b/server.h
index 982143b..b1cc57b 100644
--- 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
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;
+}
+
diff --git a/socket.h b/socket.h
index 8d8b128..472b115 100644
--- 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 62574ba..faf1ac0 100644
--- 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 9c3edd1..78f86d2 100644
--- 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