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 203 KB
Newer Older
1 2 3 4 5 6
/* Synchronet Web Server */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *																			*
 * 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										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

22 23 24
/*
 * General notes: (ToDo stuff)
 *
25
 * Support the ident protocol... the standard log format supports it.
26
 *
deuce's avatar
deuce committed
27 28 29
 * 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.
30
 *
31
 * Add support for multipart/form-data
32 33 34 35
 *
 * Add support for UNIX-domain sockets for FastCGI
 *
 * Improved Win32 support for POST data... currently will read past Content-Length
36
 *
deuce's avatar
deuce committed
37
 */
38

deuce's avatar
deuce committed
39
/* Headers for CGI stuff */
40 41
#if defined(__unix__)
	#include <sys/wait.h>		/* waitpid() */
rswindell's avatar
rswindell committed
42 43
	#include <sys/types.h>
	#include <signal.h>			/* kill() */
44 45
#endif

46
#ifndef JAVASCRIPT
47
#define JAVASCRIPT
48 49
#endif

50
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
51
#include "sbbs.h"
52
#include "sbbsdefs.h"
53
#include "sockwrap.h"		/* sendfilesocket() */
deuce's avatar
deuce committed
54
#include "multisock.h"
55
#include "threadwrap.h"
56
#include "semwrap.h"
deuce's avatar
deuce committed
57
#include "xpendian.h"
58
#include "websrvr.h"
deuce's avatar
deuce committed
59
#include "base64.h"
60
#include "md5.h"
61
#include "js_rtpool.h"
62
#include "js_request.h"
deuce's avatar
deuce committed
63
#include "js_socket.h"
64
#include "xpmap.h"
65
#include "xpprintf.h"
deuce's avatar
deuce committed
66
#include "ssl.h"
deuce's avatar
deuce committed
67
#include "fastcgi.h"
68
#include "ver.h"
69

70 71
static const char*	server_name="Synchronet Web Server";
static const char*	newline="\r\n";
72 73
static const char*	http_scheme="http://";
static const size_t	http_scheme_len=7;
deuce's avatar
deuce committed
74 75
static const char*	https_scheme="https://";
static const size_t	https_scheme_len=8;
76 77
static const char*	error_301="301 Moved Permanently";
static const char*	error_302="302 Moved Temporarily";
deuce's avatar
deuce committed
78
static const char*	error_307="307 Temporary Redirect";
79
static const char*	error_404="404 Not Found";
80
static const char*	error_416="416 Requested Range Not Satisfiable";
81
static const char*	error_500="500 Internal Server Error";
deuce's avatar
deuce committed
82
static const char*	error_503="503 Service Unavailable\r\nConnection: close\r\nContent-Length: 0\r\n\r\n";
83
static const char*	unknown=STR_UNKNOWN_USER;
84
static char*		text[TOTAL_TEXT];
deuce's avatar
deuce committed
85
static int len_503 = 0;
86

rswindell's avatar
rswindell committed
87
#define TIMEOUT_THREAD_WAIT		60		/* Seconds */
deuce's avatar
deuce committed
88
#define MAX_REQUEST_LINE		1024	/* NOT including terminator */
89
#define MAX_HEADERS_SIZE		16384	/* Maximum total size of all headers
deuce's avatar
deuce committed
90
										   (Including terminator )*/
91
#define MAX_REDIR_LOOPS			20		/* Max. times to follow internal redirects for a single request */
92
#define MAX_POST_LEN			(4*1048576)	/* Max size of body for POSTS */
93
#define	OUTBUF_LEN				(256*1024)	/* Size of output thread ring buffer */
94

95 96 97
enum {
	 CLEANUP_SSJS_TMP_FILE
	,CLEANUP_POST_DATA
98
	,MAX_CLEANUPS
99
};
100

101
static scfg_t	scfg;
102
static volatile BOOL	http_logging_thread_running=FALSE;
103
static protected_uint32_t active_clients;
104
static protected_uint32_t thread_count;
105 106
static volatile ulong	sockets=0;
static volatile BOOL	terminate_server=FALSE;
deuce's avatar
deuce committed
107
static volatile BOOL	terminated=FALSE;
108
static volatile BOOL	terminate_http_logging_thread=FALSE;
deuce's avatar
deuce committed
109
static struct xpms_set	*ws_set=NULL;
110 111
static char		root_dir[MAX_PATH+1];
static char		error_dir[MAX_PATH+1];
112
static char		cgi_dir[MAX_PATH+1];
113
static char		cgi_env_ini[MAX_PATH+1];
114
static char		default_auth_list[MAX_PATH+1];
115 116
static volatile	time_t	uptime=0;
static volatile	ulong	served=0;
117
static web_startup_t* startup=NULL;
118
static js_server_props_t js_server_props;
119 120
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
121
static str_list_t cgi_env;
122

123
static named_string_t** mime_types;
124 125
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
126

127
/* Logging stuff */
128
link_list_t	log_list;
129 130 131 132 133 134 135
struct log_data {
	char	*hostname;
	char	*ident;
	char	*user;
	char	*request;
	char	*referrer;
	char	*agent;
136
	char	*vhost;
137
	int		status;
138
	off_t	size;
139 140 141
	struct tm completed;
};

142 143 144 145
enum auth_type {
	 AUTHENTICATION_UNKNOWN
	,AUTHENTICATION_BASIC
	,AUTHENTICATION_DIGEST
deuce's avatar
deuce committed
146
	,AUTHENTICATION_TLS_PSK
147 148
};

deuce's avatar
deuce committed
149
char *auth_type_names[] = {
150 151 152
	 "Unknown"
	,"Basic"
	,"Digest"
deuce's avatar
deuce committed
153
	,"TLS-PSK"
154 155 156
	,NULL
};

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
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;
181
	unsigned char	digest[16];		/* MD5 digest */
182
	BOOL			stale;
183 184
} authentication_request_t;

185
typedef struct  {
186
	int			method;
187 188 189 190 191 192
	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];
193
	authentication_request_t	auth;
194 195
	char		host[128];				/* The requested host. (as used for self-referencing URLs) */
	char		vhost[128];				/* The requested host. (virtual host) */
196
	int			send_location;
197
	BOOL			send_content;
deuce's avatar
deuce committed
198 199 200
	BOOL			upgrading;
	char*		location_to_send;
	char*		vary_list;
201
	const char*	mime_type;
202
	str_list_t	headers;
203
	char		status[MAX_REQUEST_LINE+1];
204
	char *		post_data;
205
	struct xpmapping *post_map;
206
	size_t		post_len;
207
	int			dynamic;
208
	char		xjs_handler[MAX_PATH+1];
209
	struct log_data	*ld;
210
	char		request_line[MAX_REQUEST_LINE+1];
211
	char		orig_request_line[MAX_REQUEST_LINE+1];
212
	BOOL		finished;				/* Done processing request. */
213 214
	BOOL		read_chunked;
	BOOL		write_chunked;
215 216
	long		range_start;
	long		range_end;
217
	BOOL		accept_ranges;
deuce's avatar
deuce committed
218
	time_t		if_range;
219
	BOOL		path_info_index;
220

221 222 223
	/* CGI parameters */
	char		query_str[MAX_REQUEST_LINE+1];
	char		extra_path_info[MAX_REQUEST_LINE+1];
224 225
	str_list_t	cgi_env;
	str_list_t	dynamic_heads;
226
	BOOL		got_extra_path;
227

228 229
	/* Dynamically (sever-side JS) generated HTML parameters */
	FILE*	fp;
230
	char		*cleanup_file[MAX_CLEANUPS];
231 232
	BOOL	sent_headers;
	BOOL	prev_write;
233
	BOOL	manual_length;
234

235
	/* webctrl.ini overrides */
236 237
	char	*error_dir;
	char	*cgi_dir;
238
	char	*auth_list;
239
	char	*realm;
240
	char	*digest_realm;
deuce's avatar
deuce committed
241
	char	*fastcgi_socket;
242 243 244
} http_request_t;

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

	/* JavaScript parameters */
	JSRuntime*		js_runtime;
	JSContext*		js_cx;
	JSObject*		js_glob;
264 265
	JSObject*		js_query;
	JSObject*		js_header;
266
	JSObject*		js_cookie;
267
	JSObject*		js_request;
268
	js_callback_t	js_callback;
deuce's avatar
deuce committed
269
	subscan_t		*subscan;
270

271 272 273
	/* Ring Buffer Stuff */
	RingBuf			outbuf;
	sem_t			output_thread_terminated;
274 275
	int				outbuf_write_initialized;
	pthread_mutex_t	outbuf_write;
276

277 278
	/* Client info */
	client_t		client;
deuce's avatar
deuce committed
279 280 281

	/* Synchronization stuff */
	pthread_mutex_t	struct_filled;
deuce's avatar
deuce committed
282 283 284 285 286 287 288

	/* TLS Stuff */
	BOOL			is_tls;
	CRYPT_SESSION	tls_sess;
	BOOL			tls_pending;
	BOOL			peeked_valid;
	char			peeked;
289 290
} http_session_t;

deuce's avatar
deuce committed
291
enum {
292 293
	 HTTP_0_9
	,HTTP_1_0
294
	,HTTP_1_1
295 296 297 298
};
static char* http_vers[] = {
	 ""
	,"HTTP/1.0"
299
	,"HTTP/1.1"
rswindell's avatar
rswindell committed
300
	,NULL	/* terminator */
301
};
deuce's avatar
deuce committed
302 303 304 305 306 307
static char* response_http_vers[] = {
	 ""
	,"HTTP/1.1"
	,"HTTP/1.1"
	,NULL	/* terminator */
};
308

309
enum {
310 311
	 HTTP_HEAD
	,HTTP_GET
312 313
	,HTTP_POST
	,HTTP_OPTIONS
314
};
315

rswindell's avatar
rswindell committed
316 317 318
static char* methods[] = {
	 "HEAD"
	,"GET"
319
	,"POST"
320
	,"OPTIONS"
rswindell's avatar
rswindell committed
321 322
	,NULL	/* terminator */
};
323

324
enum {
325 326 327
	 IS_STATIC
	,IS_CGI
	,IS_SSJS
deuce's avatar
deuce committed
328
	,IS_FASTCGI
329 330
};

deuce's avatar
deuce committed
331
enum {
332 333 334
	 HEAD_DATE
	,HEAD_HOST
	,HEAD_IFMODIFIED
335 336
	,HEAD_LENGTH
	,HEAD_TYPE
337 338 339 340 341
	,HEAD_AUTH
	,HEAD_CONNECTION
	,HEAD_WWWAUTH
	,HEAD_STATUS
	,HEAD_ALLOW
342 343 344 345 346
	,HEAD_EXPIRES
	,HEAD_LASTMODIFIED
	,HEAD_LOCATION
	,HEAD_PRAGMA
	,HEAD_SERVER
347 348
	,HEAD_REFERER
	,HEAD_AGENT
349
	,HEAD_TRANSFER_ENCODING
350 351 352
	,HEAD_ACCEPT_RANGES
	,HEAD_CONTENT_RANGE
	,HEAD_RANGE
deuce's avatar
deuce committed
353
	,HEAD_IFRANGE
354
	,HEAD_COOKIE
deuce's avatar
deuce committed
355 356 357 358
	,HEAD_STS
	,HEAD_UPGRADEINSECURE
	,HEAD_VARY
	,HEAD_CSP
359 360 361 362 363 364
};

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

395
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
396
enum  {
397
	 NO_LOCATION
398
	,MOVED_PERM
deuce's avatar
deuce committed
399
	,MOVED_TEMPREDIR
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
		if(GCES_level < startup->tls_error_level)                                   \
			GCES_level = startup->tls_error_level;                                  \
411 412
		lprintf(GCES_level, "%04d TLS %s", sess->socket, GCES_estr);                \
		free_crypt_attrstr(GCES_estr);                                              \
413 414 415
	}                                                                               \
} while (0)

416 417 418
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"};

419
static void respond(http_session_t * session);
420
static BOOL js_setup_cx(http_session_t* session);
421
static BOOL js_setup(http_session_t* session);
422
static char *find_last_slash(char *str);
423
static BOOL check_extra_path(http_session_t * session);
424
static BOOL exec_ssjs(http_session_t* session, char* script);
425
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
426
static int sess_recv(http_session_t *session, char *buf, size_t length, int flags);
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 472 473
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;

474 475
        tm2 = gmtime(&t);	/* why not use gmtime_r instead? */
        if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
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 527 528
                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);
}
529

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

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

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

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

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

559
    return(startup->lputs(startup->cbdata,level,sbuf));
560 561
}

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

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

deuce's avatar
deuce committed
583 584 585
#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
586

deuce's avatar
deuce committed
587
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
588 589 590
{
	if (status == CRYPT_OK)
		return TRUE;
591 592 593 594
	if (status == ignore)
		return FALSE;
	if (status == ignore2)
		return FALSE;
deuce's avatar
deuce committed
595
	GCES(status, session, action);
deuce's avatar
deuce committed
596 597 598 599 600 601 602 603 604 605 606 607
	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;
deuce's avatar
deuce committed
608 609
		if(rd || wr == NULL) {
			if(session->tls_pending) {
deuce's avatar
deuce committed
610
				*rd_ptr = TRUE;
deuce's avatar
deuce committed
611 612 613 614 615 616 617 618 619 620 621 622 623 624
				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)
625 626
{
	size_t sent=0;
deuce's avatar
deuce committed
627
	int	tls_sent;
628
	int result;
629
	int sel;
630 631
	fd_set	wr_set;
	struct timeval tv;
deuce's avatar
deuce committed
632
	int status;
633
	BOOL local_failed = FALSE;
634

635 636 637 638
	if (failed == NULL)
		failed = &local_failed;

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

703 704 705
#ifdef _WINSOCKAPI_

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

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

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

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

#else /* No WINSOCK */

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

#endif

730 731 732 733 734
static char* server_host_name(void)
{
	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}

735 736 737
static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
738
	    startup->status(startup->cbdata,str);
739 740 741 742 743
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
744
		startup->clients(startup->cbdata,protected_uint32_value(active_clients));
745 746 747 748 749
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
750
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
751 752 753 754 755
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
756
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
757 758 759 760 761
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
762
		startup->thread_up(startup->cbdata,TRUE, setuid);
763 764 765 766
}

static void thread_down(void)
{
767
	protected_uint32_adjust(&thread_count,-1);
768
	if(startup!=NULL && startup->thread_up!=NULL)
769
		startup->thread_up(startup->cbdata,FALSE, FALSE);
770 771
}

deuce's avatar
deuce committed
772 773 774
/*********************************************************************/
/* Adds an environment variable to the sessions  cgi_env linked list */
/*********************************************************************/
775
static void add_env(http_session_t *session, const char *name,const char *value)  {
776
	char	newname[129];
777
	char	*p;
778

779
	if(name==NULL || value==NULL)  {
780
		lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
781 782 783 784 785 786 787 788 789
		return;
	}
	SAFECOPY(newname,name);

	for(p=newname;*p;p++)  {
		*p=toupper(*p);
		if(*p=='-')
			*p='_';
	}
deuce's avatar
deuce committed
790
	p=xp_asprintf("%s=%s",newname,value);
791 792 793 794
	if(p==NULL) {
		lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
		return;
	}
795
	strListPush(&session->req.cgi_env,p);
deuce's avatar
deuce committed
796
	free(p);
797 798
}

deuce's avatar
deuce committed
799 800 801
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
802 803
static void init_enviro(http_session_t *session)  {
	char	str[128];
804 805
	union xp_sockaddr sockaddr;
	socklen_t socklen = sizeof(sockaddr);
806 807

	add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
808 809
	getsockname(session->socket, &sockaddr.addr, &socklen);
	sprintf(str,"%d",inet_addrport(&sockaddr));
810 811
	add_env(session,"SERVER_PORT",str);
	add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
812
	if(!strcmp(session->host_name,session->host_ip))
813 814
		add_env(session,"REMOTE_HOST",session->host_name);
	add_env(session,"REMOTE_ADDR",session->host_ip);
815
	add_env(session,"REQUEST_URI",session->req.orig_request_line);
816 817
}

818
/*
deuce's avatar
deuce committed
819
 * Sends string str to socket sock... returns number of bytes written, or 0 on an error
820 821
 * Can not close the socket since it can not set it to INVALID_SOCKET
 */
822
static int bufprint(http_session_t *session, const char *str)
823
{
824 825 826
	int len;

	len=strlen(str);
827
	return(writebuf(session,str,len));
828 829
}

deuce's avatar
deuce committed
830 831 832 833
/**********************************************************/
/* Converts a month name/abbr to the 0-based month number */
/* ToDo: This probobly exists somewhere else already	  */
/**********************************************************/
834 835 836 837 838 839 840 841 842 843
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
844 845 846
/*******************************************************************/
/* Converts a date string in any of the common formats to a time_t */
/*******************************************************************/
847 848 849
static time_t decode_date(char *date)
{
	struct	tm	ti;
850
	char	*token;
851
	char	*last;
852
	time_t	t;
853 854 855 856 857 858 859 860 861

	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? */

862
	token=strtok_r(date,",",&last);
863 864
	if(token==NULL)
		return(0);
865 866
	/* This probobly only needs to be 9, but the extra one is for luck. */
	if(strlen(date)>15) {
867
		/* asctime() */
868
		/* Toss away week day */
869
		token=strtok_r(date," ",&last);