Newer
Older
Matthew Fredrickson
committed
/*
* Asterisk -- A telephony toolkit for Linux.
*
* UDPTL support for T.38
*
* Copyright (C) 2005, Steve Underwood, partly based on RTP code which is
* Copyright (C) 1999-2006, Digium, Inc.
Matthew Fredrickson
committed
*
* Steve Underwood <steveu@coppice.org>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
Matthew Fredrickson
committed
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
Matthew Fredrickson
committed
*
* A license has been granted to Digium (via disclaimer) for the use of
* this code.
Matthew Fredrickson
committed
*/
/*!
* \file
*
* \brief UDPTL support for T.38 faxing
*
*
* \author Mark Spencer <markster@digium.com>, Steve Underwood <steveu@coppice.org>
*
* \page T38fax_udptl T.38 support :: UDPTL
* Asterisk supports T.38 fax passthrough, origination and termination. It does
* not support gateway operation. The only channel driver that supports T.38 at
* this time is chan_sip.
*
* UDPTL is handled very much like RTP. It can be reinvited to go directly between
* the endpoints, without involving Asterisk in the media stream.
*
* \b References:
* - chan_sip.c
* - udptl.c
* - app_fax.c
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Matthew Fredrickson
committed
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include "asterisk/udptl.h"
#include "asterisk/frame.h"
#include "asterisk/channel.h"
#include "asterisk/acl.h"
#include "asterisk/config.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/netsock.h"
Matthew Fredrickson
committed
#include "asterisk/cli.h"
#include "asterisk/unaligned.h"
#define UDPTL_MTU 1200
#if !defined(FALSE)
#define FALSE 0
#endif
#if !defined(TRUE)
#define TRUE (!FALSE)
#endif
static int udptlstart = 4500;
static int udptlend = 4599;
static int udptldebug; /*!< Are we debugging? */
static struct sockaddr_in udptldebugaddr; /*!< Debug packets to/from this host */
Matthew Fredrickson
committed
#ifdef SO_NO_CHECK
Matthew Fredrickson
committed
#endif
static int udptlfectype;
static int udptlfecentries;
static int udptlfecspan;
static int udptlmaxdatagram;
Matthew Fredrickson
committed
#define LOCAL_FAX_MAX_DATAGRAM 1400
Matthew Fredrickson
committed
#define MAX_FEC_ENTRIES 5
#define MAX_FEC_SPAN 5
#define UDPTL_BUF_MASK 15
typedef struct {
int buf_len;
uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
} udptl_fec_tx_buffer_t;
typedef struct {
int buf_len;
uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
int fec_len[MAX_FEC_ENTRIES];
uint8_t fec[MAX_FEC_ENTRIES][LOCAL_FAX_MAX_DATAGRAM];
int fec_span;
int fec_entries;
} udptl_fec_rx_buffer_t;
/*! \brief Structure for an UDPTL session */
Matthew Fredrickson
committed
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
struct ast_udptl {
int fd;
char resp;
struct ast_frame f[16];
unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
unsigned int lasteventseqn;
int nat;
int flags;
struct sockaddr_in us;
struct sockaddr_in them;
int *ioid;
struct sched_context *sched;
struct io_context *io;
void *data;
ast_udptl_callback callback;
int udptl_offered_from_local;
/*! This option indicates the error correction scheme used in transmitted UDPTL
packets. */
int error_correction_scheme;
/*! This option indicates the number of error correction entries transmitted in
UDPTL packets. */
int error_correction_entries;
/*! This option indicates the span of the error correction entries in transmitted
UDPTL packets (FEC only). */
int error_correction_span;
/*! This option indicates the maximum size of a UDPTL packet that can be accepted by
the remote device. */
int far_max_datagram_size;
/*! This option indicates the maximum size of a UDPTL packet that we are prepared to
accept. */
int local_max_datagram_size;
int verbose;
struct sockaddr_in far;
int tx_seq_no;
int rx_seq_no;
int rx_expected_seq_no;
udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1];
udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1];
};
static AST_RWLIST_HEAD_STATIC(protos, ast_udptl_protocol);
Matthew Fredrickson
committed
static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len);
static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len);
Matthew Fredrickson
committed
static inline int udptl_debug_test_addr(struct sockaddr_in *addr)
{
if (udptldebug == 0)
return 0;
if (udptldebugaddr.sin_addr.s_addr) {
if (((ntohs(udptldebugaddr.sin_port) != 0)
&& (udptldebugaddr.sin_port != addr->sin_port))
|| (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
Matthew Fredrickson
committed
}
return 1;
}
static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue)
{
if (*len >= limit)
return -1;
Matthew Fredrickson
committed
if ((buf[*len] & 0x80) == 0) {
*pvalue = buf[*len];
(*len)++;
return 0;
}
if ((buf[*len] & 0x40) == 0) {
Matthew Fredrickson
committed
return -1;
*pvalue = (buf[*len] & 0x3F) << 8;
(*len)++;
*pvalue |= buf[*len];
(*len)++;
return 0;
}
*pvalue = (buf[*len] & 0x3F) << 14;
(*len)++;
/* Indicate we have a fragment */
return 1;
}
/*- End of function --------------------------------------------------------*/
static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p_object, int *p_num_octets)
{
int octet_cnt;
int octet_idx;
int length;
Matthew Fredrickson
committed
int i;
const uint8_t **pbuf;
for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) {
if ((length = decode_length(buf, limit, len, &octet_cnt)) < 0)
Matthew Fredrickson
committed
return -1;
if (octet_cnt > 0) {
*p_num_octets += octet_cnt;
pbuf = &p_object[octet_idx];
i = 0;
/* Make sure the buffer contains at least the number of bits requested */
if ((*len + octet_cnt) > limit)
return -1;
*pbuf = &buf[*len];
*len += octet_cnt;
}
if (length == 0)
Matthew Fredrickson
committed
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
break;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static int encode_length(uint8_t *buf, int *len, int value)
{
int multiplier;
if (value < 0x80) {
/* 1 octet */
buf[*len] = value;
(*len)++;
return value;
}
if (value < 0x4000) {
/* 2 octets */
/* Set the first bit of the first octet */
buf[*len] = ((0x8000 | value) >> 8) & 0xFF;
(*len)++;
buf[*len] = value & 0xFF;
(*len)++;
return value;
}
/* Fragmentation */
multiplier = (value < 0x10000) ? (value >> 14) : 4;
Matthew Fredrickson
committed
/* Set the first 2 bits of the octet */
buf[*len] = 0xC0 | multiplier;
(*len)++;
return multiplier << 14;
}
/*- End of function --------------------------------------------------------*/
static int encode_open_type(uint8_t *buf, int buflen, int *len, const uint8_t *data, int num_octets)
Matthew Fredrickson
committed
{
int enclen;
int octet_idx;
uint8_t zero_byte;
/* If open type is of zero length, add a single zero byte (10.1) */
if (num_octets == 0) {
zero_byte = 0;
data = &zero_byte;
num_octets = 1;
}
/* Encode the open type */
for (octet_idx = 0; ; num_octets -= enclen, octet_idx += enclen) {
Matthew Fredrickson
committed
if ((enclen = encode_length(buf, len, num_octets)) < 0)
return -1;
if (enclen + *len > buflen) {
ast_log(LOG_ERROR, "Buffer overflow detected (%d + %d > %d)\n", enclen, *len, buflen);
return -1;
}
Matthew Fredrickson
committed
if (enclen > 0) {
memcpy(&buf[*len], &data[octet_idx], enclen);
*len += enclen;
}
if (enclen >= num_octets)
break;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
{
int stat1;
Matthew Fredrickson
committed
int stat2;
int i;
int j;
int k;
int l;
int m;
int x;
int limit;
int which;
int ptr;
int count;
int total_count;
int seq_no;
const uint8_t *ifp;
const uint8_t *data;
int ifp_len;
int repaired[16];
const uint8_t *bufs[16];
int lengths[16];
int span;
int entries;
int ifp_no;
ptr = 0;
ifp_no = 0;
Matthew Fredrickson
committed
/* Decode seq_number */
if (ptr + 2 > len)
return -1;
seq_no = (buf[0] << 8) | buf[1];
ptr += 2;
/* Break out the primary packet */
if ((stat1 = decode_open_type(buf, len, &ptr, &ifp, &ifp_len)) != 0)
Matthew Fredrickson
committed
return -1;
/* Decode error_recovery */
if (ptr + 1 > len)
return -1;
if ((buf[ptr++] & 0x80) == 0) {
/* Secondary packet mode for error recovery */
if (seq_no > s->rx_seq_no) {
/* We received a later packet than we expected, so we need to check if we can fill in the gap from the
secondary packets. */
total_count = 0;
do {
if ((stat2 = decode_length(buf, len, &ptr, &count)) < 0)
return -1;
if ((stat1 = decode_open_type(buf, len, &ptr, &bufs[total_count + i], &lengths[total_count + i])) != 0)
Matthew Fredrickson
committed
return -1;
}
total_count += count;
}
while (stat2 > 0);
/* Step through in reverse order, so we go oldest to newest */
Matthew Fredrickson
committed
if (seq_no - i >= s->rx_seq_no) {
/* This one wasn't seen before */
/* Decode the secondary IFP packet */
//fprintf(stderr, "Secondary %d, len %d\n", seq_no - i, lengths[i - 1]);
s->f[ifp_no].frametype = AST_FRAME_MODEM;
s->f[ifp_no].subclass = AST_MODEM_T38;
s->f[ifp_no].mallocd = 0;
Matthew Fredrickson
committed
s->f[ifp_no].datalen = lengths[i - 1];
Michiel van Baak
committed
s->f[ifp_no].data.ptr = (uint8_t *) bufs[i - 1];
Matthew Fredrickson
committed
s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0)
AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
Matthew Fredrickson
committed
ifp_no++;
}
}
}
}
else
{
/* FEC mode for error recovery */
/* Our buffers cannot tolerate overlength IFP packets in FEC mode */
if (ifp_len > LOCAL_FAX_MAX_DATAGRAM)
return -1;
/* Update any missed slots in the buffer */
for ( ; seq_no > s->rx_seq_no; s->rx_seq_no++) {
Matthew Fredrickson
committed
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
x = s->rx_seq_no & UDPTL_BUF_MASK;
s->rx[x].buf_len = -1;
s->rx[x].fec_len[0] = 0;
s->rx[x].fec_span = 0;
s->rx[x].fec_entries = 0;
}
x = seq_no & UDPTL_BUF_MASK;
memset(repaired, 0, sizeof(repaired));
/* Save the new IFP packet */
memcpy(s->rx[x].buf, ifp, ifp_len);
s->rx[x].buf_len = ifp_len;
repaired[x] = TRUE;
/* Decode the FEC packets */
/* The span is defined as an unconstrained integer, but will never be more
than a small value. */
if (ptr + 2 > len)
return -1;
if (buf[ptr++] != 1)
return -1;
span = buf[ptr++];
s->rx[x].fec_span = span;
/* The number of entries is defined as a length, but will only ever be a small
value. Treat it as such. */
if (ptr + 1 > len)
return -1;
entries = buf[ptr++];
s->rx[x].fec_entries = entries;
/* Decode the elements */
if ((stat1 = decode_open_type(buf, len, &ptr, &data, &s->rx[x].fec_len[i])) != 0)
Matthew Fredrickson
committed
return -1;
if (s->rx[x].fec_len[i] > LOCAL_FAX_MAX_DATAGRAM)
return -1;
/* Save the new FEC data */
memcpy(s->rx[x].fec[i], data, s->rx[x].fec_len[i]);
#if 0
fprintf(stderr, "FEC: ");
Matthew Fredrickson
committed
fprintf(stderr, "%02X ", data[j]);
fprintf(stderr, "\n");
#endif
Matthew Fredrickson
committed
/* See if we can reconstruct anything which is missing */
/* TODO: this does not comprehensively hunt back and repair everything that is possible */
for (l = x; l != ((x - (16 - span*entries)) & UDPTL_BUF_MASK); l = (l - 1) & UDPTL_BUF_MASK) {
Matthew Fredrickson
committed
if (s->rx[l].fec_len[0] <= 0)
continue;
Matthew Fredrickson
committed
limit = (l + m) & UDPTL_BUF_MASK;
for (which = -1, k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK) {
Matthew Fredrickson
committed
if (s->rx[k].buf_len <= 0)
Matthew Fredrickson
committed
}
if (which >= 0) {
/* Repairable */
Matthew Fredrickson
committed
s->rx[which].buf[j] = s->rx[l].fec[m][j];
for (k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK)
s->rx[which].buf[j] ^= (s->rx[k].buf_len > j) ? s->rx[k].buf[j] : 0;
Matthew Fredrickson
committed
}
s->rx[which].buf_len = s->rx[l].fec_len[m];
repaired[which] = TRUE;
}
}
}
/* Now play any new packets forwards in time */
for (l = (x + 1) & UDPTL_BUF_MASK, j = seq_no - UDPTL_BUF_MASK; l != x; l = (l + 1) & UDPTL_BUF_MASK, j++) {
Matthew Fredrickson
committed
if (repaired[l]) {
//fprintf(stderr, "Fixed packet %d, len %d\n", j, l);
s->f[ifp_no].frametype = AST_FRAME_MODEM;
s->f[ifp_no].subclass = AST_MODEM_T38;
s->f[ifp_no].mallocd = 0;
Matthew Fredrickson
committed
s->f[ifp_no].datalen = s->rx[l].buf_len;
Michiel van Baak
committed
s->f[ifp_no].data.ptr = s->rx[l].buf;
Matthew Fredrickson
committed
s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0)
AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
Matthew Fredrickson
committed
ifp_no++;
}
}
}
/* If packets are received out of sequence, we may have already processed this packet from the error
recovery information in a packet already received. */
if (seq_no >= s->rx_seq_no) {
Matthew Fredrickson
committed
/* Decode the primary IFP packet */
s->f[ifp_no].frametype = AST_FRAME_MODEM;
s->f[ifp_no].subclass = AST_MODEM_T38;
Matthew Fredrickson
committed
s->f[ifp_no].mallocd = 0;
Matthew Fredrickson
committed
s->f[ifp_no].datalen = ifp_len;
Michiel van Baak
committed
s->f[ifp_no].data.ptr = (uint8_t *) ifp;
Matthew Fredrickson
committed
s->f[ifp_no].offset = 0;
s->f[ifp_no].src = "UDPTL";
if (ifp_no > 0)
AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
Matthew Fredrickson
committed
}
s->rx_seq_no = seq_no + 1;
Matthew Fredrickson
committed
}
/*- End of function --------------------------------------------------------*/
static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len)
Matthew Fredrickson
committed
{
uint8_t fec[LOCAL_FAX_MAX_DATAGRAM * 2];
Matthew Fredrickson
committed
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
int i;
int j;
int seq;
int entry;
int entries;
int span;
int m;
int len;
int limit;
int high_tide;
seq = s->tx_seq_no & 0xFFFF;
/* Map the sequence number to an entry in the circular buffer */
entry = seq & UDPTL_BUF_MASK;
/* We save the message in a circular buffer, for generating FEC or
redundancy sets later on. */
s->tx[entry].buf_len = ifp_len;
memcpy(s->tx[entry].buf, ifp, ifp_len);
/* Build the UDPTLPacket */
len = 0;
/* Encode the sequence number */
buf[len++] = (seq >> 8) & 0xFF;
buf[len++] = seq & 0xFF;
/* Encode the primary IFP packet */
if (encode_open_type(buf, buflen, &len, ifp, ifp_len) < 0)
Matthew Fredrickson
committed
return -1;
/* Encode the appropriate type of error recovery information */
switch (s->error_correction_scheme)
{
case UDPTL_ERROR_CORRECTION_NONE:
/* Encode the error recovery type */
buf[len++] = 0x00;
/* The number of entries will always be zero, so it is pointless allowing
for the fragmented case here. */
if (encode_length(buf, &len, 0) < 0)
return -1;
break;
case UDPTL_ERROR_CORRECTION_REDUNDANCY:
/* Encode the error recovery type */
buf[len++] = 0x00;
if (s->tx_seq_no > s->error_correction_entries)
entries = s->error_correction_entries;
else
entries = s->tx_seq_no;
/* The number of entries will always be small, so it is pointless allowing
for the fragmented case here. */
if (encode_length(buf, &len, entries) < 0)
return -1;
/* Encode the elements */
Matthew Fredrickson
committed
j = (entry - i - 1) & UDPTL_BUF_MASK;
if (encode_open_type(buf, buflen, &len, s->tx[j].buf, s->tx[j].buf_len) < 0) {
if (option_debug) {
ast_log(LOG_DEBUG, "Encoding failed at i=%d, j=%d\n", i, j);
}
Matthew Fredrickson
committed
return -1;
Matthew Fredrickson
committed
}
break;
case UDPTL_ERROR_CORRECTION_FEC:
span = s->error_correction_span;
entries = s->error_correction_entries;
if (seq < s->error_correction_span*s->error_correction_entries) {
/* In the initial stages, wind up the FEC smoothly */
entries = seq/s->error_correction_span;
if (seq < s->error_correction_span)
span = 0;
}
/* Encode the error recovery type */
buf[len++] = 0x80;
/* Span is defined as an inconstrained integer, which it dumb. It will only
ever be a small value. Treat it as such. */
buf[len++] = 1;
buf[len++] = span;
/* The number of entries is defined as a length, but will only ever be a small
value. Treat it as such. */
buf[len++] = entries;
Matthew Fredrickson
committed
/* Make an XOR'ed entry the maximum length */
limit = (entry + m) & UDPTL_BUF_MASK;
high_tide = 0;
for (i = (limit - span*entries) & UDPTL_BUF_MASK; i != limit; i = (i + entries) & UDPTL_BUF_MASK) {
Matthew Fredrickson
committed
if (high_tide < s->tx[i].buf_len) {
Matthew Fredrickson
committed
fec[j] ^= s->tx[i].buf[j];
Matthew Fredrickson
committed
fec[j] = s->tx[i].buf[j];
high_tide = s->tx[i].buf_len;
} else {
Matthew Fredrickson
committed
fec[j] ^= s->tx[i].buf[j];
}
}
if (encode_open_type(buf, buflen, &len, fec, high_tide) < 0)
Matthew Fredrickson
committed
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
return -1;
}
break;
}
if (s->verbose)
fprintf(stderr, "\n");
s->tx_seq_no++;
return len;
}
int ast_udptl_fd(struct ast_udptl *udptl)
{
return udptl->fd;
}
void ast_udptl_set_data(struct ast_udptl *udptl, void *data)
{
udptl->data = data;
}
void ast_udptl_set_callback(struct ast_udptl *udptl, ast_udptl_callback callback)
{
udptl->callback = callback;
}
void ast_udptl_setnat(struct ast_udptl *udptl, int nat)
{
udptl->nat = nat;
}
static int udptlread(int *id, int fd, short events, void *cbdata)
{
struct ast_udptl *udptl = cbdata;
struct ast_frame *f;
if ((f = ast_udptl_read(udptl))) {
if (udptl->callback)
udptl->callback(udptl, f, udptl->data);
}
return 1;
}
struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
{
int res;
struct sockaddr_in sin;
socklen_t len;
uint16_t seqno = 0;
uint16_t *udptlheader;
len = sizeof(sin);
/* Cache where the header will go */
res = recvfrom(udptl->fd,
udptl->rawdata + AST_FRIENDLY_OFFSET,
sizeof(udptl->rawdata) - AST_FRIENDLY_OFFSET,
0,
(struct sockaddr *) &sin,
&len);
udptlheader = (uint16_t *)(udptl->rawdata + AST_FRIENDLY_OFFSET);
if (res < 0) {
if (errno != EAGAIN)
ast_log(LOG_WARNING, "UDPTL read error: %s\n", strerror(errno));
return &ast_null_frame;
Matthew Fredrickson
committed
}
/* Ignore if the other side hasn't been given an address yet. */
if (!udptl->them.sin_addr.s_addr || !udptl->them.sin_port)
return &ast_null_frame;
Matthew Fredrickson
committed
if (udptl->nat) {
/* Send to whoever sent to us */
if ((udptl->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
(udptl->them.sin_port != sin.sin_port)) {
memcpy(&udptl->them, &sin, sizeof(udptl->them));
ast_debug(1, "UDPTL NAT: Using address %s:%d\n", ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
Matthew Fredrickson
committed
}
}
if (udptl_debug_test_addr(&sin)) {
ast_verb(1, "Got UDPTL packet from %s:%d (type %d, seq %d, len %d)\n",
ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), 0, seqno, res);
Matthew Fredrickson
committed
}
#if 0
Russell Bryant
committed
printf("Got UDPTL packet from %s:%d (seq %d, len = %d)\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), seqno, res);
Matthew Fredrickson
committed
#endif
if (udptl_rx_packet(udptl, udptl->rawdata + AST_FRIENDLY_OFFSET, res) < 1)
return &ast_null_frame;
Matthew Fredrickson
committed
return &udptl->f[0];
}
void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local)
{
if (udptl)
udptl->udptl_offered_from_local = local;
else
ast_log(LOG_WARNING, "udptl structure is null\n");
}
int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl)
{
if (udptl)
return udptl->error_correction_scheme;
else {
ast_log(LOG_WARNING, "udptl structure is null\n");
return -1;
}
Matthew Fredrickson
committed
}
void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec)
{
if (udptl) {
switch (ec) {
case UDPTL_ERROR_CORRECTION_FEC:
udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
break;
case UDPTL_ERROR_CORRECTION_REDUNDANCY:
udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
break;
case UDPTL_ERROR_CORRECTION_NONE:
udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
break;
default:
ast_log(LOG_WARNING, "error correction parameter invalid\n");
};
} else
ast_log(LOG_WARNING, "udptl structure is null\n");
Matthew Fredrickson
committed
}
int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl)
{
if (udptl)
return udptl->local_max_datagram_size;
else {
ast_log(LOG_WARNING, "udptl structure is null\n");
return -1;
}
Matthew Fredrickson
committed
}
int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl)
{
if (udptl)
return udptl->far_max_datagram_size;
else {
ast_log(LOG_WARNING, "udptl structure is null\n");
return -1;
}
Matthew Fredrickson
committed
}
void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram)
{
if (udptl)
udptl->local_max_datagram_size = max_datagram;
else
ast_log(LOG_WARNING, "udptl structure is null\n");
Matthew Fredrickson
committed
}
void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram)
{
if (udptl)
udptl->far_max_datagram_size = max_datagram;
else
ast_log(LOG_WARNING, "udptl structure is null\n");
Matthew Fredrickson
committed
}
struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr addr)
{
struct ast_udptl *udptl;
int x;
int startplace;
int i;
long int flags;
if (!(udptl = ast_calloc(1, sizeof(*udptl))))
Matthew Fredrickson
committed
return NULL;
if (udptlfectype == 2)
udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
else if (udptlfectype == 1)
udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
else
udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
udptl->error_correction_span = udptlfecspan;
udptl->error_correction_entries = udptlfecentries;
udptl->far_max_datagram_size = udptlmaxdatagram;
udptl->local_max_datagram_size = udptlmaxdatagram;
memset(&udptl->rx, 0, sizeof(udptl->rx));
memset(&udptl->tx, 0, sizeof(udptl->tx));
Matthew Fredrickson
committed
udptl->rx[i].buf_len = -1;
udptl->tx[i].buf_len = -1;
}
udptl->them.sin_family = AF_INET;
udptl->us.sin_family = AF_INET;
if ((udptl->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
Tilghman Lesher
committed
ast_free(udptl);
Matthew Fredrickson
committed
ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
return NULL;
}
flags = fcntl(udptl->fd, F_GETFL);
fcntl(udptl->fd, F_SETFL, flags | O_NONBLOCK);
#ifdef SO_NO_CHECK
if (nochecksums)
setsockopt(udptl->fd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
#endif
/* Find us a place */
x = (udptlstart == udptlend) ? udptlstart : (ast_random() % (udptlend - udptlstart)) + udptlstart;
Matthew Fredrickson
committed
startplace = x;
for (;;) {
udptl->us.sin_port = htons(x);
udptl->us.sin_addr = addr;
if (bind(udptl->fd, (struct sockaddr *) &udptl->us, sizeof(udptl->us)) == 0)
break;
if (errno != EADDRINUSE) {
ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno));
close(udptl->fd);
Tilghman Lesher
committed
ast_free(udptl);
Matthew Fredrickson
committed
return NULL;
}
if (++x > udptlend)
x = udptlstart;
if (x == startplace) {
ast_log(LOG_WARNING, "No UDPTL ports remaining\n");
close(udptl->fd);
Tilghman Lesher
committed
ast_free(udptl);
Matthew Fredrickson
committed
return NULL;
}
}
if (io && sched && callbackmode) {
/* Operate this one in a callback mode */
udptl->sched = sched;
udptl->io = io;
udptl->ioid = ast_io_add(udptl->io, udptl->fd, udptlread, AST_IO_IN, udptl);
}
return udptl;
}
struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *io, int callbackmode)
{
struct in_addr ia;
memset(&ia, 0, sizeof(ia));
return ast_udptl_new_with_bindaddr(sched, io, callbackmode, ia);
}
int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos)
Matthew Fredrickson
committed
{
return ast_netsock_set_qos(udptl->fd, tos, cos, "UDPTL");
Matthew Fredrickson
committed
}
void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
{
udptl->them.sin_port = them->sin_port;
udptl->them.sin_addr = them->sin_addr;
}
void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
{
memset(them, 0, sizeof(*them));
Matthew Fredrickson
committed
them->sin_family = AF_INET;
them->sin_port = udptl->them.sin_port;
them->sin_addr = udptl->them.sin_addr;
}
void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us)
{
memcpy(us, &udptl->us, sizeof(udptl->us));
}
void ast_udptl_stop(struct ast_udptl *udptl)
{
memset(&udptl->them.sin_addr, 0, sizeof(udptl->them.sin_addr));
memset(&udptl->them.sin_port, 0, sizeof(udptl->them.sin_port));
}
void ast_udptl_destroy(struct ast_udptl *udptl)
{
if (udptl->ioid)
ast_io_remove(udptl->io, udptl->ioid);
if (udptl->fd > -1)
close(udptl->fd);
Tilghman Lesher
committed
ast_free(udptl);
Matthew Fredrickson
committed
}
int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
{
Matthew Fredrickson
committed
int len;
int res;
uint8_t buf[LOCAL_FAX_MAX_DATAGRAM * 2];
Matthew Fredrickson
committed
/* If we have no peer, return immediately */
if (s->them.sin_addr.s_addr == INADDR_ANY)
return 0;
/* If there is no data length, return immediately */
if (f->datalen == 0)
return 0;
if (f->frametype != AST_FRAME_MODEM) {
ast_log(LOG_WARNING, "UDPTL can only send T.38 data\n");
return -1;
}
/* Save seq_no for debug output because udptl_build_packet increments it */
seq = s->tx_seq_no & 0xFFFF;
Matthew Fredrickson
committed
/* Cook up the UDPTL packet, with the relevant EC info. */
len = udptl_build_packet(s, buf, sizeof(buf), f->data.ptr, f->datalen);
Matthew Fredrickson
committed
if (len > 0 && s->them.sin_port && s->them.sin_addr.s_addr) {
Matthew Fredrickson
committed
if ((res = sendto(s->fd, buf, len, 0, (struct sockaddr *) &s->them, sizeof(s->them))) < 0)
Russell Bryant
committed
ast_log(LOG_NOTICE, "UDPTL Transmission error to %s:%d: %s\n", ast_inet_ntoa(s->them.sin_addr), ntohs(s->them.sin_port), strerror(errno));
Matthew Fredrickson
committed
#if 0
Russell Bryant
committed
printf("Sent %d bytes of UDPTL data to %s:%d\n", res, ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
Matthew Fredrickson
committed
#endif
if (udptl_debug_test_addr(&s->them))
ast_verb(1, "Sent UDPTL packet to %s:%d (type %d, seq %d, len %d)\n",
Russell Bryant
committed
ast_inet_ntoa(s->them.sin_addr),
ntohs(s->them.sin_port), 0, seq, len);
Matthew Fredrickson
committed
}
return 0;
}
void ast_udptl_proto_unregister(struct ast_udptl_protocol *proto)
{
AST_RWLIST_WRLOCK(&protos);
AST_RWLIST_REMOVE(&protos, proto, list);
AST_RWLIST_UNLOCK(&protos);
Matthew Fredrickson
committed
}
int ast_udptl_proto_register(struct ast_udptl_protocol *proto)
{
struct ast_udptl_protocol *cur;
AST_RWLIST_WRLOCK(&protos);
AST_RWLIST_TRAVERSE(&protos, cur, list) {
Matthew Fredrickson
committed
if (cur->type == proto->type) {
ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
AST_RWLIST_UNLOCK(&protos);
Matthew Fredrickson
committed
return -1;
}
}
AST_RWLIST_INSERT_TAIL(&protos, proto, list);
AST_RWLIST_UNLOCK(&protos);
Matthew Fredrickson
committed
return 0;
}
static struct ast_udptl_protocol *get_proto(struct ast_channel *chan)
{
struct ast_udptl_protocol *cur = NULL;
Matthew Fredrickson
committed
AST_RWLIST_RDLOCK(&protos);
AST_RWLIST_TRAVERSE(&protos, cur, list) {
if (cur->type == chan->tech->type)
Matthew Fredrickson
committed
}
AST_RWLIST_UNLOCK(&protos);
return cur;
Matthew Fredrickson
committed
}
int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
{
struct ast_frame *f;
struct ast_channel *who;
struct ast_channel *cs[3];
struct ast_udptl *p0;
struct ast_udptl *p1;
struct ast_udptl_protocol *pr0;
struct ast_udptl_protocol *pr1;
struct sockaddr_in ac0;
struct sockaddr_in ac1;
struct sockaddr_in t0;
struct sockaddr_in t1;
void *pvt0;
void *pvt1;
int to;
ast_channel_lock(c0);
while (ast_channel_trylock(c1)) {
ast_channel_unlock(c0);
Matthew Fredrickson
committed
usleep(1);
ast_channel_lock(c0);
Matthew Fredrickson
committed
}
pr0 = get_proto(c0);
pr1 = get_proto(c1);
if (!pr0) {
ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
ast_channel_unlock(c0);