source: Main/trunk/Server/server.c@ 19

Last change on this file since 19 was 19, checked in by Nishi, on Sep 14, 2024 at 9:51:41 AM

add fallback option

  • Property svn:keywords set to Id
File size: 7.3 KB
Line 
1/* $Id: server.c 19 2024-09-14 00:51:41Z nishi $ */
2
3#define SOURCE
4
5#include "tw_server.h"
6
7#include "tw_ssl.h"
8#include "tw_config.h"
9#include "tw_http.h"
10#include "tw_module.h"
11#include "tw_version.h"
12
13#include <unistd.h>
14#include <string.h>
15#include <stdbool.h>
16
17#include <cm_string.h>
18#include <cm_log.h>
19
20#ifdef __MINGW32__
21#include <winsock2.h>
22#include <process.h>
23#define NO_IPV6
24#else
25#include <sys/select.h>
26#include <sys/socket.h>
27#include <arpa/inet.h>
28#include <netinet/in.h>
29#include <netinet/tcp.h>
30#endif
31
32extern struct tw_config config;
33extern char tw_server[];
34
35fd_set fdset;
36int sockcount = 0;
37
38#ifdef NO_IPV6
39#define SOCKADDR struct sockaddr_in
40#else
41#define SOCKADDR struct sockaddr_in6
42#endif
43SOCKADDR addresses[MAX_PORTS];
44int sockets[MAX_PORTS];
45
46void close_socket(int sock) {
47#ifdef __MINGW32__
48 closesocket(sock);
49#else
50 close(sock);
51#endif
52}
53
54int tw_server_init(void) {
55 int i;
56#ifdef __MINGW32__
57 WSADATA wsa;
58 WSAStartup(MAKEWORD(2, 0), &wsa);
59#endif
60 for(i = 0; config.ports[i] != -1; i++)
61 ;
62 sockcount = i;
63 for(i = 0; config.ports[i] != -1; i++) {
64#ifdef NO_IPV6
65 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
66#else
67 int sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
68#endif
69#ifdef __MINGW32__
70 if(sock == INVALID_SOCKET)
71#else
72 if(sock < 0)
73#endif
74 {
75 cm_log("Server", "Socket creation failure");
76 return 1;
77 }
78 int yes = 1;
79 if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0) {
80 close_socket(sock);
81 cm_log("Server", "setsockopt failure (reuseaddr)");
82 return 1;
83 }
84 if(setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes)) < 0) {
85 close_socket(sock);
86 cm_log("Server", "setsockopt failure (nodelay)");
87 return 1;
88 }
89#ifndef NO_IPV6
90 int no = 0;
91 if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no)) < 0) {
92 close_socket(sock);
93 cm_log("Server", "setsockopt failure (IPv6)");
94 return 1;
95 }
96#endif
97 memset(&addresses[i], 0, sizeof(addresses[i]));
98#ifdef NO_IPV6
99 addresses[i].sin_family = AF_INET;
100 addresses[i].sin_addr.s_addr = INADDR_ANY;
101 addresses[i].sin_port = htons(config.ports[i]);
102#else
103 addresses[i].sin6_family = AF_INET6;
104 addresses[i].sin6_addr = in6addr_any;
105 addresses[i].sin6_port = htons(config.ports[i]);
106#endif
107 if(bind(sock, (struct sockaddr*)&addresses[i], sizeof(addresses[i])) < 0) {
108 close_socket(sock);
109 cm_log("Server", "Bind failure");
110 return 1;
111 }
112 if(listen(sock, 128) < 0) {
113 close_socket(sock);
114 cm_log("Server", "Listen failure");
115 return 1;
116 }
117 sockets[i] = sock;
118 }
119 return 0;
120}
121
122size_t tw_read(SSL* ssl, int s, void* data, size_t len) {
123 if(ssl == NULL) {
124 return recv(s, data, len, 0);
125 } else {
126 return SSL_read(ssl, data, len);
127 }
128}
129
130size_t tw_write(SSL* ssl, int s, void* data, size_t len) {
131 if(ssl == NULL) {
132 return send(s, data, len, 0);
133 } else {
134 return SSL_write(ssl, data, len);
135 }
136}
137
138#define ERROR_400 \
139 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" \
140 "<html>\n" \
141 " <head>\n" \
142 " <title>400 Bad Request</title>" \
143 " </head>\n" \
144 " <body>\n" \
145 " <h1>Bad Request</h1>\n" \
146 " <hr>\n" \
147 " ", \
148 address, \
149 "\n" \
150 " </body>\n" \
151 "</html>\n"
152
153#define ERROR_401 \
154 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" \
155 "<html>\n" \
156 " <head>\n" \
157 " <title>401 Unauthorized</title>" \
158 " </head>\n" \
159 " <body>\n" \
160 " <h1>Unauthorized</h1>\n" \
161 " <hr>\n" \
162 " ", \
163 address, \
164 "\n" \
165 " </body>\n" \
166 "</html>\n"
167
168#define ERROR_403 \
169 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" \
170 "<html>\n" \
171 " <head>\n" \
172 " <title>403 Forbidden</title>" \
173 " </head>\n" \
174 " <body>\n" \
175 " <h1>Forbidden</h1>\n" \
176 " <hr>\n" \
177 " ", \
178 address, \
179 "\n" \
180 " </body>\n" \
181 "</html>\n"
182
183#define ERROR_404 \
184 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" \
185 "<html>\n" \
186 " <head>\n" \
187 " <title>404 Not Found</title>" \
188 " </head>\n" \
189 " <body>\n" \
190 " <h1>Not Found</h1>\n" \
191 " <hr>\n" \
192 " ", \
193 address, \
194 "\n" \
195 " </body>\n" \
196 "</html>\n"
197
198void tw_process_page(SSL* ssl, int sock, const char* status, const char* type, const unsigned char* doc, size_t size) {
199 char construct[512];
200 sprintf(construct, "%llu", (unsigned long long)size);
201 tw_write(ssl, sock, "HTTP/1.1 ", 9);
202 tw_write(ssl, sock, (char*)status, strlen(status));
203 tw_write(ssl, sock, "\r\n", 2);
204 tw_write(ssl, sock, "Content-Type: ", 7 + 5 + 2);
205 tw_write(ssl, sock, (char*)type, strlen(type));
206 tw_write(ssl, sock, "\r\n", 2);
207 tw_write(ssl, sock, "Server: ", 6 + 2);
208 tw_write(ssl, sock, tw_server, strlen(tw_server));
209 tw_write(ssl, sock, "\r\n", 2);
210 tw_write(ssl, sock, "Content-Length: ", 7 + 7 + 2);
211 tw_write(ssl, sock, construct, strlen(construct));
212 tw_write(ssl, sock, "\r\n", 2);
213 tw_write(ssl, sock, "\r\n", 2);
214 size_t incr = 0;
215 while(1) {
216 tw_write(ssl, sock, (unsigned char*)doc + incr, size < 128 ? size : 128);
217 incr += 128;
218 if(size <= 128) break;
219 size -= 128;
220 }
221}
222
223const char* tw_http_status(int code) {
224 if(code == 400) {
225 return "400 Bad Request";
226 } else {
227 return "400 Bad Request";
228 }
229}
230
231char* tw_http_default_error(int code, char* name, int port) {
232 char address[1024];
233 sprintf(address, "<address>%s Server at %s Port %d</address>", tw_server, name, port);
234 if(code == 400) {
235 return cm_strcat3(ERROR_400);
236 } else {
237 return cm_strcat3(ERROR_400);
238 }
239}
240
241void tw_http_error(SSL* ssl, int sock, int error, char* name, int port) {
242 char* str = tw_http_default_error(error, name, port);
243 tw_process_page(ssl, sock, tw_http_status(error), "text/html", str, strlen(str));
244 free(str);
245}
246
247#ifdef __MINGW32__
248struct pass_entry {
249 int sock;
250 int port;
251 bool ssl;
252};
253
254unsigned int WINAPI tw_server_pass(void* ptr) {
255 int sock = ((struct pass_entry*)ptr)->sock;
256 bool ssl = ((struct pass_entry*)ptr)->ssl;
257 int port = ((struct pass_entry*)ptr)->port;
258 free(ptr);
259#else
260void tw_server_pass(int sock, bool ssl, int port) {
261#endif
262 char* name = config.hostname;
263
264 SSL_CTX* ctx = NULL;
265 SSL* s = NULL;
266 bool sslworks = false;
267 if(ssl) {
268 ctx = tw_create_ssl_ctx(port);
269 s = SSL_new(ctx);
270 SSL_set_fd(s, sock);
271 if(SSL_accept(s) <= 0) goto cleanup;
272 sslworks = true;
273 }
274 struct tw_http_request req;
275 int ret = tw_http_parse(s, sock, &req);
276 if(ret == 0) {
277 } else {
278 tw_http_error(s, sock, 400, name, port);
279 }
280cleanup:
281 if(sslworks) {
282 SSL_shutdown(s);
283 }
284 SSL_free(s);
285 close_socket(sock);
286#ifdef __MINGW32__
287 _endthreadex(0);
288#endif
289}
290
291void tw_server_loop(void) {
292 struct timeval tv;
293 while(1) {
294 FD_ZERO(&fdset);
295 int i;
296 for(i = 0; i < sockcount; i++) {
297 FD_SET(sockets[i], &fdset);
298 }
299 tv.tv_sec = 1;
300 tv.tv_usec = 0;
301 int ret = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
302 if(ret == -1) {
303 break;
304 } else if(ret > 0) {
305 /* connection */
306 int i;
307 for(i = 0; i < sockcount; i++) {
308 if(FD_ISSET(sockets[i], &fdset)) {
309 SOCKADDR claddr;
310 int clen = sizeof(claddr);
311 int sock = accept(sockets[i], (struct sockaddr*)&claddr, &clen);
312 cm_log("Server", "New connection accepted");
313#ifdef __MINGW32__
314 HANDLE thread;
315 struct pass_entry* e = malloc(sizeof(*e));
316 e->sock = sock;
317 e->ssl = config.ports[i] & (1ULL << 32);
318 e->port = config.ports[i];
319 thread = (HANDLE)_beginthreadex(NULL, 0, tw_server_pass, e, 0, NULL);
320#else
321 pid_t pid = fork();
322 if(pid == 0) {
323 tw_server_pass(sock, config.ports[i] & (1ULL << 32), config.ports[i]);
324 _exit(0);
325 } else {
326 close_socket(sock);
327 }
328#endif
329 }
330 }
331 }
332 }
333}
Note: See TracBrowser for help on using the repository browser.