2 * Copyright (c) 2007-2008, Cameron Rich
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of the axTLS project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/types.h>
41 #define HTTP_VERSION "HTTP/1.1"
43 static const char * index_file
= "index.html";
45 static int special_read(struct connstruct
*cn
, void *buf
, size_t count
);
46 static int special_write(struct connstruct
*cn
,
47 const char *buf
, size_t count
);
48 static void send_error(struct connstruct
*cn
, int err
);
49 static int hexit(char c
);
50 static void urldecode(char *buf
);
51 static void buildactualfile(struct connstruct
*cn
);
52 static int sanitizefile(const char *buf
);
53 static int sanitizehost(char *buf
);
54 static int htaccess_check(struct connstruct
*cn
);
55 static const char *getmimetype(const char *name
);
57 #if defined(CONFIG_HTTP_DIRECTORIES)
58 static void urlencode(const uint8_t *s
, char *t
);
59 static void procdirlisting(struct connstruct
*cn
);
61 #if defined(CONFIG_HTTP_HAS_CGI)
62 static void proccgi(struct connstruct
*cn
);
63 static void decode_path_info(struct connstruct
*cn
, char *path_info
);
64 static int init_read_post_data(char *buf
, char *data
, struct connstruct
*cn
, int old_rv
);
66 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
67 static int auth_check(struct connstruct
*cn
);
71 #define AXDEBUGSTART \
74 axdout = fopen("/var/log/axdebug", "a"); \
84 /* Returns 1 if elems should continue being read, 0 otherwise */
85 static int procheadelem(struct connstruct
*cn
, char *buf
)
89 if ((delim
= strchr(buf
, ' ')) == NULL
)
95 if (strcmp(buf
, "GET") == 0 || strcmp(buf
, "HEAD") == 0 ||
96 strcmp(buf
, "POST") == 0)
99 cn
->reqtype
= TYPE_HEAD
;
100 else if (buf
[0] == 'P')
101 cn
->reqtype
= TYPE_POST
;
103 if ((delim
= strchr(value
, ' ')) == NULL
) /* expect HTTP type */
109 if (sanitizefile(value
) == 0)
115 #if defined(CONFIG_HTTP_HAS_CGI)
116 decode_path_info(cn
, value
);
118 my_strncpy(cn
->filereq
, value
, MAXREQUESTLENGTH
);
120 cn
->if_modified_since
= -1;
122 else if (strcmp(buf
, "Host:") == 0)
124 if (sanitizehost(value
) == 0)
126 removeconnection(cn
);
130 my_strncpy(cn
->server_name
, value
, MAXREQUESTLENGTH
);
132 else if (strcmp(buf
, "Connection:") == 0 && strcmp(value
, "close") == 0)
134 cn
->close_when_done
= 1;
136 else if (strcmp(buf
, "If-Modified-Since:") == 0)
138 cn
->if_modified_since
= tdate_parse(value
);
140 else if (strcmp(buf
, "Expect:") == 0)
142 send_error(cn
, 417); /* expectation failed */
145 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
146 else if (strcmp(buf
, "Authorization:") == 0 &&
147 strncmp(value
, "Basic ", 6) == 0)
150 if (base64_decode(&value
[6], strlen(&value
[6]),
151 (uint8_t *)cn
->authorization
, &size
))
152 cn
->authorization
[0] = 0; /* error */
154 cn
->authorization
[size
] = 0;
157 #if defined(CONFIG_HTTP_HAS_CGI)
158 else if (strcmp(buf
, "Content-Length:") == 0)
160 sscanf(value
, "%d", &cn
->content_length
);
162 else if (strcmp(buf
, "Cookie:") == 0)
164 my_strncpy(cn
->cookie
, value
, MAXREQUESTLENGTH
);
171 #if defined(CONFIG_HTTP_DIRECTORIES)
172 static void procdirlisting(struct connstruct
*cn
)
174 char buf
[MAXREQUESTLENGTH
];
175 char actualfile
[1024];
177 if (cn
->reqtype
== TYPE_HEAD
)
179 snprintf(buf
, sizeof(buf
), HTTP_VERSION
180 " 200 OK\nContent-Type: text/html\n\n");
181 write(cn
->networkdesc
, buf
, strlen(buf
));
182 removeconnection(cn
);
186 strcpy(actualfile
, cn
->actualfile
);
189 strcat(actualfile
, "*");
190 cn
->dirp
= FindFirstFile(actualfile
, &cn
->file_data
);
192 if (cn
->dirp
== INVALID_HANDLE_VALUE
)
198 if ((cn
->dirp
= opendir(actualfile
)) == NULL
)
205 snprintf(buf
, sizeof(buf
), HTTP_VERSION
206 " 200 OK\nContent-Type: text/html\n\n"
207 "<html><body>\n<title>Directory Listing</title>\n"
208 "<h3>Directory listing of %s://%s%s</h3><br />\n",
209 cn
->is_ssl
? "https" : "http", cn
->server_name
, cn
->filereq
);
210 special_write(cn
, buf
, strlen(buf
));
211 cn
->state
= STATE_DOING_DIR
;
214 void procdodir(struct connstruct
*cn
)
219 char buf
[MAXREQUESTLENGTH
];
228 if (!FindNextFile(cn
->dirp
, &cn
->file_data
))
230 if ((dp
= readdir(cn
->dirp
)) == NULL
)
233 snprintf(buf
, sizeof(buf
), "</body></html>\n");
234 special_write(cn
, buf
, strlen(buf
));
235 removeconnection(cn
);
243 file
= cn
->file_data
.cFileName
;
248 /* if no index file, don't display the ".." directory */
249 if (cn
->filereq
[0] == '/' && cn
->filereq
[1] == '\0' &&
250 strcmp(file
, "..") == 0)
253 /* don't display files beginning with "." */
254 if (file
[0] == '.' && file
[1] != '.')
257 /* make sure a '/' is at the end of a directory */
258 if (cn
->filereq
[strlen(cn
->filereq
)-1] != '/')
259 strcat(cn
->filereq
, "/");
261 /* see if the dir + file is another directory */
262 snprintf(buf
, sizeof(buf
), "%s%s", cn
->actualfile
, file
);
266 urlencode((uint8_t *)file
, encbuf
);
267 snprintf(buf
, sizeof(buf
), "<a href=\"%s%s\">%s</a><br />\n",
268 cn
->filereq
, encbuf
, file
);
269 } while (special_write(cn
, buf
, strlen(buf
)));
272 /* Encode funny chars -> %xx in newly allocated storage */
273 /* (preserves '/' !) */
274 static void urlencode(const uint8_t *s
, char *t
)
276 const uint8_t *p
= s
;
281 if ((*p
> 0x00 && *p
< ',') ||
282 (*p
> '9' && *p
< 'A') ||
283 (*p
> 'Z' && *p
< '_') ||
284 (*p
> '_' && *p
< 'a') ||
285 (*p
> 'z' && *p
< 0xA1))
287 sprintf((char *)tp
, "%%%02X", *p
);
302 void procreadhead(struct connstruct
*cn
)
304 char buf
[MAXREQUESTLENGTH
*4], *tp
, *next
;
307 memset(buf
, 0, MAXREQUESTLENGTH
*4);
308 rv
= special_read(cn
, buf
, sizeof(buf
)-1);
311 if (rv
< 0) /* really dead? */
312 removeconnection(cn
);
319 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
320 cn
->authorization
[0] = 0;
323 /* Split up lines and send to procheadelem() */
324 while (*next
!= '\0')
326 /* If we have a blank line, advance to next stage */
327 if (*next
== '\r' || *next
== '\n')
329 #if defined(CONFIG_HTTP_HAS_CGI)
330 if (cn
->reqtype
== TYPE_POST
&& cn
->content_length
> 0)
332 if (init_read_post_data(buf
,next
,cn
,rv
) == 0)
338 cn
->state
= STATE_WANT_TO_SEND_HEAD
;
342 while (*next
!= '\r' && *next
!= '\n' && *next
!= '\0')
350 else if (*next
== '\n')
353 if (procheadelem(cn
, tp
) == 0)
360 /* In this function we assume that the file has been checked for
361 * maliciousness (".."s, etc) and has been decoded
363 void procsendhead(struct connstruct
*cn
)
365 char buf
[MAXREQUESTLENGTH
];
367 time_t now
= cn
->timeout
- CONFIG_HTTP_TIMEOUT
;
371 /* are we trying to access a file over the HTTP connection instead of a
372 * HTTPS connection? Or is this directory disabled? */
373 if (htaccess_check(cn
))
379 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
380 if (auth_check(cn
)) /* see if there is a '.htpasswd' file */
382 #ifdef CONFIG_HTTP_VERBOSE
383 printf("axhttpd: access to %s denied\n", cn
->filereq
); TTY_FLUSH();
385 removeconnection(cn
);
390 file_exists
= stat(cn
->actualfile
, &stbuf
);
392 #if defined(CONFIG_HTTP_HAS_CGI)
394 if (file_exists
!= -1 && cn
->is_cgi
)
396 if ((stbuf
.st_mode
& S_IEXEC
) == 0 || isdir(cn
->actualfile
))
398 /* A non-executable file, or directory? */
408 /* look for "index.html"? */
409 if (isdir(cn
->actualfile
))
411 char tbuf
[MAXREQUESTLENGTH
];
412 snprintf(tbuf
, MAXREQUESTLENGTH
, "%s%s", cn
->actualfile
, index_file
);
414 if ((file_exists
= stat(tbuf
, &stbuf
)) != -1)
415 my_strncpy(cn
->actualfile
, tbuf
, MAXREQUESTLENGTH
);
418 #if defined(CONFIG_HTTP_DIRECTORIES)
419 /* If not, we do a directory listing of it */
428 if (file_exists
== -1)
434 strcpy(date
, ctime(&now
));
436 /* has the file been read before? */
437 if (cn
->if_modified_since
!= -1 && (cn
->if_modified_since
== 0 ||
438 cn
->if_modified_since
>= stbuf
.st_mtime
))
440 snprintf(buf
, sizeof(buf
), HTTP_VERSION
" 304 Not Modified\nServer: "
441 "%s\nDate: %s\n", server_version
, date
);
442 special_write(cn
, buf
, strlen(buf
));
443 cn
->state
= STATE_WANT_TO_READ_HEAD
;
447 if (cn
->reqtype
== TYPE_HEAD
)
449 removeconnection(cn
);
454 int flags
= O_RDONLY
;
455 #if defined(WIN32) || defined(CONFIG_PLATFORM_CYGWIN)
458 cn
->filedesc
= open(cn
->actualfile
, flags
);
460 if (cn
->filedesc
< 0)
466 snprintf(buf
, sizeof(buf
), HTTP_VERSION
" 200 OK\nServer: %s\n"
467 "Content-Type: %s\nContent-Length: %ld\n"
468 "Date: %sLast-Modified: %s\n", server_version
,
469 getmimetype(cn
->actualfile
), (long) stbuf
.st_size
,
470 date
, ctime(&stbuf
.st_mtime
)); /* ctime() has a \n on the end */
472 special_write(cn
, buf
, strlen(buf
));
474 #ifdef CONFIG_HTTP_VERBOSE
475 printf("axhttpd: %s:/%s\n", cn
->is_ssl
? "https" : "http", cn
->filereq
);
483 if (cn
->filedesc
== -1)
489 } while (cn
->state
!= STATE_WANT_TO_READ_FILE
);
492 cn
->state
= STATE_WANT_TO_READ_FILE
;
497 void procreadfile(struct connstruct
*cn
)
499 int rv
= read(cn
->filedesc
, cn
->databuf
, BLOCKSIZE
);
506 if (cn
->close_when_done
) /* close immediately */
507 removeconnection(cn
);
509 { /* keep socket open - HTTP 1.1 */
510 cn
->state
= STATE_WANT_TO_READ_HEAD
;
518 cn
->state
= STATE_WANT_TO_SEND_FILE
;
521 void procsendfile(struct connstruct
*cn
)
523 int rv
= special_write(cn
, cn
->databuf
, cn
->numbytes
);
526 removeconnection(cn
);
527 else if (rv
== cn
->numbytes
)
529 cn
->state
= STATE_WANT_TO_READ_FILE
;
537 memmove(cn
->databuf
, cn
->databuf
+ rv
, cn
->numbytes
- rv
);
542 #if defined(CONFIG_HTTP_HAS_CGI)
543 /* Should this be a bit more dynamic? It would mean more calls to malloc etc */
544 #define CGI_ARG_SIZE 17
546 static void proccgi(struct connstruct
*cn
)
548 int tpipe
[2], spipe
[2];
550 char cgienv
[CGI_ARG_SIZE
][MAXREQUESTLENGTH
];
551 char * cgiptr
[CGI_ARG_SIZE
+4];
552 const char *type
= "HEAD";
553 int cgi_index
= 0, i
;
559 snprintf(cgienv
[0], MAXREQUESTLENGTH
,
560 HTTP_VERSION
" 200 OK\nServer: %s\n%s",
561 server_version
, (cn
->reqtype
== TYPE_HEAD
) ? "\n" : "");
562 special_write(cn
, cgienv
[0], strlen(cgienv
[0]));
564 if (cn
->reqtype
== TYPE_HEAD
)
566 removeconnection(cn
);
570 #ifdef CONFIG_HTTP_VERBOSE
571 printf("[CGI]: %s:/%s\n", cn
->is_ssl
? "https" : "http", cn
->filereq
);
575 /* win32 cgi is a bit too painful */
577 /* set up pipe that is used for sending POST query data to CGI script*/
578 if (cn
->reqtype
== TYPE_POST
)
580 if (pipe(spipe
) == -1)
582 printf("[CGI]: could not create pipe");
588 if (pipe(tpipe
) == -1)
590 printf("[CGI]: could not create pipe");
596 * use vfork() instead of fork() for performance
598 if ((pid
= vfork()) > 0) /* parent */
600 /* Send POST query data to CGI script */
601 if ((cn
->reqtype
== TYPE_POST
) && (cn
->content_length
> 0))
603 write(spipe
[1], cn
->post_data
, cn
->content_length
);
607 /* free the memory that is allocated in read_post_data() */
609 cn
->post_data
= NULL
;
612 /* Close the write descriptor */
614 cn
->filedesc
= tpipe
[0];
615 cn
->state
= STATE_WANT_TO_READ_FILE
;
616 cn
->close_when_done
= 1;
620 if (pid
< 0) /* vfork failed */
623 /* The problem child... */
625 /* Our stdout/stderr goes to the socket */
629 /* If it was a POST request, send the socket data to our stdin */
630 if (cn
->reqtype
== TYPE_POST
)
632 else /* Otherwise we can shutdown the read side of the sock */
633 shutdown(cn
->networkdesc
, 0);
635 myargs
[0] = cn
->actualfile
;
639 * set the cgi args. A url is defined by:
640 * http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO?$QUERY_STRING
641 * TODO: other CGI parameters?
643 sprintf(cgienv
[cgi_index
++], "SERVER_SOFTWARE=%s", server_version
);
644 strcpy(cgienv
[cgi_index
++], "DOCUMENT_ROOT=" CONFIG_HTTP_WEBROOT
);
645 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
646 "SERVER_NAME=%s", cn
->server_name
);
647 sprintf(cgienv
[cgi_index
++], "SERVER_PORT=%d",
648 cn
->is_ssl
? CONFIG_HTTP_HTTPS_PORT
: CONFIG_HTTP_PORT
);
649 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
650 "REQUEST_URI=%s", cn
->uri_request
);
651 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
652 "SCRIPT_NAME=%s", cn
->filereq
);
653 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
654 "PATH_INFO=%s", cn
->uri_path_info
);
655 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
656 "QUERY_STRING=%s", cn
->uri_query
);
657 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
658 "REMOTE_ADDR=%s", cn
->remote_addr
);
659 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
660 "HTTP_COOKIE=%s", cn
->cookie
); /* note: small size */
661 #if defined(CONFIG_HTTP_HAS_AUTHORIZATION)
662 snprintf(cgienv
[cgi_index
++], MAXREQUESTLENGTH
,
663 "REMOTE_USER=%s", cn
->authorization
);
674 sprintf(cgienv
[cgi_index
++],
675 "CONTENT_LENGTH=%d", cn
->content_length
);
676 strcpy(cgienv
[cgi_index
++], /* hard-code? */
677 "CONTENT_TYPE=application/x-www-form-urlencoded");
681 sprintf(cgienv
[cgi_index
++], "REQUEST_METHOD=%s", type
);
684 strcpy(cgienv
[cgi_index
++], "HTTPS=on");
686 #ifdef CONFIG_PLATFORM_CYGWIN
687 /* TODO: find out why Lua needs this */
688 strcpy(cgienv
[cgi_index
++], "PATH=/usr/bin");
691 if (cgi_index
>= CGI_ARG_SIZE
)
693 printf("Content-type: text/plain\n\nToo many CGI args (%d, %d)\n",
694 cgi_index
, CGI_ARG_SIZE
);
698 /* copy across the pointer indexes */
699 for (i
= 0; i
< cgi_index
; i
++)
700 cgiptr
[i
] = cgienv
[i
];
702 cgiptr
[i
++] = "AUTH_TYPE=Basic";
703 cgiptr
[i
++] = "GATEWAY_INTERFACE=CGI/1.1";
704 cgiptr
[i
++] = "SERVER_PROTOCOL="HTTP_VERSION
;
707 execve(myargs
[0], myargs
, cgiptr
);
708 printf("Content-type: text/plain\n\nshouldn't get here\n");
713 static char * cgi_filetype_match(struct connstruct
*cn
, const char *fn
)
715 struct cgiextstruct
*tp
= cgiexts
;
721 if ((t
= strstr(fn
, tp
->ext
)) != NULL
)
723 t
+= strlen(tp
->ext
);
725 if (*t
== '/' || *t
== '\0')
727 #ifdef CONFIG_HTTP_ENABLE_LUA
728 if (strcmp(tp
->ext
, ".lua") == 0 || strcmp(tp
->ext
, ".lp") == 0)
745 static void decode_path_info(struct connstruct
*cn
, char *path_info
)
750 #ifdef CONFIG_HTTP_ENABLE_LUA
753 *cn
->uri_request
= '\0';
754 *cn
->uri_path_info
= '\0';
755 *cn
->uri_query
= '\0';
757 my_strncpy(cn
->uri_request
, path_info
, MAXREQUESTLENGTH
);
760 if ((cgi_delim
= strchr(path_info
, '?')))
763 my_strncpy(cn
->uri_query
, cgi_delim
+1, MAXREQUESTLENGTH
);
766 if ((cgi_delim
= cgi_filetype_match(cn
, path_info
)) != NULL
)
768 cn
->is_cgi
= 1; /* definitely a CGI script */
771 if (*cgi_delim
!= '\0')
773 my_strncpy(cn
->uri_path_info
, cgi_delim
, MAXREQUESTLENGTH
);
778 /* the bit at the start must be the script name */
779 my_strncpy(cn
->filereq
, path_info
, MAXREQUESTLENGTH
);
782 static int init_read_post_data(char *buf
, char *data
,
783 struct connstruct
*cn
, int old_rv
)
789 /* Too much Post data to send. MAXPOSTDATASIZE should be
790 configured (now it can be chaged in the header file) */
791 if (cn
->content_length
> MAXPOSTDATASIZE
)
798 while ((*next
== '\r' || *next
== '\n') && (next
< &buf
[rv
]))
801 if (cn
->post_data
== NULL
)
803 cn
->post_data
= (char *) calloc(1, (cn
->content_length
+ 1));
804 /* Allocate buffer for the POST data that will be used by proccgi
805 to send POST data to the CGI script */
807 if (cn
->post_data
== NULL
)
809 printf("axhttpd: could not allocate memory for POST data\n");
818 post_data
= cn
->post_data
;
820 while (next
< &buf
[rv
])
822 /*copy POST data to buffer*/
827 if (cn
->post_read
== cn
->content_length
)
829 /* No more POST data to be copied */
835 /* More POST data has to be read. read_post_data will continue with that */
840 void read_post_data(struct connstruct
*cn
)
842 char buf
[MAXREQUESTLENGTH
*4], *next
;
846 bzero(buf
,MAXREQUESTLENGTH
*4);
847 rv
= special_read(cn
, buf
, sizeof(buf
)-1);
850 if (rv
< 0) /* really dead? */
851 removeconnection(cn
);
858 post_data
= &cn
->post_data
[cn
->post_read
];
860 while (next
< &buf
[rv
])
866 if (cn
->post_read
== cn
->content_length
)
868 /* No more POST data to be copied */
872 cn
->state
= STATE_WANT_TO_SEND_HEAD
;
877 /* More POST data to read */
880 #endif /* CONFIG_HTTP_HAS_CGI */
882 /* Decode string %xx -> char (in place) */
883 static void urldecode(char *buf
)
899 if (isxdigit((int) s
[0]) && isxdigit((int) s
[1]))
901 v
= hexit(s
[0])*16 + hexit(s
[1]);
905 /* do not decode %00 to null char */
921 static int hexit(char c
)
923 if (c
>= '0' && c
<= '9')
925 else if (c
>= 'a' && c
<= 'f')
927 else if (c
>= 'A' && c
<= 'F')
933 static void buildactualfile(struct connstruct
*cn
)
936 snprintf(cn
->actualfile
, MAXREQUESTLENGTH
, ".%s", cn
->filereq
);
939 /* Add directory slash if not there */
940 if (isdir(cn
->actualfile
) &&
941 cn
->actualfile
[strlen(cn
->actualfile
)-1] != '/')
942 strcat(cn
->actualfile
, "/");
944 /* work out the directory name */
945 strncpy(cn
->dirname
, cn
->actualfile
, MAXREQUESTLENGTH
);
946 if ((cp
= strrchr(cn
->dirname
, '/')) == NULL
)
952 char curr_dir
[MAXREQUESTLENGTH
];
953 char path
[MAXREQUESTLENGTH
];
954 char *t
= cn
->actualfile
;
956 GetCurrentDirectory(MAXREQUESTLENGTH
, curr_dir
);
958 /* convert all the forward slashes to back slashes */
959 while ((t
= strchr(t
, '/')))
962 snprintf(path
, MAXREQUESTLENGTH
, "%s%s", curr_dir
, cn
->actualfile
);
963 memcpy(cn
->actualfile
, path
, MAXREQUESTLENGTH
);
965 /* Add directory slash if not there */
966 if (isdir(cn
->actualfile
) &&
967 cn
->actualfile
[strlen(cn
->actualfile
)-1] != '\\')
968 strcat(cn
->actualfile
, "\\");
970 /* work out the directory name */
971 strncpy(cn
->dirname
, cn
->actualfile
, MAXREQUESTLENGTH
);
972 if ((cp
= strrchr(cn
->dirname
, '\\')) == NULL
)
979 #if defined(CONFIG_HTTP_ENABLE_LUA)
981 * Use the lua launcher if this file has a lua extension. Put this at the
982 * end as we need the directory name.
985 sprintf(cn
->actualfile
, "%s%s", CONFIG_HTTP_LUA_PREFIX
,
986 CONFIG_HTTP_LUA_CGI_LAUNCHER
);
990 static int sanitizefile(const char *buf
)
994 /* Don't accept anything not starting with a / */
999 for (i
= 0; i
< len
; i
++)
1001 /* Check for "/." i.e. don't send files starting with a . */
1002 if (buf
[i
] == '/' && buf
[i
+1] == '.')
1009 static int sanitizehost(char *buf
)
1011 while (*buf
!= '\0')
1013 /* Handle the port */
1020 /* Enforce some basic URL rules... */
1021 if ((isalnum(*buf
) == 0 && *buf
!= '-' && *buf
!= '.') ||
1022 (*buf
== '.' && *(buf
+1) == '.') ||
1023 (*buf
== '.' && *(buf
+1) == '-') ||
1024 (*buf
== '-' && *(buf
+1) == '.'))
1033 static FILE * exist_check(struct connstruct
*cn
, const char *check_file
)
1035 char pathname
[MAXREQUESTLENGTH
];
1036 snprintf(pathname
, MAXREQUESTLENGTH
, "%s/%s", cn
->dirname
, check_file
);
1037 return fopen(pathname
, "r");
1040 #ifdef CONFIG_HTTP_HAS_AUTHORIZATION
1041 static void send_authenticate(struct connstruct
*cn
, const char *realm
)
1045 snprintf(buf
, sizeof(buf
), HTTP_VERSION
" 401 Unauthorized\n"
1046 "WWW-Authenticate: Basic\n"
1047 "realm=\"%s\"\n", realm
);
1048 special_write(cn
, buf
, strlen(buf
));
1051 static int check_digest(char *salt
, const char *msg_passwd
)
1053 uint8_t b256_salt
[MAXREQUESTLENGTH
];
1054 uint8_t real_passwd
[MD5_SIZE
];
1057 uint8_t md5_result
[MD5_SIZE
];
1060 /* retrieve the salt */
1061 if ((b64_passwd
= strchr(salt
, '$')) == NULL
)
1065 if (base64_decode(salt
, strlen(salt
), b256_salt
, &salt_size
))
1068 if (base64_decode(b64_passwd
, strlen(b64_passwd
), real_passwd
, NULL
))
1071 /* very simple MD5 crypt algorithm, but then the salt we use is large */
1073 MD5_Update(&ctx
, b256_salt
, salt_size
); /* process the salt */
1074 MD5_Update(&ctx
, (uint8_t *)msg_passwd
, strlen(msg_passwd
));
1075 MD5_Final(md5_result
, &ctx
);
1076 return memcmp(md5_result
, real_passwd
, MD5_SIZE
);/* 0 = ok */
1079 static int auth_check(struct connstruct
*cn
)
1081 char line
[MAXREQUESTLENGTH
];
1085 if ((fp
= exist_check(cn
, ".htpasswd")) == NULL
)
1086 return 0; /* no .htpasswd file, so let though */
1088 if (cn
->authorization
[0] == 0)
1091 /* cn->authorization is in form "username:password" */
1092 if ((cp
= strchr(cn
->authorization
, ':')) == NULL
)
1095 *cp
++ = 0; /* cp becomes the password */
1097 while (fgets(line
, sizeof(line
), fp
) != NULL
)
1099 char *b64_file_passwd
;
1100 int l
= strlen(line
);
1103 if (line
[l
-1] == '\n')
1106 /* line is form "username:salt(b64)$password(b64)" */
1107 if ((b64_file_passwd
= strchr(line
, ':')) == NULL
)
1110 *b64_file_passwd
++ = 0;
1112 if (strcmp(line
, cn
->authorization
)) /* our user? */
1115 if (check_digest(b64_file_passwd
, cp
) == 0)
1124 send_authenticate(cn
, cn
->server_name
);
1129 static int htaccess_check(struct connstruct
*cn
)
1131 char line
[MAXREQUESTLENGTH
];
1135 if ((fp
= exist_check(cn
, ".htaccess")) == NULL
)
1136 return 0; /* no .htaccess file, so let though */
1138 while (fgets(line
, sizeof(line
), fp
) != NULL
)
1140 if (strstr(line
, "Deny all") || /* access to this dir denied */
1141 /* Access will be denied unless SSL is active */
1142 (!cn
->is_ssl
&& strstr(line
, "SSLRequireSSL")) ||
1143 /* Access will be denied if SSL is active */
1144 (cn
->is_ssl
&& strstr(line
, "SSLDenySSL")))
1155 static void send_error(struct connstruct
*cn
, int err
)
1157 char buf
[MAXREQUESTLENGTH
];
1164 title
= "Forbidden";
1165 text
= "File is protected";
1166 #ifdef CONFIG_HTTP_VERBOSE
1167 printf("axhttpd: access to %s denied\n", cn
->filereq
); TTY_FLUSH();
1172 title
= "Not Found";
1177 title
= "POST data size is to large";
1187 snprintf(buf
, MAXREQUESTLENGTH
, "HTTP/1.1 %d %s\n"
1188 "Content-Type: text/html\n"
1189 "Cache-Control: no-cache,no-store\n"
1190 "Connection: close\n\n"
1191 "<html>\n<head>\n<title>%d %s</title></head>\n"
1192 "<body><h1>%d %s</h1>\n</body></html>\n",
1193 err
, title
, err
, title
, err
, text
);
1194 special_write(cn
, buf
, strlen(buf
));
1195 removeconnection(cn
);
1198 static const char *getmimetype(const char *name
)
1200 /* only bother with a few mime types - let the browser figure the rest out */
1201 if (strstr(name
, ".htm"))
1203 else if (strstr(name
, ".css"))
1206 return "application/octet-stream";
1209 static int special_write(struct connstruct
*cn
,
1210 const char *buf
, size_t count
)
1215 return ssl
? ssl_write(ssl
, (uint8_t *)buf
, count
) : -1;
1218 return SOCKET_WRITE(cn
->networkdesc
, buf
, count
);
1221 static int special_read(struct connstruct
*cn
, void *buf
, size_t count
)
1228 if ((res
= ssl_read(cn
->ssl
, &read_buf
)) > SSL_OK
)
1230 memcpy(buf
, read_buf
, res
> (int)count
? count
: res
);
1234 res
= SOCKET_READ(cn
->networkdesc
, buf
, count
);