/[hydra]/hydra/src/request.c
ViewVC logotype

Contents of /hydra/src/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.35 - (show annotations)
Mon Nov 3 10:59:45 2003 UTC (20 years, 5 months ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_1_6_without_hic, hydra_0_1_7, hydra_0_1_6, hydra_0_1_4, hydra_0_1_8, HEAD
Changes since 1.34: +2 -2 lines
File MIME type: text/plain
ported to gnutls 0.9.x and some other minor fixes.

1 /*
2 * Hydra, an http server
3 * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4 * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
5 * Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
6 * Portions Copyright (C) 2002 Nikos Mavroyanopoulos <nmav@gnutls.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 */
23
24 /* $Id: request.c,v 1.34 2003/01/26 11:25:39 nmav Exp $*/
25
26 #include "boa.h"
27 #include <stddef.h> /* for offsetof */
28 #include "ssl.h"
29 #include "socket.h"
30
31 extern int boa_ssl;
32 int system_bufsize = 0; /* Default size of SNDBUF given by system */
33
34 inline static void init_vhost_stuff(request * req, char *value);
35
36 /* function prototypes located in this file only */
37 static void free_request(server_params * params, request ** list_head_addr,
38 request * req);
39
40 /*
41 * Name: new_request
42 * Description: Obtains a request struct off the free list, or if the
43 * free list is empty, allocates memory
44 *
45 * Return value: pointer to initialized request
46 */
47
48 request *new_request(server_params * params)
49 {
50 request *req;
51
52 if (params->request_free) {
53 req = params->request_free; /* first on free list */
54 dequeue(&params->request_free, params->request_free); /* dequeue the head */
55 } else {
56 req = (request *) malloc(sizeof(request));
57 if (!req) {
58 log_error_time();
59 perror("malloc for new request");
60 return NULL;
61 }
62 }
63 memset(req, 0, offsetof(request, buffer) + 1);
64 req->data_fd = -1;
65 req->post_data_fd.fds[0] = req->post_data_fd.fds[1] = -1;
66
67 return req;
68 }
69
70 #ifdef ENABLE_SMP
71 static pthread_mutex_t accept_mutex[2] = {
72 PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
73 };
74 #endif
75
76 /* Keep 2 numbers. One for the plain connections, and one
77 * for the secure ones (SSL).
78 */
79 static long int total_global_connections[2] = { 0, 0 };
80
81 /* Decreases total_global_connections, but does some locking
82 * too.
83 */
84 inline static void decrease_global_total_connections(int ssl)
85 {
86 /* if we do want to serve as much as possible, then
87 * don't bother counting connections.
88 */
89 if (max_connections == INT_MAX && max_ssl_connections == INT_MAX)
90 return;
91
92 #ifdef ENABLE_SMP
93 pthread_mutex_lock(&accept_mutex[ssl]);
94 #endif
95 total_global_connections[ssl]--;
96 #ifdef ENABLE_SMP
97 pthread_mutex_unlock(&accept_mutex[ssl]);
98 #endif
99
100 }
101
102 /* Returns the number of total connections
103 */
104 long int get_total_global_connections(int ssl)
105 {
106 long int ret;
107
108 #ifdef ENABLE_SMP
109 pthread_mutex_lock(&accept_mutex[ssl]);
110 #endif
111 ret = total_global_connections[ssl];
112 #ifdef ENABLE_SMP
113 pthread_mutex_unlock(&accept_mutex[ssl]);
114 #endif
115 return ret;
116
117 }
118
119 /*
120 * Name: get_request
121 *
122 * Description: Polls the server socket for a request. If one exists,
123 * does some basic initialization and adds it to the ready queue;.
124 */
125
126 void get_request(server_params * params, socket_type * server_s)
127 {
128 int fd; /* socket */
129 struct SOCKADDR remote_addr; /* address */
130 struct SOCKADDR salocal;
131 int remote_addrlen = sizeof(struct SOCKADDR);
132 request *conn; /* connection */
133 int len;
134 static int sockbufsize = SOCKETBUF_SIZE;
135 #ifdef ENABLE_SSL
136 gnutls_session ssl_state = NULL;
137 #endif
138
139 remote_addr.S_FAMILY = 0xdead;
140
141 #ifdef ENABLE_SMP
142 /* We make use of the fact that server_s->secure is either
143 * 0 or 1. 0 Is used for the non SSL mutex, and 1 for the
144 * secure one.
145 */
146 pthread_mutex_lock(&accept_mutex[server_s->secure]);
147 #endif
148
149 /* If we have reached our max connections limit
150 */
151 if ((!server_s->secure && total_global_connections[0] >= max_connections) ||
152 (server_s->secure && total_global_connections[1] >= max_ssl_connections))
153 {
154 server_s->pending_requests = 0;
155 goto unlock;
156 }
157
158 fd = accept(server_s->socket, (struct sockaddr *) &remote_addr,
159 &remote_addrlen);
160
161 if (fd == -1) {
162 if (errno != EAGAIN && errno != EWOULDBLOCK)
163 /* abnormal error */
164 WARN("accept");
165 else
166 /* no requests */
167 server_s->pending_requests = 0;
168 goto unlock;
169 }
170
171 /* only count, if we have enabled a connection limit */
172 if (max_connections != INT_MAX || max_ssl_connections != INT_MAX) {
173 total_global_connections[server_s->secure]++;
174 }
175
176 #ifdef ENABLE_SMP
177 /* No dead lock conditions here, since accept() is non blocking.
178 */
179 pthread_mutex_unlock(&accept_mutex[server_s->secure]);
180 #endif
181
182 if (fd >= FD_SETSIZE) {
183 WARN("Got fd >= FD_SETSIZE.");
184 close(fd);
185 return;
186 }
187
188 #ifdef ENABLE_SSL
189 if (server_s->secure) {
190 ssl_state = initialize_ssl_session();
191 if (ssl_state == NULL) {
192 WARN("Could not initialize SSL session.");
193 close(fd);
194 return;
195 }
196
197 gnutls_transport_set_ptr(ssl_state, (gnutls_transport_ptr)fd);
198 }
199 #endif
200
201 len = sizeof(salocal);
202
203 if (getsockname(fd, (struct sockaddr *) &salocal, &len) != 0) {
204 WARN("getsockname");
205 close(fd);
206 return;
207 }
208
209 conn = new_request(params);
210 if (!conn) {
211 close(fd);
212 return;
213 }
214
215 conn->fd = fd;
216 #ifdef ENABLE_SSL
217 conn->ssl_state = ssl_state;
218 #endif
219
220 if (server_s->secure != 0)
221 conn->secure = 1;
222 else
223 conn->secure = 0;
224
225 if (server_s->secure != 0)
226 conn->status = FINISH_HANDSHAKE;
227 else
228 conn->status = READ_HEADER;
229
230 conn->header_line = conn->client_stream;
231 conn->time_last = current_time;
232 conn->kacount = ka_max;
233
234 ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST);
235
236 /* nonblocking socket */
237 if (set_nonblock_fd(conn->fd) == -1)
238 WARN("fcntl: unable to set new socket to non-block");
239
240 /* set close on exec to true */
241 if (set_cloexec_fd(conn->fd) == -1)
242 WARN("fctnl: unable to set close-on-exec for new socket");
243
244 /* Increase buffer size if we have to.
245 * Only ask the system the buffer size on the first request,
246 * and assume all subsequent sockets have the same size.
247 */
248 if (system_bufsize == 0) {
249 len = sizeof(system_bufsize);
250 if (getsockopt
251 (conn->fd, SOL_SOCKET, SO_SNDBUF, &system_bufsize, &len) == 0
252 && len == sizeof(system_bufsize)) {
253 ;
254 } else {
255 WARN("getsockopt(SNDBUF)");
256 system_bufsize = sockbufsize;
257 }
258 }
259 if (system_bufsize < params->sockbufsize) {
260 if (setsockopt
261 (conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &params->sockbufsize,
262 sizeof(params->sockbufsize)) == -1) {
263 WARN("setsockopt: unable to set socket buffer size");
264 #ifdef DIE_ON_ERROR_TUNING_SNDBUF
265 exit(errno);
266 #endif
267 }
268 }
269
270 init_vhost_stuff(conn, "");
271
272 /* for log file and possible use by CGI programs */
273 ascii_sockaddr(&remote_addr, conn->remote_ip_addr, NI_MAXHOST);
274
275 /* for possible use by CGI programs */
276 conn->remote_port = net_port(&remote_addr);
277
278 params->status.requests++;
279
280 socket_set_options(conn->fd);
281
282 params->total_connections++;
283
284 enqueue(&params->request_ready, conn);
285
286 return;
287
288 unlock:
289 #ifdef ENABLE_SMP
290 pthread_mutex_unlock(&accept_mutex[server_s->secure]);
291 #endif
292 return;
293 }
294
295
296 /*
297 * Name: free_request
298 *
299 * Description: Deallocates memory for a finished request and closes
300 * down socket.
301 */
302
303 static void free_request(server_params * params, request ** list_head_addr,
304 request * req)
305 {
306 int i;
307 /* free_request should *never* get called by anything but
308 process_requests */
309
310 if (req->buffer_end && req->status != DEAD) {
311 req->status = DONE;
312 return;
313 }
314 /* put request on the free list */
315 dequeue(list_head_addr, req); /* dequeue from ready or block list */
316
317 if (req->logline) /* access log */
318 log_access(req);
319
320 if (req->mmap_entry_var)
321 release_mmap(req->mmap_entry_var);
322 /* FIXME: Why is it needed? */
323 else if (req->data_mem)
324 munmap(req->data_mem, req->filesize);
325
326 if (req->data_fd != -1)
327 close(req->data_fd);
328
329 close_tmp_fd(&req->post_data_fd);
330
331 if (req->response_status >= 400)
332 params->status.errors++;
333
334 for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) {
335 if (req->cgi_env[i]) {
336 free(req->cgi_env[i]);
337 } else {
338 log_error_time();
339 fprintf(stderr, "Warning: CGI Environment contains NULL value"
340 "(index %d of %d).\n", i, req->cgi_env_index);
341 }
342 }
343
344 free(req->pathname);
345 free(req->query_string);
346 free(req->path_info);
347 free(req->path_translated);
348 free(req->script_name);
349
350 if ((req->keepalive == KA_ACTIVE) &&
351 (req->response_status < 500) && req->kacount > 0) {
352 int bytes_to_move;
353
354 request *conn = new_request(params);
355 if (!conn) {
356 /* errors already reported */
357 enqueue(&params->request_free, req);
358 close(req->fd);
359 params->total_connections--;
360 decrease_global_total_connections(req->secure);
361 return;
362 }
363 conn->fd = req->fd;
364
365 #ifdef ENABLE_SSL
366 if (req->secure != 0) {
367 conn->secure = 1;
368 conn->ssl_state = req->ssl_state;
369
370 conn->status = READ_HEADER;
371 } else {
372 #endif
373 conn->secure = 0;
374 conn->status = READ_HEADER;
375 #ifdef ENABLE_SSL
376 conn->ssl_state = NULL;
377 }
378 #endif
379
380 conn->header_line = conn->client_stream;
381 conn->kacount = req->kacount - 1;
382
383 /* close enough and we avoid a call to time(NULL) */
384 conn->time_last = req->time_last;
385
386 /* for log file and possible use by CGI programs */
387 memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST);
388 memcpy(conn->local_ip_addr, req->local_ip_addr, NI_MAXHOST);
389
390 /* for possible use by CGI programs */
391 conn->remote_port = req->remote_port;
392
393 conn->action = req->action;
394
395 params->status.requests++;
396
397 /* we haven't parsed beyond req->parse_pos, so... */
398 bytes_to_move = req->client_stream_pos - req->parse_pos;
399
400 if (bytes_to_move) {
401 memcpy(conn->client_stream,
402 req->client_stream + req->parse_pos, bytes_to_move);
403 conn->client_stream_pos = bytes_to_move;
404 }
405 enqueue(&params->request_block, conn);
406
407 BOA_FD_SET(conn, conn->fd, BOA_READ);
408
409 enqueue(&params->request_free, req);
410
411 return;
412 }
413
414 /*
415 While debugging some weird errors, Jon Nelson learned that
416 some versions of Netscape Navigator break the
417 HTTP specification.
418
419 Some research on the issue brought up:
420
421 http://www.apache.org/docs/misc/known_client_problems.html
422
423 As quoted here:
424
425 "
426 Trailing CRLF on POSTs
427
428 This is a legacy issue. The CERN webserver required POST
429 data to have an extra CRLF following it. Thus many
430 clients send an extra CRLF that is not included in the
431 Content-Length of the request. Apache works around this
432 problem by eating any empty lines which appear before a
433 request.
434 "
435
436 Boa will (for now) hack around this stupid bug in Netscape
437 (and Internet Exploder)
438 by reading up to 32k after the connection is all but closed.
439 This should eliminate any remaining spurious crlf sent
440 by the client.
441
442 Building bugs *into* software to be compatable is
443 just plain wrong
444 */
445
446 if (req->method == M_POST) {
447 char buf[32768];
448
449 socket_recv(req, buf, sizeof(buf));
450 }
451 #ifdef ENABLE_SSL
452 if (req->secure) {
453 gnutls_bye(req->ssl_state, GNUTLS_SHUT_WR);
454 gnutls_deinit(req->ssl_state);
455 }
456 #endif
457 close(req->fd);
458
459 params->total_connections--;
460 decrease_global_total_connections(req->secure);
461
462 enqueue(&params->request_free, req);
463
464 return;
465 }
466
467 /*
468 * Name: process_requests
469 *
470 * Description: Iterates through the ready queue, passing each request
471 * to the appropriate handler for processing. It monitors the
472 * return value from handler functions, all of which return -1
473 * to indicate a block, 0 on completion and 1 to remain on the
474 * ready list for more procesing.
475 */
476
477 void process_requests(server_params * params, socket_type * server_s)
478 {
479 int retval = 0;
480 request *current, *trailer;
481
482 if (server_s->pending_requests) {
483 get_request(params, server_s);
484 #ifdef ORIGINAL_BEHAVIOR
485 server_s->pending_requests = 0;
486 #endif
487 }
488
489 current = params->request_ready;
490
491 while (current) {
492 if (current->buffer_end && /* there is data in the buffer */
493 current->status != DEAD && current->status != DONE) {
494 retval = req_flush(current);
495 /*
496 * retval can be -2=error, -1=blocked, or bytes left
497 */
498 if (retval == -2) { /* error */
499 current->status = DEAD;
500 retval = 0;
501 } else if (retval >= 0) {
502 /* notice the >= which is different from below?
503 Here, we may just be flushing headers.
504 We don't want to return 0 because we are not DONE
505 or DEAD */
506
507 retval = 1;
508 }
509 } else {
510 switch (current->status) {
511 #ifdef ENABLE_SSL
512 case FINISH_HANDSHAKE:
513 retval = finish_handshake(current);
514 break;
515 case SEND_ALERT:
516 retval = send_alert(current);
517 break;
518 #endif
519 case READ_HEADER:
520 case ONE_CR:
521 case ONE_LF:
522 case TWO_CR:
523 retval = read_header(params, current);
524 break;
525 case BODY_READ:
526 retval = read_body(current);
527 break;
528 case BODY_WRITE:
529 retval = write_body(current);
530 break;
531 case WRITE:
532 retval = process_get(params, current);
533 break;
534 case PIPE_READ:
535 retval = read_from_pipe(current);
536 break;
537 case PIPE_WRITE:
538 retval = write_from_pipe(current);
539 break;
540 case IOSHUFFLE:
541 retval = io_shuffle(current);
542 break;
543 case DONE:
544 /* a non-status that will terminate the request */
545 retval = req_flush(current);
546 /*
547 * retval can be -2=error, -1=blocked, or bytes left
548 */
549 if (retval == -2) { /* error */
550 current->status = DEAD;
551 retval = 0;
552 } else if (retval > 0) {
553 retval = 1;
554 }
555 break;
556 case DEAD:
557 retval = 0;
558 current->buffer_end = 0;
559 SQUASH_KA(current);
560 break;
561 default:
562 retval = 0;
563 fprintf(stderr, "Unknown status (%d), "
564 "closing!\n", current->status);
565 current->status = DEAD;
566 break;
567 }
568
569 }
570
571 if (params->sigterm_flag)
572 SQUASH_KA(current);
573
574 /* we put this here instead of after the switch so that
575 * if we are on the last request, and get_request is successful,
576 * current->next is valid!
577 */
578 if (server_s->pending_requests)
579 get_request(params, server_s);
580
581 switch (retval) {
582 case -1: /* request blocked */
583 trailer = current;
584 current = current->next;
585 block_request(params, trailer);
586 break;
587 case 0: /* request complete */
588 current->time_last = current_time;
589 trailer = current;
590 current = current->next;
591 free_request(params, &params->request_ready, trailer);
592 break;
593 case 1: /* more to do */
594 current->time_last = current_time;
595 current = current->next;
596 break;
597 default:
598 log_error_time();
599 fprintf(stderr, "Unknown retval in process.c - "
600 "Status: %d, retval: %d\n", current->status, retval);
601 current = current->next;
602 break;
603 }
604 }
605 }
606
607 /* Initializes several stuff that depend on the sent HTTP
608 * version number.
609 *
610 * Returns true on sucess, or 0 otherwise.
611 */
612 inline static
613 int init_http_version_specific_stuff(request * req)
614 {
615 if ( req->http_version==HTTP_1_1) {
616 if (!req->keepalive_given)
617 req->keepalive = KA_ACTIVE; /* keepalive is active by default */
618
619 if (req->hostname_given == 0) {
620 return 0;
621 }
622 }
623
624 return 1; /* success */
625 }
626
627 /*
628 * Name: process_logline
629 *
630 * Description: This is called with the first req->header_line received
631 * by a request, called "logline" because it is logged to a file.
632 * It is parsed to determine request type and method, then passed to
633 * translate_uri for further parsing. Also sets up CGI environment if
634 * needed.
635 */
636 #define SIMPLE_HTTP_VERSION "HTTP/0.9"
637 int process_logline(request * req)
638 {
639 char *stop, *stop2;
640
641 req->logline = req->client_stream;
642 if (!memcmp(req->logline, "GET ", 4))
643 req->method = M_GET;
644 else if (!memcmp(req->logline, "HEAD ", 5))
645 /* head is just get w/no body */
646 req->method = M_HEAD;
647 else if (!memcmp(req->logline, "POST ", 5))
648 req->method = M_POST;
649 else {
650 log_error_doc(req);
651 fprintf(stderr, "malformed request: \"%s\"\n", req->logline);
652 send_r_not_implemented(req);
653 return 0;
654 }
655
656 req->http_version_str = SIMPLE_HTTP_VERSION;
657 req->http_version = HTTP_0_9;
658
659 /* Guaranteed to find ' ' since we matched a method above */
660 stop = req->logline + 3;
661 if (*stop != ' ')
662 ++stop;
663
664 /* scan to start of non-whitespace */
665 while (*(++stop) == ' ');
666
667 stop2 = stop;
668
669 /* scan to end of non-whitespace */
670 while (*stop2 != '\0' && *stop2 != ' ')
671 ++stop2;
672
673 if (stop2 - stop > MAX_HEADER_LENGTH) {
674 log_error_doc(req);
675 fprintf(stderr, "URI too long %d: \"%s\"\n", MAX_HEADER_LENGTH,
676 req->logline);
677 send_r_bad_request(req);
678 return 0;
679 }
680 memcpy(req->request_uri, stop, stop2 - stop);
681 req->request_uri[stop2 - stop] = '\0';
682
683 if (*stop2 == ' ') {
684 /* if found, we should get an HTTP/x.x */
685 unsigned int p1, p2;
686
687 /* scan to end of whitespace */
688 ++stop2;
689 while (*stop2 == ' ' && *stop2 != '\0')
690 ++stop2;
691
692 /* scan in HTTP/major.minor */
693 if (sscanf(stop2, "HTTP/%u.%u", &p1, &p2) == 2) {
694 /* HTTP/{0.9,1.0,1.1} */
695 if (p1 == 1) { /* We accept all HTTP/1.x versions */
696 req->http_version_str = stop2;
697 switch(p2) {
698 case 0:
699 req->http_version = HTTP_1_0;
700 break;
701 case 1:
702 default:
703 req->http_version = HTTP_1_1;
704 }
705 } else if (p1 > 1) { /* major number > 1 is invalid for us */
706 goto BAD_VERSION;
707 }
708 } else {
709 goto BAD_VERSION;
710 }
711
712 }
713
714 if (req->method == M_HEAD && req->http_version == HTTP_0_9) {
715 send_r_bad_request(req);
716 return 0;
717 }
718 req->cgi_env_index = COMMON_CGI_COUNT;
719
720 return 1;
721
722 BAD_VERSION:
723 log_error_doc(req);
724 fprintf(stderr, "bogus HTTP version: \"%s\"\n", stop2);
725 send_r_bad_request(req);
726 return 0;
727 }
728
729 /*
730 * Name: process_header_end
731 *
732 * Description: takes a request and performs some final checking before
733 * init_cgi or init_get
734 * Returns 0 for error or NPH, or 1 for success
735 */
736
737 int process_header_end(server_params * params, request * req)
738 {
739 char *p = NULL;
740
741 if (!req->logline) {
742 send_r_error(req);
743 return 0;
744 }
745
746 /* Check if all the stuff matches the HTTP version
747 * sent.
748 */
749 if (!init_http_version_specific_stuff(req)) {
750 send_r_bad_request(req);
751 return 0;
752 }
753
754 /* Percent-decode request */
755 if (unescape_uri(req->request_uri, &p) == 0) {
756 log_error_doc(req);
757 fputs("Problem unescaping uri\n", stderr);
758 send_r_bad_request(req);
759 return 0;
760 }
761
762 if (p) {
763 req->query_string = strdup(p);
764 if (req->query_string == NULL) {
765 send_r_error(req);
766 return 0;
767 }
768 }
769
770 /* clean pathname */
771 clean_pathname(req->request_uri);
772
773 if (req->request_uri[0] != '/') {
774 send_r_bad_request(req);
775 return 0;
776 }
777
778 if (translate_uri(req) == 0) { /* unescape, parse uri */
779 SQUASH_KA(req);
780 return 0; /* failure, close down */
781 }
782
783 if (req->method == M_POST) {
784 req->post_data_fd =
785 create_temporary_file(1, boa_atoi(req->content_length));
786 if (req->post_data_fd.fds[0] == -1)
787 return (0);
788
789 if (req->post_data_fd.pipe == 0) {
790 if (set_cloexec_fd(req->post_data_fd.fds[0]) == -1) {
791 WARN("unable to set close-on-exec for req->post_data_fd!");
792 close_tmp_fd(&req->post_data_fd);
793 return (0);
794 }
795 }
796
797 return (1); /* success */
798 }
799
800 if (req->is_cgi) {
801 return init_cgi(req);
802 }
803
804 req->status = WRITE;
805 return init_get(params, req); /* get and head */
806 }
807
808 /* Parses HTTP/1.1 range values.
809 */
810 static int parse_range(const char *value, off_t * val1,
811 off_t * val2)
812 {
813 int len;
814 char *p;
815
816 *val1 = *val2 = 0;
817
818 len = strlen(value);
819 if (len < 7)
820 return -1;
821
822 /* we do not accept ranges of the form "bytes=10-20,21-30"
823 */
824 if (strchr(value, ',') != NULL)
825 return -1;
826
827 if (memcmp("bytes=", value, 6) != 0) {
828 return -1;
829 } else
830 value += 6;
831
832 while (*value == ' ')
833 value++;
834 if ((p = strchr(value, '-')) == NULL)
835 return -1;
836
837 if (value[0] == '-') { /* Handle case "bytes=-1024" */
838 *val1 = 0;
839 *val2 = boa_atoll(&value[1]);
840 return 0;
841 } else {
842 char buf[43];
843 int len;
844
845 /* two values of the form "xxx-yyy" */
846
847 if ((len = strlen(value)) >= sizeof(buf))
848 return -1;
849
850 memcpy(buf, value, len);
851 buf[len] = 0;
852
853 p = strchr(buf, '-');
854 if (p == NULL)
855 return -1;
856 *p = 0;
857 p++;
858
859 while (*p == ' ')
860 p++;
861
862 *val1 = boa_atoll(buf);
863
864 if (*p == '\0') /* form: "xxx-" */
865 *val2 = 0;
866 else
867 *val2 = boa_atoll(p);
868
869 if (*val1 == -1)
870 return -1;
871
872 return 0;
873 }
874
875 return -1;
876 }
877
878 inline static void init_range_stuff(request * req, char *value)
879 {
880 off_t p1, p2;
881 if (parse_range(value, &p1, &p2) == 0) {
882 req->range_start = p1;
883 req->range_stop = p2;
884 } else {
885 req->range_start = -1;
886 req->range_stop = -1;
887 log_error_doc(req);
888 fprintf(stderr, "bogus range: \"%s\"\n", value);
889
890 /* here we just ignore a bogus range,
891 * but we have put illegal values in
892 * range start and range stop, to
893 * be detected in init_get().
894 */
895 }
896 }
897
898 inline static void init_vhost_stuff(request * req, char *value)
899 {
900 virthost *vhost;
901 int valuelen;
902
903 valuelen = strlen(value);
904
905 vhost = find_virthost(value, valuelen);
906
907 if (vhost == NULL && value[0] != 0) {
908 value = "";
909 vhost = find_virthost("", 0);
910 }
911
912 if (vhost
913 && (vhost->ip == NULL
914 || !memcmp(vhost->ip, req->local_ip_addr, vhost->ip_len))) {
915 req->hostname = value;
916 memcpy(req->document_root, vhost->document_root,
917 vhost->document_root_len + 1);
918 if (vhost->user_dir)
919 memcpy(req->user_dir, vhost->user_dir, vhost->user_dir_len + 1);
920
921 }
922
923 }
924
925 /*
926 * Name: process_option_line
927 *
928 * Description: Parses the contents of req->header_line and takes
929 * appropriate action.
930 */
931
932 int process_option_line(request * req)
933 {
934 char c, *value, *line = req->header_line;
935
936 /* Start by aggressively hacking the in-place copy of the header line */
937
938 #ifdef FASCIST_LOGGING
939 log_error_time();
940 fprintf(stderr, "%s:%d - Parsing \"%s\"\n", __FILE__, __LINE__, line);
941 #endif
942
943 value = strchr(line, ':');
944 if (value == NULL)
945 return 0;
946 *value++ = '\0'; /* overwrite the : */
947 to_upper(line); /* header types are case-insensitive */
948 while ((c = *value) && (c == ' ' || c == '\t'))
949 value++;
950
951
952 switch (line[0]) {
953
954 case 'A':
955 if (!memcmp(line, "ACCEPT", 7))
956 add_accept_header(req, value);
957 else
958 goto just_add_header;
959 break;
960
961 case 'C':
962 if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type)
963 req->content_type = value;
964 else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length)
965 req->content_length = value;
966 else if (!memcmp(line, "CONNECTION", 11) &&
967 ka_max && req->keepalive != KA_STOPPED) {
968 req->keepalive_given = 1;
969 req->keepalive = (!strncasecmp(value, "Keep-Alive", 10) ?
970 KA_ACTIVE : KA_STOPPED);
971 } else
972 goto just_add_header;
973
974 break;
975
976 case 'H':
977 if (!memcmp(line, "HOST", 4)) {
978 req->hostname_given = 1;
979 init_vhost_stuff(req, value);
980 if (!add_cgi_env(req, "HOST", value, 1))
981 return 0;
982 } else
983 goto just_add_header;
984 break;
985
986 case 'I':
987 if (!memcmp(line, "IF_", 3)) {
988 char *p = line + 3;
989
990 if (!memcmp(p, "MODIFIED_SINCE", 15) && !req->if_modified_since) {
991 req->if_types |= IF_MODIFIED_SINCE;
992 req->if_modified_since = value;
993
994 } else if (!memcmp(p, "MATCH", 5) && !req->if_match_etag) {
995 req->if_types |= IF_MATCH;
996 req->if_match_etag = value;
997
998 } else if (!memcmp(p, "NONE_MATCH", 10)
999 && !req->if_none_match_etag) {
1000 req->if_types |= IF_NONE_MATCH;
1001 req->if_none_match_etag = value;
1002
1003 } else if (!memcmp(p, "RANGE", 5) && !req->if_range_etag) {
1004 req->if_types |= IF_RANGE;
1005 req->if_range_etag = value;
1006 }
1007
1008 if (!add_cgi_env(req, line, value, 1))
1009 return 0;
1010 break;
1011 } else
1012 goto just_add_header;
1013
1014
1015 case 'R':
1016 /* Need agent and referer for logs */
1017 if (!memcmp(line, "REFERER", 8)) {
1018 req->header_referer = value;
1019 if (!add_cgi_env(req, "REFERER", value, 1))
1020 return 0;
1021 } else if (!memcmp(line, "RANGE", 5)) {
1022 init_range_stuff(req, value);
1023 } else goto just_add_header;
1024 break;
1025
1026
1027 case 'U':
1028 if (!memcmp(line, "USER_AGENT", 11)) {
1029 req->header_user_agent = value;
1030 if (!add_cgi_env(req, "USER_AGENT", value, 1))
1031 return 0;
1032 } else
1033 goto just_add_header;
1034 break;
1035
1036 default:
1037 just_add_header:
1038 if (!add_cgi_env(req, line, value, 1))
1039 return 0;
1040 break;
1041
1042 }
1043
1044 return 1;
1045
1046 }
1047
1048 /*
1049 * Name: add_accept_header
1050 * Description: Adds a mime_type to a requests accept char buffer
1051 * silently ignore any that don't fit -
1052 * shouldn't happen because of relative buffer sizes
1053 */
1054
1055 void add_accept_header(request * req, char *mime_type)
1056 {
1057 #ifdef ACCEPT_ON
1058 int l = strlen(req->accept);
1059 int l2 = strlen(mime_type);
1060
1061 if ((l + l2 + 2) >= MAX_HEADER_LENGTH)
1062 return;
1063
1064 if (req->accept[0] == '\0')
1065 strcpy(req->accept, mime_type);
1066 else {
1067 req->accept[l] = ',';
1068 req->accept[l + 1] = ' ';
1069 memcpy(req->accept + l + 2, mime_type, l2 + 1);
1070 /* the +1 is for the '\0' */
1071 /*
1072 sprintf(req->accept + l, ", %s", mime_type);
1073 */
1074 }
1075 #endif
1076 }
1077
1078 void free_requests(server_params * params)
1079 {
1080 request *ptr, *next;
1081
1082 ptr = params->request_free;
1083 while (ptr != NULL) {
1084 next = ptr->next;
1085 free(ptr);
1086 ptr = next;
1087 }
1088 params->request_free = NULL;
1089 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26