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
*/
Mark Spencer
committed
/* SC: Changes
-- packet retransmit mechanism (simplistic)
-- per endpoint/subchannel mgcp command sequencing.
-- better transaction handling
-- fixed some mem leaks
-- run-time configuration reload
-- distinguish CA and GW default MGCP ports
-- prevent clipping of DTMF tones in an established call
-- fixed a few crash scenarios in 3-way
-- fix for a few cases where asterisk and MGW end-up in conflicting ep states
-- enclose numeric IP in [] for outgoing requests
*/
/* SC: TODO
-- piggyback support
-- responseAck support
-- enhance retransmit mechanism (RTO calc. etc.)
-- embedded command support
*/
#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 <signal.h>
#include <pthread.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
#define CANREINVITE 1
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";
Mark Spencer
committed
#define DEFAULT_MGCP_GW_PORT 2427/* From RFC 2705 */
#define DEFAULT_MGCP_CA_PORT 2727/* From RFC 2705 */
#define MGCP_MAX_PACKET 1500 /* Also from RFC 2543, should sub headers tho */
Mark Spencer
committed
#define DEFAULT_RETRANS 1000 /* How frequently to retransmit */
#define MAX_RETRANS 5 /* Try only 5 times for retransmissions */
/* 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"
};
Mark Spencer
committed
/* SC: MGCP commands */
#define MGCP_CMD_EPCF 0
#define MGCP_CMD_CRCX 1
#define MGCP_CMD_MDCX 2
#define MGCP_CMD_DLCX 3
#define MGCP_CMD_RQNT 4
#define MGCP_CMD_NTFY 5
#define MGCP_CMD_AUEP 6
#define MGCP_CMD_AUCX 7
#define MGCP_CMD_RSIP 8
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;
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 canreinvite = CANREINVITE;
/*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 ast_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
/* 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 ast_mutex_t netlock = AST_MUTEX_INITIALIZER;
static ast_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 = AST_PTHREADT_NULL;
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;
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];
Mark Spencer
committed
int cmd; /* SC: int version of verb = command */
int trid; /* SC: int version of identifier = transaction id */
struct mgcp_request *next; /* SC: next in the queue */
Mark Spencer
committed
/* SC: obsolete
static struct mgcp_pkt {
int retrans;
struct mgcp_endpoint *owner;
int packetlen;
char data[MGCP_MAX_PACKET];
struct mgcp_pkt *next;
} *packets = NULL;
Mark Spencer
committed
*/
/* MGCP message for queuing up */
struct mgcp_message {
Mark Spencer
committed
struct mgcp_endpoint *owner_ep;
struct mgcp_subchannel *owner_sub;
int retrans;
unsigned long expire;
unsigned int seqno;
int len;
struct mgcp_message *next;
unsigned char buf[0];
};
#define MAX_SUBS 2
#define SUB_REAL 0
#define SUB_ALT 1
struct mgcp_subchannel {
Mark Spencer
committed
/* SC: subchannel magic string.
Needed to prove that any subchannel pointer passed by asterisk
really points to a valid subchannel memory area.
Ugly.. But serves the purpose for the time being.
*/
#define MGCP_SUBCHANNEL_MAGIC "!978!"
char magic[6];
ast_mutex_t lock;
int id;
struct ast_channel *owner;
struct mgcp_endpoint *parent;
struct ast_rtp *rtp;
struct sockaddr_in tmpdest;
Mark Spencer
committed
char txident[80]; /* FIXME SC: txident is replaced by rqnt_ident in endpoint.
This should be obsoleted */
char cxident[80];
char callid[80];
Mark Spencer
committed
/* SC: obsolete
Mark Spencer
committed
*/
Mark Spencer
committed
struct mgcp_request *cx_queue; /* SC: pending CX commands */
ast_mutex_t cx_queue_lock; /* SC: CX queue lock */
int nat;
int iseq; /* Not used? RTP? */
int outgoing;
int alreadygone;
Mark Spencer
committed
/* SC: obsolete
Mark Spencer
committed
struct mgcp_message *msgs;
*/
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 {
ast_mutex_t lock;
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;
Mark Spencer
committed
char rqnt_ident[80]; /* SC: request identifier */
struct mgcp_request *rqnt_queue; /* SC: pending RQNT commands */
ast_mutex_t rqnt_queue_lock;
struct mgcp_request *cmd_queue; /* SC: pending commands other than RQNT */
ast_mutex_t cmd_queue_lock;
int delme; /* SC: needed for reload */
int needaudit; /* SC: needed for reload */
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];
Mark Spencer
committed
int isnamedottedip; /* SC: is the name FQDN or dotted ip */
int dynamic;
int expire; /* XXX Should we ever expire dynamic registrations? XXX */
struct mgcp_endpoint *endpoints;
struct ast_ha *ha;
Mark Spencer
committed
/* SC: obsolete
time_t lastouttime;
int lastout;
int messagepending;
*/
struct mgcp_message *msgs; /* SC: gw msg queue */
ast_mutex_t msgs_lock; /* SC: queue lock */
int retransid; /* SC: retrans timer id */
int delme; /* SC: needed for reload */
Mark Spencer
committed
static ast_mutex_t mgcp_reload_lock = AST_MUTEX_INITIALIZER;
static int mgcp_reloading = 0;
static ast_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);
static void start_rtp(struct mgcp_subchannel *sub);
Mark Spencer
committed
static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
int result, int ident, struct mgcp_request *resp);
static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub);
static int mgcp_do_reload(void);
static int mgcp_reload(int fd, int argc, char *argv[]);
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
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;
}
Mark Spencer
committed
dump_cmd_queues(NULL, sub); /* SC */
Mark Spencer
committed
/* SC: modified for new transport mechanism */
static int __mgcp_xmit(struct mgcp_gateway *gw, char *data, int len)
Mark Spencer
committed
if (gw->addr.sin_addr.s_addr)
res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->addr, sizeof(struct sockaddr_in));
Mark Spencer
committed
res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->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));
Mark Spencer
committed
res = __mgcp_xmit(p->parent, req->data, req->len);
if (res > 0)
res = 0;
return res;
}
Mark Spencer
committed
/* SC: modified for new transport framework */
static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p)
Mark Spencer
committed
struct mgcp_message *cur, *q = NULL, *w, *prev;
ast_mutex_lock(&gw->msgs_lock);
prev = NULL, cur = gw->msgs;
while (cur) {
if (!p || cur->owner_ep == p) {
if (prev)
prev->next = cur->next;
else
gw->msgs = cur->next;
ast_log(LOG_NOTICE, "Removing message from %s tansaction %d\n",
gw->name, cur->seqno);
w = cur;
cur = cur->next;
if (q) {
w->next = q;
}
else {
w->next = NULL;
}
q = w;
Mark Spencer
committed
else {
prev = cur, cur=cur->next;
}
}
ast_mutex_unlock(&gw->msgs_lock);
while (q) {
cur = q;
q = q->next;
free(cur);
}
Mark Spencer
committed
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
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
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
static int retrans_pkt(void *data)
{
struct mgcp_gateway *gw = (struct mgcp_gateway *)data;
struct mgcp_message *cur, *exq = NULL, *w, *prev;
struct timeval tv;
unsigned long t;
int res = 0;
if (gettimeofday(&tv, NULL) < 0) {
/* This shouldn't ever happen, but let's be sure */
ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
return 0;
}
t = tv.tv_sec * 1000 + tv.tv_usec / 1000;
/* find out expired msgs */
ast_mutex_lock(&gw->msgs_lock);
prev = NULL, cur = gw->msgs;
while (cur) {
if (cur->retrans < MAX_RETRANS) {
cur->retrans++;
if (mgcpdebug) {
ast_verbose("Retransmitting #%d transaction %d on [%s]\n", cur->retrans, cur->seqno, gw->name);
}
__mgcp_xmit(gw, cur->buf, cur->len);
prev = cur;
cur = cur->next;
}
else {
if (prev)
prev->next = cur->next;
else
gw->msgs = cur->next;
ast_log(LOG_WARNING, "Maximum retries exceeded for transaction %d on [%s]\n", cur->seqno, gw->name);
w = cur;
cur = cur->next;
if (exq) {
w->next = exq;
}
else {
w->next = NULL;
}
exq = w;
}
}
if (!gw->msgs) {
gw->retransid = -1;
res = 0;
}
else {
res = 1;
}
ast_mutex_unlock(&gw->msgs_lock);
while (exq) {
cur = exq;
/* time-out transaction */
handle_response(cur->owner_ep, cur->owner_sub, 406, cur->seqno, NULL);
exq = exq->next;
free(cur);
}
return res;
}
/* SC: modified for the new transaction mechanism */
static int mgcp_postrequest(struct mgcp_endpoint *p, 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;
Mark Spencer
committed
struct mgcp_gateway *gw = ((p && p->parent) ? p->parent : NULL);
struct timeval tv;
Mark Spencer
committed
if (!gw) {
return -1;
}
/* SC
Mark Spencer
committed
if (gw->messagepending && (gw->lastouttime + 20 < t)) {
ast_log(LOG_NOTICE, "Timeout waiting for response to message:%d, lastouttime: %ld, now: %ld. Dumping pending queue\n",
Mark Spencer
committed
gw->msgs ? gw->msgs->seqno : -1, (long) gw->lastouttime, (long) t);
Mark Spencer
committed
*/
msg->owner_sub = sub;
msg->owner_ep = p;
msg->seqno = seqno;
msg->next = NULL;
Mark Spencer
committed
msg->retrans = 0;
Mark Spencer
committed
ast_mutex_lock(&gw->msgs_lock);
cur = gw->msgs;
if (cur) {
while(cur->next)
cur = cur->next;
cur->next = msg;
Mark Spencer
committed
gw->msgs = msg;
Mark Spencer
committed
if (gettimeofday(&tv, NULL) < 0) {
/* This shouldn't ever happen, but let's be sure */
ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
}
else {
msg->expire = tv.tv_sec * 1000 + tv.tv_usec / 1000 + DEFAULT_RETRANS;
if (gw->retransid == -1)
gw->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, (void *)gw);
}
ast_mutex_unlock(&gw->msgs_lock);
/* SC
if (!gw->messagepending) {
gw->messagepending = 1;
gw->lastout = seqno;
gw->lastouttime = t;
*/
__mgcp_xmit(gw, msg->buf, msg->len);
/* XXX Should schedule retransmission XXX */
Mark Spencer
committed
/* SC
} else
ast_log(LOG_DEBUG, "Deferring transmission of transaction %d\n", seqno);
Mark Spencer
committed
*/
Mark Spencer
committed
/* SC: modified for new transport */
static int send_request(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
struct mgcp_request *req, unsigned int seqno)
Mark Spencer
committed
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
681
682
683
684
685
686
687
688
689
690
691
692
int res = 0;
struct mgcp_request **queue, *q, *r, *t;
ast_mutex_t *l;
switch (req->cmd) {
case MGCP_CMD_DLCX:
queue = &sub->cx_queue;
l = &sub->cx_queue_lock;
ast_mutex_lock(l);
q = sub->cx_queue;
/* delete pending cx cmds */
while (q) {
r = q->next;
free(q);
q = r;
}
*queue = NULL;
break;
case MGCP_CMD_CRCX:
case MGCP_CMD_MDCX:
queue = &sub->cx_queue;
l = &sub->cx_queue_lock;
ast_mutex_lock(l);
break;
case MGCP_CMD_RQNT:
queue = &p->rqnt_queue;
l = &p->rqnt_queue_lock;
ast_mutex_lock(l);
break;
default:
queue = &p->cmd_queue;
l = &p->cmd_queue_lock;
ast_mutex_lock(l);
break;
Mark Spencer
committed
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
r = (struct mgcp_request *) malloc (sizeof(struct mgcp_request));
if (!r) {
ast_log(LOG_WARNING, "Cannot post MGCP request: insufficient memory\n");
ast_mutex_unlock(l);
return -1;
}
memcpy(r, req, sizeof(struct mgcp_request));
if (!(*queue)) {
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(p, sub, req->data, req->len, seqno);
}
else {
if (mgcpdebug) {
ast_verbose("Queueing Request:\n%s to %s:%d\n", req->data,
inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
}
}
/* XXX SC: find tail. We could also keep tail in the data struct for faster access */
for (t = *queue; t && t->next; t = t->next);
r->next = NULL;
if (t)
t->next = r;
else
*queue = r;
ast_mutex_unlock(l);
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 (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;
}
Mark Spencer
committed
if (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) {
ast_log(LOG_DEBUG, "Invalid magic. MGCP subchannel freed up already.\n");
return 0;
}
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name);
}
if ((p->dtmfinband) && (p->dsp != NULL)){
Mark Spencer
committed
/* SC: check whether other channel is active. */
if (!sub->next->owner)
{
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_2 "MGCP free dsp on %s@%s\n", p->name, p->parent->name);
}
ast_dsp_free(p->dsp);
p->dsp = NULL;
}
}
ast_mutex_lock(&sub->lock);
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
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;
Mark Spencer
committed
/* SC: Decrement use count */
ast_mutex_lock(&usecnt_lock);
usecnt--;
ast_mutex_unlock(&usecnt_lock);
ast_update_use_count();
/* SC: Decrement use count */
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_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_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_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';
ast_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_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);
}
Mark Spencer
committed
/* SC: verbose level check */
if (option_verbose > 2) {
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);