websrvr.c 171 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* websrvr.c */

/* Synchronet Web Server */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
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
37
 *																			*
 * 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.	*
 ****************************************************************************/

38
39
40
/*
 * General notes: (ToDo stuff)
 *
41
 * Support the ident protocol... the standard log format supports it.
42
 *
deuce's avatar
deuce committed
43
44
45
 * 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.
46
47
48
 * 
 * Add support for multipart/form-data
 * 
deuce's avatar
deuce committed
49
 */
50

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

58
#ifndef JAVASCRIPT
59
#define JAVASCRIPT
60
61
#endif

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

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

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

100
101
102
enum {
	 CLEANUP_SSJS_TMP_FILE
	,CLEANUP_POST_DATA
103
	,MAX_CLEANUPS
104
};
105

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

130
static named_string_t** mime_types;
131
132
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
133

134
/* Logging stuff */
135
link_list_t	log_list;
136
137
138
139
140
141
142
struct log_data {
	char	*hostname;
	char	*ident;
	char	*user;
	char	*request;
	char	*referrer;
	char	*agent;
143
	char	*vhost;
144
145
146
147
148
	int		status;
	unsigned int	size;
	struct tm completed;
};

149
150
151
152
enum auth_type {
	 AUTHENTICATION_UNKNOWN
	,AUTHENTICATION_BASIC
	,AUTHENTICATION_DIGEST
deuce's avatar
deuce committed
153
	,AUTHENTICATION_TLS_PSK
154
155
};

deuce's avatar
deuce committed
156
char *auth_type_names[] = {
157
158
159
	 "Unknown"
	,"Basic"
	,"Digest"
deuce's avatar
deuce committed
160
	,"TLS-PSK"
161
162
163
	,NULL
};

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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;
188
	unsigned char	digest[16];		/* MD5 digest */
189
	BOOL			stale;
190
191
} authentication_request_t;

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

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

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

235
	/* webctrl.ini overrides */
236
237
	char	*error_dir;
	char	*cgi_dir;
238
	char	*auth_list;
239
	char	*realm;
240
	char	*digest_realm;
241
242
243
} http_request_t;

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

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

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

275
276
	/* Client info */
	client_t		client;
deuce's avatar
deuce committed
277
278
279

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

	/* TLS Stuff */
	BOOL			is_tls;
	CRYPT_SESSION	tls_sess;
	BOOL			tls_pending;
	BOOL			peeked_valid;
	char			peeked;
287
288
} http_session_t;

deuce's avatar
deuce committed
289
290
291
static CRYPT_CONTEXT tls_context = -1;

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
302
303
304
305
};

enum { 
	 HTTP_HEAD
	,HTTP_GET
306
307
	,HTTP_POST
	,HTTP_OPTIONS
308
};
309

rswindell's avatar
rswindell committed
310
311
312
static char* methods[] = {
	 "HEAD"
	,"GET"
313
	,"POST"
314
	,"OPTIONS"
rswindell's avatar
rswindell committed
315
316
	,NULL	/* terminator */
};
317

318
enum {
319
320
321
322
323
324
	 IS_STATIC
	,IS_CGI
	,IS_JS
	,IS_SSJS
};

325
enum { 
326
327
328
	 HEAD_DATE
	,HEAD_HOST
	,HEAD_IFMODIFIED
329
330
	,HEAD_LENGTH
	,HEAD_TYPE
331
332
333
334
335
	,HEAD_AUTH
	,HEAD_CONNECTION
	,HEAD_WWWAUTH
	,HEAD_STATUS
	,HEAD_ALLOW
336
337
338
339
340
	,HEAD_EXPIRES
	,HEAD_LASTMODIFIED
	,HEAD_LOCATION
	,HEAD_PRAGMA
	,HEAD_SERVER
341
342
	,HEAD_REFERER
	,HEAD_AGENT
343
	,HEAD_TRANSFER_ENCODING
344
345
346
	,HEAD_ACCEPT_RANGES
	,HEAD_CONTENT_RANGE
	,HEAD_RANGE
deuce's avatar
deuce committed
347
	,HEAD_IFRANGE
348
	,HEAD_COOKIE
349
350
351
352
353
354
};

static struct {
	int		id;
	char*	text;
} headers[] = {
355
356
357
	{ HEAD_DATE,			"Date"					},
	{ HEAD_HOST,			"Host"					},
	{ HEAD_IFMODIFIED,		"If-Modified-Since"		},
358
359
	{ HEAD_LENGTH,			"Content-Length"		},
	{ HEAD_TYPE,			"Content-Type"			},
360
361
362
363
364
	{ HEAD_AUTH,			"Authorization"			},
	{ HEAD_CONNECTION,		"Connection"			},
	{ HEAD_WWWAUTH,			"WWW-Authenticate"		},
	{ HEAD_STATUS,			"Status"				},
	{ HEAD_ALLOW,			"Allow"					},
365
366
367
368
369
	{ HEAD_EXPIRES,			"Expires"				},
	{ HEAD_LASTMODIFIED,	"Last-Modified"			},
	{ HEAD_LOCATION,		"Location"				},
	{ HEAD_PRAGMA,			"Pragma"				},
	{ HEAD_SERVER,			"Server"				},
370
371
	{ HEAD_REFERER,			"Referer"				},
	{ HEAD_AGENT,			"User-Agent"			},
372
	{ HEAD_TRANSFER_ENCODING,			"Transfer-Encoding"			},
373
374
375
	{ HEAD_ACCEPT_RANGES,	"Accept-Ranges"			},
	{ HEAD_CONTENT_RANGE,	"Content-Range"			},
	{ HEAD_RANGE,			"Range"					},
deuce's avatar
deuce committed
376
	{ HEAD_IFRANGE,			"If-Range"				},
377
	{ HEAD_COOKIE,			"Cookie"				},
378
	{ -1,					NULL /* terminator */	},
379
380
};

381
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
382
enum  {
383
	 NO_LOCATION
384
	,MOVED_PERM
385
	,MOVED_TEMP
386
	,MOVED_STAT
387
388
};

389
390
391
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"};

392
static void respond(http_session_t * session);
393
static BOOL js_setup(http_session_t* session);
394
static char *find_last_slash(char *str);
395
static BOOL check_extra_path(http_session_t * session);
396
static BOOL exec_ssjs(http_session_t* session, char* script);
397
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
398

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
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;

445
446
        tm2 = gmtime(&t);	/* why not use gmtime_r instead? */
        if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
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
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
                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);
}
500

501
static int lprintf(int level, const char *fmt, ...)
502
503
504
505
506
507
508
509
{
	va_list argptr;
	char sbuf[1024];

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

511
	if(level <= LOG_ERR) {
512
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf);
513
514
515
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,sbuf);
	}
516
517
518
519
520
521
522
523
524

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

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

525
    return(startup->lputs(startup->cbdata,level,sbuf));
526
527
}

528
529
static int writebuf(http_session_t	*session, const char *buf, size_t len)
{
530
531
	size_t	sent=0;
	size_t	avail;
532

533
	while(sent < len) {
deuce's avatar
deuce committed
534
		ResetEvent(session->outbuf.empty_event);
535
		avail=RingBufFree(&session->outbuf);
deuce's avatar
deuce committed
536
		if(!avail) {
deuce's avatar
deuce committed
537
			WaitForEvent(session->outbuf.empty_event, 1);
deuce's avatar
deuce committed
538
539
			continue;
		}
540
541
		if(avail > len-sent)
			avail=len-sent;
542
		sent+=RingBufWrite(&(session->outbuf), ((const BYTE *)buf)+sent, avail);
543
544
545
546
	}
	return(sent);
}

deuce's avatar
deuce committed
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
#define HANDLE_CRYPT_CALL(status, session)  handle_crypt_call(status, session, __FILE__, __LINE__)

static BOOL handle_crypt_call(int status, http_session_t *session, const char *file, int line)
{
	int		len = 0;
	char	estr[CRYPT_MAX_TEXTSIZE+1];
	int		sock = 0;

	if (status == CRYPT_OK)
		return TRUE;
	if (session != NULL) {
		if (session->is_tls)
			cryptGetAttributeString(session->tls_sess, CRYPT_ATTRIBUTE_ERRORMESSAGE, estr, &len);
		sock = session->socket;
	}
	estr[len]=0;
	if (len)
		lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr);
	else
		lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d", sock, status, file, line);
	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)
596
597
{
	size_t sent=0;
deuce's avatar
deuce committed
598
	int	tls_sent;
599
	int result;
600
	int sel;
601
602
	fd_set	wr_set;
	struct timeval tv;
deuce's avatar
deuce committed
603
	int status;
604

deuce's avatar
deuce committed
605
	while(sent<len && session->socket!=INVALID_SOCKET) {
606
		FD_ZERO(&wr_set);
deuce's avatar
deuce committed
607
		FD_SET(session->socket,&wr_set);
608
609
610
		/* Convert timeout from ms to sec/usec */
		tv.tv_sec=startup->max_inactivity;
		tv.tv_usec=0;
deuce's avatar
deuce committed
611
		sel=select(session->socket+1,NULL,&wr_set,NULL,&tv);
612
		switch(sel) {
613
			case 1:
deuce's avatar
deuce committed
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
				if (session->is_tls) {
					status = cryptPushData(session->tls_sess, buf+sent, len-sent, &tls_sent);
					if (status == CRYPT_ERROR_TIMEOUT) {
						tls_sent = 0;
						cryptPopData(session->tls_sess, "", 0, &status);
						status = CRYPT_OK;
					}
					if(!HANDLE_CRYPT_CALL(status, session)) {
						HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session);
						if (failed)
							*failed=TRUE;
						return tls_sent;
					}
					result = tls_sent;
				}
				else {
					result=sendsocket(session->socket,buf+sent,len-sent);
					if(result==SOCKET_ERROR) {
						if(ERROR_VALUE==ECONNRESET) 
							lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",session->socket);
						else if(ERROR_VALUE==ECONNABORTED) 
							lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",session->socket);
636
#ifdef EPIPE
deuce's avatar
deuce committed
637
638
						else if(ERROR_VALUE==EPIPE) 
							lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
639
#endif
deuce's avatar
deuce committed
640
641
642
643
644
645
						else
							lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
						if(failed)
							*failed=TRUE;
						return(sent);
					}
646
647
648
				}
				break;
			case 0:
deuce's avatar
deuce committed
649
				lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",session->socket);
650
651
652
653
				if(failed)
					*failed=TRUE;
				return(sent);
			case -1:
deuce's avatar
deuce committed
654
				lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",session->socket,ERROR_VALUE);
655
656
657
				if(failed)
					*failed=TRUE;
				return(sent);
658
659
660
661
662
		}
		sent+=result;
	}
	if(failed && sent<len)
		*failed=TRUE;
deuce's avatar
deuce committed
663
664
	if(session->is_tls)
		HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session);
665
666
667
	return(sent);
}

668
669
670
#ifdef _WINSOCKAPI_

static WSADATA WSAData;
671
#define SOCKLIB_DESC WSAData.szDescription
672
673
674
675
676
677
678
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
679
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
680
681
682
683
		WSAInitialized=TRUE;
		return (TRUE);
	}

684
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
685
686
687
688
689
690
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
691
#define SOCKLIB_DESC NULL
692
693
694
695
696
697

#endif

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
698
	    startup->status(startup->cbdata,str);
699
700
701
702
703
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
704
		startup->clients(startup->cbdata,protected_uint32_value(active_clients));
705
706
707
708
709
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
710
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
711
712
713
714
715
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
716
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
717
718
719
720
721
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
722
		startup->thread_up(startup->cbdata,TRUE, setuid);
723
724
725
726
}

static void thread_down(void)
{
727
	protected_uint32_adjust(&thread_count,-1);
728
	if(startup!=NULL && startup->thread_up!=NULL)
729
		startup->thread_up(startup->cbdata,FALSE, FALSE);
730
731
}

deuce's avatar
deuce committed
732
733
734
/*********************************************************************/
/* Adds an environment variable to the sessions  cgi_env linked list */
/*********************************************************************/
735
static void add_env(http_session_t *session, const char *name,const char *value)  {
736
	char	newname[129];
737
	char	*p;
738

739
	if(name==NULL || value==NULL)  {
740
		lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
741
742
743
744
745
746
747
748
749
		return;
	}
	SAFECOPY(newname,name);

	for(p=newname;*p;p++)  {
		*p=toupper(*p);
		if(*p=='-')
			*p='_';
	}
deuce's avatar
deuce committed
750
	p=xp_asprintf("%s=%s",newname,value);
751
752
753
754
	if(p==NULL) {
		lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
		return;
	}
755
	strListPush(&session->req.cgi_env,p);
deuce's avatar
deuce committed
756
	free(p);
757
758
}

deuce's avatar
deuce committed
759
760
761
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
762
763
764
765
766
767
768
static void init_enviro(http_session_t *session)  {
	char	str[128];

	add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
	sprintf(str,"%d",startup->port);
	add_env(session,"SERVER_PORT",str);
	add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
769
	if(!strcmp(session->host_name,session->host_ip))
770
771
		add_env(session,"REMOTE_HOST",session->host_name);
	add_env(session,"REMOTE_ADDR",session->host_ip);
772
	add_env(session,"REQUEST_URI",session->req.request_line);
773
774
}

775
/*
deuce's avatar
deuce committed
776
 * Sends string str to socket sock... returns number of bytes written, or 0 on an error
777
778
 * Can not close the socket since it can not set it to INVALID_SOCKET
 */
779
static int bufprint(http_session_t *session, const char *str)
780
{
781
782
783
	int len;

	len=strlen(str);
784
	return(writebuf(session,str,len));
785
786
}

deuce's avatar
deuce committed
787
788
789
790
/**********************************************************/
/* Converts a month name/abbr to the 0-based month number */
/* ToDo: This probobly exists somewhere else already	  */
/**********************************************************/
791
792
793
794
795
796
797
798
799
800
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
801
802
803
/*******************************************************************/
/* Converts a date string in any of the common formats to a time_t */
/*******************************************************************/
804
805
806
static time_t decode_date(char *date)
{
	struct	tm	ti;
807
	char	*token;
808
	char	*last;
809
	time_t	t;
810
811
812
813
814
815
816
817
818

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

819
	token=strtok_r(date,",",&last);
820
821
	if(token==NULL)
		return(0);
822
823
	/* This probobly only needs to be 9, but the extra one is for luck. */
	if(strlen(date)>15) {
824
		/* asctime() */
825
		/* Toss away week day */
826
		token=strtok_r(date," ",&last);
827
828
		if(token==NULL)
			return(0);
829
		token=strtok_r(NULL," ",&last);
830
831
832
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
833
		token=strtok_r(NULL," ",&last);
834
835
836
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
837
		token=strtok_r(NULL,":",&last);
838
839
840
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
841
		token=strtok_r(NULL,":",&last);
842
843
844
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
845
		token=strtok_r(NULL," ",&last);
846
847
848
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
849
		token=strtok_r(NULL,"",&last);
850
851
852
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token)-1900;
853
854
855
	}
	else  {
		/* RFC 1123 or RFC 850 */
856
		token=strtok_r(NULL," -",&last);
857
858
859
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
860
		token=strtok_r(NULL," -",&last);
861
862
863
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
864
		token=strtok_r(NULL," ",&last);
865
866
867
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token);
868
		token=strtok_r(NULL,":",&last);
869
870
871
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
872
		token=strtok_r(NULL,":",&last);
873
874
875
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
876
		token=strtok_r(NULL," ",&last);
877
878
879
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
880
881
882
		if(ti.tm_year>1900)
			ti.tm_year -= 1900;
	}
883

884
	t=time_gm(&ti);
885
	return(t);
886
887
}

deuce's avatar
deuce committed
888
static void open_socket(SOCKET sock, void *cbdata)
889
890
{
	char	error[256];
deuce's avatar
deuce committed
891
892
893
#ifdef SO_ACCEPTFILTER
	struct accept_filter_arg afa;
#endif
894

deuce's avatar
deuce committed
895
896
897
898
899
900
	startup->socket_open(startup->cbdata,TRUE);
	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 {
901
		if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
902
			lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
903
	}
deuce's avatar
deuce committed
904
905
906
907
908
909
910
911
912
913
914
915
916
#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)
{
	startup->socket_open(startup->cbdata,FALSE);
	sockets--;
917
918
}

919
static int close_socket(SOCKET *sock)
920
921
922
{
	int		result;

923
	if(sock==NULL || *sock==INVALID_SOCKET)
924
925
		return(-1);

deuce's avatar
deuce committed
926
927
	/* required to ensure all data is send when SO_LINGER is off (Not functional on Win32) */
	shutdown(*sock,SHUT_RDWR);
928
929
	result=closesocket(*sock);
	*sock=INVALID_SOCKET;
930
	if(startup!=NULL && startup->socket_open!=NULL) {
931
		startup->socket_open(startup->cbdata,FALSE);
932
933
934
935
	}
	sockets--;
	if(result!=0) {
		if(ERROR_VALUE!=ENOTSOCK)
936
			lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
937
938
939
940
941
	}

	return(result);
}

deuce's avatar
deuce committed
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
static int close_session_socket(http_session_t *session)
{
	char	buf[1];
	int		len;

	if(session==NULL || session->socket==INVALID_SOCKET)
		return(-1);

	if (session->is_tls) {
		// First, wait for the ringbuffer to drain...
		while(RingBufFull(&session->outbuf) && session->socket!=INVALID_SOCKET) {
			HANDLE_CRYPT_CALL(cryptPopData(session->tls_sess, buf, 1, &len), session);
			SLEEP(1);
		}
		// Now wait for tranmission to complete
		while(pthread_mutex_trylock(&session->outbuf_write) == EBUSY) {
			HANDLE_CRYPT_CALL(cryptPopData(session->tls_sess, buf, 1, &len), session);
			SLEEP(1);
		}
		pthread_mutex_unlock(&session->outbuf_write);
		HANDLE_CRYPT_CALL(cryptDestroySession(session->tls_sess), session);
	}
	return close_socket(&session->socket);
}

967
968
969
/* Waits for the outbuf to drain */
static void drain_outbuf(http_session_t * session)
{
970
971
	if(session->socket==INVALID_SOCKET)
		return;
972
973
974
	/* Force the output thread to go NOW */
	sem_post(&(session->outbuf.highwater_sem));
	/* ToDo: This should probobly timeout eventually... */
975
	while(RingBufFull(&session->outbuf) && session->socket!=INVALID_SOCKET)
976
977
		SLEEP(1);
	/* Lock the mutex to ensure data has been sent */
978
	while(session->socket!=INVALID_SOCKET && !session->outbuf_write_initialized)
979
		SLEEP(1);
980
	if(session->socket==INVALID_SOCKET)
981
		return;
982
	pthread_mutex_lock(&session->outbuf_write);		/* Win32 Access violation here on Jan-11-2006 - shutting down webserver while in use */
983
984
985
	pthread_mutex_unlock(&session->outbuf_write);
}

deuce's avatar
deuce committed
986
987
988
989
990
991
992
993
/**************************************************/
/* End of a single request...					  */
/* This is called at the end of EVERY request	  */
/*  Log the request       						  */
/*  Free request-specific data ie: dynamic stuff  */
/*  Close socket unless it's being kept alive     */
/*   If the socket is closed, the session is done */
/**************************************************/
994
995
static void close_request(http_session_t * session)
{
996
	time_t		now;
997
	int			i;
998

999
	if(session->req.write_chunked) {
1000
		drain_outbuf(session);
1001
1002
		session->req.write_chunked=0;
		writebuf(session,"0\r\n",3);
1003
		if(session->req.dynamic==IS_SSJS)
1004
			ssjs_send_headers(session,FALSE);
1005
1006
		else
			/* Non-ssjs isn't capable of generating headers during execution */
1007
			writebuf(session, newline, 2);
1008
1009
	}

1010
1011
1012
	/* Force the output thread to go NOW */
	sem_post(&(session->outbuf.highwater_sem));

1013
1014
1015
	if(session->req.ld!=NULL) {
		now=time(NULL);
		localtime_r(&now,&session->req.ld->completed);
1016
		listPushNode(&log_list,session->req.ld);
1017
1018
		session->req.ld=NULL;
	}
1019

1020
1021
1022
	strListFree(&session->req.headers);
	strListFree(&session->req.dynamic_heads);
	strListFree(&session->req.cgi_env);
1023
1024
1025
1026
1027
	if(session->req.post_map != NULL) {
		xpunmap(session->req.post_map);
		session->req.post_data=NULL;
		session->req.post_map=NULL;
	}
1028
	FREE_AND_NULL(session->req.post_data);
1029
1030
	FREE_AND_NULL(session->req.error_dir);
	FREE_AND_NULL(session->req.cgi_dir);
1031
	FREE_AND_NULL(session->req.auth_list);
1032
	FREE_AND_NULL(session->req.realm);
1033
	FREE_AND_NULL(session->req.digest_realm);
1034

1035
	FREE_AND_NULL(session->req.auth_list);
1036
1037
1038
1039
1040
1041
	FREE_AND_NULL(session->req.auth.digest_uri);
	FREE_AND_NULL(session->req.auth.cnonce);
	FREE_AND_NULL(session->req.auth.realm);
	FREE_AND_NULL(session->req.auth.nonce);
	FREE_AND_NULL(session->req.auth.nonce_count);

1042
1043
1044
1045
	/*
	 * This causes all active http_session_threads to terminate.
	 */
	if((!session->req.keep_alive) || terminate_server) {
1046
		drain_outbuf(session);
deuce's avatar
deuce committed
1047
		close_session_socket(session);
1048
	}
1049
1050
1051
	if(session->socket==INVALID_SOCKET)
		session->finished=TRUE;

deuce's avatar
deuce committed
1052
	if(session->js_cx!=NULL && (session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)) {
1053
		JS_BEGINREQUEST(session->js_cx);
deuce's avatar
deuce committed
1054
		JS_GC(session->js_cx);
1055
		JS_ENDREQUEST(session->js_cx);
deuce's avatar
deuce committed
1056
	}
deuce's avatar
deuce committed
1057
1058
	if(session->subscan!=NULL)
		putmsgptrs(&scfg, session->user.number, session->subscan);
deuce's avatar
deuce committed
1059

1060
1061
1062
	if(session->req.fp!=NULL)
		fclose(session->req.fp);

1063
1064
1065
1066
1067
1068
	for(i=0;i<MAX_CLEANUPS;i++) {
		if(session->req.cleanup_file[i]!=NULL) {