uri.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2014, Digium, Inc.
  5. *
  6. * Kevin Harwell <kharwell@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. #include "asterisk.h"
  19. #include "asterisk/astobj2.h"
  20. #include "asterisk/strings.h"
  21. #include "asterisk/uri.h"
  22. #ifdef HAVE_URIPARSER
  23. #include <uriparser/Uri.h>
  24. #endif
  25. /*! \brief Stores parsed uri information */
  26. struct ast_uri {
  27. /*! scheme (e.g. http, https, ws, wss, etc...) */
  28. char *scheme;
  29. /*! username:password */
  30. char *user_info;
  31. /*! host name or address */
  32. char *host;
  33. /*! associated port */
  34. char *port;
  35. /*! path info following host[:port] */
  36. char *path;
  37. /*! query information */
  38. char *query;
  39. /*! storage for uri string */
  40. char uri[0];
  41. };
  42. /*!
  43. * \brief Construct a uri object with the given values.
  44. *
  45. * \note The size parameters [should] include room for the string terminator
  46. * (strlen(<param>) + 1). For instance, if a scheme of 'http' is given
  47. * then the 'scheme_size' should be equal to 5.
  48. */
  49. static struct ast_uri *ast_uri_create_(
  50. const char *scheme, unsigned int scheme_size,
  51. const char *user_info, unsigned int user_info_size,
  52. const char *host, unsigned int host_size,
  53. const char *port, unsigned int port_size,
  54. const char *path, unsigned int path_size,
  55. const char *query, unsigned int query_size)
  56. {
  57. #define SET_VALUE(param, field, size) \
  58. do { if (param) { \
  59. ast_copy_string(p, param, size); \
  60. field = p; \
  61. p += size; } } while (0)
  62. char *p;
  63. struct ast_uri *res = ao2_alloc(
  64. sizeof(*res) + scheme_size + user_info_size + host_size +
  65. port_size + path_size + query_size, NULL);
  66. if (!res) {
  67. ast_log(LOG_ERROR, "Unable to create URI object\n");
  68. return NULL;
  69. }
  70. p = res->uri;
  71. SET_VALUE(scheme, res->scheme, scheme_size);
  72. SET_VALUE(user_info, res->user_info, user_info_size);
  73. SET_VALUE(host, res->host, host_size);
  74. SET_VALUE(port, res->port, port_size);
  75. SET_VALUE(path, res->path, path_size);
  76. SET_VALUE(query, res->query, query_size);
  77. return res;
  78. }
  79. struct ast_uri *ast_uri_create(const char *scheme, const char *user_info,
  80. const char *host, const char *port,
  81. const char *path, const char *query)
  82. {
  83. return ast_uri_create_(
  84. scheme, scheme ? strlen(scheme) + 1 : 0,
  85. user_info, user_info ? strlen(user_info) + 1 : 0,
  86. host, host ? strlen(host) + 1 : 0,
  87. port, port ? strlen(port) + 1 : 0,
  88. path, path ? strlen(path) + 1 : 0,
  89. query, query ? strlen(query) + 1 : 0);
  90. }
  91. struct ast_uri *ast_uri_copy_replace(const struct ast_uri *uri, const char *scheme,
  92. const char *user_info, const char *host,
  93. const char *port, const char *path,
  94. const char *query)
  95. {
  96. return ast_uri_create(
  97. scheme ? scheme : uri->scheme,
  98. user_info ? user_info : uri->user_info,
  99. host ? host : uri->host,
  100. port ? port : uri->port,
  101. path ? path : uri->path,
  102. query ? query : uri->query);
  103. }
  104. const char *ast_uri_scheme(const struct ast_uri *uri)
  105. {
  106. return uri->scheme;
  107. }
  108. const char *ast_uri_user_info(const struct ast_uri *uri)
  109. {
  110. return uri->user_info;
  111. }
  112. const char *ast_uri_host(const struct ast_uri *uri)
  113. {
  114. return uri->host;
  115. }
  116. const char *ast_uri_port(const struct ast_uri *uri)
  117. {
  118. return uri->port;
  119. }
  120. const char *ast_uri_path(const struct ast_uri *uri)
  121. {
  122. return uri->path;
  123. }
  124. const char *ast_uri_query(const struct ast_uri *uri)
  125. {
  126. return uri->query;
  127. }
  128. int ast_uri_is_secure(const struct ast_uri *uri)
  129. {
  130. return ast_strlen_zero(uri->scheme) ? 0 :
  131. *(uri->scheme + strlen(uri->scheme) - 1) == 's';
  132. }
  133. #ifdef HAVE_URIPARSER
  134. struct ast_uri *ast_uri_parse(const char *uri)
  135. {
  136. UriParserStateA state;
  137. UriUriA uria;
  138. struct ast_uri *res;
  139. unsigned int scheme_size, user_info_size, host_size;
  140. unsigned int port_size, path_size, query_size;
  141. const char *path_start, *path_end;
  142. state.uri = &uria;
  143. if (uriParseUriA(&state, uri) != URI_SUCCESS) {
  144. ast_log(LOG_ERROR, "Unable to parse URI %s\n", uri);
  145. uriFreeUriMembersA(&uria);
  146. return NULL;
  147. }
  148. scheme_size = uria.scheme.first ?
  149. uria.scheme.afterLast - uria.scheme.first + 1 : 0;
  150. user_info_size = uria.userInfo.first ?
  151. uria.userInfo.afterLast - uria.userInfo.first + 1 : 0;
  152. host_size = uria.hostText.first ?
  153. uria.hostText.afterLast - uria.hostText.first + 1 : 0;
  154. port_size = uria.portText.first ?
  155. uria.portText.afterLast - uria.portText.first + 1 : 0;
  156. path_start = uria.pathHead && uria.pathHead->text.first ?
  157. uria.pathHead->text.first : NULL;
  158. path_end = path_start ? uria.pathTail->text.afterLast : NULL;
  159. path_size = path_end ? path_end - path_start + 1 : 0;
  160. query_size = uria.query.first ?
  161. uria.query.afterLast - uria.query.first + 1 : 0;
  162. res = ast_uri_create_(uria.scheme.first, scheme_size,
  163. uria.userInfo.first, user_info_size,
  164. uria.hostText.first, host_size,
  165. uria.portText.first, port_size,
  166. path_start, path_size,
  167. uria.query.first, query_size);
  168. uriFreeUriMembersA(&uria);
  169. return res;
  170. }
  171. #else
  172. struct ast_uri *ast_uri_parse(const char *uri)
  173. {
  174. #define SET_VALUES(value) \
  175. value = uri; \
  176. size_##value = p - uri + 1; \
  177. uri = p + 1;
  178. const char *p, *scheme = NULL, *user_info = NULL, *host = NULL;
  179. const char *port = NULL, *path = NULL, *query = NULL;
  180. unsigned int size_scheme = 0, size_user_info = 0, size_host = 0;
  181. unsigned int size_port = 0, size_path = 0, size_query = 0;
  182. if ((p = strstr(uri, "://"))) {
  183. scheme = uri;
  184. size_scheme = p - uri + 1;
  185. uri = p + 3;
  186. }
  187. if ((p = strchr(uri, '@'))) {
  188. SET_VALUES(user_info);
  189. }
  190. if ((p = strchr(uri, ':'))) {
  191. SET_VALUES(host);
  192. }
  193. if ((p = strchr(uri, '/'))) {
  194. if (!host) {
  195. SET_VALUES(host);
  196. } else {
  197. SET_VALUES(port);
  198. }
  199. }
  200. if ((p = strchr(uri, '?'))) {
  201. query = p + 1;
  202. size_query = strlen(query) + 1;
  203. } else {
  204. p = uri + strlen(uri);
  205. }
  206. if (!host) {
  207. SET_VALUES(host);
  208. } else if (*(uri - 1) == ':') {
  209. SET_VALUES(port);
  210. } else if (*(uri - 1) == '/') {
  211. SET_VALUES(path);
  212. }
  213. return ast_uri_create_(scheme, size_scheme,
  214. user_info, size_user_info,
  215. host, size_host,
  216. port, size_port,
  217. path, size_path,
  218. query, size_query);
  219. }
  220. #endif
  221. static struct ast_uri *uri_parse_and_default(const char *uri, const char *scheme,
  222. const char *port, const char *secure_port)
  223. {
  224. struct ast_uri *res;
  225. int len = strlen(scheme);
  226. if (!strncmp(uri, scheme, len)) {
  227. res = ast_uri_parse(uri);
  228. } else {
  229. /* make room for <scheme>:// */
  230. char *with_scheme = ast_malloc(len + strlen(uri) + 4);
  231. if (!with_scheme) {
  232. ast_log(LOG_ERROR, "Unable to allocate uri '%s' with "
  233. "scheme '%s'", uri, scheme);
  234. return NULL;
  235. }
  236. /* safe - 'with_scheme' created with size equal to len of
  237. scheme plus length of uri plus space for extra characters
  238. '://' and terminator */
  239. sprintf(with_scheme, "%s://%s", scheme, uri);
  240. res = ast_uri_parse(with_scheme);
  241. ast_free(with_scheme);
  242. }
  243. if (res && ast_strlen_zero(ast_uri_port(res))) {
  244. /* default the port if not given */
  245. struct ast_uri *tmp = ast_uri_copy_replace(
  246. res, NULL, NULL, NULL,
  247. ast_uri_is_secure(res) ? secure_port : port,
  248. NULL, NULL);
  249. ao2_ref(res, -1);
  250. res = tmp;
  251. }
  252. return res;
  253. }
  254. struct ast_uri *ast_uri_parse_http(const char *uri)
  255. {
  256. return uri_parse_and_default(uri, "http", "80", "443");
  257. }
  258. struct ast_uri *ast_uri_parse_websocket(const char *uri)
  259. {
  260. return uri_parse_and_default(uri, "ws", "80", "443");
  261. }
  262. char *ast_uri_make_host_with_port(const struct ast_uri *uri)
  263. {
  264. int host_size = ast_uri_host(uri) ?
  265. strlen(ast_uri_host(uri)) : 0;
  266. /* if there is a port +1 for the colon */
  267. int port_size = ast_uri_port(uri) ?
  268. strlen(ast_uri_port(uri)) + 1 : 0;
  269. char *res = ast_malloc(host_size + port_size + 1);
  270. if (!res) {
  271. return NULL;
  272. }
  273. memcpy(res, ast_uri_host(uri), host_size);
  274. if (ast_uri_port(uri)) {
  275. res[host_size] = ':';
  276. memcpy(res + host_size + 1,
  277. ast_uri_port(uri), port_size - 1);
  278. }
  279. res[host_size + port_size] = '\0';
  280. return res;
  281. }