Files
2016-05-04 00:26:27 -04:00

2425 lines
60 KiB
C

#define _GNU_SOURCE
#include "gatling.h"
#include "socket.h"
#include "io.h"
#include "buffer.h"
#include "ip4.h"
#include "ip6.h"
#include "array.h"
#include "case.h"
#include "fmt.h"
#include "iob.h"
#include "str.h"
#include "scan.h"
#include "textcode.h"
#include "uint32.h"
#include "uint16.h"
#include "mmap.h"
#include "rangecheck.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#ifndef __MINGW32__
#include <sys/resource.h>
#include <sys/socket.h>
#include <pwd.h>
#include <grp.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/mman.h>
#include <fnmatch.h>
#include <sys/wait.h>
#endif
#include <stdlib.h>
#include <dirent.h>
#include <time.h>
#include <signal.h>
#include "version.h"
#include <assert.h>
#ifdef SUPPORT_SMB
#include <iconv.h>
#endif
#ifdef SUPPORT_PROXY
#include <regex.h>
#endif
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
// #include <crypt.h>
#include "havealloca.h"
#include "havesetresuid.h"
#if !defined(__OPTIMIZE__) && defined(__linux__)
#include <sys/prctl.h>
#endif
char serverroot[1024];
#ifdef SUPPORT_MULTIPROC
unsigned long instances=1;
#endif
unsigned long timeout_secs=23;
tai6464 now,next;
#ifdef TIMEOUT_DEBUG
void new_io_timeout(int64 d,tai6464 t) {
struct taia now;
struct taia diff;
taia_now(&now);
taia_sub(&diff,&t,&now);
buffer_puts(buffer_2,"DEBUG: scheduling timeout for fd #");
buffer_putlonglong(buffer_2,d);
buffer_puts(buffer_2," in ");
buffer_putlonglong(buffer_2,diff.sec.x);
buffer_putsflush(buffer_2," seconds.\n");
io_timeout(d,t);
}
int64 new_io_timeouted() {
int64 x=io_timeouted();
buffer_puts(buffer_2,"DEBUG: io_timeouted called, returned ");
buffer_putlonglong(buffer_2,x);
buffer_putnlflush(buffer_2);
return x;
}
#define io_timeout new_io_timeout
#define io_timeouted new_io_timeouted
#endif
const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
#ifdef SUPPORT_CGI
int forksock[2];
#endif
#if defined(__OpenBSD__) || defined(__NetBSD__)
#define __broken_itojun_v6__
#endif
int virtual_hosts;
int transproxy;
int directory_index;
int logging;
int nouploads;
int chmoduploads;
#ifdef __MINGW32__
char origdir[PATH_MAX];
#else
int64 origdir;
#endif
#ifdef SUPPORT_SMB
char workgroup[20]="FNORD";
int wglen;
char workgroup_utf16[100];
int wglen16;
#endif
static void carp(const char* routine) {
buffer_putmflush(buffer_2,routine,": ",strerror(errno),"\n");
}
static void panic(const char* routine) {
carp(routine);
exit(111);
}
unsigned long connections;
unsigned long http_connections, https_connections, ftp_connections, smb_connections;
unsigned long cps,cps1; /* connections per second */
unsigned long rps,rps1; /* requests per second */
unsigned long eps,eps1; /* events per second */
unsigned long long tin,tin1; /* traffic inbound */
unsigned long long tout,tout1; /* traffic outbound */
#ifdef SUPPORT_THREADED_OPEN
unsigned int threads;
int threadpipe_query[2];
int threadpipe_response[2];
void* worker_thread(void* unused) {
int src=threadpipe_query[0];
int dest=threadpipe_response[1];
(void)unused;
for (;;) {
int fd;
struct http_data* x;
if (read(src,&fd,sizeof(fd))!=fd) return 0;
x=io_getcookie(fd);
if (!x) continue;
if (fchdir(x->cwd)==-1) continue;
x->filefd=open(x->name_of_file_to_open,O_RDONLY);
write(dest,&fd,sizeof(fd));
}
}
void init_threads(int n) {
threads=0;
if (n<=0) return;
if (threads>0) {
int i;
if (pipe(threadpipe_query)==-1 || pipe(threadpipe_response)==-1) return;
for (i=0; i<n; ++i) {
pthread_t tmp;
pthread_create(&tmp,0,worker_thread,0);
pthread_detach(tmp);
}
threads=n;
}
}
#endif
#if defined(SUPPORT_PROXY) || defined(SUPPORT_CGI)
/* You configure a list of regular expressions, and if a request matches
* one of them, the request is forwarded to some other IP:port. You can
* run another httpd there that can handle CGI, PHP, JSP and whatnot. */
struct cgi_proxy* cgis,* last;
char** _envp;
/* if port==0 then execute the CGI locally */
#endif
#ifdef SUPPORT_CGI
static int add_cgi(const char* c) {
struct cgi_proxy* x=malloc(sizeof(struct cgi_proxy));
if (!x) return -1;
byte_zero(x,sizeof(struct cgi_proxy));
if (!strcmp(c,"+x"))
x->file_executable=1;
else if (regcomp(&x->r,c,REG_EXTENDED)) {
free(x);
return -1;
}
if (!last)
cgis=last=x;
else
last->next=x; last=x;
return 0;
}
#endif
void cleanup(int64 fd) {
struct http_data* h=io_getcookie(fd);
int buddyfd=-1;
if (h) {
buddyfd=h->buddy;
if (h->t==HTTPREQUEST
#ifdef SUPPORT_FTP
|| h->t==FTPCONTROL6 || h->t==FTPCONTROL4
#endif
#ifdef SUPPORT_SMB
|| h->t==SMBREQUEST
#endif
#ifdef SUPPORT_HTTPS
|| h->t==HTTPSREQUEST || h->t==HTTPSACCEPT || h->t==HTTPSPOST
#endif
) --connections;
if (h->t==HTTPREQUEST
#ifdef SUPPORT_PROXY
|| h->t==HTTPPOST
#endif
) --http_connections;
#ifdef SUPPORT_FTP
if (h->t==FTPCONTROL4 || h->t==FTPCONTROL6) --ftp_connections;
#endif
#ifdef SUPPORT_SMB
if (h->t==SMBREQUEST) --smb_connections;
#endif
#ifdef SUPPORT_HTTPS
if (h->t==HTTPSREQUEST || h->t==HTTPSPOST || h->t==HTTPSACCEPT ||
h->t==HTTPSACCEPT_CHECK || h->t==HTTPSRESPONSE) --https_connections;
#endif
#if defined(SUPPORT_FTP)
if (h->t==FTPSLAVE || h->t==FTPACTIVE || h->t==FTPPASSIVE) {
if (buddyfd!=-1) {
struct http_data* b=io_getcookie(buddyfd);
if (b)
b->buddy=-1;
}
buddyfd=-1;
}
#endif
if (h->filefd!=-1) io_close(h->filefd);
array_reset(&h->r);
iob_reset(&h->iob);
#if defined(SUPPORT_FTP) || defined(SUPPORT_SMB)
free(h->ftppath);
#endif
#ifdef SUPPORT_HTTPS
#ifdef USE_OPENSSL
if (h->ssl) SSL_free(h->ssl);
#endif
#ifdef USE_POLARSSL
ssl_free(&h->ssl);
#endif
#endif
#ifdef SUPPORT_SMB
close_all_handles(&h->h);
#endif
free(h);
}
io_close(fd);
if (buddyfd>=0) {
h=io_getcookie(buddyfd);
if (h) h->buddy=-1;
cleanup(buddyfd);
}
}
size_t header_complete(struct http_data* r,int64 sock) {
long i;
long l=array_bytes(&r->r);
const char* c=array_start(&r->r);
if (r->t==HTTPREQUEST
#ifdef SUPPORT_PROXY
|| r->t==HTTPPOST
#endif
#ifdef SUPPORT_HTTPS
|| r->t==HTTPSREQUEST || r->t==HTTPSPOST
#endif
)
{
/* first of all, in case someone is bombarding me with crap, detect
* that early so we can drop the connection */
if (l>10) {
tai6464 tarpit;
for (i=0; i<l; ++i) {
if (c[i]==' ') goto ok;
if (c[i]<'A' || c[i]>'Z') break;
}
/* detected invalid HTTP request */
if (logging) {
char buf[100];
buffer_puts(buffer_1,"close/not_http_traffic ");
buffer_putulong(buffer_1,sock);
buffer_putspace(buffer_1);
buffer_put(buffer_1,buf,fmt_ip6c(buf,r->peerip));
buffer_putnlflush(buffer_1);
}
io_dontwantread(sock);
io_dontwantwrite(sock);
--http_connections;
changestate(r,PUNISHMENT);
tarpit=now;
tarpit.sec.x+=10;
io_timeout(sock,tarpit);
return 0;
}
ok:
/* Erdgeist nudged me into optimizing this :-)
* I'd be surprised if this optimization has any measurable
* advantage, but it sure is impressive */
for (i=0; i+1<l; i+=2) {
if (c[i+1]=='\n') {
if (c[i]=='\n') return i+2;
else if (c[i]=='\r' && i+3<l && c[i+2]=='\r' && c[i+3]=='\n')
return i+4;
--i;
} else if (c[i+1]=='\r') {
if (i+4<l && c[i+2]=='\n' && c[i+3]=='\r' && c[i+4]=='\n')
return i+5;
--i;
}
}
#ifdef SUPPORT_SMB
} else if (r->t==SMBREQUEST) {
/* SMB */
/* first four bytes are the NetBIOS session;
* byte 0: 0 ("session message"),
* bytes 1-3: message length (big endian) */
uint32 len;
if (c[0]!=0) {
// printf("can't happen error: netbios byte 0 was not 0!?\n");
return 1;
}
len=uint32_read_big(c) & 0x00ffffff;
if (l>=len+4) return len+4;
#endif
} else {
/* FTP */
for (i=0; i<l; ++i)
if (c[i]=='\n')
return i+1;
}
return 0;
}
static char oom[]="HTTP/1.0 500 internal error\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nout of memory\n";
void httperror_realm(struct http_data* r,const char* title,const char* message,const char* realm,int nobody) {
char* c;
if (r->t==HTTPSERVER4 || r->t==HTTPSERVER6 || r->t==HTTPREQUEST
#ifdef SUPPORT_HTTPS
|| r->t==HTTPSSERVER4 || r->t==HTTPSSERVER6
|| r->t==HTTPSREQUEST || r->t==HTTPSRESPONSE
#endif
) {
c=r->hdrbuf=(char*)malloc(str_len(message)+str_len(title)+str_len(realm?realm:"")+300);
if (!c) {
r->hdrbuf=oom;
r->hlen=str_len(r->hdrbuf);
buffer_putsflush(buffer_1,"error_oom\n");
iob_addbuf(&r->iob,r->hdrbuf,r->hlen);
} else {
c+=fmt_str(c,"HTTP/1.0 ");
c+=fmt_str(c,title);
c+=fmt_str(c,"\r\nContent-Type: text/html\r\nConnection: ");
c+=fmt_str(c,r->keepalive?"keep-alive":"close");
c+=fmt_str(c,"\r\nServer: " RELEASE "\r\nContent-Length: ");
c+=fmt_ulong(c,str_len(message)+str_len(title)-4+17);
if (realm) {
c+=fmt_str(c,"\r\nWWW-Authenticate: Basic realm=\"");
c+=fmt_str(c,realm);
c+=fmt_str(c,"\"");
}
c+=fmt_str(c,"\r\n\r\n");
if (!nobody) {
c+=fmt_str(c,"<title>");
c+=fmt_str(c,title+4);
c+=fmt_str(c,"</title>\n");
c+=fmt_str(c,message);
c+=fmt_str(c,"\n");
}
r->hlen=c - r->hdrbuf;
iob_addbuf_free(&r->iob,r->hdrbuf,r->hlen);
}
} else {
/* FTP */
c=r->hdrbuf=(char*)malloc(str_len(title)+3);
c+=fmt_str(c,title);
c+=fmt_str(c,"\r\n");
r->hlen=c-r->hdrbuf;
iob_addbuf_free(&r->iob,r->hdrbuf,r->hlen);
}
}
void httperror(struct http_data* r,const char* title,const char* message,int nobody) {
httperror_realm(r,title,message,0,nobody);
}
unsigned int fmt_2digits(char* dest,int i) {
dest[0]=(i/10)+'0';
dest[1]=(i%10)+'0';
return 2;
}
#if 0
_ _ _
| |__ | |_| |_ _ __
| '_ \| __| __| '_ \
| | | | |_| |_| |_) |
|_| |_|\__|\__| .__/
|_|
#endif
const char* mimetypesfilename;
char* base;
int sort_name_a(de* x,de* y) { return (str_diff(base+x->name,base+y->name)); }
int sort_name_d(de* x,de* y) { return (str_diff(base+y->name,base+x->name)); }
int sort_mtime_a(de* x,de* y) { return x->ss.st_mtime-y->ss.st_mtime; }
int sort_mtime_d(de* x,de* y) { return y->ss.st_mtime-x->ss.st_mtime; }
int sort_size_a(de* x,de* y) { return x->ss.st_size-y->ss.st_size; }
int sort_size_d(de* x,de* y) { return y->ss.st_size-x->ss.st_size; }
#ifndef __MINGW32__
static uid_t __uid;
static gid_t __gid;
static int do_switch_uid;
static int prepare_switch_uid(const char* new_uid) {
uid_t u=0;
gid_t g=0;
if (new_uid) {
if (new_uid[0]>='0' && new_uid[0]<='9') {
unsigned long l;
const char *c=new_uid+scan_ulong(new_uid,&l);
if (*c && *c!=':' && *c!='.') return -1;
if ((u=l)!=l) return -1; /* catch overflow */
if (*c) {
++c;
c=c+scan_ulong(c,&l);
if ((g=l)!=l) return -1; /* catch overflow */
if (*c) return -1;
}
} else {
struct passwd *p=getpwnam(new_uid);
if (!p) return -1;
u=p->pw_uid;
g=p->pw_gid;
}
__uid=u;
__gid=g;
do_switch_uid=1;
}
return 0;
}
int switch_uid() {
if (!do_switch_uid) return 0;
#ifdef LIBC_HAS_SETRESUID
if (setresgid(__gid,__gid,__gid) ||
setgroups(1,&__gid) ||
setresuid(__uid,__uid,__uid))
#else
if (setgid(__gid) ||
setgroups(1,&__gid) ||
setuid(__uid))
#endif
{
/* if dropping privileges failed with EPERM, we are already running
* with reduced privileges. Don't make a fuss about it then. */
if (errno==EPERM) return 0;
return -1;
}
return 0;
}
#endif
static volatile int fini;
void sighandler(int sig) {
fini=(sig==SIGINT?1:2); /* 2 for SIGHUP */
}
static int is_server_connection(enum conntype t) {
return (t==HTTPSERVER6 || t==HTTPSERVER4
#ifdef SUPPORT_FTP
|| t==FTPSERVER6 || t==FTPSERVER4
#endif
#ifdef SUPPORT_SMB
|| t==SMBSERVER6 || t==SMBSERVER4
#endif
#ifdef SUPPORT_HTTPS
|| t==HTTPSSERVER6 || t==HTTPSSERVER4
#endif
);
}
#ifdef __broken_itojun_v6__
#warning "working around idiotic openbse ipv6 stupidity - please kick itojun for this!"
int s4; /* ipv4 http socket */
#ifdef SUPPORT_FTP
int f4; /* ipv4 ftp socket */
#endif
#ifdef SUPPORT_HTTPS
int httpss4; /* ipv4 https socket */
#endif
#endif
#ifdef SUPPORT_HTTPS
char* sshd;
unsigned long ssh_timeout;
#endif
int limit_to_lan;
static void accept_server_connection(int64 i,struct http_data* H,unsigned long ftptimeout_secs,tai6464 nextftp) {
/* This is an FTP or HTTP(S) or SMB server connection.
* This read event means that someone connected to us.
* accept() the connection, establish connection type from
* server connection type, and put the new connection into the
* state table */
char ip[16];
uint16 port;
uint32 scope_id;
int n;
while (1) {
int punk;
#ifdef __broken_itojun_v6__
if (H->t==HTTPSERVER4 || H->t==FTPSERVER4
#ifdef SUPPORT_SMB
|| H->t==SMBSERVER4
#endif
#ifdef SUPPORT_HTTPS
|| H->t==HTTPSSERVER4
#endif
) {
byte_copy(ip,12,V4mappedprefix);
scope_id=0;
n=socket_accept4(i,ip+12,&port);
} else
#endif
n=socket_accept6(i,ip,&port,&scope_id);
if (n==-1) break;
punk=new_request_from_ip(ip,now.sec.x-4611686018427387914ULL)==1;
++cps1;
++connections;
if (logging) {
char buf[IP6_FMT];
buffer_puts(buffer_1,punk?(timeout_secs?"dos_tarpit ":"dos_drop "):"accept ");
buffer_putulong(buffer_1,n);
buffer_puts(buffer_1," ");
buffer_put(buffer_1,buf,fmt_ip6c(buf,ip));
buffer_puts(buffer_1," ");
buffer_putulong(buffer_1,port);
buffer_puts(buffer_1," ");
buffer_putulong(buffer_1,connections-1);
buffer_puts(buffer_1," ");
{
const char* service;
switch (H->t) {
case HTTPSERVER4:
case HTTPSERVER6: service="http"; break;
#ifdef SUPPORT_HTTPS
case HTTPSSERVER4:
case HTTPSSERVER6: service="https"; break;
#endif
#ifdef SUPPORT_FTP
case FTPSERVER4:
case FTPSERVER6: service="ftp"; break;
#endif
#ifdef SUPPORT_SMB
case SMBSERVER4:
case SMBSERVER6: service="smb"; break;
#endif
default: service="unk";
}
buffer_puts(buffer_1,service);
}
buffer_putnlflush(buffer_1);
}
if (limit_to_lan) {
int passed;
/* if the -L option is given, only accept connections from local
* or reserved IP ranges */
if (byte_equal(ip,12,V4mappedprefix)) {
unsigned char* ip4=(unsigned char*)ip+12;
passed = ip4[0]==127 || /* 127.0.0.1/8 */
ip4[0]==10 || /* RFC1918: 10.0.0.0/8 */
(ip4[0]==192 && ip4[1]==168) || /* RFC1918: 192.168.0.0/16 */
(ip4[0]==172 && ip4[1]>=16 && ip4[1]<=31) || /* RFC1918: 172.16.0.0/12 */
(ip4[0]==169 && ip4[1]==254 && ip4[2]!=0 && ip4[2]!=255); /* RFC 5735: 169.254.0.0/16 */
} else { /* IPv6 */
unsigned char* ip6=(unsigned char*)ip;
passed = byte_equal(ip6,16,V6loopback) || /* ::1 */
(ip6[0]==0xfc && (ip6[1]&0xfe)==0) || /* RF4193 ULA fc00::/7 */
(ip6[0]==0xfe && (ip6[1]&0xfc)==0xc0) || /* deprecated site-local */
(ip6[0]==0xfe && (ip6[1]&0xfc)==0x80); /* RFC5735 link-local */
}
if (!passed) {
timeout_secs=0;
punk=1;
if (logging) {
buffer_puts(buffer_1,"close/outside_lan_drop ");
buffer_putulong(buffer_1,n);
buffer_putnlflush(buffer_1);
}
}
}
if (punk && !timeout_secs) {
io_close(n);
continue;
}
#ifdef SUPPORT_SERVERSTATUS
if (H->t==HTTPSERVER4 || H->t==HTTPSERVER6) ++http_connections;
#ifdef SUPPORT_HTTPS
if (H->t==HTTPSSERVER4 || H->t==HTTPSSERVER6) ++https_connections;
#endif
#ifdef SUPPORT_FTP
if (H->t==FTPSERVER4 || H->t==FTPSERVER6) ++ftp_connections;
#endif
#ifdef SUPPORT_SMB
if (H->t==SMBSERVER4 || H->t==SMBSERVER6) ++smb_connections;
#endif
#endif
#ifdef TCP_NODELAY
{
int i=1;
setsockopt(n,IPPROTO_TCP,TCP_NODELAY,&i,sizeof(i));
}
#else
#warning TCP_NODELAY not defined
#endif
if (io_fd_canwrite(n)) {
struct http_data* h;
h=(struct http_data*)malloc(sizeof(struct http_data));
if (h) {
io_nonblock(n);
H->sent=H->received=0;
if (!punk) {
if (H->t==HTTPSERVER6 || H->t==HTTPSERVER4
#ifdef SUPPORT_SMB
|| H->t==SMBSERVER6 || H->t==SMBSERVER4
#endif
#ifdef SUPPORT_HTTPS
|| H->t==HTTPSSERVER6 || H->t==HTTPSSERVER4
#endif
)
io_wantread(n);
else
io_wantwrite(n);
}
byte_zero(h,sizeof(struct http_data));
#ifdef STATE_DEBUG
h->myfd=n;
#endif
#ifdef __broken_itojun_v6__
if (i==s4 || i==f4) {
byte_copy(h->myip,12,V4mappedprefix);
socket_local4(n,h->myip+12,&h->myport);
} else
socket_local6(n,h->myip,&h->myport,0);
#else
socket_local6(n,h->myip,&h->myport,0);
#endif
byte_copy(h->peerip,16,ip);
h->peerport=port;
h->myscope_id=scope_id;
if (punk) {
changestate(h,PUNISHMENT);
io_timeout(n,next);
} else if (H->t==HTTPSERVER4 || H->t==HTTPSERVER6) {
changestate(h,HTTPREQUEST);
if (timeout_secs)
io_timeout(n,next);
#ifdef SUPPORT_HTTPS
} else if (H->t==HTTPSSERVER4 || H->t==HTTPSSERVER6) {
#ifdef __MINGW32__
// printf("chdir(\"%s\") -> %d\n",origdir,chdir(origdir));
// chdir(origdir);
#else
fchdir(origdir);
#endif
#ifdef USE_OPENSSL
if (init_serverside_tls(&h->ssl,n))
#elif defined(USE_POLARSSL)
if (init_serverside_tls(&h->ssl,n))
#endif
{
if (logging) {
char a[FMT_ULONG];
a[fmt_ulong(a,n)]=0;
buffer_putmflush(buffer_1,"ssl_setup_failed ",a," ",strerror(errno),"\nclose/readerr ",a,"\n");
}
cleanup(n);
continue;
}
changestate(h,HTTPSACCEPT_CHECK);
if (timeout_secs)
io_timeout(n,nextftp);
#endif
#ifdef SUPPORT_SMB
} else if (H->t==SMBSERVER4 || H->t==SMBSERVER6) {
changestate(h,SMBREQUEST);
if (timeout_secs)
io_timeout(n,next);
#endif
#ifdef SUPPORT_FTP
} else {
if (H->t==FTPSERVER6)
changestate(h,FTPCONTROL6);
else
changestate(h,FTPCONTROL4);
iob_addbuf(&h->iob,"220 Hi there!\r\n",15);
h->keepalive=1;
if (ftptimeout_secs)
io_timeout(n,nextftp);
#endif
}
h->buddy=-1;
h->filefd=-1;
io_setcookie(n,h);
} else
io_close(n);
} else
io_close(n);
#ifdef SUPPORT_MULTIPROC
if (instances>1) break;
#endif
}
if (errno==EAGAIN)
io_eagain(i);
else
#ifdef __broken_itojun_v6__
carp(H->t==HTTPSERVER4||H->t==FTPSERVER4?"socket_accept4":"socket_accept6");
#else
if (errno==EINVAL) {
static int64 lasteinval;
if (lasteinval!=i) {
lasteinval=i;
carp("socket_accept6");
}
}
#endif
}
#ifdef SUPPORT_HTTPS
int handle_ssl_error_code(int sock,int code,int reading) {
// printf("handle_ssl_error_code(sock %d,code %d,reading %d)\n",sock,code,reading);
switch (code) {
#ifdef USE_OPENSSL
case SSL_ERROR_WANT_READ:
io_wantread(sock);
io_dontwantwrite(sock);
return 0;
case SSL_ERROR_WANT_WRITE:
io_wantwrite(sock);
io_dontwantread(sock);
return 0;
#elif defined(USE_POLARSSL)
case POLARSSL_ERR_NET_WANT_READ:
io_wantread(sock);
io_dontwantwrite(sock);
return 0;
case POLARSSL_ERR_NET_WANT_WRITE:
io_wantwrite(sock);
io_dontwantread(sock);
return 0;
#endif
#ifdef USE_OPENSSL
case SSL_ERROR_SYSCALL:
#elif defined(USE_POLARSSL)
case POLARSSL_ERR_NET_RECV_FAILED:
case POLARSSL_ERR_NET_SEND_FAILED:
case POLARSSL_ERR_NET_CONN_RESET:
errno=ECONNRESET;
#endif
// we already signal the error up and upsteam will then write an
// error message
return -1;
default:
if (logging) {
buffer_puts(buffer_1,"ssl_protocol_error ");
buffer_putulong(buffer_1,sock);
buffer_puts(buffer_1,"\nclose/readerr ");
buffer_putulong(buffer_1,sock);
buffer_putnlflush(buffer_1);
}
errno=EPROTO;
return -1;
}
}
void do_sslaccept(int sock,struct http_data* h,int reading) {
int r;
if (h->t == HTTPSACCEPT_CHECK) {
unsigned char buf[10];
changestate(h,HTTPSACCEPT);
/* now, try to peek into the buffer, and see if it looks like an SSL
* handshake */
r=recv(sock,buf,sizeof(buf),MSG_PEEK);
if (r>5) {
if (sshd && buf[0]=='S' && buf[1]=='S' && buf[2]=='H') {
if (logging) {
char numbuf[FMT_ULONG];
numbuf[fmt_ulong(numbuf,sock)]=0;
buffer_putmflush(buffer_1,"close/ssh_handshake ",numbuf,"\n");
}
{
uint32 a=0;
write(forksock[0],&a,4);
io_passfd(forksock[0],sock);
}
--https_connections;
changestate(h,PUNISHMENT);
cleanup(sock);
return;
}
/* Apparently the packets look radically different depending on
* whether it's TLS or SSLv2. GREAT! */
/* first packet must be handshake */
if (buf[0]!=0x16 || /* content type: handshake */
buf[1]>3 || /* version major */
buf[2]>3 || /* version minor */
buf[3]>2) { /* length > 0x200 */
if (buf[0]!=0x80 ||
buf[2]!=1 || /* Client Hello */
buf[3]>3 || /* version major */
buf[4]>3) { /* version minor */
tai6464 tarpit;
/* this does not look like an SSL packet. */
if (logging) {
char tmp[100];
buffer_puts(buffer_1,"close/not_ssl_traffic ");
buffer_putulong(buffer_1,sock);
buffer_putspace(buffer_1);
buffer_put(buffer_1,tmp,fmt_ip6c(tmp,h->peerip));
buffer_putnlflush(buffer_1);
}
io_dontwantread(sock);
io_dontwantwrite(sock);
--https_connections;
changestate(h,PUNISHMENT);
tarpit=now;
tarpit.sec.x+=10;
io_timeout(sock,tarpit);
return;
}
}
}
}
#ifdef USE_OPENSSL
r=SSL_get_error(h->ssl,SSL_accept(h->ssl));
// printf("do_sslaccept -> %d\n",r);
if (r==SSL_ERROR_NONE)
#elif defined(USE_POLARSSL)
r=ssl_handshake(&h->ssl);
if (r==0)
#endif
{
#if 0
h->writefail=1;
#endif
changestate(h,HTTPSREQUEST);
if (logging) {
buffer_puts(buffer_1,"ssl_handshake_ok ");
buffer_putulong(buffer_1,sock);
buffer_putnlflush(buffer_1);
}
return;
} else
if (handle_ssl_error_code(sock,r,reading)==-1)
cleanup(sock);
}
#endif
static void handle_read_misc(int64 i,struct http_data* h,unsigned long ftptimeout_secs,tai6464 nextftp) {
/* This is a TCP client connection waiting for input, i.e.
* - an HTTP connection waiting for a HTTP request, or
* - an FTP connection waiting for a command, or
* - an FTP upload waiting for more data, or
* - an SMB connection waiting for the next command */
char buf[8192];
int l;
#ifdef MOREDEBUG
printf("MOREDEBUG: entering handle_read_misc for fd #%d\n",(int)i);
#endif
#ifdef SUPPORT_HTTPS
assert(h->t != HTTPSRESPONSE);
if (h->t == HTTPSREQUEST) {
#ifdef USE_OPENSSL
l=SSL_read(h->ssl,buf,sizeof(buf));
#elif defined(USE_POLARSSL)
l=ssl_read(&h->ssl,(unsigned char*)buf,sizeof(buf));
#else
#error fixme
#endif
// printf("SSL_read(sock %d,buf %p,n %d) -> %d\n",i,buf,sizeof(buf),l);
#ifdef USE_OPENSSL
if (l==-1) {
l=SSL_get_error(h->ssl,l);
if (l==SSL_ERROR_WANT_READ || l==SSL_ERROR_WANT_WRITE) {
#elif defined(USE_POLARSSL)
if (l<0) {
if (l==POLARSSL_ERR_NET_WANT_READ || l==POLARSSL_ERR_NET_WANT_WRITE) {
#else
#error fixme
#endif
// printf(" error %d %s\n",l,ERR_error_string(l,0));
io_eagain(i);
if (handle_ssl_error_code(i,l,1)==-1) {
cleanup(i);
return;
}
l=-1;
#ifdef USE_POLARSSL
} else if (l==POLARSSL_ERR_NET_RECV_FAILED) {
l=0;
#endif
} else {
printf("got polarssl error %x\n",l);
errno=ECONNRESET;
l=-3;
}
}
} else
#endif
l=io_tryread(i,buf,sizeof buf);
#ifdef MOREDEBUG
printf("MOREDEBUG: io_tryread on fd #%d returned %ld\n",(int)i,(long)l);
#endif
if (l==-3) {
#ifdef SUPPORT_FTP
ioerror:
#endif
if (logging) {
char a[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
buffer_putmflush(buffer_1,"io_error ",a," ",strerror(errno),"\nclose/readerr ",a,"\n");
}
cleanup(i);
} else if (l==0) {
if (logging) {
buffer_puts(buffer_1,"close/read0 ");
buffer_putulong(buffer_1,i);
buffer_putnlflush(buffer_1);
}
#ifdef SUPPORT_FTP
if (h->t==FTPSLAVE) {
/* This is an FTP upload, it just finished. */
struct http_data* b=io_getcookie(h->buddy);
assert(b);
b->buddy=-1;
iob_reset(&b->iob);
iob_adds(&b->iob,"226 Got it.\r\n");
io_dontwantread(h->buddy);
io_wantwrite(h->buddy);
if (chmoduploads)
fchmod(h->filefd,0644);
if (logging) {
struct stat ss;
if (fstat(h->filefd,&ss)==0) {
char a[FMT_ULONG];
char b[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
b[fmt_ulong(b,ss.st_size)]=0;
buffer_putmflush(buffer_1,"received ",a," ",b,"\n");
}
}
}
#endif
#ifdef SUPPORT_HTTPS
if (h->t==HTTPSPOST || h->t==HTTPSRESPONSE || h->t==HTTPSREQUEST) {
#ifdef USE_OPENSSL
SSL_shutdown(h->ssl);
#endif
}
#endif
cleanup(i);
} else if (l>0) {
/* successfully read some data (l bytes) */
h->received+=l;
tin1+=l;
#ifdef SUPPORT_FTP
if (h->t==FTPCONTROL4 || h->t==FTPCONTROL6) {
if (ftptimeout_secs)
io_timeout(i,nextftp);
} else {
if (timeout_secs)
io_timeout(i,next);
}
if (h->t==FTPSLAVE) {
/* receive an upload */
if (ftptimeout_secs)
io_timeout(h->buddy,nextftp);
if (write(h->filefd,buf,l)!=l)
goto ioerror;
} else
#endif
{
/* received a request */
// printf("previous size: %lu, adding %lu bytes.\n",(unsigned long)array_bytes(&h->r),(unsigned long)l);
array_catb(&h->r,buf,l);
if (array_failed(&h->r)) {
httperror(h,"500 Server Error","request too long.",0);
emerge:
io_dontwantread(i);
io_wantwrite(i);
} else if (array_bytes(&h->r)>MAX_HEADER_SIZE) {
httperror(h,"500 request too long","You sent too much header data",0);
array_reset(&h->r);
goto emerge;
} else if ((l=header_complete(h,i))) {
/* at this point we saw a \r\n\r\n */
long alen;
pipeline:
/* The h->mimetype reference is here so that we don't count both the HTTP
* connection and the first request on it as a dos attack. */
if (h->mimetype && new_request_from_ip(h->peerip,now.sec.x-4611686018427387914ULL)==1) {
changestate(h,PUNISHMENT);
if (logging) {
char buf[IP6_FMT];
char n[FMT_LONG];
n[fmt_ulong(n,i)]=0;
buf[fmt_ip6c(buf,h->peerip)]=0;
buffer_putmflush(buffer_1,"dos_tarpit2 ",n," ",buf,"\n");
}
if (timeout_secs) {
io_dontwantread(i);
io_dontwantwrite(i);
} else
/* if we don't have timeouts enabled, just drop the connection */
cleanup(i);
return;
}
#ifdef SUPPORT_HTTPS
if (h->t==HTTPREQUEST || h->t==HTTPSREQUEST) {
httpresponse(h,i,l);
if (h->t == HTTPSREQUEST) changestate(h,HTTPSRESPONSE);
}
#else
if (h->t==HTTPREQUEST)
httpresponse(h,i,l);
#endif
#ifdef SUPPORT_SMB
else if (h->t==SMBREQUEST) {
if (smbresponse(h,i)==-1) {
cleanup(i);
return;
}
}
#endif
#ifdef SUPPORT_FTP
else
ftpresponse(h,i);
#endif
#ifdef SUPPORT_PROXY
if (h->t != HTTPPOST
#ifdef SUPPORT_HTTPS
&& h->t != HTTPSPOST
#endif
) {
#endif
if (l < (alen=array_bytes(&h->r))) {
char* c=array_start(&h->r);
#if 0
printf("continuation: consumed %lu bytes from buffer (%lu still there)\n",
(unsigned long)l,
(unsigned long)(alen-l));
#endif
byte_copy(c,alen-l,c+l);
array_truncate(&h->r,1,alen-l);
l=header_complete(h,i);
#if 0
write(1,"\n\n",2);
write(1,array_start(&h->r),array_bytes(&h->r));
write(1,"\n\n",2);
#endif
if (l) {
/* this breaks SMB: */
// if (h->r.initialized) --h->r.initialized;
goto pipeline;
}
} else
array_reset(&h->r);
#ifdef SUPPORT_PROXY
}
#endif
}
}
}
#ifdef MOREDEBUG
printf("MOREDEBUG: entering handle_read_misc for fd #%d\n",(int)i);
#endif
}
#ifdef SUPPORT_HTTPS
int64 https_write_callback(int64 sock,const void* buf,uint64 n) {
int l;
struct http_data* H=io_getcookie(sock);
if (!H) return -3;
#if 0
H->writefail=!H->writefail;
if (H->writefail) { errno=EAGAIN; return -1; }
#endif
if (n>65536) n=65536;
#ifdef USE_OPENSSL
l=SSL_write(H->ssl,buf,n);
if (l<0) {
l=SSL_get_error(H->ssl,l);
#elif defined(USE_POLARSSL)
l=ssl_write(&H->ssl,buf,n);
if (l<0) {
#endif
if (handle_ssl_error_code(sock,l,0)==-1) {
// cleanup(sock);
return -3;
}
#ifdef USE_OPENSSL
if (l==SSL_ERROR_WANT_READ || l==SSL_ERROR_WANT_WRITE) {
#elif defined(USE_POLARSSL)
if (l==POLARSSL_ERR_NET_WANT_READ || l==POLARSSL_ERR_NET_WANT_WRITE) {
#endif
l=-1; errno=EAGAIN;
} else
l=-3;
}
return l;
}
#endif
static void handle_write_misc(int64 i,struct http_data* h,uint64 prefetchquantum) {
int64 r;
#ifdef MOREDEBUG
puts("MOREDEBUG: entering handle_write_misc");
#endif
#ifdef SUPPORT_HTTPS
assert(h->t != HTTPSREQUEST);
if (h->t == HTTPSRESPONSE)
r=iob_write(i,&h->iob,https_write_callback);
else
#endif
r=iob_send(i,&h->iob);
#ifdef MOREDEBUG
printf("MOREDEBUG: iob_send on fd #%d returned %ld (%lu remaining)\n",(int)i,(long)r,(unsigned long)iob_bytesleft(&h->iob));
#endif
if (r==-1)
io_eagain(i);
else if (r<=0) {
if (r==-3) {
if (logging) {
char a[FMT_ULONG];
char r[FMT_ULONG];
char s[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
r[fmt_ulonglong(r,h->received)]=0;
s[fmt_ulonglong(s,h->sent)]=0;
buffer_putmflush(buffer_1,"socket_error ",a," ",strerror(errno),"\nclose/writefail ",a," ",r," ",s,"\n");
}
#ifdef SUPPORT_FTP
if (h->t==FTPSLAVE || h->t==FTPACTIVE) {
struct http_data* b=io_getcookie(h->buddy);
if (b) {
b->buddy=-1;
iob_reset(&b->iob);
iob_adds(&b->iob,"554 socket error.\r\n");
io_wantwrite(h->buddy);
} else {
/* Apparently the main control connection already died.
* Nothing more to do here except clean up. */
}
}
#endif
cleanup(i);
} else { /* returned 0, i.e. we wrote it all */
wroteitall:
#ifdef SUPPORT_HTTPS
if (h->t == HTTPSRESPONSE) h->t = HTTPSREQUEST;
#endif
#ifdef SUPPORT_PROXY
#ifdef SUPPORT_HTTPS
if ((h->t == HTTPREQUEST || h->t == HTTPSREQUEST) && h->buddy!=-1)
#else
if (h->t == HTTPREQUEST && h->buddy!=-1)
#endif
{
io_dontwantwrite(i);
io_wantread(h->buddy);
return;
}
#endif
if (logging && (h->t == HTTPREQUEST
#ifdef SUPPORT_HTTPS
|| h->t == HTTPSREQUEST
#endif
)) {
buffer_puts(buffer_1,"request_done ");
buffer_putulong(buffer_1,i);
buffer_puts(buffer_1," ");
buffer_putulonglong(buffer_1,h->received);
buffer_puts(buffer_1," ");
buffer_putulonglong(buffer_1,h->sent);
buffer_putnlflush(buffer_1);
h->received=h->sent=0;
}
if (array_bytes(&h->r)>0) --h->r.initialized;
iob_reset(&h->iob);
h->hdrbuf=0;
if (h->keepalive) {
// iob_reset(&h->iob);
io_dontwantwrite(i);
io_wantread(i);
} else {
if (logging) {
buffer_puts(buffer_1,"close/reqdone ");
buffer_putulong(buffer_1,i);
buffer_putnlflush(buffer_1);
}
#ifdef SUPPORT_FTP
if (h->t==FTPSLAVE) {
struct http_data* b=io_getcookie(h->buddy);
if (b) {
b->buddy=-1;
// iob_reset(&b->iob);
iob_adds(&b->iob,"226 Done.\r\n");
io_dontwantread(h->buddy);
io_wantwrite(h->buddy);
} else
buffer_putsflush(buffer_2,"ARGH: no cookie or no buddy for FTP slave!\n");
}
#endif
cleanup(i);
}
}
} else {
h->sent+=r;
tout1+=r;
/* write OK, now would be a good time to do some prefetching */
h->sent_until+=r;
if (prefetchquantum) {
if (h->prefetched_until<h->sent_until || h->prefetched_until+prefetchquantum<h->sent_until) {
if (prefetchquantum) iob_prefetch(&h->iob,2*prefetchquantum);
h->prefetched_until+=2*prefetchquantum;
}
}
if (iob_bytesleft(&h->iob)==0) /* optimization, not strictly necessary */
goto wroteitall;
}
#ifdef MOREDEBUG
puts("MOREDEBUG: leaving handle_write_misc");
#endif
}
static void prepare_listen(int s,void* whatever) {
if (s!=-1) {
if (!io_fd(s))
panic("io_fd");
io_setcookie(s,whatever);
io_wantread(s);
}
}
#ifdef SUPPORT_BITTORRENT
int handle_torrent_request(int64 sock,struct http_data* h) {
/* http://wiki.theory.org/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol */
/* http://www.bittorrent.org/protocol.html */
char* req=array_start(&h->r); /* "GET /announce?info_hash=%c3%f4%31%0e%aa%ec%ae%3d%84%c1%63%70%a2%36%67%6b%24%99%b6%e1&peer_id=-TR0006-u0u5j57kcmm4&port=6887&uploaded=0&downloaded=0&left=243269632&compact=1&numwant=50&key=njyytouhv5fymdafkhzi&event=started\r\n" */
char* t=strchr(req,'\n');
char* s=strchr(req,'?');
if (s && t && s<t) {
if (t[-1]=='\r') --t;
} else
httperror(h,"500 invalid bittorrent request","Invalid BitTorrent request",*req=='H');
return 0;
}
#endif
int main(int argc,char* argv[],char* envp[]) {
int s; /* http socket */
int f=-1; /* ftp socket */
#ifdef SUPPORT_SMB
int smbs=-1; /* smb socket */
enum conntype sct=SMBSERVER6;
#endif
int doftp=0; /* -1 = don't, 0 = try, but don't fail if not working, 1 = do */
int dohttp=0; /* -1 = don't, 0 = try, but don't fail if not working, 1 = do */
int dosmb=0;
enum { HTTP, FTP, SMB, HTTPS } lastopt=HTTP;
enum conntype ct=HTTPSERVER6; /* used as cookie to recognize server connections */
#ifdef SUPPORT_FTP
enum conntype fct=FTPSERVER6; /* dito */
#endif
#ifdef SUPPORT_HTTPS
int httpss=-1; /* https socket */
enum conntype httpsct=HTTPSSERVER6;
int dohttps=0;
#endif
#ifdef __broken_itojun_v6__
enum conntype ct4=HTTPSERVER4;
#ifdef SUPPORT_FTP
enum conntype fct4=FTPSERVER4;
#endif
#ifdef SUPPORT_HTTPS
enum conntype httpsct4=HTTPSSERVER4;
#endif
#endif
uint32 scope_id;
char ip[16];
uint16 port,fport,sport;
#ifdef SUPPORT_HTTPS
uint16 httpsport=0;
tai6464 nexthttps;
#endif
tai6464 last,tick,nextftp;
unsigned long ftptimeout_secs=600;
char* new_uid=0;
char* chroot_to=0;
unsigned long long prefetchquantum=0;
#ifdef SUPPORT_MULTIPROC
pid_t* Instances;
#endif
#if defined(DEBUG_EVENTS) || defined(MOREDEBUG) || defined(SMDEBUG) || defined(STATE_DEBUG)
#ifdef __dietlibc__
fflush(stdout);
#endif
#endif
#ifdef SUPPORT_HTTPS
#ifdef USE_OPENSSL
SSL_load_error_strings();
#endif
#endif
#if defined(SUPPORT_CGI) || defined(SUPPORT_PROXY)
_envp=envp;
#endif
#ifdef SUPPORT_CGI
{
int found;
int _argc=argc;
char* new_uid=0;
char** _argv=argv;
found=0;
for (;;) {
int c=getopt(_argc,_argv,"HP:hnfFi:p:vVdDtT:c:u:Uaw:sSO:C:lLeEr:o:N:m:A:X:I:");
if (c==-1) break;
switch (c) {
case 'c':
chroot_to=optarg;
break;
#ifdef SUPPORT_HTTPS
case 'X':
sshd=optarg;
if (isdigit(*sshd)) {
while (isdigit(*sshd)) ++sshd;
++sshd;
}
/* fall through */
#endif
case 'C':
found=1;
break;
case 'u':
new_uid=optarg;
break;
case '?':
break;
}
}
optind=0;
forksock[0]=forksock[1]=-1;
if (found) {
if (socketpair(AF_UNIX,SOCK_STREAM,0,forksock)==-1)
panic("socketpair");
switch (fork()) {
case -1:
panic("fork");
case 0:
close(forksock[0]);
{
int64 savedir;
buffer fsb;
/* we want to drop privileges here unless we are configured to
* support sshd mode, in which case sshd won't work if we
* change our uid before executing it */
#ifdef SUPPORT_HTTPS
if (!sshd) {
#endif
#ifndef __MINGW32__
if (chroot_to) { chdir(chroot_to); chroot(chroot_to); }
if (prepare_switch_uid(new_uid)==-1 || switch_uid()==-1) panic("switch_uid failed");
#endif
#ifdef SUPPORT_HTTPS
#ifndef __MINGW32__
} else {
if (prepare_switch_uid(new_uid)==-1) panic("switch_uid failed");
#endif
}
#endif
if (!io_readfile(&savedir,".")) panic("open()");
buffer_init(&fsb,(void*)read,forksock[1],fsbuf,sizeof fsbuf);
while (1) {
pid_t r;
do {
r=waitpid(-1,0,WNOHANG);
} while (r!=0 && r!=-1);
forkslave(forksock[1],&fsb,savedir,chroot_to);
fchdir(savedir);
}
}
break;
default:
close(forksock[1]);
break;
}
}
}
#if 0
{ /* debug test for the forkslave code */
int64 fd;
uint32 a; uint16 b;
char* req="GET /?/ HTTP/1.0\r\nHost: localhost:80\r\n\r\n";
char* dir="default";
char* ra="127.0.0.1";
a=strlen(req); write(forksock[0],&a,4);
a=strlen(dir); write(forksock[0],&a,4);
a=strlen(ra); write(forksock[0],&a,4);
write(forksock[0],req,strlen(req));
write(forksock[0],dir,strlen(dir));
write(forksock[0],ra,strlen(ra));
b=12345; write(forksock[0],&b,2);
b=80; write(forksock[0],&b,2);
read(forksock[0],&a,4);
buffer_puts(buffer_1,"code ");
buffer_putulong(buffer_1,a);
buffer_putnlflush(buffer_1);
read(forksock[0],&a,4);
if (a) {
char* c=alloca(a+1);
read(forksock[0],c,a);
buffer_put(buffer_1,c,a);
buffer_putnlflush(buffer_1);
} else {
read(forksock[0],&a,4); /* PID */
fd=io_receivefd(forksock[0]);
if (fd==-1)
buffer_putsflush(buffer_2,"received no file descriptor for CGI\n");
else {
char buf[1024];
int l;
while ((l=read(fd,buf,sizeof buf))) {
write(1,buf,l);
}
}
}
}
exit(0);
#endif
#endif
s=socket_tcp6();
#ifdef __broken_itojun_v6__
#ifdef SUPPORT_FTP
f4=socket_tcp4();
#endif
s4=socket_tcp4();
#endif
#ifndef __MINGW32__
signal(SIGPIPE,SIG_IGN);
{
struct sigaction sa;
byte_zero(&sa,sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler=sighandler;
sigaction(SIGINT,&sa,0);
sigaction(SIGHUP,&sa,0);
}
if (!geteuid()) {
struct rlimit orig,rl;
long l;
getrlimit(RLIMIT_NOFILE,&orig);
#ifdef RLIMIT_NPROC
rl.rlim_cur=RLIM_INFINITY; rl.rlim_max=RLIM_INFINITY;
setrlimit(RLIMIT_NPROC,&rl);
#endif
for (l=orig.rlim_max; l<20000; l+=500) {
rl.rlim_cur=l; rl.rlim_max=l;
if (setrlimit(RLIMIT_NOFILE,&rl)==-1) {
if (errno==EPERM) {
/* We run as root but we still get EPERM? That only happens
* on linux-vserver. There is no good way to handle this, so
* we'll set the soft limit to the hard limit */
orig.rlim_cur=orig.rlim_max;
setrlimit(RLIMIT_NOFILE,&orig);
}
break;
}
}
}
#endif
byte_zero(ip,16);
port=0; fport=0; sport=0; scope_id=0;
logging=1;
#if !defined(__linux__)
optind=1;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
optreset=1;
#endif
#endif
for (;;) {
int i;
int c=getopt(argc,argv,"HP:hnfFi:p:vVdDtT:c:u:Uaw:sSO:C:lLeEr:o:N:m:A:X:I:");
if (c==-1) break;
switch (c) {
case 'L':
limit_to_lan=1;
break;
case 'U':
nouploads=1;
break;
case 'a':
chmoduploads=1;
break;
case 'n':
logging=0;
break;
case 'u':
new_uid=optarg;
break;
case 'c':
chroot_to=optarg;
break;
case 'm':
mimetypesfilename=optarg;
break;
case 'P':
i=scan_ulonglong(optarg,&prefetchquantum);
if (i==0) {
buffer_puts(buffer_2,"gatling: warning: could not parse prefetch quantum");
buffer_puts(buffer_2,optarg+i+1);
buffer_putsflush(buffer_2,".\n");
}
if (optarg[i]=='M') prefetchquantum*=1024*1024;
if (optarg[i]=='G') prefetchquantum*=1024*1024*1024;
break;
case 'i':
i=scan_ip6if(optarg,ip,&scope_id);
if (optarg[i]!=0) {
buffer_puts(buffer_2,"gatling: warning: could not parse IP address ");
buffer_puts(buffer_2,optarg+i+1);
buffer_putsflush(buffer_2,".\n");
}
break;
case 'I':
defaultindex=optarg;
break;
case 'p':
if (lastopt==FTP)
i=scan_ushort(optarg,&fport);
else if (lastopt==SMB)
i=scan_ushort(optarg,&sport);
#ifdef SUPPORT_HTTPS
else if (lastopt==HTTPS)
i=scan_ushort(optarg,&httpsport);
#endif
else
i=scan_ushort(optarg,&port);
if (i==0) {
buffer_puts(buffer_2,"gatling: warning: could not parse port ");
buffer_puts(buffer_2,optarg+i+1);
buffer_putsflush(buffer_2,".\n");
}
break;
case 'v': virtual_hosts=1; break;
case 'V': virtual_hosts=-1; break;
case 't': transproxy=1; break;
case 'd': directory_index=1; break;
case 'D': directory_index=-1; break;
#ifdef SUPPORT_FTP
case 'f': doftp=1; lastopt=FTP; break;
case 'F': doftp=-1; break;
case 'l':
askforpassword=1;
break;
#endif
#ifdef SUPPORT_HTTPS
case 'e': dohttps=1; lastopt=HTTPS; break;
case 'E': dohttps=-1; break;
case 'X': sshd=optarg;
if (isdigit(sshd[0])) {
i=scan_ulong(optarg,&ssh_timeout);
if (sshd[i]!=',' && sshd[i]!=' ') goto usage;
sshd+=i+1;
} else ssh_timeout=2;
break;
#endif
case 'H': dohttp=-1; break;
case 's': dosmb=1; lastopt=SMB; break;
case 'S': dosmb=-1; break;
case 'T':
i=scan_ulong(optarg,doftp?&ftptimeout_secs:&timeout_secs);
if (i==0) {
buffer_puts(buffer_2,"gatling: warning: could not parse timeout in seconds ");
buffer_puts(buffer_2,optarg+i+1);
buffer_putsflush(buffer_2,".\n");
}
break;
#ifdef SUPPORT_SMB
case 'w':
if (str_len(optarg)>12)
buffer_putsflush(buffer_2,"gatling: workgroup name too long (12 max)\n");
else
str_copy(workgroup,optarg);
break;
#endif
#ifdef SUPPORT_CGI
case 'C':
errno=0;
if (add_cgi(optarg)) {
if (errno==ENOMEM)
buffer_putmflush(buffer_2,"gatling: out of memory\n");
else
buffer_putmflush(buffer_2,"gatling: could not parse `",optarg,"': expected something like `\\.cgi$'\n");
}
break;
#endif
#ifdef SUPPORT_PROXY
case 'O':
errno=0;
if (add_proxy(optarg)) {
if (errno==ENOMEM)
buffer_putmflush(buffer_2,"gatling: out of memory\n");
else
buffer_putmflush(buffer_2,"gatling: could not parse `",optarg,"': expected something like `127.0.0.1/8001/\\.jsp'\n");
}
break;
#endif
#ifdef SUPPORT_FALLBACK_REDIR
case 'r':
if (strstr(optarg,"://"))
redir=optarg;
else
buffer_putmflush(buffer_2,"gatling: -r needs something like http://fallback.example.com as argument!\n");
break;
#endif
#ifdef SUPPORT_MULTIPROC
case 'N':
i=scan_ulong(optarg,&instances);
if (i==0) {
buffer_puts(buffer_2,"gatling: warning: could not parse instances at ");
buffer_puts(buffer_2,optarg+i+1);
buffer_putsflush(buffer_2,".\n");
}
break;
#endif
case 'A':
i=scan_uint(optarg,&max_requests_per_minute);
if (i==0) {
buffer_puts(buffer_2,"gatling: warning: could not parse max_requests_per_minute at ");
buffer_puts(buffer_2,optarg+i+1);
buffer_putsflush(buffer_2,".\n");
}
break;
#ifdef SUPPORT_THREADED_OPEN
case 'o':
#endif
default:
case '?':
case 'h':
usage:
buffer_putsflush(buffer_2,
"usage: gatling [-hnvVtdDfFUa] [-i bind-to-ip] [-p bind-to-port] [-T seconds]\n"
" [-u uid] [-c dir] [-w workgroup] [-P bytes] [-O ip/port/regex]\n"
" [-r redirurl] [-N processes] [-I filename]\n"
"\n"
"\t-h\tprint this help\n"
"\t-v\tenable virtual hosting mode\n"
"\t-V\tdisable virtual hosting mode\n"
"\t\t(default is to try both)\n"
"\t-t\ttransproxy mode: do not replace :port in Host headers\n"
"\t-d\tgenerate directory index\n"
"\t-D\tdo not generate directory index\n"
"\t\t(default is -d unless in virtual hosting mode)\n"
"\t-T n\tset timeout in seconds (0 to disable, default 23)\n"
"\t-u uid\tswitch to this UID after binding\n"
"\t-c dir\tchroot to dir after binding\n"
"\t-n\tdo not produce logging output\n"
"\t-f\tprovide FTP (default); next -p is meant for the FTP port (default: 21)\n"
"\t-F\tdo not provide FTP\n"
"\t-U\tdisallow FTP uploads, even to world writable directories\n"
"\t-a\tchmod go+r uploaded files, so they can be downloaded immediately\n"
"\t-P n\tenable experimental prefetching code (may actually be slower)\n"
"\t-l\task for password (FTP server; work around buggy proxies)\n"
"\t-m fn\tparse fn as mime.types style mime type database\n"
"\t-A rpm\ttarpit clients if they have more than rpm request per minute\n"
#ifdef SUPPORT_MULTIPROC
"\t-N n\tfork n instances of gatling\n"
#endif
#ifdef SUPPORT_CGI
"\t-C regex\tregex for local CGI execution (\"\\.cgi\")\n"
"\t\tuse -C+x to assume executables are CGIs\n"
#endif
"\t-I name\talso try name, used for \"index.php\" etc\n"
#ifdef SUPPORT_PROXY
"\t-O [flag/]ip/port/regex\tregex for proxy mode (\"F/127.0.0.1/8001/\\.jsp$\")\n"
"\t-O [flag/]|filename|regex\tregex for proxy mode (\"F/|/tmp/php.sock|\\.jsp$\")\n"
"\t\tflags: F - FastCGI, S - SCGI, J - JSP\n"
#endif
#ifdef SUPPORT_SMB
"\t-s\tprovide SMB service (default)\n"
"\t-S\tdo not provide SMB service\n"
"\t-w name\tset SMB workgroup\n"
#endif
#ifdef SUPPORT_HTTPS
"\t-e\tprovide encryption (https://...)\n"
"\t-E\tdo not provide encryption\n"
"\t-X timeout,sshd (\"2,/opt/diet/sbin/sshd -u0\")\n"
"\t\tforward TLS socket to sshd if no activity after connect\n"
#endif
#ifdef SUPPORT_FALLBACK_REDIR
"\t-r url\tinstead of a 404, generate a redirect to url+localpart\n"
#endif
"\t-L\tonly accept connections from localhost or link/site local reserved IP addresses\n"
);
return 0;
}
}
#ifdef SUPPORT_SMB
{
iconv_t i=iconv_open("UTF-16LE","ISO-8859-1");
size_t X,Y;
char* x,* y;
X=str_len(workgroup)+1;
Y=sizeof(workgroup_utf16);
x=workgroup;
y=workgroup_utf16;
#ifdef __sun__
if (iconv(i,(const char**)&x,&X,&y,&Y)) panic("UTF-16 conversion of workgroup failed.\n");
#else
if (iconv(i,&x,&X,&y,&Y)) panic("UTF-16 conversion of workgroup failed.\n");
#endif
wglen=str_len(workgroup);
wglen16=sizeof(workgroup_utf16)-Y;
iconv_close(i);
}
{
extern iconv_t wc2utf8;
extern iconv_t utf82wc2;
wc2utf8=iconv_open("UTF-8","UTF-16LE");
utf82wc2=iconv_open("UTF-16LE","UTF-8");
}
#endif
if (!directory_index)
directory_index=virtual_hosts<1;
else if (directory_index==-1)
directory_index=0;
if (timeout_secs) {
taia_now(&last);
byte_copy(&next,sizeof(next),&last);
next.sec.x += timeout_secs;
byte_copy(&nextftp,sizeof(next),&last);
nextftp.sec.x += ftptimeout_secs;
#ifdef SUPPORT_HTTPS
if (ssh_timeout) {
byte_copy(&nexthttps,sizeof(now),&now);
nexthttps.sec.x += ssh_timeout;
} else
byte_copy(&nexthttps,sizeof(next),&next);
#endif
byte_copy(&tick,sizeof(next),&last);
++tick.sec.x;
}
{
#ifdef __MINGW32__
int euid=0;
#else
uid_t euid=geteuid();
#endif
if (port==0)
port=euid?8000:80;
if (fport==0)
fport=euid?2121:21;
#ifdef SUPPORT_SMB
if (sport==0)
sport=445;
#endif
#ifdef SUPPORT_HTTPS
if (httpsport==0)
httpsport=euid?4433:443;
#endif
}
#ifdef __broken_itojun_v6__
if (byte_equal(ip,12,V4mappedprefix) || byte_equal(ip,16,V6any)) {
if (byte_equal(ip,16,V6any)) {
if (dohttp==-1) {
close(s); s=-1;
} else {
if (socket_bind6_reuse(s,ip,port,scope_id)==-1 || socket_listen(s,16)==-1)
panic("socket_bind6_reuse for http");
}
#ifdef SUPPORT_FTP
f=socket_tcp6();
if (doftp>=0)
if (socket_bind6_reuse(f,ip,fport,scope_id)==-1 || socket_listen(f,16)==-1) {
if (doftp==1)
panic("socket_bind6_reuse for ftp");
buffer_putsflush(buffer_2,"warning: could not bind to FTP port; FTP will be unavailable.\n");
io_close(f); f=-1;
}
#endif
} else {
io_close(s); s=-1;
}
if (socket_bind4_reuse(s4,ip+12,port)==-1 || socket_listen(s4,16)==-1)
panic("socket_bind4_reuse");
#ifdef SUPPORT_FTP
if (doftp>=0)
if (socket_bind4_reuse(f4,ip+12,fport)==-1 || socket_listen(f4,16)==-1) {
if (doftp==1)
panic("socket_bind4_reuse");
buffer_putsflush(buffer_2,"warning: could not bind to FTP port; FTP will be unavailable.\n");
io_close(f4); f4=-1;
}
#endif
} else {
if (dohttp==-1) {
close(s);
s=-1;
} else {
if (socket_bind6_reuse(s,ip,port,scope_id)==-1 || socket_listen(s,16)==-1)
panic("socket_bind6_reuse");
}
s4=-1;
#ifdef SUPPORT_FTP
if (doftp>=0)
if (socket_bind6_reuse(f,ip,fport,scope_id)==-1 || socket_listen(f,16)==-1) {
if (doftp==1)
panic("socket_bind6_reuse");
buffer_putsflush(buffer_2,"warning: could not bind to FTP port; FTP will be unavailable.\n");
io_close(f); f=-1;
}
f4=-1;
#endif
}
buffer_putsflush(buffer_2,"WARNING: We are taking heavy losses working around itojun KAME madness here.\n"
" Please consider using an operating system with real IPv6 support instead!\n");
#else
if (dohttp==-1) {
close(s);
s=-1;
} else {
#ifndef __linux__
socket_deferaccept(s,HTTPIN);
#endif
if (socket_bind6_reuse(s,ip,port,0)==-1 || socket_listen(s,16)==-1)
panic("socket_bind6_reuse");
}
#ifdef SUPPORT_FTP
if (doftp>=0) {
f=socket_tcp6();
if (socket_bind6_reuse(f,ip,fport,scope_id)==-1 || socket_listen(f,16)==-1) {
if (doftp==1)
panic("socket_bind6_reuse");
buffer_putsflush(buffer_2,"warning: could not bind to FTP port; FTP will be unavailable.\n");
io_close(f); f=-1;
}
}
#endif
#ifdef SUPPORT_SMB
if (dosmb>=0) {
smbs=socket_tcp6();
#ifndef __linux__
socket_deferaccept(smbs,DATAIN);
#endif
if (socket_bind6_reuse(smbs,ip,sport,scope_id)==-1 || socket_listen(smbs,16)) {
if (dosmb==1)
panic("socket_bind6_reuse");
buffer_putsflush(buffer_2,"warning: could not bind to SMB port; SMB will be unavailable.\n");
io_close(smbs); smbs=-1;
}
}
#endif
#ifdef SUPPORT_HTTPS
if (dohttps>=0) {
httpss=socket_tcp6();
#ifndef __linux__
if (ssh_timeout==0)
socket_deferaccept(httpss,DATAIN);
#endif
if (socket_bind6_reuse(httpss,ip,httpsport,scope_id)==-1 || socket_listen(httpss,16)) {
if (dohttps==1)
panic("socket_bind6_reuse");
buffer_putsflush(buffer_2,"warning: could not bind to HTTPS port; HTTPS will be unavailable.\n");
io_close(httpss); httpss=-1;
}
}
#endif
#endif
#ifndef __MINGW32__
if (prepare_switch_uid(new_uid)==-1)
goto usage;
if (chroot_to) {
if (chroot(chroot_to)==-1)
panic("chroot");
if (chdir("/")==-1)
panic("chdir");
}
if (new_uid && switch_uid()==-1)
panic("switch_uid");
#endif
#ifdef __MINGW32__
_getcwd(origdir,sizeof(origdir));
strncpy(serverroot,origdir,sizeof(serverroot));
// printf("origdir is \"%s\"\n",origdir);
#else
/* get fd for . so we can always fchdir back */
if (!io_readfile(&origdir,".")) panic("open()");
/* note the server root path for CGI $SCRIPT_FILENAME */
if (!getcwd(serverroot,sizeof(serverroot))) {
serverroot[0]='.';
serverroot[1]=0;
}
#endif
#ifdef SUPPORT_MULTIPROC
if (instances>1) {
unsigned long i;
--instances;
if (instances>100) instances=100;
Instances=alloca(instances*sizeof(pid_t));
for (i=0; i<instances; ++i) {
if ((Instances[i]=fork()) == -1)
panic("fork failed");
else if (Instances[i] == 0) {
instances=0;
break;
}
}
#ifdef __broken_itojun_v6__
prepare_listen(s,&ct);
prepare_listen(s4,&ct4);
prepare_listen(f,&fct);
prepare_listen(f4,&fct4);
#else
prepare_listen(s,&ct);
#ifdef SUPPORT_FTP
prepare_listen(f,&fct);
#endif
#ifdef SUPPORT_SMB
prepare_listen(smbs,&sct);
#endif
#ifdef SUPPORT_HTTPS
prepare_listen(httpss,&httpsct);
#endif
#endif
} else {
#endif
#ifdef SUPPORT_MULTIPROC
Instances=0;
instances=0;
#endif
#ifdef __broken_itojun_v6__
prepare_listen(s,&ct);
prepare_listen(s4,&ct4);
prepare_listen(f,&fct);
prepare_listen(f4,&fct4);
#else
prepare_listen(s,&ct);
#ifdef SUPPORT_FTP
prepare_listen(f,&fct);
#endif
#ifdef SUPPORT_SMB
prepare_listen(smbs,&sct);
#endif
#ifdef SUPPORT_HTTPS
prepare_listen(httpss,&httpsct);
#endif
#endif
#ifdef SUPPORT_MULTIPROC
}
#endif
{
char buf[IP6_FMT];
if (s!=-1) {
buffer_puts(buffer_1,"starting_up 0 ");
buffer_put(buffer_1,buf,fmt_ip6c(buf,ip));
buffer_puts(buffer_1," ");
buffer_putulong(buffer_1,port);
buffer_putnlflush(buffer_1);
}
if (f!=-1) {
buffer_puts(buffer_1,"start_ftp 0 ");
buffer_put(buffer_1,buf,fmt_ip6c(buf,ip));
buffer_puts(buffer_1," ");
buffer_putulong(buffer_1,fport);
buffer_putnlflush(buffer_1);
}
#ifdef SUPPORT_SMB
if (smbs!=-1) {
buffer_puts(buffer_1,"start_smb 0 ");
buffer_put(buffer_1,buf,fmt_ip6c(buf,ip));
buffer_puts(buffer_1," ");
buffer_putulong(buffer_1,sport);
buffer_putnlflush(buffer_1);
}
#endif
#ifdef SUPPORT_HTTPS
if (httpss!=-1) {
buffer_puts(buffer_1,"start_https 0 ");
buffer_put(buffer_1,buf,fmt_ip6c(buf,ip));
buffer_puts(buffer_1," ");
buffer_putulong(buffer_1,httpsport);
buffer_putnlflush(buffer_1);
}
#endif
}
connections=1;
#if !defined(__OPTIMIZE__) && defined(__linux__)
/* This happens only if the code was compiled without optimization */
/* make sure we can dump core even if we switched uid */
prctl(PR_SET_DUMPABLE,1,0,0);
/* and make sure we don't have a core dump size limit in place */
{
struct rlimit orig;
orig.rlim_cur=orig.rlim_max=RLIM_INFINITY;
setrlimit(RLIMIT_CORE,&orig);
}
#endif
for (;;) {
int events; /* accept new connections asap */
int64 i;
events=0;
if (fini==2) {
--connections;
io_close(s);
#ifdef __broken_itojun_v6__
io_close(s4);
#endif
#ifdef SUPPORT_FTP
io_close(f);
#ifdef __broken_itojun_v6__
io_close(f4);
#endif
#endif
#ifdef SUPPORT_SMB
io_close(smbs);
#endif
buffer_puts(buffer_1,"closing_server_sockets ");
buffer_putulong(buffer_1,connections);
buffer_putnlflush(buffer_1);
fini=0;
}
if (!connections) fini=1;
if (fini) {
buffer_putsflush(buffer_1,"stopping\n");
break;
}
if (timeout_secs)
io_waituntil(tick);
else
io_wait();
taia_now(&now);
if (timeout_secs) {
if (now.sec.x != last.sec.x) {
cps=cps1; cps1=0;
rps=rps1; rps1=0;
eps=eps1; eps1=0;
tin=tin1; tin1=0;
tout=tout1; tout1=0;
byte_copy(&last,sizeof(now),&now);
byte_copy(&next,sizeof(now),&now);
next.sec.x += timeout_secs;
byte_copy(&nextftp,sizeof(now),&now);
nextftp.sec.x += ftptimeout_secs;
#if defined(SUPPORT_HTTPS) && !defined(SUPPORT_MULTIPROC)
if (ssh_timeout) {
byte_copy(&nexthttps,sizeof(now),&now);
nexthttps.sec.x += ssh_timeout;
} else
byte_copy(&nexthttps,sizeof(next),&next);
#endif
byte_copy(&tick,sizeof(next),&now);
++tick.sec.x;
while ((i=io_timeouted())!=-1) {
struct http_data* x;
#if defined(SUPPORT_HTTPS) && !defined(SUPPORT_MULTIPROC)
if (ssh_timeout && (x=io_getcookie(i)) && x->t == HTTPSACCEPT_CHECK) {
if (logging) {
char numbuf[FMT_ULONG];
numbuf[fmt_ulong(numbuf,i)]=0;
buffer_putmflush(buffer_1,"timeout/sshd ",numbuf,"\n");
}
{
uint32 a=0;
write(forksock[0],&a,4);
io_passfd(forksock[0],i);
}
--https_connections;
}
#endif
if (logging) {
/* shut up in the tarpit case, don't give them the
* satisfaction of spamming our logs too much */
if ((x=io_getcookie(i)) && x->t != PUNISHMENT) {
char numbuf[FMT_ULONG];
numbuf[fmt_ulong(numbuf,i)]=0;
buffer_putmflush(buffer_1,"timeout ",numbuf,"\nclose/timeout ",numbuf,"\n");
}
}
cleanup(i);
}
}
}
/* HANDLE READ EVENTS */
while ((i=io_canread())!=-1) {
struct http_data* H=io_getcookie(i);
++eps1;
if (!H) {
char a[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
buffer_putmflush(buffer_2,"canthappen ",a,": got read event on socket with no cookie!\n");
io_dontwantread(i);
io_close(i);
continue;
}
#ifdef SMDEBUG
{
char a[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
buffer_putmflush(buffer_2,"DEBUG: fd ",a," got READ event, state is ",state2string(H->t),"!\n");
}
#endif
/* This is a speed hack. If we have a LOT of connections, we
* might get swamped handling events and it might take a while for
* us to get around to accepting new connections. Unfortunately,
* the kernel has a limited number of connections that can be
* incoming but not accepted; if more attempts come in, the kernel
* just drops them, and they then run into a timeout and try
* again. To avoid this, we try accepting connections even
* without any events. */
if (++events==10) {
events=0;
if (s!=-1) accept_server_connection(s,(struct http_data*)&ct,ftptimeout_secs,nextftp);
#ifdef SUPPORT_FTP
if (f!=-1) accept_server_connection(f,(struct http_data*)&fct,ftptimeout_secs,nextftp);
#endif
#ifdef SUPPORT_HTTPS
if (httpss!=-1) {
if (ssh_timeout)
accept_server_connection(httpss,(struct http_data*)&httpsct,ssh_timeout,nexthttps);
else
accept_server_connection(httpss,(struct http_data*)&httpsct,ftptimeout_secs,nextftp);
}
#endif
#ifdef __broken_itojun_v6__
if (s4!=-1) accept_server_connection(s4,(struct http_data*)&ct4,ftptimeout_secs,nextftp);
#ifdef SUPPORT_FTP
if (f4!=-1) accept_server_connection(f4,(struct http_data*)&fct4,ftptimeout_secs,nextftp);
#endif
#ifdef SUPPORT_HTTPS
if (httpss4!=-1) {
if (ssh_timeout)
accept_server_connection(httpss4,(struct http_data*)&httpsct4,ssh_timeout,nexthttps);
else
accept_server_connection(httpss4,(struct http_data*)&httpsct4,ftptimeout_secs,nextftp);
}
#endif
#endif
}
if (H->t == HTTPREQUEST
#ifdef SUPPORT_FTP
|| H->t == FTPSLAVE
#endif
#ifdef SUPPORT_SMB
|| H->t == SMBREQUEST
#endif
#ifdef SUPPORT_HTTPS
|| H->t == HTTPSRESPONSE
#endif
)
H->sent_until=H->prefetched_until=0;
#ifdef SUPPORT_PROXY
/* read on PROXYPOST means the CGI sent some data */
if (H->t==PROXYPOST)
handle_read_proxypost(i,H);
else if (H->t==HTTPPOST
#ifdef SUPPORT_HTTPS
|| H->t==HTTPSPOST
#endif
)
/* read on HTTPPOST means the browser sent some more POST data */
handle_read_httppost(i,H);
else
#endif
#ifdef SUPPORT_FTP
if (H->t==FTPPASSIVE)
handle_read_ftppassive(i,H);
else
#endif
#ifdef SUPPORT_HTTPS
if (H->t==HTTPSACCEPT || H->t==HTTPSACCEPT_CHECK)
do_sslaccept(i,H,1);
else
#endif
if (is_server_connection(H->t)) {
#ifdef SUPPORT_HTTPS
if (ssh_timeout && (H->t == HTTPSSERVER6 || H->t == HTTPSSERVER4))
accept_server_connection(i,H,ssh_timeout,nexthttps);
else
#endif
accept_server_connection(i,H,ftptimeout_secs,nextftp);
}
else {
#ifdef SUPPORT_HTTPS
/* This looks wrong but isn't. SSL has renegotiation.
If that happens, we can be waiting for read when we are actually trying to write something */
if (H->t == HTTPSRESPONSE)
handle_write_misc(i,H,prefetchquantum);
else
#endif
handle_read_misc(i,H,ftptimeout_secs,nextftp);
}
}
/* HANDLE WRITABLE EVENTS */
while ((i=io_canwrite())!=-1) {
struct http_data* h=io_getcookie(i);
++eps1;
if (!h) {
char a[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
buffer_putmflush(buffer_2,"canthappen ",a,": got write event on socket with no cookie!\n");
io_dontwantwrite(i);
io_close(i);
continue;
}
#ifdef SMDEBUG
{
char a[FMT_ULONG];
a[fmt_ulong(a,i)]=0;
buffer_putmflush(buffer_2,"DEBUG: fd ",a," got WRITE event, state is ",state2string(h->t),"!\n");
}
#endif
if (++events==10) {
events=0;
accept_server_connection(s,(struct http_data*)&ct,ftptimeout_secs,nextftp);
#ifdef SUPPORT_FTP
if (f!=-1) accept_server_connection(f,(struct http_data*)&fct,ftptimeout_secs,nextftp);
#endif
#ifdef SUPPORT_HTTPS
if (httpss!=-1) {
if (ssh_timeout)
accept_server_connection(httpss,(struct http_data*)&httpsct,ssh_timeout,nexthttps);
else
accept_server_connection(httpss,(struct http_data*)&httpsct,ftptimeout_secs,nextftp);
}
#endif
#ifdef __broken_itojun_v6__
if (s4!=-1) accept_server_connection(s4,(struct http_data*)&ct4,ftptimeout_secs,nextftp);
#ifdef SUPPORT_FTP
if (f4!=-1) accept_server_connection(f4,(struct http_data*)&fct4,ftptimeout_secs,nextftp);
#endif
#ifdef SUPPORT_HTTPS
if (httpss4!=-1) {
if (ssh_timeout)
accept_server_connection(httpss4,(struct http_data*)&httpsct4,ssh_timeout,nexthttps);
else
accept_server_connection(httpss4,(struct http_data*)&httpsct4,ftptimeout_secs,nextftp);
}
#endif
#endif
}
#ifdef SUPPORT_FTP
if (h->t==FTPCONTROL4 || h->t==FTPCONTROL6) {
if (ftptimeout_secs)
io_timeout(i,nextftp);
} else
#endif
if (timeout_secs) {
io_timeout(i,next);
if (h->buddy>0) {
#ifdef SUPPORT_FTP
if (h->t==FTPSLAVE) {
io_timeout(h->buddy,nextftp);
} else
#endif
io_timeout(h->buddy,next);
}
}
#ifdef SUPPORT_PROXY
if (h->t==PROXYSLAVE)
handle_write_proxyslave(i,h);
else if (h->t==PROXYPOST)
handle_write_proxypost(i,h);
else if (h->t==HTTPPOST
#ifdef SUPPORT_HTTPS
|| h->t==HTTPSPOST
#endif
)
handle_write_httppost(i,h);
else
#endif
#ifdef SUPPORT_HTTPS
if (h->t==HTTPSACCEPT)
do_sslaccept(i,h,0);
else if (h->t == HTTPSREQUEST)
handle_read_misc(i,h,ftptimeout_secs,nextftp);
else
#endif
#ifdef SUPPORT_FTP
if (h->t==FTPACTIVE)
handle_write_ftpactive(i,h);
else
#endif
handle_write_misc(i,h,prefetchquantum);
}
}
#ifdef SUPPORT_MULTIPROC
if (instances) {
unsigned long i;
for (i=0; i<instances; ++i)
kill(Instances[i],15);
}
#endif
io_finishandshutdown();
#ifdef SUPPORT_HTTPS
#ifdef USE_OPENSSL
ENGINE_cleanup();
CRYPTO_cleanup_all_ex_data();
ASN1_STRING_TABLE_cleanup();
ERR_free_strings();
#endif
#endif
#ifdef SUPPORT_SMB
{
extern iconv_t wc2utf8;
extern iconv_t utf82wc2;
iconv_close(wc2utf8);
iconv_close(utf82wc2);
}
#endif
_exit(0);
return 0;
}
#if 0
int epoll_create(int i) { return -1; }
#endif