mirror of
https://github.com/yrutschle/sslh.git
synced 2025-04-12 15:17:14 +03:00
sort target protocols as TCP or UDP, so only appropriate probes are called by the listeners
This commit is contained in:
parent
78827d75fe
commit
f6fe735171
4
Makefile
4
Makefile
@ -32,8 +32,8 @@ CC ?= gcc
|
|||||||
CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
|
CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
|
||||||
|
|
||||||
LIBS=-lm -lpcre2-8
|
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
|
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
|
FORK_OBJS=$(OBJS) sslh-fork.o
|
||||||
SELECT_OBJS=$(OBJS) processes.o udp-listener.o sslh-select.o hash.o tcp-listener.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
|
EV_OBJS=$(OBJS) processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o
|
||||||
|
|
||||||
|
52
probe.c
52
probe.c
@ -340,12 +340,20 @@ static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Run all the probes on a buffer
|
/* 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
|
* Returns
|
||||||
* PROBE_AGAIN if not enough data, and set *proto to NULL
|
* PROBE_AGAIN if not enough data, and set *proto to NULL
|
||||||
* PROBE_MATCH if protocol is identified, in which case *proto is set to
|
* PROBE_MATCH if protocol is identified, in which case *proto is set to
|
||||||
* point to the appropriate protocol
|
* 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;
|
struct sslhcfg_protocols_item* p;
|
||||||
int i, res, again = 0;
|
int i, res, again = 0;
|
||||||
@ -353,17 +361,17 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
|
|||||||
print_message(msg_packets, "hexdump of incoming packet:\n");
|
print_message(msg_packets, "hexdump of incoming packet:\n");
|
||||||
hexdump(msg_packets, buf, len);
|
hexdump(msg_packets, buf, len);
|
||||||
|
|
||||||
*proto = NULL;
|
*proto_out = NULL;
|
||||||
for (i = 0; i < cfg.protocols_len; i++) {
|
for (i = 0; i < proto_len; i++) {
|
||||||
char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
|
char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
|
||||||
p = &cfg.protocols[i];
|
p = proto_in[i];
|
||||||
|
|
||||||
if (! p->probe) continue;
|
if (! p->probe) continue;
|
||||||
|
|
||||||
print_message(msg_probe_info, "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) */
|
/* 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;
|
break;
|
||||||
|
|
||||||
if (p->minlength_is_present && (len < p->minlength )) {
|
if (p->minlength_is_present && (len < p->minlength )) {
|
||||||
@ -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]);
|
print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]);
|
||||||
|
|
||||||
if (res == PROBE_MATCH) {
|
if (res == PROBE_MATCH) {
|
||||||
*proto = p;
|
*proto_out = p;
|
||||||
return PROBE_MATCH;
|
return PROBE_MATCH;
|
||||||
}
|
}
|
||||||
if (res == PROBE_AGAIN)
|
if (res == PROBE_AGAIN)
|
||||||
@ -387,37 +395,7 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto)
|
|||||||
return PROBE_AGAIN;
|
return PROBE_AGAIN;
|
||||||
|
|
||||||
/* Everything failed: match the last one */
|
/* Everything failed: match the last one */
|
||||||
*proto = &cfg.protocols[cfg.protocols_len-1];
|
*proto_out = proto_in[proto_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];
|
|
||||||
return PROBE_MATCH;
|
return PROBE_MATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
probe.h
8
probe.h
@ -48,8 +48,12 @@ void set_protocol_list(struct sslhcfg_protocols_item*);
|
|||||||
*/
|
*/
|
||||||
int probe_client_protocol(struct connection *cnx);
|
int probe_client_protocol(struct connection *cnx);
|
||||||
|
|
||||||
/* Probe, but on a buffer */
|
/* Probe on a buffer */
|
||||||
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
|
||||||
|
);
|
||||||
|
|
||||||
/* set the protocol to connect to in case of timeout */
|
/* set the protocol to connect to in case of timeout */
|
||||||
void set_ontimeout(const char* name);
|
void set_ontimeout(const char* name);
|
||||||
|
@ -143,6 +143,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
|||||||
ev_info.collection = collection_init(0);
|
ev_info.collection = collection_init(0);
|
||||||
ev_info.probing_list = gap_init(0);
|
ev_info.probing_list = gap_init(0);
|
||||||
udp_init(&ev_info);
|
udp_init(&ev_info);
|
||||||
|
tcp_init();
|
||||||
|
|
||||||
watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
|
watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
|
||||||
ev_set_userdata(EV_A_ &ev_info);
|
ev_set_userdata(EV_A_ &ev_info);
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "probe.h"
|
#include "probe.h"
|
||||||
#include "sslh-conf.h"
|
#include "sslh-conf.h"
|
||||||
#include "udp-listener.h"
|
#include "tcp-probe.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#ifdef LIBBSD
|
#ifdef LIBBSD
|
||||||
@ -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]));
|
listener_pid = malloc(listener_pid_number * sizeof(listener_pid[0]));
|
||||||
CHECK_ALLOC(listener_pid, "malloc");
|
CHECK_ALLOC(listener_pid, "malloc");
|
||||||
|
|
||||||
|
tcp_init();
|
||||||
|
|
||||||
/* Start one process for each listening address */
|
/* Start one process for each listening address */
|
||||||
for (i = 0; i < num_addr_listen; i++) {
|
for (i = 0; i < num_addr_listen; i++) {
|
||||||
listener_pid[i] = fork();
|
listener_pid[i] = fork();
|
||||||
|
@ -131,6 +131,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
|||||||
fd_info.num_probing = 0;
|
fd_info.num_probing = 0;
|
||||||
fd_info.probing_list = gap_init(0);
|
fd_info.probing_list = gap_init(0);
|
||||||
udp_init(&fd_info);
|
udp_init(&fd_info);
|
||||||
|
tcp_init();
|
||||||
|
|
||||||
watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);
|
watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);
|
||||||
|
|
||||||
|
30
t_load
30
t_load
@ -65,21 +65,21 @@ my %connect_params = (
|
|||||||
test_data => "foo bar",
|
test_data => "foo bar",
|
||||||
resp_len => 12,
|
resp_len => 12,
|
||||||
},
|
},
|
||||||
# ssh => {
|
ssh => {
|
||||||
# sleep => 20, # So it times out 50% of connections
|
sleep => 20, # So it times out 50% of connections
|
||||||
# test_data => "SSH-2.0 hello",
|
test_data => "SSH-2.0 hello",
|
||||||
# resp_len => 18, # length "ssh: SSH-2.0 hello" => 18
|
resp_len => 18, # length "ssh: SSH-2.0 hello" => 18
|
||||||
# },
|
},
|
||||||
# tinc => {
|
tinc => {
|
||||||
# sleep => 0,
|
sleep => 0,
|
||||||
# test_data => "0 ",
|
test_data => "0 ",
|
||||||
# resp_len => 8, # length "tinc: 0 " => 10
|
resp_len => 8, # length "tinc: 0 " => 10
|
||||||
# },
|
},
|
||||||
# openvpn => {
|
openvpn => {
|
||||||
# sleep => 0,
|
sleep => 0,
|
||||||
# test_data => "\x00\x00",
|
test_data => "\x00\x00",
|
||||||
# resp_len => 11, # length "openvpn: \x0\x0" => 11
|
resp_len => 11, # length "openvpn: \x0\x0" => 11
|
||||||
# },
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
sub connect_service {
|
sub connect_service {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "processes.h"
|
#include "processes.h"
|
||||||
#include "collection.h"
|
#include "collection.h"
|
||||||
|
#include "tcp-probe.h"
|
||||||
|
|
||||||
void tcp_read_process(struct loop_info* fd_info, int fd);
|
void tcp_read_process(struct loop_info* fd_info, int fd);
|
||||||
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info);
|
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info);
|
||||||
|
76
tcp-probe.c
Normal file
76
tcp-probe.c
Normal file
@ -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
tcp-probe.h
Normal file
6
tcp-probe.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TCP_PROBE_H
|
||||||
|
#define TCP_PROBE_H
|
||||||
|
|
||||||
|
void tcp_init(void);
|
||||||
|
|
||||||
|
#endif
|
4
test.cfg
4
test.cfg
@ -35,6 +35,10 @@ listen:
|
|||||||
{ host: "ip4-localhost"; is_udp: true; port: "8086"; }
|
{ 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:
|
protocols:
|
||||||
(
|
(
|
||||||
|
@ -76,6 +76,21 @@ static int hash_make_key(hash_item new)
|
|||||||
return out;
|
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.
|
/* Init the UDP subsystem.
|
||||||
* - Initialise the hash
|
* - Initialise the hash
|
||||||
* - that's all, folks
|
* - that's all, folks
|
||||||
@ -83,6 +98,8 @@ static int hash_make_key(hash_item new)
|
|||||||
void udp_init(struct loop_info* fd_info)
|
void udp_init(struct loop_info* fd_info)
|
||||||
{
|
{
|
||||||
fd_info->hash_sources = hash_init(cfg.udp_max_connections, &hash_make_key, &cnx_cmp);
|
fd_info->hash_sources = hash_init(cfg.udp_max_connections, &hash_make_key, &cnx_cmp);
|
||||||
|
|
||||||
|
udp_protocol_list_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -215,7 +232,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
|||||||
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
|
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
|
||||||
|
|
||||||
if (target == -1) {
|
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
|
/* First version: if we can't work out the protocol from the first
|
||||||
* packet, drop it. Conceivably, we could store several packets to
|
* packet, drop it. Conceivably, we could store several packets to
|
||||||
* run probes on packet sets */
|
* run probes on packet sets */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user