Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

websrvr.c 197 KB
Newer Older
1 2 3
/* Synchronet Web Server */

/* $Id$ */
4
// vi: tabstop=4
5 6 7 8 9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *																			*
 * 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.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

37 38 39
/*
 * General notes: (ToDo stuff)
 *
40
 * Support the ident protocol... the standard log format supports it.
41
 *
deuce's avatar
deuce committed
42 43 44
 * Add in support to pass connections through to a different webserver...
 *      probobly in access.ars... with like a simplified mod_rewrite.
 *      This would allow people to run apache and Synchronet as the same site.
45
 *
46
 * Add support for multipart/form-data
47 48 49 50
 *
 * Add support for UNIX-domain sockets for FastCGI
 *
 * Improved Win32 support for POST data... currently will read past Content-Length
51
 *
deuce's avatar
deuce committed
52
 */
53

deuce's avatar
deuce committed
54
/* Headers for CGI stuff */
55 56
#if defined(__unix__)
	#include <sys/wait.h>		/* waitpid() */
rswindell's avatar
rswindell committed
57 58
	#include <sys/types.h>
	#include <signal.h>			/* kill() */
59 60
#endif

61
#ifndef JAVASCRIPT
62
#define JAVASCRIPT
63 64
#endif

65
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
66
#include "sbbs.h"
67
#include "sbbsdefs.h"
68
#include "sockwrap.h"		/* sendfilesocket() */
deuce's avatar
deuce committed
69
#include "multisock.h"
70
#include "threadwrap.h"
71
#include "semwrap.h"
deuce's avatar
deuce committed
72
#include "xpendian.h"
73
#include "websrvr.h"
deuce's avatar
deuce committed
74
#include "base64.h"
75
#include "md5.h"
76
#include "js_rtpool.h"
77
#include "js_request.h"
deuce's avatar
deuce committed
78
#include "js_socket.h"
79
#include "xpmap.h"
80
#include "xpprintf.h"
deuce's avatar
deuce committed
81
#include "ssl.h"
deuce's avatar
deuce committed
82
#include "fastcgi.h"
83

84 85
static const char*	server_name="Synchronet Web Server";
static const char*	newline="\r\n";
86 87
static const char*	http_scheme="http://";
static const size_t	http_scheme_len=7;
88 89
static const char*	error_301="301 Moved Permanently";
static const char*	error_302="302 Moved Temporarily";
90
static const char*	error_404="404 Not Found";
91
static const char*	error_416="416 Requested Range Not Satisfiable";
92
static const char*	error_500="500 Internal Server Error";
deuce's avatar
deuce committed
93
static const char*	error_503="503 Service Unavailable\r\nConnection: close\r\nContent-Length: 0\r\n\r\n";
94
static const char*	unknown=STR_UNKNOWN_USER;
deuce's avatar
deuce committed
95
static int len_503 = 0;
96

rswindell's avatar
rswindell committed
97
#define TIMEOUT_THREAD_WAIT		60		/* Seconds */
deuce's avatar
deuce committed
98
#define MAX_REQUEST_LINE		1024	/* NOT including terminator */
99
#define MAX_HEADERS_SIZE		16384	/* Maximum total size of all headers
deuce's avatar
deuce committed
100
										   (Including terminator )*/
101
#define MAX_REDIR_LOOPS			20		/* Max. times to follow internal redirects for a single request */
102
#define MAX_POST_LEN			(4*1048576)	/* Max size of body for POSTS */
103
#define	OUTBUF_LEN				20480	/* Size of output thread ring buffer */
104

105 106 107
enum {
	 CLEANUP_SSJS_TMP_FILE
	,CLEANUP_POST_DATA
108
	,MAX_CLEANUPS
109
};
110

111
static scfg_t	scfg;
112
static volatile BOOL	http_logging_thread_running=FALSE;
113
static protected_uint32_t active_clients;
114
static protected_uint32_t thread_count;
115 116
static volatile ulong	sockets=0;
static volatile BOOL	terminate_server=FALSE;
deuce's avatar
deuce committed
117
static volatile BOOL	terminated=FALSE;
118
static volatile BOOL	terminate_http_logging_thread=FALSE;
deuce's avatar
deuce committed
119
static struct xpms_set	*ws_set=NULL;
120
static char		revision[16];
121 122
static char		root_dir[MAX_PATH+1];
static char		error_dir[MAX_PATH+1];
123
static char		temp_dir[MAX_PATH+1];
124
static char		cgi_dir[MAX_PATH+1];
125
static char		cgi_env_ini[MAX_PATH+1];
126
static char		default_auth_list[MAX_PATH+1];
127 128
static volatile	time_t	uptime=0;
static volatile	ulong	served=0;
129
static web_startup_t* startup=NULL;
130
static js_server_props_t js_server_props;
131 132
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
133
static str_list_t cgi_env;
134

135
static named_string_t** mime_types;
136 137
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
138

139
/* Logging stuff */
140
link_list_t	log_list;
141 142 143 144 145 146 147
struct log_data {
	char	*hostname;
	char	*ident;
	char	*user;
	char	*request;
	char	*referrer;
	char	*agent;
148
	char	*vhost;
149 150 151 152 153
	int		status;
	unsigned int	size;
	struct tm completed;
};

154 155 156 157
enum auth_type {
	 AUTHENTICATION_UNKNOWN
	,AUTHENTICATION_BASIC
	,AUTHENTICATION_DIGEST
deuce's avatar
deuce committed
158
	,AUTHENTICATION_TLS_PSK
159 160
};

deuce's avatar
deuce committed
161
char *auth_type_names[] = {
162 163 164
	 "Unknown"
	,"Basic"
	,"Digest"
deuce's avatar
deuce committed
165
	,"TLS-PSK"
166 167 168
	,NULL
};

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
enum algorithm {
	 ALGORITHM_UNKNOWN
	,ALGORITHM_MD5
	,ALGORITHM_MD5_SESS
};

enum qop_option {
	 QOP_NONE
	,QOP_AUTH
	,QOP_AUTH_INT
	,QOP_UNKNOWN
};

typedef struct {
	enum auth_type	type;
	char			username[(LEN_ALIAS > LEN_NAME ? LEN_ALIAS : LEN_NAME)+1];
	char			password[LEN_PASS+1];
	char			*digest_uri;
	char			*realm;
	char			*nonce;
	enum algorithm	algorithm;
	enum qop_option	qop_value;
	char			*cnonce;
	char			*nonce_count;
193
	unsigned char	digest[16];		/* MD5 digest */
194
	BOOL			stale;
195 196
} authentication_request_t;

197
typedef struct  {
198
	int			method;
199 200 201 202 203 204
	char		virtual_path[MAX_PATH+1];
	char		physical_path[MAX_PATH+1];
	BOOL    	expect_go_ahead;
	time_t		if_modified_since;
	BOOL		keep_alive;
	char		ars[256];
205
	authentication_request_t	auth;
206 207
	char		host[128];				/* The requested host. (as used for self-referencing URLs) */
	char		vhost[128];				/* The requested host. (virtual host) */
208
	int			send_location;
209
	BOOL			send_content;
210
	const char*	mime_type;
211
	str_list_t	headers;
212
	char		status[MAX_REQUEST_LINE+1];
213
	char *		post_data;
214
	struct xpmapping *post_map;
215
	size_t		post_len;
216
	int			dynamic;
217
	char		xjs_handler[MAX_PATH+1];
218
	struct log_data	*ld;
219
	char		request_line[MAX_REQUEST_LINE+1];
220
	char		orig_request_line[MAX_REQUEST_LINE+1];
221
	BOOL		finished;				/* Done processing request. */
222 223
	BOOL		read_chunked;
	BOOL		write_chunked;
224 225
	long		range_start;
	long		range_end;
226
	BOOL		accept_ranges;
deuce's avatar
deuce committed
227
	time_t		if_range;
228
	BOOL		path_info_index;
229

230 231 232
	/* CGI parameters */
	char		query_str[MAX_REQUEST_LINE+1];
	char		extra_path_info[MAX_REQUEST_LINE+1];
233 234
	str_list_t	cgi_env;
	str_list_t	dynamic_heads;
235
	BOOL		got_extra_path;
236

237 238
	/* Dynamically (sever-side JS) generated HTML parameters */
	FILE*	fp;
239
	char		*cleanup_file[MAX_CLEANUPS];
240 241
	BOOL	sent_headers;
	BOOL	prev_write;
242
	BOOL	manual_length;
243

244
	/* webctrl.ini overrides */
245 246
	char	*error_dir;
	char	*cgi_dir;
247
	char	*auth_list;
248
	char	*realm;
249
	char	*digest_realm;
deuce's avatar
deuce committed
250
	char	*fastcgi_socket;
251 252 253
} http_request_t;

typedef struct  {
254
	SOCKET			socket;
deuce's avatar
deuce committed
255 256
	union xp_sockaddr	addr;
	socklen_t		addr_len;
257
	http_request_t	req;
deuce's avatar
deuce committed
258
	char			host_ip[INET6_ADDRSTRLEN];
259
	char			host_name[128];	/* Resolved remote host */
deuce's avatar
deuce committed
260
	int				http_ver;       /* Request HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
261
	BOOL			finished;		/* Do not accept any more imput from client */
262 263 264
	user_t			user;
	int				last_user_num;
	time_t			logon_time;
265
	char			username[LEN_NAME+1];
266
	int				last_js_user_num;
267
	char			redir_req[MAX_REQUEST_LINE+1];
268 269 270 271 272

	/* JavaScript parameters */
	JSRuntime*		js_runtime;
	JSContext*		js_cx;
	JSObject*		js_glob;
273 274
	JSObject*		js_query;
	JSObject*		js_header;
275
	JSObject*		js_cookie;
276
	JSObject*		js_request;
277
	js_callback_t	js_callback;
deuce's avatar
deuce committed
278
	subscan_t		*subscan;
279

280 281 282
	/* Ring Buffer Stuff */
	RingBuf			outbuf;
	sem_t			output_thread_terminated;
283 284
	int				outbuf_write_initialized;
	pthread_mutex_t	outbuf_write;
285

286 287
	/* Client info */
	client_t		client;
deuce's avatar
deuce committed
288 289 290

	/* Synchronization stuff */
	pthread_mutex_t	struct_filled;
deuce's avatar
deuce committed
291 292 293 294 295 296 297

	/* TLS Stuff */
	BOOL			is_tls;
	CRYPT_SESSION	tls_sess;
	BOOL			tls_pending;
	BOOL			peeked_valid;
	char			peeked;
298 299
} http_session_t;

deuce's avatar
deuce committed
300
enum {
301 302
	 HTTP_0_9
	,HTTP_1_0
303
	,HTTP_1_1
304 305 306 307
};
static char* http_vers[] = {
	 ""
	,"HTTP/1.0"
308
	,"HTTP/1.1"
rswindell's avatar
rswindell committed
309
	,NULL	/* terminator */
310
};
deuce's avatar
deuce committed
311 312 313 314 315 316
static char* response_http_vers[] = {
	 ""
	,"HTTP/1.1"
	,"HTTP/1.1"
	,NULL	/* terminator */
};
317

318
enum {
319 320
	 HTTP_HEAD
	,HTTP_GET
321 322
	,HTTP_POST
	,HTTP_OPTIONS
323
};
324

rswindell's avatar
rswindell committed
325 326 327
static char* methods[] = {
	 "HEAD"
	,"GET"
328
	,"POST"
329
	,"OPTIONS"
rswindell's avatar
rswindell committed
330 331
	,NULL	/* terminator */
};
332

333
enum {
334 335 336
	 IS_STATIC
	,IS_CGI
	,IS_SSJS
deuce's avatar
deuce committed
337
	,IS_FASTCGI
338 339
};

deuce's avatar
deuce committed
340
enum {
341 342 343
	 HEAD_DATE
	,HEAD_HOST
	,HEAD_IFMODIFIED
344 345
	,HEAD_LENGTH
	,HEAD_TYPE
346 347 348 349 350
	,HEAD_AUTH
	,HEAD_CONNECTION
	,HEAD_WWWAUTH
	,HEAD_STATUS
	,HEAD_ALLOW
351 352 353 354 355
	,HEAD_EXPIRES
	,HEAD_LASTMODIFIED
	,HEAD_LOCATION
	,HEAD_PRAGMA
	,HEAD_SERVER
356 357
	,HEAD_REFERER
	,HEAD_AGENT
358
	,HEAD_TRANSFER_ENCODING
359 360 361
	,HEAD_ACCEPT_RANGES
	,HEAD_CONTENT_RANGE
	,HEAD_RANGE
deuce's avatar
deuce committed
362
	,HEAD_IFRANGE
363
	,HEAD_COOKIE
364 365 366 367 368 369
};

static struct {
	int		id;
	char*	text;
} headers[] = {
370 371 372
	{ HEAD_DATE,			"Date"					},
	{ HEAD_HOST,			"Host"					},
	{ HEAD_IFMODIFIED,		"If-Modified-Since"		},
373 374
	{ HEAD_LENGTH,			"Content-Length"		},
	{ HEAD_TYPE,			"Content-Type"			},
375 376 377 378 379
	{ HEAD_AUTH,			"Authorization"			},
	{ HEAD_CONNECTION,		"Connection"			},
	{ HEAD_WWWAUTH,			"WWW-Authenticate"		},
	{ HEAD_STATUS,			"Status"				},
	{ HEAD_ALLOW,			"Allow"					},
380 381 382 383 384
	{ HEAD_EXPIRES,			"Expires"				},
	{ HEAD_LASTMODIFIED,	"Last-Modified"			},
	{ HEAD_LOCATION,		"Location"				},
	{ HEAD_PRAGMA,			"Pragma"				},
	{ HEAD_SERVER,			"Server"				},
385 386
	{ HEAD_REFERER,			"Referer"				},
	{ HEAD_AGENT,			"User-Agent"			},
387
	{ HEAD_TRANSFER_ENCODING,			"Transfer-Encoding"			},
388 389 390
	{ HEAD_ACCEPT_RANGES,	"Accept-Ranges"			},
	{ HEAD_CONTENT_RANGE,	"Content-Range"			},
	{ HEAD_RANGE,			"Range"					},
deuce's avatar
deuce committed
391
	{ HEAD_IFRANGE,			"If-Range"				},
392
	{ HEAD_COOKIE,			"Cookie"				},
393
	{ -1,					NULL /* terminator */	},
394 395
};

396
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
397
enum  {
398
	 NO_LOCATION
399
	,MOVED_PERM
400
	,MOVED_TEMP
401
	,MOVED_STAT
402 403
};

404
#define GCES(status, sess, action) do {                                             \
405 406 407 408
	char *GCES_estr;                                                                \
	int GCES_level;                                                                 \
	get_crypt_error_string(status, sess->tls_sess, &GCES_estr, action, &GCES_level);\
	if (GCES_estr) {                                                                \
409 410
		lprintf(GCES_level, "%04d TLS %s", sess->socket, GCES_estr);                \
		free_crypt_attrstr(GCES_estr);                                              \
411 412 413
	}                                                                               \
} while (0)

414 415 416
static char	*days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static char	*months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

417
static void respond(http_session_t * session);
418
static BOOL js_setup_cx(http_session_t* session);
419
static BOOL js_setup(http_session_t* session);
420
static char *find_last_slash(char *str);
421
static BOOL check_extra_path(http_session_t * session);
422
static BOOL exec_ssjs(http_session_t* session, char* script);
423
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
424
static int sess_recv(http_session_t *session, char *buf, size_t length, int flags);
425

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
static time_t
sub_mkgmt(struct tm *tm)
{
        int y, nleapdays;
        time_t t;
        /* days before the month */
        static const unsigned short moff[12] = {
                0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
        };

        /*
         * XXX: This code assumes the given time to be normalized.
         * Normalizing here is impossible in case the given time is a leap
         * second but the local time library is ignorant of leap seconds.
         */

        /* minimal sanity checking not to access outside of the array */
        if ((unsigned) tm->tm_mon >= 12)
                return (time_t) -1;
        if (tm->tm_year < 1970 - 1900)
                return (time_t) -1;

        y = tm->tm_year + 1900 - (tm->tm_mon < 2);
        nleapdays = y / 4 - y / 100 + y / 400 -
            ((1970-1) / 4 - (1970-1) / 100 + (1970-1) / 400);
        t = ((((time_t) (tm->tm_year - (1970 - 1900)) * 365 +
                        moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 +
                tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;

        return (t < 0 ? (time_t) -1 : t);
}

time_t
time_gm(struct tm *tm)
{
        time_t t, t2;
        struct tm *tm2;
        int sec;

        /* Do the first guess. */
        if ((t = sub_mkgmt(tm)) == (time_t) -1)
                return (time_t) -1;

        /* save value in case *tm is overwritten by gmtime() */
        sec = tm->tm_sec;

472 473
        tm2 = gmtime(&t);	/* why not use gmtime_r instead? */
        if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
                return (time_t) -1;

        if (t2 < t || tm2->tm_sec != sec) {
                /*
                 * Adjust for leap seconds.
                 *
                 *     real time_t time
                 *           |
                 *          tm
                 *         /        ... (a) first sub_mkgmt() conversion
                 *       t
                 *       |
                 *      tm2
                 *     /        ... (b) second sub_mkgmt() conversion
                 *   t2
                 *                        --->time
                 */
                /*
                 * Do the second guess, assuming (a) and (b) are almost equal.
                 */
                t += t - t2;
                tm2 = gmtime(&t);

                /*
                 * Either (a) or (b), may include one or two extra
                 * leap seconds.  Try t, t + 2, t - 2, t + 1, and t - 1.
                 */
                if (tm2->tm_sec == sec
                    || (t += 2, tm2 = gmtime(&t), tm2->tm_sec == sec)
                    || (t -= 4, tm2 = gmtime(&t), tm2->tm_sec == sec)
                    || (t += 3, tm2 = gmtime(&t), tm2->tm_sec == sec)
                    || (t -= 2, tm2 = gmtime(&t), tm2->tm_sec == sec))
                        ;        /* found */
                else {
                        /*
                         * Not found.
                         */
                        if (sec >= 60)
                                /*
                                 * The given time is a leap second
                                 * (sec 60 or 61), but the time library
                                 * is ignorant of the leap second.
                                 */
                                ;        /* treat sec 60 as 59,
                                           sec 61 as 0 of the next minute */
                        else
                                /* The given time may not be normalized. */
                                t++;        /* restore t */
                }
        }

        return (t < 0 ? (time_t) -1 : t);
}
527

528 529 530
#if defined(__GNUC__)   // Catch printf-format errors with lprintf
static int lprintf(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif
531
static int lprintf(int level, const char *fmt, ...)
532 533 534 535 536 537 538 539
{
	va_list argptr;
	char sbuf[1024];

	va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
540

541
	if(level <= LOG_ERR) {
542
		char errmsg[sizeof(sbuf)+16];
543
		SAFEPRINTF(errmsg, "web  %s", sbuf);
544
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, errmsg);
545
		if(startup!=NULL && startup->errormsg!=NULL)
546
			startup->errormsg(startup->cbdata,level,errmsg);
547
	}
548 549 550 551 552 553 554 555 556

    if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
        return(0);

#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

557
    return(startup->lputs(startup->cbdata,level,sbuf));
558 559
}

560 561
static int writebuf(http_session_t	*session, const char *buf, size_t len)
{
562 563
	size_t	sent=0;
	size_t	avail;
564

565 566
	if (session->req.sent_headers && session->req.send_content == FALSE)
		return (0);
567
	while(sent < len) {
deuce's avatar
deuce committed
568
		ResetEvent(session->outbuf.empty_event);
569
		avail=RingBufFree(&session->outbuf);
deuce's avatar
deuce committed
570
		if(!avail) {
deuce's avatar
deuce committed
571
			WaitForEvent(session->outbuf.empty_event, 1);
deuce's avatar
deuce committed
572 573
			continue;
		}
574 575
		if(avail > len-sent)
			avail=len-sent;
576
		sent+=RingBufWrite(&(session->outbuf), ((const BYTE *)buf)+sent, avail);
577 578 579 580
	}
	return(sent);
}

deuce's avatar
deuce committed
581 582 583
#define HANDLE_CRYPT_CALL(status, session, action)  handle_crypt_call_except2(status, session, action, CRYPT_OK, CRYPT_OK, __FILE__, __LINE__)
#define HANDLE_CRYPT_CALL_EXCEPT(status, session, action, ignore)  handle_crypt_call_except2(status, session, action, ignore, ignore, __FILE__, __LINE__)
#define HANDLE_CRYPT_CALL_EXCEPT2(status, session, action, ignore, ignore2)  handle_crypt_call_except2(status, session, action, ignore, ignore2, __FILE__, __LINE__)
deuce's avatar
deuce committed
584

deuce's avatar
deuce committed
585
static BOOL handle_crypt_call_except2(int status, http_session_t *session, const char *action, int ignore, int ignore2, const char *file, int line)
deuce's avatar
deuce committed
586 587 588
{
	if (status == CRYPT_OK)
		return TRUE;
589 590 591 592
	if (status == ignore)
		return FALSE;
	if (status == ignore2)
		return FALSE;
deuce's avatar
deuce committed
593
	GCES(status, session, action);
deuce's avatar
deuce committed
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
	return FALSE;
}

static BOOL session_check(http_session_t *session, BOOL *rd, BOOL *wr, unsigned timeout)
{
	BOOL	ret = FALSE;
	BOOL	lcl_rd;
	BOOL	*rd_ptr = rd?rd:&lcl_rd;

	if (session->is_tls) {
		if(wr)
			*wr=1;
		if(rd) {
			if(session->tls_pending) {
				*rd = TRUE;
				return TRUE;
			}
		}
		ret = socket_check(session->socket, rd_ptr, wr, timeout);
		if (ret && *rd_ptr) {
			session->tls_pending = TRUE;
			return TRUE;
		}
		return ret;
	}
	return socket_check(session->socket, rd, wr, timeout);
}

static int sess_sendbuf(http_session_t *session, const char *buf, size_t len, BOOL *failed)
623 624
{
	size_t sent=0;
deuce's avatar
deuce committed
625
	int	tls_sent;
626
	int result;
627
	int sel;
628 629
	fd_set	wr_set;
	struct timeval tv;
deuce's avatar
deuce committed
630
	int status;
631
	BOOL local_failed = FALSE;
632

633 634 635 636
	if (failed == NULL)
		failed = &local_failed;

	while(sent<len && session->socket!=INVALID_SOCKET && *failed == FALSE) {
637
		FD_ZERO(&wr_set);
deuce's avatar
deuce committed
638
		FD_SET(session->socket,&wr_set);
639 640 641
		/* Convert timeout from ms to sec/usec */
		tv.tv_sec=startup->max_inactivity;
		tv.tv_usec=0;
deuce's avatar
deuce committed
642
		sel=select(session->socket+1,NULL,&wr_set,NULL,&tv);
643
		switch(sel) {
644
			case 1:
deuce's avatar
deuce committed
645 646
				if (session->is_tls) {
					status = cryptPushData(session->tls_sess, buf+sent, len-sent, &tls_sent);
647
					GCES(status, session, "pushing data");
deuce's avatar
deuce committed
648 649
					if (status == CRYPT_ERROR_TIMEOUT) {
						tls_sent = 0;
650 651
						if(!cryptStatusOK(status=cryptPopData(session->tls_sess, "", 0, &status))) {
							if (status != CRYPT_ERROR_TIMEOUT && status != CRYPT_ERROR_PARAM2)
652
								GCES(status, session, "popping data after timeout");
653
						}
deuce's avatar
deuce committed
654 655
						status = CRYPT_OK;
					}
deuce's avatar
deuce committed
656
					if(cryptStatusOK(status)) {
657
						HANDLE_CRYPT_CALL_EXCEPT(status = cryptFlushData(session->tls_sess), session, "flushing data", CRYPT_ERROR_COMPLETE);
658 659
						if (cryptStatusError(status))
							*failed=TRUE;
deuce's avatar
deuce committed
660 661
						return tls_sent;
					}
662
					*failed=TRUE;
deuce's avatar
deuce committed
663 664 665 666 667
					result = tls_sent;
				}
				else {
					result=sendsocket(session->socket,buf+sent,len-sent);
					if(result==SOCKET_ERROR) {
668
						if(ERROR_VALUE==ECONNRESET)
deuce's avatar
deuce committed
669
							lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",session->socket);
670
						else if(ERROR_VALUE==ECONNABORTED)
deuce's avatar
deuce committed
671
							lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",session->socket);
672
#ifdef EPIPE
673
						else if(ERROR_VALUE==EPIPE)
deuce's avatar
deuce committed
674
							lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
675
#endif
deuce's avatar
deuce committed
676 677
						else
							lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
678
						*failed=TRUE;
deuce's avatar
deuce committed
679 680
						return(sent);
					}
681 682 683
				}
				break;
			case 0:
deuce's avatar
deuce committed
684
				lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",session->socket);
685
				*failed=TRUE;
686 687
				return(sent);
			case -1:
deuce's avatar
deuce committed
688
				lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",session->socket,ERROR_VALUE);
689
				*failed=TRUE;
690
				return(sent);
691 692 693
		}
		sent+=result;
	}
694
	if(sent<len)
695
		*failed=TRUE;
deuce's avatar
deuce committed
696
	if(session->is_tls)
deuce's avatar
deuce committed
697
		HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session, "flushing data");
698 699 700
	return(sent);
}

701 702 703
#ifdef _WINSOCKAPI_

static WSADATA WSAData;
704
#define SOCKLIB_DESC WSAData.szDescription
705 706 707 708 709 710 711
static BOOL WSAInitialized=FALSE;

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
712
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
713 714 715 716
		WSAInitialized=TRUE;
		return (TRUE);
	}

717
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
718 719 720 721 722 723
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
724
#define SOCKLIB_DESC NULL
725 726 727 728 729 730

#endif

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
731
	    startup->status(startup->cbdata,str);
732 733 734 735 736
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
737
		startup->clients(startup->cbdata,protected_uint32_value(active_clients));
738 739 740 741 742
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
743
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
744 745 746 747 748
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
749
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
750 751 752 753 754
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
755
		startup->thread_up(startup->cbdata,TRUE, setuid);
756 757 758 759
}

static void thread_down(void)
{
760
	protected_uint32_adjust(&thread_count,-1);
761
	if(startup!=NULL && startup->thread_up!=NULL)
762
		startup->thread_up(startup->cbdata,FALSE, FALSE);
763 764
}

deuce's avatar
deuce committed
765 766 767
/*********************************************************************/
/* Adds an environment variable to the sessions  cgi_env linked list */
/*********************************************************************/
768
static void add_env(http_session_t *session, const char *name,const char *value)  {
769
	char	newname[129];
770
	char	*p;
771

772
	if(name==NULL || value==NULL)  {
773
		lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
774 775 776 777 778 779 780 781 782
		return;
	}
	SAFECOPY(newname,name);

	for(p=newname;*p;p++)  {
		*p=toupper(*p);
		if(*p=='-')
			*p='_';
	}
deuce's avatar
deuce committed
783
	p=xp_asprintf("%s=%s",newname,value);
784 785 786 787
	if(p==NULL) {
		lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
		return;
	}
788
	strListPush(&session->req.cgi_env,p);
deuce's avatar
deuce committed
789
	free(p);
790 791
}

deuce's avatar
deuce committed
792 793 794
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
795 796
static void init_enviro(http_session_t *session)  {
	char	str[128];
797 798
	union xp_sockaddr sockaddr;
	socklen_t socklen = sizeof(sockaddr);
799 800

	add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
801 802
	getsockname(session->socket, &sockaddr.addr, &socklen);
	sprintf(str,"%d",inet_addrport(&sockaddr));
803 804
	add_env(session,"SERVER_PORT",str);
	add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
805
	if(!strcmp(session->host_name,session->host_ip))
806 807
		add_env(session,"REMOTE_HOST",session->host_name);
	add_env(session,"REMOTE_ADDR",session->host_ip);
808
	add_env(session,"REQUEST_URI",session->req.orig_request_line);
809 810
}

811
/*
deuce's avatar
deuce committed
812
 * Sends string str to socket sock... returns number of bytes written, or 0 on an error
813 814
 * Can not close the socket since it can not set it to INVALID_SOCKET
 */
815
static int bufprint(http_session_t *session, const char *str)
816
{
817 818 819
	int len;

	len=strlen(str);
820
	return(writebuf(session,str,len));
821 822
}

deuce's avatar
deuce committed
823 824 825 826
/**********************************************************/
/* Converts a month name/abbr to the 0-based month number */
/* ToDo: This probobly exists somewhere else already	  */
/**********************************************************/
827 828 829 830 831 832 833 834 835 836
static int getmonth(char *mon)
{
	int	i;
	for(i=0;i<12;i++)
		if(!stricmp(mon,months[i]))
			return(i);

	return 0;
}

deuce's avatar
deuce committed
837 838 839
/*******************************************************************/
/* Converts a date string in any of the common formats to a time_t */
/*******************************************************************/
840 841 842
static time_t decode_date(char *date)
{
	struct	tm	ti;
843
	char	*token;
844
	char	*last;
845
	time_t	t;
846 847 848 849 850 851 852 853 854

	ti.tm_sec=0;		/* seconds (0 - 60) */
	ti.tm_min=0;		/* minutes (0 - 59) */
	ti.tm_hour=0;		/* hours (0 - 23) */
	ti.tm_mday=1;		/* day of month (1 - 31) */
	ti.tm_mon=0;		/* month of year (0 - 11) */
	ti.tm_year=0;		/* year - 1900 */
	ti.tm_isdst=0;		/* is summer time in effect? */

855
	token=strtok_r(date,",",&last);
856 857
	if(token==NULL)
		return(0);
858 859
	/* This probobly only needs to be 9, but the extra one is for luck. */
	if(strlen(date)>15) {
860
		/* asctime() */
861
		/* Toss away week day */
862
		token=strtok_r(date," ",&last);
863 864
		if(token==NULL)
			return(0);
865
		token=strtok_r(NULL," ",&last);
866 867 868
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
869
		token=strtok_r(NULL," ",&last);
870 871 872
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
873
		token=strtok_r(NULL,":",&last);
874 875 876
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
877
		token=strtok_r(NULL,":",&last);
878 879 880
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
881
		token=strtok_r(NULL," ",&last);
882 883 884
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
885
		token=strtok_r(NULL,"",&last);
886 887 888
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token)-1900;
889 890 891
	}
	else  {
		/* RFC 1123 or RFC 850 */
892
		token=strtok_r(NULL," -",&last);
893 894 895
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
896
		token=strtok_r(NULL," -",&last);
897 898 899
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
900
		token=strtok_r(NULL," ",&last);
901 902 903
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token);
904
		token=strtok_r(NULL,":",&last);
905 906 907
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
908
		token=strtok_r(NULL,":",&last);
909 910 911
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
912
		token=strtok_r(NULL," ",&last);
913 914 915
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
916 917 918
		if(ti.tm_year>1900)
			ti.tm_year -= 1900;
	}
919

920
	t=time_gm(&ti);
921
	return(t);
922 923
}

deuce's avatar
deuce committed
924
static void open_socket(SOCKET sock, void *cbdata)
925 926
{
	char	error[256];
deuce's avatar
deuce committed
927 928 929
#ifdef SO_ACCEPTFILTER
	struct accept_filter_arg afa;
#endif
930

931 932
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
933 934 935 936 937
	if (cbdata != NULL && !strcmp(cbdata, "TLS")) {
		if(set_socket_options(&scfg, sock, "web|http|tls", error, sizeof(error)))
			lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
	}
	else {
938
		if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
939
			lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
940
	}
deuce's avatar
deuce committed
941 942 943 944 945 946 947 948 949 950 951
#ifdef SO_ACCEPTFILTER
	memset(&afa, 0, sizeof(afa));
	strcpy(afa.af_name, "httpready");
	setsockopt(sock, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
#endif

	sockets++;
}

static void close_socket_cb(SOCKET sock, void *cbdata)
{
952 953
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,FALSE);
deuce's avatar
deuce committed
954
	sockets--;
955 956
}

957
static int close_socket(SOCKET *sock)
958 959
{
	int		result;
960 961 962
	char	ch;
	time_t	end = time(NULL) + startup->max_inactivity;
	BOOL	rd;
963