X-Git-Url: https://git.pterodactylus.net/?p=fms.git;a=blobdiff_plain;f=libs%2Fshttpd%2Fconfig.c;h=bd290fb6d10b4aa1d72f7e5b8ca27b1cd9c6fb10;hp=dcc6dadca54b98fae69a17fa77113618e03b68a6;hb=1dee4e3cd008a27789bbce05b0eb47b0eb5d121a;hpb=3dc3ac3cfe10b7196a7977e9c041c29fa141c35e diff --git a/libs/shttpd/config.c b/libs/shttpd/config.c index dcc6dad..bd290fb 100644 --- a/libs/shttpd/config.c +++ b/libs/shttpd/config.c @@ -10,32 +10,44 @@ #include "defs.h" -/* - * Configuration parameters setters - */ -static void -set_int(struct shttpd_ctx *ctx, void *ptr, const char *string) -{ - ctx = NULL; /* Unused */ - * (int *) ptr = atoi(string); -} +static int isbyte(int n) { return (n >= 0 && n <= 255); } static void -set_str(struct shttpd_ctx *ctx, void *ptr, const char *string) +set_acl(struct shttpd_ctx *ctx, const char *s) { - ctx = NULL; /* Unused */ - * (char **) ptr = my_strdup(string); -} - -static void -set_log_file(struct shttpd_ctx *ctx, void *ptr, const char *string) -{ - FILE **fp = ptr; - ctx = NULL; + struct acl *acl = NULL; + char flag; + int len, a, b, c, d, n, mask; + struct llhead *lp, *tmp; + + /* Delete the old ACLs if any */ + LL_FOREACH_SAFE(&ctx->acl, lp, tmp) + free(LL_ENTRY(lp, struct acl, link)); + + FOR_EACH_WORD_IN_LIST(s, len) { + + mask = 32; + + if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) { + elog(E_FATAL, NULL, "[%s]: subnet must be " + "[+|-]x.x.x.x[/x]", s); + } else if (flag != '+' && flag != '-') { + elog(E_FATAL, NULL, "flag must be + or -: [%s]", s); + } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) { + elog(E_FATAL, NULL, "bad ip address: [%s]", s); + } else if ((acl = malloc(sizeof(*acl))) == NULL) { + elog(E_FATAL, NULL, "%s", "cannot malloc subnet"); + } else if (sscanf(s + n, "/%d", &mask) == 0) { + /* Do nothing, no mask specified */ + } else if (mask < 0 || mask > 32) { + elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s); + } - if ((*fp = fopen(string, "a")) == NULL) - elog(E_FATAL, NULL, "cannot open log file %s: %s", - string, strerror(errno)); + acl->ip = (a << 24) | (b << 16) | (c << 8) | d; + acl->mask = mask ? 0xffffffffU << (32 - mask) : 0; + acl->flag = flag; + LL_TAIL(&ctx->acl, &acl->link); + } } #ifndef NO_SSL @@ -43,14 +55,12 @@ set_log_file(struct shttpd_ctx *ctx, void *ptr, const char *string) * Dynamically load SSL library. Set up ctx->ssl_ctx pointer. */ static void -set_ssl(struct shttpd_ctx *ctx, void *arg, const char *pem) +set_ssl(struct shttpd_ctx *ctx, const char *pem) { SSL_CTX *CTX; void *lib; struct ssl_func *fp; - arg = NULL; /* Unused */ - /* Load SSL library dynamically */ if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB); @@ -73,262 +83,225 @@ set_ssl(struct shttpd_ctx *ctx, void *arg, const char *pem) #endif /* NO_SSL */ static void -set_mime(struct shttpd_ctx *ctx, void *arg, const char *string) +open_log_file(FILE **fpp, const char *path) { - arg = NULL; - set_mime_types(ctx, string); -} + if (*fpp != NULL) + (void) fclose(*fpp); -#define OFS(x) offsetof(struct shttpd_ctx, x) -#define BOOL_OPT "0|1" -const struct opt options[] = { - {'d', "document_root", "Web root directory", set_str, - OFS(document_root), "directory", NULL, OPT_DIR}, - {'i', "index_files", "Index files", set_str, OFS(index_files), - "file_list", INDEX_FILES, OPT_ADVANCED}, - {'p', "listen_ports", "Listening ports", set_str, - OFS(ports), "ports", LISTENING_PORTS, OPT_ADVANCED}, - {'D', "list_directories", "Directory listing", set_int, - OFS(dirlist), BOOL_OPT, "1", OPT_BOOL | OPT_ADVANCED}, -#ifndef NO_CGI - {'c', "cgi_extensions", "CGI extensions", set_str, - OFS(cgi_extensions), "ext_list", CGI_EXT, OPT_ADVANCED}, - {'C', "cgi_interpreter", "CGI interpreter", set_str, - OFS(cgi_interpreter), "file", NULL, OPT_FILE | OPT_ADVANCED}, - {'V', "cgi_envvar", "CGI envir variables", set_str, - OFS(cgi_vars), "X=Y,....", NULL, OPT_ADVANCED}, -#endif /* NO_CGI */ -#if !defined(NO_SSI) - {'S', "ssi_extensions", "SSI extensions", set_str, - OFS(ssi_extensions), "ext_list", SSI_EXT, OPT_ADVANCED}, -#endif /* NO_SSI */ - {'N', "auth_realm", "Authentication realm", set_str, - OFS(auth_realm), "auth_realm", REALM, OPT_ADVANCED}, - {'l', "access_log", "Access log file", set_log_file, - OFS(access_log), "file", NULL, OPT_FILE | OPT_ADVANCED}, - {'e', "error_log", "Error log file", set_log_file, - OFS(error_log), "file", NULL, OPT_FILE | OPT_ADVANCED}, - {'m', "mime_types", "Mime types file", set_mime, - OFS(mime_file), "file", NULL, OPT_FILE | OPT_ADVANCED}, - {'P', "global_htpasswd", "Global passwords file", set_str, - OFS(global_passwd_file), "file", NULL, OPT_FILE | OPT_ADVANCED}, -#ifndef NO_SSL - {'s', "ssl_certificate", "SSL certificate file", set_ssl, - OFS(ssl_ctx), "pem_file", NULL, OPT_FILE | OPT_ADVANCED}, -#endif /* NO_SSL */ - {'U', "put_auth", "PUT,DELETE auth file",set_str, - OFS(put_auth_file), "file", NULL, OPT_FILE | OPT_ADVANCED}, - {'a', "aliases", "Aliases", set_str, - OFS(aliases), "X=Y,...", NULL, OPT_ADVANCED}, - {'b', "io_buf_size", "IO buffer size", set_int, OFS(io_buf_size), - "bytes", DFLT_IO_SIZ, OPT_INT | OPT_ADVANCED}, -#ifdef _WIN32 - {'B', "auto_start", "Autostart with Windows", set_int, - OFS(auto_start), BOOL_OPT, "1", OPT_BOOL}, -#else - {'I', "inetd_mode", "Inetd mode", set_int, - OFS(inetd_mode), BOOL_OPT, NULL, OPT_BOOL }, - {'u', "runtime_uid", "Run as user", set_str, - OFS(uid), "user_name", NULL, 0 }, -#endif /* _WIN32 */ - {0, NULL, NULL, NULL, 0, NULL, NULL, 0 } -}; + if (path == NULL) { + *fpp = NULL; + } else if ((*fpp = fopen(path, "a")) == NULL) { + elog(E_FATAL, NULL, "cannot open log file %s: %s", + path, strerror(errno)); + } +} -static const struct opt * -find_option(int sw, const char *name) +static void +set_alog(struct shttpd_ctx *ctx, const char *path) { - const struct opt *opt; - - for (opt = options; opt->sw != 0; opt++) - if (sw == opt->sw || (name && strcmp(opt->name, name) == 0)) - return (opt); - - return (NULL); + open_log_file(&ctx->access_log, path); } static void -set_option(const struct opt *opt, const char *val, char **tmpvars) +set_elog(struct shttpd_ctx *ctx, const char *path) { - tmpvars += opt - options; + open_log_file(&ctx->error_log, path); +} + +static void show_cfg_page(struct shttpd_arg *arg); - if (*tmpvars != NULL) - free(*tmpvars); +static void +set_cfg_uri(struct shttpd_ctx *ctx, const char *uri) +{ + free_list(&ctx->registered_uris, ®istered_uri_destructor); - *tmpvars = my_strdup(val); + if (uri != NULL) { + shttpd_register_uri(ctx, uri, &show_cfg_page, ctx); + } } -/* - * Initialize shttpd context - */ static void -initialize_context(struct shttpd_ctx *ctx, const char *config_file, - int argc, char *argv[], char **tmpvars) +set_ports(struct shttpd_ctx *ctx, const char *p) { - char line[FILENAME_MAX], root[FILENAME_MAX], - var[sizeof(line)], val[sizeof(line)]; - const char *arg; - size_t i; - const struct opt *opt; - FILE *fp; - struct tm *tm; + int len, is_ssl; - current_time = time(NULL); - tm = localtime(¤t_time); - tz_offset = 0; -#if 0 - tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0); -#endif + free_list(&ctx->listeners, &listener_destructor); - (void) memset(ctx, 0, sizeof(*ctx)); + FOR_EACH_WORD_IN_LIST(p, len) { + is_ssl = p[len - 1] == 's' ? 1 : 0; + if (shttpd_listen(ctx, atoi(p), is_ssl) == -1) + elog(E_FATAL, NULL, + "Cannot open socket on port %d", atoi(p)); + } +} - ctx->start_time = current_time; - InitializeCriticalSection(&ctx->mutex); +static const struct opt { + int index; /* Index in shttpd_ctx */ + const char *name; /* Option name in config file */ + const char *description; /* Description */ + const char *default_value; /* Default option value */ + void (*setter)(struct shttpd_ctx *, const char *); +} known_options[] = { + {OPT_ROOT, "root", "\tWeb root directory", ".", NULL}, + {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL}, + {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports}, + {OPT_DIR_LIST, "dir_list", "Directory listing", "1", NULL}, + {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri}, + {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL}, +#ifndef NO_CGI + {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL}, + {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL}, + {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL}, +#endif /* NO_CGI */ + {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL}, +#ifndef NO_AUTH + {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL}, + {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL}, + {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL}, +#endif /* !NO_AUTH */ + {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog}, + {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog}, + {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL}, +#ifndef NO_SSL + {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl}, +#endif /* NO_SSL */ + {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL}, + {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl}, +#ifdef _WIN32 +#else + {OPT_INETD, "inetd", "Inetd mode", "0", NULL}, + {OPT_UID, "uid", "\tRun as user", NULL, NULL}, +#endif /* _WIN32 */ + {-1, NULL, NULL, NULL, NULL} +}; - LL_INIT(&ctx->connections); - LL_INIT(&ctx->mime_types); - LL_INIT(&ctx->registered_uris); - LL_INIT(&ctx->uri_auths); - LL_INIT(&ctx->error_handlers); +void shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val) +{ + const struct opt *o; -#if !defined(NO_SSI) - LL_INIT(&ctx->ssi_funcs); -#endif /* NO_SSI */ + for (o = known_options; o->name != NULL; o++) + if (!strcmp(opt, o->name)) + break; - /* First pass: set the defaults */ - for (opt = options; opt->sw != 0; opt++) - if (tmpvars[opt - options] == NULL && opt->def != NULL) - tmpvars[opt - options] = my_strdup(opt->def); + if (o->name == NULL) + elog(E_FATAL, NULL, "no such option: [%s]", opt); - /* Second pass: load config file */ - if (config_file != NULL && (fp = fopen(config_file, "r")) != NULL) { - DBG(("init_ctx: config file %s", config_file)); + /* Call option setter first, so it can use both new and old values */ + if (o->setter != NULL) + o->setter(ctx, val); - /* Loop through the lines in config file */ - while (fgets(line, sizeof(line), fp) != NULL) { + /* Free old value if any */ + if (ctx->options[o->index] != NULL) + free(ctx->options[o->index]); + + /* Set new option value */ + ctx->options[o->index] = val ? my_strdup(val) : NULL; +} - /* Skip comments and empty lines */ - if (line[0] == '#' || line[0] == '\n') - continue; +static void +show_cfg_page(struct shttpd_arg *arg) +{ + struct shttpd_ctx *ctx = arg->user_data; + char opt_name[20], value[BUFSIZ]; + const struct opt *o; + + opt_name[0] = value[0] = '\0'; + + if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) { + if (arg->flags & SHTTPD_MORE_POST_DATA) + return; + (void) shttpd_get_var("o", arg->in.buf, arg->in.len, + opt_name, sizeof(opt_name)); + (void) shttpd_get_var("v", arg->in.buf, arg->in.len, + value, sizeof(value)); + shttpd_set_option(ctx, opt_name, value[0] ? value : NULL); + } - /* Trim trailing newline character */ - line[strlen(line) - 1] = '\0'; + shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" + "

SHTTPD v. %s

", shttpd_version()); - if (sscanf(line, "%s %[^#\n]", var, val) != 2) - elog(E_FATAL,0,"init_ctx: bad line: [%s]",line); + shttpd_printf(arg, "%s", "" + ""); - if ((opt = find_option(0, var)) == NULL) - elog(E_FATAL, NULL, - "set_option: unknown variable [%s]", var); - set_option(opt, val, tmpvars); - } - (void) fclose(fp); - } + if (opt_name[0] != '\0' && value[0] != '\0') + shttpd_printf(arg, "

Saved: %s=%s

", + opt_name, value[0] ? value : "NULL"); - /* Third pass: process command line args */ - for (i = 1; i < (size_t) argc && argv[i][0] == '-'; i++) - if ((opt = find_option(argv[i][1], NULL)) != NULL) { - arg = argv[i][2] ? &argv[i][2] : argv[++i]; - - if (arg == NULL) - usage(argv[0]); - - set_option(opt, arg, tmpvars); - } else { - usage(argv[0]); - } - /* Call setters functions now */ - for (i = 0; i < NELEMS(options); i++) - if (tmpvars[i] != NULL) { - options[i].setter(ctx, - ((char *) ctx) + options[i].ofs, tmpvars[i]); - free(tmpvars[i]); - } - - /* If document_root is not set, set it to current directory */ - if (ctx->document_root == NULL) { - (void) my_getcwd(root, sizeof(root)); - ctx->document_root = my_strdup(root); + for (o = known_options; o->name != NULL; o++) { + shttpd_printf(arg, + "" + "" + "" + "", + o->name, o->description, o->name, + ctx->options[o->index] ? ctx->options[o->index] : ""); } -#ifdef _WIN32 - {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);} -#endif /* _WIN32 */ - - DBG(("init_ctx: initialized context %p", (void *) ctx)); + shttpd_printf(arg, "%s", "
OptionDescriptionValue
%s%s
"); + arg->flags |= SHTTPD_END_OF_OUTPUT; } - /* * Show usage string and exit. */ void usage(const char *prog) { - const struct opt *opt; + const struct opt *o; (void) fprintf(stderr, "SHTTPD version %s (c) Sergey Lyubka\n" - "usage: %s [OPTIONS] [config_file]\n" - "Note: config line keyword for every option is in the " - "round brackets\n", VERSION, prog); + "usage: %s [options] [config_file]\n", VERSION, prog); #if !defined(NO_AUTH) - (void) fprintf(stderr, "-A \n"); + fprintf(stderr, " -A \n"); #endif /* NO_AUTH */ - for (opt = options; opt->name != NULL; opt++) - (void) fprintf(stderr, "-%c <%s>\t\t%s (%s)\n", - opt->sw, opt->arg, opt->desc, opt->name); + for (o = known_options; o->name != NULL; o++) { + (void) fprintf(stderr, " -%s\t%s", o->name, o->description); + if (o->default_value != NULL) + fprintf(stderr, " (default: %s)", o->default_value); + fputc('\n', stderr); + } exit(EXIT_FAILURE); } -struct shttpd_ctx * -init_from_argc_argv(const char *config_file, int argc, char *argv[]) +struct shttpd_ctx *shttpd_init(void) { struct shttpd_ctx *ctx; - char *tmpvars[NELEMS(options)]; - size_t i; + struct tm *tm; + const struct opt *o; - /* Initialize all temporary holders to NULL */ - for (i = 0; i < NELEMS(tmpvars); i++) - tmpvars[i] = NULL; + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) + elog(E_FATAL, NULL, "cannot allocate shttpd context"); - if ((ctx = malloc(sizeof(*ctx))) != NULL) - initialize_context(ctx, config_file, argc, argv, tmpvars); - - return (ctx); -} + /* Set default values */ + for (o = known_options; o->name != NULL; o++) { + ctx->options[o->index] = o->default_value == NULL ? + NULL : my_strdup(o->default_value); + } -struct shttpd_ctx * -shttpd_init(const char *config_file, ...) -{ - struct shttpd_ctx *ctx; - va_list ap; - const char *opt_name, *opt_value; - char *tmpvars[NELEMS(options)]; - const struct opt *opt; - size_t i; - - /* Initialize all temporary holders to NULL */ - for (i = 0; i < NELEMS(tmpvars); i++) - tmpvars[i] = NULL; - - if ((ctx = malloc(sizeof(*ctx))) != NULL) { - - va_start(ap, config_file); - while ((opt_name = va_arg(ap, const char *)) != NULL) { - opt_value = va_arg(ap, const char *); - - if ((opt = find_option(0, opt_name)) == NULL) - elog(E_FATAL, NULL, "shttpd_init: " - "unknown variable [%s]", opt_name); - set_option(opt, opt_value, tmpvars); - } - va_end(ap); + current_time = ctx->start_time = time(NULL); + tm = localtime(¤t_time); + tz_offset = 0; +#if 0 + tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0); +#endif - initialize_context(ctx, config_file, 0, NULL, tmpvars); - } + InitializeCriticalSection(&ctx->mutex); + + LL_INIT(&ctx->connections); + LL_INIT(&ctx->registered_uris); + LL_INIT(&ctx->error_handlers); + LL_INIT(&ctx->acl); + LL_INIT(&ctx->ssi_funcs); + LL_INIT(&ctx->listeners); + +#ifdef _WIN32 + {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);} +#endif /* _WIN32 */ return (ctx); }