|
|
#include <sys/types.h> | #include <sys/types.h> |
#include <sys/param.h> | #include <sys/param.h> |
#include <netdb.h> | #include <netdb.h> |
|
#include <openssl/ssl.h> |
#include "uint16.h" | #include "uint16.h" |
#include "str.h" | #include "str.h" |
#include "byte.h" | #include "byte.h" |
|
|
unsigned long timeout = 26; | unsigned long timeout = 26; |
uint32 netif = 0; | uint32 netif = 0; |
| |
|
#ifdef WITH_SSL |
|
int flagssl = 0; |
|
struct stralloc certfile = {0}; |
|
#define CERTFILE "./cert.pem" |
|
|
|
void translate(SSL*, int, int, unsigned int); |
|
#endif |
|
|
static stralloc tcpremoteinfo; | static stralloc tcpremoteinfo; |
| |
uint16 localport; | uint16 localport; |
|
Lines 130-135
void found(char *data,unsigned int datal
|
Link Here
|
|---|
|
env(data + 1,data + 1 + split + 1); | env(data + 1,data + 1 + split + 1); |
} | } |
break; | break; |
|
case '-': |
|
env(data + 1, (char *)0); |
|
break; |
} | } |
++next0; | ++next0; |
data += next0; datalen -= next0; | data += next0; datalen -= next0; |
|
|
| |
void usage(void) | void usage(void) |
{ | { |
|
#ifndef WITH_SSL |
strerr_warn1("\ | strerr_warn1("\ |
tcpserver: usage: tcpserver \ | tcpserver: usage: tcpserver \ |
[ -461UXpPhHrRoOdDqQv ] \ | [ -461UXpPhHrRoOdDqQv ] \ |
[ -c limit ] \ | [ -c limit ] \ |
|
[ -C [address[/len]:]limit ] \ |
|
[ -e name=var ] \ |
|
[ -x rules.cdb ] \ |
|
[ -B banner ] \ |
|
[ -g gid ] \ |
|
[ -u uid ] \ |
|
[ -b backlog ] \ |
|
[ -l localname ] \ |
|
[ -t timeout ] \ |
|
host port program",0); |
|
#else |
|
strerr_warn1("\ |
|
tcpserver: usage: tcpserver \ |
|
[ -461UXpPhHrRoOdDqQsSv ] \ |
|
[ -c limit ] \ |
|
[ -C [address[/len]:]limit ] \ |
|
[ -e name=var ] \ |
[ -x rules.cdb ] \ | [ -x rules.cdb ] \ |
[ -B banner ] \ | [ -B banner ] \ |
[ -g gid ] \ | [ -g gid ] \ |
|
Lines 283-289
tcpserver: usage: tcpserver \
|
Link Here
|
|---|
|
[ -l localname ] \ | [ -l localname ] \ |
[ -t timeout ] \ | [ -t timeout ] \ |
[ -I interface ] \ | [ -I interface ] \ |
|
[ -n certfile ] \ |
host port program",0); | host port program",0); |
|
#endif |
_exit(100); | _exit(100); |
} | } |
| |
|
|
_exit(0); | _exit(0); |
} | } |
| |
|
struct conn { |
|
int pid; |
|
char remoteip[16]; |
|
} *conns; |
|
|
|
struct ip_limelt { |
|
char ip[16]; |
|
char mask[16]; |
|
unsigned long limit; |
|
unsigned long count; |
|
}; |
|
|
|
#include "gen_alloc.h" |
|
#include "gen_allocdefs.h" |
|
GEN_ALLOC_typedef(ip_limit,struct ip_limelt,l,len,a) |
|
GEN_ALLOC_readyplus(ip_limit, struct ip_limelt,l,len,a,i,n,x,10, |
|
ip_limit_rp) |
|
|
|
ip_limit ipl = {0}; |
|
unsigned long limit_ip = 0; |
|
|
|
void |
|
ip_limit_add(char *str) |
|
{ |
|
unsigned int n, len; |
|
unsigned long ul = 0; |
|
struct ip_limelt lim; |
|
|
|
byte_zero(&lim, sizeof(lim)); |
|
n = str_rchr(str, ':'); |
|
if (str[n] == ':') { |
|
str[n] = 0; |
|
scan_ulong(str + n + 1, &ul); |
|
lim.limit = ul; |
|
/* parse ip */ |
|
ul = 32; |
|
n = str_chr(str, '/'); |
|
if (str[n] == '/') |
|
scan_ulong(str + n + 1, &ul); |
|
if (scan_ip6(str, lim.ip) == 0) |
|
strerr_die2x(111,FATAL,"bad ip address"); |
|
if (str[str_chr(str, ':')] != ':') { |
|
if (ul > 32) |
|
strerr_die2x(111,FATAL,"ip prefix len > 32"); |
|
ul += 96; |
|
} else { |
|
if (ul > 128) |
|
strerr_die2x(111,FATAL,"ip6 prefix len > 128"); |
|
} |
|
for (n = 0; n < 16; n++) |
|
if (ul > 8) { |
|
lim.mask[n] = 0xff; |
|
ul -= 8; |
|
} else { |
|
lim.mask[n] = 0xff << (8 - ul); |
|
ul = 0; |
|
} |
|
if (!ip_limit_rp(&ipl,1)) |
|
strerr_die2x(111,FATAL,"out of memory"); |
|
ipl.l[ipl.len++] = lim; |
|
} else { |
|
scan_ulong(str, &ul); |
|
limit_ip = ul; |
|
} |
|
} |
|
|
|
int |
|
ip_limit_check(char ip[16], int d) |
|
{ |
|
unsigned long c; |
|
unsigned int l; |
|
unsigned int i; |
|
|
|
for (l = 0; l < ipl.len; l++) { |
|
for(i = 0; i < 16; i++) |
|
if ((ip[i] & ipl.l[l].mask[i]) != |
|
(ipl.l[l].ip[i] & ipl.l[l].mask[i])) |
|
break; |
|
if (i >= 16) { |
|
if (ipl.l[l].count + d > ipl.l[l].limit) |
|
return 1; |
|
ipl.l[l].count += d; |
|
return 0; |
|
} |
|
} |
|
|
|
/* global per ip limit */ |
|
for (l = 0, c= 0; l < limit; l++) |
|
if (!byte_diff(conns[l].remoteip, 16, ip)) |
|
c++; |
|
if (limit_ip != 0 && c + d > limit_ip) |
|
return 1; |
|
|
|
return 0; |
|
} |
|
|
void sigchld() | void sigchld() |
{ | { |
int wstat; | int wstat; |
int pid; | int pid; |
|
unsigned int i; |
|
|
while ((pid = wait_nohang(&wstat)) > 0) { | while ((pid = wait_nohang(&wstat)) > 0) { |
|
for (i = 0; i < limit; i++) |
|
if (conns[i].pid == pid) { |
|
ip_limit_check(conns[i].remoteip, -1); |
|
byte_zero(&conns[i], sizeof(struct conn)); |
|
} |
if (verbosity >= 2) { | if (verbosity >= 2) { |
strnum[fmt_ulong(strnum,pid)] = 0; | strnum[fmt_ulong(strnum,pid)] = 0; |
strnum2[fmt_ulong(strnum2,wstat)] = 0; | strnum2[fmt_ulong(strnum2,wstat)] = 0; |
|
Lines 330-343
main(int argc,char **argv)
|
Link Here
|
|---|
|
int opt; | int opt; |
struct servent *se; | struct servent *se; |
char *x; | char *x; |
|
char *iplimenv; |
|
int flagiplim; |
unsigned long u; | unsigned long u; |
int s; | int s; |
int t; | int t; |
| |
|
unsigned int i; |
|
int pid; |
|
#ifdef WITH_SSL |
|
BIO *sbio; |
|
SSL *ssl; |
|
SSL_CTX *ctx; |
|
int pi2c[2], pi4c[2]; |
|
|
|
ctx = NULL; |
|
|
|
if (!stralloc_copys(&certfile, CERTFILE) || !stralloc_0(&certfile) ) |
|
strerr_die2x(111,FATAL,"out of memory"); |
|
while ((opt = getopt(argc,argv,"46dDvqQhHrRsS1UXx:t:u:g:l:b:B:c:I:C:e:n:pPoO")) != opteof) |
|
#else |
while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:pPoO")) != opteof) | while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:pPoO")) != opteof) |
|
#endif |
switch(opt) { | switch(opt) { |
case 'b': scan_ulong(optarg,&backlog); break; | case 'b': scan_ulong(optarg,&backlog); break; |
case 'c': scan_ulong(optarg,&limit); break; | case 'c': scan_ulong(optarg,&limit); break; |
|
case 'C': ip_limit_add(optarg); break; |
case 'X': flagallownorules = 1; break; | case 'X': flagallownorules = 1; break; |
case 'x': fnrules = optarg; break; | case 'x': fnrules = optarg; break; |
case 'B': banner = optarg; break; | case 'B': banner = optarg; break; |
|
Lines 364-369
main(int argc,char **argv)
|
Link Here
|
|---|
|
case '4': noipv6 = 1; break; | case '4': noipv6 = 1; break; |
case '6': forcev6 = 1; break; | case '6': forcev6 = 1; break; |
case 'l': localhost = optarg; break; | case 'l': localhost = optarg; break; |
|
case 'e': iplimenv = optarg; |
|
if (iplimenv[str_chr(iplimenv, '=')] != '=') |
|
strerr_die2x(100,FATAL, "no '=' in ip limit env-var"); |
|
break; |
|
#ifdef WITH_SSL |
|
case 's': flagssl = 1; break; |
|
case 'S': flagssl = 0; break; |
|
case 'n': if (!stralloc_copys(&certfile, optarg) || |
|
!stralloc_0(&certfile) ) |
|
strerr_die2x(111,FATAL,"out of memory"); |
|
break; |
|
#endif |
default: usage(); | default: usage(); |
} | } |
argc -= optind; | argc -= optind; |
|
Lines 371-376
main(int argc,char **argv)
|
Link Here
|
|---|
|
| |
if (!verbosity) | if (!verbosity) |
buffer_2->fd = -1; | buffer_2->fd = -1; |
|
|
|
if (limit == 0) |
|
strerr_die2x(100,FATAL,"limit may not be set to 0"); |
|
if (limit > 65000) |
|
strerr_die2x(100,FATAL,"limit way to high"); |
|
conns = (struct conn *)alloc(limit * sizeof(struct conn)); |
|
if (!conns) |
|
strerr_die2x(111,FATAL,"out of memory"); |
|
byte_zero(conns, limit * sizeof(struct conn)); |
| |
hostname = *argv++; | hostname = *argv++; |
if (!hostname) usage(); | if (!hostname) usage(); |
|
Lines 408-413
main(int argc,char **argv)
|
Link Here
|
|---|
|
noipv6=1; | noipv6=1; |
} | } |
| |
|
#ifdef WITH_SSL |
|
if (flagssl == 1) { |
|
/* setup SSL context (load key and cert into ctx) */ |
|
SSL_library_init(); |
|
ctx=SSL_CTX_new(SSLv23_server_method()); |
|
if (!ctx) strerr_die2x(111,FATAL,"unable to create SSL context"); |
|
|
|
/* set prefered ciphers */ |
|
if (env_get("SSL_CIPHER")) |
|
if (SSL_CTX_set_cipher_list(ctx, env_get("SSL_CIPHER")) == 0) |
|
strerr_die2x(111,FATAL,"unable to set cipher list"); |
|
|
|
if(SSL_CTX_use_RSAPrivateKey_file(ctx, certfile.s, SSL_FILETYPE_PEM) != 1) |
|
strerr_die2x(111,FATAL,"unable to load RSA private key"); |
|
if(SSL_CTX_use_certificate_chain_file(ctx, certfile.s) != 1) |
|
strerr_die2x(111,FATAL,"unable to load certificate"); |
|
} |
|
#endif |
|
|
s = socket_tcp6(); | s = socket_tcp6(); |
if (s == -1) | if (s == -1) |
strerr_die2sys(111,FATAL,"unable to create socket: "); | strerr_die2sys(111,FATAL,"unable to create socket: "); |
|
Lines 446-454
main(int argc,char **argv)
|
Link Here
|
|---|
|
| |
if (t == -1) continue; | if (t == -1) continue; |
++numchildren; printstatus(); | ++numchildren; printstatus(); |
|
|
|
/* per ip handling */ |
|
flagiplim = 0; |
|
if (ip_limit_check(remoteip, 1)) { |
|
if (!forcev6 && ip6_isv4mapped(remoteip)) |
|
remoteipstr[ip4_fmt(remoteipstr,remoteip+12)] = 0; |
|
else |
|
remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0; |
|
if (iplimenv) { |
|
strerr_warn4(DROP,"too many connections from ",remoteipstr, |
|
" only flagged",0); |
|
flagiplim = 1; |
|
} else { |
|
strerr_warn3(DROP,"too many connections from ",remoteipstr,0); |
|
--numchildren; printstatus(); |
|
close(t); |
|
continue; |
|
} |
|
} |
| |
switch(fork()) { |
switch(pid = fork()) { |
case 0: | case 0: |
|
if (flagiplim) { |
|
int split; |
|
split = str_chr(iplimenv,'='); |
|
iplimenv[split] = 0; |
|
env(iplimenv,iplimenv + split + 1); |
|
} |
close(s); | close(s); |
doit(t); | doit(t); |
if ((fd_move(0,t) == -1) || (fd_copy(1,0) == -1)) | if ((fd_move(0,t) == -1) || (fd_copy(1,0) == -1)) |
|
Lines 457-468
main(int argc,char **argv)
|
Link Here
|
|---|
|
sig_unblock(sig_child); | sig_unblock(sig_child); |
sig_uncatch(sig_term); | sig_uncatch(sig_term); |
sig_uncatch(sig_pipe); | sig_uncatch(sig_pipe); |
|
#ifdef WITH_SSL |
|
if (flagssl == 1) { |
|
if (pipe(pi2c) != 0) |
|
strerr_die2sys(111,DROP,"unable to create pipe: "); |
|
if (pipe(pi4c) != 0) |
|
strerr_die2sys(111,DROP,"unable to create pipe: "); |
|
switch(fork()) { |
|
case 0: |
|
close(0); close(1); |
|
close(pi2c[1]); |
|
close(pi4c[0]); |
|
if ((fd_move(0,pi2c[0]) == -1) || (fd_move(1,pi4c[1]) == -1)) |
|
strerr_die2sys(111,DROP,"unable to set up descriptors: "); |
|
/* signals are allready set in the parent */ |
|
pathexec(argv); |
|
strerr_die4sys(111,DROP,"unable to run ",*argv,": "); |
|
case -1: |
|
strerr_die2sys(111,DROP,"unable to fork: "); |
|
default: |
|
ssl = SSL_new(ctx); |
|
if (!ssl) |
|
strerr_die2x(111,DROP,"unable to set up SSL session"); |
|
sbio = BIO_new_socket(0,BIO_NOCLOSE); |
|
if (!sbio) |
|
strerr_die2x(111,DROP,"unable to set up BIO socket"); |
|
SSL_set_bio(ssl,sbio,sbio); |
|
close(pi2c[0]); |
|
close(pi4c[1]); |
|
translate(ssl, pi2c[1], pi4c[0], 3600); |
|
_exit(0); |
|
} |
|
} |
|
#endif |
pathexec(argv); | pathexec(argv); |
strerr_die4sys(111,DROP,"unable to run ",*argv,": "); | strerr_die4sys(111,DROP,"unable to run ",*argv,": "); |
case -1: | case -1: |
strerr_warn2(DROP,"unable to fork: ",&strerr_sys); | strerr_warn2(DROP,"unable to fork: ",&strerr_sys); |
--numchildren; printstatus(); | --numchildren; printstatus(); |
|
break; |
|
default: |
|
for (i = 0; i < limit; i++) |
|
if (conns[i].pid == 0) { |
|
conns[i].pid = pid; |
|
byte_copy(conns[i].remoteip, 16, remoteip); |
|
break; |
|
} |
} | } |
close(t); | close(t); |
} | } |
} | } |
|
|
|
#ifdef WITH_SSL |
|
static int allwrite(int fd, char *buf, int len) |
|
{ |
|
int w; |
|
|
|
while (len) { |
|
w = write(fd,buf,len); |
|
if (w == -1) { |
|
if (errno == error_intr) continue; |
|
return -1; /* note that some data may have been written */ |
|
} |
|
if (w == 0) ; /* luser's fault */ |
|
buf += w; |
|
len -= w; |
|
} |
|
return 0; |
|
} |
|
|
|
static int allwritessl(SSL* ssl, char *buf, int len) |
|
{ |
|
int w; |
|
|
|
while (len) { |
|
w = SSL_write(ssl,buf,len); |
|
if (w == -1) { |
|
if (errno == error_intr) continue; |
|
return -1; /* note that some data may have been written */ |
|
} |
|
if (w == 0) ; /* luser's fault */ |
|
buf += w; |
|
len -= w; |
|
} |
|
return 0; |
|
} |
|
|
|
char tbuf[2048]; |
|
|
|
void translate(SSL* ssl, int clearout, int clearin, unsigned int iotimeout) |
|
{ |
|
struct taia now; |
|
struct taia deadline; |
|
iopause_fd iop[2]; |
|
int flagexitasap; |
|
int iopl; |
|
int sslout, sslin; |
|
int n, r; |
|
|
|
sslin = SSL_get_fd(ssl); |
|
sslout = SSL_get_fd(ssl); |
|
if (sslin == -1 || sslout == -1) |
|
strerr_die2x(111,DROP,"unable to set up SSL connection"); |
|
|
|
flagexitasap = 0; |
|
|
|
if (SSL_accept(ssl)<=0) |
|
strerr_die2x(111,DROP,"unable to accept SSL connection"); |
|
|
|
while (!flagexitasap) { |
|
taia_now(&now); |
|
taia_uint(&deadline,iotimeout); |
|
taia_add(&deadline,&now,&deadline); |
|
|
|
/* fill iopause struct */ |
|
iopl = 2; |
|
iop[0].fd = sslin; |
|
iop[0].events = IOPAUSE_READ; |
|
iop[1].fd = clearin; |
|
iop[1].events = IOPAUSE_READ; |
|
|
|
/* do iopause read */ |
|
iopause(iop,iopl,&deadline,&now); |
|
if (iop[0].revents) { |
|
do { |
|
/* data on sslin */ |
|
n = SSL_read(ssl, tbuf, sizeof(tbuf)); |
|
if ( n < 0 ) |
|
strerr_die2sys(111,DROP,"unable to read form network: "); |
|
if ( n == 0 ) |
|
flagexitasap = 1; |
|
r = allwrite(clearout, tbuf, n); |
|
if ( r < 0 ) |
|
strerr_die2sys(111,DROP,"unable to write to client: "); |
|
/* |
|
* if the data payload was longer than sizeof(tbuf) then SSL will have |
|
* bytes processed and pending. We need to pick them up and write them |
|
* to clearout. |
|
*/ |
|
} while (SSL_pending(ssl)); |
|
} |
|
if (iop[1].revents) { |
|
/* data on clearin */ |
|
n = read(clearin, tbuf, sizeof(tbuf)); |
|
if ( n < 0 ) |
|
strerr_die2sys(111,DROP,"unable to read form client: "); |
|
if ( n == 0 ) |
|
flagexitasap = 1; |
|
r = allwritessl(ssl, tbuf, n); |
|
if ( r < 0 ) |
|
strerr_die2sys(111,DROP,"unable to write to network: "); |
|
} |
|
if (!iop[0].revents && !iop[1].revents) |
|
strerr_die2x(0, DROP,"timeout reached without input"); |
|
} |
|
} |
|
#endif |