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

Contents of /hydra/src/util.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.21 - (show annotations)
Sun Jan 26 11:25:39 2003 UTC (21 years, 2 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.20: +446 -445 lines
File MIME type: text/plain
Better large file support (now uses the included autoconf macros).
(++some indentation)

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 Charles F. Randall <crandall@goldsys.com>
6 * Some changes Copyright (C) 1996-99 Jon Nelson <jnelson@boa.org>
7 * Portions Copyright (C) 2002 Nikos Mavroyanopoulos <nmav@gnutls.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25 /* $Id: util.c,v 1.20 2003/01/22 07:51:50 nmav Exp $ */
26
27 #include "boa.h"
28
29 #define HEX_TO_DECIMAL(char1, char2) \
30 (((char1 >= 'A') ? (((char1 & 0xdf) - 'A') + 10) : (char1 - '0')) * 16) + \
31 (((char2 >= 'A') ? (((char2 & 0xdf) - 'A') + 10) : (char2 - '0')))
32
33 const char month_tab[48] =
34 "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ";
35 const char day_tab[] = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,";
36
37 /*
38 * Name: clean_pathname
39 *
40 * Description: Replaces unsafe/incorrect instances of:
41 * //[...] with /
42 * /./ with /
43 * /../ with / (technically not what we want, but browsers should deal
44 * with this, not servers)
45 */
46
47 void clean_pathname(char *pathname)
48 {
49 char *cleanpath, c;
50
51 cleanpath = pathname;
52 while ((c = *pathname++)) {
53 if (c == '/') {
54 while (1) {
55 if (*pathname == '/')
56 pathname++;
57 else if (*pathname == '.' && *(pathname + 1) == '/')
58 pathname += 2;
59 else if (*pathname == '.' && *(pathname + 1) == '.' &&
60 *(pathname + 2) == '/') {
61 pathname += 3;
62 } else
63 break;
64 }
65 c = '/';
66 }
67 *cleanpath++ = c;
68 }
69
70 *cleanpath = '\0';
71 }
72
73 /*
74 * Name: get_commonlog_time
75 *
76 * Description: Returns the current time in common log format in a static
77 * char buffer.
78 *
79 * commonlog time is exactly 25 characters long
80 * because this is only used in logging, we add " [" before and "] " after
81 * making 29 characters
82 * "[27/Feb/1998:20:20:04 +0000] "
83 *
84 * Constrast with rfc822 time:
85 * "Sun, 06 Nov 1994 08:49:37 GMT"
86 *
87 * Altered 10 Jan 2000 by Jon Nelson ala Drew Streib for non UTC logging
88 *
89 */
90
91 void get_commonlog_time(char buf[30])
92 {
93 struct tm *t;
94 char *p;
95 unsigned int a;
96 int time_offset;
97
98 if (use_localtime) {
99 t = localtime(&current_time);
100 time_offset = TIMEZONE_OFFSET(t);
101 } else {
102 t = gmtime(&current_time);
103 time_offset = 0;
104 }
105
106 p = buf + 29;
107 *p-- = '\0';
108 *p-- = ' ';
109 *p-- = ']';
110 a = abs(time_offset / 60);
111 *p-- = '0' + a % 10;
112 a /= 10;
113 *p-- = '0' + a % 6;
114 a /= 6;
115 *p-- = '0' + a % 10;
116 *p-- = '0' + a / 10;
117 *p-- = (time_offset >= 0) ? '+' : '-';
118 *p-- = ' ';
119
120 a = t->tm_sec;
121 *p-- = '0' + a % 10;
122 *p-- = '0' + a / 10;
123 *p-- = ':';
124 a = t->tm_min;
125 *p-- = '0' + a % 10;
126 *p-- = '0' + a / 10;
127 *p-- = ':';
128 a = t->tm_hour;
129 *p-- = '0' + a % 10;
130 *p-- = '0' + a / 10;
131 *p-- = ':';
132 a = 1900 + t->tm_year;
133 while (a) {
134 *p-- = '0' + a % 10;
135 a /= 10;
136 }
137 /* p points to an unused spot */
138 *p-- = '/';
139 p -= 2;
140 memcpy(p--, month_tab + 4 * (t->tm_mon), 3);
141 *p-- = '/';
142 a = t->tm_mday;
143 *p-- = '0' + a % 10;
144 *p-- = '0' + a / 10;
145 *p = '[';
146 return; /* should be same as returning buf */
147 }
148
149 /*
150 * Name: month2int
151 *
152 * Description: Turns a three letter month into a 0-11 int
153 *
154 * Note: This function is from wn-v1.07 -- it's clever and fast
155 */
156
157 int month2int(char *monthname)
158 {
159 switch (*monthname) {
160 case 'A':
161 return (*++monthname == 'p' ? 3 : 7);
162 case 'D':
163 return (11);
164 case 'F':
165 return (1);
166 case 'J':
167 if (*++monthname == 'a')
168 return (0);
169 return (*++monthname == 'n' ? 5 : 6);
170 case 'M':
171 return (*(monthname + 2) == 'r' ? 2 : 4);
172 case 'N':
173 return (10);
174 case 'O':
175 return (9);
176 case 'S':
177 return (8);
178 default:
179 return (-1);
180 }
181 }
182
183 /*
184 * Name: modified_since
185 * Description: Decides whether a file's mtime is newer than the
186 * If-Modified-Since header of a request.
187 *
188
189 Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
190 Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
191 Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
192 31 September 2000 23:59:59 GMT ; non-standard
193
194 * RETURN VALUES:
195 * 0: File has not been modified since specified time.
196 * 1: File has been.
197 * -1: Error!
198 */
199
200 int modified_since(time_t mtime, char *if_modified_since)
201 {
202 struct tm *file_gmt;
203 char *ims_info;
204 char monthname[10 + 1];
205 int day, month, year, hour, minute, second;
206 int comp;
207
208 ims_info = if_modified_since;
209 while (*ims_info != ' ' && *ims_info != '\0')
210 ++ims_info;
211 if (*ims_info != ' ')
212 return -1;
213
214 /* the pre-space in the third scanf skips whitespace for the string */
215 if (sscanf(ims_info, "%d %3s %d %d:%d:%d GMT", /* RFC 1123 */
216 &day, monthname, &year, &hour, &minute, &second) == 6);
217 else if (sscanf(ims_info, "%d-%3s-%d %d:%d:%d GMT", /* RFC 1036 */
218 &day, monthname, &year, &hour, &minute, &second) == 6)
219 year += 1900;
220 else if (sscanf(ims_info, " %3s %d %d:%d:%d %d", /* asctime() format */
221 monthname, &day, &hour, &minute, &second, &year) == 6);
222 /* allow this non-standard date format: 31 September 2000 23:59:59 GMT */
223 /* NOTE: Use if_modified_since here, because the date *starts*
224 * with the day, versus a throwaway item
225 */
226 else if (sscanf(if_modified_since, "%d %10s %d %d:%d:%d GMT",
227 &day, monthname, &year, &hour, &minute, &second) == 6);
228 else {
229 log_error_time();
230 fprintf(stderr, "Error in %s, line %d: Unable to sscanf \"%s\"\n",
231 __FILE__, __LINE__, ims_info);
232 return -1; /* error */
233 }
234
235 file_gmt = gmtime(&mtime);
236 month = month2int(monthname);
237
238 /* Go through from years to seconds -- if they are ever unequal,
239 we know which one is newer and can return */
240
241 if ((comp = 1900 + file_gmt->tm_year - year))
242 return (comp > 0);
243 if ((comp = file_gmt->tm_mon - month))
244 return (comp > 0);
245 if ((comp = file_gmt->tm_mday - day))
246 return (comp > 0);
247 if ((comp = file_gmt->tm_hour - hour))
248 return (comp > 0);
249 if ((comp = file_gmt->tm_min - minute))
250 return (comp > 0);
251 if ((comp = file_gmt->tm_sec - second))
252 return (comp > 0);
253
254 return 0; /* this person must really be into the latest/greatest */
255 }
256
257
258 /*
259 * Name: to_upper
260 *
261 * Description: Turns a string into all upper case (for HTTP_ header forming)
262 * AND changes - into _
263 */
264
265 char *to_upper(char *str)
266 {
267 char *start = str;
268
269 while (*str) {
270 if (*str == '-')
271 *str = '_';
272 else
273 *str = toupper(*str);
274
275 str++;
276 }
277
278 return start;
279 }
280
281 /*
282 * Name: unescape_uri
283 *
284 * Description: Decodes a uri, changing %xx encodings with the actual
285 * character. The query_string should already be gone.
286 *
287 * Return values:
288 * 1: success
289 * 0: illegal string
290 */
291
292 int unescape_uri(char *uri, char **query_string)
293 {
294 char c, d;
295 char *uri_old;
296
297 uri_old = uri;
298
299 while ((c = *uri_old)) {
300 if (c == '%') {
301 uri_old++;
302 if ((c = *uri_old++) && (d = *uri_old++))
303 *uri++ = HEX_TO_DECIMAL(c, d);
304 else
305 return 0; /* NULL in chars to be decoded */
306 } else if (c == '?') { /* query string */
307 if (query_string)
308 *query_string = ++uri_old;
309 /* stop here */
310 *uri = '\0';
311 return (1);
312 break;
313 } else if (c == '#') { /* fragment */
314 /* legal part of URL, but we do *not* care.
315 * However, we still have to look for the query string */
316 if (query_string) {
317 ++uri_old;
318 while ((c = *uri_old)) {
319 if (c == '?') {
320 *query_string = ++uri_old;
321 break;
322 }
323 ++uri_old;
324 }
325 }
326 break;
327 } else {
328 *uri++ = c;
329 uri_old++;
330 }
331 }
332
333 *uri = '\0';
334 return 1;
335 }
336
337 /* rfc822 (1123) time is exactly 29 characters long
338 * "Sun, 06 Nov 1994 08:49:37 GMT"
339 */
340
341 void rfc822_time_buf(char *buf, time_t s)
342 {
343 struct tm *t;
344 char *p;
345 unsigned int a;
346
347 if (!s) {
348 t = gmtime(&current_time);
349 } else
350 t = gmtime(&s);
351
352 p = buf + 28;
353 /* p points to the last char in the buf */
354
355 p -= 3;
356 /* p points to where the ' ' will go */
357 memcpy(p--, " GMT", 4);
358
359 a = t->tm_sec;
360 *p-- = '0' + a % 10;
361 *p-- = '0' + a / 10;
362 *p-- = ':';
363 a = t->tm_min;
364 *p-- = '0' + a % 10;
365 *p-- = '0' + a / 10;
366 *p-- = ':';
367 a = t->tm_hour;
368 *p-- = '0' + a % 10;
369 *p-- = '0' + a / 10;
370 *p-- = ' ';
371 a = 1900 + t->tm_year;
372 while (a) {
373 *p-- = '0' + a % 10;
374 a /= 10;
375 }
376 /* p points to an unused spot to where the space will go */
377 p -= 3;
378 /* p points to where the first char of the month will go */
379 memcpy(p--, month_tab + 4 * (t->tm_mon), 4);
380 *p-- = ' ';
381 a = t->tm_mday;
382 *p-- = '0' + a % 10;
383 *p-- = '0' + a / 10;
384 *p-- = ' ';
385 p -= 3;
386 memcpy(p, day_tab + t->tm_wday * 4, 4);
387 }
388
389 /* Converts an integer to a string and
390 * returns the number of digits. Does not accept negative
391 * values.
392 */
393 int simple_itoa(off_t i, char buf[22])
394 {
395 /* 21 digits plus null terminator, good for 64-bit or smaller ints
396 * for bigger ints, use a bigger buffer!
397 *
398 * 4294967295 is, incidentally, MAX_UINT (on 32bit systems at this time)
399 * and is 10 bytes long
400 */
401 char *p = &buf[21];
402 int digits = 1; /* include null char */
403
404 if (i < 0) {
405 buf[0] = 0;
406 return 0;
407 }
408
409 *p-- = '\0';
410 do {
411 digits++;
412 *p-- = '0' + i % 10;
413 i /= 10;
414 } while (i > 0);
415
416 p++;
417 if (p != buf)
418 memmove(buf, p, digits);
419
420 return digits - 1;
421 }
422
423 /* Generates an Etag, by using the file size, and the modification time
424 */
425 int create_etag(unsigned long int size, unsigned long int mod_time,
426 char etag[MAX_ETAG_LENGTH])
427 {
428 char buf[22];
429 int len, len2;
430
431 etag[0] = '\"';
432 len = 1;
433
434 len2 = simple_itoa(size % 100000, buf); /* up to 5 digits */
435 memcpy(&etag[len], buf, len2);
436 len += len2;
437
438 etag[len++] = '-';
439 len2 = simple_itoa(mod_time % 100000, buf); /* also 5 digits */
440 memcpy(&etag[len], buf, len2);
441 len += len2;
442
443 etag[len++] = '\"';
444 etag[len] = 0; /* etag is null terminated */
445
446 return len;
447 }
448
449
450 /* I don't "do" negative conversions
451 * Therefore, -1 indicates error
452 */
453
454 int boa_atoi(const char *s)
455 {
456 int retval;
457 char reconv[22];
458
459 if (!isdigit(*s))
460 return -1;
461
462 retval = atoi(s);
463 if (retval < 0)
464 return -1;
465
466 simple_itoa(retval, reconv);
467 if (memcmp(s, reconv, strlen(s)) != 0) {
468 return -1;
469 }
470 return retval;
471 }
472
473 off_t boa_atoll(const char *s)
474 {
475 long int retval;
476 char reconv[22];
477
478 if (!isdigit(*s))
479 return -1;
480
481 #ifdef HAVE_ATOLL
482 retval = atoll(s);
483 #else
484 retval = atol(s);
485 #endif
486 if (retval < 0)
487 return -1;
488
489 simple_itoa(retval, reconv);
490 if (memcmp(s, reconv, strlen(s)) != 0) {
491 return -1;
492 }
493 return retval;
494 }
495
496 #define TEMP_FILE_TEMPLATE "/hydra.temp.XXXXXX"
497 #define TEMP_FILE_TEMPLATE_LEN sizeof(TEMP_FILE_TEMPLATE)-1
498
499 void close_tmp_fd(tmp_fd * fds)
500 {
501 if (fds->pipe) {
502 if (fds->fds[1] != -1)
503 close(fds->fds[1]);
504 }
505
506 if (fds->fds[0] != -1)
507 close(fds->fds[0]);
508
509 fds->fds[0] = fds->fds[1] = -1;
510
511 }
512
513 const static tmp_fd EMPTY_FDS = { {-1, -1}, 0 };
514
515 /* returns -1 on error */
516 /* size holds the number of data that will be written to
517 * the temporary file.
518 */
519 tmp_fd create_temporary_file(short want_unlink, int size)
520 {
521 char boa_tempfile[MAX_PATH_LENGTH + 1];
522 tmp_fd fd;
523 int total_len;
524
525 fd.pipe = 0;
526
527 if (size > 0 && size < PIPE_BUF)
528 if (pipe(fd.fds) != -1) {
529 fd.pipe = 1;
530 return fd;
531 }
532
533 total_len = tempdir_len + TEMP_FILE_TEMPLATE_LEN;
534 if (total_len > MAX_PATH_LENGTH) {
535 log_error_time();
536 fprintf(stderr, "Temporary file length (%d) is too long\n",
537 total_len);
538 return EMPTY_FDS;
539 }
540
541 memcpy(boa_tempfile, tempdir, tempdir_len);
542 memcpy(&boa_tempfile[tempdir_len], TEMP_FILE_TEMPLATE,
543 TEMP_FILE_TEMPLATE_LEN);
544 boa_tempfile[total_len] = 0; /* null terminated */
545
546 fd.fds[0] = fd.fds[1] = -1;
547 /* open temp file
548 */
549 fd.fds[0] = mkstemp(boa_tempfile);
550 if (fd.fds[0] == -1) {
551 log_error_time();
552 perror("mkstemp");
553 return EMPTY_FDS;
554 }
555
556 if (want_unlink) {
557 if (unlink(boa_tempfile) == -1) {
558 close(fd.fds[0]);
559 fd.fds[0] = -1;
560 log_error_time();
561 fprintf(stderr, "unlink temp file\n");
562 }
563 }
564
565 fd.fds[1] = fd.fds[0];
566 return (fd);
567 }
568
569 int set_block_fd(int fd)
570 {
571 int flags;
572
573 flags = fcntl(fd, F_GETFL);
574 if (flags == -1)
575 return -1;
576
577 flags &= ~O_NONBLOCK;
578 flags = fcntl(fd, F_SETFL, flags);
579 return flags;
580 }
581
582 int set_nonblock_fd(int fd)
583 {
584 int flags;
585
586 flags = fcntl(fd, F_GETFL);
587 if (flags == -1)
588 return -1;
589
590 flags |= O_NONBLOCK;
591 flags = fcntl(fd, F_SETFL, flags);
592 return flags;
593 }
594
595 int set_cloexec_fd(int fd)
596 {
597 int flags;
598
599 flags = fcntl(fd, F_GETFL);
600 if (flags == -1)
601 return -1;
602
603 flags |= FD_CLOEXEC;
604 flags = fcntl(fd, F_SETFL, flags);
605 return flags;
606 }
607
608 void create_url(char *buffer, int buffer_size, int secure,
609 const char *hostname, int port, const char *request_uri)
610 {
611 char *proto;
612 char str_port[23];
613 int do_port = 0;
614
615 buffer[0] = 0; /* in case we fail */
616 if (hostname == NULL)
617 hostname = "";
618
619 if (request_uri == NULL)
620 request_uri = "";
621
622 if (secure) {
623 proto = "https";
624 if (port != 443)
625 do_port = 1;
626 } else {
627 proto = "http";
628 if (port != 80)
629 do_port = 1;
630 }
631
632 if (do_port) {
633 str_port[0] = ':';
634 simple_itoa(port, &str_port[1]);
635 }
636
637 if ((strlen(proto) + strlen(str_port) + strlen(hostname) +
638 strlen(request_uri) + 5) > buffer_size) {
639 /* This is more than impossible. The buffer is long enough */
640 log_error_time();
641 fprintf(stderr, "Could not create URL. Buffer was not enough.\n");
642 return;
643 }
644
645 sprintf(buffer, "%s://%s%s%s/", proto, hostname, str_port, request_uri);
646
647 return;
648 }
649
650 /* Breaks a list of "xxx", "yyy", to a character array, of
651 * MAX_COMMA_SEP_ELEMENTS size; Note that the given string is modified.
652 */
653 void break_comma_list(char *etag,
654 char *broken_etag[MAX_COMMA_SEP_ELEMENTS],
655 int *elements)
656 {
657 char *p = etag;
658
659 *elements = 0;
660
661 do {
662 broken_etag[*elements] = p;
663
664 (*elements)++;
665
666 p = strchr(p, ',');
667 if (p) {
668 *p = 0;
669 p++; /* move to next entry and skip white
670 * space.
671 */
672 while (*p == ' ')
673 p++;
674 }
675 } while (p != NULL && *elements < MAX_COMMA_SEP_ELEMENTS);
676 }
677
678 /* Quoting from rfc1034:
679
680 <domain> ::= <subdomain> | " "
681
682 <subdomain> ::= <label> | <subdomain> "." <label>
683
684 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
685
686 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
687
688 <let-dig-hyp> ::= <let-dig> | "-"
689
690 <let-dig> ::= <letter> | <digit>
691
692 <letter> ::= any one of the 52 alphabetic characters A through Z in
693 upper case and a through z in lower case
694
695 <digit> ::= any one of the ten digits 0 through 9
696
697 and
698
699 The labels must follow the rules for ARPANET host names. They must
700 start with a letter, end with a letter or digit, and have as interior
701 characters only letters, digits, and hyphen. There are also some
702 restrictions on the length. Labels must be 63 characters or less.
703
704 */
705
706 int check_host(char *r)
707 {
708 /* a hostname can only consist of
709 * chars and numbers, and sep. by only
710 * one period.
711 * It may not end with a period, and must
712 * not start with a number.
713 *
714 * >0: correct
715 * -1: error
716 * 0: not returned
717 *
718 */
719 char *c;
720 short period_ok = 0;
721 short len = 0;
722
723 c = r;
724 if (c == NULL) {
725 return -1;
726 }
727
728 /* must start with a letter */
729 if (!isalpha(*c))
730 return -1;
731
732 len = 1;
733 while (*(++c) != '\0') {
734 /* interior letters may be alphanumeric, '-', or '.' */
735 /* '.' may not follow '.' */
736 if (isalnum(*c) || *c == '-')
737 period_ok = 1;
738 else if (*c == '.' && period_ok)
739 period_ok = 0;
740 else
741 return -1;
742 ++len;
743 }
744 /* c points to '\0' */
745 --c;
746 /* must end with a letter or digit */
747 if (!isalnum(*c))
748 return -1;
749 return len;
750 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26