00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "klone_conf.h"
00012 #include <ctype.h>
00013 #include <sys/stat.h>
00014 #include <sys/types.h>
00015 #include <sys/wait.h>
00016 #include <unistd.h>
00017 #include <fcntl.h>
00018 #include <klone/http.h>
00019 #include <klone/supplier.h>
00020 #include <klone/io.h>
00021 #include <klone/utils.h>
00022 #include <klone/rsfilter.h>
00023
00024
00025 typedef struct cgi_env_s
00026 {
00027 char **env;
00028 int size, count;
00029 } cgi_env_t;
00030
00031 static int cgi_get_config(http_t *h, request_t *rq, u_config_t **pc)
00032 {
00033 u_config_t *config = NULL;
00034 dbg_err_if(h == NULL);
00035 dbg_err_if(rq == NULL);
00036
00037 if(http_get_vhost_config(h, rq, &config) != 0)
00038 dbg_err_if((config = http_get_config(h)) == NULL);
00039
00040 *pc = config;
00041
00042 return 0;
00043 err:
00044 return ~0;
00045 }
00046
00047
00048 static int cgi_script(http_t *h, request_t *rq, const char *fqn)
00049 {
00050 u_config_t *config, *sub, *base;
00051 const char *dir;
00052 int i, t;
00053
00054 if(fqn == NULL)
00055 return 0;
00056
00057
00058 dbg_err_if(cgi_get_config(h, rq, &base));
00059
00060
00061 nop_err_if(u_config_get_subkey(base, "cgi", &config));
00062
00063
00064 for(i = 0; !u_config_get_subkey_nth(config, "script_alias", i, &sub); ++i)
00065 {
00066 if((dir = u_config_get_value(sub)) == NULL)
00067 continue;
00068
00069
00070 for(t = strlen(dir) - 1; t > 0; --t)
00071 if(dir[t] == ' ' || dir[t] == '\t')
00072 break;
00073
00074 if(t == 0)
00075 continue;
00076
00077
00078 dir += ++t;
00079
00080
00081 if(!strncmp(fqn, dir, strlen(dir)) && !access(fqn, X_OK))
00082 return 1;
00083 }
00084
00085 err:
00086 return 0;
00087 }
00088
00089
00090 static int cgi_ext(http_t *h, request_t *rq, const char *fqn,
00091 const char **phandler)
00092 {
00093 u_config_t *config, *base;
00094 char buf[U_FILENAME_MAX];
00095 char *ext = NULL;
00096 const char *handler;
00097
00098 if(fqn == NULL)
00099 return 0;
00100
00101 for(ext = NULL; *fqn; ++fqn)
00102 if(*fqn == '.')
00103 ext = (char*)fqn;
00104
00105 if(ext == NULL)
00106 return 0;
00107
00108 ext++;
00109
00110
00111 dbg_err_if(cgi_get_config(h, rq, &base));
00112
00113
00114 nop_err_if(u_config_get_subkey(base, "cgi", &config));
00115
00116 dbg_err_if(u_snprintf(buf, sizeof(buf), "%s.handler", ext));
00117
00118
00119 handler = u_config_get_subkey_value(config, buf);
00120
00121 if(handler)
00122 {
00123 if(phandler)
00124 *phandler = handler;
00125 return 1;
00126 }
00127
00128 err:
00129 return 0;
00130 }
00131
00132 static int cgi_setenv(cgi_env_t *env, const char *name, const char *value)
00133 {
00134 enum { CHUNK = 32 };
00135 char *keyval = NULL, **nenv = NULL;
00136 int i, nl, vl;
00137
00138 dbg_return_if(!env || !name || !value, ~0);
00139
00140 if((nl = strlen(name)) == 0)
00141 return ~0;
00142
00143 vl = strlen(value);
00144
00145
00146 if(env->size == 0 || env->size == env->count)
00147 {
00148 env->size += CHUNK;
00149 if(env->env == NULL)
00150 nenv = u_zalloc(env->size * sizeof(char*));
00151 else {
00152 nenv = u_realloc(env->env, env->size * sizeof(char*));
00153 }
00154 dbg_err_if(nenv == NULL);
00155
00156 for(i = env->count; i < env->size; ++i)
00157 nenv[i] = NULL;
00158 env->env = nenv;
00159 }
00160
00161 keyval = u_malloc(nl + vl + 2);
00162 dbg_err_if(keyval == NULL);
00163
00164 sprintf(keyval, "%s=%s", name, value);
00165
00166 env->env[env->count++] = keyval;
00167
00168 return 0;
00169 err:
00170 U_FREE(keyval);
00171 U_FREE(nenv);
00172 return ~0;
00173 }
00174
00175 static int cgi_is_valid_uri(http_t *h, request_t *rq, const char *uri,
00176 size_t len, time_t *mtime)
00177 {
00178 struct stat st;
00179 char fqn[1+U_FILENAME_MAX];
00180
00181 dbg_return_if (uri == NULL, 0);
00182 dbg_return_if (mtime == NULL, 0);
00183 dbg_return_if (len > U_FILENAME_MAX, 0);
00184
00185 memcpy(fqn, uri, len);
00186 fqn[len] = 0;
00187
00188
00189 if(strstr(fqn, ".."))
00190 return 0;
00191
00192 if( stat(fqn, &st) == 0 && S_ISREG(st.st_mode))
00193 {
00194
00195 if(!cgi_ext(h, rq, fqn, NULL) && !cgi_script(h, rq, fqn))
00196 return 0;
00197
00198 *mtime = st.st_mtime;
00199 return 1;
00200 } else
00201 return 0;
00202 }
00203
00204 static const char *cgi_addr_to_ip(addr_t *addr, char *buf, size_t bufsz)
00205 {
00206 const char *cstr;
00207
00208 #ifndef NO_IPV6
00209 cstr = inet_ntop( addr->type == ADDR_IPV4 ? AF_INET : AF_INET6,
00210 (addr->type == ADDR_IPV4 ?
00211 (const void*)&(addr->sa.sin.sin_addr) :
00212 (const void*)&(addr->sa.sin6.sin6_addr)),
00213 buf, bufsz);
00214 #else
00215 cstr = inet_ntoa(addr->sa.sin.sin_addr);
00216 #endif
00217
00218 dbg_err_if(cstr == NULL);
00219
00220 return cstr;
00221 err:
00222 return NULL;
00223 }
00224
00225 static int cgi_setenv_addr(cgi_env_t *env, addr_t *addr,
00226 const char *label_addr, const char *label_port)
00227 {
00228 const char *cstr;
00229 char buf[128];
00230
00231 dbg_return_if(addr->type == ADDR_UNIX, 0);
00232
00233 if((cstr = cgi_addr_to_ip(addr, buf, sizeof(buf))) != NULL)
00234 dbg_err_if(cgi_setenv(env, label_addr, cstr));
00235
00236 u_snprintf(buf, sizeof(buf), "%u", ntohs(addr->sa.sin.sin_port));
00237 dbg_err_if(cgi_setenv(env, label_port, buf));
00238
00239 return 0;
00240 err:
00241 return ~0;
00242 }
00243
00244 static int cgi_setenv_ctype(cgi_env_t *env, request_t *rq)
00245 {
00246 char *ct;
00247
00248 if((ct = request_get_field_value(rq, "Content-type")) != NULL)
00249 dbg_err_if(cgi_setenv(env, "CONTENT_TYPE", ct));
00250
00251 return 0;
00252 err:
00253 return ~0;
00254 }
00255
00256 static int cgi_setenv_clen(cgi_env_t *env, request_t *rq)
00257 {
00258 char buf[32];
00259 ssize_t len;
00260
00261 if((len = request_get_content_length(rq)) > 0)
00262 {
00263 dbg_err_if(u_snprintf(buf, sizeof(buf), "%ld", len));
00264 dbg_err_if(cgi_setenv(env, "CONTENT_LENGTH", buf));
00265 }
00266
00267 return 0;
00268 err:
00269 return ~0;
00270 }
00271
00272 static int cgi_set_blocking(int fd)
00273 {
00274 int flags;
00275
00276 warn_err_sif((flags = fcntl(fd, F_GETFL)) < 0);
00277
00278 nop_err_if(fcntl(fd, F_SETFL, flags & (~O_NONBLOCK)) < 0);
00279
00280 return 0;
00281 err:
00282 return ~0;
00283 }
00284
00285 static int cgi_makeenv(request_t *rq, response_t *rs, cgi_env_t *env)
00286 {
00287 addr_t *addr;
00288 header_t *h;
00289 field_t *field;
00290 const char *cstr;
00291 char *p, buf[1024];
00292 int i;
00293
00294 u_unused_args(rs);
00295
00296 dbg_err_if(cgi_setenv(env, "SERVER_SOFTWARE", "klone/" KLONE_VERSION));
00297 dbg_err_if(cgi_setenv(env, "SERVER_PROTOCOL", "HTTP/1.0"));
00298 dbg_err_if(cgi_setenv(env, "GATEWAY_INTERFACE", "CGI/1.1"));
00299 dbg_err_if(cgi_setenv(env, "REDIRECT_STATUS", "200"));
00300
00301
00302 if((addr = request_get_addr(rq)) != NULL)
00303 {
00304 dbg_err_if(cgi_setenv_addr(env, addr, "SERVER_ADDR", "SERVER_PORT"));
00305 if((cstr = cgi_addr_to_ip(addr, buf, sizeof(buf))) != NULL)
00306 dbg_err_if(cgi_setenv(env, "SERVER_NAME", cstr));
00307 }
00308
00309
00310 if((addr = request_get_peer_addr(rq)) != NULL)
00311 dbg_err_if(cgi_setenv_addr(env, addr, "REMOTE_ADDR", "REMOTE_PORT"));
00312
00313
00314 switch(request_get_method(rq))
00315 {
00316 case HM_GET: cstr = "GET"; break;
00317 case HM_HEAD: cstr = "HEAD"; break;
00318 case HM_POST: cstr = "POST"; break;
00319 default:
00320 cstr = "UNKNOWN";
00321 }
00322 dbg_err_if(cgi_setenv(env, "REQUEST_METHOD", cstr));
00323
00324 if(io_is_secure(request_io(rq)))
00325 dbg_err_if(cgi_setenv(env, "HTTPS", "on"));
00326
00327 if((cstr = request_get_path_info(rq)) != NULL)
00328 dbg_err_if(cgi_setenv(env, "PATH_INFO", cstr));
00329
00330 if((cstr = request_get_resolved_path_info(rq)) != NULL)
00331 dbg_err_if(cgi_setenv(env, "PATH_TRANSLATED", cstr));
00332
00333 if((cstr = request_get_query_string(rq)) != NULL)
00334 dbg_err_if(cgi_setenv(env, "QUERY_STRING", cstr));
00335
00336
00337 dbg_err_if(cgi_setenv_clen(env, rq));
00338
00339
00340 dbg_err_if(cgi_setenv_ctype(env, rq));
00341
00342 if((cstr = request_get_filename(rq)) != NULL)
00343 dbg_err_if(cgi_setenv(env, "SCRIPT_NAME", cstr));
00344
00345 if((cstr = request_get_uri(rq)) != NULL)
00346 dbg_err_if(cgi_setenv(env, "REQUEST_URI", cstr));
00347
00348 if((cstr = request_get_resolved_filename(rq)) != NULL)
00349 dbg_err_if(cgi_setenv(env, "SCRIPT_FILENAME", cstr));
00350
00351 if((cstr = getenv("SYSTEMROOT")) != NULL)
00352 dbg_err_if(cgi_setenv(env, "SYSTEMROOT", cstr));
00353
00354 dbg_err_if((h = request_get_header(rq)) == NULL);
00355
00356
00357 for(i = 0; i < header_field_count(h); ++i)
00358 {
00359 field = header_get_fieldn(h, i);
00360 dbg_err_if(field == NULL);
00361
00362 dbg_err_if(u_snprintf(buf, sizeof(buf), "HTTP_%s",
00363 field_get_name(field)));
00364
00365
00366 for(p = buf; *p && *p != ':'; ++p)
00367 {
00368 if(*p == '-')
00369 *p = '_';
00370 else
00371 *p = toupper(*p);
00372 }
00373
00374 if(field_get_value(field))
00375 dbg_err_if(cgi_setenv(env, buf, field_get_value(field)));
00376 else
00377 dbg_err_if(cgi_setenv(env, buf, ""));
00378 }
00379
00380 return 0;
00381 err:
00382 return ~0;
00383 }
00384
00385 #define close_pipe(fd) \
00386 do { \
00387 if(fd[0] != -1) close(fd[0]); \
00388 if(fd[1] != -1) close(fd[1]); \
00389 } while(0);
00390
00391 static int cgi_exec(request_t *rq, response_t *rs, pid_t *pchild,
00392 int *pcgi_stdin, int *pcgi_stdout)
00393 {
00394 enum { RD_END , WR_END };
00395 int cgi_stdin[2] = { -1, -1 };
00396 int cgi_stdout[2] = { -1, -1 };
00397 cgi_env_t cgi_env = { NULL, 0, 0 };
00398 http_t *h;
00399 const char *argv[] = { NULL, NULL, NULL };
00400 const char *cgi_file, *handler;
00401 char *p, *cgi_path = NULL;
00402 pid_t child;
00403 int fd;
00404
00405 dbg_err_if((h = request_get_http(rq)) == NULL);
00406
00407
00408 dbg_err_if(pipe(cgi_stdin) < 0);
00409 dbg_err_if(pipe(cgi_stdout) < 0);
00410
00411 crit_err_if((child = fork()) < 0);
00412
00413 if(child == 0)
00414 {
00415
00416
00417 close(cgi_stdin[WR_END]);
00418 close(cgi_stdout[RD_END]);
00419
00420
00421 close(STDOUT_FILENO);
00422 crit_err_if(dup2(cgi_stdout[WR_END], STDOUT_FILENO) < 0);
00423 close(cgi_stdout[WR_END]);
00424
00425
00426 close(STDIN_FILENO);
00427 crit_err_if(dup2(cgi_stdin[RD_END], STDIN_FILENO) < 0);
00428 close(cgi_stdin[RD_END]);
00429
00430
00431 fd = open("/dev/null", O_WRONLY);
00432 dbg_err_if(fd < 0);
00433 crit_err_if(dup2(fd, STDERR_FILENO) < 0);
00434 close(fd);
00435
00436
00437 cgi_set_blocking(STDOUT_FILENO);
00438 cgi_set_blocking(STDIN_FILENO);
00439 cgi_set_blocking(STDERR_FILENO);
00440
00441
00442 for(fd = 3; fd < 255; ++fd)
00443 close(fd);
00444
00445
00446 dbg_err_if((cgi_file = request_get_resolved_filename(rq)) == NULL);
00447
00448 cgi_path = u_strdup(cgi_file);
00449 dbg_err_if(cgi_path == NULL);
00450
00451
00452 dbg_err_if((p = strrchr(cgi_path, '/')) == NULL);
00453 ++p; *p = 0;
00454
00455 crit_err_sifm(chdir(cgi_path) < 0, "unable to chdir to %s", cgi_path);
00456
00457 U_FREE(cgi_path);
00458
00459
00460 crit_err_sif(cgi_makeenv(rq, rs, &cgi_env));
00461
00462
00463
00464 if(!cgi_ext(h, rq, cgi_file, &handler) || !strcasecmp(handler, "exec"))
00465 {
00466
00467 argv[0] = cgi_file;
00468
00469 } else {
00470
00471 argv[0] = handler;
00472 argv[1] = cgi_file;
00473 }
00474
00475
00476 crit_err_sif(execve(argv[0], argv, cgi_env.env));
00477
00478
00479
00480 } else if(child > 0) {
00481
00482
00483
00484 close(cgi_stdin[RD_END]);
00485 close(cgi_stdout[WR_END]);
00486
00487
00488 *pcgi_stdin = cgi_stdin[WR_END];
00489 *pcgi_stdout = cgi_stdout[RD_END];
00490 *pchild = child;
00491
00492 return 0;
00493
00494 } else {
00495 warn_err("fork error");
00496 }
00497
00498 err:
00499 if(child == 0)
00500 _exit(1);
00501 close_pipe(cgi_stdin);
00502 close_pipe(cgi_stdout);
00503 return ~0;
00504 }
00505
00506 static int cgi_serve(request_t *rq, response_t *rs)
00507 {
00508 codec_t *filter = NULL;
00509 header_t *head = NULL;
00510 field_t *field = NULL;
00511 const char *fqn, *filename;
00512 char buf[4096];
00513 io_t *out = NULL, *cgi_in = NULL, *cgi_out = NULL;;
00514 ssize_t n, tot = 0, clen;
00515 int cgi_stdin = -1, cgi_stdout = -1, status;
00516 pid_t child;
00517
00518 dbg_err_if (rq == NULL);
00519 dbg_err_if (rs == NULL);
00520
00521
00522 dbg_err_if((out = response_io(rs)) == NULL);
00523 dbg_err_if((head = response_get_header(rs)) == NULL);
00524
00525
00526 response_set_status(rs, HTTP_STATUS_BAD_REQUEST);
00527
00528
00529 fqn = request_get_resolved_filename(rq);
00530
00531
00532 crit_err_if(cgi_exec(rq, rs, &child, &cgi_stdin, &cgi_stdout));
00533
00534
00535 response_disable_caching(rs);
00536
00537
00538 if(request_get_method(rq) == HM_POST &&
00539 (clen = request_get_content_length(rq)) > 0)
00540 {
00541
00542 crit_err_sif(io_fd_create(cgi_stdin, O_WRONLY, &cgi_out));
00543
00544
00545
00546
00547
00548
00549
00550
00551 crit_if(io_copy(cgi_out, request_io(rq), clen) < 0);
00552
00553 io_free(cgi_out); cgi_out = NULL;
00554 close(cgi_stdin); cgi_stdin = -1;
00555 }
00556
00557
00558 crit_err_sif(io_fd_create(cgi_stdout, O_RDONLY, &cgi_in));
00559
00560
00561 crit_err_if((filename = strrchr(fqn, '/')) == NULL);
00562 filename++;
00563
00564
00565 if(strncmp(filename, "nph-", 4))
00566 {
00567
00568 dbg_err_if(response_filter_create(rq, rs, NULL, &filter));
00569 io_codec_add_tail(out, filter);
00570 filter = NULL;
00571
00572
00573 crit_err_if(header_load_ex(head, cgi_in, HLM_OVERRIDE));
00574
00575
00576 if((field = header_get_field(head, "Status")) != NULL &&
00577 field_get_value(field))
00578 {
00579 response_set_status(rs, atoi(field_get_value(field)));
00580 } else {
00581 if(header_get_field(head, "Location"))
00582 response_set_status(rs, HTTP_STATUS_MOVED_TEMPORARILY);
00583 else
00584 response_set_status(rs, HTTP_STATUS_OK);
00585 }
00586 } else
00587 response_set_status(rs, HTTP_STATUS_OK);
00588
00589
00590 while((n = io_read(cgi_in, buf, sizeof(buf))) > 0)
00591 {
00592 if(io_write(out, buf, n) < 0)
00593 break;
00594 tot += n;
00595 }
00596
00597
00598
00599
00600 if(tot == 0)
00601 io_write(out, "\n", 1);
00602
00603 if(cgi_in)
00604 io_free(cgi_in);
00605 if(cgi_out)
00606 io_free(cgi_out);
00607
00608 close(cgi_stdin);
00609 close(cgi_stdout);
00610
00611
00612 waitpid(child, &status, 0);
00613 if(WIFEXITED(status) && WEXITSTATUS(status))
00614 warn("cgi exited with [%d]", WEXITSTATUS(status));
00615
00616 return 0;
00617 err:
00618 if(cgi_out)
00619 io_free(cgi_out);
00620 if(cgi_in)
00621 io_free(cgi_in);
00622 if(cgi_stdin != -1)
00623 close(cgi_stdin);
00624 if(cgi_stdout != -1)
00625 close(cgi_stdout);
00626 return ~0;
00627 }
00628
00629 static int cgi_init(void)
00630 {
00631 return 0;
00632 }
00633
00634 static void cgi_term(void)
00635 {
00636 return;
00637 }
00638
00639 supplier_t sup_cgi = {
00640 "cgi supplier",
00641 cgi_init,
00642 cgi_term,
00643 cgi_is_valid_uri,
00644 cgi_serve
00645 };
00646