X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fpatplex.c;h=99395439123500ab914cf5cd385093c83b5a93a1;hb=7d1e14f405f389656604c68e18a381feabcd3d4a;hp=2ea28d5f54c1fb82cd65e261daf0b6e22b462f3e;hpb=0fc6fd13c5bd44e6d595b56337a95156fd42ceea;p=ashd.git diff --git a/src/patplex.c b/src/patplex.c index 2ea28d5..9939543 100644 --- a/src/patplex.c +++ b/src/patplex.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include @@ -43,6 +44,7 @@ #define PAT_DEFAULT 5 #define PATFL_MSS 1 +#define PATFL_UNQ 2 struct config { struct child *children; @@ -56,18 +58,26 @@ struct rule { regex_t *pattern; }; +struct headmod { + struct headmod *next; + char *name, *value; +}; + struct pattern { struct pattern *next; + struct headmod *headers; char *childnm; struct rule **rules; char *restpat; }; static struct config *gconfig, *lconfig; +static volatile int reload = 0; static void freepattern(struct pattern *pat) { struct rule **rule; + struct headmod *head; for(rule = pat->rules; *rule; rule++) { if((*rule)->header != NULL) @@ -78,11 +88,33 @@ static void freepattern(struct pattern *pat) } free(*rule); } + while((head = pat->headers) != NULL) { + pat->headers = head->next; + free(head->name); + free(head->value); + free(head); + } if(pat->childnm != NULL) free(pat->childnm); free(pat); } +static void freeconfig(struct config *cf) +{ + struct child *ch, *nch; + struct pattern *pat, *npat; + + for(ch = cf->children; ch != NULL; ch = nch) { + nch = ch->next; + freechild(ch); + } + for(pat = cf->patterns; pat != NULL; pat = npat) { + npat = pat->next; + freepattern(pat); + } + free(cf); +} + static struct child *getchild(struct config *cf, char *name) { struct child *ch; @@ -136,6 +168,7 @@ static struct pattern *parsepattern(struct cfstate *s) struct pattern *pat; int sl; struct rule *rule; + struct headmod *head; regex_t *regex; int rxfl; @@ -156,6 +189,7 @@ static struct pattern *parsepattern(struct cfstate *s) flog(LOG_WARNING, "%s:%i: missing pattern for `%s' match", s->file, s->lno, s->argv[0]); continue; } + rxfl = 0; if(s->argc >= 3) { if(strchr(s->argv[2], 'i')) rxfl |= REG_ICASE; @@ -175,12 +209,15 @@ static struct pattern *parsepattern(struct cfstate *s) if(s->argc >= 3) { if(strchr(s->argv[2], 's')) rule->fl |= PATFL_MSS; + if(strchr(s->argv[2], 'q')) + rule->fl |= PATFL_UNQ; } } else if(!strcmp(s->argv[0], "header")) { if(s->argc < 3) { flog(LOG_WARNING, "%s:%i: missing header name or pattern for `header' match", s->file, s->lno); continue; } + rxfl = 0; if(s->argc >= 4) { if(strchr(s->argv[3], 'i')) rxfl |= REG_ICASE; @@ -217,6 +254,19 @@ static struct pattern *parsepattern(struct cfstate *s) if(pat->restpat != NULL) free(pat->restpat); pat->restpat = sstrdup(s->argv[1]); + } else if(!strcmp(s->argv[0], "set") || !strcmp(s->argv[0], "xset")) { + if(s->argc < 3) { + flog(LOG_WARNING, "%s:%i: missing header name or pattern for `%s' directive", s->file, s->lno, s->argv[0]); + continue; + } + omalloc(head); + if(!strcmp(s->argv[0], "xset")) + head->name = sprintf2("X-Ash-%s", s->argv[1]); + else + head->name = sstrdup(s->argv[1]); + head->value = sstrdup(s->argv[2]); + head->next = pat->headers; + pat->headers = head; } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) { break; } else { @@ -315,12 +365,42 @@ static void exprestpat(struct hthead *req, struct pattern *pat, char **mstr) buffree(buf); } -static char *findmatch(struct config *cf, struct hthead *req, int trydefault) +static void qoffsets(char *buf, int *obuf, char *pstr, int unquote) +{ + int i, o, d1, d2; + + if(unquote) { + i = o = 0; + while(pstr[i]) { + obuf[o] = i; + if((pstr[i] == '%') && ((d1 = hexdigit(pstr[i + 1])) >= 0) && ((d2 = hexdigit(pstr[i + 2])) >= 0)) { + buf[o] = (d1 << 4) | d2; + i += 3; + } else { + buf[o] = pstr[i]; + i++; + } + o++; + } + buf[o] = 0; + obuf[o] = i; + } else { + for(i = 0; pstr[i]; i++) { + buf[i] = pstr[i]; + obuf[i] = i; + } + buf[i] = 0; + obuf[i] = i; + } +} + +static struct pattern *findmatch(struct config *cf, struct hthead *req, int trydefault) { int i, o; struct pattern *pat; struct rule *rule; - int rmo, matched; + int rmo; + regex_t *rx; char *pstr; char **mstr; regmatch_t gr[10]; @@ -329,46 +409,52 @@ static char *findmatch(struct config *cf, struct hthead *req, int trydefault) for(pat = cf->patterns; pat != NULL; pat = pat->next) { rmo = -1; for(i = 0; (rule = pat->rules[i]) != NULL; i++) { - matched = 0; + rx = NULL; if(rule->type == PAT_REST) { - if((matched = !regexec(rule->pattern, pstr = req->rest, 10, gr, 0))) - rmo = gr[0].rm_eo; - else - break; + rx = rule->pattern; + pstr = req->rest; } else if(rule->type == PAT_URL) { - if(!(matched = !regexec(rule->pattern, pstr = req->url, 10, gr, 0))) - break; + rx = rule->pattern; + pstr = req->url; } else if(rule->type == PAT_METHOD) { - if(!(matched = !regexec(rule->pattern, pstr = req->method, 10, gr, 0))) - break; + rx = rule->pattern; + pstr = req->method; } else if(rule->type == PAT_HEADER) { + rx = rule->pattern; if(!(pstr = getheader(req, rule->header))) break; - if(!(matched = !regexec(rule->pattern, pstr, 10, gr, 0))) + } + if(rx != NULL) { + char pbuf[strlen(pstr) + 1]; + int obuf[strlen(pstr) + 1]; + qoffsets(pbuf, obuf, pstr, !!(rule->fl & PATFL_UNQ)); + if(regexec(rx, pbuf, 10, gr, 0)) break; + else if(rule->type == PAT_REST) + rmo = obuf[gr[0].rm_eo]; + if(rule->fl & PATFL_MSS) { + if(mstr) { + flog(LOG_WARNING, "two pattern rules marked with `s' flag found (for handler %s)", pat->childnm); + freeca(mstr); + } + for(o = 0; o < 10; o++) { + if(gr[o].rm_so < 0) + break; + } + mstr = szmalloc((o + 1) * sizeof(*mstr)); + for(o = 0; o < 10; o++) { + if(gr[o].rm_so < 0) + break; + mstr[o] = smalloc(obuf[gr[o].rm_eo] - obuf[gr[o].rm_so] + 1); + memcpy(mstr[o], pstr + obuf[gr[o].rm_so], obuf[gr[o].rm_eo] - obuf[gr[o].rm_so]); + mstr[o][obuf[gr[o].rm_eo] - obuf[gr[o].rm_so]] = 0; + } + } } else if(rule->type == PAT_ALL) { } else if(rule->type == PAT_DEFAULT) { if(!trydefault) break; } - if(matched && (rule->fl & PATFL_MSS)) { - if(mstr) { - flog(LOG_WARNING, "two pattern rules marked with `s' flag found (for handler %s)", pat->childnm); - freeca(mstr); - } - for(o = 0; o < 10; o++) { - if(gr[o].rm_so < 0) - break; - } - mstr = szmalloc((o + 1) * sizeof(*mstr)); - for(o = 0; o < 10; o++) { - if(gr[o].rm_so < 0) - break; - mstr[o] = smalloc(gr[o].rm_eo - gr[o].rm_so + 1); - memcpy(mstr[o], pstr + gr[o].rm_so, gr[o].rm_eo - gr[o].rm_so); - mstr[o][gr[o].rm_eo - gr[o].rm_so] = 0; - } - } } if(!rule) { if(pat->restpat) { @@ -378,7 +464,7 @@ static char *findmatch(struct config *cf, struct hthead *req, int trydefault) } if(mstr) freeca(mstr); - return(pat->childnm); + return(pat); } if(mstr) { freeca(mstr); @@ -388,41 +474,84 @@ static char *findmatch(struct config *cf, struct hthead *req, int trydefault) return(NULL); } +static void childerror(struct hthead *req, int fd) +{ + if(errno == EAGAIN) + simpleerror(fd, 500, "Server Error", "The request handler is overloaded."); + else + simpleerror(fd, 500, "Server Error", "The request handler crashed."); +} + static void serve(struct hthead *req, int fd) { - char *chnm; + struct pattern *pat; + struct headmod *head; struct child *ch; - chnm = NULL; - if(chnm == NULL) - chnm = findmatch(lconfig, req, 0); - if(chnm == NULL) - chnm = findmatch(lconfig, req, 1); + pat = NULL; + if(pat == NULL) + pat = findmatch(lconfig, req, 0); + if(pat == NULL) + pat = findmatch(lconfig, req, 1); if(gconfig != NULL) { - if(chnm == NULL) - chnm = findmatch(gconfig, req, 0); - if(chnm == NULL) - chnm = findmatch(gconfig, req, 1); + if(pat == NULL) + pat = findmatch(gconfig, req, 0); + if(pat == NULL) + pat = findmatch(gconfig, req, 1); } - if(chnm == NULL) { + if(pat == NULL) { simpleerror(fd, 404, "Not Found", "The requested resource could not be found on this server."); return; } ch = NULL; if(ch == NULL) - ch = getchild(lconfig, chnm); + ch = getchild(lconfig, pat->childnm); if(gconfig != NULL) { if(ch == NULL) - ch = getchild(gconfig, chnm); + ch = getchild(gconfig, pat->childnm); } if(ch == NULL) { - flog(LOG_ERR, "child %s requested, but was not declared", chnm); - simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", chnm); + flog(LOG_ERR, "child %s requested, but was not declared", pat->childnm); + simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", pat->childnm); return; } - if(childhandle(ch, req, fd)) - simpleerror(fd, 500, "Server Error", "The request handler crashed."); + for(head = pat->headers; head != NULL; head = head->next) { + headrmheader(req, head->name); + headappheader(req, head->name, head->value); + } + if(childhandle(ch, req, fd, NULL, NULL)) + childerror(req, fd); +} + +static void reloadconf(char *nm) +{ + struct config *cf; + + if((cf = readconfig(nm)) == NULL) { + flog(LOG_WARNING, "could not reload configuration file `%s'", nm); + return; + } + mergechildren(cf->children, lconfig->children); + freeconfig(lconfig); + lconfig = cf; +} + +static void chldhandler(int sig) +{ + pid_t pid; + int st; + + while((pid = waitpid(-1, &st, WNOHANG)) > 0) { + if(WCOREDUMP(st)) + flog(LOG_WARNING, "child process %i dumped core", pid); + } +} + +static void sighandler(int sig) +{ + if(sig == SIGHUP) + reload = 1; } static void usage(FILE *out) @@ -434,7 +563,7 @@ int main(int argc, char **argv) { int c; int nodef; - char *gcf; + char *gcf, *lcf; struct hthead *req; int fd; @@ -462,10 +591,27 @@ int main(int argc, char **argv) free(gcf); } } - lconfig = readconfig(argv[optind]); - signal(SIGCHLD, SIG_IGN); + if((strchr(lcf = argv[optind], '/')) == NULL) { + if((lcf = findstdconf(sprintf3("ashd/%s", lcf))) == NULL) { + flog(LOG_ERR, "could not find requested configuration file `%s'", argv[optind]); + exit(1); + } + } + if((lconfig = readconfig(lcf)) == NULL) { + flog(LOG_ERR, "could not read `%s'", lcf); + exit(1); + } + signal(SIGCHLD, chldhandler); + signal(SIGHUP, sighandler); + signal(SIGPIPE, sighandler); while(1) { + if(reload) { + reloadconf(lcf); + reload = 0; + } if((fd = recvreq(0, &req)) < 0) { + if(errno == EINTR) + continue; if(errno != 0) flog(LOG_ERR, "recvreq: %s", strerror(errno)); break;