res_ari.c 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2012 - 2013, Digium, Inc.
  5. *
  6. * David M. Lee, II <dlee@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. /*! \file
  19. *
  20. * \brief HTTP binding for the Stasis API
  21. * \author David M. Lee, II <dlee@digium.com>
  22. *
  23. * The API itself is documented using <a
  24. * href="https://developers.helloreverb.com/swagger/">Swagger</a>, a lightweight
  25. * mechanism for documenting RESTful API's using JSON. This allows us to use <a
  26. * href="https://github.com/wordnik/swagger-ui">swagger-ui</a> to provide
  27. * executable documentation for the API, generate client bindings in different
  28. * <a href="https://github.com/asterisk/asterisk_rest_libraries">languages</a>,
  29. * and generate a lot of the boilerplate code for implementing the RESTful
  30. * bindings. The API docs live in the \c rest-api/ directory.
  31. *
  32. * The RESTful bindings are generated from the Swagger API docs using a set of
  33. * <a href="http://mustache.github.io/mustache.5.html">Mustache</a> templates.
  34. * The code generator is written in Python, and uses the Python implementation
  35. * <a href="https://github.com/defunkt/pystache">pystache</a>. Pystache has no
  36. * dependencies, and be installed easily using \c pip. Code generation code
  37. * lives in \c rest-api-templates/.
  38. *
  39. * The generated code reduces a lot of boilerplate when it comes to handling
  40. * HTTP requests. It also helps us have greater consistency in the REST API.
  41. *
  42. * The structure of the generated code is:
  43. *
  44. * - res/ari/resource_{resource}.h
  45. * - For each operation in the resouce, a generated argument structure
  46. * (holding the parsed arguments from the request) and function
  47. * declarations (to implement in res/ari/resource_{resource}.c)
  48. * - res_ari_{resource}.c
  49. * - A set of \ref stasis_rest_callback functions, which glue the two
  50. * together. They parse out path variables and request parameters to
  51. * populate a specific \c *_args which is passed to the specific request
  52. * handler (in res/ari/resource_{resource}.c)
  53. * - A tree of \ref stasis_rest_handlers for routing requests to its
  54. * \ref stasis_rest_callback
  55. *
  56. * The basic flow of an HTTP request is:
  57. *
  58. * - ast_ari_callback()
  59. * 1. Initial request validation
  60. * 2. Routes as either a doc request (ast_ari_get_docs) or API
  61. * request (ast_ari_invoke)
  62. * - ast_ari_invoke()
  63. * 1. Further request validation
  64. * 2. Routes the request through the tree of generated
  65. * \ref stasis_rest_handlers.
  66. * 3. Dispatch to the generated callback
  67. * - \c ast_ari_*_cb
  68. * 1. Populate \c *_args struct with path and get params
  69. * 2. Invoke the request handler
  70. * 3. Validates and sends response
  71. */
  72. /*** MODULEINFO
  73. <depend type="module">res_http_websocket</depend>
  74. <depend type="module">res_stasis</depend>
  75. <support_level>core</support_level>
  76. ***/
  77. /*** DOCUMENTATION
  78. <configInfo name="res_ari" language="en_US">
  79. <synopsis>HTTP binding for the Stasis API</synopsis>
  80. <configFile name="ari.conf">
  81. <configObject name="general">
  82. <synopsis>General configuration settings</synopsis>
  83. <configOption name="enabled">
  84. <synopsis>Enable/disable the ARI module</synopsis>
  85. <description>
  86. <para>This option enables or disables the ARI module.</para>
  87. <note>
  88. <para>ARI uses Asterisk's HTTP server, which must also be enabled in <filename>http.conf</filename>.</para>
  89. </note>
  90. </description>
  91. <see-also>
  92. <ref type="filename">http.conf</ref>
  93. <ref type="link">https://wiki.asterisk.org/wiki/display/AST/Asterisk+Builtin+mini-HTTP+Server</ref>
  94. </see-also>
  95. </configOption>
  96. <configOption name="websocket_write_timeout">
  97. <synopsis>The timeout (in milliseconds) to set on WebSocket connections.</synopsis>
  98. <description>
  99. <para>If a websocket connection accepts input slowly, the timeout
  100. for writes to it can be increased to keep it from being disconnected.
  101. Value is in milliseconds; default is 100 ms.</para>
  102. </description>
  103. </configOption>
  104. <configOption name="pretty">
  105. <synopsis>Responses from ARI are formatted to be human readable</synopsis>
  106. </configOption>
  107. <configOption name="auth_realm">
  108. <synopsis>Realm to use for authentication. Defaults to Asterisk REST Interface.</synopsis>
  109. </configOption>
  110. <configOption name="allowed_origins">
  111. <synopsis>Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins.</synopsis>
  112. </configOption>
  113. </configObject>
  114. <configObject name="user">
  115. <synopsis>Per-user configuration settings</synopsis>
  116. <configOption name="type">
  117. <synopsis>Define this configuration section as a user.</synopsis>
  118. <description>
  119. <enumlist>
  120. <enum name="user"><para>Configure this section as a <replaceable>user</replaceable></para></enum>
  121. </enumlist>
  122. </description>
  123. </configOption>
  124. <configOption name="read_only">
  125. <synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
  126. </configOption>
  127. <configOption name="password">
  128. <synopsis>Crypted or plaintext password (see password_format)</synopsis>
  129. </configOption>
  130. <configOption name="password_format">
  131. <synopsis>password_format may be set to plain (the default) or crypt. When set to crypt, crypt(3) is used to validate the password. A crypted password can be generated using mkpasswd -m sha-512. When set to plain, the password is in plaintext</synopsis>
  132. </configOption>
  133. </configObject>
  134. </configFile>
  135. </configInfo>
  136. ***/
  137. #include "asterisk.h"
  138. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  139. #include "ari/internal.h"
  140. #include "asterisk/ari.h"
  141. #include "asterisk/astobj2.h"
  142. #include "asterisk/module.h"
  143. #include "asterisk/paths.h"
  144. #include "asterisk/stasis_app.h"
  145. #include <string.h>
  146. #include <sys/stat.h>
  147. #include <unistd.h>
  148. /*! \brief Helper function to check if module is enabled. */
  149. static int is_enabled(void)
  150. {
  151. RAII_VAR(struct ast_ari_conf *, cfg, ast_ari_config_get(), ao2_cleanup);
  152. return cfg && cfg->general && cfg->general->enabled;
  153. }
  154. /*! Lock for \ref root_handler */
  155. static ast_mutex_t root_handler_lock;
  156. /*! Handler for root RESTful resource. */
  157. static struct stasis_rest_handlers *root_handler;
  158. /*! Pre-defined message for allocation failures. */
  159. static struct ast_json *oom_json;
  160. struct ast_json *ast_ari_oom_json(void)
  161. {
  162. return oom_json;
  163. }
  164. int ast_ari_add_handler(struct stasis_rest_handlers *handler)
  165. {
  166. RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
  167. size_t old_size, new_size;
  168. SCOPED_MUTEX(lock, &root_handler_lock);
  169. old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
  170. new_size = old_size + sizeof(handler);
  171. new_handler = ao2_alloc(new_size, NULL);
  172. if (!new_handler) {
  173. return -1;
  174. }
  175. memcpy(new_handler, root_handler, old_size);
  176. new_handler->children[new_handler->num_children++] = handler;
  177. ao2_cleanup(root_handler);
  178. ao2_ref(new_handler, +1);
  179. root_handler = new_handler;
  180. ast_module_ref(ast_module_info->self);
  181. return 0;
  182. }
  183. int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
  184. {
  185. struct stasis_rest_handlers *new_handler;
  186. size_t size;
  187. size_t i;
  188. size_t j;
  189. ast_assert(root_handler != NULL);
  190. ast_mutex_lock(&root_handler_lock);
  191. size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
  192. new_handler = ao2_alloc(size, NULL);
  193. if (!new_handler) {
  194. ast_mutex_unlock(&root_handler_lock);
  195. return -1;
  196. }
  197. /* Create replacement root_handler less the handler to remove. */
  198. memcpy(new_handler, root_handler, sizeof(*new_handler));
  199. for (i = 0, j = 0; i < root_handler->num_children; ++i) {
  200. if (root_handler->children[i] == handler) {
  201. ast_module_unref(ast_module_info->self);
  202. continue;
  203. }
  204. new_handler->children[j++] = root_handler->children[i];
  205. }
  206. new_handler->num_children = j;
  207. /* Replace the old root_handler with the new. */
  208. ao2_cleanup(root_handler);
  209. root_handler = new_handler;
  210. ast_mutex_unlock(&root_handler_lock);
  211. return 0;
  212. }
  213. static struct stasis_rest_handlers *get_root_handler(void)
  214. {
  215. SCOPED_MUTEX(lock, &root_handler_lock);
  216. ao2_ref(root_handler, +1);
  217. return root_handler;
  218. }
  219. static struct stasis_rest_handlers *root_handler_create(void)
  220. {
  221. RAII_VAR(struct stasis_rest_handlers *, handler, NULL, ao2_cleanup);
  222. handler = ao2_alloc(sizeof(*handler), NULL);
  223. if (!handler) {
  224. return NULL;
  225. }
  226. handler->path_segment = "ari";
  227. ao2_ref(handler, +1);
  228. return handler;
  229. }
  230. void ast_ari_response_error(struct ast_ari_response *response,
  231. int response_code,
  232. const char *response_text,
  233. const char *message_fmt, ...)
  234. {
  235. RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
  236. va_list ap;
  237. va_start(ap, message_fmt);
  238. message = ast_json_vstringf(message_fmt, ap);
  239. va_end(ap);
  240. response->message = ast_json_pack("{s: o}",
  241. "message", ast_json_ref(message));
  242. response->response_code = response_code;
  243. response->response_text = response_text;
  244. }
  245. void ast_ari_response_ok(struct ast_ari_response *response,
  246. struct ast_json *message)
  247. {
  248. response->message = message;
  249. response->response_code = 200;
  250. response->response_text = "OK";
  251. }
  252. void ast_ari_response_no_content(struct ast_ari_response *response)
  253. {
  254. response->message = ast_json_null();
  255. response->response_code = 204;
  256. response->response_text = "No Content";
  257. }
  258. void ast_ari_response_accepted(struct ast_ari_response *response)
  259. {
  260. response->message = ast_json_null();
  261. response->response_code = 202;
  262. response->response_text = "Accepted";
  263. }
  264. void ast_ari_response_alloc_failed(struct ast_ari_response *response)
  265. {
  266. response->message = ast_json_ref(oom_json);
  267. response->response_code = 500;
  268. response->response_text = "Internal Server Error";
  269. }
  270. void ast_ari_response_created(struct ast_ari_response *response,
  271. const char *url, struct ast_json *message)
  272. {
  273. RAII_VAR(struct stasis_rest_handlers *, root, get_root_handler(), ao2_cleanup);
  274. response->message = message;
  275. response->response_code = 201;
  276. response->response_text = "Created";
  277. ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
  278. }
  279. static void add_allow_header(struct stasis_rest_handlers *handler,
  280. struct ast_ari_response *response)
  281. {
  282. enum ast_http_method m;
  283. ast_str_append(&response->headers, 0,
  284. "Allow: OPTIONS");
  285. for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
  286. if (handler->callbacks[m] != NULL) {
  287. ast_str_append(&response->headers, 0,
  288. ",%s", ast_get_http_method(m));
  289. }
  290. }
  291. ast_str_append(&response->headers, 0, "\r\n");
  292. }
  293. static int origin_allowed(const char *origin)
  294. {
  295. RAII_VAR(struct ast_ari_conf *, cfg, ast_ari_config_get(), ao2_cleanup);
  296. char *allowed = ast_strdupa(cfg->general->allowed_origins);
  297. char *current;
  298. while ((current = strsep(&allowed, ","))) {
  299. if (!strcmp(current, "*")) {
  300. return 1;
  301. }
  302. if (!strcmp(current, origin)) {
  303. return 1;
  304. }
  305. }
  306. return 0;
  307. }
  308. #define ACR_METHOD "Access-Control-Request-Method"
  309. #define ACR_HEADERS "Access-Control-Request-Headers"
  310. #define ACA_METHODS "Access-Control-Allow-Methods"
  311. #define ACA_HEADERS "Access-Control-Allow-Headers"
  312. /*!
  313. * \brief Handle OPTIONS request, mainly for CORS preflight requests.
  314. *
  315. * Some browsers will send this prior to non-simple methods (i.e. DELETE).
  316. * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
  317. */
  318. static void handle_options(struct stasis_rest_handlers *handler,
  319. struct ast_variable *headers,
  320. struct ast_ari_response *response)
  321. {
  322. struct ast_variable *header;
  323. char const *acr_method = NULL;
  324. char const *acr_headers = NULL;
  325. char const *origin = NULL;
  326. RAII_VAR(struct ast_str *, allow, NULL, ast_free);
  327. enum ast_http_method m;
  328. int allowed = 0;
  329. /* Regular OPTIONS response */
  330. add_allow_header(handler, response);
  331. ast_ari_response_no_content(response);
  332. /* Parse CORS headers */
  333. for (header = headers; header != NULL; header = header->next) {
  334. if (strcmp(ACR_METHOD, header->name) == 0) {
  335. acr_method = header->value;
  336. } else if (strcmp(ACR_HEADERS, header->name) == 0) {
  337. acr_headers = header->value;
  338. } else if (strcmp("Origin", header->name) == 0) {
  339. origin = header->value;
  340. }
  341. }
  342. /* CORS 6.2, #1 - "If the Origin header is not present terminate this
  343. * set of steps."
  344. */
  345. if (origin == NULL) {
  346. return;
  347. }
  348. /* CORS 6.2, #2 - "If the value of the Origin header is not a
  349. * case-sensitive match for any of the values in list of origins do not
  350. * set any additional headers and terminate this set of steps.
  351. *
  352. * Always matching is acceptable since the list of origins can be
  353. * unbounded.
  354. *
  355. * The Origin header can only contain a single origin as the user agent
  356. * will not follow redirects."
  357. */
  358. if (!origin_allowed(origin)) {
  359. ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
  360. return;
  361. }
  362. /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
  363. * or if parsing failed, do not set any additional headers and terminate
  364. * this set of steps."
  365. */
  366. if (acr_method == NULL) {
  367. return;
  368. }
  369. /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
  370. * headers let header field-names be the empty list."
  371. */
  372. if (acr_headers == NULL) {
  373. acr_headers = "";
  374. }
  375. /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
  376. * the values in list of methods do not set any additional headers and
  377. * terminate this set of steps."
  378. */
  379. allow = ast_str_create(20);
  380. if (!allow) {
  381. ast_ari_response_alloc_failed(response);
  382. return;
  383. }
  384. /* Go ahead and build the ACA_METHODS header at the same time */
  385. for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
  386. if (handler->callbacks[m] != NULL) {
  387. char const *m_str = ast_get_http_method(m);
  388. if (strcmp(m_str, acr_method) == 0) {
  389. allowed = 1;
  390. }
  391. ast_str_append(&allow, 0, ",%s", m_str);
  392. }
  393. }
  394. if (!allowed) {
  395. return;
  396. }
  397. /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
  398. * case-insensitive match for any of the values in list of headers do
  399. * not set any additional headers and terminate this set of steps.
  400. *
  401. * Note: Always matching is acceptable since the list of headers can be
  402. * unbounded."
  403. */
  404. /* CORS 6.2 #7 - "If the resource supports credentials add a single
  405. * Access-Control-Allow-Origin header, with the value of the Origin
  406. * header as value, and add a single Access-Control-Allow-Credentials
  407. * header with the case-sensitive string "true" as value."
  408. *
  409. * Added by process_cors_request() earlier in the request.
  410. */
  411. /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
  412. * header..."
  413. */
  414. /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
  415. * consisting of (a subset of) the list of methods."
  416. */
  417. ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
  418. ACA_METHODS, ast_str_buffer(allow));
  419. /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
  420. * consisting of (a subset of) the list of headers.
  421. *
  422. * Since the list of headers can be unbounded simply returning headers
  423. * can be enough."
  424. */
  425. if (!ast_strlen_zero(acr_headers)) {
  426. ast_str_append(&response->headers, 0, "%s: %s\r\n",
  427. ACA_HEADERS, acr_headers);
  428. }
  429. }
  430. void ast_ari_invoke(struct ast_tcptls_session_instance *ser,
  431. const char *uri, enum ast_http_method method,
  432. struct ast_variable *get_params, struct ast_variable *headers,
  433. struct ast_json *body, struct ast_ari_response *response)
  434. {
  435. RAII_VAR(struct stasis_rest_handlers *, root, NULL, ao2_cleanup);
  436. struct stasis_rest_handlers *handler;
  437. RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
  438. char *path = ast_strdupa(uri);
  439. char *path_segment;
  440. stasis_rest_callback callback;
  441. root = handler = get_root_handler();
  442. ast_assert(root != NULL);
  443. while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
  444. struct stasis_rest_handlers *found_handler = NULL;
  445. int i;
  446. ast_uri_decode(path_segment, ast_uri_http_legacy);
  447. ast_debug(3, "Finding handler for %s\n", path_segment);
  448. for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
  449. struct stasis_rest_handlers *child = handler->children[i];
  450. ast_debug(3, " Checking %s\n", child->path_segment);
  451. if (child->is_wildcard) {
  452. /* Record the path variable */
  453. struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
  454. path_var->next = path_vars;
  455. path_vars = path_var;
  456. found_handler = child;
  457. } else if (strcmp(child->path_segment, path_segment) == 0) {
  458. found_handler = child;
  459. }
  460. }
  461. if (found_handler == NULL) {
  462. /* resource not found */
  463. ast_debug(3, " Handler not found\n");
  464. ast_ari_response_error(
  465. response, 404, "Not Found",
  466. "Resource not found");
  467. return;
  468. } else {
  469. ast_debug(3, " Got it!\n");
  470. handler = found_handler;
  471. }
  472. }
  473. ast_assert(handler != NULL);
  474. if (method == AST_HTTP_OPTIONS) {
  475. handle_options(handler, headers, response);
  476. return;
  477. }
  478. if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
  479. add_allow_header(handler, response);
  480. ast_ari_response_error(
  481. response, 405, "Method Not Allowed",
  482. "Invalid method");
  483. return;
  484. }
  485. if (handler->ws_server && method == AST_HTTP_GET) {
  486. /* WebSocket! */
  487. ari_handle_websocket(handler->ws_server, ser, uri, method,
  488. get_params, headers);
  489. /* Since the WebSocket code handles the connection, we shouldn't
  490. * do anything else; setting no_response */
  491. response->no_response = 1;
  492. return;
  493. }
  494. callback = handler->callbacks[method];
  495. if (callback == NULL) {
  496. add_allow_header(handler, response);
  497. ast_ari_response_error(
  498. response, 405, "Method Not Allowed",
  499. "Invalid method");
  500. return;
  501. }
  502. callback(ser, get_params, path_vars, headers, body, response);
  503. if (response->message == NULL && response->response_code == 0) {
  504. /* Really should not happen */
  505. ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
  506. ast_get_http_method(method), uri);
  507. ast_ari_response_error(
  508. response, 501, "Not Implemented",
  509. "Method not implemented");
  510. }
  511. }
  512. void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
  513. struct ast_ari_response *response)
  514. {
  515. RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
  516. RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
  517. RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
  518. struct ast_json *obj = NULL;
  519. struct ast_variable *host = NULL;
  520. struct ast_json_error error = {};
  521. struct stat file_stat;
  522. ast_debug(3, "%s(%s)\n", __func__, uri);
  523. absolute_path_builder = ast_str_create(80);
  524. if (absolute_path_builder == NULL) {
  525. ast_ari_response_alloc_failed(response);
  526. return;
  527. }
  528. /* absolute path to the rest-api directory */
  529. ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
  530. ast_str_append(&absolute_path_builder, 0, "/rest-api/");
  531. absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
  532. if (absolute_api_dirname == NULL) {
  533. ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
  534. ast_ari_response_error(
  535. response, 500, "Internal Server Error",
  536. "Cannot find rest-api directory");
  537. return;
  538. }
  539. /* absolute path to the requested file */
  540. ast_str_append(&absolute_path_builder, 0, "%s", uri);
  541. absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
  542. if (absolute_filename == NULL) {
  543. switch (errno) {
  544. case ENAMETOOLONG:
  545. case ENOENT:
  546. case ENOTDIR:
  547. ast_ari_response_error(
  548. response, 404, "Not Found",
  549. "Resource not found");
  550. break;
  551. case EACCES:
  552. ast_ari_response_error(
  553. response, 403, "Forbidden",
  554. "Permission denied");
  555. break;
  556. default:
  557. ast_log(LOG_ERROR,
  558. "Error determining real path for uri '%s': %s\n",
  559. uri, strerror(errno));
  560. ast_ari_response_error(
  561. response, 500, "Internal Server Error",
  562. "Cannot find file");
  563. break;
  564. }
  565. return;
  566. }
  567. if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
  568. /* HACKERZ! */
  569. ast_log(LOG_ERROR,
  570. "Invalid attempt to access '%s' (not in %s)\n",
  571. absolute_filename, absolute_api_dirname);
  572. ast_ari_response_error(
  573. response, 404, "Not Found",
  574. "Resource not found");
  575. return;
  576. }
  577. if (stat(absolute_filename, &file_stat) == 0) {
  578. if (!(file_stat.st_mode & S_IFREG)) {
  579. /* Not a file */
  580. ast_ari_response_error(
  581. response, 403, "Forbidden",
  582. "Invalid access");
  583. return;
  584. }
  585. } else {
  586. /* Does not exist */
  587. ast_ari_response_error(
  588. response, 404, "Not Found",
  589. "Resource not found");
  590. return;
  591. }
  592. /* Load resource object from file */
  593. obj = ast_json_load_new_file(absolute_filename, &error);
  594. if (obj == NULL) {
  595. ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
  596. error.source, error.line, error.column, error.text);
  597. ast_ari_response_error(
  598. response, 500, "Internal Server Error",
  599. "Yikes! Cannot parse resource");
  600. return;
  601. }
  602. /* Update the basePath properly */
  603. if (ast_json_object_get(obj, "basePath") != NULL) {
  604. for (host = headers; host; host = host->next) {
  605. if (strcasecmp(host->name, "Host") == 0) {
  606. break;
  607. }
  608. }
  609. if (host != NULL) {
  610. if (prefix != NULL && strlen(prefix) > 0) {
  611. ast_json_object_set(
  612. obj, "basePath",
  613. ast_json_stringf("http://%s%s/ari", host->value,prefix));
  614. } else {
  615. ast_json_object_set(
  616. obj, "basePath",
  617. ast_json_stringf("http://%s/ari", host->value));
  618. }
  619. } else {
  620. /* Without the host, we don't have the basePath */
  621. ast_json_object_del(obj, "basePath");
  622. }
  623. }
  624. ast_ari_response_ok(response, obj);
  625. }
  626. static void remove_trailing_slash(const char *uri,
  627. struct ast_ari_response *response)
  628. {
  629. char *slashless = ast_strdupa(uri);
  630. slashless[strlen(slashless) - 1] = '\0';
  631. /* While it's tempting to redirect the client to the slashless URL,
  632. * that is problematic. A 302 Found is the most appropriate response,
  633. * but most clients issue a GET on the location you give them,
  634. * regardless of the method of the original request.
  635. *
  636. * While there are some ways around this, it gets into a lot of client
  637. * specific behavior and corner cases in the HTTP standard. There's also
  638. * very little practical benefit of redirecting; only GET and HEAD can
  639. * be redirected automagically; all other requests "MUST NOT
  640. * automatically redirect the request unless it can be confirmed by the
  641. * user, since this might change the conditions under which the request
  642. * was issued."
  643. *
  644. * Given all of that, a 404 with a nice message telling them what to do
  645. * is probably our best bet.
  646. */
  647. ast_ari_response_error(response, 404, "Not Found",
  648. "ARI URLs do not end with a slash. Try /ari/%s", slashless);
  649. }
  650. /*!
  651. * \brief Handle CORS headers for simple requests.
  652. *
  653. * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
  654. */
  655. static void process_cors_request(struct ast_variable *headers,
  656. struct ast_ari_response *response)
  657. {
  658. char const *origin = NULL;
  659. struct ast_variable *header;
  660. /* Parse CORS headers */
  661. for (header = headers; header != NULL; header = header->next) {
  662. if (strcmp("Origin", header->name) == 0) {
  663. origin = header->value;
  664. }
  665. }
  666. /* CORS 6.1, #1 - "If the Origin header is not present terminate this
  667. * set of steps."
  668. */
  669. if (origin == NULL) {
  670. return;
  671. }
  672. /* CORS 6.1, #2 - "If the value of the Origin header is not a
  673. * case-sensitive match for any of the values in list of origins, do not
  674. * set any additional headers and terminate this set of steps.
  675. *
  676. * Note: Always matching is acceptable since the list of origins can be
  677. * unbounded."
  678. */
  679. if (!origin_allowed(origin)) {
  680. ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
  681. return;
  682. }
  683. /* CORS 6.1, #3 - "If the resource supports credentials add a single
  684. * Access-Control-Allow-Origin header, with the value of the Origin
  685. * header as value, and add a single Access-Control-Allow-Credentials
  686. * header with the case-sensitive string "true" as value.
  687. *
  688. * Otherwise, add a single Access-Control-Allow-Origin header, with
  689. * either the value of the Origin header or the string "*" as value."
  690. */
  691. ast_str_append(&response->headers, 0,
  692. "Access-Control-Allow-Origin: %s\r\n", origin);
  693. ast_str_append(&response->headers, 0,
  694. "Access-Control-Allow-Credentials: true\r\n");
  695. /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
  696. * or more Access-Control-Expose-Headers headers, with as values the
  697. * header field names given in the list of exposed headers."
  698. *
  699. * No exposed headers; skipping
  700. */
  701. }
  702. enum ast_json_encoding_format ast_ari_json_format(void)
  703. {
  704. RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
  705. cfg = ast_ari_config_get();
  706. return cfg->general->format;
  707. }
  708. /*!
  709. * \brief Authenticate a <code>?api_key=userid:password</code>
  710. *
  711. * \param api_key API key query parameter
  712. * \return User object for the authenticated user.
  713. * \return \c NULL if authentication failed.
  714. */
  715. static struct ast_ari_conf_user *authenticate_api_key(const char *api_key)
  716. {
  717. RAII_VAR(char *, copy, NULL, ast_free);
  718. char *username;
  719. char *password;
  720. password = copy = ast_strdup(api_key);
  721. if (!copy) {
  722. return NULL;
  723. }
  724. username = strsep(&password, ":");
  725. if (!password) {
  726. ast_log(LOG_WARNING, "Invalid api_key\n");
  727. return NULL;
  728. }
  729. return ast_ari_config_validate_user(username, password);
  730. }
  731. /*!
  732. * \brief Authenticate an HTTP request.
  733. *
  734. * \param get_params GET parameters of the request.
  735. * \param header HTTP headers.
  736. * \return User object for the authenticated user.
  737. * \return \c NULL if authentication failed.
  738. */
  739. static struct ast_ari_conf_user *authenticate_user(struct ast_variable *get_params,
  740. struct ast_variable *headers)
  741. {
  742. RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
  743. struct ast_variable *v;
  744. /* HTTP Basic authentication */
  745. http_auth = ast_http_get_auth(headers);
  746. if (http_auth) {
  747. return ast_ari_config_validate_user(http_auth->userid,
  748. http_auth->password);
  749. }
  750. /* ?api_key authentication */
  751. for (v = get_params; v; v = v->next) {
  752. if (strcasecmp("api_key", v->name) == 0) {
  753. return authenticate_api_key(v->value);
  754. }
  755. }
  756. return NULL;
  757. }
  758. /*!
  759. * \internal
  760. * \brief ARI HTTP handler.
  761. *
  762. * This handler takes the HTTP request and turns it into the appropriate
  763. * RESTful request (conversion to JSON, routing, etc.)
  764. *
  765. * \param ser TCP session.
  766. * \param urih URI handler.
  767. * \param uri URI requested.
  768. * \param method HTTP method.
  769. * \param get_params HTTP \c GET params.
  770. * \param headers HTTP headers.
  771. */
  772. static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
  773. const struct ast_http_uri *urih,
  774. const char *uri,
  775. enum ast_http_method method,
  776. struct ast_variable *get_params,
  777. struct ast_variable *headers)
  778. {
  779. RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
  780. RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
  781. RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
  782. struct ast_ari_response response = {};
  783. RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
  784. struct ast_variable *var;
  785. const char *app_name = NULL;
  786. RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
  787. int debug_app = 0;
  788. if (!response_body) {
  789. ast_http_request_close_on_completion(ser);
  790. ast_http_error(ser, 500, "Server Error", "Out of memory");
  791. return 0;
  792. }
  793. response.headers = ast_str_create(40);
  794. if (!response.headers) {
  795. ast_http_request_close_on_completion(ser);
  796. ast_http_error(ser, 500, "Server Error", "Out of memory");
  797. return 0;
  798. }
  799. conf = ast_ari_config_get();
  800. if (!conf || !conf->general) {
  801. ast_free(response.headers);
  802. ast_http_request_close_on_completion(ser);
  803. ast_http_error(ser, 500, "Server Error", "URI handler config missing");
  804. return 0;
  805. }
  806. process_cors_request(headers, &response);
  807. /* Process form data from a POST. It could be mixed with query
  808. * parameters, which seems a bit odd. But it's allowed, so that's okay
  809. * with us.
  810. */
  811. post_vars = ast_http_get_post_vars(ser, headers);
  812. if (!post_vars) {
  813. switch (errno) {
  814. case EFBIG:
  815. ast_ari_response_error(&response, 413,
  816. "Request Entity Too Large",
  817. "Request body too large");
  818. goto request_failed;
  819. case ENOMEM:
  820. ast_http_request_close_on_completion(ser);
  821. ast_ari_response_error(&response, 500,
  822. "Internal Server Error",
  823. "Out of memory");
  824. goto request_failed;
  825. case EIO:
  826. ast_ari_response_error(&response, 400,
  827. "Bad Request", "Error parsing request body");
  828. goto request_failed;
  829. }
  830. /* Look for a JSON request entity only if there were no post_vars.
  831. * If there were post_vars, then the request body would already have
  832. * been consumed and can not be read again.
  833. */
  834. body = ast_http_get_json(ser, headers);
  835. if (!body) {
  836. switch (errno) {
  837. case EFBIG:
  838. ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
  839. goto request_failed;
  840. case ENOMEM:
  841. ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
  842. goto request_failed;
  843. case EIO:
  844. ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
  845. goto request_failed;
  846. }
  847. }
  848. }
  849. if (get_params == NULL) {
  850. get_params = post_vars;
  851. } else if (get_params && post_vars) {
  852. /* Has both post_vars and get_params */
  853. struct ast_variable *last_var = post_vars;
  854. while (last_var->next) {
  855. last_var = last_var->next;
  856. }
  857. /* The duped get_params will get freed when post_vars gets
  858. * ast_variables_destroyed.
  859. */
  860. last_var->next = ast_variables_dup(get_params);
  861. get_params = post_vars;
  862. }
  863. /* At this point, get_params will contain post_vars (if any) */
  864. app_name = ast_variable_find_in_list(get_params, "app");
  865. if (!app_name) {
  866. struct ast_json *app = ast_json_object_get(body, "app");
  867. app_name = (app ? ast_json_string_get(app) : NULL);
  868. }
  869. /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
  870. * and the global debug flag.
  871. */
  872. debug_app = stasis_app_get_debug_by_name(app_name);
  873. if (debug_app) {
  874. struct ast_str *buf = ast_str_create(512);
  875. char *str = ast_json_dump_string_format(body, ast_ari_json_format());
  876. if (!buf || (body && !str)) {
  877. ast_http_request_close_on_completion(ser);
  878. ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
  879. ast_json_free(str);
  880. ast_free(buf);
  881. goto request_failed;
  882. }
  883. ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
  884. ast_sockaddr_stringify(&ser->remote_address));
  885. for (var = headers; var; var = var->next) {
  886. ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
  887. }
  888. for (var = get_params; var; var = var->next) {
  889. ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
  890. }
  891. ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
  892. ast_json_free(str);
  893. ast_free(buf);
  894. }
  895. user = authenticate_user(get_params, headers);
  896. if (response.response_code > 0) {
  897. /* POST parameter processing error. Do nothing. */
  898. } else if (!user) {
  899. /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
  900. * message is used by an origin server to challenge the
  901. * authorization of a user agent. This response MUST include a
  902. * WWW-Authenticate header field containing at least one
  903. * challenge applicable to the requested resource.
  904. */
  905. ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required");
  906. /* Section 1.2:
  907. * realm = "realm" "=" realm-value
  908. * realm-value = quoted-string
  909. * Section 2:
  910. * challenge = "Basic" realm
  911. */
  912. ast_str_append(&response.headers, 0,
  913. "WWW-Authenticate: Basic realm=\"%s\"\r\n",
  914. conf->general->auth_realm);
  915. } else if (!ast_fully_booted) {
  916. ast_http_request_close_on_completion(ser);
  917. ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted");
  918. } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
  919. ast_ari_response_error(&response, 403, "Forbidden", "Write access denied");
  920. } else if (ast_ends_with(uri, "/")) {
  921. remove_trailing_slash(uri, &response);
  922. } else if (ast_begins_with(uri, "api-docs/")) {
  923. /* Serving up API docs */
  924. if (method != AST_HTTP_GET) {
  925. ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method");
  926. } else {
  927. /* Skip the api-docs prefix */
  928. ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, &response);
  929. }
  930. } else {
  931. /* Other RESTful resources */
  932. ast_ari_invoke(ser, uri, method, get_params, headers, body,
  933. &response);
  934. }
  935. if (response.no_response) {
  936. /* The handler indicates no further response is necessary.
  937. * Probably because it already handled it */
  938. ast_free(response.headers);
  939. return 0;
  940. }
  941. request_failed:
  942. /* If you explicitly want to have no content, set message to
  943. * ast_json_null().
  944. */
  945. ast_assert(response.message != NULL);
  946. ast_assert(response.response_code > 0);
  947. /* response.message could be NULL, in which case the empty response_body
  948. * is correct
  949. */
  950. if (response.message && !ast_json_is_null(response.message)) {
  951. ast_str_append(&response.headers, 0,
  952. "Content-type: application/json\r\n");
  953. if (ast_json_dump_str_format(response.message, &response_body,
  954. conf->general->format) != 0) {
  955. /* Error encoding response */
  956. response.response_code = 500;
  957. response.response_text = "Internal Server Error";
  958. ast_str_set(&response_body, 0, "%s", "");
  959. ast_str_set(&response.headers, 0, "%s", "");
  960. }
  961. }
  962. if (debug_app) {
  963. ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
  964. ast_sockaddr_stringify(&ser->remote_address), response.response_code,
  965. response.response_text, ast_str_buffer(response.headers),
  966. ast_str_buffer(response_body));
  967. }
  968. ast_http_send(ser, method, response.response_code,
  969. response.response_text, response.headers, response_body,
  970. 0, 0);
  971. /* ast_http_send takes ownership, so we don't have to free them */
  972. response_body = NULL;
  973. ast_json_unref(response.message);
  974. return 0;
  975. }
  976. static struct ast_http_uri http_uri = {
  977. .callback = ast_ari_callback,
  978. .description = "Asterisk RESTful API",
  979. .uri = "ari",
  980. .has_subtree = 1,
  981. .data = NULL,
  982. .key = __FILE__,
  983. .no_decode_uri = 1,
  984. };
  985. static int unload_module(void)
  986. {
  987. ast_ari_cli_unregister();
  988. if (is_enabled()) {
  989. ast_debug(3, "Disabling ARI\n");
  990. ast_http_uri_unlink(&http_uri);
  991. }
  992. ast_ari_config_destroy();
  993. ao2_cleanup(root_handler);
  994. root_handler = NULL;
  995. ast_mutex_destroy(&root_handler_lock);
  996. ast_json_unref(oom_json);
  997. oom_json = NULL;
  998. return 0;
  999. }
  1000. static int load_module(void)
  1001. {
  1002. ast_mutex_init(&root_handler_lock);
  1003. /* root_handler may have been built during a declined load */
  1004. if (!root_handler) {
  1005. root_handler = root_handler_create();
  1006. }
  1007. if (!root_handler) {
  1008. return AST_MODULE_LOAD_DECLINE;
  1009. }
  1010. /* oom_json may have been built during a declined load */
  1011. if (!oom_json) {
  1012. oom_json = ast_json_pack(
  1013. "{s: s}", "error", "Allocation failed");
  1014. }
  1015. if (!oom_json) {
  1016. /* Ironic */
  1017. unload_module();
  1018. return AST_MODULE_LOAD_DECLINE;
  1019. }
  1020. if (ast_ari_config_init() != 0) {
  1021. unload_module();
  1022. return AST_MODULE_LOAD_DECLINE;
  1023. }
  1024. if (is_enabled()) {
  1025. ast_debug(3, "ARI enabled\n");
  1026. ast_http_uri_link(&http_uri);
  1027. } else {
  1028. ast_debug(3, "ARI disabled\n");
  1029. }
  1030. if (ast_ari_cli_register() != 0) {
  1031. unload_module();
  1032. return AST_MODULE_LOAD_DECLINE;
  1033. }
  1034. return AST_MODULE_LOAD_SUCCESS;
  1035. }
  1036. static int reload_module(void)
  1037. {
  1038. char was_enabled = is_enabled();
  1039. if (ast_ari_config_reload() != 0) {
  1040. return AST_MODULE_LOAD_DECLINE;
  1041. }
  1042. if (was_enabled && !is_enabled()) {
  1043. ast_debug(3, "Disabling ARI\n");
  1044. ast_http_uri_unlink(&http_uri);
  1045. } else if (!was_enabled && is_enabled()) {
  1046. ast_debug(3, "Enabling ARI\n");
  1047. ast_http_uri_link(&http_uri);
  1048. }
  1049. return AST_MODULE_LOAD_SUCCESS;
  1050. }
  1051. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk RESTful Interface",
  1052. .support_level = AST_MODULE_SUPPORT_CORE,
  1053. .load = load_module,
  1054. .unload = unload_module,
  1055. .reload = reload_module,
  1056. .load_pri = AST_MODPRI_APP_DEPEND,
  1057. );