From 4ba1a4b0b4488e5058af67b789187735b529075e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Jan 2005 22:08:15 +0000 Subject: add infrastrtcur for creating and sending DNS packets git-svn-id: file:///home/lennart/svn/public/avahi/trunk@10 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- Makefile | 2 +- dns.c | 269 +++++++++++++++++++++++++++++++++++++++++ dns.h | 55 +++++++++ iface.c | 24 ++++ main.c | 11 +- server.c | 21 ++++ server.h | 2 + socket.c | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ socket.h | 10 +- util.c | 49 ++++++++ util.h | 4 + 11 files changed, 854 insertions(+), 8 deletions(-) create mode 100644 dns.c create mode 100644 dns.h create mode 100644 socket.c 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 +#include +#include + +#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 + +#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 #include #include +#include #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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#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 #include +#include +#include #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 -- cgit