Mon, 26 Dec 2016 16:46:55 +0100
adds support for ssl cert chain files and improves ssl error handling
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include "http.h" #include "protocol.h" #include "../util/pblock.h" #include "../util/pool.h" #include "../util/util.h" static int http_etag = 1; /* --------------------------- http_format_etag --------------------------- */ NSAPI_PUBLIC void http_format_etag(Session *sn, Request *rq, char *etagp, int etaglen, off_t size, time_t mtime) { /* * Heuristic for weak/strong validator: if the request was made within * a second of the last-modified date, then we send a weak Etag. */ if (rq->req_start - mtime <= 1) { /* Send a weak Etag */ *etagp++ = 'W'; *etagp++ = '/'; etaglen -= 2; } /* * Create an Etag out of the metadata (size, mtime) to obtain Etag * uniqueness. */ util_snprintf(etagp, etaglen, "\"%x-%x\"", (int)size, (int)mtime); } /* --------------------------- http_weaken_etag --------------------------- */ NSAPI_PUBLIC void http_weaken_etag(Session *sn, Request *rq) { /* Replace any existing strong Etag with a weak Etag */ pb_param *pp = pblock_findkey(pb_key_etag, rq->srvhdrs); if (pp) { if (pp->value[0] != 'W' || pp->value[1] != '/') { char *weak = (char *) pool_malloc(sn->pool, 2 + strlen(pp->value) + 1); weak[0] = 'W'; weak[1] = '/'; strcpy(weak + 2, pp->value); pool_free(sn->pool, pp->value); pp->value = weak; } } } /* ------------------------------ match_etag ------------------------------ */ NSAPI_PUBLIC int http_match_etag(const char *header, const char *etag, int strong) { if (!etag) return 0; /* mismatch */ if (header[0] == '*') return 1; /* match */ if (etag[0] == 'W' && etag[1] == '/') { /* Weak Etags never match when using the strong validator */ if (strong) return 0; /* mismatch */ etag += 2; } /* Look for Etag in header */ const char *found = strstr(header, etag); if (!found) return 0; /* mismatch */ /* Weak Etags never match when using the strong validator */ if (strong && found >= header + 2 && found[-2] == 'W' && found[-1] == '/') return 0; /* mismatch */ return 1; /* match */ } /* ----------------------- http_check_preconditions ----------------------- */ NSAPI_PUBLIC int http_check_preconditions(Session *sn, Request *rq, struct tm *mtm, const char *etag) { char *header; /* If-modified-since */ header = pblock_findkeyval(pb_key_if_modified_since, rq->headers); if (header) { if (mtm && util_later_than(mtm, header)) { protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL); return REQ_ABORTED; } } /* If-unmodified-since */ header = pblock_findkeyval(pb_key_if_unmodified_since, rq->headers); if (header) { if (mtm && !util_later_than(mtm, header)) { if(util_isdate(header)) { protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); return REQ_ABORTED; } } } /* If-none-match */ header = pblock_findkeyval(pb_key_if_none_match, rq->headers); if (header) { /* If the If-none-match header matches the current Etag... */ if (ISMGET(rq) || ISMHEAD(rq)) { if (http_match_etag(header, etag, PR_FALSE)) { protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL); return REQ_ABORTED; } } else { if (http_match_etag(header, etag, PR_TRUE)) { protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); return REQ_ABORTED; } } } /* If-match */ header = pblock_findkeyval(pb_key_if_match, rq->headers); if (header) { /* If the If-match header matches the current Etag... */ if (!http_match_etag(header, etag, PR_TRUE)) { protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); return REQ_ABORTED; } } /* If-range */ header = pblock_findkeyval(pb_key_if_range, rq->headers); char *range = pblock_findkeyval(pb_key_range, rq->headers); if (range && header) { int rmir = PR_FALSE; if (util_isdate(header)) { if (mtm && !util_later_than(mtm, header)) { rmir = PR_TRUE; } } else { if (!http_match_etag(header, etag, PR_TRUE)) { rmir = PR_TRUE; } } if(rmir) { pblock_removekey(pb_key_range, rq->headers); } } return REQ_PROCEED; } /* ---------------------------- http_set_finfo ---------------------------- */ static inline int set_finfo(Session *sn, Request *rq, off_t size, time_t mtime) { struct tm mtms; struct tm *mtm = system_gmtime(&mtime, &mtms); pb_param *pp; /* Insert Last-modified */ if (mtm) { pp = pblock_key_param_create(rq->srvhdrs, pb_key_last_modified, NULL, HTTP_DATE_LEN); if (!pp || !pp->value) return REQ_ABORTED; strftime(pp->value, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); pblock_kpinsert(pb_key_last_modified, pp, rq->srvhdrs); } /* Insert Content-length */ const int content_length_size = 21; pp = pblock_key_param_create(rq->srvhdrs, pb_key_content_length, NULL, content_length_size); if (!pp || !pp->value) return REQ_ABORTED; snprintf(pp->value, content_length_size, "%lld", (long long)size); pblock_kpinsert(pb_key_content_length, pp, rq->srvhdrs); char *etag; if (http_etag) { /* Insert Etag */ pp = pblock_key_param_create(rq->srvhdrs, pb_key_etag, NULL, MAX_ETAG); if (!pp || !pp->value) return REQ_ABORTED; http_format_etag(sn, rq, pp->value, MAX_ETAG, size, mtime); pblock_kpinsert(pb_key_etag, pp, rq->srvhdrs); etag = pp->value; } else { etag = NULL; } /* Check If-modified-since, etc. */ return http_check_preconditions(sn, rq, mtm, etag); } NSAPI_PUBLIC int http_set_finfo(Session *sn, Request *rq, struct stat *finfo) { return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime); } /* ---------------------------- http_hdrs2env ----------------------------- */ NSAPI_PUBLIC char **http_hdrs2env(pblock *pb) { char *t, *n, **env; struct pb_entry *p; pb_param *pp; register int x, y, z; int pos, ts, ln; /* Find out how many there are. */ for(x = 0, y = 0; x < pb->hsize; x++) { p = pb->ht[x]; while(p) { ++y; p = p->next; } } env = util_env_create(NULL, y, &pos); ts = 1024; t = (char *) MALLOC(ts); for(x = 0; x < pb->hsize; x++) { p = pb->ht[x]; while(p) { pp = p->param; ln = strlen(pp->name) + 6; if(ln >= ts) { ts = ln; t = (char *) REALLOC(t, ts); } n = pp->name; /* Skip authorization for CGI */ if(strcasecmp(n, "authorization")) { if(strcasecmp(n, "content-type") && strcasecmp(n, "content-length")) { strncpy(t, "HTTP_", 5); z = 5; } else z = 0; for(y = 0; n[y]; ++y, ++z) t[z] = (n[y] == '-' ? '_' : toupper(n[y])); t[z] = '\0'; env[pos++] = util_env_str(t, pp->value); } p = p->next; } } env[pos] = NULL; FREE(t); return env; }