v1.9: 02AUG2011

WARNING: Options changed, you'll need to update your
	start-up scripts! Log format changed, you'll need to
	update log processing scripts!

	Now supports IPv6 throughout (both on listening and
	forwarding)

	Logs now contain IPv6 addresses, local forwarding
	address, and resolves names (unless --numeric is
	specified).

	Introduced long options.

	Options -l, -s and -o replaced by their long
	counterparts.

	Defaults for SSL and SSH options suppressed (it's
	legitimate to want to use sslh to mux OpenVPN and
	tinc while not caring about SSH nor SSL).

	Bind to multiple addresses with multiple -p options.

	Support for tinc VPN (experimental).

	Numeric logging option.
This commit is contained in:
Yves Rutschle 2013-07-10 23:13:32 +02:00
parent 80f76c6fc5
commit a9c9941988
10 changed files with 315 additions and 171 deletions

View File

@ -1,4 +1,31 @@
v1.8:
v1.9: 02AUG2011
WARNING: Options changed, you'll need to update your
start-up scripts! Log format changed, you'll need to
update log processing scripts!
Now supports IPv6 throughout (both on listening and
forwarding)
Logs now contain IPv6 addresses, local forwarding
address, and resolves names (unless --numeric is
specified).
Introduced long options.
Options -l, -s and -o replaced by their long
counterparts.
Defaults for SSL and SSH options suppressed (it's
legitimate to want to use sslh to mux OpenVPN and
tinc while not caring about SSH nor SSL).
Bind to multiple addresses with multiple -p options.
Support for tinc VPN (experimental).
Numeric logging option.
v1.8: 15JUL2011
Changed log format to make it possible to link
connections to subsequent logs from other services.

View File

@ -1,6 +1,6 @@
# Configuration
VERSION="v1.8"
VERSION="v1.9"
USELIBWRAP= # Use libwrap?
PREFIX=/usr/local
@ -29,11 +29,11 @@ all: sslh $(MAN)
sslh: $(OBJS) sslh-fork sslh-select
sslh-fork: $(OBJS) sslh-fork.o Makefile
sslh-fork: $(OBJS) sslh-fork.o Makefile common.h
$(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh-fork sslh-fork.o $(OBJS) $(LIBS)
strip sslh-fork
sslh-select: $(OBJS) sslh-select.o Makefile
sslh-select: $(OBJS) sslh-select.o Makefile common.h
$(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh-select sslh-select.o $(OBJS) $(LIBS)
strip sslh-select

8
README
View File

@ -104,22 +104,22 @@ In that case, you end up with something like this:
ssh -> proxytunnel -e --------ssh/ssl------> stunnel ---ssh---> sslh --> sshd
navigateur --------http/ssl------> stunnel ---http---> sslh --> http:80
Web browser --------http/ssl------> stunnel ---http---> sslh --> http:80
Configuration goes like this:
On the server side, using stunnel3:
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- sslh -i -l localhost:80 -s localhost:22
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- sslh -i --ssl localhost:80 --ssh localhost:22
stunnel options: -f for foreground/debugging, -p specifies
the key + certificate, -d specifies which interface and port
we're listening to for incoming connexions, -l summons sslh
in inetd mode.
sslh options: -i for inetd mode, -l to forward SSL
sslh options: -i for inetd mode, --ssl to forward SSL
connexions (in fact normal HTTP at that stage) to port 80,
and SSH connexions to port 22. This works because sslh
considers that anything that is not SSH is SSL.
considers that any protocol it doesn't recognise is SSL.
==== IP_TPROXY support ====

296
common.c
View File

@ -21,6 +21,7 @@
#include <pwd.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include "common.h"
@ -32,31 +33,34 @@
int is_ssh_protocol(const char *p, int len);
int is_openvpn_protocol(const char *p, int len);
int is_tinc_protocol(const char *p, int len);
int is_true(const char *p, int len) { return 1; }
struct proto protocols[] = {
/* affected description service saddr probe */
{ 0, "SSH", "sshd", {0}, is_ssh_protocol },
{ 0, "OpenVPN", NULL, {0}, is_openvpn_protocol },
{ 0, "ssh", "sshd", {0}, is_ssh_protocol },
{ 0, "openvpn", NULL, {0}, is_openvpn_protocol },
{ 0, "tinc", NULL, {0}, is_tinc_protocol },
/* probe for SSL always successes: it's the default, and must be tried last
**/
{ 0, "SSL", NULL, {0}, is_true }
{ 0, "ssl", NULL, {0}, is_true }
};
const char* USAGE_STRING =
"sslh " VERSION "\n" \
"usage:\n" \
"\tsslh [-v] [-i] [-V] [-f]"
"[-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
"\t\t-s [sshhost:]port -l [sslhost:]port [-P pidfile]\n\n" \
"\tsslh [-v] [-i] [-V] [-f]\n"
"\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
"\t[--ssh <addr>] [--ssl <addr>] [--openvpn <addr>] [--tinc <addr>]\n\n" \
"-v: verbose\n" \
"-V: version\n" \
"-f: foreground\n" \
"-p: address and port to listen on. default: 0.0.0.0:443\n" \
"-s: SSH address: where to connect an SSH connection. default: localhost:22\n" \
"-l: SSL address: where to connect an SSL connection.\n" \
"-o: OpenVPN address: where to connect an OpenVPN connection.\n" \
"-p: address and port to listen on. default: 0.0.0.0:443.\n Can be used several times to bind to several addresses.\n" \
"--ssh: SSH address: where to connect an SSH connection.\n" \
"--ssl: SSL address: where to connect an SSL connection.\n" \
"--openvpn: OpenVPN address: where to connect an OpenVPN connection.\n" \
"--tinc: tinc address: where to connect a tinc connection.\n" \
"-P: PID file. Default: /var/run/sslh.pid.\n" \
"-i: Run as a inetd service.\n" \
"";
@ -71,38 +75,59 @@ int verbose = 0;
int probing_timeout = 2;
int inetd = 0;
int foreground = 0;
struct sockaddr addr_listen;
int numeric = 0;
char *user_name, *pid_file;
struct sockaddr_storage *addr_listen = NULL; /* what addresses do we listen to? */
int num_addr_listen = 0; /* How many addresses do we listen to? */
#ifdef LIBWRAP
#include <tcpd.h>
int allow_severity =0, deny_severity = 0;
#endif
/* check result and die, printing the offending address and error */
void check_res_dumpdie(int res, struct sockaddr_storage *sock, char* syscall)
{
char buf[64];
/* Starts a listening socket on specified address.
if (res == -1) {
fprintf(stderr, "%s:%s: %s\n",
sprintaddr(buf, sizeof(buf), sock),
syscall,
strerror(errno));
exit(1);
}
}
/* Starts listening sockets on specified addresses.
* IN: addr[], num_addr
* OUT: sockfd[]
* Bound file descriptors are returned in alread-allocated *sockfd pointer
Returns file descriptor
*/
int start_listen_socket(struct sockaddr *addr)
void start_listen_sockets(int sockfd[], struct sockaddr_storage addr[], int num_addr)
{
struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
int sockfd, res, reuse;
struct sockaddr_storage *saddr;
int i, res, reuse;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
CHECK_RES_DIE(sockfd, "socket");
for (i = 0; i < num_addr; i++) {
saddr = &addr[i];
reuse = 1;
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
CHECK_RES_DIE(res, "setsockopt");
sockfd[i] = socket(saddr->ss_family, SOCK_STREAM, 0);
check_res_dumpdie(sockfd[i], saddr, "socket");
res = bind (sockfd, (struct sockaddr*)saddr, sizeof(*saddr));
CHECK_RES_DIE(res, "bind");
reuse = 1;
res = setsockopt(sockfd[i], SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
check_res_dumpdie(res, saddr, "setsockopt");
res = listen (sockfd, 50);
CHECK_RES_DIE(res, "listen");
res = bind (sockfd[i], (struct sockaddr*)saddr, sizeof(*saddr));
check_res_dumpdie(res, saddr, "bind");
return sockfd;
res = listen (sockfd[i], 50);
check_res_dumpdie(res, saddr, "listen");
}
}
/* Store some data to write to the queue later */
@ -260,6 +285,13 @@ int is_openvpn_protocol (const char*p,int len)
return 0;
}
/* Is the buffer the beginning of a tinc connections?
* (protocol is undocumented, but starts with "0 " in 1.0.15)
* */
int is_tinc_protocol( const char *p, int len)
{
return !strncmp(p, "0 ", len);
}
/*
* Read the beginning of data coming from the client connection and check if
@ -290,17 +322,26 @@ T_PROTO_ID probe_client_protocol(struct connection *cnx)
}
}
/* If none worked, return the last one */
return ARRAY_SIZE(protocols) - 1;
/* If none worked, return the first one affected (that's completely
* arbitrary) */
for (i = 0; i < ARRAY_SIZE(protocols); i++)
if (protocols[i].affected)
return i;
/* At this stage... nothing is affected. This shouldn't happen as we check
* at least one target exists when we parse the commnand line */
fprintf(stderr, "FATAL: No protocol affected. This should not happen.\n");
exit(1);
}
/* returns a string that prints the IP and port of the sockaddr */
char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
char* sprintaddr(char* buf, size_t size, struct sockaddr_storage* s)
{
char addr_str[1024];
char host[NI_MAXHOST], serv[NI_MAXSERV];
getnameinfo((struct sockaddr*)s, sizeof(*s), host, sizeof(host), serv, sizeof(serv), numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
snprintf(buf, size, "%s:%s", host, serv);
inet_ntop(AF_INET, &((struct sockaddr_in*)s)->sin_addr, addr_str, sizeof(addr_str));
snprintf(buf, size, "%s:%d", addr_str, ntohs(((struct sockaddr_in*)s)->sin_port));
return buf;
}
@ -308,28 +349,26 @@ char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
sock: socket address to which to copy the addr
fullname: input string -- it gets clobbered
*/
void resolve_name(struct sockaddr *sock, char* fullname)
void resolve_name(struct sockaddr_storage *sock, char* fullname)
{
struct addrinfo *addr, hint;
char *serv, *host;
int res;
char *sep = strchr(fullname, ':');
char *sep = strrchr(fullname, ':');
if (!sep) /* No separator: parameter is just a port */
{
serv = fullname;
fprintf(stderr, "names must be fully specified as hostname:port\n");
exit(1);
}
else {
host = fullname;
serv = sep+1;
*sep = 0;
}
host = fullname;
serv = sep+1;
*sep = 0;
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_INET;
hint.ai_family = PF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
res = getaddrinfo(host, serv, &hint, &addr);
@ -363,19 +402,40 @@ void log_message(int type, char* msg, ...)
/* syslogs who connected to where */
void log_connection(struct connection *cnx)
{
struct sockaddr peeraddr, localaddr;
struct sockaddr_storage peeraddr; /* Who's connecting to sshd */
struct sockaddr_storage listenaddr; /* Where is it connecting to */
struct sockaddr_storage forwardfromaddr; /* Where is it forwarded from */
struct sockaddr_storage targetaddr; /* Where is it forwarded to */
socklen_t size = sizeof(peeraddr);
char buf[64], buf2[64];
#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
char buf[MAX_NAMELENGTH], buf2[MAX_NAMELENGTH], buf3[MAX_NAMELENGTH], buf4[MAX_NAMELENGTH];
int res;
res = getpeername(cnx->q[0].fd, &peeraddr, &size);
memset(&peeraddr, 0, sizeof(peeraddr));
memset(&listenaddr, 0, sizeof(listenaddr));
memset(&forwardfromaddr, 0, sizeof(forwardfromaddr));
memset(&targetaddr, 0, sizeof(targetaddr));
res = getpeername(cnx->q[0].fd, (struct sockaddr*)&peeraddr, &size);
if (res == -1) return; /* that should never happen, right? */
res = getpeername(cnx->q[1].fd, &localaddr, &size);
if (res == -1) return; /* that should never happen, right? */
size = sizeof(listenaddr);
res = getsockname(cnx->q[0].fd, (struct sockaddr*)&listenaddr, &size);
if (res == -1) return;
log_message(LOG_INFO, "connection from %s forwarded to %s\n",
sprintaddr(buf, sizeof(buf), &peeraddr), sprintaddr(buf2, sizeof(buf2), &localaddr));
size = sizeof(targetaddr);
res = getpeername(cnx->q[1].fd, (struct sockaddr*)&targetaddr, &size);
if (res == -1) return;
size = sizeof(forwardfromaddr);
res = getsockname(cnx->q[1].fd, (struct sockaddr*)&forwardfromaddr, &size);
if (res == -1) return;
log_message(LOG_INFO, "connection from %s to %s forwarded from %s to %s\n",
sprintaddr(buf, sizeof(buf), &peeraddr),
sprintaddr(buf2, sizeof(buf2), &listenaddr),
sprintaddr(buf3, sizeof(buf3), &forwardfromaddr),
sprintaddr(buf4, sizeof(buf4), &targetaddr));
}
@ -386,27 +446,38 @@ void log_connection(struct connection *cnx)
*
* Returns -1 if access is denied, 0 otherwise
*/
int check_access_rights(int in_socket, const char* service)
int check_access_rights(int in_socket, char* service)
{
#ifdef LIBWRAP
struct sockaddr peeraddr;
socklen_t size = sizeof(peeraddr);
char addr_str[1024];
struct hostent *host;
struct in_addr addr;
char addr_str[NI_MAXHOST], host[NI_MAXHOST];
int res;
res = getpeername(in_socket, &peeraddr, &size);
CHECK_RES_DIE(res, "getpeername");
inet_ntop(AF_INET, &((struct sockaddr_in*)&peeraddr)->sin_addr, addr_str, sizeof(addr_str));
addr.s_addr = inet_addr(addr_str);
host = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
/* extract peer address */
res = getnameinfo(&peeraddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
if (res) {
if (verbose)
fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
strcpy(addr_str, STRING_UNKNOWN);
}
/* extract peer name */
strcpy(host, STRING_UNKNOWN);
if (!numeric) {
res = getnameinfo(&peeraddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
if (res) {
if (verbose)
fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
}
}
if (!hosts_ctl(service, (host ? host->h_name : STRING_UNKNOWN), addr_str, STRING_UNKNOWN)) {
if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
if (verbose)
fprintf(stderr, "access denied\n");
log_connection(in_socket, "access denied");
log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
close(in_socket);
return -1;
}
@ -489,14 +560,62 @@ void printsettings(void)
sprintaddr(buf, sizeof(buf), &protocols[i].saddr),
protocols[i].service);
}
fprintf(stderr, "listening on %s\n", sprintaddr(buf, sizeof(buf), &addr_listen));
fprintf(stderr, "listening on:\n");
for (i = 0; i < num_addr_listen; i++) {
fprintf(stderr, "\t%s\n", sprintaddr(buf, sizeof(buf), &addr_listen[i]));
}
fprintf(stderr, "timeout to ssh: %d\n", probing_timeout);
}
/* Adds protocols to the list of options, so command-line parsing uses the
* protocol definition array
* options: array of options to add to; must be big enough
* n_opts: number of options in *options before calling (i.e. where to append)
* prot: array of protocols
* n_prots: number of protocols in *prot
* */
#define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */
void append_protocols(struct option *options, int n_opts, struct proto *prot, int n_prots)
{
int o, p;
for (o = n_opts, p = 0; p < n_prots; o++, p++) {
options[o].name = prot[p].description;
options[o].has_arg = required_argument;
options[o].flag = 0;
options[o].val = p + PROT_SHIFT;
}
}
void parse_cmdline(int argc, char* argv[])
{
int c;
int c, affected = 0;
struct option const_options[] = {
{ "inetd", no_argument, &inetd, 1 },
{ "foreground", no_argument, &foreground, 1 },
{ "verbose", no_argument, &verbose, 1 },
{ "numeric", no_argument, &numeric, 1 },
{ "user", required_argument, 0, 'u' },
{ "pidfile", required_argument, 0, 'P' },
{ "timeout", required_argument, 0, 't' },
{ "listen", required_argument, 0, 'p' },
};
struct option all_options[ARRAY_SIZE(const_options) + ARRAY_SIZE(protocols) + 1];
memset(all_options, 0, sizeof(all_options));
memcpy(all_options, const_options, sizeof(const_options));
append_protocols(all_options, ARRAY_SIZE(const_options), protocols, ARRAY_SIZE(protocols));
while ((c = getopt_long_only(argc, argv, "t:l:s:o:T:p:VP:", all_options, NULL)) != -1) {
if (c == 0) continue;
if (c >= PROT_SHIFT) {
affected++;
protocols[c - PROT_SHIFT].affected = 1;
resolve_name(&protocols[c - PROT_SHIFT].saddr, optarg);
continue;
}
while ((c = getopt(argc, argv, "t:l:s:o:p:P:ivfVu:")) != EOF) {
switch (c) {
case 't':
@ -504,34 +623,9 @@ void parse_cmdline(int argc, char* argv[])
break;
case 'p':
resolve_name(&addr_listen, optarg);
break;
case 'l':
protocols[PROT_SSL].affected = 1;
resolve_name(&protocols[PROT_SSL].saddr, optarg);
break;
case 's':
protocols[PROT_SSH].affected = 1;
resolve_name(&protocols[PROT_SSH].saddr, optarg);
break;
case 'o':
protocols[PROT_OPENVPN].affected = 1;
resolve_name(&protocols[PROT_OPENVPN].saddr, optarg);
break;
case 'i':
inetd = 1;
break;
case 'f':
foreground = 1;
break;
case 'v':
verbose += 1;
num_addr_listen++;
addr_listen = realloc(addr_listen, num_addr_listen * sizeof(addr_listen[0]));
resolve_name(&addr_listen[num_addr_listen - 1], optarg);
break;
case 'V':
@ -551,6 +645,17 @@ void parse_cmdline(int argc, char* argv[])
exit(2);
}
}
if (!affected) {
fprintf(stderr, "At least one target protocol must be specified.\n");
exit(2);
}
if (!num_addr_listen) {
fprintf(stderr, "No listening address specified; use at least one -p option\n");
exit(1);
}
}
int main(int argc, char *argv[])
@ -560,22 +665,13 @@ int main(int argc, char *argv[])
extern int optind;
int res;
int listen_socket;
int *listen_sockets;
/* Init defaults */
char listen_str[] = "0.0.0.0:443";
char ssl_str[] = "localhost:443";
char ssh_str[] = "localhost:22";
pid_file = "/var/run/sslh.pid";
user_name = "nobody";
foreground = 0;
resolve_name(&addr_listen, listen_str);
protocols[PROT_SSL].affected = 1;
resolve_name(&protocols[PROT_SSL].saddr, ssl_str);
protocols[PROT_SSH].affected = 1;
resolve_name(&protocols[PROT_SSH].saddr, ssh_str);
parse_cmdline(argc, argv);
if (inetd)
@ -588,7 +684,9 @@ int main(int argc, char *argv[])
if (verbose)
printsettings();
listen_socket = start_listen_socket(&addr_listen);
listen_sockets = malloc(num_addr_listen * sizeof(*listen_sockets));
start_listen_sockets(listen_sockets, addr_listen, num_addr_listen);
free(addr_listen);
if (!foreground)
if (fork() > 0) exit(0); /* Detach */
@ -608,7 +706,7 @@ int main(int argc, char *argv[])
/* Open syslog connection */
setup_syslog(argv[0]);
main_loop(listen_socket);
main_loop(listen_sockets, num_addr_listen);
return 0;
}

View File

@ -52,15 +52,16 @@ enum connection_state {
typedef enum protocol_type {
PROT_SSH,
PROT_OPENVPN,
PROT_TINC,
PROT_SSL,
} T_PROTO_ID;
/* For each protocol we need: */
struct proto {
int affected; /* are we actually using it? */
char* description; /* a string that says what it is (for logging) */
char* description; /* a string that says what it is (for logging and command-line parsing) */
char* service; /* service name to do libwrap checks */
struct sockaddr saddr; /* where to switch that protocol */
struct sockaddr_storage saddr; /* where to switch that protocol */
int (*probe)(const char*, int); /* function to probe that protocol */
};
@ -93,13 +94,13 @@ struct connection {
/* common.c */
void init_cnx(struct connection *cnx);
int start_listen_socket(struct sockaddr *addr);
void start_listen_sockets(int sockfd[], struct sockaddr_storage addr[], int num_addr);
int fd2fd(struct queue *target, struct queue *from);
char* sprintaddr(char* buf, size_t size, struct sockaddr_storage* s);
void resolve_name(struct sockaddr_storage *sock, char* fullname) ;
T_PROTO_ID probe_client_protocol(struct connection *cnx);
char* sprintaddr(char* buf, size_t size, struct sockaddr* s);
void resolve_name(struct sockaddr *sock, char* fullname) ;
void log_connection(struct connection *cnx);
int check_access_rights(int in_socket, const char* service);
int check_access_rights(int in_socket, char* service);
void setup_signals(void);
void setup_syslog(char* bin_name);
void drop_privileges(char* user_name);
@ -114,7 +115,7 @@ int defer_write(struct queue *q, void* data, int data_size);
int flush_defered(struct queue *q);
extern int probing_timeout, verbose, inetd;
extern struct sockaddr addr_listen, addr_ssl, addr_ssh, addr_openvpn;
extern struct sockaddr_storage *addr_listen, addr_ssl, addr_ssh, addr_openvpn;
extern const char* USAGE_STRING;
extern char* user_name, *pid_file;
extern const char* server_type;
@ -122,6 +123,6 @@ extern const char* server_type;
/* sslh-fork.c */
void start_shoveler(int);
void main_loop(int);
void main_loop(int *listen_sockets, int num_addr_listen);

View File

@ -30,7 +30,7 @@ DAEMON=$PREFIX/sbin/sslh
start()
{
echo "Start services: sslh"
$DAEMON -u nobody -p ${LISTEN} -s ${SSH} -l ${SSL}
$DAEMON --user nobody --listen ${LISTEN} --ssh ${SSH} --ssl ${SSL}
logger -t ${tag} -p ${facility} -i 'Started sslh'
}

View File

@ -20,7 +20,7 @@
SSLH="/usr/local/sbin/sslh"
PIDFILE="/var/run/sslh"
OPTIONS="-p 0.0.0.0:8443 -l 127.0.0.1:443 -s 127.0.0.1:22"
OPTIONS="-p 0.0.0.0:8443 --ssl 127.0.0.1:443 --ssh 127.0.0.1:22"
if [ -f /etc/sysconfig/sslh ]; then
. /etc/sysconfig/sslh

View File

@ -68,7 +68,7 @@ void start_shoveler(int in_socket)
{
fd_set fds;
struct timeval tv;
struct sockaddr *saddr;
struct sockaddr_storage *saddr;
int res;
int out_socket;
char *target;
@ -103,8 +103,8 @@ void start_shoveler(int in_socket)
}
/* Connect the target socket */
out_socket = socket(AF_INET, SOCK_STREAM, 0);
res = connect(out_socket, saddr, sizeof(addr_ssl));
out_socket = socket(saddr->ss_family, SOCK_STREAM, 0);
res = connect(out_socket, (struct sockaddr*)saddr, sizeof(addr_ssl));
CHECK_RES_DIE(res, "connect");
if (verbose)
fprintf(stderr, "connected to something\n");
@ -126,23 +126,27 @@ void start_shoveler(int in_socket)
exit(0);
}
void main_loop(int listen_socket)
void main_loop(int *listen_sockets, int num_addr_listen)
{
int in_socket;
int in_socket, i;
while (1)
{
in_socket = accept(listen_socket, 0, 0);
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
for (i = 0; i < num_addr_listen; i++) {
if (!fork()) {
while (1)
{
in_socket = accept(listen_sockets[i], 0, 0);
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
if (!fork())
{
close(listen_socket);
start_shoveler(in_socket);
exit(0);
}
close(in_socket);
}
if (!fork())
{
close(listen_sockets[i]);
start_shoveler(in_socket);
exit(0);
}
close(in_socket);
}
}
}
}
/* The actual main is in common.c: it's the same for both version of

View File

@ -111,15 +111,15 @@ int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_
}
/* Connect queue 1 of connection to SSL; returns new file descriptor */
int connect_queue(struct connection *cnx, struct sockaddr *addr,
int connect_queue(struct connection *cnx, struct sockaddr_storage *addr,
char* cnx_name,
fd_set *fds_r, fd_set *fds_w)
{
struct queue *q = &cnx->q[1];
int res;
q->fd = socket(AF_INET, SOCK_STREAM, 0);
res = connect(q->fd, addr, sizeof(*addr));
q->fd = socket(addr->ss_family, SOCK_STREAM, 0);
res = connect(q->fd, (struct sockaddr*)addr, sizeof(*addr));
log_connection(cnx);
if (res == -1) {
tidy_connection(cnx, fds_r, fds_w);
@ -188,7 +188,7 @@ int is_fd_active(int fd, fd_set* set)
* That way, each pair of file descriptor (read from one, write to the other)
* is monitored either for read or for write, but never for both.
*/
void main_loop(int listen_socket)
void main_loop(int *listen_sockets, int num_addr_listen)
{
fd_set fds_r, fds_w; /* reference fd sets (used to init the next 2) */
fd_set readfds, writefds; /* working read and write fd sets */
@ -203,10 +203,12 @@ void main_loop(int listen_socket)
FD_ZERO(&fds_r);
FD_ZERO(&fds_w);
FD_SET(listen_socket, &fds_r);
max_fd = listen_socket + 1;
set_nonblock(listen_socket);
for (i = 0; i < num_addr_listen; i++) {
FD_SET(listen_sockets[i], &fds_r);
set_nonblock(listen_sockets[i]);
}
max_fd = listen_sockets[num_addr_listen-1] + 1;
cnx_num_alloc = getpagesize() / sizeof(struct connection);
@ -231,16 +233,18 @@ void main_loop(int listen_socket)
/* Check main socket for new connections */
if (FD_ISSET(listen_socket, &readfds)) {
in_socket = accept_new_connection(listen_socket, &cnx, &num_cnx);
num_probing++;
for (i = 0; i < num_addr_listen; i++) {
if (FD_ISSET(listen_sockets[i], &readfds)) {
in_socket = accept_new_connection(listen_sockets[i], &cnx, &num_cnx);
num_probing++;
if (in_socket > 0) {
FD_SET(in_socket, &fds_r);
if (in_socket >= max_fd)
max_fd = in_socket + 1;;
if (in_socket > 0) {
FD_SET(in_socket, &fds_r);
if (in_socket >= max_fd)
max_fd = in_socket + 1;;
}
FD_CLR(listen_sockets[i], &readfds);
}
FD_CLR(listen_socket, &readfds);
}
/* Check all sockets for write activity */

View File

@ -6,7 +6,7 @@
=head1 SYNOPSIS
sslh [ B<-t> I<num> ] [B<-p> I<listening address>] [B<-l> I<target address for SSL>] [B<-s> I<target address for SSH>] [B<-o> I<target address for OpenVPN>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f]
sslh [ B<-t> I<num> ] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<-l> I<target address for SSL>] [B<-s> I<target address for SSH>] [B<-o> I<target address for OpenVPN>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
=head1 DESCRIPTION
@ -57,69 +57,79 @@ defined in F</etc/hosts.allow> and F</etc/hosts.deny>.
=over 4
=item B<-t> I<num>
=item B<-t> I<num>, B<--timeout> I<num>
Timeout before a connection is considered to be SSH. Default
is 2s.
=item B<-p> I<listening address>
=item B<-p> I<listening address>, B<--listen> I<listening address>
Interface and port on which to listen, e.g. I<foobar:443>,
where I<foobar> is the name of an interface (typically the
IP address on which the Internet connection ends up).
Defaults to I<0.0.0.0:443> (listen to port 443 on all
available interfaces).
This can be specified several times to bind B<sslh> to
several addresses.
=item B<-l> I<target address for SSL>
=item B<--ssl> I<target address>
Interface and port on which to forward SSL connection,
typically I<localhost:443>.
Defaults to I<localhost:443> (this assumes you would
configure your B<httpd> process to listen to port 443).
Note that you can set B<sslh> to listen on I<ext_ip:443> and
B<httpd> to listen on I<localhost:443>: this allows clients
inside your network to just connect directly to B<httpd>.
=item B<-s> I<target address for SSH>
=item B<--ssh> I<target address>
Interface and port on which to forward SSH connection,
defaults to I<localhost:22>.
Interface and port on which to forward SSH connections,
typically I<localhost:22>.
=item B<-o> I<target address for OpenVPN>
=item B<--openvpn> I<target address>
Interface and port on which to forward OpenVPN connections.
This parameter is optional, and has no default. If not
specified, incoming OpenVPN connections will not be detected
as such and treated the same as SSL.
Interface and port on which to forward OpenVPN connections,
typically I<localhost:1194>.
=item B<-v>
=item B<--tinc> I<target address>
Interface and port on which to forward tinc connections,
typically I<localhost:655>.
This is experimental. If you use this feature, please report
the results (even if it works!)
=item B<-v>, B<--verbose>
Increase verboseness.
=item B<-n>, B<--numeric>
Do not attempt to resolve hostnames: logs will contain IP
addresses. This is mostly useful if the system's DNS is slow
and running the I<sslh-select> variant, as DNS requests will
hang all connections.
=item B<-V>
Prints B<sslh> version.
=item B<-u> I<username>
=item B<-u> I<username>, B<--user> I<username>
Requires to run under the specified username. Defaults to
I<nobody> (which is not perfect -- ideally B<sslh> should
run under its own UID).
=item B<-P> I<pidfile>
=item B<-P> I<pidfile>, B<--pid-file> I<pidfile>
Specifies the file in which to write the PID of the main
server. Defaults to I</var/run/sslh.pid>.
=item B<-i>
=item B<-i>, B<--inetd>
Runs as an I<inetd> server. Options B<-P> (PID file), B<-p>
(listen address), B<-u> (user) are ignored.
=item B<-f>
=item B<-f>, B<--foreground>
Runs in foreground. The server will not fork and will remain connected
to the terminal. Messages normally sent to B<syslog> will also be sent