diff options
-rw-r--r-- | networking/httpd.c | 211 |
1 files changed, 104 insertions, 107 deletions
diff --git a/networking/httpd.c b/networking/httpd.c index 32ecde0b8..a57504bac 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
@@ -934,43 +934,108 @@ static void send_cgi_and_exit( | |||
934 | { | 934 | { |
935 | struct { int rd; int wr; } fromCgi; /* CGI -> httpd pipe */ | 935 | struct { int rd; int wr; } fromCgi; /* CGI -> httpd pipe */ |
936 | struct { int rd; int wr; } toCgi; /* httpd -> CGI pipe */ | 936 | struct { int rd; int wr; } toCgi; /* httpd -> CGI pipe */ |
937 | char *argp[] = { NULL, NULL }; | 937 | char *fullpath; |
938 | int pid = 0; | 938 | char *script; |
939 | char *purl; | ||
940 | size_t post_read_size, post_read_idx; | ||
939 | int buf_count; | 941 | int buf_count; |
940 | int status; | 942 | int status; |
941 | size_t post_read_size, post_read_idx; | 943 | int pid = 0; |
944 | int sv_accepted_socket = accepted_socket; | ||
945 | |||
946 | /* | ||
947 | * We are mucking with environment _first_ and then vfork/exec, | ||
948 | * this allows us to use vfork safely. Parent don't care about | ||
949 | * these environment changes anyway. | ||
950 | */ | ||
951 | |||
952 | /* | ||
953 | * Find PATH_INFO. | ||
954 | */ | ||
955 | purl = xstrdup(url); | ||
956 | script = purl; | ||
957 | while ((script = strchr(script + 1, '/')) != NULL) { | ||
958 | /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ | ||
959 | struct stat sb; | ||
960 | |||
961 | *script = '\0'; | ||
962 | if (!is_directory(purl + 1, 1, &sb)) { | ||
963 | /* not directory, found script.cgi/PATH_INFO */ | ||
964 | *script = '/'; | ||
965 | break; | ||
966 | } | ||
967 | *script = '/'; /* is directory, find next '/' */ | ||
968 | } | ||
969 | setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ | ||
970 | setenv1("REQUEST_METHOD", request); | ||
971 | if (g_query) { | ||
972 | putenv(xasprintf("%s=%s?%s", "REQUEST_URI", purl, g_query)); | ||
973 | } else { | ||
974 | setenv1("REQUEST_URI", purl); | ||
975 | } | ||
976 | if (script != NULL) | ||
977 | *script = '\0'; /* cut off /PATH_INFO */ | ||
978 | |||
979 | /* SCRIPT_FILENAME required by PHP in CGI mode */ | ||
980 | fullpath = concat_path_file(home_httpd, purl); | ||
981 | setenv1("SCRIPT_FILENAME", fullpath); | ||
982 | /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ | ||
983 | setenv1("SCRIPT_NAME", purl); | ||
984 | /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: | ||
985 | * QUERY_STRING: The information which follows the ? in the URL | ||
986 | * which referenced this script. This is the query information. | ||
987 | * It should not be decoded in any fashion. This variable | ||
988 | * should always be set when there is query information, | ||
989 | * regardless of command line decoding. */ | ||
990 | /* (Older versions of bbox seem to do some decoding) */ | ||
991 | setenv1("QUERY_STRING", g_query); | ||
992 | putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER); | ||
993 | putenv((char*)"SERVER_PROTOCOL=HTTP/1.0"); | ||
994 | putenv((char*)"GATEWAY_INTERFACE=CGI/1.1"); | ||
995 | /* Having _separate_ variables for IP and port defeats | ||
996 | * the purpose of having socket abstraction. Which "port" | ||
997 | * are you using on Unix domain socket? | ||
998 | * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense. | ||
999 | * Oh well... */ | ||
1000 | { | ||
1001 | char *p = rmt_ip_str ? rmt_ip_str : (char*)""; | ||
1002 | char *cp = strrchr(p, ':'); | ||
1003 | if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']')) | ||
1004 | cp = NULL; | ||
1005 | if (cp) *cp = '\0'; /* delete :PORT */ | ||
1006 | setenv1("REMOTE_ADDR", p); | ||
1007 | if (cp) *cp = ':'; | ||
1008 | } | ||
1009 | setenv1("HTTP_USER_AGENT", user_agent); | ||
1010 | #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
1011 | setenv_long("REMOTE_PORT", tcp_port); | ||
1012 | #endif | ||
1013 | if (bodyLen) | ||
1014 | setenv_long("CONTENT_LENGTH", bodyLen); | ||
1015 | if (cookie) | ||
1016 | setenv1("HTTP_COOKIE", cookie); | ||
1017 | if (content_type) | ||
1018 | setenv1("CONTENT_TYPE", content_type); | ||
1019 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1020 | if (remoteuser) { | ||
1021 | setenv1("REMOTE_USER", remoteuser); | ||
1022 | putenv((char*)"AUTH_TYPE=Basic"); | ||
1023 | } | ||
1024 | #endif | ||
1025 | if (referer) | ||
1026 | setenv1("HTTP_REFERER", referer); | ||
942 | 1027 | ||
943 | xpipe(&fromCgi.rd); | 1028 | xpipe(&fromCgi.rd); |
944 | xpipe(&toCgi.rd); | 1029 | xpipe(&toCgi.rd); |
945 | 1030 | ||
946 | /* | ||
947 | * Note: We can use vfork() here in the no-mmu case, although | ||
948 | * the child modifies the parent's variables, due to: | ||
949 | * 1) The parent does not use the child-modified variables. | ||
950 | * 2) The allocated memory (in the child) is freed when the process | ||
951 | * exits. This happens instantly after the child finishes, | ||
952 | * since httpd is run from inetd (and it can't run standalone | ||
953 | * in uClinux). | ||
954 | * TODO: we can muck with environment _first_ and then fork/exec, | ||
955 | * that will be more understandable, and safer wrt vfork! | ||
956 | */ | ||
957 | |||
958 | #if !BB_MMU | ||
959 | pid = vfork(); | 1031 | pid = vfork(); |
960 | #else | ||
961 | pid = fork(); | ||
962 | #endif | ||
963 | if (pid < 0) { | 1032 | if (pid < 0) { |
964 | /* TODO: log perror? */ | 1033 | /* TODO: log perror? */ |
965 | log_and_exit(); | 1034 | log_and_exit(); |
966 | } | 1035 | } |
967 | 1036 | ||
968 | if (!pid) { | 1037 | if (!pid) { |
969 | /* child process */ | 1038 | /* Child process */ |
970 | char *fullpath; | ||
971 | char *script; | ||
972 | char *purl; | ||
973 | |||
974 | xfunc_error_retval = 242; | 1039 | xfunc_error_retval = 242; |
975 | 1040 | ||
976 | if (accepted_socket > 1) | 1041 | if (accepted_socket > 1) |
@@ -982,95 +1047,18 @@ static void send_cgi_and_exit( | |||
982 | xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */ | 1047 | xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */ |
983 | close(fromCgi.rd); | 1048 | close(fromCgi.rd); |
984 | close(toCgi.wr); | 1049 | close(toCgi.wr); |
985 | /* Huh? User seeing stderr can be a security problem. | 1050 | /* User seeing stderr output can be a security problem. |
986 | * If CGI really wants that, it can always do dup itself. */ | 1051 | * If CGI really wants that, it can always do dup itself. */ |
987 | /* dup2(1, 2); */ | 1052 | /* dup2(1, 2); */ |
988 | 1053 | ||
989 | /* | 1054 | /* script must have absolute path */ |
990 | * Find PATH_INFO. | ||
991 | */ | ||
992 | purl = xstrdup(url); | ||
993 | script = purl; | ||
994 | while ((script = strchr(script + 1, '/')) != NULL) { | ||
995 | /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ | ||
996 | struct stat sb; | ||
997 | |||
998 | *script = '\0'; | ||
999 | if (!is_directory(purl + 1, 1, &sb)) { | ||
1000 | /* not directory, found script.cgi/PATH_INFO */ | ||
1001 | *script = '/'; | ||
1002 | break; | ||
1003 | } | ||
1004 | *script = '/'; /* is directory, find next '/' */ | ||
1005 | } | ||
1006 | setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ | ||
1007 | setenv1("REQUEST_METHOD", request); | ||
1008 | if (g_query) { | ||
1009 | putenv(xasprintf("%s=%s?%s", "REQUEST_URI", purl, g_query)); | ||
1010 | } else { | ||
1011 | setenv1("REQUEST_URI", purl); | ||
1012 | } | ||
1013 | if (script != NULL) | ||
1014 | *script = '\0'; /* cut off /PATH_INFO */ | ||
1015 | |||
1016 | /* SCRIPT_FILENAME required by PHP in CGI mode */ | ||
1017 | fullpath = concat_path_file(home_httpd, purl); | ||
1018 | setenv1("SCRIPT_FILENAME", fullpath); | ||
1019 | /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ | ||
1020 | setenv1("SCRIPT_NAME", purl); | ||
1021 | /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: | ||
1022 | * QUERY_STRING: The information which follows the ? in the URL | ||
1023 | * which referenced this script. This is the query information. | ||
1024 | * It should not be decoded in any fashion. This variable | ||
1025 | * should always be set when there is query information, | ||
1026 | * regardless of command line decoding. */ | ||
1027 | /* (Older versions of bbox seem to do some decoding) */ | ||
1028 | setenv1("QUERY_STRING", g_query); | ||
1029 | putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER); | ||
1030 | putenv((char*)"SERVER_PROTOCOL=HTTP/1.0"); | ||
1031 | putenv((char*)"GATEWAY_INTERFACE=CGI/1.1"); | ||
1032 | /* Having _separate_ variables for IP and port defeats | ||
1033 | * the purpose of having socket abstraction. Which "port" | ||
1034 | * are you using on Unix domain socket? | ||
1035 | * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense. | ||
1036 | * Oh well... */ | ||
1037 | { | ||
1038 | char *p = rmt_ip_str ? rmt_ip_str : (char*)""; | ||
1039 | char *cp = strrchr(p, ':'); | ||
1040 | if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']')) | ||
1041 | cp = NULL; | ||
1042 | if (cp) *cp = '\0'; /* delete :PORT */ | ||
1043 | setenv1("REMOTE_ADDR", p); | ||
1044 | if (cp) *cp = ':'; | ||
1045 | } | ||
1046 | setenv1("HTTP_USER_AGENT", user_agent); | ||
1047 | #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
1048 | setenv_long("REMOTE_PORT", tcp_port); | ||
1049 | #endif | ||
1050 | if (bodyLen) | ||
1051 | setenv_long("CONTENT_LENGTH", bodyLen); | ||
1052 | if (cookie) | ||
1053 | setenv1("HTTP_COOKIE", cookie); | ||
1054 | if (content_type) | ||
1055 | setenv1("CONTENT_TYPE", content_type); | ||
1056 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
1057 | if (remoteuser) { | ||
1058 | setenv1("REMOTE_USER", remoteuser); | ||
1059 | putenv((char*)"AUTH_TYPE=Basic"); | ||
1060 | } | ||
1061 | #endif | ||
1062 | if (referer) | ||
1063 | setenv1("HTTP_REFERER", referer); | ||
1064 | |||
1065 | /* set execve argp[0] without path */ | ||
1066 | argp[0] = (char*)bb_basename(purl); | ||
1067 | /* but script argp[0] must have absolute path */ | ||
1068 | script = strrchr(fullpath, '/'); | 1055 | script = strrchr(fullpath, '/'); |
1069 | if (!script) | 1056 | if (!script) |
1070 | goto error_execing_cgi; | 1057 | goto error_execing_cgi; |
1071 | *script = '\0'; | 1058 | *script = '\0'; |
1072 | /* chdiring to script's dir */ | 1059 | /* chdiring to script's dir */ |
1073 | if (chdir(fullpath) == 0) { | 1060 | if (chdir(fullpath) == 0) { |
1061 | char *argv[2]; | ||
1074 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | 1062 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
1075 | char *interpr = NULL; | 1063 | char *interpr = NULL; |
1076 | char *suffix = strrchr(purl, '.'); | 1064 | char *suffix = strrchr(purl, '.'); |
@@ -1086,12 +1074,15 @@ static void send_cgi_and_exit( | |||
1086 | } | 1074 | } |
1087 | #endif | 1075 | #endif |
1088 | *script = '/'; | 1076 | *script = '/'; |
1077 | /* set argv[0] to name without path */ | ||
1078 | argv[0] = (char*)bb_basename(purl); | ||
1079 | argv[1] = NULL; | ||
1089 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | 1080 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
1090 | if (interpr) | 1081 | if (interpr) |
1091 | execv(interpr, argp); | 1082 | execv(interpr, argv); |
1092 | else | 1083 | else |
1093 | #endif | 1084 | #endif |
1094 | execv(fullpath, argp); | 1085 | execv(fullpath, argv); |
1095 | } | 1086 | } |
1096 | error_execing_cgi: | 1087 | error_execing_cgi: |
1097 | /* send to stdout | 1088 | /* send to stdout |
@@ -1100,7 +1091,13 @@ static void send_cgi_and_exit( | |||
1100 | send_headers_and_exit(HTTP_NOT_FOUND); | 1091 | send_headers_and_exit(HTTP_NOT_FOUND); |
1101 | } /* end child */ | 1092 | } /* end child */ |
1102 | 1093 | ||
1103 | /* parent process */ | 1094 | /* Parent process */ |
1095 | |||
1096 | /* First, restore variables possibly changed by child */ | ||
1097 | xfunc_error_retval = 0; | ||
1098 | accepted_socket = sv_accepted_socket; | ||
1099 | |||
1100 | /* Prepare for pumping data */ | ||
1104 | buf_count = 0; | 1101 | buf_count = 0; |
1105 | post_read_size = 0; | 1102 | post_read_size = 0; |
1106 | post_read_idx = 0; /* for gcc */ | 1103 | post_read_idx = 0; /* for gcc */ |