summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/http.c107
-rw-r--r--main/uri.c321
2 files changed, 428 insertions, 0 deletions
diff --git a/main/http.c b/main/http.c
index 783a34cfe..0c9395d9c 100644
--- a/main/http.c
+++ b/main/http.c
@@ -1176,6 +1176,113 @@ struct ast_http_auth *ast_http_get_auth(struct ast_variable *headers)
return NULL;
}
+int ast_http_response_status_line(const char *buf, const char *version, int code)
+{
+ int status_code;
+ size_t size = strlen(version);
+
+ if (strncmp(buf, version, size) || buf[size] != ' ') {
+ ast_log(LOG_ERROR, "HTTP version not supported - "
+ "expected %s\n", version);
+ return -1;
+ }
+
+ /* skip to status code (version + space) */
+ buf += size + 1;
+
+ if (sscanf(buf, "%d", &status_code) != 1) {
+ ast_log(LOG_ERROR, "Could not read HTTP status code - "
+ "%s\n", buf);
+ return -1;
+ }
+
+ return status_code;
+}
+
+static void remove_excess_lws(char *s)
+{
+ char *p, *res = s;
+ char *buf = ast_malloc(strlen(s) + 1);
+ char *buf_end;
+
+ if (!buf) {
+ return;
+ }
+
+ buf_end = buf;
+
+ while (*s && *(s = ast_skip_blanks(s))) {
+ p = s;
+ s = ast_skip_nonblanks(s);
+
+ if (buf_end != buf) {
+ *buf_end++ = ' ';
+ }
+
+ memcpy(buf_end, p, s - p);
+ buf_end += s - p;
+ }
+ *buf_end = '\0';
+ /* safe since buf will always be less than or equal to res */
+ strcpy(res, buf);
+ ast_free(buf);
+}
+
+int ast_http_header_parse(char *buf, char **name, char **value)
+{
+ ast_trim_blanks(buf);
+ if (ast_strlen_zero(buf)) {
+ return -1;
+ }
+
+ *value = buf;
+ *name = strsep(value, ":");
+ if (!*value) {
+ return 1;
+ }
+
+ *value = ast_skip_blanks(*value);
+ if (ast_strlen_zero(*value) || ast_strlen_zero(*name)) {
+ return 1;
+ }
+
+ remove_excess_lws(*value);
+ return 0;
+}
+
+int ast_http_header_match(const char *name, const char *expected_name,
+ const char *value, const char *expected_value)
+{
+ if (strcasecmp(name, expected_name)) {
+ /* no value to validate if names don't match */
+ return 0;
+ }
+
+ if (strcasecmp(value, expected_value)) {
+ ast_log(LOG_ERROR, "Invalid header value - expected %s "
+ "received %s", value, expected_value);
+ return -1;
+ }
+ return 1;
+}
+
+int ast_http_header_match_in(const char *name, const char *expected_name,
+ const char *value, const char *expected_value)
+{
+ if (strcasecmp(name, expected_name)) {
+ /* no value to validate if names don't match */
+ return 0;
+ }
+
+ if (!strcasestr(expected_value, value)) {
+ ast_log(LOG_ERROR, "Header '%s' - could not locate '%s' "
+ "in '%s'\n", name, value, expected_value);
+ return -1;
+
+ }
+ return 1;
+}
+
/*! Limit the number of request headers in case the sender is being ridiculous. */
#define MAX_HTTP_REQUEST_HEADERS 100
diff --git a/main/uri.c b/main/uri.c
new file mode 100644
index 000000000..6642d843b
--- /dev/null
+++ b/main/uri.c
@@ -0,0 +1,321 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Digium, Inc.
+ *
+ * Kevin Harwell <kharwell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#include "asterisk.h"
+
+#include "asterisk/astobj2.h"
+#include "asterisk/strings.h"
+#include "asterisk/uri.h"
+
+#ifdef HAVE_URIPARSER
+#include <uriparser/Uri.h>
+#endif
+
+/*! \brief Stores parsed uri information */
+struct ast_uri {
+ /*! scheme (e.g. http, https, ws, wss, etc...) */
+ char *scheme;
+ /*! username:password */
+ char *user_info;
+ /*! host name or address */
+ char *host;
+ /*! associated port */
+ char *port;
+ /*! path info following host[:port] */
+ char *path;
+ /*! query information */
+ char *query;
+ /*! storage for uri string */
+ char uri[0];
+};
+
+/*!
+ * \brief Construct a uri object with the given values.
+ *
+ * \note The size parameters [should] include room for the string terminator
+ * (strlen(<param>) + 1). For instance, if a scheme of 'http' is given
+ * then the 'scheme_size' should be equal to 5.
+ */
+static struct ast_uri *ast_uri_create_(
+ const char *scheme, unsigned int scheme_size,
+ const char *user_info, unsigned int user_info_size,
+ const char *host, unsigned int host_size,
+ const char *port, unsigned int port_size,
+ const char *path, unsigned int path_size,
+ const char *query, unsigned int query_size)
+{
+#define SET_VALUE(param, field, size) \
+ do { if (param) { \
+ ast_copy_string(p, param, size); \
+ field = p; \
+ p += size; } } while (0)
+
+ char *p;
+ struct ast_uri *res = ao2_alloc(
+ sizeof(*res) + scheme_size + user_info_size + host_size +
+ port_size + path_size + query_size, NULL);
+
+ if (!res) {
+ ast_log(LOG_ERROR, "Unable to create URI object\n");
+ return NULL;
+ }
+
+ p = res->uri;
+ SET_VALUE(scheme, res->scheme, scheme_size);
+ SET_VALUE(user_info, res->user_info, user_info_size);
+ SET_VALUE(host, res->host, host_size);
+ SET_VALUE(port, res->port, port_size);
+ SET_VALUE(path, res->path, path_size);
+ SET_VALUE(query, res->query, query_size);
+ return res;
+}
+
+struct ast_uri *ast_uri_create(const char *scheme, const char *user_info,
+ const char *host, const char *port,
+ const char *path, const char *query)
+{
+ return ast_uri_create_(
+ scheme, scheme ? strlen(scheme) + 1 : 0,
+ user_info, user_info ? strlen(user_info) + 1 : 0,
+ host, host ? strlen(host) + 1 : 0,
+ port, port ? strlen(port) + 1 : 0,
+ path, path ? strlen(path) + 1 : 0,
+ query, query ? strlen(query) + 1 : 0);
+}
+
+struct ast_uri *ast_uri_copy_replace(const struct ast_uri *uri, const char *scheme,
+ const char *user_info, const char *host,
+ const char *port, const char *path,
+ const char *query)
+{
+ return ast_uri_create(
+ scheme ? scheme : uri->scheme,
+ user_info ? user_info : uri->user_info,
+ host ? host : uri->host,
+ port ? port : uri->port,
+ path ? path : uri->path,
+ query ? query : uri->query);
+}
+
+const char *ast_uri_scheme(const struct ast_uri *uri)
+{
+ return uri->scheme;
+}
+
+const char *ast_uri_user_info(const struct ast_uri *uri)
+{
+ return uri->user_info;
+}
+
+const char *ast_uri_host(const struct ast_uri *uri)
+{
+ return uri->host;
+}
+
+const char *ast_uri_port(const struct ast_uri *uri)
+{
+ return uri->port;
+}
+
+const char *ast_uri_path(const struct ast_uri *uri)
+{
+ return uri->path;
+}
+
+const char *ast_uri_query(const struct ast_uri *uri)
+{
+ return uri->query;
+}
+
+const int ast_uri_is_secure(const struct ast_uri *uri)
+{
+ return ast_strlen_zero(uri->scheme) ? 0 :
+ *(uri->scheme + strlen(uri->scheme) - 1) == 's';
+}
+
+#ifdef HAVE_URIPARSER
+struct ast_uri *ast_uri_parse(const char *uri)
+{
+ UriParserStateA state;
+ UriUriA uria;
+ struct ast_uri *res;
+ unsigned int scheme_size, user_info_size, host_size;
+ unsigned int port_size, path_size, query_size;
+ const char *path_start, *path_end;
+
+ state.uri = &uria;
+ if (uriParseUriA(&state, uri) != URI_SUCCESS) {
+ ast_log(LOG_ERROR, "Unable to parse URI %s\n", uri);
+ uriFreeUriMembersA(&uria);
+ return NULL;
+ }
+
+ scheme_size = uria.scheme.first ?
+ uria.scheme.afterLast - uria.scheme.first + 1 : 0;
+ user_info_size = uria.userInfo.first ?
+ uria.userInfo.afterLast - uria.userInfo.first + 1 : 0;
+ host_size = uria.hostText.first ?
+ uria.hostText.afterLast - uria.hostText.first + 1 : 0;
+ port_size = uria.portText.first ?
+ uria.portText.afterLast - uria.portText.first + 1 : 0;
+
+ path_start = uria.pathHead && uria.pathHead->text.first ?
+ uria.pathHead->text.first : NULL;
+ path_end = path_start ? uria.pathTail->text.afterLast : NULL;
+ path_size = path_end ? path_end - path_start + 1 : 0;
+
+ query_size = uria.query.first ?
+ uria.query.afterLast - uria.query.first + 1 : 0;
+
+ res = ast_uri_create_(uria.scheme.first, scheme_size,
+ uria.userInfo.first, user_info_size,
+ uria.hostText.first, host_size,
+ uria.portText.first, port_size,
+ path_start, path_size,
+ uria.query.first, query_size);
+ uriFreeUriMembersA(&uria);
+ return res;
+}
+#else
+struct ast_uri *ast_uri_parse(const char *uri)
+{
+#define SET_VALUES(value) \
+ value = uri; \
+ size_##value = p - uri + 1; \
+ uri = p + 1;
+
+ const char *p, *scheme = NULL, *user_info = NULL, *host = NULL;
+ const char *port = NULL, *path = NULL, *query = NULL;
+ unsigned int size_scheme = 0, size_user_info = 0, size_host = 0;
+ unsigned int size_port = 0, size_path = 0, size_query = 0;
+
+ if ((p = strstr(uri, "://"))) {
+ scheme = uri;
+ size_scheme = p - uri + 1;
+ uri = p + 3;
+ }
+
+ if ((p = strchr(uri, '@'))) {
+ SET_VALUES(user_info);
+ }
+
+ if ((p = strchr(uri, ':'))) {
+ SET_VALUES(host);
+ }
+
+ if ((p = strchr(uri, '/'))) {
+ if (!host) {
+ SET_VALUES(host);
+ } else {
+ SET_VALUES(port);
+ }
+ }
+
+ if ((p = strchr(uri, '?'))) {
+ query = p + 1;
+ size_query = strlen(query) + 1;
+ }
+
+ if (!host) {
+ SET_VALUES(host);
+ } else if (*(uri - 1) == ':') {
+ SET_VALUES(port);
+ } else if (*(uri - 1) == '/') {
+ SET_VALUES(path);
+ }
+
+ return ast_uri_create_(scheme, size_scheme,
+ user_info, size_user_info,
+ host, size_host,
+ port, size_port,
+ path, size_path,
+ query, size_query);
+}
+#endif
+
+static struct ast_uri *uri_parse_and_default(const char *uri, const char *scheme,
+ const char *port, const char *secure_port)
+{
+ struct ast_uri *res;
+ int len = strlen(scheme);
+
+ if (!strncmp(uri, scheme, len)) {
+ res = ast_uri_parse(uri);
+ } else {
+ /* make room for <scheme>:// */
+ char *with_scheme = ast_malloc(len + strlen(uri) + 4);
+ if (!with_scheme) {
+ ast_log(LOG_ERROR, "Unable to allocate uri '%s' with "
+ "scheme '%s'", uri, scheme);
+ return NULL;
+ }
+
+ /* safe - 'with_scheme' created with size equal to len of
+ scheme plus length of uri plus space for extra characters
+ '://' and terminator */
+ sprintf(with_scheme, "%s://%s", scheme, uri);
+ res = ast_uri_parse(with_scheme);
+ ast_free(with_scheme);
+ }
+
+ if (res && ast_strlen_zero(ast_uri_port(res))) {
+ /* default the port if not given */
+ struct ast_uri *tmp = ast_uri_copy_replace(
+ res, NULL, NULL, NULL,
+ ast_uri_is_secure(res) ? secure_port : port,
+ NULL, NULL);
+ ao2_ref(res, -1);
+ res = tmp;
+ }
+ return res;
+}
+
+struct ast_uri *ast_uri_parse_http(const char *uri)
+{
+ return uri_parse_and_default(uri, "http", "80", "443");
+}
+
+struct ast_uri *ast_uri_parse_websocket(const char *uri)
+{
+ return uri_parse_and_default(uri, "ws", "80", "443");
+}
+
+char *ast_uri_make_host_with_port(const struct ast_uri *uri)
+{
+ int host_size = ast_uri_host(uri) ?
+ strlen(ast_uri_host(uri)) : 0;
+ /* if there is a port +1 for the colon */
+ int port_size = ast_uri_port(uri) ?
+ strlen(ast_uri_port(uri)) + 1 : 0;
+ char *res = ast_malloc(host_size + port_size + 1);
+
+ if (!res) {
+ return NULL;
+ }
+
+ memcpy(res, ast_uri_host(uri), host_size);
+
+ if (ast_uri_port(uri)) {
+ res[host_size] = ':';
+ memcpy(res + host_size + 1,
+ ast_uri_port(uri), port_size);
+ }
+
+ res[host_size + port_size + 1] = '\0';
+ return res;
+}