Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/networking/httpd.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/networking/httpd.c
r902 r1770 21 21 * 22 22 * 23 * When a url contains "cgi-bin" it is assumed to be a cgi script. The23 * When a url starts by "/cgi-bin/" it is assumed to be a cgi script. The 24 24 * server changes directory to the location of the script and executes it 25 25 * after setting QUERY_STRING and other environment variables. 26 * 27 * Doc: 28 * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html 26 29 * 27 30 * The server can also be invoked as a url arg decoder and html text encoder … … 40 43 * A:127.0.0.1 # Allow local loopback connections 41 44 * D:* # Deny from other IP connections 45 * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page 42 46 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ 43 47 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ … … 82 86 * result, the subdir settings only have a lifetime of a single request. 83 87 * 88 * Custom error pages can contain an absolute path or be relative to 89 * 'home_httpd'. Error pages are to be static files (no CGI or script). Error 90 * page can only be defined in the root configuration file and are not taken 91 * into account in local (directories) config files. 84 92 * 85 93 * If -c is not set, an attempt will be made to open the default … … 87 95 * server exits with an error. 88 96 * 89 */ 90 91 92 #include <stdio.h> 93 #include <ctype.h> /* for isspace */ 94 #include <string.h> 95 #include <stdlib.h> /* for malloc */ 96 #include <time.h> 97 #include <unistd.h> /* for close */ 98 #include <signal.h> 99 #include <sys/types.h> 100 #include <sys/socket.h> /* for connect and socket*/ 101 #include <netinet/in.h> /* for sockaddr_in */ 102 #include <sys/stat.h> 103 #include <sys/wait.h> 104 #include <fcntl.h> /* for open modes */ 105 #include "busybox.h" 106 107 108 static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004"; 109 static const char default_path_httpd_conf[] = "/etc"; 110 static const char httpd_conf[] = "httpd.conf"; 111 static const char home[] = "./"; 112 113 #ifdef CONFIG_LFS 114 # define cont_l_fmt "%lld" 115 # define cont_l_type (long long) 116 #else 117 # define cont_l_fmt "%ld" 118 # define cont_l_type (long) 119 #endif 120 121 #define TIMEOUT 60 122 123 // Note: busybox xfuncs are not used because we want the server to keep running 124 // if something bad happens due to a malformed user request. 125 // As a result, all memory allocation after daemonize 126 // is checked rigorously 97 */ 98 99 #include "libbb.h" 100 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 101 #include <sys/sendfile.h> 102 #endif 127 103 128 104 //#define DEBUG 1 129 130 #ifndef DEBUG 131 # define DEBUG 0 132 #endif 133 134 #define MAX_MEMORY_BUFF 8192 /* IO buffer */ 135 136 typedef struct HT_ACCESS { 105 #define DEBUG 0 106 107 /* amount of buffering in a pipe */ 108 #ifndef PIPE_BUF 109 # define PIPE_BUF 4096 110 #endif 111 112 #define IOBUF_SIZE 8192 /* IO buffer */ 113 114 #define HEADER_READ_TIMEOUT 60 115 116 static const char default_path_httpd_conf[] ALIGN1 = "/etc"; 117 static const char httpd_conf[] ALIGN1 = "httpd.conf"; 118 static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n"; 119 120 typedef struct has_next_ptr { 121 struct has_next_ptr *next; 122 } has_next_ptr; 123 124 /* Must have "next" as a first member */ 125 typedef struct Htaccess { 126 struct Htaccess *next; 137 127 char *after_colon; 138 struct HT_ACCESS *next; 139 char before_colon[1]; /* really bigger, must last */ 128 char before_colon[1]; /* really bigger, must be last */ 140 129 } Htaccess; 141 130 142 typedef struct HT_ACCESS_IP { 143 unsigned int ip; 144 unsigned int mask; 131 /* Must have "next" as a first member */ 132 typedef struct Htaccess_IP { 133 struct Htaccess_IP *next; 134 unsigned ip; 135 unsigned mask; 145 136 int allow_deny; 146 struct HT_ACCESS_IP *next;147 137 } Htaccess_IP; 148 138 149 typedef struct 150 { 151 char buf[MAX_MEMORY_BUFF]; 152 153 USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;) 154 USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 155 156 const char *query; 157 158 USE_FEATURE_HTTPD_CGI(char *referer;) 159 160 const char *configFile; 161 162 unsigned int rmt_ip; 163 #if defined(CONFIG_FEATURE_HTTPD_CGI) || DEBUG 164 char rmt_ip_str[16]; /* for set env REMOTE_ADDR */ 165 #endif 166 unsigned port; /* server initial port and for 167 set env REMOTE_PORT */ 168 union HTTPD_FOUND { 139 enum { 140 HTTP_OK = 200, 141 HTTP_MOVED_TEMPORARILY = 302, 142 HTTP_BAD_REQUEST = 400, /* malformed syntax */ 143 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ 144 HTTP_NOT_FOUND = 404, 145 HTTP_FORBIDDEN = 403, 146 HTTP_REQUEST_TIMEOUT = 408, 147 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ 148 HTTP_INTERNAL_SERVER_ERROR = 500, 149 HTTP_CONTINUE = 100, 150 #if 0 /* future use */ 151 HTTP_SWITCHING_PROTOCOLS = 101, 152 HTTP_CREATED = 201, 153 HTTP_ACCEPTED = 202, 154 HTTP_NON_AUTHORITATIVE_INFO = 203, 155 HTTP_NO_CONTENT = 204, 156 HTTP_MULTIPLE_CHOICES = 300, 157 HTTP_MOVED_PERMANENTLY = 301, 158 HTTP_NOT_MODIFIED = 304, 159 HTTP_PAYMENT_REQUIRED = 402, 160 HTTP_BAD_GATEWAY = 502, 161 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ 162 HTTP_RESPONSE_SETSIZE = 0xffffffff 163 #endif 164 }; 165 166 static const uint16_t http_response_type[] ALIGN2 = { 167 HTTP_OK, 168 HTTP_MOVED_TEMPORARILY, 169 HTTP_REQUEST_TIMEOUT, 170 HTTP_NOT_IMPLEMENTED, 171 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 172 HTTP_UNAUTHORIZED, 173 #endif 174 HTTP_NOT_FOUND, 175 HTTP_BAD_REQUEST, 176 HTTP_FORBIDDEN, 177 HTTP_INTERNAL_SERVER_ERROR, 178 #if 0 /* not implemented */ 179 HTTP_CREATED, 180 HTTP_ACCEPTED, 181 HTTP_NO_CONTENT, 182 HTTP_MULTIPLE_CHOICES, 183 HTTP_MOVED_PERMANENTLY, 184 HTTP_NOT_MODIFIED, 185 HTTP_BAD_GATEWAY, 186 HTTP_SERVICE_UNAVAILABLE, 187 #endif 188 }; 189 190 static const struct { 191 const char *name; 192 const char *info; 193 } http_response[ARRAY_SIZE(http_response_type)] = { 194 { "OK", NULL }, 195 { "Found", "Directories must end with a slash" }, /* ?? */ 196 { "Request Timeout", "No request appeared within 60 seconds" }, 197 { "Not Implemented", "The requested method is not recognized" }, 198 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 199 { "Unauthorized", "" }, 200 #endif 201 { "Not Found", "The requested URL was not found" }, 202 { "Bad Request", "Unsupported method" }, 203 { "Forbidden", "" }, 204 { "Internal Server Error", "Internal Server Error" }, 205 #if 0 /* not implemented */ 206 { "Created" }, 207 { "Accepted" }, 208 { "No Content" }, 209 { "Multiple Choices" }, 210 { "Moved Permanently" }, 211 { "Not Modified" }, 212 { "Bad Gateway", "" }, 213 { "Service Unavailable", "" }, 214 #endif 215 }; 216 217 struct globals { 218 int verbose; /* must be int (used by getopt32) */ 219 smallint flg_deny_all; 220 221 unsigned rmt_ip; /* used for IP-based allow/deny rules */ 222 time_t last_mod; 223 off_t ContentLength; /* -1 - unknown */ 224 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ 225 const char *bind_addr_or_port; 226 227 const char *g_query; 228 const char *configFile; 229 const char *home_httpd; 230 169 231 const char *found_mime_type; 170 232 const char *found_moved_temporarily; 171 } httpd_found; 172 173 off_t ContentLength; /* -1 - unknown */ 174 time_t last_mod; 175 176 Htaccess_IP *ip_a_d; /* config allow/deny lines */ 177 int flg_deny_all; 178 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 179 Htaccess *auth; /* config user:password lines */ 180 #endif 181 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 182 Htaccess *mime_a; /* config mime types */ 183 #endif 184 185 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 186 int accepted_socket; 187 # define a_c_r config->accepted_socket 188 # define a_c_w config->accepted_socket 189 #else 190 # define a_c_r 0 191 # define a_c_w 1 192 #endif 193 volatile int alarm_signaled; 194 195 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 196 Htaccess *script_i; /* config script interpreters */ 197 #endif 198 } HttpdConfig; 199 200 static HttpdConfig *config; 201 202 static const char request_GET[] = "GET"; /* size algorithmic optimize */ 203 204 static const char* const suffixTable [] = { 205 /* Warning: shorted equivalent suffix in one line must be first */ 206 ".htm.html", "text/html", 207 ".jpg.jpeg", "image/jpeg", 208 ".gif", "image/gif", 209 ".png", "image/png", 210 ".txt.h.c.cc.cpp", "text/plain", 211 ".css", "text/css", 212 ".wav", "audio/wav", 213 ".avi", "video/x-msvideo", 214 ".qt.mov", "video/quicktime", 215 ".mpe.mpeg", "video/mpeg", 216 ".mid.midi", "audio/midi", 217 ".mp3", "audio/mpeg", 218 #if 0 /* unpopular */ 219 ".au", "audio/basic", 220 ".pac", "application/x-ns-proxy-autoconfig", 221 ".vrml.wrl", "model/vrml", 222 #endif 223 0, "application/octet-stream" /* default */ 224 }; 225 226 typedef enum 227 { 228 HTTP_OK = 200, 229 HTTP_MOVED_TEMPORARILY = 302, 230 HTTP_BAD_REQUEST = 400, /* malformed syntax */ 231 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ 232 HTTP_NOT_FOUND = 404, 233 HTTP_FORBIDDEN = 403, 234 HTTP_REQUEST_TIMEOUT = 408, 235 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ 236 HTTP_INTERNAL_SERVER_ERROR = 500, 237 #if 0 /* future use */ 238 HTTP_CONTINUE = 100, 239 HTTP_SWITCHING_PROTOCOLS = 101, 240 HTTP_CREATED = 201, 241 HTTP_ACCEPTED = 202, 242 HTTP_NON_AUTHORITATIVE_INFO = 203, 243 HTTP_NO_CONTENT = 204, 244 HTTP_MULTIPLE_CHOICES = 300, 245 HTTP_MOVED_PERMANENTLY = 301, 246 HTTP_NOT_MODIFIED = 304, 247 HTTP_PAYMENT_REQUIRED = 402, 248 HTTP_BAD_GATEWAY = 502, 249 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ 250 HTTP_RESPONSE_SETSIZE=0xffffffff 251 #endif 252 } HttpResponseNum; 253 254 typedef struct 255 { 256 HttpResponseNum type; 257 const char *name; 258 const char *info; 259 } HttpEnumString; 260 261 static const HttpEnumString httpResponseNames[] = { 262 { HTTP_OK, "OK", NULL }, 263 { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." }, 264 { HTTP_REQUEST_TIMEOUT, "Request Timeout", 265 "No request appeared within a reasonable time period." }, 266 { HTTP_NOT_IMPLEMENTED, "Not Implemented", 267 "The requested method is not recognized by this server." }, 268 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 269 { HTTP_UNAUTHORIZED, "Unauthorized", "" }, 270 #endif 271 { HTTP_NOT_FOUND, "Not Found", 272 "The requested URL was not found on this server." }, 273 { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." }, 274 { HTTP_FORBIDDEN, "Forbidden", "" }, 275 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", 276 "Internal Server Error" }, 277 #if 0 /* not implemented */ 278 { HTTP_CREATED, "Created" }, 279 { HTTP_ACCEPTED, "Accepted" }, 280 { HTTP_NO_CONTENT, "No Content" }, 281 { HTTP_MULTIPLE_CHOICES, "Multiple Choices" }, 282 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, 283 { HTTP_NOT_MODIFIED, "Not Modified" }, 284 { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, 285 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, 233 Htaccess_IP *ip_a_d; /* config allow/deny lines */ 234 235 USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) 236 USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 237 USE_FEATURE_HTTPD_CGI(char *referer;) 238 USE_FEATURE_HTTPD_CGI(char *user_agent;) 239 240 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 241 Htaccess *g_auth; /* config user:password lines */ 242 #endif 243 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 244 Htaccess *mime_a; /* config mime types */ 245 #endif 246 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 247 Htaccess *script_i; /* config script interpreters */ 248 #endif 249 char *iobuf; /* [IOBUF_SIZE] */ 250 #define hdr_buf bb_common_bufsiz1 251 char *hdr_ptr; 252 int hdr_cnt; 253 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 254 const char *http_error_page[ARRAY_SIZE(http_response_type)]; 286 255 #endif 287 256 }; 288 289 290 static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; 291 static const char Content_length[] = "Content-length:"; 292 293 294 static int 295 scan_ip (const char **ep, unsigned int *ip, unsigned char endc) 296 { 297 const char *p = *ep; 298 int auto_mask = 8; 299 int j; 300 301 *ip = 0; 302 for (j = 0; j < 4; j++) { 303 unsigned int octet; 304 305 if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0) 306 return -auto_mask; 307 octet = 0; 308 while (*p >= '0' && *p <= '9') { 309 octet *= 10; 310 octet += *p - '0'; 311 if (octet > 255) 257 #define G (*ptr_to_globals) 258 #define verbose (G.verbose ) 259 #define flg_deny_all (G.flg_deny_all ) 260 #define rmt_ip (G.rmt_ip ) 261 #define bind_addr_or_port (G.bind_addr_or_port) 262 #define g_query (G.g_query ) 263 #define configFile (G.configFile ) 264 #define home_httpd (G.home_httpd ) 265 #define found_mime_type (G.found_mime_type ) 266 #define found_moved_temporarily (G.found_moved_temporarily) 267 #define ContentLength (G.ContentLength ) 268 #define last_mod (G.last_mod ) 269 #define ip_a_d (G.ip_a_d ) 270 #define g_realm (G.g_realm ) 271 #define remoteuser (G.remoteuser ) 272 #define referer (G.referer ) 273 #define user_agent (G.user_agent ) 274 #define rmt_ip_str (G.rmt_ip_str ) 275 #define g_auth (G.g_auth ) 276 #define mime_a (G.mime_a ) 277 #define script_i (G.script_i ) 278 #define iobuf (G.iobuf ) 279 #define hdr_ptr (G.hdr_ptr ) 280 #define hdr_cnt (G.hdr_cnt ) 281 #define http_error_page (G.http_error_page ) 282 #define INIT_G() do { \ 283 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ 284 USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ 285 bind_addr_or_port = "80"; \ 286 ContentLength = -1; \ 287 } while (0) 288 289 290 #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) 291 292 /* Prototypes */ 293 static void send_file_and_exit(const char *url, int headers) ATTRIBUTE_NORETURN; 294 295 static void free_llist(has_next_ptr **pptr) 296 { 297 has_next_ptr *cur = *pptr; 298 while (cur) { 299 has_next_ptr *t = cur; 300 cur = cur->next; 301 free(t); 302 } 303 *pptr = NULL; 304 } 305 306 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ 307 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ 308 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 309 static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr) 310 { 311 free_llist((has_next_ptr**)pptr); 312 } 313 #endif 314 315 static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) 316 { 317 free_llist((has_next_ptr**)pptr); 318 } 319 320 /* Returns presumed mask width in bits or < 0 on error. 321 * Updates strp, stores IP at provided pointer */ 322 static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc) 323 { 324 const char *p = *strp; 325 int auto_mask = 8; 326 unsigned ip = 0; 327 int j; 328 329 if (*p == '/') 312 330 return -auto_mask; 313 p++; 314 } 315 if (*p == '.') 316 p++; 317 if (*p != '/' && *p != 0) 318 auto_mask += 8; 319 *ip = ((*ip) << 8) | octet; 320 } 321 if (*p != 0) { 322 if (*p != endc) 323 return -auto_mask; 324 p++; 325 if(*p == 0) 326 return -auto_mask; 327 } 328 *ep = p; 329 return auto_mask; 330 } 331 332 static int 333 scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask) 334 { 335 int i; 336 unsigned int msk; 337 338 i = scan_ip(&ipm, ip, '/'); 339 if(i < 0) 340 return i; 341 if(*ipm) { 342 const char *p = ipm; 343 344 i = 0; 345 while (*p) { 346 if (*p < '0' || *p > '9') { 347 if (*p == '.') { 348 i = scan_ip (&ipm, mask, 0); 349 return i != 32; 350 } 331 332 for (j = 0; j < 4; j++) { 333 unsigned octet; 334 335 if ((*p < '0' || *p > '9') && *p != '/' && *p) 336 return -auto_mask; 337 octet = 0; 338 while (*p >= '0' && *p <= '9') { 339 octet *= 10; 340 octet += *p - '0'; 341 if (octet > 255) 342 return -auto_mask; 343 p++; 344 } 345 if (*p == '.') 346 p++; 347 if (*p != '/' && *p) 348 auto_mask += 8; 349 ip = (ip << 8) | octet; 350 } 351 if (*p) { 352 if (*p != endc) 353 return -auto_mask; 354 p++; 355 if (*p == '\0') 356 return -auto_mask; 357 } 358 *ipp = ip; 359 *strp = p; 360 return auto_mask; 361 } 362 363 /* Returns 0 on success. Stores IP and mask at provided pointers */ 364 static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp) 365 { 366 int i; 367 unsigned mask; 368 char *p; 369 370 i = scan_ip(&str, ipp, '/'); 371 if (i < 0) 372 return i; 373 374 if (*str) { 375 /* there is /xxx after dotted-IP address */ 376 i = bb_strtou(str, &p, 10); 377 if (*p == '.') { 378 /* 'xxx' itself is dotted-IP mask, parse it */ 379 /* (return 0 (success) only if it has N.N.N.N form) */ 380 return scan_ip(&str, maskp, '\0') - 32; 381 } 382 if (*p) 351 383 return -1; 352 } 353 i *= 10; 354 i += *p - '0'; 355 p++; 356 } 357 } 358 if (i > 32 || i < 0) 359 return -1; 360 msk = 0x80000000; 361 *mask = 0; 362 while (i > 0) { 363 *mask |= msk; 364 msk >>= 1; 365 i--; 366 } 367 return 0; 368 } 369 370 #if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) 371 static void free_config_lines(Htaccess **pprev) 372 { 373 Htaccess *prev = *pprev; 374 375 while( prev ) { 376 Htaccess *cur = prev; 377 378 prev = cur->next; 379 free(cur); 380 } 381 *pprev = NULL; 382 } 383 #endif 384 384 } 385 386 if (i > 32) 387 return -1; 388 389 if (sizeof(unsigned) == 4 && i == 32) { 390 /* mask >>= 32 below may not work */ 391 mask = 0; 392 } else { 393 mask = 0xffffffff; 394 mask >>= i; 395 } 396 /* i == 0 -> *maskp = 0x00000000 397 * i == 1 -> *maskp = 0x80000000 398 * i == 4 -> *maskp = 0xf0000000 399 * i == 31 -> *maskp = 0xfffffffe 400 * i == 32 -> *maskp = 0xffffffff */ 401 *maskp = (uint32_t)(~mask); 402 return 0; 403 } 404 405 /* 406 * Parse configuration file into in-memory linked list. 407 * 408 * The first non-white character is examined to determine if the config line 409 * is one of the following: 410 * .ext:mime/type # new mime type not compiled into httpd 411 * [adAD]:from # ip address allow/deny, * for wildcard 412 * /path:user:pass # username/password 413 * Ennn:error.html # error page for status nnn 414 * 415 * Any previous IP rules are discarded. 416 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules 417 * are also discarded. That is, previous settings are retained if flag is 418 * SUBDIR_PARSE. 419 * Error pages are only parsed on the main config file. 420 * 421 * path Path where to look for httpd.conf (without filename). 422 * flag Type of the parse request. 423 */ 385 424 /* flag */ 386 425 #define FIRST_PARSE 0 … … 388 427 #define SIGNALED_PARSE 2 389 428 #define FIND_FROM_HTTPD_ROOT 3 390 /****************************************************************************391 *392 > $Function: parse_conf()393 *394 * $Description: parse configuration file into in-memory linked list.395 *396 * The first non-white character is examined to determine if the config line397 * is one of the following:398 * .ext:mime/type # new mime type not compiled into httpd399 * [adAD]:from # ip address allow/deny, * for wildcard400 * /path:user:pass # username/password401 *402 * Any previous IP rules are discarded.403 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules404 * are also discarded. That is, previous settings are retained if flag is405 * SUBDIR_PARSE.406 *407 * $Parameters:408 * (const char *) path . . null for ip address checks, path for password409 * checks.410 * (int) flag . . . . . . the source of the parse request.411 *412 * $Return: (None)413 *414 ****************************************************************************/415 429 static void parse_conf(const char *path, int flag) 416 430 { 417 FILE *f; 418 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 419 Htaccess *prev, *cur; 420 #elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 421 Htaccess *cur; 422 #endif 423 424 const char *cf = config->configFile; 425 char buf[160]; 426 char *p0 = NULL; 427 char *c, *p; 428 429 /* free previous ip setup if present */ 430 Htaccess_IP *pip = config->ip_a_d; 431 432 while( pip ) { 433 Htaccess_IP *cur_ipl = pip; 434 435 pip = cur_ipl->next; 436 free(cur_ipl); 437 } 438 config->ip_a_d = NULL; 439 440 config->flg_deny_all = 0; 441 442 #if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR) 443 /* retain previous auth and mime config only for subdir parse */ 444 if(flag != SUBDIR_PARSE) { 445 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 446 free_config_lines(&config->auth); 447 #endif 448 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 449 free_config_lines(&config->mime_a); 450 #endif 451 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 452 free_config_lines(&config->script_i); 453 #endif 454 } 455 #endif 456 457 if(flag == SUBDIR_PARSE || cf == NULL) { 458 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2); 459 if(cf == NULL) { 460 if(flag == FIRST_PARSE) 461 bb_error_msg_and_die(bb_msg_memory_exhausted); 462 return; 463 } 464 sprintf((char *)cf, "%s/%s", path, httpd_conf); 465 } 466 467 while((f = fopen(cf, "r")) == NULL) { 468 if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) { 469 /* config file not found, no changes to config */ 470 return; 471 } 472 if(config->configFile && flag == FIRST_PARSE) /* if -c option given */ 473 bb_perror_msg_and_die("%s", cf); 474 flag = FIND_FROM_HTTPD_ROOT; 475 cf = httpd_conf; 476 } 477 478 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 479 prev = config->auth; 480 #endif 481 /* This could stand some work */ 482 while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) { 483 c = NULL; 484 for(p = p0; *p0 != 0 && *p0 != '#'; p0++) { 485 if(!isspace(*p0)) { 486 *p++ = *p0; 487 if(*p0 == ':' && c == NULL) 488 c = p; 489 } 490 } 491 *p = 0; 492 493 /* test for empty or strange line */ 494 if (c == NULL || *c == 0) 495 continue; 496 p0 = buf; 497 if(*p0 == 'd') 498 *p0 = 'D'; 499 if(*c == '*') { 500 if(*p0 == 'D') { 501 /* memorize deny all */ 502 config->flg_deny_all++; 503 } 504 /* skip default other "word:*" config lines */ 505 continue; 506 } 507 508 if(*p0 == 'a') 509 *p0 = 'A'; 510 else if(*p0 != 'D' && *p0 != 'A' 511 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 512 && *p0 != '/' 513 #endif 514 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 515 && *p0 != '.' 516 #endif 517 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 518 && *p0 != '*' 519 #endif 520 ) 521 continue; 522 if(*p0 == 'A' || *p0 == 'D') { 523 /* storing current config IP line */ 524 pip = calloc(1, sizeof(Htaccess_IP)); 525 if(pip) { 526 if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) { 527 /* syntax IP{/mask} error detected, protect all */ 431 FILE *f; 432 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 433 Htaccess *prev; 434 #endif 435 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ 436 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ 437 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 438 Htaccess *cur; 439 #endif 440 const char *cf = configFile; 441 char buf[160]; 442 char *p0 = NULL; 443 char *c, *p; 444 Htaccess_IP *pip; 445 446 /* discard old rules */ 447 free_Htaccess_IP_list(&ip_a_d); 448 flg_deny_all = 0; 449 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ 450 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ 451 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 452 /* retain previous auth and mime config only for subdir parse */ 453 if (flag != SUBDIR_PARSE) { 454 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 455 free_Htaccess_list(&g_auth); 456 #endif 457 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 458 free_Htaccess_list(&mime_a); 459 #endif 460 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 461 free_Htaccess_list(&script_i); 462 #endif 463 } 464 #endif 465 466 if (flag == SUBDIR_PARSE || cf == NULL) { 467 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2); 468 sprintf((char *)cf, "%s/%s", path, httpd_conf); 469 } 470 471 while ((f = fopen(cf, "r")) == NULL) { 472 if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) { 473 /* config file not found, no changes to config */ 474 return; 475 } 476 if (configFile && flag == FIRST_PARSE) /* if -c option given */ 477 bb_perror_msg_and_die("%s", cf); 478 flag = FIND_FROM_HTTPD_ROOT; 479 cf = httpd_conf; 480 } 481 482 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 483 prev = g_auth; 484 #endif 485 /* This could stand some work */ 486 while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) { 487 c = NULL; 488 for (p = p0; *p0 != '\0' && *p0 != '#'; p0++) { 489 if (!isspace(*p0)) { 490 *p++ = *p0; 491 if (*p0 == ':' && c == NULL) 492 c = p; 493 } 494 } 495 *p = '\0'; 496 497 /* test for empty or strange line */ 498 if (c == NULL || *c == '\0') 499 continue; 500 p0 = buf; 501 if (*p0 == 'd') 528 502 *p0 = 'D'; 529 pip->mask = 0; 530 } 531 pip->allow_deny = *p0; 532 if(*p0 == 'D') { 533 /* Deny:form_IP move top */ 534 pip->next = config->ip_a_d; 535 config->ip_a_d = pip; 536 } else { 537 /* add to bottom A:form_IP config line */ 538 Htaccess_IP *prev_IP = config->ip_a_d; 539 540 if(prev_IP == NULL) { 541 config->ip_a_d = pip; 542 } else { 543 while(prev_IP->next) 544 prev_IP = prev_IP->next; 545 prev_IP->next = pip; 546 } 547 } 548 } 549 continue; 550 } 551 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 552 if(*p0 == '/') { 553 /* make full path from httpd root / curent_path / config_line_path */ 554 cf = flag == SUBDIR_PARSE ? path : ""; 555 p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c)); 556 if(p0 == NULL) 557 continue; 558 c[-1] = 0; 559 sprintf(p0, "/%s%s", cf, buf); 560 561 /* another call bb_simplify_path */ 562 cf = p = p0; 563 564 do { 565 if (*p == '/') { 566 if (*cf == '/') { /* skip duplicate (or initial) slash */ 567 continue; 568 } else if (*cf == '.') { 569 if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */ 503 if (*c == '*') { 504 if (*p0 == 'D') { 505 /* memorize deny all */ 506 flg_deny_all = 1; 507 } 508 /* skip default other "word:*" config lines */ 509 continue; 510 } 511 512 if (*p0 == 'a') 513 *p0 = 'A'; 514 if (*p0 == 'A' || *p0 == 'D') { 515 /* storing current config IP line */ 516 pip = xzalloc(sizeof(Htaccess_IP)); 517 if (pip) { 518 if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) { 519 /* syntax IP{/mask} error detected, protect all */ 520 *p0 = 'D'; 521 pip->mask = 0; 522 } 523 pip->allow_deny = *p0; 524 if (*p0 == 'D') { 525 /* Deny:from_IP move top */ 526 pip->next = ip_a_d; 527 ip_a_d = pip; 528 } else { 529 /* add to bottom A:form_IP config line */ 530 Htaccess_IP *prev_IP = ip_a_d; 531 532 if (prev_IP == NULL) { 533 ip_a_d = pip; 534 } else { 535 while (prev_IP->next) 536 prev_IP = prev_IP->next; 537 prev_IP->next = pip; 538 } 539 } 540 } 541 continue; 542 } 543 544 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 545 if (flag == FIRST_PARSE && *p0 == 'E') { 546 int i; 547 /* error status code */ 548 int status = atoi(++p0); 549 /* c already points at the character following ':' in parse loop */ 550 /* c = strchr(p0, ':'); c++; */ 551 if (status < HTTP_CONTINUE) { 552 bb_error_msg("config error '%s' in '%s'", buf, cf); 570 553 continue; 571 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) { 572 ++cf; 573 if (p > p0) { 574 while (*--p != '/'); /* omit previous dir */ 575 } 576 continue; 577 } 578 } 579 } 580 *++p = *cf; 581 } while (*++cf); 582 583 if ((p == p0) || (*p != '/')) { /* not a trailing slash */ 584 ++p; /* so keep last character */ 585 } 586 *p = 0; 587 sprintf(p0, "%s:%s", p0, c); 588 } 589 #endif 590 591 #if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR) 592 /* storing current config line */ 593 cur = calloc(1, sizeof(Htaccess) + strlen(p0)); 594 if(cur) { 595 cf = strcpy(cur->before_colon, p0); 596 c = strchr(cf, ':'); 597 *c++ = 0; 598 cur->after_colon = c; 599 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 600 if(*cf == '.') { 601 /* config .mime line move top for overwrite previous */ 602 cur->next = config->mime_a; 603 config->mime_a = cur; 604 continue; 605 } 606 #endif 607 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 608 if(*cf == '*' && cf[1] == '.') { 609 /* config script interpreter line move top for overwrite previous */ 610 cur->next = config->script_i; 611 config->script_i = cur; 612 continue; 613 } 614 #endif 615 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 616 free(p0); 617 if(prev == NULL) { 618 /* first line */ 619 config->auth = prev = cur; 620 } else { 621 /* sort path, if current lenght eq or bigger then move up */ 622 Htaccess *prev_hti = config->auth; 623 size_t l = strlen(cf); 624 Htaccess *hti; 625 626 for(hti = prev_hti; hti; hti = hti->next) { 627 if(l >= strlen(hti->before_colon)) { 628 /* insert before hti */ 629 cur->next = hti; 630 if(prev_hti != hti) { 631 prev_hti->next = cur; 632 } else { 633 /* insert as top */ 634 config->auth = cur; 635 } 636 break; 637 } 638 if(prev_hti != hti) 639 prev_hti = prev_hti->next; 640 } 641 if(!hti) { /* not inserted, add to bottom */ 642 prev->next = cur; 643 prev = cur; 644 } 645 } 646 #endif 647 } 648 #endif 649 } 650 fclose(f); 651 } 652 653 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 654 /**************************************************************************** 655 * 656 > $Function: encodeString() 657 * 658 * $Description: Given a string, html encode special characters. 659 * This is used for the -e command line option to provide an easy way 660 * for scripts to encode result data without confusing browsers. The 661 * returned string pointer is memory allocated by malloc(). 662 * 663 * $Parameters: 664 * (const char *) string . . The first string to encode. 665 * 666 * $Return: (char *) . . . .. . . A pointer to the encoded string. 667 * 668 * $Errors: Returns a null string ("") if memory is not available. 669 * 670 ****************************************************************************/ 671 static char *encodeString(const char *string) 672 { 673 /* take the simple route and encode everything */ 674 /* could possibly scan once to get length. */ 675 int len = strlen(string); 676 char *out = malloc(len * 6 + 1); 677 char *p=out; 678 char ch; 679 680 if (!out) return ""; 681 while ((ch = *string++)) { 682 // very simple check for what to encode 683 if (isalnum(ch)) *p++ = ch; 684 else p += sprintf(p, "&#%d;", (unsigned char) ch); 685 } 686 *p=0; 687 return out; 688 } 689 #endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */ 690 691 /**************************************************************************** 692 * 693 > $Function: decodeString() 694 * 695 * $Description: Given a URL encoded string, convert it to plain ascii. 696 * Since decoding always makes strings smaller, the decode is done in-place. 697 * Thus, callers should strdup() the argument if they do not want the 698 * argument modified. The return is the original pointer, allowing this 699 * function to be easily used as arguments to other functions. 700 * 701 * $Parameters: 702 * (char *) string . . . The first string to decode. 703 * (int) flag . . . 1 if require decode '+' as ' ' for CGI 704 * 705 * $Return: (char *) . . . . A pointer to the decoded string (same as input). 706 * 707 * $Errors: None 708 * 709 ****************************************************************************/ 710 static char *decodeString(char *orig, int flag_plus_to_space) 711 { 712 /* note that decoded string is always shorter than original */ 713 char *string = orig; 714 char *ptr = string; 715 716 while (*ptr) 717 { 718 if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; } 719 else if (*ptr != '%') *string++ = *ptr++; 720 else { 721 unsigned int value1, value2; 722 723 ptr++; 724 if(sscanf(ptr, "%1X", &value1) != 1 || 725 sscanf(ptr+1, "%1X", &value2) != 1) { 726 if(!flag_plus_to_space) 727 return NULL; 728 *string++ = '%'; 729 } else { 730 value1 = value1 * 16 + value2; 731 if(value1 == '/' || value1 == 0) 732 return orig+1; 733 *string++ = value1; 734 ptr += 2; 735 } 736 } 737 } 738 *string = '\0'; 739 return orig; 740 } 741 742 743 #ifdef CONFIG_FEATURE_HTTPD_CGI 744 /**************************************************************************** 745 * 746 > $Function: addEnv() 747 * 748 * $Description: Add an environment variable setting to the global list. 749 * A NAME=VALUE string is allocated, filled, and added to the list of 750 * environment settings passed to the cgi execution script. 751 * 752 * $Parameters: 753 * (char *) name_before_underline - The first part environment variable name. 754 * (char *) name_after_underline - The second part environment variable name. 755 * (char *) value . . The value to which the env variable is set. 756 * 757 * $Return: (void) 758 * 759 * $Errors: Silently returns if the env runs out of space to hold the new item 760 * 761 ****************************************************************************/ 762 static void addEnv(const char *name_before_underline, 763 const char *name_after_underline, const char *value) 764 { 765 char *s = NULL; 766 const char *underline; 767 768 if (!value) 769 value = ""; 770 underline = *name_after_underline ? "_" : ""; 771 asprintf(&s, "%s%s%s=%s", name_before_underline, underline, 772 name_after_underline, value); 773 if(s) { 774 putenv(s); 775 } 776 } 777 778 #if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || defined(CONFIG_FEATURE_HTTPD_WITHOUT_INETD) 779 /* set environs SERVER_PORT and REMOTE_PORT */ 780 static void addEnvPort(const char *port_name) 781 { 782 char buf[16]; 783 784 sprintf(buf, "%u", config->port); 785 addEnv(port_name, "PORT", buf); 786 } 787 #endif 788 #endif /* CONFIG_FEATURE_HTTPD_CGI */ 789 790 791 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 792 /**************************************************************************** 793 * 794 > $Function: decodeBase64() 795 * 796 > $Description: Decode a base 64 data stream as per rfc1521. 797 * Note that the rfc states that none base64 chars are to be ignored. 798 * Since the decode always results in a shorter size than the input, it is 799 * OK to pass the input arg as an output arg. 800 * 801 * $Parameter: 802 * (char *) Data . . . . A pointer to a base64 encoded string. 803 * Where to place the decoded data. 804 * 805 * $Return: void 806 * 807 * $Errors: None 808 * 809 ****************************************************************************/ 810 static void decodeBase64(char *Data) 811 { 812 813 const unsigned char *in = (const unsigned char *)Data; 814 // The decoded size will be at most 3/4 the size of the encoded 815 unsigned long ch = 0; 816 int i = 0; 817 818 while (*in) { 819 int t = *in++; 820 821 if(t >= '0' && t <= '9') 822 t = t - '0' + 52; 823 else if(t >= 'A' && t <= 'Z') 824 t = t - 'A'; 825 else if(t >= 'a' && t <= 'z') 826 t = t - 'a' + 26; 827 else if(t == '+') 828 t = 62; 829 else if(t == '/') 830 t = 63; 831 else if(t == '=') 832 t = 0; 833 else 834 continue; 835 836 ch = (ch << 6) | t; 837 i++; 838 if (i == 4) { 839 *Data++ = (char) (ch >> 16); 840 *Data++ = (char) (ch >> 8); 841 *Data++ = (char) ch; 842 i = 0; 843 } 844 } 845 *Data = 0; 846 } 847 #endif 848 849 850 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 851 /**************************************************************************** 852 * 853 > $Function: openServer() 854 * 855 * $Description: create a listen server socket on the designated port. 856 * 857 * $Return: (int) . . . A connection socket. -1 for errors. 858 * 859 * $Errors: None 860 * 861 ****************************************************************************/ 862 static int openServer(void) 863 { 864 struct sockaddr_in lsocket; 865 int fd; 866 int on = 1; 867 868 /* create the socket right now */ 869 /* inet_addr() returns a value that is already in network order */ 870 memset(&lsocket, 0, sizeof(lsocket)); 871 lsocket.sin_family = AF_INET; 872 lsocket.sin_addr.s_addr = INADDR_ANY; 873 lsocket.sin_port = htons(config->port); 874 fd = bb_xsocket(AF_INET, SOCK_STREAM, 0); 875 /* tell the OS it's OK to reuse a previous address even though */ 876 /* it may still be in a close down state. Allows bind to succeed. */ 877 #ifdef SO_REUSEPORT 878 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ; 879 #else 880 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ; 881 #endif 882 bb_xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)); 883 listen(fd, 9); /* bb_xlisten? */ 884 signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ 885 return fd; 886 } 887 #endif /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */ 888 889 /**************************************************************************** 890 * 891 > $Function: sendHeaders() 892 * 893 * $Description: Create and send HTTP response headers. 894 * The arguments are combined and sent as one write operation. Note that 895 * IE will puke big-time if the headers are not sent in one packet and the 896 * second packet is delayed for any reason. 897 * 898 * $Parameter: 899 * (HttpResponseNum) responseNum . . . The result code to send. 900 * 901 * $Return: (int) . . . . writing errors 902 * 903 ****************************************************************************/ 904 static int sendHeaders(HttpResponseNum responseNum) 905 { 906 char *buf = config->buf; 907 const char *responseString = ""; 908 const char *infoString = 0; 909 const char *mime_type; 910 unsigned int i; 911 time_t timer = time(0); 912 char timeStr[80]; 913 int len; 914 915 for (i = 0; 916 i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) { 917 if (httpResponseNames[i].type == responseNum) { 918 responseString = httpResponseNames[i].name; 919 infoString = httpResponseNames[i].info; 920 break; 921 } 922 } 923 /* error message is HTML */ 924 mime_type = responseNum == HTTP_OK ? 925 config->httpd_found.found_mime_type : "text/html"; 926 927 /* emit the current date */ 928 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer)); 929 len = sprintf(buf, 930 "HTTP/1.0 %d %s\r\nContent-type: %s\r\n" 931 "Date: %s\r\nConnection: close\r\n", 932 responseNum, responseString, mime_type, timeStr); 933 934 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 935 if (responseNum == HTTP_UNAUTHORIZED) { 936 len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n", 937 config->realm); 938 } 939 #endif 940 if(responseNum == HTTP_MOVED_TEMPORARILY) { 941 len += sprintf(buf+len, "Location: %s/%s%s\r\n", 942 config->httpd_found.found_moved_temporarily, 943 (config->query ? "?" : ""), 944 (config->query ? config->query : "")); 945 } 946 947 if (config->ContentLength != -1) { /* file */ 948 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod)); 949 len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\r\n", 950 timeStr, Content_length, cont_l_type config->ContentLength); 951 } 952 strcat(buf, "\r\n"); 953 len += 2; 954 if (infoString) { 955 len += sprintf(buf+len, 956 "<HEAD><TITLE>%d %s</TITLE></HEAD>\n" 957 "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n", 958 responseNum, responseString, 959 responseNum, responseString, infoString); 960 } 961 #if DEBUG 962 fprintf(stderr, "Headers: '%s'", buf); 963 #endif 964 return bb_full_write(a_c_w, buf, len); 965 } 966 967 /**************************************************************************** 968 * 969 > $Function: getLine() 970 * 971 * $Description: Read from the socket until an end of line char found. 972 * 973 * Characters are read one at a time until an eol sequence is found. 974 * 975 * $Return: (int) . . . . number of characters read. -1 if error. 976 * 977 ****************************************************************************/ 978 static int getLine(void) 979 { 980 int count = 0; 981 char *buf = config->buf; 982 983 while (read(a_c_r, buf + count, 1) == 1) { 984 if (buf[count] == '\r') continue; 985 if (buf[count] == '\n') { 986 buf[count] = 0; 987 return count; 988 } 989 if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */ 990 count++; 991 } 992 if (count) return count; 993 else return -1; 994 } 995 996 #ifdef CONFIG_FEATURE_HTTPD_CGI 997 /**************************************************************************** 998 * 999 > $Function: sendCgi() 1000 * 1001 * $Description: Execute a CGI script and send it's stdout back 1002 * 1003 * Environment variables are set up and the script is invoked with pipes 1004 * for stdin/stdout. If a post is being done the script is fed the POST 1005 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). 1006 * 1007 * $Parameters: 1008 * (const char *) url . . . . . . The requested URL (with leading /). 1009 * (int bodyLen) . . . . . . . . Length of the post body. 1010 * (const char *cookie) . . . . . For set HTTP_COOKIE. 1011 * (const char *content_type) . . For set CONTENT_TYPE. 1012 1013 * 1014 * $Return: (char *) . . . . A pointer to the decoded string (same as input). 1015 * 1016 * $Errors: None 1017 * 1018 ****************************************************************************/ 1019 static int sendCgi(const char *url, 1020 const char *request, int bodyLen, const char *cookie, 1021 const char *content_type) 1022 { 1023 int fromCgi[2]; /* pipe for reading data from CGI */ 1024 int toCgi[2]; /* pipe for sending data to CGI */ 1025 1026 static char * argp[] = { 0, 0 }; 1027 int pid = 0; 1028 int inFd; 1029 int outFd; 1030 int firstLine = 1; 1031 1032 do { 1033 if (pipe(fromCgi) != 0) { 1034 break; 1035 } 1036 if (pipe(toCgi) != 0) { 1037 break; 1038 } 1039 1040 pid = fork(); 1041 if (pid < 0) { 1042 pid = 0; 1043 break; 1044 } 1045 1046 if (!pid) { 1047 /* child process */ 1048 char *script; 1049 char *purl = strdup( url ); 1050 char realpath_buff[MAXPATHLEN]; 1051 1052 if(purl == NULL) 1053 _exit(242); 1054 1055 inFd = toCgi[0]; 1056 outFd = fromCgi[1]; 1057 1058 dup2(inFd, 0); // replace stdin with the pipe 1059 dup2(outFd, 1); // replace stdout with the pipe 1060 if(!DEBUG) 1061 dup2(outFd, 2); // replace stderr with the pipe 1062 1063 close(toCgi[0]); 1064 close(toCgi[1]); 1065 close(fromCgi[0]); 1066 close(fromCgi[1]); 1067 1068 /* 1069 * Find PATH_INFO. 1070 */ 1071 script = purl; 1072 while((script = strchr( script + 1, '/' )) != NULL) { 1073 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ 1074 struct stat sb; 1075 1076 *script = '\0'; 1077 if(is_directory(purl + 1, 1, &sb) == 0) { 1078 /* not directory, found script.cgi/PATH_INFO */ 1079 *script = '/'; 1080 break; 1081 } 1082 *script = '/'; /* is directory, find next '/' */ 1083 } 1084 addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */ 1085 addEnv("PATH", "", getenv("PATH")); 1086 addEnv("REQUEST", "METHOD", request); 1087 if(config->query) { 1088 char *uri = alloca(strlen(purl) + 2 + strlen(config->query)); 1089 if(uri) 1090 sprintf(uri, "%s?%s", purl, config->query); 1091 addEnv("REQUEST", "URI", uri); 1092 } else { 1093 addEnv("REQUEST", "URI", purl); 1094 } 1095 if(script != NULL) 1096 *script = '\0'; /* reduce /PATH_INFO */ 1097 /* SCRIPT_FILENAME required by PHP in CGI mode */ 1098 if(realpath(purl + 1, realpath_buff)) 1099 addEnv("SCRIPT", "FILENAME", realpath_buff); 1100 else 1101 *realpath_buff = 0; 1102 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ 1103 addEnv("SCRIPT_NAME", "", purl); 1104 addEnv("QUERY_STRING", "", config->query); 1105 addEnv("SERVER", "SOFTWARE", httpdVersion); 1106 addEnv("SERVER", "PROTOCOL", "HTTP/1.0"); 1107 addEnv("GATEWAY_INTERFACE", "", "CGI/1.1"); 1108 addEnv("REMOTE", "ADDR", config->rmt_ip_str); 1109 #ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 1110 addEnvPort("REMOTE"); 1111 #endif 1112 if(bodyLen) { 1113 char sbl[32]; 1114 1115 sprintf(sbl, "%d", bodyLen); 1116 addEnv("CONTENT", "LENGTH", sbl); 1117 } 1118 if(cookie) 1119 addEnv("HTTP", "COOKIE", cookie); 1120 if(content_type) 1121 addEnv("CONTENT", "TYPE", content_type); 1122 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1123 if(config->remoteuser) { 1124 addEnv("REMOTE", "USER", config->remoteuser); 1125 addEnv("AUTH_TYPE", "", "Basic"); 1126 } 1127 #endif 1128 if(config->referer) 1129 addEnv("HTTP", "REFERER", config->referer); 1130 1131 /* set execve argp[0] without path */ 1132 argp[0] = strrchr( purl, '/' ) + 1; 1133 /* but script argp[0] must have absolute path and chdiring to this */ 1134 if(*realpath_buff) { 1135 script = strrchr(realpath_buff, '/'); 1136 if(script) { 1137 *script = '\0'; 1138 if(chdir(realpath_buff) == 0) { 1139 // now run the program. If it fails, 1140 // use _exit() so no destructors 1141 // get called and make a mess. 1142 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1143 char *interpr = NULL; 1144 char *suffix = strrchr(purl, '.'); 1145 1146 if(suffix) { 1147 Htaccess * cur; 1148 for (cur = config->script_i; cur; cur = cur->next) 1149 if(strcmp(cur->before_colon + 1, suffix) == 0) { 1150 interpr = cur->after_colon; 554 } 555 556 /* then error page; find matching status */ 557 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { 558 if (http_response_type[i] == status) { 559 http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c); 1151 560 break; 1152 561 } 1153 } 1154 #endif 1155 *script = '/'; 1156 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1157 if (interpr) 1158 execv(interpr, argp); 1159 else 1160 #endif 1161 execv(realpath_buff, argp); 1162 } 1163 } 1164 } 1165 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1166 config->accepted_socket = 1; /* send to stdout */ 1167 #endif 1168 sendHeaders(HTTP_NOT_FOUND); 1169 _exit(242); 1170 } /* end child */ 1171 1172 } while (0); 1173 1174 if (pid) { 1175 /* parent process */ 1176 int status; 1177 size_t post_readed_size = 0, post_readed_idx = 0; 1178 1179 inFd = fromCgi[0]; 1180 outFd = toCgi[1]; 1181 close(fromCgi[1]); 1182 close(toCgi[0]); 1183 signal(SIGPIPE, SIG_IGN); 1184 1185 while (1) { 1186 fd_set readSet; 1187 fd_set writeSet; 1188 char wbuf[128]; 1189 int nfound; 1190 int count; 1191 1192 FD_ZERO(&readSet); 1193 FD_ZERO(&writeSet); 1194 FD_SET(inFd, &readSet); 1195 if(bodyLen > 0 || post_readed_size > 0) { 1196 FD_SET(outFd, &writeSet); 1197 nfound = outFd > inFd ? outFd : inFd; 1198 if(post_readed_size == 0) { 1199 FD_SET(a_c_r, &readSet); 1200 if(nfound < a_c_r) 1201 nfound = a_c_r; 1202 } 1203 /* Now wait on the set of sockets! */ 1204 nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL); 1205 } else { 1206 if(!bodyLen) { 1207 close(outFd); 1208 bodyLen = -1; 1209 } 1210 nfound = select(inFd + 1, &readSet, 0, 0, NULL); 1211 } 1212 1213 if (nfound <= 0) { 1214 if (waitpid(pid, &status, WNOHANG) > 0) { 1215 close(inFd); 562 } 563 continue; 564 } 565 #endif 566 567 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 568 if (*p0 == '/') { 569 /* make full path from httpd root / current_path / config_line_path */ 570 cf = (flag == SUBDIR_PARSE ? path : ""); 571 p0 = xmalloc(strlen(cf) + (c - buf) + 2 + strlen(c)); 572 c[-1] = '\0'; 573 sprintf(p0, "/%s%s", cf, buf); 574 575 /* another call bb_simplify_path */ 576 cf = p = p0; 577 578 do { 579 if (*p == '/') { 580 if (*cf == '/') { /* skip duplicate (or initial) slash */ 581 continue; 582 } else if (*cf == '.') { 583 if (cf[1] == '/' || cf[1] == '\0') { /* remove extra '.' */ 584 continue; 585 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == '\0')) { 586 ++cf; 587 if (p > p0) { 588 while (*--p != '/') /* omit previous dir */; 589 } 590 continue; 591 } 592 } 593 } 594 *++p = *cf; 595 } while (*++cf); 596 597 if ((p == p0) || (*p != '/')) { /* not a trailing slash */ 598 ++p; /* so keep last character */ 599 } 600 *p = '\0'; 601 sprintf(p0 + strlen(p0), ":%s", c); 602 } 603 #endif 604 605 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ 606 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ 607 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 608 /* storing current config line */ 609 cur = xzalloc(sizeof(Htaccess) + strlen(p0)); 610 if (cur) { 611 cf = strcpy(cur->before_colon, p0); 612 c = strchr(cf, ':'); 613 *c++ = 0; 614 cur->after_colon = c; 615 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 616 if (*cf == '.') { 617 /* config .mime line move top for overwrite previous */ 618 cur->next = mime_a; 619 mime_a = cur; 620 continue; 621 } 622 #endif 623 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 624 if (*cf == '*' && cf[1] == '.') { 625 /* config script interpreter line move top for overwrite previous */ 626 cur->next = script_i; 627 script_i = cur; 628 continue; 629 } 630 #endif 631 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 632 free(p0); 633 if (prev == NULL) { 634 /* first line */ 635 g_auth = prev = cur; 636 } else { 637 /* sort path, if current lenght eq or bigger then move up */ 638 Htaccess *prev_hti = g_auth; 639 size_t l = strlen(cf); 640 Htaccess *hti; 641 642 for (hti = prev_hti; hti; hti = hti->next) { 643 if (l >= strlen(hti->before_colon)) { 644 /* insert before hti */ 645 cur->next = hti; 646 if (prev_hti != hti) { 647 prev_hti->next = cur; 648 } else { 649 /* insert as top */ 650 g_auth = cur; 651 } 652 break; 653 } 654 if (prev_hti != hti) 655 prev_hti = prev_hti->next; 656 } 657 if (!hti) { /* not inserted, add to bottom */ 658 prev->next = cur; 659 prev = cur; 660 } 661 } 662 #endif 663 } 664 #endif 665 } 666 fclose(f); 667 } 668 669 #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR 670 /* 671 * Given a string, html-encode special characters. 672 * This is used for the -e command line option to provide an easy way 673 * for scripts to encode result data without confusing browsers. The 674 * returned string pointer is memory allocated by malloc(). 675 * 676 * Returns a pointer to the encoded string (malloced). 677 */ 678 static char *encodeString(const char *string) 679 { 680 /* take the simple route and encode everything */ 681 /* could possibly scan once to get length. */ 682 int len = strlen(string); 683 char *out = xmalloc(len * 6 + 1); 684 char *p = out; 685 char ch; 686 687 while ((ch = *string++)) { 688 /* very simple check for what to encode */ 689 if (isalnum(ch)) 690 *p++ = ch; 691 else 692 p += sprintf(p, "&#%d;", (unsigned char) ch); 693 } 694 *p = '\0'; 695 return out; 696 } 697 #endif /* FEATURE_HTTPD_ENCODE_URL_STR */ 698 699 /* 700 * Given a URL encoded string, convert it to plain ascii. 701 * Since decoding always makes strings smaller, the decode is done in-place. 702 * Thus, callers should strdup() the argument if they do not want the 703 * argument modified. The return is the original pointer, allowing this 704 * function to be easily used as arguments to other functions. 705 * 706 * string The first string to decode. 707 * option_d 1 if called for httpd -d 708 * 709 * Returns a pointer to the decoded string (same as input). 710 */ 711 static unsigned hex_to_bin(unsigned char c) 712 { 713 unsigned v; 714 715 v = c - '0'; 716 if (v <= 9) 717 return v; 718 /* c | 0x20: letters to lower case, non-letters 719 * to (potentially different) non-letters */ 720 v = (unsigned)(c | 0x20) - 'a'; 721 if (v <= 5) 722 return v + 10; 723 return ~0; 724 } 725 /* For testing: 726 void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); } 727 int main() { t(0x10); t(0x20); t('0'); t('9'); t('A'); t('F'); t('a'); t('f'); 728 t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; } 729 */ 730 static char *decodeString(char *orig, int option_d) 731 { 732 /* note that decoded string is always shorter than original */ 733 char *string = orig; 734 char *ptr = string; 735 char c; 736 737 while ((c = *ptr++) != '\0') { 738 unsigned v; 739 740 if (option_d && c == '+') { 741 *string++ = ' '; 742 continue; 743 } 744 if (c != '%') { 745 *string++ = c; 746 continue; 747 } 748 v = hex_to_bin(ptr[0]); 749 if (v > 15) { 750 bad_hex: 751 if (!option_d) 752 return NULL; 753 *string++ = '%'; 754 continue; 755 } 756 v = (v * 16) | hex_to_bin(ptr[1]); 757 if (v > 255) 758 goto bad_hex; 759 if (!option_d && (v == '/' || v == '\0')) { 760 /* caller takes it as indication of invalid 761 * (dangerous wrt exploits) chars */ 762 return orig + 1; 763 } 764 *string++ = v; 765 ptr += 2; 766 } 767 *string = '\0'; 768 return orig; 769 } 770 771 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 772 /* 773 * Decode a base64 data stream as per rfc1521. 774 * Note that the rfc states that non base64 chars are to be ignored. 775 * Since the decode always results in a shorter size than the input, 776 * it is OK to pass the input arg as an output arg. 777 * Parameter: a pointer to a base64 encoded string. 778 * Decoded data is stored in-place. 779 */ 780 static void decodeBase64(char *Data) 781 { 782 const unsigned char *in = (const unsigned char *)Data; 783 /* The decoded size will be at most 3/4 the size of the encoded */ 784 unsigned ch = 0; 785 int i = 0; 786 787 while (*in) { 788 int t = *in++; 789 790 if (t >= '0' && t <= '9') 791 t = t - '0' + 52; 792 else if (t >= 'A' && t <= 'Z') 793 t = t - 'A'; 794 else if (t >= 'a' && t <= 'z') 795 t = t - 'a' + 26; 796 else if (t == '+') 797 t = 62; 798 else if (t == '/') 799 t = 63; 800 else if (t == '=') 801 t = 0; 802 else 803 continue; 804 805 ch = (ch << 6) | t; 806 i++; 807 if (i == 4) { 808 *Data++ = (char) (ch >> 16); 809 *Data++ = (char) (ch >> 8); 810 *Data++ = (char) ch; 811 i = 0; 812 } 813 } 814 *Data = '\0'; 815 } 816 #endif 817 818 /* 819 * Create a listen server socket on the designated port. 820 */ 821 static int openServer(void) 822 { 823 int n = bb_strtou(bind_addr_or_port, NULL, 10); 824 if (!errno && n && n <= 0xffff) 825 n = create_and_bind_stream_or_die(NULL, n); 826 else 827 n = create_and_bind_stream_or_die(bind_addr_or_port, 80); 828 xlisten(n, 9); 829 return n; 830 } 831 832 /* 833 * Log the connection closure and exit. 834 */ 835 static void log_and_exit(void) ATTRIBUTE_NORETURN; 836 static void log_and_exit(void) 837 { 838 /* Paranoia. IE said to be buggy. It may send some extra data 839 * or be confused by us just exiting without SHUT_WR. Oh well. */ 840 shutdown(1, SHUT_WR); 841 ndelay_on(0); 842 while (read(0, iobuf, IOBUF_SIZE) > 0) 843 continue; 844 845 if (verbose > 2) 846 bb_error_msg("closed"); 847 _exit(xfunc_error_retval); 848 } 849 850 /* 851 * Create and send HTTP response headers. 852 * The arguments are combined and sent as one write operation. Note that 853 * IE will puke big-time if the headers are not sent in one packet and the 854 * second packet is delayed for any reason. 855 * responseNum - the result code to send. 856 */ 857 static void send_headers(int responseNum) 858 { 859 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; 860 861 const char *responseString = ""; 862 const char *infoString = NULL; 863 const char *mime_type; 864 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 865 const char *error_page = 0; 866 #endif 867 unsigned i; 868 time_t timer = time(0); 869 char tmp_str[80]; 870 int len; 871 872 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { 873 if (http_response_type[i] == responseNum) { 874 responseString = http_response[i].name; 875 infoString = http_response[i].info; 876 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 877 error_page = http_error_page[i]; 878 #endif 879 break; 880 } 881 } 882 /* error message is HTML */ 883 mime_type = responseNum == HTTP_OK ? 884 found_mime_type : "text/html"; 885 886 if (verbose) 887 bb_error_msg("response:%u", responseNum); 888 889 /* emit the current date */ 890 strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&timer)); 891 len = sprintf(iobuf, 892 "HTTP/1.0 %d %s\r\nContent-type: %s\r\n" 893 "Date: %s\r\nConnection: close\r\n", 894 responseNum, responseString, mime_type, tmp_str); 895 896 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 897 if (responseNum == HTTP_UNAUTHORIZED) { 898 len += sprintf(iobuf + len, 899 "WWW-Authenticate: Basic realm=\"%s\"\r\n", 900 g_realm); 901 } 902 #endif 903 if (responseNum == HTTP_MOVED_TEMPORARILY) { 904 len += sprintf(iobuf + len, "Location: %s/%s%s\r\n", 905 found_moved_temporarily, 906 (g_query ? "?" : ""), 907 (g_query ? g_query : "")); 908 } 909 910 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 911 if (error_page && !access(error_page, R_OK)) { 912 strcat(iobuf, "\r\n"); 913 len += 2; 914 915 if (DEBUG) 916 fprintf(stderr, "headers: '%s'\n", iobuf); 917 full_write(1, iobuf, len); 918 if (DEBUG) 919 fprintf(stderr, "writing error page: '%s'\n", error_page); 920 return send_file_and_exit(error_page, FALSE); 921 } 922 #endif 923 924 if (ContentLength != -1) { /* file */ 925 strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); 926 len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", 927 tmp_str, "Content-length:", ContentLength); 928 } 929 iobuf[len++] = '\r'; 930 iobuf[len++] = '\n'; 931 if (infoString) { 932 len += sprintf(iobuf + len, 933 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n" 934 "<BODY><H1>%d %s</H1>\n%s\n</BODY></HTML>\n", 935 responseNum, responseString, 936 responseNum, responseString, infoString); 937 } 938 if (DEBUG) 939 fprintf(stderr, "headers: '%s'\n", iobuf); 940 if (full_write(1, iobuf, len) != len) { 941 if (verbose > 1) 942 bb_perror_msg("error"); 943 log_and_exit(); 944 } 945 } 946 947 static void send_headers_and_exit(int responseNum) ATTRIBUTE_NORETURN; 948 static void send_headers_and_exit(int responseNum) 949 { 950 send_headers(responseNum); 951 log_and_exit(); 952 } 953 954 /* 955 * Read from the socket until '\n' or EOF. '\r' chars are removed. 956 * '\n' is replaced with NUL. 957 * Return number of characters read or 0 if nothing is read 958 * ('\r' and '\n' are not counted). 959 * Data is returned in iobuf. 960 */ 961 static int get_line(void) 962 { 963 int count = 0; 964 char c; 965 966 while (1) { 967 if (hdr_cnt <= 0) { 968 hdr_cnt = safe_read(0, hdr_buf, sizeof(hdr_buf)); 969 if (hdr_cnt <= 0) 970 break; 971 hdr_ptr = hdr_buf; 972 } 973 iobuf[count] = c = *hdr_ptr++; 974 hdr_cnt--; 975 976 if (c == '\r') 977 continue; 978 if (c == '\n') { 979 iobuf[count] = '\0'; 980 return count; 981 } 982 if (count < (IOBUF_SIZE - 1)) /* check overflow */ 983 count++; 984 } 985 return count; 986 } 987 988 #if ENABLE_FEATURE_HTTPD_CGI 989 static void setenv1(const char *name, const char *value) 990 { 991 setenv(name, value ? value : "", 1); 992 } 993 994 /* 995 * Spawn CGI script, forward CGI's stdin/out <=> network 996 * 997 * Environment variables are set up and the script is invoked with pipes 998 * for stdin/stdout. If a post is being done the script is fed the POST 999 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). 1000 * 1001 * Parameters: 1002 * const char *url The requested URL (with leading /). 1003 * int bodyLen Length of the post body. 1004 * const char *cookie For set HTTP_COOKIE. 1005 * const char *content_type For set CONTENT_TYPE. 1006 */ 1007 static void send_cgi_and_exit( 1008 const char *url, 1009 const char *request, 1010 int bodyLen, 1011 const char *cookie, 1012 const char *content_type) ATTRIBUTE_NORETURN; 1013 static void send_cgi_and_exit( 1014 const char *url, 1015 const char *request, 1016 int bodyLen, 1017 const char *cookie, 1018 const char *content_type) 1019 { 1020 struct { int rd; int wr; } fromCgi; /* CGI -> httpd pipe */ 1021 struct { int rd; int wr; } toCgi; /* httpd -> CGI pipe */ 1022 char *fullpath; 1023 char *script; 1024 char *purl; 1025 int buf_count; 1026 int status; 1027 int pid = 0; 1028 1029 /* 1030 * We are mucking with environment _first_ and then vfork/exec, 1031 * this allows us to use vfork safely. Parent don't care about 1032 * these environment changes anyway. 1033 */ 1034 1035 /* 1036 * Find PATH_INFO. 1037 */ 1038 purl = xstrdup(url); 1039 script = purl; 1040 while ((script = strchr(script + 1, '/')) != NULL) { 1041 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ 1042 struct stat sb; 1043 1044 *script = '\0'; 1045 if (!is_directory(purl + 1, 1, &sb)) { 1046 /* not directory, found script.cgi/PATH_INFO */ 1047 *script = '/'; 1048 break; 1049 } 1050 *script = '/'; /* is directory, find next '/' */ 1051 } 1052 setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */ 1053 setenv1("REQUEST_METHOD", request); 1054 if (g_query) { 1055 putenv(xasprintf("%s=%s?%s", "REQUEST_URI", purl, g_query)); 1056 } else { 1057 setenv1("REQUEST_URI", purl); 1058 } 1059 if (script != NULL) 1060 *script = '\0'; /* cut off /PATH_INFO */ 1061 1062 /* SCRIPT_FILENAME required by PHP in CGI mode */ 1063 fullpath = concat_path_file(home_httpd, purl); 1064 setenv1("SCRIPT_FILENAME", fullpath); 1065 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ 1066 setenv1("SCRIPT_NAME", purl); 1067 /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: 1068 * QUERY_STRING: The information which follows the ? in the URL 1069 * which referenced this script. This is the query information. 1070 * It should not be decoded in any fashion. This variable 1071 * should always be set when there is query information, 1072 * regardless of command line decoding. */ 1073 /* (Older versions of bbox seem to do some decoding) */ 1074 setenv1("QUERY_STRING", g_query); 1075 putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER); 1076 putenv((char*)"SERVER_PROTOCOL=HTTP/1.0"); 1077 putenv((char*)"GATEWAY_INTERFACE=CGI/1.1"); 1078 /* Having _separate_ variables for IP and port defeats 1079 * the purpose of having socket abstraction. Which "port" 1080 * are you using on Unix domain socket? 1081 * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense. 1082 * Oh well... */ 1083 { 1084 char *p = rmt_ip_str ? rmt_ip_str : (char*)""; 1085 char *cp = strrchr(p, ':'); 1086 if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']')) 1087 cp = NULL; 1088 if (cp) *cp = '\0'; /* delete :PORT */ 1089 setenv1("REMOTE_ADDR", p); 1090 if (cp) { 1091 *cp = ':'; 1092 #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 1093 setenv1("REMOTE_PORT", cp + 1); 1094 #endif 1095 } 1096 } 1097 setenv1("HTTP_USER_AGENT", user_agent); 1098 if (bodyLen) 1099 putenv(xasprintf("CONTENT_LENGTH=%d", bodyLen)); 1100 if (cookie) 1101 setenv1("HTTP_COOKIE", cookie); 1102 if (content_type) 1103 setenv1("CONTENT_TYPE", content_type); 1104 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1105 if (remoteuser) { 1106 setenv1("REMOTE_USER", remoteuser); 1107 putenv((char*)"AUTH_TYPE=Basic"); 1108 } 1109 #endif 1110 if (referer) 1111 setenv1("HTTP_REFERER", referer); 1112 1113 xpipe(&fromCgi.rd); 1114 xpipe(&toCgi.rd); 1115 1116 pid = vfork(); 1117 if (pid < 0) { 1118 /* TODO: log perror? */ 1119 log_and_exit(); 1120 } 1121 1122 if (!pid) { 1123 /* Child process */ 1124 xfunc_error_retval = 242; 1125 1126 xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */ 1127 xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */ 1128 close(fromCgi.rd); 1129 close(toCgi.wr); 1130 /* User seeing stderr output can be a security problem. 1131 * If CGI really wants that, it can always do dup itself. */ 1132 /* dup2(1, 2); */ 1133 1134 /* script must have absolute path */ 1135 script = strrchr(fullpath, '/'); 1136 if (!script) 1137 goto error_execing_cgi; 1138 *script = '\0'; 1139 /* chdiring to script's dir */ 1140 if (chdir(fullpath) == 0) { 1141 char *argv[2]; 1142 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1143 char *interpr = NULL; 1144 char *suffix = strrchr(purl, '.'); 1145 1146 if (suffix) { 1147 Htaccess *cur; 1148 for (cur = script_i; cur; cur = cur->next) { 1149 if (strcmp(cur->before_colon + 1, suffix) == 0) { 1150 interpr = cur->after_colon; 1151 break; 1152 } 1153 } 1154 } 1155 #endif 1156 *script = '/'; 1157 /* set argv[0] to name without path */ 1158 argv[0] = (char*)bb_basename(purl); 1159 argv[1] = NULL; 1160 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1161 if (interpr) 1162 execv(interpr, argv); 1163 else 1164 #endif 1165 execv(fullpath, argv); 1166 } 1167 error_execing_cgi: 1168 /* send to stdout 1169 * (we are CGI here, our stdout is pumped to the net) */ 1170 send_headers_and_exit(HTTP_NOT_FOUND); 1171 } /* end child */ 1172 1173 /* Parent process */ 1174 1175 /* First, restore variables possibly changed by child */ 1176 xfunc_error_retval = 0; 1177 1178 /* Prepare for pumping data. 1179 * iobuf is used for CGI -> network data, 1180 * hdr_buf is for network -> CGI data (POSTDATA) */ 1181 buf_count = 0; 1182 close(fromCgi.wr); 1183 close(toCgi.rd); 1184 1185 /* If CGI dies, we still want to correctly finish reading its output 1186 * and send it to the peer. So please no SIGPIPEs! */ 1187 signal(SIGPIPE, SIG_IGN); 1188 1189 /* This loop still looks messy. What is an exit criteria? 1190 * "CGI's output closed"? Or "CGI has exited"? 1191 * What to do if CGI has closed both input and output, but 1192 * didn't exit? etc... */ 1193 1194 /* NB: breaking out of this loop jumps to log_and_exit() */ 1195 while (1) { 1196 fd_set readSet; 1197 fd_set writeSet; 1198 int nfound; 1199 int count; 1200 1201 FD_ZERO(&readSet); 1202 FD_ZERO(&writeSet); 1203 FD_SET(fromCgi.rd, &readSet); 1204 if (bodyLen > 0 || hdr_cnt > 0) { 1205 FD_SET(toCgi.wr, &writeSet); 1206 nfound = toCgi.wr > fromCgi.rd ? toCgi.wr : fromCgi.rd; 1207 if (hdr_cnt <= 0) 1208 FD_SET(0, &readSet); 1209 /* Now wait on the set of sockets! */ 1210 nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL); 1211 } else { 1212 if (!bodyLen) { 1213 close(toCgi.wr); /* no more POST data to CGI */ 1214 bodyLen = -1; 1215 } 1216 nfound = select(fromCgi.rd + 1, &readSet, NULL, NULL, NULL); 1217 } 1218 1219 if (nfound <= 0) { 1220 if (waitpid(pid, &status, WNOHANG) <= 0) { 1221 /* Weird. CGI didn't exit and no fd's 1222 * are ready, yet select returned?! */ 1223 continue; 1224 } 1225 close(fromCgi.rd); 1226 if (DEBUG && WIFEXITED(status)) 1227 bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status)); 1228 if (DEBUG && WIFSIGNALED(status)) 1229 bb_error_msg("CGI killed, signal=%d", WTERMSIG(status)); 1230 break; 1231 } 1232 1233 if (hdr_cnt > 0 && FD_ISSET(toCgi.wr, &writeSet)) { 1234 /* Have data from peer and can write to CGI */ 1235 count = safe_write(toCgi.wr, hdr_ptr, hdr_cnt); 1236 /* Doesn't happen, we dont use nonblocking IO here 1237 *if (count < 0 && errno == EAGAIN) { 1238 * ... 1239 *} else */ 1240 if (count > 0) { 1241 hdr_ptr += count; 1242 hdr_cnt -= count; 1243 } else { 1244 hdr_cnt = bodyLen = 0; /* EOF/broken pipe to CGI */ 1245 } 1246 } else if (bodyLen > 0 && hdr_cnt == 0 1247 && FD_ISSET(0, &readSet) 1248 ) { 1249 /* We expect data, prev data portion is eaten by CGI 1250 * and there *is* data to read from the peer 1251 * (POSTDATA?) */ 1252 count = bodyLen > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : bodyLen; 1253 count = safe_read(0, hdr_buf, count); 1254 if (count > 0) { 1255 hdr_cnt = count; 1256 hdr_ptr = hdr_buf; 1257 bodyLen -= count; 1258 } else { 1259 bodyLen = 0; /* closed */ 1260 } 1261 } 1262 1263 #define PIPESIZE PIPE_BUF 1264 #if PIPESIZE >= IOBUF_SIZE 1265 # error "PIPESIZE >= IOBUF_SIZE" 1266 #endif 1267 if (FD_ISSET(fromCgi.rd, &readSet)) { 1268 /* There is something to read from CGI */ 1269 char *rbuf = iobuf; 1270 1271 /* Are we still buffering CGI output? */ 1272 if (buf_count >= 0) { 1273 /* HTTP_200[] has single "\r\n" at the end. 1274 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html, 1275 * CGI scripts MUST send their own header terminated by 1276 * empty line, then data. That's why we have only one 1277 * <cr><lf> pair here. We will output "200 OK" line 1278 * if needed, but CGI still has to provide blank line 1279 * between header and body */ 1280 1281 /* Must use safe_read, not full_read, because 1282 * CGI may output a few first bytes and then wait 1283 * for POSTDATA without closing stdout. 1284 * With full_read we may wait here forever. */ 1285 count = safe_read(fromCgi.rd, rbuf + buf_count, PIPESIZE - 8); 1286 if (count <= 0) { 1287 /* eof (or error) and there was no "HTTP", 1288 * so write it, then write received data */ 1289 if (buf_count) { 1290 full_write(1, HTTP_200, sizeof(HTTP_200)-1); 1291 full_write(1, rbuf, buf_count); 1292 } 1293 break; /* CGI stdout is closed, exiting */ 1294 } 1295 buf_count += count; 1296 count = 0; 1297 /* "Status" header format is: "Status: 302 Redirected\r\n" */ 1298 if (buf_count >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { 1299 /* send "HTTP/1.0 " */ 1300 if (full_write(1, HTTP_200, 9) != 9) 1301 break; 1302 rbuf += 8; /* skip "Status: " */ 1303 count = buf_count - 8; 1304 buf_count = -1; /* buffering off */ 1305 } else if (buf_count >= 4) { 1306 /* Did CGI add "HTTP"? */ 1307 if (memcmp(rbuf, HTTP_200, 4) != 0) { 1308 /* there is no "HTTP", do it ourself */ 1309 if (full_write(1, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) 1310 break; 1311 } 1312 /* Commented out: 1313 if (!strstr(rbuf, "ontent-")) { 1314 full_write(s, "Content-type: text/plain\r\n\r\n", 28); 1315 } 1316 * Counter-example of valid CGI without Content-type: 1317 * echo -en "HTTP/1.0 302 Found\r\n" 1318 * echo -en "Location: http://www.busybox.net\r\n" 1319 * echo -en "\r\n" 1320 */ 1321 count = buf_count; 1322 buf_count = -1; /* buffering off */ 1323 } 1324 } else { 1325 count = safe_read(fromCgi.rd, rbuf, PIPESIZE); 1326 if (count <= 0) 1327 break; /* eof (or error) */ 1328 } 1329 if (full_write(1, rbuf, count) != count) 1330 break; 1331 if (DEBUG) 1332 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf); 1333 } /* if (FD_ISSET(fromCgi.rd)) */ 1334 } /* while (1) */ 1335 log_and_exit(); 1336 } 1337 #endif /* FEATURE_HTTPD_CGI */ 1338 1339 /* 1340 * Send a file response to a HTTP request, and exit 1341 * 1342 * Parameters: 1343 * const char *url The requested URL (with leading /). 1344 * headers Don't send headers before if FALSE. 1345 */ 1346 static void send_file_and_exit(const char *url, int headers) 1347 { 1348 static const char *const suffixTable[] = { 1349 /* Warning: shorter equivalent suffix in one line must be first */ 1350 ".htm.html", "text/html", 1351 ".jpg.jpeg", "image/jpeg", 1352 ".gif", "image/gif", 1353 ".png", "image/png", 1354 ".txt.h.c.cc.cpp", "text/plain", 1355 ".css", "text/css", 1356 ".wav", "audio/wav", 1357 ".avi", "video/x-msvideo", 1358 ".qt.mov", "video/quicktime", 1359 ".mpe.mpeg", "video/mpeg", 1360 ".mid.midi", "audio/midi", 1361 ".mp3", "audio/mpeg", 1362 #if 0 /* unpopular */ 1363 ".au", "audio/basic", 1364 ".pac", "application/x-ns-proxy-autoconfig", 1365 ".vrml.wrl", "model/vrml", 1366 #endif 1367 NULL 1368 }; 1369 1370 char *suffix; 1371 int f; 1372 const char *const *table; 1373 const char *try_suffix; 1374 ssize_t count; 1375 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 1376 off_t offset = 0; 1377 #endif 1378 1379 suffix = strrchr(url, '.'); 1380 1381 /* If not found, set default as "application/octet-stream"; */ 1382 found_mime_type = "application/octet-stream"; 1383 if (suffix) { 1384 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 1385 Htaccess *cur; 1386 #endif 1387 for (table = suffixTable; *table; table += 2) { 1388 try_suffix = strstr(table[0], suffix); 1389 if (try_suffix) { 1390 try_suffix += strlen(suffix); 1391 if (*try_suffix == '\0' || *try_suffix == '.') { 1392 found_mime_type = table[1]; 1393 break; 1394 } 1395 } 1396 } 1397 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 1398 for (cur = mime_a; cur; cur = cur->next) { 1399 if (strcmp(cur->before_colon, suffix) == 0) { 1400 found_mime_type = cur->after_colon; 1401 break; 1402 } 1403 } 1404 #endif 1405 } 1406 1407 if (DEBUG) 1408 bb_error_msg("sending file '%s' content-type: %s", 1409 url, found_mime_type); 1410 1411 f = open(url, O_RDONLY); 1412 if (f < 0) { 1413 if (DEBUG) 1414 bb_perror_msg("cannot open '%s'", url); 1415 if (headers) 1416 send_headers_and_exit(HTTP_NOT_FOUND); 1417 } 1418 1419 if (headers) 1420 send_headers(HTTP_OK); 1421 1422 /* If you want to know about EPIPE below 1423 * (happens if you abort downloads from local httpd): */ 1424 signal(SIGPIPE, SIG_IGN); 1425 1426 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 1427 do { 1428 /* byte count (3rd arg) is rounded down to 64k */ 1429 count = sendfile(1, f, &offset, MAXINT(ssize_t) - 0xffff); 1430 if (count < 0) { 1431 if (offset == 0) 1432 goto fallback; 1433 goto fin; 1434 } 1435 } while (count > 0); 1436 log_and_exit(); 1437 1438 fallback: 1439 #endif 1440 while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) { 1441 ssize_t n = count; 1442 count = full_write(1, iobuf, count); 1443 if (count != n) 1444 break; 1445 } 1446 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 1447 fin: 1448 #endif 1449 if (count < 0 && verbose > 1) 1450 bb_perror_msg("error"); 1451 log_and_exit(); 1452 } 1453 1454 static int checkPermIP(void) 1455 { 1456 Htaccess_IP *cur; 1457 1458 /* This could stand some work */ 1459 for (cur = ip_a_d; cur; cur = cur->next) { 1216 1460 #if DEBUG 1217 if (WIFEXITED(status)) 1218 bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status)); 1219 if (WIFSIGNALED(status)) 1220 bb_error_msg("piped has exited with signal=%d", WTERMSIG(status)); 1221 #endif 1222 break; 1223 } 1224 } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) { 1225 count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size); 1226 if(count > 0) { 1227 post_readed_size -= count; 1228 post_readed_idx += count; 1229 if(post_readed_size == 0) 1230 post_readed_idx = 0; 1231 } else { 1232 post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */ 1233 } 1234 } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) { 1235 count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen; 1236 count = safe_read(a_c_r, wbuf, count); 1237 if(count > 0) { 1238 post_readed_size += count; 1239 bodyLen -= count; 1240 } else { 1241 bodyLen = 0; /* closed */ 1242 } 1243 } 1244 if(FD_ISSET(inFd, &readSet)) { 1245 int s = a_c_w; 1246 char *rbuf = config->buf; 1247 1248 #ifndef PIPE_BUF 1249 # define PIPESIZE 4096 /* amount of buffering in a pipe */ 1461 fprintf(stderr, 1462 "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n", 1463 rmt_ip_str, 1464 (unsigned char)(cur->ip >> 24), 1465 (unsigned char)(cur->ip >> 16), 1466 (unsigned char)(cur->ip >> 8), 1467 (unsigned char)(cur->ip), 1468 (unsigned char)(cur->mask >> 24), 1469 (unsigned char)(cur->mask >> 16), 1470 (unsigned char)(cur->mask >> 8), 1471 (unsigned char)(cur->mask) 1472 ); 1473 #endif 1474 if ((rmt_ip & cur->mask) == cur->ip) 1475 return cur->allow_deny == 'A'; /* Allow/Deny */ 1476 } 1477 1478 /* if unconfigured, return 1 - access from all */ 1479 return !flg_deny_all; 1480 } 1481 1482 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1483 /* 1484 * Check the permission file for access password protected. 1485 * 1486 * If config file isn't present, everything is allowed. 1487 * Entries are of the form you can see example from header source 1488 * 1489 * path The file path. 1490 * request User information to validate. 1491 * 1492 * Returns 1 if request is OK. 1493 */ 1494 static int checkPerm(const char *path, const char *request) 1495 { 1496 Htaccess *cur; 1497 const char *p; 1498 const char *p0; 1499 1500 const char *prev = NULL; 1501 1502 /* This could stand some work */ 1503 for (cur = g_auth; cur; cur = cur->next) { 1504 size_t l; 1505 1506 p0 = cur->before_colon; 1507 if (prev != NULL && strcmp(prev, p0) != 0) 1508 continue; /* find next identical */ 1509 p = cur->after_colon; 1510 if (DEBUG) 1511 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request); 1512 1513 l = strlen(p0); 1514 if (strncmp(p0, path, l) == 0 1515 && (l == 1 || path[l] == '/' || path[l] == '\0') 1516 ) { 1517 char *u; 1518 /* path match found. Check request */ 1519 /* for check next /path:user:password */ 1520 prev = p0; 1521 u = strchr(request, ':'); 1522 if (u == NULL) { 1523 /* bad request, ':' required */ 1524 break; 1525 } 1526 1527 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { 1528 char *cipher; 1529 char *pp; 1530 1531 if (strncmp(p, request, u - request) != 0) { 1532 /* user doesn't match */ 1533 continue; 1534 } 1535 pp = strchr(p, ':'); 1536 if (pp && pp[1] == '$' && pp[2] == '1' 1537 && pp[3] == '$' && pp[4] 1538 ) { 1539 pp++; 1540 cipher = pw_encrypt(u+1, pp); 1541 if (strcmp(cipher, pp) == 0) 1542 goto set_remoteuser_var; /* Ok */ 1543 /* unauthorized */ 1544 continue; 1545 } 1546 } 1547 1548 if (strcmp(p, request) == 0) { 1549 set_remoteuser_var: 1550 remoteuser = strdup(request); 1551 if (remoteuser) 1552 remoteuser[u - request] = '\0'; 1553 return 1; /* Ok */ 1554 } 1555 /* unauthorized */ 1556 } 1557 } /* for */ 1558 1559 return prev == NULL; 1560 } 1561 #endif /* FEATURE_HTTPD_BASIC_AUTH */ 1562 1563 /* 1564 * Handle timeouts 1565 */ 1566 static void exit_on_signal(int sig) ATTRIBUTE_NORETURN; 1567 static void exit_on_signal(int sig) 1568 { 1569 send_headers_and_exit(HTTP_REQUEST_TIMEOUT); 1570 } 1571 1572 /* 1573 * Handle an incoming http request and exit. 1574 */ 1575 static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) ATTRIBUTE_NORETURN; 1576 static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) 1577 { 1578 static const char request_GET[] ALIGN1 = "GET"; 1579 1580 struct stat sb; 1581 char *urlcopy; 1582 char *urlp; 1583 char *tptr; 1584 int http_major_version; 1585 int ip_allowed; 1586 #if ENABLE_FEATURE_HTTPD_CGI 1587 const char *prequest; 1588 unsigned long length = 0; 1589 char *cookie = 0; 1590 char *content_type = 0; 1591 #endif 1592 struct sigaction sa; 1593 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1594 int credentials = -1; /* if not required this is Ok */ 1595 #endif 1596 1597 /* Allocation of iobuf is postponed until now 1598 * (IOW, server process doesn't need to waste 8k) */ 1599 iobuf = xmalloc(IOBUF_SIZE); 1600 1601 rmt_ip = 0; 1602 if (fromAddr->sa.sa_family == AF_INET) { 1603 rmt_ip = ntohl(fromAddr->sin.sin_addr.s_addr); 1604 } 1605 #if ENABLE_FEATURE_IPV6 1606 if (fromAddr->sa.sa_family == AF_INET6 1607 && fromAddr->sin6.sin6_addr.s6_addr32[0] == 0 1608 && fromAddr->sin6.sin6_addr.s6_addr32[1] == 0 1609 && ntohl(fromAddr->sin6.sin6_addr.s6_addr32[2]) == 0xffff) 1610 rmt_ip = ntohl(fromAddr->sin6.sin6_addr.s6_addr32[3]); 1611 #endif 1612 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) { 1613 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->sa); 1614 } 1615 if (verbose) { 1616 /* this trick makes -v logging much simpler */ 1617 applet_name = rmt_ip_str; 1618 if (verbose > 2) 1619 bb_error_msg("connected"); 1620 } 1621 1622 /* Install timeout handler */ 1623 memset(&sa, 0, sizeof(sa)); 1624 sa.sa_handler = exit_on_signal; 1625 /* sigemptyset(&sa.sa_mask); - memset should be enough */ 1626 /*sa.sa_flags = 0; - no SA_RESTART */ 1627 sigaction(SIGALRM, &sa, NULL); 1628 alarm(HEADER_READ_TIMEOUT); 1629 1630 if (!get_line()) /* EOF or error or empty line */ 1631 send_headers_and_exit(HTTP_BAD_REQUEST); 1632 1633 /* Determine type of request (GET/POST) */ 1634 urlp = strpbrk(iobuf, " \t"); 1635 if (urlp == NULL) 1636 send_headers_and_exit(HTTP_BAD_REQUEST); 1637 *urlp++ = '\0'; 1638 #if ENABLE_FEATURE_HTTPD_CGI 1639 prequest = request_GET; 1640 if (strcasecmp(iobuf, prequest) != 0) { 1641 prequest = "POST"; 1642 if (strcasecmp(iobuf, prequest) != 0) 1643 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 1644 } 1250 1645 #else 1251 # define PIPESIZE PIPE_BUF 1252 #endif 1253 #if PIPESIZE >= MAX_MEMORY_BUFF 1254 # error "PIPESIZE >= MAX_MEMORY_BUFF" 1255 #endif 1256 1257 // There is something to read 1258 count = safe_read(inFd, rbuf, PIPESIZE); 1259 if (count == 0) 1260 break; /* closed */ 1261 if (count > 0) { 1262 if (firstLine) { 1263 rbuf[count] = 0; 1264 /* check to see if the user script added headers */ 1265 if(strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) { 1266 bb_full_write(s, "HTTP/1.0 200 OK\r\n", 17); 1267 } 1268 if (strstr(rbuf, "ontent-") == 0) { 1269 bb_full_write(s, "Content-type: text/plain\r\n\r\n", 28); 1270 } 1271 firstLine = 0; 1272 } 1273 if (bb_full_write(s, rbuf, count) != count) 1274 break; 1275 1276 #if DEBUG 1277 fprintf(stderr, "cgi read %d bytes\n", count); 1278 #endif 1279 } 1280 } 1281 } 1282 } 1283 return 0; 1284 } 1285 #endif /* CONFIG_FEATURE_HTTPD_CGI */ 1286 1287 /**************************************************************************** 1288 * 1289 > $Function: sendFile() 1290 * 1291 * $Description: Send a file response to an HTTP request 1292 * 1293 * $Parameter: 1294 * (const char *) url . . The URL requested. 1295 * 1296 * $Return: (int) . . . . . . Always 0. 1297 * 1298 ****************************************************************************/ 1299 static int sendFile(const char *url) 1300 { 1301 char * suffix; 1302 int f; 1303 const char * const * table; 1304 const char * try_suffix; 1305 1306 suffix = strrchr(url, '.'); 1307 1308 for (table = suffixTable; *table; table += 2) 1309 if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) { 1310 try_suffix += strlen(suffix); 1311 if(*try_suffix == 0 || *try_suffix == '.') 1312 break; 1313 } 1314 /* also, if not found, set default as "application/octet-stream"; */ 1315 config->httpd_found.found_mime_type = *(table+1); 1316 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 1317 if (suffix) { 1318 Htaccess * cur; 1319 1320 for (cur = config->mime_a; cur; cur = cur->next) { 1321 if(strcmp(cur->before_colon, suffix) == 0) { 1322 config->httpd_found.found_mime_type = cur->after_colon; 1323 break; 1324 } 1325 } 1326 } 1327 #endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */ 1328 1329 #if DEBUG 1330 fprintf(stderr, "Sending file '%s' Content-type: %s\n", 1331 url, config->httpd_found.found_mime_type); 1332 #endif 1333 1334 f = open(url, O_RDONLY); 1335 if (f >= 0) { 1336 int count; 1337 char *buf = config->buf; 1338 1339 sendHeaders(HTTP_OK); 1340 while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) { 1341 if (bb_full_write(a_c_w, buf, count) != count) 1342 break; 1343 } 1344 close(f); 1345 } else { 1346 #if DEBUG 1347 bb_perror_msg("Unable to open '%s'", url); 1348 #endif 1349 sendHeaders(HTTP_NOT_FOUND); 1350 } 1351 1352 return 0; 1353 } 1354 1355 static int checkPermIP(void) 1356 { 1357 Htaccess_IP * cur; 1358 1359 /* This could stand some work */ 1360 for (cur = config->ip_a_d; cur; cur = cur->next) { 1361 #if DEBUG 1362 fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str); 1363 fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n", 1364 (unsigned char)(cur->ip >> 24), 1365 (unsigned char)(cur->ip >> 16), 1366 (unsigned char)(cur->ip >> 8), 1367 cur->ip & 0xff, 1368 (unsigned char)(cur->mask >> 24), 1369 (unsigned char)(cur->mask >> 16), 1370 (unsigned char)(cur->mask >> 8), 1371 cur->mask & 0xff); 1372 #endif 1373 if((config->rmt_ip & cur->mask) == cur->ip) 1374 return cur->allow_deny == 'A'; /* Allow/Deny */ 1375 } 1376 1377 /* if unconfigured, return 1 - access from all */ 1378 return !config->flg_deny_all; 1379 } 1380 1381 /**************************************************************************** 1382 * 1383 > $Function: checkPerm() 1384 * 1385 * $Description: Check the permission file for access password protected. 1386 * 1387 * If config file isn't present, everything is allowed. 1388 * Entries are of the form you can see example from header source 1389 * 1390 * $Parameters: 1391 * (const char *) path . . . . The file path. 1392 * (const char *) request . . . User information to validate. 1393 * 1394 * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. 1395 * 1396 ****************************************************************************/ 1397 1398 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1399 static int checkPerm(const char *path, const char *request) 1400 { 1401 Htaccess * cur; 1402 const char *p; 1403 const char *p0; 1404 1405 const char *prev = NULL; 1406 1407 /* This could stand some work */ 1408 for (cur = config->auth; cur; cur = cur->next) { 1409 p0 = cur->before_colon; 1410 if(prev != NULL && strcmp(prev, p0) != 0) 1411 continue; /* find next identical */ 1412 p = cur->after_colon; 1413 #if DEBUG 1414 fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request); 1415 #endif 1646 if (strcasecmp(iobuf, request_GET) != 0) 1647 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 1648 #endif 1649 urlp = skip_whitespace(urlp); 1650 if (urlp[0] != '/') 1651 send_headers_and_exit(HTTP_BAD_REQUEST); 1652 1653 /* Find end of URL and parse HTTP version, if any */ 1654 http_major_version = -1; 1655 tptr = strchrnul(urlp, ' '); 1656 /* Is it " HTTP/"? */ 1657 if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) 1658 http_major_version = (tptr[6] - '0'); 1659 *tptr = '\0'; 1660 1661 /* Copy URL from after "GET "/"POST " to stack-allocated char[] */ 1662 urlcopy = alloca((tptr - urlp) + sizeof("/index.html")); 1663 /*if (urlcopy == NULL) 1664 * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/ 1665 strcpy(urlcopy, urlp); 1666 /* NB: urlcopy ptr is never changed after this */ 1667 1668 /* Extract url args if present */ 1669 tptr = strchr(urlcopy, '?'); 1670 g_query = NULL; 1671 if (tptr) { 1672 *tptr++ = '\0'; 1673 g_query = tptr; 1674 } 1675 1676 /* Decode URL escape sequences */ 1677 tptr = decodeString(urlcopy, 0); 1678 if (tptr == NULL) 1679 send_headers_and_exit(HTTP_BAD_REQUEST); 1680 if (tptr == urlcopy + 1) { 1681 /* '/' or NUL is encoded */ 1682 send_headers_and_exit(HTTP_NOT_FOUND); 1683 } 1684 1685 /* Canonicalize path */ 1686 /* Algorithm stolen from libbb bb_simplify_path(), 1687 * but don't strdup and reducing trailing slash and protect out root */ 1688 urlp = tptr = urlcopy; 1689 do { 1690 if (*urlp == '/') { 1691 /* skip duplicate (or initial) slash */ 1692 if (*tptr == '/') { 1693 continue; 1694 } 1695 if (*tptr == '.') { 1696 /* skip extra '.' */ 1697 if (tptr[1] == '/' || !tptr[1]) { 1698 continue; 1699 } 1700 /* '..': be careful */ 1701 if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) { 1702 ++tptr; 1703 if (urlp == urlcopy) /* protect root */ 1704 send_headers_and_exit(HTTP_BAD_REQUEST); 1705 while (*--urlp != '/') /* omit previous dir */; 1706 continue; 1707 } 1708 } 1709 } 1710 *++urlp = *tptr; 1711 } while (*++tptr); 1712 *++urlp = '\0'; /* so keep last character */ 1713 tptr = urlp; /* end ptr */ 1714 1715 /* If URL is a directory, add '/' */ 1716 if (tptr[-1] != '/') { 1717 if (is_directory(urlcopy + 1, 1, &sb)) { 1718 found_moved_temporarily = urlcopy; 1719 } 1720 } 1721 1722 /* Log it */ 1723 if (verbose > 1) 1724 bb_error_msg("url:%s", urlcopy); 1725 1726 tptr = urlcopy; 1727 ip_allowed = checkPermIP(); 1728 while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) { 1729 /* have path1/path2 */ 1730 *tptr = '\0'; 1731 if (is_directory(urlcopy + 1, 1, &sb)) { 1732 /* may be having subdir config */ 1733 parse_conf(urlcopy + 1, SUBDIR_PARSE); 1734 ip_allowed = checkPermIP(); 1735 } 1736 *tptr = '/'; 1737 } 1738 if (http_major_version >= 0) { 1739 /* Request was with "... HTTP/nXXX", and n >= 0 */ 1740 1741 /* Read until blank line for HTTP version specified, else parse immediate */ 1742 while (1) { 1743 alarm(HEADER_READ_TIMEOUT); 1744 if (!get_line()) 1745 break; /* EOF or error or empty line */ 1746 if (DEBUG) 1747 bb_error_msg("header: '%s'", iobuf); 1748 1749 #if ENABLE_FEATURE_HTTPD_CGI 1750 /* try and do our best to parse more lines */ 1751 if ((STRNCASECMP(iobuf, "Content-length:") == 0)) { 1752 /* extra read only for POST */ 1753 if (prequest != request_GET) { 1754 tptr = iobuf + sizeof("Content-length:") - 1; 1755 if (!tptr[0]) 1756 send_headers_and_exit(HTTP_BAD_REQUEST); 1757 errno = 0; 1758 /* not using strtoul: it ignores leading minus! */ 1759 length = strtol(tptr, &tptr, 10); 1760 /* length is "ulong", but we need to pass it to int later */ 1761 /* so we check for negative or too large values in one go: */ 1762 /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */ 1763 if (tptr[0] || errno || length > INT_MAX) 1764 send_headers_and_exit(HTTP_BAD_REQUEST); 1765 } 1766 } else if (STRNCASECMP(iobuf, "Cookie:") == 0) { 1767 cookie = strdup(skip_whitespace(iobuf + sizeof("Cookie:")-1)); 1768 } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) { 1769 content_type = strdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1)); 1770 } else if (STRNCASECMP(iobuf, "Referer:") == 0) { 1771 referer = strdup(skip_whitespace(iobuf + sizeof("Referer:")-1)); 1772 } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) { 1773 user_agent = strdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); 1774 } 1775 #endif 1776 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1777 if (STRNCASECMP(iobuf, "Authorization:") == 0) { 1778 /* We only allow Basic credentials. 1779 * It shows up as "Authorization: Basic <userid:password>" where 1780 * the userid:password is base64 encoded. 1781 */ 1782 tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1); 1783 if (STRNCASECMP(tptr, "Basic") != 0) 1784 continue; 1785 tptr += sizeof("Basic")-1; 1786 /* decodeBase64() skips whitespace itself */ 1787 decodeBase64(tptr); 1788 credentials = checkPerm(urlcopy, tptr); 1789 } 1790 #endif /* FEATURE_HTTPD_BASIC_AUTH */ 1791 } /* while extra header reading */ 1792 } 1793 1794 /* We read headers, disable peer timeout */ 1795 alarm(0); 1796 1797 if (strcmp(bb_basename(urlcopy), httpd_conf) == 0 || ip_allowed == 0) { 1798 /* protect listing [/path]/httpd_conf or IP deny */ 1799 send_headers_and_exit(HTTP_FORBIDDEN); 1800 } 1801 1802 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1803 if (credentials <= 0 && checkPerm(urlcopy, ":") == 0) { 1804 send_headers_and_exit(HTTP_UNAUTHORIZED); 1805 } 1806 #endif 1807 1808 if (found_moved_temporarily) { 1809 send_headers_and_exit(HTTP_MOVED_TEMPORARILY); 1810 } 1811 1812 tptr = urlcopy + 1; /* skip first '/' */ 1813 1814 #if ENABLE_FEATURE_HTTPD_CGI 1815 if (strncmp(tptr, "cgi-bin/", 8) == 0) { 1816 if (tptr[8] == '\0') { 1817 /* protect listing "cgi-bin/" */ 1818 send_headers_and_exit(HTTP_FORBIDDEN); 1819 } 1820 send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); 1821 } 1822 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1416 1823 { 1417 size_t l = strlen(p0); 1418 1419 if(strncmp(p0, path, l) == 0 && 1420 (l == 1 || path[l] == '/' || path[l] == 0)) { 1421 char *u; 1422 /* path match found. Check request */ 1423 /* for check next /path:user:password */ 1424 prev = p0; 1425 u = strchr(request, ':'); 1426 if(u == NULL) { 1427 /* bad request, ':' required */ 1428 break; 1429 } 1430 1431 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 1432 { 1433 char *cipher; 1434 char *pp; 1435 1436 if(strncmp(p, request, u-request) != 0) { 1437 /* user uncompared */ 1438 continue; 1439 } 1440 pp = strchr(p, ':'); 1441 if(pp && pp[1] == '$' && pp[2] == '1' && 1442 pp[3] == '$' && pp[4]) { 1443 pp++; 1444 cipher = pw_encrypt(u+1, pp); 1445 if (strcmp(cipher, pp) == 0) 1446 goto set_remoteuser_var; /* Ok */ 1447 /* unauthorized */ 1448 continue; 1449 } 1450 } 1451 #endif 1452 if (strcmp(p, request) == 0) { 1453 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 1454 set_remoteuser_var: 1455 #endif 1456 config->remoteuser = strdup(request); 1457 if(config->remoteuser) 1458 config->remoteuser[(u - request)] = 0; 1459 return 1; /* Ok */ 1460 } 1461 /* unauthorized */ 1462 } 1463 } 1464 } /* for */ 1465 1466 return prev == NULL; 1467 } 1468 1469 #endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ 1470 1471 /**************************************************************************** 1472 * 1473 > $Function: handle_sigalrm() 1474 * 1475 * $Description: Handle timeouts 1476 * 1477 ****************************************************************************/ 1478 1479 static void 1480 handle_sigalrm( int sig ) 1481 { 1482 sendHeaders(HTTP_REQUEST_TIMEOUT); 1483 config->alarm_signaled = sig; 1484 } 1485 1486 /**************************************************************************** 1487 * 1488 > $Function: handleIncoming() 1489 * 1490 * $Description: Handle an incoming http request. 1491 * 1492 ****************************************************************************/ 1493 static void handleIncoming(void) 1494 { 1495 char *buf = config->buf; 1496 char *url; 1497 char *purl; 1498 int blank = -1; 1499 char *test; 1500 struct stat sb; 1501 int ip_allowed; 1502 #ifdef CONFIG_FEATURE_HTTPD_CGI 1503 const char *prequest = request_GET; 1504 long length=0; 1505 char *cookie = 0; 1506 char *content_type = 0; 1507 #endif 1508 fd_set s_fd; 1509 struct timeval tv; 1510 int retval; 1511 struct sigaction sa; 1512 1513 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1514 int credentials = -1; /* if not requred this is Ok */ 1515 #endif 1516 1517 sa.sa_handler = handle_sigalrm; 1518 sigemptyset(&sa.sa_mask); 1519 sa.sa_flags = 0; /* no SA_RESTART */ 1520 sigaction(SIGALRM, &sa, NULL); 1521 1522 do { 1523 int count; 1524 1525 (void) alarm( TIMEOUT ); 1526 if (getLine() <= 0) 1527 break; /* closed */ 1528 1529 purl = strpbrk(buf, " \t"); 1530 if(purl == NULL) { 1531 BAD_REQUEST: 1532 sendHeaders(HTTP_BAD_REQUEST); 1533 break; 1534 } 1535 *purl = 0; 1536 #ifdef CONFIG_FEATURE_HTTPD_CGI 1537 if(strcasecmp(buf, prequest) != 0) { 1538 prequest = "POST"; 1539 if(strcasecmp(buf, prequest) != 0) { 1540 sendHeaders(HTTP_NOT_IMPLEMENTED); 1541 break; 1542 } 1543 } 1824 char *suffix = strrchr(tptr, '.'); 1825 if (suffix) { 1826 Htaccess *cur; 1827 for (cur = script_i; cur; cur = cur->next) { 1828 if (strcmp(cur->before_colon + 1, suffix) == 0) { 1829 send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); 1830 } 1831 } 1832 } 1833 } 1834 #endif 1835 if (prequest != request_GET) { 1836 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 1837 } 1838 #endif /* FEATURE_HTTPD_CGI */ 1839 1840 if (urlp[-1] == '/') 1841 strcpy(urlp, "index.html"); 1842 if (stat(tptr, &sb) == 0) { 1843 /* It's a dir URL and there is index.html */ 1844 ContentLength = sb.st_size; 1845 last_mod = sb.st_mtime; 1846 } 1847 #if ENABLE_FEATURE_HTTPD_CGI 1848 else if (urlp[-1] == '/') { 1849 /* It's a dir URL and there is no index.html 1850 * Try cgi-bin/index.cgi */ 1851 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { 1852 urlp[0] = '\0'; 1853 g_query = urlcopy; 1854 send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type); 1855 } 1856 } 1857 #endif 1858 /* else { 1859 * fall through to send_file, it errors out if open fails 1860 * } 1861 */ 1862 1863 send_file_and_exit(tptr, TRUE); 1864 } 1865 1866 /* 1867 * The main http server function. 1868 * Given a socket, listen for new connections and farm out 1869 * the processing as a [v]forked process. 1870 * Never returns. 1871 */ 1872 #if BB_MMU 1873 static void mini_httpd(int server_socket) ATTRIBUTE_NORETURN; 1874 static void mini_httpd(int server_socket) 1875 { 1876 /* NB: it's best to not use xfuncs in this loop before fork(). 1877 * Otherwise server may die on transient errors (temporary 1878 * out-of-memory condition, etc), which is Bad(tm). 1879 * Try to do any dangerous calls after fork. 1880 */ 1881 while (1) { 1882 int n; 1883 len_and_sockaddr fromAddr; 1884 1885 /* Wait for connections... */ 1886 fromAddr.len = LSA_SIZEOF_SA; 1887 n = accept(server_socket, &fromAddr.sa, &fromAddr.len); 1888 1889 if (n < 0) 1890 continue; 1891 /* set the KEEPALIVE option to cull dead connections */ 1892 setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); 1893 1894 if (fork() == 0) { 1895 /* child */ 1896 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1897 /* Do not reload config on HUP */ 1898 signal(SIGHUP, SIG_IGN); 1899 #endif 1900 close(server_socket); 1901 xmove_fd(n, 0); 1902 xdup2(0, 1); 1903 1904 handle_incoming_and_exit(&fromAddr); 1905 } 1906 /* parent, or fork failed */ 1907 close(n); 1908 } /* while (1) */ 1909 /* never reached */ 1910 } 1544 1911 #else 1545 if(strcasecmp(buf, request_GET) != 0) { 1546 sendHeaders(HTTP_NOT_IMPLEMENTED); 1547 break; 1548 } 1549 #endif 1550 *purl = ' '; 1551 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank); 1552 1553 if (count < 1 || buf[0] != '/') { 1554 /* Garbled request/URL */ 1555 goto BAD_REQUEST; 1556 } 1557 url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */ 1558 if(url == NULL) { 1559 sendHeaders(HTTP_INTERNAL_SERVER_ERROR); 1560 break; 1561 } 1562 strcpy(url, buf); 1563 /* extract url args if present */ 1564 test = strchr(url, '?'); 1565 if (test) { 1566 *test++ = 0; 1567 config->query = test; 1568 } 1569 1570 test = decodeString(url, 0); 1571 if(test == NULL) 1572 goto BAD_REQUEST; 1573 if(test == (buf+1)) { 1574 sendHeaders(HTTP_NOT_FOUND); 1575 break; 1576 } 1577 /* algorithm stolen from libbb bb_simplify_path(), 1578 but don`t strdup and reducing trailing slash and protect out root */ 1579 purl = test = url; 1580 1581 do { 1582 if (*purl == '/') { 1583 if (*test == '/') { /* skip duplicate (or initial) slash */ 1584 continue; 1585 } else if (*test == '.') { 1586 if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */ 1587 continue; 1588 } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) { 1589 ++test; 1590 if (purl == url) { 1591 /* protect out root */ 1592 goto BAD_REQUEST; 1593 } 1594 while (*--purl != '/'); /* omit previous dir */ 1595 continue; 1596 } 1597 } 1598 } 1599 *++purl = *test; 1600 } while (*++test); 1601 1602 *++purl = 0; /* so keep last character */ 1603 test = purl; /* end ptr */ 1604 1605 /* If URL is directory, adding '/' */ 1606 if(test[-1] != '/') { 1607 if ( is_directory(url + 1, 1, &sb) ) { 1608 config->httpd_found.found_moved_temporarily = url; 1609 } 1610 } 1611 #if DEBUG 1612 fprintf(stderr, "url='%s', args=%s\n", url, config->query); 1613 #endif 1614 1615 test = url; 1616 ip_allowed = checkPermIP(); 1617 while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) { 1618 /* have path1/path2 */ 1619 *test = '\0'; 1620 if( is_directory(url + 1, 1, &sb) ) { 1621 /* may be having subdir config */ 1622 parse_conf(url + 1, SUBDIR_PARSE); 1623 ip_allowed = checkPermIP(); 1624 } 1625 *test = '/'; 1626 } 1627 if(blank >= 0) { 1628 // read until blank line for HTTP version specified, else parse immediate 1629 while(1) { 1630 alarm(TIMEOUT); 1631 count = getLine(); 1632 if(count <= 0) 1633 break; 1634 1635 #if DEBUG 1636 fprintf(stderr, "Header: '%s'\n", buf); 1637 #endif 1638 1639 #ifdef CONFIG_FEATURE_HTTPD_CGI 1640 /* try and do our best to parse more lines */ 1641 if ((strncasecmp(buf, Content_length, 15) == 0)) { 1642 if(prequest != request_GET) 1643 length = strtol(buf + 15, 0, 0); // extra read only for POST 1644 } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) { 1645 for(test = buf + 7; isspace(*test); test++) 1646 ; 1647 cookie = strdup(test); 1648 } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) { 1649 for(test = buf + 13; isspace(*test); test++) 1650 ; 1651 content_type = strdup(test); 1652 } else if ((strncasecmp(buf, "Referer:", 8) == 0)) { 1653 for(test = buf + 8; isspace(*test); test++) 1654 ; 1655 config->referer = strdup(test); 1656 } 1657 #endif 1658 1659 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1660 if (strncasecmp(buf, "Authorization:", 14) == 0) { 1661 /* We only allow Basic credentials. 1662 * It shows up as "Authorization: Basic <userid:password>" where 1663 * the userid:password is base64 encoded. 1664 */ 1665 for(test = buf + 14; isspace(*test); test++) 1666 ; 1667 if (strncasecmp(test, "Basic", 5) != 0) 1668 continue; 1669 1670 test += 5; /* decodeBase64() skiping space self */ 1671 decodeBase64(test); 1672 credentials = checkPerm(url, test); 1673 } 1674 #endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ 1675 1676 } /* while extra header reading */ 1677 } 1678 (void) alarm( 0 ); 1679 if(config->alarm_signaled) 1680 break; 1681 1682 if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) { 1683 /* protect listing [/path]/httpd_conf or IP deny */ 1684 #ifdef CONFIG_FEATURE_HTTPD_CGI 1685 FORBIDDEN: /* protect listing /cgi-bin */ 1686 #endif 1687 sendHeaders(HTTP_FORBIDDEN); 1688 break; 1689 } 1690 1691 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1692 if (credentials <= 0 && checkPerm(url, ":") == 0) { 1693 sendHeaders(HTTP_UNAUTHORIZED); 1694 break; 1695 } 1696 #endif 1697 1698 if(config->httpd_found.found_moved_temporarily) { 1699 sendHeaders(HTTP_MOVED_TEMPORARILY); 1700 #if DEBUG 1701 /* clear unforked memory flag */ 1702 config->httpd_found.found_moved_temporarily = NULL; 1703 #endif 1704 break; 1705 } 1706 1707 test = url + 1; /* skip first '/' */ 1708 1709 #ifdef CONFIG_FEATURE_HTTPD_CGI 1710 /* if strange Content-Length */ 1711 if (length < 0) 1712 break; 1713 1714 if (strncmp(test, "cgi-bin", 7) == 0) { 1715 if(test[7] == '/' && test[8] == 0) 1716 goto FORBIDDEN; // protect listing cgi-bin/ 1717 sendCgi(url, prequest, length, cookie, content_type); 1718 } else { 1719 if (prequest != request_GET) 1720 sendHeaders(HTTP_NOT_IMPLEMENTED); 1721 else { 1722 #endif /* CONFIG_FEATURE_HTTPD_CGI */ 1723 if(purl[-1] == '/') 1724 strcpy(purl, "index.html"); 1725 if ( stat(test, &sb ) == 0 ) { 1726 config->ContentLength = sb.st_size; 1727 config->last_mod = sb.st_mtime; 1728 } 1729 sendFile(test); 1730 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1731 /* unset if non inetd looped */ 1732 config->ContentLength = -1; 1733 #endif 1734 1735 #ifdef CONFIG_FEATURE_HTTPD_CGI 1736 } 1737 } 1738 #endif 1739 1740 } while (0); 1741 1742 1743 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1744 /* from inetd don`t looping: freeing, closing automatic from exit always */ 1745 # if DEBUG 1746 fprintf(stderr, "closing socket\n"); 1747 # endif 1748 # ifdef CONFIG_FEATURE_HTTPD_CGI 1749 free(cookie); 1750 free(content_type); 1751 free(config->referer); 1752 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1753 free(config->remoteuser); 1754 #endif 1755 # endif 1756 #endif /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */ 1757 shutdown(a_c_w, SHUT_WR); 1758 1759 /* Properly wait for remote to closed */ 1760 FD_ZERO (&s_fd) ; 1761 FD_SET (a_c_r, &s_fd) ; 1762 1763 do { 1764 tv.tv_sec = 2 ; 1765 tv.tv_usec = 0 ; 1766 retval = select (a_c_r + 1, &s_fd, NULL, NULL, &tv); 1767 } while (retval > 0 && (read (a_c_r, buf, sizeof (config->buf)) > 0)); 1768 1769 shutdown(a_c_r, SHUT_RD); 1770 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1771 close(config->accepted_socket); 1772 #endif /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */ 1773 } 1774 1775 /**************************************************************************** 1776 * 1777 > $Function: miniHttpd() 1778 * 1779 * $Description: The main http server function. 1780 * 1781 * Given an open socket fildes, listen for new connections and farm out 1782 * the processing as a forked process. 1783 * 1784 * $Parameters: 1785 * (int) server. . . The server socket fildes. 1786 * 1787 * $Return: (int) . . . . Always 0. 1788 * 1789 ****************************************************************************/ 1790 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1791 static int miniHttpd(int server) 1792 { 1793 fd_set readfd, portfd; 1794 1795 FD_ZERO(&portfd); 1796 FD_SET(server, &portfd); 1797 1798 /* copy the ports we are watching to the readfd set */ 1799 while (1) { 1800 readfd = portfd; 1801 1802 /* Now wait INDEFINITELY on the set of sockets! */ 1803 if (select(server + 1, &readfd, 0, 0, 0) > 0) { 1804 if (FD_ISSET(server, &readfd)) { 1805 int on; 1806 struct sockaddr_in fromAddr; 1807 1808 socklen_t fromAddrLen = sizeof(fromAddr); 1809 int s = accept(server, 1810 (struct sockaddr *)&fromAddr, &fromAddrLen); 1811 1812 if (s < 0) { 1813 continue; 1814 } 1815 config->accepted_socket = s; 1816 config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr); 1817 #if defined(CONFIG_FEATURE_HTTPD_CGI) || DEBUG 1818 sprintf(config->rmt_ip_str, "%u.%u.%u.%u", 1819 (unsigned char)(config->rmt_ip >> 24), 1820 (unsigned char)(config->rmt_ip >> 16), 1821 (unsigned char)(config->rmt_ip >> 8), 1822 config->rmt_ip & 0xff); 1823 config->port = ntohs(fromAddr.sin_port); 1824 #if DEBUG 1825 bb_error_msg("connection from IP=%s, port %u\n", 1826 config->rmt_ip_str, config->port); 1827 #endif 1828 #endif /* CONFIG_FEATURE_HTTPD_CGI */ 1829 1830 /* set the KEEPALIVE option to cull dead connections */ 1831 on = 1; 1832 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on)); 1833 1834 #if !DEBUG 1835 if (fork() == 0) 1836 #endif 1837 { 1838 /* This is the spawned thread */ 1839 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1840 /* protect reload config, may be confuse checking */ 1841 signal(SIGHUP, SIG_IGN); 1842 #endif 1843 handleIncoming(); 1844 #if !DEBUG 1845 exit(0); 1846 #endif 1847 } 1848 close(s); 1849 } 1850 } 1851 } // while (1) 1852 return 0; 1853 } 1854 1855 #else 1856 /* from inetd */ 1857 1858 static int miniHttpd(void) 1859 { 1860 struct sockaddr_in fromAddrLen; 1861 socklen_t sinlen = sizeof (struct sockaddr_in); 1862 1863 getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen); 1864 config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr); 1865 #ifdef CONFIG_FEATURE_HTTPD_CGI 1866 sprintf(config->rmt_ip_str, "%u.%u.%u.%u", 1867 (unsigned char)(config->rmt_ip >> 24), 1868 (unsigned char)(config->rmt_ip >> 16), 1869 (unsigned char)(config->rmt_ip >> 8), 1870 config->rmt_ip & 0xff); 1871 #endif 1872 config->port = ntohs(fromAddrLen.sin_port); 1873 handleIncoming(); 1874 return 0; 1875 } 1876 #endif /* CONFIG_FEATURE_HTTPD_WITHOUT_INETD */ 1877 1878 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1912 static void mini_httpd_nommu(int server_socket, int argc, char **argv) ATTRIBUTE_NORETURN; 1913 static void mini_httpd_nommu(int server_socket, int argc, char **argv) 1914 { 1915 char *argv_copy[argc + 2]; 1916 1917 argv_copy[0] = argv[0]; 1918 argv_copy[1] = (char*)"-i"; 1919 memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0])); 1920 1921 /* NB: it's best to not use xfuncs in this loop before vfork(). 1922 * Otherwise server may die on transient errors (temporary 1923 * out-of-memory condition, etc), which is Bad(tm). 1924 * Try to do any dangerous calls after fork. 1925 */ 1926 while (1) { 1927 int n; 1928 len_and_sockaddr fromAddr; 1929 1930 /* Wait for connections... */ 1931 fromAddr.len = LSA_SIZEOF_SA; 1932 n = accept(server_socket, &fromAddr.sa, &fromAddr.len); 1933 1934 if (n < 0) 1935 continue; 1936 /* set the KEEPALIVE option to cull dead connections */ 1937 setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); 1938 1939 if (vfork() == 0) { 1940 /* child */ 1941 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1942 /* Do not reload config on HUP */ 1943 signal(SIGHUP, SIG_IGN); 1944 #endif 1945 close(server_socket); 1946 xmove_fd(n, 0); 1947 xdup2(0, 1); 1948 1949 /* Run a copy of ourself in inetd mode */ 1950 re_exec(argv_copy); 1951 } 1952 /* parent, or vfork failed */ 1953 close(n); 1954 } /* while (1) */ 1955 /* never reached */ 1956 } 1957 #endif 1958 1959 /* 1960 * Process a HTTP connection on stdin/out. 1961 * Never returns. 1962 */ 1963 static void mini_httpd_inetd(void) ATTRIBUTE_NORETURN; 1964 static void mini_httpd_inetd(void) 1965 { 1966 len_and_sockaddr fromAddr; 1967 1968 fromAddr.len = LSA_SIZEOF_SA; 1969 getpeername(0, &fromAddr.sa, &fromAddr.len); 1970 handle_incoming_and_exit(&fromAddr); 1971 } 1972 1973 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1879 1974 static void sighup_handler(int sig) 1880 1975 { 1881 /* set and reset */1882 1976 struct sigaction sa; 1883 1977 1884 parse_conf(default_path_httpd_conf, 1885 sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); 1978 parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); 1979 1980 memset(&sa, 0, sizeof(sa)); 1886 1981 sa.sa_handler = sighup_handler; 1887 sigemptyset(&sa.sa_mask);1982 /*sigemptyset(&sa.sa_mask); - memset should be enough */ 1888 1983 sa.sa_flags = SA_RESTART; 1889 1984 sigaction(SIGHUP, &sa, NULL); … … 1891 1986 #endif 1892 1987 1893 enum httpd_opts_nums{1988 enum { 1894 1989 c_opt_config_file = 0, 1895 1990 d_opt_decode_url, 1896 1991 h_opt_home_httpd, 1897 1992 USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,) 1898 USE_FEATURE_HTTPD_BASIC_AUTH(r_opt_realm,) 1899 USE_FEATURE_HTTPD_AUTH_MD5(m_opt_md5,) 1900 USE_FEATURE_HTTPD_SETUID(u_opt_setuid,) 1901 USE_FEATURE_HTTPD_WITHOUT_INETD(p_opt_port,) 1993 USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,) 1994 USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) 1995 USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,) 1996 p_opt_port , 1997 p_opt_inetd , 1998 p_opt_foreground, 1999 p_opt_verbose , 2000 OPT_CONFIG_FILE = 1 << c_opt_config_file, 2001 OPT_DECODE_URL = 1 << d_opt_decode_url, 2002 OPT_HOME_HTTPD = 1 << h_opt_home_httpd, 2003 OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0, 2004 OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0, 2005 OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, 2006 OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, 2007 OPT_PORT = 1 << p_opt_port, 2008 OPT_INETD = 1 << p_opt_inetd, 2009 OPT_FOREGROUND = 1 << p_opt_foreground, 2010 OPT_VERBOSE = 1 << p_opt_verbose, 1902 2011 }; 1903 2012 1904 static const char httpd_opts[]="c:d:h:" 1905 USE_FEATURE_HTTPD_ENCODE_URL_STR("e:") 1906 USE_FEATURE_HTTPD_BASIC_AUTH("r:") 1907 USE_FEATURE_HTTPD_AUTH_MD5("m:") 1908 USE_FEATURE_HTTPD_SETUID("u:") 1909 USE_FEATURE_HTTPD_WITHOUT_INETD("p:"); 1910 1911 #define OPT_CONFIG_FILE (1<<c_opt_config_file) 1912 #define OPT_DECODE_URL (1<<d_opt_decode_url) 1913 #define OPT_HOME_HTTPD (1<<h_opt_home_httpd) 1914 1915 #define OPT_ENCODE_URL USE_FEATURE_HTTPD_ENCODE_URL_STR((1<<e_opt_encode_url)) \ 1916 SKIP_FEATURE_HTTPD_ENCODE_URL_STR(0) 1917 1918 #define OPT_REALM USE_FEATURE_HTTPD_BASIC_AUTH((1<<r_opt_realm)) \ 1919 SKIP_FEATURE_HTTPD_BASIC_AUTH(0) 1920 1921 #define OPT_MD5 USE_FEATURE_HTTPD_AUTH_MD5((1<<m_opt_md5)) \ 1922 SKIP_FEATURE_HTTPD_AUTH_MD5(0) 1923 1924 #define OPT_SETUID USE_FEATURE_HTTPD_SETUID((1<<u_opt_setuid)) \ 1925 SKIP_FEATURE_HTTPD_SETUID(0) 1926 1927 #define OPT_PORT USE_FEATURE_HTTPD_WITHOUT_INETD((1<<p_opt_port)) \ 1928 SKIP_FEATURE_HTTPD_WITHOUT_INETD(0) 1929 1930 1931 int httpd_main(int argc, char *argv[]) 1932 { 1933 unsigned long opt; 1934 const char *home_httpd = home; 1935 char *url_for_decode; 1936 USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) 1937 USE_FEATURE_HTTPD_WITHOUT_INETD(const char *s_port;) 1938 USE_FEATURE_HTTPD_WITHOUT_INETD(int server;) 1939 1940 USE_FEATURE_HTTPD_SETUID(const char *s_uid;) 1941 USE_FEATURE_HTTPD_SETUID(long uid = -1;) 1942 1943 USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;) 1944 1945 config = xcalloc(1, sizeof(*config)); 1946 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1947 config->realm = "Web Server Authentication"; 1948 #endif 1949 1950 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1951 config->port = 80; 1952 #endif 1953 1954 config->ContentLength = -1; 1955 1956 opt = bb_getopt_ulflags(argc, argv, httpd_opts, 1957 &(config->configFile), &url_for_decode, &home_httpd 2013 2014 int httpd_main(int argc, char **argv); 2015 int httpd_main(int argc, char **argv) 2016 { 2017 int server_socket = server_socket; /* for gcc */ 2018 unsigned opt; 2019 char *url_for_decode; 2020 USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) 2021 USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) 2022 USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) 2023 USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;) 2024 2025 INIT_G(); 2026 2027 #if ENABLE_LOCALE_SUPPORT 2028 /* Undo busybox.c: we want to speak English in http (dates etc) */ 2029 setlocale(LC_TIME, "C"); 2030 #endif 2031 2032 home_httpd = xrealloc_getcwd_or_warn(NULL); 2033 /* -v counts, -i implies -f */ 2034 opt_complementary = "vv:if"; 2035 /* We do not "absolutize" path given by -h (home) opt. 2036 * If user gives relative path in -h, $SCRIPT_FILENAME can end up 2037 * relative too. */ 2038 opt = getopt32(argv, "c:d:h:" 2039 USE_FEATURE_HTTPD_ENCODE_URL_STR("e:") 2040 USE_FEATURE_HTTPD_BASIC_AUTH("r:") 2041 USE_FEATURE_HTTPD_AUTH_MD5("m:") 2042 USE_FEATURE_HTTPD_SETUID("u:") 2043 "p:ifv", 2044 &configFile, &url_for_decode, &home_httpd 1958 2045 USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) 1959 USE_FEATURE_HTTPD_BASIC_AUTH(, & (config->realm))2046 USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) 1960 2047 USE_FEATURE_HTTPD_AUTH_MD5(, &pass) 1961 USE_FEATURE_HTTPD_SETUID(, &s_uid) 1962 USE_FEATURE_HTTPD_WITHOUT_INETD(, &s_port) 1963 ); 1964 1965 if(opt & OPT_DECODE_URL) { 1966 printf("%s", decodeString(url_for_decode, 1)); 1967 return 0; 1968 } 1969 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 1970 if(opt & OPT_ENCODE_URL) { 1971 printf("%s", encodeString(url_for_encode)); 1972 return 0; 1973 } 1974 #endif 1975 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5 1976 if(opt & OPT_MD5) { 1977 printf("%s\n", pw_encrypt(pass, "$1$")); 1978 return 0; 1979 } 1980 #endif 1981 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1982 if(opt & OPT_PORT) 1983 config->port = bb_xgetlarg(s_port, 10, 1, 0xffff); 1984 #ifdef CONFIG_FEATURE_HTTPD_SETUID 1985 if(opt & OPT_SETUID) { 1986 char *e; 1987 1988 uid = strtol(s_uid, &e, 0); 1989 if(*e != '\0') { 1990 /* not integer */ 1991 uid = bb_xgetpwnam(s_uid); 1992 } 1993 } 1994 #endif 1995 #endif 1996 1997 bb_xchdir(home_httpd); 1998 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 1999 server = openServer(); 2000 # ifdef CONFIG_FEATURE_HTTPD_SETUID 2001 /* drop privileges */ 2002 if(uid > 0) 2003 setuid(uid); 2004 # endif 2005 #endif 2006 2007 #ifdef CONFIG_FEATURE_HTTPD_CGI 2008 { 2009 char *p = getenv("PATH"); 2010 if(p) { 2011 p = bb_xstrdup(p); 2012 } 2013 clearenv(); 2014 if(p) 2015 setenv("PATH", p, 1); 2016 # ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 2017 addEnvPort("SERVER"); 2018 # endif 2019 } 2020 #endif 2021 2022 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 2023 sighup_handler(0); 2048 USE_FEATURE_HTTPD_SETUID(, &s_ugid) 2049 , &bind_addr_or_port 2050 , &verbose 2051 ); 2052 if (opt & OPT_DECODE_URL) { 2053 fputs(decodeString(url_for_decode, 1), stdout); 2054 return 0; 2055 } 2056 #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR 2057 if (opt & OPT_ENCODE_URL) { 2058 fputs(encodeString(url_for_encode), stdout); 2059 return 0; 2060 } 2061 #endif 2062 #if ENABLE_FEATURE_HTTPD_AUTH_MD5 2063 if (opt & OPT_MD5) { 2064 puts(pw_encrypt(pass, "$1$")); 2065 return 0; 2066 } 2067 #endif 2068 #if ENABLE_FEATURE_HTTPD_SETUID 2069 if (opt & OPT_SETUID) { 2070 if (!get_uidgid(&ugid, s_ugid, 1)) 2071 bb_error_msg_and_die("unrecognized user[:group] " 2072 "name '%s'", s_ugid); 2073 } 2074 #endif 2075 2076 #if !BB_MMU 2077 if (!(opt & OPT_FOREGROUND)) { 2078 bb_daemonize_or_rexec(0, argv); /* don't change current directory */ 2079 } 2080 #endif 2081 2082 xchdir(home_httpd); 2083 if (!(opt & OPT_INETD)) { 2084 signal(SIGCHLD, SIG_IGN); 2085 server_socket = openServer(); 2086 #if ENABLE_FEATURE_HTTPD_SETUID 2087 /* drop privileges */ 2088 if (opt & OPT_SETUID) { 2089 if (ugid.gid != (gid_t)-1) { 2090 if (setgroups(1, &ugid.gid) == -1) 2091 bb_perror_msg_and_die("setgroups"); 2092 xsetgid(ugid.gid); 2093 } 2094 xsetuid(ugid.uid); 2095 } 2096 #endif 2097 } 2098 2099 #if ENABLE_FEATURE_HTTPD_CGI 2100 { 2101 char *p = getenv("PATH"); 2102 /* env strings themself are not freed, no need to strdup(p): */ 2103 clearenv(); 2104 if (p) 2105 putenv(p - 5); 2106 // if (!(opt & OPT_INETD)) 2107 // setenv_long("SERVER_PORT", ???); 2108 } 2109 #endif 2110 2111 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 2112 if (!(opt & OPT_INETD)) 2113 sighup_handler(0); 2114 else /* do not install HUP handler in inetd mode */ 2115 #endif 2116 parse_conf(default_path_httpd_conf, FIRST_PARSE); 2117 2118 xfunc_error_retval = 0; 2119 if (opt & OPT_INETD) 2120 mini_httpd_inetd(); 2121 #if BB_MMU 2122 if (!(opt & OPT_FOREGROUND)) 2123 bb_daemonize(0); /* don't change current directory */ 2124 mini_httpd(server_socket); /* never returns */ 2024 2125 #else 2025 parse_conf(default_path_httpd_conf, FIRST_PARSE); 2026 #endif 2027 2028 #ifdef CONFIG_FEATURE_HTTPD_WITHOUT_INETD 2029 # if !DEBUG 2030 bb_xdaemon(1, 0); /* don`t change curent directory */ 2031 # endif 2032 return miniHttpd(server); 2033 #else 2034 return miniHttpd(); 2035 #endif 2036 } 2126 mini_httpd_nommu(server_socket, argc, argv); /* never returns */ 2127 #endif 2128 /* return 0; */ 2129 }
Note:
See TracChangeset
for help on using the changeset viewer.