mirror of https://github.com/getdnsapi/getdns.git
Move serving requests out of getdns_query
This commit is contained in:
parent
09b4ef9e9c
commit
3cc369a27d
|
@ -66,7 +66,9 @@ CHECK_CFLAGS=@CHECK_CFLAGS@
|
|||
CHECK_OBJS=check_getdns_common.lo check_getdns_context_set_timeout.lo \
|
||||
check_getdns.lo check_getdns_transport.lo
|
||||
|
||||
DECOMPOSED_OBJS_WITHOUT_JSMN=getdns_str2dict.lo getdns_context_config.lo
|
||||
DECOMPOSED_OBJS_WITHOUT_JSMN=getdns_str2dict.lo getdns_context_config.lo \
|
||||
getdns_context_set_listen_addresses.lo
|
||||
|
||||
DECOMPOSED_OBJS=$(DECOMPOSED_OBJS_WITHOUT_JSMN) jsmn.lo
|
||||
|
||||
ALL_OBJS=$(CHECK_OBJS) check_getdns_libevent.lo check_getdns_libev.lo \
|
||||
|
@ -273,9 +275,14 @@ check_getdns_transport.lo check_getdns_transport.o: $(srcdir)/check_getdns_trans
|
|||
../getdns/getdns_extra.h
|
||||
getdns_context_config.lo getdns_context_config.o: $(srcdir)/getdns_context_config.c $(srcdir)/getdns_context_config.h \
|
||||
../getdns/getdns.h ../getdns/getdns_extra.h
|
||||
getdns_context_set_listen_addresses.lo getdns_context_set_listen_addresses.o: \
|
||||
$(srcdir)/getdns_context_set_listen_addresses.c ../config.h \
|
||||
$(srcdir)/getdns_context_set_listen_addresses.h ../getdns/getdns.h \
|
||||
../getdns/getdns_extra.h $(srcdir)/../types-internal.h ../getdns/getdns.h \
|
||||
../getdns/getdns_extra.h $(srcdir)/../util/rbtree.h
|
||||
getdns_query.lo getdns_query.o: $(srcdir)/getdns_query.c ../config.h $(srcdir)/../debug.h ../config.h \
|
||||
$(srcdir)/getdns_str2dict.h ../getdns/getdns.h $(srcdir)/getdns_context_config.h \
|
||||
../getdns/getdns_extra.h
|
||||
$(srcdir)/getdns_context_set_listen_addresses.h ../getdns/getdns_extra.h
|
||||
getdns_str2dict.lo getdns_str2dict.o: $(srcdir)/getdns_str2dict.c ../config.h $(srcdir)/../const-info.h \
|
||||
$(srcdir)/jsmn/jsmn.h $(srcdir)/getdns_str2dict.h ../getdns/getdns.h $(srcdir)/../types-internal.h \
|
||||
../getdns/getdns.h ../getdns/getdns_extra.h $(srcdir)/../util/rbtree.h $(srcdir)/../list.h \
|
||||
|
|
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
* Copyright (c) 2013, NLNet Labs, Verisign, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the names of the copyright holders nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "getdns_context_set_listen_addresses.h"
|
||||
#include "getdns/getdns_extra.h"
|
||||
#include "types-internal.h"
|
||||
#include <netdb.h>
|
||||
|
||||
#define DNS_REQUEST_SZ 4096
|
||||
#define DOWNSTREAM_IDLE_TIMEOUT 5000
|
||||
#define TCP_LISTEN_BACKLOG 16
|
||||
|
||||
typedef struct listen_data {
|
||||
getdns_eventloop_event event;
|
||||
socklen_t addr_len;
|
||||
struct sockaddr_storage addr;
|
||||
int fd;
|
||||
getdns_transport_list_t transport;
|
||||
|
||||
getdns_context *context;
|
||||
/* Should be per context eventually */
|
||||
getdns_request_handler_t handler;
|
||||
} listen_data;
|
||||
|
||||
|
||||
typedef struct dns_msg {
|
||||
listen_data *ld;
|
||||
} dns_msg;
|
||||
|
||||
typedef struct udp_msg {
|
||||
listen_data *ld;
|
||||
struct sockaddr_storage remote_in;
|
||||
socklen_t addrlen;
|
||||
} udp_msg;
|
||||
|
||||
typedef struct tcp_to_write tcp_to_write;
|
||||
struct tcp_to_write {
|
||||
size_t write_buf_len;
|
||||
size_t written;
|
||||
tcp_to_write *next;
|
||||
uint8_t write_buf[];
|
||||
};
|
||||
|
||||
typedef struct downstream {
|
||||
listen_data *ld;
|
||||
struct sockaddr_storage remote_in;
|
||||
socklen_t addrlen;
|
||||
int fd;
|
||||
getdns_eventloop_event event;
|
||||
|
||||
uint8_t *read_buf;
|
||||
size_t read_buf_len;
|
||||
uint8_t *read_pos;
|
||||
size_t to_read;
|
||||
|
||||
tcp_to_write *to_write;
|
||||
size_t to_answer;
|
||||
} downstream;
|
||||
|
||||
typedef struct tcp_msg {
|
||||
listen_data *ld;
|
||||
downstream *conn;
|
||||
} tcp_msg;
|
||||
|
||||
static void downstream_destroy(downstream *conn)
|
||||
{
|
||||
struct mem_funcs *mf;
|
||||
getdns_eventloop *loop;
|
||||
|
||||
tcp_to_write *cur, *next;
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(conn->ld->context)))
|
||||
return;
|
||||
|
||||
if (getdns_context_get_eventloop(conn->ld->context, &loop))
|
||||
return;
|
||||
|
||||
if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb)
|
||||
loop->vmt->clear(loop, &conn->event);
|
||||
if (conn->fd >= 0) {
|
||||
if (close(conn->fd) == -1)
|
||||
; /* Whatever */
|
||||
}
|
||||
GETDNS_FREE(*mf, conn->read_buf);
|
||||
for (cur = conn->to_write; cur; cur = next) {
|
||||
next = cur->next;
|
||||
GETDNS_FREE(*mf, cur);
|
||||
}
|
||||
GETDNS_FREE(*mf, conn);
|
||||
}
|
||||
|
||||
static void tcp_write_cb(void *userarg)
|
||||
{
|
||||
downstream *conn = (downstream *)userarg;
|
||||
struct mem_funcs *mf;
|
||||
getdns_eventloop *loop;
|
||||
|
||||
tcp_to_write *to_write;
|
||||
ssize_t written;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(conn->ld->context)))
|
||||
return;
|
||||
|
||||
if (getdns_context_get_eventloop(conn->ld->context, &loop))
|
||||
return;
|
||||
|
||||
/* Reset downstream idle timeout */
|
||||
loop->vmt->clear(loop, &conn->event);
|
||||
|
||||
if (!conn->to_write) {
|
||||
conn->event.write_cb = NULL;
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
return;
|
||||
}
|
||||
to_write = conn->to_write;
|
||||
if ((written = write(conn->fd, &to_write->write_buf[to_write->written],
|
||||
to_write->write_buf_len - to_write->written)) == -1) {
|
||||
|
||||
/* IO error, close connection */
|
||||
conn->event.read_cb = conn->event.write_cb =
|
||||
conn->event.timeout_cb = NULL;
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
to_write->written += written;
|
||||
if (to_write->written == to_write->write_buf_len) {
|
||||
conn->to_write = to_write->next;
|
||||
GETDNS_FREE(*mf, to_write);
|
||||
}
|
||||
if (!conn->to_write)
|
||||
conn->event.write_cb = NULL;
|
||||
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
}
|
||||
|
||||
void
|
||||
_getdns_cancel_reply(getdns_context *context, getdns_transaction_t request_id)
|
||||
{
|
||||
/* TODO: Check request_id at context->outbound_requests */
|
||||
dns_msg *msg = (dns_msg *)(intptr_t)request_id;
|
||||
struct mem_funcs *mf;
|
||||
|
||||
if (context && msg &&
|
||||
(mf = priv_getdns_context_mf(msg->ld->context)))
|
||||
GETDNS_FREE(*mf, msg);
|
||||
}
|
||||
|
||||
getdns_return_t
|
||||
getdns_reply(
|
||||
getdns_context *context, getdns_transaction_t request_id, getdns_dict *reply)
|
||||
{
|
||||
/* TODO: Check request_id at context->outbound_requests */
|
||||
dns_msg *msg = (dns_msg *)(intptr_t)request_id;
|
||||
struct mem_funcs *mf;
|
||||
getdns_eventloop *loop;
|
||||
uint8_t buf[65536];
|
||||
size_t len;
|
||||
getdns_return_t r;
|
||||
|
||||
if (!context || !reply || !msg)
|
||||
return GETDNS_RETURN_INVALID_PARAMETER;
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(msg->ld->context)))
|
||||
return GETDNS_RETURN_GENERIC_ERROR;;
|
||||
|
||||
if ((r = getdns_context_get_eventloop(msg->ld->context, &loop)))
|
||||
return r;
|
||||
|
||||
len = sizeof(buf);
|
||||
if ((r = getdns_msg_dict2wire_buf(reply, buf, &len)))
|
||||
return r;
|
||||
|
||||
else if (msg->ld->transport == GETDNS_TRANSPORT_UDP) {
|
||||
udp_msg *msg = (udp_msg *)(intptr_t)request_id;
|
||||
|
||||
if (sendto(msg->ld->fd, buf, len, 0,
|
||||
(struct sockaddr *)&msg->remote_in, msg->addrlen) == -1)
|
||||
; /* IO error, TODO: cleanup this listener */
|
||||
|
||||
} else if (msg->ld->transport == GETDNS_TRANSPORT_TCP) {
|
||||
tcp_msg *msg = (tcp_msg *)(intptr_t)request_id;
|
||||
tcp_to_write **to_write_p;
|
||||
tcp_to_write *to_write = (tcp_to_write *)GETDNS_XMALLOC(
|
||||
*mf, uint8_t, sizeof(tcp_to_write) + len + 2);
|
||||
|
||||
if (!to_write)
|
||||
return GETDNS_RETURN_MEMORY_ERROR;
|
||||
|
||||
to_write->write_buf_len = len + 2;
|
||||
to_write->write_buf[0] = (len >> 8) & 0xFF;
|
||||
to_write->write_buf[1] = len & 0xFF;
|
||||
to_write->written = 0;
|
||||
to_write->next = NULL;
|
||||
(void) memcpy(to_write->write_buf + 2, buf, len);
|
||||
|
||||
/* Appen to_write to conn->to_write list */
|
||||
for ( to_write_p = &msg->conn->to_write
|
||||
; *to_write_p
|
||||
; to_write_p = &(*to_write_p)->next)
|
||||
; /* pass */
|
||||
*to_write_p = to_write;
|
||||
|
||||
loop->vmt->clear(loop, &msg->conn->event);
|
||||
msg->conn->event.write_cb = tcp_write_cb;
|
||||
(void) loop->vmt->schedule(loop,
|
||||
msg->conn->fd, DOWNSTREAM_IDLE_TIMEOUT,
|
||||
&msg->conn->event);
|
||||
}
|
||||
/* TODO: other transport types */
|
||||
if (msg)
|
||||
GETDNS_FREE(*mf, msg);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void tcp_read_cb(void *userarg)
|
||||
{
|
||||
downstream *conn = (downstream *)userarg;
|
||||
ssize_t bytes_read;
|
||||
tcp_msg *msg;
|
||||
getdns_return_t r;
|
||||
struct mem_funcs *mf;
|
||||
getdns_eventloop *loop;
|
||||
getdns_dict *request_dict;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(conn->ld->context)))
|
||||
return;
|
||||
|
||||
if ((r = getdns_context_get_eventloop(conn->ld->context, &loop)))
|
||||
return;
|
||||
|
||||
/* Reset downstream idle timeout */
|
||||
loop->vmt->clear(loop, &conn->event);
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
|
||||
if ((bytes_read = read(conn->fd, conn->read_pos, conn->to_read)) == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return; /* Come back to do the read later */
|
||||
|
||||
/* IO error, close connection */
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
/* remote end closed connection, cleanup */
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
assert(bytes_read <= conn->to_read);
|
||||
|
||||
conn->to_read -= bytes_read;
|
||||
conn->read_pos += bytes_read;
|
||||
if (conn->to_read)
|
||||
return; /* More to read */
|
||||
|
||||
if (conn->read_pos - conn->read_buf == 2) {
|
||||
/* read length of dns msg to read */
|
||||
conn->to_read = (conn->read_buf[0] << 8) | conn->read_buf[1];
|
||||
if (conn->to_read > conn->read_buf_len) {
|
||||
GETDNS_FREE(*mf, conn->read_buf);
|
||||
while (conn->to_read > conn->read_buf_len)
|
||||
conn->read_buf_len *= 2;
|
||||
if (!(conn->read_buf = GETDNS_XMALLOC(
|
||||
*mf, uint8_t, conn->read_buf_len))) {
|
||||
/* Memory error */
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (conn->to_read < 12) {
|
||||
/* Request smaller than DNS header, FORMERR */
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
conn->read_pos = conn->read_buf;
|
||||
return; /* Read DNS message */
|
||||
}
|
||||
if (!(msg = GETDNS_MALLOC(*mf, tcp_msg))) {
|
||||
/* Memory error */
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
msg->ld = conn->ld;
|
||||
msg->conn = conn;
|
||||
if ((r = getdns_wire2msg_dict(conn->read_buf,
|
||||
(conn->read_pos - conn->read_buf), &request_dict)))
|
||||
; /* FROMERR on input, ignore */
|
||||
|
||||
else {
|
||||
conn->to_answer += 1;
|
||||
|
||||
/* Call request handler */
|
||||
conn->ld->handler(
|
||||
conn->ld->context, request_dict, (intptr_t)msg);
|
||||
|
||||
conn->read_pos = conn->read_buf;
|
||||
conn->to_read = 2;
|
||||
return; /* Read more requests */
|
||||
}
|
||||
GETDNS_FREE(*mf, msg);
|
||||
conn->read_pos = conn->read_buf;
|
||||
conn->to_read = 2;
|
||||
/* Read more requests */
|
||||
}
|
||||
|
||||
static void tcp_timeout_cb(void *userarg)
|
||||
{
|
||||
downstream *conn = (downstream *)userarg;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
downstream_destroy(conn);
|
||||
}
|
||||
|
||||
static void tcp_accept_cb(void *userarg)
|
||||
{
|
||||
listen_data *ld = (listen_data *)userarg;
|
||||
downstream *conn;
|
||||
struct mem_funcs *mf;
|
||||
getdns_eventloop *loop;
|
||||
getdns_return_t r;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(ld->context)))
|
||||
return;
|
||||
|
||||
if ((r = getdns_context_get_eventloop(ld->context, &loop)))
|
||||
return;
|
||||
|
||||
if (!(conn = GETDNS_MALLOC(*mf, downstream)))
|
||||
return;
|
||||
|
||||
(void) memset(conn, 0, sizeof(downstream));
|
||||
|
||||
conn->ld = ld;
|
||||
conn->addrlen = sizeof(conn->remote_in);
|
||||
if ((conn->fd = accept(ld->fd,
|
||||
(struct sockaddr *)&conn->remote_in, &conn->addrlen)) == -1) {
|
||||
/* IO error, TODO: cleanup this listener */
|
||||
GETDNS_FREE(*mf, conn);
|
||||
}
|
||||
if (!(conn->read_buf = malloc(DNS_REQUEST_SZ))) {
|
||||
/* Memory error */
|
||||
GETDNS_FREE(*mf, conn);
|
||||
return;
|
||||
}
|
||||
conn->read_buf_len = DNS_REQUEST_SZ;
|
||||
conn->read_pos = conn->read_buf;
|
||||
conn->to_read = 2;
|
||||
conn->event.userarg = conn;
|
||||
conn->event.read_cb = tcp_read_cb;
|
||||
conn->event.timeout_cb = tcp_timeout_cb;
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
}
|
||||
|
||||
static void udp_read_cb(void *userarg)
|
||||
{
|
||||
listen_data *ld = (listen_data *)userarg;
|
||||
udp_msg *msg;
|
||||
struct mem_funcs *mf;
|
||||
getdns_dict *request_dict;
|
||||
|
||||
/* Maximum reasonable size for requests */
|
||||
uint8_t buf[4096];
|
||||
ssize_t len;
|
||||
getdns_return_t r;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(ld->context)))
|
||||
return;
|
||||
|
||||
if (!(msg = GETDNS_MALLOC(*mf, udp_msg)))
|
||||
return;
|
||||
|
||||
msg->ld = ld;
|
||||
msg->addrlen = sizeof(msg->remote_in);
|
||||
if ((len = recvfrom(ld->fd, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&msg->remote_in, &msg->addrlen)) == -1)
|
||||
; /* IO error, TODO: cleanup this listener */
|
||||
|
||||
else if ((r = getdns_wire2msg_dict(buf, len, &request_dict)))
|
||||
; /* FROMERR on input, ignore */
|
||||
|
||||
else {
|
||||
/* Call request handler */
|
||||
ld->handler(ld->context, request_dict, (intptr_t)msg);
|
||||
return;
|
||||
}
|
||||
GETDNS_FREE(*mf, msg);
|
||||
}
|
||||
|
||||
getdns_return_t getdns_context_set_listen_addresses(getdns_context *context,
|
||||
getdns_request_handler_t request_handler, getdns_list *listen_addresses)
|
||||
{
|
||||
static const getdns_transport_list_t listen_transports[]
|
||||
= { GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP };
|
||||
static const uint32_t transport_ports[] = { 53, 53 };
|
||||
static const size_t n_transports = sizeof( listen_transports)
|
||||
/ sizeof(*listen_transports);
|
||||
|
||||
/* Things that should (eventually) be stored in the getdns_context */
|
||||
size_t listen_count;
|
||||
listen_data *listening;
|
||||
struct mem_funcs *mf;
|
||||
getdns_eventloop *loop;
|
||||
|
||||
/* auxiliary variables */
|
||||
getdns_return_t r;
|
||||
size_t i;
|
||||
size_t t;
|
||||
struct addrinfo hints;
|
||||
char addrstr[1024], portstr[1024], *eos;
|
||||
const int enable = 1; /* For SO_REUSEADDR */
|
||||
|
||||
if (!(mf = priv_getdns_context_mf(context)))
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
if ((r = getdns_context_get_eventloop(context, &loop)))
|
||||
return r;
|
||||
|
||||
if ((r = getdns_list_get_length(listen_addresses, &listen_count)))
|
||||
return r;
|
||||
|
||||
if (!listen_count)
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
||||
if (!(listening = GETDNS_XMALLOC(
|
||||
*mf, listen_data, listen_count * n_transports)))
|
||||
return GETDNS_RETURN_MEMORY_ERROR;
|
||||
|
||||
(void) memset(listening, 0,
|
||||
sizeof(listen_data) * n_transports * listen_count);
|
||||
(void) memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
(void) memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
for (i = 0; !r && i < listen_count; i++) {
|
||||
getdns_dict *dict = NULL;
|
||||
getdns_bindata *address_data;
|
||||
struct sockaddr_storage addr;
|
||||
getdns_bindata *scope_id;
|
||||
|
||||
if ((r = getdns_list_get_dict(listen_addresses, i, &dict))) {
|
||||
if ((r = getdns_list_get_bindata(
|
||||
listen_addresses, i, &address_data)))
|
||||
break;
|
||||
|
||||
} else if ((r = getdns_dict_get_bindata(
|
||||
dict, "address_data", &address_data)))
|
||||
break;
|
||||
|
||||
if (address_data->size == 4)
|
||||
addr.ss_family = AF_INET;
|
||||
else if (address_data->size == 16)
|
||||
addr.ss_family = AF_INET6;
|
||||
else {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
if (inet_ntop(addr.ss_family,
|
||||
address_data->data, addrstr, 1024) == NULL) {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
if (dict && getdns_dict_get_bindata(dict,"scope_id",&scope_id)
|
||||
== GETDNS_RETURN_GOOD) {
|
||||
if (strlen(addrstr) + scope_id->size > 1022) {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
eos = &addrstr[strlen(addrstr)];
|
||||
*eos++ = '%';
|
||||
(void) memcpy(eos, scope_id->data, scope_id->size);
|
||||
eos[scope_id->size] = 0;
|
||||
}
|
||||
for (t = 0; !r && t < n_transports; t++) {
|
||||
getdns_transport_list_t transport
|
||||
= listen_transports[t];
|
||||
uint32_t port = transport_ports[t];
|
||||
struct addrinfo *ai;
|
||||
listen_data *ld = &listening[i * n_transports + t];
|
||||
|
||||
ld->fd = -1;
|
||||
if (dict)
|
||||
(void) getdns_dict_get_int(dict,
|
||||
( transport == GETDNS_TRANSPORT_TLS
|
||||
? "tls_port" : "port" ), &port);
|
||||
|
||||
(void) snprintf(portstr, 1024, "%d", (int)port);
|
||||
|
||||
if (getaddrinfo(addrstr, portstr, &hints, &ai)) {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
ld->addr.ss_family = addr.ss_family;
|
||||
ld->addr_len = ai->ai_addrlen;
|
||||
(void) memcpy(&ld->addr, ai->ai_addr, ai->ai_addrlen);
|
||||
ld->transport = transport;
|
||||
ld->handler = request_handler;
|
||||
ld->context = context;
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
}
|
||||
if (r) {
|
||||
GETDNS_FREE(*mf, listening);
|
||||
listening = NULL;
|
||||
|
||||
} else for (i = 0; !r && i < listen_count * n_transports; i++) {
|
||||
listen_data *ld = &listening[i];
|
||||
|
||||
if (ld->transport != GETDNS_TRANSPORT_UDP &&
|
||||
ld->transport != GETDNS_TRANSPORT_TCP)
|
||||
continue;
|
||||
|
||||
if ((ld->fd = socket(ld->addr.ss_family,
|
||||
( ld->transport == GETDNS_TRANSPORT_UDP
|
||||
? SOCK_DGRAM : SOCK_STREAM), 0)) == -1)
|
||||
/* IO error, TODO: report? */
|
||||
continue;
|
||||
|
||||
if (setsockopt(ld->fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&enable, sizeof(int)) < 0)
|
||||
; /* Ignore */
|
||||
|
||||
if (bind(ld->fd, (struct sockaddr *)&ld->addr,
|
||||
ld->addr_len) == -1) {
|
||||
/* IO error, TODO: report? */
|
||||
(void) close(ld->fd);
|
||||
ld->fd = -1;
|
||||
}
|
||||
if (ld->transport == GETDNS_TRANSPORT_UDP) {
|
||||
ld->event.userarg = ld;
|
||||
ld->event.read_cb = udp_read_cb;
|
||||
(void) loop->vmt->schedule(
|
||||
loop, ld->fd, -1, &ld->event);
|
||||
|
||||
} else if (listen(ld->fd, TCP_LISTEN_BACKLOG) == -1) {
|
||||
/* IO error, TODO: report? */
|
||||
(void) close(ld->fd);
|
||||
ld->fd = -1;
|
||||
} else {
|
||||
ld->event.userarg = ld;
|
||||
ld->event.read_cb = tcp_accept_cb;
|
||||
(void) loop->vmt->schedule(
|
||||
loop, ld->fd, -1, &ld->event);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2013, NLNet Labs, Verisign, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the names of the copyright holders nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef GETDNS_CONTEXT_SET_LISTEN_ADDRESSES_H_
|
||||
#define GETDNS_CONTEXT_SET_LISTEN_ADDRESSES_H_
|
||||
#include "getdns/getdns.h"
|
||||
|
||||
typedef void (*getdns_request_handler_t)(
|
||||
getdns_context *context,
|
||||
getdns_dict *request,
|
||||
getdns_transaction_t request_id
|
||||
);
|
||||
|
||||
getdns_return_t getdns_context_set_listen_addresses(getdns_context *context,
|
||||
getdns_request_handler_t request_handler, getdns_list *listen_addresses);
|
||||
|
||||
getdns_return_t getdns_reply(getdns_context *context,
|
||||
getdns_transaction_t request_id, getdns_dict *reply);
|
||||
|
||||
void _getdns_cancel_reply(getdns_context *context,
|
||||
getdns_transaction_t request_id);
|
||||
|
||||
#endif
|
|
@ -29,6 +29,7 @@
|
|||
#include "debug.h"
|
||||
#include "getdns_str2dict.h"
|
||||
#include "getdns_context_config.h"
|
||||
#include "getdns_context_set_listen_addresses.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -383,9 +384,7 @@ static void parse_config(const char *config_str)
|
|||
if (!(r = getdns_dict_get_list(
|
||||
config_dict, "listen_addresses", &list))) {
|
||||
if (listen_list && !listen_dict) {
|
||||
/* TODO: Stop listening */
|
||||
getdns_list_destroy(listen_list);
|
||||
listen_count = 0;
|
||||
listen_list = NULL;
|
||||
}
|
||||
/* Strange construction to copy the list.
|
||||
|
@ -1091,117 +1090,14 @@ void read_line_cb(void *userarg)
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct listen_data {
|
||||
socklen_t addr_len;
|
||||
struct sockaddr_storage addr;
|
||||
int fd;
|
||||
getdns_transport_list_t transport;
|
||||
getdns_eventloop_event event;
|
||||
} listen_data;
|
||||
|
||||
|
||||
listen_data *listening = NULL;
|
||||
|
||||
typedef struct dns_msg {
|
||||
listen_data *ld;
|
||||
getdns_dict *query;
|
||||
uint32_t rt;
|
||||
uint32_t do_bit;
|
||||
getdns_transaction_t request_id;
|
||||
getdns_dict *query;
|
||||
uint32_t rt;
|
||||
uint32_t do_bit;
|
||||
uint32_t cd_bit;
|
||||
} dns_msg;
|
||||
|
||||
typedef struct udp_msg {
|
||||
dns_msg super;
|
||||
struct sockaddr_storage remote_in;
|
||||
socklen_t addrlen;
|
||||
} udp_msg;
|
||||
|
||||
typedef struct tcp_to_write tcp_to_write;
|
||||
struct tcp_to_write {
|
||||
size_t write_buf_len;
|
||||
size_t written;
|
||||
tcp_to_write *next;
|
||||
uint8_t write_buf[];
|
||||
};
|
||||
|
||||
#define DOWNSTREAM_IDLE_TIMEOUT 5000
|
||||
typedef struct downstream {
|
||||
listen_data *ld;
|
||||
struct sockaddr_storage remote_in;
|
||||
socklen_t addrlen;
|
||||
int fd;
|
||||
getdns_eventloop_event event;
|
||||
|
||||
uint8_t *read_buf;
|
||||
size_t read_buf_len;
|
||||
uint8_t *read_pos;
|
||||
size_t to_read;
|
||||
|
||||
tcp_to_write *to_write;
|
||||
size_t to_answer;
|
||||
} downstream;
|
||||
|
||||
typedef struct tcp_msg {
|
||||
dns_msg super;
|
||||
downstream *conn;
|
||||
} tcp_msg;
|
||||
|
||||
void downstream_destroy(downstream *conn)
|
||||
{
|
||||
tcp_to_write *cur, *next;
|
||||
|
||||
if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb)
|
||||
loop->vmt->clear(loop, &conn->event);
|
||||
if (conn->fd >= 0) {
|
||||
if (close(conn->fd) == -1)
|
||||
perror("close");
|
||||
}
|
||||
free(conn->read_buf);
|
||||
for (cur = conn->to_write; cur; cur = next) {
|
||||
next = cur->next;
|
||||
free(cur);
|
||||
}
|
||||
free(conn);
|
||||
}
|
||||
|
||||
void tcp_write_cb(void *userarg)
|
||||
{
|
||||
downstream *conn = (downstream *)userarg;
|
||||
tcp_to_write *to_write;
|
||||
ssize_t written;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
/* Reset downstream idle timeout */
|
||||
loop->vmt->clear(loop, &conn->event);
|
||||
|
||||
if (!conn->to_write) {
|
||||
conn->event.write_cb = NULL;
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
return;
|
||||
}
|
||||
to_write = conn->to_write;
|
||||
if ((written = write(conn->fd, &to_write->write_buf[to_write->written],
|
||||
to_write->write_buf_len - to_write->written)) == -1) {
|
||||
|
||||
perror("write");
|
||||
conn->event.read_cb = conn->event.write_cb =
|
||||
conn->event.timeout_cb = NULL;
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
to_write->written += written;
|
||||
if (to_write->written == to_write->write_buf_len) {
|
||||
conn->to_write = to_write->next;
|
||||
free(to_write);
|
||||
}
|
||||
if (!conn->to_write)
|
||||
conn->event.write_cb = NULL;
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
}
|
||||
|
||||
#if defined(TRACE_DEBUG) && TRACE_DEBUG
|
||||
#define SERVFAIL(error,r,msg,resp_p) do { \
|
||||
if (r) DEBUG_TRACE("%s: %s\n", error, getdns_get_errorstr_by_id(r)); \
|
||||
|
@ -1238,8 +1134,6 @@ void request_cb(getdns_context *context, getdns_callback_type_t callback_type,
|
|||
dns_msg *msg = (dns_msg *)userarg;
|
||||
uint32_t qid;
|
||||
getdns_return_t r = GETDNS_RETURN_GOOD;
|
||||
uint8_t buf[65536];
|
||||
size_t len = sizeof(buf);
|
||||
uint32_t n;
|
||||
|
||||
DEBUG_TRACE("reply for: %p %"PRIu64" %d\n", msg, transaction_id, (int)callback_type);
|
||||
|
@ -1291,48 +1185,13 @@ void request_cb(getdns_context *context, getdns_callback_type_t callback_type,
|
|||
SERVFAIL("Recursion not available", 0, msg, &response);
|
||||
|
||||
if (!response)
|
||||
; /* No response, no reply */
|
||||
/* No response, no reply */
|
||||
_getdns_cancel_reply(context, msg->request_id);
|
||||
|
||||
else if ((r = getdns_msg_dict2wire_buf(response, buf, &len)))
|
||||
fprintf(stderr, "Could not convert reply: %s\n",
|
||||
else if ((r = getdns_reply(context, msg->request_id, response))) {
|
||||
fprintf(stderr, "Could not reply: %s\n",
|
||||
getdns_get_errorstr_by_id(r));
|
||||
|
||||
else if (msg->ld->transport == GETDNS_TRANSPORT_UDP) {
|
||||
udp_msg *msg = (udp_msg *)userarg;
|
||||
|
||||
if (sendto(msg->super.ld->fd, buf, len, 0,
|
||||
(struct sockaddr *)&msg->remote_in, msg->addrlen) == -1)
|
||||
perror("sendto");
|
||||
|
||||
} else if (msg->ld->transport == GETDNS_TRANSPORT_TCP) {
|
||||
tcp_msg *msg = (tcp_msg *)userarg;
|
||||
tcp_to_write **to_write_p;
|
||||
tcp_to_write *to_write = malloc(sizeof(tcp_to_write) + len + 2);
|
||||
|
||||
if (!to_write)
|
||||
fprintf(stderr, "Could not allocate memory for"
|
||||
"message to write on tcp stream\n");
|
||||
else {
|
||||
to_write->write_buf_len = len + 2;
|
||||
to_write->write_buf[0] = (len >> 8) & 0xFF;
|
||||
to_write->write_buf[1] = len & 0xFF;
|
||||
to_write->written = 0;
|
||||
to_write->next = NULL;
|
||||
(void) memcpy(to_write->write_buf + 2, buf, len);
|
||||
|
||||
/* Appen to_write to conn->to_write list */
|
||||
for ( to_write_p = &msg->conn->to_write
|
||||
; *to_write_p
|
||||
; to_write_p = &(*to_write_p)->next)
|
||||
; /* pass */
|
||||
*to_write_p = to_write;
|
||||
|
||||
loop->vmt->clear(loop, &msg->conn->event);
|
||||
msg->conn->event.write_cb = tcp_write_cb;
|
||||
(void) loop->vmt->schedule(loop,
|
||||
msg->conn->fd, DOWNSTREAM_IDLE_TIMEOUT,
|
||||
&msg->conn->event);
|
||||
}
|
||||
_getdns_cancel_reply(context, msg->request_id);
|
||||
}
|
||||
if (msg) {
|
||||
getdns_dict_destroy(msg->query);
|
||||
|
@ -1342,7 +1201,8 @@ void request_cb(getdns_context *context, getdns_callback_type_t callback_type,
|
|||
getdns_dict_destroy(response);
|
||||
}
|
||||
|
||||
getdns_return_t schedule_request(dns_msg *msg)
|
||||
void incoming_request_handler(getdns_context *context,
|
||||
getdns_dict *request, getdns_transaction_t request_id)
|
||||
{
|
||||
getdns_bindata *qname;
|
||||
char *qname_str = NULL;
|
||||
|
@ -1353,6 +1213,20 @@ getdns_return_t schedule_request(dns_msg *msg)
|
|||
getdns_list *list;
|
||||
getdns_transaction_t transaction_id;
|
||||
getdns_dict *qext;
|
||||
dns_msg *msg;
|
||||
getdns_dict *response = NULL;
|
||||
|
||||
assert(context);
|
||||
assert(request);
|
||||
|
||||
if (!(msg = malloc(sizeof(dns_msg)))) {
|
||||
fprintf(stderr, "Could not handle incoming query due to "
|
||||
"memory error\n");
|
||||
getdns_dict_destroy(request);
|
||||
return;
|
||||
}
|
||||
msg->query = request;
|
||||
msg->request_id = request_id;
|
||||
|
||||
if (!query_extensions_spc &&
|
||||
!(query_extensions_spc = getdns_dict_create()))
|
||||
|
@ -1437,308 +1311,27 @@ getdns_return_t schedule_request(dns_msg *msg)
|
|||
qext, msg, &transaction_id, request_cb)))
|
||||
fprintf(stderr, "Could not schedule query: %s\n",
|
||||
getdns_get_errorstr_by_id(r));
|
||||
|
||||
DEBUG_TRACE("scheduled: %p %"PRIu64" for %s %d\n",
|
||||
msg, transaction_id, qname_str, (int)qtype);
|
||||
else {
|
||||
DEBUG_TRACE("scheduled: %p %"PRIu64" for %s %d\n",
|
||||
msg, transaction_id, qname_str, (int)qtype);
|
||||
free(qname_str);
|
||||
return;
|
||||
}
|
||||
free(qname_str);
|
||||
servfail(msg, &response);
|
||||
if (!response)
|
||||
/* No response, no reply */
|
||||
_getdns_cancel_reply(context, msg->request_id);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void tcp_read_cb(void *userarg)
|
||||
{
|
||||
downstream *conn = (downstream *)userarg;
|
||||
ssize_t bytes_read;
|
||||
tcp_msg *msg;
|
||||
getdns_return_t r;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
/* Reset downstream idle timeout */
|
||||
loop->vmt->clear(loop, &conn->event);
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
|
||||
if ((bytes_read = read(conn->fd, conn->read_pos, conn->to_read)) == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return;
|
||||
perror("read");
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
/* fprintf(stderr, "Remote end closed connection\n"); */
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
assert(bytes_read <= conn->to_read);
|
||||
|
||||
conn->to_read -= bytes_read;
|
||||
conn->read_pos += bytes_read;
|
||||
if (conn->to_read)
|
||||
return; /* More to read */
|
||||
|
||||
if (conn->read_pos - conn->read_buf == 2) {
|
||||
/* read length of dns msg to read */
|
||||
conn->to_read = (conn->read_buf[0] << 8) | conn->read_buf[1];
|
||||
if (conn->to_read > conn->read_buf_len) {
|
||||
free(conn->read_buf);
|
||||
while (conn->to_read > conn->read_buf_len)
|
||||
conn->read_buf_len *= 2;
|
||||
if (!(conn->read_buf = malloc(conn->read_buf_len))) {
|
||||
fprintf(stderr, "Could not enlarge "
|
||||
"downstream read buffer\n");
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (conn->to_read < 12) {
|
||||
fprintf(stderr, "Request smaller than DNS header\n");
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
conn->read_pos = conn->read_buf;
|
||||
return; /* Read DNS message */
|
||||
}
|
||||
if (!(msg = malloc(sizeof(tcp_msg)))) {
|
||||
fprintf(stderr, "Could not allocate tcp_msg\n");
|
||||
downstream_destroy(conn);
|
||||
return;
|
||||
}
|
||||
msg->super.ld = conn->ld;
|
||||
msg->conn = conn;
|
||||
if ((r = getdns_wire2msg_dict(conn->read_buf,
|
||||
(conn->read_pos - conn->read_buf), &msg->super.query)))
|
||||
fprintf(stderr, "Error converting query dns msg: %s\n",
|
||||
else if ((r = getdns_reply(context, msg->request_id, response))) {
|
||||
fprintf(stderr, "Could not reply: %s\n",
|
||||
getdns_get_errorstr_by_id(r));
|
||||
|
||||
else if ((r = schedule_request(&msg->super))) {
|
||||
fprintf(stderr, "Error scheduling query: %s\n",
|
||||
getdns_get_errorstr_by_id(r));
|
||||
|
||||
getdns_dict_destroy(msg->super.query);
|
||||
} else {
|
||||
conn->to_answer += 1;
|
||||
conn->read_pos = conn->read_buf;
|
||||
conn->to_read = 2;
|
||||
return; /* Read more requests */
|
||||
_getdns_cancel_reply(context, msg->request_id);
|
||||
}
|
||||
getdns_dict_destroy(msg->query);
|
||||
free(msg);
|
||||
conn->read_pos = conn->read_buf;
|
||||
conn->to_read = 2;
|
||||
/* Read more requests */
|
||||
}
|
||||
|
||||
void tcp_timeout_cb(void *userarg)
|
||||
{
|
||||
downstream *conn = (downstream *)userarg;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
downstream_destroy(conn);
|
||||
}
|
||||
|
||||
void tcp_accept_cb(void *userarg)
|
||||
{
|
||||
listen_data *ld = (listen_data *)userarg;
|
||||
downstream *conn;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
if (!(conn = malloc(sizeof(downstream))))
|
||||
return;
|
||||
|
||||
(void) memset(conn, 0, sizeof(downstream));
|
||||
|
||||
conn->ld = ld;
|
||||
conn->addrlen = sizeof(conn->remote_in);
|
||||
if ((conn->fd = accept(ld->fd,
|
||||
(struct sockaddr *)&conn->remote_in, &conn->addrlen)) == -1) {
|
||||
perror("accept");
|
||||
free(conn);
|
||||
}
|
||||
if (!(conn->read_buf = malloc(4096))) {
|
||||
fprintf(stderr, "Could not allocate downstream read buffer.\n");
|
||||
free(conn);
|
||||
}
|
||||
conn->read_buf_len = 4096;
|
||||
conn->read_pos = conn->read_buf;
|
||||
conn->to_read = 2;
|
||||
conn->event.userarg = conn;
|
||||
conn->event.read_cb = tcp_read_cb;
|
||||
conn->event.timeout_cb = tcp_timeout_cb;
|
||||
(void) loop->vmt->schedule(loop, conn->fd,
|
||||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
|
||||
}
|
||||
|
||||
void udp_read_cb(void *userarg)
|
||||
{
|
||||
listen_data *ld = (listen_data *)userarg;
|
||||
udp_msg *msg;
|
||||
uint8_t buf[65536];
|
||||
ssize_t len;
|
||||
getdns_return_t r;
|
||||
|
||||
assert(userarg);
|
||||
|
||||
if (!(msg = malloc(sizeof(udp_msg))))
|
||||
return;
|
||||
|
||||
msg->super.ld = ld;
|
||||
msg->addrlen = sizeof(msg->remote_in);
|
||||
if ((len = recvfrom(ld->fd, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&msg->remote_in, &msg->addrlen)) == -1)
|
||||
perror("recvfrom");
|
||||
|
||||
else if ((r = getdns_wire2msg_dict(buf, len, &msg->super.query)))
|
||||
fprintf(stderr, "Error converting query dns msg: %s\n",
|
||||
getdns_get_errorstr_by_id(r));
|
||||
|
||||
else if ((r = schedule_request(&msg->super))) {
|
||||
fprintf(stderr, "Error scheduling query: %s\n",
|
||||
getdns_get_errorstr_by_id(r));
|
||||
|
||||
getdns_dict_destroy(msg->super.query);
|
||||
} else
|
||||
return;
|
||||
free(msg);
|
||||
}
|
||||
|
||||
getdns_return_t start_daemon()
|
||||
{
|
||||
static const getdns_transport_list_t listen_transports[]
|
||||
= { GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP };
|
||||
static const uint32_t transport_ports[] = { 53, 53 };
|
||||
static const size_t n_transports = sizeof( listen_transports)
|
||||
/ sizeof(*listen_transports);
|
||||
|
||||
size_t i;
|
||||
size_t t;
|
||||
struct addrinfo hints;
|
||||
getdns_return_t r = GETDNS_RETURN_GOOD;
|
||||
char addrstr[1024], portstr[1024], *eos;
|
||||
const int enable = 1; /* For SO_REUSEADDR */
|
||||
|
||||
if (!listen_count)
|
||||
return GETDNS_RETURN_GOOD;
|
||||
|
||||
if (!(listening = malloc(
|
||||
sizeof(listen_data) * n_transports * listen_count)))
|
||||
return GETDNS_RETURN_MEMORY_ERROR;
|
||||
|
||||
(void) memset(listening, 0,
|
||||
sizeof(listen_data) * n_transports * listen_count);
|
||||
(void) memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
for (i = 0; !r && i < listen_count; i++) {
|
||||
getdns_dict *dict = NULL;
|
||||
getdns_bindata *address_data;
|
||||
struct sockaddr_storage addr;
|
||||
getdns_bindata *scope_id;
|
||||
|
||||
if ((r = getdns_list_get_dict(listen_list, i, &dict))) {
|
||||
if ((r = getdns_list_get_bindata(
|
||||
listen_list, i, &address_data)))
|
||||
break;
|
||||
} else if ((r = getdns_dict_get_bindata(
|
||||
dict, "address_data", &address_data)))
|
||||
break;
|
||||
if (address_data->size == 4)
|
||||
addr.ss_family = AF_INET;
|
||||
else if (address_data->size == 16)
|
||||
addr.ss_family = AF_INET6;
|
||||
else {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
if (inet_ntop(addr.ss_family,
|
||||
address_data->data, addrstr, 1024) == NULL) {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
if (dict && getdns_dict_get_bindata(dict,"scope_id",&scope_id)
|
||||
== GETDNS_RETURN_GOOD) {
|
||||
if (strlen(addrstr) + scope_id->size > 1022) {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
eos = &addrstr[strlen(addrstr)];
|
||||
*eos++ = '%';
|
||||
(void) memcpy(eos, scope_id->data, scope_id->size);
|
||||
eos[scope_id->size] = 0;
|
||||
}
|
||||
for (t = 0; !r && t < n_transports; t++) {
|
||||
getdns_transport_list_t transport
|
||||
= listen_transports[t];
|
||||
uint32_t port = transport_ports[t];
|
||||
struct addrinfo *ai;
|
||||
listen_data *ld = &listening[i * n_transports + t];
|
||||
|
||||
ld->fd = -1;
|
||||
if (dict)
|
||||
(void) getdns_dict_get_int(dict,
|
||||
( transport == GETDNS_TRANSPORT_TLS
|
||||
? "tls_port" : "port" ), &port);
|
||||
|
||||
(void) snprintf(portstr, 1024, "%d", (int)port);
|
||||
|
||||
if (getaddrinfo(addrstr, portstr, &hints, &ai)) {
|
||||
r = GETDNS_RETURN_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
ld->addr.ss_family = addr.ss_family;
|
||||
ld->addr_len = ai->ai_addrlen;
|
||||
(void) memcpy(&ld->addr, ai->ai_addr, ai->ai_addrlen);
|
||||
ld->transport = transport;
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
|
||||
}
|
||||
if (r) {
|
||||
free(listening);
|
||||
listening = NULL;
|
||||
} else for (i = 0; !r && i < listen_count * n_transports; i++) {
|
||||
listen_data *ld = &listening[i];
|
||||
|
||||
if (ld->transport != GETDNS_TRANSPORT_UDP &&
|
||||
ld->transport != GETDNS_TRANSPORT_TCP)
|
||||
continue;
|
||||
|
||||
if ((ld->fd = socket(ld->addr.ss_family,
|
||||
( ld->transport == GETDNS_TRANSPORT_UDP
|
||||
? SOCK_DGRAM : SOCK_STREAM), 0)) == -1)
|
||||
perror("socket");
|
||||
|
||||
else if (setsockopt(ld->fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&enable, sizeof(int)) < 0)
|
||||
perror("setsockopt(SO_REUSEADDR) failed");
|
||||
|
||||
else if (bind(ld->fd, (struct sockaddr *)&ld->addr,
|
||||
ld->addr_len) == -1)
|
||||
perror("bind");
|
||||
|
||||
else if (ld->transport == GETDNS_TRANSPORT_UDP) {
|
||||
ld->event.userarg = ld;
|
||||
ld->event.read_cb = udp_read_cb;
|
||||
(void) loop->vmt->schedule(
|
||||
loop, ld->fd, -1, &ld->event);
|
||||
|
||||
} else if (listen(ld->fd, 16) == -1)
|
||||
perror("listen");
|
||||
|
||||
else {
|
||||
ld->event.userarg = ld;
|
||||
ld->event.read_cb = tcp_accept_cb;
|
||||
(void) loop->vmt->schedule(
|
||||
loop, ld->fd, -1, &ld->event);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
if (response)
|
||||
getdns_dict_destroy(response);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1776,7 +1369,11 @@ main(int argc, char **argv)
|
|||
goto done_destroy_context;
|
||||
assert(loop);
|
||||
}
|
||||
start_daemon();
|
||||
if (listen_count)
|
||||
if ((r = getdns_context_set_listen_addresses(context,
|
||||
incoming_request_handler, listen_list)))
|
||||
goto done_destroy_context;
|
||||
|
||||
/* Make the call */
|
||||
if (interactive) {
|
||||
getdns_eventloop_event read_line_ev = {
|
||||
|
|
Loading…
Reference in New Issue