Subversion Repositories Tewi

Rev

Rev 187 | Rev 212 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
8 nishi 1
/* $Id: server.c 189 2024-09-29 00:37:00Z nishi $ */
2
 
16 nishi 3
#define SOURCE
4
 
43 nishi 5
#include "../config.h"
6
 
8 nishi 7
#include "tw_server.h"
8
 
43 nishi 9
#ifndef NO_SSL
12 nishi 10
#include "tw_ssl.h"
43 nishi 11
#endif
12
 
8 nishi 13
#include "tw_config.h"
16 nishi 14
#include "tw_http.h"
18 nishi 15
#include "tw_module.h"
16
#include "tw_version.h"
8 nishi 17
 
18
#include <unistd.h>
19
#include <string.h>
11 nishi 20
#include <stdbool.h>
20 nishi 21
#include <stdarg.h>
43 nishi 22
#include <stdio.h>
23
#include <stdlib.h>
84 nishi 24
#include <errno.h>
21 nishi 25
#include <sys/stat.h>
182 nishi 26
#include <sys/types.h>
32 nishi 27
#include <time.h>
8 nishi 28
 
18 nishi 29
#include <cm_string.h>
8 nishi 30
#include <cm_log.h>
21 nishi 31
#include <cm_dir.h>
8 nishi 32
 
33
#ifdef __MINGW32__
163 nishi 34
#ifndef NO_GETADDRINFO
116 nishi 35
#include <ws2tcpip.h>
36
#include <wspiapi.h>
163 nishi 37
#endif
8 nishi 38
#include <winsock2.h>
11 nishi 39
#include <process.h>
62 nishi 40
#include <windows.h>
32 nishi 41
 
42
#include "strptime.h"
8 nishi 43
#else
118 nishi 44
#ifdef USE_POLL
187 nishi 45
#ifdef __PPU__
46
#include <net/poll.h>
47
#else
118 nishi 48
#include <poll.h>
187 nishi 49
#endif
118 nishi 50
#else
8 nishi 51
#include <sys/select.h>
118 nishi 52
#endif
8 nishi 53
#include <sys/socket.h>
54
#include <arpa/inet.h>
55
#include <netinet/in.h>
187 nishi 56
#ifndef __PPU__
8 nishi 57
#include <netinet/tcp.h>
187 nishi 58
#endif
163 nishi 59
#ifndef NO_GETADDRINFO
116 nishi 60
#include <netdb.h>
8 nishi 61
#endif
163 nishi 62
#endif
8 nishi 63
 
189 nishi 64
#if defined(_PSP) || defined(__ps2sdk__)
182 nishi 65
#include "strptime.h"
66
#endif
67
 
97 nishi 68
#ifdef __HAIKU__
69
#include <OS.h>
70
#endif
71
 
8 nishi 72
extern struct tw_config config;
18 nishi 73
extern char tw_server[];
8 nishi 74
 
75
int sockcount = 0;
76
 
9 nishi 77
SOCKADDR addresses[MAX_PORTS];
78
int sockets[MAX_PORTS];
8 nishi 79
 
70 nishi 80
#ifdef __MINGW32__
81
const char* reserved_names[] = {"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"};
82
#endif
83
 
23 nishi 84
/* https://qiita.com/gyu-don/items/5a640c6d2252a860c8cd */
85
int tw_wildcard_match(const char* wildcard, const char* target) {
86
	const char *pw = wildcard, *pt = target;
87
 
88
	while(1) {
89
		if(*pt == 0) {
90
			while(*pw == '*') pw++;
91
			return *pw == 0;
92
		} else if(*pw == 0) {
93
			return 0;
94
		} else if(*pw == '*') {
95
			return *(pw + 1) == 0 || tw_wildcard_match(pw, pt + 1) || tw_wildcard_match(pw + 1, pt);
96
		} else if(*pw == '?' || (*pw == *pt)) {
97
			pw++;
98
			pt++;
99
			continue;
100
		} else {
101
			return 0;
102
		}
103
	}
104
}
105
 
8 nishi 106
void close_socket(int sock) {
88 nishi 107
#if defined(__MINGW32__)
8 nishi 108
	closesocket(sock);
109
#else
110
	close(sock);
111
#endif
112
}
113
 
114
int tw_server_init(void) {
115
	int i;
116
#ifdef __MINGW32__
117
	WSADATA wsa;
118
	WSAStartup(MAKEWORD(2, 0), &wsa);
119
#endif
120
	for(i = 0; config.ports[i] != -1; i++)
121
		;
122
	sockcount = i;
123
	for(i = 0; config.ports[i] != -1; i++) {
124
#ifdef NO_IPV6
125
		int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
126
#else
127
		int sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
128
#endif
129
#ifdef __MINGW32__
130
		if(sock == INVALID_SOCKET)
131
#else
132
		if(sock < 0)
133
#endif
134
		{
135
			cm_log("Server", "Socket creation failure");
136
			return 1;
137
		}
138
		int yes = 1;
139
		if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0) {
140
			close_socket(sock);
141
			cm_log("Server", "setsockopt failure (reuseaddr)");
142
			return 1;
143
		}
187 nishi 144
#ifndef __PPU__
8 nishi 145
		if(setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes)) < 0) {
146
			close_socket(sock);
147
			cm_log("Server", "setsockopt failure (nodelay)");
148
			return 1;
149
		}
187 nishi 150
#endif
8 nishi 151
#ifndef NO_IPV6
152
		int no = 0;
153
		if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no)) < 0) {
154
			close_socket(sock);
155
			cm_log("Server", "setsockopt failure (IPv6)");
156
			return 1;
157
		}
158
#endif
159
		memset(&addresses[i], 0, sizeof(addresses[i]));
160
#ifdef NO_IPV6
161
		addresses[i].sin_family = AF_INET;
162
		addresses[i].sin_addr.s_addr = INADDR_ANY;
163
		addresses[i].sin_port = htons(config.ports[i]);
164
#else
165
		addresses[i].sin6_family = AF_INET6;
166
		addresses[i].sin6_addr = in6addr_any;
167
		addresses[i].sin6_port = htons(config.ports[i]);
168
#endif
169
		if(bind(sock, (struct sockaddr*)&addresses[i], sizeof(addresses[i])) < 0) {
170
			close_socket(sock);
171
			cm_log("Server", "Bind failure");
172
			return 1;
173
		}
174
		if(listen(sock, 128) < 0) {
175
			close_socket(sock);
176
			cm_log("Server", "Listen failure");
177
			return 1;
178
		}
179
		sockets[i] = sock;
180
	}
181
	return 0;
182
}
9 nishi 183
 
16 nishi 184
size_t tw_read(SSL* ssl, int s, void* data, size_t len) {
43 nishi 185
#ifndef NO_SSL
16 nishi 186
	if(ssl == NULL) {
187
		return recv(s, data, len, 0);
188
	} else {
189
		return SSL_read(ssl, data, len);
190
	}
43 nishi 191
#else
192
	return recv(s, data, len, 0);
193
#endif
16 nishi 194
}
195
 
196
size_t tw_write(SSL* ssl, int s, void* data, size_t len) {
43 nishi 197
#ifndef NO_SSL
16 nishi 198
	if(ssl == NULL) {
199
		return send(s, data, len, 0);
200
	} else {
201
		return SSL_write(ssl, data, len);
202
	}
43 nishi 203
#else
204
	return send(s, data, len, 0);
205
#endif
16 nishi 206
}
207
 
20 nishi 208
#define ERROR_HTML \
18 nishi 209
	"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" \
210
	"<html>\n" \
211
	"	<head>\n" \
20 nishi 212
	"		<title>%s</title>\n" \
18 nishi 213
	"	</head>\n" \
214
	"	<body>\n" \
20 nishi 215
	"		<h1>%s</h1>\n" \
18 nishi 216
	"		<hr>\n" \
217
	"		", \
218
	    address, \
219
	    "\n" \
220
	    "	</body>\n" \
221
	    "</html>\n"
222
 
32 nishi 223
void _tw_process_page(SSL* ssl, int sock, const char* status, const char* type, FILE* f, const unsigned char* doc, size_t size, char** headers, time_t mtime, time_t cmtime) {
18 nishi 224
	char construct[512];
32 nishi 225
	if(mtime != 0 && cmtime != 0 && mtime <= cmtime) {
226
		status = "304 Not Modified";
227
		type = NULL;
228
		size = 0;
229
		headers = NULL;
230
		f = NULL;
231
		doc = NULL;
232
	}
18 nishi 233
	sprintf(construct, "%llu", (unsigned long long)size);
234
	tw_write(ssl, sock, "HTTP/1.1 ", 9);
235
	tw_write(ssl, sock, (char*)status, strlen(status));
236
	tw_write(ssl, sock, "\r\n", 2);
24 nishi 237
	if(type != NULL) {
238
		tw_write(ssl, sock, "Content-Type: ", 7 + 5 + 2);
239
		tw_write(ssl, sock, (char*)type, strlen(type));
240
		tw_write(ssl, sock, "\r\n", 2);
241
	}
18 nishi 242
	tw_write(ssl, sock, "Server: ", 6 + 2);
243
	tw_write(ssl, sock, tw_server, strlen(tw_server));
244
	tw_write(ssl, sock, "\r\n", 2);
24 nishi 245
	if(size != 0) {
246
		tw_write(ssl, sock, "Content-Length: ", 7 + 7 + 2);
247
		tw_write(ssl, sock, construct, strlen(construct));
248
		tw_write(ssl, sock, "\r\n", 2);
32 nishi 249
		if(mtime != 0) {
250
			struct tm* tm = gmtime(&mtime);
251
			char date[513];
252
			strftime(date, 512, "%a, %d %b %Y %H:%M:%S GMT", tm);
253
			tw_write(ssl, sock, "Last-Modified: ", 5 + 8 + 2);
254
			tw_write(ssl, sock, date, strlen(date));
255
			tw_write(ssl, sock, "\r\n", 2);
256
		}
24 nishi 257
	}
258
	int i;
259
	if(headers != NULL) {
260
		for(i = 0; headers[i] != NULL; i += 2) {
261
			tw_write(ssl, sock, headers[i], strlen(headers[i]));
262
			tw_write(ssl, sock, ": ", 2);
263
			tw_write(ssl, sock, headers[i + 1], strlen(headers[i + 1]));
264
			tw_write(ssl, sock, "\r\n", 2);
265
		}
266
	}
18 nishi 267
	tw_write(ssl, sock, "\r\n", 2);
24 nishi 268
	if(doc == NULL && f == NULL) return;
18 nishi 269
	size_t incr = 0;
270
	while(1) {
22 nishi 271
		if(f != NULL) {
21 nishi 272
			char buffer[128];
273
			fread(buffer, size < 128 ? size : 128, 1, f);
274
			tw_write(ssl, sock, buffer, size < 128 ? size : 128);
22 nishi 275
		} else {
21 nishi 276
			tw_write(ssl, sock, (unsigned char*)doc + incr, size < 128 ? size : 128);
277
		}
18 nishi 278
		incr += 128;
19 nishi 279
		if(size <= 128) break;
18 nishi 280
		size -= 128;
281
	}
282
}
283
 
32 nishi 284
void tw_process_page(SSL* ssl, int sock, const char* status, const char* type, FILE* f, const unsigned char* doc, size_t size, time_t mtime, time_t cmtime) { _tw_process_page(ssl, sock, status, type, f, doc, size, NULL, mtime, cmtime); }
24 nishi 285
 
18 nishi 286
const char* tw_http_status(int code) {
20 nishi 287
	if(code == 200) {
288
		return "200 OK";
166 nishi 289
	} else if(code == 301) {
167 nishi 290
		return "301 Moved Permanently";
24 nishi 291
	} else if(code == 308) {
292
		return "308 Permanent Redirect";
20 nishi 293
	} else if(code == 400) {
18 nishi 294
		return "400 Bad Request";
20 nishi 295
	} else if(code == 401) {
296
		return "401 Unauthorized";
297
	} else if(code == 403) {
298
		return "403 Forbidden";
299
	} else if(code == 404) {
300
		return "404 Not Found";
161 nishi 301
	} else if(code == 500) {
302
		return "500 Internal Server Error";
18 nishi 303
	} else {
304
		return "400 Bad Request";
305
	}
306
}
307
 
123 nishi 308
char* tw_http_default_error(int code, char* name, int port, struct tw_config_entry* vhost) {
18 nishi 309
	char address[1024];
20 nishi 310
 
123 nishi 311
	if((vhost->hideport == -1 ? config.root.hideport : vhost->hideport) == 1) {
312
		sprintf(address, "<address>%s Server at %s</address>", tw_server, name, port);
313
	} else {
314
		sprintf(address, "<address>%s Server at %s Port %d</address>", tw_server, name, port);
315
	}
316
 
20 nishi 317
	char* st = cm_strdup(tw_http_status(code));
318
	char* st2;
319
	int i;
320
	for(i = 0; st[i] != 0; i++) {
321
		if(st[i] == ' ') {
322
			st2 = cm_strdup(st + i + 1);
323
			break;
324
		}
18 nishi 325
	}
20 nishi 326
	char* buffer = malloc(4096);
327
	char* str = cm_strcat3(ERROR_HTML);
328
	sprintf(buffer, str, st, st2);
329
	free(str);
330
	free(st);
331
	return buffer;
18 nishi 332
}
333
 
123 nishi 334
void tw_http_error(SSL* ssl, int sock, int error, char* name, int port, struct tw_config_entry* vhost) {
335
	char* str = tw_http_default_error(error, name, port, vhost);
32 nishi 336
	tw_process_page(ssl, sock, tw_http_status(error), "text/html", NULL, str, strlen(str), 0, 0);
18 nishi 337
	free(str);
338
}
339
 
20 nishi 340
void addstring(char** str, const char* add, ...) {
341
	int i;
342
	char cbuf[2];
343
	cbuf[1] = 0;
344
	va_list va;
345
	va_start(va, add);
346
	for(i = 0; add[i] != 0; i++) {
347
		cbuf[0] = add[i];
348
		if(add[i] == '%') {
349
			i++;
350
			if(add[i] == 's') {
351
				char* tmp = *str;
352
				*str = cm_strcat(tmp, va_arg(va, const char*));
353
				free(tmp);
354
			} else if(add[i] == 'h') {
355
				char* h = cm_html_escape(va_arg(va, const char*));
356
				char* tmp = *str;
357
				*str = cm_strcat(tmp, h);
358
				free(tmp);
359
				free(h);
21 nishi 360
			} else if(add[i] == 'l') {
361
				char* h = cm_url_escape(va_arg(va, const char*));
362
				char* tmp = *str;
363
				*str = cm_strcat(tmp, h);
364
				free(tmp);
365
				free(h);
20 nishi 366
			} else if(add[i] == 'd') {
367
				int n = va_arg(va, int);
368
				char* h = malloc(512);
369
				sprintf(h, "%d", n);
370
				char* tmp = *str;
371
				*str = cm_strcat(tmp, h);
372
				free(tmp);
373
				free(h);
374
			} else if(add[i] == '%') {
375
				char* tmp = *str;
376
				*str = cm_strcat(tmp, "%");
377
				free(tmp);
378
			}
379
		} else {
380
			char* tmp = *str;
381
			*str = cm_strcat(tmp, cbuf);
382
			free(tmp);
383
		}
384
	}
74 nishi 385
	va_end(va);
20 nishi 386
}
387
 
22 nishi 388
char* tw_get_mime(const char* ext, struct tw_config_entry* vhost_entry) {
389
	char* mime = "application/octet-stream";
390
	if(ext == NULL) return mime;
391
	bool set = false;
392
	int i;
393
	for(i = 0; i < vhost_entry->mime_count; i++) {
23 nishi 394
		if(strcmp(vhost_entry->mimes[i].ext, "all") == 0 || (ext != NULL && tw_wildcard_match(vhost_entry->mimes[i].ext, ext))) {
22 nishi 395
			mime = vhost_entry->mimes[i].mime;
396
			set = true;
397
		}
398
	}
399
	if(!set) {
400
		for(i = 0; i < config.root.mime_count; i++) {
23 nishi 401
			if(strcmp(config.root.mimes[i].ext, "all") == 0 || (ext != NULL && tw_wildcard_match(config.root.mimes[i].ext, ext))) {
22 nishi 402
				mime = config.root.mimes[i].mime;
403
			}
404
		}
405
	}
406
	return mime;
407
}
408
 
409
char* tw_get_icon(const char* mime, struct tw_config_entry* vhost_entry) {
410
	char* icon = "";
411
	if(mime == NULL) return "";
412
	bool set = false;
413
	int i;
414
	for(i = 0; i < vhost_entry->icon_count; i++) {
23 nishi 415
		if(strcmp(vhost_entry->icons[i].mime, "all") == 0 || (mime != NULL && tw_wildcard_match(vhost_entry->icons[i].mime, mime))) {
22 nishi 416
			icon = vhost_entry->icons[i].icon;
417
			set = true;
418
		}
419
	}
420
	if(!set) {
421
		for(i = 0; i < config.root.icon_count; i++) {
23 nishi 422
			if(strcmp(config.root.icons[i].mime, "all") == 0 || (mime != NULL && tw_wildcard_match(config.root.icons[i].mime, mime))) {
22 nishi 423
				icon = config.root.icons[i].icon;
424
			}
425
		}
426
	}
427
	return icon;
428
}
429
 
11 nishi 430
struct pass_entry {
431
	int sock;
12 nishi 432
	int port;
11 nishi 433
	bool ssl;
21 nishi 434
	SOCKADDR addr;
11 nishi 435
};
436
 
97 nishi 437
#ifdef __MINGW32__
11 nishi 438
unsigned int WINAPI tw_server_pass(void* ptr) {
97 nishi 439
#elif defined(__HAIKU__)
440
int32_t tw_server_pass(void* ptr) {
187 nishi 441
#elif defined(_PSP) || defined(__PPU__)
183 nishi 442
int tw_server_pass(void* ptr) {
105 nishi 443
#endif
187 nishi 444
#if defined(__HAIKU__) || defined(__MINGW32__) || defined(_PSP) || defined(__PPU__)
11 nishi 445
	int sock = ((struct pass_entry*)ptr)->sock;
446
	bool ssl = ((struct pass_entry*)ptr)->ssl;
14 nishi 447
	int port = ((struct pass_entry*)ptr)->port;
21 nishi 448
	SOCKADDR addr = ((struct pass_entry*)ptr)->addr;
12 nishi 449
	free(ptr);
11 nishi 450
#else
105 nishi 451
	void tw_server_pass(int sock, bool ssl, int port, SOCKADDR addr) {
11 nishi 452
#endif
13 nishi 453
	char* name = config.hostname;
454
 
43 nishi 455
#ifndef NO_SSL
12 nishi 456
	SSL_CTX* ctx = NULL;
457
	SSL* s = NULL;
15 nishi 458
	bool sslworks = false;
12 nishi 459
	if(ssl) {
460
		ctx = tw_create_ssl_ctx(port);
461
		s = SSL_new(ctx);
462
		SSL_set_fd(s, sock);
463
		if(SSL_accept(s) <= 0) goto cleanup;
15 nishi 464
		sslworks = true;
12 nishi 465
	}
43 nishi 466
#else
105 nishi 467
		void* s = NULL;
43 nishi 468
#endif
116 nishi 469
 
470
	char address[513];
471
	address[0] = 0;
163 nishi 472
#ifndef NO_GETADDRINFO
116 nishi 473
	struct sockaddr* sa = (struct sockaddr*)&addr;
474
	getnameinfo(sa, sizeof(addr), address, 512, NULL, 0, NI_NUMERICHOST);
163 nishi 475
#endif
116 nishi 476
 
16 nishi 477
	struct tw_http_request req;
20 nishi 478
	struct tw_http_response res;
479
	struct tw_tool tools;
480
	res._processed = false;
481
	tw_init_tools(&tools);
16 nishi 482
	int ret = tw_http_parse(s, sock, &req);
17 nishi 483
	if(ret == 0) {
116 nishi 484
		char date[513];
485
		time_t t = time(NULL);
486
		struct tm* tm = localtime(&t);
487
		strftime(date, 512, "%a, %d %b %Y %H:%M:%S %Z", tm);
488
 
489
		char* useragent = cm_strdup("");
490
 
491
		int i;
117 nishi 492
		for(i = 0; req.headers[i] != NULL; i += 2) {
493
			if(cm_strcaseequ(req.headers[i], "User-Agent")) {
116 nishi 494
				free(useragent);
495
				useragent = cm_strdup(req.headers[i + 1]);
496
			}
497
		}
498
 
499
		char* tmp = cm_strcat3(address, " - [", date);
500
		char* tmp2 = cm_strcat3(tmp, "] \"", req.method);
501
		char* tmp3 = cm_strcat3(tmp2, " ", req.path);
502
		char* tmp4 = cm_strcat3(tmp3, " ", req.version);
503
		char* tmp5 = cm_strcat3(tmp4, "\" \"", useragent);
504
		char* log = cm_strcat(tmp5, "\"");
505
		free(tmp);
506
		free(tmp2);
507
		free(tmp3);
508
		free(tmp4);
509
		free(tmp5);
510
		free(useragent);
511
		cm_force_log(log);
512
		free(log);
513
 
21 nishi 514
		char* vhost = cm_strdup(config.hostname);
32 nishi 515
		time_t cmtime = 0;
70 nishi 516
		if(req.headers != NULL) {
64 nishi 517
			for(i = 0; req.headers[i] != NULL; i += 2) {
518
				if(cm_strcaseequ(req.headers[i], "Host")) {
519
					free(vhost);
520
					vhost = cm_strdup(req.headers[i + 1]);
521
				} else if(cm_strcaseequ(req.headers[i], "If-Modified-Since")) {
522
					struct tm tm;
523
					strptime(req.headers[i + 1], "%a, %d %b %Y %H:%M:%S GMT", &tm);
189 nishi 524
#if defined(__MINGW32__) || defined(_PSP) || defined(__PPU__) || defined(__ps2sdk__)
64 nishi 525
					time_t t = 0;
526
					struct tm* btm = localtime(&t);
527
					cmtime = mktime(&tm);
528
					cmtime -= (btm->tm_hour * 60 + btm->tm_min) * 60;
32 nishi 529
#else
140 nishi 530
						cmtime = timegm(&tm);
32 nishi 531
#endif
64 nishi 532
				}
21 nishi 533
			}
534
		}
161 nishi 535
		bool rej = false;
21 nishi 536
		cm_log("Server", "Host is %s", vhost);
537
		int port = s == NULL ? 80 : 443;
538
		char* host = cm_strdup(vhost);
22 nishi 539
		for(i = 0; vhost[i] != 0; i++) {
540
			if(vhost[i] == ':') {
21 nishi 541
				host[i] = 0;
542
				port = atoi(host + i + 1);
543
				break;
544
			}
545
		}
136 nishi 546
		name = host;
21 nishi 547
		cm_log("Server", "Hostname is `%s', port is `%d'", host, port);
548
		struct tw_config_entry* vhost_entry = tw_vhost_match(host, port);
161 nishi 549
#ifdef HAS_CHROOT
550
		char* chrootpath = vhost_entry->chroot_path != NULL ? vhost_entry->chroot_path : config.root.chroot_path;
551
		if(chrootpath != NULL) {
552
			if(chdir(chrootpath) == 0) {
553
				if(chroot(".") == 0) {
554
					cm_log("Server", "Chroot successful");
555
				}
556
			} else {
557
				cm_log("Server", "chdir() failed, cannot chroot");
558
				tw_http_error(s, sock, 500, name, port, vhost_entry);
559
				rej = true;
560
			}
561
		}
562
#endif
20 nishi 563
		for(i = 0; i < config.module_count; i++) {
564
			tw_mod_request_t mod_req = (tw_mod_request_t)tw_module_symbol(config.modules[i], "mod_request");
565
			if(mod_req != NULL) {
566
				int ret = mod_req(&tools, &req, &res);
567
				int co = ret & 0xff;
141 nishi 568
				if(co == _TW_MODULE_PASS) {
569
					continue;
570
				} else if(co == _TW_MODULE_STOP) {
571
					/* Handle response here ... */
20 nishi 572
					res._processed = true;
573
					break;
141 nishi 574
				} else if(co == _TW_MODULE_STOP2) {
575
					res._processed = true;
576
					break;
577
				} else if(co == _TW_MODULE_ERROR) {
123 nishi 578
					tw_http_error(s, sock, (ret & 0xffff00) >> 8, name, port, vhost_entry);
20 nishi 579
					break;
580
				}
581
			}
582
		}
583
		if(!res._processed) {
21 nishi 584
			cm_log("Server", "Document root is %s", vhost_entry->root == NULL ? "not set" : vhost_entry->root);
585
			char* path = cm_strcat(vhost_entry->root == NULL ? "" : vhost_entry->root, req.path);
586
			cm_log("Server", "Filesystem path is %s", path);
70 nishi 587
#ifdef __MINGW32__
71 nishi 588
			char* rpath = cm_strdup(path);
72 nishi 589
			for(i = strlen(rpath) - 1; i >= 0; i--) {
73 nishi 590
				if(rpath[i] == '/') {
591
					int j;
592
					for(j = i + 1; rpath[j] != 0; j++) {
593
						if(rpath[j] == ':' || rpath[j] == '.') {
594
							rpath[j] = 0;
595
							break;
596
						}
597
					}
71 nishi 598
					break;
599
				}
600
			}
70 nishi 601
			for(i = 0; i < sizeof(reserved_names) / sizeof(reserved_names[0]); i++) {
602
				char* n = cm_strcat("/", reserved_names[i]);
71 nishi 603
				if(cm_nocase_endswith(rpath, n)) {
124 nishi 604
					tw_http_error(s, sock, 403, name, port, vhost_entry);
70 nishi 605
					free(n);
606
					rej = true;
607
					cm_log("Server", "XP Patch ; rejecting access to device");
608
					break;
609
				}
610
				free(n);
611
			}
71 nishi 612
			free(rpath);
70 nishi 613
#endif
21 nishi 614
			struct stat st;
70 nishi 615
			if(!rej && stat(path, &st) == 0) {
22 nishi 616
				if(!tw_permission_allowed(path, addr, req, vhost_entry)) {
123 nishi 617
					tw_http_error(s, sock, 403, name, port, vhost_entry);
22 nishi 618
				} else if(S_ISDIR(st.st_mode)) {
24 nishi 619
					if(req.path[strlen(req.path) - 1] != '/') {
61 nishi 620
						cm_log("Server", "Accessing directory without the slash at the end");
24 nishi 621
						char* headers[3] = {"Location", cm_strcat(req.path, "/"), NULL};
166 nishi 622
						_tw_process_page(s, sock, tw_http_status(301), NULL, NULL, NULL, 0, headers, 0, 0);
24 nishi 623
						free(headers[1]);
624
					} else {
625
						char** indexes = vhost_entry->index_count == 0 ? config.root.indexes : vhost_entry->indexes;
626
						int index_count = vhost_entry->index_count == 0 ? config.root.index_count : vhost_entry->index_count;
627
						bool found = false;
628
						for(i = 0; i < index_count; i++) {
629
							char* p = cm_strcat3(path, "/", indexes[i]);
630
							FILE* f = fopen(p, "rb");
631
							if(f != NULL) {
632
								char* ext = NULL;
633
								int j;
634
								for(j = strlen(p) - 1; j >= 0; j--) {
635
									if(p[j] == '.') {
636
										ext = cm_strdup(p + j);
637
										break;
638
									} else if(p[j] == '/') {
639
										break;
640
									}
22 nishi 641
								}
24 nishi 642
								struct stat st;
643
								stat(p, &st);
644
								char* mime = tw_get_mime(ext, vhost_entry);
32 nishi 645
								tw_process_page(s, sock, tw_http_status(200), mime, f, NULL, st.st_size, 0, 0);
24 nishi 646
								fclose(f);
74 nishi 647
								if(ext != NULL) free(ext);
24 nishi 648
								free(p);
649
								found = true;
650
								break;
22 nishi 651
							}
24 nishi 652
							free(p);
653
						}
654
						if(!found) {
655
							char* str = malloc(1);
656
							str[0] = 0;
657
							char** items = cm_scandir(path);
122 nishi 658
							addstring(&str, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n");
24 nishi 659
							addstring(&str, "<html>\n");
660
							addstring(&str, "	<head>\n");
661
							addstring(&str, "		<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
122 nishi 662
							addstring(&str, "		<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n");
24 nishi 663
							addstring(&str, "		<title>Index of %h</title>\n", req.path);
664
							addstring(&str, "	</head>\n");
665
							addstring(&str, "	<body>\n");
666
							addstring(&str, "		<h1>Index of %h</h1>\n", req.path);
667
							addstring(&str, "		<hr>\n");
668
							addstring(&str, "		<table border=\"0\">\n");
669
							addstring(&str, "			<tr>\n");
670
							addstring(&str, "				<th></th>\n");
671
							addstring(&str, "				<th>Filename</th>\n");
28 nishi 672
							addstring(&str, "				<th>MIME</th>\n");
673
							addstring(&str, "				<th>Size</th>\n");
24 nishi 674
							addstring(&str, "			</tr>\n");
33 nishi 675
							int readme = -1;
676
							char** readmes = vhost_entry->readme_count == 0 ? config.root.readmes : vhost_entry->readmes;
677
							int readme_count = vhost_entry->readme_count == 0 ? config.root.readme_count : vhost_entry->readme_count;
24 nishi 678
							if(items != NULL) {
29 nishi 679
								int phase = 0;
680
							doit:
24 nishi 681
								for(i = 0; items[i] != NULL; i++) {
33 nishi 682
									int j;
28 nishi 683
									char* fpth = cm_strcat3(path, "/", items[i]);
684
									struct stat s;
685
									char size[512];
686
									size[0] = 0;
687
									stat(fpth, &s);
29 nishi 688
									if(phase == 0 && !S_ISDIR(s.st_mode)) {
689
										free(fpth);
690
										continue;
691
									} else if(phase == 1 && S_ISDIR(s.st_mode)) {
692
										free(fpth);
693
										continue;
694
									}
33 nishi 695
									if(readme == -1) {
696
										for(j = 0; j < readme_count; j++) {
697
											if(strcmp(items[i], readmes[j]) == 0) {
698
												readme = j;
699
												break;
700
											}
701
										}
702
										if(readme != -1) {
703
											free(fpth);
704
											continue;
705
										}
706
									}
29 nishi 707
									if(s.st_size < 1024ULL) {
31 nishi 708
										sprintf(size, "%d", (int)s.st_size);
29 nishi 709
									} else if(s.st_size < 1024ULL * 1024) {
710
										sprintf(size, "%.1fK", (double)s.st_size / 1024);
711
									} else if(s.st_size < 1024ULL * 1024 * 1024) {
712
										sprintf(size, "%.1fM", (double)s.st_size / 1024 / 1024);
713
									} else if(s.st_size < 1024ULL * 1024 * 1024 * 1024) {
714
										sprintf(size, "%.1fG", (double)s.st_size / 1024 / 1024 / 1024);
715
									} else if(s.st_size < 1024ULL * 1024 * 1024 * 1024 * 1024) {
716
										sprintf(size, "%.1fT", (double)s.st_size / 1024 / 1024 / 1024 / 1024);
28 nishi 717
									}
718
 
719
									free(fpth);
720
 
24 nishi 721
									char* ext = NULL;
722
									for(j = strlen(items[i]) - 1; j >= 0; j--) {
723
										if(items[i][j] == '.') {
724
											ext = cm_strdup(items[i] + j);
725
											break;
726
										} else if(items[i][j] == '/') {
727
											break;
728
										}
729
									}
28 nishi 730
									char* showmime = "";
24 nishi 731
									char* mime = tw_get_mime(ext, vhost_entry);
732
									if(strcmp(items[i], "../") == 0) {
733
										mime = "misc/parent";
29 nishi 734
										size[0] = 0;
24 nishi 735
									} else if(items[i][strlen(items[i]) - 1] == '/') {
736
										mime = "misc/dir";
29 nishi 737
										size[0] = 0;
738
									} else {
28 nishi 739
										showmime = mime;
24 nishi 740
									}
741
									char* icon = tw_get_icon(mime, vhost_entry);
742
									if(ext != NULL) free(ext);
743
									char* itm = cm_strdup(items[i]);
744
									if(strlen(itm) >= 32) {
745
										if(itm[strlen(itm) - 1] == '/') {
746
											itm[31] = 0;
747
											itm[30] = '/';
748
											itm[29] = '.';
749
											itm[28] = '.';
750
											itm[27] = '.';
751
										} else {
752
											itm[31] = 0;
753
											itm[30] = '.';
754
											itm[29] = '.';
755
											itm[28] = '.';
756
										}
757
									}
758
									addstring(&str, "<tr>\n");
759
									addstring(&str, "	<td><img src=\"%s\" alt=\"icon\"></td>\n", icon);
760
									addstring(&str, "	<td><a href=\"%l\"><code>%h</code></a></td>\n", items[i], itm);
60 nishi 761
									addstring(&str, "	<td><code>  %h  </code></td>\n", showmime);
762
									addstring(&str, "	<td><code>  %s  </code></td>\n", size);
24 nishi 763
									addstring(&str, "</tr>\n");
764
									free(itm);
22 nishi 765
								}
29 nishi 766
								phase++;
767
								if(phase != 2) goto doit;
768
								for(i = 0; items[i] != NULL; i++) free(items[i]);
769
								free(items);
22 nishi 770
							}
24 nishi 771
							addstring(&str, "		</table>\n");
33 nishi 772
							if(readme != -1) {
773
								addstring(&str, "<hr>\n");
774
								char* fpth = cm_strcat3(path, "/", readmes[readme]);
775
								struct stat s;
776
								stat(fpth, &s);
777
								FILE* fr = fopen(fpth, "r");
778
								if(fr != NULL) {
779
									char* rmbuf = malloc(s.st_size + 1);
780
									rmbuf[s.st_size] = 0;
781
									fread(rmbuf, s.st_size, 1, fr);
782
									addstring(&str, "<pre><code>%h</code></pre>\n", rmbuf);
783
									fclose(fr);
70 nishi 784
									free(rmbuf);
33 nishi 785
								}
66 nishi 786
								free(fpth);
33 nishi 787
							}
24 nishi 788
							addstring(&str, "		<hr>\n");
123 nishi 789
							int hp = vhost_entry->hideport == -1 ? config.root.hideport : vhost_entry->hideport;
790
							if(hp == 0) {
791
								addstring(&str, "		<address>%s Server at %s Port %d</address>\n", tw_server, name, port);
792
							} else {
793
								addstring(&str, "		<address>%s Server at %s</address>\n", tw_server, name, port);
794
							}
24 nishi 795
							addstring(&str, "	</body>\n");
796
							addstring(&str, "</html>\n");
32 nishi 797
							tw_process_page(s, sock, tw_http_status(200), "text/html", NULL, str, strlen(str), 0, 0);
24 nishi 798
							free(str);
21 nishi 799
						}
800
					}
22 nishi 801
				} else {
21 nishi 802
					char* ext = NULL;
22 nishi 803
					for(i = strlen(req.path) - 1; i >= 0; i--) {
804
						if(req.path[i] == '.') {
21 nishi 805
							ext = cm_strdup(req.path + i);
806
							break;
24 nishi 807
						} else if(req.path[i] == '/') {
808
							break;
21 nishi 809
						}
810
					}
22 nishi 811
					char* mime = tw_get_mime(ext, vhost_entry);
21 nishi 812
					if(ext != NULL) free(ext);
813
					FILE* f = fopen(path, "rb");
173 nishi 814
					if(f == NULL) {
815
						tw_http_error(s, sock, 403, name, port, vhost_entry);
816
					} else {
817
						tw_process_page(s, sock, tw_http_status(200), mime, f, NULL, st.st_size, st.st_mtime, cmtime);
818
						fclose(f);
819
					}
21 nishi 820
				}
22 nishi 821
			} else {
161 nishi 822
				if(!rej) {
823
					tw_http_error(s, sock, 404, name, port, vhost_entry);
824
				}
21 nishi 825
			}
826
			free(path);
20 nishi 827
		}
21 nishi 828
		free(vhost);
829
		free(host);
22 nishi 830
	} else if(ret == -1) {
18 nishi 831
	} else {
123 nishi 832
		tw_http_error(s, sock, 400, name, port, &config.root);
17 nishi 833
	}
70 nishi 834
	tw_free_request(&req);
12 nishi 835
cleanup:
43 nishi 836
#ifndef NO_SSL
16 nishi 837
	if(sslworks) {
15 nishi 838
		SSL_shutdown(s);
839
	}
840
	SSL_free(s);
46 nishi 841
#endif
11 nishi 842
	close_socket(sock);
843
#ifdef __MINGW32__
844
	_endthreadex(0);
100 nishi 845
#elif defined(__HAIKU__)
105 nishi 846
		exit_thread(0);
11 nishi 847
#endif
43 nishi 848
	;
11 nishi 849
}
850
 
62 nishi 851
#ifdef SERVICE
852
extern SERVICE_STATUS status;
853
extern SERVICE_STATUS_HANDLE status_handle;
854
#endif
855
 
101 nishi 856
#if defined(__MINGW32__) || defined(__HAIKU__)
65 nishi 857
struct thread_entry {
101 nishi 858
#ifdef __HAIKU__
859
	thread_id thread;
860
#else
65 nishi 861
	HANDLE handle;
101 nishi 862
#endif
65 nishi 863
	bool used;
864
};
865
#endif
866
 
183 nishi 867
extern int running;
868
 
11 nishi 869
void tw_server_loop(void) {
68 nishi 870
	int i;
101 nishi 871
#if defined(__MINGW32__) || defined(__HAIKU__)
65 nishi 872
	struct thread_entry threads[2048];
70 nishi 873
	for(i = 0; i < sizeof(threads) / sizeof(threads[0]); i++) {
65 nishi 874
		threads[i].used = false;
875
	}
876
#endif
118 nishi 877
#ifdef USE_POLL
878
	struct pollfd pollfds[sockcount];
879
	for(i = 0; i < sockcount; i++) {
880
		pollfds[i].fd = sockets[i];
881
		pollfds[i].events = POLLIN | POLLPRI;
882
	}
883
#else
884
		fd_set fdset;
885
		struct timeval tv;
886
#endif
183 nishi 887
	while(running) {
118 nishi 888
#ifdef USE_POLL
889
		int ret = poll(pollfds, sockcount, 1000);
890
#else
891
			FD_ZERO(&fdset);
892
			for(i = 0; i < sockcount; i++) {
893
				FD_SET(sockets[i], &fdset);
894
			}
895
			tv.tv_sec = 1;
896
			tv.tv_usec = 0;
86 nishi 897
#ifdef __HAIKU__
118 nishi 898
			int ret = select(32, &fdset, NULL, NULL, &tv);
86 nishi 899
#else
105 nishi 900
			int ret = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
86 nishi 901
#endif
118 nishi 902
#endif
11 nishi 903
		if(ret == -1) {
84 nishi 904
#ifndef __MINGW32__
905
			cm_log("Server", "Select failure: %s", strerror(errno));
906
#endif
9 nishi 907
			break;
70 nishi 908
		} else if(ret == 0) {
62 nishi 909
#ifdef SERVICE
70 nishi 910
			if(status.dwCurrentState == SERVICE_STOP_PENDING) {
62 nishi 911
				break;
912
			}
913
#endif
11 nishi 914
		} else if(ret > 0) {
9 nishi 915
			/* connection */
916
			int i;
11 nishi 917
			for(i = 0; i < sockcount; i++) {
118 nishi 918
				bool cond;
919
#ifdef USE_POLL
920
				cond = pollfds[i].revents & POLLIN;
921
#else
922
					cond = FD_ISSET(sockets[i], &fdset);
923
#endif
924
				if(cond) {
9 nishi 925
					SOCKADDR claddr;
182 nishi 926
					socklen_t clen = sizeof(claddr);
9 nishi 927
					int sock = accept(sockets[i], (struct sockaddr*)&claddr, &clen);
12 nishi 928
					cm_log("Server", "New connection accepted");
187 nishi 929
#if defined(__MINGW32__) || defined(__HAIKU__) || defined(_PSP) || defined(__PPU__)
11 nishi 930
					struct pass_entry* e = malloc(sizeof(*e));
931
					e->sock = sock;
932
					e->ssl = config.ports[i] & (1ULL << 32);
12 nishi 933
					e->port = config.ports[i];
21 nishi 934
					e->addr = claddr;
97 nishi 935
#endif
936
#ifdef __MINGW32__
65 nishi 937
					int j;
70 nishi 938
					for(j = 0; j < sizeof(threads) / sizeof(threads[0]); j++) {
939
						if(threads[j].used) {
65 nishi 940
							DWORD ex;
941
							GetExitCodeThread(threads[j].handle, &ex);
70 nishi 942
							if(ex != STILL_ACTIVE) {
65 nishi 943
								CloseHandle(threads[j].handle);
944
								threads[j].used = false;
945
							}
946
						}
947
					}
70 nishi 948
					for(j = 0; j < sizeof(threads) / sizeof(threads[0]); j++) {
949
						if(!threads[j].used) {
65 nishi 950
							threads[j].handle = (HANDLE)_beginthreadex(NULL, 0, tw_server_pass, e, 0, NULL);
951
							threads[j].used = true;
952
							break;
953
						}
954
					}
187 nishi 955
#elif defined(_PSP) || defined(__PPU__)
183 nishi 956
						tw_server_pass(e);
97 nishi 957
#elif defined(__HAIKU__)
183 nishi 958
					int j;
959
					for(j = 0; j < sizeof(threads) / sizeof(threads[0]); j++) {
960
						if(threads[j].used) {
961
							thread_info info;
962
							bool kill = false;
963
							if(get_thread_info(threads[j].thread, &info) == B_OK) {
964
							} else {
965
								kill = true;
101 nishi 966
							}
183 nishi 967
							if(kill) {
968
								threads[j].used = false;
101 nishi 969
							}
970
						}
183 nishi 971
					}
972
					for(j = 0; j < sizeof(threads) / sizeof(threads[0]); j++) {
973
						if(!threads[j].used) {
974
							threads[j].thread = spawn_thread(tw_server_pass, "Tewi HTTPd", 60, e);
975
							threads[j].used = true;
976
							resume_thread(threads[j].thread);
977
							break;
978
						}
979
					}
11 nishi 980
#else
981
					pid_t pid = fork();
982
					if(pid == 0) {
89 nishi 983
						int j;
984
						for(j = 0; j < sockcount; j++) close_socket(sockets[j]);
21 nishi 985
						tw_server_pass(sock, config.ports[i] & (1ULL << 32), config.ports[i], claddr);
11 nishi 986
						_exit(0);
987
					} else {
988
						close_socket(sock);
989
					}
990
#endif
9 nishi 991
				}
992
			}
993
		}
994
	}
995
}