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

Contents of /hydra/src/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.32.2.2 - (show annotations)
Sun Dec 15 23:47:52 2002 UTC (21 years, 4 months ago) by nmav
Branch: hydra_0_1_0_patches
CVS Tags: hydra_0_1_1
Changes since 1.32.2.1: +5 -14 lines
File MIME type: text/plain
Corrected Keep Alive mode in SSL and TLS connections.

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.32.2.1 2002/12/14 08:54:16 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->secure = 1;
377 conn->ssl_state = req->ssl_state;
378
379 conn->status = READ_HEADER;
380 } else {
381 #endif
382 conn->secure = 0;
383 conn->status = READ_HEADER;
384 #ifdef ENABLE_SSL
385 conn->ssl_state = NULL;
386 }
387 #endif
388
389 conn->header_line = conn->client_stream;
390 conn->kacount = req->kacount - 1;
391
392 /* close enough and we avoid a call to time(NULL) */
393 conn->time_last = req->time_last;
394
395 /* for log file and possible use by CGI programs */
396 memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST);
397 memcpy(conn->local_ip_addr, req->local_ip_addr, NI_MAXHOST);
398
399 /* for possible use by CGI programs */
400 conn->remote_port = req->remote_port;
401
402 params->status.requests++;
403
404 /* we haven't parsed beyond req->parse_pos, so... */
405 bytes_to_move = req->client_stream_pos - req->parse_pos;
406
407 if (bytes_to_move) {
408 memcpy(conn->client_stream,
409 req->client_stream + req->parse_pos, bytes_to_move);
410 conn->client_stream_pos = bytes_to_move;
411 }
412 enqueue(&params->request_block, conn);
413
414 BOA_FD_SET(conn, conn->fd, BOA_READ);
415
416 enqueue(&params->request_free, req);
417
418 return;
419 }
420
421 /*
422 While debugging some weird errors, Jon Nelson learned that
423 some versions of Netscape Navigator break the
424 HTTP specification.
425
426 Some research on the issue brought up:
427
428 http://www.apache.org/docs/misc/known_client_problems.html
429
430 As quoted here:
431
432 "
433 Trailing CRLF on POSTs
434
435 This is a legacy issue. The CERN webserver required POST
436 data to have an extra CRLF following it. Thus many
437 clients send an extra CRLF that is not included in the
438 Content-Length of the request. Apache works around this
439 problem by eating any empty lines which appear before a
440 request.
441 "
442
443 Boa will (for now) hack around this stupid bug in Netscape
444 (and Internet Exploder)
445 by reading up to 32k after the connection is all but closed.
446 This should eliminate any remaining spurious crlf sent
447 by the client.
448
449 Building bugs *into* software to be compatable is
450 just plain wrong
451 */
452
453 if (req->method == M_POST) {
454 char buf[32768];
455
456 socket_recv( req, buf, sizeof(buf));
457 }
458
459 #ifdef ENABLE_SSL
460 if ( req->secure) {
461 gnutls_bye(req->ssl_state, GNUTLS_SHUT_WR);
462 gnutls_deinit( req->ssl_state);
463 }
464 #endif
465 close(req->fd);
466
467 params->total_connections--;
468 decrease_global_total_connections();
469
470 enqueue(&params->request_free, req);
471
472 return;
473 }
474
475 /*
476 * Name: process_requests
477 *
478 * Description: Iterates through the ready queue, passing each request
479 * to the appropriate handler for processing. It monitors the
480 * return value from handler functions, all of which return -1
481 * to indicate a block, 0 on completion and 1 to remain on the
482 * ready list for more procesing.
483 */
484
485 void process_requests(server_params* params, socket_type *server_s)
486 {
487 int retval = 0;
488 request *current, *trailer;
489
490 if (server_s->pending_requests) {
491 get_request(params, server_s);
492 #ifdef ORIGINAL_BEHAVIOR
493 server_s->pending_requests = 0;
494 #endif
495 }
496
497 current = params->request_ready;
498
499 while (current) {
500 if (current->buffer_end && /* there is data in the buffer */
501 current->status != DEAD && current->status != DONE) {
502 retval = req_flush(current);
503 /*
504 * retval can be -2=error, -1=blocked, or bytes left
505 */
506 if (retval == -2) { /* error */
507 current->status = DEAD;
508 retval = 0;
509 } else if (retval >= 0) {
510 /* notice the >= which is different from below?
511 Here, we may just be flushing headers.
512 We don't want to return 0 because we are not DONE
513 or DEAD */
514
515 retval = 1;
516 }
517 } else {
518 switch (current->status) {
519 #ifdef ENABLE_SSL
520 case FINISH_HANDSHAKE:
521 retval = finish_handshake( current);
522 break;
523 case SEND_ALERT:
524 retval = send_alert( current);
525 break;
526 #endif
527 case READ_HEADER:
528 case ONE_CR:
529 case ONE_LF:
530 case TWO_CR:
531 retval = read_header(params, current);
532 break;
533 case BODY_READ:
534 retval = read_body(current);
535 break;
536 case BODY_WRITE:
537 retval = write_body(current);
538 break;
539 case WRITE:
540 retval = process_get(params, current);
541 break;
542 case PIPE_READ:
543 retval = read_from_pipe(current);
544 break;
545 case PIPE_WRITE:
546 retval = write_from_pipe(current);
547 break;
548 case IOSHUFFLE:
549 retval = io_shuffle(current);
550 break;
551 case DONE:
552 /* a non-status that will terminate the request */
553 retval = req_flush(current);
554 /*
555 * retval can be -2=error, -1=blocked, or bytes left
556 */
557 if (retval == -2) { /* error */
558 current->status = DEAD;
559 retval = 0;
560 } else if (retval > 0) {
561 retval = 1;
562 }
563 break;
564 case DEAD:
565 retval = 0;
566 current->buffer_end = 0;
567 SQUASH_KA(current);
568 break;
569 default:
570 retval = 0;
571 fprintf(stderr, "Unknown status (%d), "
572 "closing!\n", current->status);
573 current->status = DEAD;
574 break;
575 }
576
577 }
578
579 if (params->sigterm_flag)
580 SQUASH_KA(current);
581
582 /* we put this here instead of after the switch so that
583 * if we are on the last request, and get_request is successful,
584 * current->next is valid!
585 */
586 if (server_s->pending_requests)
587 get_request(params, server_s);
588
589 switch (retval) {
590 case -1: /* request blocked */
591 trailer = current;
592 current = current->next;
593 block_request(params, trailer);
594 break;
595 case 0: /* request complete */
596 current->time_last = current_time;
597 trailer = current;
598 current = current->next;
599 free_request(params, &params->request_ready, trailer);
600 break;
601 case 1: /* more to do */
602 current->time_last = current_time;
603 current = current->next;
604 break;
605 default:
606 log_error_time();
607 fprintf(stderr, "Unknown retval in process.c - "
608 "Status: %d, retval: %d\n", current->status, retval);
609 current = current->next;
610 break;
611 }
612 }
613 }
614
615 /*
616 * Name: process_logline
617 *
618 * Description: This is called with the first req->header_line received
619 * by a request, called "logline" because it is logged to a file.
620 * It is parsed to determine request type and method, then passed to
621 * translate_uri for further parsing. Also sets up CGI environment if
622 * needed.
623 */
624 #define SIMPLE_HTTP_VERSION "HTTP/0.9"
625 int process_logline(request * req)
626 {
627 char *stop, *stop2;
628
629 req->logline = req->client_stream;
630 if (!memcmp(req->logline, "GET ", 4))
631 req->method = M_GET;
632 else if (!memcmp(req->logline, "HEAD ", 5))
633 /* head is just get w/no body */
634 req->method = M_HEAD;
635 else if (!memcmp(req->logline, "POST ", 5))
636 req->method = M_POST;
637 else {
638 log_error_time();
639 fprintf(stderr, "malformed request: \"%s\"\n", req->logline);
640 send_r_not_implemented(req);
641 return 0;
642 }
643
644 req->http_version = SIMPLE_HTTP_VERSION;
645 req->simple = 1;
646
647 /* Guaranteed to find ' ' since we matched a method above */
648 stop = req->logline + 3;
649 if (*stop != ' ')
650 ++stop;
651
652 /* scan to start of non-whitespace */
653 while (*(++stop) == ' ');
654
655 stop2 = stop;
656
657 /* scan to end of non-whitespace */
658 while (*stop2 != '\0' && *stop2 != ' ')
659 ++stop2;
660
661 if (stop2 - stop > MAX_HEADER_LENGTH) {
662 log_error_time();
663 fprintf(stderr, "URI too long %d: \"%s\"\n", MAX_HEADER_LENGTH,
664 req->logline);
665 send_r_bad_request(req);
666 return 0;
667 }
668 memcpy(req->request_uri, stop, stop2 - stop);
669 req->request_uri[stop2 - stop] = '\0';
670
671 if (*stop2 == ' ') {
672 /* if found, we should get an HTTP/x.x */
673 unsigned int p1, p2;
674
675 /* scan to end of whitespace */
676 ++stop2;
677 while (*stop2 == ' ' && *stop2 != '\0')
678 ++stop2;
679
680 /* scan in HTTP/major.minor */
681 if (sscanf(stop2, "HTTP/%u.%u", &p1, &p2) == 2) {
682 /* HTTP/{0.9,1.0,1.1} */
683 if (p1 == 1) { /* We accept all HTTP/1.x versions */
684 req->http_version = stop2;
685 req->simple = 0;
686 } else if (p1 > 1) { /* major number > 1 is invalid for us */
687 goto BAD_VERSION;
688 }
689 } else {
690 goto BAD_VERSION;
691 }
692 }
693
694 if (req->method == M_HEAD && req->simple) {
695 send_r_bad_request(req);
696 return 0;
697 }
698 req->cgi_env_index = COMMON_CGI_COUNT;
699
700 return 1;
701
702 BAD_VERSION:
703 log_error_time();
704 fprintf(stderr, "bogus HTTP version: \"%s\"\n", stop2);
705 send_r_bad_request(req);
706 return 0;
707 }
708
709 /*
710 * Name: process_header_end
711 *
712 * Description: takes a request and performs some final checking before
713 * init_cgi or init_get
714 * Returns 0 for error or NPH, or 1 for success
715 */
716
717 int process_header_end(server_params* params, request * req)
718 {
719 char *p = NULL;
720
721 if (!req->logline) {
722 send_r_error(req);
723 return 0;
724 }
725
726 /* Percent-decode request */
727 if (unescape_uri(req->request_uri, &p) == 0) {
728 log_error_doc(req);
729 fputs("Problem unescaping uri\n", stderr);
730 send_r_bad_request(req);
731 return 0;
732 }
733
734 if (p) {
735 req->query_string = strdup( p);
736 if (req->query_string == NULL) {
737 send_r_error( req);
738 return 0;
739 }
740 }
741
742 /* clean pathname */
743 clean_pathname(req->request_uri);
744
745 if (req->request_uri[0] != '/') {
746 send_r_bad_request(req);
747 return 0;
748 }
749
750 if (translate_uri(req) == 0) { /* unescape, parse uri */
751 SQUASH_KA(req);
752 return 0; /* failure, close down */
753 }
754
755 if (req->method == M_POST) {
756 req->post_data_fd = create_temporary_file(1, boa_atoi(req->content_length));
757 if (req->post_data_fd.fds[0] == -1)
758 return(0);
759
760 if (req->post_data_fd.pipe==0) {
761 if (set_cloexec_fd( req->post_data_fd.fds[0]) == -1) {
762 WARN("unable to set close-on-exec for req->post_data_fd!");
763 close_tmp_fd(&req->post_data_fd);
764 return(0);
765 }
766 }
767
768 return(1); /* success */
769 }
770
771 if (req->is_cgi) {
772 return init_cgi(req);
773 }
774
775 req->status = WRITE;
776 return init_get(params, req); /* get and head */
777 }
778
779 /* Parses HTTP/1.1 range values.
780 */
781 static int parse_range( const char* value, large_int *val1, large_int *val2)
782 {
783 int len;
784 char *p;
785
786 *val1 = *val2 = 0;
787
788 len = strlen( value);
789 if (len < 7) return -1;
790
791 /* we do not accept ranges of the form "bytes=10-20,21-30"
792 */
793 if (strchr( value, ',') != NULL) return -1;
794
795 if ( memcmp("bytes=", value, 6) != 0) {
796 return -1;
797 } else value += 6;
798
799 while( *value==' ') value++;
800 if ((p=strchr( value, '-')) == NULL) return -1;
801
802 if (value[0] == '-') { /* Handle case "bytes=-1024" */
803 *val1 = 0;
804 *val2 = boa_atoll( &value[1]);
805 return 0;
806 } else {
807 char buf[43];
808 int len;
809
810 /* two values of the form "xxx-yyy" */
811
812 if ((len=strlen( value)) >= sizeof(buf))
813 return -1;
814
815 memcpy( buf, value, len);
816 buf[len] = 0;
817
818 p=strchr( buf, '-');
819 if (p==NULL) return -1;
820 *p = 0;
821 p++;
822
823 while( *p==' ') p++;
824
825 *val1 = boa_atoll( buf);
826
827 if (*p == '\0') /* form: "xxx-" */
828 *val2 = 0;
829 else
830 *val2 = boa_atoll( p);
831
832 if ( *val1 == -1) return -1;
833
834 return 0;
835 }
836
837 return -1;
838 }
839
840 inline
841 static void init_range_stuff( request* req, char* value)
842 {
843 large_int p1, p2;
844 if (parse_range( value, &p1, &p2) == 0) {
845 req->range_start = p1;
846 req->range_stop = p2;
847 } else {
848 req->range_start = 0;
849 req->range_stop = 0;
850 log_error_time();
851 fprintf(stderr, "bogus range: \"%s\"\n", value);
852 /* we just ignore a bogus range */
853 /* send_r_bad_request(req); */
854 }
855 }
856
857 inline
858 static void init_vhost_stuff( request* req, char* value)
859 {
860 virthost* vhost;
861 int valuelen;
862
863 valuelen = strlen(value);
864
865 vhost = find_virthost( value, valuelen);
866
867 if (vhost == NULL && value[0]!=0) {
868 value = "";
869 vhost = find_virthost( "", 0);
870 }
871
872 if ( vhost && ( vhost->ip == NULL || !memcmp( vhost->ip, req->local_ip_addr, vhost->ip_len) ))
873 {
874 req->hostname = value;
875 memcpy( req->document_root, vhost->document_root, vhost->document_root_len + 1);
876 if (vhost->user_dir)
877 memcpy( req->user_dir, vhost->user_dir, vhost->user_dir_len + 1);
878
879 }
880
881 }
882
883 /*
884 * Name: process_option_line
885 *
886 * Description: Parses the contents of req->header_line and takes
887 * appropriate action.
888 */
889
890 int process_option_line(request * req)
891 {
892 char c, *value, *line = req->header_line;
893
894 /* Start by aggressively hacking the in-place copy of the header line */
895
896 #ifdef FASCIST_LOGGING
897 log_error_time();
898 fprintf(stderr, "%s:%d - Parsing \"%s\"\n", __FILE__, __LINE__, line);
899 #endif
900
901 value = strchr(line, ':');
902 if (value == NULL)
903 return 0;
904 *value++ = '\0'; /* overwrite the : */
905 to_upper(line); /* header types are case-insensitive */
906 while ((c = *value) && (c == ' ' || c == '\t'))
907 value++;
908
909
910 if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type)
911 req->content_type = value;
912
913 else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length)
914 req->content_length = value;
915
916 else if (!memcmp(line, "CONNECTION", 11) &&
917 ka_max && req->keepalive != KA_STOPPED) {
918 req->keepalive = (!strncasecmp(value, "Keep-Alive", 10) ?
919 KA_ACTIVE : KA_STOPPED);
920 }
921 /* #ifdef ACCEPT_ON */
922 else if (!memcmp(line, "ACCEPT", 7))
923 add_accept_header(req, value);
924 /* #endif */
925
926 /* Need agent and referer for logs */
927 else if (!memcmp(line, "REFERER", 8)) {
928 req->header_referer = value;
929 if (!add_cgi_env(req, "REFERER", value, 1))
930 return 0;
931 } else if (!memcmp(line, "USER_AGENT", 11)) {
932 req->header_user_agent = value;
933 if (!add_cgi_env(req, "USER_AGENT", value, 1))
934 return 0;
935 } else if (!memcmp(line, "RANGE", 5)) {
936 init_range_stuff( req, value);
937 } else if (!memcmp(line, "HOST", 4)) {
938 init_vhost_stuff( req, value);
939 if (!add_cgi_env(req, "HOST", value, 1))
940 return 0;
941 } else if (!memcmp(line, "IF_", 3)) {
942 char *p = line+3;
943
944 if (!memcmp( p, "MODIFIED_SINCE", 15) && !req->if_modified_since) {
945 req->if_types |= IF_MODIFIED_SINCE;
946 req->if_modified_since = value;
947
948 } else if (!memcmp( p, "MATCH", 5) && !req->if_match_etag) {
949 req->if_types |= IF_MATCH;
950 req->if_match_etag = value;
951
952 } else if (!memcmp( p, "NONE_MATCH", 10) && !req->if_none_match_etag) {
953 req->if_types |= IF_NONE_MATCH;
954 req->if_none_match_etag = value;
955
956 } else if (!memcmp( p, "RANGE", 5) && !req->if_range_etag) {
957 req->if_types |= IF_RANGE;
958 req->if_range_etag = value;
959 }
960
961 if (!add_cgi_env(req, line, value, 1))
962 return 0;
963 } else {
964 if (!add_cgi_env(req, line, value, 1))
965 return 0;
966 }
967
968 return 1;
969 }
970
971 /*
972 * Name: add_accept_header
973 * Description: Adds a mime_type to a requests accept char buffer
974 * silently ignore any that don't fit -
975 * shouldn't happen because of relative buffer sizes
976 */
977
978 void add_accept_header(request * req, char *mime_type)
979 {
980 #ifdef ACCEPT_ON
981 int l = strlen(req->accept);
982 int l2 = strlen(mime_type);
983
984 if ((l + l2 + 2) >= MAX_HEADER_LENGTH)
985 return;
986
987 if (req->accept[0] == '\0')
988 strcpy(req->accept, mime_type);
989 else {
990 req->accept[l] = ',';
991 req->accept[l + 1] = ' ';
992 memcpy(req->accept + l + 2, mime_type, l2 + 1);
993 /* the +1 is for the '\0' */
994 /*
995 sprintf(req->accept + l, ", %s", mime_type);
996 */
997 }
998 #endif
999 }
1000
1001 void free_requests(server_params* params)
1002 {
1003 request *ptr, *next;
1004
1005 ptr = params->request_free;
1006 while (ptr != NULL) {
1007 next = ptr->next;
1008 free(ptr);
1009 ptr = next;
1010 }
1011 params->request_free = NULL;
1012 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26