evhtp.c
Go to the documentation of this file.
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <signal.h>
#include <strings.h>
#include <inttypes.h>
#include <stdbool.h>
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#else
#define WINVER 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#ifndef NO_SYS_UN
#include <sys/un.h>
#endif
#include <limits.h>
#include <event2/dns.h>
#include "evhtp/config.h"
#include "internal.h"
#include "numtoa.h"
#include "evhtp/evhtp.h"
struct evhtp_callback {
evhtp_callback_type type;
evhtp_callback_cb cb;
void * cbarg;
evhtp_hooks_t * hooks;
size_t len;
union {
char * path;
char * glob;
#ifndef EVHTP_DISABLE_REGEX
regex_t * regex;
#endif
} val;
TAILQ_ENTRY(evhtp_callback) next;
};
TAILQ_HEAD(evhtp_callbacks, evhtp_callback);
#define SET_BIT(VAR, FLAG) VAR |= FLAG
#define UNSET_BIT(VAR, FLAG) VAR &= ~FLAG
#define HTP_FLAG_ON(PRE, FLAG) SET_BIT(PRE->flags, FLAG)
#define HTP_FLAG_OFF(PRE, FLAG) UNSET_BIT(PRE->flags, FLAG)
#define HTP_IS_READING(b) ((bufferevent_get_enabled(b) & \
EV_READ) ? true : false)
#define HTP_IS_WRITING(b) ((bufferevent_get_enabled(b) & \
EV_WRITE) ? true : false)
#define HTP_LEN_OUTPUT(b) (evbuffer_get_length(bufferevent_get_output(b)))
#define HTP_LEN_INPUT(b) (evbuffer_get_length(bufferevent_get_input(b)))
#define HOOK_AVAIL(var, hook_name) (var->hooks && var->hooks->hook_name)
#define HOOK_FUNC(var, hook_name) (var->hooks->hook_name)
#define HOOK_ARGS(var, hook_name) var->hooks->hook_name ## _arg
#define HOOK_REQUEST_RUN(request, hook_name, ...) do { \
if (HOOK_AVAIL(request, hook_name)) { \
return HOOK_FUNC(request, hook_name) (request, __VA_ARGS__, \
HOOK_ARGS(request, hook_name)); \
} \
if (request->conn && HOOK_AVAIL(request->conn, hook_name)) { \
return HOOK_FUNC(request->conn, hook_name) (request, __VA_ARGS__, \
HOOK_ARGS(request->conn, hook_name)); \
} \
} while (0)
#define HOOK_REQUEST_RUN_NARGS(__request, hook_name) do { \
if (HOOK_AVAIL(__request, hook_name)) { \
return HOOK_FUNC(__request, hook_name) (__request, \
HOOK_ARGS(__request, hook_name)); \
} \
if (__request->conn && HOOK_AVAIL(__request->conn, hook_name)) { \
return HOOK_FUNC(__request->conn, hook_name) (request, \
HOOK_ARGS(__request->conn, hook_name)); \
} \
} while (0);
#ifndef EVHTP_DISABLE_EVTHR
#define htp__lock_(h) do { \
if (h->lock) { \
pthread_mutex_lock(h->lock); \
} \
} while (0)
#define htp__unlock_(h) do { \
if (h->lock) { \
pthread_mutex_unlock(h->lock); \
} \
} while (0)
#else
#define htp__lock_(h) do { \
} while (0)
#define htp__unlock_(h) do { \
} while (0)
#endif
#ifndef TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#endif
/* rc == request->conn. Just little things to make life easier */
#define rc_scratch conn->scratch_buf
#define rc_parser conn->parser
/* ch_ == conn->hooks->on_... */
#define ch_fini_arg hooks->on_connection_fini_arg
#define ch_fini hooks->on_connection_fini
/* cr_ == conn->request */
#define cr_status request->status
#define cr_flags request->flags
#define cr_proto request->proto
/* rh_ == request->hooks->on_ */
#define rh_err hooks->on_error
#define rh_err_arg hooks->on_error_arg
#ifndef EVHTP_DISABLE_MEMFUNCTIONS
static void * (*malloc_)(size_t sz) = malloc;
static void * (* realloc_)(void * d, size_t sz) = realloc;
static void (* free_)(void * d) = free;
static void *
htp__malloc_(size_t size)
{
return malloc_(size);
}
static void *
htp__realloc_(void * ptr, size_t size)
{
return realloc_(ptr, size);
}
static void
htp__free_(void * ptr)
{
if (ptr == NULL) {
return;
}
evhtp_safe_free(ptr, free_);
}
static void *
htp__calloc_(size_t nmemb, size_t size)
{
if (malloc_ != malloc) {
size_t len = nmemb * size;
void * p;
if ((p = malloc_(len)) == NULL) {
return NULL;
}
memset(p, 0, len);
return p;
}
return calloc(nmemb, size);
}
static char *
htp__strdup_(const char * str)
{
if (malloc_ != malloc) {
size_t len;
void * p;
len = strlen(str);
if ((p = malloc_(len + 1)) == NULL) {
return NULL;
}
memcpy(p, str, len + 1);
return p;
}
return strdup(str);
}
static char *
htp__strndup_(const char * str, size_t len)
{
if (malloc_ != malloc) {
char * p;
if ((p = malloc_(len + 1)) != NULL) {
memcpy(p, str, len + 1);
} else {
return NULL;
}
p[len] = '\0';
return p;
}
return strndup(str, len);
}
#else
#define htp__malloc_(sz) malloc(sz)
#define htp__calloc_(n, sz) calloc(n, sz)
#define htp__strdup_(s) strdup(s)
#define htp__strndup_(n, sz) strndup(n, sz)
#define htp__realloc_(p, sz) realloc(p, sz)
#define htp__free_(p) free(p)
#endif
void
evhtp_set_mem_functions(void *(*mallocfn_)(size_t len),
void *(*reallocfn_)(void * p, size_t sz),
void (*freefn_)(void * p))
{
#ifndef EVHTP_DISABLE_MEMFUNCTIONS
malloc_ = mallocfn_;
realloc_ = reallocfn_;
free_ = freefn_;
return event_set_mem_functions(malloc_, realloc_, free_);
#endif
}
static const char *
status_code_to_str(evhtp_res code)
{
switch (code) {
case EVHTP_RES_200:
return "OK";
case EVHTP_RES_300:
return "Redirect";
case EVHTP_RES_400:
return "Bad Request";
case EVHTP_RES_NOTFOUND:
return "Not Found";
case EVHTP_RES_SERVERR:
return "Internal Server Error";
case EVHTP_RES_CONTINUE:
return "Continue";
case EVHTP_RES_FORBIDDEN:
return "Forbidden";
case EVHTP_RES_SWITCH_PROTO:
return "Switching Protocols";
case EVHTP_RES_MOVEDPERM:
return "Moved Permanently";
case EVHTP_RES_PROCESSING:
return "Processing";
case EVHTP_RES_URI_TOOLONG:
return "URI Too Long";
case EVHTP_RES_CREATED:
return "Created";
case EVHTP_RES_ACCEPTED:
return "Accepted";
case EVHTP_RES_NAUTHINFO:
return "No Auth Info";
case EVHTP_RES_NOCONTENT:
return "No Content";
case EVHTP_RES_RSTCONTENT:
return "Reset Content";
case EVHTP_RES_PARTIAL:
return "Partial Content";
case EVHTP_RES_MSTATUS:
return "Multi-Status";
case EVHTP_RES_IMUSED:
return "IM Used";
case EVHTP_RES_FOUND:
return "Found";
case EVHTP_RES_SEEOTHER:
return "See Other";
case EVHTP_RES_NOTMOD:
return "Not Modified";
case EVHTP_RES_USEPROXY:
return "Use Proxy";
case EVHTP_RES_SWITCHPROXY:
return "Switch Proxy";
case EVHTP_RES_TMPREDIR:
return "Temporary Redirect";
case EVHTP_RES_UNAUTH:
return "Unauthorized";
case EVHTP_RES_PAYREQ:
return "Payment Required";
case EVHTP_RES_METHNALLOWED:
return "Not Allowed";
case EVHTP_RES_NACCEPTABLE:
return "Not Acceptable";
case EVHTP_RES_PROXYAUTHREQ:
return "Proxy Authentication Required";
case EVHTP_RES_TIMEOUT:
return "Request Timeout";
case EVHTP_RES_CONFLICT:
return "Conflict";
case EVHTP_RES_GONE:
return "Gone";
case EVHTP_RES_LENREQ:
return "Length Required";
case EVHTP_RES_PRECONDFAIL:
return "Precondition Failed";
case EVHTP_RES_ENTOOLARGE:
return "Entity Too Large";
case EVHTP_RES_URITOOLARGE:
return "Request-URI Too Long";
case EVHTP_RES_UNSUPPORTED:
return "Unsupported Media Type";
case EVHTP_RES_RANGENOTSC:
return "Requested Range Not Satisfiable";
case EVHTP_RES_EXPECTFAIL:
return "Expectation Failed";
case EVHTP_RES_IAMATEAPOT:
return "I'm a teapot";
case EVHTP_RES_NOTIMPL:
return "Not Implemented";
case EVHTP_RES_BADGATEWAY:
return "Bad Gateway";
case EVHTP_RES_SERVUNAVAIL:
return "Service Unavailable";
case EVHTP_RES_GWTIMEOUT:
return "Gateway Timeout";
case EVHTP_RES_VERNSUPPORT:
return "HTTP Version Not Supported";
case EVHTP_RES_BWEXEED:
return "Bandwidth Limit Exceeded";
} /* switch */
return "UNKNOWN";
} /* status_code_to_str */
#ifndef EVHTP_DISABLE_SSL
static int session_id_context = 1;
#ifndef EVHTP_DISABLE_EVTHR
static int ssl_num_locks;
static evhtp_mutex_t * ssl_locks;
static int ssl_locks_initialized = 0;
#endif
#endif
/*
* COMPAT FUNCTIONS
*/
#ifdef NO_STRNLEN
static size_t
strnlen(const char * s, size_t maxlen)
{
const char * e;
size_t n;
for (e = s, n = 0; *e && n < maxlen; e++, n++) {
;
}
return n;
}
#endif
#ifdef NO_STRNDUP
static char *
strndup(const char * s, size_t n)
{
size_t len = strnlen(s, n);
char * ret;
if (len < n) {
return htp__strdup_(s);
}
if ((ret = htp__malloc_(n + 1)) == NULL) {
return NULL;
}
ret[n] = '\0';
memcpy(ret, s, n);
return ret;
}
#endif
/*
* PRIVATE FUNCTIONS
*/
#define htp__is_http_11_(_major, _minor) \
(_major >= 1 && _minor >= 1)
#define htp__is_http_10_(_major, _minor) \
(_major >= 1 && _minor <= 0)
static inline evhtp_proto
htp__protocol_(const char major, const char minor)
{
if (htp__is_http_10_(major, minor)) {
return EVHTP_PROTO_10;
}
if (htp__is_http_11_(major, minor)) {
return EVHTP_PROTO_11;
}
return EVHTP_PROTO_INVALID;
}
static inline evhtp_res
htp__hook_path_(struct evhtp_request * request, struct evhtp_path * path)
{
HOOK_REQUEST_RUN(request, on_path, path);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_header_(struct evhtp_request * request, evhtp_header_t * header)
{
HOOK_REQUEST_RUN(request, on_header, header);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_headers_(struct evhtp_request * request, evhtp_headers_t * headers)
{
HOOK_REQUEST_RUN(request, on_headers, headers);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_body_(struct evhtp_request * request, struct evbuffer * buf)
{
if (request == NULL) {
return EVHTP_RES_500;
}
HOOK_REQUEST_RUN(request, on_read, buf);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_request_fini_(struct evhtp_request * request)
{
if (request == NULL) {
return EVHTP_RES_500;
}
HOOK_REQUEST_RUN_NARGS(request, on_request_fini);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_chunk_new_(struct evhtp_request * request, uint64_t len)
{
HOOK_REQUEST_RUN(request, on_new_chunk, len);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_chunk_fini_(struct evhtp_request * request)
{
HOOK_REQUEST_RUN_NARGS(request, on_chunk_fini);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_chunks_fini_(struct evhtp_request * request)
{
HOOK_REQUEST_RUN_NARGS(request, on_chunks_fini);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_headers_start_(struct evhtp_request * request)
{
HOOK_REQUEST_RUN_NARGS(request, on_headers_start);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_connection_fini_(struct evhtp_connection * connection)
{
if (evhtp_unlikely(connection == NULL)) {
return 500;
}
if (connection->hooks != NULL && connection->ch_fini != NULL) {
return (connection->ch_fini)(connection, connection->ch_fini_arg);
}
return EVHTP_RES_OK;
}
static inline void
htp__hook_error_(struct evhtp_request * request, evhtp_error_flags errtype)
{
if (request && request->hooks && request->rh_err) {
(*request->rh_err)(request, errtype, request->rh_err_arg);
}
}
static inline evhtp_res
htp__hook_connection_error_(struct evhtp_connection * connection, evhtp_error_flags errtype)
{
if (connection == NULL) {
return EVHTP_RES_FATAL;
}
if (connection->request != NULL) {
htp__hook_error_(connection->request, errtype);
}
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_hostname_(struct evhtp_request * r, const char * hostname)
{
HOOK_REQUEST_RUN(r, on_hostname, hostname);
return EVHTP_RES_OK;
}
static inline evhtp_res
htp__hook_connection_write_(struct evhtp_connection * connection)
{
if (connection->hooks && connection->hooks->on_write) {
return (connection->hooks->on_write)(connection,
connection->hooks->on_write_arg);
}
return EVHTP_RES_OK;
}
static int
htp__glob_match_(const char * pattern, size_t plen,
const char * string, size_t str_len)
{
while (plen) {
switch (pattern[0]) {
case '*':
while (pattern[1] == '*') {
pattern++;
plen--;
}
if (plen == 1) {
return 1; /* match */
}
while (str_len) {
if (htp__glob_match_(pattern + 1, plen - 1,
string, str_len)) {
return 1; /* match */
}
string++;
str_len--;
}
return 0; /* no match */
default:
if (pattern[0] != string[0]) {
return 0; /* no match */
}
string++;
str_len--;
break;
} /* switch */
pattern++;
plen--;
if (str_len == 0) {
while (*pattern == '*') {
pattern++;
plen--;
}
break;
}
}
if (plen == 0 && str_len == 0) {
return 1;
}
return 0;
} /* htp__glob_match_ */
static evhtp_callback_t *
htp__callback_find_(evhtp_callbacks_t * cbs,
const char * path,
unsigned int * start_offset,
unsigned int * end_offset)
{
size_t path_len;
evhtp_callback_t * callback;
#ifndef EVHTP_DISABLE_REGEX
regmatch_t pmatch[28];
#endif
if (evhtp_unlikely(cbs == NULL)) {
return NULL;
}
path_len = strlen(path);
TAILQ_FOREACH(callback, cbs, next) {
switch (callback->type) {
case evhtp_callback_type_hash:
if (strncmp(path, callback->val.path, callback->len) == 0) {
*start_offset = 0;
*end_offset = path_len;
return callback;
}
break;
#ifndef EVHTP_DISABLE_REGEX
case evhtp_callback_type_regex:
if (regexec(callback->val.regex,
path,
callback->val.regex->re_nsub + 1,
pmatch, 0) == 0) {
*start_offset = pmatch[callback->val.regex->re_nsub].rm_so;
*end_offset = pmatch[callback->val.regex->re_nsub].rm_eo;
return callback;
}
break;
#endif
case evhtp_callback_type_glob:
{
size_t glob_len = strlen(callback->val.glob);
if (htp__glob_match_(callback->val.glob,
glob_len,
path,
path_len) == 1) {
*start_offset = 0;
*end_offset = path_len;
return callback;
}
}
default:
break;
} /* switch */
}
return NULL;
} /* htp__callback_find_ */
static void
htp__path_free_(struct evhtp_path * path)
{
if (evhtp_unlikely(path == NULL)) {
return;
}
evhtp_safe_free(path->full, htp__free_);
evhtp_safe_free(path->path, htp__free_);
evhtp_safe_free(path->file, htp__free_);
evhtp_safe_free(path->match_start, htp__free_);
evhtp_safe_free(path->match_end, htp__free_);
evhtp_safe_free(path, htp__free_);
}
static int
htp__path_new_(evhtp_path_t ** out, const char * data, size_t len)
{
struct evhtp_path * req_path = NULL;
const char * data_end = (const char *)(data + len);
char * path = NULL;
char * file = NULL;
req_path = htp__calloc_(1, sizeof(*req_path));
#ifndef NDEBUG
if (req_path == NULL) {
return -1;
}
#endif
*out = NULL;
if (evhtp_unlikely(len == 0)) {
/*
* odd situation here, no preceding "/", so just assume the path is "/"
*/
path = htp__strdup_("/");
if (evhtp_unlikely(path == NULL)) {
goto error;
}
} else if (*data != '/') {
/* request like GET stupid HTTP/1.0, treat stupid as the file, and
* assume the path is "/"
*/
path = htp__strdup_("/");
if (evhtp_unlikely(path == NULL)) {
goto error;
}
file = htp__strndup_(data, len);
if (evhtp_unlikely(file == NULL)) {
goto error;
}
} else {
if (data[len - 1] != '/') {
/*
* the last character in data is assumed to be a file, not the end of path
* loop through the input data backwards until we find a "/"
*/
size_t i;
for (i = (len - 1); i != 0; i--) {
if (data[i] == '/') {
/*
* we have found a "/" representing the start of the file,
* and the end of the path
*/
size_t path_len;
size_t file_len;
path_len = (size_t)(&data[i] - data) + 1;
file_len = (size_t)(data_end - &data[i + 1]);
/* check for overflow */
if ((const char *)(data + path_len) > data_end) {
goto error;
}
/* check for overflow */
if ((const char *)(&data[i + 1] + file_len) > data_end) {
goto error;
}
path = htp__strndup_(data, path_len);
if (evhtp_unlikely(path == NULL)) {
goto error;
}
file = htp__strndup_(&data[i + 1], file_len);
if (evhtp_unlikely(file == NULL)) {
goto error;
}
break;
}
}
if (i == 0 && data[i] == '/' && !file && !path) {
/* drops here if the request is something like GET /foo */
path = htp__strdup_("/");
if (evhtp_unlikely(path == NULL)) {
goto error;
}
if (len > 1) {
file = htp__strndup_((const char *)(data + 1), len);
if (evhtp_unlikely(file == NULL)) {
goto error;
}
}
}
} else {
/* the last character is a "/", thus the request is just a path */
path = htp__strndup_(data, len);
if (evhtp_unlikely(path == NULL)) {
goto error;
}
}
}
if (len != 0) {
req_path->full = htp__strndup_(data, len);
} else {
req_path->full = htp__strdup_("/");
}
if (evhtp_unlikely(req_path->full == NULL)) {
goto error;
}
req_path->path = path;
req_path->file = file;
*out = req_path;
return 0;
error:
evhtp_safe_free(path, htp__free_);
evhtp_safe_free(file, htp__free_);
evhtp_safe_free(req_path, htp__path_free_);
return -1;
} /* htp__path_new_ */
static int
htp__authority_new_(evhtp_authority_t ** out)
{
struct evhtp_authority * authority;
if (evhtp_unlikely(out == NULL)) {
return -1;
}
*out = htp__calloc_(1, sizeof(*authority));
return (*out != NULL) ? 0 : -1;
}
static void
htp__authority_free_(evhtp_authority_t * authority)
{
if (authority == NULL) {
return;
}
evhtp_safe_free(authority->username, htp__free_);
evhtp_safe_free(authority->password, htp__free_);
evhtp_safe_free(authority->hostname, htp__free_);
evhtp_safe_free(authority, htp__free_);
}
static void
htp__uri_free_(evhtp_uri_t * uri)
{
if (evhtp_unlikely(uri == NULL)) {
return;
}
evhtp_safe_free(uri->query, evhtp_query_free);
evhtp_safe_free(uri->path, htp__path_free_);
evhtp_safe_free(uri->authority, htp__authority_free_);
evhtp_safe_free(uri->fragment, htp__free_);
evhtp_safe_free(uri->query_raw, htp__free_);
evhtp_safe_free(uri, htp__free_);
}
static int
htp__uri_new_(evhtp_uri_t ** out)
{
struct evhtp_uri * uri;
*out = NULL;
if ((uri = htp__calloc_(1, sizeof(*uri))) == NULL) {
return -1;
}
uri->authority = NULL;
if (htp__authority_new_(&uri->authority) == -1) {
evhtp_safe_free(uri, htp__uri_free_);
return -1;
}
*out = uri;
return 0;
}
static void
htp__request_free_(evhtp_request_t * request)
{
if (request == NULL) {
return;
}
htp__hook_request_fini_(request);
evhtp_safe_free(request->uri, htp__uri_free_);
evhtp_safe_free(request->headers_in, evhtp_kvs_free);
evhtp_safe_free(request->headers_out, evhtp_kvs_free);
if (request->conn && request->conn->request == request) {
request->conn->request = NULL;
}
if (request->buffer_in != NULL) {
evhtp_safe_free(request->buffer_in, evbuffer_free);
}
if (request->buffer_out != NULL) {
evhtp_safe_free(request->buffer_out, evbuffer_free);
}
evhtp_safe_free(request->hooks, htp__free_);
evhtp_safe_free(request, htp__free_);
}
static evhtp_request_t *
htp__request_new_(evhtp_connection_t * c)
{
struct evhtp_request * req;
uint8_t error;
if (evhtp_unlikely(!(req = htp__calloc_(sizeof(*req), 1)))) {
return NULL;
}
error = 1;
req->conn = c;
req->htp = c ? c->htp : NULL;
req->status = EVHTP_RES_OK;
do {
if (!(req->buffer_in = evbuffer_new())) {
break;
}
if (!(req->buffer_out = evbuffer_new())) {
break;
}
if (!(req->headers_in = htp__malloc_(sizeof(*req->headers_in)))) {
break;
}
if (!(req->headers_out = htp__malloc_(sizeof(evhtp_headers_t)))) {
break;
}
TAILQ_INIT(req->headers_in);
TAILQ_INIT(req->headers_out);
error = 0;
} while (0);
if (error == 0) {
return req;
}
evhtp_safe_free(req, htp__request_free_);
return req;
} /* htp__request_new_ */
static int
htp__request_parse_start_(htparser * p)
{
evhtp_connection_t * c;
if (p == NULL) {
return 0;
}
if (!(c = htparser_get_userdata(p))) {
return 0;
}
if (evhtp_unlikely(c->type == evhtp_type_client)) {
return 0;
}
if (c->flags & EVHTP_CONN_FLAG_PAUSED) {
return -1;
}
if (c->request) {
if (c->request->flags & EVHTP_REQ_FLAG_FINISHED) {
evhtp_safe_free(c->request, htp__request_free_);
} else {
return -1;
}
}
if (((c->request = htp__request_new_(c))) == NULL) {
return -1;
}
return 0;
}
static int
htp__request_parse_args_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
evhtp_uri_t * uri = c->request->uri;
const char * fragment;
int ignore_fragment;
if (c->type == evhtp_type_client) {
/* as a client, technically we should never get here, but just in case
* we return a 0 to the parser to continue.
*/
return 0;
}
/* if the parser flags has the IGNORE_FRAGMENTS bit set, skip
* the fragment parsing
*/
ignore_fragment = (c->htp->parser_flags &
EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS);
if (!ignore_fragment && (fragment = memchr(data, '#', len))) {
/* Separate fragment from query according to RFC 3986.
*
* XXX: not happy about using strchr stuff, maybe this functionality
* is more apt as part of evhtp_parse_query()
*/
ptrdiff_t frag_offset;
frag_offset = fragment - data;
if (frag_offset < len) {
size_t fraglen;
/* Skip '#'. */
fragment += 1;
frag_offset += 1;
fraglen = len - frag_offset;
uri->fragment = htp__malloc_(fraglen + 1);
evhtp_alloc_assert(uri->fragment);
memcpy(uri->fragment, fragment, fraglen);
uri->fragment[fraglen] = '\0';
len -= fraglen + 1; /* Skip '#' + fragment string. */
}
}
uri->query = evhtp_parse_query_wflags(data, len, c->htp->parser_flags);
if (evhtp_unlikely(!uri->query)) {
c->cr_status = EVHTP_RES_ERROR;
return -1;
}
uri->query_raw = htp__malloc_(len + 1);
evhtp_alloc_assert(uri->query_raw);
memcpy(uri->query_raw, data, len);
uri->query_raw[len] = '\0';
return 0;
} /* htp__request_parse_args_ */
static int
htp__request_parse_headers_start_(htparser * p)
{
evhtp_connection_t * c = htparser_get_userdata(p);
if ((c->cr_status = htp__hook_headers_start_(c->request)) != EVHTP_RES_OK) {
return -1;
}
return 0;
}
static int
htp__request_parse_header_key_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
char * key_s;
evhtp_header_t * hdr;
key_s = htp__malloc_(len + 1);
evhtp_alloc_assert(key_s);
if (key_s == NULL) {
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
key_s[len] = '\0';
memcpy(key_s, data, len);
if ((hdr = evhtp_header_key_add(c->request->headers_in, key_s, 0)) == NULL) {
htp__free_(key_s);
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
hdr->k_heaped = 1;
return 0;
}
static int
htp__request_parse_header_val_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
char * val_s;
evhtp_header_t * header;
val_s = htp__malloc_(len + 1);
evhtp_alloc_assert(val_s);
val_s[len] = '\0';
memcpy(val_s, data, len);
if ((header = evhtp_header_val_add(c->request->headers_in, val_s, 0)) == NULL) {
evhtp_safe_free(val_s, htp__free_);
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
header->v_heaped = 1;
if ((c->cr_status = htp__hook_header_(c->request, header)) != EVHTP_RES_OK) {
return -1;
}
return 0;
}
static inline evhtp_t *
htp__request_find_vhost_(evhtp_t * evhtp, const char * name)
{
evhtp_t * evhtp_vhost;
evhtp_alias_t * evhtp_alias;
TAILQ_FOREACH(evhtp_vhost, &evhtp->vhosts, next_vhost) {
if (evhtp_unlikely(evhtp_vhost->server_name == NULL)) {
continue;
}
if (htp__glob_match_(evhtp_vhost->server_name,
strlen(evhtp_vhost->server_name), name,
strlen(name)) == 1) {
return evhtp_vhost;
}
TAILQ_FOREACH(evhtp_alias, &evhtp_vhost->aliases, next) {
if (evhtp_alias->alias == NULL) {
continue;
}
if (htp__glob_match_(evhtp_alias->alias,
strlen(evhtp_alias->alias), name,
strlen(name)) == 1) {
return evhtp_vhost;
}
}
}
return NULL;
}
static inline int
htp__request_set_callbacks_(evhtp_request_t * request)
{
evhtp_t * evhtp;
evhtp_connection_t * conn;
evhtp_uri_t * uri;
evhtp_path_t * path;
evhtp_hooks_t * hooks;
evhtp_callback_t * callback;
evhtp_callback_cb cb;
void * cbarg;
if (request == NULL) {
return -1;
}
if ((evhtp = request->htp) == NULL) {
return -1;
}
if ((conn = request->conn) == NULL) {
return -1;
}
if ((uri = request->uri) == NULL) {
return -1;
}
if ((path = uri->path) == NULL) {
return -1;
}
hooks = NULL;
callback = NULL;
cb = NULL;
cbarg = NULL;
if ((callback = htp__callback_find_(evhtp->callbacks, path->full,
&path->matched_soff, &path->matched_eoff))) {
/* matched a callback using both path and file (/a/b/c/d) */
cb = callback->cb;
cbarg = callback->cbarg;
hooks = callback->hooks;
} else if ((callback = htp__callback_find_(evhtp->callbacks, path->path,
&path->matched_soff, &path->matched_eoff))) {
/* matched a callback using *just* the path (/a/b/c/) */
cb = callback->cb;
cbarg = callback->cbarg;
hooks = callback->hooks;
} else {
/* no callbacks found for either case, use defaults */
cb = evhtp->defaults.cb;
cbarg = evhtp->defaults.cbarg;
path->matched_soff = 0;
path->matched_eoff = (unsigned int)strlen(path->full);
}
if (path->match_start == NULL) {
path->match_start = htp__calloc_(strlen(path->full) + 1, 1);
evhtp_alloc_assert(path->match_start);
}
if (path->match_end == NULL) {
path->match_end = htp__calloc_(strlen(path->full) + 1, 1);
evhtp_alloc_assert(path->match_end);
}
if (path->matched_soff != UINT_MAX /*ONIG_REGION_NOTPOS*/) {
if (path->matched_eoff - path->matched_soff) {
memcpy(path->match_start, (void *)(path->full + path->matched_soff),
path->matched_eoff - path->matched_soff);
} else {
memcpy(path->match_start, (void *)(path->full + path->matched_soff),
strlen((const char *)(path->full + path->matched_soff)));
}
memcpy(path->match_end,
(void *)(path->full + path->matched_eoff),
strlen(path->full) - path->matched_eoff);
}
if (hooks != NULL) {
if (request->hooks == NULL) {
request->hooks = htp__malloc_(sizeof(evhtp_hooks_t));
evhtp_alloc_assert(request->hooks);
}
memcpy(request->hooks, hooks, sizeof(evhtp_hooks_t));
}
request->cb = cb;
request->cbarg = cbarg;
return 0;
} /* htp__request_set_callbacks_ */
static int
htp__request_parse_hostname_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
evhtp_t * evhtp;
evhtp_t * evhtp_vhost;
#ifndef EVHTP_DISABLE_SSL
if ((c->flags & EVHTP_CONN_FLAG_VHOST_VIA_SNI) && c->ssl != NULL) {
/* use the SNI set hostname instead of the header hostname */
const char * host;
host = SSL_get_servername(c->ssl, TLSEXT_NAMETYPE_host_name);
if ((c->cr_status = htp__hook_hostname_(c->request, host)) != EVHTP_RES_OK) {
return -1;
}
return 0;
}
#endif
evhtp = c->htp;
/* since this is called after htp__request_parse_path_(), which already
* setup callbacks for the URI, we must now attempt to find callbacks which
* are specific to this host.
*/
htp__lock_(evhtp);
{
if ((evhtp_vhost = htp__request_find_vhost_(evhtp, data))) {
htp__lock_(evhtp_vhost);
{
/* if we found a match for the host, we must set the htp
* variables for both the connection and the request.
*/
c->htp = evhtp_vhost;
c->request->htp = evhtp_vhost;
htp__request_set_callbacks_(c->request);
}
htp__unlock_(evhtp_vhost);
}
}
htp__unlock_(evhtp);
if ((c->cr_status = htp__hook_hostname_(c->request, data)) != EVHTP_RES_OK) {
return -1;
}
return 0;
} /* htp__request_parse_hostname_ */
static int
htp__require_uri_(evhtp_connection_t * c)
{
if (c != NULL && c->request != NULL) {
if (c->request->uri == NULL) {
return htp__uri_new_(&c->request->uri);
}
return 0;
}
return -1;
}
static int
htp__request_parse_host_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c;
evhtp_authority_t * authority;
if (evhtp_unlikely(p == NULL)) {
return -1;
}
c = htparser_get_userdata(p);
/* all null checks are done in require_uri_,
* no need to check twice
*/
if (htp__require_uri_(c) == -1) {
return -1;
}
authority = c->request->uri->authority;
authority->hostname = htp__malloc_(len + 1);
evhtp_alloc_assert(authority->hostname);
if (authority->hostname == NULL) {
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
memcpy(authority->hostname, data, len);
authority->hostname[len] = '\0';
return 0;
}
static int
htp__request_parse_port_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
evhtp_authority_t * authority;
char * endptr;
unsigned long port;
if (htp__require_uri_(c) == -1) {
return -1;
}
authority = c->request->uri->authority;
port = strtoul(data, &endptr, 10);
if (endptr - data != len || port > 65535) {
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
authority->port = port;
return 0;
}
static int
htp__request_parse_path_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
evhtp_path_t * path;
if (evhtp_unlikely(p == NULL || c == NULL)) {
return -1;
}
if (htp__require_uri_(c) == -1) {
return -1;
}
if (htp__path_new_(&path, data, len) == -1) {
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
c->request->uri->path = path;
c->request->uri->scheme = htparser_get_scheme(p);
c->request->method = htparser_get_method(p);
htp__lock_(c->htp);
{
htp__request_set_callbacks_(c->request);
}
htp__unlock_(c->htp);
if ((c->cr_status = htp__hook_path_(c->request, path)) != EVHTP_RES_OK) {
return -1;
}
return 0;
} /* htp__request_parse_path_ */
static int
htp__request_parse_headers_(htparser * p)
{
evhtp_connection_t * c;
if ((c = htparser_get_userdata(p)) == NULL) {
return -1;
}
/* XXX proto should be set with htparsers on_hdrs_begin hook */
if (htparser_should_keep_alive(p) == 1) {
HTP_FLAG_ON(c->request, EVHTP_REQ_FLAG_KEEPALIVE);
}
c->cr_proto = htp__protocol_(htparser_get_major(p), htparser_get_minor(p));
c->cr_status = htp__hook_headers_(c->request, c->request->headers_in);
if (c->cr_status != EVHTP_RES_OK) {
return -1;
}
if (c->type == evhtp_type_server
&& c->htp->flags & EVHTP_FLAG_ENABLE_100_CONT) {
/* only send a 100 continue response if it hasn't been disabled via
* evhtp_disable_100_continue.
*/
if (!evhtp_header_find(c->request->headers_in, "Expect")) {
return 0;
}
evbuffer_add_printf(bufferevent_get_output(c->bev),
"HTTP/%c.%c 100 Continue\r\n\r\n",
evhtp_modp_uchartoa(htparser_get_major(p)),
evhtp_modp_uchartoa(htparser_get_minor(p)));
}
return 0;
}
static int
htp__request_parse_body_(htparser * p, const char * data, size_t len)
{
evhtp_connection_t * c = htparser_get_userdata(p);
struct evbuffer * buf;
int res = 0;
if (c->max_body_size > 0 && c->body_bytes_read + len >= c->max_body_size) {
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR);
c->cr_status = EVHTP_RES_DATA_TOO_LONG;
return -1;
}
if ((buf = c->scratch_buf) == NULL) {
return -1;
}
evbuffer_add(buf, data, len);
if ((c->cr_status = htp__hook_body_(c->request, buf)) != EVHTP_RES_OK) {
res = -1;
}
if (evbuffer_get_length(buf)) {
evbuffer_add_buffer(c->request->buffer_in, buf);
}
evbuffer_drain(buf, -1);
c->body_bytes_read += len;
return res;
}
static int
htp__request_parse_chunk_new_(htparser * p)
{
evhtp_connection_t * c = htparser_get_userdata(p);
if (c == NULL) {
return -1;
}
if ((c->cr_status =
htp__hook_chunk_new_(c->request, htparser_get_content_length(p))) != EVHTP_RES_OK) {
return -1;
}
return 0;
}
static int
htp__request_parse_chunk_fini_(htparser * p)
{
evhtp_connection_t * c = htparser_get_userdata(p);
if (c == NULL) {
return -1;
}
if ((c->cr_status = htp__hook_chunk_fini_(c->request)) != EVHTP_RES_OK) {
return -1;
}
return 0;
}
static int
htp__request_parse_chunks_fini_(htparser * p)
{
evhtp_connection_t * c = htparser_get_userdata(p);
if (c == NULL) {
return -1;
}
if ((c->cr_status = htp__hook_chunks_fini_(c->request)) != EVHTP_RES_OK) {
return -1;
}
return 0;
}
static int
htp__should_parse_query_body_(evhtp_request_t * req)
{
const char * content_type;
if (req == NULL) {
return 0;
}
if (req->uri == NULL || req->uri->query != NULL) {
return 0;
}
if (evhtp_request_content_len(req) == 0) {
return 0;
}
if (evhtp_request_content_len(req) !=
evbuffer_get_length(req->buffer_in)) {
return 0;
}
content_type = evhtp_kv_find(req->headers_in, "content-type");
if (content_type == NULL) {
return 0;
}
if (strncasecmp(content_type, "application/x-www-form-urlencoded", 33)) {
return 0;
}
return 1;
}
static int
htp__request_parse_fini_(htparser * p)
{
evhtp_connection_t * c = htparser_get_userdata(p);
if (c == NULL) {
return -1;
}
if (c->flags & EVHTP_CONN_FLAG_PAUSED) {
return -1;
}
/* check to see if we should use the body of the request as the query
* arguments.
*
* htp__should_parse_query_body_ does all the proper null checks.
*/
if (htp__should_parse_query_body_(c->request) == 1) {
const char * body;
size_t body_len;
evhtp_uri_t * uri;
struct evbuffer * buf_in;
uri = c->request->uri;
buf_in = c->request->buffer_in;
body_len = evbuffer_get_length(buf_in);
body = (const char *)evbuffer_pullup(buf_in, body_len);
uri->query_raw = htp__calloc_(body_len + 1, 1);
if (evhtp_unlikely(uri->query_raw == NULL)) {
c->cr_status = EVHTP_RES_FATAL;
return -1;
}
memcpy(uri->query_raw, body, body_len);
uri->query = evhtp_parse_query(body, body_len);
}
/*
* XXX c->request should never be NULL, but we have found some path of
* execution where this actually happens. We will check for now, but the bug
* path needs to be tracked down.
*
*/
if (c->request && c->request->cb) {
(c->request->cb)(c->request, c->request->cbarg);
}
if (c->flags & EVHTP_CONN_FLAG_PAUSED) {
return -1;
}
return 0;
} /* htp__request_parse_fini_ */
static size_t
htp__evbuffer_add_iovec_(struct evbuffer * buf, struct evbuffer_iovec * vec, int n_vec)
{
#if LIBEVENT_VERSION_NUMBER < 0x02010000
int n;
size_t res;
size_t to_alloc;
res = to_alloc = 0;
for (n = 0; n < n_vec; n++) {
to_alloc += vec[n].iov_len;
}
evbuffer_expand(buf, to_alloc);
for (n = 0; n < n_vec; n++) {
evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len);
res += vec[n].iov_len;
}
return res;
#else
return evbuffer_add_iovec(buf, vec, n_vec);
#endif
}
static int
htp__create_headers_(evhtp_header_t * header, void * arg)
{
struct evbuffer * buf = arg;
struct evbuffer_iovec iov[4] = {
{ header->key, header->klen },
{ ": ", 2 },
{ header->val, header->vlen },
{ "\r\n", 2 }
};
htp__evbuffer_add_iovec_(buf, iov, 4);
return 0;
}
static struct evbuffer *
htp__create_reply_(evhtp_request_t * request, evhtp_res code)
{
struct evbuffer * buf;
const char * content_type;
char res_buf[2048];
int sres;
size_t out_len;
unsigned char major;
unsigned char minor;
char out_buf[64];
evhtp_assert(request
&& request->headers_out
&& request->buffer_out
&& request->conn
&& request->rc_parser);
request->status = code;
content_type = evhtp_header_find(request->headers_out, "Content-Type");
out_len = evbuffer_get_length(request->buffer_out);
if ((buf = request->rc_scratch) == NULL) {
request->rc_scratch = evbuffer_new();
evhtp_alloc_assert(request->rc_scratch);
}
evbuffer_drain(buf, -1);
if (htparser_get_multipart(request->rc_parser) == 1) {
goto check_proto;
}
if (out_len && !(request->flags & EVHTP_REQ_FLAG_CHUNKED)) {
/* add extra headers (like content-length/type) if not already present */
if (!evhtp_header_find(request->headers_out, "Content-Length")) {
/* convert the buffer_out length to a string and set
* and add the new Content-Length header.
*/
evhtp_modp_sizetoa(out_len, out_buf);
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Content-Length", out_buf, 0, 1));
}
}
check_proto:
/* add the proper keep-alive type headers based on http version */
switch (request->proto) {
case EVHTP_PROTO_11:
if (!(request->flags & EVHTP_REQ_FLAG_KEEPALIVE)) {
/* protocol is HTTP/1.1 but client wanted to close */
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Connection", "close", 0, 0));
}
if (!evhtp_header_find(request->headers_out, "Content-Length") &&
/* cannot have both chunked and content-length */
!(request->flags & EVHTP_REQ_FLAG_CHUNKED)) {
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Content-Length", "0", 0, 0));
}
break;
case EVHTP_PROTO_10:
if (request->flags & EVHTP_REQ_FLAG_KEEPALIVE) {
/* protocol is HTTP/1.0 and clients wants to keep established */
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Connection", "keep-alive", 0, 0));
}
break;
default:
/* this sometimes happens when a response is made but paused before
* the method has been parsed */
htparser_set_major(request->rc_parser, 1);
htparser_set_minor(request->rc_parser, 0);
break;
} /* switch */
if (!content_type) {
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Content-Type", "text/plain", 0, 0));
}
/* attempt to add the status line into a temporary buffer and then use
* evbuffer_add(). Using plain old snprintf() will be faster than
* evbuffer_add_printf(). If the snprintf() fails, which it rarely should,
* we fallback to using evbuffer_add_printf().
*/
major = evhtp_modp_uchartoa(htparser_get_major(request->rc_parser));
minor = evhtp_modp_uchartoa(htparser_get_minor(request->rc_parser));
evhtp_modp_u32toa((uint32_t)code, out_buf);
/* create the initial reply status via scatter-gather io (note: this used to
* be a formatted write which led to some spurrious performance problems.
* This now uses iovec/scatter/gather to create the status reply portion
* of the header.
*/
{
const char * status_str = status_code_to_str(code);
struct evbuffer_iovec iov[9] = {
{ "HTTP/1", 5 }, /* data == "HTTP/" */
{ (void *)&major, 1 }, /* data == "HTTP/X */
{ ".", 1 }, /* data == "HTTP/X." */
{ (void *)&minor, 1 }, /* data == "HTTP/X.X" */
{ " ", 1 }, /* data == "HTTP/X.X " */
{ out_buf, strlen(out_buf) }, /* data = "HTTP/X.X YYY" */
{ " ", 1 }, /* data = "HTTP/X.X YYY " */
{ (void *)status_str, strlen(status_str) }, /* data = "HTTP/X.X YYY ZZZ" */
{ "\r\n", 2 }, /* data = "HTTP/X.X YYY ZZZ\r\n" */
};
htp__evbuffer_add_iovec_(buf, iov, 9);
}
evhtp_headers_for_each(request->headers_out, htp__create_headers_, buf);
evbuffer_add(buf, "\r\n", 2);
if (evbuffer_get_length(request->buffer_out)) {
evbuffer_add_buffer(buf, request->buffer_out);
}
return buf;
} /* htp__create_reply_ */
static htparse_hooks request_psets = {
.on_msg_begin = htp__request_parse_start_,
.method = NULL,
.scheme = NULL,
.host = htp__request_parse_host_,
.port = htp__request_parse_port_,
.path = htp__request_parse_path_,
.args = htp__request_parse_args_,
.uri = NULL,
.on_hdrs_begin = htp__request_parse_headers_start_,
.hdr_key = htp__request_parse_header_key_,
.hdr_val = htp__request_parse_header_val_,
.hostname = htp__request_parse_hostname_,
.on_hdrs_complete = htp__request_parse_headers_,
.on_new_chunk = htp__request_parse_chunk_new_,
.on_chunk_complete = htp__request_parse_chunk_fini_,
.on_chunks_complete = htp__request_parse_chunks_fini_,
.body = htp__request_parse_body_,
.on_msg_complete = htp__request_parse_fini_
};
static void
htp__connection_readcb_(struct bufferevent * bev, void * arg)
{
evhtp_connection_t * c = arg;
void * buf;
size_t nread;
size_t avail;
if (evhtp_unlikely(bev == NULL)) {
return;
}
avail = HTP_LEN_INPUT(bev);
if (evhtp_unlikely(avail == 0)) {
return;
}
if (c->flags & EVHTP_CONN_FLAG_PAUSED) {
log_debug("connection is paused, returning");
return;
}
if (c->request) {
c->cr_status = EVHTP_RES_OK;
}
buf = evbuffer_pullup(bufferevent_get_input(bev), avail);
evhtp_assert(buf != NULL);
evhtp_assert(c->parser != NULL);
nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail);
log_debug("nread = %zu", nread);
if (!(c->flags & EVHTP_CONN_FLAG_OWNER)) {
/*
* someone has taken the ownership of this connection, we still need to
* drain the input buffer that had been read up to this point.
*/
log_debug("EVHTP_CONN_FLAG_OWNER set, removing contexts");
evbuffer_drain(bufferevent_get_input(bev), nread);
evhtp_safe_free(c, evhtp_connection_free);
return;
}
if (c->request) {
switch (c->cr_status) {
case EVHTP_RES_DATA_TOO_LONG:
htp__hook_connection_error_(c, -1);
evhtp_safe_free(c, evhtp_connection_free);
return;
default:
break;
}
}
evbuffer_drain(bufferevent_get_input(bev), nread);
if (c->request && c->cr_status == EVHTP_RES_PAUSE) {
log_debug("Pausing connection");
evhtp_request_pause(c->request);
} else if (htparser_get_error(c->parser) != htparse_error_none) {
log_debug("error %d, freeing connection",
htparser_get_error(c->parser));
evhtp_safe_free(c, evhtp_connection_free);
} else if (nread < avail) {
/* we still have more data to read (piped request probably) */
log_debug("Reading more data via resumption");
evhtp_connection_resume(c);
}
} /* htp__connection_readcb_ */
static void
htp__connection_writecb_(struct bufferevent * bev, void * arg)
{
evhtp_connection_t * conn;
uint64_t keepalive_max;
const char * errstr;
evhtp_assert(bev != NULL);
log_debug("writecb");
if (evhtp_unlikely(arg == NULL)) {
log_error("No data associated with the bufferevent %p", bev);
evhtp_safe_free(bev, bufferevent_free);
return;
}
errstr = NULL;
conn = (evhtp_connection_t *)arg;
do {
if (evhtp_unlikely(conn->request == NULL)) {
errstr = "no request associated with connection";
break;
}
if (evhtp_unlikely(conn->parser == NULL)) {
errstr = "no parser registered with connection";
break;
}
if (evhtp_likely(conn->type == evhtp_type_server)) {
if (evhtp_unlikely(conn->htp == NULL)) {
errstr = "no context associated with the server-connection";
break;
}
keepalive_max = conn->htp->max_keepalive_requests;
} else {
keepalive_max = 0;
}
} while (0);
if (evhtp_unlikely(errstr != NULL)) {
log_error("shutting down connection: %s", errstr);
evhtp_safe_free(conn, evhtp_connection_free);
return;
}
/* connection is in a paused state, no further processing yet */
if (conn->flags & EVHTP_CONN_FLAG_PAUSED) {
log_debug("is paused");
return;
}
/* run user-hook for on_write callback before further analysis */
htp__hook_connection_write_(conn);
if (conn->flags & EVHTP_CONN_FLAG_WAITING) {
log_debug("Disabling WAIT flag");
HTP_FLAG_OFF(conn, EVHTP_CONN_FLAG_WAITING);
if (HTP_IS_READING(bev) == false) {
log_debug("enabling EV_READ");
bufferevent_enable(bev, EV_READ);
}
if (HTP_LEN_INPUT(bev)) {
log_debug("have input data, will travel");
htp__connection_readcb_(bev, arg);
return;
}
}
/* if the connection is not finished, OR there is data ready to output
* (can only happen if a user-defined connection_write hook added data
* manually, since this is called only when all data has been flushed)
* just return and wait.
*/
if (!(conn->cr_flags & EVHTP_REQ_FLAG_FINISHED) || HTP_LEN_OUTPUT(bev)) {
log_debug("not finished");
return;
}
/*
* if there is a set maximum number of keepalive requests configured, check
* to make sure we are not over it. If we have gone over the max we set the
* keepalive bit to 0, thus closing the connection.
*/
if (keepalive_max > 0) {
if (++conn->num_requests >= keepalive_max) {
HTP_FLAG_OFF(conn->request, EVHTP_REQ_FLAG_KEEPALIVE);
}
}
if (conn->cr_flags & EVHTP_REQ_FLAG_KEEPALIVE) {
htp_type type;
log_debug("keep-alive on");
/* free up the current request, set it to NULL, making
* way for the next request.
*/
evhtp_safe_free(conn->request, htp__request_free_);
/* since the request is keep-alive, assure that the connection
* is aware of the same.
*/
HTP_FLAG_ON(conn, EVHTP_CONN_FLAG_KEEPALIVE);
conn->body_bytes_read = 0;
if (conn->type == evhtp_type_server) {
if (conn->htp->parent != NULL
&& !(conn->flags & EVHTP_CONN_FLAG_VHOST_VIA_SNI)) {
/* this request was served by a virtual host evhtp_t structure
* which was *NOT* found via SSL SNI lookup. In this case we want to
* reset our connections evhtp_t structure back to the original so
* that subsequent requests can have a different 'Host' header.
*/
conn->htp = conn->htp->parent;
}
}
switch (conn->type) {
case evhtp_type_client:
type = htp_type_response;
break;
case evhtp_type_server:
type = htp_type_request;
break;
default:
log_error("Unknown connection type");
evhtp_safe_free(conn, evhtp_connection_free);
return;
}
htparser_init(conn->parser, type);
htparser_set_userdata(conn->parser, conn);
return;
} else {
log_debug("goodbye connection");
evhtp_safe_free(conn, evhtp_connection_free);
return;
}
return;
} /* htp__connection_writecb_ */
static void
htp__connection_eventcb_(struct bufferevent * bev, short events, void * arg)
{
evhtp_connection_t * c = arg;
log_debug("%p %p eventcb %s%s%s%s", arg, (void *)bev,
events & BEV_EVENT_CONNECTED ? "connected" : "",
events & BEV_EVENT_ERROR ? "error" : "",
events & BEV_EVENT_TIMEOUT ? "timeout" : "",
events & BEV_EVENT_EOF ? "eof" : "");
if (c->hooks && c->hooks->on_event) {
(c->hooks->on_event)(c, events, c->hooks->on_event_arg);
}
if ((events & BEV_EVENT_CONNECTED)) {
log_debug("CONNECTED");
if (evhtp_likely(c->type == evhtp_type_client)) {
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_CONNECTED);
bufferevent_setcb(bev,
htp__connection_readcb_,
htp__connection_writecb_,
htp__connection_eventcb_, c);
}
return;
}
#ifndef EVHTP_DISABLE_SSL
if (c->ssl && !(events & BEV_EVENT_EOF)) {
#ifdef EVHTP_DEBUG
unsigned long sslerr;
while ((sslerr = bufferevent_get_openssl_error(bev))) {
log_error("SSL ERROR %lu:%i:%s:%i:%s:%i:%s",
sslerr,
ERR_GET_REASON(sslerr),
ERR_reason_error_string(sslerr),
ERR_GET_LIB(sslerr),
ERR_lib_error_string(sslerr),
ERR_GET_FUNC(sslerr),
ERR_func_error_string(sslerr));
}
#endif
/* XXX need to do better error handling for SSL specific errors */
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR);
if (c->request) {
HTP_FLAG_ON(c->request, EVHTP_REQ_FLAG_ERROR);
}
}
#endif
if (events == (BEV_EVENT_EOF | BEV_EVENT_READING)) {
log_debug("EOF | READING");
if (errno == EAGAIN) {
/* libevent will sometimes recv again when it's not actually ready,
* this results in a 0 return value, and errno will be set to EAGAIN
* (try again). This does not mean there is a hard socket error, but
* simply needs to be read again.
*
* but libevent will disable the read side of the bufferevent
* anyway, so we must re-enable it.
*/
log_debug("errno EAGAIN");
if (HTP_IS_READING(bev) == false) {
bufferevent_enable(bev, EV_READ);
}
errno = 0;
return;
}
}
/* set the error mask */
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR);
/* unset connected flag */
HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_CONNECTED);
htp__hook_connection_error_(c, events);
if (c->flags & EVHTP_CONN_FLAG_PAUSED) {
/* we are currently paused, so we don't want to free just yet, let's
* wait till the next loop.
*/
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_FREE_CONN);
} else {
evhtp_safe_free(c, evhtp_connection_free);
}
} /* htp__connection_eventcb_ */
static void
htp__connection_resumecb_(int fd, short events, void * arg)
{
evhtp_connection_t * c = arg;
log_debug("resumecb");
/* unset the pause flag */
HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_PAUSED);
if (c->request) {
log_debug("cr status = OK %d", c->cr_status);
c->cr_status = EVHTP_RES_OK;
}
if (c->flags & EVHTP_CONN_FLAG_FREE_CONN) {
log_debug("flags == FREE_CONN");
evhtp_safe_free(c, evhtp_connection_free);
return;
}
/* XXX this is a hack to show a potential fix for issues/86, the main indea
* is that you call resume AFTER you have sent the reply (not BEFORE).
*
* When it has been decided this is a proper fix, the pause bit should be
* changed to a state-type flag.
*/
if (HTP_LEN_OUTPUT(c->bev)) {
log_debug("SET WAITING");
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_WAITING);
if (HTP_IS_WRITING(c->bev) == false) {
log_debug("ENABLING EV_WRITE");
bufferevent_enable(c->bev, EV_WRITE);
}
} else {
log_debug("SET READING");
if (HTP_IS_READING(c->bev) == false) {
log_debug("ENABLING EV_READ");
bufferevent_enable(c->bev, EV_READ | EV_WRITE);
}
if (HTP_LEN_INPUT(c->bev)) {
log_debug("calling readcb directly");
htp__connection_readcb_(c->bev, c);
}
}
} /* htp__connection_resumecb_ */
static int
htp__run_pre_accept_(evhtp_t * htp, evhtp_connection_t * conn)
{
void * args;
evhtp_res res;
if (evhtp_likely(htp->defaults.pre_accept == NULL)) {
return 0;
}
args = htp->defaults.pre_accept_cbarg;
res = htp->defaults.pre_accept(conn, args);
if (res != EVHTP_RES_OK) {
return -1;
}
return 0;
}
static int
htp__connection_accept_(struct event_base * evbase, evhtp_connection_t * connection)
{
struct timeval * c_recv_timeo;
struct timeval * c_send_timeo;
if (htp__run_pre_accept_(connection->htp, connection) < 0) {
evutil_closesocket(connection->sock);
return -1;
}
#ifndef EVHTP_DISABLE_SSL
if (connection->htp->ssl_ctx != NULL) {
connection->ssl = SSL_new(connection->htp->ssl_ctx);
connection->bev = bufferevent_openssl_socket_new(evbase,
connection->sock,
connection->ssl,
BUFFEREVENT_SSL_ACCEPTING,
connection->htp->bev_flags);
SSL_set_app_data(connection->ssl, connection);
goto end;
}
#endif
connection->bev = bufferevent_socket_new(evbase,
connection->sock,
connection->htp->bev_flags);
log_debug("enter sock=%d\n", connection->sock);
#ifndef EVHTP_DISABLE_SSL
end:
#endif
if (connection->recv_timeo.tv_sec || connection->recv_timeo.tv_usec) {
c_recv_timeo = &connection->recv_timeo;
} else if (connection->htp->recv_timeo.tv_sec ||
connection->htp->recv_timeo.tv_usec) {
c_recv_timeo = &connection->htp->recv_timeo;
} else {
c_recv_timeo = NULL;
}
if (connection->send_timeo.tv_sec || connection->send_timeo.tv_usec) {
c_send_timeo = &connection->send_timeo;
} else if (connection->htp->send_timeo.tv_sec ||
connection->htp->send_timeo.tv_usec) {
c_send_timeo = &connection->htp->send_timeo;
} else {
c_send_timeo = NULL;
}
evhtp_connection_set_timeouts(connection, c_recv_timeo, c_send_timeo);
connection->resume_ev = event_new(evbase, -1, EV_READ | EV_PERSIST,
htp__connection_resumecb_, connection);
event_add(connection->resume_ev, NULL);
bufferevent_setcb(connection->bev,
htp__connection_readcb_,
htp__connection_writecb_,
htp__connection_eventcb_, connection);
bufferevent_enable(connection->bev, EV_READ);
return 0;
} /* htp__connection_accept_ */
static void
htp__default_request_cb_(evhtp_request_t * request, void * arg)
{
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Content-Length", "0", 0, 0));
evhtp_send_reply(request, EVHTP_RES_NOTFOUND);
}
static evhtp_connection_t *
htp__connection_new_(evhtp_t * htp, evutil_socket_t sock, evhtp_type type)
{
evhtp_connection_t * connection;
htp_type ptype;
switch (type) {
case evhtp_type_client:
ptype = htp_type_response;
break;
case evhtp_type_server:
ptype = htp_type_request;
break;
default:
return NULL;
}
connection = htp__calloc_(sizeof(*connection), 1);
if (evhtp_unlikely(connection == NULL)) {
return NULL;
}
connection->scratch_buf = evbuffer_new();
if (evhtp_unlikely(connection->scratch_buf == NULL)) {
evhtp_safe_free(connection->scratch_buf, htp__free_);
return NULL;
}
if (htp != NULL) {
connection->max_body_size = htp->max_body_size;
}
connection->flags = EVHTP_CONN_FLAG_OWNER;
connection->sock = sock;
connection->htp = htp;
connection->type = type;
connection->parser = htparser_new();
if (evhtp_unlikely(connection->parser == NULL)) {
evhtp_safe_free(connection, evhtp_connection_free);
return NULL;
}
htparser_init(connection->parser, ptype);
htparser_set_userdata(connection->parser, connection);
return connection;
} /* htp__connection_new_ */
#ifdef LIBEVENT_HAS_SHUTDOWN
#ifndef EVHTP_DISABLE_SSL
static void
htp__shutdown_eventcb_(struct bufferevent * bev, short events, void * arg)
{
}
#endif
#endif
static int
htp__run_post_accept_(evhtp_t * htp, evhtp_connection_t * connection)
{
void * args;
evhtp_res res;
if (evhtp_likely(htp->defaults.post_accept == NULL)) {
return 0;
}
args = htp->defaults.post_accept_cbarg;
res = htp->defaults.post_accept(connection, args);
if (res != EVHTP_RES_OK) {
return -1;
}
return 0;
}
#ifndef EVHTP_DISABLE_EVTHR
static void
htp__run_in_thread_(evthr_t * thr, void * arg, void * shared)
{
evhtp_t * htp = shared;
evhtp_connection_t * connection = arg;
connection->evbase = evthr_get_base(thr);
connection->thread = thr;
if (htp__connection_accept_(connection->evbase, connection) < 0) {
evhtp_safe_free(connection, evhtp_connection_free);
return;
}
if (htp__run_post_accept_(htp, connection) < 0) {
evhtp_safe_free(connection, evhtp_connection_free);
return;
}
}
#endif
static void
htp__accept_cb_(struct evconnlistener * serv, int fd, struct sockaddr * s, int sl, void * arg)
{
evhtp_t * htp = arg;
evhtp_connection_t * connection;
evhtp_assert(htp && serv && serv && s);
connection = htp__connection_new_(htp, fd, evhtp_type_server);
if (evhtp_unlikely(connection == NULL)) {
return;
}
log_debug("fd = %d, conn = %p", fd, connection);
connection->saddr = htp__malloc_(sl);
if (evhtp_unlikely(connection->saddr == NULL)) {
/* should probably start doing error callbacks */
evhtp_safe_free(connection, evhtp_connection_free);
return;
}
memcpy(connection->saddr, s, sl);
#ifndef EVHTP_DISABLE_EVTHR
if (htp->thr_pool != NULL) {
if (evthr_pool_defer(htp->thr_pool,
htp__run_in_thread_, connection) != EVTHR_RES_OK) {
evutil_closesocket(connection->sock);
evhtp_safe_free(connection, evhtp_connection_free);
return;
}
return;
}
#endif
connection->evbase = htp->evbase;
if (htp__connection_accept_(htp->evbase, connection) == -1) {
evhtp_safe_free(connection, evhtp_connection_free);
return;
}
if (htp__run_post_accept_(htp, connection) == -1) {
evhtp_safe_free(connection, evhtp_connection_free);
return;
}
} /* htp__accept_cb_ */
#ifndef EVHTP_DISABLE_SSL
#ifndef EVHTP_DISABLE_EVTHR
#ifndef WIN32
#define _HTP_tid (unsigned long)pthread_self()
#else
#define _HTP_tid pthread_self().p
#endif
#if OPENSSL_VERSION_NUMBER < 0x10000000L
static unsigned long
htp__ssl_get_thread_id_(void)
{
return _HTP_tid;
}
#else
static void
htp__ssl_get_thread_id_(CRYPTO_THREADID * id)
{
CRYPTO_THREADID_set_numeric(id, _HTP_tid);
}
#endif
static void
htp__ssl_thread_lock_(int mode, int type, const char * file, int line)
{
if (type < ssl_num_locks) {
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&(ssl_locks[type]));
} else {
pthread_mutex_unlock(&(ssl_locks[type]));
}
}
}
#endif
static void
htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess)
{
evhtp_t * htp;
evhtp_ssl_cfg_t * cfg;
evhtp_ssl_data_t * sid;
unsigned int slen;
htp = (evhtp_t *)SSL_CTX_get_app_data(ctx);
cfg = htp->ssl_cfg;
sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen);
if (cfg->scache_del) {
(cfg->scache_del)(htp, sid, slen);
}
}
static int
htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess)
{
evhtp_connection_t * connection;
evhtp_ssl_cfg_t * cfg;
evhtp_ssl_data_t * sid;
unsigned int slen;
connection = (evhtp_connection_t *)SSL_get_app_data(ssl);
if (connection->htp == NULL) {
return 0; /* We cannot get the ssl_cfg */
}
cfg = connection->htp->ssl_cfg;
sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen);
SSL_set_timeout(sess, cfg->scache_timeout);
if (cfg->scache_add) {
return (cfg->scache_add)(connection, sid, slen, sess);
}
return 0;
}
static evhtp_ssl_sess_t *
htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, int * copy)
{
evhtp_connection_t * connection;
evhtp_ssl_cfg_t * cfg;
evhtp_ssl_sess_t * sess;
connection = (evhtp_connection_t * )SSL_get_app_data(ssl);
if (connection->htp == NULL) {
return NULL; /* We have no way of getting ssl_cfg */
}
cfg = connection->htp->ssl_cfg;
sess = NULL;
if (cfg->scache_get) {
sess = (cfg->scache_get)(connection, sid, sid_len);
}
*copy = 0;
return sess;
}
static int
htp__ssl_servername_(evhtp_ssl_t * ssl, int * unused, void * arg)
{
const char * sname;
evhtp_connection_t * connection;
evhtp_t * evhtp;
evhtp_t * evhtp_vhost;
if (evhtp_unlikely(ssl == NULL)) {
return SSL_TLSEXT_ERR_NOACK;
}
if (!(sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
return SSL_TLSEXT_ERR_NOACK;
}
if (!(connection = SSL_get_app_data(ssl))) {
return SSL_TLSEXT_ERR_NOACK;
}
if (!(evhtp = connection->htp)) {
return SSL_TLSEXT_ERR_NOACK;
}
if ((evhtp_vhost = htp__request_find_vhost_(evhtp, sname))) {
SSL_CTX * ctx = SSL_get_SSL_CTX(ssl);
connection->htp = evhtp_vhost;
HTP_FLAG_ON(connection, EVHTP_CONN_FLAG_VHOST_VIA_SNI);
SSL_set_SSL_CTX(ssl, evhtp_vhost->ssl_ctx);
SSL_set_options(ssl, SSL_CTX_get_options(ctx));
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
(SSL_num_renegotiations(ssl) == 0)) {
SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx),
SSL_CTX_get_verify_callback(ctx));
}
return SSL_TLSEXT_ERR_OK;
}
return SSL_TLSEXT_ERR_NOACK;
} /* htp__ssl_servername_ */
#endif
/*
* PUBLIC FUNCTIONS
*/
htp_method
evhtp_request_get_method(evhtp_request_t * r)
{
evhtp_assert(r != NULL);
evhtp_assert(r->conn != NULL);
evhtp_assert(r->conn->parser != NULL);
return htparser_get_method(r->conn->parser);
}
void
evhtp_connection_pause(evhtp_connection_t * c)
{
evhtp_assert(c != NULL);
if (c->flags & EVHTP_CONN_FLAG_PAUSED) {
log_debug("connection is already paused");
return;
}
log_debug("setting PAUSED flag");
HTP_FLAG_ON(c, EVHTP_CONN_FLAG_PAUSED);
if (HTP_IS_READING(c->bev) == true) {
log_debug("disabling EV_READ");
bufferevent_disable(c->bev, EV_READ);
}
return;
}
void
evhtp_connection_resume(evhtp_connection_t * c)
{
evhtp_assert(c != NULL);
if (!(c->flags & EVHTP_CONN_FLAG_PAUSED)) {
log_error("ODDITY, resuming when not paused?!?");
return;
}
HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_PAUSED);
log_debug("resume");
event_active(c->resume_ev, EV_WRITE, 1);
return;
}
void
evhtp_request_pause(evhtp_request_t * request)
{
evhtp_assert(request != NULL);
request->status = EVHTP_RES_PAUSE;
evhtp_connection_pause(request->conn);
}
void
evhtp_request_resume(evhtp_request_t * request)
{
evhtp_assert(request != NULL);
evhtp_connection_resume(request->conn);
}
evhtp_header_t *
evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char key_alloc)
{
evhtp_header_t * header;
if (!(header = evhtp_header_new(key, NULL, key_alloc, 0))) {
return NULL;
}
evhtp_headers_add_header(headers, header);
return header;
}
evhtp_header_t *
evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char val_alloc)
{
evhtp_header_t * header;
if (evhtp_unlikely(headers == NULL || val == NULL)) {
return NULL;
}
if (!(header = TAILQ_LAST(headers, evhtp_kvs))) {
return NULL;
}
if (header->val != NULL) {
return NULL;
}
header->vlen = strlen(val);
if (val_alloc == 1) {
header->val = htp__malloc_(header->vlen + 1);
evhtp_alloc_assert(header->val);
header->val[header->vlen] = '\0';
memcpy(header->val, val, header->vlen);
} else {
header->val = (char *)val;
}
header->v_heaped = val_alloc;
return header;
}
evhtp_kvs_t *
evhtp_kvs_new(void)
{
evhtp_kvs_t * kvs;
kvs = htp__malloc_(sizeof(*kvs));
if (evhtp_unlikely(kvs == NULL)) {
return NULL;
}
TAILQ_INIT(kvs);
return kvs;
}
evhtp_kv_t *
evhtp_kv_new(const char * key, const char * val,
char key_alloc, char val_alloc)
{
evhtp_kv_t * kv;
kv = htp__malloc_(sizeof(*kv));
if (evhtp_unlikely(kv == NULL)) {
return NULL;
}
kv->k_heaped = key_alloc;
kv->v_heaped = val_alloc;
kv->klen = 0;
kv->vlen = 0;
kv->key = NULL;
kv->val = NULL;
if (key != NULL) {
kv->klen = strlen(key);
if (key_alloc == 1) {
char * s;
if (!(s = htp__malloc_(kv->klen + 1))) {
evhtp_safe_free(kv, htp__free_);
return NULL;
}
memcpy(s, key, kv->klen);
s[kv->klen] = '\0';
kv->key = s;
} else {
kv->key = (char *)key;
}
}
if (val != NULL) {
kv->vlen = strlen(val);
if (val_alloc == 1) {
char * s = htp__malloc_(kv->vlen + 1);
if (evhtp_unlikely(s == NULL)) {
evhtp_safe_free(kv, evhtp_kv_free);
return NULL;
}
s[kv->vlen] = '\0';
memcpy(s, val, kv->vlen);
kv->val = s;
} else {
kv->val = (char *)val;
}
}
return kv;
} /* evhtp_kv_new */
void
evhtp_kv_free(evhtp_kv_t * kv)
{
if (evhtp_unlikely(kv == NULL)) {
return;
}
if (kv->k_heaped == 1) {
evhtp_safe_free(kv->key, htp__free_);
}
if (kv->v_heaped == 1) {
evhtp_safe_free(kv->val, htp__free_);
}
evhtp_safe_free(kv, htp__free_);
}
void
evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv)
{
if (evhtp_unlikely(kvs == NULL || kv == NULL)) {
return;
}
TAILQ_REMOVE(kvs, kv, next);
evhtp_safe_free(kv, evhtp_kv_free);
}
void
evhtp_kvs_free(evhtp_kvs_t * kvs)
{
evhtp_kv_t * kv;
evhtp_kv_t * save;
if (evhtp_unlikely(kvs == NULL)) {
return;
}
kv = NULL;
save = NULL;
for (kv = TAILQ_FIRST(kvs); kv != NULL; kv = save) {
save = TAILQ_NEXT(kv, next);
TAILQ_REMOVE(kvs, kv, next);
evhtp_safe_free(kv, evhtp_kv_free);
}
evhtp_safe_free(kvs, htp__free_);
}
int
evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg)
{
evhtp_kv_t * kv;
if (kvs == NULL || cb == NULL) {
return -1;
}
TAILQ_FOREACH(kv, kvs, next) {
int res;
if ((res = cb(kv, arg))) {
return res;
}
}
return 0;
}
const char *
evhtp_kv_find(evhtp_kvs_t * kvs, const char * key)
{
evhtp_kv_t * kv;
if (evhtp_unlikely(kvs == NULL || key == NULL)) {
return NULL;
}
TAILQ_FOREACH(kv, kvs, next) {
if (strcasecmp(kv->key, key) == 0) {
return kv->val;
}
}
return NULL;
}
const char *
evhtp_header_find(evhtp_headers_t * headers, const char * key)
{
return evhtp_kv_find(headers, key);
}
void
evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header)
{
return evhtp_kvs_add_kv(headers, header);
}
evhtp_header_t *
evhtp_header_new(const char * key, const char * val, char kalloc, char valloc)
{
return evhtp_kv_new(key, val, kalloc, valloc);
}
evhtp_kv_t *
evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key)
{
evhtp_kv_t * kv;
if (evhtp_unlikely(kvs == NULL || key == NULL)) {
return NULL;
}
TAILQ_FOREACH(kv, kvs, next) {
if (strcasecmp(kv->key, key) == 0) {
return kv;
}
}
return NULL;
}
void
evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv)
{
if (evhtp_unlikely(kvs == NULL || kv == NULL)) {
return;
}
TAILQ_INSERT_TAIL(kvs, kv, next);
}
void
evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src)
{
evhtp_kv_t * kv;
if (dst == NULL || src == NULL) {
return;
}
TAILQ_FOREACH(kv, src, next) {
evhtp_kvs_add_kv(dst, evhtp_kv_new(kv->key,
kv->val,
kv->k_heaped,
kv->v_heaped));
}
}
typedef enum {
s_query_start = 0,
s_query_separator,
s_query_key,
s_query_val,
s_query_key_hex_1,
s_query_key_hex_2,
s_query_val_hex_1,
s_query_val_hex_2,
s_query_done
} query_parser_state;
static inline int
evhtp_is_hex_query_char(unsigned char ch)
{
switch (ch) {
case 'a': case 'A':
case 'b': case 'B':
case 'c': case 'C':
case 'd': case 'D':
case 'e': case 'E':
case 'f': case 'F':
case '0': case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
case '8': case '9':
return 1;
default:
return 0;
} /* switch */
}
enum unscape_state {
unscape_state_start = 0,
unscape_state_hex1,
unscape_state_hex2
};
int
evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len)
{
unsigned char * optr;
unsigned char * sptr;
unsigned char d;
unsigned char ch;
unsigned char c;
size_t i;
enum unscape_state state;
state = unscape_state_start;
optr = *out;
sptr = str;
d = 0;
for (i = 0; i < str_len; i++) {
ch = *sptr++;
switch (state) {
case unscape_state_start:
if (ch == '%') {
state = unscape_state_hex1;
break;
}
*optr++ = ch;
break;
case unscape_state_hex1:
if (ch >= '0' && ch <= '9') {
d = (unsigned char)(ch - '0');
state = unscape_state_hex2;
break;
}
c = (unsigned char)(ch | 0x20);
if (c >= 'a' && c <= 'f') {
d = (unsigned char)(c - 'a' + 10);
state = unscape_state_hex2;
break;
}
state = unscape_state_start;
*optr++ = ch;
break;
case unscape_state_hex2:
state = unscape_state_start;
if (ch >= '0' && ch <= '9') {
ch = (unsigned char)((d << 4) + ch - '0');
*optr++ = ch;
break;
}
c = (unsigned char)(ch | 0x20);
if (c >= 'a' && c <= 'f') {
ch = (unsigned char)((d << 4) + c - 'a' + 10);
*optr++ = ch;
break;
}
break;
} /* switch */
}
*out = optr;
return 0;
} /* evhtp_unescape_string */
evhtp_query_t *
evhtp_parse_query_wflags(const char * query, const size_t len, const int flags)
{
evhtp_query_t * query_args;
query_parser_state state;
size_t key_idx;
size_t val_idx;
unsigned char ch;
size_t i;
if (len > (SIZE_MAX - (len + 2))) {
return NULL;
}
query_args = evhtp_query_new();
state = s_query_start;
key_idx = 0;
val_idx = 0;
#ifdef EVHTP_HAS_C99
char key_buf[len + 1];
char val_buf[len + 1];
#else
char * key_buf;
char * val_buf;
key_buf = htp__malloc_(len + 1);
if (evhtp_unlikely(key_buf == NULL)) {
evhtp_safe_free(query_args, evhtp_query_free);
return NULL;
}
val_buf = htp__malloc_(len + 1);
if (evhtp_unlikely(val_buf == NULL)) {
evhtp_safe_free(query_args, evhtp_query_free);
return NULL;
}
#endif
for (i = 0; i < len; i++) {
ch = query[i];
if (key_idx >= len || val_idx >= len) {
goto error;
}
switch (state) {
case s_query_start:
key_idx = 0;
val_idx = 0;
key_buf[0] = '\0';
val_buf[0] = '\0';
state = s_query_key;
/* Fall through. */
case s_query_key:
switch (ch) {
case '=':
state = s_query_val;
break;
case '%':
key_buf[key_idx++] = ch;
key_buf[key_idx] = '\0';
if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) {
state = s_query_key_hex_1;
}
break;
case ';':
if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) {
key_buf[key_idx++] = ch;
key_buf[key_idx] = '\0';
break;
}
/* otherwise we fallthrough */
case '&':
/* in this state, we have a NULL value */
if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) {
goto error;
}
/* insert the key with value of NULL and set the
* state back to parsing s_query_key.
*/
evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1));
key_idx = 0;
val_idx = 0;
key_buf[0] = '\0';
val_buf[0] = '\0';
state = s_query_key;
break;
default:
key_buf[key_idx++] = ch;
key_buf[key_idx] = '\0';
break;
} /* switch */
break;
case s_query_key_hex_1:
if (!evhtp_is_hex_query_char(ch)) {
/* not hex, so we treat as a normal key */
if ((key_idx + 2) >= len) {
/* we need to insert \%<ch>, but not enough space */
goto error;
}
key_buf[key_idx - 1] = '%';
key_buf[key_idx++] = ch;
key_buf[key_idx] = '\0';
state = s_query_key;
break;
}
key_buf[key_idx++] = ch;
key_buf[key_idx] = '\0';
state = s_query_key_hex_2;
break;
case s_query_key_hex_2:
if (!evhtp_is_hex_query_char(ch)) {
goto error;
}
key_buf[key_idx++] = ch;
key_buf[key_idx] = '\0';
state = s_query_key;
break;
case s_query_val:
switch (ch) {
case ';':
if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) {
val_buf[val_idx++] = ch;
val_buf[val_idx] = '\0';
break;
}
case '&':
evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1));
key_idx = 0;
val_idx = 0;
key_buf[0] = '\0';
val_buf[0] = '\0';
state = s_query_key;
break;
case '%':
val_buf[val_idx++] = ch;
val_buf[val_idx] = '\0';
if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) {
state = s_query_val_hex_1;
}
break;
default:
val_buf[val_idx++] = ch;
val_buf[val_idx] = '\0';
break;
} /* switch */
break;
case s_query_val_hex_1:
if (!evhtp_is_hex_query_char(ch)) {
/* not really a hex val */
if ((val_idx + 2) >= len) {
/* we need to insert \%<ch>, but not enough space */
goto error;
}
if (val_idx == 0) {
goto error;
}
val_buf[val_idx - 1] = '%';
val_buf[val_idx++] = ch;
val_buf[val_idx] = '\0';
state = s_query_val;
break;
}
val_buf[val_idx++] = ch;
val_buf[val_idx] = '\0';
state = s_query_val_hex_2;
break;
case s_query_val_hex_2:
if (!evhtp_is_hex_query_char(ch)) {
goto error;
}
val_buf[val_idx++] = ch;
val_buf[val_idx] = '\0';
state = s_query_val;
break;
default:
/* bad state */
goto error;
} /* switch */
}
if (key_idx) {
do {
if (val_idx) {
evhtp_kvs_add_kv(query_args,
evhtp_kv_new(key_buf, val_buf, 1, 1));
break;
}
if (state >= s_query_val) {
if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS)) {
goto error;
}
evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, "", 1, 1));
break;
}
if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) {
goto error;
}
evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 0));
} while (0);
}
#ifndef EVHTP_HAS_C99
evhtp_safe_free(key_buf, htp__free_);
evhtp_safe_free(val_buf, htp__free_);
#endif
return query_args;
error:
#ifndef EVHTP_HAS_C99
evhtp_safe_free(key_buf, htp__free_);
evhtp_safe_free(val_buf, htp__free_);
#endif
evhtp_safe_free(query_args, evhtp_query_free);
return NULL;
} /* evhtp_parse_query */
evhtp_query_t *
evhtp_parse_query(const char * query, size_t len)
{
return evhtp_parse_query_wflags(query, len,
EVHTP_PARSE_QUERY_FLAG_STRICT);
}
void
evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code)
{
evhtp_connection_t * c;
struct evbuffer * reply_buf;
c = evhtp_request_get_connection(request);
if (!(reply_buf = htp__create_reply_(request, code))) {
evhtp_safe_free(c, evhtp_connection_free);
return;
}
bufferevent_write_buffer(c->bev, reply_buf);
evbuffer_drain(reply_buf, -1);
}
void
evhtp_send_reply_body(evhtp_request_t * request, struct evbuffer * buf)
{
evhtp_connection_t * c;
c = request->conn;
bufferevent_write_buffer(c->bev, buf);
}
void
evhtp_send_reply_end(evhtp_request_t * request)
{
HTP_FLAG_ON(request, EVHTP_REQ_FLAG_FINISHED);
}
void
evhtp_send_reply(evhtp_request_t * request, evhtp_res code)
{
evhtp_connection_t * c;
struct evbuffer * reply_buf;
struct bufferevent * bev;
c = request->conn;
log_debug("set finished flag");
HTP_FLAG_ON(request, EVHTP_REQ_FLAG_FINISHED);
if (!(reply_buf = htp__create_reply_(request, code))) {
evhtp_safe_free(request->conn, evhtp_connection_free);
return;
}
bev = c->bev;
log_debug("writing to bev %p", bev);
bufferevent_write_buffer(bev, reply_buf);
evbuffer_drain(reply_buf, -1);
}
int
evhtp_response_needs_body(const evhtp_res code, const htp_method method)
{
return code != EVHTP_RES_NOCONTENT &&
code != EVHTP_RES_NOTMOD &&
(code < 100 || code >= 200) &&
method != htp_method_HEAD;
}
void
evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code)
{
evhtp_header_t * content_len;
if (evhtp_response_needs_body(code, request->method)) {
content_len = evhtp_headers_find_header(request->headers_out, "Content-Length");
switch (request->proto) {
case EVHTP_PROTO_11:
/*
* prefer HTTP/1.1 chunked encoding to closing the connection;
* note RFC 2616 section 4.4 forbids it with Content-Length:
* and it's not necessary then anyway.
*/
evhtp_kv_rm_and_free(request->headers_out, content_len);
HTP_FLAG_ON(request, EVHTP_REQ_FLAG_CHUNKED);
break;
case EVHTP_PROTO_10:
/*
* HTTP/1.0 can be chunked as long as the Content-Length header
* is set to 0
*/
evhtp_kv_rm_and_free(request->headers_out, content_len);
HTP_FLAG_ON(request, EVHTP_REQ_FLAG_CHUNKED);
break;
default:
HTP_FLAG_OFF(request, EVHTP_REQ_FLAG_CHUNKED);
break;
} /* switch */
} else {
HTP_FLAG_OFF(request, EVHTP_REQ_FLAG_CHUNKED);
}
if (request->flags & EVHTP_REQ_FLAG_CHUNKED) {
evhtp_headers_add_header(request->headers_out,
evhtp_header_new("Transfer-Encoding", "chunked", 0, 0));
/*
* if data already exists on the output buffer, we automagically convert
* it to the first chunk.
*/
if (evbuffer_get_length(request->buffer_out) > 0) {
char lstr[128];
int sres;
sres = snprintf(lstr, sizeof(lstr), "%x\r\n",
(unsigned)evbuffer_get_length(request->buffer_out));
if (sres >= sizeof(lstr) || sres < 0) {
/* overflow condition, shouldn't ever get here, but lets
* terminate the connection asap */
goto end;
}
evbuffer_prepend(request->buffer_out, lstr, strlen(lstr));
evbuffer_add(request->buffer_out, "\r\n", 2);
}
}
end:
evhtp_send_reply_start(request, code);
} /* evhtp_send_reply_chunk_start */
void
evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf)
{
struct evbuffer * output;
if (evbuffer_get_length(buf) == 0) {
return;
}
output = bufferevent_get_output(request->conn->bev);
if (request->flags & EVHTP_REQ_FLAG_CHUNKED) {
evbuffer_add_printf(output, "%x\r\n",
(unsigned)evbuffer_get_length(buf));
}
evhtp_send_reply_body(request, buf);
if (request->flags & EVHTP_REQ_FLAG_CHUNKED) {
evbuffer_add(output, "\r\n", 2);
}
bufferevent_flush(request->conn->bev, EV_WRITE, BEV_FLUSH);
}
void
evhtp_send_reply_chunk_end(evhtp_request_t * request)
{
if (request->flags & EVHTP_REQ_FLAG_CHUNKED) {
evbuffer_add(bufferevent_get_output(evhtp_request_get_bev(request)),
"0\r\n\r\n", 5);
}
evhtp_send_reply_end(request);
}
void
evhtp_unbind_socket(evhtp_t * htp)
{
if (htp == NULL || htp->server == NULL) {
return;
}
evhtp_safe_free(htp->server, evconnlistener_free);
}
static int
htp__serv_setsockopts_(evhtp_t * htp, evutil_socket_t sock)
{
int on = 1;
if (htp == NULL || sock == -1) {
log_error("htp = %p && sock = %d", htp, sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)) == -1) {
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) == -1) {
return -1;
}
#if defined(SO_REUSEPORT)
if (htp->flags & EVHTP_FLAG_ENABLE_REUSEPORT) {
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) == -1) {
if (errno != EOPNOTSUPP) {
log_error("SO_REUSEPORT error");
return -1;
}
log_warn("SO_REUSEPORT NOT SUPPORTED");
}
}
#endif
#if defined(TCP_NODELAY)
if (htp->flags & EVHTP_FLAG_ENABLE_NODELAY) {
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) == -1) {
if (errno != EOPNOTSUPP) {
log_error("TCP_NODELAY error");
return -1;
}
log_warn("NODELAY NOT SUPPORTED");
}
}
#endif
#if defined(TCP_DEFER_ACCEPT)
if (htp->flags & EVHTP_FLAG_ENABLE_DEFER_ACCEPT) {
if (setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (void *)&on, sizeof(on)) == -1) {
if (errno != EOPNOTSUPP) {
log_error("DEFER_ACCEPT error");
return -1;
}
log_warn("DEFER_ACCEPT NOT SUPPORTED");
}
}
#endif
return 0;
} /* htp__setsockopts_ */
int
evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog)
{
int err = 1;
if (htp == NULL || sock == -1) {
log_error("htp = %p && sock = %d", htp, sock);
return -1;
}
do {
htp->server = evconnlistener_new(htp->evbase,
htp__accept_cb_,
htp,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
backlog,
sock);
if (htp->server == NULL) {
break;
}
#ifndef EVHTP_DISABLE_SSL
if (htp->ssl_ctx != NULL) {
/* if ssl is enabled and we have virtual hosts, set our servername
* callback. We do this here because we want to make sure that this gets
* set after all potential virtualhosts have been set, not just after
* ssl_init.
*/
if (TAILQ_FIRST(&htp->vhosts) != NULL) {
SSL_CTX_set_tlsext_servername_callback(htp->ssl_ctx,
htp__ssl_servername_);
}
}
#endif
err = 0;
} while (0);
if (err == 1) {
if (htp->server != NULL) {
evhtp_safe_free(htp->server, evconnlistener_free);
}
return -1;
}
return 0;
} /* evhtp_accept_socket */
int
evhtp_bind_sockaddr(evhtp_t * htp,
struct sockaddr * sa,
size_t sin_len,
int backlog)
{
evutil_socket_t fd = -1;
int on = 1;
int error = 1;
if (htp == NULL) {
log_error("NULL param passed");
return -1;
}
/* XXX: API's should not set signals */
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
do {
if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) {
log_error("couldn't create socket");
return -1;
}
evutil_make_socket_closeonexec(fd);
evutil_make_socket_nonblocking(fd);
if (htp__serv_setsockopts_(htp, fd) == -1) {
break;
}
if (sa->sa_family == AF_INET6) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
break;
}
}
if (bind(fd, sa, sin_len) == -1) {
break;
}
error = 0;
} while (0);
if (error == 1) {
if (fd != -1) {
evutil_closesocket(fd);
}
return -1;
}
if (evhtp_accept_socket(htp, fd, backlog) == -1) {
/* accept_socket() does not close the descriptor
* on error, but this function does.
*/
evutil_closesocket(fd);
return -1;
}
return 0;
} /* evhtp_bind_sockaddr */
int
evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog)
{
#ifndef NO_SYS_UN
struct sockaddr_un sockun = { 0 };
#endif
struct sockaddr * sa;
struct sockaddr_in6 sin6 = { 0 };
struct sockaddr_in sin = { 0 };
size_t sin_len;
if (!strncmp(baddr, "ipv6:", 5)) {
baddr += 5;
sin_len = sizeof(struct sockaddr_in6);
sin6.sin6_port = htons(port);
sin6.sin6_family = AF_INET6;
evutil_inet_pton(AF_INET6, baddr, &sin6.sin6_addr);
sa = (struct sockaddr *)&sin6;
} else if (!strncmp(baddr, "unix:", 5)) {
#ifndef NO_SYS_UN
baddr += 5;
if (strlen(baddr) >= sizeof(sockun.sun_path)) {
return -1;
}
sin_len = sizeof(struct sockaddr_un);
sockun.sun_family = AF_UNIX;
strncpy(sockun.sun_path, baddr, strlen(baddr));
sa = (struct sockaddr *)&sockun;
#else
return -1;
#endif
} else {
if (!strncmp(baddr, "ipv4:", 5)) {
baddr += 5;
}
sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr(baddr);
sa = (struct sockaddr *)&sin;
}
return evhtp_bind_sockaddr(htp, sa, sin_len, backlog);
} /* evhtp_bind_socket */
void
evhtp_callbacks_free(evhtp_callbacks_t * callbacks)
{
evhtp_callback_t * callback;
evhtp_callback_t * tmp;
if (callbacks == NULL) {
return;
}
TAILQ_FOREACH_SAFE(callback, callbacks, next, tmp) {
TAILQ_REMOVE(callbacks, callback, next);
evhtp_safe_free(callback, evhtp_callback_free);
}
evhtp_safe_free(callbacks, htp__free_);
}
evhtp_callback_t *
evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg)
{
evhtp_callback_t * hcb;
hcb = htp__calloc_(sizeof(*hcb), 1);
if (evhtp_unlikely(hcb == NULL)) {
evhtp_safe_free(hcb, htp__free_);
return NULL;
}
hcb->type = type;
hcb->cb = cb;
hcb->cbarg = arg;
hcb->len = strlen(path);
switch (type) {
case evhtp_callback_type_hash:
hcb->val.path = htp__strdup_(path);
if (evhtp_unlikely(hcb->val.path == NULL)) {
evhtp_safe_free(hcb, evhtp_callback_free);
return NULL;
}
break;
#ifndef EVHTP_DISABLE_REGEX
case evhtp_callback_type_regex:
hcb->val.regex = htp__malloc_(sizeof(regex_t));
if (evhtp_unlikely(hcb->val.regex == NULL)) {
evhtp_safe_free(hcb, evhtp_callback_free);
return NULL;
}
if (regcomp(hcb->val.regex, (char *)path, REG_EXTENDED) != 0) {
evhtp_safe_free(hcb->val.regex, htp__free_);
evhtp_safe_free(hcb, htp__free_);
return NULL;
}
break;
#endif
case evhtp_callback_type_glob:
hcb->val.glob = htp__strdup_(path);
if (evhtp_unlikely(hcb->val.glob == NULL)) {
evhtp_safe_free(hcb, evhtp_callback_free);
return NULL;
}
break;
default:
evhtp_safe_free(hcb, htp__free_);
return NULL;
} /* switch */
return hcb;
} /* evhtp_callback_new */
void
evhtp_callback_free(evhtp_callback_t * callback)
{
if (callback == NULL) {
return;
}
switch (callback->type) {
case evhtp_callback_type_hash:
evhtp_safe_free(callback->val.path, htp__free_);
break;
case evhtp_callback_type_glob:
evhtp_safe_free(callback->val.glob, htp__free_);
break;
#ifndef EVHTP_DISABLE_REGEX
case evhtp_callback_type_regex:
evhtp_safe_free(callback->val.regex, htp__free_);
break;
#endif
}
if (callback->hooks) {
evhtp_safe_free(callback->hooks, htp__free_);
}
evhtp_safe_free(callback, htp__free_);
return;
}
int
evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb)
{
TAILQ_INSERT_TAIL(cbs, cb, next);
return 0;
}
static int
htp__set_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg)
{
if (*hooks == NULL) {
if (!(*hooks = htp__calloc_(sizeof(evhtp_hooks_t), 1))) {
return -1;
}
}
switch (type) {
case evhtp_hook_on_headers_start:
(*hooks)->on_headers_start = (evhtp_hook_headers_start_cb)cb;
(*hooks)->on_headers_start_arg = arg;
break;
case evhtp_hook_on_header:
(*hooks)->on_header = (evhtp_hook_header_cb)cb;
(*hooks)->on_header_arg = arg;
break;
case evhtp_hook_on_headers:
(*hooks)->on_headers = (evhtp_hook_headers_cb)cb;
(*hooks)->on_headers_arg = arg;
break;
case evhtp_hook_on_path:
(*hooks)->on_path = (evhtp_hook_path_cb)cb;
(*hooks)->on_path_arg = arg;
break;
case evhtp_hook_on_read:
(*hooks)->on_read = (evhtp_hook_read_cb)cb;
(*hooks)->on_read_arg = arg;
break;
case evhtp_hook_on_request_fini:
(*hooks)->on_request_fini = (evhtp_hook_request_fini_cb)cb;
(*hooks)->on_request_fini_arg = arg;
break;
case evhtp_hook_on_connection_fini:
(*hooks)->on_connection_fini = (evhtp_hook_connection_fini_cb)cb;
(*hooks)->on_connection_fini_arg = arg;
break;
case evhtp_hook_on_conn_error:
(*hooks)->on_connection_error = (evhtp_hook_conn_err_cb)cb;
(*hooks)->on_connection_error_arg = arg;
break;
case evhtp_hook_on_error:
(*hooks)->on_error = (evhtp_hook_err_cb)cb;
(*hooks)->on_error_arg = arg;
break;
case evhtp_hook_on_new_chunk:
(*hooks)->on_new_chunk = (evhtp_hook_chunk_new_cb)cb;
(*hooks)->on_new_chunk_arg = arg;
break;
case evhtp_hook_on_chunk_complete:
(*hooks)->on_chunk_fini = (evhtp_hook_chunk_fini_cb)cb;
(*hooks)->on_chunk_fini_arg = arg;
break;
case evhtp_hook_on_chunks_complete:
(*hooks)->on_chunks_fini = (evhtp_hook_chunks_fini_cb)cb;
(*hooks)->on_chunks_fini_arg = arg;
break;
case evhtp_hook_on_hostname:
(*hooks)->on_hostname = (evhtp_hook_hostname_cb)cb;
(*hooks)->on_hostname_arg = arg;
break;
case evhtp_hook_on_write:
(*hooks)->on_write = (evhtp_hook_write_cb)cb;
(*hooks)->on_write_arg = arg;
break;
case evhtp_hook_on_event:
(*hooks)->on_event = (evhtp_hook_event_cb)cb;
(*hooks)->on_event_arg = arg;
break;
default:
return -1;
} /* switch */
return 0;
} /* htp__set_hook_ */
static int
htp__unset_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type)
{
return htp__set_hook_(hooks, type, NULL, NULL);
}
int
evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type)
{
return htp__unset_hook_(&callback->hooks, type);
}
int
evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type)
{
return htp__unset_hook_(&req->hooks, type);
}
int
evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type)
{
return htp__unset_hook_(&conn->hooks, type);
}
int
evhtp_callback_set_hook(evhtp_callback_t * callback, evhtp_hook_type type, evhtp_hook cb,
void * arg)
{
return htp__set_hook_(&callback->hooks, type, cb, arg);
}
int
evhtp_request_set_hook(evhtp_request_t * req, evhtp_hook_type type, evhtp_hook cb, void * arg)
{
return htp__set_hook_(&req->hooks, type, cb, arg);
}
int
evhtp_connection_set_hook(evhtp_connection_t * conn, evhtp_hook_type type, evhtp_hook cb,
void * arg)
{
return htp__set_hook_(&conn->hooks, type, cb, arg);
}
int
evhtp_unset_all_hooks(evhtp_hooks_t ** hooks)
{
int i;
struct {
enum evhtp_hook_type type;
} hooklist_[] = {
{ evhtp_hook_on_header },
{ evhtp_hook_on_headers },
{ evhtp_hook_on_path },
{ evhtp_hook_on_read },
{ evhtp_hook_on_request_fini },
{ evhtp_hook_on_connection_fini },
{ evhtp_hook_on_new_chunk },
{ evhtp_hook_on_chunk_complete },
{ evhtp_hook_on_chunks_complete },
{ evhtp_hook_on_headers_start },
{ evhtp_hook_on_error },
{ evhtp_hook_on_hostname },
{ evhtp_hook_on_write },
{ evhtp_hook_on_event },
{ evhtp_hook_on_conn_error },
{ evhtp_hook__max }
};
if (hooks == NULL) {
return -1;
}
for (i = 0; hooklist_[i].type != evhtp_hook__max; i++) {
if (htp__unset_hook_(hooks, hooklist_[i].type) == -1) {
return -1;
}
}
return 0;
}
evhtp_hooks_t *
evhtp_connection_get_hooks(evhtp_connection_t * c)
{
if (evhtp_unlikely(c == NULL)) {
return NULL;
}
return c->hooks;
}
evhtp_hooks_t *
evhtp_request_get_hooks(evhtp_request_t * r)
{
if (evhtp_unlikely(r == NULL)) {
return NULL;
}
return r->hooks;
}
evhtp_hooks_t *
evhtp_callback_get_hooks(evhtp_callback_t * cb)
{
return cb->hooks;
}
evhtp_callback_t *
evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg)
{
evhtp_callback_t * hcb;
htp__lock_(htp);
if (htp->callbacks == NULL) {
if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) {
htp__unlock_(htp);
return NULL;
}
TAILQ_INIT(htp->callbacks);
}
if (!(hcb = evhtp_callback_new(path, evhtp_callback_type_hash, cb, arg))) {
htp__unlock_(htp);
return NULL;
}
if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
evhtp_safe_free(hcb, evhtp_callback_free);
htp__unlock_(htp);
return NULL;
}
htp__unlock_(htp);
return hcb;
}
evhtp_callback_t *
evhtp_get_cb(evhtp_t * htp, const char * path)
{
evhtp_callback_t * callback;
evhtp_assert(htp != NULL);
if (evhtp_unlikely(htp->callbacks == NULL)) {
return NULL;
}
TAILQ_FOREACH(callback, htp->callbacks, next) {
if (strcmp(callback->val.path, path) == 0) {
return callback;
}
}
return NULL;
}
#ifndef EVHTP_DISABLE_EVTHR
static void
htp__thread_init_(evthr_t * thr, void * arg)
{
evhtp_t * htp = (evhtp_t *)arg;
if (htp->thread_init_cb) {
htp->thread_init_cb(htp, thr, htp->thread_cbarg);
}
}
static void
htp__thread_exit_(evthr_t * thr, void * arg)
{
evhtp_t * htp = (evhtp_t *)arg;
if (htp->thread_exit_cb) {
htp->thread_exit_cb(htp, thr, htp->thread_cbarg);
}
}
static int
htp__use_threads_(evhtp_t * htp,
evhtp_thread_init_cb init_cb,
evhtp_thread_exit_cb exit_cb,
int nthreads, void * arg)
{
if (htp == NULL) {
return -1;
}
htp->thread_cbarg = arg;
htp->thread_init_cb = init_cb;
htp->thread_exit_cb = exit_cb;
#ifndef EVHTP_DISABLE_SSL
evhtp_ssl_use_threads();
#endif
if (!(htp->thr_pool = evthr_pool_wexit_new(nthreads,
htp__thread_init_,
htp__thread_exit_, htp))) {
return -1;
}
evthr_pool_start(htp->thr_pool);
return 0;
}
int
evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb,
int nthreads, void * arg)
{
return htp__use_threads_(htp, init_cb, NULL, nthreads, arg);
}
int
evhtp_use_threads_wexit(evhtp_t * htp,
evhtp_thread_init_cb init_cb,
evhtp_thread_exit_cb exit_cb,
int nthreads, void * arg)
{
return htp__use_threads_(htp, init_cb, exit_cb, nthreads, arg);
}
#endif
#ifndef EVHTP_DISABLE_EVTHR
int
evhtp_use_callback_locks(evhtp_t * htp)
{
if (htp == NULL) {
return -1;
}
if (!(htp->lock = htp__malloc_(sizeof(pthread_mutex_t)))) {
return -1;
}
return pthread_mutex_init(htp->lock, NULL);
}
#endif
#ifndef EVHTP_DISABLE_REGEX
evhtp_callback_t *
evhtp_set_regex_cb(evhtp_t * htp,
const char * pattern,
evhtp_callback_cb cb,
void * arg)
{
evhtp_callback_t * hcb;
htp__lock_(htp);
if (htp->callbacks == NULL) {
if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) {
htp__unlock_(htp);
return NULL;
}
TAILQ_INIT(htp->callbacks);
}
if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_regex, cb, arg))) {
htp__unlock_(htp);
return NULL;
}
if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
evhtp_safe_free(hcb, evhtp_callback_free);
htp__unlock_(htp);
return NULL;
}
htp__unlock_(htp);
return hcb;
}
#endif
evhtp_callback_t *
evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg)
{
evhtp_callback_t * hcb;
htp__lock_(htp);
if (htp->callbacks == NULL) {
if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) {
htp__unlock_(htp);
return NULL;
}
TAILQ_INIT(htp->callbacks);
}
if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) {
htp__unlock_(htp);
return NULL;
}
if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
evhtp_safe_free(hcb, evhtp_callback_free);
htp__unlock_(htp);
return NULL;
}
htp__unlock_(htp);
return hcb;
}
void
evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg)
{
htp->defaults.cb = cb;
htp->defaults.cbarg = arg;
}
void
evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb cb, void * arg)
{
htp->defaults.pre_accept = cb;
htp->defaults.pre_accept_cbarg = arg;
}
void
evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb cb, void * arg)
{
htp->defaults.post_accept = cb;
htp->defaults.post_accept_cbarg = arg;
}
#ifndef EVHTP_DISABLE_SSL
#ifndef EVHTP_DISABLE_EVTHR
int
evhtp_ssl_use_threads(void)
{
int i;
if (ssl_locks_initialized == 1) {
return 0;
}
ssl_locks_initialized = 1;
ssl_num_locks = CRYPTO_num_locks();
if ((ssl_locks = htp__calloc_(ssl_num_locks,
sizeof(evhtp_mutex_t))) == NULL) {
return -1;
}
for (i = 0; i < ssl_num_locks; i++) {
pthread_mutex_init(&(ssl_locks[i]), NULL);
}
#if OPENSSL_VERSION_NUMBER < 0x10000000L
CRYPTO_set_id_callback(htp__ssl_get_thread_id_);
#else
CRYPTO_THREADID_set_callback(htp__ssl_get_thread_id_);
#endif
CRYPTO_set_locking_callback(htp__ssl_thread_lock_);
return 0;
}
#endif
int
evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg)
{
long cache_mode;
unsigned char c;
if (cfg == NULL || htp == NULL || cfg->pemfile == NULL) {
return -1;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
ERR_load_crypto_strings();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#else
/* unnecessary in OpenSSL 1.1.0 */
/*
* if (OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL) == 0) {
* log_error("OPENSSL_init_ssl");
* return -1;
* }
*
* if (OPENSSL_init_crypto(
* OPENSSL_INIT_ADD_ALL_CIPHERS |
* OPENSSL_INIT_ADD_ALL_DIGESTS |
* OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) {
* log_error("OPENSSL_init_crypto");
* return -1;
* }
*/
#endif
if (RAND_poll() != 1) {
log_error("RAND_poll");
return -1;
}
if (RAND_bytes(&c, 1) != 1) {
log_error("RAND_bytes");
return -1;
}
#if OPENSSL_VERSION_NUMBER < 0x10000000L
STACK_OF(SSL_COMP) * comp_methods = SSL_COMP_get_compression_methods();
sk_SSL_COMP_zero(comp_methods);
#endif
htp->ssl_cfg = cfg;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
htp->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
#else
htp->ssl_ctx = SSL_CTX_new(TLS_server_method());
#endif
evhtp_alloc_assert(htp->ssl_ctx);
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
SSL_CTX_set_options(htp->ssl_ctx, SSL_MODE_RELEASE_BUFFERS | SSL_OP_NO_COMPRESSION);
SSL_CTX_set_timeout(htp->ssl_ctx, cfg->ssl_ctx_timeout);
#endif
SSL_CTX_set_options(htp->ssl_ctx, cfg->ssl_opts);
#ifndef OPENSSL_NO_ECDH
if (cfg->named_curve != NULL) {
EC_KEY * ecdh = NULL;
int nid = 0;
nid = OBJ_sn2nid(cfg->named_curve);
if (nid == 0) {
log_error("ECDH initialization failed: unknown curve %s", cfg->named_curve);
}
ecdh = EC_KEY_new_by_curve_name(nid);
if (ecdh == NULL) {
log_error("ECDH initialization failed for curve %s", cfg->named_curve);
}
SSL_CTX_set_tmp_ecdh(htp->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
}
#endif /* OPENSSL_NO_ECDH */
#ifndef OPENSSL_NO_DH
if (cfg->dhparams != NULL) {
FILE * fh;
DH * dh;
fh = fopen(cfg->dhparams, "r");
if (fh != NULL) {
dh = PEM_read_DHparams(fh, NULL, NULL, NULL);
if (dh != NULL) {
SSL_CTX_set_tmp_dh(htp->ssl_ctx, dh);
DH_free(dh);
} else {
log_error("DH initialization failed: unable to parse file %s", cfg->dhparams);
}
fclose(fh);
} else {
log_error("DH initialization failed: unable to open file %s", cfg->dhparams);
}
}
#endif /* OPENSSL_NO_DH */
if (cfg->ciphers != NULL) {
if (SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers) == 0) {
log_error("set_cipher_list");
return -1;
}
}
SSL_CTX_load_verify_locations(htp->ssl_ctx, cfg->cafile, cfg->capath);
X509_STORE_set_flags(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->store_flags);
SSL_CTX_set_verify(htp->ssl_ctx, cfg->verify_peer, cfg->x509_verify_cb);
if (cfg->x509_chk_issued_cb != NULL) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb;
#else
X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb);
#endif
}
if (cfg->verify_depth) {
SSL_CTX_set_verify_depth(htp->ssl_ctx, cfg->verify_depth);
}
switch (cfg->scache_type) {
case evhtp_ssl_scache_type_disabled:
cache_mode = SSL_SESS_CACHE_OFF;
break;
default:
cache_mode = SSL_SESS_CACHE_SERVER;
break;
} /* switch */
SSL_CTX_use_certificate_chain_file(htp->ssl_ctx, cfg->pemfile);
char * const key = cfg->privfile ? cfg->privfile : cfg->pemfile;
if (cfg->decrypt_cb != NULL) {
EVP_PKEY * pkey = cfg->decrypt_cb(key);
if (pkey == NULL) {
return -1;
}
SSL_CTX_use_PrivateKey(htp->ssl_ctx, pkey);
/*cleanup */
EVP_PKEY_free(pkey);
} else {
SSL_CTX_use_PrivateKey_file(htp->ssl_ctx, key, SSL_FILETYPE_PEM);
}
SSL_CTX_set_session_id_context(htp->ssl_ctx,
(void *)&session_id_context,
sizeof(session_id_context));
SSL_CTX_set_app_data(htp->ssl_ctx, htp);
SSL_CTX_set_session_cache_mode(htp->ssl_ctx, cache_mode);
if (cache_mode != SSL_SESS_CACHE_OFF) {
SSL_CTX_sess_set_cache_size(htp->ssl_ctx,
cfg->scache_size ? cfg->scache_size : 1024);
if (cfg->scache_type == evhtp_ssl_scache_type_builtin ||
cfg->scache_type == evhtp_ssl_scache_type_user) {
SSL_CTX_sess_set_new_cb(htp->ssl_ctx, htp__ssl_add_scache_ent_);
SSL_CTX_sess_set_get_cb(htp->ssl_ctx, htp__ssl_get_scache_ent_);
SSL_CTX_sess_set_remove_cb(htp->ssl_ctx, htp__ssl_delete_scache_ent_);
if (cfg->scache_init) {
cfg->args = (cfg->scache_init)(htp);
}
}
}
return 0;
} /* evhtp_use_ssl */
#endif
struct bufferevent *
evhtp_connection_get_bev(evhtp_connection_t * connection)
{
return connection->bev;
}
struct bufferevent *
evhtp_connection_take_ownership(evhtp_connection_t * connection)
{
struct bufferevent * bev = evhtp_connection_get_bev(connection);
if (connection->hooks) {
evhtp_unset_all_hooks(&connection->hooks);
}
if (connection->request && connection->request->hooks) {
evhtp_unset_all_hooks(&connection->request->hooks);
}
evhtp_connection_set_bev(connection, NULL);
/* relinquish ownership of this connection, unset
* the ownership flag.
*/
HTP_FLAG_OFF(connection, EVHTP_CONN_FLAG_OWNER);
bufferevent_disable(bev, EV_READ);
bufferevent_setcb(bev, NULL, NULL, NULL, NULL);
return bev;
}
struct bufferevent *
evhtp_request_get_bev(evhtp_request_t * request)
{
return evhtp_connection_get_bev(request->conn);
}
struct bufferevent *
evhtp_request_take_ownership(evhtp_request_t * request)
{
return evhtp_connection_take_ownership(request->conn);
}
void
evhtp_connection_set_bev(evhtp_connection_t * conn, struct bufferevent * bev)
{
conn->bev = bev;
}
void
evhtp_request_set_bev(evhtp_request_t * request, struct bufferevent * bev)
{
evhtp_connection_set_bev(request->conn, bev);
}
void
evhtp_request_set_keepalive(evhtp_request_t * request, int val)
{
if (val) {
HTP_FLAG_ON(request, EVHTP_REQ_FLAG_KEEPALIVE);
}
}
evhtp_connection_t *
evhtp_request_get_connection(evhtp_request_t * request)
{
return request->conn;
}
evhtp_proto
evhtp_request_get_proto(evhtp_request_t * request)
{
return request->proto;
}
evhtp_res
evhtp_request_get_status_code(evhtp_request_t * request)
{
return request->status;
}
const char *
evhtp_request_get_status_code_str(evhtp_request_t * request)
{
return status_code_to_str(request->status);
}
inline void
evhtp_connection_set_timeouts(evhtp_connection_t * c,
const struct timeval * rtimeo,
const struct timeval * wtimeo)
{
if (evhtp_unlikely(c == NULL)) {
return;
}
bufferevent_set_timeouts(c->bev, rtimeo, wtimeo);
}
void
evhtp_connection_set_max_body_size(evhtp_connection_t * c, uint64_t len)
{
if (len == 0) {
c->max_body_size = c->htp->max_body_size;
} else {
c->max_body_size = len;
}
}
void
evhtp_request_set_max_body_size(evhtp_request_t * req, uint64_t len)
{
evhtp_connection_set_max_body_size(req->conn, len);
}
void
evhtp_connection_free(evhtp_connection_t * connection)
{
if (evhtp_unlikely(connection == NULL)) {
return;
}
htp__hook_connection_fini_(connection);
evhtp_safe_free(connection->request, htp__request_free_);
evhtp_safe_free(connection->parser, htp__free_);
evhtp_safe_free(connection->hooks, htp__free_);
evhtp_safe_free(connection->saddr, htp__free_);
evhtp_safe_free(connection->scratch_buf, evbuffer_free);
if (connection->resume_ev) {
evhtp_safe_free(connection->resume_ev, event_free);
}
if (connection->bev) {
#ifdef LIBEVENT_HAS_SHUTDOWN
bufferevent_shutdown(connection->bev, htp__shutdown_eventcb_);
#else
#ifndef EVHTP_DISABLE_SSL
if (connection->ssl != NULL) {
SSL_set_shutdown(connection->ssl, SSL_RECEIVED_SHUTDOWN);
SSL_shutdown(connection->ssl);
}
#endif
evhtp_safe_free(connection->bev, bufferevent_free);
#endif
}
evhtp_safe_free(connection, htp__free_);
} /* evhtp_connection_free */
void
evhtp_request_free(evhtp_request_t * request)
{
if (request == NULL) {
return;
}
evhtp_safe_free(request, htp__request_free_);
}
void
evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r_timeo, const struct timeval * w_timeo)
{
if (r_timeo != NULL) {
htp->recv_timeo = *r_timeo;
}
if (w_timeo != NULL) {
htp->send_timeo = *w_timeo;
}
}
void
evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num)
{
htp->max_keepalive_requests = num;
}
void
evhtp_set_bev_flags(evhtp_t * htp, int flags)
{
htp->bev_flags = flags;
}
void
evhtp_set_max_body_size(evhtp_t * htp, uint64_t len)
{
htp->max_body_size = len;
}
void
evhtp_disable_100_continue(evhtp_t * htp)
{
HTP_FLAG_OFF(htp, EVHTP_FLAG_ENABLE_100_CONT);
}
void
evhtp_set_parser_flags(evhtp_t * htp, int flags)
{
htp->parser_flags = flags;
}
#define HTP_FLAG_FNGEN(NAME, TYPE) void \
evhtp ## NAME ## _enable_flag(TYPE v, int flag) { \
HTP_FLAG_ON(v, flag); \
} \
\
void \
evhtp ## NAME ## _disable_flag(TYPE v, int flag) { \
HTP_FLAG_OFF(v, flag); \
} \
\
int \
evhtp ## NAME ## _get_flags(TYPE v) { \
if (v) { \
return v->flags; \
} \
return -1; \
}
HTP_FLAG_FNGEN(, evhtp_t *);
HTP_FLAG_FNGEN(_connection, evhtp_connection_t *);
HTP_FLAG_FNGEN(_request, evhtp_request_t *);
int
evhtp_add_alias(evhtp_t * evhtp, const char * name)
{
evhtp_alias_t * alias;
if (evhtp_unlikely(evhtp == NULL || name == NULL)) {
return -1;
}
if (!(alias = htp__calloc_(sizeof(evhtp_alias_t), 1))) {
return -1;
}
log_debug("Adding %s to aliases", name);
alias->alias = htp__strdup_(name);
if (evhtp_unlikely(alias->alias == NULL)) {
evhtp_safe_free(alias, htp__free_);
return -1;
}
TAILQ_INSERT_TAIL(&evhtp->aliases, alias, next);
return 0;
}
int
evhtp_add_aliases(evhtp_t * htp, const char * name, ...)
{
va_list argp;
if (evhtp_add_alias(htp, name) == -1) {
return -1;
}
va_start(argp, name);
{
const char * p;
while ((p = va_arg(argp, const char *)) != NULL) {
if (evhtp_add_alias(htp, p) == -1) {
log_error("Unable to add %s alias", p);
va_end(argp);
return -1;
}
}
}
va_end(argp);
return 0;
}
int
evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost)
{
if (evhtp == NULL || name == NULL || vhost == NULL) {
return -1;
}
if (TAILQ_FIRST(&vhost->vhosts) != NULL) {
/* vhosts cannot have secondary vhosts defined */
return -1;
}
vhost->server_name = htp__strdup_(name);
if (evhtp_unlikely(vhost->server_name == NULL)) {
return -1;
}
/* set the parent of this vhost so when the request has been completely
* serviced, the vhost can be reset to the original evhtp structure.
*
* This allows for a keep-alive connection to make multiple requests with
* different Host: values.
*/
vhost->parent = evhtp;
/* inherit various flags from the parent evhtp structure */
vhost->bev_flags = evhtp->bev_flags;
vhost->max_body_size = evhtp->max_body_size;
vhost->max_keepalive_requests = evhtp->max_keepalive_requests;
vhost->recv_timeo = evhtp->recv_timeo;
vhost->send_timeo = evhtp->send_timeo;
TAILQ_INSERT_TAIL(&evhtp->vhosts, vhost, next_vhost);
return 0;
}
static int
evhtp__new_(evhtp_t ** out, struct event_base * evbase, void * arg)
{
evhtp_t * htp;
if (evhtp_unlikely(evbase == NULL)) {
return -1;
}
*out = NULL;
if ((htp = htp__calloc_(1, sizeof(*htp))) == NULL) {
return -1;
}
htp->arg = arg;
htp->evbase = evbase;
htp->flags = EVHTP_FLAG_DEFAULTS;
htp->bev_flags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS;
/* default to lenient argument parsing */
htp->parser_flags = EVHTP_PARSE_QUERY_FLAG_DEFAULT;
TAILQ_INIT(&htp->vhosts);
TAILQ_INIT(&htp->aliases);
/* note that we pass the htp context to the callback,
* not the user supplied arguments. That is stored
* within the context itself.
*/
evhtp_set_gencb(htp, htp__default_request_cb_, (void *)htp);
*out = htp;
return 0;
}
evhtp_t *
evhtp_new(struct event_base * evbase, void * arg)
{
evhtp_t * htp;
if (evhtp__new_(&htp, evbase, arg) == -1) {
return NULL;
}
return htp;
}
void
evhtp_free(evhtp_t * evhtp)
{
evhtp_alias_t * evhtp_alias, * tmp;
if (evhtp == NULL) {
return;
}
#ifndef EVHTP_DISABLE_EVTHR
if (evhtp->thr_pool) {
evthr_pool_stop(evhtp->thr_pool);
evthr_pool_free(evhtp->thr_pool);
}
#endif
#ifndef EVHTP_DISABLE_SSL
if (evhtp->ssl_ctx) {
evhtp_safe_free(evhtp->ssl_ctx, SSL_CTX_free);
}
#endif
if (evhtp->server_name) {
evhtp_safe_free(evhtp->server_name, htp__free_);
}
if (evhtp->callbacks) {
evhtp_safe_free(evhtp->callbacks, evhtp_callbacks_free);
}
TAILQ_FOREACH_SAFE(evhtp_alias, &evhtp->aliases, next, tmp) {
if (evhtp_alias->alias != NULL) {
evhtp_safe_free(evhtp_alias->alias, htp__free_);
}
TAILQ_REMOVE(&evhtp->aliases, evhtp_alias, next);
evhtp_safe_free(evhtp_alias, htp__free_);
}
evhtp_safe_free(evhtp, htp__free_);
} /* evhtp_free */
/*****************************************************************
* client request functions *
*****************************************************************/
evhtp_connection_t *
evhtp_connection_new(struct event_base * evbase, const char * addr, uint16_t port)
{
return evhtp_connection_new_dns(evbase, NULL, addr, port);
}
evhtp_connection_t *
evhtp_connection_new_dns(struct event_base * evbase, struct evdns_base * dns_base,
const char * addr, uint16_t port)
{
evhtp_connection_t * conn;
int err;
log_debug("Enter");
evhtp_assert(evbase != NULL);
if (!(conn = htp__connection_new_(NULL, -1, evhtp_type_client))) {
return NULL;
}
conn->evbase = evbase;
conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE);
if (conn->bev == NULL) {
evhtp_safe_free(conn, evhtp_connection_free);
return NULL;
}
bufferevent_enable(conn->bev, EV_READ);
bufferevent_setcb(conn->bev, NULL, NULL,
htp__connection_eventcb_, conn);
if (dns_base != NULL) {
err = bufferevent_socket_connect_hostname(conn->bev, dns_base,
AF_UNSPEC, addr, port);
} else {
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
struct sockaddr * sin;
int salen;
if (inet_pton(AF_INET, addr, &sin4.sin_addr)) {
sin4.sin_family = AF_INET;
sin4.sin_port = htons(port);
sin = (struct sockaddr *)&sin4;
salen = sizeof(sin4);
} else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) {
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
sin = (struct sockaddr *)&sin6;
salen = sizeof(sin6);
} else {
/* Not a valid IP. */
evhtp_safe_free(conn, evhtp_connection_free);
return NULL;
}
err = bufferevent_socket_connect(conn->bev, sin, salen);
}
/* not needed since any of the bufferevent errors will go straight to
* the eventcb
*/
if (err) {
return NULL;
}
return conn;
} /* evhtp_connection_new_dns */
#ifndef EVHTP_DISABLE_SSL
#define ssl_sk_new_ bufferevent_openssl_socket_new
#define ssl_sk_connect_ bufferevent_socket_connect
evhtp_connection_t *
evhtp_connection_ssl_new(struct event_base * evbase,
const char * addr,
uint16_t port,
evhtp_ssl_ctx_t * ctx)
{
evhtp_connection_t * conn;
struct sockaddr_in sin;
const char * errstr;
if (evbase == NULL) {
return NULL;
}
if (!(conn = htp__connection_new_(NULL, -1, evhtp_type_client))) {
return NULL;
}
conn->evbase = evbase;
errstr = NULL;
do {
if ((conn->ssl = SSL_new(ctx)) == NULL) {
errstr = "unable to allocate SSL context";
break;
}
if ((conn->bev = ssl_sk_new_(evbase, -1, conn->ssl,
BUFFEREVENT_SSL_CONNECTING,
BEV_OPT_CLOSE_ON_FREE)) == NULL) {
errstr = "unable to allocate bev context";
break;
}
if (bufferevent_enable(conn->bev, EV_READ) == -1) {
errstr = "unable to enable reading";
break;
}
bufferevent_setcb(conn->bev, NULL, NULL,
htp__connection_eventcb_, conn);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(addr);
sin.sin_port = htons(port);
if (ssl_sk_connect_(conn->bev,
(struct sockaddr *)&sin,
sizeof(sin)) == -1) {
errstr = "sk_connect_ failure";
break;
}
} while (0);
if (errstr != NULL) {
log_error("%s", errstr);
evhtp_safe_free(conn, evhtp_connection_free);
return NULL;
}
return conn;
} /* evhtp_connection_ssl_new */
#endif
evhtp_request_t *
evhtp_request_new(evhtp_callback_cb cb, void * arg)
{
evhtp_request_t * r;
r = htp__request_new_(NULL);
evhtp_alloc_assert(r);
r->cb = cb;
r->cbarg = arg;
r->proto = EVHTP_PROTO_11;
return r;
}
int
evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r,
htp_method meth, const char * uri)
{
struct evbuffer * obuf;
char * proto;
obuf = bufferevent_get_output(c->bev);
r->conn = c;
r->method = meth;
c->request = r;
switch (r->proto) {
case EVHTP_PROTO_10:
proto = "1.0";
break;
case EVHTP_PROTO_11:
default:
proto = "1.1";
break;
}
evbuffer_add_printf(obuf, "%s %s HTTP/%s\r\n",
htparser_get_methodstr_m(meth), uri, proto);
if (evbuffer_get_length(r->buffer_out)) {
if (evhtp_header_find(r->headers_out, "content-length") == NULL) {
char out_buf[64] = { 0 };
size_t out_len = evbuffer_get_length(r->buffer_out);
evhtp_modp_sizetoa(out_len, out_buf);
evhtp_headers_add_header(r->headers_out,
evhtp_header_new("Content-Length", out_buf, 0, 1));
}
}
evhtp_headers_for_each(r->headers_out, htp__create_headers_, obuf);
evbuffer_add_reference(obuf, "\r\n", 2, NULL, NULL);
if (evbuffer_get_length(r->buffer_out)) {
evbuffer_add_buffer(obuf, r->buffer_out);
}
return 0;
} /* evhtp_make_request */
unsigned int
evhtp_request_status(evhtp_request_t * r)
{
return htparser_get_status(r->conn->parser);
}
evhtp.h
EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP
#define EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP
Definition: evhtp/evhtp.h:1107
evhtp_mutex_t
pthread_mutex_t evhtp_mutex_t
Definition: evhtp/evhtp.h:73
EVHTP_CONN_FLAG_VHOST_VIA_SNI
#define EVHTP_CONN_FLAG_VHOST_VIA_SNI
set to 1 if the vhost was found via SSL SNI
Definition: evhtp/evhtp.h:451
EVHTP_PARSE_QUERY_FLAG_STRICT
#define EVHTP_PARSE_QUERY_FLAG_STRICT
Definition: evhtp/evhtp.h:1103
EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS
#define EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS
Definition: evhtp/evhtp.h:1108
EVHTP_CONN_FLAG_OWNER
#define EVHTP_CONN_FLAG_OWNER
set to 1 if this structure owns the bufferevent
Definition: evhtp/evhtp.h:450
EVHTP_RES_OK
#define EVHTP_RES_OK
Definition: evhtp/evhtp.h:204
evhtp_ssl_data_t
unsigned char evhtp_ssl_data_t
Definition: evhtp/evhtp.h:50
evhtp_hook_headers_start_cb
evhtp_res(* evhtp_hook_headers_start_cb)(evhtp_request_t *r, void *arg)
Definition: evhtp/evhtp.h:173
evhtp_hook_chunk_new_cb
evhtp_res(* evhtp_hook_chunk_new_cb)(evhtp_request_t *r, uint64_t len, void *arg)
Definition: evhtp/evhtp.h:170
EVHTP_RES_FATAL
#define EVHTP_RES_FATAL
Definition: evhtp/evhtp.h:201
evhtp_headers_find_header
#define evhtp_headers_find_header
Definition: evhtp/evhtp.h:1212
EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS
#define EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS
Definition: evhtp/evhtp.h:1105
evhtp_hook_hostname_cb
evhtp_res(* evhtp_hook_hostname_cb)(evhtp_request_t *r, const char *hostname, void *arg)
Definition: evhtp/evhtp.h:174
evhtp_query_new
#define evhtp_query_new
Definition: evhtp/evhtp.h:1219
evhtp_hook_connection_fini_cb
evhtp_res(* evhtp_hook_connection_fini_cb)(evhtp_connection_t *connection, void *arg)
Definition: evhtp/evhtp.h:169
evhtp_headers_t
struct evhtp_kvs evhtp_headers_t
Definition: evhtp/evhtp.h:94
EVHTP_RES_ERROR
#define EVHTP_RES_ERROR
Definition: evhtp/evhtp.h:199
evhtp_ssl_ctx_t
SSL_CTX evhtp_ssl_ctx_t
Definition: evhtp/evhtp.h:46
EVHTP_CONN_FLAG_WAITING
#define EVHTP_CONN_FLAG_WAITING
used to make sure resuming happens AFTER sending a reply
Definition: evhtp/evhtp.h:454
evhtp_hook_conn_err_cb
evhtp_res(* evhtp_hook_conn_err_cb)(evhtp_connection_t *connection, evhtp_error_flags errtype, void *arg)
Definition: evhtp/evhtp.h:161
EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX
#define EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX
Definition: evhtp/evhtp.h:1104
evhtp_hook_request_fini_cb
evhtp_res(* evhtp_hook_request_fini_cb)(evhtp_request_t *req, void *arg)
Definition: evhtp/evhtp.h:168
EVHTP_CONN_FLAG_PAUSED
#define EVHTP_CONN_FLAG_PAUSED
this connection has been marked as paused
Definition: evhtp/evhtp.h:452
evhtp_ssl_sess_t
SSL_SESSION evhtp_ssl_sess_t
Definition: evhtp/evhtp.h:41
EVHTP_FLAG_DEFAULTS
#define EVHTP_FLAG_DEFAULTS
Definition: evhtp/evhtp.h:297
evhtp_hook_path_cb
evhtp_res(* evhtp_hook_path_cb)(evhtp_request_t *req, evhtp_path_t *path, void *arg)
Definition: evhtp/evhtp.h:166
EVHTP_FLAG_ENABLE_100_CONT
#define EVHTP_FLAG_ENABLE_100_CONT
Definition: evhtp/evhtp.h:293
EVHTP_REQ_FLAG_CHUNKED
#define EVHTP_REQ_FLAG_CHUNKED
Definition: evhtp/evhtp.h:415
evhtp_callback_cb
void(* evhtp_callback_cb)(evhtp_request_t *req, void *arg)
Definition: evhtp/evhtp.h:154
EVHTP_FLAG_ENABLE_NODELAY
#define EVHTP_FLAG_ENABLE_NODELAY
Definition: evhtp/evhtp.h:295
evhtp_query_free
#define evhtp_query_free
Definition: evhtp/evhtp.h:1220
evhtp_post_accept_cb
evhtp_res(* evhtp_post_accept_cb)(evhtp_connection_t *conn, void *arg)
Definition: evhtp/evhtp.h:163
evhtp_callback_type
evhtp_callback_type
Definition: evhtp/evhtp.h:127
evhtp_callback_type_glob
@ evhtp_callback_type_glob
Definition: evhtp/evhtp.h:129
evhtp_callback_type_hash
@ evhtp_callback_type_hash
Definition: evhtp/evhtp.h:128
evhtp_callback_type_regex
@ evhtp_callback_type_regex
Definition: evhtp/evhtp.h:131
EVHTP_REQ_FLAG_FINISHED
#define EVHTP_REQ_FLAG_FINISHED
Definition: evhtp/evhtp.h:414
EVHTP_PARSE_QUERY_FLAG_DEFAULT
#define EVHTP_PARSE_QUERY_FLAG_DEFAULT
Definition: evhtp/evhtp.h:1115
evhtp_hook_chunks_fini_cb
evhtp_res(* evhtp_hook_chunks_fini_cb)(evhtp_request_t *r, void *arg)
Definition: evhtp/evhtp.h:172
EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS
#define EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS
Definition: evhtp/evhtp.h:1106
EVHTP_CONN_FLAG_FREE_CONN
#define EVHTP_CONN_FLAG_FREE_CONN
Definition: evhtp/evhtp.h:455
evhtp_safe_free
#define evhtp_safe_free(_var, _freefn)
Definition: evhtp/evhtp.h:1445
evhtp_hook_type
evhtp_hook_type
types associated with where a developer can hook into during the request processing cycle.
Definition: evhtp/evhtp.h:108
evhtp_hook_on_error
@ evhtp_hook_on_error
type which defines to hook whenever an error occurs
Definition: evhtp/evhtp.h:119
evhtp_hook_on_header
@ evhtp_hook_on_header
type which defines to hook after one header has been parsed
Definition: evhtp/evhtp.h:109
evhtp_hook_on_read
@ evhtp_hook_on_read
type which defines to hook whenever the parser recieves data in a body
Definition: evhtp/evhtp.h:112
evhtp_hook_on_new_chunk
@ evhtp_hook_on_new_chunk
Definition: evhtp/evhtp.h:115
evhtp_hook_on_chunk_complete
@ evhtp_hook_on_chunk_complete
Definition: evhtp/evhtp.h:116
evhtp_hook_on_write
@ evhtp_hook_on_write
Definition: evhtp/evhtp.h:121
evhtp_hook_on_request_fini
@ evhtp_hook_on_request_fini
type which defines to hook before the request is free'd
Definition: evhtp/evhtp.h:113
evhtp_hook_on_hostname
@ evhtp_hook_on_hostname
Definition: evhtp/evhtp.h:120
evhtp_hook_on_chunks_complete
@ evhtp_hook_on_chunks_complete
Definition: evhtp/evhtp.h:117
evhtp_hook_on_event
@ evhtp_hook_on_event
Definition: evhtp/evhtp.h:122
evhtp_hook_on_conn_error
@ evhtp_hook_on_conn_error
type which defines to hook whenever a connection error occurs
Definition: evhtp/evhtp.h:123
evhtp_hook_on_headers_start
@ evhtp_hook_on_headers_start
Definition: evhtp/evhtp.h:118
evhtp_hook_on_path
@ evhtp_hook_on_path
type which defines to hook once a path has been parsed
Definition: evhtp/evhtp.h:111
evhtp_hook_on_connection_fini
@ evhtp_hook_on_connection_fini
Definition: evhtp/evhtp.h:114
evhtp_hook_on_headers
@ evhtp_hook_on_headers
type which defines to hook after all headers have been parsed
Definition: evhtp/evhtp.h:110
evhtp_hook__max
@ evhtp_hook__max
Definition: evhtp/evhtp.h:124
evhtp_ssl_t
SSL evhtp_ssl_t
Definition: evhtp/evhtp.h:45
evhtp_request_content_len
#define evhtp_request_content_len(r)
Definition: evhtp/evhtp.h:425
evhtp_hook_read_cb
evhtp_res(* evhtp_hook_read_cb)(evhtp_request_t *req, struct evbuffer *buf, void *arg)
Definition: evhtp/evhtp.h:167
EVHTP_REQ_FLAG_ERROR
#define EVHTP_REQ_FLAG_ERROR
Definition: evhtp/evhtp.h:416
evhtp_hook_header_cb
evhtp_res(* evhtp_hook_header_cb)(evhtp_request_t *req, evhtp_header_t *hdr, void *arg)
Definition: evhtp/evhtp.h:164
evhtp_headers_for_each
#define evhtp_headers_for_each
Definition: evhtp/evhtp.h:1213
evhtp_error_flags
uint8_t evhtp_error_flags
Definition: evhtp/evhtp.h:91
EVHTP_CONN_FLAG_KEEPALIVE
#define EVHTP_CONN_FLAG_KEEPALIVE
set to 1 after the first request has been processed and the connection is kept open
Definition: evhtp/evhtp.h:456
evhtp_hook
evhtp_res(* evhtp_hook)()
Definition: evhtp/evhtp.h:159
evhtp_kvs_iterator
int(* evhtp_kvs_iterator)(evhtp_kv_t *kv, void *arg)
Definition: evhtp/evhtp.h:177
EVHTP_CONN_FLAG_ERROR
#define EVHTP_CONN_FLAG_ERROR
Definition: evhtp/evhtp.h:449
evhtp_ssl_scache_type_user
@ evhtp_ssl_scache_type_user
Definition: evhtp/evhtp.h:100
evhtp_ssl_scache_type_builtin
@ evhtp_ssl_scache_type_builtin
Definition: evhtp/evhtp.h:101
evhtp_ssl_scache_type_disabled
@ evhtp_ssl_scache_type_disabled
Definition: evhtp/evhtp.h:98
EVHTP_RES_PAUSE
#define EVHTP_RES_PAUSE
Definition: evhtp/evhtp.h:200
evhtp_hook_chunk_fini_cb
evhtp_res(* evhtp_hook_chunk_fini_cb)(evhtp_request_t *r, void *arg)
Definition: evhtp/evhtp.h:171
evhtp_hook_headers_cb
evhtp_res(* evhtp_hook_headers_cb)(evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
Definition: evhtp/evhtp.h:165
evhtp_pre_accept_cb
evhtp_res(* evhtp_pre_accept_cb)(evhtp_connection_t *conn, void *arg)
Definition: evhtp/evhtp.h:162
evhtp_hook_err_cb
void(* evhtp_hook_err_cb)(evhtp_request_t *req, evhtp_error_flags errtype, void *arg)
Definition: evhtp/evhtp.h:155
evhtp_thread_exit_cb
void(* evhtp_thread_exit_cb)(evhtp_t *htp, evthr_t *thr, void *arg)
Definition: evhtp/evhtp.h:153
evhtp_kvs_t
struct evhtp_kvs evhtp_kvs_t
Definition: evhtp/evhtp.h:81
evhtp_proto
evhtp_proto
Definition: evhtp/evhtp.h:135
EVHTP_PROTO_10
@ EVHTP_PROTO_10
Definition: evhtp/evhtp.h:137
EVHTP_PROTO_INVALID
@ EVHTP_PROTO_INVALID
Definition: evhtp/evhtp.h:136
EVHTP_PROTO_11
@ EVHTP_PROTO_11
Definition: evhtp/evhtp.h:138
evhtp_res
uint16_t evhtp_res
Definition: evhtp/evhtp.h:90
evhtp_hook_write_cb
evhtp_res(* evhtp_hook_write_cb)(evhtp_connection_t *conn, void *arg)
Definition: evhtp/evhtp.h:175
EVHTP_FLAG_ENABLE_REUSEPORT
#define EVHTP_FLAG_ENABLE_REUSEPORT
Definition: evhtp/evhtp.h:294
evhtp_thread_init_cb
void(* evhtp_thread_init_cb)(evhtp_t *htp, evthr_t *thr, void *arg)
Definition: evhtp/evhtp.h:152
EVHTP_FLAG_ENABLE_DEFER_ACCEPT
#define EVHTP_FLAG_ENABLE_DEFER_ACCEPT
Definition: evhtp/evhtp.h:296
evhtp_query_t
struct evhtp_kvs evhtp_query_t
Definition: evhtp/evhtp.h:95
EVHTP_RES_DATA_TOO_LONG
#define EVHTP_RES_DATA_TOO_LONG
Definition: evhtp/evhtp.h:203
evhtp_type
evhtp_type
Definition: evhtp/evhtp.h:141
evhtp_type_server
@ evhtp_type_server
Definition: evhtp/evhtp.h:143
evhtp_type_client
@ evhtp_type_client
Definition: evhtp/evhtp.h:142
EVHTP_REQ_FLAG_KEEPALIVE
#define EVHTP_REQ_FLAG_KEEPALIVE
Definition: evhtp/evhtp.h:413
evhtp_hook_event_cb
void(* evhtp_hook_event_cb)(evhtp_connection_t *conn, short events, void *arg)
Definition: evhtp/evhtp.h:156
evhtp_callbacks_t
struct evhtp_callbacks evhtp_callbacks_t
Definition: evhtp/evhtp.h:78
EVHTP_CONN_FLAG_CONNECTED
#define EVHTP_CONN_FLAG_CONNECTED
client specific - set after successful connection
Definition: evhtp/evhtp.h:453
query_parser_state
query_parser_state
Definition: evhtp.c:3373
s_query_done
@ s_query_done
Definition: evhtp.c:3382
s_query_val
@ s_query_val
Definition: evhtp.c:3377
s_query_val_hex_1
@ s_query_val_hex_1
Definition: evhtp.c:3380
s_query_key_hex_2
@ s_query_key_hex_2
Definition: evhtp.c:3379
s_query_val_hex_2
@ s_query_val_hex_2
Definition: evhtp.c:3381
s_query_key
@ s_query_key
Definition: evhtp.c:3376
s_query_start
@ s_query_start
Definition: evhtp.c:3374
s_query_separator
@ s_query_separator
Definition: evhtp.c:3375
s_query_key_hex_1
@ s_query_key_hex_1
Definition: evhtp.c:3378
evhtp_set_pre_accept_cb
void evhtp_set_pre_accept_cb(evhtp_t *htp, evhtp_pre_accept_cb cb, void *arg)
call a user-defined function before the connection is accepted.
Definition: evhtp.c:4712
HTP_IS_WRITING
#define HTP_IS_WRITING(b)
Definition: evhtp.c:79
realloc_
static void *(* realloc_)(void *d, size_t sz)
Definition: evhtp.c:167
evhtp_header_find
const char * evhtp_header_find(evhtp_headers_t *headers, const char *key)
finds the value of a key in a evhtp_headers_t structure
Definition: evhtp.c:3311
evhtp_kvs_add_kv
void evhtp_kvs_add_kv(evhtp_kvs_t *kvs, evhtp_kv_t *kv)
appends a key/val structure to a evhtp_kvs_t tailq
Definition: evhtp.c:3347
htp__request_parse_hostname_
static int htp__request_parse_hostname_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1623
htp__malloc_
static void * htp__malloc_(size_t size)
Wrapper for malloc so that a different malloc can be used if desired.
Definition: evhtp.c:181
evhtp_connection_new
evhtp_connection_t * evhtp_connection_new(struct event_base *evbase, const char *addr, uint16_t port)
allocate a new connection
Definition: evhtp.c:5378
evhtp_connection_set_max_body_size
void evhtp_connection_set_max_body_size(evhtp_connection_t *c, uint64_t len)
set a max body size for a specific connection, this will default to the size set by evhtp_set_max_bod...
Definition: evhtp.c:5050
evhtp_kv_rm_and_free
void evhtp_kv_rm_and_free(evhtp_kvs_t *kvs, evhtp_kv_t *kv)
free's resources associated with 'kv' if ONLY found within the key/value list
Definition: evhtp.c:3237
evhtp_free
void evhtp_free(evhtp_t *evhtp)
Frees evhtp_t structure; will stop and free threads associated with the structure,...
Definition: evhtp.c:5330
evhtp_set_timeouts
void evhtp_set_timeouts(evhtp_t *htp, const struct timeval *r_timeo, const struct timeval *w_timeo)
set a read/write timeout on all things evhtp_t.
Definition: evhtp.c:5113
evhtp_connection_set_timeouts
void evhtp_connection_set_timeouts(evhtp_connection_t *c, const struct timeval *rtimeo, const struct timeval *wtimeo)
sets a connection-specific read/write timeout which overrides the global read/write settings.
Definition: evhtp.c:5038
htp__thread_exit_
static void htp__thread_exit_(evthr_t *thr, void *arg)
Definition: evhtp.c:4555
htp__hook_connection_error_
static evhtp_res htp__hook_connection_error_(struct evhtp_connection *connection, evhtp_error_flags errtype)
runs the user-defined hook when a connection error occurs
Definition: evhtp.c:761
evhtp_send_reply
void evhtp_send_reply(evhtp_request_t *request, evhtp_res code)
generates all the right information for a reply to be sent to the client
Definition: evhtp.c:3785
htp__hook_chunk_new_
static evhtp_res htp__hook_chunk_new_(struct evhtp_request *request, uint64_t len)
Runs the user defined request hook.
Definition: evhtp.c:669
evhtp_send_reply_end
void evhtp_send_reply_end(evhtp_request_t *request)
Definition: evhtp.c:3779
evhtp_bind_socket
int evhtp_bind_socket(evhtp_t *htp, const char *baddr, uint16_t port, int backlog)
bind to a socket, optionally with specific protocol support formatting.
Definition: evhtp.c:4114
evhtp_callback_set_hook
int evhtp_callback_set_hook(evhtp_callback_t *callback, evhtp_hook_type type, evhtp_hook cb, void *arg)
Definition: evhtp.c:4392
unscape_state
unscape_state
Definition: evhtp.c:3406
unscape_state_hex1
@ unscape_state_hex1
Definition: evhtp.c:3408
unscape_state_start
@ unscape_state_start
Definition: evhtp.c:3407
unscape_state_hex2
@ unscape_state_hex2
Definition: evhtp.c:3409
evhtp_send_reply_body
void evhtp_send_reply_body(evhtp_request_t *request, struct evbuffer *buf)
Definition: evhtp.c:3769
htp__request_free_
static void htp__request_free_(evhtp_request_t *request)
frees all data in an evhtp_request_t along with calling finished hooks
Definition: evhtp.c:1217
htp__request_parse_headers_start_
static int htp__request_parse_headers_start_(htparser *p)
Definition: evhtp.c:1420
evhtp_parse_query
evhtp_query_t * evhtp_parse_query(const char *query, size_t len)
Parses the query portion of the uri into a set of key/values in a strict manner.
Definition: evhtp.c:3745
htp__protocol_
static evhtp_proto htp__protocol_(const char major, const char minor)
returns the HTTP protocol version
Definition: evhtp.c:555
htp__hook_headers_
static evhtp_res htp__hook_headers_(struct evhtp_request *request, evhtp_headers_t *headers)
runs the user-defined on_Headers hook for a request after all headers have been parsed.
Definition: evhtp.c:612
TAILQ_HEAD
TAILQ_HEAD(evhtp_callbacks, evhtp_callback)
htp__request_parse_headers_
static int htp__request_parse_headers_(htparser *p)
Definition: evhtp.c:1789
htp__accept_cb_
static void htp__accept_cb_(struct evconnlistener *serv, int fd, struct sockaddr *s, int sl, void *arg)
Definition: evhtp.c:2812
htp__thread_init_
static void htp__thread_init_(evthr_t *thr, void *arg)
Definition: evhtp.c:4545
htp__realloc_
static void * htp__realloc_(void *ptr, size_t size)
Wrapper for realloc so that a different realloc can be used if desired.
Definition: evhtp.c:198
evhtp_callbacks_add_callback
int evhtp_callbacks_add_callback(evhtp_callbacks_t *cbs, evhtp_callback_t *cb)
Adds a evhtp_callback_t to the evhtp_callbacks_t list.
Definition: evhtp.c:4283
htp__strdup_
static char * htp__strdup_(const char *str)
implementation of strdup function.
Definition: evhtp.c:261
evhtp_set_gencb
void evhtp_set_gencb(evhtp_t *htp, evhtp_callback_cb cb, void *arg)
sets a callback which is called if no other callbacks are matched
Definition: evhtp.c:4705
htp__request_parse_header_val_
static int htp__request_parse_header_val_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1464
htp__ssl_get_thread_id_
static unsigned long htp__ssl_get_thread_id_(void)
Definition: evhtp.c:2876
session_id_context
static int session_id_context
Definition: evhtp.c:445
htp__request_parse_chunk_fini_
static int htp__request_parse_chunk_fini_(htparser *p)
Definition: evhtp.c:1881
HOOK_REQUEST_RUN_NARGS
#define HOOK_REQUEST_RUN_NARGS(__request, hook_name)
Definition: evhtp.c:100
htp__path_new_
static int htp__path_new_(evhtp_path_t **out, const char *data, size_t len)
parses the path and file from an input buffer
Definition: evhtp.c:985
request_psets
static htparse_hooks request_psets
callback definitions for request processing from libhtparse
Definition: evhtp.c:2193
evhtp_request_resume
void evhtp_request_resume(evhtp_request_t *request)
Wrapper around evhtp_connection_resume.
Definition: evhtp.c:3084
evhtp_connection_pause
void evhtp_connection_pause(evhtp_connection_t *c)
pauses a connection (disables reading)
Definition: evhtp.c:3034
htp__hook_header_
static evhtp_res htp__hook_header_(struct evhtp_request *request, evhtp_header_t *header)
runs the user-defined on_header hook for a request
Definition: evhtp.c:595
htp__path_free_
static void htp__path_free_(struct evhtp_path *path)
Correctly frees the evhtp_path_t ptr that is passed in.
Definition: evhtp.c:953
HTP_FLAG_FNGEN
#define HTP_FLAG_FNGEN(NAME, TYPE)
Definition: evhtp.c:5154
evhtp_kvs_find_kv
evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t *kvs, const char *key)
find the evhtp_kv_t reference 'key' from the k/val list 'kvs'
Definition: evhtp.c:3329
htp__ssl_thread_lock_
static void htp__ssl_thread_lock_(int mode, int type, const char *file, int line)
Definition: evhtp.c:2892
htp__require_uri_
static int htp__require_uri_(evhtp_connection_t *c)
Definition: evhtp.c:1677
evhtp_bind_sockaddr
int evhtp_bind_sockaddr(evhtp_t *htp, struct sockaddr *sa, size_t sin_len, int backlog)
bind to an already allocated sockaddr.
Definition: evhtp.c:4047
evhtp_callbacks_free
void evhtp_callbacks_free(evhtp_callbacks_t *callbacks)
Definition: evhtp.c:4167
htp__uri_free_
static void htp__uri_free_(evhtp_uri_t *uri)
frees an overlay URI structure
Definition: evhtp.c:1167
evhtp_ssl_use_threads
int evhtp_ssl_use_threads(void)
Definition: evhtp.c:4728
htp__calloc_
static void * htp__calloc_(size_t nmemb, size_t size)
Wrapper for calloc so that a different calloc can be used if desired.
Definition: evhtp.c:234
htp__request_parse_chunk_new_
static int htp__request_parse_chunk_new_(htparser *p)
Definition: evhtp.c:1864
evhtp_request_new
evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void *arg)
allocate a new request
Definition: evhtp.c:5526
evhtp_add_vhost
int evhtp_add_vhost(evhtp_t *evhtp, const char *name, evhtp_t *vhost)
add an evhtp_t structure (with its own callbacks) to a base evhtp_t structure for virtual hosts.
Definition: evhtp.c:5232
htp__unlock_
#define htp__unlock_(h)
Helper macro to unlock htp lock.
Definition: evhtp.c:128
free_
static void(* free_)(void *d)
Definition: evhtp.c:168
evhtp_kv_find
const char * evhtp_kv_find(evhtp_kvs_t *kvs, const char *key)
find the string value of 'key' from the key/value list 'kvs'
Definition: evhtp.c:3293
evhtp_get_cb
evhtp_callback_t * evhtp_get_cb(evhtp_t *htp, const char *path)
attempts to find the callback matching the exact string 'needle'.
Definition: evhtp.c:4524
htp__evbuffer_add_iovec_
static size_t htp__evbuffer_add_iovec_(struct evbuffer *buf, struct evbuffer_iovec *vec, int n_vec)
Definition: evhtp.c:2017
htp__hook_chunks_fini_
static evhtp_res htp__hook_chunks_fini_(struct evhtp_request *request)
Runs the user defined on chunk_finis hook.
Definition: evhtp.c:697
htp__run_pre_accept_
static int htp__run_pre_accept_(evhtp_t *htp, evhtp_connection_t *conn)
Definition: evhtp.c:2606
htp__connection_eventcb_
static void htp__connection_eventcb_(struct bufferevent *bev, short events, void *arg)
Definition: evhtp.c:2452
htp__ssl_servername_
static int htp__ssl_servername_(evhtp_ssl_t *ssl, int *unused, void *arg)
Definition: evhtp.c:2972
htp__request_parse_fini_
static int htp__request_parse_fini_(htparser *p)
Definition: evhtp.c:1958
htp__request_parse_body_
static int htp__request_parse_body_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1829
ssl_locks_initialized
static int ssl_locks_initialized
Definition: evhtp.c:449
evhtp_send_reply_start
void evhtp_send_reply_start(evhtp_request_t *request, evhtp_res code)
Definition: evhtp.c:3752
evhtp_is_hex_query_char
static int evhtp_is_hex_query_char(unsigned char ch)
Definition: evhtp.c:3386
evhtp_add_aliases
int evhtp_add_aliases(evhtp_t *htp, const char *name,...)
set a variable number of aliases in one call @reference evhtp_add_alias
Definition: evhtp.c:5205
htp__callback_find_
static evhtp_callback_t * htp__callback_find_(evhtp_callbacks_t *cbs, const char *path, unsigned int *start_offset, unsigned int *end_offset)
Locates a given callback offsets performs a regex pattern match.
Definition: evhtp.c:883
malloc_
static void *(* malloc_)(size_t sz)
Definition: evhtp.c:166
htp__request_parse_chunks_fini_
static int htp__request_parse_chunks_fini_(htparser *p)
Definition: evhtp.c:1897
_HTP_tid
#define _HTP_tid
Definition: evhtp.c:2869
evhtp_kvs_free
void evhtp_kvs_free(evhtp_kvs_t *kvs)
frees a the list of key/values, and all underlying entries
Definition: evhtp.c:3249
evhtp_send_reply_chunk
void evhtp_send_reply_chunk(evhtp_request_t *request, struct evbuffer *buf)
send a chunk reply.
Definition: evhtp.c:3888
evhtp_request_get_bev
struct bufferevent * evhtp_request_get_bev(evhtp_request_t *request)
returns the underlying requests bufferevent
Definition: evhtp.c:4982
HTP_LEN_INPUT
#define HTP_LEN_INPUT(b)
Definition: evhtp.c:83
htp__hook_request_fini_
static evhtp_res htp__hook_request_fini_(struct evhtp_request *request)
runs the user-defined hook called just prior to a request been free()'d
Definition: evhtp.c:650
htp__ssl_delete_scache_ent_
static void htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t *ctx, evhtp_ssl_sess_t *sess)
Definition: evhtp.c:2905
evhtp_request_free
void evhtp_request_free(evhtp_request_t *request)
Definition: evhtp.c:5103
HTP_IS_READING
#define HTP_IS_READING(b)
Definition: evhtp.c:77
htp__hook_headers_start_
static evhtp_res htp__hook_headers_start_(struct evhtp_request *request)
Runs the user defined on_headers_start hook.
Definition: evhtp.c:711
evhtp_accept_socket
int evhtp_accept_socket(evhtp_t *htp, evutil_socket_t sock, int backlog)
create the listener plus setup various options with an already-bound socket.
Definition: evhtp.c:3997
evhtp_connection_unset_hook
int evhtp_connection_unset_hook(evhtp_connection_t *conn, evhtp_hook_type type)
Definition: evhtp.c:4386
evhtp_set_mem_functions
void evhtp_set_mem_functions(void *(*mallocfn_)(size_t len), void *(*reallocfn_)(void *p, size_t sz), void(*freefn_)(void *p))
Definition: evhtp.c:321
htp__glob_match_
static int htp__glob_match_(const char *pattern, size_t plen, const char *string, size_t str_len)
glob/wildcard type pattern matching.
Definition: evhtp.c:817
htp__unset_hook_
static int htp__unset_hook_(evhtp_hooks_t **hooks, evhtp_hook_type type)
Definition: evhtp.c:4368
htp__request_parse_start_
static int htp__request_parse_start_(htparser *p)
Starts the parser for the connection associated with the parser struct.
Definition: evhtp.c:1306
evhtp_header_key_add
evhtp_header_t * evhtp_header_key_add(evhtp_headers_t *headers, const char *key, char key_alloc)
creates a new evhtp_header_t, sets only the key, and adds to the evhtp_headers TAILQ
Definition: evhtp.c:3092
htp__request_set_callbacks_
static int htp__request_set_callbacks_(evhtp_request_t *request)
Definition: evhtp.c:1526
htp__connection_accept_
static int htp__connection_accept_(struct event_base *evbase, evhtp_connection_t *connection)
Definition: evhtp.c:2626
evhtp_parse_query_wflags
evhtp_query_t * evhtp_parse_query_wflags(const char *query, const size_t len, const int flags)
Parses the query portion of the uri into a set of key/values.
Definition: evhtp.c:3486
evhtp_unescape_string
int evhtp_unescape_string(unsigned char **out, unsigned char *str, size_t str_len)
Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}'.
Definition: evhtp.c:3413
evhtp_kv_free
void evhtp_kv_free(evhtp_kv_t *kv)
frees resources allocated for a single key/value
Definition: evhtp.c:3219
htp__request_parse_args_
static int htp__request_parse_args_(htparser *p, const char *data, size_t len)
parses http request arguments
Definition: evhtp.c:1352
HTP_LEN_OUTPUT
#define HTP_LEN_OUTPUT(b)
Definition: evhtp.c:82
htp__hook_path_
static evhtp_res htp__hook_path_(struct evhtp_request *request, struct evhtp_path *path)
runs the user-defined on_path hook for a request
Definition: evhtp.c:577
evhtp_send_reply_chunk_start
void evhtp_send_reply_chunk_start(evhtp_request_t *request, evhtp_res code)
start a chunked response.
Definition: evhtp.c:3820
htp__is_http_10_
#define htp__is_http_10_(_major, _minor)
helper function to determine if http version is HTTP/1.1
Definition: evhtp.c:541
evhtp_set_max_keepalive_requests
void evhtp_set_max_keepalive_requests(evhtp_t *htp, uint64_t num)
sets a maximum number of requests that a single connection can make.
Definition: evhtp.c:5125
evhtp_set_post_accept_cb
void evhtp_set_post_accept_cb(evhtp_t *htp, evhtp_post_accept_cb cb, void *arg)
call a user-defined function right after a connection is accepted.
Definition: evhtp.c:4719
evhtp_send_reply_chunk_end
void evhtp_send_reply_chunk_end(evhtp_request_t *request)
call when all chunks have been sent and you wish to send the last bits.
Definition: evhtp.c:3913
evhtp_header_new
evhtp_header_t * evhtp_header_new(const char *key, const char *val, char kalloc, char valloc)
creates a new evhtp_header_t key/val structure
Definition: evhtp.c:3323
evhtp_request_get_hooks
evhtp_hooks_t * evhtp_request_get_hooks(evhtp_request_t *r)
returns request hooks
Definition: evhtp.c:4467
evhtp_set_bev_flags
void evhtp_set_bev_flags(evhtp_t *htp, int flags)
bufferevent flags which will be used for bev sockets.
Definition: evhtp.c:5131
htp__is_http_11_
#define htp__is_http_11_(_major, _minor)
helper macro to determine if http version is HTTP/1.0
Definition: evhtp.c:529
htp__hook_chunk_fini_
static evhtp_res htp__hook_chunk_fini_(struct evhtp_request *request)
Runs the user defined on_chunk_fini hook.
Definition: evhtp.c:683
evhtp_new
evhtp_t * evhtp_new(struct event_base *evbase, void *arg)
creates a new evhtp_t instance
Definition: evhtp.c:5318
evhtp_callback_new
evhtp_callback_t * evhtp_callback_new(const char *path, evhtp_callback_type type, evhtp_callback_cb cb, void *arg)
creates a new evhtp_callback_t structure.
Definition: evhtp.c:4186
evhtp_set_parser_flags
void evhtp_set_parser_flags(evhtp_t *htp, int flags)
during the request processing cycle, these flags will be used to for query argument parsing.
Definition: evhtp.c:5149
htp__connection_readcb_
static void htp__connection_readcb_(struct bufferevent *bev, void *arg)
Definition: evhtp.c:2215
evhtp_use_callback_locks
int evhtp_use_callback_locks(evhtp_t *htp)
creates a lock around callbacks and hooks, allowing for threaded applications to add/remove/modify ho...
Definition: evhtp.c:4613
htp__free_
static void htp__free_(void *ptr)
Wrapper for free so that a different free can be used if desired.
Definition: evhtp.c:213
htp__request_find_vhost_
static evhtp_t * htp__request_find_vhost_(evhtp_t *evhtp, const char *name)
Definition: evhtp.c:1493
htp__lock_
#define htp__lock_(h)
Helper macro to lock htp structure.
Definition: evhtp.c:117
htp__request_parse_header_key_
static int htp__request_parse_header_key_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1432
evhtp__new_
static int evhtp__new_(evhtp_t **out, struct event_base *evbase, void *arg)
Allocates new evhtp_t structure.
Definition: evhtp.c:5279
htp__connection_new_
static evhtp_connection_t * htp__connection_new_(evhtp_t *htp, evutil_socket_t sock, evhtp_type type)
Definition: evhtp.c:2704
htp__create_headers_
static int htp__create_headers_(evhtp_header_t *header, void *arg)
Definition: evhtp.c:2045
evhtp_callback_free
void evhtp_callback_free(evhtp_callback_t *callback)
safely frees callback structure memory and internals
Definition: evhtp.c:4253
evhtp_request_take_ownership
struct bufferevent * evhtp_request_take_ownership(evhtp_request_t *request)
Definition: evhtp.c:4988
htp__hook_body_
static evhtp_res htp__hook_body_(struct evhtp_request *request, struct evbuffer *buf)
runs the user-defined on_body hook for requests containing a body.
Definition: evhtp.c:630
evhtp_connection_set_hook
int evhtp_connection_set_hook(evhtp_connection_t *conn, evhtp_hook_type type, evhtp_hook cb, void *arg)
sets a callback hook for either a connection or a path/regex .
Definition: evhtp.c:4405
evhtp_connection_get_hooks
evhtp_hooks_t * evhtp_connection_get_hooks(evhtp_connection_t *c)
Definition: evhtp.c:4451
evhtp_request_get_proto
evhtp_proto evhtp_request_get_proto(evhtp_request_t *request)
Definition: evhtp.c:5020
htp__request_parse_path_
static int htp__request_parse_path_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1752
evhtp_set_glob_cb
evhtp_callback_t * evhtp_set_glob_cb(evhtp_t *htp, const char *pattern, evhtp_callback_cb cb, void *arg)
sets a callback to to be executed on simple glob/wildcard patterns this is useful if the app does not...
Definition: evhtp.c:4670
evhtp_request_pause
void evhtp_request_pause(evhtp_request_t *request)
Wrapper around evhtp_connection_pause.
Definition: evhtp.c:3075
evhtp_kvs_for_each
int evhtp_kvs_for_each(evhtp_kvs_t *kvs, evhtp_kvs_iterator cb, void *arg)
callback iterator which executes 'cb' for every entry in 'kvs'
Definition: evhtp.c:3273
HTP_FLAG_ON
#define HTP_FLAG_ON(PRE, FLAG)
Definition: evhtp.c:74
evhtp_use_threads_wexit
int evhtp_use_threads_wexit(evhtp_t *htp, evhtp_thread_init_cb init_cb, evhtp_thread_exit_cb exit_cb, int nthreads, void *arg)
Temporary function which will be renamed evhtp_use_threads in the future.
Definition: evhtp.c:4601
evhtp_connection_new_dns
evhtp_connection_t * evhtp_connection_new_dns(struct event_base *evbase, struct evdns_base *dns_base, const char *addr, uint16_t port)
allocate a new connection
Definition: evhtp.c:5384
evhtp_request_unset_hook
int evhtp_request_unset_hook(evhtp_request_t *req, evhtp_hook_type type)
Definition: evhtp.c:4380
htp__default_request_cb_
static void htp__default_request_cb_(evhtp_request_t *request, void *arg)
Definition: evhtp.c:2696
htp__strndup_
static char * htp__strndup_(const char *str, size_t len)
implementation of strndup function.
Definition: evhtp.c:291
evhtp_response_needs_body
int evhtp_response_needs_body(const evhtp_res code, const htp_method method)
Determine if a response should have a body.
Definition: evhtp.c:3811
evhtp_headers_add_header
void evhtp_headers_add_header(evhtp_headers_t *headers, evhtp_header_t *header)
adds an evhtp_header_t to the end of the evhtp_headers_t tailq
Definition: evhtp.c:3317
htp__request_new_
static evhtp_request_t * htp__request_new_(evhtp_connection_t *c)
Creates a new evhtp_request_t.
Definition: evhtp.c:1253
evhtp_use_threads
int evhtp_use_threads(evhtp_t *htp, evhtp_thread_init_cb init_cb, int nthreads, void *arg)
Enable thread-pool support for an evhtp_t context.
Definition: evhtp.c:4594
htp__hook_connection_write_
static evhtp_res htp__hook_connection_write_(struct evhtp_connection *connection)
Runs the user defined on_write hook.
Definition: evhtp.c:796
ssl_sk_connect_
#define ssl_sk_connect_
Definition: evhtp.c:5452
evhtp_unbind_socket
void evhtp_unbind_socket(evhtp_t *htp)
stops the listening socket.
Definition: evhtp.c:3924
htp__request_parse_host_
static int htp__request_parse_host_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1691
evhtp_connection_resume
void evhtp_connection_resume(evhtp_connection_t *c)
resumes a connection (enables reading) and activates resume event.
Definition: evhtp.c:3056
evhtp_request_status
unsigned int evhtp_request_status(evhtp_request_t *r)
Definition: evhtp.c:5589
htp__hook_hostname_
static evhtp_res htp__hook_hostname_(struct evhtp_request *r, const char *hostname)
Runs the user defined hostname processing hook.
Definition: evhtp.c:782
htp__hook_connection_fini_
static evhtp_res htp__hook_connection_fini_(struct evhtp_connection *connection)
runs the user-definedhook called just prior to a connection being closed
Definition: evhtp.c:727
evhtp_request_set_max_body_size
void evhtp_request_set_max_body_size(evhtp_request_t *req, uint64_t len)
just calls evhtp_connection_set_max_body_size for the request.
Definition: evhtp.c:5060
htp__ssl_add_scache_ent_
static int htp__ssl_add_scache_ent_(evhtp_ssl_t *ssl, evhtp_ssl_sess_t *sess)
Definition: evhtp.c:2922
evhtp_connection_get_bev
struct bufferevent * evhtp_connection_get_bev(evhtp_connection_t *connection)
returns the underlying connections bufferevent
Definition: evhtp.c:4950
evhtp_unset_all_hooks
int evhtp_unset_all_hooks(evhtp_hooks_t **hooks)
removes all hooks.
Definition: evhtp.c:4412
htp__ssl_get_scache_ent_
static evhtp_ssl_sess_t * htp__ssl_get_scache_ent_(evhtp_ssl_t *ssl, evhtp_ssl_data_t *sid, int sid_len, int *copy)
Definition: evhtp.c:2947
ssl_locks
static evhtp_mutex_t * ssl_locks
Definition: evhtp.c:448
htp__connection_resumecb_
static void htp__connection_resumecb_(int fd, short events, void *arg)
Definition: evhtp.c:2551
htp__hook_error_
static void htp__hook_error_(struct evhtp_request *request, evhtp_error_flags errtype)
runs the user-defined hook when a connection error occurs
Definition: evhtp.c:747
ssl_num_locks
static int ssl_num_locks
Definition: evhtp.c:447
evhtp_request_get_status_code_str
const char * evhtp_request_get_status_code_str(evhtp_request_t *request)
wrapper around get_status_code that returns the string version of the last status code set for a requ...
Definition: evhtp.c:5032
htp__connection_writecb_
static void htp__connection_writecb_(struct bufferevent *bev, void *arg)
Definition: evhtp.c:2296
status_code_to_str
static const char * status_code_to_str(evhtp_res code)
returns string status code from enum code
Definition: evhtp.c:342
evhtp_set_cb
evhtp_callback_t * evhtp_set_cb(evhtp_t *htp, const char *path, evhtp_callback_cb cb, void *arg)
sets a callback to be executed on a specific path
Definition: evhtp.c:4489
HTP_FLAG_OFF
#define HTP_FLAG_OFF(PRE, FLAG)
Definition: evhtp.c:75
evhtp_connection_free
void evhtp_connection_free(evhtp_connection_t *connection)
free's all connection related resources, this will also call your request fini hook and request fini ...
Definition: evhtp.c:5066
evhtp_request_get_connection
evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t *request)
returns the underlying evhtp_connection_t structure from a request
Definition: evhtp.c:5014
evhtp_connection_set_bev
void evhtp_connection_set_bev(evhtp_connection_t *conn, struct bufferevent *bev)
Sets the connections underlying bufferevent.
Definition: evhtp.c:4994
ssl_sk_new_
#define ssl_sk_new_
Definition: evhtp.c:5451
evhtp_header_val_add
evhtp_header_t * evhtp_header_val_add(evhtp_headers_t *headers, const char *val, char val_alloc)
finds the last header in the headers tailq and adds the value
Definition: evhtp.c:3106
evhtp_set_regex_cb
evhtp_callback_t * evhtp_set_regex_cb(evhtp_t *htp, const char *pattern, evhtp_callback_cb cb, void *arg)
sets a callback to be executed based on a regex pattern
Definition: evhtp.c:4630
evhtp_callback_unset_hook
int evhtp_callback_unset_hook(evhtp_callback_t *callback, evhtp_hook_type type)
Definition: evhtp.c:4374
evhtp_callback_get_hooks
evhtp_hooks_t * evhtp_callback_get_hooks(evhtp_callback_t *cb)
returns callback hooks
Definition: evhtp.c:4483
HOOK_REQUEST_RUN
#define HOOK_REQUEST_RUN(request, hook_name,...)
Definition: evhtp.c:89
evhtp_connection_ssl_new
evhtp_connection_t * evhtp_connection_ssl_new(struct event_base *evbase, const char *addr, uint16_t port, evhtp_ssl_ctx_t *ctx)
Definition: evhtp.c:5455
evhtp_kvs_new
evhtp_kvs_t * evhtp_kvs_new(void)
creates an empty list of key/values
Definition: evhtp.c:3140
htp__use_threads_
static int htp__use_threads_(evhtp_t *htp, evhtp_thread_init_cb init_cb, evhtp_thread_exit_cb exit_cb, int nthreads, void *arg)
Definition: evhtp.c:4565
evhtp_kvs_add_kvs
void evhtp_kvs_add_kvs(evhtp_kvs_t *dst, evhtp_kvs_t *src)
appends all key/val structures from src tailq onto dst tailq
Definition: evhtp.c:3357
evhtp_disable_100_continue
void evhtp_disable_100_continue(evhtp_t *htp)
Definition: evhtp.c:5143
htp__serv_setsockopts_
static int htp__serv_setsockopts_(evhtp_t *htp, evutil_socket_t sock)
Definition: evhtp.c:3934
htp__authority_free_
static void htp__authority_free_(evhtp_authority_t *authority)
frees an authority structure
Definition: evhtp.c:1148
htp__authority_new_
static int htp__authority_new_(evhtp_authority_t **out)
create an authority structure
Definition: evhtp.c:1129
htp__set_hook_
static int htp__set_hook_(evhtp_hooks_t **hooks, evhtp_hook_type type, evhtp_hook cb, void *arg)
Definition: evhtp.c:4291
evhtp_request_set_keepalive
void evhtp_request_set_keepalive(evhtp_request_t *request, int val)
Definition: evhtp.c:5006
evhtp_add_alias
int evhtp_add_alias(evhtp_t *evhtp, const char *name)
Add an alias hostname for a virtual-host specific evhtp_t.
Definition: evhtp.c:5177
evhtp_request_get_method
htp_method evhtp_request_get_method(evhtp_request_t *r)
returns the htp_method enum version of the request method.
Definition: evhtp.c:3024
evhtp_kv_new
evhtp_kv_t * evhtp_kv_new(const char *key, const char *val, char key_alloc, char val_alloc)
Allocates a new key/value structure.
Definition: evhtp.c:3156
evhtp_make_request
int evhtp_make_request(evhtp_connection_t *c, evhtp_request_t *r, htp_method meth, const char *uri)
make a client request
Definition: evhtp.c:5541
evhtp_request_set_hook
int evhtp_request_set_hook(evhtp_request_t *req, evhtp_hook_type type, evhtp_hook cb, void *arg)
Definition: evhtp.c:4399
evhtp_request_set_bev
void evhtp_request_set_bev(evhtp_request_t *request, struct bufferevent *bev)
sets the underlying bufferevent for a evhtp_request
Definition: evhtp.c:5000
htp__run_in_thread_
static void htp__run_in_thread_(evthr_t *thr, void *arg, void *shared)
Definition: evhtp.c:2788
htp__should_parse_query_body_
static int htp__should_parse_query_body_(evhtp_request_t *req)
determines if the request body contains the query arguments.
Definition: evhtp.c:1923
htp__run_post_accept_
static int htp__run_post_accept_(evhtp_t *htp, evhtp_connection_t *connection)
Definition: evhtp.c:2767
htp__create_reply_
static struct evbuffer * htp__create_reply_(evhtp_request_t *request, evhtp_res code)
Definition: evhtp.c:2061
evhtp_set_max_body_size
void evhtp_set_max_body_size(evhtp_t *htp, uint64_t len)
set a max body size to accept for an incoming request, this will default to unlimited.
Definition: evhtp.c:5137
evhtp_request_get_status_code
evhtp_res evhtp_request_get_status_code(evhtp_request_t *request)
Returns the last status code set for a request (request/response)
Definition: evhtp.c:5026
evhtp_connection_take_ownership
struct bufferevent * evhtp_connection_take_ownership(evhtp_connection_t *connection)
let a user take ownership of the underlying bufferevent and free all other underlying resources.
Definition: evhtp.c:4956
htp__uri_new_
static int htp__uri_new_(evhtp_uri_t **out)
create an overlay URI structure
Definition: evhtp.c:1189
evhtp_ssl_init
int evhtp_ssl_init(evhtp_t *htp, evhtp_ssl_cfg_t *cfg)
Definition: evhtp.c:4762
htp__request_parse_port_
static int htp__request_parse_port_(htparser *p, const char *data, size_t len)
Definition: evhtp.c:1726
internal.h
log_warn
#define log_warn(M,...)
Definition: internal.h:67
log_error
#define log_error(M,...)
Definition: internal.h:57
TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: internal.h:22
evhtp_alloc_assert
#define evhtp_alloc_assert(x)
Definition: internal.h:93
log_debug
#define log_debug(M,...)
Definition: internal.h:47
evhtp_assert
#define evhtp_assert(x)
Definition: internal.h:83
evhtp_unlikely
#define evhtp_unlikely(x)
Definition: internal.h:18
evhtp_likely
#define evhtp_likely(x)
Definition: internal.h:17
numtoa.h
evhtp_modp_uchartoa
#define evhtp_modp_uchartoa(_val)
Definition: numtoa.h:43
evhtp_modp_u32toa
EVHTP_EXPORT size_t evhtp_modp_u32toa(uint32_t value, char *str)
converts uint32_t value to string
evhtp_modp_sizetoa
EVHTP_EXPORT size_t evhtp_modp_sizetoa(size_t value, char *str)
based on the system architecture, convert a size_t number to a string.
htparser_get_error
htpparse_error htparser_get_error(htparser *p)
Definition: parser.c:426
htparser_get_userdata
void * htparser_get_userdata(htparser *p)
Definition: parser.c:533
htparser_run
size_t htparser_run(htparser *p, htparse_hooks *hooks, const char *data, size_t len)
Definition: parser.c:723
htparser_get_method
htp_method htparser_get_method(htparser *p)
Definition: parser.c:480
htparser_set_major
void htparser_set_major(htparser *p, unsigned char major)
Definition: parser.c:503
htparser_should_keep_alive
int htparser_should_keep_alive(htparser *p)
Definition: parser.c:451
htparser_get_minor
unsigned char htparser_get_minor(htparser *p)
Definition: parser.c:521
htparser_get_scheme
htp_scheme htparser_get_scheme(htparser *p)
Definition: parser.c:474
htparser_init
void htparser_init(htparser *p, htp_type type)
Definition: parser.c:569
htparser_get_content_length
uint64_t htparser_get_content_length(htparser *p)
Definition: parser.c:551
htparser_get_methodstr_m
const char * htparser_get_methodstr_m(htp_method meth)
Definition: parser.c:486
htparser_set_minor
void htparser_set_minor(htparser *p, unsigned char minor)
Definition: parser.c:509
htparser_get_major
unsigned char htparser_get_major(htparser *p)
Definition: parser.c:515
htparser_get_status
unsigned int htparser_get_status(htparser *p)
Definition: parser.c:445
htparser_get_multipart
unsigned char htparser_get_multipart(htparser *p)
Definition: parser.c:527
htparser_set_userdata
void htparser_set_userdata(htparser *p, void *ud)
Definition: parser.c:539
htparser_new
htparser * htparser_new(void)
Definition: parser.c:581
evhtp_alias
Definition: evhtp/evhtp.h:273
evhtp_alias::alias
char * alias
Definition: evhtp/evhtp.h:274
evhtp_authority
structure which represents authority information in a URI
Definition: evhtp/evhtp.h:372
evhtp_callback
structure containing a single callback and configuration
Definition: evhtp.c:51
evhtp_callback::path
char * path
Definition: evhtp.c:59
evhtp_callback::len
size_t len
Definition: evhtp.c:56
evhtp_callback::cbarg
void * cbarg
user-defind arguments passed to the cb
Definition: evhtp.c:54
evhtp_callback::type
evhtp_callback_type type
the type of callback (regex|path)
Definition: evhtp.c:52
evhtp_callback::val
union evhtp_callback::@0 val
evhtp_callback::regex
regex_t * regex
Definition: evhtp.c:62
evhtp_callback::hooks
evhtp_hooks_t * hooks
per-callback hooks
Definition: evhtp.c:55
evhtp_callback::glob
char * glob
Definition: evhtp.c:60
evhtp_callback::cb
evhtp_callback_cb cb
the actual callback function
Definition: evhtp.c:53
evhtp_connection
Definition: evhtp/evhtp.h:427
evhtp_connection::request
evhtp_request_t * request
the request currently being processed
Definition: evhtp/evhtp.h:444
evhtp_connection::hooks
evhtp_hooks_t * hooks
Definition: evhtp/evhtp.h:437
evhtp_path
structure which represents a URI path and or file
Definition: evhtp/evhtp.h:383
evhtp_path::file
char * file
the filename if present (c.html)
Definition: evhtp/evhtp.h:386
evhtp_path::match_start
char * match_start
Definition: evhtp/evhtp.h:387
evhtp_path::full
char * full
the full path+file (/a/b/c.html)
Definition: evhtp/evhtp.h:384
evhtp_path::match_end
char * match_end
Definition: evhtp/evhtp.h:388
evhtp_path::path
char * path
the path (/a/b/)
Definition: evhtp/evhtp.h:385
evhtp_request
a structure containing all information for a http request.
Definition: evhtp/evhtp.h:401
evhtp_request::buffer_in
struct evbuffer * buffer_in
buffer containing data from client
Definition: evhtp/evhtp.h:406
evhtp_request::cbarg
void * cbarg
argument which is passed to the cb function
Definition: evhtp/evhtp.h:420
evhtp_request::uri
evhtp_uri_t * uri
request URI information
Definition: evhtp/evhtp.h:405
evhtp_request::buffer_out
struct evbuffer * buffer_out
buffer containing data to client
Definition: evhtp/evhtp.h:407
evhtp_request::headers_out
evhtp_headers_t * headers_out
headers to client
Definition: evhtp/evhtp.h:409
evhtp_request::headers_in
evhtp_headers_t * headers_in
headers from client
Definition: evhtp/evhtp.h:408
evhtp_request::htp
evhtp_t * htp
the parent evhtp_t structure
Definition: evhtp/evhtp.h:402
evhtp_request::hooks
evhtp_hooks_t * hooks
request specific hooks
Definition: evhtp/evhtp.h:404
evhtp_request::status
evhtp_res status
The HTTP response code or other error conditions.
Definition: evhtp/evhtp.h:412
evhtp_request::conn
evhtp_connection_t * conn
the associated connection
Definition: evhtp/evhtp.h:403
evhtp_uri
a generic container representing an entire URI strucutre
Definition: evhtp/evhtp.h:359
evhtp_uri::authority
evhtp_authority_t * authority
Definition: evhtp/evhtp.h:360
evhtp
main structure containing all configuration information
Definition: evhtp/evhtp.h:283
evhtp::server_name
char * server_name
the name included in Host: responses
Definition: evhtp/evhtp.h:287
evhtp::thr_pool
evthr_pool_t * thr_pool
connection threadpool
Definition: evhtp/evhtp.h:312
evhtp::send_timeo
struct timeval send_timeo
Definition: evhtp/evhtp.h:328
evhtp::bev_flags
int bev_flags
bufferevent flags to use on bufferevent_*_socket_new()
Definition: evhtp/evhtp.h:289
evhtp::recv_timeo
struct timeval recv_timeo
Definition: evhtp/evhtp.h:327
evhtp::ssl_ctx
evhtp_ssl_ctx_t * ssl_ctx
if ssl enabled, this is the servers CTX
Definition: evhtp/evhtp.h:307
evhtp::max_keepalive_requests
uint64_t max_keepalive_requests
Definition: evhtp/evhtp.h:291
evhtp::max_body_size
uint64_t max_body_size
Definition: evhtp/evhtp.h:290
evhtp::callbacks
evhtp_callbacks_t * callbacks
Definition: evhtp/evhtp.h:324
evhtp::defaults
evhtp_defaults_t defaults
Definition: evhtp/evhtp.h:325
htparser
Definition: parser.c:91
evthr_get_base
evbase_t * evthr_get_base(evthr_t *thr)
Definition: thread.c:176
evthr_pool_free
void evthr_pool_free(evthr_pool_t *pool)
Definition: thread.c:316
cb
evthr_cb cb
Definition: thread.c:2
evthr_pool_defer
evthr_res evthr_pool_defer(evthr_pool_t *pool, evthr_cb cb, void *arg)
Definition: thread.c:362
args
void * args
Definition: thread.c:1
evthr_pool_wexit_new
evthr_pool_t * evthr_pool_wexit_new(int nthreads, evthr_init_cb init_cb, evthr_exit_cb exit_cb, void *shared)
Definition: thread.c:468
evthr_pool_start
int evthr_pool_start(evthr_pool_t *pool)
Definition: thread.c:476
evthr_pool_stop
evthr_res evthr_pool_stop(evthr_pool_t *pool)
Definition: thread.c:335