rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <string.h>
|
nuclear@3
|
4 #include <ctype.h>
|
nuclear@3
|
5 #include <signal.h>
|
nuclear@0
|
6 #include <errno.h>
|
nuclear@0
|
7 #include <unistd.h>
|
nuclear@0
|
8 #include <fcntl.h>
|
nuclear@5
|
9 #include <sys/stat.h>
|
nuclear@0
|
10 #include <sys/select.h>
|
nuclear@5
|
11 #include <sys/mman.h>
|
nuclear@0
|
12 #include <sys/types.h>
|
nuclear@0
|
13 #include <sys/socket.h>
|
nuclear@0
|
14 #include <arpa/inet.h>
|
nuclear@1
|
15 #include "http.h"
|
nuclear@5
|
16 #include "mime.h"
|
nuclear@1
|
17
|
nuclear@1
|
18 /* HTTP version */
|
nuclear@1
|
19 #define HTTP_VER_MAJOR 1
|
nuclear@1
|
20 #define HTTP_VER_MINOR 1
|
nuclear@1
|
21 #define HTTP_VER_STR "1.1"
|
nuclear@1
|
22
|
nuclear@1
|
23 /* maximum request length: 64mb */
|
nuclear@1
|
24 #define MAX_REQ_LENGTH (65536 * 1024)
|
nuclear@0
|
25
|
nuclear@0
|
26 struct client {
|
nuclear@0
|
27 int s;
|
nuclear@0
|
28 char *rcvbuf;
|
nuclear@0
|
29 int bufsz;
|
nuclear@0
|
30 struct client *next;
|
nuclear@0
|
31 };
|
nuclear@0
|
32
|
nuclear@0
|
33 int start_server(void);
|
nuclear@0
|
34 int accept_conn(int lis);
|
nuclear@3
|
35 void close_conn(struct client *c);
|
nuclear@0
|
36 int handle_client(struct client *c);
|
nuclear@5
|
37 int do_get(struct client *c, const char *uri, int with_body);
|
nuclear@1
|
38 void respond_error(struct client *c, int errcode);
|
nuclear@3
|
39 void sighandler(int s);
|
nuclear@1
|
40 int parse_args(int argc, char **argv);
|
nuclear@0
|
41
|
nuclear@0
|
42 static int lis;
|
nuclear@0
|
43 static int port = 8080;
|
nuclear@0
|
44 static struct client *clist;
|
nuclear@0
|
45
|
nuclear@5
|
46 static const char *indexfiles[] = {
|
nuclear@5
|
47 "index.cgi",
|
nuclear@5
|
48 "index.html",
|
nuclear@5
|
49 "index.htm",
|
nuclear@5
|
50 0
|
nuclear@5
|
51 };
|
nuclear@5
|
52
|
nuclear@5
|
53
|
nuclear@0
|
54 int main(int argc, char **argv)
|
nuclear@0
|
55 {
|
nuclear@1
|
56 if(parse_args(argc, argv) == -1) {
|
nuclear@1
|
57 return 1;
|
nuclear@1
|
58 }
|
nuclear@1
|
59
|
nuclear@3
|
60 signal(SIGINT, sighandler);
|
nuclear@3
|
61 signal(SIGTERM, sighandler);
|
nuclear@3
|
62 signal(SIGQUIT, sighandler);
|
nuclear@3
|
63
|
nuclear@0
|
64 if((lis = start_server()) == -1) {
|
nuclear@0
|
65 return 1;
|
nuclear@0
|
66 }
|
nuclear@0
|
67
|
nuclear@0
|
68 for(;;) {
|
nuclear@0
|
69 struct client *c, dummy;
|
nuclear@0
|
70 int maxfd = lis;
|
nuclear@0
|
71 fd_set rdset;
|
nuclear@0
|
72
|
nuclear@0
|
73 FD_ZERO(&rdset);
|
nuclear@0
|
74 FD_SET(lis, &rdset);
|
nuclear@0
|
75
|
nuclear@0
|
76 c = clist;
|
nuclear@0
|
77 while(c) {
|
nuclear@0
|
78 if(c->s > maxfd) {
|
nuclear@0
|
79 maxfd = c->s;
|
nuclear@0
|
80 }
|
nuclear@0
|
81 FD_SET(c->s, &rdset);
|
nuclear@0
|
82 c = c->next;
|
nuclear@0
|
83 }
|
nuclear@0
|
84
|
nuclear@0
|
85 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
|
nuclear@0
|
86
|
nuclear@0
|
87 c = clist;
|
nuclear@0
|
88 while(c) {
|
nuclear@0
|
89 if(FD_ISSET(c->s, &rdset)) {
|
nuclear@0
|
90 handle_client(c);
|
nuclear@0
|
91 }
|
nuclear@0
|
92 c = c->next;
|
nuclear@0
|
93 }
|
nuclear@0
|
94
|
nuclear@0
|
95 if(FD_ISSET(lis, &rdset)) {
|
nuclear@0
|
96 accept_conn(lis);
|
nuclear@0
|
97 }
|
nuclear@0
|
98
|
nuclear@0
|
99 dummy.next = clist;
|
nuclear@0
|
100 c = &dummy;
|
nuclear@0
|
101
|
nuclear@0
|
102 while(c->next) {
|
nuclear@0
|
103 struct client *n = c->next;
|
nuclear@0
|
104
|
nuclear@0
|
105 if(n->s == -1) {
|
nuclear@0
|
106 /* marked for removal */
|
nuclear@0
|
107 c->next = n->next;
|
nuclear@0
|
108 free(n);
|
nuclear@0
|
109 } else {
|
nuclear@0
|
110 c = c->next;
|
nuclear@0
|
111 }
|
nuclear@0
|
112 }
|
nuclear@4
|
113 clist = dummy.next;
|
nuclear@0
|
114 }
|
nuclear@3
|
115
|
nuclear@3
|
116 return 0; /* unreachable */
|
nuclear@0
|
117 }
|
nuclear@0
|
118
|
nuclear@0
|
119 int start_server(void)
|
nuclear@0
|
120 {
|
nuclear@0
|
121 int s;
|
nuclear@0
|
122 struct sockaddr_in sa;
|
nuclear@0
|
123
|
nuclear@0
|
124 if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
|
nuclear@0
|
125 perror("failed to create listening socket");
|
nuclear@0
|
126 return -1;
|
nuclear@0
|
127 }
|
nuclear@0
|
128 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
|
nuclear@0
|
129
|
nuclear@0
|
130 memset(&sa, 0, sizeof sa);
|
nuclear@0
|
131 sa.sin_family = AF_INET;
|
nuclear@0
|
132 sa.sin_addr.s_addr = INADDR_ANY;
|
nuclear@0
|
133 sa.sin_port = htons(port);
|
nuclear@0
|
134
|
nuclear@0
|
135 if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
|
nuclear@0
|
136 fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
|
nuclear@0
|
137 return -1;
|
nuclear@0
|
138 }
|
nuclear@0
|
139 listen(s, 16);
|
nuclear@0
|
140
|
nuclear@0
|
141 return s;
|
nuclear@0
|
142 }
|
nuclear@0
|
143
|
nuclear@0
|
144 int accept_conn(int lis)
|
nuclear@0
|
145 {
|
nuclear@0
|
146 int s;
|
nuclear@0
|
147 struct client *c;
|
nuclear@0
|
148 struct sockaddr_in addr;
|
nuclear@0
|
149 socklen_t addr_sz = sizeof addr;
|
nuclear@0
|
150
|
nuclear@0
|
151 if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
|
nuclear@0
|
152 perror("failed to accept incoming connection");
|
nuclear@0
|
153 return -1;
|
nuclear@0
|
154 }
|
nuclear@0
|
155 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
|
nuclear@0
|
156
|
nuclear@0
|
157 if(!(c = malloc(sizeof *c))) {
|
nuclear@0
|
158 perror("failed to allocate memory while accepting connection");
|
nuclear@0
|
159 return -1;
|
nuclear@0
|
160 }
|
nuclear@0
|
161 c->s = s;
|
nuclear@0
|
162 c->rcvbuf = 0;
|
nuclear@0
|
163 c->bufsz = 0;
|
nuclear@0
|
164 c->next = clist;
|
nuclear@0
|
165 clist = c;
|
nuclear@0
|
166 return 0;
|
nuclear@0
|
167 }
|
nuclear@0
|
168
|
nuclear@1
|
169 void close_conn(struct client *c)
|
nuclear@1
|
170 {
|
nuclear@1
|
171 close(c->s);
|
nuclear@1
|
172 c->s = -1; /* mark it for removal */
|
nuclear@1
|
173 free(c->rcvbuf);
|
nuclear@1
|
174 }
|
nuclear@1
|
175
|
nuclear@0
|
176 int handle_client(struct client *c)
|
nuclear@0
|
177 {
|
nuclear@3
|
178 struct http_req_header hdr;
|
nuclear@0
|
179 static char buf[2048];
|
nuclear@3
|
180 int rdsz, status;
|
nuclear@0
|
181
|
nuclear@0
|
182 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
|
nuclear@4
|
183 char *newbuf;
|
nuclear@4
|
184 int newsz = c->bufsz + rdsz;
|
nuclear@4
|
185 if(newsz > MAX_REQ_LENGTH) {
|
nuclear@4
|
186 respond_error(c, 413);
|
nuclear@4
|
187 return -1;
|
nuclear@4
|
188 }
|
nuclear@1
|
189
|
nuclear@4
|
190 if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) {
|
nuclear@4
|
191 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
|
nuclear@4
|
192 respond_error(c, 503);
|
nuclear@4
|
193 return -1;
|
nuclear@4
|
194 }
|
nuclear@0
|
195
|
nuclear@4
|
196 memcpy(newbuf + c->bufsz, buf, rdsz);
|
nuclear@4
|
197 newbuf[newsz] = 0;
|
nuclear@0
|
198
|
nuclear@4
|
199 c->rcvbuf = newbuf;
|
nuclear@4
|
200 c->bufsz = newsz;
|
nuclear@0
|
201 }
|
nuclear@0
|
202
|
nuclear@3
|
203 if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) {
|
nuclear@3
|
204 http_print_header(&hdr);
|
nuclear@3
|
205 switch(status) {
|
nuclear@3
|
206 case HTTP_HDR_INVALID:
|
nuclear@3
|
207 respond_error(c, 400);
|
nuclear@3
|
208 return -1;
|
nuclear@3
|
209
|
nuclear@3
|
210 case HTTP_HDR_NOMEM:
|
nuclear@3
|
211 respond_error(c, 503);
|
nuclear@3
|
212 return -1;
|
nuclear@3
|
213
|
nuclear@3
|
214 case HTTP_HDR_PARTIAL:
|
nuclear@3
|
215 return 0; /* partial header, continue reading */
|
nuclear@3
|
216 }
|
nuclear@3
|
217 }
|
nuclear@3
|
218 http_print_header(&hdr);
|
nuclear@3
|
219
|
nuclear@3
|
220 /* we only support GET and HEAD at this point, so freak out on anything else */
|
nuclear@3
|
221 switch(hdr.method) {
|
nuclear@3
|
222 case HTTP_GET:
|
nuclear@5
|
223 do_get(c, hdr.uri, 1);
|
nuclear@5
|
224 break;
|
nuclear@5
|
225
|
nuclear@3
|
226 case HTTP_HEAD:
|
nuclear@5
|
227 do_get(c, hdr.uri, 0);
|
nuclear@3
|
228 break;
|
nuclear@3
|
229
|
nuclear@3
|
230 default:
|
nuclear@3
|
231 respond_error(c, 501);
|
nuclear@3
|
232 return -1;
|
nuclear@1
|
233 }
|
nuclear@1
|
234
|
nuclear@3
|
235 close_conn(c);
|
nuclear@3
|
236 return 0;
|
nuclear@3
|
237 }
|
nuclear@1
|
238
|
nuclear@5
|
239 int do_get(struct client *c, const char *uri, int with_body)
|
nuclear@3
|
240 {
|
nuclear@5
|
241 const char *ptr;
|
nuclear@5
|
242 struct http_resp_header resp;
|
nuclear@5
|
243
|
nuclear@5
|
244 if((ptr = strstr(uri, "://"))) {
|
nuclear@5
|
245 /* skip the host part */
|
nuclear@5
|
246 if(!(uri = strchr(ptr + 3, '/'))) {
|
nuclear@5
|
247 respond_error(c, 404);
|
nuclear@5
|
248 return -1;
|
nuclear@5
|
249 }
|
nuclear@5
|
250 ++uri;
|
nuclear@5
|
251 }
|
nuclear@5
|
252
|
nuclear@5
|
253 if(*uri) {
|
nuclear@5
|
254 struct stat st;
|
nuclear@5
|
255 char *path = 0;
|
nuclear@5
|
256 char *buf;
|
nuclear@5
|
257 const char *type;
|
nuclear@5
|
258 int fd, size;
|
nuclear@5
|
259
|
nuclear@5
|
260 if(stat(uri, &st) == -1) {
|
nuclear@5
|
261 respond_error(c, 404);
|
nuclear@5
|
262 return -1;
|
nuclear@5
|
263 }
|
nuclear@5
|
264
|
nuclear@5
|
265 if(S_ISDIR(st.st_mode)) {
|
nuclear@5
|
266 int i;
|
nuclear@5
|
267 path = alloca(strlen(uri) + 64);
|
nuclear@5
|
268
|
nuclear@5
|
269 for(i=0; indexfiles[i]; i++) {
|
nuclear@5
|
270 sprintf(path, "%s/%s", uri, indexfiles[i]);
|
nuclear@5
|
271 if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
|
nuclear@5
|
272 break;
|
nuclear@5
|
273 }
|
nuclear@5
|
274 }
|
nuclear@5
|
275
|
nuclear@5
|
276 if(indexfiles[i] == 0) {
|
nuclear@5
|
277 respond_error(c, 404);
|
nuclear@5
|
278 return -1;
|
nuclear@5
|
279 }
|
nuclear@5
|
280 } else {
|
nuclear@5
|
281 path = (char*)uri;
|
nuclear@5
|
282 }
|
nuclear@5
|
283
|
nuclear@5
|
284 if((fd = open(path, O_RDONLY)) == -1) {
|
nuclear@5
|
285 respond_error(c, 403);
|
nuclear@5
|
286 return -1;
|
nuclear@5
|
287 }
|
nuclear@5
|
288
|
nuclear@5
|
289 /* construct response header */
|
nuclear@5
|
290 http_init_resp(&resp);
|
nuclear@5
|
291 http_add_resp_field(&resp, "Content-Length: %d", st.st_size);
|
nuclear@5
|
292 if((type = mime_type(path))) {
|
nuclear@5
|
293 http_add_resp_field(&resp, "Content-Type: %s", type);
|
nuclear@5
|
294 }
|
nuclear@5
|
295
|
nuclear@5
|
296 size = http_serialize_resp(&resp, 0);
|
nuclear@5
|
297 buf = alloca(size);
|
nuclear@5
|
298 http_serialize_resp(&resp, buf);
|
nuclear@5
|
299 send(c->s, buf, size, 0);
|
nuclear@5
|
300
|
nuclear@5
|
301 if(with_body) {
|
nuclear@5
|
302 char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
nuclear@5
|
303 if(cont == (void*)-1) {
|
nuclear@5
|
304 respond_error(c, 503);
|
nuclear@5
|
305 close(fd);
|
nuclear@5
|
306 return -1;
|
nuclear@5
|
307 }
|
nuclear@5
|
308 }
|
nuclear@5
|
309
|
nuclear@5
|
310 close(fd);
|
nuclear@5
|
311 }
|
nuclear@5
|
312 return 0;
|
nuclear@1
|
313 }
|
nuclear@0
|
314
|
nuclear@1
|
315 void respond_error(struct client *c, int errcode)
|
nuclear@1
|
316 {
|
nuclear@1
|
317 char buf[512];
|
nuclear@1
|
318
|
nuclear@1
|
319 sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
|
nuclear@1
|
320
|
nuclear@1
|
321 send(c->s, buf, strlen(buf), 0);
|
nuclear@1
|
322 close_conn(c);
|
nuclear@1
|
323 }
|
nuclear@1
|
324
|
nuclear@3
|
325 void sighandler(int s)
|
nuclear@3
|
326 {
|
nuclear@3
|
327 if(s == SIGINT || s == SIGTERM || s == SIGQUIT) {
|
nuclear@3
|
328 close(lis);
|
nuclear@3
|
329 while(clist) {
|
nuclear@3
|
330 struct client *c = clist;
|
nuclear@3
|
331 clist = clist->next;
|
nuclear@3
|
332 close_conn(c);
|
nuclear@3
|
333 free(c);
|
nuclear@3
|
334 }
|
nuclear@3
|
335 clist = 0;
|
nuclear@3
|
336
|
nuclear@3
|
337 printf("bye!\n");
|
nuclear@3
|
338 exit(0);
|
nuclear@3
|
339 }
|
nuclear@3
|
340 }
|
nuclear@3
|
341
|
nuclear@1
|
342
|
nuclear@1
|
343 static void print_help(const char *argv0)
|
nuclear@1
|
344 {
|
nuclear@1
|
345 printf("Usage: %s [options]\n", argv0);
|
nuclear@1
|
346 printf("Options:\n");
|
nuclear@1
|
347 printf(" -p <port> set the TCP/IP port number to use\n");
|
nuclear@1
|
348 printf(" -h print usage help and exit\n");
|
nuclear@1
|
349 }
|
nuclear@1
|
350
|
nuclear@1
|
351 int parse_args(int argc, char **argv)
|
nuclear@1
|
352 {
|
nuclear@1
|
353 int i;
|
nuclear@1
|
354
|
nuclear@1
|
355 for(i=1; i<argc; i++) {
|
nuclear@1
|
356 if(argv[i][0] == '-' && argv[i][2] == 0) {
|
nuclear@1
|
357 switch(argv[i][1]) {
|
nuclear@1
|
358 case 'p':
|
nuclear@1
|
359 if((port = atoi(argv[++i])) == 0) {
|
nuclear@1
|
360 fprintf(stderr, "-p must be followed by a valid port number\n");
|
nuclear@1
|
361 return -1;
|
nuclear@1
|
362 }
|
nuclear@1
|
363 break;
|
nuclear@1
|
364
|
nuclear@1
|
365 case 'h':
|
nuclear@1
|
366 print_help(argv[0]);
|
nuclear@1
|
367 exit(0);
|
nuclear@1
|
368
|
nuclear@1
|
369 default:
|
nuclear@1
|
370 fprintf(stderr, "unrecognized option: %s\n", argv[i]);
|
nuclear@1
|
371 return -1;
|
nuclear@1
|
372 }
|
nuclear@1
|
373 } else {
|
nuclear@1
|
374 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
|
nuclear@1
|
375 return -1;
|
nuclear@1
|
376 }
|
nuclear@0
|
377 }
|
nuclear@0
|
378 return 0;
|
nuclear@0
|
379 }
|