websrvr.c 187 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"
deuce's avatar
deuce committed
69
#include "xpendian.h"
70
#include "websrvr.h"
deuce's avatar
deuce committed
71
#include "base64.h"
72
#include "md5.h"
73
#include "js_rtpool.h"
74
#include "js_request.h"
deuce's avatar
deuce committed
75
#include "js_socket.h"
76
#include "xpmap.h"
77
#include "xpprintf.h"
deuce's avatar
deuce committed
78
#include "ssl.h"
deuce's avatar
deuce committed
79
#include "fastcgi.h"
80

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

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

102
103
104
enum {
	 CLEANUP_SSJS_TMP_FILE
	,CLEANUP_POST_DATA
105
	,MAX_CLEANUPS
106
};
107

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

132
static named_string_t** mime_types;
133
134
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
135

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

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

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

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

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

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

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

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

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

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

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

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

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

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

deuce's avatar
deuce committed
292
293
294
static CRYPT_CONTEXT tls_context = -1;

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

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

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

321
enum {
322
323
324
325
	 IS_STATIC
	,IS_CGI
	,IS_JS
	,IS_SSJS
deuce's avatar
deuce committed
326
	,IS_FASTCGI
327
328
};

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

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

385
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
386
enum  {
387
	 NO_LOCATION
388
	,MOVED_PERM
389
	,MOVED_TEMP
390
	,MOVED_STAT
391
392
};

393
394
395
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"};

396
static void respond(http_session_t * session);
397
static BOOL js_setup(http_session_t* session);
398
static char *find_last_slash(char *str);
399
static BOOL check_extra_path(http_session_t * session);
400
static BOOL exec_ssjs(http_session_t* session, char* script);
401
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
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
445
446
447
448
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;

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

505
static int lprintf(int level, const char *fmt, ...)
506
507
508
509
510
511
512
513
{
	va_list argptr;
	char sbuf[1024];

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

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

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

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

529
    return(startup->lputs(startup->cbdata,level,sbuf));
530
531
}

532
533
static int writebuf(http_session_t	*session, const char *buf, size_t len)
{
534
535
	size_t	sent=0;
	size_t	avail;
536

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

deuce's avatar
deuce committed
551
552
553
554
#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)
{
555
	char	*estr = NULL;
deuce's avatar
deuce committed
556
557
558
559
560
	int		sock = 0;

	if (status == CRYPT_OK)
		return TRUE;
	if (session != NULL) {
561
562
		if (session->is_tls)
			estr = get_crypt_error(session->tls_sess);
deuce's avatar
deuce committed
563
564
		sock = session->socket;
	}
deuce's avatar
deuce committed
565
	if (estr) {
566
		lprintf(LOG_WARNING, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr);
567
		free_crypt_attrstr(estr);
deuce's avatar
deuce committed
568
	}
deuce's avatar
deuce committed
569
	else
570
		lprintf(LOG_WARNING, "%04d cryptlib error %d at %s:%d", sock, status, file, line);
deuce's avatar
deuce committed
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
596
597
598
599
	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)
600
601
{
	size_t sent=0;
deuce's avatar
deuce committed
602
	int	tls_sent;
603
	int result;
604
	int sel;
605
606
	fd_set	wr_set;
	struct timeval tv;
deuce's avatar
deuce committed
607
	int status;
608

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

673
674
675
#ifdef _WINSOCKAPI_

static WSADATA WSAData;
676
#define SOCKLIB_DESC WSAData.szDescription
677
678
679
680
681
682
683
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
684
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
685
686
687
688
		WSAInitialized=TRUE;
		return (TRUE);
	}

689
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
690
691
692
693
694
695
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
696
#define SOCKLIB_DESC NULL
697
698
699
700
701
702

#endif

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
703
	    startup->status(startup->cbdata,str);
704
705
706
707
708
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
709
		startup->clients(startup->cbdata,protected_uint32_value(active_clients));
710
711
712
713
714
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
715
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
716
717
718
719
720
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
721
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
722
723
724
725
726
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
727
		startup->thread_up(startup->cbdata,TRUE, setuid);
728
729
730
731
}

static void thread_down(void)
{
732
	protected_uint32_adjust(&thread_count,-1);
733
	if(startup!=NULL && startup->thread_up!=NULL)
734
		startup->thread_up(startup->cbdata,FALSE, FALSE);
735
736
}

deuce's avatar
deuce committed
737
738
739
/*********************************************************************/
/* Adds an environment variable to the sessions  cgi_env linked list */
/*********************************************************************/
740
static void add_env(http_session_t *session, const char *name,const char *value)  {
741
	char	newname[129];
742
	char	*p;
743

744
	if(name==NULL || value==NULL)  {
745
		lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
746
747
748
749
750
751
752
753
754
		return;
	}
	SAFECOPY(newname,name);

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

deuce's avatar
deuce committed
764
765
766
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
767
768
static void init_enviro(http_session_t *session)  {
	char	str[128];
769
770
	union xp_sockaddr sockaddr;
	socklen_t socklen = sizeof(sockaddr);
771
772

	add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
773
774
	getsockname(session->socket, &sockaddr.addr, &socklen);
	sprintf(str,"%d",inet_addrport(&sockaddr));
775
776
	add_env(session,"SERVER_PORT",str);
	add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
777
	if(!strcmp(session->host_name,session->host_ip))
778
779
		add_env(session,"REMOTE_HOST",session->host_name);
	add_env(session,"REMOTE_ADDR",session->host_ip);
780
	add_env(session,"REQUEST_URI",session->req.request_line);
781
782
}

783
/*
deuce's avatar
deuce committed
784
 * Sends string str to socket sock... returns number of bytes written, or 0 on an error
785
786
 * Can not close the socket since it can not set it to INVALID_SOCKET
 */
787
static int bufprint(http_session_t *session, const char *str)
788
{
789
790
791
	int len;

	len=strlen(str);
792
	return(writebuf(session,str,len));
793
794
}

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

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

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

892
	t=time_gm(&ti);
893
	return(t);
894
895
}

deuce's avatar
deuce committed
896
static void open_socket(SOCKET sock, void *cbdata)
897
898
{
	char	error[256];
deuce's avatar
deuce committed
899
900
901
#ifdef SO_ACCEPTFILTER
	struct accept_filter_arg afa;
#endif
902

deuce's avatar
deuce committed
903
904
905
906
907
908
	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 {
909
		if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
910
			lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
911
	}
deuce's avatar
deuce committed
912
913
914
915
916
917
918
919
920
921
922
923
924
#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--;
925
926
}

927
static int close_socket(SOCKET *sock)
928
929
930
{
	int		result;

931
	if(sock==NULL || *sock==INVALID_SOCKET)
932
933
		return(-1);

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

	return(result);
}

deuce's avatar
deuce committed
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
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);
}

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

deuce's avatar
deuce committed
994
995
996
997
998
999
1000
1001
/**************************************************/
/* 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 */
/**************************************************/
1002
1003
static void close_request(http_session_t * session)
{
1004
	time_t		now;
1005
	int			i;
1006

1007
	if(session->req.write_chunked) {
1008
		drain_outbuf(session);
1009
1010
		session->req.write_chunked=0;
		writebuf(session,"0\r\n",3);
1011
		if(session->req.dynamic==IS_SSJS)
1012
			ssjs_send_headers(session,FALSE);
1013
1014
		else
			/* Non-ssjs isn't capable of generating headers during execution */
1015
			writebuf(session, newline, 2);
1016
1017
	}

1018
1019
1020
	/* Force the output thread to go NOW */
	sem_post(&(session->outbuf.highwater_sem));

1021
1022
1023
	if(session->req.ld!=NULL) {
		now=time(NULL);
		localtime_r(&now,&session->req.ld->completed);
1024
		listPushNode(&log_list,session->req.ld);
1025
1026
		session->req.ld=NULL;
	}
1027

1028
1029
1030
	strListFree(&session->req.headers);
	strListFree(&session->req.dynamic_heads);
	strListFree(&session->req.cgi_env);
1031
1032
1033
1034
1035
	if(session->req.post_map != NULL) {
		xpunmap(session->req.post_map);
		session->req.post_data=NULL;
		session->req.post_map=NULL;
	}
1036
	FREE_AND_NULL(session->req.post_data);
1037
1038
	FREE_AND_NULL(session->req.error_dir);
	FREE_AND_NULL(session->req.cgi_dir);
1039
	FREE_AND_NULL(session->req.auth_list);
1040
	FREE_AND_NULL(session->req.realm);
1041
	FREE_AND_NULL(session->req.digest_realm);
deuce's avatar
deuce committed
1042
	FREE_AND_NULL(session->req.fastcgi_socket);
1043

1044
	FREE_AND_NULL(session->req.auth_list);
1045
1046
1047
1048
1049
1050
	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);

1051
1052
1053
1054
	/*
	 * This causes all active http_session_threads to terminate.
	 */
	if((!session->req.keep_alive) || terminate_server) {
1055
		drain_outbuf(session);
deuce's avatar
deuce committed
1056
		close_session_socket(session);
1057
	}
1058
1059
1060
	if(session->socket==INVALID_SOCKET)
		session->finished=TRUE;

deuce's avatar
deuce committed
1061
	if(session->js_cx!=NULL && (session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)) {
1062
		JS_BEGINREQUEST(session->js_cx);
deuce's avatar
deuce committed
1063
		JS_GC(session->js_cx);
1064
		JS_ENDREQUEST(session->js_cx);
deuce's avatar
deuce committed
1065
	}
deuce's avatar
deuce committed
1066
1067
	if(session->subscan!=NULL)
		putmsgptrs(&scfg, session->user.number, session->subscan);
deuce's avatar
deuce committed
1068

1069
1070
1071
	if(session->req.fp!=NULL)
		fclose(session->req.fp);

1072
1073
1074
1075
1076
1077
	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]);
		}
1078
1079
	}

1080
	memset(&session->req,0,sizeof(session->req));
1081
1082
1083
1084
}

static int get_header_type(char *header)
{
1085
	int i;
1086
1087
1088
1089
1090
1091
1092
1093
	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
1094
/* Opposite of get_header_type() */
1095
1096
static char *get_header(int id) 
{
1097
	int i;
1098
1099
	if(headers[id].id==id)
		return(headers[id].text);
1100
1101
1102
1103
1104
1105
1106
1107
1108

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

1109
1110
static const char* unknown_mime_type="application/octet-stream";

1111
static const char* get_mime_type(char *ext)
1112
1113
1114
{
	uint i;

1115
	if(ext==NULL || mime_types==NULL)
1116
1117
		return(unknown_mime_type);

1118
	for(i=0;mime_types[i]!=NULL;i++)
1119
		if(stricmp(ext+1,mime_types[i]->name)==0)
1120
			return(mime_types[i]->value);
1121
1122

	return(unknown_mime_type);
1123
1124
}

1125
static char* get_cgi_handler(const char* fname)
1126
1127
1128
1129
{
	char*	ext;
	size_t	i;

1130
1131
	if(cgi_handlers==NULL || (ext=getfext(fname))==NULL)
		return(NULL);
1132
	for(i=0;cgi_handlers[i]!=NULL;i++) {
1133
1134
		if(stricmp(cgi_handlers[i]->name, ext+1)==0)
			return(cgi_handlers[i]->value);
1135
	}
1136
	return(NULL);
1137
1138
1139
1140
1141
1142
}

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

deuce's avatar
deuce committed
1143
	if(ext==NULL || xjs_handlers==NULL || ext[0]==0)
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
		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);
}

1158
1159
/* 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) {
1160
	size_t dstlen,appendlen;
1161
1162
1163
1164
1165
1166
1167
1168
	dstlen=strlen(dst);
	appendlen=strlen(append);
	if(dstlen+appendlen+2 < maxlen) {
		strcat(dst,append);
		strcat(dst,newline);
	}
}

deuce's avatar
deuce committed
1169
1170
1171
1172
/*************************************************/
/* Sends headers for the reply.					 */
/* HTTP/0.9 doesn't use headers, so just returns */
/*************************************************/
1173
static BOOL send_headers(http_session_t *session, const char *status, int chunked)
1174
{
1175
	int		ret;