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

Annotation of /hydra/src/util.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.20 - (hide annotations)
Wed Jan 22 07:51:50 2003 UTC (21 years, 3 months ago) by nmav
Branch: MAIN
Changes since 1.19: +10 -2 lines
File MIME type: text/plain
merged changes from 0.1.x branch.

1 nmav 1.1 /*
2 nmav 1.7 * Hydra, an http server
3 nmav 1.1 * 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 nmav 1.9 * Portions Copyright (C) 2002 Nikos Mavroyanopoulos <nmav@gnutls.org>
8 nmav 1.1 *
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 nmav 1.20 /* $Id: util.c,v 1.19.2.1 2003/01/15 11:28:21 nmav Exp $ */
26 nmav 1.1
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 nmav 1.2 void get_commonlog_time(char buf[30])
92 nmav 1.1 {
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 nmav 1.2 return; /* should be same as returning buf */
147 nmav 1.1 }
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 nmav 1.10 int modified_since(time_t mtime, char *if_modified_since)
201 nmav 1.1 {
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 nmav 1.10 file_gmt = gmtime(&mtime);
236 nmav 1.1 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 nmav 1.20 /* Converts an integer to a string and
390     * returns the number of digits. Does not accept negative
391     * values.
392     */
393 nmav 1.17 int simple_itoa(large_int i, char buf[22])
394 nmav 1.1 {
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 nmav 1.2 char *p = &buf[21];
402 nmav 1.3 int digits = 1; /* include null char */
403 nmav 1.20
404     if ( i < 0) {
405     buf[0] = 0;
406     return 0;
407     }
408 nmav 1.3
409 nmav 1.1 *p-- = '\0';
410     do {
411 nmav 1.3 digits++;
412 nmav 1.1 *p-- = '0' + i % 10;
413     i /= 10;
414     } while (i > 0);
415 nmav 1.2
416 nmav 1.3 p++;
417     if (p!=buf) memmove( buf, p, digits);
418    
419 nmav 1.11 return digits - 1;
420 nmav 1.1 }
421 nmav 1.11
422     /* Generates an Etag, by using the file size, and the modification time
423     */
424     int create_etag(unsigned long int size, unsigned long int mod_time,
425     char etag[MAX_ETAG_LENGTH])
426     {
427     char buf[22];
428     int len, len2;
429    
430     etag[0] = '\"';
431     len = 1;
432    
433 nmav 1.12 len2 = simple_itoa( size % 100000, buf); /* up to 5 digits */
434 nmav 1.11 memcpy( &etag[len], buf, len2);
435     len += len2;
436    
437     etag[len++] = '-';
438 nmav 1.12 len2 = simple_itoa( mod_time % 100000, buf); /* also 5 digits */
439 nmav 1.11 memcpy( &etag[len], buf, len2);
440     len += len2;
441    
442     etag[len++] = '\"';
443     etag[len] = 0; /* etag is null terminated */
444    
445     return len;
446     }
447    
448 nmav 1.1
449     /* I don't "do" negative conversions
450     * Therefore, -1 indicates error
451     */
452    
453 nmav 1.3 int boa_atoi(const char *s)
454 nmav 1.1 {
455     int retval;
456 nmav 1.2 char reconv[22];
457 nmav 1.1
458     if (!isdigit(*s))
459     return -1;
460    
461 nmav 1.3 retval = atoi( s);
462 nmav 1.6 if (retval < 0)
463     return -1;
464    
465     simple_itoa(retval, reconv);
466     if (memcmp(s,reconv,strlen(s)) != 0) {
467     return -1;
468     }
469     return retval;
470     }
471    
472 nmav 1.17 large_int boa_atoll(const char *s)
473 nmav 1.6 {
474     long int retval;
475     char reconv[22];
476    
477     if (!isdigit(*s))
478     return -1;
479    
480 nmav 1.13 #ifdef HAVE_ATOLL
481     retval = atoll( s);
482     #else
483 nmav 1.6 retval = atol( s);
484 nmav 1.13 #endif
485 nmav 1.1 if (retval < 0)
486     return -1;
487    
488 nmav 1.2 simple_itoa(retval, reconv);
489 nmav 1.1 if (memcmp(s,reconv,strlen(s)) != 0) {
490     return -1;
491     }
492     return retval;
493     }
494    
495 nmav 1.8 #define TEMP_FILE_TEMPLATE "/hydra.temp.XXXXXX"
496     #define TEMP_FILE_TEMPLATE_LEN sizeof(TEMP_FILE_TEMPLATE)-1
497    
498 nmav 1.17 void close_tmp_fd( tmp_fd* fds)
499     {
500     if (fds->pipe) {
501 nmav 1.18 if (fds->fds[1]!=-1)
502     close( fds->fds[1]);
503     }
504    
505     if (fds->fds[0]!=-1)
506 nmav 1.17 close( fds->fds[0]);
507    
508     fds->fds[0] = fds->fds[1] = -1;
509    
510     }
511    
512     const static tmp_fd EMPTY_FDS = { {-1, -1}, 0};
513    
514 nmav 1.5 /* returns -1 on error */
515 nmav 1.17 /* size holds the number of data that will be written to
516     * the temporary file.
517     */
518     tmp_fd create_temporary_file(short want_unlink, int size)
519 nmav 1.1 {
520 nmav 1.2 char boa_tempfile[MAX_PATH_LENGTH + 1];
521 nmav 1.17 tmp_fd fd;
522     int total_len;
523    
524     fd.pipe = 0;
525    
526     if (size > 0 && size < PIPE_BUF)
527     if (pipe( fd.fds) != -1) {
528     fd.pipe = 1;
529     return fd;
530     }
531 nmav 1.1
532 nmav 1.8 total_len = tempdir_len + TEMP_FILE_TEMPLATE_LEN;
533     if (total_len > MAX_PATH_LENGTH) {
534     log_error_time();
535     fprintf(stderr, "Temporary file length (%d) is too long\n", total_len);
536 nmav 1.17 return EMPTY_FDS;
537 nmav 1.8 }
538 nmav 1.1
539 nmav 1.8 memcpy( boa_tempfile, tempdir, tempdir_len);
540     memcpy( &boa_tempfile[tempdir_len], TEMP_FILE_TEMPLATE, TEMP_FILE_TEMPLATE_LEN);
541     boa_tempfile[total_len] = 0; /* null terminated */
542    
543 nmav 1.17 fd.fds[0] = fd.fds[1] = -1;
544 nmav 1.8 /* open temp file
545     */
546 nmav 1.17 fd.fds[0] = mkstemp(boa_tempfile);
547     if (fd.fds[0] == -1) {
548 nmav 1.1 log_error_time();
549     perror("mkstemp");
550 nmav 1.17 return EMPTY_FDS;
551 nmav 1.1 }
552    
553     if (want_unlink) {
554     if (unlink(boa_tempfile) == -1) {
555 nmav 1.17 close(fd.fds[0]);
556     fd.fds[0] = -1;
557 nmav 1.1 log_error_time();
558     fprintf(stderr, "unlink temp file\n");
559     }
560     }
561    
562 nmav 1.17 fd.fds[1] = fd.fds[0];
563 nmav 1.1 return (fd);
564     }
565    
566 nmav 1.15 int set_block_fd(int fd)
567 nmav 1.1 {
568 nmav 1.15 int flags;
569 nmav 1.1
570 nmav 1.15 flags = fcntl(fd, F_GETFL);
571     if (flags == -1)
572     return -1;
573 nmav 1.1
574 nmav 1.15 flags &= ~O_NONBLOCK;
575     flags = fcntl(fd, F_SETFL, flags);
576     return flags;
577 nmav 1.1 }
578    
579 nmav 1.15 int set_nonblock_fd(int fd)
580 nmav 1.1 {
581     int flags;
582    
583     flags = fcntl(fd, F_GETFL);
584     if (flags == -1)
585     return -1;
586    
587 nmav 1.15 flags |= O_NONBLOCK;
588 nmav 1.1 flags = fcntl(fd, F_SETFL, flags);
589     return flags;
590     }
591    
592 nmav 1.15 int set_cloexec_fd(int fd)
593 nmav 1.1 {
594     int flags;
595    
596     flags = fcntl(fd, F_GETFL);
597     if (flags == -1)
598     return -1;
599    
600 nmav 1.15 flags |= FD_CLOEXEC;
601 nmav 1.1 flags = fcntl(fd, F_SETFL, flags);
602     return flags;
603 nmav 1.9 }
604    
605     void create_url( char * buffer, int buffer_size, int secure,
606     const char* hostname, int port, const char* request_uri)
607     {
608     char * proto;
609     char str_port[23];
610     int do_port = 0;
611    
612     buffer[0] = 0; /* in case we fail */
613 nmav 1.19 if (hostname == NULL)
614     hostname = "";
615 nmav 1.16
616 nmav 1.19 if (request_uri == NULL)
617     request_uri = "";
618 nmav 1.9
619     if (secure) {
620     proto = "https";
621     if (port != 443)
622     do_port = 1;
623     } else {
624     proto = "http";
625 nmav 1.19 if (port != 80)
626 nmav 1.9 do_port = 1;
627     }
628    
629     if (do_port) {
630     str_port[0] = ':';
631     simple_itoa( port, &str_port[1]);
632     }
633    
634     if ((strlen(proto) + strlen(str_port) + strlen(hostname) +
635     strlen(request_uri) + 5) > buffer_size) {
636     /* This is more than impossible. The buffer is long enough */
637     log_error_time();
638     fprintf(stderr, "Could not create URL. Buffer was not enough.\n");
639     return;
640     }
641    
642     sprintf( buffer, "%s://%s%s%s/", proto, hostname, str_port, request_uri);
643    
644     return;
645 nmav 1.14 }
646    
647     /* Breaks a list of "xxx", "yyy", to a character array, of
648     * MAX_COMMA_SEP_ELEMENTS size; Note that the given string is modified.
649     */
650     void break_comma_list(char *etag,
651     char *broken_etag[MAX_COMMA_SEP_ELEMENTS],
652     int *elements)
653     {
654     char *p = etag;
655    
656     *elements = 0;
657    
658     do {
659     broken_etag[*elements] = p;
660    
661     (*elements)++;
662    
663     p = strchr(p, ',');
664     if (p) {
665     *p = 0;
666     p++; /* move to next entry and skip white
667     * space.
668     */
669     while (*p == ' ')
670     p++;
671     }
672     } while (p != NULL && *elements < MAX_COMMA_SEP_ELEMENTS);
673 nmav 1.1 }
674 nmav 1.15
675     /* Quoting from rfc1034:
676    
677     <domain> ::= <subdomain> | " "
678    
679     <subdomain> ::= <label> | <subdomain> "." <label>
680    
681     <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
682    
683     <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
684    
685     <let-dig-hyp> ::= <let-dig> | "-"
686    
687     <let-dig> ::= <letter> | <digit>
688    
689     <letter> ::= any one of the 52 alphabetic characters A through Z in
690     upper case and a through z in lower case
691    
692     <digit> ::= any one of the ten digits 0 through 9
693    
694     and
695    
696     The labels must follow the rules for ARPANET host names. They must
697     start with a letter, end with a letter or digit, and have as interior
698     characters only letters, digits, and hyphen. There are also some
699     restrictions on the length. Labels must be 63 characters or less.
700    
701     */
702    
703     int check_host(char *r)
704     {
705     /* a hostname can only consist of
706     * chars and numbers, and sep. by only
707     * one period.
708     * It may not end with a period, and must
709     * not start with a number.
710     *
711     * >0: correct
712     * -1: error
713     * 0: not returned
714     *
715     */
716     char *c;
717     short period_ok = 0;
718     short len = 0;
719    
720     c = r;
721     if (c == NULL) {
722     return -1;
723     }
724    
725     /* must start with a letter */
726     if (!isalpha(*c))
727     return -1;
728    
729     len = 1;
730     while (*(++c) != '\0') {
731     /* interior letters may be alphanumeric, '-', or '.' */
732     /* '.' may not follow '.' */
733     if (isalnum(*c) || *c == '-')
734     period_ok = 1;
735     else if (*c == '.' && period_ok)
736     period_ok = 0;
737     else
738     return -1;
739     ++len;
740     }
741     /* c points to '\0' */
742     --c;
743     /* must end with a letter or digit */
744     if (!isalnum(*c))
745     return -1;
746     return len;
747     }
748    
749    

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26