mirror of https://github.com/moparisthebest/sslh
Browse Source
Fixed calls referring to sockaddr length so they work with FreeBSD. Try target addresses in turn until one works if there are several (e.g. "localhost:22" resolves to an IPv6 address and an IPv4 address and sshd does not listen on IPv6). Fixed sslh-fork so killing the head process kills the listener processes. Heavily cleaned up test suite. Added stress test t_load script. Added coverage (requires lcov). Support for XMPP (Arnaud Gendre). Updated README.MacOSX (Aaron Madlon-Kay).master v1.10

13 changed files with 1035 additions and 473 deletions
@ -0,0 +1,166 @@
@@ -0,0 +1,166 @@
|
||||
/* echosrv: a simple line echo server with optional prefix adding.
|
||||
* |
||||
* echsrv --listen localhost6:1234 --prefix "ssl: " |
||||
* |
||||
* This will bind to 1234, and echo every line pre-pending "ssl: ". This is |
||||
* used for testing: we create several such servers with different prefixes, |
||||
* then we connect test clients that can then check they get the proper data |
||||
* back (thus testing that shoveling works both ways) with the correct prefix |
||||
* (thus testing it connected to the expected service). |
||||
* **/ |
||||
|
||||
#define _GNU_SOURCE |
||||
#include <sys/types.h> |
||||
#include <fcntl.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <stdio.h> |
||||
#include <signal.h> |
||||
#include <sys/socket.h> |
||||
#include <sys/wait.h> |
||||
#include <netinet/in.h> |
||||
#include <arpa/inet.h> |
||||
#include <netdb.h> |
||||
#include <pwd.h> |
||||
#include <syslog.h> |
||||
#include <libgen.h> |
||||
#include <getopt.h> |
||||
|
||||
#include "common.h" |
||||
|
||||
/* Added to make the code compilable under CYGWIN
|
||||
* */ |
||||
#ifndef SA_NOCLDWAIT |
||||
#define SA_NOCLDWAIT 0 |
||||
#endif |
||||
|
||||
const char* USAGE_STRING = |
||||
"echosrv\n" \
|
||||
"usage:\n" \
|
||||
"\techosrv [-v] --listen <address:port> [--prefix <prefix>]\n" |
||||
"-v: verbose\n" \
|
||||
"--listen: address to listen on. Can be specified multiple times.\n" \
|
||||
"--prefix: add specified prefix before every line echoed.\n" |
||||
""; |
||||
|
||||
const char* server_type = "echsrv"; /* keep setup_syslog happy */ |
||||
|
||||
/*
|
||||
* Settings that depend on the command line.
|
||||
*/ |
||||
char* prefix = ""; |
||||
int port; |
||||
|
||||
void parse_cmdline(int argc, char* argv[]) |
||||
{ |
||||
int c; |
||||
struct option options[] = { |
||||
{ "verbose", no_argument, &verbose, 1 }, |
||||
{ "numeric", no_argument, &numeric, 1 }, |
||||
{ "listen", required_argument, 0, 'l' }, |
||||
{ "prefix", required_argument, 0, 'p' }, |
||||
}; |
||||
struct addrinfo **a; |
||||
|
||||
while ((c = getopt_long_only(argc, argv, "l:p:", options, NULL)) != -1) { |
||||
if (c == 0) continue; |
||||
|
||||
switch (c) { |
||||
|
||||
case 'l': |
||||
/* find the end of the listen list */ |
||||
for (a = &addr_listen; *a; a = &((*a)->ai_next)); |
||||
/* append the specified addresses */ |
||||
resolve_name(a, optarg); |
||||
break; |
||||
|
||||
case 'p': |
||||
prefix = optarg; |
||||
break; |
||||
|
||||
default: |
||||
fprintf(stderr, "%s", USAGE_STRING); |
||||
exit(2); |
||||
} |
||||
} |
||||
|
||||
if (!addr_listen) { |
||||
fprintf(stderr, "No listening port specified\n"); |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
void start_echo(int fd) |
||||
{ |
||||
fd_set fds; |
||||
int res; |
||||
char buffer[1 << 20]; |
||||
char* ret; |
||||
FILE *socket; |
||||
|
||||
FD_ZERO(&fds); |
||||
|
||||
socket = fdopen(fd, "r+"); |
||||
if (!socket) { |
||||
CHECK_RES_DIE(-1, "fdopen"); |
||||
} |
||||
|
||||
while (1) { |
||||
ret = fgets(buffer, sizeof(buffer), socket); |
||||
if (!ret) { |
||||
fprintf(stderr, "%s", strerror(ferror(socket))); |
||||
return; |
||||
} |
||||
res = fprintf(socket, "%s%s", prefix, buffer); |
||||
if (res < 0) { |
||||
fprintf(stderr, "%s", strerror(ferror(socket))); |
||||
return; |
||||
} |
||||
|
||||
fflush(socket); |
||||
} |
||||
} |
||||
|
||||
void main_loop(int listen_sockets[], int num_addr_listen) |
||||
{ |
||||
int in_socket, i; |
||||
|
||||
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_sockets[i]); |
||||
start_echo(in_socket); |
||||
exit(0); |
||||
} |
||||
close(in_socket); |
||||
} |
||||
} |
||||
} |
||||
wait(NULL); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
|
||||
extern char *optarg; |
||||
extern int optind; |
||||
int num_addr_listen; |
||||
|
||||
int *listen_sockets; |
||||
|
||||
parse_cmdline(argc, argv); |
||||
|
||||
num_addr_listen = start_listen_sockets(&listen_sockets, addr_listen); |
||||
|
||||
main_loop(listen_sockets, num_addr_listen); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,196 @@
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
# main: processing of command line options and start the main loop. |
||||
# |
||||
# Copyright (C) 2007-2011 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 _GNU_SOURCE |
||||
#include <sys/types.h> |
||||
#include <fcntl.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <stdio.h> |
||||
#include <signal.h> |
||||
#include <sys/socket.h> |
||||
#include <sys/wait.h> |
||||
#include <netinet/in.h> |
||||
#include <arpa/inet.h> |
||||
#include <netdb.h> |
||||
#include <pwd.h> |
||||
#include <syslog.h> |
||||
#include <libgen.h> |
||||
#include <getopt.h> |
||||
|
||||
#include "common.h" |
||||
|
||||
const char* USAGE_STRING = |
||||
"sslh " VERSION "\n" \
|
||||
"usage:\n" \
|
||||
"\tsslh [-v] [-i] [-V] [-f] [-n]\n" |
||||
"\t[-t <timeout>] [-P <pidfile>] -u <username> -p <add> [-p <addr> ...] \n" \
|
||||
"%s\n\n" \
|
||||
"-v: verbose\n" \
|
||||
"-V: version\n" \
|
||||
"-f: foreground\n" \
|
||||
"-n: numeric output\n" \
|
||||
"-t: timeout before connecting to SSH.\n" \
|
||||
"-p: address and port to listen on.\n Can be used several times to bind to several addresses.\n" \
|
||||
"--[ssh,ssl,...]: where to connect connections from corresponding protocol.\n" \
|
||||
"-P: PID file. Default: /var/run/sslh.pid.\n" \
|
||||
"-i: Run as a inetd service.\n" \
|
||||
""; |
||||
|
||||
void print_usage(void) |
||||
{ |
||||
int i; |
||||
char *prots = ""; |
||||
|
||||
for (i = 0; i < num_known_protocols; i++) |
||||
asprintf(&prots, "%s\t[--%s <addr>]\n", prots, protocols[i].description); |
||||
|
||||
fprintf(stderr, USAGE_STRING, prots); |
||||
} |
||||
|
||||
void parse_cmdline(int argc, char* argv[]) |
||||
{ |
||||
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) + num_known_protocols + 1]; |
||||
struct addrinfo *addr, **a; |
||||
|
||||
memset(all_options, 0, sizeof(all_options)); |
||||
memcpy(all_options, const_options, sizeof(const_options)); |
||||
append_protocols(all_options, ARRAY_SIZE(const_options), protocols, num_known_protocols); |
||||
|
||||
while ((c = getopt_long_only(argc, argv, "t:T:p:VP:", all_options, NULL)) != -1) { |
||||
if (c == 0) continue; |
||||
|
||||
if (c >= PROT_SHIFT) { |
||||
affected++; |
||||
protocols[c - PROT_SHIFT].affected = 1; |
||||
resolve_name(&addr, optarg); |
||||
protocols[c - PROT_SHIFT].saddr= *addr; |
||||
continue; |
||||
} |
||||
|
||||
switch (c) { |
||||
|
||||
case 't': |
||||
probing_timeout = atoi(optarg); |
||||
break; |
||||
|
||||
case 'p': |
||||
/* find the end of the listen list */ |
||||
for (a = &addr_listen; *a; a = &((*a)->ai_next)); |
||||
/* append the specified addresses */ |
||||
resolve_name(a, optarg); |
||||
|
||||
break; |
||||
|
||||
case 'V': |
||||
printf("%s %s\n", server_type, VERSION); |
||||
exit(0); |
||||
|
||||
case 'u': |
||||
user_name = optarg; |
||||
break; |
||||
|
||||
case 'P': |
||||
pid_file = optarg; |
||||
break; |
||||
|
||||
default: |
||||
print_usage(); |
||||
exit(2); |
||||
} |
||||
} |
||||
|
||||
if (!affected) { |
||||
fprintf(stderr, "At least one target protocol must be specified.\n"); |
||||
exit(2); |
||||
} |
||||
|
||||
if (!addr_listen) { |
||||
fprintf(stderr, "No listening address specified; use at least one -p option\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
|
||||
extern char *optarg; |
||||
extern int optind; |
||||
int res, num_addr_listen; |
||||
|
||||
int *listen_sockets; |
||||
|
||||
/* Init defaults */ |
||||
pid_file = "/var/run/sslh.pid"; |
||||
user_name = "nobody"; |
||||
foreground = 0; |
||||
|
||||
parse_cmdline(argc, argv); |
||||
|
||||
if (inetd) |
||||
{ |
||||
verbose = 0; |
||||
start_shoveler(0); |
||||
exit(0); |
||||
} |
||||
|
||||
if (verbose) |
||||
printsettings(); |
||||
|
||||
num_addr_listen = start_listen_sockets(&listen_sockets, addr_listen); |
||||
|
||||
if (!foreground) |
||||
if (fork() > 0) exit(0); /* Detach */ |
||||
|
||||
setup_signals(); |
||||
|
||||
drop_privileges(user_name); |
||||
|
||||
/* New session -- become group leader */ |
||||
if (getuid() == 0) { |
||||
res = setsid(); |
||||
CHECK_RES_DIE(res, "setsid: already process leader"); |
||||
} |
||||
|
||||
write_pid_file(pid_file); |
||||
|
||||
/* Open syslog connection */ |
||||
setup_syslog(argv[0]); |
||||
|
||||
main_loop(listen_sockets, num_addr_listen); |
||||
|
||||
return 0; |
||||
} |