httpd.c 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * httpd implementation for busybox
  4. *
  5. * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
  6. * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
  7. *
  8. * simplify patch stolen from libbb without using strdup
  9. *
  10. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  11. *
  12. *****************************************************************************
  13. *
  14. * Typical usage:
  15. * for non root user
  16. * httpd -p 8080 -h $HOME/public_html
  17. * or for daemon start from rc script with uid=0:
  18. * httpd -u www
  19. * This is equivalent if www user have uid=80 to
  20. * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
  21. *
  22. *
  23. * When a url contains "cgi-bin" it is assumed to be a cgi script. The
  24. * server changes directory to the location of the script and executes it
  25. * after setting QUERY_STRING and other environment variables.
  26. *
  27. * Doc:
  28. * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
  29. *
  30. * The server can also be invoked as a url arg decoder and html text encoder
  31. * as follows:
  32. * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
  33. * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
  34. * Note that url encoding for arguments is not the same as html encoding for
  35. * presentation. -d decodes a url-encoded argument while -e encodes in html
  36. * for page display.
  37. *
  38. * httpd.conf has the following format:
  39. *
  40. * A:172.20. # Allow address from 172.20.0.0/16
  41. * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
  42. * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
  43. * A:127.0.0.1 # Allow local loopback connections
  44. * D:* # Deny from other IP connections
  45. * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
  46. * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
  47. * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
  48. * .au:audio/basic # additional mime type for audio.au files
  49. * *.php:/path/php # running cgi.php scripts through an interpreter
  50. *
  51. * A/D may be as a/d or allow/deny - first char case insensitive
  52. * Deny IP rules take precedence over allow rules.
  53. *
  54. *
  55. * The Deny/Allow IP logic:
  56. *
  57. * - Default is to allow all. No addresses are denied unless
  58. * denied with a D: rule.
  59. * - Order of Deny/Allow rules is significant
  60. * - Deny rules take precedence over allow rules.
  61. * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
  62. * addresses.
  63. * - Specification of Allow all (A:*) is a no-op
  64. *
  65. * Example:
  66. * 1. Allow only specified addresses
  67. * A:172.20 # Allow any address that begins with 172.20.
  68. * A:10.10. # Allow any address that begins with 10.10.
  69. * A:127.0.0.1 # Allow local loopback connections
  70. * D:* # Deny from other IP connections
  71. *
  72. * 2. Only deny specified addresses
  73. * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
  74. * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
  75. * A:* # (optional line added for clarity)
  76. *
  77. * If a sub directory contains a config file it is parsed and merged with
  78. * any existing settings as if it was appended to the original configuration.
  79. *
  80. * subdir paths are relative to the containing subdir and thus cannot
  81. * affect the parent rules.
  82. *
  83. * Note that since the sub dir is parsed in the forked thread servicing the
  84. * subdir http request, any merge is discarded when the process exits. As a
  85. * result, the subdir settings only have a lifetime of a single request.
  86. *
  87. *
  88. * If -c is not set, an attempt will be made to open the default
  89. * root configuration file. If -c is set and the file is not found, the
  90. * server exits with an error.
  91. *
  92. */
  93. #include "busybox.h"
  94. static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
  95. static const char default_path_httpd_conf[] = "/etc";
  96. static const char httpd_conf[] = "httpd.conf";
  97. static const char home[] = "./";
  98. #define TIMEOUT 60
  99. // Note: busybox xfuncs are not used because we want the server to keep running
  100. // if something bad happens due to a malformed user request.
  101. // As a result, all memory allocation after daemonize
  102. // is checked rigorously
  103. //#define DEBUG 1
  104. #ifndef DEBUG
  105. # define DEBUG 0
  106. #endif
  107. #define MAX_MEMORY_BUFF 8192 /* IO buffer */
  108. typedef struct HT_ACCESS {
  109. char *after_colon;
  110. struct HT_ACCESS *next;
  111. char before_colon[1]; /* really bigger, must last */
  112. } Htaccess;
  113. typedef struct HT_ACCESS_IP {
  114. unsigned int ip;
  115. unsigned int mask;
  116. int allow_deny;
  117. struct HT_ACCESS_IP *next;
  118. } Htaccess_IP;
  119. typedef struct {
  120. char buf[MAX_MEMORY_BUFF];
  121. USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
  122. USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
  123. const char *query;
  124. USE_FEATURE_HTTPD_CGI(char *referer;)
  125. const char *configFile;
  126. unsigned int rmt_ip;
  127. #if ENABLE_FEATURE_HTTPD_CGI || DEBUG
  128. char rmt_ip_str[16]; /* for set env REMOTE_ADDR */
  129. #endif
  130. unsigned port; /* server initial port and for
  131. set env REMOTE_PORT */
  132. const char *found_mime_type;
  133. const char *found_moved_temporarily;
  134. off_t ContentLength; /* -1 - unknown */
  135. time_t last_mod;
  136. Htaccess_IP *ip_a_d; /* config allow/deny lines */
  137. int flg_deny_all;
  138. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  139. Htaccess *auth; /* config user:password lines */
  140. #endif
  141. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  142. Htaccess *mime_a; /* config mime types */
  143. #endif
  144. int server_socket;
  145. int accepted_socket;
  146. volatile int alarm_signaled;
  147. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  148. Htaccess *script_i; /* config script interpreters */
  149. #endif
  150. } HttpdConfig;
  151. static HttpdConfig *config;
  152. static const char request_GET[] = "GET"; /* size algorithmic optimize */
  153. static const char* const suffixTable [] = {
  154. /* Warning: shorted equivalent suffix in one line must be first */
  155. ".htm.html", "text/html",
  156. ".jpg.jpeg", "image/jpeg",
  157. ".gif", "image/gif",
  158. ".png", "image/png",
  159. ".txt.h.c.cc.cpp", "text/plain",
  160. ".css", "text/css",
  161. ".wav", "audio/wav",
  162. ".avi", "video/x-msvideo",
  163. ".qt.mov", "video/quicktime",
  164. ".mpe.mpeg", "video/mpeg",
  165. ".mid.midi", "audio/midi",
  166. ".mp3", "audio/mpeg",
  167. #if 0 /* unpopular */
  168. ".au", "audio/basic",
  169. ".pac", "application/x-ns-proxy-autoconfig",
  170. ".vrml.wrl", "model/vrml",
  171. #endif
  172. 0, "application/octet-stream" /* default */
  173. };
  174. typedef enum {
  175. HTTP_OK = 200,
  176. HTTP_MOVED_TEMPORARILY = 302,
  177. HTTP_BAD_REQUEST = 400, /* malformed syntax */
  178. HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
  179. HTTP_NOT_FOUND = 404,
  180. HTTP_FORBIDDEN = 403,
  181. HTTP_REQUEST_TIMEOUT = 408,
  182. HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
  183. HTTP_INTERNAL_SERVER_ERROR = 500,
  184. #if 0 /* future use */
  185. HTTP_CONTINUE = 100,
  186. HTTP_SWITCHING_PROTOCOLS = 101,
  187. HTTP_CREATED = 201,
  188. HTTP_ACCEPTED = 202,
  189. HTTP_NON_AUTHORITATIVE_INFO = 203,
  190. HTTP_NO_CONTENT = 204,
  191. HTTP_MULTIPLE_CHOICES = 300,
  192. HTTP_MOVED_PERMANENTLY = 301,
  193. HTTP_NOT_MODIFIED = 304,
  194. HTTP_PAYMENT_REQUIRED = 402,
  195. HTTP_BAD_GATEWAY = 502,
  196. HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
  197. HTTP_RESPONSE_SETSIZE = 0xffffffff
  198. #endif
  199. } HttpResponseNum;
  200. typedef struct {
  201. HttpResponseNum type;
  202. const char *name;
  203. const char *info;
  204. } HttpEnumString;
  205. static const HttpEnumString httpResponseNames[] = {
  206. { HTTP_OK, "OK", NULL },
  207. { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
  208. { HTTP_REQUEST_TIMEOUT, "Request Timeout",
  209. "No request appeared within a reasonable time period." },
  210. { HTTP_NOT_IMPLEMENTED, "Not Implemented",
  211. "The requested method is not recognized by this server." },
  212. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  213. { HTTP_UNAUTHORIZED, "Unauthorized", "" },
  214. #endif
  215. { HTTP_NOT_FOUND, "Not Found",
  216. "The requested URL was not found on this server." },
  217. { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
  218. { HTTP_FORBIDDEN, "Forbidden", "" },
  219. { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
  220. "Internal Server Error" },
  221. #if 0 /* not implemented */
  222. { HTTP_CREATED, "Created" },
  223. { HTTP_ACCEPTED, "Accepted" },
  224. { HTTP_NO_CONTENT, "No Content" },
  225. { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
  226. { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
  227. { HTTP_NOT_MODIFIED, "Not Modified" },
  228. { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
  229. { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
  230. #endif
  231. };
  232. static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
  233. #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
  234. static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc)
  235. {
  236. const char *p = *ep;
  237. int auto_mask = 8;
  238. int j;
  239. *ip = 0;
  240. for (j = 0; j < 4; j++) {
  241. unsigned int octet;
  242. if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
  243. return -auto_mask;
  244. octet = 0;
  245. while (*p >= '0' && *p <= '9') {
  246. octet *= 10;
  247. octet += *p - '0';
  248. if (octet > 255)
  249. return -auto_mask;
  250. p++;
  251. }
  252. if (*p == '.')
  253. p++;
  254. if (*p != '/' && *p != 0)
  255. auto_mask += 8;
  256. *ip = ((*ip) << 8) | octet;
  257. }
  258. if (*p != 0) {
  259. if (*p != endc)
  260. return -auto_mask;
  261. p++;
  262. if (*p == 0)
  263. return -auto_mask;
  264. }
  265. *ep = p;
  266. return auto_mask;
  267. }
  268. static int scan_ip_mask(const char *ipm, unsigned int *ip, unsigned int *mask)
  269. {
  270. int i;
  271. unsigned int msk;
  272. i = scan_ip(&ipm, ip, '/');
  273. if (i < 0)
  274. return i;
  275. if (*ipm) {
  276. const char *p = ipm;
  277. i = 0;
  278. while (*p) {
  279. if (*p < '0' || *p > '9') {
  280. if (*p == '.') {
  281. i = scan_ip(&ipm, mask, 0);
  282. return i != 32;
  283. }
  284. return -1;
  285. }
  286. i *= 10;
  287. i += *p - '0';
  288. p++;
  289. }
  290. }
  291. if (i > 32 || i < 0)
  292. return -1;
  293. msk = 0x80000000;
  294. *mask = 0;
  295. while (i > 0) {
  296. *mask |= msk;
  297. msk >>= 1;
  298. i--;
  299. }
  300. return 0;
  301. }
  302. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  303. static void free_config_lines(Htaccess **pprev)
  304. {
  305. Htaccess *prev = *pprev;
  306. while (prev) {
  307. Htaccess *cur = prev;
  308. prev = cur->next;
  309. free(cur);
  310. }
  311. *pprev = NULL;
  312. }
  313. #endif
  314. /* flag */
  315. #define FIRST_PARSE 0
  316. #define SUBDIR_PARSE 1
  317. #define SIGNALED_PARSE 2
  318. #define FIND_FROM_HTTPD_ROOT 3
  319. /****************************************************************************
  320. *
  321. > $Function: parse_conf()
  322. *
  323. * $Description: parse configuration file into in-memory linked list.
  324. *
  325. * The first non-white character is examined to determine if the config line
  326. * is one of the following:
  327. * .ext:mime/type # new mime type not compiled into httpd
  328. * [adAD]:from # ip address allow/deny, * for wildcard
  329. * /path:user:pass # username/password
  330. *
  331. * Any previous IP rules are discarded.
  332. * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
  333. * are also discarded. That is, previous settings are retained if flag is
  334. * SUBDIR_PARSE.
  335. *
  336. * $Parameters:
  337. * (const char *) path . . null for ip address checks, path for password
  338. * checks.
  339. * (int) flag . . . . . . the source of the parse request.
  340. *
  341. * $Return: (None)
  342. *
  343. ****************************************************************************/
  344. static void parse_conf(const char *path, int flag)
  345. {
  346. FILE *f;
  347. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  348. Htaccess *prev, *cur;
  349. #elif ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  350. Htaccess *cur;
  351. #endif
  352. const char *cf = config->configFile;
  353. char buf[160];
  354. char *p0 = NULL;
  355. char *c, *p;
  356. /* free previous ip setup if present */
  357. Htaccess_IP *pip = config->ip_a_d;
  358. while (pip) {
  359. Htaccess_IP *cur_ipl = pip;
  360. pip = cur_ipl->next;
  361. free(cur_ipl);
  362. }
  363. config->ip_a_d = NULL;
  364. config->flg_deny_all = 0;
  365. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  366. /* retain previous auth and mime config only for subdir parse */
  367. if (flag != SUBDIR_PARSE) {
  368. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  369. free_config_lines(&config->auth);
  370. #endif
  371. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  372. free_config_lines(&config->mime_a);
  373. #endif
  374. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  375. free_config_lines(&config->script_i);
  376. #endif
  377. }
  378. #endif
  379. if (flag == SUBDIR_PARSE || cf == NULL) {
  380. cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
  381. if (cf == NULL) {
  382. if (flag == FIRST_PARSE)
  383. bb_error_msg_and_die(bb_msg_memory_exhausted);
  384. return;
  385. }
  386. sprintf((char *)cf, "%s/%s", path, httpd_conf);
  387. }
  388. while ((f = fopen(cf, "r")) == NULL) {
  389. if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
  390. /* config file not found, no changes to config */
  391. return;
  392. }
  393. if (config->configFile && flag == FIRST_PARSE) /* if -c option given */
  394. bb_perror_msg_and_die("%s", cf);
  395. flag = FIND_FROM_HTTPD_ROOT;
  396. cf = httpd_conf;
  397. }
  398. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  399. prev = config->auth;
  400. #endif
  401. /* This could stand some work */
  402. while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
  403. c = NULL;
  404. for (p = p0; *p0 != 0 && *p0 != '#'; p0++) {
  405. if (!isspace(*p0)) {
  406. *p++ = *p0;
  407. if (*p0 == ':' && c == NULL)
  408. c = p;
  409. }
  410. }
  411. *p = 0;
  412. /* test for empty or strange line */
  413. if (c == NULL || *c == 0)
  414. continue;
  415. p0 = buf;
  416. if (*p0 == 'd')
  417. *p0 = 'D';
  418. if (*c == '*') {
  419. if (*p0 == 'D') {
  420. /* memorize deny all */
  421. config->flg_deny_all++;
  422. }
  423. /* skip default other "word:*" config lines */
  424. continue;
  425. }
  426. if (*p0 == 'a')
  427. *p0 = 'A';
  428. else if (*p0 != 'D' && *p0 != 'A'
  429. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  430. && *p0 != '/'
  431. #endif
  432. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  433. && *p0 != '.'
  434. #endif
  435. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  436. && *p0 != '*'
  437. #endif
  438. )
  439. continue;
  440. if (*p0 == 'A' || *p0 == 'D') {
  441. /* storing current config IP line */
  442. pip = calloc(1, sizeof(Htaccess_IP));
  443. if (pip) {
  444. if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
  445. /* syntax IP{/mask} error detected, protect all */
  446. *p0 = 'D';
  447. pip->mask = 0;
  448. }
  449. pip->allow_deny = *p0;
  450. if (*p0 == 'D') {
  451. /* Deny:form_IP move top */
  452. pip->next = config->ip_a_d;
  453. config->ip_a_d = pip;
  454. } else {
  455. /* add to bottom A:form_IP config line */
  456. Htaccess_IP *prev_IP = config->ip_a_d;
  457. if (prev_IP == NULL) {
  458. config->ip_a_d = pip;
  459. } else {
  460. while (prev_IP->next)
  461. prev_IP = prev_IP->next;
  462. prev_IP->next = pip;
  463. }
  464. }
  465. }
  466. continue;
  467. }
  468. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  469. if (*p0 == '/') {
  470. /* make full path from httpd root / curent_path / config_line_path */
  471. cf = flag == SUBDIR_PARSE ? path : "";
  472. p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
  473. if (p0 == NULL)
  474. continue;
  475. c[-1] = 0;
  476. sprintf(p0, "/%s%s", cf, buf);
  477. /* another call bb_simplify_path */
  478. cf = p = p0;
  479. do {
  480. if (*p == '/') {
  481. if (*cf == '/') { /* skip duplicate (or initial) slash */
  482. continue;
  483. } else if (*cf == '.') {
  484. if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
  485. continue;
  486. } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
  487. ++cf;
  488. if (p > p0) {
  489. while (*--p != '/') /* omit previous dir */;
  490. }
  491. continue;
  492. }
  493. }
  494. }
  495. *++p = *cf;
  496. } while (*++cf);
  497. if ((p == p0) || (*p != '/')) { /* not a trailing slash */
  498. ++p; /* so keep last character */
  499. }
  500. *p = 0;
  501. sprintf(p0, "%s:%s", p0, c);
  502. }
  503. #endif
  504. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  505. /* storing current config line */
  506. cur = calloc(1, sizeof(Htaccess) + strlen(p0));
  507. if (cur) {
  508. cf = strcpy(cur->before_colon, p0);
  509. c = strchr(cf, ':');
  510. *c++ = 0;
  511. cur->after_colon = c;
  512. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  513. if (*cf == '.') {
  514. /* config .mime line move top for overwrite previous */
  515. cur->next = config->mime_a;
  516. config->mime_a = cur;
  517. continue;
  518. }
  519. #endif
  520. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  521. if (*cf == '*' && cf[1] == '.') {
  522. /* config script interpreter line move top for overwrite previous */
  523. cur->next = config->script_i;
  524. config->script_i = cur;
  525. continue;
  526. }
  527. #endif
  528. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  529. free(p0);
  530. if (prev == NULL) {
  531. /* first line */
  532. config->auth = prev = cur;
  533. } else {
  534. /* sort path, if current lenght eq or bigger then move up */
  535. Htaccess *prev_hti = config->auth;
  536. size_t l = strlen(cf);
  537. Htaccess *hti;
  538. for (hti = prev_hti; hti; hti = hti->next) {
  539. if (l >= strlen(hti->before_colon)) {
  540. /* insert before hti */
  541. cur->next = hti;
  542. if (prev_hti != hti) {
  543. prev_hti->next = cur;
  544. } else {
  545. /* insert as top */
  546. config->auth = cur;
  547. }
  548. break;
  549. }
  550. if (prev_hti != hti)
  551. prev_hti = prev_hti->next;
  552. }
  553. if (!hti) { /* not inserted, add to bottom */
  554. prev->next = cur;
  555. prev = cur;
  556. }
  557. }
  558. #endif
  559. }
  560. #endif
  561. }
  562. fclose(f);
  563. }
  564. #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
  565. /****************************************************************************
  566. *
  567. > $Function: encodeString()
  568. *
  569. * $Description: Given a string, html encode special characters.
  570. * This is used for the -e command line option to provide an easy way
  571. * for scripts to encode result data without confusing browsers. The
  572. * returned string pointer is memory allocated by malloc().
  573. *
  574. * $Parameters:
  575. * (const char *) string . . The first string to encode.
  576. *
  577. * $Return: (char *) . . . .. . . A pointer to the encoded string.
  578. *
  579. * $Errors: Returns a null string ("") if memory is not available.
  580. *
  581. ****************************************************************************/
  582. static char *encodeString(const char *string)
  583. {
  584. /* take the simple route and encode everything */
  585. /* could possibly scan once to get length. */
  586. int len = strlen(string);
  587. char *out = malloc(len * 6 + 1);
  588. char *p = out;
  589. char ch;
  590. if (!out) return "";
  591. while ((ch = *string++)) {
  592. // very simple check for what to encode
  593. if (isalnum(ch)) *p++ = ch;
  594. else p += sprintf(p, "&#%d;", (unsigned char) ch);
  595. }
  596. *p = 0;
  597. return out;
  598. }
  599. #endif /* FEATURE_HTTPD_ENCODE_URL_STR */
  600. /****************************************************************************
  601. *
  602. > $Function: decodeString()
  603. *
  604. * $Description: Given a URL encoded string, convert it to plain ascii.
  605. * Since decoding always makes strings smaller, the decode is done in-place.
  606. * Thus, callers should strdup() the argument if they do not want the
  607. * argument modified. The return is the original pointer, allowing this
  608. * function to be easily used as arguments to other functions.
  609. *
  610. * $Parameters:
  611. * (char *) string . . . The first string to decode.
  612. * (int) option_d . . 1 if called for httpd -d
  613. *
  614. * $Return: (char *) . . . . A pointer to the decoded string (same as input).
  615. *
  616. * $Errors: None
  617. *
  618. ****************************************************************************/
  619. static char *decodeString(char *orig, int option_d)
  620. {
  621. /* note that decoded string is always shorter than original */
  622. char *string = orig;
  623. char *ptr = string;
  624. char c;
  625. while ((c = *ptr++) != '\0') {
  626. unsigned value1, value2;
  627. if (option_d && c == '+') {
  628. *string++ = ' ';
  629. continue;
  630. }
  631. if (c != '%') {
  632. *string++ = c;
  633. continue;
  634. }
  635. if (sscanf(ptr, "%1X", &value1) != 1
  636. || sscanf(ptr+1, "%1X", &value2) != 1
  637. ) {
  638. if (!option_d)
  639. return NULL;
  640. *string++ = '%';
  641. continue;
  642. }
  643. value1 = value1 * 16 + value2;
  644. if (!option_d && (value1 == '/' || value1 == '\0')) {
  645. /* caller takes it as indication of invalid
  646. * (dangerous wrt exploits) chars */
  647. return orig + 1;
  648. }
  649. *string++ = value1;
  650. ptr += 2;
  651. }
  652. *string = '\0';
  653. return orig;
  654. }
  655. #if ENABLE_FEATURE_HTTPD_CGI
  656. /****************************************************************************
  657. * setenv helpers
  658. ****************************************************************************/
  659. static void setenv1(const char *name, const char *value)
  660. {
  661. if (!value)
  662. value = "";
  663. setenv(name, value, 1);
  664. }
  665. static void setenv_long(const char *name, long value)
  666. {
  667. char buf[sizeof(value)*3 + 1];
  668. sprintf(buf, "%ld", value);
  669. setenv(name, buf, 1);
  670. }
  671. #endif
  672. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  673. /****************************************************************************
  674. *
  675. > $Function: decodeBase64()
  676. *
  677. > $Description: Decode a base 64 data stream as per rfc1521.
  678. * Note that the rfc states that none base64 chars are to be ignored.
  679. * Since the decode always results in a shorter size than the input, it is
  680. * OK to pass the input arg as an output arg.
  681. *
  682. * $Parameter:
  683. * (char *) Data . . . . A pointer to a base64 encoded string.
  684. * Where to place the decoded data.
  685. *
  686. * $Return: void
  687. *
  688. * $Errors: None
  689. *
  690. ****************************************************************************/
  691. static void decodeBase64(char *Data)
  692. {
  693. const unsigned char *in = (const unsigned char *)Data;
  694. // The decoded size will be at most 3/4 the size of the encoded
  695. unsigned long ch = 0;
  696. int i = 0;
  697. while (*in) {
  698. int t = *in++;
  699. if (t >= '0' && t <= '9')
  700. t = t - '0' + 52;
  701. else if (t >= 'A' && t <= 'Z')
  702. t = t - 'A';
  703. else if (t >= 'a' && t <= 'z')
  704. t = t - 'a' + 26;
  705. else if (t == '+')
  706. t = 62;
  707. else if (t == '/')
  708. t = 63;
  709. else if (t == '=')
  710. t = 0;
  711. else
  712. continue;
  713. ch = (ch << 6) | t;
  714. i++;
  715. if (i == 4) {
  716. *Data++ = (char) (ch >> 16);
  717. *Data++ = (char) (ch >> 8);
  718. *Data++ = (char) ch;
  719. i = 0;
  720. }
  721. }
  722. *Data = 0;
  723. }
  724. #endif
  725. /****************************************************************************
  726. *
  727. > $Function: openServer()
  728. *
  729. * $Description: create a listen server socket on the designated port.
  730. *
  731. * $Return: (int) . . . A connection socket. -1 for errors.
  732. *
  733. * $Errors: None
  734. *
  735. ****************************************************************************/
  736. static int openServer(void)
  737. {
  738. struct sockaddr_in lsocket;
  739. int fd;
  740. /* create the socket right now */
  741. /* inet_addr() returns a value that is already in network order */
  742. memset(&lsocket, 0, sizeof(lsocket));
  743. lsocket.sin_family = AF_INET;
  744. lsocket.sin_addr.s_addr = INADDR_ANY;
  745. lsocket.sin_port = htons(config->port);
  746. fd = xsocket(AF_INET, SOCK_STREAM, 0);
  747. /* tell the OS it's OK to reuse a previous address even though */
  748. /* it may still be in a close down state. Allows bind to succeed. */
  749. #ifdef SO_REUSEPORT
  750. {
  751. static const int on = 1;
  752. setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
  753. (void *)&on, sizeof(on));
  754. }
  755. #else
  756. setsockopt_reuseaddr(fd);
  757. #endif
  758. xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket));
  759. xlisten(fd, 9);
  760. signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
  761. return fd;
  762. }
  763. /****************************************************************************
  764. *
  765. > $Function: sendHeaders()
  766. *
  767. * $Description: Create and send HTTP response headers.
  768. * The arguments are combined and sent as one write operation. Note that
  769. * IE will puke big-time if the headers are not sent in one packet and the
  770. * second packet is delayed for any reason.
  771. *
  772. * $Parameter:
  773. * (HttpResponseNum) responseNum . . . The result code to send.
  774. *
  775. * $Return: (int) . . . . writing errors
  776. *
  777. ****************************************************************************/
  778. static int sendHeaders(HttpResponseNum responseNum)
  779. {
  780. char *buf = config->buf;
  781. const char *responseString = "";
  782. const char *infoString = 0;
  783. const char *mime_type;
  784. unsigned int i;
  785. time_t timer = time(0);
  786. char timeStr[80];
  787. int len;
  788. enum {
  789. numNames = sizeof(httpResponseNames) / sizeof(httpResponseNames[0])
  790. };
  791. for (i = 0; i < numNames; i++) {
  792. if (httpResponseNames[i].type == responseNum) {
  793. responseString = httpResponseNames[i].name;
  794. infoString = httpResponseNames[i].info;
  795. break;
  796. }
  797. }
  798. /* error message is HTML */
  799. mime_type = responseNum == HTTP_OK ?
  800. config->found_mime_type : "text/html";
  801. /* emit the current date */
  802. strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
  803. len = sprintf(buf,
  804. "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
  805. "Date: %s\r\nConnection: close\r\n",
  806. responseNum, responseString, mime_type, timeStr);
  807. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  808. if (responseNum == HTTP_UNAUTHORIZED) {
  809. len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
  810. config->realm);
  811. }
  812. #endif
  813. if (responseNum == HTTP_MOVED_TEMPORARILY) {
  814. len += sprintf(buf+len, "Location: %s/%s%s\r\n",
  815. config->found_moved_temporarily,
  816. (config->query ? "?" : ""),
  817. (config->query ? config->query : ""));
  818. }
  819. if (config->ContentLength != -1) { /* file */
  820. strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
  821. len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
  822. timeStr, "Content-length:", config->ContentLength);
  823. }
  824. strcat(buf, "\r\n");
  825. len += 2;
  826. if (infoString) {
  827. len += sprintf(buf+len,
  828. "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
  829. "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
  830. responseNum, responseString,
  831. responseNum, responseString, infoString);
  832. }
  833. if (DEBUG)
  834. fprintf(stderr, "headers: '%s'\n", buf);
  835. return full_write(config->accepted_socket, buf, len);
  836. }
  837. /****************************************************************************
  838. *
  839. > $Function: getLine()
  840. *
  841. * $Description: Read from the socket until an end of line char found.
  842. *
  843. * Characters are read one at a time until an eol sequence is found.
  844. *
  845. * $Return: (int) . . . . number of characters read. -1 if error.
  846. *
  847. ****************************************************************************/
  848. static int getLine(void)
  849. {
  850. int count = 0;
  851. char *buf = config->buf;
  852. while (read(config->accepted_socket, buf + count, 1) == 1) {
  853. if (buf[count] == '\r') continue;
  854. if (buf[count] == '\n') {
  855. buf[count] = 0;
  856. return count;
  857. }
  858. if (count < (MAX_MEMORY_BUFF-1)) /* check overflow */
  859. count++;
  860. }
  861. if (count) return count;
  862. else return -1;
  863. }
  864. #if ENABLE_FEATURE_HTTPD_CGI
  865. /****************************************************************************
  866. *
  867. > $Function: sendCgi()
  868. *
  869. * $Description: Execute a CGI script and send it's stdout back
  870. *
  871. * Environment variables are set up and the script is invoked with pipes
  872. * for stdin/stdout. If a post is being done the script is fed the POST
  873. * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
  874. *
  875. * $Parameters:
  876. * (const char *) url . . . . . . The requested URL (with leading /).
  877. * (int bodyLen) . . . . . . . . Length of the post body.
  878. * (const char *cookie) . . . . . For set HTTP_COOKIE.
  879. * (const char *content_type) . . For set CONTENT_TYPE.
  880. *
  881. * $Return: (char *) . . . . A pointer to the decoded string (same as input).
  882. *
  883. * $Errors: None
  884. *
  885. ****************************************************************************/
  886. static int sendCgi(const char *url,
  887. const char *request, int bodyLen, const char *cookie,
  888. const char *content_type)
  889. {
  890. int fromCgi[2]; /* pipe for reading data from CGI */
  891. int toCgi[2]; /* pipe for sending data to CGI */
  892. static char * argp[] = { 0, 0 };
  893. int pid = 0;
  894. int inFd;
  895. int outFd;
  896. int firstLine = 1;
  897. int status;
  898. size_t post_readed_size, post_readed_idx;
  899. if (pipe(fromCgi) != 0)
  900. return 0;
  901. if (pipe(toCgi) != 0)
  902. return 0;
  903. pid = fork();
  904. if (pid < 0)
  905. return 0;
  906. if (!pid) {
  907. /* child process */
  908. char *script;
  909. char *purl = strdup(url);
  910. char realpath_buff[MAXPATHLEN];
  911. if (purl == NULL)
  912. _exit(242);
  913. inFd = toCgi[0];
  914. outFd = fromCgi[1];
  915. dup2(inFd, 0); // replace stdin with the pipe
  916. dup2(outFd, 1); // replace stdout with the pipe
  917. if (!DEBUG)
  918. dup2(outFd, 2); // replace stderr with the pipe
  919. close(toCgi[0]);
  920. close(toCgi[1]);
  921. close(fromCgi[0]);
  922. close(fromCgi[1]);
  923. close(config->accepted_socket);
  924. close(config->server_socket);
  925. /*
  926. * Find PATH_INFO.
  927. */
  928. script = purl;
  929. while ((script = strchr(script + 1, '/')) != NULL) {
  930. /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
  931. struct stat sb;
  932. *script = '\0';
  933. if (is_directory(purl + 1, 1, &sb) == 0) {
  934. /* not directory, found script.cgi/PATH_INFO */
  935. *script = '/';
  936. break;
  937. }
  938. *script = '/'; /* is directory, find next '/' */
  939. }
  940. setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */
  941. /* setenv1("PATH", getenv("PATH")); redundant */
  942. setenv1("REQUEST_METHOD", request);
  943. if (config->query) {
  944. char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
  945. if (uri)
  946. sprintf(uri, "%s?%s", purl, config->query);
  947. setenv1("REQUEST_URI", uri);
  948. } else {
  949. setenv1("REQUEST_URI", purl);
  950. }
  951. if (script != NULL)
  952. *script = '\0'; /* cut off /PATH_INFO */
  953. /* SCRIPT_FILENAME required by PHP in CGI mode */
  954. if (!realpath(purl + 1, realpath_buff))
  955. goto error_execing_cgi;
  956. setenv1("SCRIPT_FILENAME", realpath_buff);
  957. /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
  958. setenv1("SCRIPT_NAME", purl);
  959. /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
  960. * QUERY_STRING: The information which follows the ? in the URL
  961. * which referenced this script. This is the query information.
  962. * It should not be decoded in any fashion. This variable
  963. * should always be set when there is query information,
  964. * regardless of command line decoding. */
  965. /* (Older versions of bbox seemed to do some decoding) */
  966. setenv1("QUERY_STRING", config->query);
  967. setenv1("SERVER_SOFTWARE", httpdVersion);
  968. putenv("SERVER_PROTOCOL=HTTP/1.0");
  969. putenv("GATEWAY_INTERFACE=CGI/1.1");
  970. setenv1("REMOTE_ADDR", config->rmt_ip_str);
  971. #if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
  972. setenv_long("REMOTE_PORT", config->port);
  973. #endif
  974. if (bodyLen)
  975. setenv_long("CONTENT_LENGTH", bodyLen);
  976. if (cookie)
  977. setenv1("HTTP_COOKIE", cookie);
  978. if (content_type)
  979. setenv1("CONTENT_TYPE", content_type);
  980. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  981. if (config->remoteuser) {
  982. setenv1("REMOTE_USER", config->remoteuser);
  983. putenv("AUTH_TYPE=Basic");
  984. }
  985. #endif
  986. if (config->referer)
  987. setenv1("HTTP_REFERER", config->referer);
  988. /* set execve argp[0] without path */
  989. argp[0] = strrchr(purl, '/') + 1;
  990. /* but script argp[0] must have absolute path and chdiring to this */
  991. script = strrchr(realpath_buff, '/');
  992. if (!script)
  993. goto error_execing_cgi;
  994. *script = '\0';
  995. if (chdir(realpath_buff) == 0) {
  996. // now run the program. If it fails,
  997. // use _exit() so no destructors
  998. // get called and make a mess.
  999. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  1000. char *interpr = NULL;
  1001. char *suffix = strrchr(purl, '.');
  1002. if (suffix) {
  1003. Htaccess *cur;
  1004. for (cur = config->script_i; cur; cur = cur->next) {
  1005. if (strcmp(cur->before_colon + 1, suffix) == 0) {
  1006. interpr = cur->after_colon;
  1007. break;
  1008. }
  1009. }
  1010. }
  1011. #endif
  1012. *script = '/';
  1013. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
  1014. if (interpr)
  1015. execv(interpr, argp);
  1016. else
  1017. #endif
  1018. execv(realpath_buff, argp);
  1019. }
  1020. error_execing_cgi:
  1021. /* send to stdout (even if we are not from inetd) */
  1022. config->accepted_socket = 1;
  1023. sendHeaders(HTTP_NOT_FOUND);
  1024. _exit(242);
  1025. } /* end child */
  1026. /* parent process */
  1027. post_readed_size = 0;
  1028. post_readed_idx = 0;
  1029. inFd = fromCgi[0];
  1030. outFd = toCgi[1];
  1031. close(fromCgi[1]);
  1032. close(toCgi[0]);
  1033. signal(SIGPIPE, SIG_IGN);
  1034. while (1) {
  1035. fd_set readSet;
  1036. fd_set writeSet;
  1037. char wbuf[128];
  1038. int nfound;
  1039. int count;
  1040. FD_ZERO(&readSet);
  1041. FD_ZERO(&writeSet);
  1042. FD_SET(inFd, &readSet);
  1043. if (bodyLen > 0 || post_readed_size > 0) {
  1044. FD_SET(outFd, &writeSet);
  1045. nfound = outFd > inFd ? outFd : inFd;
  1046. if (post_readed_size == 0) {
  1047. FD_SET(config->accepted_socket, &readSet);
  1048. if (nfound < config->accepted_socket)
  1049. nfound = config->accepted_socket;
  1050. }
  1051. /* Now wait on the set of sockets! */
  1052. nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL);
  1053. } else {
  1054. if (!bodyLen) {
  1055. close(outFd);
  1056. bodyLen = -1;
  1057. }
  1058. nfound = select(inFd + 1, &readSet, 0, 0, NULL);
  1059. }
  1060. if (nfound <= 0) {
  1061. if (waitpid(pid, &status, WNOHANG) > 0) {
  1062. close(inFd);
  1063. if (DEBUG && WIFEXITED(status))
  1064. bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
  1065. if (DEBUG && WIFSIGNALED(status))
  1066. bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
  1067. break;
  1068. }
  1069. } else if (post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) {
  1070. count = full_write(outFd, wbuf + post_readed_idx, post_readed_size);
  1071. if (count > 0) {
  1072. post_readed_size -= count;
  1073. post_readed_idx += count;
  1074. if (post_readed_size == 0)
  1075. post_readed_idx = 0;
  1076. } else {
  1077. post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */
  1078. }
  1079. } else if (bodyLen > 0 && post_readed_size == 0 && FD_ISSET(config->accepted_socket, &readSet)) {
  1080. count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
  1081. count = safe_read(config->accepted_socket, wbuf, count);
  1082. if (count > 0) {
  1083. post_readed_size += count;
  1084. bodyLen -= count;
  1085. } else {
  1086. bodyLen = 0; /* closed */
  1087. }
  1088. }
  1089. if (FD_ISSET(inFd, &readSet)) {
  1090. int s = config->accepted_socket;
  1091. char *rbuf = config->buf;
  1092. #ifndef PIPE_BUF
  1093. # define PIPESIZE 4096 /* amount of buffering in a pipe */
  1094. #else
  1095. # define PIPESIZE PIPE_BUF
  1096. #endif
  1097. #if PIPESIZE >= MAX_MEMORY_BUFF
  1098. # error "PIPESIZE >= MAX_MEMORY_BUFF"
  1099. #endif
  1100. /* There is something to read */
  1101. count = safe_read(inFd, rbuf, PIPESIZE);
  1102. if (count == 0)
  1103. break; /* closed */
  1104. if (count > 0) {
  1105. if (firstLine) {
  1106. rbuf[count] = 0;
  1107. /* check to see if the user script added headers */
  1108. if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) {
  1109. full_write(s, "HTTP/1.0 200 OK\r\n", 17);
  1110. }
  1111. /* Sometimes CGI is writing to pipe in small chunks
  1112. * and we don't see Content-type (because the read
  1113. * is too short) and we emit bogus "text/plain"!
  1114. * Is it a bug or CGI *has to* write it in one piece? */
  1115. if (strstr(rbuf, "ontent-") == 0) {
  1116. full_write(s, "Content-type: text/plain\r\n\r\n", 28);
  1117. }
  1118. firstLine = 0;
  1119. }
  1120. if (full_write(s, rbuf, count) != count)
  1121. break;
  1122. if (DEBUG)
  1123. fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
  1124. }
  1125. }
  1126. }
  1127. return 0;
  1128. }
  1129. #endif /* FEATURE_HTTPD_CGI */
  1130. /****************************************************************************
  1131. *
  1132. > $Function: sendFile()
  1133. *
  1134. * $Description: Send a file response to a HTTP request
  1135. *
  1136. * $Parameter:
  1137. * (const char *) url . . The URL requested.
  1138. *
  1139. * $Return: (int) . . . . . . Always 0.
  1140. *
  1141. ****************************************************************************/
  1142. static int sendFile(const char *url)
  1143. {
  1144. char * suffix;
  1145. int f;
  1146. const char * const * table;
  1147. const char * try_suffix;
  1148. suffix = strrchr(url, '.');
  1149. for (table = suffixTable; *table; table += 2)
  1150. if (suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
  1151. try_suffix += strlen(suffix);
  1152. if (*try_suffix == 0 || *try_suffix == '.')
  1153. break;
  1154. }
  1155. /* also, if not found, set default as "application/octet-stream"; */
  1156. config->found_mime_type = table[1];
  1157. #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
  1158. if (suffix) {
  1159. Htaccess * cur;
  1160. for (cur = config->mime_a; cur; cur = cur->next) {
  1161. if (strcmp(cur->before_colon, suffix) == 0) {
  1162. config->found_mime_type = cur->after_colon;
  1163. break;
  1164. }
  1165. }
  1166. }
  1167. #endif /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
  1168. if (DEBUG)
  1169. fprintf(stderr, "sending file '%s' content-type: %s\n",
  1170. url, config->found_mime_type);
  1171. f = open(url, O_RDONLY);
  1172. if (f >= 0) {
  1173. int count;
  1174. char *buf = config->buf;
  1175. sendHeaders(HTTP_OK);
  1176. /* TODO: sendfile() */
  1177. while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
  1178. if (full_write(config->accepted_socket, buf, count) != count)
  1179. break;
  1180. }
  1181. close(f);
  1182. } else {
  1183. if (DEBUG)
  1184. bb_perror_msg("cannot open '%s'", url);
  1185. sendHeaders(HTTP_NOT_FOUND);
  1186. }
  1187. return 0;
  1188. }
  1189. static int checkPermIP(void)
  1190. {
  1191. Htaccess_IP * cur;
  1192. /* This could stand some work */
  1193. for (cur = config->ip_a_d; cur; cur = cur->next) {
  1194. if (DEBUG)
  1195. fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
  1196. if (DEBUG)
  1197. fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
  1198. (unsigned char)(cur->ip >> 24),
  1199. (unsigned char)(cur->ip >> 16),
  1200. (unsigned char)(cur->ip >> 8),
  1201. cur->ip & 0xff,
  1202. (unsigned char)(cur->mask >> 24),
  1203. (unsigned char)(cur->mask >> 16),
  1204. (unsigned char)(cur->mask >> 8),
  1205. cur->mask & 0xff);
  1206. if ((config->rmt_ip & cur->mask) == cur->ip)
  1207. return cur->allow_deny == 'A'; /* Allow/Deny */
  1208. }
  1209. /* if unconfigured, return 1 - access from all */
  1210. return !config->flg_deny_all;
  1211. }
  1212. /****************************************************************************
  1213. *
  1214. > $Function: checkPerm()
  1215. *
  1216. * $Description: Check the permission file for access password protected.
  1217. *
  1218. * If config file isn't present, everything is allowed.
  1219. * Entries are of the form you can see example from header source
  1220. *
  1221. * $Parameters:
  1222. * (const char *) path . . . . The file path.
  1223. * (const char *) request . . . User information to validate.
  1224. *
  1225. * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
  1226. *
  1227. ****************************************************************************/
  1228. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  1229. static int checkPerm(const char *path, const char *request)
  1230. {
  1231. Htaccess * cur;
  1232. const char *p;
  1233. const char *p0;
  1234. const char *prev = NULL;
  1235. /* This could stand some work */
  1236. for (cur = config->auth; cur; cur = cur->next) {
  1237. size_t l;
  1238. p0 = cur->before_colon;
  1239. if (prev != NULL && strcmp(prev, p0) != 0)
  1240. continue; /* find next identical */
  1241. p = cur->after_colon;
  1242. if (DEBUG)
  1243. fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request);
  1244. l = strlen(p0);
  1245. if (strncmp(p0, path, l) == 0
  1246. && (l == 1 || path[l] == '/' || path[l] == '\0')
  1247. ) {
  1248. char *u;
  1249. /* path match found. Check request */
  1250. /* for check next /path:user:password */
  1251. prev = p0;
  1252. u = strchr(request, ':');
  1253. if (u == NULL) {
  1254. /* bad request, ':' required */
  1255. break;
  1256. }
  1257. if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
  1258. char *cipher;
  1259. char *pp;
  1260. if (strncmp(p, request, u-request) != 0) {
  1261. /* user uncompared */
  1262. continue;
  1263. }
  1264. pp = strchr(p, ':');
  1265. if (pp && pp[1] == '$' && pp[2] == '1' &&
  1266. pp[3] == '$' && pp[4]) {
  1267. pp++;
  1268. cipher = pw_encrypt(u+1, pp);
  1269. if (strcmp(cipher, pp) == 0)
  1270. goto set_remoteuser_var; /* Ok */
  1271. /* unauthorized */
  1272. continue;
  1273. }
  1274. }
  1275. if (strcmp(p, request) == 0) {
  1276. set_remoteuser_var:
  1277. config->remoteuser = strdup(request);
  1278. if (config->remoteuser)
  1279. config->remoteuser[(u - request)] = 0;
  1280. return 1; /* Ok */
  1281. }
  1282. /* unauthorized */
  1283. }
  1284. } /* for */
  1285. return prev == NULL;
  1286. }
  1287. #endif /* FEATURE_HTTPD_BASIC_AUTH */
  1288. /****************************************************************************
  1289. *
  1290. > $Function: handle_sigalrm()
  1291. *
  1292. * $Description: Handle timeouts
  1293. *
  1294. ****************************************************************************/
  1295. static void handle_sigalrm(int sig)
  1296. {
  1297. sendHeaders(HTTP_REQUEST_TIMEOUT);
  1298. config->alarm_signaled = sig;
  1299. }
  1300. /****************************************************************************
  1301. *
  1302. > $Function: handleIncoming()
  1303. *
  1304. * $Description: Handle an incoming http request.
  1305. *
  1306. ****************************************************************************/
  1307. static void handleIncoming(void)
  1308. {
  1309. char *buf = config->buf;
  1310. char *url;
  1311. char *purl;
  1312. int blank = -1;
  1313. char *test;
  1314. struct stat sb;
  1315. int ip_allowed;
  1316. #if ENABLE_FEATURE_HTTPD_CGI
  1317. const char *prequest = request_GET;
  1318. unsigned long length = 0;
  1319. char *cookie = 0;
  1320. char *content_type = 0;
  1321. #endif
  1322. fd_set s_fd;
  1323. struct timeval tv;
  1324. int retval;
  1325. struct sigaction sa;
  1326. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  1327. int credentials = -1; /* if not required this is Ok */
  1328. #endif
  1329. sa.sa_handler = handle_sigalrm;
  1330. sigemptyset(&sa.sa_mask);
  1331. sa.sa_flags = 0; /* no SA_RESTART */
  1332. sigaction(SIGALRM, &sa, NULL);
  1333. do {
  1334. int count;
  1335. (void) alarm(TIMEOUT);
  1336. if (getLine() <= 0)
  1337. break; /* closed */
  1338. purl = strpbrk(buf, " \t");
  1339. if (purl == NULL) {
  1340. BAD_REQUEST:
  1341. sendHeaders(HTTP_BAD_REQUEST);
  1342. break;
  1343. }
  1344. *purl = '\0';
  1345. #if ENABLE_FEATURE_HTTPD_CGI
  1346. if (strcasecmp(buf, prequest) != 0) {
  1347. prequest = "POST";
  1348. if (strcasecmp(buf, prequest) != 0) {
  1349. sendHeaders(HTTP_NOT_IMPLEMENTED);
  1350. break;
  1351. }
  1352. }
  1353. #else
  1354. if (strcasecmp(buf, request_GET) != 0) {
  1355. sendHeaders(HTTP_NOT_IMPLEMENTED);
  1356. break;
  1357. }
  1358. #endif
  1359. *purl = ' ';
  1360. count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
  1361. if (count < 1 || buf[0] != '/') {
  1362. /* Garbled request/URL */
  1363. goto BAD_REQUEST;
  1364. }
  1365. url = alloca(strlen(buf) + sizeof("/index.html"));
  1366. if (url == NULL) {
  1367. sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
  1368. break;
  1369. }
  1370. strcpy(url, buf);
  1371. /* extract url args if present */
  1372. test = strchr(url, '?');
  1373. config->query = NULL;
  1374. if (test) {
  1375. *test++ = '\0';
  1376. config->query = test;
  1377. }
  1378. test = decodeString(url, 0);
  1379. if (test == NULL)
  1380. goto BAD_REQUEST;
  1381. if (test == url+1) {
  1382. /* '/' or NUL is encoded */
  1383. sendHeaders(HTTP_NOT_FOUND);
  1384. break;
  1385. }
  1386. /* algorithm stolen from libbb bb_simplify_path(),
  1387. but don't strdup and reducing trailing slash and protect out root */
  1388. purl = test = url;
  1389. do {
  1390. if (*purl == '/') {
  1391. /* skip duplicate (or initial) slash */
  1392. if (*test == '/') {
  1393. continue;
  1394. }
  1395. if (*test == '.') {
  1396. /* skip extra '.' */
  1397. if (test[1] == '/' || test[1] == 0) {
  1398. continue;
  1399. } else
  1400. /* '..': be careful */
  1401. if (test[1] == '.' && (test[2] == '/' || test[2] == 0)) {
  1402. ++test;
  1403. if (purl == url) {
  1404. /* protect out root */
  1405. goto BAD_REQUEST;
  1406. }
  1407. while (*--purl != '/') /* omit previous dir */;
  1408. continue;
  1409. }
  1410. }
  1411. }
  1412. *++purl = *test;
  1413. } while (*++test);
  1414. *++purl = '\0'; /* so keep last character */
  1415. test = purl; /* end ptr */
  1416. /* If URL is directory, adding '/' */
  1417. if (test[-1] != '/') {
  1418. if (is_directory(url + 1, 1, &sb)) {
  1419. config->found_moved_temporarily = url;
  1420. }
  1421. }
  1422. if (DEBUG)
  1423. fprintf(stderr, "url='%s', args=%s\n", url, config->query);
  1424. test = url;
  1425. ip_allowed = checkPermIP();
  1426. while (ip_allowed && (test = strchr(test + 1, '/')) != NULL) {
  1427. /* have path1/path2 */
  1428. *test = '\0';
  1429. if (is_directory(url + 1, 1, &sb)) {
  1430. /* may be having subdir config */
  1431. parse_conf(url + 1, SUBDIR_PARSE);
  1432. ip_allowed = checkPermIP();
  1433. }
  1434. *test = '/';
  1435. }
  1436. if (blank >= 0) {
  1437. /* read until blank line for HTTP version specified, else parse immediate */
  1438. while (1) {
  1439. alarm(TIMEOUT);
  1440. count = getLine();
  1441. if (count <= 0)
  1442. break;
  1443. if (DEBUG)
  1444. fprintf(stderr, "header: '%s'\n", buf);
  1445. #if ENABLE_FEATURE_HTTPD_CGI
  1446. /* try and do our best to parse more lines */
  1447. if ((STRNCASECMP(buf, "Content-length:") == 0)) {
  1448. /* extra read only for POST */
  1449. if (prequest != request_GET) {
  1450. test = buf + sizeof("Content-length:")-1;
  1451. if (!test[0]) goto bail_out;
  1452. errno = 0;
  1453. /* not using strtoul: it ignores leading munis! */
  1454. length = strtol(test, &test, 10);
  1455. /* length is "ulong", but we need to pass it to int later */
  1456. /* so we check for negative or too large values in one go: */
  1457. /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */
  1458. if (test[0] || errno || length > INT_MAX)
  1459. goto bail_out;
  1460. }
  1461. } else if ((STRNCASECMP(buf, "Cookie:") == 0)) {
  1462. cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1));
  1463. } else if ((STRNCASECMP(buf, "Content-Type:") == 0)) {
  1464. content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
  1465. } else if ((STRNCASECMP(buf, "Referer:") == 0)) {
  1466. config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
  1467. }
  1468. #endif
  1469. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  1470. if (STRNCASECMP(buf, "Authorization:") == 0) {
  1471. /* We only allow Basic credentials.
  1472. * It shows up as "Authorization: Basic <userid:password>" where
  1473. * the userid:password is base64 encoded.
  1474. */
  1475. test = skip_whitespace(buf + sizeof("Authorization:")-1);
  1476. if (STRNCASECMP(test, "Basic") != 0)
  1477. continue;
  1478. test += sizeof("Basic")-1;
  1479. /* decodeBase64() skips whitespace itself */
  1480. decodeBase64(test);
  1481. credentials = checkPerm(url, test);
  1482. }
  1483. #endif /* FEATURE_HTTPD_BASIC_AUTH */
  1484. } /* while extra header reading */
  1485. }
  1486. alarm(0);
  1487. if (config->alarm_signaled)
  1488. break;
  1489. if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
  1490. /* protect listing [/path]/httpd_conf or IP deny */
  1491. #if ENABLE_FEATURE_HTTPD_CGI
  1492. FORBIDDEN: /* protect listing /cgi-bin */
  1493. #endif
  1494. sendHeaders(HTTP_FORBIDDEN);
  1495. break;
  1496. }
  1497. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  1498. if (credentials <= 0 && checkPerm(url, ":") == 0) {
  1499. sendHeaders(HTTP_UNAUTHORIZED);
  1500. break;
  1501. }
  1502. #endif
  1503. if (config->found_moved_temporarily) {
  1504. sendHeaders(HTTP_MOVED_TEMPORARILY);
  1505. /* clear unforked memory flag */
  1506. config->found_moved_temporarily = NULL;
  1507. break;
  1508. }
  1509. test = url + 1; /* skip first '/' */
  1510. #if ENABLE_FEATURE_HTTPD_CGI
  1511. if (strncmp(test, "cgi-bin", 7) == 0) {
  1512. if (test[7] == '/' && test[8] == 0)
  1513. goto FORBIDDEN; /* protect listing cgi-bin/ */
  1514. sendCgi(url, prequest, length, cookie, content_type);
  1515. break;
  1516. }
  1517. if (prequest != request_GET) {
  1518. sendHeaders(HTTP_NOT_IMPLEMENTED);
  1519. break;
  1520. }
  1521. #endif /* FEATURE_HTTPD_CGI */
  1522. if (purl[-1] == '/')
  1523. strcpy(purl, "index.html");
  1524. if (stat(test, &sb) == 0) {
  1525. /* It's a dir URL and there is index.html */
  1526. config->ContentLength = sb.st_size;
  1527. config->last_mod = sb.st_mtime;
  1528. }
  1529. #if ENABLE_FEATURE_HTTPD_CGI
  1530. else if (purl[-1] == '/') {
  1531. /* It's a dir URL and there is no index.html
  1532. * Try cgi-bin/index.cgi */
  1533. if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
  1534. purl[0] = '\0';
  1535. config->query = url;
  1536. sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
  1537. break;
  1538. }
  1539. }
  1540. #endif /* FEATURE_HTTPD_CGI */
  1541. sendFile(test);
  1542. config->ContentLength = -1;
  1543. } while (0);
  1544. bail_out:
  1545. if (DEBUG)
  1546. fprintf(stderr, "closing socket\n\n");
  1547. #if ENABLE_FEATURE_HTTPD_CGI
  1548. free(cookie);
  1549. free(content_type);
  1550. free(config->referer);
  1551. config->referer = NULL;
  1552. # if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  1553. free(config->remoteuser);
  1554. config->remoteuser = NULL;
  1555. # endif
  1556. #endif
  1557. shutdown(config->accepted_socket, SHUT_WR);
  1558. /* Properly wait for remote to closed */
  1559. FD_ZERO(&s_fd);
  1560. FD_SET(config->accepted_socket, &s_fd);
  1561. do {
  1562. tv.tv_sec = 2;
  1563. tv.tv_usec = 0;
  1564. retval = select(config->accepted_socket + 1, &s_fd, NULL, NULL, &tv);
  1565. } while (retval > 0 && read(config->accepted_socket, buf, sizeof(config->buf) > 0));
  1566. shutdown(config->accepted_socket, SHUT_RD);
  1567. /* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */
  1568. close(config->accepted_socket);
  1569. }
  1570. /****************************************************************************
  1571. *
  1572. > $Function: miniHttpd()
  1573. *
  1574. * $Description: The main http server function.
  1575. *
  1576. * Given an open socket fildes, listen for new connections and farm out
  1577. * the processing as a forked process.
  1578. *
  1579. * $Parameters:
  1580. * (int) server. . . The server socket fildes.
  1581. *
  1582. * $Return: (int) . . . . Always 0.
  1583. *
  1584. ****************************************************************************/
  1585. static int miniHttpd(int server)
  1586. {
  1587. fd_set readfd, portfd;
  1588. FD_ZERO(&portfd);
  1589. FD_SET(server, &portfd);
  1590. /* copy the ports we are watching to the readfd set */
  1591. while (1) {
  1592. int on, s;
  1593. socklen_t fromAddrLen;
  1594. struct sockaddr_in fromAddr;
  1595. /* Now wait INDEFINITELY on the set of sockets! */
  1596. readfd = portfd;
  1597. if (select(server + 1, &readfd, 0, 0, 0) <= 0)
  1598. continue;
  1599. if (!FD_ISSET(server, &readfd))
  1600. continue;
  1601. fromAddrLen = sizeof(fromAddr);
  1602. s = accept(server, (struct sockaddr *)&fromAddr, &fromAddrLen);
  1603. if (s < 0)
  1604. continue;
  1605. config->accepted_socket = s;
  1606. config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
  1607. #if ENABLE_FEATURE_HTTPD_CGI || DEBUG
  1608. sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
  1609. (unsigned char)(config->rmt_ip >> 24),
  1610. (unsigned char)(config->rmt_ip >> 16),
  1611. (unsigned char)(config->rmt_ip >> 8),
  1612. config->rmt_ip & 0xff);
  1613. config->port = ntohs(fromAddr.sin_port);
  1614. #if DEBUG
  1615. bb_error_msg("connection from IP=%s, port %u",
  1616. config->rmt_ip_str, config->port);
  1617. #endif
  1618. #endif /* FEATURE_HTTPD_CGI */
  1619. /* set the KEEPALIVE option to cull dead connections */
  1620. on = 1;
  1621. setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
  1622. if (DEBUG || fork() == 0) {
  1623. /* child */
  1624. #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
  1625. /* protect reload config, may be confuse checking */
  1626. signal(SIGHUP, SIG_IGN);
  1627. #endif
  1628. handleIncoming();
  1629. if (!DEBUG)
  1630. exit(0);
  1631. }
  1632. close(s);
  1633. } /* while (1) */
  1634. return 0;
  1635. }
  1636. /* from inetd */
  1637. static int miniHttpd_inetd(void)
  1638. {
  1639. struct sockaddr_in fromAddrLen;
  1640. socklen_t sinlen = sizeof(struct sockaddr_in);
  1641. getpeername(0, (struct sockaddr *)&fromAddrLen, &sinlen);
  1642. config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
  1643. #if ENABLE_FEATURE_HTTPD_CGI
  1644. sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
  1645. (unsigned char)(config->rmt_ip >> 24),
  1646. (unsigned char)(config->rmt_ip >> 16),
  1647. (unsigned char)(config->rmt_ip >> 8),
  1648. config->rmt_ip & 0xff);
  1649. #endif
  1650. config->port = ntohs(fromAddrLen.sin_port);
  1651. handleIncoming();
  1652. return 0;
  1653. }
  1654. #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
  1655. static void sighup_handler(int sig)
  1656. {
  1657. /* set and reset */
  1658. struct sigaction sa;
  1659. parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
  1660. sa.sa_handler = sighup_handler;
  1661. sigemptyset(&sa.sa_mask);
  1662. sa.sa_flags = SA_RESTART;
  1663. sigaction(SIGHUP, &sa, NULL);
  1664. }
  1665. #endif
  1666. enum {
  1667. c_opt_config_file = 0,
  1668. d_opt_decode_url,
  1669. h_opt_home_httpd,
  1670. USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
  1671. USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
  1672. USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
  1673. USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
  1674. p_opt_port ,
  1675. p_opt_inetd ,
  1676. p_opt_foreground,
  1677. OPT_CONFIG_FILE = 1 << c_opt_config_file,
  1678. OPT_DECODE_URL = 1 << d_opt_decode_url,
  1679. OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
  1680. OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
  1681. OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
  1682. OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
  1683. OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
  1684. OPT_PORT = 1 << p_opt_port,
  1685. OPT_INETD = 1 << p_opt_inetd,
  1686. OPT_FOREGROUND = 1 << p_opt_foreground,
  1687. };
  1688. static const char httpd_opts[] = "c:d:h:"
  1689. USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
  1690. USE_FEATURE_HTTPD_BASIC_AUTH("r:")
  1691. USE_FEATURE_HTTPD_AUTH_MD5("m:")
  1692. USE_FEATURE_HTTPD_SETUID("u:")
  1693. "p:if";
  1694. int httpd_main(int argc, char *argv[])
  1695. {
  1696. unsigned opt;
  1697. const char *home_httpd = home;
  1698. char *url_for_decode;
  1699. USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
  1700. const char *s_port;
  1701. USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
  1702. USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
  1703. USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
  1704. #if ENABLE_LOCALE_SUPPORT
  1705. /* Undo busybox.c: we want to speak English in http (dates etc) */
  1706. setlocale(LC_TIME, "C");
  1707. #endif
  1708. config = xzalloc(sizeof(*config));
  1709. #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
  1710. config->realm = "Web Server Authentication";
  1711. #endif
  1712. config->port = 80;
  1713. config->ContentLength = -1;
  1714. opt = getopt32(argc, argv, httpd_opts,
  1715. &(config->configFile), &url_for_decode, &home_httpd
  1716. USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
  1717. USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
  1718. USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
  1719. USE_FEATURE_HTTPD_SETUID(, &s_ugid)
  1720. , &s_port
  1721. );
  1722. if (opt & OPT_DECODE_URL) {
  1723. printf("%s", decodeString(url_for_decode, 1));
  1724. return 0;
  1725. }
  1726. #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
  1727. if (opt & OPT_ENCODE_URL) {
  1728. printf("%s", encodeString(url_for_encode));
  1729. return 0;
  1730. }
  1731. #endif
  1732. #if ENABLE_FEATURE_HTTPD_AUTH_MD5
  1733. if (opt & OPT_MD5) {
  1734. puts(pw_encrypt(pass, "$1$"));
  1735. return 0;
  1736. }
  1737. #endif
  1738. if (opt & OPT_PORT)
  1739. config->port = xatou16(s_port);
  1740. #if ENABLE_FEATURE_HTTPD_SETUID
  1741. if (opt & OPT_SETUID) {
  1742. char *e;
  1743. // FIXME: what the default group should be?
  1744. ugid.gid = -1;
  1745. ugid.uid = bb_strtoul(s_ugid, &e, 0);
  1746. if (*e == ':') {
  1747. e++;
  1748. ugid.gid = bb_strtoul(e, NULL, 0);
  1749. }
  1750. if (errno) {
  1751. /* not integer */
  1752. if (!uidgid_get(&ugid, s_ugid))
  1753. bb_error_msg_and_die("unrecognized user[:group] "
  1754. "name '%s'", s_ugid);
  1755. }
  1756. }
  1757. #endif
  1758. xchdir(home_httpd);
  1759. if (!(opt & OPT_INETD)) {
  1760. config->server_socket = openServer();
  1761. #if ENABLE_FEATURE_HTTPD_SETUID
  1762. /* drop privileges */
  1763. if (opt & OPT_SETUID) {
  1764. if (ugid.gid != (gid_t)-1) {
  1765. if (setgroups(1, &ugid.gid) == -1)
  1766. bb_perror_msg_and_die("setgroups");
  1767. xsetgid(ugid.gid);
  1768. }
  1769. xsetuid(ugid.uid);
  1770. }
  1771. #endif
  1772. }
  1773. #if ENABLE_FEATURE_HTTPD_CGI
  1774. {
  1775. char *p = getenv("PATH");
  1776. p = xstrdup(p); /* if gets NULL, returns NULL */
  1777. clearenv();
  1778. if (p)
  1779. setenv1("PATH", p);
  1780. if (!(opt & OPT_INETD))
  1781. setenv_long("SERVER_PORT", config->port);
  1782. }
  1783. #endif
  1784. #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
  1785. sighup_handler(0);
  1786. #else
  1787. parse_conf(default_path_httpd_conf, FIRST_PARSE);
  1788. #endif
  1789. if (opt & OPT_INETD)
  1790. return miniHttpd_inetd();
  1791. if (!(opt & OPT_FOREGROUND))
  1792. xdaemon(1, 0); /* don't change current directory */
  1793. return miniHttpd(config->server_socket);
  1794. }