Merge "res_pjsip: New endpoint option "refer_blind_progress""
[asterisk/asterisk.git] / res / res_hep.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2014, Digium, Inc.
5  *
6  * Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
7  * Matt Jordan <mjordan@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*!
21  * \file
22  * \brief Routines for integration with Homer using HEPv3
23  *
24  * \author Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
25  * \author Matt Jordan <mjordan@digium.com>
26  *
27  */
28
29 /*!
30  * \li \ref res_hep.c uses the configuration file \ref hep.conf
31  * \addtogroup configuration_file Configuration Files
32  */
33
34 /*!
35  * \page hep.conf hep.conf
36  * \verbinclude hep.conf.sample
37  */
38
39 /*** MODULEINFO
40         <support_level>extended</support_level>
41  ***/
42
43 /*** DOCUMENTATION
44         <configInfo name="res_hep" language="en_US">
45                 <synopsis>Resource for integration with Homer using HEPv3</synopsis>
46                 <configFile name="hep.conf">
47                         <configObject name="general">
48                                 <synopsis>General settings.</synopsis>
49                                 <description><para>
50                                         The <emphasis>general</emphasis> settings section contains information
51                                         to configure Asterisk as a Homer capture agent.
52                                         </para>
53                                 </description>
54                                 <configOption name="enabled" default="yes">
55                                         <synopsis>Enable or disable packet capturing.</synopsis>
56                                         <description>
57                                                 <enumlist>
58                                                         <enum name="no" />
59                                                         <enum name="yes" />
60                                                 </enumlist>
61                                         </description>
62                                 </configOption>
63                                 <configOption name="uuid_type" default="call-id">
64                                         <synopsis>The preferred type of UUID to pass to Homer.</synopsis>
65                                         <description>
66                                                 <enumlist>
67                                                         <enum name="call-id"><para>Use the PJSIP Call-Id</para></enum>
68                                                         <enum name="channel"><para>Use the Asterisk channel name</para></enum>
69                                                 </enumlist>
70                                         </description>
71                                 </configOption>
72                                 <configOption name="capture_address">
73                                         <synopsis>The address and port of the Homer server to send packets to.</synopsis>
74                                 </configOption>
75                                 <configOption name="capture_password">
76                                         <synopsis>If set, the authentication password to send to Homer.</synopsis>
77                                 </configOption>
78                                 <configOption name="capture_id" default="0">
79                                         <synopsis>The ID for this capture agent.</synopsis>
80                                 </configOption>
81                         </configObject>
82                 </configFile>
83         </configInfo>
84 ***/
85
86 #include "asterisk.h"
87
88 #include "asterisk/module.h"
89 #include "asterisk/astobj2.h"
90 #include "asterisk/config_options.h"
91 #include "asterisk/taskprocessor.h"
92 #include "asterisk/res_hep.h"
93
94 #include <netinet/ip.h>
95 #include <netinet/tcp.h>
96 #include <netinet/udp.h>
97 #include <netinet/ip6.h>
98
99 /*! Generic vendor ID. Used for HEPv3 standard packets */
100 #define GENERIC_VENDOR_ID 0x0000
101
102 /*! Asterisk vendor ID. Used for custom data to send to a capture node */
103 #define ASTERISK_VENDOR_ID 0x0004
104
105 /*! Chunk types from the HEPv3 Spec */
106 enum hepv3_chunk_types {
107
108         /*! THE IP PROTOCOL FAMILY */
109         CHUNK_TYPE_IP_PROTOCOL_FAMILY = 0X0001,
110
111         /*! THE IP PROTOCOL ID (UDP, TCP, ETC.) */
112         CHUNK_TYPE_IP_PROTOCOL_ID = 0X0002,
113
114         /*! IF IPV4, THE SOURCE ADDRESS */
115         CHUNK_TYPE_IPV4_SRC_ADDR = 0X0003,
116
117         /*! IF IPV4, THE DESTINATION ADDRESS */
118         CHUNK_TYPE_IPV4_DST_ADDR = 0X0004,
119
120         /*! IF IPV6, THE SOURCE ADDRESS */
121         CHUNK_TYPE_IPV6_SRC_ADDR = 0X0005,
122
123         /*! IF IPV6, THE DESTINATION ADDRESS */
124         CHUNK_TYPE_IPV6_DST_ADDR = 0X0006,
125
126         /*! THE SOURCE PORT */
127         CHUNK_TYPE_SRC_PORT = 0X0007,
128
129         /*! THE DESTINATION PORT */
130         CHUNK_TYPE_DST_PORT = 0X0008,
131
132         /*! THE CAPTURE TIME (SECONDS) */
133         CHUNK_TYPE_TIMESTAMP_SEC = 0X0009,
134
135         /*! THE CAPTURE TIME (MICROSECONDS) */
136         CHUNK_TYPE_TIMESTAMP_USEC = 0X000A,
137
138         /*! THE PROTOCOL PACKET TYPE. SEE /REF HEPV3_CAPTURE_TYPE */
139         CHUNK_TYPE_PROTOCOL_TYPE = 0X000B,
140
141         /*! OUR CAPTURE AGENT ID */
142         CHUNK_TYPE_CAPTURE_AGENT_ID = 0X000C,
143
144         /*! A KEEP ALIVE TIMER */
145         CHUNK_TYPE_KEEP_ALIVE_TIMER = 0X000D,
146
147         /*! THE \REF CAPTURE_PASSWORD IF DEFINED */
148         CHUNK_TYPE_AUTH_KEY = 0X000E,
149
150         /*! THE ONE AND ONLY PAYLOAD */
151         CHUNK_TYPE_PAYLOAD = 0X000F,
152
153         /*! THE ONE AND ONLY (ZIPPED) PAYLOAD */
154         CHUNK_TYPE_PAYLOAD_ZIP = 0X0010,
155
156         /*! THE UUID FOR THIS PACKET */
157         CHUNK_TYPE_UUID = 0X0011,
158 };
159
160 #define INITIALIZE_GENERIC_HEP_IDS(hep_chunk, type) do { \
161         (hep_chunk)->vendor_id = htons(GENERIC_VENDOR_ID); \
162         (hep_chunk)->type_id = htons((type)); \
163         } while (0)
164
165 #define INITIALIZE_GENERIC_HEP_IDS_VAR(hep_chunk, type, len) do { \
166         INITIALIZE_GENERIC_HEP_IDS((hep_chunk), (type)); \
167         (hep_chunk)->length = htons(sizeof(*(hep_chunk)) + len); \
168         } while (0)
169
170 #define INITIALIZE_GENERIC_HEP_CHUNK(hep_item, type) do { \
171         INITIALIZE_GENERIC_HEP_IDS(&(hep_item)->chunk, (type)); \
172         (hep_item)->chunk.length = htons(sizeof(*(hep_item))); \
173         } while (0)
174
175 #define INITIALIZE_GENERIC_HEP_CHUNK_DATA(hep_item, type, value) do { \
176         INITIALIZE_GENERIC_HEP_CHUNK((hep_item), (type)); \
177         (hep_item)->data = (value); \
178         } while (0)
179
180 /*
181  * HEPv3 Types.
182  * Note that the content in these is stored in network byte order.
183  */
184
185 struct hep_chunk {
186         u_int16_t vendor_id;
187         u_int16_t type_id;
188         u_int16_t length;
189 } __attribute__((packed));
190
191 struct hep_chunk_uint8 {
192         struct hep_chunk chunk;
193         u_int8_t data;
194 } __attribute__((packed));
195
196 struct hep_chunk_uint16 {
197         struct hep_chunk chunk;
198         u_int16_t data;
199 } __attribute__((packed));
200
201 struct hep_chunk_uint32 {
202         struct hep_chunk chunk;
203         u_int32_t data;
204 } __attribute__((packed));
205
206 struct hep_chunk_ip4 {
207         struct hep_chunk chunk;
208         struct in_addr data;
209 } __attribute__((packed));
210
211 struct hep_chunk_ip6 {
212         struct hep_chunk chunk;
213         struct in6_addr data;
214 } __attribute__((packed));
215
216 struct hep_ctrl {
217         char id[4];
218         u_int16_t length;
219 } __attribute__((packed));
220
221 /* HEP structures */
222
223 struct hep_generic {
224         struct hep_ctrl         header;
225         struct hep_chunk_uint8  ip_family;
226         struct hep_chunk_uint8  ip_proto;
227         struct hep_chunk_uint16 src_port;
228         struct hep_chunk_uint16 dst_port;
229         struct hep_chunk_uint32 time_sec;
230         struct hep_chunk_uint32 time_usec;
231         struct hep_chunk_uint8  proto_t;
232         struct hep_chunk_uint32 capt_id;
233 } __attribute__((packed));
234
235 /*! \brief Global configuration for the module */
236 struct hepv3_global_config {
237         unsigned int enabled;                    /*!< Whether or not sending is enabled */
238         unsigned int capture_id;                 /*!< Capture ID for this agent */
239         enum hep_uuid_type uuid_type;            /*!< The preferred type of the UUID */
240         AST_DECLARE_STRING_FIELDS(
241                 AST_STRING_FIELD(capture_address);   /*!< Address to send to */
242                 AST_STRING_FIELD(capture_password);  /*!< Password for Homer server */
243         );
244 };
245
246 /*! \brief The actual module config */
247 struct module_config {
248         struct hepv3_global_config *general; /*!< The general config settings */
249 };
250
251 /*! \brief Run-time data derived from \ref hepv3_global_config */
252 struct hepv3_runtime_data {
253         struct ast_sockaddr remote_addr;  /*!< The address to send to */
254         int sockfd;                       /*!< The socket file descriptor */
255 };
256
257 static struct aco_type global_option = {
258         .type = ACO_GLOBAL,
259         .name = "general",
260         .item_offset = offsetof(struct module_config, general),
261         .category_match = ACO_WHITELIST,
262         .category = "^general$",
263 };
264
265 struct aco_type *global_options[] = ACO_TYPES(&global_option);
266
267 struct aco_file hepv3_conf = {
268         .filename = "hep.conf",
269         .types = ACO_TYPES(&global_option),
270 };
271
272 /*! \brief The module configuration container */
273 static AO2_GLOBAL_OBJ_STATIC(global_config);
274
275 /*! \brief Current module data */
276 static AO2_GLOBAL_OBJ_STATIC(global_data);
277
278 static struct ast_taskprocessor *hep_queue_tp;
279
280 static void *module_config_alloc(void);
281 static int hepv3_config_pre_apply(void);
282 static void hepv3_config_post_apply(void);
283
284 /*! \brief Register information about the configs being processed by this module */
285 CONFIG_INFO_STANDARD(cfg_info, global_config, module_config_alloc,
286         .files = ACO_FILES(&hepv3_conf),
287         .pre_apply_config = hepv3_config_pre_apply,
288         .post_apply_config = hepv3_config_post_apply,
289 );
290
291 static void hepv3_config_dtor(void *obj)
292 {
293         struct hepv3_global_config *config = obj;
294
295         ast_string_field_free_memory(config);
296 }
297
298 /*! \brief HEPv3 configuration object allocation */
299 static void *hepv3_config_alloc(void)
300 {
301         struct hepv3_global_config *config;
302
303         config = ao2_alloc(sizeof(*config), hepv3_config_dtor);
304         if (!config || ast_string_field_init(config, 32)) {
305                 return NULL;
306         }
307
308         return config;
309 }
310
311 /*! \brief Configuration object destructor */
312 static void module_config_dtor(void *obj)
313 {
314         struct module_config *config = obj;
315
316         if (config->general) {
317                 ao2_ref(config->general, -1);
318         }
319 }
320
321 /*! \brief Module config constructor */
322 static void *module_config_alloc(void)
323 {
324         struct module_config *config;
325
326         config = ao2_alloc(sizeof(*config), module_config_dtor);
327         if (!config) {
328                 return NULL;
329         }
330
331         config->general = hepv3_config_alloc();
332         if (!config->general) {
333                 ao2_ref(config, -1);
334                 config = NULL;
335         }
336
337         return config;
338 }
339
340 /*! \brief Handler for the uuid_type attribute */
341 static int uuid_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
342 {
343         struct hepv3_global_config *global_config = obj;
344
345         if (strcasecmp(var->name, "uuid_type")) {
346                 return -1;
347         }
348
349         if (!strcasecmp(var->value, "channel")) {
350                 global_config->uuid_type = HEP_UUID_TYPE_CHANNEL;
351         } else if (!strcasecmp(var->value, "call-id")) {
352                 global_config->uuid_type = HEP_UUID_TYPE_CALL_ID;
353         } else {
354                 return -1;
355         }
356         return 0;
357 }
358
359 /*! \brief HEPv3 run-time data destructor */
360 static void hepv3_data_dtor(void *obj)
361 {
362         struct hepv3_runtime_data *data = obj;
363
364         if (data->sockfd > -1) {
365                 close(data->sockfd);
366                 data->sockfd = -1;
367         }
368 }
369
370 /*! \brief Allocate the HEPv3 run-time data */
371 static struct hepv3_runtime_data *hepv3_data_alloc(struct hepv3_global_config *config)
372 {
373         struct hepv3_runtime_data *data;
374
375         data = ao2_alloc(sizeof(*data), hepv3_data_dtor);
376         if (!data) {
377                 return NULL;
378         }
379
380         data->sockfd = -1;
381
382         if (!ast_sockaddr_parse(&data->remote_addr, config->capture_address, PARSE_PORT_REQUIRE)) {
383                 ast_log(AST_LOG_WARNING, "Failed to create address from %s\n", config->capture_address);
384                 ao2_ref(data, -1);
385                 return NULL;
386         }
387
388         data->sockfd = socket(ast_sockaddr_is_ipv6(&data->remote_addr) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
389         if (data->sockfd < 0) {
390                 ast_log(AST_LOG_WARNING, "Failed to create socket for address %s: %s\n",
391                                 config->capture_address, strerror(errno));
392                 ao2_ref(data, -1);
393                 return NULL;
394         }
395
396         return data;
397 }
398
399 /*! \brief Destructor for a \ref hepv3_capture_info object */
400 static void capture_info_dtor(void *obj)
401 {
402         struct hepv3_capture_info *info = obj;
403
404         ast_free(info->uuid);
405         ast_free(info->payload);
406 }
407
408 enum hep_uuid_type hepv3_get_uuid_type(void)
409 {
410         RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
411
412         if (!config) {
413                 /* Well, that's unfortunate. Return something. */
414                 return HEP_UUID_TYPE_CALL_ID;
415         }
416
417         return config->general->uuid_type;
418 }
419
420 int hepv3_is_loaded(void)
421 {
422         RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
423
424         return (config != NULL) ? 1 : 0;
425 }
426
427 struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len)
428 {
429         struct hepv3_capture_info *info;
430
431         info = ao2_alloc(sizeof(*info), capture_info_dtor);
432         if (!info) {
433                 return NULL;
434         }
435
436         info->payload = ast_malloc(len);
437         if (!info->payload) {
438                 ao2_ref(info, -1);
439                 return NULL;
440         }
441         memcpy(info->payload, payload, len);
442         info->len = len;
443
444         /* Set a reasonable default */
445         info->protocol_id = IPPROTO_UDP;
446
447         return info;
448 }
449
450 /*! \brief Callback function for the \ref hep_queue_tp taskprocessor */
451 static int hep_queue_cb(void *data)
452 {
453         RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
454         RAII_VAR(struct hepv3_runtime_data *, hepv3_data, ao2_global_obj_ref(global_data), ao2_cleanup);
455         RAII_VAR(struct hepv3_capture_info *, capture_info, data, ao2_cleanup);
456         struct hep_generic hg_pkt;
457         unsigned int packet_len = 0, sock_buffer_len;
458         struct hep_chunk_ip4 ipv4_src, ipv4_dst;
459         struct hep_chunk_ip6 ipv6_src, ipv6_dst;
460         struct hep_chunk auth_key, payload, uuid;
461         void *sock_buffer;
462         int res;
463
464         if (!capture_info || !config || !hepv3_data) {
465                 return 0;
466         }
467
468         if (ast_sockaddr_is_ipv4(&capture_info->src_addr) != ast_sockaddr_is_ipv4(&capture_info->dst_addr)) {
469                 ast_log(AST_LOG_NOTICE, "Unable to send packet: Address Family mismatch between source/destination\n");
470                 return -1;
471         }
472
473         packet_len = sizeof(hg_pkt);
474
475         /* Build HEPv3 header, capture info, and calculate the total packet size */
476         memcpy(hg_pkt.header.id, "\x48\x45\x50\x33", 4);
477
478         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_proto, CHUNK_TYPE_IP_PROTOCOL_ID, capture_info->protocol_id);
479         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.src_port, CHUNK_TYPE_SRC_PORT, htons(ast_sockaddr_port(&capture_info->src_addr)));
480         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.dst_port, CHUNK_TYPE_DST_PORT, htons(ast_sockaddr_port(&capture_info->dst_addr)));
481         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.time_sec, CHUNK_TYPE_TIMESTAMP_SEC, htonl(capture_info->capture_time.tv_sec));
482         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.time_usec, CHUNK_TYPE_TIMESTAMP_USEC, htonl(capture_info->capture_time.tv_usec));
483         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.proto_t, CHUNK_TYPE_PROTOCOL_TYPE, capture_info->capture_type);
484         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.capt_id, CHUNK_TYPE_CAPTURE_AGENT_ID, htonl(config->general->capture_id));
485
486         if (ast_sockaddr_is_ipv4(&capture_info->src_addr)) {
487                 INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_family,
488                         CHUNK_TYPE_IP_PROTOCOL_FAMILY, AF_INET);
489
490                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv4_src, CHUNK_TYPE_IPV4_SRC_ADDR);
491                 inet_pton(AF_INET, ast_sockaddr_stringify_addr(&capture_info->src_addr), &ipv4_src.data);
492
493                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv4_dst, CHUNK_TYPE_IPV4_DST_ADDR);
494                 inet_pton(AF_INET, ast_sockaddr_stringify_addr(&capture_info->dst_addr), &ipv4_dst.data);
495
496                 packet_len += (sizeof(ipv4_src) + sizeof(ipv4_dst));
497         } else {
498                 INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_family,
499                         CHUNK_TYPE_IP_PROTOCOL_FAMILY, AF_INET6);
500
501                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv6_src, CHUNK_TYPE_IPV6_SRC_ADDR);
502                 inet_pton(AF_INET6, ast_sockaddr_stringify_addr(&capture_info->src_addr), &ipv6_src.data);
503
504                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv6_dst, CHUNK_TYPE_IPV6_DST_ADDR);
505                 inet_pton(AF_INET6, ast_sockaddr_stringify_addr(&capture_info->dst_addr), &ipv6_dst.data);
506
507                 packet_len += (sizeof(ipv6_src) + sizeof(ipv6_dst));
508         }
509
510         if (!ast_strlen_zero(config->general->capture_password))  {
511                 INITIALIZE_GENERIC_HEP_IDS_VAR(&auth_key, CHUNK_TYPE_AUTH_KEY, strlen(config->general->capture_password));
512                 packet_len += (sizeof(auth_key) + strlen(config->general->capture_password));
513         }
514         INITIALIZE_GENERIC_HEP_IDS_VAR(&uuid, CHUNK_TYPE_UUID, strlen(capture_info->uuid));
515         packet_len += (sizeof(uuid) + strlen(capture_info->uuid));
516         INITIALIZE_GENERIC_HEP_IDS_VAR(&payload,
517                 capture_info->zipped ? CHUNK_TYPE_PAYLOAD_ZIP : CHUNK_TYPE_PAYLOAD, capture_info->len);
518         packet_len += (sizeof(payload) + capture_info->len);
519         hg_pkt.header.length = htons(packet_len);
520
521         /* Build the buffer to send */
522         sock_buffer = ast_malloc(packet_len);
523         if (!sock_buffer) {
524                 return -1;
525         }
526
527         /* Copy in the header */
528         memcpy(sock_buffer, &hg_pkt, sizeof(hg_pkt));
529         sock_buffer_len = sizeof(hg_pkt);
530
531         /* Addresses */
532         if (ast_sockaddr_is_ipv4(&capture_info->src_addr)) {
533                 memcpy(sock_buffer + sock_buffer_len, &ipv4_src, sizeof(ipv4_src));
534                 sock_buffer_len += sizeof(ipv4_src);
535                 memcpy(sock_buffer + sock_buffer_len, &ipv4_dst, sizeof(ipv4_dst));
536                 sock_buffer_len += sizeof(ipv4_dst);
537         } else {
538                 memcpy(sock_buffer + sock_buffer_len, &ipv6_src, sizeof(ipv6_src));
539                 sock_buffer_len += sizeof(ipv6_src);
540                 memcpy(sock_buffer + sock_buffer_len, &ipv6_dst, sizeof(ipv6_dst));
541                 sock_buffer_len += sizeof(ipv6_dst);
542         }
543
544         /* Auth Key */
545         if (!ast_strlen_zero(config->general->capture_password)) {
546                 memcpy(sock_buffer + sock_buffer_len, &auth_key, sizeof(auth_key));
547                 sock_buffer_len += sizeof(auth_key);
548                 memcpy(sock_buffer + sock_buffer_len, config->general->capture_password, strlen(config->general->capture_password));
549                 sock_buffer_len += strlen(config->general->capture_password);
550         }
551
552         /* UUID */
553         memcpy(sock_buffer + sock_buffer_len, &uuid, sizeof(uuid));
554         sock_buffer_len += sizeof(uuid);
555         memcpy(sock_buffer + sock_buffer_len, capture_info->uuid, strlen(capture_info->uuid));
556         sock_buffer_len += strlen(capture_info->uuid);
557
558         /* Packet! */
559         memcpy(sock_buffer + sock_buffer_len, &payload, sizeof(payload));
560         sock_buffer_len += sizeof(payload);
561         memcpy(sock_buffer + sock_buffer_len, capture_info->payload, capture_info->len);
562         sock_buffer_len += capture_info->len;
563
564         ast_assert(sock_buffer_len == packet_len);
565
566         res = ast_sendto(hepv3_data->sockfd, sock_buffer, sock_buffer_len, 0, &hepv3_data->remote_addr);
567         if (res < 0) {
568                 ast_log(AST_LOG_ERROR, "Error [%d] while sending packet to HEPv3 server: %s\n",
569                         errno, strerror(errno));
570         } else if (res != sock_buffer_len) {
571                 ast_log(AST_LOG_WARNING, "Failed to send complete packet to HEPv3 server: %d of %u sent\n",
572                         res, sock_buffer_len);
573                 res = -1;
574         }
575
576         ast_free(sock_buffer);
577         return res;
578 }
579
580 int hepv3_send_packet(struct hepv3_capture_info *capture_info)
581 {
582         RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
583         int res;
584
585         if (!config || !config->general->enabled) {
586                 ao2_ref(capture_info, -1);
587                 return 0;
588         }
589
590         res = ast_taskprocessor_push(hep_queue_tp, hep_queue_cb, capture_info);
591         if (res == -1) {
592                 ao2_ref(capture_info, -1);
593         }
594
595         return res;
596 }
597
598 /*!
599  * \brief Pre-apply callback for the config framework.
600  *
601  * This validates that required fields exist and are populated.
602  */
603 static int hepv3_config_pre_apply(void)
604 {
605         struct module_config *config = aco_pending_config(&cfg_info);
606
607         if (!config->general->enabled) {
608                 /* If we're not enabled, we don't care about anything else */
609                 return 0;
610         }
611
612         if (ast_strlen_zero(config->general->capture_address)) {
613                 ast_log(AST_LOG_ERROR, "Missing required configuration option 'capture_address'\n");
614                 return -1;
615         }
616
617         return 0;
618 }
619
620 /*!
621  * \brief Post-apply callback for the config framework.
622  *
623  * This will create the run-time information from the supplied
624  * configuration.
625  */
626 static void hepv3_config_post_apply(void)
627 {
628         RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);
629         struct hepv3_runtime_data *data;
630
631         data = hepv3_data_alloc(mod_cfg->general);
632         if (!data) {
633                 return;
634         }
635
636         ao2_global_obj_replace_unref(global_data, data);
637         ao2_ref(data, -1);
638 }
639
640 /*!
641  * \brief Reload the module
642  */
643 static int reload_module(void)
644 {
645         if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
646                 return -1;
647         }
648         return 0;
649 }
650
651 /*!
652  * \brief Unload the module
653  */
654 static int unload_module(void)
655 {
656         hep_queue_tp = ast_taskprocessor_unreference(hep_queue_tp);
657
658         ao2_global_obj_release(global_config);
659         ao2_global_obj_release(global_data);
660         aco_info_destroy(&cfg_info);
661
662         return 0;
663 }
664
665 /*!
666  * \brief Load the module
667  */
668 static int load_module(void)
669 {
670         if (aco_info_init(&cfg_info)) {
671                 goto error;
672         }
673
674         hep_queue_tp = ast_taskprocessor_get("hep_queue_tp", TPS_REF_DEFAULT);
675         if (!hep_queue_tp) {
676                 goto error;
677         }
678
679         aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct hepv3_global_config, enabled));
680         aco_option_register(&cfg_info, "capture_address", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct hepv3_global_config, capture_address));
681         aco_option_register(&cfg_info, "capture_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_password));
682         aco_option_register(&cfg_info, "capture_id", ACO_EXACT, global_options, "0", OPT_UINT_T, 0, STRFLDSET(struct hepv3_global_config, capture_id));
683         aco_option_register_custom(&cfg_info, "uuid_type", ACO_EXACT, global_options, "call-id", uuid_type_handler, 0);
684
685         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
686                 goto error;
687         }
688
689         return AST_MODULE_LOAD_SUCCESS;
690
691 error:
692         aco_info_destroy(&cfg_info);
693         return AST_MODULE_LOAD_DECLINE;
694 }
695
696 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HEPv3 API",
697         .support_level = AST_MODULE_SUPPORT_EXTENDED,
698         .load = load_module,
699         .unload = unload_module,
700         .reload = reload_module,
701         .load_pri = AST_MODPRI_APP_DEPEND,
702 );