[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