websrvr.c 192 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
 * 
 * Add support for multipart/form-data
48
49
50
51
 *
 * Add support for UNIX-domain sockets for FastCGI
 *
 * Improved Win32 support for POST data... currently will read past Content-Length
52
 * 
deuce's avatar
deuce committed
53
 */
54

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

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

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

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

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

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

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

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

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

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

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

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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;
194
	unsigned char	digest[16];		/* MD5 digest */
195
	BOOL			stale;
196
197
} authentication_request_t;

198
typedef struct  {
199
	int			method;
200
201
202
203
204
205
	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];
206
	authentication_request_t	auth;
207
208
	char		host[128];				/* The requested host. (as used for self-referencing URLs) */
	char		vhost[128];				/* The requested host. (virtual host) */
209
	int			send_location;
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

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

typedef struct  {
253
	SOCKET			socket;
deuce's avatar
deuce committed
254
255
	union xp_sockaddr	addr;
	socklen_t		addr_len;
256
	http_request_t	req;
deuce's avatar
deuce committed
257
	char			host_ip[INET6_ADDRSTRLEN];
258
	char			host_name[128];	/* Resolved remote host */
259
260
	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 */
261
262
263
	user_t			user;
	int				last_user_num;
	time_t			logon_time;
264
	char			username[LEN_NAME+1];
265
	int				last_js_user_num;
266
	char			redir_req[MAX_REQUEST_LINE+1];
267
268
269
270
271

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

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

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

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

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

deuce's avatar
deuce committed
299
300
301
static CRYPT_CONTEXT tls_context = -1;

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

enum { 
	 HTTP_HEAD
	,HTTP_GET
316
317
	,HTTP_POST
	,HTTP_OPTIONS
318
};
319

rswindell's avatar
rswindell committed
320
321
322
static char* methods[] = {
	 "HEAD"
	,"GET"
323
	,"POST"
324
	,"OPTIONS"
rswindell's avatar
rswindell committed
325
326
	,NULL	/* terminator */
};
327

328
enum {
329
330
331
332
	 IS_STATIC
	,IS_CGI
	,IS_JS
	,IS_SSJS
deuce's avatar
deuce committed
333
	,IS_FASTCGI
334
335
};

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

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

392
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
393
enum  {
394
	 NO_LOCATION
395
	,MOVED_PERM
396
	,MOVED_TEMP
397
	,MOVED_STAT
398
399
};

400
401
402
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"};

403
static void respond(http_session_t * session);
404
static BOOL js_setup_cx(http_session_t* session);
405
static BOOL js_setup(http_session_t* session);
406
static char *find_last_slash(char *str);
407
static BOOL check_extra_path(http_session_t * session);
408
static BOOL exec_ssjs(http_session_t* session, char* script);
409
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
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
445
446
447
448
449
450
451
452
453
454
455
456
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;

457
458
        tm2 = gmtime(&t);	/* why not use gmtime_r instead? */
        if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
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
500
501
502
503
504
505
506
507
508
509
510
511
                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);
}
512

513
static int lprintf(int level, const char *fmt, ...)
514
515
516
517
518
519
520
521
{
	va_list argptr;
	char sbuf[1024];

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

523
	if(level <= LOG_ERR) {
524
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf);
525
526
527
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,sbuf);
	}
528
529
530
531
532
533
534
535
536

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

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

537
    return(startup->lputs(startup->cbdata,level,sbuf));
538
539
}

540
541
static int writebuf(http_session_t	*session, const char *buf, size_t len)
{
542
543
	size_t	sent=0;
	size_t	avail;
544

545
	while(sent < len) {
deuce's avatar
deuce committed
546
		ResetEvent(session->outbuf.empty_event);
547
		avail=RingBufFree(&session->outbuf);
deuce's avatar
deuce committed
548
		if(!avail) {
deuce's avatar
deuce committed
549
			WaitForEvent(session->outbuf.empty_event, 1);
deuce's avatar
deuce committed
550
551
			continue;
		}
552
553
		if(avail > len-sent)
			avail=len-sent;
554
		sent+=RingBufWrite(&(session->outbuf), ((const BYTE *)buf)+sent, avail);
555
556
557
558
	}
	return(sent);
}

deuce's avatar
deuce committed
559
560
561
562
#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)
{
563
	char	*estr = NULL;
deuce's avatar
deuce committed
564
565
566
567
568
	int		sock = 0;

	if (status == CRYPT_OK)
		return TRUE;
	if (session != NULL) {
569
570
		if (session->is_tls)
			estr = get_crypt_error(session->tls_sess);
deuce's avatar
deuce committed
571
572
		sock = session->socket;
	}
deuce's avatar
deuce committed
573
	if (estr) {
574
		lprintf(LOG_WARNING, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr);
575
		free_crypt_attrstr(estr);
deuce's avatar
deuce committed
576
	}
deuce's avatar
deuce committed
577
	else
578
		lprintf(LOG_WARNING, "%04d cryptlib error %d at %s:%d", sock, status, file, line);
deuce's avatar
deuce committed
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
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;
		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)
608
609
{
	size_t sent=0;
deuce's avatar
deuce committed
610
	int	tls_sent;
611
	int result;
612
	int sel;
613
614
	fd_set	wr_set;
	struct timeval tv;
deuce's avatar
deuce committed
615
	int status;
616

deuce's avatar
deuce committed
617
	while(sent<len && session->socket!=INVALID_SOCKET) {
618
		FD_ZERO(&wr_set);
deuce's avatar
deuce committed
619
		FD_SET(session->socket,&wr_set);
620
621
622
		/* Convert timeout from ms to sec/usec */
		tv.tv_sec=startup->max_inactivity;
		tv.tv_usec=0;
deuce's avatar
deuce committed
623
		sel=select(session->socket+1,NULL,&wr_set,NULL,&tv);
624
		switch(sel) {
625
			case 1:
deuce's avatar
deuce committed
626
627
628
629
				if (session->is_tls) {
					status = cryptPushData(session->tls_sess, buf+sent, len-sent, &tls_sent);
					if (status == CRYPT_ERROR_TIMEOUT) {
						tls_sent = 0;
deuce's avatar
deuce committed
630
631
						if(!cryptStatusOK(cryptPopData(session->tls_sess, "", 0, &status)))
							lprintf(LOG_NOTICE,"%04d Cryptlib error popping data after timeout",session->socket);
deuce's avatar
deuce committed
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
						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);
649
#ifdef EPIPE
deuce's avatar
deuce committed
650
651
						else if(ERROR_VALUE==EPIPE) 
							lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
652
#endif
deuce's avatar
deuce committed
653
654
655
656
657
658
						else
							lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
						if(failed)
							*failed=TRUE;
						return(sent);
					}
659
660
661
				}
				break;
			case 0:
deuce's avatar
deuce committed
662
				lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",session->socket);
663
664
665
666
				if(failed)
					*failed=TRUE;
				return(sent);
			case -1:
deuce's avatar
deuce committed
667
				lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",session->socket,ERROR_VALUE);
668
669
670
				if(failed)
					*failed=TRUE;
				return(sent);
671
672
673
674
675
		}
		sent+=result;
	}
	if(failed && sent<len)
		*failed=TRUE;
deuce's avatar
deuce committed
676
677
	if(session->is_tls)
		HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session);
678
679
680
	return(sent);
}

681
682
683
#ifdef _WINSOCKAPI_

static WSADATA WSAData;
684
#define SOCKLIB_DESC WSAData.szDescription
685
686
687
688
689
690
691
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
692
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
693
694
695
696
		WSAInitialized=TRUE;
		return (TRUE);
	}

697
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
698
699
700
701
702
703
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
704
#define SOCKLIB_DESC NULL
705
706
707
708
709
710

#endif

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
711
	    startup->status(startup->cbdata,str);
712
713
714
715
716
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
717
		startup->clients(startup->cbdata,protected_uint32_value(active_clients));
718
719
720
721
722
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
723
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
724
725
726
727
728
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
729
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
730
731
732
733
734
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
735
		startup->thread_up(startup->cbdata,TRUE, setuid);
736
737
738
739
}

static void thread_down(void)
{
740
	protected_uint32_adjust(&thread_count,-1);
741
	if(startup!=NULL && startup->thread_up!=NULL)
742
		startup->thread_up(startup->cbdata,FALSE, FALSE);
743
744
}

deuce's avatar
deuce committed
745
746
747
/*********************************************************************/
/* Adds an environment variable to the sessions  cgi_env linked list */
/*********************************************************************/
748
static void add_env(http_session_t *session, const char *name,const char *value)  {
749
	char	newname[129];
750
	char	*p;
751

752
	if(name==NULL || value==NULL)  {
753
		lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
754
755
756
757
758
759
760
761
762
		return;
	}
	SAFECOPY(newname,name);

	for(p=newname;*p;p++)  {
		*p=toupper(*p);
		if(*p=='-')
			*p='_';
	}
deuce's avatar
deuce committed
763
	p=xp_asprintf("%s=%s",newname,value);
764
765
766
767
	if(p==NULL) {
		lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
		return;
	}
768
	strListPush(&session->req.cgi_env,p);
deuce's avatar
deuce committed
769
	free(p);
770
771
}

deuce's avatar
deuce committed
772
773
774
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
775
776
static void init_enviro(http_session_t *session)  {
	char	str[128];
777
778
	union xp_sockaddr sockaddr;
	socklen_t socklen = sizeof(sockaddr);
779
780

	add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
781
782
	getsockname(session->socket, &sockaddr.addr, &socklen);
	sprintf(str,"%d",inet_addrport(&sockaddr));
783
784
	add_env(session,"SERVER_PORT",str);
	add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
785
	if(!strcmp(session->host_name,session->host_ip))
786
787
		add_env(session,"REMOTE_HOST",session->host_name);
	add_env(session,"REMOTE_ADDR",session->host_ip);
788
	add_env(session,"REQUEST_URI",session->req.orig_request_line);
789
790
}

791
/*
deuce's avatar
deuce committed
792
 * Sends string str to socket sock... returns number of bytes written, or 0 on an error
793
794
 * Can not close the socket since it can not set it to INVALID_SOCKET
 */
795
static int bufprint(http_session_t *session, const char *str)
796
{
797
798
799
	int len;

	len=strlen(str);
800
	return(writebuf(session,str,len));
801
802
}

deuce's avatar
deuce committed
803
804
805
806
/**********************************************************/
/* Converts a month name/abbr to the 0-based month number */
/* ToDo: This probobly exists somewhere else already	  */
/**********************************************************/
807
808
809
810
811
812
813
814
815
816
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
817
818
819
/*******************************************************************/
/* Converts a date string in any of the common formats to a time_t */
/*******************************************************************/
820
821
822
static time_t decode_date(char *date)
{
	struct	tm	ti;
823
	char	*token;
824
	char	*last;
825
	time_t	t;
826
827
828
829
830
831
832
833
834

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

835
	token=strtok_r(date,",",&last);
836
837
	if(token==NULL)
		return(0);
838
839
	/* This probobly only needs to be 9, but the extra one is for luck. */
	if(strlen(date)>15) {
840
		/* asctime() */
841
		/* Toss away week day */
842
		token=strtok_r(date," ",&last);
843
844
		if(token==NULL)
			return(0);
845
		token=strtok_r(NULL," ",&last);
846
847
848
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
849
		token=strtok_r(NULL," ",&last);
850
851
852
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
853
		token=strtok_r(NULL,":",&last);
854
855
856
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
857
		token=strtok_r(NULL,":",&last);
858
859
860
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
861
		token=strtok_r(NULL," ",&last);
862
863
864
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
865
		token=strtok_r(NULL,"",&last);
866
867
868
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token)-1900;
869
870
871
	}
	else  {
		/* RFC 1123 or RFC 850 */
872
		token=strtok_r(NULL," -",&last);
873
874
875
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
876
		token=strtok_r(NULL," -",&last);
877
878
879
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
880
		token=strtok_r(NULL," ",&last);
881
882
883
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token);
884
		token=strtok_r(NULL,":",&last);
885
886
887
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
888
		token=strtok_r(NULL,":",&last);
889
890
891
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
892
		token=strtok_r(NULL," ",&last);
893
894
895
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
896
897
898
		if(ti.tm_year>1900)
			ti.tm_year -= 1900;
	}
899

900
	t=time_gm(&ti);
901
	return(t);
902
903
}

deuce's avatar
deuce committed
904
static void open_socket(SOCKET sock, void *cbdata)
905
906
{
	char	error[256];
deuce's avatar
deuce committed
907
908
909
#ifdef SO_ACCEPTFILTER
	struct accept_filter_arg afa;
#endif
910

deuce's avatar
deuce committed
911
912
913
914
915
916
	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 {
917
		if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
918
			lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
919
	}
deuce's avatar
deuce committed
920
921
922
923
924
925
926
927
928
929
930
931
932
#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--;
933
934
}

935
static int close_socket(SOCKET *sock)
936
937
938
{
	int		result;

939
	if(sock==NULL || *sock==INVALID_SOCKET)
940
941
		return(-1);

deuce's avatar
deuce committed
942
943
	/* required to ensure all data is send when SO_LINGER is off (Not functional on Win32) */
	shutdown(*sock,SHUT_RDWR);
944
945
	result=closesocket(*sock);
	*sock=INVALID_SOCKET;
946
	if(startup!=NULL && startup->socket_open!=NULL) {
947
		startup->socket_open(startup->cbdata,FALSE);
948
949
950
951
	}
	sockets--;
	if(result!=0) {
		if(ERROR_VALUE!=ENOTSOCK)
952
			lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
953
954
955
956
957
	}

	return(result);
}

deuce's avatar
deuce committed
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
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);
}

983
984
985
/* Waits for the outbuf to drain */
static void drain_outbuf(http_session_t * session)
{
986
987
	if(session->socket==INVALID_SOCKET)
		return;
988
989
990
	/* Force the output thread to go NOW */
	sem_post(&(session->outbuf.highwater_sem));
	/* ToDo: This should probobly timeout eventually... */
991
	while(RingBufFull(&session->outbuf) && session->socket!=INVALID_SOCKET)
992
993
		SLEEP(1);
	/* Lock the mutex to ensure data has been sent */
994
	while(session->socket!=INVALID_SOCKET && !session->outbuf_write_initialized)
995
		SLEEP(1);
996
	if(session->socket==INVALID_SOCKET)
997
		return;
998
	pthread_mutex_lock(&session->outbuf_write);		/* Win32 Access violation here on Jan-11-2006 - shutting down webserver while in use */
999
1000
1001
	pthread_mutex_unlock(&session->outbuf_write);
}

deuce's avatar
deuce committed
1002
1003
1004
1005
1006
1007
1008
1009
/**************************************************/
/* 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 */
/**************************************************/
1010
1011
static void close_request(http_session_t * session)
{
1012
	time_t		now;
1013
	int			i;
1014

1015
	if(session->req.write_chunked) {
1016
		drain_outbuf(session);
1017
1018
		session->req.write_chunked=0;
		writebuf(session,"0\r\n",3);
1019
		if(session->req.dynamic==IS_SSJS)
1020
			ssjs_send_headers(session,FALSE);
1021
1022
		else
			/* Non-ssjs isn't capable of generating headers during execution */
1023
			writebuf(session, newline, 2);
1024
1025
	}

1026
1027
1028
	/* Force the output thread to go NOW */
	sem_post(&(session->outbuf.highwater_sem));

1029
1030
1031
	if(session->req.ld!=NULL) {
		now=time(NULL);
		localtime_r(&now,&session->req.ld->completed);
1032
		listPushNode(&log_list,session->req.ld);
1033
1034
		session->req.ld=NULL;
	}
1035

1036
1037
1038
	strListFree(&session->req.headers);
	strListFree(&session->req.dynamic_heads);
	strListFree(&session->req.cgi_env);
1039
1040
1041
1042
1043
	if(session->req.post_map != NULL) {
		xpunmap(session->req.post_map);
		session->req.post_data=NULL;
		session->req.post_map=NULL;
	}
1044
	FREE_AND_NULL(session->req.post_data);
1045
1046
	FREE_AND_NULL(session->req.error_dir);
	FREE_AND_NULL(session->req.cgi_dir);
1047
	FREE_AND_NULL(session->req.auth_list);
1048
	FREE_AND_NULL(session->req.realm);
1049
	FREE_AND_NULL(session->req.digest_realm);
deuce's avatar
deuce committed
1050
	FREE_AND_NULL(session->req.fastcgi_socket);
1051

1052
	FREE_AND_NULL(session->req.auth_list);
1053
1054
1055
1056
1057
1058
	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);

1059
1060
1061
1062
	/*
	 * This causes all active http_session_threads to terminate.
	 */
	if((!session->req.keep_alive) || terminate_server) {
1063
		drain_outbuf(session);
deuce's avatar
deuce committed
1064
		close_session_socket(session);
1065
	}
1066
1067
1068
	if(session->socket==INVALID_SOCKET)
		session->finished=TRUE;

deuce's avatar
deuce committed
1069
	if(session->js_cx!=NULL && (session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)) {
1070
		JS_BEGINREQUEST(session->js_cx);
deuce's avatar
deuce committed
1071
		JS_GC(session->js_cx);
1072
		JS_ENDREQUEST(session->js_cx);
deuce's avatar
deuce committed
1073
	}
deuce's avatar
deuce committed
1074
	if(session->subscan!=NULL)
1075
		putmsgptrs(&scfg, &session->user, session->subscan);
deuce's avatar
deuce committed
1076

1077
1078
1079
	if(session->req.fp!=NULL)
		fclose(session->req.fp);

1080
1081
1082
1083
1084
1085
	for(i=0;i<MAX_CLEANUPS;i++) {
		if(session->req.cleanup_file[i]!=NULL) {
			if(!(startup->options&WEB_OPT_DEBUG_SSJS))
				remove(session->req.cleanup_file[i]);
			free(session->req.cleanup_file[i]);
		}
1086
1087
	}

1088
	memset(&session->req,0,sizeof(session->req));
1089
1090
1091
1092
}

static int get_header_type(char *header)
{
1093
	int i;
1094
1095
1096
1097
1098
1099
1100
1101
	for(i=0; headers[i].text!=NULL; i++) {
		if(!stricmp(header,headers[i].text)) {
			return(headers[i].id);
		}
	}
	return(-1);
}

deuce's avatar
deuce committed
1102
/* Opposite of get_header_type() */
1103
1104
static char *get_header(int id) 
{
1105
	int i;
1106
1107
	if(headers[id].id==id)
		return(headers[id].text);
1108
1109
1110
1111
1112
1113
1114
1115
1116

	for(i=0;headers[i].text!=NULL;i++) {
		if(headers[i].id==id) {
			return(headers[i].text);
		}
	}
	return(NULL);
}

1117
1118
static const char* unknown_mime_type="application/octet-stream";

1119
static const char* get_mime_type(char *ext)
1120
1121
1122
{
	uint i;

1123
	if(ext==NULL || mime_types==NULL)
1124
1125
		return(unknown_mime_type);

1126
	for(i=0;mime_types[i]!=NULL;i++)
1127
		if(stricmp(ext+1,mime_types[i]->name)==0)
1128
			return(mime_types[i]->value);
1129
1130

	return(unknown_mime_type);
1131
1132
}

1133
static char* get_cgi_handler(const char* fname)
1134
1135
1136
1137
{
	char*	ext;
	size_t	i;

1138
1139
	if(cgi_handlers==NULL || (ext=getfext(fname))==NULL)
		return(NULL);
1140
	for(i=0;cgi_handlers[i]!=NULL;i++) {
1141
1142
		if(stricmp(cgi_handlers[i]->name, ext+1)==0)
			return(cgi_handlers[i]->value);
1143
	}
1144
	return(NULL);
1145
1146
1147
1148
1149
1150
}

static BOOL get_xjs_handler(char* ext, http_session_t* session)
{
	size_t	i;

deuce's avatar
deuce committed
1151
	if(ext==NULL || xjs_handlers==NULL || ext[0]==0)
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
		return(FALSE);

	for(i=0;xjs_handlers[i]!=NULL;i++) {
		if(stricmp(xjs_handlers[i]->name, ext+1)==0) {
			if(getfname(xjs_handlers[i]->value)==xjs_handlers[i]->value)	/* no path specified */
				SAFEPRINTF2(session->req.xjs_handler,"%s%s",scfg.exec_dir,xjs_handlers[i]->value);
			else
				SAFECOPY(session->req.xjs_handler,xjs_handlers[i]->value);
			return(TRUE);
		}
	}
	return(FALSE);
}

1166
1167
/* This function appends append plus a newline IF the final dst string would have a length less than maxlen */
static void safecat(char *dst, const char *append, size_t maxlen) {
rswindell's avatar