Skip to content
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.60])
AC_INIT([sniproxy], [0.6.0])
AC_INIT([sniproxy], [0.6.0+git.17.g6867569])
AC_CONFIG_SRCDIR([src/sniproxy.c])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([subdir-objects])
Expand Down
2 changes: 1 addition & 1 deletion redhat/sniproxy.spec
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Name: sniproxy
Version: 0.6.0
Version: 0.6.0+git.17.g6867569
Release: 1%{?dist}
Summary: Transparent TLS and HTTP layer 4 proxy with SNI support

Expand Down
23 changes: 22 additions & 1 deletion src/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <stdlib.h> /* malloc */
#include <string.h> /* memcpy */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <time.h>
#include <errno.h>
Expand Down Expand Up @@ -147,6 +146,28 @@ buffer_send(struct Buffer *buffer, int sockfd, int flags, struct ev_loop *loop)
return bytes;
}

ssize_t
buffer_sendto(struct Buffer *buffer, int sockfd, int flags,
const struct sockaddr *dest_addr, socklen_t dest_addr_len,
struct ev_loop *loop) {
struct iovec iov[2];
struct msghdr msg = {
.msg_name = (struct sockaddr *)dest_addr,
.msg_namelen = dest_addr_len,
.msg_iov = iov,
.msg_iovlen = setup_read_iov(buffer, iov, 0)
};

ssize_t bytes = sendmsg(sockfd, &msg, flags);

buffer->last_send = ev_now(loop);

if (bytes > 0)
advance_read_position(buffer, (size_t)bytes);

return bytes;
}

/*
* Read data from file into buffer
*/
Expand Down
2 changes: 2 additions & 0 deletions src/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ev.h>


Expand All @@ -47,6 +48,7 @@ void free_buffer(struct Buffer *);

ssize_t buffer_recv(struct Buffer *, int, int, struct ev_loop *);
ssize_t buffer_send(struct Buffer *, int, int, struct ev_loop *);
ssize_t buffer_sendto(struct Buffer *, int, int, const struct sockaddr *, socklen_t, struct ev_loop *);
ssize_t buffer_read(struct Buffer *, int);
ssize_t buffer_write(struct Buffer *, int);
ssize_t buffer_resize(struct Buffer *, size_t);
Expand Down
4 changes: 4 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ static const struct Keyword listener_stanza_grammar[] = {
.keyword="reuseport",
.parse_arg=(int(*)(void *, const char *))accept_listener_reuseport,
},
{
.keyword="fastopen",
.parse_arg=(int(*)(void *, const char *))accept_listener_fastopen,
},
{
.keyword="ipv6_v6only",
.parse_arg=(int(*)(void *, const char *))accept_listener_ipv6_v6only,
Expand Down
74 changes: 52 additions & 22 deletions src/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h> /* getaddrinfo */
#include <unistd.h> /* close */
#include <fcntl.h>
Expand Down Expand Up @@ -253,7 +254,9 @@ connection_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
/* Receive first in case the socket was closed */
if (revents & EV_READ && buffer_room(input_buffer)) {
ssize_t bytes_received = buffer_recv(input_buffer, w->fd, 0, loop);
if (bytes_received < 0 && !IS_TEMPORARY_SOCKERR(errno)) {
if (bytes_received < 0 &&
!IS_TEMPORARY_SOCKERR(errno) &&
!con->server.addr_once) {
warn("recv(%s): %s, closing connection",
socket_name,
strerror(errno));
Expand All @@ -268,8 +271,19 @@ connection_cb(struct ev_loop *loop, struct ev_io *w, int revents) {

/* Transmit */
if (revents & EV_WRITE && buffer_len(output_buffer)) {
ssize_t bytes_transmitted = buffer_send(output_buffer, w->fd, 0, loop);
if (bytes_transmitted < 0 && !IS_TEMPORARY_SOCKERR(errno)) {
ssize_t bytes_transmitted = -1;
if (!is_client && con->server.addr_once) {
bytes_transmitted =
buffer_sendto(output_buffer, w->fd,
MSG_FASTOPEN, con->server.addr_once,
con->server.addr_len, loop);
con->server.addr_once = NULL;
} else {
bytes_transmitted = buffer_send(output_buffer, w->fd, 0, loop);
}

if (bytes_transmitted < 0 &&
errno != EINPROGRESS && !IS_TEMPORARY_SOCKERR(errno)) {
warn("send(%s): %s, closing connection",
socket_name,
strerror(errno));
Expand Down Expand Up @@ -628,22 +642,27 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) {
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
#endif

int on = 1;
#ifdef TCP_NODELAY
int result = setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &on, sizeof(on));
#else
int result = -EPERM;
/* XXX error: not implemented would be better, but this shouldn't be
* reached since it is prohibited in the configuration parser. */
#endif
result = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));

if (con->listener->transparent_proxy &&
con->client.addr.ss_family == con->server.addr.ss_family) {
#ifdef IP_TRANSPARENT
int on = 1;
int result = setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on));
#else
int result = -EPERM;
/* XXX error: not implemented would be better, but this shouldn't be
* reached since it is prohibited in the configuration parser. */
#endif
result = setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on));
if (result < 0) {
err("setsockopt IP_TRANSPARENT failed: %s", strerror(errno));
close(sockfd);
abort_connection(con);
return;
}
#endif

result = bind(sockfd, (struct sockaddr *)&con->client.addr,
con->client.addr_len);
Expand Down Expand Up @@ -680,18 +699,29 @@ initiate_server_connect(struct Connection *con, struct ev_loop *loop) {
}
}

int result = connect(sockfd,
(struct sockaddr *)&con->server.addr,
con->server.addr_len);
/* TODO retry connect in EADDRNOTAVAIL case */
if (result < 0 && errno != EINPROGRESS) {
close(sockfd);
char server[INET6_ADDRSTRLEN + 8];
warn("Failed to open connection to %s: %s",
display_sockaddr(&con->server.addr, server, sizeof(server)),
strerror(errno));
abort_connection(con);
return;
#ifndef MSG_FASTOPEN
con->listener->fastopen = 0;
con->server.addr_once = NULL;
warn("TCP Fast Open for client sockets not supported in this build");
#endif

if (con->listener->fastopen == 1 ||
con->listener->fastopen == 3) {
con->server.addr_once = (struct sockaddr *)&con->server.addr;
} else {
result = connect(sockfd,
(struct sockaddr *)&con->server.addr,
con->server.addr_len);
/* TODO retry connect in EADDRNOTAVAIL case */
if (result < 0 && errno != EINPROGRESS) {
close(sockfd);
char server[INET6_ADDRSTRLEN + 8];
warn("Failed to open connection to %s: %s",
display_sockaddr(&con->server.addr, server, sizeof(server)),
strerror(errno));
abort_connection(con);
return;
}
}

if (getsockname(sockfd, (struct sockaddr *)&con->server.local_addr,
Expand Down
1 change: 1 addition & 0 deletions src/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct Connection {

struct {
struct sockaddr_storage addr, local_addr;
struct sockaddr *addr_once;
socklen_t addr_len, local_addr_len;
struct ev_io watcher;
struct Buffer *buffer;
Expand Down
41 changes: 41 additions & 0 deletions src/listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <assert.h>
#include "address.h"
Expand Down Expand Up @@ -216,6 +217,7 @@ new_listener() {
listener->table_name = NULL;
listener->access_log = NULL;
listener->log_bad_requests = 0;
listener->fastopen = 0;
listener->reuseport = 0;
listener->ipv6_v6only = 0;
listener->transparent_proxy = 0;
Expand Down Expand Up @@ -292,6 +294,28 @@ accept_listener_protocol(struct Listener *listener, const char *protocol) {
return 1;
}

int
accept_listener_fastopen(struct Listener *listener, const char *fastopen) {
listener->fastopen = parse_boolean(fastopen);
if (listener->fastopen)
listener->fastopen = 3;
else if (strcasecmp(fastopen, "frontend") == 0)
listener->fastopen = 2;
else if (strcasecmp(fastopen, "backend") == 0)
listener->fastopen = 1;
else
return 0;

#ifndef TCP_FASTOPEN
if (listener->fastopen != -1) {
err("TCP Fast Open not supported in this build");
return 0;
}
#endif

return 1;
}

int
accept_listener_reuseport(struct Listener *listener, const char *reuseport) {
listener->reuseport = parse_boolean(reuseport);
Expand Down Expand Up @@ -536,6 +560,10 @@ init_listener(struct Listener *listener, const struct Table_head *tables,
return result;
}

#ifdef TCP_NODELAY
result = setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &on, sizeof(on));
#endif

if (listener->reuseport == 1) {
#ifdef SO_REUSEPORT
/* set SO_REUSEPORT on server socket to allow binding of multiple
Expand Down Expand Up @@ -567,6 +595,19 @@ init_listener(struct Listener *listener, const struct Table_head *tables,
}
}

#ifdef TCP_FASTOPEN
if (listener->fastopen == 2 ||
listener->fastopen == 3) {
int qlen = SOMAXCONN;
result = setsockopt(sockfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
if (result < 0) {
err("setsockopt TCP_FASTOPEN failed: %s", strerror(errno));
close(sockfd);
return result;
}
}
#endif

result = bind(sockfd, address_sa(listener->address),
address_sa_len(listener->address));
if (result < 0 && errno == EACCES) {
Expand Down
3 changes: 2 additions & 1 deletion src/listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct Listener {
const struct Protocol *protocol;
char *table_name;
struct Logger *access_log;
int log_bad_requests, reuseport, transparent_proxy, ipv6_v6only;
int log_bad_requests, fastopen, reuseport, transparent_proxy, ipv6_v6only;
int fallback_use_proxy_header;

/* Runtime fields */
Expand All @@ -58,6 +58,7 @@ int accept_listener_table_name(struct Listener *, const char *);
int accept_listener_fallback_address(struct Listener *, const char *);
int accept_listener_source_address(struct Listener *, const char *);
int accept_listener_protocol(struct Listener *, const char *);
int accept_listener_fastopen(struct Listener *, const char *);
int accept_listener_reuseport(struct Listener *, const char *);
int accept_listener_ipv6_v6only(struct Listener *, const char *);
int accept_listener_bad_request_action(struct Listener *, const char *);
Expand Down