Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

websrvr.c 197 KB
Newer Older
1 2 3
/* Synchronet Web Server */

/* $Id$ */
4
// vi: tabstop=4
5 6 7 8 9

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

37 38 39
/*
 * General notes: (ToDo stuff)
 *
40
 * Support the ident protocol... the standard log format supports it.
41
 *
deuce's avatar
deuce committed
42 43 44
 * 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.
45
 *
46
 * Add support for multipart/form-data
47 48 49 50
 *
 * Add support for UNIX-domain sockets for FastCGI
 *
 * Improved Win32 support for POST data... currently will read past Content-Length
51
 *
deuce's avatar
deuce committed
52
 */
53

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

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

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

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

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

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

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

134
static named_string_t** mime_types;
135 136
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
137

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

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

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

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

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

229 230 231
	/* CGI parameters */
	char		query_str[MAX_REQUEST_LINE+1];
	char		extra_path_info[MAX_REQUEST_LINE+1];
232 233
	str_list_t	cgi_env;
	str_list_t	dynamic_heads;
234
	BOOL		got_extra_path;
235

236 237
	/* Dynamically (sever-side JS) generated HTML parameters */
	FILE*	fp;
238
	char		*cleanup_file[MAX_CLEANUPS];
239 240
	BOOL	sent_headers;
	BOOL	prev_write;
241
	BOOL	manual_length;
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 */
deuce's avatar
deuce committed
259
	int				http_ver;       /* Request HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
260
	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
enum {
300 301
	 HTTP_0_9
	,HTTP_1_0
302
	,HTTP_1_1
303 304 305 306
};
static char* http_vers[] = {
	 ""
	,"HTTP/1.0"
307
	,"HTTP/1.1"
rswindell's avatar
rswindell committed
308
	,NULL	/* terminator */
309
};
deuce's avatar
deuce committed
310 311 312 313 314 315
static char* response_http_vers[] = {
	 ""
	,"HTTP/1.1"
	,"HTTP/1.1"
	,NULL	/* terminator */
};
316

317
enum {
318 319
	 HTTP_HEAD
	,HTTP_GET
320 321
	,HTTP_POST
	,HTTP_OPTIONS
322
};
323

rswindell's avatar
rswindell committed
324 325 326
static char* methods[] = {
	 "HEAD"
	,"GET"
327
	,"POST"
328
	,"OPTIONS"
rswindell's avatar
rswindell committed
329 330
	,NULL	/* terminator */
};
331

332
enum {
333 334 335
	 IS_STATIC
	,IS_CGI
	,IS_SSJS
deuce's avatar
deuce committed
336
	,IS_FASTCGI
337 338
};

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

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

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

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

413 414 415
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"};

416
static void respond(http_session_t * session);
417
static BOOL js_setup_cx(http_session_t* session);
418
static BOOL js_setup(http_session_t* session);
419
static char *find_last_slash(char *str);
420
static BOOL check_extra_path(http_session_t * session);
421
static BOOL exec_ssjs(http_session_t* session, char* script);
422
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
423
static int sess_recv(http_session_t *session, char *buf, size_t length, int flags);
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 457 458 459 460 461 462 463 464 465 466 467 468 469 470
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;

471 472
        tm2 = gmtime(&t);	/* why not use gmtime_r instead? */
        if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
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 512 513 514 515 516 517 518 519 520 521 522 523 524 525
                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);
}
526

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

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

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

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

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

556
    return(startup->lputs(startup->cbdata,level,sbuf));
557 558
}

559 560
static int writebuf(http_session_t	*session, const char *buf, size_t len)
{
561 562
	size_t	sent=0;
	size_t	avail;
563

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

deuce's avatar
deuce committed
580 581 582
#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
583

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

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

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

700 701 702
#ifdef _WINSOCKAPI_

static WSADATA WSAData;
703
#define SOCKLIB_DESC WSAData.szDescription
704 705 706 707 708 709 710
static BOOL WSAInitialized=FALSE;

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

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

716
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
717 718 719 720 721 722
	return (FALSE);
}

#else /* No WINSOCK */

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

#endif

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

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

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

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

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

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

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

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

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

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

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

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

	len=strlen(str);
819
	return(writebuf(session,str,len));
820 821
}

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

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

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

919
	t=time_gm(&ti);
920
	return(t);
921 922
}

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

930 931
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
932 933 934 935 936
	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 {
937
		if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
938
			lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
939
	}
deuce's avatar
deuce committed
940 941 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

	sockets++;
}

static void close_socket_cb(SOCKET sock, void *cbdata)
{
951 952
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,FALSE);
deuce's avatar
deuce committed
953
	sockets--;
954 955
}

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

963
	if(sock==NULL || *sock==INVALID_SOCKET)