mirror of
https://github.com/yrutschle/sslh.git
synced 2025-04-13 07:37:15 +03:00
Overhaul of the logging system: logs all have classes, and
each class can be configured independently to write to stderr or syslog.
This commit is contained in:
commit
0e118a109c
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
||||
vNEXT:
|
||||
New log system: instead of --verbose with arbitrary
|
||||
levels, there are now several message classes. Each
|
||||
message class can be set to go to stderr, syslog, or
|
||||
both. Classes are documented in example.cfg.
|
||||
|
||||
inetd merges stderr output to what is sent to the
|
||||
client, which is a security issue as it might give
|
||||
information to an attacker. When inetd is activated,
|
||||
stderr is forcibly closed.
|
||||
|
||||
v1.22: 17AUG2021
|
||||
sslh-select now supports UDP protocols.
|
||||
Probes specified in the `protocols`
|
||||
|
2
Makefile
2
Makefile
@ -27,7 +27,7 @@ CC ?= gcc
|
||||
CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV)
|
||||
|
||||
LIBS=-lm -lpcre2-8
|
||||
OBJS=sslh-conf.o common.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o
|
||||
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o
|
||||
|
||||
CONDITIONAL_TARGETS=
|
||||
|
||||
|
144
common.c
144
common.c
@ -4,7 +4,6 @@
|
||||
* No code here should assume whether sockets are blocking or not.
|
||||
**/
|
||||
|
||||
#define SYSLOG_NAMES
|
||||
#define _GNU_SOURCE
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
@ -16,6 +15,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "probe.h"
|
||||
#include "log.h"
|
||||
#include "sslh-conf.h"
|
||||
|
||||
/* Added to make the code compilable under CYGWIN
|
||||
@ -42,8 +42,6 @@ struct sslhcfg_item cfg;
|
||||
struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
|
||||
|
||||
|
||||
static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */
|
||||
|
||||
#ifdef LIBWRAP
|
||||
#include <tcpd.h>
|
||||
int allow_severity =0, deny_severity = 0;
|
||||
@ -60,7 +58,7 @@ void check_res_dump(CR_ACTION act, int res, struct addrinfo *addr, char* syscall
|
||||
char buf[NI_MAXHOST];
|
||||
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s:%s: %s\n",
|
||||
print_message(msg_system_error, "%s:%s: %s\n",
|
||||
sprintaddr(buf, sizeof(buf), addr),
|
||||
syscall,
|
||||
strerror(errno));
|
||||
@ -77,7 +75,7 @@ int get_fd_sockets(struct listen_endpoint *sockfd[])
|
||||
#ifdef SYSTEMD
|
||||
sd = sd_listen_fds(0);
|
||||
if (sd < 0) {
|
||||
fprintf(stderr, "sd_listen_fds(): %s\n", strerror(-sd));
|
||||
print_message(msg_system_error, "sd_listen_fds(): %s\n", strerror(-sd));
|
||||
exit(1);
|
||||
}
|
||||
if (sd > 0) {
|
||||
@ -177,7 +175,7 @@ int start_listen_sockets(struct listen_endpoint *sockfd[])
|
||||
|
||||
*sockfd = NULL;
|
||||
|
||||
if (cfg.verbose) fprintf(stderr, "Listening to:\n");
|
||||
print_message(msg_config, "Listening to:\n");
|
||||
|
||||
for (i = 0; i < cfg.listen_len; i++) {
|
||||
keepalive = cfg.listen[i].keepalive;
|
||||
@ -191,10 +189,9 @@ int start_listen_sockets(struct listen_endpoint *sockfd[])
|
||||
*sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd[0]));
|
||||
(*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp);
|
||||
(*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM;
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr),
|
||||
cfg.listen[i].keepalive ? "keepalive" : "",
|
||||
cfg.listen[i].is_udp ? "udp" : "");
|
||||
print_message(msg_config, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr),
|
||||
cfg.listen[i].keepalive ? "keepalive" : "",
|
||||
cfg.listen[i].is_udp ? "udp" : "");
|
||||
}
|
||||
freeaddrinfo(start_addr);
|
||||
}
|
||||
@ -326,15 +323,14 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking)
|
||||
/* When transparent, make sure both connections use the same address family */
|
||||
if (transparent && a->ai_family != from.ai_addr->sa_family)
|
||||
continue;
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "connecting to %s family %d len %d\n",
|
||||
print_message(msg_connections_try, "trying to connect to %s family %d len %d\n",
|
||||
sprintaddr(buf, sizeof(buf), a),
|
||||
a->ai_addr->sa_family, a->ai_addrlen);
|
||||
|
||||
/* XXX Needs to match ai_family from fd_from when being transparent! */
|
||||
fd = socket(a->ai_family, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
log_message(LOG_ERR, "forward to %s failed:socket: %s\n",
|
||||
print_message(msg_connections_error, "forward to %s failed:socket: %s\n",
|
||||
cnx->proto->name, strerror(errno));
|
||||
} else {
|
||||
one = 1;
|
||||
@ -354,7 +350,7 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking)
|
||||
/* EINPROGRESS indicates it might take time. If it eventually
|
||||
* fails, it'll be caught as a failed read */
|
||||
if ((res == -1) && (errno != EINPROGRESS)) {
|
||||
log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
|
||||
print_message(msg_connections_error, "forward to %s failed:connect: %s\n",
|
||||
cnx->proto->name, strerror(errno));
|
||||
close(fd);
|
||||
continue; /* Try the next address */
|
||||
@ -374,9 +370,8 @@ int defer_write(struct queue *q, void* data, int data_size)
|
||||
{
|
||||
char *p;
|
||||
ptrdiff_t data_offset = q->deferred_data - q->begin_deferred_data;
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "**** writing deferred on fd %d\n", q->fd);
|
||||
|
||||
print_message(msg_fd, "writing deferred on fd %d\n", q->fd);
|
||||
p = realloc(q->begin_deferred_data, data_offset + q->deferred_data_size + data_size);
|
||||
CHECK_ALLOC(p, "realloc");
|
||||
|
||||
@ -397,8 +392,7 @@ int flush_deferred(struct queue *q)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "flushing deferred data to fd %d\n", q->fd);
|
||||
print_message(msg_fd, "flushing deferred data to fd %d\n", q->fd);
|
||||
|
||||
n = write(q->fd, q->deferred_data, q->deferred_data_size);
|
||||
if (n == -1)
|
||||
@ -430,11 +424,11 @@ void init_cnx(struct connection *cnx)
|
||||
|
||||
void dump_connection(struct connection *cnx)
|
||||
{
|
||||
printf("state: %d\n", cnx->state);
|
||||
printf("0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size);
|
||||
hexdump(cnx->q[0].deferred_data, cnx->q[0].deferred_data_size);
|
||||
printf("1: fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size);
|
||||
hexdump(cnx->q[1].deferred_data, cnx->q[1].deferred_data_size);
|
||||
print_message(msg_int_error, "state: %d\n", cnx->state);
|
||||
print_message(msg_int_error, "0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size);
|
||||
hexdump(msg_int_error, cnx->q[0].deferred_data, cnx->q[0].deferred_data_size);
|
||||
print_message(msg_int_error, "1: fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size);
|
||||
hexdump(msg_int_error, cnx->q[1].deferred_data, cnx->q[1].deferred_data_size);
|
||||
}
|
||||
|
||||
|
||||
@ -459,8 +453,6 @@ int fd2fd(struct queue *target_q, struct queue *from_q)
|
||||
if (size_r == -1) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "reading 0 from %d\n", from);
|
||||
return FD_NODATA;
|
||||
|
||||
case ECONNRESET:
|
||||
@ -511,7 +503,7 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
|
||||
cfg.numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
|
||||
|
||||
if (res) {
|
||||
log_message(LOG_ERR, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
|
||||
print_message(msg_system_error, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
|
||||
/* Name resolution failed: do it numerically instead */
|
||||
res = getnameinfo(a->ai_addr, a->ai_addrlen,
|
||||
host, sizeof(host),
|
||||
@ -519,7 +511,7 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
/* should not fail but... */
|
||||
if (res) {
|
||||
log_message(LOG_ERR, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res));
|
||||
print_message(msg_system_error, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res));
|
||||
strcpy(host, "?");
|
||||
strcpy(serv, "?");
|
||||
}
|
||||
@ -548,7 +540,7 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv)
|
||||
if (host[0] == '[') {
|
||||
end = strrchr(host, ']');
|
||||
if (!end) {
|
||||
fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host);
|
||||
print_message(msg_config_error, "%s: no closing bracket in IPv6 address?\n", host);
|
||||
return -1;
|
||||
}
|
||||
host++; /* skip first bracket */
|
||||
@ -557,7 +549,7 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv)
|
||||
|
||||
res = getaddrinfo(host, serv, &hint, out);
|
||||
if (res)
|
||||
log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv);
|
||||
print_message(msg_system_error, "%s `%s:%s'\n", gai_strerror(res), host, serv);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -573,7 +565,7 @@ void resolve_name(struct addrinfo **out, char* fullname)
|
||||
/* Find port */
|
||||
char *sep = strrchr(fullname, ':');
|
||||
if (!sep) { /* No separator: parameter is just a port */
|
||||
fprintf(stderr, "%s: names must be fully specified as hostname:port\n", fullname);
|
||||
print_message(msg_config_error, "%s: names must be fully specified as hostname:port\n", fullname);
|
||||
exit(1);
|
||||
}
|
||||
serv = sep+1;
|
||||
@ -583,30 +575,13 @@ void resolve_name(struct addrinfo **out, char* fullname)
|
||||
|
||||
res = resolve_split_name(out, host, serv);
|
||||
if (res) {
|
||||
fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
|
||||
print_message(msg_config_error, "%s `%s'\n", gai_strerror(res), fullname);
|
||||
if (res == EAI_SERVICE)
|
||||
fprintf(stderr, "(Check you have specified all ports)\n");
|
||||
print_message(msg_config_error, "(Check you have specified all ports)\n");
|
||||
exit(4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Log to syslog or stderr if foreground */
|
||||
void log_message(int type, const char* msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, msg);
|
||||
if (cfg.foreground)
|
||||
vfprintf(stderr, msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (do_syslog) {
|
||||
va_start(ap, msg);
|
||||
vsyslog(type, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Fills a connection description; returns 0 on failure */
|
||||
int get_connection_desc(struct connection_desc* desc, const struct connection *cnx)
|
||||
@ -641,30 +616,6 @@ int get_connection_desc(struct connection_desc* desc, const struct connection *c
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* syslogs who connected to where
|
||||
* desc: string description of the connection. if NULL, log_connection will
|
||||
* manage on its own
|
||||
* cnx: connection descriptor
|
||||
* */
|
||||
void log_connection(struct connection_desc* desc, const struct connection *cnx)
|
||||
{
|
||||
struct connection_desc d;
|
||||
|
||||
if (cnx->proto->log_level < 1)
|
||||
return;
|
||||
|
||||
if (!desc) {
|
||||
desc = &d;
|
||||
get_connection_desc(desc, cnx);
|
||||
}
|
||||
|
||||
log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n",
|
||||
cnx->proto->name,
|
||||
desc->peer,
|
||||
desc->service,
|
||||
desc->local,
|
||||
desc->target);
|
||||
}
|
||||
|
||||
void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx)
|
||||
{
|
||||
@ -708,8 +659,7 @@ int check_access_rights(int in_socket, const char* service)
|
||||
/* extract peer address */
|
||||
res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
|
||||
if (res) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
|
||||
print_message(msg_system_error, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
|
||||
strcpy(addr_str, STRING_UNKNOWN);
|
||||
}
|
||||
/* extract peer name */
|
||||
@ -717,15 +667,12 @@ int check_access_rights(int in_socket, const char* service)
|
||||
if (!cfg.numeric) {
|
||||
res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
|
||||
if (res) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
|
||||
print_message(msg_system_error, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "access denied\n");
|
||||
log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
|
||||
print_message(msg_connections, "connection from %s(%s): access denied", host, addr_str);
|
||||
close(in_socket);
|
||||
return -1;
|
||||
}
|
||||
@ -760,35 +707,6 @@ void setup_signals(void)
|
||||
|
||||
}
|
||||
|
||||
/* Open syslog connection with appropriate banner;
|
||||
* banner is made up of basename(bin_name)+"[pid]" */
|
||||
void setup_syslog(const char* bin_name) {
|
||||
char *name1, *name2;
|
||||
int res, fn;
|
||||
|
||||
if (!strcmp(cfg.syslog_facility, "none")) {
|
||||
do_syslog = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
name1 = strdup(bin_name);
|
||||
res = asprintf(&name2, "%s[%d]", basename(name1), getpid());
|
||||
CHECK_RES_DIE(res, "asprintf");
|
||||
|
||||
for (fn = 0; facilitynames[fn].c_val != -1; fn++)
|
||||
if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0)
|
||||
break;
|
||||
if (facilitynames[fn].c_val == -1) {
|
||||
fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
openlog(name2, LOG_CONS, facilitynames[fn].c_val);
|
||||
free(name1);
|
||||
/* Don't free name2, as openlog(3) uses it (at least in glibc) */
|
||||
|
||||
log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
|
||||
}
|
||||
|
||||
/* Ask OS to keep capabilities over a setuid(nonzero) */
|
||||
void set_keepcaps(int val) {
|
||||
@ -866,16 +784,14 @@ void drop_privileges(const char* user_name, const char* chroot_path)
|
||||
if (user_name) {
|
||||
pw = getpwnam(user_name);
|
||||
if (!pw) {
|
||||
fprintf(stderr, "%s: not found\n", user_name);
|
||||
print_message(msg_config_error, "%s: not found\n", user_name);
|
||||
exit(2);
|
||||
}
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "turning into %s\n", user_name);
|
||||
print_message(msg_config, "turning into %s\n", user_name);
|
||||
}
|
||||
|
||||
if (chroot_path) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "chrooting into %s\n", chroot_path);
|
||||
print_message(msg_config, "chrooting into %s\n", chroot_path);
|
||||
|
||||
res = chroot(chroot_path);
|
||||
CHECK_RES_DIE(res, "chroot");
|
||||
|
7
common.h
7
common.h
@ -40,20 +40,20 @@
|
||||
|
||||
#define CHECK_RES_DIE(res, str) \
|
||||
if (res == -1) { \
|
||||
fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \
|
||||
print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \
|
||||
perror(str); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define CHECK_RES_RETURN(res, str, ret) \
|
||||
if (res == -1) { \
|
||||
log_message(LOG_CRIT, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \
|
||||
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define CHECK_ALLOC(a, str) \
|
||||
if (!a) { \
|
||||
fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \
|
||||
print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \
|
||||
perror(str); \
|
||||
exit(1); \
|
||||
}
|
||||
@ -160,7 +160,6 @@ void setup_syslog(const char* bin_name);
|
||||
void drop_privileges(const char* user_name, const char* chroot_path);
|
||||
void set_capabilities(int cap_net_admin);
|
||||
void write_pid_file(const char* pidfile);
|
||||
void log_message(int type, const char* msg, ...);
|
||||
void dump_connection(struct connection *cnx);
|
||||
int resolve_split_name(struct addrinfo **out, char* hostname, char* port);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
||||
* on Fri Aug 13 18:03:20 2021.
|
||||
* on Sun Sep 19 21:54:08 2021.
|
||||
|
||||
# conf2struct: generate libconf parsers that read to structs
|
||||
# Copyright (C) 2018-2021 Yves Rutschle
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
||||
* on Fri Aug 13 18:03:20 2021.
|
||||
* on Sun Sep 19 21:54:08 2021.
|
||||
|
||||
# conf2struct: generate libconf parsers that read to structs
|
||||
# Copyright (C) 2018-2021 Yves Rutschle
|
||||
|
@ -100,7 +100,10 @@ void tcp_echo(struct listen_endpoint* listen_socket)
|
||||
{
|
||||
while (1) {
|
||||
int in_socket = accept(listen_socket->socketfd, 0, 0);
|
||||
CHECK_RES_DIE(in_socket, "accept");
|
||||
if (in_socket == -1) {
|
||||
perror("tcp_echo:accept");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!fork())
|
||||
{
|
||||
|
18
example.cfg
18
example.cfg
@ -3,7 +3,6 @@
|
||||
# not be used as a starting point for a working
|
||||
# configuration. Instead use basic.cfg.
|
||||
|
||||
verbose: 0;
|
||||
foreground: true;
|
||||
inetd: false;
|
||||
numeric: false;
|
||||
@ -13,6 +12,23 @@ user: "nobody";
|
||||
pidfile: "/var/run/sslh.pid";
|
||||
chroot: "/var/empty";
|
||||
|
||||
# Logging configuration
|
||||
# Value: 1: stdout; 2: syslog; 3: both
|
||||
# Defaults are indicated here, and should be sensible. Generally, you want *-error
|
||||
# to be always enabled, to know if something is going wrong.
|
||||
verbose-config: 0; # print configuration at startup
|
||||
verbose-config-error: 3; # print configuration errors
|
||||
verbose-connections: 3; # trace established incoming address to forward address
|
||||
verbose-connections-error: 3; # connection errors
|
||||
verbose-connections-try: 0; # connection attempts towards targets
|
||||
verbose-fd: 0; # file descriptor activity, open/close/whatnot
|
||||
verbose-packets: 0; # hexdump packets on which probing is done
|
||||
verbose-probe-info: 0; # what's happening during the probe process
|
||||
verbose-probe-error: 3; # failures and problems during probing
|
||||
verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing
|
||||
verbose-int-error: 3; # internal errors, the kind that should never happen
|
||||
|
||||
|
||||
# Specify which syslog facility to use (names for your
|
||||
# system are usually defined in /usr/include/*/sys/syslog.h
|
||||
# or equivalent)
|
||||
|
172
log.c
Normal file
172
log.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
# log: processing of all outgoing messages
|
||||
#
|
||||
# Copyright (C) 2007-2021 Yves Rutschle
|
||||
#
|
||||
# This program is free software; you can redistribute it
|
||||
# and/or modify it under the terms of the GNU General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be
|
||||
# useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
# PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# The full text for the General Public License is here:
|
||||
# http://www.gnu.org/licenses/gpl.html
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#define SYSLOG_NAMES
|
||||
#define _GNU_SOURCE
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "sslh-conf.h"
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
|
||||
msg_info msg_config = {
|
||||
LOG_INFO,
|
||||
&cfg.verbose_config
|
||||
};
|
||||
|
||||
msg_info msg_config_error = {
|
||||
LOG_ERR,
|
||||
&cfg.verbose_config_error
|
||||
};
|
||||
|
||||
msg_info msg_fd = {
|
||||
LOG_DEBUG,
|
||||
&cfg.verbose_fd
|
||||
};
|
||||
|
||||
/* Internal errors: inconsistent states, impossible values, things that should never happen, and are therefore the sign of memory corruption: hence the LOG_CRIT */
|
||||
msg_info msg_int_error = {
|
||||
LOG_CRIT,
|
||||
&cfg.verbose_system_error
|
||||
};
|
||||
|
||||
/* System errors: when the system around us fails us: memory allocation, fork, ... */
|
||||
msg_info msg_system_error = {
|
||||
LOG_ERR,
|
||||
&cfg.verbose_system_error
|
||||
};
|
||||
|
||||
msg_info msg_packets = {
|
||||
LOG_INFO,
|
||||
&cfg.verbose_packets
|
||||
};
|
||||
|
||||
/* additional info when attempting outgoing connections */
|
||||
msg_info msg_connections_try = {
|
||||
LOG_DEBUG,
|
||||
&cfg.verbose_connections_try
|
||||
};
|
||||
|
||||
/* Connection information and failures (e.g. forbidden by policy) */
|
||||
msg_info msg_connections = {
|
||||
LOG_INFO,
|
||||
&cfg.verbose_connections
|
||||
};
|
||||
|
||||
/* Connection failures, e.g. target server not present */
|
||||
msg_info msg_connections_error = {
|
||||
LOG_ERR,
|
||||
&cfg.verbose_connections_error
|
||||
};
|
||||
|
||||
|
||||
/* comment the probing process */
|
||||
msg_info msg_probe_info = {
|
||||
LOG_INFO,
|
||||
&cfg.verbose_probe_info
|
||||
};
|
||||
|
||||
/* probing errors, e.g. inconsistent data in connections */
|
||||
msg_info msg_probe_error = {
|
||||
LOG_ERR,
|
||||
&cfg.verbose_probe_error
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Bitmasks in verbose-* values */
|
||||
#define MSG_STDOUT 1
|
||||
#define MSG_SYSLOG 2
|
||||
|
||||
/* Prints a message to stderr and/or syslog if appropriate */
|
||||
void print_message(msg_info info, const char* str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
|
||||
if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd)
|
||||
vfprintf(stderr, str, ap);
|
||||
|
||||
if (*info.verbose & MSG_SYSLOG) {
|
||||
va_start(ap, str);
|
||||
vsyslog(info.log_level, str, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */
|
||||
|
||||
/* Open syslog connection with appropriate banner;
|
||||
* banner is made up of basename(bin_name)+"[pid]" */
|
||||
void setup_syslog(const char* bin_name) {
|
||||
char *name1, *name2;
|
||||
int res, fn;
|
||||
|
||||
if (!strcmp(cfg.syslog_facility, "none")) {
|
||||
do_syslog = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
name1 = strdup(bin_name);
|
||||
res = asprintf(&name2, "%s[%d]", basename(name1), getpid());
|
||||
CHECK_RES_DIE(res, "asprintf");
|
||||
|
||||
for (fn = 0; facilitynames[fn].c_val != -1; fn++)
|
||||
if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0)
|
||||
break;
|
||||
if (facilitynames[fn].c_val == -1) {
|
||||
fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
openlog(name2, LOG_CONS, facilitynames[fn].c_val);
|
||||
free(name1);
|
||||
/* Don't free name2, as openlog(3) uses it (at least in glibc) */
|
||||
}
|
||||
|
||||
|
||||
/* syslogs who connected to where
|
||||
* desc: string description of the connection. if NULL, log_connection will
|
||||
* manage on its own
|
||||
* cnx: connection descriptor
|
||||
* */
|
||||
void log_connection(struct connection_desc* desc, const struct connection *cnx)
|
||||
{
|
||||
struct connection_desc d;
|
||||
|
||||
if (cnx->proto->log_level < 1)
|
||||
return;
|
||||
|
||||
if (!desc) {
|
||||
desc = &d;
|
||||
get_connection_desc(desc, cnx);
|
||||
}
|
||||
|
||||
print_message(msg_connections, "%s:connection from %s to %s forwarded from %s to %s\n",
|
||||
cnx->proto->name,
|
||||
desc->peer,
|
||||
desc->service,
|
||||
desc->local,
|
||||
desc->target);
|
||||
}
|
32
log.h
Normal file
32
log.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
void setup_syslog(const char* bin_name);
|
||||
|
||||
void log_message(int type, const char* msg, ...);
|
||||
|
||||
void log_connection(struct connection_desc* desc, const struct connection *cnx);
|
||||
|
||||
typedef struct s_msg_info{
|
||||
int log_level;
|
||||
int *verbose;
|
||||
} msg_info;
|
||||
|
||||
void print_message(msg_info info, const char* str, ...);
|
||||
extern msg_info msg_config;
|
||||
extern msg_info msg_config_error;
|
||||
|
||||
extern msg_info msg_fd;
|
||||
extern msg_info msg_packets;
|
||||
|
||||
extern msg_info msg_int_error;
|
||||
extern msg_info msg_system_error;
|
||||
|
||||
extern msg_info msg_connections_try;
|
||||
extern msg_info msg_connections_error;
|
||||
extern msg_info msg_connections;
|
||||
|
||||
extern msg_info msg_probe_info;
|
||||
extern msg_info msg_probe_error;
|
||||
|
||||
#endif /* LOG_H */
|
39
probe.c
39
probe.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
# probe.c: Code for probing protocols
|
||||
#
|
||||
# Copyright (C) 2007-2019 Yves Rutschle
|
||||
# Copyright (C) 2007-2021 Yves Rutschle
|
||||
#
|
||||
# This program is free software; you can redistribute it
|
||||
# and/or modify it under the terms of the GNU General Public
|
||||
@ -27,6 +27,7 @@
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include "probe.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
|
||||
@ -81,33 +82,38 @@ struct sslhcfg_protocols_item* timeout_protocol(void)
|
||||
|
||||
/* From http://grapsus.net/blog/post/Hexadecimal-dump-in-C */
|
||||
#define HEXDUMP_COLS 16
|
||||
void hexdump(const char *mem, unsigned int len)
|
||||
void hexdump(msg_info msg_info, const char *mem, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
char str[10 + HEXDUMP_COLS * 4 + 2];
|
||||
int c = 0; /* index in str */
|
||||
|
||||
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
|
||||
{
|
||||
/* print offset */
|
||||
if(i % HEXDUMP_COLS == 0)
|
||||
fprintf(stderr, "0x%06x: ", i);
|
||||
c += sprintf(&str[c], "0x%06x: ", i);
|
||||
|
||||
/* print hex data */
|
||||
if(i < len)
|
||||
fprintf(stderr, "%02x ", 0xFF & mem[i]);
|
||||
c += sprintf(&str[c], "%02x ", 0xFF & mem[i]);
|
||||
else /* end of block, just aligning for ASCII dump */
|
||||
fprintf(stderr, " ");
|
||||
c+= sprintf(&str[c], " ");
|
||||
|
||||
/* print ASCII dump */
|
||||
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
|
||||
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
|
||||
if(j >= len) /* end of block, not really printing */
|
||||
fputc(' ', stderr);
|
||||
str[c++] = ' ';
|
||||
else if(isprint(mem[j])) /* printable char */
|
||||
fputc(0xFF & mem[j], stderr);
|
||||
str[c++] = 0xFF & mem[j];
|
||||
else /* other char */
|
||||
fputc('.', stderr);
|
||||
str[c++] = '.';
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
str[c++] = '\n';
|
||||
str[c++] = 0;
|
||||
print_message(msg_info, str);
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -328,7 +334,7 @@ static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item
|
||||
return 0;
|
||||
#else
|
||||
/* Should never happen as we check when loading config file */
|
||||
fprintf(stderr, "FATAL: regex probe called but not built in\n");
|
||||
print_message(msg_int_error, "FATAL: regex probe called but not built in\n");
|
||||
exit(5);
|
||||
#endif
|
||||
}
|
||||
@ -344,10 +350,8 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
|
||||
struct sslhcfg_protocols_item* p;
|
||||
int i, res, again = 0;
|
||||
|
||||
if (cfg.verbose > 1) {
|
||||
fprintf(stderr, "hexdump of incoming packet:\n");
|
||||
hexdump(buf, len);
|
||||
}
|
||||
print_message(msg_packets, "hexdump of incoming packet:\n");
|
||||
hexdump(msg_packets, buf, len);
|
||||
|
||||
*proto = NULL;
|
||||
for (i = 0; i < cfg.protocols_len; i++) {
|
||||
@ -356,20 +360,21 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
|
||||
|
||||
if (! p->probe) continue;
|
||||
|
||||
if (cfg.verbose) fprintf(stderr, "probing for %s\n", p->name);
|
||||
print_message(msg_probe_info, "probing for %s\n", p->name);
|
||||
|
||||
/* Don't probe last protocol if it is anyprot (and store last protocol) */
|
||||
if ((i == cfg.protocols_len - 1) && (!strcmp(p->name, "anyprot")))
|
||||
break;
|
||||
|
||||
if (p->minlength_is_present && (len < p->minlength )) {
|
||||
fprintf(stderr, "input too short, %d bytes but need %d\n", len , p->minlength);
|
||||
print_message(msg_probe_info, "input too short, %d bytes but need %d\n",
|
||||
len , p->minlength);
|
||||
again++;
|
||||
continue;
|
||||
}
|
||||
|
||||
res = p->probe(buf, len, p);
|
||||
if (cfg.verbose) fprintf(stderr, "probed for %s: %s\n", p->name, probe_str[res]);
|
||||
print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]);
|
||||
|
||||
if (res == PROBE_MATCH) {
|
||||
*proto = p;
|
||||
|
3
probe.h
3
probe.h
@ -5,6 +5,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "tls.h"
|
||||
#include "log.h"
|
||||
|
||||
typedef enum {
|
||||
PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
|
||||
@ -59,6 +60,6 @@ void set_ontimeout(const char* name);
|
||||
*/
|
||||
struct sslhcfg_protocols_item* timeout_protocol(void);
|
||||
|
||||
void hexdump(const char*, unsigned int);
|
||||
void hexdump(msg_info, const char*, unsigned int);
|
||||
|
||||
#endif
|
||||
|
248
sslh-conf.c
248
sslh-conf.c
@ -1,5 +1,5 @@
|
||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
||||
* on Tue Aug 24 13:53:04 2021.
|
||||
* on Sat Oct 2 09:01:25 2021.
|
||||
|
||||
# conf2struct: generate libconf parsers that read to structs
|
||||
# Copyright (C) 2018-2021 Yves Rutschle
|
||||
@ -443,7 +443,17 @@ struct compound_cl_arg {
|
||||
|
||||
|
||||
struct arg_file* sslhcfg_conffile;
|
||||
struct arg_int* sslhcfg_verbose;
|
||||
struct arg_int* sslhcfg_verbose_config;
|
||||
struct arg_int* sslhcfg_verbose_config_error;
|
||||
struct arg_int* sslhcfg_verbose_connections;
|
||||
struct arg_int* sslhcfg_verbose_connections_try;
|
||||
struct arg_int* sslhcfg_verbose_connections_error;
|
||||
struct arg_int* sslhcfg_verbose_fd;
|
||||
struct arg_int* sslhcfg_verbose_packets;
|
||||
struct arg_int* sslhcfg_verbose_probe_info;
|
||||
struct arg_int* sslhcfg_verbose_probe_error;
|
||||
struct arg_int* sslhcfg_verbose_system_error;
|
||||
struct arg_int* sslhcfg_verbose_int_error;
|
||||
struct arg_lit* sslhcfg_version;
|
||||
struct arg_lit* sslhcfg_foreground;
|
||||
struct arg_lit* sslhcfg_inetd;
|
||||
@ -783,17 +793,17 @@ static struct config_desc table_sslhcfg_listen[] = {
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
static struct config_desc table_sslhcfg[] = {
|
||||
|
||||
|
||||
{
|
||||
/* name */ "verbose",
|
||||
/* name */ "verbose_config",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose,
|
||||
/* arg_cl */ & sslhcfg_verbose_config,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose),
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_config),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
@ -803,6 +813,166 @@ static struct config_desc table_sslhcfg[] = {
|
||||
/* default_val*/ .default_val.def_int = 0
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_config_error",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_config_error,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_config_error),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 3
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_connections",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_connections,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_connections),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 3
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_connections_try",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_connections_try,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_connections_try),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 0
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_connections_error",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_connections_error,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_connections_error),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 3
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_fd",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_fd,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_fd),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 0
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_packets",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_packets,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_packets),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 0
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_probe_info",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_probe_info,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_probe_info),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 0
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_probe_error",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_probe_error,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_probe_error),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 3
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_system_error",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_system_error,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_system_error),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 3
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "verbose_int_error",
|
||||
/* type */ CFG_INT,
|
||||
/* sub_group*/ NULL,
|
||||
/* arg_cl */ & sslhcfg_verbose_int_error,
|
||||
/* base_addr */ NULL,
|
||||
/* offset */ offsetof(struct sslhcfg_item, verbose_int_error),
|
||||
/* offset_len */ 0,
|
||||
/* offset_present */ 0,
|
||||
/* size */ sizeof(int),
|
||||
/* array_type */ -1,
|
||||
/* mandatory */ 0,
|
||||
/* optional */ 0,
|
||||
/* default_val*/ .default_val.def_int = 3
|
||||
},
|
||||
|
||||
{
|
||||
/* name */ "version",
|
||||
/* type */ CFG_BOOL,
|
||||
@ -1123,7 +1293,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: listen */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_listen,
|
||||
.base_entry = & table_sslhcfg [13],
|
||||
.base_entry = & table_sslhcfg [23],
|
||||
.targets = sslhcfg_listen_targets,
|
||||
|
||||
|
||||
@ -1135,7 +1305,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: ssh */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_ssh,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_ssh_targets,
|
||||
|
||||
|
||||
@ -1147,7 +1317,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: tls */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_tls,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_tls_targets,
|
||||
|
||||
|
||||
@ -1159,7 +1329,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: openvpn */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_openvpn,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_openvpn_targets,
|
||||
|
||||
|
||||
@ -1171,7 +1341,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: tinc */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_tinc,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_tinc_targets,
|
||||
|
||||
|
||||
@ -1183,7 +1353,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: xmpp */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_xmpp,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_xmpp_targets,
|
||||
|
||||
|
||||
@ -1195,7 +1365,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: http */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_http,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_http_targets,
|
||||
|
||||
|
||||
@ -1207,7 +1377,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: adb */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_adb,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_adb_targets,
|
||||
|
||||
|
||||
@ -1219,7 +1389,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: socks5 */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_socks5,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_socks5_targets,
|
||||
|
||||
|
||||
@ -1231,7 +1401,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: syslog */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_syslog,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_syslog_targets,
|
||||
|
||||
|
||||
@ -1243,7 +1413,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
{ /* arg: anyprot */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_anyprot,
|
||||
.base_entry = & table_sslhcfg [14],
|
||||
.base_entry = & table_sslhcfg [24],
|
||||
.targets = sslhcfg_anyprot_targets,
|
||||
|
||||
|
||||
@ -1908,7 +2078,17 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg)
|
||||
#ifdef LIBCONFIG
|
||||
sslhcfg_conffile = arg_filen("F", "config", "<file>", 0, 1, "Specify configuration file"),
|
||||
#endif
|
||||
sslhcfg_verbose = arg_intn("v", "verbose", "<n>", 0, 1, ""),
|
||||
sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "<n>", 0, 1, "Print configuration at startup"),
|
||||
sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "<n>", 0, 1, "Print configuration errors"),
|
||||
sslhcfg_verbose_connections = arg_intn(NULL, "verbose-connections", "<n>", 0, 1, "Trace established incoming address to forward address"),
|
||||
sslhcfg_verbose_connections_try = arg_intn(NULL, "verbose-connections-try", "<n>", 0, 1, "Connection errors"),
|
||||
sslhcfg_verbose_connections_error = arg_intn(NULL, "verbose-connections-error", "<n>", 0, 1, "Connection attempts towards targets"),
|
||||
sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "<n>", 0, 1, "File descriptor activity, open/close/whatnot"),
|
||||
sslhcfg_verbose_packets = arg_intn(NULL, "verbose-packets", "<n>", 0, 1, "Hexdump packets on which probing is done"),
|
||||
sslhcfg_verbose_probe_info = arg_intn(NULL, "verbose-probe-info", "<n>", 0, 1, "Trace the probe process"),
|
||||
sslhcfg_verbose_probe_error = arg_intn(NULL, "verbose-probe-error", "<n>", 0, 1, "Failures and problems during probing"),
|
||||
sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "<n>", 0, 1, "System call failures"),
|
||||
sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "<n>", 0, 1, "Internal errors that should never happen"),
|
||||
sslhcfg_version = arg_litn("V", "version", 0, 1, "Print version information and exit"),
|
||||
sslhcfg_foreground = arg_litn("f", "foreground", 0, 1, "Run in foreground instead of as a daemon"),
|
||||
sslhcfg_inetd = arg_litn("i", "inetd", 0, 1, "Run in inetd mode: use stdin/stdout instead of network listen"),
|
||||
@ -2077,7 +2257,37 @@ void sslhcfg_fprint(
|
||||
{
|
||||
int i;
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose: %d", sslhcfg->verbose);
|
||||
fprintf(out, "verbose_config: %d", sslhcfg->verbose_config);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_config_error: %d", sslhcfg->verbose_config_error);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_connections: %d", sslhcfg->verbose_connections);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_connections_try: %d", sslhcfg->verbose_connections_try);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_connections_error: %d", sslhcfg->verbose_connections_error);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_fd: %d", sslhcfg->verbose_fd);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_packets: %d", sslhcfg->verbose_packets);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_probe_info: %d", sslhcfg->verbose_probe_info);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_probe_error: %d", sslhcfg->verbose_probe_error);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_system_error: %d", sslhcfg->verbose_system_error);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "verbose_int_error: %d", sslhcfg->verbose_int_error);
|
||||
fprintf(out, "\n");
|
||||
indent(out, depth);
|
||||
fprintf(out, "version: %d", sslhcfg->version);
|
||||
|
14
sslh-conf.h
14
sslh-conf.h
@ -1,5 +1,5 @@
|
||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
||||
* on Tue Aug 24 13:53:04 2021.
|
||||
* on Sat Oct 2 09:01:25 2021.
|
||||
|
||||
# conf2struct: generate libconf parsers that read to structs
|
||||
# Copyright (C) 2018-2021 Yves Rutschle
|
||||
@ -74,7 +74,17 @@ struct sslhcfg_protocols_item {
|
||||
};
|
||||
|
||||
struct sslhcfg_item {
|
||||
int verbose;
|
||||
int verbose_config;
|
||||
int verbose_config_error;
|
||||
int verbose_connections;
|
||||
int verbose_connections_try;
|
||||
int verbose_connections_error;
|
||||
int verbose_fd;
|
||||
int verbose_packets;
|
||||
int verbose_probe_info;
|
||||
int verbose_probe_error;
|
||||
int verbose_system_error;
|
||||
int verbose_int_error;
|
||||
int version;
|
||||
int foreground;
|
||||
int inetd;
|
||||
|
17
sslh-fork.c
17
sslh-fork.c
@ -24,6 +24,7 @@
|
||||
#include "probe.h"
|
||||
#include "sslh-conf.h"
|
||||
#include "udp-listener.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef LIBBSD
|
||||
#include <bsd/unistd.h>
|
||||
@ -58,8 +59,7 @@ int shovel(struct connection *cnx)
|
||||
if (FD_ISSET(cnx->q[i].fd, &fds)) {
|
||||
res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
|
||||
if (res == FD_CNXCLOSED) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "%s %s", i ? "client" : "server", "socket closed\n");
|
||||
print_message(msg_fd, "%s %s", i ? "client" : "server", "socket closed\n");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@ -100,7 +100,7 @@ void start_shoveler(int in_socket)
|
||||
/* Timed out: it's necessarily SSH */
|
||||
cnx.proto = timeout_protocol();
|
||||
if (cfg.verbose)
|
||||
log_message(LOG_INFO, "timed out, connect to %s\n", cnx.proto->name);
|
||||
print_message(msg_fd, "timed out, connect to %s\n", cnx.proto->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -129,8 +129,7 @@ void start_shoveler(int in_socket)
|
||||
close(in_socket);
|
||||
close(out_socket);
|
||||
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "connection closed down\n");
|
||||
print_message(msg_fd, "connection closed down\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@ -179,10 +178,10 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
|
||||
|
||||
while (1) {
|
||||
in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0);
|
||||
if (cfg.verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
|
||||
print_message(msg_fd, "accepted fd %d\n", in_socket);
|
||||
|
||||
switch(fork()) {
|
||||
case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
break;
|
||||
|
||||
case 0: /* In child process */
|
||||
@ -214,13 +213,13 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
listener_pid[i] = fork();
|
||||
switch(listener_pid[i]) {
|
||||
/* Log if fork() fails for some reason */
|
||||
case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
break;
|
||||
/* We're in the child, we have work to do */
|
||||
case 0:
|
||||
set_listen_procname(&listen_sockets[i]);
|
||||
if (listen_sockets[i].type == SOCK_DGRAM)
|
||||
log_message(LOG_ERR, "UDP not (yet?) supported in sslh-fork\n");
|
||||
print_message(msg_config_error, "UDP not (yet?) supported in sslh-fork\n");
|
||||
else
|
||||
tcp_listener(listen_sockets, num_addr_listen, i);
|
||||
break;
|
||||
|
31
sslh-main.c
31
sslh-main.c
@ -36,6 +36,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "probe.h"
|
||||
#include "log.h"
|
||||
|
||||
/* Constants for options that have no one-character shorthand */
|
||||
#define OPT_ONTIMEOUT 257
|
||||
@ -50,7 +51,7 @@ static void printcaps(void) {
|
||||
|
||||
desc = cap_to_text(caps, &len);
|
||||
|
||||
fprintf(stderr, "capabilities: %s\n", desc);
|
||||
print_message(msg_config, "capabilities: %s\n", desc);
|
||||
|
||||
cap_free(caps);
|
||||
cap_free(desc);
|
||||
@ -65,7 +66,7 @@ static void printsettings(void)
|
||||
|
||||
for (i = 0; i < cfg.protocols_len; i++ ) {
|
||||
p = &cfg.protocols[i];
|
||||
fprintf(stderr,
|
||||
print_message(msg_config,
|
||||
"%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s] [%s]\n",
|
||||
p->name,
|
||||
sprintaddr(buf, sizeof(buf), p->saddr),
|
||||
@ -78,7 +79,7 @@ static void printsettings(void)
|
||||
p->transparent ? "transparent" : ""
|
||||
);
|
||||
}
|
||||
fprintf(stderr, "timeout: %d\non-timeout: %s\n", cfg.timeout,
|
||||
print_message(msg_config, "timeout: %d\non-timeout: %s\n", cfg.timeout,
|
||||
timeout_protocol()->name);
|
||||
}
|
||||
|
||||
@ -103,7 +104,7 @@ static void setup_regex_probe(struct sslhcfg_protocols_item *p)
|
||||
&error, &error_offset, NULL);
|
||||
if (!pattern_list[i]) {
|
||||
pcre2_get_error_message(error, err_str, sizeof(err_str));
|
||||
fprintf(stderr, "compiling pattern /%s/:%d:%s at offset %ld\n",
|
||||
print_message(msg_config_error, "compiling pattern /%s/:%d:%s at offset %ld\n",
|
||||
p->regex_patterns[i], error, err_str, error_offset);
|
||||
exit(1);
|
||||
}
|
||||
@ -124,13 +125,13 @@ static void config_protocols()
|
||||
for (i = 0; i < cfg.protocols_len; i++) {
|
||||
struct sslhcfg_protocols_item* p = &(cfg.protocols[i]);
|
||||
if (resolve_split_name(&(p->saddr), p->host, p->port)) {
|
||||
fprintf(stderr, "cannot resolve %s:%s\n", p->host, p->port);
|
||||
print_message(msg_config_error, "cannot resolve %s:%s\n", p->host, p->port);
|
||||
exit(4);
|
||||
}
|
||||
|
||||
p->probe = get_probe(p->name);
|
||||
if (!p->probe) {
|
||||
fprintf(stderr, "%s: probe unknown\n", p->name);
|
||||
print_message(msg_config_error, "%s: probe unknown\n", p->name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -155,14 +156,14 @@ static void config_protocols()
|
||||
|
||||
void config_sanity_check(struct sslhcfg_item* cfg) {
|
||||
if (!cfg->protocols_len) {
|
||||
fprintf(stderr, "At least one target protocol must be specified.\n");
|
||||
print_message(msg_config_error, "At least one target protocol must be specified.\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/* If compiling with systemd socket support no need to require listen address */
|
||||
#ifndef SYSTEMD
|
||||
if (!cfg->listen_len && !cfg->inetd) {
|
||||
fprintf(stderr, "No listening address specified; use at least one -p option\n");
|
||||
print_message(msg_config_error, "No listening address specified; use at least one -p option\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
@ -184,8 +185,6 @@ int main(int argc, char *argv[], char* envp[])
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
res = sslhcfg_cl_parse(argc, argv, &cfg);
|
||||
if (res) exit(6);
|
||||
if (cfg.verbose > 3)
|
||||
sslhcfg_fprint(stderr, &cfg, 0);
|
||||
|
||||
if (cfg.version) {
|
||||
printf("%s %s\n", server_type, VERSION);
|
||||
@ -197,19 +196,18 @@ int main(int argc, char *argv[], char* envp[])
|
||||
|
||||
if (cfg.inetd)
|
||||
{
|
||||
cfg.verbose = 0;
|
||||
close(fileno(stderr)); /* Make sure no error will go to client */
|
||||
start_shoveler(0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (cfg.verbose)
|
||||
printsettings();
|
||||
printsettings();
|
||||
|
||||
num_addr_listen = start_listen_sockets(&listen_sockets);
|
||||
|
||||
#ifdef SYSTEMD
|
||||
if (num_addr_listen < 1) {
|
||||
fprintf(stderr, "No listening sockets found, restart sockets or specify addresses in config\n");
|
||||
print_message(msg_config_error, "No listening sockets found, restart sockets or specify addresses in config\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
@ -235,8 +233,9 @@ int main(int argc, char *argv[], char* envp[])
|
||||
if (cfg.user || cfg.chroot)
|
||||
drop_privileges(cfg.user, cfg.chroot);
|
||||
|
||||
if (cfg.verbose)
|
||||
printcaps();
|
||||
printcaps();
|
||||
|
||||
print_message(msg_config, "%s %s started\n", server_type, VERSION);
|
||||
|
||||
main_loop(listen_sockets, num_addr_listen);
|
||||
|
||||
|
@ -37,8 +37,7 @@
|
||||
#include "udp-listener.h"
|
||||
#include "collection.h"
|
||||
#include "gap.h"
|
||||
|
||||
static int debug = 0;
|
||||
#include "log.h"
|
||||
|
||||
const char* server_type = "sslh-select";
|
||||
|
||||
@ -67,8 +66,7 @@ static int tidy_connection(struct connection *cnx, struct select_info* fd_info)
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (cnx->q[i].fd != -1) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "closing fd %d\n", cnx->q[i].fd);
|
||||
print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd);
|
||||
|
||||
FD_CLR(cnx->q[i].fd, fds);
|
||||
FD_CLR(cnx->q[i].fd, fds2);
|
||||
@ -85,7 +83,7 @@ static int tidy_connection(struct connection *cnx, struct select_info* fd_info)
|
||||
* and FD_CLR. Need to drop connections if we go above that limit */
|
||||
static int fd_is_in_range(int fd) {
|
||||
if (fd >= FD_SETSIZE) {
|
||||
log_message(LOG_ERR, "too many open file descriptor to monitor them all -- dropping connection\n");
|
||||
print_message(msg_system_error, "too many open file descriptor to monitor them all -- dropping connection\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@ -99,7 +97,7 @@ static struct connection* accept_new_connection(int listen_socket, struct cnx_co
|
||||
int in_socket, res;
|
||||
|
||||
|
||||
if (cfg.verbose) fprintf(stderr, "accepting from %d\n", listen_socket);
|
||||
print_message(msg_fd, "accepting from %d\n", listen_socket);
|
||||
|
||||
in_socket = accept(listen_socket, 0, 0);
|
||||
CHECK_RES_RETURN(in_socket, "accept", NULL);
|
||||
@ -158,8 +156,7 @@ static void shovel(struct connection *cnx, int active_fd, struct select_info* fd
|
||||
read_q = &cnx->q[active_fd];
|
||||
write_q = &cnx->q[1-active_fd];
|
||||
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "activity on fd%d\n", read_q->fd);
|
||||
print_message(msg_fd, "activity on fd%d\n", read_q->fd);
|
||||
|
||||
switch(fd2fd(write_q, read_q)) {
|
||||
case -1:
|
||||
@ -212,16 +209,14 @@ static void shovel_single(struct connection *cnx)
|
||||
if (FD_ISSET(cnx->q[i].fd, &fds_w)) {
|
||||
res = flush_deferred(&cnx->q[i]);
|
||||
if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "%s socket closed\n", i ? "server" : "client");
|
||||
print_message(msg_fd, "%s socket closed\n", i ? "server" : "client");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(cnx->q[i].fd, &fds_r)) {
|
||||
res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
|
||||
if (!res) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "socket closed\n");
|
||||
print_message(msg_fd, "socket closed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -258,8 +253,7 @@ static void connect_proxy(struct connection *cnx)
|
||||
close(in_socket);
|
||||
close(out_socket);
|
||||
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "connection closed down\n");
|
||||
print_message(msg_fd, "connection closed down\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@ -292,10 +286,7 @@ static void probing_read_process(struct connection* cnx,
|
||||
* data so probe the protocol */
|
||||
if ((cnx->probe_timeout < time(NULL))) {
|
||||
cnx->proto = timeout_protocol();
|
||||
if (cfg.verbose)
|
||||
log_message(LOG_INFO,
|
||||
"timed out, connect to %s\n",
|
||||
cnx->proto->name);
|
||||
print_message(msg_fd, "timed out, connect to %s\n", cnx->proto->name);
|
||||
} else {
|
||||
res = probe_client_protocol(cnx);
|
||||
if (res == PROBE_AGAIN)
|
||||
@ -317,7 +308,7 @@ static void probing_read_process(struct connection* cnx,
|
||||
/* free(cnx); */
|
||||
connect_proxy(cnx);
|
||||
exit(0);
|
||||
case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
break;
|
||||
default: /* parent */
|
||||
break;
|
||||
@ -339,7 +330,7 @@ int active_queue(struct connection* cnx, int fd)
|
||||
if (cnx->q[0].fd == fd) return 0;
|
||||
if (cnx->q[1].fd == fd) return 1;
|
||||
|
||||
log_message(LOG_ERR, "file descriptor %d not found in connection object\n", fd);
|
||||
print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -347,8 +338,6 @@ int active_queue(struct connection* cnx, int fd)
|
||||
static void tcp_read_process(struct select_info* fd_info,
|
||||
int fd)
|
||||
{
|
||||
if (debug) fprintf(stderr, "cnx_read_process fd %d\n", fd);
|
||||
|
||||
cnx_collection* collection = fd_info->collection;
|
||||
struct connection* cnx = collection_get_cnx_from_fd(collection, fd);
|
||||
/* Determine active queue (0 or 1): if fd is that of q[1], active_q = 1,
|
||||
@ -359,7 +348,7 @@ static void tcp_read_process(struct select_info* fd_info,
|
||||
|
||||
case ST_PROBING:
|
||||
if (active_q == 1) {
|
||||
fprintf(stderr, "Activity on fd2 while probing, impossible\n");
|
||||
print_message(msg_int_error, "Activity on fd2 while probing, impossible\n");
|
||||
dump_connection(cnx);
|
||||
exit(1);
|
||||
}
|
||||
@ -373,7 +362,7 @@ static void tcp_read_process(struct select_info* fd_info,
|
||||
break;
|
||||
|
||||
default: /* illegal */
|
||||
log_message(LOG_ERR, "Illegal connection state %d\n", cnx->state);
|
||||
print_message(msg_int_error, "Illegal connection state %d\n", cnx->state);
|
||||
dump_connection(cnx);
|
||||
exit(1);
|
||||
}
|
||||
@ -393,18 +382,15 @@ static void cnx_read_process(struct select_info* fd_info, int fd)
|
||||
break;
|
||||
|
||||
default:
|
||||
log_message(LOG_ERR, "cnx_read_process: Illegal connection type %d\n", cnx->type);
|
||||
print_message(msg_int_error, "cnx_read_process: Illegal connection type %d\n", cnx->type);
|
||||
dump_connection(cnx);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Process a connection that is active in write */
|
||||
static void cnx_write_process(struct select_info* fd_info, int fd)
|
||||
{
|
||||
if (debug) fprintf(stderr, "cnx_write_process fd %d\n", fd);
|
||||
|
||||
struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd);
|
||||
int res;
|
||||
int queue = active_queue(cnx, fd);
|
||||
@ -433,8 +419,6 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis
|
||||
struct connection* cnx;
|
||||
int new_fd;
|
||||
|
||||
if (debug) fprintf(stderr, "cnx_accept_process fd %d\n", fd);
|
||||
|
||||
switch (type) {
|
||||
case SOCK_STREAM:
|
||||
cnx = accept_new_connection(fd, fd_info->collection);
|
||||
@ -447,13 +431,13 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis
|
||||
|
||||
case SOCK_DGRAM:
|
||||
new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->max_fd);
|
||||
fprintf(stderr, "new_fd %d\n", new_fd);
|
||||
print_message(msg_fd, "new_fd %d\n", new_fd);
|
||||
if (new_fd == -1)
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_message(LOG_ERR, "Inconsistent cnx type: %d\n", type);
|
||||
print_message(msg_int_error, "Inconsistent cnx type: %d\n", type);
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
@ -486,8 +470,7 @@ static void udp_timeouts(struct select_info* fd_info)
|
||||
time_t timeout = udp_timeout(cnx);
|
||||
if (!timeout) continue; /* Not a UDP connection */
|
||||
if (cnx && (timeout <= now)) {
|
||||
if (cfg.verbose > 3)
|
||||
fprintf(stderr, "timed out UDP %d\n", cnx->target_sock);
|
||||
print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock);
|
||||
close(cnx->target_sock);
|
||||
FD_CLR(i, &fd_info->fds_r);
|
||||
FD_CLR(i, &fd_info->fds_w);
|
||||
@ -545,8 +528,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
memcpy(&readfds, &fd_info.fds_r, sizeof(readfds));
|
||||
memcpy(&writefds, &fd_info.fds_w, sizeof(writefds));
|
||||
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "selecting... max_fd=%d num_probing=%d\n",
|
||||
print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n",
|
||||
fd_info.max_fd, fd_info.num_probing);
|
||||
res = select(fd_info.max_fd, &readfds, &writefds,
|
||||
NULL, fd_info.num_probing ? &tv : NULL);
|
||||
@ -578,14 +560,13 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
for (i = 0; i < fd_info.num_probing; i++) {
|
||||
struct connection* cnx = gap_get(fd_info.probing_list, i);
|
||||
if (!cnx || cnx->state != ST_PROBING) {
|
||||
log_message(LOG_ERR, "Inconsistent probing: cnx=%0xp\n", cnx);
|
||||
print_message(msg_int_error, "Inconsistent probing: cnx=%0xp\n", cnx);
|
||||
if (cnx)
|
||||
log_message(LOG_ERR, "Inconsistent probing: state=%d\n", cnx);
|
||||
print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx);
|
||||
exit(1);
|
||||
}
|
||||
if (cnx->probe_timeout < time(NULL)) {
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "timeout slot %d\n", i);
|
||||
print_message(msg_fd, "timeout slot %d\n", i);
|
||||
probing_read_process(cnx, &fd_info);
|
||||
}
|
||||
}
|
||||
@ -604,7 +585,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
|
||||
|
||||
void start_shoveler(int listen_socket) {
|
||||
fprintf(stderr, "inetd mode is not supported in select mode\n");
|
||||
print_message(msg_config_error, "inetd mode is not supported in select mode\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
26
sslhconf.cfg
26
sslhconf.cfg
@ -25,7 +25,31 @@ config: {
|
||||
name : "sslhcfg",
|
||||
type: "list",
|
||||
items: (
|
||||
{ name: "verbose"; type: "int"; default: 0; short: "v"; },
|
||||
{ name: "verbose-config"; type: "int"; default: 0;
|
||||
description: "Print configuration at startup" },
|
||||
{ name: "verbose-config-error"; type: "int"; default: 3;
|
||||
description: "Print configuration errors" },
|
||||
{ name: "verbose-connections"; type: "int"; default: 3;
|
||||
description: "Trace established incoming address to forward address" },
|
||||
{ name: "verbose-connections-try"; type: "int"; default: 0;
|
||||
description: "Connection errors" },
|
||||
{ name: "verbose-connections-error"; type: "int"; default: 3;
|
||||
description: "Connection attempts towards targets" },
|
||||
{ name: "verbose-fd"; type: "int"; default: 0;
|
||||
description: "File descriptor activity, open/close/whatnot" },
|
||||
{ name: "verbose-packets"; type: "int"; default: 0;
|
||||
description: "Hexdump packets on which probing is done" },
|
||||
|
||||
{ name: "verbose-probe-info"; type: "int"; default: 0;
|
||||
description: "Trace the probe process" },
|
||||
{ name: "verbose-probe-error"; type: "int"; default: 3;
|
||||
description: "Failures and problems during probing" },
|
||||
|
||||
{ name: "verbose-system-error"; type: "int"; default: 3;
|
||||
description: "System call failures" },
|
||||
{ name: "verbose-int-error"; type: "int"; default: 3;
|
||||
description: "Internal errors that should never happen" },
|
||||
|
||||
{ name: "version"; type: "bool"; default: false;
|
||||
short: "V";
|
||||
description: "Print version information and exit"; },
|
||||
|
16
test.cfg
16
test.cfg
@ -1,7 +1,6 @@
|
||||
# Configuration file for testing (use both by sslh under
|
||||
# test and the test script `t`)
|
||||
|
||||
verbose: 4;
|
||||
foreground: true;
|
||||
inetd: false;
|
||||
numeric: true;
|
||||
@ -11,6 +10,21 @@ pidfile: "/tmp/sslh_test.pid";
|
||||
|
||||
syslog_facility: "auth";
|
||||
|
||||
# Logging configuration
|
||||
# Value: 1: stdout; 2: syslog; 3: both
|
||||
# Defaults should be sensible. Generally, you want *-error
|
||||
# to be always enabled, to know if something is going wrong.
|
||||
verbose-config: 3; # print configuration at startup
|
||||
verbose-config-error: 3; # print configuration errors
|
||||
verbose-connections: 3; # trace established incoming address to forward address
|
||||
verbose-connections-error: 3; # connection errors
|
||||
verbose-connections-try: 3; # connection attempts towards targets
|
||||
verbose-fd: 3; # file descriptor activity, open/close/whatnot
|
||||
verbose-packets: 3; # hexdump packets on which probing is done
|
||||
verbose-probe-info: 3; # what's happening during the probe process
|
||||
verbose-probe-error: 3; # failures and problems during probing
|
||||
verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing
|
||||
verbose-int-error: 3; # internal errors, the kind that should never happen
|
||||
|
||||
# List of interfaces on which we should listen
|
||||
# Options:
|
||||
|
15
tls.c
15
tls.c
@ -33,6 +33,7 @@
|
||||
#include <fnmatch.h> /* fnmatch() */
|
||||
#include "tls.h"
|
||||
#include "sslh-conf.h"
|
||||
#include "log.h"
|
||||
|
||||
#define TLS_HEADER_LEN 5
|
||||
#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
|
||||
@ -82,14 +83,14 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
|
||||
|
||||
tls_content_type = data[0];
|
||||
if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
|
||||
if (cfg.verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n");
|
||||
print_message(msg_probe_error, "Request did not begin with TLS handshake.\n");
|
||||
return TLS_EPROTOCOL;
|
||||
}
|
||||
|
||||
tls_version_major = data[1];
|
||||
tls_version_minor = data[2];
|
||||
if (tls_version_major < 3) {
|
||||
if (cfg.verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n",
|
||||
print_message(msg_probe_error, "Received SSL %d.%d handshake which cannot be parsed.\n",
|
||||
tls_version_major, tls_version_minor);
|
||||
|
||||
return TLS_EVERSION;
|
||||
@ -111,7 +112,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
|
||||
return TLS_EPROTOCOL;
|
||||
}
|
||||
if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
|
||||
if (cfg.verbose) fprintf(stderr, "Not a client hello\n");
|
||||
print_message(msg_probe_error, "Not a client hello\n");
|
||||
|
||||
return TLS_EPROTOCOL;
|
||||
}
|
||||
@ -144,7 +145,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
|
||||
pos += 1 + len;
|
||||
|
||||
if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) {
|
||||
if (cfg.verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n");
|
||||
print_message(msg_probe_error, "Received SSL 3.0 handshake without extensions\n");
|
||||
return TLS_EVERSION;
|
||||
}
|
||||
|
||||
@ -228,7 +229,7 @@ parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data
|
||||
return TLS_ENOEXT;
|
||||
}
|
||||
default:
|
||||
if (cfg.verbose) fprintf(stderr, "Unknown server name extension name type: %d\n",
|
||||
print_message(msg_probe_error, "Unknown server name extension name type: %d\n",
|
||||
data[pos]);
|
||||
}
|
||||
pos += 3 + len;
|
||||
@ -254,7 +255,7 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_
|
||||
if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) {
|
||||
return len;
|
||||
} else if (len > 0) {
|
||||
if (cfg.verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1);
|
||||
print_message(msg_probe_error, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1);
|
||||
}
|
||||
pos += 1 + len;
|
||||
}
|
||||
@ -276,7 +277,7 @@ has_match(const char** list, size_t list_len, const char* name, size_t name_len)
|
||||
|
||||
for (i = 0; i < list_len; i++) {
|
||||
item = &list[i];
|
||||
if (cfg.verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
|
||||
print_message(msg_probe_error, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
|
||||
if(!fnmatch(*item, name_nullterminated, 0)) {
|
||||
free(name_nullterminated);
|
||||
return 1;
|
||||
|
@ -76,15 +76,15 @@ int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd)
|
||||
target = known_source(collection, max_fd, &src_addr, addrlen);
|
||||
addrinfo.ai_addr = &src_addr;
|
||||
addrinfo.ai_addrlen = addrlen;
|
||||
if (cfg.verbose)
|
||||
fprintf(stderr, "received %ld UDP from %d:%s\n", len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
|
||||
print_message(msg_probe_info, "received %ld UDP from %d:%s\n",
|
||||
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
|
||||
|
||||
if (target == -1) {
|
||||
res = probe_buffer(data, len, &proto);
|
||||
/* First version: if we can't work out the protocol from the first
|
||||
* packet, drop it. Conceivably, we could store several packets to
|
||||
* run probes on packet sets */
|
||||
if (cfg.verbose) fprintf(stderr, "UDP probed: %d\n", res);
|
||||
print_message(msg_probe_info, "UDP probed: %d\n", res);
|
||||
if (res != PROBE_MATCH) {
|
||||
return -1;
|
||||
}
|
||||
@ -106,7 +106,7 @@ int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd)
|
||||
res = sendto(cnx->target_sock, data, len, 0,
|
||||
cnx->proto->saddr->ai_addr, cnx->proto->saddr->ai_addrlen);
|
||||
cnx->last_active = time(NULL);
|
||||
fprintf(stderr, "sending %d to %s\n",
|
||||
print_message(msg_fd, "sending %d to %s\n",
|
||||
res, sprintaddr(data, sizeof(data), cnx->proto->saddr));
|
||||
return out;
|
||||
}
|
||||
@ -119,12 +119,10 @@ void udp_s2c_forward(struct connection* cnx)
|
||||
int res;
|
||||
|
||||
res = recvfrom(sockfd, data, sizeof(data), 0, NULL, NULL);
|
||||
fprintf(stderr, "recvfrom %d\n", res);
|
||||
CHECK_RES_DIE(res, "udp_listener/recvfrom");
|
||||
res = sendto(cnx->local_endpoint, data, res, 0,
|
||||
&cnx->client_addr, cnx->addrlen);
|
||||
cnx->last_active = time(NULL);
|
||||
fprintf(stderr, "sendto %d to\n", res);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user