diff options
-rw-r--r-- | networking/httpd.c | 396 |
1 files changed, 197 insertions, 199 deletions
diff --git a/networking/httpd.c b/networking/httpd.c index 71792862e..40f37ba07 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
@@ -963,212 +963,211 @@ static int sendCgi(const char *url, | |||
963 | int inFd; | 963 | int inFd; |
964 | int outFd; | 964 | int outFd; |
965 | int firstLine = 1; | 965 | int firstLine = 1; |
966 | int status; | ||
967 | size_t post_readed_size, post_readed_idx; | ||
966 | 968 | ||
967 | if (pipe(fromCgi) != 0) | 969 | if (pipe(fromCgi) != 0) |
968 | return 0; | 970 | return 0; |
969 | if (pipe(toCgi) != 0) | 971 | if (pipe(toCgi) != 0) |
970 | return 0; | 972 | return 0; |
973 | |||
971 | pid = fork(); | 974 | pid = fork(); |
972 | if (pid < 0) | 975 | if (pid < 0) |
973 | return 0; | 976 | return 0; |
977 | |||
978 | if (!pid) { | ||
979 | /* child process */ | ||
980 | char *script; | ||
981 | char *purl = strdup(url); | ||
982 | char realpath_buff[MAXPATHLEN]; | ||
983 | |||
984 | if (purl == NULL) | ||
985 | _exit(242); | ||
974 | 986 | ||
975 | do { | 987 | inFd = toCgi[0]; |
976 | if (!pid) { | 988 | outFd = fromCgi[1]; |
977 | /* child process */ | 989 | |
978 | char *script; | 990 | dup2(inFd, 0); // replace stdin with the pipe |
979 | char *purl = strdup(url); | 991 | dup2(outFd, 1); // replace stdout with the pipe |
980 | char realpath_buff[MAXPATHLEN]; | 992 | if (!DEBUG) |
981 | 993 | dup2(outFd, 2); // replace stderr with the pipe | |
982 | if (purl == NULL) | 994 | |
983 | _exit(242); | 995 | close(toCgi[0]); |
984 | 996 | close(toCgi[1]); | |
985 | inFd = toCgi[0]; | 997 | close(fromCgi[0]); |
986 | outFd = fromCgi[1]; | 998 | close(fromCgi[1]); |
987 | 999 | ||
988 | dup2(inFd, 0); // replace stdin with the pipe | 1000 | close(config->accepted_socket); |
989 | dup2(outFd, 1); // replace stdout with the pipe | 1001 | close(config->server_socket); |
990 | if (!DEBUG) | 1002 | |
991 | dup2(outFd, 2); // replace stderr with the pipe | 1003 | /* |
992 | 1004 | * Find PATH_INFO. | |
993 | close(toCgi[0]); | 1005 | */ |
994 | close(toCgi[1]); | 1006 | script = purl; |
995 | close(fromCgi[0]); | 1007 | while ((script = strchr(script + 1, '/')) != NULL) { |
996 | close(fromCgi[1]); | 1008 | /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ |
997 | 1009 | struct stat sb; | |
998 | close(config->accepted_socket); | 1010 | |
999 | close(config->server_socket); | 1011 | *script = '\0'; |
1000 | 1012 | if (is_directory(purl + 1, 1, &sb) == 0) { | |
1001 | /* | 1013 | /* not directory, found script.cgi/PATH_INFO */ |
1002 | * Find PATH_INFO. | 1014 | *script = '/'; |
1003 | */ | 1015 | break; |
1004 | script = purl; | ||
1005 | while ((script = strchr(script + 1, '/')) != NULL) { | ||
1006 | /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ | ||
1007 | struct stat sb; | ||
1008 | |||
1009 | *script = '\0'; | ||
1010 | if (is_directory(purl + 1, 1, &sb) == 0) { | ||
1011 | /* not directory, found script.cgi/PATH_INFO */ | ||
1012 | *script = '/'; | ||
1013 | break; | ||
1014 | } | ||
1015 | *script = '/'; /* is directory, find next '/' */ | ||
1016 | } | ||
1017 | setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ | ||
1018 | /* setenv1("PATH", getenv("PATH")); redundant */ | ||
1019 | setenv1("REQUEST_METHOD", request); | ||
1020 | if (config->query) { | ||
1021 | char *uri = alloca(strlen(purl) + 2 + strlen(config->query)); | ||
1022 | if (uri) | ||
1023 | sprintf(uri, "%s?%s", purl, config->query); | ||
1024 | setenv1("REQUEST_URI", uri); | ||
1025 | } else { | ||
1026 | setenv1("REQUEST_URI", purl); | ||
1027 | } | 1016 | } |
1028 | if (script != NULL) | 1017 | *script = '/'; /* is directory, find next '/' */ |
1029 | *script = '\0'; /* reduce /PATH_INFO */ | 1018 | } |
1030 | /* SCRIPT_FILENAME required by PHP in CGI mode */ | 1019 | setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ |
1031 | if (!realpath(purl + 1, realpath_buff)) | 1020 | /* setenv1("PATH", getenv("PATH")); redundant */ |
1032 | goto error_execing_cgi; | 1021 | setenv1("REQUEST_METHOD", request); |
1033 | setenv1("SCRIPT_FILENAME", realpath_buff); | 1022 | if (config->query) { |
1034 | /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ | 1023 | char *uri = alloca(strlen(purl) + 2 + strlen(config->query)); |
1035 | setenv1("SCRIPT_NAME", purl); | 1024 | if (uri) |
1036 | setenv1("QUERY_STRING", config->query); | 1025 | sprintf(uri, "%s?%s", purl, config->query); |
1037 | setenv1("SERVER_SOFTWARE", httpdVersion); | 1026 | setenv1("REQUEST_URI", uri); |
1038 | putenv("SERVER_PROTOCOL=HTTP/1.0"); | 1027 | } else { |
1039 | putenv("GATEWAY_INTERFACE=CGI/1.1"); | 1028 | setenv1("REQUEST_URI", purl); |
1040 | setenv1("REMOTE_ADDR", config->rmt_ip_str); | 1029 | } |
1030 | if (script != NULL) | ||
1031 | *script = '\0'; /* reduce /PATH_INFO */ | ||
1032 | /* SCRIPT_FILENAME required by PHP in CGI mode */ | ||
1033 | if (!realpath(purl + 1, realpath_buff)) | ||
1034 | goto error_execing_cgi; | ||
1035 | setenv1("SCRIPT_FILENAME", realpath_buff); | ||
1036 | /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ | ||
1037 | setenv1("SCRIPT_NAME", purl); | ||
1038 | setenv1("QUERY_STRING", config->query); | ||
1039 | setenv1("SERVER_SOFTWARE", httpdVersion); | ||
1040 | putenv("SERVER_PROTOCOL=HTTP/1.0"); | ||
1041 | putenv("GATEWAY_INTERFACE=CGI/1.1"); | ||
1042 | setenv1("REMOTE_ADDR", config->rmt_ip_str); | ||
1041 | #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | 1043 | #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV |
1042 | setenv_long("REMOTE_PORT", config->port); | 1044 | setenv_long("REMOTE_PORT", config->port); |
1043 | #endif | 1045 | #endif |
1044 | if (bodyLen) | 1046 | if (bodyLen) |
1045 | setenv_long("CONTENT_LENGTH", bodyLen); | 1047 | setenv_long("CONTENT_LENGTH", bodyLen); |
1046 | if (cookie) | 1048 | if (cookie) |
1047 | setenv1("HTTP_COOKIE", cookie); | 1049 | setenv1("HTTP_COOKIE", cookie); |
1048 | if (content_type) | 1050 | if (content_type) |
1049 | setenv1("CONTENT_TYPE", content_type); | 1051 | setenv1("CONTENT_TYPE", content_type); |
1050 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | 1052 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
1051 | if (config->remoteuser) { | 1053 | if (config->remoteuser) { |
1052 | setenv1("REMOTE_USER", config->remoteuser); | 1054 | setenv1("REMOTE_USER", config->remoteuser); |
1053 | putenv("AUTH_TYPE=Basic"); | 1055 | putenv("AUTH_TYPE=Basic"); |
1054 | } | 1056 | } |
1055 | #endif | 1057 | #endif |
1056 | if (config->referer) | 1058 | if (config->referer) |
1057 | setenv1("HTTP_REFERER", config->referer); | 1059 | setenv1("HTTP_REFERER", config->referer); |
1058 | 1060 | ||
1059 | /* set execve argp[0] without path */ | 1061 | /* set execve argp[0] without path */ |
1060 | argp[0] = strrchr(purl, '/') + 1; | 1062 | argp[0] = strrchr(purl, '/') + 1; |
1061 | /* but script argp[0] must have absolute path and chdiring to this */ | 1063 | /* but script argp[0] must have absolute path and chdiring to this */ |
1062 | script = strrchr(realpath_buff, '/'); | 1064 | script = strrchr(realpath_buff, '/'); |
1063 | if (!script) | 1065 | if (!script) |
1064 | goto error_execing_cgi; | 1066 | goto error_execing_cgi; |
1065 | *script = '\0'; | 1067 | *script = '\0'; |
1066 | if (chdir(realpath_buff) == 0) { | 1068 | if (chdir(realpath_buff) == 0) { |
1067 | // now run the program. If it fails, | 1069 | // now run the program. If it fails, |
1068 | // use _exit() so no destructors | 1070 | // use _exit() so no destructors |
1069 | // get called and make a mess. | 1071 | // get called and make a mess. |
1070 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | 1072 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
1071 | char *interpr = NULL; | 1073 | char *interpr = NULL; |
1072 | char *suffix = strrchr(purl, '.'); | 1074 | char *suffix = strrchr(purl, '.'); |
1073 | 1075 | ||
1074 | if (suffix) { | 1076 | if (suffix) { |
1075 | Htaccess *cur; | 1077 | Htaccess *cur; |
1076 | for (cur = config->script_i; cur; cur = cur->next) { | 1078 | for (cur = config->script_i; cur; cur = cur->next) { |
1077 | if (strcmp(cur->before_colon + 1, suffix) == 0) { | 1079 | if (strcmp(cur->before_colon + 1, suffix) == 0) { |
1078 | interpr = cur->after_colon; | 1080 | interpr = cur->after_colon; |
1079 | break; | 1081 | break; |
1080 | } | ||
1081 | } | 1082 | } |
1082 | } | 1083 | } |
1084 | } | ||
1083 | #endif | 1085 | #endif |
1084 | *script = '/'; | 1086 | *script = '/'; |
1085 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | 1087 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
1086 | if (interpr) | 1088 | if (interpr) |
1087 | execv(interpr, argp); | 1089 | execv(interpr, argp); |
1088 | else | 1090 | else |
1089 | #endif | 1091 | #endif |
1090 | execv(realpath_buff, argp); | 1092 | execv(realpath_buff, argp); |
1091 | } | 1093 | } |
1092 | error_execing_cgi: | 1094 | error_execing_cgi: |
1093 | /* send to stdout (even if we are not from inetd) */ | 1095 | /* send to stdout (even if we are not from inetd) */ |
1094 | config->accepted_socket = 1; | 1096 | config->accepted_socket = 1; |
1095 | sendHeaders(HTTP_NOT_FOUND); | 1097 | sendHeaders(HTTP_NOT_FOUND); |
1096 | _exit(242); | 1098 | _exit(242); |
1097 | } /* end child */ | 1099 | } /* end child */ |
1098 | 1100 | ||
1099 | } while (0); | 1101 | /* parent process */ |
1100 | 1102 | ||
1101 | if (pid > 0) { | 1103 | post_readed_size = 0; |
1102 | /* parent process */ | 1104 | post_readed_idx = 0; |
1103 | int status; | 1105 | inFd = fromCgi[0]; |
1104 | size_t post_readed_size = 0, post_readed_idx = 0; | 1106 | outFd = toCgi[1]; |
1107 | close(fromCgi[1]); | ||
1108 | close(toCgi[0]); | ||
1109 | signal(SIGPIPE, SIG_IGN); | ||
1105 | 1110 | ||
1106 | inFd = fromCgi[0]; | 1111 | while (1) { |
1107 | outFd = toCgi[1]; | 1112 | fd_set readSet; |
1108 | close(fromCgi[1]); | 1113 | fd_set writeSet; |
1109 | close(toCgi[0]); | 1114 | char wbuf[128]; |
1110 | signal(SIGPIPE, SIG_IGN); | 1115 | int nfound; |
1111 | 1116 | int count; | |
1112 | while (1) { | 1117 | |
1113 | fd_set readSet; | 1118 | FD_ZERO(&readSet); |
1114 | fd_set writeSet; | 1119 | FD_ZERO(&writeSet); |
1115 | char wbuf[128]; | 1120 | FD_SET(inFd, &readSet); |
1116 | int nfound; | 1121 | if (bodyLen > 0 || post_readed_size > 0) { |
1117 | int count; | 1122 | FD_SET(outFd, &writeSet); |
1118 | 1123 | nfound = outFd > inFd ? outFd : inFd; | |
1119 | FD_ZERO(&readSet); | 1124 | if (post_readed_size == 0) { |
1120 | FD_ZERO(&writeSet); | 1125 | FD_SET(config->accepted_socket, &readSet); |
1121 | FD_SET(inFd, &readSet); | 1126 | if (nfound < config->accepted_socket) |
1122 | if (bodyLen > 0 || post_readed_size > 0) { | 1127 | nfound = config->accepted_socket; |
1123 | FD_SET(outFd, &writeSet); | ||
1124 | nfound = outFd > inFd ? outFd : inFd; | ||
1125 | if (post_readed_size == 0) { | ||
1126 | FD_SET(config->accepted_socket, &readSet); | ||
1127 | if (nfound < config->accepted_socket) | ||
1128 | nfound = config->accepted_socket; | ||
1129 | } | ||
1130 | /* Now wait on the set of sockets! */ | ||
1131 | nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); | ||
1132 | } else { | ||
1133 | if (!bodyLen) { | ||
1134 | close(outFd); | ||
1135 | bodyLen = -1; | ||
1136 | } | ||
1137 | nfound = select(inFd + 1, &readSet, 0, 0, NULL); | ||
1138 | } | 1128 | } |
1129 | /* Now wait on the set of sockets! */ | ||
1130 | nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); | ||
1131 | } else { | ||
1132 | if (!bodyLen) { | ||
1133 | close(outFd); | ||
1134 | bodyLen = -1; | ||
1135 | } | ||
1136 | nfound = select(inFd + 1, &readSet, 0, 0, NULL); | ||
1137 | } | ||
1139 | 1138 | ||
1140 | if (nfound <= 0) { | 1139 | if (nfound <= 0) { |
1141 | if (waitpid(pid, &status, WNOHANG) > 0) { | 1140 | if (waitpid(pid, &status, WNOHANG) > 0) { |
1142 | close(inFd); | 1141 | close(inFd); |
1143 | if (DEBUG && WIFEXITED(status)) | 1142 | if (DEBUG && WIFEXITED(status)) |
1144 | bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status)); | 1143 | bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status)); |
1145 | if (DEBUG && WIFSIGNALED(status)) | 1144 | if (DEBUG && WIFSIGNALED(status)) |
1146 | bb_error_msg("piped has exited with signal=%d", WTERMSIG(status)); | 1145 | bb_error_msg("piped has exited with signal=%d", WTERMSIG(status)); |
1147 | break; | 1146 | break; |
1148 | } | 1147 | } |
1149 | } else if (post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { | 1148 | } else if (post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { |
1150 | count = full_write(outFd, wbuf + post_readed_idx, post_readed_size); | 1149 | count = full_write(outFd, wbuf + post_readed_idx, post_readed_size); |
1151 | if (count > 0) { | 1150 | if (count > 0) { |
1152 | post_readed_size -= count; | 1151 | post_readed_size -= count; |
1153 | post_readed_idx += count; | 1152 | post_readed_idx += count; |
1154 | if (post_readed_size == 0) | 1153 | if (post_readed_size == 0) |
1155 | post_readed_idx = 0; | 1154 | post_readed_idx = 0; |
1156 | } else { | 1155 | } else { |
1157 | post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */ | 1156 | post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */ |
1158 | } | ||
1159 | } else if (bodyLen > 0 && post_readed_size == 0 && FD_ISSET(config->accepted_socket, &readSet)) { | ||
1160 | count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen; | ||
1161 | count = safe_read(config->accepted_socket, wbuf, count); | ||
1162 | if (count > 0) { | ||
1163 | post_readed_size += count; | ||
1164 | bodyLen -= count; | ||
1165 | } else { | ||
1166 | bodyLen = 0; /* closed */ | ||
1167 | } | ||
1168 | } | 1157 | } |
1169 | if (FD_ISSET(inFd, &readSet)) { | 1158 | } else if (bodyLen > 0 && post_readed_size == 0 && FD_ISSET(config->accepted_socket, &readSet)) { |
1170 | int s = config->accepted_socket; | 1159 | count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen; |
1171 | char *rbuf = config->buf; | 1160 | count = safe_read(config->accepted_socket, wbuf, count); |
1161 | if (count > 0) { | ||
1162 | post_readed_size += count; | ||
1163 | bodyLen -= count; | ||
1164 | } else { | ||
1165 | bodyLen = 0; /* closed */ | ||
1166 | } | ||
1167 | } | ||
1168 | if (FD_ISSET(inFd, &readSet)) { | ||
1169 | int s = config->accepted_socket; | ||
1170 | char *rbuf = config->buf; | ||
1172 | 1171 | ||
1173 | #ifndef PIPE_BUF | 1172 | #ifndef PIPE_BUF |
1174 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ | 1173 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ |
@@ -1179,28 +1178,27 @@ static int sendCgi(const char *url, | |||
1179 | # error "PIPESIZE >= MAX_MEMORY_BUFF" | 1178 | # error "PIPESIZE >= MAX_MEMORY_BUFF" |
1180 | #endif | 1179 | #endif |
1181 | 1180 | ||
1182 | // There is something to read | 1181 | // There is something to read |
1183 | count = safe_read(inFd, rbuf, PIPESIZE); | 1182 | count = safe_read(inFd, rbuf, PIPESIZE); |
1184 | if (count == 0) | 1183 | if (count == 0) |
1185 | break; /* closed */ | 1184 | break; /* closed */ |
1186 | if (count > 0) { | 1185 | if (count > 0) { |
1187 | if (firstLine) { | 1186 | if (firstLine) { |
1188 | rbuf[count] = 0; | 1187 | rbuf[count] = 0; |
1189 | /* check to see if the user script added headers */ | 1188 | /* check to see if the user script added headers */ |
1190 | if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) { | 1189 | if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) { |
1191 | full_write(s, "HTTP/1.0 200 OK\r\n", 17); | 1190 | full_write(s, "HTTP/1.0 200 OK\r\n", 17); |
1192 | } | ||
1193 | if (strstr(rbuf, "ontent-") == 0) { | ||
1194 | full_write(s, "Content-type: text/plain\r\n\r\n", 28); | ||
1195 | } | ||
1196 | firstLine = 0; | ||
1197 | } | 1191 | } |
1198 | if (full_write(s, rbuf, count) != count) | 1192 | if (strstr(rbuf, "ontent-") == 0) { |
1199 | break; | 1193 | full_write(s, "Content-type: text/plain\r\n\r\n", 28); |
1200 | 1194 | } | |
1201 | if (DEBUG) | 1195 | firstLine = 0; |
1202 | fprintf(stderr, "cgi read %d bytes\n", count); | ||
1203 | } | 1196 | } |
1197 | if (full_write(s, rbuf, count) != count) | ||
1198 | break; | ||
1199 | |||
1200 | if (DEBUG) | ||
1201 | fprintf(stderr, "cgi read %d bytes\n", count); | ||
1204 | } | 1202 | } |
1205 | } | 1203 | } |
1206 | } | 1204 | } |