/*
This file is part of GNUnet.
- (C) 2012 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2012 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
*/
/**
* @file dns/gnunet-service-dns.c
+ * @brief service to intercept and modify DNS queries (and replies) of this system
* @author Christian Grothoff
+ *
+ * For "secure" interaction with the legacy DNS system, we permit
+ * replies only to arrive within a 5s window (and they must match
+ * ports, IPs and request IDs). Furthermore, we let the OS pick a
+ * source port, opening up to 128 sockets per address family (IPv4 or
+ * IPv6). Those sockets are closed if they are not in use for 5s
+ * (which means they will be freshly randomized afterwards). For new
+ * requests, we pick a random slot in the array with 128 socket slots
+ * (and re-use an existing socket if the slot is still in use). Thus
+ * each request will be given one of 128 random source ports, and the
+ * 128 random source ports will also change "often" (less often if the
+ * system is very busy, each time if we are mostly idle). At the same
+ * time, the system will never use more than 256 UDP sockets.
*/
#include "platform.h"
#include "gnunet_util_lib.h"
+#include "gnunet_applications.h"
#include "gnunet_constants.h"
#include "gnunet_protocols.h"
#include "gnunet_signatures.h"
#include "dns.h"
#include "gnunet_dns_service.h"
+#include "gnunet_dnsparser_lib.h"
+#include "gnunet_dnsstub_lib.h"
#include "gnunet_statistics_service.h"
#include "gnunet_tun_lib.h"
+/**
+ * Port number for DNS
+ */
+#define DNS_PORT 53
+
+
+/**
+ * Generic logging shorthand
+ */
+#define LOG(kind, ...) \
+ GNUNET_log_from (kind, "dns", __VA_ARGS__);
+
/**
* Phases each request goes through.
* Global Internet query is now pending.
*/
RP_INTERNET_DNS,
-
+
/**
* Client (or global DNS request) has resulted in a response.
* Forward to all POST-RESOLUTION clients. If client list is empty,
/**
* Entry we keep for each client.
- */
+ */
struct ClientRecord
{
/**
* Kept in doubly-linked list.
- */
+ */
struct ClientRecord *next;
/**
* Kept in doubly-linked list.
- */
+ */
struct ClientRecord *prev;
/**
* Handle to the client.
- */
+ */
struct GNUNET_SERVER_Client *client;
/**
/**
* Entry we keep for each active request.
- */
+ */
struct RequestRecord
{
*/
char *payload;
+ /**
+ * Socket we are using to transmit this request (must match if we receive
+ * a response).
+ */
+ struct GNUNET_DNSSTUB_RequestSocket *rs;
+
/**
* Source address of the original request (for sending response).
*/
/**
* Number of bytes in payload.
- */
+ */
size_t payload_length;
/**
/**
- * The IPv4 UDP-Socket through which DNS-Resolves will be sent if they are not to be
- * sent through gnunet. The port of this socket will not be hijacked.
- */
-static struct GNUNET_NETWORK_Handle *dnsout4;
-
-/**
- * The IPv6 UDP-Socket through which DNS-Resolves will be sent if they are not to be
- * sent through gnunet. The port of this socket will not be hijacked.
- */
-static struct GNUNET_NETWORK_Handle *dnsout6;
-
-/**
- * Task for reading from dnsout4.
- */
-static GNUNET_SCHEDULER_TaskIdentifier read4_task;
-
-/**
- * Task for reading from dnsout6.
+ * Global return value from 'main'.
*/
-static GNUNET_SCHEDULER_TaskIdentifier read6_task;
-
-/**
- * The port bound to the socket dnsout (and/or dnsout6). We always (try) to bind
- * both sockets to the same port.
- */
-static uint16_t dnsoutport;
+static int global_ret;
/**
* The configuration to use
*/
static uint64_t request_id_gen;
+/**
+ * Handle to the DNS Stub resolver.
+ */
+static struct GNUNET_DNSSTUB_Context *dnsstub;
+
/**
* We're done processing a DNS request, free associated memory.
* Task run during shutdown.
*
* @param cls unused
- * @param tc unused
*/
static void
-cleanup_task (void *cls GNUNET_UNUSED,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+cleanup_task (void *cls GNUNET_UNUSED)
{
unsigned int i;
- GNUNET_HELPER_stop (hijacker);
- hijacker = NULL;
- for (i=0;i<8;i++)
- GNUNET_free_non_null (helper_argv[i]);
- if (NULL != dnsout4)
- {
- GNUNET_NETWORK_socket_close (dnsout4);
- dnsout4 = NULL;
- }
- if (GNUNET_SCHEDULER_NO_TASK != read4_task)
- {
- GNUNET_SCHEDULER_cancel (read4_task);
- read4_task = GNUNET_SCHEDULER_NO_TASK;
- }
- if (NULL != dnsout6)
+ if (NULL != hijacker)
{
- GNUNET_NETWORK_socket_close (dnsout6);
- dnsout6 = NULL;
+ GNUNET_HELPER_stop (hijacker, GNUNET_NO);
+ hijacker = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != read6_task)
- {
- GNUNET_SCHEDULER_cancel (read6_task);
- read6_task = GNUNET_SCHEDULER_NO_TASK;
- }
- for (i=0;i<65536;i++)
+ for (i=0;i<8;i++)
+ GNUNET_free_non_null (helper_argv[i]);
+ for (i=0;i<=UINT16_MAX;i++)
cleanup_rr (&requests[i]);
GNUNET_SERVER_notification_context_destroy (nc);
nc = NULL;
if (stats != NULL)
{
- GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
+ GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
stats = NULL;
}
+ if (NULL != dnsstub)
+ {
+ GNUNET_DNSSTUB_stop (dnsstub);
+ dnsstub = NULL;
+ }
}
{
struct GNUNET_MessageHeader *hdr;
size_t reply_len;
- uint16_t spt;
- uint16_t dpt;
+ uint16_t source_port;
+ uint16_t destination_port;
GNUNET_array_grow (rr->client_wait_list,
rr->client_wait_list_length,
- 0);
+ 0);
if (RP_RESPONSE_MONITOR != rr->phase)
{
/* no response, drop */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got no response for request %llu, dropping\n",
+ (unsigned long long) rr->request_id);
cleanup_rr (rr);
return;
}
-
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmitting response for request %llu\n",
+ (unsigned long long) rr->request_id);
/* send response via hijacker */
reply_len = sizeof (struct GNUNET_MessageHeader);
reply_len += sizeof (struct GNUNET_TUN_Layer2PacketHeader);
default:
GNUNET_break (0);
cleanup_rr (rr);
- return;
+ return;
}
reply_len += sizeof (struct GNUNET_TUN_UdpHeader);
reply_len += rr->payload_length;
/* response too big, drop */
GNUNET_break (0); /* how can this be? */
cleanup_rr(rr);
- return;
+ return;
}
{
- char buf[reply_len];
+ char buf[reply_len] GNUNET_ALIGN;
size_t off;
- uint32_t udp_crc_sum;
+ struct GNUNET_TUN_IPv4Header ip4;
+ struct GNUNET_TUN_IPv6Header ip6;
/* first, GNUnet message header */
hdr = (struct GNUNET_MessageHeader*) buf;
tun.flags = htons (0);
if (rr->src_addr.ss_family == AF_INET)
- tun.proto = htons (ETH_P_IPV4);
+ tun.proto = htons (ETH_P_IPV4);
else
tun.proto = htons (ETH_P_IPV6);
memcpy (&buf[off], &tun, sizeof (struct GNUNET_TUN_Layer2PacketHeader));
}
/* now IP header */
- udp_crc_sum = 0;
switch (rr->src_addr.ss_family)
{
case AF_INET:
{
struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
- struct GNUNET_TUN_IPv4Header ip;
-
- spt = dst->sin_port;
- dpt = src->sin_port;
- GNUNET_TUN_initialize_ipv4_header (&ip,
+
+ source_port = dst->sin_port;
+ destination_port = src->sin_port;
+ GNUNET_TUN_initialize_ipv4_header (&ip4,
IPPROTO_UDP,
reply_len - off - sizeof (struct GNUNET_TUN_IPv4Header),
&dst->sin_addr,
&src->sin_addr);
-
-
-
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- &ip.source_address,
- sizeof (struct in_addr) * 2);
- {
- uint16_t tmp;
-
- tmp = htons (IPPROTO_UDP);
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- &tmp,
- sizeof (uint16_t));
- tmp = htons (rr->payload_length + sizeof (struct GNUNET_TUN_UdpHeader));
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- &tmp,
- sizeof (uint16_t));
- }
- memcpy (&buf[off], &ip, sizeof (ip));
- off += sizeof (ip);
+ memcpy (&buf[off], &ip4, sizeof (ip4));
+ off += sizeof (ip4);
}
break;
case AF_INET6:
{
struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
- struct GNUNET_TUN_IPv6Header ip;
- spt = dst->sin6_port;
- dpt = src->sin6_port;
- GNUNET_TUN_initialize_ipv6_header (&ip,
+ source_port = dst->sin6_port;
+ destination_port = src->sin6_port;
+ GNUNET_TUN_initialize_ipv6_header (&ip6,
IPPROTO_UDP,
reply_len - sizeof (struct GNUNET_TUN_IPv6Header),
&dst->sin6_addr,
&src->sin6_addr);
- {
- uint32_t tmp;
-
- tmp = htons (rr->payload_length + sizeof (struct GNUNET_TUN_UdpHeader));
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- &tmp,
- sizeof (uint32_t));
- tmp = htons (IPPROTO_UDP);
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- &tmp,
- sizeof (uint32_t));
- }
- memcpy (&buf[off], &ip, sizeof (ip));
- off += sizeof (ip);
+ memcpy (&buf[off], &ip6, sizeof (ip6));
+ off += sizeof (ip6);
}
break;
default:
{
struct GNUNET_TUN_UdpHeader udp;
- udp.spt = spt;
- udp.dpt = dpt;
+ udp.source_port = source_port;
+ udp.destination_port = destination_port;
udp.len = htons (reply_len - off);
- udp.crc = 0;
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- &udp,
- sizeof (udp));
- udp_crc_sum = GNUNET_CRYPTO_crc16_step (udp_crc_sum,
- rr->payload,
- rr->payload_length);
- udp.crc = GNUNET_CRYPTO_crc16_finish (udp_crc_sum);
+ if (AF_INET == rr->src_addr.ss_family)
+ GNUNET_TUN_calculate_udp4_checksum (&ip4,
+ &udp,
+ rr->payload,
+ rr->payload_length);
+ else
+ GNUNET_TUN_calculate_udp6_checksum (&ip6,
+ &udp,
+ rr->payload,
+ rr->payload_length);
memcpy (&buf[off], &udp, sizeof (udp));
off += sizeof (udp);
}
+
/* now DNS payload */
{
memcpy (&buf[off], rr->payload, rr->payload_length);
}
/* final checks & sending */
GNUNET_assert (off == reply_len);
- GNUNET_HELPER_send (hijacker,
- hdr,
- GNUNET_YES,
- NULL, NULL);
+ (void) GNUNET_HELPER_send (hijacker,
+ hdr,
+ GNUNET_YES,
+ NULL, NULL);
GNUNET_STATISTICS_update (stats,
gettext_noop ("# DNS requests answered via TUN interface"),
1, GNUNET_NO);
send_request_to_client (struct RequestRecord *rr,
struct GNUNET_SERVER_Client *client)
{
- char buf[sizeof (struct GNUNET_DNS_Request) + rr->payload_length];
+ char buf[sizeof (struct GNUNET_DNS_Request) + rr->payload_length] GNUNET_ALIGN;
struct GNUNET_DNS_Request *req;
if (sizeof (buf) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
cleanup_rr (rr);
return;
}
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending information about request %llu to local client\n",
+ (unsigned long long) rr->request_id);
req = (struct GNUNET_DNS_Request*) buf;
req->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST);
req->header.size = htons (sizeof (buf));
req->reserved = htonl (0);
req->request_id = rr->request_id;
memcpy (&req[1], rr->payload, rr->payload_length);
- GNUNET_SERVER_notification_context_unicast (nc,
+ GNUNET_SERVER_notification_context_unicast (nc,
client,
&req->header,
GNUNET_NO);
}
+
+/**
+ * Callback called from DNSSTUB resolver when a resolution
+ * succeeded.
+ *
+ * @param cls NULL
+ * @param rs the socket that received the response
+ * @param dns the response itself
+ * @param r number of bytes in dns
+ */
+static void
+process_dns_result (void *cls,
+ struct GNUNET_DNSSTUB_RequestSocket *rs,
+ const struct GNUNET_TUN_DnsHeader *dns,
+ size_t r);
+
+
/**
* A client has completed its processing for this
* request. Move on.
struct ClientRecord *cr;
int nz;
unsigned int j;
- struct GNUNET_NETWORK_Handle *dnsout;
socklen_t salen;
if (rr->phase == RP_DROP)
nz = (int) j;
break;
}
- }
- if (-1 != nz)
+ }
+ if (-1 != nz)
{
send_request_to_client (rr, rr->client_wait_list[nz]->client);
return;
}
/* done with current phase, advance! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Request %llu now in phase %d\n",
+ rr->request_id,
+ rr->phase);
switch (rr->phase)
{
case RP_INIT:
next_phase (rr);
return;
case RP_QUERY:
- rr->phase = RP_INTERNET_DNS;
switch (rr->dst_addr.ss_family)
{
case AF_INET:
- dnsout = dnsout4;
- salen = sizeof (struct GNUNET_TUN_IPv4Header);
+ salen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
- dnsout = dnsout6;
- salen = sizeof (struct GNUNET_TUN_IPv6Header);
+ salen = sizeof (struct sockaddr_in6);
break;
default:
- GNUNET_break (0);
- cleanup_rr (rr);
- return;
+ GNUNET_assert (0);
}
- if (NULL == dnsout)
+
+ rr->phase = RP_INTERNET_DNS;
+ rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
+ (struct sockaddr*) &rr->dst_addr,
+ salen,
+ rr->payload,
+ rr->payload_length,
+ &process_dns_result,
+ NULL);
+ if (NULL == rr->rs)
{
GNUNET_STATISTICS_update (stats,
- gettext_noop ("# DNS exit failed (address family not supported)"),
+ gettext_noop ("# DNS exit failed (failed to open socket)"),
1, GNUNET_NO);
cleanup_rr (rr);
return;
}
- GNUNET_NETWORK_socket_sendto (dnsout,
- rr->payload,
- rr->payload_length,
- (struct sockaddr*) &rr->dst_addr,
- salen);
return;
case RP_INTERNET_DNS:
rr->phase = RP_MODIFY;
*
* @param cls unused
* @param client handle of client that disconnected
- */
+ */
static void
client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
{
if (rr->client_wait_list[j] == cr)
{
rr->client_wait_list[j] = NULL;
- next_phase (rr);
+ next_phase (rr);
}
}
}
/**
- * Read a DNS response from the (unhindered) UDP-Socket
+ * Callback called from DNSSTUB resolver when a resolution
+ * succeeded.
*
- * @param cls socket to read from
- * @param tc scheduler context (must be shutdown or read ready)
+ * @param cls NULL
+ * @param rs the socket that received the response
+ * @param dns the response itself
+ * @param r number of bytes in dns
*/
static void
-read_response (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+process_dns_result (void *cls,
+ struct GNUNET_DNSSTUB_RequestSocket *rs,
+ const struct GNUNET_TUN_DnsHeader *dns,
+ size_t r)
{
- struct GNUNET_NETWORK_Handle *dnsout = cls;
- struct sockaddr_in addr4;
- struct sockaddr_in6 addr6;
- struct sockaddr *addr;
- struct GNUNET_TUN_DnsHeader *dns;
- socklen_t addrlen;
struct RequestRecord *rr;
- ssize_t r;
- int len;
- if (dnsout == dnsout4)
- {
- addrlen = sizeof (struct sockaddr_in);
- addr = (struct sockaddr* ) &addr4;
- read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- dnsout,
- &read_response,
- dnsout);
- }
- else
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Processing DNS result from stub resolver\n");
+ GNUNET_assert (NULL == cls);
+ rr = &requests[dns->id];
+ if ( (rr->phase != RP_INTERNET_DNS) ||
+ (rr->rs != rs) )
{
- addrlen = sizeof (struct sockaddr_in6);
- addr = (struct sockaddr* ) &addr6;
- read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- dnsout,
- &read_response,
- dnsout);
- }
- if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ /* unexpected / bogus reply */
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# External DNS response discarded (no matching request)"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received DNS reply that does not match any pending request. Dropping.\n");
return;
-
-#ifndef MINGW
- if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
- {
- /* conservative choice: */
- len = 65536;
}
-#else
- /* port the code above? */
- len = 65536;
-#endif
-
- {
- unsigned char buf[len];
-
- memset (addr, 0, addrlen);
- r = GNUNET_NETWORK_socket_recvfrom (dnsout,
- buf, sizeof (buf),
- addr, &addrlen);
- if (-1 == r)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
- return;
- }
- if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Received DNS response that is too small (%u bytes)"),
- r);
- return;
- }
- dns = (struct GNUNET_TUN_DnsHeader *) buf;
- rr = &requests[dns->id];
- if (rr->phase != RP_INTERNET_DNS)
- {
- /* unexpected / bogus reply */
- GNUNET_STATISTICS_update (stats,
- gettext_noop ("# External DNS response discarded (no matching request)"),
- 1, GNUNET_NO);
- return;
- }
- GNUNET_free_non_null (rr->payload);
- rr->payload = GNUNET_malloc (len);
- memcpy (rr->payload, buf, len);
- rr->payload_length = len;
- next_phase (rr);
- }
-}
-
-
-/**
- * Open source port for sending DNS request on IPv4.
- *
- * @return GNUNET_OK on success
- */
-static int
-open_port4 ()
-{
- struct sockaddr_in addr;
- socklen_t addrlen;
-
- dnsout4 = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
- if (dnsout4 == NULL)
- return GNUNET_SYSERR;
-
- memset (&addr, 0, sizeof (struct sockaddr_in));
- addr.sin_family = AF_INET;
- int err = GNUNET_NETWORK_socket_bind (dnsout4,
- (struct sockaddr *) &addr,
- sizeof (struct sockaddr_in));
-
- if (err != GNUNET_OK)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Could not bind to any port: %s\n"),
- STRERROR (errno));
- GNUNET_NETWORK_socket_close (dnsout4);
- dnsout4 = NULL;
- return GNUNET_SYSERR;
- }
-
- /* Read the port we bound to */
- addrlen = sizeof (struct sockaddr_in);
- if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout4),
- (struct sockaddr *) &addr,
- &addrlen))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Could not determine port I got: %s\n"),
- STRERROR (errno));
- GNUNET_NETWORK_socket_close (dnsout4);
- dnsout4 = NULL;
- return GNUNET_SYSERR;
- }
- dnsoutport = htons (addr.sin_port);
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("GNUnet DNS will exit on source port %u\n"),
- (unsigned int) dnsoutport);
- read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- dnsout4,
- &read_response, dnsout4);
- return GNUNET_OK;
-}
-
-
-/**
- * Open source port for sending DNS request on IPv6. Should be
- * called AFTER open_port4.
- *
- * @return GNUNET_OK on success
- */
-static int
-open_port6 ()
-{
- struct sockaddr_in6 addr;
- socklen_t addrlen;
-
- dnsout6 = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_DGRAM, 0);
- if (dnsout6 == NULL)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Could not create IPv6 socket: %s\n"),
- STRERROR (errno));
- return GNUNET_SYSERR;
- }
- memset (&addr, 0, sizeof (struct sockaddr_in6));
- addr.sin6_family = AF_INET6;
- addr.sin6_port = htons (dnsoutport);
- int err = GNUNET_NETWORK_socket_bind (dnsout6,
- (struct sockaddr *) &addr,
- sizeof (struct sockaddr_in6));
-
- if (err != GNUNET_OK)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Could not bind to port %u: %s\n"),
- (unsigned int) dnsoutport,
- STRERROR (errno));
- GNUNET_NETWORK_socket_close (dnsout6);
- dnsout6 = NULL;
- return GNUNET_SYSERR;
- }
- if (0 == dnsoutport)
- {
- addrlen = sizeof (struct sockaddr_in6);
- if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout6),
- (struct sockaddr *) &addr,
- &addrlen))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Could not determine port I got: %s\n"),
- STRERROR (errno));
- GNUNET_NETWORK_socket_close (dnsout6);
- dnsout6 = NULL;
- return GNUNET_SYSERR;
- }
- }
- dnsoutport = htons (addr.sin6_port);
- read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- dnsout6,
- &read_response, dnsout6);
- return GNUNET_YES;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got a response from the stub resolver for DNS request %llu intercepted locally!\n",
+ (unsigned long long) rr->request_id);
+ GNUNET_free_non_null (rr->payload);
+ rr->payload = GNUNET_malloc (r);
+ memcpy (rr->payload, dns, r);
+ rr->payload_length = r;
+ next_phase (rr);
}
* @param message the init message (unused)
*/
static void
-handle_client_init (void *cls GNUNET_UNUSED,
+handle_client_init (void *cls GNUNET_UNUSED,
struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
struct ClientRecord *cr;
const struct GNUNET_DNS_Register *reg = (const struct GNUNET_DNS_Register*) message;
- cr = GNUNET_malloc (sizeof (struct ClientRecord));
+ cr = GNUNET_new (struct ClientRecord);
cr->client = client;
- cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
+ cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
GNUNET_SERVER_client_keep (client);
GNUNET_CONTAINER_DLL_insert (clients_head,
clients_tail,
* @param message the response
*/
static void
-handle_client_response (void *cls GNUNET_UNUSED,
+handle_client_response (void *cls GNUNET_UNUSED,
struct GNUNET_SERVER_Client *client,
const struct GNUNET_MessageHeader *message)
{
resp = (const struct GNUNET_DNS_Response*) message;
off = (uint16_t) resp->request_id;
rr = &requests[off];
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received DNS response with ID %llu from local client!\n",
+ (unsigned long long) resp->request_id);
if (rr->request_id != resp->request_id)
{
GNUNET_STATISTICS_update (stats,
{
GNUNET_break (0);
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
- next_phase (rr);
+ next_phase (rr);
return;
}
GNUNET_free_non_null (rr->payload);
-#if DEBUG_DNS
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- _("Changing DNS reply according to client specifications\n"));
-#endif
+ "Changing DNS reply according to client specifications\n");
rr->payload = GNUNET_malloc (msize);
rr->payload_length = msize;
memcpy (rr->payload, &resp[1], msize);
rr->client_wait_list_length,
0);
}
+ /* if query changed to answer, move past DNS resolution phase... */
+ if ( (RP_QUERY == rr->phase) &&
+ (rr->payload_length > sizeof (struct GNUNET_TUN_DnsHeader)) &&
+ ((struct GNUNET_TUN_DnsFlags*)&(((struct GNUNET_TUN_DnsHeader*) rr->payload)->flags))->query_or_response == 1)
+ {
+ rr->phase = RP_INTERNET_DNS;
+ GNUNET_array_grow (rr->client_wait_list,
+ rr->client_wait_list_length,
+ 0);
+ }
break;
}
- next_phase (rr);
+ next_phase (rr);
GNUNET_SERVER_receive_done (client, GNUNET_OK);
- return;
+ return;
}
/* odd, client was not on our list for the request, that ought
to be an error */
* @param client identification of the client
* @param message the actual message, a DNS request we should handle
*/
-static void
+static int
process_helper_messages (void *cls GNUNET_UNUSED, void *client,
const struct GNUNET_MessageHeader *message)
{
struct sockaddr_in *dsta4;
struct sockaddr_in6 *dsta6;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Intercepted message via DNS hijacker\n");
msize = ntohs (message->size);
if (msize < sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader) + sizeof (struct GNUNET_TUN_IPv4Header))
{
/* non-IP packet received on TUN!? */
GNUNET_break (0);
- return;
+ return GNUNET_OK;
}
msize -= sizeof (struct GNUNET_MessageHeader);
tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
{
case ETH_P_IPV4:
ip4 = (const struct GNUNET_TUN_IPv4Header *) &tun[1];
+ ip6 = NULL; /* make compiler happy */
if ( (msize < sizeof (struct GNUNET_TUN_IPv4Header)) ||
(ip4->version != 4) ||
(ip4->header_length != sizeof (struct GNUNET_TUN_IPv4Header) / 4) ||
/* non-IP/UDP packet received on TUN (or with options) */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Received malformed IPv4-UDP packet on TUN interface.\n"));
- return;
+ return GNUNET_OK;
}
udp = (const struct GNUNET_TUN_UdpHeader*) &ip4[1];
msize -= sizeof (struct GNUNET_TUN_IPv4Header);
break;
case ETH_P_IPV6:
+ ip4 = NULL; /* make compiler happy */
ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
if ( (msize < sizeof (struct GNUNET_TUN_IPv6Header)) ||
(ip6->version != 6) ||
/* non-IP/UDP packet received on TUN (or with extensions) */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Received malformed IPv6-UDP packet on TUN interface.\n"));
- return;
+ return GNUNET_OK;
}
udp = (const struct GNUNET_TUN_UdpHeader*) &ip6[1];
msize -= sizeof (struct GNUNET_TUN_IPv6Header);
_("Got non-IP packet with %u bytes and protocol %u from TUN\n"),
(unsigned int) msize,
ntohs (tun->proto));
- return;
+ return GNUNET_OK;
}
- if (msize <= sizeof (struct GNUNET_TUN_UdpHeader) + sizeof (struct GNUNET_TUN_DnsHeader))
- {
+ if ( (msize <= sizeof (struct GNUNET_TUN_UdpHeader) + sizeof (struct GNUNET_TUN_DnsHeader)) ||
+ (DNS_PORT != ntohs (udp->destination_port)) )
+ {
/* non-DNS packet received on TUN, ignore */
GNUNET_STATISTICS_update (stats,
gettext_noop ("# Non-DNS UDP packet received via TUN interface"),
1, GNUNET_NO);
- return;
+ return GNUNET_OK;
}
msize -= sizeof (struct GNUNET_TUN_UdpHeader);
dns = (const struct GNUNET_TUN_DnsHeader*) &udp[1];
/* setup new request */
rr->phase = RP_INIT;
- if (ip4->version == 4)
+ switch (ntohs (tun->proto))
{
- srca4 = (struct sockaddr_in*) &rr->src_addr;
- dsta4 = (struct sockaddr_in*) &rr->dst_addr;
- memset (srca4, 0, sizeof (struct sockaddr_in));
- memset (dsta4, 0, sizeof (struct sockaddr_in));
- srca4->sin_family = AF_INET;
- dsta4->sin_family = AF_INET;
- srca4->sin_addr = ip4->source_address;
- dsta4->sin_addr = ip4->destination_address;
- srca4->sin_port = udp->spt;
- dsta4->sin_port = udp->dpt;
+ case ETH_P_IPV4:
+ {
+ srca4 = (struct sockaddr_in*) &rr->src_addr;
+ dsta4 = (struct sockaddr_in*) &rr->dst_addr;
+ memset (srca4, 0, sizeof (struct sockaddr_in));
+ memset (dsta4, 0, sizeof (struct sockaddr_in));
+ srca4->sin_family = AF_INET;
+ dsta4->sin_family = AF_INET;
+ srca4->sin_addr = ip4->source_address;
+ dsta4->sin_addr = ip4->destination_address;
+ srca4->sin_port = udp->source_port;
+ dsta4->sin_port = udp->destination_port;
#if HAVE_SOCKADDR_IN_SIN_LEN
- srca4->sin_len = sizeof (sizeof (struct sockaddr_in));
- dsta4->sin_len = sizeof (sizeof (struct sockaddr_in));
+ srca4->sin_len = sizeof (struct sockaddr_in);
+ dsta4->sin_len = sizeof (struct sockaddr_in);
#endif
- }
- else /* ipv6 */
- {
- srca6 = (struct sockaddr_in6*) &rr->src_addr;
- dsta6 = (struct sockaddr_in6*) &rr->dst_addr;
- memset (srca6, 0, sizeof (struct sockaddr_in6));
- memset (dsta6, 0, sizeof (struct sockaddr_in6));
- srca6->sin6_family = AF_INET6;
- dsta6->sin6_family = AF_INET6;
- srca6->sin6_addr = ip6->source_address;
- dsta6->sin6_addr = ip6->destination_address;
- srca6->sin6_port = udp->spt;
- dsta6->sin6_port = udp->dpt;
+ }
+ break;
+ case ETH_P_IPV6:
+ {
+ srca6 = (struct sockaddr_in6*) &rr->src_addr;
+ dsta6 = (struct sockaddr_in6*) &rr->dst_addr;
+ memset (srca6, 0, sizeof (struct sockaddr_in6));
+ memset (dsta6, 0, sizeof (struct sockaddr_in6));
+ srca6->sin6_family = AF_INET6;
+ dsta6->sin6_family = AF_INET6;
+ srca6->sin6_addr = ip6->source_address;
+ dsta6->sin6_addr = ip6->destination_address;
+ srca6->sin6_port = udp->source_port;
+ dsta6->sin6_port = udp->destination_port;
#if HAVE_SOCKADDR_IN_SIN_LEN
- srca6->sin6_len = sizeof (sizeof (struct sockaddr_in6));
- dsta6->sin6_len = sizeof (sizeof (struct sockaddr_in6));
+ srca6->sin6_len = sizeof (struct sockaddr_in6);
+ dsta6->sin6_len = sizeof (struct sockaddr_in6);
#endif
+ }
+ break;
+ default:
+ GNUNET_assert (0);
}
rr->payload = GNUNET_malloc (msize);
rr->payload_length = msize;
memcpy (rr->payload, dns, msize);
rr->request_id = dns->id | (request_id_gen << 16);
request_id_gen++;
-
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating new DNS request %llu\n",
+ (unsigned long long) rr->request_id);
GNUNET_STATISTICS_update (stats,
gettext_noop ("# DNS requests received via TUN interface"),
1, GNUNET_NO);
/* start request processing state machine */
next_phase (rr);
+ return GNUNET_OK;
}
{
static const struct GNUNET_SERVER_MessageHandler handlers[] = {
/* callback, cls, type, size */
- {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
+ {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
sizeof (struct GNUNET_DNS_Register)},
{&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0},
{NULL, NULL, 0, 0}
};
- char port_s[6];
char *ifc_name;
char *ipv4addr;
char *ipv4mask;
char *ipv6addr;
char *ipv6prefix;
+ struct in_addr dns_exit4;
+ struct in6_addr dns_exit6;
+ char *dns_exit;
+ char *binary;
+ int nortsetup;
cfg = cfg_;
stats = GNUNET_STATISTICS_create ("dns", cfg);
nc = GNUNET_SERVER_notification_context_create (server, 1);
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
- cls);
- if (GNUNET_YES ==
- GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT"))
+ GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
+ cls);
+ dns_exit = NULL;
+ if ( ( (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "dns",
+ "DNS_EXIT",
+ &dns_exit)) ||
+ ( (1 != inet_pton (AF_INET, dns_exit, &dns_exit4)) &&
+ (1 != inet_pton (AF_INET6, dns_exit, &dns_exit6)) ) ) )
{
- if ( (GNUNET_OK != open_port4 ()) &&
- (GNUNET_OK != open_port6 ()) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Failed to open any port to provide DNS exit\n"));
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "dns",
+ "DNS_EXIT",
+ _("need a valid IPv4 or IPv6 address\n"));
+ GNUNET_free_non_null (dns_exit);
+ dns_exit = NULL;
}
+ dnsstub = GNUNET_DNSSTUB_start (dns_exit);
+ GNUNET_free_non_null (dns_exit);
+ GNUNET_SERVER_add_handlers (server,
+ handlers);
+ GNUNET_SERVER_disconnect_notify (server,
+ &client_disconnect,
+ NULL);
+ binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-dns");
+ if (GNUNET_YES !=
+ GNUNET_OS_check_helper_binary (binary,
+ GNUNET_YES,
+ NULL)) // TODO: once we have a windows-testcase, add test parameters here
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("`%s' must be installed SUID, will not run DNS interceptor\n"),
+ binary);
+ global_ret = 1;
+ GNUNET_free (binary);
+ return;
+ }
+ GNUNET_free (binary);
helper_argv[0] = GNUNET_strdup ("gnunet-dns");
if (GNUNET_SYSERR ==
return;
}
helper_argv[5] = ipv4mask;
- GNUNET_snprintf (port_s,
- sizeof (port_s),
- "%u",
- (unsigned int) dnsoutport);
- helper_argv[6] = GNUNET_strdup (port_s);
+
+ nortsetup = GNUNET_CONFIGURATION_get_value_yesno (cfg, "dns",
+ "SKIP_ROUTING_SETUP");
+ if (GNUNET_YES == nortsetup)
+ helper_argv[6] = GNUNET_strdup("1");
+ else
+ helper_argv[6] = GNUNET_strdup("0");
+
helper_argv[7] = NULL;
- hijacker = GNUNET_HELPER_start ("gnunet-helper-dns",
+ hijacker = GNUNET_HELPER_start (GNUNET_NO,
+ "gnunet-helper-dns",
helper_argv,
&process_helper_messages,
- NULL);
- GNUNET_SERVER_add_handlers (server, handlers);
- GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
+ NULL, NULL);
}
int
main (int argc, char *const *argv)
{
+ /* make use of SGID capabilities on POSIX */
+ /* FIXME: this might need a port on systems without 'getresgid' */
+#if HAVE_GETRESGID
+ gid_t rgid;
+ gid_t egid;
+ gid_t sgid;
+
+ if (-1 == getresgid (&rgid, &egid, &sgid))
+ {
+ fprintf (stderr,
+ "getresgid failed: %s\n",
+ strerror (errno));
+ }
+ else if (sgid != rgid)
+ {
+ if (-1 == setregid (sgid, sgid))
+ fprintf (stderr, "setregid failed: %s\n", strerror (errno));
+ }
+#endif
return (GNUNET_OK ==
GNUNET_SERVICE_run (argc, argv, "dns", GNUNET_SERVICE_OPTION_NONE,
- &run, NULL)) ? 0 : 1;
+ &run, NULL)) ? global_ret : 1;
}
-/* end of gnunet-service-dns_new.c */
+/* end of gnunet-service-dns.c */