Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
res_audiosocket.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2019, CyCore Systems, Inc
5 *
6 * Seán C McCord <scm@cycoresys.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
19/*! \file
20 *
21 * \brief AudioSocket support for Asterisk
22 *
23 * \author Seán C McCord <scm@cycoresys.com>
24 *
25 */
26
27/*** MODULEINFO
28 <support_level>extended</support_level>
29 ***/
30
31#include "asterisk.h"
32#include "errno.h"
33#include <uuid/uuid.h>
34#include <arpa/inet.h> /* For byte-order conversion. */
35
36#include "asterisk/file.h"
38#include "asterisk/channel.h"
39#include "asterisk/module.h"
40#include "asterisk/uuid.h"
42
43#define MODULE_DESCRIPTION "AudioSocket support functions for Asterisk"
44
45#define MAX_CONNECT_TIMEOUT_MSEC 2000
46
47/*!
48 * \internal
49 * \brief Attempt to complete the audiosocket connection.
50 *
51 * \param server Url that we are trying to connect to.
52 * \param addr Address that host was resolved to.
53 * \param netsockfd File descriptor of socket.
54 *
55 * \retval 0 when connection is succesful.
56 * \retval 1 when there is an error.
57 */
58static int handle_audiosocket_connection(const char *server,
59 const struct ast_sockaddr addr, const int netsockfd)
60{
61 struct pollfd pfds[1];
62 int res, conresult;
63 socklen_t reslen;
64
65 reslen = sizeof(conresult);
66
67 pfds[0].fd = netsockfd;
68 pfds[0].events = POLLOUT;
69
70 while ((res = ast_poll(pfds, 1, MAX_CONNECT_TIMEOUT_MSEC)) != 1) {
71 if (errno != EINTR) {
72 if (!res) {
73 ast_log(LOG_WARNING, "AudioSocket connection to '%s' timed"
74 "out after MAX_CONNECT_TIMEOUT_MSEC (%d) milliseconds.\n",
76 } else {
77 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", server,
78 strerror(errno));
79 }
80
81 return -1;
82 }
83 }
84
85 if (getsockopt(pfds[0].fd, SOL_SOCKET, SO_ERROR, &conresult, &reslen) < 0) {
86 ast_log(LOG_WARNING, "Connection to '%s' failed with error: %s\n",
87 ast_sockaddr_stringify(&addr), strerror(errno));
88 return -1;
89 }
90
91 if (conresult) {
92 ast_log(LOG_WARNING, "Connecting to '%s' failed for url '%s': %s\n",
93 ast_sockaddr_stringify(&addr), server, strerror(conresult));
94 return -1;
95 }
96
97 return 0;
98}
99
100const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
101{
102 int s = -1;
103 struct ast_sockaddr *addrs = NULL;
104 int num_addrs = 0, i = 0;
105
106 if (chan && ast_autoservice_start(chan) < 0) {
107 ast_log(LOG_WARNING, "Failed to start autoservice for channel "
108 "'%s'\n", ast_channel_name(chan));
109 goto end;
110 }
111
112 if (ast_strlen_zero(server)) {
113 ast_log(LOG_ERROR, "No AudioSocket server provided\n");
114 goto end;
115 }
116
117 if (!(num_addrs = ast_sockaddr_resolve(&addrs, server, PARSE_PORT_REQUIRE,
118 AST_AF_UNSPEC))) {
119 ast_log(LOG_ERROR, "Failed to resolve AudioSocket service using '%s' - "
120 "requires a valid hostname and port\n", server);
121 goto end;
122 }
123
124 /* Connect to AudioSocket service */
125 for (i = 0; i < num_addrs; i++) {
126
127 if (!ast_sockaddr_port(&addrs[i])) {
128 /* If there's no port, other addresses should have the
129 * same problem. Stop here.
130 */
131 ast_log(LOG_ERROR, "No port provided for '%s'\n",
132 ast_sockaddr_stringify(&addrs[i]));
133 s = -1;
134 goto end;
135 }
136
137 if ((s = ast_socket_nonblock(addrs[i].ss.ss_family, SOCK_STREAM,
138 IPPROTO_TCP)) < 0) {
139 ast_log(LOG_WARNING, "Unable to create socket: '%s'\n", strerror(errno));
140 continue;
141 }
142
143 if (ast_connect(s, &addrs[i]) && errno == EINPROGRESS) {
144
145 if (handle_audiosocket_connection(server, addrs[i], s)) {
146 close(s);
147 continue;
148 }
149
150 } else {
151 ast_log(LOG_ERROR, "Connection to '%s' failed with unexpected error: %s\n",
152 ast_sockaddr_stringify(&addrs[i]), strerror(errno));
153 close(s);
154 s = -1;
155 }
156
157 break;
158 }
159
160end:
161 if (addrs) {
162 ast_free(addrs);
163 }
164
165 if (chan && ast_autoservice_stop(chan) < 0) {
166 ast_log(LOG_WARNING, "Failed to stop autoservice for channel '%s'\n",
167 ast_channel_name(chan));
168 close(s);
169 return -1;
170 }
171
172 if (i == num_addrs) {
173 ast_log(LOG_ERROR, "Failed to connect to AudioSocket service\n");
174 close(s);
175 return -1;
176 }
177
178 return s;
179}
180
181const int ast_audiosocket_init(const int svc, const char *id)
182{
183 uuid_t uu;
184 int ret = 0;
185 uint8_t buf[3 + 16];
186
187 if (ast_strlen_zero(id)) {
188 ast_log(LOG_ERROR, "No UUID for AudioSocket\n");
189 return -1;
190 }
191
192 if (uuid_parse(id, uu)) {
193 ast_log(LOG_ERROR, "Failed to parse UUID '%s'\n", id);
194 return -1;
195 }
196
198 buf[1] = 0x00;
199 buf[2] = 0x10;
200 memcpy(buf + 3, uu, 16);
201
202 if (write(svc, buf, 3 + 16) != 3 + 16) {
203 ast_log(LOG_WARNING, "Failed to write data to AudioSocket because: %s\n", strerror(errno));
204 ret = -1;
205 }
206
207 return ret;
208}
209
210const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
211{
212 int datalen = f->datalen;
213 if (f->frametype == AST_FRAME_DTMF) {
214 datalen = 1;
215 }
216
217 {
218 uint8_t buf[3 + datalen];
219 uint16_t *length = (uint16_t *) &buf[1];
220
221 /* Audio format is 16-bit, 8kHz signed linear mono for dialplan app,
222 depends on agreed upon audio codec for channel driver interface. */
223 switch (f->frametype) {
224 case AST_FRAME_VOICE:
226 *length = htons(datalen);
227 memcpy(&buf[3], f->data.ptr, datalen);
228 break;
229 case AST_FRAME_DTMF:
231 buf[3] = (uint8_t) f->subclass.integer;
232 *length = htons(1);
233 break;
234 default:
235 ast_log(LOG_ERROR, "Unsupported frame type %d for AudioSocket\n", f->frametype);
236 return -1;
237 }
238
239 if (write(svc, buf, 3 + datalen) != 3 + datalen) {
240 ast_log(LOG_WARNING, "Failed to write data to AudioSocket because: %s\n", strerror(errno));
241 return -1;
242 }
243 }
244
245 return 0;
246}
247
249{
251}
252
254 int *const hangup)
255{
256 int i = 0, n = 0, ret = 0;
257 struct ast_frame f = {
259 .subclass.format = ast_format_slin,
260 .src = "AudioSocket",
261 .mallocd = AST_MALLOCD_DATA,
262 };
263 uint8_t header[3];
264 uint8_t *kind = &header[0];
265 uint16_t *length = (uint16_t *) &header[1];
266 uint8_t *data;
267
268 if (hangup) {
269 *hangup = 0;
270 }
271
272 n = read(svc, &header, 3);
273 if (n == -1) {
274 ast_log(LOG_WARNING, "Failed to read header from AudioSocket because: %s\n", strerror(errno));
275 return NULL;
276 }
277
278 if (n == 0 || *kind == AST_AUDIOSOCKET_KIND_HANGUP) {
279 /* Socket closure or requested hangup. */
280 if (hangup) {
281 *hangup = 1;
282 }
283 return NULL;
284 }
285
286 if (*kind != AST_AUDIOSOCKET_KIND_AUDIO) {
287 ast_log(LOG_ERROR, "Received AudioSocket message other than hangup or audio, refer to protocol specification for valid message types\n");
288 return NULL;
289 }
290
291 /* Swap endianess of length if needed. */
292 *length = ntohs(*length);
293 if (*length < 1) {
294 ast_log(LOG_ERROR, "Invalid message length received from AudioSocket server. \n");
295 return NULL;
296 }
297
298 data = ast_malloc(*length);
299 if (!data) {
300 ast_log(LOG_ERROR, "Failed to allocate for data from AudioSocket\n");
301 return NULL;
302 }
303
304 ret = 0;
305 n = 0;
306 i = 0;
307 while (i < *length) {
308 n = read(svc, data + i, *length - i);
309 if (n == -1) {
310 ast_log(LOG_ERROR, "Failed to read payload from AudioSocket: %s\n", strerror(errno));
311 ret = -1;
312 break;
313 }
314 if (n == 0) {
315 ast_log(LOG_ERROR, "Insufficient payload read from AudioSocket\n");
316 ret = -1;
317 break;
318 }
319 i += n;
320 }
321
322 if (ret != 0) {
323 ast_free(data);
324 return NULL;
325 }
326
327 f.data.ptr = data;
328 f.datalen = *length;
329 f.samples = *length / 2;
330
331 /* The frame steals data, so it doesn't need to be freed here */
332 return ast_frisolate(&f);
333}
334
335static int load_module(void)
336{
337 ast_verb(5, "Loading AudioSocket Support module\n");
339}
340
341static int unload_module(void)
342{
343 ast_verb(5, "Unloading AudioSocket Support module\n");
345}
346
348 .support_level = AST_MODULE_SUPPORT_EXTENDED,
349 .load = load_module,
350 .unload = unload_module,
351 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
static int hangup(void *data)
Definition: chan_pjsip.c:2520
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
char * end
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
@ PARSE_PORT_REQUIRE
#define AST_FRAME_DTMF
#define ast_frisolate(fr)
Makes a frame independent of any static storage.
#define AST_MALLOCD_DATA
@ AST_FRAME_VOICE
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:595
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
@ AST_AF_UNSPEC
Definition: netsock2.h:54
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
const int ast_audiosocket_init(const int svc, const char *id)
Send the initial message to an AudioSocket server.
const int ast_audiosocket_send_frame(const int svc, const struct ast_frame *f)
Send an Asterisk audio frame to an AudioSocket server.
struct ast_frame * ast_audiosocket_receive_frame(const int svc)
Receive an Asterisk frame from an AudioSocket server.
struct ast_frame * ast_audiosocket_receive_frame_with_hangup(const int svc, int *const hangup)
Receive an Asterisk frame from an AudioSocket server.
const int ast_audiosocket_connect(const char *server, struct ast_channel *chan)
Send the initial message to an AudioSocket server.
#define MAX_CONNECT_TIMEOUT_MSEC
static int load_module(void)
static int unload_module(void)
static int handle_audiosocket_connection(const char *server, const struct ast_sockaddr addr, const int netsockfd)
AudioSocket support functions.
@ AST_AUDIOSOCKET_KIND_AUDIO
Messages contains audio data, direction: Sent and received.
@ AST_AUDIOSOCKET_KIND_HANGUP
Message indicates the channel should be hung up, direction: Sent only.
@ AST_AUDIOSOCKET_KIND_UUID
Message contains the connection's UUID, direction: Received only.
@ AST_AUDIOSOCKET_KIND_DTMF
Message contains a DTMF digit, direction: Received only.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
Data structure associated with a single frame of data.
union ast_frame::@228 data
struct ast_frame_subclass subclass
enum ast_frame_type frametype
Socket address structure.
Definition: netsock2.h:97
struct sockaddr_storage ss
Definition: netsock2.h:98
#define ast_socket_nonblock(domain, type, protocol)
Create a non-blocking socket.
Definition: utils.h:1073
Universally unique identifier support.