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

Contents of /hydra/src/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.32 - (show annotations)
Sun Oct 27 08:15:25 2002 UTC (21 years, 5 months ago) by nmav
Branch: MAIN
CVS Tags: hydra_0_0_10, hydra_0_0_9, hydra_0_1_0
Branch point for: hydra_0_1_0_patches
Changes since 1.31: +11 -7 lines
File MIME type: text/plain
Corrected redirection/aliases behaviour, to match the documented one.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26