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