websrvr.c 202 KB
Newer Older
1
2
3
4
5
6
/* Synchronet Web Server */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

22
23
24
/*
 * General notes: (ToDo stuff)
 *
25
 * Support the ident protocol... the standard log format supports it.
26
 *
deuce's avatar
deuce committed
27
28
29
 * Add in support to pass connections through to a different webserver...
 *      probobly in access.ars... with like a simplified mod_rewrite.
 *      This would allow people to run apache and Synchronet as the same site.
30
 *
31
 * Add support for multipart/form-data
32
33
34
35
 *
 * Add support for UNIX-domain sockets for FastCGI
 *
 * Improved Win32 support for POST data... currently will read past Content-Length
36
 *
deuce's avatar
deuce committed
37
 */
38

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

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

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

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

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

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

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

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

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

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

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

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

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

222
223
224
	/* CGI parameters */
	char		query_str[MAX_REQUEST_LINE+1];
	char		extra_path_info[MAX_REQUEST_LINE+1];
225
226
	str_list_t	cgi_env;
	str_list_t	dynamic_heads;
227
	BOOL		got_extra_path;
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
	BOOL	manual_length;
235

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

typedef struct  {
246
	SOCKET			socket;
deuce's avatar
deuce committed
247
248
	union xp_sockaddr	addr;
	socklen_t		addr_len;
249
	http_request_t	req;
deuce's avatar
deuce committed
250
	char			host_ip[INET6_ADDRSTRLEN];
251
	char			host_name[128];	/* Resolved remote host */
deuce's avatar
deuce committed
252
	int				http_ver;       /* Request HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
253
	BOOL			finished;		/* Do not accept any more imput from client */
254
255
256
	user_t			user;
	int				last_user_num;
	time_t			logon_time;
257
	char			username[LEN_NAME+1];
258
	int				last_js_user_num;
259
	char			redir_req[MAX_REQUEST_LINE+1];
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
enum {
293
294
	 HTTP_0_9
	,HTTP_1_0
295
	,HTTP_1_1
296
297
298
299
};
static char* http_vers[] = {
	 ""
	,"HTTP/1.0"
300
	,"HTTP/1.1"
rswindell's avatar
rswindell committed
301
	,NULL	/* terminator */
302
};
deuce's avatar
deuce committed
303
304
305
306
307
308
static char* response_http_vers[] = {
	 ""
	,"HTTP/1.1"
	,"HTTP/1.1"
	,NULL	/* terminator */
};
309

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

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

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

deuce's avatar
deuce committed
332
enum {
333
334
335
	 HEAD_DATE
	,HEAD_HOST
	,HEAD_IFMODIFIED
336
337
	,HEAD_LENGTH
	,HEAD_TYPE
338
339
340
341
342
	,HEAD_AUTH
	,HEAD_CONNECTION
	,HEAD_WWWAUTH
	,HEAD_STATUS
	,HEAD_ALLOW
343
344
345
346
347
	,HEAD_EXPIRES
	,HEAD_LASTMODIFIED
	,HEAD_LOCATION
	,HEAD_PRAGMA
	,HEAD_SERVER
348
349
	,HEAD_REFERER
	,HEAD_AGENT
350
	,HEAD_TRANSFER_ENCODING
351
352
353
	,HEAD_ACCEPT_RANGES
	,HEAD_CONTENT_RANGE
	,HEAD_RANGE
deuce's avatar
deuce committed
354
	,HEAD_IFRANGE
355
	,HEAD_COOKIE
deuce's avatar
deuce committed
356
357
358
359
	,HEAD_STS
	,HEAD_UPGRADEINSECURE
	,HEAD_VARY
	,HEAD_CSP
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"				},
deuce's avatar
deuce committed
389
390
391
392
	{ HEAD_STS,			"Strict-Transport-Security"		},
	{ HEAD_UPGRADEINSECURE,		"Upgrade-Insecure-Requests"		},
	{ HEAD_VARY,			"Vary"					},
	{ HEAD_CSP,			"Content-Security-Policy"		},
393
	{ -1,					NULL /* terminator */	},
394
395
};

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

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

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

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

429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
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;

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

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

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

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

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

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

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

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

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

deuce's avatar
deuce committed
584
585
586
#define HANDLE_CRYPT_CALL(status, session, action)  handle_crypt_call_except2(status, session, action, CRYPT_OK, CRYPT_OK, __FILE__, __LINE__)
#define HANDLE_CRYPT_CALL_EXCEPT(status, session, action, ignore)  handle_crypt_call_except2(status, session, action, ignore, ignore, __FILE__, __LINE__)
#define HANDLE_CRYPT_CALL_EXCEPT2(status, session, action, ignore, ignore2)  handle_crypt_call_except2(status, session, action, ignore, ignore2, __FILE__, __LINE__)
deuce's avatar
deuce committed
587

deuce's avatar
deuce committed
588
static BOOL handle_crypt_call_except2(int status, http_session_t *session, const char *action, int ignore, int ignore2, const char *file, int line)
deuce's avatar
deuce committed
589
590
591
{
	if (status == CRYPT_OK)
		return TRUE;
592
593
594
595
	if (status == ignore)
		return FALSE;
	if (status == ignore2)
		return FALSE;
deuce's avatar
deuce committed
596
	GCES(status, session, action);
deuce's avatar
deuce committed
597
598
599
600
601
602
603
604
605
606
607
608
	return FALSE;
}

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

	if (session->is_tls) {
		if(wr)
			*wr=1;
deuce's avatar
deuce committed
609
610
		if(rd || wr == NULL) {
			if(session->tls_pending) {
deuce's avatar
deuce committed
611
				*rd_ptr = TRUE;
deuce's avatar
deuce committed
612
613
614
615
616
617
618
619
620
621
622
623
624
625
				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)
626
627
{
	size_t sent=0;
deuce's avatar
deuce committed
628
	int	tls_sent;
629
	int result;
deuce's avatar
deuce committed
630
	int status;
631
	BOOL local_failed = FALSE;
632

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

	while(sent<len && session->socket!=INVALID_SOCKET && *failed == FALSE) {
Deucе's avatar
Deucе committed
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
		if (socket_writable(session->socket, startup->max_inactivity * 1000)) {
			if (session->is_tls) {
				/*
				 * Limit as per js_socket.c.
				 * Sure, this is TLS, not SSH, but we see weird stuff here in sz file transfers.
				 */
				size_t sendbytes = len-sent;
				if (sendbytes > 0x2000)
					sendbytes = 0x2000;
				status = cryptPushData(session->tls_sess, buf+sent, sendbytes, &tls_sent);
				GCES(status, session, "pushing data");
				if (status == CRYPT_ERROR_TIMEOUT) {
					tls_sent = 0;
					if(!cryptStatusOK(status=cryptPopData(session->tls_sess, "", 0, &status))) {
						if (status != CRYPT_ERROR_TIMEOUT && status != CRYPT_ERROR_PARAM2)
							GCES(status, session, "popping data after timeout");
deuce's avatar
deuce committed
653
					}
Deucе's avatar
Deucе committed
654
					status = CRYPT_OK;
deuce's avatar
deuce committed
655
				}
Deucе's avatar
Deucе committed
656
657
658
				if(cryptStatusOK(status)) {
					HANDLE_CRYPT_CALL_EXCEPT(status = cryptFlushData(session->tls_sess), session, "flushing data", CRYPT_ERROR_COMPLETE);
					if (cryptStatusError(status))
659
						*failed=TRUE;
Deucе's avatar
Deucе committed
660
					return tls_sent;
661
				}
662
				*failed=TRUE;
Deucе's avatar
Deucе committed
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
				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);
#ifdef EPIPE
					else if(ERROR_VALUE==EPIPE)
						lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
#endif
					else
						lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
					*failed=TRUE;
					return(sent);
				}
			}
		}
		else {
			lprintf(LOG_WARNING,"%04d Timeout waiting for socket to become writable",session->socket);
			*failed=TRUE;
			return(sent);
687
688
689
		}
		sent+=result;
	}
690
	if(sent<len)
691
		*failed=TRUE;
deuce's avatar
deuce committed
692
	if(session->is_tls)
deuce's avatar
deuce committed
693
		HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session, "flushing data");
694
695
696
	return(sent);
}

697
698
699
#ifdef _WINSOCKAPI_

static WSADATA WSAData;
700
#define SOCKLIB_DESC WSAData.szDescription
701
702
703
704
705
706
707
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
708
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
709
710
711
712
		WSAInitialized=TRUE;
		return (TRUE);
	}

713
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
714
715
716
717
718
719
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
720
#define SOCKLIB_DESC NULL
721
722
723

#endif

724
725
726
727
728
static char* server_host_name(void)
{
	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static void close_socket_cb(SOCKET sock, void *cbdata)
{
951
952
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,FALSE);
953
954
}

955
static int close_socket(SOCKET *sock)
956
957
{
	int		result;
958
959
	char	ch;
	time_t	end = time(NULL) + startup->max_inactivity;
960

961
	if(sock==NULL || *sock==INVALID_SOCKET)
deuce's avatar
deuce committed
962
		return -1;
963

964
965
	/* required to ensure all data is sent */
	shutdown(*sock,SHUT_WR);
Deucе's avatar
Deucе committed
966
967
968
	while(socket_readable(*sock, startup->max_inactivity * 1000))  {
		if (recv(*sock,&ch,1,0) <= 0)
			break;
969
970
971
		if (time(NULL) >= end)
			break;
	}
972
973
	result=closesocket(*sock);
	*sock=INVALID_SOCKET;
974
	if(startup!=NULL && startup->socket_open!=NULL) {
975
		startup->socket_open(startup->cbdata,FALSE);
976
977
978
	}
	if(result!=0) {
		if(ERROR_VALUE!=ENOTSOCK)
979
			lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
980
981
982
983
984
	}

	return(result);
}

deuce's avatar
deuce committed
985
986
987
988
989
990
991
992
993
994
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...
995
		len = 1;
deuce's avatar
deuce committed
996
		while(RingBufFull(&session->outbuf) && session->socket!=INVALID_SOCKET) {
997
998
999
1000
			if (len) {
				if (cryptPopData(session->tls_sess, buf, 1, &len) != CRYPT_OK)
					len = 0;
			}
deuce's avatar
deuce committed
1001
1002
1003
			SLEEP(1);
		}
		// Now wait for tranmission to complete
1004
		len = 1;
deuce's avatar
deuce committed
1005
		while(pthread_mutex_trylock(&session->outbuf_write) == EBUSY) {
1006
1007
1008
1009
			if (len) {
				if (cryptPopData(session->tls_sess, buf, 1, &len) != CRYPT_OK)
					len = 0;
			}
deuce's avatar
deuce committed