Commit 904942bc authored by Petr Špaček's avatar Petr Špaček

Merge branch 'ci-pytests-new-proxy' into 'master'

pytests: update proxy + test_random_close

See merge request !732
parents 7809ecd1 7d8ff621
Pipeline #44677 passed with stages
in 17 minutes and 24 seconds
......@@ -61,6 +61,8 @@ _obj
/tests/test_zonecut
/tests/dnstap/src/dnstap-test/vendor/github.com/
/tests/dnstap/src/github.com/
/tests/pytests/*/tcproxy
/tests/pytests/*/tlsproxy
.pytest_cache
kresd.amalg.c
libkres.amalg.c
......
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-stable:knot-2.7
variables:
DEBIAN_FRONTEND: noninteractive
LC_ALL: C.UTF-8
......@@ -11,6 +9,9 @@ variables:
RESPDIFF_COUNT: 1
RESPDIFF_FORCE: 0
RESPERF_FORCE: 0
KNOT_VERSION: '2.7'
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-buster:knot-$KNOT_VERSION
stages:
- build
......@@ -52,7 +53,7 @@ lint:pedantic:
dependencies: [] # do not download build artifacts
except:
- master
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-unstable:knot-2.7 # newer Debian for newer compilers
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-unstable:knot-$KNOT_VERSION # newer Debian for newer compilers
variables:
CFLAGS: -Werror -Wall -Wpedantic -ggdb -std=gnu11
script:
......@@ -97,7 +98,7 @@ lint:c:
stage: test
except:
- master
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-unstable:knot-2.7 # newer Debian for newer Clang
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-unstable:knot-$KNOT_VERSION # newer Debian for newer Clang
dependencies: [] # do not download build artifacts
script:
- make lint-c CLANG_TIDY="clang-tidy -quiet"
......@@ -108,7 +109,7 @@ lint:clang-scan-build:
stage: test
except:
- master
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-unstable:knot-2.7 # newer Debian for newer Clang
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-unstable:knot-$KNOT_VERSION # newer Debian for newer Clang
dependencies: [] # do not download build artifacts
script:
- MAKEFLAGS="-k -j$(nproc)" SCAN_BUILD="scan-build -o scan-results --status-bugs -no-failure-reports" ./tests/clang_scan_build.sh make || true
......@@ -162,6 +163,8 @@ docker:build:
installcheck:linux:amd64:
# TODO use debian-buster once lua packet resize issue is resolved
image: $CI_REGISTRY/knot/knot-resolver/ci/debian-stable:knot-$KNOT_VERSION
stage: test
except:
- master
......@@ -300,9 +303,7 @@ pytests:run:
except:
- master
script:
- pushd tests/pytests/rehandshake
- make all
- popd
- pushd tests/pytests/proxy && make all && popd
- PATH="$PREFIX/sbin:$PATH" ./ci/pytests/run.sh &> pytests.log.txt
after_script:
- tail -1 pytests.log.txt
......
FROM debian:buster-20181226
MAINTAINER Knot Resolver <knot-resolver@labs.nic.cz>
ARG KNOT_BRANCH=2.7
WORKDIR /root
CMD ["/bin/bash"]
# generic cleanup
RUN apt-get update -qq
# TODO: run upgrade once buster reaches a stable release
# RUN apt-get upgrade -y -qqq
# Knot and Knot Resolver dependecies
RUN apt-get install -y -qqq git make cmake pkg-config build-essential bsdmainutils libtool autoconf liburcu-dev libgnutls28-dev libedit-dev liblmdb-dev libcap-ng-dev libsystemd-dev libidn11-dev protobuf-c-compiler libfstrm-dev libuv1-dev libcmocka-dev libluajit-5.1-dev lua-sec lua-socket lua-http
# documentation dependecies
RUN apt-get install -y -qqq doxygen python3-sphinx python3-breathe python3-sphinx-rtd-theme
# Python packags required for Deckard CI
# Python: grab latest versions from PyPi
# (dnspython and Augeas binding in Debian packages are slow and buggy)
RUN apt-get install -y -qqq python3-pip wget augeas-tools
RUN pip3 install --upgrade pip
RUN pip3 install pylint
RUN pip3 install pep8
RUN pip3 install pytest-xdist
# tests/pytest dependencies
RUN pip3 install dnspython jinja2 pytest pytest-html pytest-xdist
# C dependencies for python-augeas
RUN apt-get install -y -qqq libaugeas-dev libffi-dev
# Python dependencies for Deckard
RUN wget https://gitlab.labs.nic.cz/knot/deckard/raw/master/requirements.txt -O /tmp/deckard-req.txt
RUN pip3 install -r /tmp/deckard-req.txt
# build and install latest version of Knot DNS
# (kresd depends on libknot and libdnssec)
RUN git clone --depth=1 --branch=$KNOT_BRANCH https://gitlab.labs.nic.cz/knot/knot-dns.git /tmp/knot
WORKDIR /tmp/knot
RUN pwd
RUN autoreconf -if
RUN ./configure --prefix=/usr
RUN make
RUN make install
RUN ldconfig
# Valgrind for kresd CI
RUN apt-get install valgrind -y -qqq
RUN wget https://raw.githubusercontent.com/LuaJIT/LuaJIT/v2.0.4/src/lj.supp -O /lj.supp
# TODO: rebuild LuaJIT with Valgrind support
# Lua lint for kresd CI
RUN apt-get install luarocks -y -qqq
RUN luarocks install luacheck
# respdiff for kresd CI
RUN apt-get install lmdb-utils -y -qqq
RUN git clone --depth=1 https://gitlab.labs.nic.cz/knot/respdiff /var/opt/respdiff
RUN pip3 install -r /var/opt/respdiff/requirements.txt
# Python static analysis for respdiff
RUN pip3 install mypy
RUN pip3 install flake8
# Python requests for CI scripts
RUN pip3 install requests
# Unbound for respdiff
RUN apt-get install unbound unbound-anchor -y -qqq
RUN printf "server:\n interface: 127.0.0.1@53535\n use-syslog: yes\n do-ip6: no\nremote-control:\n control-enable: no\n" >> /etc/unbound/unbound.conf
# BIND for respdiff
RUN apt-get install bind9 -y -qqq
RUN printf '\nOPTIONS="-4 $OPTIONS"' >> /etc/default/bind9
RUN printf 'options {\n directory "/var/cache/bind";\n listen-on port 53533 { 127.0.0.1; };\n listen-on-v6 port 53533 { ::1; };\n};\n' > /etc/bind/named.conf.options
# PowerDNS Recursor for Deckard CI
RUN apt-get install pdns-recursor -y -qqq
# code coverage
RUN apt-get install -y -qqq lcov
RUN luarocks install luacov
# LuaJIT binary for stand-alone scripting
RUN apt-get install -y -qqq luajit
# OpenBuildService CLI tool
RUN apt-get install -y osc
# curl (API)
RUN apt-get install -y curl
......@@ -48,7 +48,7 @@ Forward = namedtuple('Forward', ['proto', 'ip', 'port', 'hostname', 'ca_file'])
class Kresd(ContextDecorator):
def __init__(
self, workdir, port=None, tls_port=None, ip=None, ip6=None, certname=None,
verbose=True, hints=None, forward=None):
verbose=True, hints=None, forward=None, policy_test_pass=False):
if ip is None and ip6 is None:
raise ValueError("IPv4 or IPv6 must be specified!")
self.workdir = str(workdir)
......@@ -62,6 +62,7 @@ class Kresd(ContextDecorator):
self.verbose = verbose
self.hints = {} if hints is None else hints
self.forward = forward
self.policy_test_pass = policy_test_pass
if certname:
self.tls_cert_path = os.path.join(CERTS_DIR, certname + '.cert.pem')
......@@ -160,7 +161,7 @@ class Kresd(ContextDecorator):
continue
finally:
sock.close()
raise RuntimeError("Kresd didn't start in time")
raise RuntimeError("Kresd didn't start in time {}".format(dest))
def socket_dest(self, family, tls=False):
port = self.tls_port if tls else self.port
......@@ -297,9 +298,7 @@ KRESD_LOG_IO_CLOSE = re.compile(r'^\[io\].*closed by peer.*')
@contextmanager
def make_kresd(
workdir, certname=None, ip='127.0.0.1', ip6='::1', forward=None, hints=None,
port=None, tls_port=None):
with Kresd(workdir, port, tls_port, ip, ip6, certname, forward=forward, hints=hints) as kresd:
def make_kresd(workdir, certname=None, ip='127.0.0.1', ip6='::1', **kwargs):
with Kresd(workdir, ip=ip, ip6=ip6, certname=certname, **kwargs) as kresd:
yield kresd
print(kresd.partial_log())
from contextlib import contextmanager, ContextDecorator
import os
import subprocess
from typing import Any, Dict, Optional
import dns
import dns.rcode
import pytest
from kresd import CERTS_DIR, Forward, Kresd, make_kresd, make_port, PYTESTS_DIR
import utils
HINTS = {
'0.foo.': '127.0.0.1',
'1.foo.': '127.0.0.1',
'2.foo.': '127.0.0.1',
'3.foo.': '127.0.0.1',
}
def resolve_hint(sock, qname):
buff, msgid = utils.get_msgbuff(qname)
sock.sendall(buff)
answer = utils.receive_parse_answer(sock)
assert answer.id == msgid
assert answer.rcode() == dns.rcode.NOERROR
assert answer.answer[0][0].address == HINTS[qname]
class Proxy(ContextDecorator):
PATH = ''
def __init__(
self,
local_ip: str = '127.0.0.1',
local_port: Optional[int] = None,
upstream_ip: str = '127.0.0.1',
upstream_port: Optional[int] = None
) -> None:
self.local_ip = local_ip
self.local_port = local_port
self.upstream_ip = upstream_ip
self.upstream_port = upstream_port
self.proxy = None
def get_args(self):
args = []
args.append('--local')
args.append(self.local_ip)
if self.local_port is not None:
args.append('--lport')
args.append(str(self.local_port))
args.append('--upstream')
args.append(self.upstream_ip)
if self.upstream_port is not None:
args.append('--uport')
args.append(str(self.upstream_port))
return args
def __enter__(self):
if not os.path.exists(self.PATH):
pytest.skip("proxy executable '{}' not found (did you compile it?)".format(self.PATH))
cwd, cmd = os.path.split(self.PATH)
cmd = './' + cmd
args = [cmd] + self.get_args()
print(' '.join(args))
self.proxy = subprocess.Popen(
args, cwd=cwd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.proxy is not None:
self.proxy.terminate()
self.proxy = None
class TLSProxy(Proxy):
PATH = os.path.join(PYTESTS_DIR, 'proxy', 'tlsproxy')
def __init__(
self,
local_ip: str = '127.0.0.1',
local_port: Optional[int] = None,
upstream_ip: str = '127.0.0.1',
upstream_port: Optional[int] = None,
certname: Optional[str] = 'tt',
close: Optional[int] = None,
rehandshake: bool = False,
force_tls13: bool = False
) -> None:
super().__init__(local_ip, local_port, upstream_ip, upstream_port)
if certname is not None:
self.cert_path = os.path.join(CERTS_DIR, certname + '.cert.pem')
self.key_path = os.path.join(CERTS_DIR, certname + '.key.pem')
else:
self.cert_path = None
self.key_path = None
self.close = close
self.rehandshake = rehandshake
self.force_tls13 = force_tls13
def get_args(self):
args = super().get_args()
if self.cert_path is not None:
args.append('--cert')
args.append(self.cert_path)
if self.key_path is not None:
args.append('--key')
args.append(self.key_path)
if self.close is not None:
args.append('--close')
args.append(str(self.close))
if self.rehandshake:
args.append('--rehandshake')
if self.force_tls13:
args.append('--tls13')
return args
@contextmanager
def kresd_tls_client(
workdir: str,
proxy: TLSProxy,
kresd_tls_client_kwargs: Optional[Dict[Any, Any]] = None,
kresd_fwd_target_kwargs: Optional[Dict[Any, Any]] = None
) -> Kresd:
"""kresd_tls_client --(tls)--> tlsproxy --(tcp)--> kresd_fwd_target"""
ALLOWED_IPS = {'127.0.0.1', '::1'}
assert proxy.local_ip in ALLOWED_IPS, "only localhost IPs supported for proxy"
assert proxy.upstream_ip in ALLOWED_IPS, "only localhost IPs are supported for proxy"
if kresd_tls_client_kwargs is None:
kresd_tls_client_kwargs = dict()
if kresd_fwd_target_kwargs is None:
kresd_fwd_target_kwargs = dict()
# run forward target instance
dir1 = os.path.join(workdir, 'kresd_fwd_target')
os.makedirs(dir1)
with make_kresd(dir1, hints=HINTS, **kresd_fwd_target_kwargs) as kresd_fwd_target:
sock = kresd_fwd_target.ip_tcp_socket()
resolve_hint(sock, list(HINTS.keys())[0])
proxy.local_port = make_port('127.0.0.1', '::1')
proxy.upstream_port = kresd_fwd_target.port
with proxy:
# run test kresd instance
dir2 = os.path.join(workdir, 'kresd_tls_client')
os.makedirs(dir2)
forward = Forward(
proto='tls', ip=proxy.local_ip, port=proxy.local_port,
hostname='transport-test-server.com', ca_file=proxy.cert_path)
with make_kresd(dir2, forward=forward, **kresd_tls_client_kwargs) as kresd:
yield kresd
CC=gcc
CFLAGS_TLS=-DDEBUG -ggdb3 -O0 -lgnutls -luv
CFLAGS_TCP=-DDEBUG -ggdb3 -O0 -luv
CFLAGS_TLS=-DDEBUG -ggdb3 -O0 -lgnutls -luv -lasan -fsanitize=address -fno-omit-frame-pointer
all: tcproxy tlsproxy
all: tlsproxy
tlsproxy: tls-proxy.o tlsproxy.o
$(CC) tls-proxy.o tlsproxy.o -o tlsproxy $(CFLAGS_TLS)
......@@ -13,16 +12,7 @@ tls-proxy.o: tls-proxy.c tls-proxy.h array.h
tlsproxy.o: tlsproxy.c tls-proxy.h
$(CC) -c -o $@ $< $(CFLAGS_TLS)
tcproxy: tcp-proxy.o tcproxy.o
$(CC) tcp-proxy.o tcproxy.o -o tcproxy $(CFLAGS_TCP)
tcp-proxy.o: tcp-proxy.c tcp-proxy.h array.h
$(CC) -c -o $@ $< $(CFLAGS_TCP)
tcproxy.o: tcproxy.c tcp-proxy.h
$(CC) -c -o $@ $< $(CFLAGS_TCP)
clean:
rm -f tcp-proxy.o tcproxy.o tcproxy tls-proxy.o tlsproxy.o tlsproxy
rm -f tls-proxy.o tlsproxy.o tlsproxy
.PHONY: all clean
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <gnutls/gnutls.h>
#include <uv.h>
#include "array.h"
#include "tls-proxy.h"
#define TLS_MAX_SEND_RETRIES 100
#define CLIENT_ANSWER_CHUNK_SIZE 8
#define MAX_CLIENT_PENDING_SIZE 4096
struct buf {
size_t size;
char buf[];
};
enum peer_state {
STATE_NOT_CONNECTED,
STATE_LISTENING,
STATE_CONNECTED,
STATE_CONNECT_IN_PROGRESS,
STATE_CLOSING_IN_PROGRESS
};
enum handshake_state {
TLS_HS_NOT_STARTED = 0,
TLS_HS_EXPECTED,
TLS_HS_REAUTH_EXPECTED,
TLS_HS_IN_PROGRESS,
TLS_HS_DONE,
TLS_HS_CLOSING,
TLS_HS_LAST
};
struct tls_ctx {
gnutls_session_t session;
enum handshake_state handshake_state;
/* for reading from the network */
const uint8_t *buf;
ssize_t nread;
ssize_t consumed;
uint8_t recv_buf[4096];
};
struct peer {
uv_tcp_t handle;
enum peer_state state;
struct sockaddr_storage addr;
array_t(struct buf *) pending_buf;
uint64_t connection_timestamp;
struct tls_ctx *tls;
struct peer *peer;
int active_requests;
};
struct tls_proxy_ctx {
const struct args *a;
uv_loop_t *loop;
gnutls_certificate_credentials_t tls_credentials;
gnutls_priority_t tls_priority_cache;
struct {
uv_tcp_t handle;
struct sockaddr_storage addr;
} server;
struct sockaddr_storage upstream_addr;
array_t(struct peer *) client_list;
char uv_wire_buf[65535 * 2];
int conn_sequence;
};
static void read_from_upstream_cb(uv_stream_t *upstream, ssize_t nread, const uv_buf_t *buf);
static void read_from_client_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf);
static ssize_t proxy_gnutls_pull(gnutls_transport_ptr_t h, void *buf, size_t len);
static ssize_t proxy_gnutls_push(gnutls_transport_ptr_t h, const void *buf, size_t len);
static int tls_process_from_upstream(struct peer *upstream, const uint8_t *buf, ssize_t nread);
static int tls_process_from_client(struct peer *client, const uint8_t *buf, ssize_t nread);
static int write_to_upstream_pending(struct peer *peer);
static int write_to_client_pending(struct peer *peer);
static void on_client_close(uv_handle_t *handle);
static void on_upstream_close(uv_handle_t *handle);
static int gnutls_references = 0;
static const char * const tlsv12_priorities =
"NORMAL:" /* GnuTLS defaults */
"-VERS-TLS1.0:-VERS-TLS1.1:+VERS-TLS1.2:-VERS-TLS1.3:" /* TLS 1.2 only */
"-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
static const char * const tlsv13_priorities =
"NORMAL:" /* GnuTLS defaults */
"-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:+VERS-TLS1.3:" /* TLS 1.3 only */
"-VERS-SSL3.0:-ARCFOUR-128:-COMP-ALL:+COMP-NULL";
static struct tls_proxy_ctx *get_proxy(struct peer *peer)
{
return (struct tls_proxy_ctx *)peer->handle.loop->data;
}
const void *ip_addr(const struct sockaddr *addr)
{
if (!addr) {
return NULL;
}
switch (addr->sa_family) {
case AF_INET: return (const void *)&(((const struct sockaddr_in *)addr)->sin_addr);
case AF_INET6: return (const void *)&(((const struct sockaddr_in6 *)addr)->sin6_addr);
default: return NULL;
}
}
uint16_t ip_addr_port(const struct sockaddr *addr)
{
if (!addr) {
return 0;
}
switch (addr->sa_family) {
case AF_INET: return ntohs(((const struct sockaddr_in *)addr)->sin_port);
case AF_INET6: return ntohs(((const struct sockaddr_in6 *)addr)->sin6_port);
default: return 0;
}
}
static int ip_addr_str(const struct sockaddr *addr, char *buf, size_t *buflen)
{
int ret = 0;
if (!addr || !buf || !buflen) {
return EINVAL;
}
char str[INET6_ADDRSTRLEN + 6];
if (!inet_ntop(addr->sa_family, ip_addr(addr), str, sizeof(str))) {
return errno;
}
int len = strlen(str);
str[len] = '#';
snprintf(&str[len + 1], 6, "%hu", ip_addr_port(addr));
len += 6;
str[len] = 0;
if (len >= *buflen) {
ret = ENOSPC;
} else {
memcpy(buf, str, len + 1);
}
*buflen = len;
return ret;
}
static inline char *ip_straddr(const struct sockaddr_storage *saddr_storage)
{
assert(saddr_storage != NULL);
const struct sockaddr *addr = (const struct sockaddr *)saddr_storage;
/* We are the sinle-threaded application */
static char str[INET6_ADDRSTRLEN + 6];
size_t len = sizeof(str);
int ret = ip_addr_str(addr, str, &len);
return ret != 0 || len == 0 ? NULL : str;
}
static struct buf *alloc_io_buffer(size_t size)
{
struct buf *buf = calloc(1, sizeof (struct buf) + size);
buf->size = size;
return buf;
}
static void free_io_buffer(struct buf *buf)
{
if (!buf) {
return;
}
free(buf);
}
static struct buf *get_first_pending_buf(struct peer *peer)
{
struct buf *buf = NULL;
if (peer->pending_buf.len > 0) {
buf = peer->pending_buf.at[0];
}
return buf;
}
static struct buf *remove_first_pending_buf(struct peer *peer)
{
if (peer->pending_buf.len == 0) {
return NULL;
}
struct buf * buf = peer->pending_buf.at[0];
for (int i = 1; i < peer->pending_buf.len; ++i) {
peer->pending_buf.at[i - 1] = peer->pending_buf.at[i];
}
peer->pending_buf.len -= 1;
return buf;
}
static void clear_pending_bufs(struct peer *peer)
{
for (int i = 0; i < peer->pending_buf.len; ++i) {
struct buf *b = peer->pending_buf.at[i];
free_io_buffer(b);
}
peer->pending_buf.len = 0;
}
static void alloc_uv_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
struct tls_proxy_ctx *proxy = (struct tls_proxy_ctx *)handle->loop->data;
buf->base = proxy->uv_wire_buf;
buf->len = sizeof(proxy->uv_wire_buf);
}
static void on_client_close(uv_handle_t *handle)
{
struct peer *client = (struct peer *)handle->data;
struct peer *upstream = client->peer;
fprintf(stdout, "[client] connection with '%s' closed\n", ip_straddr(&client->addr));
assert(client->tls);
gnutls_deinit(client->tls->session);
client->tls->handshake_state = TLS_HS_NOT_STARTED;
client->state = STATE_NOT_CONNECTED;
if (upstream->state != STATE_NOT_CONNECTED) {
if (upstream->state == STATE_CONNECTED) {
fprintf(stdout, "[client] closing connection with upstream for '%s'\n",
ip_straddr(&client->addr));
upstream->state = STATE_CLOSING_IN_PROGRESS;
uv_close((uv_handle_t*)&upstream->handle, on_upstream_close);
}
return;
}
struct tls_proxy_ctx *proxy = get_proxy(client);
for (size_t i = 0; i < proxy->client_list.len; ++i) {
struct peer *client_i = proxy->client_list.at[i];
if (client_i == client) {
fprintf(stdout, "[client] connection structures deallocated for '%s'\n",
ip_straddr(&client->addr));
array_del(proxy->client_list, i);
free(client->tls);
free(client);
break;
}
}
}
static void on_upstream_close(uv_handle_t *handle)
{
struct peer *upstream = (struct peer *)handle->data;
struct peer *client = upstream->peer;
assert(upstream->tls == NULL);
upstream->state = STATE_NOT_CONNECTED;
fprintf(stdout, "[upstream] connection with upstream closed for client '%s'\n", ip_straddr(&client->addr));
if (client->state != STATE_NOT_CONNECTED) {
if (client->state == STATE_CONNECTED) {
fprintf(stdout, "[upstream] closing connection to client '%s'\n",
ip_straddr(&client->addr));
client->state = STATE_CLOSING_IN_PROGRESS;
uv_close((uv_handle_t*)&client->handle, on_client_close);
}
return;
}
struct tls_proxy_ctx *proxy = get_proxy(upstream);
for (size_t i = 0; i < proxy->client_list.len; ++i) {
struct peer *client_i = proxy->client_list.at[i];
if (client_i == client) {
fprintf(stdout, "[upstream] connection structures deallocated for '%s'\n",
ip_straddr(&client->addr));
array_del(proxy->client_list, i);
free(upstream);
free(client->tls);
free(client);
break;
}
}
}
static void write_to_client_cb(uv_write_t *req, int status)
{
struct peer *client = (struct peer *)req->handle->data;
free(req);
client->active_requests -= 1;
if (status) {
fprintf(stdout, "[client] error writing to client '%s': %s\n",
ip_straddr(&client->addr), uv_strerror(status));
clear_pending_bufs(client);
if (client->state == STATE_CONNECTED) {
client->state = STATE_CLOSING_IN_PROGRESS;
uv_close((uv_handle_t*)&client->handle, on_client_close);
return;
}
}
fprintf(stdout, "[client] successfully wrote to client '%s', pending len is %zd, active requests %i\n",
ip_straddr(&client->addr), client->pending_buf.len, client->active_requests);
if (client->state == STATE_CONNECTED &&
client->tls->handshake_state == TLS_HS_DONE) {
struct tls_proxy_ctx *proxy = get_proxy(client);
uint64_t elapsed = uv_now(proxy->loop) - client->connection_timestamp;
if (!proxy->a->close_connection || elapsed < proxy->a->close_timeout) {
write_to_client_pending(client);
} else {
clear_pending_bufs(client);
client->state = STATE_CLOSING_IN_PROGRESS;
uv_close((uv_handle_t*)&client->handle, on_client_close);
fprintf(stdout, "[client] closing connection to client '%s'\n", ip_straddr(&client->addr));
}
}
}
static void write_to_upstream_cb(uv_write_t *req, int status)
{
struct peer *upstream = (struct peer *)req->handle->data;
void *data = req->data;
free(req);
if (status) {
fprintf(stdout, "[upstream] error writing to upstream: %s\n", uv_strerror(status));
clear_pending_bufs(upstream);
upstream->state = STATE_CLOSING_IN_PROGRESS;
uv_close((uv_handle_t*)&upstream->handle, on_upstream_close);
return;
}
if (data != NULL) {
assert(upstream->pending_buf.len > 0);
struct buf *buf = get_first_pending_buf(upstream);
assert(data == (void *)buf->buf);