Skip to content

Commit

Permalink
sort target protocols as TCP or UDP, so only appropriate probes are c…
Browse files Browse the repository at this point in the history
…alled by the listeners
  • Loading branch information
yrutschle committed May 5, 2022
1 parent 78827d7 commit f6fe735
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 58 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ CC ?= gcc
CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)

LIBS=-lm -lpcre2-8
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o
FORK_OBJS=$(OBJS) sslh-fork.o
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o
FORK_OBJS=$(OBJS) sslh-fork.o
SELECT_OBJS=$(OBJS) processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o
EV_OBJS=$(OBJS) processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o

Expand Down
52 changes: 15 additions & 37 deletions probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,30 +340,38 @@ static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item
}

/* Run all the probes on a buffer
* buf, len: buffer to test on
* proto_in, proto_len: array of protocols to try
* proto_out: protocol that matched
*
* Returns
* PROBE_AGAIN if not enough data, and set *proto to NULL
* PROBE_MATCH if protocol is identified, in which case *proto is set to
* point to the appropriate protocol
* */
int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
int probe_buffer(char* buf, int len,
struct sslhcfg_protocols_item** proto_in,
int proto_len,
struct sslhcfg_protocols_item** proto_out
)
{
struct sslhcfg_protocols_item* p;
int i, res, again = 0;

print_message(msg_packets, "hexdump of incoming packet:\n");
hexdump(msg_packets, buf, len);

*proto = NULL;
for (i = 0; i < cfg.protocols_len; i++) {
*proto_out = NULL;
for (i = 0; i < proto_len; i++) {
char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
p = &cfg.protocols[i];
p = proto_in[i];

if (! p->probe) continue;

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")))
if ((i == proto_len - 1) && (!strcmp(p->name, "anyprot")))
break;

if (p->minlength_is_present && (len < p->minlength )) {
Expand All @@ -377,7 +385,7 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]);

if (res == PROBE_MATCH) {
*proto = p;
*proto_out = p;
return PROBE_MATCH;
}
if (res == PROBE_AGAIN)
Expand All @@ -387,37 +395,7 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
return PROBE_AGAIN;

/* Everything failed: match the last one */
*proto = &cfg.protocols[cfg.protocols_len-1];
return PROBE_MATCH;
}

/*
* Read the beginning of data coming from the client connection and check if
* it's a known protocol.
* Return PROBE_AGAIN if not enough data, or PROBE_MATCH if it succeeded in
* which case cnx->proto is set to the appropriate protocol.
*/
int probe_client_protocol(struct connection *cnx)
{
char buffer[BUFSIZ];
ssize_t n;

n = read(cnx->q[0].fd, buffer, sizeof(buffer));
/* It's possible that read() returns an error, e.g. if the client
* disconnected between the previous call to select() and now. If that
* happens, we just connect to the default protocol so the caller of this
* function does not have to deal with a specific failure condition (the
* connection will just fail later normally). */

if (n > 0) {
defer_write(&cnx->q[1], buffer, n);
return probe_buffer(cnx->q[1].begin_deferred_data,
cnx->q[1].deferred_data_size,
&cnx->proto);
}

/* read() returned an error, so just connect to the last protocol to die */
cnx->proto = &cfg.protocols[cfg.protocols_len-1];
*proto_out = proto_in[proto_len-1];
return PROBE_MATCH;
}

Expand Down
8 changes: 6 additions & 2 deletions probe.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ void set_protocol_list(struct sslhcfg_protocols_item*);
*/
int probe_client_protocol(struct connection *cnx);

/* Probe, but on a buffer */
int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto);
/* Probe on a buffer */
int probe_buffer(char* buf, int len,
struct sslhcfg_protocols_item** proto_in,
int proto_len,
struct sslhcfg_protocols_item** proto_out
);

/* set the protocol to connect to in case of timeout */
void set_ontimeout(const char* name);
Expand Down
1 change: 1 addition & 0 deletions sslh-ev.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
ev_info.collection = collection_init(0);
ev_info.probing_list = gap_init(0);
udp_init(&ev_info);
tcp_init();

watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
ev_set_userdata(EV_A_ &ev_info);
Expand Down
4 changes: 3 additions & 1 deletion sslh-fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "common.h"
#include "probe.h"
#include "sslh-conf.h"
#include "udp-listener.h"
#include "tcp-probe.h"
#include "log.h"

#ifdef LIBBSD
Expand Down Expand Up @@ -207,6 +207,8 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
listener_pid = malloc(listener_pid_number * sizeof(listener_pid[0]));
CHECK_ALLOC(listener_pid, "malloc");

tcp_init();

/* Start one process for each listening address */
for (i = 0; i < num_addr_listen; i++) {
listener_pid[i] = fork();
Expand Down
1 change: 1 addition & 0 deletions sslh-select.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
fd_info.num_probing = 0;
fd_info.probing_list = gap_init(0);
udp_init(&fd_info);
tcp_init();

watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);

Expand Down
30 changes: 15 additions & 15 deletions t_load
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,21 @@ my %connect_params = (
test_data => "foo bar",
resp_len => 12,
},
# ssh => {
# sleep => 20, # So it times out 50% of connections
# test_data => "SSH-2.0 hello",
# resp_len => 18, # length "ssh: SSH-2.0 hello" => 18
# },
# tinc => {
# sleep => 0,
# test_data => "0 ",
# resp_len => 8, # length "tinc: 0 " => 10
# },
# openvpn => {
# sleep => 0,
# test_data => "\x00\x00",
# resp_len => 11, # length "openvpn: \x0\x0" => 11
# },
ssh => {
sleep => 20, # So it times out 50% of connections
test_data => "SSH-2.0 hello",
resp_len => 18, # length "ssh: SSH-2.0 hello" => 18
},
tinc => {
sleep => 0,
test_data => "0 ",
resp_len => 8, # length "tinc: 0 " => 10
},
openvpn => {
sleep => 0,
test_data => "\x00\x00",
resp_len => 11, # length "openvpn: \x0\x0" => 11
},
);

sub connect_service {
Expand Down
1 change: 1 addition & 0 deletions tcp-listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "processes.h"
#include "collection.h"
#include "tcp-probe.h"

void tcp_read_process(struct loop_info* fd_info, int fd);
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info);
Expand Down
76 changes: 76 additions & 0 deletions tcp-probe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
# tcp-probe.c: TCP code that is common to the sslh-fork and sslh-[ev|select]
#
# Copyright (C) 2022 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
*/


#include "probe.h"

static struct sslhcfg_protocols_item** tcp_protocols;
static int tcp_protocols_len = 0;

/*
* Read the beginning of data coming from the client connection and check if
* it's a known protocol.
* Return PROBE_AGAIN if not enough data, or PROBE_MATCH if it succeeded in
* which case cnx->proto is set to the appropriate protocol.
*/
int probe_client_protocol(struct connection *cnx)
{
char buffer[BUFSIZ];
ssize_t n;

n = read(cnx->q[0].fd, buffer, sizeof(buffer));
/* It's possible that read() returns an error, e.g. if the client
* disconnected between the previous call to select() and now. If that
* happens, we just connect to the default protocol so the caller of this
* function does not have to deal with a specific failure condition (the
* connection will just fail later normally). */

if (n > 0) {
defer_write(&cnx->q[1], buffer, n);
return probe_buffer(cnx->q[1].begin_deferred_data,
cnx->q[1].deferred_data_size,
tcp_protocols, tcp_protocols_len,
&cnx->proto
);
}

/* read() returned an error, so just connect to the last protocol to die */
cnx->proto = &cfg.protocols[cfg.protocols_len-1];
return PROBE_MATCH;
}


static void tcp_protocol_list_init(void)
{
for (int i = 0; i < cfg.protocols_len; i++) {
struct sslhcfg_protocols_item* p = &cfg.protocols[i];
if (!p->is_udp) {
tcp_protocols_len++;
tcp_protocols = realloc(tcp_protocols, tcp_protocols_len * sizeof(*tcp_protocols));
tcp_protocols[tcp_protocols_len-1] = p;
}
}
}

void tcp_init(void)
{
tcp_protocol_list_init();
}
6 changes: 6 additions & 0 deletions tcp-probe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef TCP_PROBE_H
#define TCP_PROBE_H

void tcp_init(void);

#endif
4 changes: 4 additions & 0 deletions test.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ listen:
{ host: "ip4-localhost"; is_udp: true; port: "8086"; }
);


# Tester beware: when using fork, the forked process loses
# track of buffers of other, concurrent connections. Memory
# leak tools thus complain each time a forked process stops.

protocols:
(
Expand Down
19 changes: 18 additions & 1 deletion udp-listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,30 @@ static int hash_make_key(hash_item new)
return out;
}

static struct sslhcfg_protocols_item** udp_protocols;
static int udp_protocols_len = 0;

static void udp_protocol_list_init(void)
{
for (int i = 0; i < cfg.protocols_len; i++) {
struct sslhcfg_protocols_item* p = &cfg.protocols[i];
if (p->is_udp) {
udp_protocols_len++;
udp_protocols = realloc(udp_protocols, udp_protocols_len * sizeof(*udp_protocols));
udp_protocols[udp_protocols_len-1] = p;
}
}
}

/* Init the UDP subsystem.
* - Initialise the hash
* - that's all, folks
* */
void udp_init(struct loop_info* fd_info)
{
fd_info->hash_sources = hash_init(cfg.udp_max_connections, &hash_make_key, &cnx_cmp);

udp_protocol_list_init();
}


Expand Down Expand Up @@ -215,7 +232,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info)
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));

if (target == -1) {
res = probe_buffer(data, len, &proto);
res = probe_buffer(data, len, udp_protocols, udp_protocols_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 */
Expand Down

0 comments on commit f6fe735

Please sign in to comment.