129 changes: 100 additions & 29 deletions testcode/streamtcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "util/log.h"
#include "util/net_help.h"
#include "util/data/msgencode.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/dname.h"

Expand All @@ -65,6 +66,7 @@ static void usage(char* argv[])
printf("-f server what ipaddr@portnr to send the queries to\n");
printf("-u use UDP. No retries are attempted.\n");
printf("-n do not wait for an answer.\n");
printf("-s use ssl\n");
printf("-h this help text\n");
exit(1);
}
Expand Down Expand Up @@ -105,7 +107,7 @@ open_svr(const char* svr, int udp)

/** write a query over the TCP fd */
static void
write_q(int fd, int udp, ldns_buffer* buf, int id,
write_q(int fd, int udp, SSL* ssl, ldns_buffer* buf, uint16_t id,
const char* strname, const char* strtype, const char* strclass)
{
struct query_info qinfo;
Expand All @@ -128,65 +130,111 @@ write_q(int fd, int udp, ldns_buffer* buf, int id,

/* make query */
qinfo_query_encode(buf, &qinfo);
ldns_buffer_write_u16_at(buf, 0, (uint16_t)id);
ldns_buffer_write_u16_at(buf, 0, id);
ldns_buffer_write_u16_at(buf, 2, BIT_RD);

if(1) {
/* add EDNS DO */
struct edns_data edns;
memset(&edns, 0, sizeof(edns));
edns.edns_present = 1;
edns.bits = EDNS_DO;
edns.udp_size = 4096;
attach_edns_record(buf, &edns);
}

/* send it */
if(!udp) {
len = (uint16_t)ldns_buffer_limit(buf);
len = htons(len);
if(send(fd, (void*)&len, sizeof(len), 0)<(ssize_t)sizeof(len)){
if(ssl) {
if(SSL_write(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
log_crypto_err("cannot SSL_write");
exit(1);
}
} else {
if(send(fd, (void*)&len, sizeof(len), 0) <
(ssize_t)sizeof(len)){
#ifndef USE_WINSOCK
perror("send() len failed");
perror("send() len failed");
#else
printf("send len: %s\n",
wsa_strerror(WSAGetLastError()));
printf("send len: %s\n",
wsa_strerror(WSAGetLastError()));
#endif
exit(1);
exit(1);
}
}
}
if(send(fd, (void*)ldns_buffer_begin(buf), ldns_buffer_limit(buf), 0) <
(ssize_t)ldns_buffer_limit(buf)) {
if(ssl) {
if(SSL_write(ssl, (void*)ldns_buffer_begin(buf),
(int)ldns_buffer_limit(buf)) <= 0) {
log_crypto_err("cannot SSL_write");
exit(1);
}
} else {
if(send(fd, (void*)ldns_buffer_begin(buf),
ldns_buffer_limit(buf), 0) <
(ssize_t)ldns_buffer_limit(buf)) {
#ifndef USE_WINSOCK
perror("send() data failed");
perror("send() data failed");
#else
printf("send data: %s\n", wsa_strerror(WSAGetLastError()));
printf("send data: %s\n", wsa_strerror(WSAGetLastError()));
#endif
exit(1);
exit(1);
}
}

free(qinfo.qname);
}

/** receive DNS datagram over TCP and print it */
static void
recv_one(int fd, int udp, ldns_buffer* buf)
recv_one(int fd, int udp, SSL* ssl, ldns_buffer* buf)
{
uint16_t len;
ldns_pkt* pkt;
ldns_status status;
if(!udp) {
if(recv(fd, (void*)&len, sizeof(len), 0)<(ssize_t)sizeof(len)){
if(ssl) {
if(SSL_read(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
log_crypto_err("could not SSL_read");
exit(1);
}
} else {
if(recv(fd, (void*)&len, sizeof(len), 0) <
(ssize_t)sizeof(len)) {
#ifndef USE_WINSOCK
perror("read() len failed");
perror("read() len failed");
#else
printf("read len: %s\n",
wsa_strerror(WSAGetLastError()));
printf("read len: %s\n",
wsa_strerror(WSAGetLastError()));
#endif
exit(1);
exit(1);
}
}
len = ntohs(len);
ldns_buffer_clear(buf);
ldns_buffer_set_limit(buf, len);
if(recv(fd, (void*)ldns_buffer_begin(buf), len, 0) <
(ssize_t)len) {
if(ssl) {
int r = SSL_read(ssl, (void*)ldns_buffer_begin(buf),
(int)len);
if(r <= 0) {
log_crypto_err("could not SSL_read");
exit(1);
}
if(r != (int)len)
fatal_exit("ssl_read %d of %d", r, len);
} else {
if(recv(fd, (void*)ldns_buffer_begin(buf), len, 0) <
(ssize_t)len) {
#ifndef USE_WINSOCK
perror("read() data failed");
perror("read() data failed");
#else
printf("read data: %s\n",
wsa_strerror(WSAGetLastError()));
printf("read data: %s\n",
wsa_strerror(WSAGetLastError()));
#endif
exit(1);
exit(1);
}
}
} else {
ssize_t l;
Expand Down Expand Up @@ -220,20 +268,34 @@ recv_one(int fd, int udp, ldns_buffer* buf)

/** send the TCP queries and print answers */
static void
send_em(const char* svr, int udp, int noanswer, int num, char** qs)
send_em(const char* svr, int udp, int usessl, int noanswer, int num, char** qs)
{
ldns_buffer* buf = ldns_buffer_new(65553);
int fd = open_svr(svr, udp);
int i;
SSL_CTX* ctx;
SSL* ssl;
if(!buf) fatal_exit("out of memory");
if(usessl) {
ctx = connect_sslctx_create(NULL, NULL, NULL);
if(!ctx) fatal_exit("cannot create ssl ctx");
ssl = outgoing_ssl_fd(ctx, fd);
if(!ssl) fatal_exit("cannot create ssl");
}
for(i=0; i<num; i+=3) {
printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
write_q(fd, udp, buf, i, qs[i], qs[i+1], qs[i+2]);
write_q(fd, udp, ssl, buf, ldns_get_random(), qs[i],
qs[i+1], qs[i+2]);
/* print at least one result */
if(!noanswer)
recv_one(fd, udp, buf);
recv_one(fd, udp, ssl, buf);
}

if(usessl) {
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
}
#ifndef USE_WINSOCK
close(fd);
#else
Expand Down Expand Up @@ -268,6 +330,7 @@ int main(int argc, char** argv)
const char* svr = "127.0.0.1";
int udp = 0;
int noanswer = 0;
int usessl = 0;

#ifdef USE_WINSOCK
WSADATA wsa_data;
Expand All @@ -292,7 +355,7 @@ int main(int argc, char** argv)
if(argc == 1) {
usage(argv);
}
while( (c=getopt(argc, argv, "f:hnu")) != -1) {
while( (c=getopt(argc, argv, "f:hnsu")) != -1) {
switch(c) {
case 'f':
svr = optarg;
Expand All @@ -303,6 +366,9 @@ int main(int argc, char** argv)
case 'u':
udp = 1;
break;
case 's':
usessl = 1;
break;
case 'h':
case '?':
default:
Expand All @@ -316,7 +382,12 @@ int main(int argc, char** argv)
printf("queries must be multiples of name,type,class\n");
return 1;
}
send_em(svr, udp, noanswer, argc, argv);
if(usessl) {
ERR_load_SSL_strings();
OpenSSL_add_all_algorithms();
SSL_library_init();
}
send_em(svr, udp, usessl, noanswer, argc, argv);
checklock_stop();
#ifdef USE_WINSOCK
WSACleanup();
Expand Down
11 changes: 11 additions & 0 deletions util/config_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ config_create(void)
cfg->do_udp = 1;
cfg->do_tcp = 1;
cfg->tcp_upstream = 0;
cfg->ssl_service_key = NULL;
cfg->ssl_service_pem = NULL;
cfg->ssl_upstream = 0;
cfg->use_syslog = 1;
cfg->log_time_ascii = 0;
cfg->log_queries = 0;
Expand Down Expand Up @@ -326,6 +329,9 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_YNO("do-udp:", do_udp)
else S_YNO("do-tcp:", do_tcp)
else S_YNO("tcp-upstream:", tcp_upstream)
else S_YNO("ssl-upstream:", ssl_upstream)
else S_STR("ssl-service-key:", ssl_service_key)
else S_STR("ssl-service-pem:", ssl_service_pem)
else S_YNO("interface-automatic:", if_automatic)
else S_YNO("do-daemonize:", do_daemonize)
else S_NUMBER_NONZERO("port:", port)
Expand Down Expand Up @@ -574,6 +580,9 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_YNO(opt, "do-udp", do_udp)
else O_YNO(opt, "do-tcp", do_tcp)
else O_YNO(opt, "tcp-upstream", tcp_upstream)
else O_YNO(opt, "ssl-upstream", ssl_upstream)
else O_STR(opt, "ssl-service-key", ssl_service_key)
else O_STR(opt, "ssl-service-pem", ssl_service_pem)
else O_YNO(opt, "do-daemonize", do_daemonize)
else O_STR(opt, "chroot", chrootdir)
else O_STR(opt, "username", username)
Expand Down Expand Up @@ -728,6 +737,8 @@ config_delete(struct config_file* cfg)
free(cfg->logfile);
free(cfg->pidfile);
free(cfg->target_fetch_policy);
free(cfg->ssl_service_key);
free(cfg->ssl_service_pem);
if(cfg->ifs) {
int i;
for(i=0; i<cfg->num_ifs; i++)
Expand Down
7 changes: 7 additions & 0 deletions util/config_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ struct config_file {
/** tcp upstream queries (no UDP upstream queries) */
int tcp_upstream;

/** private key file for dnstcp-ssl service (enabled if not NULL) */
char* ssl_service_key;
/** public key file for dnstcp-ssl service */
char* ssl_service_pem;
/** if outgoing tcp connections use SSL */
int ssl_upstream;

/** outgoing port range number of ports (per thread) */
int outgoing_num_ports;
/** number of outgoing tcp buffers per (per thread) */
Expand Down
2,303 changes: 1,171 additions & 1,132 deletions util/configlexer.c

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions util/configlexer.lex
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ do-ip6{COLON} { YDVAR(1, VAR_DO_IP6) }
do-udp{COLON} { YDVAR(1, VAR_DO_UDP) }
do-tcp{COLON} { YDVAR(1, VAR_DO_TCP) }
tcp-upstream{COLON} { YDVAR(1, VAR_TCP_UPSTREAM) }
ssl-upstream{COLON} { YDVAR(1, VAR_SSL_UPSTREAM) }
ssl-service-key{COLON} { YDVAR(1, VAR_SSL_SERVICE_KEY) }
ssl-service-pem{COLON} { YDVAR(1, VAR_SSL_SERVICE_PEM) }
do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) }
interface{COLON} { YDVAR(1, VAR_INTERFACE) }
outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) }
Expand Down
979 changes: 516 additions & 463 deletions util/configparser.c

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions util/configparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@
VAR_HARDEN_BELOW_NXDOMAIN = 373,
VAR_IGNORE_CD_FLAG = 374,
VAR_LOG_QUERIES = 375,
VAR_TCP_UPSTREAM = 376
VAR_TCP_UPSTREAM = 376,
VAR_SSL_UPSTREAM = 377,
VAR_SSL_SERVICE_KEY = 378,
VAR_SSL_SERVICE_PEM = 379
};
#endif
/* Tokens. */
Expand Down Expand Up @@ -279,6 +282,9 @@
#define VAR_IGNORE_CD_FLAG 374
#define VAR_LOG_QUERIES 375
#define VAR_TCP_UPSTREAM 376
#define VAR_SSL_UPSTREAM 377
#define VAR_SSL_SERVICE_KEY 378
#define VAR_SSL_SERVICE_PEM 379



Expand All @@ -295,7 +301,7 @@ typedef union YYSTYPE


/* Line 1685 of yacc.c */
#line 299 "util/configparser.h"
#line 305 "util/configparser.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
Expand Down
29 changes: 27 additions & 2 deletions util/configparser.y
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ extern struct config_parser_state* cfg_parser;
%token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN
%token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE VAR_PREFETCH
%token VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_HARDEN_BELOW_NXDOMAIN
%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM
%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
%token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM

%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
Expand Down Expand Up @@ -157,7 +158,8 @@ content_server: server_num_threads | server_verbosity | server_port |
server_del_holddown | server_keep_missing | server_so_rcvbuf |
server_edns_buffer_size | server_prefetch | server_prefetch_key |
server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
server_log_queries | server_tcp_upstream
server_log_queries | server_tcp_upstream | server_ssl_upstream |
server_ssl_service_key | server_ssl_service_pem
;
stubstart: VAR_STUB_ZONE
{
Expand Down Expand Up @@ -374,6 +376,29 @@ server_tcp_upstream: VAR_TCP_UPSTREAM STRING_ARG
free($2);
}
;
server_ssl_upstream: VAR_SSL_UPSTREAM STRING_ARG
{
OUTYY(("P(server_ssl_upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ssl_upstream = (strcmp($2, "yes")==0);
free($2);
}
;
server_ssl_service_key: VAR_SSL_SERVICE_KEY STRING_ARG
{
OUTYY(("P(server_ssl_service_key:%s)\n", $2));
free(cfg_parser->cfg->ssl_service_key);
cfg_parser->cfg->ssl_service_key = $2;
}
;
server_ssl_service_pem: VAR_SSL_SERVICE_PEM STRING_ARG
{
OUTYY(("P(server_ssl_service_pem:%s)\n", $2));
free(cfg_parser->cfg->ssl_service_pem);
cfg_parser->cfg->ssl_service_pem = $2;
}
;
server_do_daemonize: VAR_DO_DAEMONIZE STRING_ARG
{
OUTYY(("P(server_do_daemonize:%s)\n", $2));
Expand Down
138 changes: 138 additions & 0 deletions util/net_help.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#include "util/module.h"
#include "util/regional.h"
#include <fcntl.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

/** max length of an IP address (the address portion) that we allow */
#define MAX_ADDR_STRLEN 128 /* characters */
Expand Down Expand Up @@ -553,3 +555,139 @@ void sock_list_merge(struct sock_list** list, struct regional* region,
sock_list_insert(list, &p->addr, p->len, region);
}
}

void
log_crypto_err(const char* str)
{
/* error:[error code]:[library name]:[function name]:[reason string] */
char buf[128];
unsigned long e;
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
log_err("%s crypto %s", str, buf);
while( (e=ERR_get_error()) ) {
ERR_error_string_n(e, buf, sizeof(buf));
log_err("and additionally crypto %s", buf);
}
}

void* listen_sslctx_create(char* key, char* pem, char* verifypem)
{
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
if(!ctx) {
log_crypto_err("could not SSL_CTX_new");
return NULL;
}
/* no SSLv2 because has defects */
if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)){
log_crypto_err("could not set SSL_OP_NO_SSLv2");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_certificate_file(ctx, pem, SSL_FILETYPE_PEM)) {
log_err("error for cert file: %s", pem);
log_crypto_err("error in SSL_CTX use_certificate_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("error for private key file: %s", key);
log_crypto_err("Error in SSL_CTX use_PrivateKey_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("error for key file: %s", key);
log_crypto_err("Error in SSL_CTX check_private_key");
SSL_CTX_free(ctx);
return NULL;
}

if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
log_crypto_err("Error in SSL_CTX verify locations");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(
verifypem));
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
return ctx;
}

void* connect_sslctx_create(char* key, char* pem, char* verifypem)
{
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
if(!ctx) {
log_crypto_err("could not allocate SSL_CTX pointer");
return NULL;
}
if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)) {
log_crypto_err("could not set SSL_OP_NO_SSLv2");
SSL_CTX_free(ctx);
return NULL;
}
if(key && key[0]) {
if(!SSL_CTX_use_certificate_file(ctx, pem, SSL_FILETYPE_PEM)) {
log_err("error in client certificate %s", pem);
log_crypto_err("error in certificate file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("error in client private key %s", key);
log_crypto_err("error in key file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("error in client key %s", key);
log_crypto_err("error in SSL_CTX_check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
}
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL) != 1) {
log_crypto_err("error in SSL_CTX verify");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
return ctx;
}

void* incoming_ssl_fd(void* sslctx, int fd)
{
SSL* ssl = SSL_new((SSL_CTX*)sslctx);
if(!ssl) {
log_crypto_err("could not SSL_new");
return NULL;
}
SSL_set_accept_state(ssl);
(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(ssl, fd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(ssl);
return NULL;
}
return ssl;
}

void* outgoing_ssl_fd(void* sslctx, int fd)
{
SSL* ssl = SSL_new((SSL_CTX*)sslctx);
if(!ssl) {
log_crypto_err("could not SSL_new");
return NULL;
}
SSL_set_connect_state(ssl);
(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(ssl, fd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(ssl);
return NULL;
}
return ssl;
}
40 changes: 40 additions & 0 deletions util/net_help.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,44 @@ int sock_list_find(struct sock_list* list, struct sockaddr_storage* addr,
void sock_list_merge(struct sock_list** list, struct regional* region,
struct sock_list* add);

/**
* Log libcrypto error with descriptive string. Calls log_err().
* @param str: what failed.
*/
void log_crypto_err(const char* str);

/**
* create SSL listen context
* @param key: private key file.
* @param pem: public key cert.
* @param verifypem: if nonNULL, verifylocation file.
* return SSL_CTX* or NULL on failure (logged).
*/
void* listen_sslctx_create(char* key, char* pem, char* verifypem);

/**
* create SSL connect context
* @param key: if nonNULL (also pem nonNULL), the client private key.
* @param pem: client public key (or NULL if key is NULL).
* @param verifypem: if nonNULL used for verifylocation file.
* @return SSL_CTX* or NULL on failure (logged).
*/
void* connect_sslctx_create(char* key, char* pem, char* verifypem);

/**
* accept a new fd and wrap it in a BIO in SSL
* @param sslctx: the SSL_CTX to use (from listen_sslctx_create()).
* @param fd: from accept, nonblocking.
* @return SSL or NULL on alloc failure.
*/
void* incoming_ssl_fd(void* sslctx, int fd);

/**
* connect a new fd and wrap it in a BIO in SSL
* @param sslctx: the SSL_CTX to use (from connect_sslctx_create())
* @param fd: from connect.
* @return SSL or NULL on alloc failure
*/
void* outgoing_ssl_fd(void* sslctx, int fd);

#endif /* NET_HELP_H */
281 changes: 281 additions & 0 deletions util/netevent.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#include "util/log.h"
#include "util/net_help.h"
#include "util/fptr_wlist.h"
#include <openssl/ssl.h>
#include <openssl/err.h>

/* -------- Start of local definitions -------- */
/** if CMSG_ALIGN is not defined on this platform, a workaround */
Expand Down Expand Up @@ -683,6 +685,32 @@ int comm_point_perform_accept(struct comm_point* c,
return new_fd;
}

#ifdef USE_WINSOCK
static long win_bio_cb(BIO *b, int oper, const char* ATTR_UNUSED(argp),
int ATTR_UNUSED(argi), long argl, long retvalue)
{
verbose(VERB_ALGO, "bio_cb %d, %s %s %s", oper,
(oper&BIO_CB_RETURN)?"return":"before",
(oper&BIO_CB_READ)?"read":((oper&BIO_CB_WRITE)?"write":"other"),
WSAGetLastError()==WSAEWOULDBLOCK?"wsawb":"");
/* on windows, check if previous operation caused EWOULDBLOCK */
if( (oper == (BIO_CB_READ|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_GETS|BIO_CB_RETURN) && argl == 0)) {
if(WSAGetLastError() == WSAEWOULDBLOCK)
winsock_tcp_wouldblock((struct event*)
BIO_get_callback_arg(b), EV_READ);
}
if( (oper == (BIO_CB_WRITE|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_PUTS|BIO_CB_RETURN) && argl == 0)) {
if(WSAGetLastError() == WSAEWOULDBLOCK)
winsock_tcp_wouldblock((struct event*)
BIO_get_callback_arg(b), EV_WRITE);
}
/* return original return value */
return retvalue;
}
#endif

void
comm_point_tcp_accept_callback(int fd, short event, void* arg)
{
Expand All @@ -706,6 +734,21 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
&c_hdl->repinfo.addrlen);
if(new_fd == -1)
return;
if(c->ssl) {
c_hdl->ssl = incoming_ssl_fd(c->ssl, new_fd);
if(!c_hdl->ssl)
return;
c_hdl->ssl_shake_state = comm_ssl_shake_read;
#ifdef USE_WINSOCK
/* set them both just in case, but usually they are the same BIO */
BIO_set_callback(SSL_get_rbio(c_hdl->ssl), &win_bio_cb);
BIO_set_callback_arg(SSL_get_rbio(c_hdl->ssl),
(char*)comm_point_internal(c_hdl));
BIO_set_callback(SSL_get_wbio(c_hdl->ssl), &win_bio_cb);
BIO_set_callback_arg(SSL_get_wbio(c_hdl->ssl),
(char*)comm_point_internal(c_hdl));
#endif
}

/* grab the tcp handler buffers */
c->tcp_free = c_hdl->tcp_free;
Expand All @@ -722,6 +765,11 @@ static void
reclaim_tcp_handler(struct comm_point* c)
{
log_assert(c->type == comm_tcp);
if(c->ssl) {
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
c->ssl = NULL;
}
comm_point_close(c);
if(c->tcp_parent) {
c->tcp_free = c->tcp_parent->tcp_free;
Expand Down Expand Up @@ -764,6 +812,231 @@ tcp_callback_reader(struct comm_point* c)
}
}

/** continue ssl handshake */
static int
ssl_handshake(struct comm_point* c)
{
int r;
if(c->ssl_shake_state == comm_ssl_shake_hs_read) {
/* read condition satisfied back to writing */
comm_point_listen_for_rw(c, 1, 1);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
if(c->ssl_shake_state == comm_ssl_shake_hs_write) {
/* write condition satisfied, back to reading */
comm_point_listen_for_rw(c, 1, 0);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}

ERR_clear_error();
r = SSL_do_handshake(c->ssl);
if(r != 1) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_WANT_READ) {
if(c->ssl_shake_state == comm_ssl_shake_read)
return 1;
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1;
} else if(want == SSL_ERROR_WANT_WRITE) {
if(c->ssl_shake_state == comm_ssl_shake_write)
return 1;
c->ssl_shake_state = comm_ssl_shake_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(r == 0) {
return 0; /* closed */
} else if(want == SSL_ERROR_SYSCALL) {
/* SYSCALL and errno==0 means closed uncleanly */
if(errno != 0)
log_err("SSL_handshake syscall: %s",
strerror(errno));
return 0;
} else {
log_crypto_err("ssl handshake failed");
log_addr(1, "ssl handshake failed", &c->repinfo.addr,
c->repinfo.addrlen);
return 0;
}
}
/* this is where peer verification could take place */
log_addr(VERB_ALGO, "SSL connection from", &c->repinfo.addr,
c->repinfo.addrlen);

/* setup listen rw correctly */
if(c->tcp_is_reading) {
if(c->ssl_shake_state != comm_ssl_shake_read)
comm_point_listen_for_rw(c, 1, 0);
} else {
comm_point_listen_for_rw(c, 1, 1);
}
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}

/** ssl read callback on TCP */
static int
ssl_handle_read(struct comm_point* c)
{
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
if(c->tcp_byte_count < sizeof(uint16_t)) {
/* read length bytes */
ERR_clear_error();
if((r=SSL_read(c->ssl, (void*)ldns_buffer_at(c->buffer,
c->tcp_byte_count), (int)(sizeof(uint16_t) -
c->tcp_byte_count))) <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_read");
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count != sizeof(uint16_t))
return 1;
if(ldns_buffer_read_u16_at(c->buffer, 0) >
ldns_buffer_capacity(c->buffer)) {
verbose(VERB_QUERY, "ssl: dropped larger than buffer");
return 0;
}
ldns_buffer_set_limit(c->buffer,
ldns_buffer_read_u16_at(c->buffer, 0));
if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "ssl: dropped bogus too short.");
return 0;
}
verbose(VERB_ALGO, "Reading ssl tcp query of length %d",
(int)ldns_buffer_limit(c->buffer));
}
log_assert(ldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_read(c->ssl, (void*)ldns_buffer_current(c->buffer),
(int)ldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_read");
return 0;
}
ldns_buffer_skip(c->buffer, (ssize_t)r);
if(ldns_buffer_remaining(c->buffer) <= 0) {
tcp_callback_reader(c);
}
return 1;
}

/** ssl write callback on TCP */
static int
ssl_handle_write(struct comm_point* c)
{
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
/* ignore return, if fails we may simply block */
(void)SSL_set_mode(c->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(ldns_buffer_limit(c->buffer));
ERR_clear_error();
r = SSL_write(c->ssl,
(void*)(((uint8_t*)&len)+c->tcp_byte_count),
(int)(sizeof(uint16_t)-c->tcp_byte_count));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_write");
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
ldns_buffer_set_position(c->buffer, c->tcp_byte_count -
sizeof(uint16_t));
if(ldns_buffer_remaining(c->buffer) == 0) {
tcp_callback_writer(c);
return 1;
}
}
log_assert(ldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_write(c->ssl, (void*)ldns_buffer_current(c->buffer),
(int)ldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_write");
return 0;
}
ldns_buffer_skip(c->buffer, (ssize_t)r);

if(ldns_buffer_remaining(c->buffer) == 0) {
tcp_callback_writer(c);
}
return 1;
}

/** Handle tcp reading callback.
* @param fd: file descriptor of socket.
* @param c: comm point to read from into buffer.
Expand All @@ -777,6 +1050,8 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
log_assert(c->type == comm_tcp || c->type == comm_local);
if(!c->tcp_is_reading)
return 0;
if(c->ssl)
return ssl_handle_read(c);

log_assert(fd != -1);
if(c->tcp_byte_count < sizeof(uint16_t)) {
Expand Down Expand Up @@ -915,6 +1190,8 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
return 0;
}
}
if(c->ssl)
return ssl_handle_write(c);

if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(ldns_buffer_limit(c->buffer));
Expand Down Expand Up @@ -1476,6 +1753,10 @@ comm_point_delete(struct comm_point* c)
{
if(!c)
return;
if(c->type == comm_tcp && c->ssl) {
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
}
comm_point_close(c);
if(c->tcp_handlers) {
int i;
Expand Down
17 changes: 17 additions & 0 deletions util/netevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ struct comm_point {
For tcp_accept the first entry, for tcp_handlers the next one. */
struct comm_point* tcp_free;

/* -------- SSL TCP DNS ------- */
/** the SSL object with rw bio (owned) or for commaccept ctx ref */
void* ssl;
/** handshake state for init and renegotiate */
enum {
/** no handshake, it has been done */
comm_ssl_shake_none = 0,
/** ssl initial handshake wants to read */
comm_ssl_shake_read,
/** ssl initial handshake wants to write */
comm_ssl_shake_write,
/** ssl_write wants to read */
comm_ssl_shake_hs_read,
/** ssl_read wants to write */
comm_ssl_shake_hs_write
} ssl_shake_state;

/** is this a UDP, TCP-accept or TCP socket. */
enum comm_point_type {
/** UDP socket - handle datagrams. */
Expand Down