pxdns.c revision 2b114c590cf5a19f8047cd7bde9c7e5ae00aa22b
/* -*- indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2009-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*
* Copyright (c) 2003,2004,2005 Armin Wolfermann
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define LOG_GROUP LOG_GROUP_NAT_SERVICE
#include "winutils.h"
#include "proxy.h"
#include "proxy_pollmgr.h"
#ifndef RT_OS_WINDOWS
#include <netdb.h>
#else
#include "winpoll.h"
#endif
#include <stdio.h>
#include <string.h>
union sockaddr_inet {
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
struct request;
/**
* DNS Proxy
*/
struct pxdns {
struct pollmgr_handler pmhdl4;
struct pollmgr_handler pmhdl6;
union sockaddr_inet *resolvers;
#define TIMEOUT 5
#define HASHSIZE 10
} g_pxdns;
struct request {
/**
* Request ID that we use in relayed request.
*/
/**
* pxdns::generation used for this request
*/
/**
* Current index into pxdns::resolvers
*/
/**
* PCB from which we have received this request. lwIP doesn't
* support listening for both IPv4 and IPv6 on the same pcb, so we
* use two and need to keep track.
*/
/**
* Client this request is from and its original request ID.
*/
/**
* Chaining for pxdns::request_hash
*/
struct request **pprev_hash;
/**
* Chaining for pxdns::timeout_list
*/
struct request **pprev_timeout;
struct request *next_timeout;
/**
* Slot in pxdns::timeout_list
*/
/**
* Pbuf with reply received on pollmgr thread.
*/
/**
* Preallocated lwIP message to send reply from the lwIP thread.
*/
/**
* Client request. ID is replaced with ours, original saved in
* client_id. Use a copy since we might need to resend and we
* don't want to hold onto pbuf of the request.
*/
};
const char **nameservers);
static void pxdns_timer(void *arg);
static void pxdns_pcb_reply(void *ctx);
{
goto err_cleanup_pcb;
}
goto err_cleanup_pcb;
}
goto err_cleanup_pcb;
}
goto err_cleanup_pcb;
}
goto err_cleanup_pcb;
}
/* it's ok if the host doesn't support IPv6 */
/* XXX: TODO: log */
}
pxdns->generation = 0;
pxdns->nresolvers = 0;
pxdns->timeout_slot = 0;
pxdns->timeout_mask = 0;
/* NB: assumes pollmgr thread is not running yet */
}
return ERR_OK;
}
}
return error;
}
/**
* lwIP thread callback to set the new list of nameservers.
*/
void
pxdns_set_nameservers(void *arg)
{
const char **nameservers = (const char **)arg;
}
}
/**
* Use this list of nameservers to resolve guest requests.
*
* Runs on lwIP thread, so no new queries or retramsmits compete with
* it for the use of the existing list of resolvers (to be replaced).
*/
static void
{
union sockaddr_inet *resolvers;
const char **p;
int status;
nresolvers = 0;
if (nameservers == NULL) {
goto update_resolvers;
}
nnames = 0;
for (p = nameservers; *p != NULL; ++p) {
++nnames;
}
if (nnames == 0) {
goto update_resolvers;
}
nresolvers = 0;
goto update_resolvers;
}
for (p = nameservers; *p != NULL; ++p) {
const char *name = *p;
if (status != 0) {
/* XXX: log failed resolution */
continue;
}
/* XXX: log unsupported address family */
continue;
}
/* XXX: log */
continue;
}
/* no IPv6 support on the host, can't use this resolver */
continue;
}
++nresolvers;
}
if (nresolvers == 0) {
}
}
++pxdns->generation;
}
}
static void
{
}
}
static void
{
++pxdns->hash_collisions;
}
}
static void
{
}
if (omask == 0) {
}
}
static void
{
--pxdns->active_queries;
}
}
static void
{
}
/* may be on pollmgr thread so no sys_untimeout */
}
}
/**
* Do bookkeeping on new request. Called from pxdns_query().
*/
static void
{
++pxdns->active_queries;
}
static void
{
--pxdns->active_queries;
}
/**
* Find request by the id we used when relaying it and remove it from
* id hash and timeout list. Called from pxdns_pmgr_pump() when reply
* comes.
*/
static struct request *
{
/* find request in the id->req hash */
break;
}
}
--pxdns->active_queries;
}
return req;
}
/**
* Retransmit of g/c expired requests and move timeout slot forward.
*/
static void
pxdns_timer(void *arg)
{
/*
* Move timeout slot first. New slot points to the list of
* expired requests. If any expired request is retransmitted, we
* keep it on the list (that is now current), effectively
* resetting the timeout.
*/
pxdns->timeout_slot = 0;
}
continue;
}
++pxdns->expired_queries;
}
}
else {
}
if (mask != 0) {
}
}
static void
{
}
static void
{
}
static void
{
int sent;
if (pxdns->nresolvers == 0) {
/* nothing we can do */
pbuf_free(p);
return;
}
pbuf_free(p);
return;
}
/* copy request data */
/* save client identity and client's request id */
/* slap our request id onto it */
/* resolver to forward to */
/* prepare for relaying the reply back to guest */
DPRINTF2(("%s: req=%p: client id %d -> id %d\n",
if (!sent) {
}
if (!sent) {
}
}
/**
* Forward request to the req::residx resolver in the pxdns::resolvers
* array of upstream resolvers.
*
* Returns 1 on success, 0 on failure.
*/
static int
{
union sockaddr_inet *resolver;
DPRINTF2(("%s: req %p: sending to resolver #%lu\n",
}
}
else {
/* shouldn't happen, we should have weeded out IPv6 resolvers */
return 0;
}
}
else {
/* shouldn't happen, we should have weeded out unsupported families */
return 0;
}
return 1; /* sent */
}
if (nsent < 0) {
}
else {
DPRINTF2(("%s: sent only %lu of %lu\n",
}
return 0; /* not sent, caller will retry as necessary */
}
/**
* Forward request to the next resolver in the pxdns::resolvers array
* of upstream resolvers if there are any left.
*/
static int
{
int sent;
DPRINTF2(("%s: req %p: generation %lu != pxdns generation %lu\n",
(unsigned long)req->generation,
(unsigned long)pxdns->generation));
return 0;
}
do {
return 0;
}
} while (!sent);
return 1;
}
static int
{
return POLLIN;
}
int sockerr = -1;
int status;
if (status < 0) {
DPRINTF(("%s: sock %d: SO_ERROR failed: %R[sockerr]\n",
}
else {
DPRINTF(("%s: sock %d: %R[sockerr]\n",
}
}
return POLLIN;
}
if (nread < 0) {
return POLLIN;
}
/* check for minimum dns packet length */
if (nread < 12) {
DPRINTF2(("%s: short reply %lu bytes\n",
return POLLIN;
}
/* XXX: shall we proxy back RCODE=Refused responses? */
++pxdns->late_answers;
return POLLIN;
}
DPRINTF2(("%s: reply for req=%p: id %d -> client id %d\n",
return POLLIN;
}
return POLLIN;
}
return POLLIN;
}
/**
* Called on lwIP thread via request::msg_reply callback.
*/
static void
pxdns_pcb_reply(void *ctx)
{
DPRINTF(("%s: udp_sendto err %s\n",
}
}