Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Implementation of Media Gateway Control Protocol
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <asterisk/lock.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/config.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
#include <asterisk/pbx.h>
#include <asterisk/options.h>
#include <asterisk/lock.h>
#include <asterisk/sched.h>
#include <asterisk/io.h>
#include <asterisk/rtp.h>
#include <asterisk/acl.h>
#include <asterisk/callerid.h>
#include <asterisk/cli.h>
#include <asterisk/say.h>
#include <asterisk/cdr.h>
#include <asterisk/astdb.h>
#include <asterisk/parking.h>
#include <asterisk/app.h>
#include <asterisk/musiconhold.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifndef IPTOS_MINCOST
#define IPTOS_MINCOST 0x02
#endif
#define MGCPDUMPER
#define DEFAULT_EXPIREY 120
#define MAX_EXPIREY 3600
static char *desc = "Media Gateway Control Protocol (MGCP)";
static char *type = "MGCP";
static char *tdesc = "Media Gateway Control Protocol (MGCP)";
static char *config = "mgcp.conf";
#define DEFAULT_MGCP_PORT 2427/* From RFC 2705 */
#define MGCP_MAX_PACKET 1500 /* Also from RFC 2543, should sub headers tho */
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* MGCP rtp stream modes */
#define MGCP_CX_SENDONLY 0
#define MGCP_CX_RECVONLY 1
#define MGCP_CX_SENDRECV 2
#define MGCP_CX_CONF 3
#define MGCP_CX_CONFERENCE 3
#define MGCP_CX_MUTE 4
#define MGCP_CX_INACTIVE 4
static char *mgcp_cxmodes[] = {
"sendonly",
"recvonly",
"sendrecv",
"confrnce",
"inactive"
};
static char context[AST_MAX_EXTENSION] = "default";
static char language[MAX_LANGUAGE] = "";
static char musicclass[MAX_LANGUAGE] = "";
static char callerid[AST_MAX_EXTENSION] = "";
static int inbanddtmf = 0;
static int nat = 0;
/* Not used. Dosn't hurt for us to always send cid */
/* to the mgcp box. */
/* static int use_callerid = 1;*/
/*static int cur_signalling = -1;*/
/*static unsigned int cur_group = 0;*/
static unsigned int cur_callergroup = 0;
static unsigned int cur_pickupgroup = 0;
/* XXX Is this needed? */
/* Doesn't look like the dsp stuff for */
/* inbanddtmf is actually hooked up. */
/* static int relaxdtmf = 0; */
static int tos = 0;
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
static int immediate = 0;
static int callwaiting = 0;
/* Not used. Dosn't hurt for us to always send cid */
/* to the mgcp box. */
/*static int callwaitingcallerid = 0;*/
/*static int hidecallerid = 0;*/
static int callreturn = 0;
static int threewaycalling = 0;
/* This is for flashhook transfers */
static int transfer = 0;
static int cancallforward = 0;
/*static int busycount = 3;*/
/*static int callprogress = 0;*/
static char accountcode[20] = "";
static char mailbox[AST_MAX_EXTENSION];
static int amaflags = 0;
static int adsi = 0;
static int usecnt =0;
static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
static int oseq;
/* Wait up to 16 seconds for first digit (FXO logic) */
static int firstdigittimeout = 16000;
/* How long to wait for following digits (FXO logic) */
static int gendigittimeout = 8000;
/* How long to wait for an extra digit, if there is an ambiguous match */
static int matchdigittimeout = 3000;
/* Protect the monitoring thread, so only one process can kill or start it, and not
when it's doing something critical. */
static pthread_mutex_t netlock = AST_MUTEX_INITIALIZER;
static pthread_mutex_t monlock = AST_MUTEX_INITIALIZER;
/* This is the thread for the monitor which checks for input on the channels
which are not currently in use. */
static pthread_t monitor_thread = 0;
static int restart_monitor(void);
/* Just about everybody seems to support ulaw, so make it a nice default */
static int capability = AST_FORMAT_ULAW;
static int nonCodecCapability = AST_RTP_DTMF;
static char ourhost[256];
static struct in_addr __ourip;
static int ourport;
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
static struct sched_context *sched;
static struct io_context *io;
/* The private structures of the mgcp channels are linked for
selecting outgoing channels */
#define MGCP_MAX_HEADERS 64
#define MGCP_MAX_LINES 64
struct mgcp_request {
int len;
char *verb;
char *identifier;
char *endpoint;
char *version;
int headers; /* MGCP Headers */
char *header[MGCP_MAX_HEADERS];
int lines; /* SDP Content */
char *line[MGCP_MAX_LINES];
char data[MGCP_MAX_PACKET];
};
static struct mgcp_pkt {
int retrans;
struct mgcp_endpoint *owner;
int packetlen;
char data[MGCP_MAX_PACKET];
struct mgcp_pkt *next;
} *packets = NULL;
/* MGCP message for queuing up */
struct mgcp_message {
unsigned int seqno;
int len;
struct mgcp_message *next;
unsigned char buf[0];
};
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#define MAX_SUBS 2
#define SUB_REAL 0
#define SUB_ALT 1
struct mgcp_subchannel {
pthread_mutex_t lock;
int id;
struct ast_channel *owner;
struct mgcp_endpoint *parent;
struct ast_rtp *rtp;
struct sockaddr_in tmpdest;
char txident[80];
char cxident[80];
char callid[80];
time_t lastouttime;
int lastout;
int cxmode;
int nat;
int iseq; /* Not used? RTP? */
int outgoing;
int alreadygone;
int messagepending;
struct mgcp_message *msgs; /* Message queue */
struct mgcp_subchannel *next; /* for out circular linked list */
};
#define MGCP_ONHOOK 1
#define MGCP_OFFHOOK 2
#define TYPE_TRUNK 1
#define TYPE_LINE 2
struct mgcp_endpoint {
pthread_mutex_t lock;
char name[80];
struct mgcp_subchannel *sub; /* pointer to our current connection, channel and stuff */
char accountcode[80];
char exten[AST_MAX_EXTENSION]; /* Extention where to start */
char context[AST_MAX_EXTENSION];
char language[MAX_LANGUAGE];
char callerid[AST_MAX_EXTENSION]; /* Caller*ID */
char lastcallerid[AST_MAX_EXTENSION]; /* Last Caller*ID */
char call_forward[AST_MAX_EXTENSION]; /* Last Caller*ID */
char mailbox[AST_MAX_EXTENSION];
char musicclass[MAX_LANGUAGE];
unsigned int callgroup;
unsigned int pickupgroup;
int callwaiting;
int transfer;
int threewaycalling;
int cancallforward;
int callreturn;
int dnd; /* How does this affect callwait? Do we just deny a mgcp_request if we're dnd? */
int iseq; /* Not used? */
int lastout; /* tracking this on the subchannels. Is it needed here? */
int needdestroy; /* Not used? */
int onhooktime;
int msgstate; /* voicemail message state */
int immediate;
int hookstate;
int adsi;
struct ast_dsp *dsp; /* XXX Should there be a dsp/subchannel? XXX */
/* owner is tracked on the subchannels, and the *sub indicates whos in charge */
/* struct ast_channel *owner; */
/* struct ast_rtp *rtp; */
/* struct sockaddr_in tmpdest; */
/* message go the the endpoint and not the channel so they stay here */
struct mgcp_endpoint *next;
struct mgcp_gateway *parent;
};
/* A gateway containing one or more endpoints */
char name[80];
struct sockaddr_in addr;
int dynamic;
int expire; /* XXX Should we ever expire dynamic registrations? XXX */
struct mgcp_endpoint *endpoints;
struct ast_ha *ha;
struct mgcp_gateway *next;
} *gateways;
static pthread_mutex_t gatelock = AST_MUTEX_INITIALIZER;
static int mgcpsock = -1;
static struct sockaddr_in bindaddr;
static struct ast_frame *mgcp_read(struct ast_channel *ast);
static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest);
static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone);
static int transmit_modify_request(struct mgcp_subchannel *sub);
static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callerid);
static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp);
static int transmit_connection_del(struct mgcp_subchannel *sub);
static int transmit_audit_endpoint(struct mgcp_endpoint *p);
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
static void start_rtp(struct mgcp_subchannel *sub);
static int has_voicemail(struct mgcp_endpoint *p)
{
return ast_app_has_voicemail(p->mailbox);
}
static int unalloc_sub(struct mgcp_subchannel *sub)
{
struct mgcp_endpoint *p = sub->parent;
if (p->sub == sub) {
ast_log(LOG_WARNING, "Trying to unalloc the real channel %s@%s?!?\n", p->name, p->parent->name);
return -1;
}
ast_log(LOG_DEBUG, "Released sub %d of channel %s@%s\n", sub->id, p->name, p->parent->name);
sub->owner = NULL;
if (strlen(sub->cxident)) {
transmit_connection_del(sub);
}
sub->cxident[0] = '\0';
sub->callid[0] = '\0';
sub->cxmode = MGCP_CX_INACTIVE;
sub->outgoing = 0;
sub->alreadygone = 0;
memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
if (sub->rtp) {
ast_rtp_destroy(sub->rtp);
sub->rtp = NULL;
}
return 0;
}
static int __mgcp_xmit(struct mgcp_subchannel *sub, char *data, int len)
struct mgcp_endpoint *p = sub->parent;
if (p->parent->addr.sin_addr.s_addr)
res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->addr, sizeof(struct sockaddr_in));
else
res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->defaddr, sizeof(struct sockaddr_in));
if (res != len) {
ast_log(LOG_WARNING, "mgcp_xmit returned %d: %s\n", res, strerror(errno));
}
return res;
}
static int send_response(struct mgcp_subchannel *sub, struct mgcp_request *req)
struct mgcp_endpoint *p = sub->parent;
ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
}
res = __mgcp_xmit(sub, req->data, req->len);
if (res > 0)
res = 0;
return res;
}
static void dump_queue(struct mgcp_endpoint *p)
{
struct mgcp_message *cur;
struct mgcp_subchannel *sub = p->sub;
do {
while(sub->msgs) {
cur = sub->msgs;
sub->msgs = sub->msgs->next;
ast_log(LOG_NOTICE, "Removing message from %s@%s-%d tansaction %d\n", p->name, p->parent->name, sub->id, cur->seqno);
free(cur);
}
sub->messagepending = 0;
sub->msgs = NULL;
sub = sub->next;
} while(sub != p->sub);
static int mgcp_postrequest(struct mgcp_subchannel *sub, unsigned char *data, int len, unsigned int seqno)
{
struct mgcp_message *msg = malloc(sizeof(struct mgcp_message) + len);
struct mgcp_message *cur;
}
time(&t);
if (sub->messagepending && (sub->lastouttime + 20 < t)) {
ast_log(LOG_NOTICE, "Timeout waiting for response to message:%d, lastouttime: %ld, now: %ld. Dumping pending queue\n",
sub->msgs ? sub->msgs->seqno : -1, sub->lastouttime, t);
dump_queue(sub->parent);
}
msg->seqno = seqno;
msg->next = NULL;
if (cur) {
while(cur->next)
cur = cur->next;
cur->next = msg;
} else {
sub->msgs = msg;
}
if (!sub->messagepending) {
sub->messagepending = 1;
sub->lastout = seqno;
sub->lastouttime = t;
__mgcp_xmit(sub, msg->buf, msg->len);
/* XXX Should schedule retransmission XXX */
} else
ast_log(LOG_DEBUG, "Deferring transmission of transaction %d\n", seqno);
return 0;
}
static int send_request(struct mgcp_subchannel *sub, struct mgcp_request *req, unsigned int seqno)
struct mgcp_endpoint *p = sub->parent;
if (mgcpdebug) {
ast_verbose("Posting Request:\n%s to %s:%d\n", req->data,
inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
}
res = mgcp_postrequest(sub, req->data, req->len, seqno);
return res;
}
static int mgcp_call(struct ast_channel *ast, char *dest, int timeout)
{
int res;
struct mgcp_endpoint *p;
struct mgcp_subchannel *sub;
char *tone;
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_call(%s)\n", ast->name);
}
sub = ast->pvt->pvt;
p = sub->parent;
switch (p->hookstate) {
case MGCP_OFFHOOK:
tone = "L/wt";
break;
case MGCP_ONHOOK:
default:
tone = "L/rg";
break;
}
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "mgcp_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
res = 0;
sub->outgoing = 1;
sub->cxmode = MGCP_CX_RECVONLY;
if (!sub->rtp) {
start_rtp(sub);
} else {
transmit_modify_request(sub);
}
if (sub->next->owner && strlen(sub->next->cxident) && strlen(sub->next->callid)) {
/* try to prevent a callwait from disturbing the other connection */
sub->next->cxmode = MGCP_CX_RECVONLY;
transmit_modify_request(sub->next);
}
transmit_notify_request_with_callerid(sub, tone, ast->callerid);
ast_setstate(ast, AST_STATE_RINGING);
ast_queue_control(ast, AST_CONTROL_RINGING, 0);
if (sub->next->owner && strlen(sub->next->cxident) && strlen(sub->next->callid)) {
/* Put the connection back in sendrecv */
sub->next->cxmode = MGCP_CX_SENDRECV;
transmit_modify_request(sub->next);
}
} else {
ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n");
res = -1;
}
return res;
}
static int mgcp_hangup(struct ast_channel *ast)
{
struct mgcp_subchannel *sub = ast->pvt->pvt;
struct mgcp_endpoint *p = sub->parent;
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name);
}
if (option_debug)
ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name);
if (!ast->pvt->pvt) {
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
return 0;
}
if ((p->dtmfinband) && (p->dsp != NULL)){
ast_dsp_free(p->dsp);
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
ast_pthread_mutex_lock(&sub->lock);
sub->owner = NULL;
if (strlen(sub->cxident)) {
transmit_connection_del(sub);
}
strcpy(sub->cxident, "");
if ((sub == p->sub) && sub->next->owner) {
if (p->hookstate == MGCP_OFFHOOK) {
if (sub->next->owner && sub->next->owner->bridge) {
transmit_notify_request_with_callerid(p->sub, "L/wt", sub->next->owner->bridge->callerid);
}
} else {
/* set our other connection as the primary and swith over to it */
p->sub = sub->next;
p->sub->cxmode = MGCP_CX_RECVONLY;
transmit_modify_request(p->sub);
if (sub->next->owner && sub->next->owner->bridge) {
transmit_notify_request_with_callerid(p->sub, "L/rg", sub->next->owner->callerid);
}
}
} else if ((sub == p->sub->next) && p->hookstate == MGCP_OFFHOOK) {
transmit_notify_request(sub, "v");
} else if (p->hookstate == MGCP_OFFHOOK) {
transmit_notify_request(sub, "ro");
} else {
transmit_notify_request(sub, "");
}
sub->alreadygone = 0;
sub->outgoing = 0;
sub->cxmode = MGCP_CX_INACTIVE;
strcpy(sub->callid, "");
memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
if (sub->rtp) {
ast_rtp_destroy(sub->rtp);
sub->rtp = NULL;
if ((p->hookstate == MGCP_ONHOOK) && (!sub->next->rtp)) {
if (has_voicemail(p)) {
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s set vmwi(+)\n", ast->name, p->name, p->parent->name);
}
transmit_notify_request(sub, "vmwi(+)");
} else {
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s set vmwi(-)\n", ast->name, p->name, p->parent->name);
}
transmit_notify_request(sub, "vmwi(-)");
}
}
ast_pthread_mutex_unlock(&sub->lock);
return 0;
}
static int mgcp_show_endpoints(int fd, int argc, char *argv[])
{
struct mgcp_gateway *g;
struct mgcp_endpoint *e;
int hasendpoints = 0;
if (argc != 3)
return RESULT_SHOWUSAGE;
ast_pthread_mutex_lock(&gatelock);
g = gateways;
while(g) {
e = g->endpoints;
ast_cli(fd, "Gateway '%s' at %s (%s)\n", g->name, g->addr.sin_addr.s_addr ? inet_ntoa(g->addr.sin_addr) : inet_ntoa(g->defaddr.sin_addr), g->dynamic ? "Dynamic" : "Static");
ast_cli(fd, " -- '%s@%s in '%s' is %s\n", e->name, g->name, e->context, e->sub->owner ? "active" : "idle");
hasendpoints = 1;
e = e->next;
}
if (!hasendpoints) {
ast_cli(fd, " << No Endpoints Defined >> ");
}
g = g->next;
}
ast_pthread_mutex_unlock(&gatelock);
return RESULT_SUCCESS;
}
static char show_endpoints_usage[] =
"Usage: mgcp show endpoints\n"
" Lists all endpoints known to the MGCP (Media Gateawy Control Protocol) subsystem.\n";
static struct ast_cli_entry cli_show_endpoints =
{ { "mgcp", "show", "endpoints", NULL }, mgcp_show_endpoints, "Show defined MGCP endpoints", show_endpoints_usage };
static int mgcp_audit_endpoint(int fd, int argc, char *argv[])
{
struct mgcp_gateway *g;
struct mgcp_endpoint *e;
int found = 0;
if (!mgcpdebug) {
return RESULT_SHOWUSAGE;
}
if (argc != 4)
return RESULT_SHOWUSAGE;
/* split the name into parts by null */
ename = argv[3];
gname = ename;
while (*gname) {
if (*gname == '@') {
*gname = 0;
gname++;
break;
}
gname++;
}
if (gname[0] == '[')
gname++;
if ((c = strrchr(gname, ']')))
*c = '\0';
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
ast_pthread_mutex_lock(&gatelock);
g = gateways;
while(g) {
if (!strcasecmp(g->name, gname)) {
e = g->endpoints;
while(e) {
if (!strcasecmp(e->name, ename)) {
found = 1;
transmit_audit_endpoint(e);
break;
}
e = e->next;
}
if (found) {
break;
}
}
g = g->next;
}
if (!found) {
ast_cli(fd, " << Could not find endpoint >> ");
}
ast_pthread_mutex_unlock(&gatelock);
return RESULT_SUCCESS;
}
static char audit_endpoint_usage[] =
"Usage: mgcp audit endpoint <endpointid>\n"
" List the capabilities of an endpoint in the MGCP (Media Gateawy Control Protocol) subsystem.\n"
" mgcp debug MUST be on to see the results of this command.\n";
static struct ast_cli_entry cli_audit_endpoint =
{ { "mgcp", "audit", "endpoint", NULL }, mgcp_audit_endpoint, "Audit specified MGCP endpoint", audit_endpoint_usage };
static int mgcp_answer(struct ast_channel *ast)
{
int res = 0;
struct mgcp_subchannel *sub = ast->pvt->pvt;
struct mgcp_endpoint *p = sub->parent;
sub->cxmode = MGCP_CX_SENDRECV;
if (!sub->rtp) {
start_rtp(sub);
} else {
transmit_modify_request(sub);
}
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_answer(%s) on %s@%s-%d\n", ast->name, p->name, p->parent->name, sub->id);
if (ast->_state != AST_STATE_UP) {
ast_setstate(ast, AST_STATE_UP);
if (option_debug)
ast_log(LOG_DEBUG, "mgcp_answer(%s)\n", ast->name);
transmit_notify_request(sub, "");
transmit_modify_request(sub);
static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub)
/* Retrieve audio/etc from channel. Assumes sub->lock is already held. */
f = ast_rtp_read(sub->rtp);
if (sub->owner) {
/* We already hold the channel lock */
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != sub->owner->nativeformats) {
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
sub->owner->nativeformats = f->subclass;
ast_set_read_format(sub->owner, sub->owner->readformat);
ast_set_write_format(sub->owner, sub->owner->writeformat);
/* Courtesy fearnor aka alex@pilosoft.com */
if (sub->parent->dtmfinband) {
ast_log(LOG_NOTICE, "MGCP ast_dsp_process\n");
f = ast_dsp_process(sub->owner, sub->parent->dsp, f, 0);
}
static struct ast_frame *mgcp_read(struct ast_channel *ast)
{
struct mgcp_subchannel *sub = ast->pvt->pvt;
ast_pthread_mutex_lock(&sub->lock);
fr = mgcp_rtp_read(sub);
ast_pthread_mutex_unlock(&sub->lock);
}
static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame)
{
struct mgcp_subchannel *sub = ast->pvt->pvt;
int res = 0;
if (frame->frametype != AST_FRAME_VOICE) {
if (frame->frametype == AST_FRAME_IMAGE)
return 0;
else {
ast_log(LOG_WARNING, "Can't send %d type frames with MGCP write\n", frame->frametype);
return 0;
}
} else {
if (!(frame->subclass & ast->nativeformats)) {
ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
return -1;
}
}
if (sub) {
ast_pthread_mutex_lock(&sub->lock);
if (sub->rtp) {
res = ast_rtp_write(sub->rtp, frame);
ast_pthread_mutex_unlock(&sub->lock);
}
return res;
}
static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
{
struct mgcp_subchannel *sub = newchan->pvt->pvt;
ast_log(LOG_NOTICE, "mgcp_fixup(%s, %s)\n", oldchan->name, newchan->name);
if (sub->owner != oldchan) {
ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
return 0;
}
static int mgcp_senddigit(struct ast_channel *ast, char digit)
{
struct mgcp_subchannel *sub = ast->pvt->pvt;
char tmp[2];
tmp[0] = digit;
tmp[1] = '\0';
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
static char *control2str(int ind) {
switch (ind) {
case AST_CONTROL_HANGUP:
return "Other end has hungup";
case AST_CONTROL_RING:
return "Local ring";
case AST_CONTROL_RINGING:
return "Remote end is ringing";
case AST_CONTROL_ANSWER:
return "Remote end has answered";
case AST_CONTROL_BUSY:
return "Remote end is busy";
case AST_CONTROL_TAKEOFFHOOK:
return "Make it go off hook";
case AST_CONTROL_OFFHOOK:
return "Line is off hook";
case AST_CONTROL_CONGESTION:
return "Congestion (circuits busy)";
case AST_CONTROL_FLASH:
return "Flash hook";
case AST_CONTROL_WINK:
return "Wink";
case AST_CONTROL_OPTION:
return "Set a low-level option";
case AST_CONTROL_RADIO_KEY:
return "Key Radio";
case AST_CONTROL_RADIO_UNKEY:
return "Un-Key Radio";
}
return "UNKNOWN";
}
static int mgcp_indicate(struct ast_channel *ast, int ind)
{
struct mgcp_subchannel *sub = ast->pvt->pvt;
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP asked to indicate %d '%s' condition on channel %s\n", ind, control2str(ind), ast->name);
}
break;
default:
ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
return -1;
}
return 0;
}
static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state)
struct mgcp_endpoint *i = sub->parent;
tmp = ast_channel_alloc(1);
if (tmp) {
tmp->nativeformats = i->capability;
if (!tmp->nativeformats)
tmp->nativeformats = capability;
fmt = ast_best_codec(tmp->nativeformats);
snprintf(tmp->name, sizeof(tmp->name), "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
if (sub->rtp)
tmp->fds[0] = ast_rtp_fd(sub->rtp);
i->dsp = ast_dsp_new();
ast_dsp_set_features(i->dsp,DSP_FEATURE_DTMF_DETECT);
ast_setstate(tmp, state);
if (state == AST_STATE_RING)
tmp->rings = 1;
tmp->writeformat = fmt;
tmp->pvt->rawwriteformat = fmt;
tmp->readformat = fmt;
tmp->pvt->rawreadformat = fmt;
tmp->pvt->call = mgcp_call;
tmp->pvt->hangup = mgcp_hangup;
tmp->pvt->answer = mgcp_answer;
tmp->pvt->read = mgcp_read;
tmp->pvt->write = mgcp_write;
tmp->pvt->indicate = mgcp_indicate;
tmp->pvt->fixup = mgcp_fixup;
tmp->pvt->send_digit = mgcp_senddigit;
if (strlen(i->language))
strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
if (strlen(i->accountcode))
strncpy(tmp->accountcode, i->accountcode, sizeof(tmp->accountcode)-1);
if (i->amaflags)
tmp->amaflags = i->amaflags;
sub->owner = tmp;
ast_pthread_mutex_lock(&usecnt_lock);
usecnt++;
ast_pthread_mutex_unlock(&usecnt_lock);
ast_update_use_count();
tmp->callgroup = i->callgroup;
tmp->pickupgroup = i->pickupgroup;
strncpy(tmp->call_forward, i->call_forward, sizeof(tmp->call_forward));
strncpy(tmp->context, i->context, sizeof(tmp->context)-1);
strncpy(tmp->exten, i->exten, sizeof(tmp->exten)-1);
if (strlen(i->callerid))
tmp->callerid = strdup(i->callerid);
if (!i->adsi)
tmp->adsicpe = AST_ADSI_UNAVAILABLE;
tmp->priority = 1;
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);
tmp = NULL;
}
}
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_new(%s) created in state: %s\n", tmp->name, ast_state2str(state));
} else {
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
static char* get_sdp_by_line(char* line, char *name, int nameLen) {
if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') {
char* r = line + nameLen + 1;
while (*r && (*r < 33)) ++r;
return r;
}
return "";
}
static char *get_sdp(struct mgcp_request *req, char *name) {
int x;
int len = strlen(name);
char *r;
for (x=0; x<req->lines; x++) {
r = get_sdp_by_line(req->line[x], name, len);
if (r[0] != '\0') return r;
}
return "";
}
static void sdpLineNum_iterator_init(int* iterator) {
*iterator = 0;
}
static char* get_sdp_iterate(int* iterator,
struct mgcp_request *req, char *name) {
int len = strlen(name);
char *r;
while (*iterator < req->lines) {
r = get_sdp_by_line(req->line[(*iterator)++], name, len);
if (r[0] != '\0') return r;
}
return "";
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
}
static char *__get_header(struct mgcp_request *req, char *name, int *start)
{
int x;
int len = strlen(name);
char *r;
for (x=*start;x<req->headers;x++) {
if (!strncasecmp(req->header[x], name, len) &&
(req->header[x][len] == ':')) {
r = req->header[x] + len + 1;
while(*r && (*r < 33))
r++;
*start = x+1;
return r;
}
}
/* Don't return NULL, so get_header is always a valid pointer */
return "";
}
static char *get_header(struct mgcp_request *req, char *name)
{
int start = 0;
return __get_header(req, name, &start);
}
static int rtpready(struct ast_rtp *rtp, struct ast_frame *f, void *data)
{
/* Just deliver the audio directly */
struct mgcp_endpoint *p = data;
ast_pthread_mutex_lock(&p->lock);
if (p->owner) {
/* Generally, you lock in the order channel lock, followed by private
lock. Since here we are doing the reverse, there is the possibility
of deadlock. As a result, in the case of a deadlock, we simply fail out
here. */
if (!pthread_mutex_trylock(&p->owner->lock)) {
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != p->owner->nativeformats) {
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
p->owner->nativeformats = f->subclass;