websrvr.c 206 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 "filedat.h"
54
#include "sockwrap.h"		/* sendfilesocket() */
deuce's avatar
deuce committed
55
#include "multisock.h"
56
#include "threadwrap.h"
57
#include "semwrap.h"
deuce's avatar
deuce committed
58
#include "xpendian.h"
59
#include "websrvr.h"
deuce's avatar
deuce committed
60
#include "base64.h"
61
#include "md5.h"
62
#include "hex.h"
63
#include "js_rtpool.h"
64
#include "js_request.h"
deuce's avatar
deuce committed
65
#include "js_socket.h"
66
#include "xpmap.h"
67
#include "xpprintf.h"
deuce's avatar
deuce committed
68
#include "ssl.h"
deuce's avatar
deuce committed
69
#include "fastcgi.h"
70 71
#include "git_branch.h"
#include "git_hash.h"
72

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

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

98 99 100
enum {
	 CLEANUP_SSJS_TMP_FILE
	,CLEANUP_POST_DATA
101
	,MAX_CLEANUPS
102
};
103

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

126
static named_string_t** mime_types;
127 128
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
129
static named_string_t** alias_list; // request path aliases
130

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

146 147 148 149
enum auth_type {
	 AUTHENTICATION_UNKNOWN
	,AUTHENTICATION_BASIC
	,AUTHENTICATION_DIGEST
deuce's avatar
deuce committed
150
	,AUTHENTICATION_TLS_PSK
151 152
};

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

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

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

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

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

typedef struct  {
249
	SOCKET			socket;
deuce's avatar
deuce committed
250 251
	union xp_sockaddr	addr;
	socklen_t		addr_len;
252
	http_request_t	req;
deuce's avatar
deuce committed
253
	char			host_ip[INET6_ADDRSTRLEN];
254
	char			host_name[128];	/* Resolved remote host */
deuce's avatar
deuce committed
255
	int				http_ver;       /* Request HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
256
	BOOL			finished;		/* Do not accept any more imput from client */
257 258
	BOOL			filebase_access;
	file_t			file;
259 260 261
	user_t			user;
	int				last_user_num;
	time_t			logon_time;
262
	char			username[LEN_NAME+1];
263
	int				last_js_user_num;
264
	char			redir_req[MAX_REQUEST_LINE+1];
265 266 267 268 269

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

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

283 284
	/* Client info */
	client_t		client;
deuce's avatar
deuce committed
285 286 287

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

	/* TLS Stuff */
	BOOL			is_tls;
	CRYPT_SESSION	tls_sess;
	BOOL			tls_pending;
	BOOL			peeked_valid;
	char			peeked;
295 296
} http_session_t;

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

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

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

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

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

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

401
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
402
enum  {
403
	 NO_LOCATION
404
	,MOVED_PERM
deuce's avatar
deuce committed
405
	,MOVED_TEMPREDIR
406
	,MOVED_TEMP
407
	,MOVED_STAT
408 409
};

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

422 423 424
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"};

425
static void respond(http_session_t * session);
426
static BOOL js_setup_cx(http_session_t* session);
427
static BOOL js_setup(http_session_t* session);
428
static char *find_last_slash(char *str);
429
static BOOL check_extra_path(http_session_t * session);
430
static BOOL exec_ssjs(http_session_t* session, char* script);
431
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
432
static int sess_recv(http_session_t *session, char *buf, size_t length, int flags);
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 475 476 477 478 479
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;

480 481
        tm2 = gmtime(&t);	/* why not use gmtime_r instead? */
        if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
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 530 531 532 533 534
                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);
}
535

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

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

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

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

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

565
    return(startup->lputs(startup->cbdata,level,sbuf));
566 567
}

568 569
static int writebuf(http_session_t	*session, const char *buf, size_t len)
{
570 571
	size_t	sent=0;
	size_t	avail;
572

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

deuce's avatar
deuce committed
589 590 591
#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
592

deuce's avatar
deuce committed
593
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
594 595 596
{
	if (status == CRYPT_OK)
		return TRUE;
597 598 599 600
	if (status == ignore)
		return FALSE;
	if (status == ignore2)
		return FALSE;
deuce's avatar
deuce committed
601
	GCES(status, session, action);
deuce's avatar
deuce committed
602 603 604 605 606 607 608 609 610 611 612 613
	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
614 615
		if(rd || wr == NULL) {
			if(session->tls_pending) {
deuce's avatar
deuce committed
616
				*rd_ptr = TRUE;
deuce's avatar
deuce committed
617 618 619 620 621 622 623 624 625 626 627 628 629 630
				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)
631 632
{
	size_t sent=0;
deuce's avatar
deuce committed
633
	int	tls_sent;
634
	int result;
deuce's avatar
deuce committed
635
	int status;
636
	BOOL local_failed = FALSE;
637

638 639 640 641
	if (failed == NULL)
		failed = &local_failed;

	while(sent<len && session->socket!=INVALID_SOCKET && *failed == FALSE) {
Deucе's avatar
Deucе committed
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
		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
658
					}
Deucе's avatar
Deucе committed
659
					status = CRYPT_OK;
deuce's avatar
deuce committed
660
				}
Deucе's avatar
Deucе committed
661 662 663
				if(cryptStatusOK(status)) {
					HANDLE_CRYPT_CALL_EXCEPT(status = cryptFlushData(session->tls_sess), session, "flushing data", CRYPT_ERROR_COMPLETE);
					if (cryptStatusError(status))
664
						*failed=TRUE;
Deucе's avatar
Deucе committed
665
					return tls_sent;
666
				}
667
				*failed=TRUE;
Deucе's avatar
Deucе committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
				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);
692 693 694
		}
		sent+=result;
	}
695
	if(sent<len)
696
		*failed=TRUE;
deuce's avatar
deuce committed
697
	if(session->is_tls)
deuce's avatar
deuce committed
698
		HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session, "flushing data");
699 700 701
	return(sent);
}

702 703 704
#ifdef _WINSOCKAPI_

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

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

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

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

#else /* No WINSOCK */

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

#endif

729 730 731 732 733
static char* server_host_name(void)
{
	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}

734 735 736
static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
737
	    startup->status(startup->cbdata,str);
738 739 740 741 742
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
743
		startup->clients(startup->cbdata,protected_uint32_value(active_clients));
744 745 746 747 748
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
749
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
750 751 752 753 754
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
755
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
756 757 758 759 760
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
761
		startup->thread_up(startup->cbdata,TRUE, setuid);
762 763 764 765
}

static void thread_down(void)
{
766
	(void)protected_uint32_adjust(&thread_count,-1);
767
	if(startup!=NULL && startup->thread_up!=NULL)
768
		startup->thread_up(startup->cbdata,FALSE, FALSE);
769 770
}

deuce's avatar
deuce committed
771 772 773
/*********************************************************************/
/* Adds an environment variable to the sessions  cgi_env linked list */
/*********************************************************************/
774
static void add_env(http_session_t *session, const char *name,const char *value)  {
775
	char	newname[129];
776
	char	*p;
777

778
	if(name==NULL || value==NULL)  {
779
		lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
780 781 782 783 784 785 786 787 788
		return;
	}
	SAFECOPY(newname,name);

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

deuce's avatar
deuce committed
798 799 800
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
801 802
static void init_enviro(http_session_t *session)  {
	char	str[128];
803 804
	union xp_sockaddr sockaddr;
	socklen_t socklen = sizeof(sockaddr);
805 806

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

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

	len=strlen(str);
826
	return(writebuf(session,str,len));
827 828
}

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

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

861
	token=strtok_r(date,",",&last);
862 863
	if(token==NULL)
		return(0);
864 865
	/* This probobly only needs to be 9, but the extra one is for luck. */
	if(strlen(date)>15) {
866
		/* asctime() */
867
		/* Toss away week day */
868
		token=strtok_r(date," ",&last);
869 870
		if(token==NULL)
			return(0);
871
		token=strtok_r(NULL," ",&last);
872 873 874
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
875
		token=strtok_r(NULL," ",&last);
876 877 878
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
879
		token=strtok_r(NULL,":",&last);
880 881 882
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
883
		token=strtok_r(NULL,":",&last);
884 885 886
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
887
		token=strtok_r(NULL," ",&last);
888 889 890
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
891
		token=strtok_r(NULL,"",&last);
892 893 894
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token)-1900;
895 896 897
	}
	else  {
		/* RFC 1123 or RFC 850 */