res_pjsip_endpoint_identifier_ip.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Mark Michelson <mmichelson@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. /*** MODULEINFO
  19. <depend>pjproject</depend>
  20. <depend>res_pjsip</depend>
  21. <support_level>core</support_level>
  22. ***/
  23. #include "asterisk.h"
  24. #include <pjsip.h>
  25. #include "asterisk/res_pjsip.h"
  26. #include "asterisk/res_pjsip_cli.h"
  27. #include "asterisk/module.h"
  28. #include "asterisk/acl.h"
  29. #include "asterisk/manager.h"
  30. #include "res_pjsip/include/res_pjsip_private.h"
  31. /*** DOCUMENTATION
  32. <configInfo name="res_pjsip_endpoint_identifier_ip" language="en_US">
  33. <synopsis>Module that identifies endpoints via source IP address</synopsis>
  34. <configFile name="pjsip.conf">
  35. <configObject name="identify">
  36. <synopsis>Identifies endpoints via source IP address</synopsis>
  37. <configOption name="endpoint">
  38. <synopsis>Name of Endpoint</synopsis>
  39. </configOption>
  40. <configOption name="match">
  41. <synopsis>IP addresses or networks to match against</synopsis>
  42. <description><para>
  43. The value is a comma-delimited list of IP addresses. IP addresses may
  44. have a subnet mask appended. The subnet mask may be written in either
  45. CIDR or dot-decimal notation. Separate the IP address and subnet
  46. mask with a slash ('/')
  47. </para></description>
  48. </configOption>
  49. <configOption name="type">
  50. <synopsis>Must be of type 'identify'.</synopsis>
  51. </configOption>
  52. </configObject>
  53. </configFile>
  54. </configInfo>
  55. ***/
  56. /*! \brief Structure for an IP identification matching object */
  57. struct ip_identify_match {
  58. /*! \brief Sorcery object details */
  59. SORCERY_OBJECT(details);
  60. /*! \brief Stringfields */
  61. AST_DECLARE_STRING_FIELDS(
  62. /*! The name of the endpoint */
  63. AST_STRING_FIELD(endpoint_name);
  64. );
  65. /*! \brief Networks or addresses that should match this */
  66. struct ast_ha *matches;
  67. };
  68. /*! \brief Destructor function for a matching object */
  69. static void ip_identify_destroy(void *obj)
  70. {
  71. struct ip_identify_match *identify = obj;
  72. ast_string_field_free_memory(identify);
  73. ast_free_ha(identify->matches);
  74. }
  75. /*! \brief Allocator function for a matching object */
  76. static void *ip_identify_alloc(const char *name)
  77. {
  78. struct ip_identify_match *identify = ast_sorcery_generic_alloc(sizeof(*identify), ip_identify_destroy);
  79. if (!identify || ast_string_field_init(identify, 256)) {
  80. ao2_cleanup(identify);
  81. return NULL;
  82. }
  83. return identify;
  84. }
  85. /*! \brief Comparator function for a matching object */
  86. static int ip_identify_match_check(void *obj, void *arg, int flags)
  87. {
  88. struct ip_identify_match *identify = obj;
  89. struct ast_sockaddr *addr = arg;
  90. int sense;
  91. sense = ast_apply_ha(identify->matches, addr);
  92. if (sense != AST_SENSE_ALLOW) {
  93. ast_debug(3, "Source address %s matches identify '%s'\n",
  94. ast_sockaddr_stringify(addr),
  95. ast_sorcery_object_get_id(identify));
  96. return CMP_MATCH | CMP_STOP;
  97. } else {
  98. ast_debug(3, "Source address %s does not match identify '%s'\n",
  99. ast_sockaddr_stringify(addr),
  100. ast_sorcery_object_get_id(identify));
  101. return 0;
  102. }
  103. }
  104. static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata)
  105. {
  106. struct ast_sockaddr addr = { { 0, } };
  107. RAII_VAR(struct ao2_container *, candidates, NULL, ao2_cleanup);
  108. RAII_VAR(struct ip_identify_match *, match, NULL, ao2_cleanup);
  109. struct ast_sip_endpoint *endpoint;
  110. /* If no possibilities exist return early to save some time */
  111. if (!(candidates = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) ||
  112. !ao2_container_count(candidates)) {
  113. ast_debug(3, "No identify sections to match against\n");
  114. return NULL;
  115. }
  116. ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID);
  117. ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port);
  118. if (!(match = ao2_callback(candidates, 0, ip_identify_match_check, &addr))) {
  119. ast_debug(3, "'%s' did not match any identify section rules\n",
  120. ast_sockaddr_stringify(&addr));
  121. return NULL;
  122. }
  123. endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", match->endpoint_name);
  124. if (endpoint) {
  125. ast_debug(3, "Retrieved endpoint %s\n", ast_sorcery_object_get_id(endpoint));
  126. } else {
  127. ast_log(LOG_WARNING, "Identify section '%s' points to endpoint '%s' but endpoint could not be looked up\n",
  128. ast_sorcery_object_get_id(match), match->endpoint_name);
  129. }
  130. return endpoint;
  131. }
  132. static struct ast_sip_endpoint_identifier ip_identifier = {
  133. .identify_endpoint = ip_identify,
  134. };
  135. /*! \brief Custom handler for match field */
  136. static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
  137. {
  138. struct ip_identify_match *identify = obj;
  139. char *input_string = ast_strdupa(var->value);
  140. char *current_string;
  141. if (ast_strlen_zero(var->value)) {
  142. return 0;
  143. }
  144. while ((current_string = strsep(&input_string, ","))) {
  145. struct ast_sockaddr *addrs;
  146. int num_addrs = 0, error = 0, i;
  147. char *mask = strrchr(current_string, '/');
  148. if (mask) {
  149. identify->matches = ast_append_ha("d", current_string, identify->matches, &error);
  150. if (!identify->matches || error) {
  151. ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
  152. current_string, ast_sorcery_object_get_id(obj));
  153. return -1;
  154. }
  155. continue;
  156. }
  157. num_addrs = ast_sockaddr_resolve(&addrs, current_string, PARSE_PORT_FORBID, AST_AF_UNSPEC);
  158. if (!num_addrs) {
  159. ast_log(LOG_ERROR, "Address '%s' provided on ip endpoint identifier '%s' did not resolve to any address\n",
  160. var->value, ast_sorcery_object_get_id(obj));
  161. return -1;
  162. }
  163. for (i = 0; i < num_addrs; ++i) {
  164. /* We deny what we actually want to match because there is an implicit permit all rule for ACLs */
  165. identify->matches = ast_append_ha("d", ast_sockaddr_stringify_addr(&addrs[i]), identify->matches, &error);
  166. if (!identify->matches || error) {
  167. ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
  168. ast_sockaddr_stringify_addr(&addrs[i]), ast_sorcery_object_get_id(obj));
  169. error = -1;
  170. break;
  171. }
  172. }
  173. ast_free(addrs);
  174. if (error) {
  175. return -1;
  176. }
  177. }
  178. return 0;
  179. }
  180. static int match_to_str(const void *obj, const intptr_t *args, char **buf)
  181. {
  182. RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
  183. const struct ip_identify_match *identify = obj;
  184. ast_ha_join(identify->matches, &str);
  185. *buf = ast_strdup(ast_str_buffer(str));
  186. return 0;
  187. }
  188. static int match_to_var_list(const void *obj, struct ast_variable **fields)
  189. {
  190. char str[MAX_OBJECT_FIELD];
  191. const struct ip_identify_match *identify = obj;
  192. struct ast_variable *head = NULL;
  193. struct ast_ha *ha = identify->matches;
  194. for (; ha; ha = ha->next) {
  195. const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
  196. snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
  197. addr, ast_sockaddr_stringify_addr(&ha->netmask));
  198. ast_variable_list_append(&head, ast_variable_new("match", str, ""));
  199. }
  200. if (head) {
  201. *fields = head;
  202. }
  203. return 0;
  204. }
  205. static int sip_identify_to_ami(const struct ip_identify_match *identify,
  206. struct ast_str **buf)
  207. {
  208. return ast_sip_sorcery_object_to_ami(identify, buf);
  209. }
  210. static int find_identify_by_endpoint(void *obj, void *arg, int flags)
  211. {
  212. struct ip_identify_match *identify = obj;
  213. const char *endpoint_name = arg;
  214. return strcmp(identify->endpoint_name, endpoint_name) ? 0 : CMP_MATCH;
  215. }
  216. static int format_ami_endpoint_identify(const struct ast_sip_endpoint *endpoint,
  217. struct ast_sip_ami *ami)
  218. {
  219. RAII_VAR(struct ao2_container *, identifies, NULL, ao2_cleanup);
  220. RAII_VAR(struct ip_identify_match *, identify, NULL, ao2_cleanup);
  221. RAII_VAR(struct ast_str *, buf, NULL, ast_free);
  222. identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
  223. AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
  224. if (!identifies) {
  225. return -1;
  226. }
  227. identify = ao2_callback(identifies, 0, find_identify_by_endpoint,
  228. (void *) ast_sorcery_object_get_id(endpoint));
  229. if (!identify) {
  230. return 1;
  231. }
  232. if (!(buf = ast_sip_create_ami_event("IdentifyDetail", ami))) {
  233. return -1;
  234. }
  235. if (sip_identify_to_ami(identify, &buf)) {
  236. return -1;
  237. }
  238. ast_str_append(&buf, 0, "EndpointName: %s\r\n",
  239. ast_sorcery_object_get_id(endpoint));
  240. astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
  241. ami->count++;
  242. return 0;
  243. }
  244. struct ast_sip_endpoint_formatter endpoint_identify_formatter = {
  245. .format_ami = format_ami_endpoint_identify
  246. };
  247. static int cli_populate_container(void *obj, void *arg, int flags)
  248. {
  249. ao2_link(arg, obj);
  250. return 0;
  251. }
  252. static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
  253. {
  254. const struct ast_sip_endpoint *endpoint = container;
  255. struct ao2_container *identifies;
  256. struct ast_variable fields = {
  257. .name = "endpoint",
  258. .value = ast_sorcery_object_get_id(endpoint),
  259. .next = NULL,
  260. };
  261. identifies = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify",
  262. AST_RETRIEVE_FLAG_MULTIPLE, &fields);
  263. if (!identifies) {
  264. return -1;
  265. }
  266. ao2_callback(identifies, OBJ_NODATA, callback, args);
  267. return 0;
  268. }
  269. static int cli_endpoint_gather_identifies(void *obj, void *arg, int flags)
  270. {
  271. struct ast_sip_endpoint *endpoint = obj;
  272. struct ao2_container *container = arg;
  273. cli_iterator(endpoint, cli_populate_container, container);
  274. return 0;
  275. }
  276. static struct ao2_container *cli_get_container(void)
  277. {
  278. RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
  279. RAII_VAR(struct ao2_container *, s_parent_container, NULL, ao2_cleanup);
  280. struct ao2_container *child_container;
  281. parent_container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint",
  282. AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
  283. if (!parent_container) {
  284. return NULL;
  285. }
  286. s_parent_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
  287. ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
  288. if (!s_parent_container) {
  289. return NULL;
  290. }
  291. if (ao2_container_dup(s_parent_container, parent_container, 0)) {
  292. return NULL;
  293. }
  294. child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
  295. ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
  296. if (!child_container) {
  297. return NULL;
  298. }
  299. ao2_callback(s_parent_container, OBJ_NODATA, cli_endpoint_gather_identifies, child_container);
  300. return child_container;
  301. }
  302. static void *cli_retrieve_by_id(const char *id)
  303. {
  304. return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "identify", id);
  305. }
  306. static int cli_print_header(void *obj, void *arg, int flags)
  307. {
  308. struct ast_sip_cli_context *context = arg;
  309. int indent = CLI_INDENT_TO_SPACES(context->indent_level);
  310. int filler = CLI_MAX_WIDTH - indent - 14;
  311. ast_assert(context->output_buffer != NULL);
  312. ast_str_append(&context->output_buffer, 0,
  313. "%*s: <MatchList%*.*s>\n",
  314. indent, "Identify", filler, filler, CLI_HEADER_FILLER);
  315. return 0;
  316. }
  317. static int cli_print_body(void *obj, void *arg, int flags)
  318. {
  319. RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
  320. struct ip_identify_match *ident = obj;
  321. struct ast_sip_cli_context *context = arg;
  322. ast_assert(context->output_buffer != NULL);
  323. ast_str_append(&context->output_buffer, 0, "%*s: ",
  324. CLI_INDENT_TO_SPACES(context->indent_level), "Identify");
  325. ast_ha_join_cidr(ident->matches, &str);
  326. ast_str_append(&context->output_buffer, 0, "%s\n", ast_str_buffer(str));
  327. return 0;
  328. }
  329. static struct ast_sip_cli_formatter_entry *cli_formatter;
  330. static int load_module(void)
  331. {
  332. CHECK_PJSIP_MODULE_LOADED();
  333. ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_endpoint_identifier_ip");
  334. ast_sorcery_apply_default(ast_sip_get_sorcery(), "identify", "config", "pjsip.conf,criteria=type=identify");
  335. if (ast_sorcery_object_register(ast_sip_get_sorcery(), "identify", ip_identify_alloc, NULL, NULL)) {
  336. return AST_MODULE_LOAD_DECLINE;
  337. }
  338. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
  339. ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
  340. ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
  341. ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify");
  342. ast_sip_register_endpoint_identifier(&ip_identifier);
  343. ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
  344. cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
  345. if (!cli_formatter) {
  346. ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
  347. return -1;
  348. }
  349. cli_formatter->name = "identify";
  350. cli_formatter->print_header = cli_print_header;
  351. cli_formatter->print_body = cli_print_body;
  352. cli_formatter->get_container = cli_get_container;
  353. cli_formatter->iterate = cli_iterator;
  354. cli_formatter->get_id = ast_sorcery_object_get_id;
  355. cli_formatter->retrieve_by_id = cli_retrieve_by_id;
  356. ast_sip_register_cli_formatter(cli_formatter);
  357. return AST_MODULE_LOAD_SUCCESS;
  358. }
  359. static int reload_module(void)
  360. {
  361. ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify");
  362. return 0;
  363. }
  364. static int unload_module(void)
  365. {
  366. ast_sip_unregister_cli_formatter(cli_formatter);
  367. ast_sip_unregister_endpoint_formatter(&endpoint_identify_formatter);
  368. ast_sip_unregister_endpoint_identifier(&ip_identifier);
  369. return 0;
  370. }
  371. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP IP endpoint identifier",
  372. .support_level = AST_MODULE_SUPPORT_CORE,
  373. .load = load_module,
  374. .reload = reload_module,
  375. .unload = unload_module,
  376. .load_pri = AST_MODPRI_APP_DEPEND,
  377. );