/*
This file is part of GNUnet.
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
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
*/
/**
* @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.
*/
enum RequestPhase
{
/**
* Request has just been received.
*/
RP_INIT,
/**
* Showing the request to all monitor clients. If
* client list is empty, will enter QUERY phase.
*/
RP_REQUEST_MONITOR,
/**
* Showing the request to PRE-RESOLUTION clients to find an answer.
* If client list is empty, will trigger global DNS request.
*/
RP_QUERY,
/**
* 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,
* will enter RESPONSE_MONITOR phase.
*/
RP_MODIFY,
/**
* Showing the request to all monitor clients. If
* client list is empty, give the result to the hijacker (and be done).
*/
RP_RESPONSE_MONITOR,
/**
* Some client has told us to drop the request.
*/
RP_DROP
};
/**
* 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_SERVICE_Client *client;
/**
* Message queue to talk to @a client.
*/
struct GNUNET_MQ_Handle *mq;
/**
* Flags for the client.
*/
enum GNUNET_DNS_Flags flags;
};
/**
* Entry we keep for each active request.
*/
struct RequestRecord
{
/**
* List of clients that still need to see this request (each entry
* is set to NULL when the client is done).
*/
struct ClientRecord **client_wait_list;
/**
* Payload of the UDP packet (the UDP payload), can be either query
* or already the response.
*/
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).
*/
struct sockaddr_storage src_addr;
/**
* Destination address of the original request (for potential use as exit).
*/
struct sockaddr_storage dst_addr;
/**
* ID of this request, also basis for hashing. Lowest 16 bit will
* be our message ID when doing a global DNS request and our index
* into the 'requests' array.
*/
uint64_t request_id;
/**
* Number of bytes in payload.
*/
size_t payload_length;
/**
* Length of the @e client_wait_list.
*/
unsigned int client_wait_list_length;
/**
* In which phase this this request?
*/
enum RequestPhase phase;
};
/**
* Global return value from 'main'.
*/
static int global_ret;
/**
* The configuration to use
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Statistics.
*/
static struct GNUNET_STATISTICS_Handle *stats;
/**
* Handle to DNS hijacker helper process ("gnunet-helper-dns").
*/
static struct GNUNET_HELPER_Handle *hijacker;
/**
* Command-line arguments we are giving to the hijacker process.
*/
static char *helper_argv[8];
/**
* Head of DLL of clients we consult.
*/
static struct ClientRecord *clients_head;
/**
* Tail of DLL of clients we consult.
*/
static struct ClientRecord *clients_tail;
/**
* Array of all open requests.
*/
static struct RequestRecord requests[UINT16_MAX