[svn-commits] jdixon: branch jdixon/chan_usbradio-1.4 r139009 - /team/jdixon/chan_usbradio-...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Tue Aug 19 23:45:15 CDT 2008
Author: jdixon
Date: Tue Aug 19 23:45:14 2008
New Revision: 139009
URL: http://svn.digium.com/view/asterisk?view=rev&rev=139009
Log:
Added chan_echolink
Added:
team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_echolink.c (with props)
Added: team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_echolink.c
URL: http://svn.digium.com/view/asterisk/team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_echolink.c?view=auto&rev=139009
==============================================================================
--- team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_echolink.c (added)
+++ team/jdixon/chan_usbradio-1.4/dev-1.0/channels/chan_echolink.c Tue Aug 19 23:45:14 2008
@@ -1,0 +1,1666 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Copyright (C) 2008, Scott Lawson/KI4LKF
+ * ScottLawson/KI4LKF <ham44865 at yahoo.com>
+ * Jim Dixon, WB6NIL <jim at lambdatel.com>
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Echolink channel driver for Asterisk
+ *
+ * \author Scott Lawson/KI4LKF <ham44865 at yahoo.com>
+ * \author Jim Dixon, WB6NIL <jim at lambdatel.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ ***/
+
+/* Version 0.2, 08/19/2008
+Echolink channel driver for Asterisk/app_rpt.
+This is a second attempt.
+A lot more has to be added,
+Here is what comes to mind first:
+
+---> does not send its station info.
+---> does not process chat text.
+---> recognizes a few remote text commands.
+---> no busy, deaf or mute.
+---> no capacity limits.
+---> no banned or privare station list.
+---> no admin list, only local 127.0.0.1 access.
+---> node number unused.
+---> no welcome text message.
+---> no welcome audio message.
+---> no login or connect timeouts.
+---> no max TX time limit.
+---> no activity reporting.
+---> no event notififications.
+---> no stats.
+---> no callsign prefix restrictions.
+---> no announcements on connects/disconnects.
+---> no loop detection.
+---> allows "doubles"
+
+Otherwise it works as an Echolink conference and it
+will accept multiple station connects.
+It also detects disconnect requests and
+removes dead stations on heartbeat timeouts.
+Default ports are 5198,5199.
+
+Remote text commands thru netcat:
+o.conip <IPaddress> (request a connect)
+o.dconip <IPaddress> (request a disconnect)
+o.rec (turn on/off recording)
+
+It is invoked as echolink/identifier (like el0 for example)
+Example:
+Under a node stanza in rpt.conf,
+rxchannel=echolink/el0
+
+The el0 or whatever you put there must match the stanza in the
+echolink.conf file.
+
+If the linux box is protected by a NAT router,
+leave the IP address as 0.0.0.0,
+do not use 127.0.0.1
+
+It would be better to have written all this code
+in C++ classes instead of C, but Asterisk wants C only.
+
+*/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <search.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+
+#define MAX_RXKEY_TIME 4
+
+/* 50 * 10 * 20ms iax2 = 10,000ms = 10 seconds heartbeat */
+#define KEEPALIVE_TIME 50 * 10
+#define BLOCKING_FACTOR 4
+#define GSM_FRAME_SIZE 33
+#define QUEUE_OVERLOAD_THRESHOLD_AST 25
+#define QUEUE_OVERLOAD_THRESHOLD_EL 20
+
+#define EL_IP_SIZE 15
+#define EL_CALL_SIZE 15
+#define EL_NAME_SIZE 31
+#define EL_PWD_SIZE 15
+#define EL_EMAIL_SIZE 31
+#define EL_QTH_SIZE 31
+#define EL_MAX_SERVERS 3
+#define EL_SERVERNAME_SIZE 63
+#define EL_MAX_INSTANCES 100
+
+/*
+ If you want to compile/link this code
+ on "BIG-ENDIAN" platforms, then
+ use this: #define RTP_BIG_ENDIAN
+ Have only tested this code on "little-endian"
+ platforms running Linux.
+*/
+
+static const char tdesc[] = "Echolink channel driver by KI4LKF";
+static int prefformat = AST_FORMAT_GSM;
+static char context[AST_MAX_EXTENSION] = "default";
+static char type[] = "echolink";
+
+struct el_instance
+{
+ char name[EL_NAME_SIZE + 1];
+ char mycall[EL_CALL_SIZE + 1];
+ char myname[EL_NAME_SIZE + 1];
+ char mypwd[EL_PWD_SIZE + 1];
+ char myemail[EL_EMAIL_SIZE + 1];
+ char myqth[EL_QTH_SIZE + 1];
+ char elservers[EL_MAX_SERVERS][EL_SERVERNAME_SIZE + 1];
+ char ipaddr[EL_IP_SIZE + 1];
+ char port[EL_IP_SIZE + 1];
+ int maxstns;
+ /* missed 10 heartbeats, you're out */
+ short rtcptimeout;
+ unsigned int mynode;
+ char fdr_file[FILENAME_MAX];
+ int audio_sock;
+ int ctrl_sock;
+ uint16_t audio_port;
+ uint16_t ctrl_port;
+ int fdr;
+ unsigned long seqno;
+} ;
+
+struct el_rxqast {
+ struct el_rxqast *qe_forw;
+ struct el_rxqast *qe_back;
+ char buf[GSM_FRAME_SIZE];
+};
+
+struct el_rxqel {
+ struct el_rxqel *qe_forw;
+ struct el_rxqel *qe_back;
+ char buf[BLOCKING_FACTOR * GSM_FRAME_SIZE];
+ char fromip[EL_IP_SIZE + 1];
+};
+
+struct el_pvt {
+ struct ast_channel *owner;
+ struct el_instance *instp;
+ char app[16];
+ char stream[80];
+ char txkey;
+ int rxkey;
+ int keepalive;
+ struct ast_frame fr;
+ int txindex;
+ struct el_rxqast rxqast;
+ struct el_rxqel rxqel;
+ struct ast_module_user *u;
+};
+
+struct rtcp_sdes_request_item {
+ unsigned char r_item;
+ char *r_text;
+};
+
+struct rtcp_sdes_request {
+ int nitems;
+ unsigned char ssrc[4];
+ struct rtcp_sdes_request_item item[10];
+};
+
+struct rtcp_common_t {
+#ifdef RTP_BIG_ENDIAN
+ uint8_t version:2;
+ uint8_t p:1;
+ uint8_t count:5;
+#else
+ uint8_t count:5;
+ uint8_t p:1;
+ uint8_t version:2;
+#endif
+ uint8_t pt:8;
+ uint16_t length;
+};
+
+struct rtcp_sdes_item_t {
+ uint8_t type;
+ uint8_t length;
+ char data[1];
+};
+
+struct rtcp_t {
+ struct rtcp_common_t common;
+ union {
+ struct {
+ uint32_t src[1];
+ } bye;
+
+ struct rtcp_sdes_t {
+ uint32_t src;
+ struct rtcp_sdes_item_t item[1];
+ } sdes;
+ } r;
+};
+
+/* Echolink audio packet heafer */
+struct gsmVoice_t {
+#ifdef RTP_BIG_ENDIAN
+ uint8_t version:2;
+ uint8_t pad:1;
+ uint8_t ext:1;
+ uint8_t csrc:4;
+ uint8_t marker:1;
+ uint8_t payt:7;
+#else
+ uint8_t csrc:4;
+ uint8_t ext:1;
+ uint8_t pad:1;
+ uint8_t version:2;
+ uint8_t payt:7;
+ uint8_t marker:1;
+#endif
+ uint16_t seqnum;
+ uint32_t time;
+ uint32_t ssrc;
+ unsigned char data[BLOCKING_FACTOR * GSM_FRAME_SIZE];
+};
+static struct gsmVoice_t audio_all_but_one;
+static struct gsmVoice_t audio_all;
+
+/* Echolink node details */
+/* Also each node in binary tree in memory */
+struct el_node {
+ char ip[EL_IP_SIZE + 1];
+ char call[EL_CALL_SIZE + 1];
+ char name[EL_NAME_SIZE + 1];
+ unsigned int nodenum; /* not used yet */
+ short countdown;
+ uint16_t seqnum;
+ struct el_instance *instp;
+};
+
+struct el_instance *instances[EL_MAX_INSTANCES];
+int ninstances = 0;
+
+/* testing existence of a node ... */
+static struct el_node el_node_test;
+
+/* binary search tree in memory, root node */
+static void *el_node_list = NULL;
+
+/* Echolink registration thread */
+static pthread_t el_register_thread;
+static char run_forever = (char)1;
+
+static char *config = "echolink.conf";
+
+#ifdef OLD_ASTERISK
+#define ast_free free
+#define ast_malloc malloc
+#endif
+
+static struct ast_channel *el_request(const char *type, int format, void *data, int *cause);
+static int el_call(struct ast_channel *ast, char *dest, int timeout);
+static int el_hangup(struct ast_channel *ast);
+static struct ast_frame *el_xread(struct ast_channel *ast);
+static int el_xwrite(struct ast_channel *ast, struct ast_frame *frame);
+static int el_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen);
+static int el_digit_begin(struct ast_channel *c, char digit);
+static int el_digit_end(struct ast_channel *c, char digit, unsigned int duratiion);
+static int el_text(struct ast_channel *c, const char *text);
+
+static int rtcp_make_sdes(unsigned char *pkt, int pktLen, char *call, char *name);
+static int rtcp_make_bye(unsigned char *p, char *reason);
+static void parse_sdes(unsigned char *packet, struct rtcp_sdes_request *r);
+static void copy_sdes_item(char *source, char *dest, int destlen);
+static int is_rtcp_bye(unsigned char *p, int len);
+static int is_rtcp_sdes(unsigned char *p, int len);
+/* remove binary tree functions if Asterisk has similar functionality */
+static int compare_ip(const void *pa, const void *pb);
+static void send_audio_all_but_one(const void *nodep, const VISIT which, const int depth);
+static void send_audio_all(const void *nodep, const VISIT which, const int depth);
+static void send_heartbeat(const void *nodep, const VISIT which, const int depth);
+static void print_users(const void *nodep, const VISIT which, const int depth);
+static void free_node(void *nodep);
+static void process_cmd(char *buf, ssize_t recvlen, struct el_node *from,
+ struct el_instance *instp);
+static int find_delete(struct el_node *key);
+static int sendcmd(char *server,struct el_instance *instp);
+
+/* remove writen if Asterisk has similar functionality */
+static int writen(int fd, char *ptr, int nbytes);
+
+static const struct ast_channel_tech el_tech = {
+ .type = type,
+ .description = tdesc,
+ .capabilities = AST_FORMAT_GSM,
+ .requester = el_request,
+ .call = el_call,
+ .hangup = el_hangup,
+ .read = el_xread,
+ .write = el_xwrite,
+ .indicate = el_indicate,
+ .send_text = el_text,
+ .send_digit_begin = el_digit_begin,
+ .send_digit_end = el_digit_end,
+};
+
+static int rtcp_make_sdes(unsigned char *pkt, int pktLen, char *call, char *name)
+{
+ unsigned char zp[1500];
+ unsigned char *p = zp;
+ struct rtcp_t *rp;
+ unsigned char *ap;
+ char line[EL_CALL_SIZE + EL_NAME_SIZE + 1];
+ int l, hl;
+
+ hl = 0;
+ *p++ = 3 << 6;
+ *p++ = 201;
+ *p++ = 0;
+ *p++ = 1;
+ *((long *) p) = htonl(0);
+ p += 4;
+ hl = 8;
+
+ rp = (struct rtcp_t *) p;
+ *((short *) p) = htons((3 << 14) | 202 | (1 << 8));
+ rp->r.sdes.src = htonl(0);
+ ap = (unsigned char *) rp->r.sdes.item;
+
+ strncpy(line,"CALLSIGN",EL_CALL_SIZE + EL_NAME_SIZE);
+ *ap++ = 1;
+ *ap++ = l = strlen(line);
+ memcpy(ap,line,l);
+ ap += l;
+
+ snprintf(line,EL_CALL_SIZE + EL_NAME_SIZE,"%s %s",call,name);
+ *ap++ = 2;
+ *ap++ = l = strlen(line);
+ memcpy(ap,line,l);
+ ap += l;
+
+ *ap++ = 0;
+ *ap++ = 0;
+ l = ap - p;
+
+ rp->common.length = htons(((l + 3) / 4) - 1);
+ l = hl + ((ntohs(rp->common.length) + 1) * 4);
+
+ int pl = (l & 4) ? l : l + 4;
+
+ if (pl > l) {
+ int pad = pl - l;
+ memset(zp + l, '\0', pad);
+ zp[pl - 1] = pad;
+ p[0] |= 0x20;
+ rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4));
+ l = pl;
+ }
+
+ if (l > pktLen)
+ return 0;
+ memcpy(pkt,zp,l);
+ return l;
+}
+
+static int rtcp_make_bye(unsigned char *p, char *reason)
+{
+ struct rtcp_t *rp;
+ unsigned char *ap, *zp;
+ int l, hl;
+
+ zp = p;
+ hl = 0;
+
+ *p++ = 3 << 6;
+ *p++ = 201;
+ *p++ = 0;
+ *p++ = 1;
+ *((long *) p) = htonl(0);
+ p += 4;
+ hl = 8;
+
+ rp = (struct rtcp_t *)p;
+ *((short *) p) = htons((3 << 14) | 203 | (1 << 8));
+ rp->r.bye.src[0] = htonl(0);
+ ap = (unsigned char *) rp->r.sdes.item;
+ l = 0;
+ if (reason != NULL) {
+ l = strlen(reason);
+ if (l > 0) {
+ *ap++ = l;
+ memcpy(ap,reason,l);
+ ap += l;
+ }
+ }
+ while ((ap - p) & 3)
+ *ap++ = 0;
+ l = ap - p;
+ rp->common.length = htons((l / 4) - 1);
+ l = hl + ((ntohs(rp->common.length) + 1) * 4);
+
+ int pl = (l & 4) ? l : l + 4;
+ if (pl > l) {
+ int pad = pl - l;
+ memset(zp + l, '\0', pad);
+ zp[pl - 1] = pad;
+ p[0] |= 0x20;
+ rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4));
+ l = pl;
+ }
+ return l;
+}
+
+static void parse_sdes(unsigned char *packet, struct rtcp_sdes_request *r)
+{
+ int i;
+ unsigned char *p = packet;
+
+ for (i = 0; i < r->nitems; i++)
+ r->item[i].r_text = NULL;
+
+ while ((p[0] >> 6 & 3) == 3 || (p[0] >> 6 & 3) == 1) {
+ if ((p[1] == 202) && ((p[0] & 0x1F) > 0)) {
+ unsigned char *cp = p + 8,
+ *lp = cp + (ntohs(*((short *) (p + 2))) + 1) * 4;
+ memcpy(r->ssrc, p + 4, 4);
+ while (cp < lp) {
+ unsigned char itype = *cp;
+ if (itype == 0)
+ break;
+
+ for (i = 0; i < r->nitems; i++) {
+ if (r->item[i].r_item == itype &&
+ r->item[i].r_text == NULL) {
+ r->item[i].r_text = (char *) cp;
+ break;
+ }
+ }
+ cp += cp[1] + 2;
+ }
+ break;
+ }
+ p += (ntohs(*((short *) (p + 2))) + 1) * 4;
+ }
+ return;
+}
+
+static void copy_sdes_item(char *source, char *dest, int destlen)
+{
+ int len = source[1] & 0xFF;
+ if (len > destlen)
+ len = destlen;
+ memcpy(dest,source + 2, len);
+ dest[len] = 0;
+ return;
+}
+
+static int is_rtcp_bye(unsigned char *p, int len)
+{
+ unsigned char *end;
+ int sawbye = 0;
+
+ if ((((p[0] >> 6) & 3) != 3 && ((p[0] >> 6) & 3) != 1) ||
+ ((p[0] & 0x20) != 0) ||
+ ((p[1] != 200) && (p[1] != 201)))
+ return 0;
+
+ end = p + len;
+
+ do {
+ if (p[1] == 203)
+ sawbye = 1;
+
+ p += (ntohs(*((short *) (p + 2))) + 1) * 4;
+ } while (p < end && (((p[0] >> 6) & 3) == 3));
+
+ return sawbye;
+}
+
+static int is_rtcp_sdes(unsigned char *p, int len)
+{
+ unsigned char *end;
+ int sawsdes = 0;
+
+ if ((((p[0] >> 6) & 3) != 3 && ((p[0] >> 6) & 3) != 1) ||
+ ((p[0] & 0x20) != 0) ||
+ ((p[1] != 200) && (p[1] != 201)))
+ return 0;
+
+ end = p + len;
+ do {
+ if (p[1] == 202)
+ sawsdes = 1;
+
+ p += (ntohs(*((short *) (p + 2))) + 1) * 4;
+ } while (p < end && (((p[0] >> 6) & 3) == 3));
+
+ return sawsdes;
+}
+
+static int el_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct el_pvt *p;
+
+ p = ast->tech_pvt;
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "el_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+ /* When we call, it just works, really, there's no destination... Just
+ ring the phone and wait for someone to answer */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name);
+
+ ast_setstate(ast,AST_STATE_UP);
+ return 0;
+}
+
+static void el_destroy(struct el_pvt *p)
+{
+ ast_module_user_remove(p->u);
+ ast_free(p);
+}
+
+static struct el_pvt *el_alloc(void *data)
+{
+ struct el_pvt *p;
+ int n;
+ /* int flags = 0; */
+ char stream[256];
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(idstr);
+ );
+
+ if (ast_strlen_zero(data)) return NULL;
+
+ AST_STANDARD_APP_ARGS(args,data);
+
+ for(n = 0; n < ninstances; n++)
+ {
+ if (!strcmp(instances[n]->name,args.idstr)) break;
+ }
+ if (n >= ninstances) return NULL;
+
+ p = ast_malloc(sizeof(struct el_pvt));
+ if (p) {
+ memset(p, 0, sizeof(struct el_pvt));
+
+ sprintf(stream,"%s-%lu",args.idstr,instances[n]->seqno++);
+ strcpy(p->stream,stream);
+ p->rxqast.qe_forw = &p->rxqast;
+ p->rxqast.qe_back = &p->rxqast;
+
+ p->rxqel.qe_forw = &p->rxqel;
+ p->rxqel.qe_back = &p->rxqel;
+
+ p->instp = instances[n];
+ }
+ return p;
+}
+
+static int el_hangup(struct ast_channel *ast)
+{
+ struct el_pvt *p;
+ p = ast->tech_pvt;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "el_hangup(%s)\n", ast->name);
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ el_destroy(p);
+ ast->tech_pvt = NULL;
+ ast_setstate(ast, AST_STATE_DOWN);
+ return 0;
+}
+
+static int el_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen)
+{
+ struct el_pvt *p = ast->tech_pvt;
+
+ switch (cond) {
+ case AST_CONTROL_RADIO_KEY:
+ p->txkey = 1;
+ break;
+ case AST_CONTROL_RADIO_UNKEY:
+ p->txkey = 0;
+ break;
+ case AST_CONTROL_HANGUP:
+ return -1;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int el_text(struct ast_channel *ast, const char *text)
+{
+ return 0;
+}
+
+static int el_digit_begin(struct ast_channel *ast, char digit)
+{
+ return 0;
+}
+
+static int el_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ return 0;
+}
+
+static int compare_ip(const void *pa, const void *pb)
+{
+ return strncmp(((struct el_node *)pa)->ip,((struct el_node *)pb)->ip,EL_IP_SIZE);
+}
+
+/* Echolink ---> Echolink */
+void send_audio_all_but_one(const void *nodep, const VISIT which, const int depth)
+{
+ struct sockaddr_in sin;
+ struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+ if ((which == leaf) || (which == postorder)) {
+ if (strncmp((*(struct el_node **)nodep)->ip, el_node_test.ip,EL_IP_SIZE) != 0) {
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(instp->audio_port);
+ sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+
+ audio_all_but_one.version = 3;
+ audio_all_but_one.pad = 0;
+ audio_all_but_one.ext = 0;
+ audio_all_but_one.csrc = 0;
+ audio_all_but_one.marker = 0;
+ audio_all_but_one.payt = 3;
+ audio_all_but_one.seqnum = htons((*(struct el_node **)nodep)->seqnum++);
+ audio_all_but_one.time = htonl(0);
+ audio_all_but_one.ssrc = instp->mynode;
+
+ /*
+ ast_log(LOG_NOTICE, "sending to %s(%s)\n",
+ (*(struct el_node **)nodep)->call, (*(struct el_node **)nodep)->ip);
+ */
+
+ sendto(instp->audio_sock, (char *)&audio_all_but_one, sizeof(audio_all_but_one),
+ 0,(struct sockaddr *)&sin,sizeof(sin));
+ }
+ }
+}
+
+/* Asterisk ---> Echolink */
+void send_audio_all(const void *nodep, const VISIT which, const int depth)
+{
+ struct sockaddr_in sin;
+ struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+ if ((which == leaf) || (which == postorder)) {
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(instp->audio_port);
+ sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+
+ audio_all.version = 3;
+ audio_all.pad = 0;
+ audio_all.ext = 0;
+ audio_all.csrc = 0;
+ audio_all.marker = 0;
+ audio_all.payt = 3;
+ audio_all.seqnum = htons((*(struct el_node **)nodep)->seqnum++);
+ audio_all.time = htonl(0);
+ audio_all.ssrc = instp->mynode;
+
+ /*
+ ast_log(LOG_NOTICE, "sending to %s(%s)\n",
+ (*(struct el_node **)nodep)->call, (*(struct el_node **)nodep)->ip);
+ */
+ sendto(instp->audio_sock, (char *)&audio_all, sizeof(audio_all),
+ 0,(struct sockaddr *)&sin,sizeof(sin));
+ }
+}
+
+static void print_users(const void *nodep, const VISIT which, const int depth)
+{
+ if ((which == leaf) || (which == postorder)) {
+ ast_log(LOG_NOTICE,"call=%s,ip=%s,name=%s\n",
+ (*(struct el_node **)nodep)->call,
+ (*(struct el_node **)nodep)->ip,
+ (*(struct el_node **)nodep)->name);
+ }
+}
+
+static void send_heartbeat(const void *nodep, const VISIT which, const int depth)
+{
+ struct sockaddr_in sin;
+ unsigned char sdes_packet[256];
+ int sdes_length;
+ struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+ if ((which == leaf) || (which == postorder)) {
+ if ((*(struct el_node **)nodep)->countdown >= 0)
+ (*(struct el_node **)nodep)->countdown --;
+
+ if ((*(struct el_node **)nodep)->countdown < 0) {
+ strncpy(el_node_test.ip,(*(struct el_node **)nodep)->ip,EL_IP_SIZE);
+ strncpy(el_node_test.call,(*(struct el_node **)nodep)->call,EL_CALL_SIZE);
+ ast_log(LOG_NOTICE,"countdown for %s(%s) negative\n",el_node_test.call,el_node_test.ip);
+ }
+ sdes_length = rtcp_make_sdes(sdes_packet,sizeof(sdes_packet),
+ instp->mycall,instp->myname);
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(instp->ctrl_port);
+ sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+ sendto(instp->ctrl_sock, sdes_packet, sdes_length,
+ 0,(struct sockaddr *)&sin,sizeof(sin));
+ }
+}
+
+static void free_node(void *nodep)
+{
+}
+
+static int find_delete(struct el_node *key)
+{
+ int found = 0;
+ struct el_node **found_key = NULL;
+
+ found_key = (struct el_node **)tfind(key, &el_node_list, compare_ip);
+ if (found_key) {
+ ast_log(LOG_NOTICE,"...removing %s(%s)\n", (*found_key)->call, (*found_key)->ip);
+ found = 1;
+ tdelete(key, &el_node_list, compare_ip);
+ }
+ return found;
+}
+
+static void process_cmd(char *buf, ssize_t recvlen, struct el_node *from,
+ struct el_instance *instp)
+{
+ char *cmd = NULL;
+ char *arg1 = NULL;
+ char *arg2 = NULL;
+ char *arg3 = NULL;
+
+ char delim = ' ';
+ char *saveptr;
+ char *ptr;
+ struct sockaddr_in sin;
+ unsigned char pack[256];
+ int pack_length;
+ unsigned short i;
+ struct el_node key;
+
+ if (strncmp(from->ip,"127.0.0.1",EL_IP_SIZE) != 0)
+ return;
+ ptr = strchr(buf, (int)'\r');
+ if (ptr)
+ *ptr = '\0';
+ ptr = strchr(buf, (int)'\n');
+ if (ptr)
+ *ptr = '\0';
+
+ /* all commands with no arguments go first */
+
+ if (strcmp(buf,"o.users") == 0) {
+ twalk(el_node_list, print_users);
+ return;
+ }
+
+ if (strcmp(buf, "o.rec") == 0) {
+ if (instp->fdr >= 0) {
+ close(instp->fdr);
+ instp->fdr = -1;
+ ast_log(LOG_NOTICE, "rec stopped\n");
+ }
+ else {
+ instp->fdr = open(instp->fdr_file, O_CREAT | O_WRONLY | O_APPEND | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ if (instp->fdr >= 0)
+ ast_log(LOG_NOTICE, "rec into %s started\n", instp->fdr_file);
+ }
+ return;
+ }
+
+ cmd = strtok_r(buf, &delim, &saveptr);
+ if (!cmd)
+ return;
+
+ /* This version: up to 3 parameters */
+ arg1 = strtok_r(NULL, &delim, &saveptr);
+ arg2 = strtok_r(NULL, &delim, &saveptr);
+ arg3 = strtok_r(NULL, &delim, &saveptr);
+
+ if ((strcmp(cmd, "o.conip") == 0) ||
+ (strcmp(cmd, "o.dconip") == 0)) {
+ if (!arg1)
+ return;
+
+ if (strcmp(cmd, "o.conip") == 0)
+ pack_length = rtcp_make_sdes(pack,sizeof(pack),instp->mycall,instp->myname);
+ else
+ pack_length = rtcp_make_bye(pack,"bye");
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(instp->ctrl_port);
+ sin.sin_addr.s_addr = inet_addr(arg1);
+
+ if (strcmp(cmd, "o.dconip") == 0) {
+ strncpy(key.ip,arg1,EL_IP_SIZE);
+ if (find_delete(&key)) {
+ for (i = 0; i < 20; i++)
+ sendto(instp->ctrl_sock, pack, pack_length,
+ 0,(struct sockaddr *)&sin,sizeof(sin));
+ ast_log(LOG_NOTICE,"disconnect request sent to %s\n",key.ip);
+ }
+ else
+ ast_log(LOG_NOTICE, "Did not find ip=%s to request disconnect\n",key.ip);
+ }
+ else {
+ for (i = 0; i < 20; i++)
+ sendto(instp->ctrl_sock, pack, pack_length,
+ 0,(struct sockaddr *)&sin,sizeof(sin));
+ ast_log(LOG_NOTICE,"connect request sent to %s\n", arg1);
+ }
+ return;
+ }
+
+ return;
+}
+
+static struct ast_frame *el_xread(struct ast_channel *ast)
+{
+ struct el_pvt *p = ast->tech_pvt;
+ struct el_instance *instp = p->instp;
+ char buf[1024];
+ struct sockaddr_in sin;
+ int i;
+ /* struct ast_frame fr; */
+ struct el_rxqast *qpast;
+ struct el_rxqel *qpel;
+
+ socklen_t fromlen;
+ ssize_t recvlen;
+ size_t len;
+
+ struct el_node *el_node_key = NULL;
+ struct el_node **found_key = NULL;
+ struct rtcp_sdes_request items;
+ char call_name[128];
+ char *call = NULL;
+ char *name = NULL;
+ char *ptr = NULL;
+
+ len = 0;
+ ioctl(instp->ctrl_sock,FIONREAD,&len);
+ if (len > 0) {
+ fromlen = sizeof(struct sockaddr_in);
+ recvlen = recvfrom(instp->ctrl_sock,
+ buf,
+ sizeof(buf) - 1,
+ 0,
+ (struct sockaddr *)&sin,&fromlen);
+ if (recvlen > 0) {
+ buf[recvlen] = '\0';
+#ifdef OLD_ASTERISK
+ ast_inet_ntoa(el_node_test.ip,EL_IP_SIZE,sin.sin_addr);
+#else
+ strncpy(el_node_test.ip,ast_inet_ntoa(sin.sin_addr),EL_IP_SIZE);
+#endif
+ if (is_rtcp_sdes((unsigned char *)buf,recvlen)) {
+ call_name[0] = '\0';
+ items.nitems = 1;
+ items.item[0].r_item = 2;
+ items.item[0].r_text = NULL;
+ parse_sdes((unsigned char *)buf,&items);
+ if (items.item[0].r_text != NULL)
+ copy_sdes_item(items.item[0].r_text, call_name, 127);
+
+ if (call_name[0] != '\0') {
+ ptr = strchr(call_name, (int)' ');
+ if (ptr) {
+ *ptr = '\0';
+ call = call_name;
+ name = ptr + 1;
+ found_key = (struct el_node **)tfind(&el_node_test, &el_node_list, compare_ip);
+ if (found_key) {
+ (*found_key)->countdown = instp->rtcptimeout;
+
+ /* different callsigns behind a NAT router, running -L, -R, ... */
+ if (strncmp((*found_key)->call,call,EL_CALL_SIZE) != 0) {
+ ast_log(LOG_NOTICE,"Call changed from %s to %s\n",
+ (*found_key)->call,call);
+ strncpy((*found_key)->call,call,EL_CALL_SIZE);
+ }
+
+ if (strncmp((*found_key)->name, name, EL_NAME_SIZE) != 0)
+ strncpy((*found_key)->name,name,EL_NAME_SIZE);
+ }
+ else {
+ el_node_key = (struct el_node *)malloc(sizeof(struct el_node));
+ if (el_node_key) {
+ strncpy(el_node_key->call,call,EL_CALL_SIZE);
+ strncpy(el_node_key->ip, el_node_test.ip, EL_IP_SIZE);
+
+ strncpy(el_node_key->name,name,EL_NAME_SIZE);
+ el_node_key->nodenum = 0;
+ el_node_key->countdown = instp->rtcptimeout;
+ el_node_key->seqnum = 1;
+ el_node_key->instp = instp;
+
+ if (tsearch(el_node_key, &el_node_list, compare_ip))
+ ast_log(LOG_NOTICE, "new CALL=%s,ip=%s,name=%s\n",
+ el_node_key->call,el_node_key->ip,el_node_key->name);
+ else {
+ ast_log(LOG_ERROR, "tsearch() failed to add CALL=%s,ip=%s,name=%s\n",
+ el_node_key->call,el_node_key->ip,el_node_key->name);
+ free(el_node_key);
+ }
+ }
+ else
+ ast_log(LOG_ERROR,"malloc() failed for new CALL=%s, ip=%s\n",
+ call,el_node_test.ip);
+ }
+ }
+ }
+ }
+ else
+ if (is_rtcp_bye((unsigned char *)buf,recvlen))
+ {
+ if (find_delete(&el_node_test))
+ ast_log(LOG_NOTICE,"disconnect from ip=%s\n",el_node_test.ip);
+ else
+ ast_log(LOG_NOTICE, "ip=%s,busy, denied or multiple disconnects\n",el_node_test.ip);
+ }
+ }
+ }
+
+ len = 0;
+ ioctl(instp->audio_sock,FIONREAD,&len);
+ if (len > 0)
+ {
+ fromlen = sizeof(struct sockaddr_in);
+ recvlen = recvfrom(instp->audio_sock,
+ buf,
+ sizeof(buf) - 1,
+ 0,
+ (struct sockaddr *)&sin,&fromlen);
+ if (recvlen > 0) {
+ buf[recvlen] = '\0';
+#ifdef OLD_ASTERISK
+ ast_inet_ntoa(el_node_test.ip,EL_IP_SIZE,sin.sin_addr);
+#else
+ strncpy(el_node_test.ip,ast_inet_ntoa(sin.sin_addr),EL_IP_SIZE);
+#endif
+ if (buf[0] == 0x6f) {
+ process_cmd(buf,recvlen,&el_node_test,instp);
+ }
+ else {
+ found_key = (struct el_node **)tfind(&el_node_test, &el_node_list, compare_ip);
+ if (found_key) {
+ (*found_key)->countdown = instp->rtcptimeout;
+ if (recvlen == sizeof(struct gsmVoice_t)) {
+ if ((((struct gsmVoice_t *)buf)->version == 3) &&
+ (((struct gsmVoice_t *)buf)->payt == 3)) {
+
+ /* break them up for Asterisk */
+ for (i = 0; i < BLOCKING_FACTOR; i++) {
+ qpast = ast_malloc(sizeof(struct el_rxqast));
+ if (!qpast) {
+ ast_log(LOG_NOTICE,"Cannot malloc for qpast\n");
+ break;
+ }
+ memcpy(qpast->buf,((struct gsmVoice_t *)buf)->data + (GSM_FRAME_SIZE * i),GSM_FRAME_SIZE);
+ insque((struct qelem *)qpast,(struct qelem *)p->rxqast.qe_back);
+ }
+
+ /* need complete packet and IP address for Echolink */
+ qpel = ast_malloc(sizeof(struct el_rxqel));
+ if (!qpel)
+ ast_log(LOG_NOTICE,"Cannot malloc for qpel\n");
+ else {
+ memcpy(qpel->buf,((struct gsmVoice_t *)buf)->data,BLOCKING_FACTOR * GSM_FRAME_SIZE);
+ strncpy(qpel->fromip,el_node_test.ip,EL_IP_SIZE);
+ insque((struct qelem *)qpel,(struct qelem *)p->rxqel.qe_back);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ p->fr.frametype = 0;
+ p->fr.subclass = 0;
+ p->fr.datalen = 0;
+ p->fr.samples = 0;
+ p->fr.data = NULL;
+ p->fr.src = type;
+ p->fr.offset = 0;
+ p->fr.mallocd=0;
+ p->fr.delivery.tv_sec = 0;
+ p->fr.delivery.tv_usec = 0;
+ return &p->fr;
+}
+
+static int el_xwrite(struct ast_channel *ast, struct ast_frame *frame)
+{
+ int bye_length;
+ unsigned char bye[50];
+ unsigned short i;
+ struct sockaddr_in sin;
+ struct el_pvt *p = ast->tech_pvt;
+ struct el_instance *instp = p->instp;
+ struct ast_frame fr;
+ struct el_rxqast *qpast;
+ int n;
+ struct el_rxqel *qpel;
+ int m;
+ char buf[GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET];
+
+ if (ast->_state != AST_STATE_UP) {
+ return 0;
+ }
+
+ if (frame->frametype != AST_FRAME_VOICE) return 0;
+
+ /* Echolink to Asterisk */
+ if (p->rxqast.qe_forw != &p->rxqast) {
+ for(n = 0,qpast = p->rxqast.qe_forw; qpast != &p->rxqast; qpast = qpast->qe_forw) {
+ n++;
+ }
+ if (n > QUEUE_OVERLOAD_THRESHOLD_AST) {
+ while(p->rxqast.qe_forw != &p->rxqast) {
+ qpast = p->rxqast.qe_forw;
+ remque((struct qelem *)qpast);
+ ast_free(qpast);
+ }
+ if (p->rxkey) p->rxkey = 1;
+ } else {
+ if (!p->rxkey) {
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.frametype = AST_FRAME_CONTROL;
+ fr.subclass = AST_CONTROL_RADIO_KEY;
+ fr.data = 0;
+ fr.src = type;
+ fr.offset = 0;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+ ast_queue_frame(ast,&fr);
+ }
+ p->rxkey = MAX_RXKEY_TIME;
+ qpast = p->rxqast.qe_forw;
+ remque((struct qelem *)qpast);
+ memcpy(buf + AST_FRIENDLY_OFFSET,qpast->buf,GSM_FRAME_SIZE);
+ ast_free(qpast);
+
+ fr.datalen = GSM_FRAME_SIZE;
+ fr.samples = 160;
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_GSM;
+ fr.data = buf + AST_FRIENDLY_OFFSET;
+ fr.src = type;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+ ast_queue_frame(ast,&fr);
+ }
+ }
+ if (p->rxkey == 1) {
+ fr.datalen = 0;
+ fr.samples = 0;
+ fr.frametype = AST_FRAME_CONTROL;
+ fr.subclass = AST_CONTROL_RADIO_UNKEY;
+ fr.data = 0;
+ fr.src = type;
+ fr.offset = 0;
+ fr.mallocd=0;
+ fr.delivery.tv_sec = 0;
+ fr.delivery.tv_usec = 0;
+ ast_queue_frame(ast,&fr);
+ }
+ if (p->rxkey) p->rxkey--;
+
+ if (p->rxqel.qe_forw != &p->rxqel)
+ {
+ for(m = 0,qpel = p->rxqel.qe_forw; qpel != &p->rxqel; qpel = qpel->qe_forw)
+ m++;
+
+ if (m > QUEUE_OVERLOAD_THRESHOLD_EL)
+ {
+ while(p->rxqel.qe_forw != &p->rxqel)
+ {
+ qpel = p->rxqel.qe_forw;
+ remque((struct qelem *)qpel);
+ ast_free(qpel);
+ }
+ }
+ else
+ {
+ qpel = p->rxqel.qe_forw;
+ remque((struct qelem *)qpel);
+
+ memcpy(audio_all_but_one.data,qpel->buf,BLOCKING_FACTOR * GSM_FRAME_SIZE);
+ strncpy(el_node_test.ip, qpel->fromip, EL_IP_SIZE);
+
+ ast_free(qpel);
+ p->keepalive = KEEPALIVE_TIME;
+ twalk(el_node_list, send_audio_all_but_one);
+
+ if (instp->fdr >= 0)
+ write(instp->fdr, audio_all_but_one.data, BLOCKING_FACTOR * GSM_FRAME_SIZE);
+ }
+ }
+ else
+ {
+ /* Asterisk to Echolink */
+ if (!(frame->subclass & (AST_FORMAT_GSM))) {
+ ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+ return 0;
+ }
+ if (p->txkey || p->txindex) {
+ p->keepalive = KEEPALIVE_TIME;
+ memcpy(audio_all.data + (GSM_FRAME_SIZE * p->txindex++), frame->data,GSM_FRAME_SIZE);
+ }
+ if (p->txindex >= BLOCKING_FACTOR) {
+ twalk(el_node_list, send_audio_all);
+ p->txindex = 0;
+ }
+ }
+
+ if (p->txkey) return 0;
+ if (p->keepalive--) return 0;
+ p->keepalive = KEEPALIVE_TIME;
+
+ /* Echolink: send heartbeats and drop dead stations */
+ el_node_test.ip[0] = '\0';
+ twalk(el_node_list, send_heartbeat);
+ if (el_node_test.ip[0] != '\0') {
+ if (find_delete(&el_node_test)) {
+ bye_length = rtcp_make_bye(bye,"rtcp timeout");
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = inet_addr(el_node_test.ip);
+ sin.sin_port = htons(instp->ctrl_port);
+ for (i = 0; i < 20; i++)
+ sendto(instp->ctrl_sock, bye, bye_length,
+ 0,(struct sockaddr *)&sin,sizeof(sin));
+ ast_log(LOG_NOTICE,"call=%s RTCP timeout, removing\n",el_node_test.call);
+ }
+ el_node_test.ip[0] = '\0';
+ }
+ return 0;
+}
+
+static struct ast_channel *el_new(struct el_pvt *i, int state)
+{
+ struct ast_channel *tmp;
+ struct el_instance *instp = i->instp;
+ tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "echolink/%s", i->stream);
+ if (tmp) {
+ tmp->tech = &el_tech;
+ tmp->fds[0] = instp->audio_sock;
+ tmp->fds[1] = instp->ctrl_sock;
+ tmp->nativeformats = prefformat;
+ tmp->rawreadformat = prefformat;
+ tmp->rawwriteformat = prefformat;
+ tmp->writeformat = prefformat;
+ tmp->readformat = prefformat;
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->tech_pvt = i;
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
+ ast_string_field_set(tmp, language, "");
+ i->owner = tmp;
+ i->u = ast_module_user_add(tmp);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(tmp)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+ ast_hangup(tmp);
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+ return tmp;
+}
+
+
+static struct ast_channel *el_request(const char *type, int format, void *data, int *cause)
+{
+ int oldformat;
+ struct el_pvt *p;
+ struct ast_channel *tmp = NULL;
+
+ oldformat = format;
+ format &= (AST_FORMAT_GSM);
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ return NULL;
+ }
+ p = el_alloc(data);
+ if (p) {
+ tmp = el_new(p, AST_STATE_DOWN);
+ if (!tmp)
+ el_destroy(p);
+ }
+ return tmp;
+}
+
+static int unload_module(void)
+{
+int n;
+
+ run_forever = (char)0;
+ tdestroy(el_node_list, free_node);
+ for(n = 0; n < ninstances; n++)
+ {
+ if (instances[n]->audio_sock != -1)
+ {
+ close(instances[n]->audio_sock);
+ instances[n]->audio_sock = -1;
+ }
+ if (instances[n]->ctrl_sock != -1)
+ {
+ close(instances[n]->ctrl_sock);
+ instances[n]->ctrl_sock = -1;
+ }
+ }
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&el_tech);
+ for(n = 0; n < ninstances; n++) ast_free(instances[n]);
+ return 0;
+}
+
+/*
+ If asterisk has a function that writes at least n bytes to a TCP socket,
+ remove writen function and use the one provided by Asterisk
+*/
+static int writen(int fd, char *ptr, int nbytes)
+{
+ int nleft, nwritten;
+ char *local_ptr;
+
+ nleft = nbytes;
+ local_ptr = ptr;
+
+ while (nleft > 0)
+ {
+ nwritten = write(fd, local_ptr, nleft);
+ if (nwritten < 0)
+ return nwritten;
+ nleft -= nwritten;
+ local_ptr += nwritten;
+ }
+ return (nbytes - nleft);
+}
+
+/* Feel free to make this code smaller, I know it works, so I use it */
+static int sendcmd(char *server, struct el_instance *instp)
+{
+ struct hostent *ahp;
+ struct ast_hostent ah;
+ struct in_addr ia;
+
+ char ip[EL_IP_SIZE + 1];
+ struct sockaddr_in el;
+ int el_len;
+ int sd;
+ int rc;
+ time_t now;
+ struct tm *p_tm;
+ char *id = NULL;
+ const size_t LOGINSIZE = 1023;
+ char buf[LOGINSIZE + 1];
+ size_t len;
+
+ ahp = ast_gethostbyname(server,&ah);
+ if (ahp) {
+ memcpy(&ia,ahp->h_addr,sizeof(in_addr_t));
+#ifdef OLD_ASTERISK
+ ast_inet_ntoa(ip,EL_IP_SIZE,ia);
+#else
+ strncpy(ip,ast_inet_ntoa(ia),EL_IP_SIZE);
+#endif
[... 353 lines stripped ...]
More information about the svn-commits
mailing list