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