tinywebd

view src/main.c @ 4:9e054c002489

fixed the header parsing bugs
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 16 Apr 2015 17:34:15 +0300
parents 852a745503cf
children def49a046566
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <signal.h>
6 #include <errno.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <sys/select.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <arpa/inet.h>
13 #include "http.h"
15 /* HTTP version */
16 #define HTTP_VER_MAJOR 1
17 #define HTTP_VER_MINOR 1
18 #define HTTP_VER_STR "1.1"
20 /* maximum request length: 64mb */
21 #define MAX_REQ_LENGTH (65536 * 1024)
23 struct client {
24 int s;
25 char *rcvbuf;
26 int bufsz;
27 struct client *next;
28 };
30 int start_server(void);
31 int accept_conn(int lis);
32 void close_conn(struct client *c);
33 int handle_client(struct client *c);
34 void do_get_head(struct client *c);
35 void respond_error(struct client *c, int errcode);
36 void sighandler(int s);
37 int parse_args(int argc, char **argv);
39 static int lis;
40 static int port = 8080;
41 static struct client *clist;
43 int main(int argc, char **argv)
44 {
45 if(parse_args(argc, argv) == -1) {
46 return 1;
47 }
49 signal(SIGINT, sighandler);
50 signal(SIGTERM, sighandler);
51 signal(SIGQUIT, sighandler);
53 if((lis = start_server()) == -1) {
54 return 1;
55 }
57 for(;;) {
58 struct client *c, dummy;
59 int maxfd = lis;
60 fd_set rdset;
62 FD_ZERO(&rdset);
63 FD_SET(lis, &rdset);
65 c = clist;
66 while(c) {
67 if(c->s > maxfd) {
68 maxfd = c->s;
69 }
70 FD_SET(c->s, &rdset);
71 c = c->next;
72 }
74 while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
76 c = clist;
77 while(c) {
78 if(FD_ISSET(c->s, &rdset)) {
79 handle_client(c);
80 }
81 c = c->next;
82 }
84 if(FD_ISSET(lis, &rdset)) {
85 accept_conn(lis);
86 }
88 dummy.next = clist;
89 c = &dummy;
91 while(c->next) {
92 struct client *n = c->next;
94 if(n->s == -1) {
95 /* marked for removal */
96 c->next = n->next;
97 free(n);
98 } else {
99 c = c->next;
100 }
101 }
102 clist = dummy.next;
103 }
105 return 0; /* unreachable */
106 }
108 int start_server(void)
109 {
110 int s;
111 struct sockaddr_in sa;
113 if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
114 perror("failed to create listening socket");
115 return -1;
116 }
117 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
119 memset(&sa, 0, sizeof sa);
120 sa.sin_family = AF_INET;
121 sa.sin_addr.s_addr = INADDR_ANY;
122 sa.sin_port = htons(port);
124 if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
125 fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
126 return -1;
127 }
128 listen(s, 16);
130 return s;
131 }
133 int accept_conn(int lis)
134 {
135 int s;
136 struct client *c;
137 struct sockaddr_in addr;
138 socklen_t addr_sz = sizeof addr;
140 if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
141 perror("failed to accept incoming connection");
142 return -1;
143 }
144 fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
146 if(!(c = malloc(sizeof *c))) {
147 perror("failed to allocate memory while accepting connection");
148 return -1;
149 }
150 c->s = s;
151 c->rcvbuf = 0;
152 c->bufsz = 0;
153 c->next = clist;
154 clist = c;
155 return 0;
156 }
158 void close_conn(struct client *c)
159 {
160 close(c->s);
161 c->s = -1; /* mark it for removal */
162 free(c->rcvbuf);
163 }
165 int handle_client(struct client *c)
166 {
167 struct http_req_header hdr;
168 static char buf[2048];
169 int rdsz, status;
171 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
172 char *newbuf;
173 int newsz = c->bufsz + rdsz;
174 if(newsz > MAX_REQ_LENGTH) {
175 respond_error(c, 413);
176 return -1;
177 }
179 if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) {
180 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
181 respond_error(c, 503);
182 return -1;
183 }
185 memcpy(newbuf + c->bufsz, buf, rdsz);
186 newbuf[newsz] = 0;
188 c->rcvbuf = newbuf;
189 c->bufsz = newsz;
190 }
192 if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) {
193 http_print_header(&hdr);
194 switch(status) {
195 case HTTP_HDR_INVALID:
196 respond_error(c, 400);
197 return -1;
199 case HTTP_HDR_NOMEM:
200 respond_error(c, 503);
201 return -1;
203 case HTTP_HDR_PARTIAL:
204 return 0; /* partial header, continue reading */
205 }
206 }
207 http_print_header(&hdr);
209 /* we only support GET and HEAD at this point, so freak out on anything else */
210 switch(hdr.method) {
211 case HTTP_GET:
212 case HTTP_HEAD:
213 do_get_head(c);
214 break;
216 default:
217 respond_error(c, 501);
218 return -1;
219 }
221 close_conn(c);
222 return 0;
223 }
225 void do_get_head(struct client *c)
226 {
227 }
229 void respond_error(struct client *c, int errcode)
230 {
231 char buf[512];
233 sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
235 send(c->s, buf, strlen(buf), 0);
236 close_conn(c);
237 }
239 void sighandler(int s)
240 {
241 if(s == SIGINT || s == SIGTERM || s == SIGQUIT) {
242 close(lis);
243 while(clist) {
244 struct client *c = clist;
245 clist = clist->next;
246 close_conn(c);
247 free(c);
248 }
249 clist = 0;
251 printf("bye!\n");
252 exit(0);
253 }
254 }
257 static void print_help(const char *argv0)
258 {
259 printf("Usage: %s [options]\n", argv0);
260 printf("Options:\n");
261 printf(" -p <port> set the TCP/IP port number to use\n");
262 printf(" -h print usage help and exit\n");
263 }
265 int parse_args(int argc, char **argv)
266 {
267 int i;
269 for(i=1; i<argc; i++) {
270 if(argv[i][0] == '-' && argv[i][2] == 0) {
271 switch(argv[i][1]) {
272 case 'p':
273 if((port = atoi(argv[++i])) == 0) {
274 fprintf(stderr, "-p must be followed by a valid port number\n");
275 return -1;
276 }
277 break;
279 case 'h':
280 print_help(argv[0]);
281 exit(0);
283 default:
284 fprintf(stderr, "unrecognized option: %s\n", argv[i]);
285 return -1;
286 }
287 } else {
288 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
289 return -1;
290 }
291 }
292 return 0;
293 }