From dbf659576f9ebec2431698ffe36ecc53eba461b4 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Sat, 8 Jun 2013 13:24:32 +0000 Subject: [PATCH] patch: added HTTP parser --- Makefile | 2 +- include/re.h | 1 + include/re_http.h | 149 +++++++++++++ src/http/auth.c | 166 +++++++++++++++ src/http/mod.mk | 9 + src/http/msg.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++ src/http/server.c | 504 ++++++++++++++++++++++++++++++++++++++++++++ src/sip/auth.c | 2 +- 8 files changed, 1358 insertions(+), 2 deletions(-) create mode 100644 include/re_http.h create mode 100644 src/http/auth.c create mode 100644 src/http/mod.mk create mode 100644 src/http/msg.c create mode 100644 src/http/server.c diff --git a/Makefile b/Makefile index 081dff0..46a2119 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ include $(MK) # List of modules MODULES += sip sipevent sipreg sipsess -MODULES += uri httpauth +MODULES += uri http httpauth MODULES += stun turn ice MODULES += natbd MODULES += rtp sdp jbuf telev diff --git a/include/re.h b/include/re.h index 095b9bb..575a7f7 100644 --- a/include/re.h +++ b/include/re.h @@ -25,6 +25,7 @@ extern "C" { #include "re_crc32.h" #include "re_dns.h" #include "re_hash.h" +#include "re_http.h" #include "re_httpauth.h" #include "re_ice.h" #include "re_jbuf.h" diff --git a/include/re_http.h b/include/re_http.h new file mode 100644 index 0000000..c2869af --- /dev/null +++ b/include/re_http.h @@ -0,0 +1,149 @@ +/** + * @file re_http.h Hypertext Transfer Protocol + * + * Copyright (C) 2010 Creytiv.com + */ + + +/** HTTP Header ID (perfect hash value) */ +enum http_hdrid { + HTTP_HDR_ACCEPT = 3186, + HTTP_HDR_ACCEPT_CHARSET = 24, + HTTP_HDR_ACCEPT_ENCODING = 708, + HTTP_HDR_ACCEPT_LANGUAGE = 2867, + HTTP_HDR_ACCEPT_RANGES = 3027, + HTTP_HDR_AGE = 742, + HTTP_HDR_ALLOW = 2429, + HTTP_HDR_AUTHORIZATION = 2503, + HTTP_HDR_CACHE_CONTROL = 2530, + HTTP_HDR_CONNECTION = 865, + HTTP_HDR_CONTENT_ENCODING = 580, + HTTP_HDR_CONTENT_LANGUAGE = 3371, + HTTP_HDR_CONTENT_LENGTH = 3861, + HTTP_HDR_CONTENT_LOCATION = 3927, + HTTP_HDR_CONTENT_MD5 = 406, + HTTP_HDR_CONTENT_RANGE = 2846, + HTTP_HDR_CONTENT_TYPE = 809, + HTTP_HDR_DATE = 1027, + HTTP_HDR_ETAG = 2392, + HTTP_HDR_EXPECT = 1550, + HTTP_HDR_EXPIRES = 1983, + HTTP_HDR_FROM = 1963, + HTTP_HDR_HOST = 3191, + HTTP_HDR_IF_MATCH = 2684, + HTTP_HDR_IF_MODIFIED_SINCE = 2187, + HTTP_HDR_IF_NONE_MATCH = 4030, + HTTP_HDR_IF_RANGE = 2220, + HTTP_HDR_IF_UNMODIFIED_SINCE = 962, + HTTP_HDR_LAST_MODIFIED = 2946, + HTTP_HDR_LOCATION = 2514, + HTTP_HDR_MAX_FORWARDS = 3549, + HTTP_HDR_PRAGMA = 1673, + HTTP_HDR_PROXY_AUTHENTICATE = 116, + HTTP_HDR_PROXY_AUTHORIZATION = 2363, + HTTP_HDR_RANGE = 4004, + HTTP_HDR_REFERER = 2991, + HTTP_HDR_RETRY_AFTER = 409, + HTTP_HDR_SEC_WEBSOCKET_ACCEPT = 2959, + HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS = 2937, + HTTP_HDR_SEC_WEBSOCKET_KEY = 746, + HTTP_HDR_SEC_WEBSOCKET_PROTOCOL = 2076, + HTTP_HDR_SEC_WEBSOCKET_VERSION = 3158, + HTTP_HDR_SERVER = 973, + HTTP_HDR_TE = 2035, + HTTP_HDR_TRAILER = 2577, + HTTP_HDR_TRANSFER_ENCODING = 2115, + HTTP_HDR_UPGRADE = 717, + HTTP_HDR_USER_AGENT = 4064, + HTTP_HDR_VARY = 3076, + HTTP_HDR_VIA = 3961, + HTTP_HDR_WARNING = 2108, + HTTP_HDR_WWW_AUTHENTICATE = 2763, + + HTTP_HDR_NONE = -1 +}; + + +/** HTTP Header */ +struct http_hdr { + struct le le; + struct pl name; + struct pl val; + enum http_hdrid id; +}; + +/** HTTP Message */ +struct http_msg { + struct pl ver; + struct pl met; + struct pl path; + struct pl prm; + uint16_t scode; + struct pl reason; + struct list hdrl; + struct pl ctype; + struct mbuf *mb; + uint32_t clen; +}; + +typedef bool(http_hdr_h)(const struct http_hdr *hdr, void *arg); + +int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req); + + +const struct http_hdr *http_msg_hdr(const struct http_msg *msg, + enum http_hdrid id); +const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg, + bool fwd, enum http_hdrid id, + http_hdr_h *h, void *arg); +const struct http_hdr *http_msg_xhdr(const struct http_msg *msg, + const char *name); +const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg, + bool fwd, const char *name, + http_hdr_h *h, void *arg); +uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id); +uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name); +bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id, + const char *value); +bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name, + const char *value); +int http_msg_print(struct re_printf *pf, const struct http_msg *msg); + + +/* Server */ +struct http_sock; +struct http_conn; + +typedef void (http_req_h)(struct http_conn *conn, const struct http_msg *msg, + void *arg); + +int http_listen(struct http_sock **sockp, const struct sa *laddr, + http_req_h *reqh, void *arg); +int https_listen(struct http_sock **sockp, const struct sa *laddr, + const char *cert, http_req_h *reqh, void *arg); +const struct sa *http_conn_peer(const struct http_conn *conn); +struct tcp_conn *http_conn_tcp(struct http_conn *conn); +struct tls_conn *http_conn_tls(struct http_conn *conn); +void http_conn_close(struct http_conn *conn); +int http_reply(struct http_conn *conn, uint16_t scode, const char *reason, + const char *fmt, ...); +int http_creply(struct http_conn *conn, uint16_t scode, const char *reason, + const char *ctype, const char *fmt, ...); +int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason); + + +/* Authentication */ +struct http_auth { + const char *realm; + bool stale; +}; + +typedef int (http_auth_h)(const struct pl *username, uint8_t *ha1, void *arg); + +int http_auth_print_challenge(struct re_printf *pf, + const struct http_auth *auth); +bool http_auth_check(const struct pl *hval, const struct pl *method, + struct http_auth *auth, http_auth_h *authh, void *arg); +bool http_auth_check_request(const struct http_msg *msg, + struct http_auth *auth, + http_auth_h *authh, void *arg); diff --git a/src/http/auth.c b/src/http/auth.c new file mode 100644 index 0000000..85448d7 --- /dev/null +++ b/src/http/auth.c @@ -0,0 +1,166 @@ +/** + * @file http/auth.c HTTP Authentication + * + * Copyright (C) 2011 Creytiv.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +enum { + NONCE_EXPIRES = 300, + NONCE_MIN_SIZE = 33, +}; + + +static uint64_t secret; +static bool secret_set; + + +/** + * Print HTTP digest authentication challenge + * + * @param pf Print function for output + * @param auth Authentication parameteres + * + * @return 0 if success, otherwise errorcode + */ +int http_auth_print_challenge(struct re_printf *pf, + const struct http_auth *auth) +{ + uint8_t key[MD5_SIZE]; + uint64_t nv[2]; + + if (!auth) + return 0; + + if (!secret_set) { + secret = rand_u64(); + secret_set = true; + } + + nv[0] = time(NULL); + nv[1] = secret; + + md5((uint8_t *)nv, sizeof(nv), key); + + return re_hprintf(pf, + "Digest realm=\"%s\", nonce=\"%w%llx\", " + "qop=\"auth\"%s", + auth->realm, + key, sizeof(key), nv[0], + auth->stale ? ", stale=true" : ""); +} + + +static int chk_nonce(const struct pl *nonce, uint32_t expires) +{ + uint8_t nkey[MD5_SIZE], ckey[MD5_SIZE]; + uint64_t nv[2]; + struct pl pl; + int64_t age; + unsigned i; + + if (!nonce || !nonce->p || nonce->l < NONCE_MIN_SIZE) + return EINVAL; + + pl = *nonce; + + for (i=0; i expires) + return ETIMEDOUT; + + return 0; +} + + +/** + * Check HTTP digest authorization + * + * @param hval Authorization header value + * @param method Request method + * @param auth Authentication parameteres + * @param authh Authentication handler + * @param arg Authentication handler argument + * + * @return true if check is passed, otherwise false + */ +bool http_auth_check(const struct pl *hval, const struct pl *method, + struct http_auth *auth, http_auth_h *authh, void *arg) +{ + struct httpauth_digest_resp resp; + uint8_t ha1[MD5_SIZE]; + + if (!hval || !method || !auth || !authh) + return false; + + if (httpauth_digest_response_decode(&resp, hval)) + return false; + + if (pl_strcasecmp(&resp.realm, auth->realm)) + return false; + + if (chk_nonce(&resp.nonce, NONCE_EXPIRES)) { + auth->stale = true; + return false; + } + + if (authh(&resp.username, ha1, arg)) + return false; + + if (httpauth_digest_response_auth(&resp, method, ha1)) + return false; + + return true; +} + + +/** + * Check HTTP digest authorization of an HTTP request + * + * @param msg HTTP message + * @param auth Authentication parameteres + * @param authh Authentication handler + * @param arg Authentication handler argument + * + * @return true if check is passed, otherwise false + */ +bool http_auth_check_request(const struct http_msg *msg, + struct http_auth *auth, + http_auth_h *authh, void *arg) +{ + const struct http_hdr *hdr; + + if (!msg) + return false; + + hdr = http_msg_hdr(msg, HTTP_HDR_AUTHORIZATION); + if (!hdr) + return false; + + return http_auth_check(&hdr->val, &msg->met, auth, authh, arg); +} diff --git a/src/http/mod.mk b/src/http/mod.mk new file mode 100644 index 0000000..8cfb6c9 --- /dev/null +++ b/src/http/mod.mk @@ -0,0 +1,9 @@ +# +# mod.mk +# +# Copyright (C) 2010 Creytiv.com +# + +SRCS += http/auth.c +SRCS += http/msg.c +SRCS += http/server.c diff --git a/src/http/msg.c b/src/http/msg.c new file mode 100644 index 0000000..7815dc4 --- /dev/null +++ b/src/http/msg.c @@ -0,0 +1,527 @@ +/** + * @file http/msg.c HTTP Message decode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +enum { + STARTLINE_MAX = 8192, +}; + + +static void hdr_destructor(void *arg) +{ + struct http_hdr *hdr = arg; + + list_unlink(&hdr->le); +} + + +static void destructor(void *arg) +{ + struct http_msg *msg = arg; + + list_flush(&msg->hdrl); + mem_deref(msg->mb); +} + + +static enum http_hdrid hdr_hash(const struct pl *name) +{ + if (!name->l) + return HTTP_HDR_NONE; + + switch (name->p[0]) { + + case 'x': + case 'X': + if (name->l > 1 && name->p[1] == '-') + return HTTP_HDR_NONE; + + break; + } + + return (enum http_hdrid)(hash_joaat_ci(name->p, name->l) & 0xfff); +} + + +static inline bool hdr_comma_separated(enum http_hdrid id) +{ + switch (id) { + + case HTTP_HDR_ACCEPT: + case HTTP_HDR_ACCEPT_CHARSET: + case HTTP_HDR_ACCEPT_ENCODING: + case HTTP_HDR_ACCEPT_LANGUAGE: + case HTTP_HDR_ACCEPT_RANGES: + case HTTP_HDR_ALLOW: + case HTTP_HDR_CACHE_CONTROL: + case HTTP_HDR_CONNECTION: + case HTTP_HDR_CONTENT_ENCODING: + case HTTP_HDR_CONTENT_LANGUAGE: + case HTTP_HDR_EXPECT: + case HTTP_HDR_IF_MATCH: + case HTTP_HDR_IF_NONE_MATCH: + case HTTP_HDR_PRAGMA: + case HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS: + case HTTP_HDR_SEC_WEBSOCKET_PROTOCOL: + case HTTP_HDR_SEC_WEBSOCKET_VERSION: + case HTTP_HDR_TE: + case HTTP_HDR_TRAILER: + case HTTP_HDR_TRANSFER_ENCODING: + case HTTP_HDR_UPGRADE: + case HTTP_HDR_VARY: + case HTTP_HDR_VIA: + case HTTP_HDR_WARNING: + return true; + + default: + return false; + } +} + + +static inline int hdr_add(struct http_msg *msg, const struct pl *name, + enum http_hdrid id, const char *p, ssize_t l) +{ + struct http_hdr *hdr; + + hdr = mem_zalloc(sizeof(*hdr), hdr_destructor); + if (!hdr) + return ENOMEM; + + hdr->name = *name; + hdr->val.p = p; + hdr->val.l = MAX(l, 0); + hdr->id = id; + + list_append(&msg->hdrl, &hdr->le, hdr); + + /* parse common headers */ + switch (id) { + + case HTTP_HDR_CONTENT_TYPE: + msg->ctype = hdr->val; + break; + + case HTTP_HDR_CONTENT_LENGTH: + msg->clen = pl_u32(&hdr->val); + break; + + default: + break; + } + + return 0; +} + + +/** + * Decode a HTTP message + * + * @param msgp Pointer to allocated HTTP Message + * @param mb Buffer containing HTTP Message + * @param req True for request, false for response + * + * @return 0 if success, otherwise errorcode + */ +int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req) +{ + struct pl b, s, e, name, scode; + const char *p, *cv; + struct http_msg *msg; + bool comsep, quote; + enum http_hdrid id = HTTP_HDR_NONE; + uint32_t ws, lf; + size_t l; + int err; + + if (!msgp || !mb) + return EINVAL; + + p = (const char *)mbuf_buf(mb); + l = mbuf_get_left(mb); + + if (re_regex(p, l, "[\r\n]*[^\r\n]+[\r]*[\n]1", &b, &s, NULL, &e)) + return (l > STARTLINE_MAX) ? EBADMSG : ENODATA; + + msg = mem_zalloc(sizeof(*msg), destructor); + if (!msg) + return ENOMEM; + + msg->mb = mem_ref(mb); + + if (req) { + if (re_regex(s.p, s.l, "[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+", + &msg->met, &msg->path, &msg->prm, &msg->ver) || + msg->met.p != s.p) { + err = EBADMSG; + goto out; + } + } + else { + if (re_regex(s.p, s.l, "HTTP/[0-9.]+ [0-9]+ [^]*", + &msg->ver, &scode, &msg->reason) || + msg->ver.p != s.p + 5) { + err = EBADMSG; + goto out; + } + + msg->scode = pl_u32(&scode); + } + + l -= e.p + e.l - p; + p = e.p + e.l; + + name.p = cv = NULL; + name.l = ws = lf = 0; + comsep = false; + quote = false; + + for (; l > 0; p++, l--) { + + switch (*p) { + + case ' ': + case '\t': + lf = 0; /* folding */ + ++ws; + break; + + case '\r': + ++ws; + break; + + case '\n': + ++ws; + + if (!name.p) { + ++p; --l; /* no headers */ + err = 0; + goto out; + } + + if (!lf++) + break; + + ++p; --l; /* eoh */ + + /*@fallthrough@*/ + + default: + if (lf || (*p == ',' && comsep && !quote)) { + + if (!name.l) { + err = EBADMSG; + goto out; + } + + err = hdr_add(msg, &name, id, cv ? cv : p, + cv ? p - cv - ws : 0); + if (err) + goto out; + + if (!lf) { /* comma separated */ + cv = NULL; + break; + } + + if (lf > 1) { /* eoh */ + err = 0; + goto out; + } + + comsep = false; + name.p = NULL; + cv = NULL; + lf = 0; + } + + if (!name.p) { + name.p = p; + name.l = 0; + ws = 0; + } + + if (!name.l) { + if (*p != ':') { + ws = 0; + break; + } + + name.l = MAX((int)(p - name.p - ws), 0); + if (!name.l) { + err = EBADMSG; + goto out; + } + + id = hdr_hash(&name); + comsep = hdr_comma_separated(id); + break; + } + + if (!cv) { + quote = false; + cv = p; + } + + if (*p == '"') + quote = !quote; + + ws = 0; + break; + } + } + + err = ENODATA; + + out: + if (err) + mem_deref(msg); + else { + *msgp = msg; + mb->pos = mb->end - l; + } + + return err; +} + + +/** + * Get a HTTP Header from a HTTP Message + * + * @param msg HTTP Message + * @param id HTTP Header ID + * + * @return HTTP Header if found, NULL if not found + */ +const struct http_hdr *http_msg_hdr(const struct http_msg *msg, + enum http_hdrid id) +{ + return http_msg_hdr_apply(msg, true, id, NULL, NULL); +} + + +/** + * Apply a function handler to certain HTTP Headers + * + * @param msg HTTP Message + * @param fwd True to traverse forwards, false to traverse backwards + * @param id HTTP Header ID + * @param h Function handler + * @param arg Handler argument + * + * @return HTTP Header if handler returns true, otherwise NULL + */ +const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg, + bool fwd, enum http_hdrid id, + http_hdr_h *h, void *arg) +{ + struct le *le; + + if (!msg) + return NULL; + + le = fwd ? msg->hdrl.head : msg->hdrl.tail; + + while (le) { + const struct http_hdr *hdr = le->data; + + le = fwd ? le->next : le->prev; + + if (hdr->id != id) + continue; + + if (!h || h(hdr, arg)) + return hdr; + } + + return NULL; +} + + +/** + * Get an unknown HTTP Header from a HTTP Message + * + * @param msg HTTP Message + * @param name Header name + * + * @return HTTP Header if found, NULL if not found + */ +const struct http_hdr *http_msg_xhdr(const struct http_msg *msg, + const char *name) +{ + return http_msg_xhdr_apply(msg, true, name, NULL, NULL); +} + + +/** + * Apply a function handler to certain unknown HTTP Headers + * + * @param msg HTTP Message + * @param fwd True to traverse forwards, false to traverse backwards + * @param name HTTP Header name + * @param h Function handler + * @param arg Handler argument + * + * @return HTTP Header if handler returns true, otherwise NULL + */ +const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg, + bool fwd, const char *name, + http_hdr_h *h, void *arg) +{ + struct le *le; + struct pl pl; + + if (!msg || !name) + return NULL; + + pl_set_str(&pl, name); + + le = fwd ? msg->hdrl.head : msg->hdrl.tail; + + while (le) { + const struct http_hdr *hdr = le->data; + + le = fwd ? le->next : le->prev; + + if (pl_casecmp(&hdr->name, &pl)) + continue; + + if (!h || h(hdr, arg)) + return hdr; + } + + return NULL; +} + + +static bool count_handler(const struct http_hdr *hdr, void *arg) +{ + uint32_t *n = arg; + (void)hdr; + + ++(*n); + + return false; +} + + +/** + * Count the number of HTTP Headers + * + * @param msg HTTP Message + * @param id HTTP Header ID + * + * @return Number of HTTP Headers + */ +uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id) +{ + uint32_t n = 0; + + http_msg_hdr_apply(msg, true, id, count_handler, &n); + + return n; +} + + +/** + * Count the number of unknown HTTP Headers + * + * @param msg HTTP Message + * @param name HTTP Header name + * + * @return Number of HTTP Headers + */ +uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name) +{ + uint32_t n = 0; + + http_msg_xhdr_apply(msg, true, name, count_handler, &n); + + return n; +} + + +static bool value_handler(const struct http_hdr *hdr, void *arg) +{ + return 0 == pl_strcasecmp(&hdr->val, (const char *)arg); +} + + +/** + * Check if a HTTP Header matches a certain value + * + * @param msg HTTP Message + * @param id HTTP Header ID + * @param value Header value to check + * + * @return True if value matches, false if not + */ +bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id, + const char *value) +{ + return NULL != http_msg_hdr_apply(msg, true, id, value_handler, + (void *)value); +} + + +/** + * Check if an unknown HTTP Header matches a certain value + * + * @param msg HTTP Message + * @param name HTTP Header name + * @param value Header value to check + * + * @return True if value matches, false if not + */ +bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name, + const char *value) +{ + return NULL != http_msg_xhdr_apply(msg, true, name, value_handler, + (void *)value); +} + + +/** + * Print a HTTP Message + * + * @param pf Print function for output + * @param msg HTTP Message + * + * @return 0 if success, otherwise errorcode + */ +int http_msg_print(struct re_printf *pf, const struct http_msg *msg) +{ + struct le *le; + int err; + + if (!msg) + return 0; + + if (pl_isset(&msg->met)) + err = re_hprintf(pf, "%r %r%r HTTP/%r\n", &msg->met, + &msg->path, &msg->prm, &msg->ver); + else + err = re_hprintf(pf, "HTTP/%r %u %r\n", &msg->ver, msg->scode, + &msg->reason); + + for (le=msg->hdrl.head; le; le=le->next) { + + const struct http_hdr *hdr = le->data; + + err |= re_hprintf(pf, "%r: %r (%i)\n", &hdr->name, &hdr->val, + hdr->id); + } + + return err; +} diff --git a/src/http/server.c b/src/http/server.c new file mode 100644 index 0000000..32bd821 --- /dev/null +++ b/src/http/server.c @@ -0,0 +1,504 @@ +/** + * @file http/server.c HTTP Server + * + * Copyright (C) 2011 Creytiv.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +enum { + TIMEOUT_IDLE = 600000, + TIMEOUT_INIT = 10000, + BUFSIZE_MAX = 524288, +}; + +struct http_sock { + struct list connl; + struct tcp_sock *ts; + struct tls *tls; + http_req_h *reqh; + void *arg; +}; + +struct http_conn { + struct le le; + struct tmr tmr; + struct sa peer; + struct http_sock *sock; + struct tcp_conn *tc; + struct tls_conn *sc; + struct mbuf *mb; +}; + + +static void conn_close(struct http_conn *conn); + + +static void sock_destructor(void *arg) +{ + struct http_sock *sock = arg; + struct le *le; + + for (le=sock->connl.head; le;) { + + struct http_conn *conn = le->data; + + le = le->next; + + conn_close(conn); + mem_deref(conn); + } + + mem_deref(sock->tls); + mem_deref(sock->ts); +} + + +static void conn_destructor(void *arg) +{ + struct http_conn *conn = arg; + + list_unlink(&conn->le); + tmr_cancel(&conn->tmr); + mem_deref(conn->sc); + mem_deref(conn->tc); + mem_deref(conn->mb); +} + + +static void conn_close(struct http_conn *conn) +{ + list_unlink(&conn->le); + tmr_cancel(&conn->tmr); + conn->sc = mem_deref(conn->sc); + conn->tc = mem_deref(conn->tc); + conn->sock = NULL; +} + + +static void timeout_handler(void *arg) +{ + struct http_conn *conn = arg; + + conn_close(conn); + mem_deref(conn); +} + + +static void recv_handler(struct mbuf *mb, void *arg) +{ + struct http_conn *conn = arg; + int err = 0; + + if (conn->mb) { + + const size_t len = mbuf_get_left(mb), pos = conn->mb->pos; + + if ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) { + err = EOVERFLOW; + goto out; + } + + conn->mb->pos = conn->mb->end; + + err = mbuf_write_mem(conn->mb, mbuf_buf(mb), len); + if (err) + goto out; + + conn->mb->pos = pos; + } + else { + conn->mb = mem_ref(mb); + } + + while (conn->mb) { + size_t end, pos = conn->mb->pos; + struct http_msg *msg; + + err = http_msg_decode(&msg, conn->mb, true); + if (err) { + if (err == ENODATA) { + conn->mb->pos = pos; + err = 0; + break; + } + + goto out; + } + + if (mbuf_get_left(conn->mb) < msg->clen) { + conn->mb->pos = pos; + mem_deref(msg); + break; + } + + mb = conn->mb; + + end = mb->end; + mb->end = mb->pos + msg->clen; + + if (end > mb->end) { + struct mbuf *mbn = mbuf_alloc(end - mb->end); + if (!mbn) { + mem_deref(msg); + err = ENOMEM; + goto out; + } + + (void)mbuf_write_mem(mbn, mb->buf + mb->end, + end - mb->end); + mbn->pos = 0; + + mem_deref(conn->mb); + conn->mb = mbn; + } + else { + conn->mb = mem_deref(conn->mb); + } + + if (conn->sock) + conn->sock->reqh(conn, msg, conn->sock->arg); + + mem_deref(msg); + + if (!conn->tc) { + err = ENOTCONN; + goto out; + } + + tmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn); + } + + out: + if (err) { + conn_close(conn); + mem_deref(conn); + } +} + + +static void close_handler(int err, void *arg) +{ + struct http_conn *conn = arg; + (void)err; + + conn_close(conn); + mem_deref(conn); +} + + +static void connect_handler(const struct sa *peer, void *arg) +{ + struct http_sock *sock = arg; + struct http_conn *conn; + int err; + + conn = mem_zalloc(sizeof(*conn), conn_destructor); + if (!conn) { + err = ENOMEM; + goto out; + } + + list_append(&sock->connl, &conn->le, conn); + conn->peer = *peer; + conn->sock = sock; + + err = tcp_accept(&conn->tc, sock->ts, NULL, recv_handler, + close_handler, conn); + if (err) + goto out; + +#ifdef USE_TLS + if (sock->tls) { + err = tls_start_tcp(&conn->sc, sock->tls, conn->tc, 0); + if (err) + goto out; + } +#endif + + tmr_start(&conn->tmr, TIMEOUT_INIT, timeout_handler, conn); + + out: + if (err) { + mem_deref(conn); + tcp_reject(sock->ts); + } +} + + +/** + * Create an HTTP socket + * + * @param sockp Pointer to returned HTTP Socket + * @param laddr Network address to listen on + * @param reqh Request handler + * @param arg Handler argument + * + * @return 0 if success, otherwise errorcode + */ +int http_listen(struct http_sock **sockp, const struct sa *laddr, + http_req_h *reqh, void *arg) +{ + struct http_sock *sock; + int err; + + if (!sockp || !laddr || !reqh) + return EINVAL; + + sock = mem_zalloc(sizeof(*sock), sock_destructor); + if (!sock) + return ENOMEM; + + err = tcp_listen(&sock->ts, laddr, connect_handler, sock); + if (err) + goto out; + + sock->reqh = reqh; + sock->arg = arg; + + out: + if (err) + mem_deref(sock); + else + *sockp = sock; + + return err; +} + + +/** + * Create an HTTP secure socket + * + * @param sockp Pointer to returned HTTP Socket + * @param laddr Network address to listen on + * @param cert File path of TLS certificate + * @param reqh Request handler + * @param arg Handler argument + * + * @return 0 if success, otherwise errorcode + */ +int https_listen(struct http_sock **sockp, const struct sa *laddr, + const char *cert, http_req_h *reqh, void *arg) +{ + struct http_sock *sock; + int err; + + if (!sockp || !laddr || !cert || !reqh) + return EINVAL; + + err = http_listen(&sock, laddr, reqh, arg); + if (err) + return err; + +#ifdef USE_TLS + err = tls_alloc(&sock->tls, TLS_METHOD_SSLV23, cert, NULL); +#else + err = EPROTONOSUPPORT; +#endif + if (err) + goto out; + + out: + if (err) + mem_deref(sock); + else + *sockp = sock; + + return err; +} + + +/** + * Get the peer address of an HTTP connection + * + * @param conn HTTP connection + * + * @return Peer address + */ +const struct sa *http_conn_peer(const struct http_conn *conn) +{ + return conn ? &conn->peer : NULL; +} + + +/** + * Get the TCP connection of an HTTP connection + * + * @param conn HTTP connection + * + * @return TCP connection + */ +struct tcp_conn *http_conn_tcp(struct http_conn *conn) +{ + return conn ? conn->tc : NULL; +} + + +/** + * Get the TLS connection of an HTTP connection + * + * @param conn HTTP connection + * + * @return TLS connection + */ +struct tls_conn *http_conn_tls(struct http_conn *conn) +{ + return conn ? conn->sc : NULL; +} + + +/** + * Close the HTTP connection + * + * @param conn HTTP connection + */ +void http_conn_close(struct http_conn *conn) +{ + if (!conn) + return; + + conn->sc = mem_deref(conn->sc); + conn->tc = mem_deref(conn->tc); +} + + +static int http_vreply(struct http_conn *conn, uint16_t scode, + const char *reason, const char *fmt, va_list ap) +{ + struct mbuf *mb; + int err; + + if (!conn || !scode || !reason) + return EINVAL; + + if (!conn->tc) + return ENOTCONN; + + mb = mbuf_alloc(8192); + if (!mb) + return ENOMEM; + + err = mbuf_printf(mb, "HTTP/1.1 %u %s\r\n", scode, reason); + if (fmt) + err |= mbuf_vprintf(mb, fmt, ap); + else + err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n"); + if (err) + goto out; + + mb->pos = 0; + + err = tcp_send(conn->tc, mb); + if (err) + goto out; + + out: + mem_deref(mb); + + return err; +} + + +/** + * Send an HTTP response + * + * @param conn HTTP connection + * @param scode Response status code + * @param reason Response reason phrase + * @param fmt Formatted HTTP message + * + * @return 0 if success, otherwise errorcode + */ +int http_reply(struct http_conn *conn, uint16_t scode, const char *reason, + const char *fmt, ...) +{ + va_list ap; + int err; + + va_start(ap, fmt); + err = http_vreply(conn, scode, reason, fmt, ap); + va_end(ap); + + return err; +} + + +/** + * Send an HTTP response with content formating + * + * @param conn HTTP connection + * @param scode Response status code + * @param reason Response reason phrase + * @param ctype Content type + * @param fmt Formatted HTTP content + * + * @return 0 if success, otherwise errorcode + */ +int http_creply(struct http_conn *conn, uint16_t scode, const char *reason, + const char *ctype, const char *fmt, ...) +{ + struct mbuf *mb; + va_list ap; + int err; + + if (!ctype || !fmt) + return EINVAL; + + mb = mbuf_alloc(8192); + if (!mb) + return ENOMEM; + + va_start(ap, fmt); + err = mbuf_vprintf(mb, fmt, ap); + va_end(ap); + if (err) + goto out; + + err = http_reply(conn, scode, reason, + "Content-Type: %s\r\n" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + ctype, + mb->end, + mb->buf, mb->end); + if (err) + goto out; + + out: + mem_deref(mb); + + return err; +} + + +/** + * Send an HTTP error response + * + * @param conn HTTP connection + * @param scode Response status code + * @param reason Response reason phrase + * + * @return 0 if success, otherwise errorcode + */ +int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason) +{ + return http_creply(conn, scode, reason, "text/html", + "\n" + "\n" + "%u %s\n" + "

%u %s

\n" + "\n", + scode, reason, + scode, reason); +} diff --git a/src/sip/auth.c b/src/sip/auth.c index 0570a5d..1813e15 100644 --- a/src/sip/auth.c +++ b/src/sip/auth.c @@ -1,5 +1,5 @@ /** - * @file auth.c SIP Authentication + * @file sip/auth.c SIP Authentication * * Copyright (C) 2010 Creytiv.com */