Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Implementation of Media Gateway Control Protocol
*
* Copyright (C) 1999-2004, Digium, Inc.
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
/* FO: Changes
* -- add distinctive ring signalling (part of RFC 3660)
*/
/* JS: Changes
-- add support for the wildcard endpoint
-- seteable wildcard with wcardep on mgcp.conf
-- added package indicator on RQNT, i.e "dl" --> "L/dl"
-- removed MDCX just before DLCX, do we need this ?
*/
/* JS: TODO
-- reload for wildcard endpoint probably buggy
-- when hf is notified we're sending CRCX after MDCX, without waiting for
OK on the MDCX which fails on Cisco IAD 24XX
-- honour codec order, by now the lowest codec number in "allow" is the prefered
*/
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
*/
/* FS: Changes
* -- fixed reload_config() / do_monitor to stay responsive during reloads
*/
Mark Spencer
committed
#include <stdio.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>
Mark Spencer
committed
#include <asterisk/features.h>
#include <asterisk/app.h>
#include <asterisk/musiconhold.h>
#include <asterisk/utils.h>
Mark Spencer
committed
#include <asterisk/causes.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
Mark Spencer
committed
/*
* Define to work around buggy dlink MGCP phone firmware which
* appears not to know that "rt" is part of the "G" package.
*/
/* #define DLINK_BUGGY_FIRMWARE */
#define MGCPDUMPER
#define DEFAULT_EXPIREY 120
#define MAX_EXPIREY 3600
#define CANREINVITE 1
#ifndef INADDR_NONE
#define INADDR_NONE (in_addr_t)(-1)
#endif
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 MGCP_DTMF_RFC2833 (1 << 0)
#define MGCP_DTMF_INBAND (1 << 1)
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 cid_num[AST_MAX_EXTENSION] = "";
static char cid_name[AST_MAX_EXTENSION] = "";
static int dtmfmode = 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 */
/* dtmfmode is actually hooked up. */
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;
Mark Spencer
committed
static int slowsequence = 0;
static int threewaycalling = 0;
/* This is for flashhook transfers */
static int transfer = 0;
static int cancallforward = 0;
static int singlepath = 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;
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
/* SC: transaction id should always be positive */
static unsigned 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. */
AST_MUTEX_DEFINE_STATIC(netlock);
AST_MUTEX_DEFINE_STATIC(monlock);
/* 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 */
unsigned int trid; /* SC: int version of identifier = transaction id */
Mark Spencer
committed
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 RESPONSE_TIMEOUT 30 /* in seconds */
struct mgcp_response {
time_t whensent;
int len;
int seqno;
struct mgcp_response *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 cid_num[AST_MAX_EXTENSION]; /* Caller*ID */
char cid_name[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 callreturn;
int dnd; /* How does this affect callwait? Do we just deny a mgcp_request if we're dnd? */
int dtmfmode;
Mark Spencer
committed
int slowsequence; /* MS: Sequence the endpoint as a whole */
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;
*/
/* JS: Wildcard endpoint name */
char wcardep[30];
Mark Spencer
committed
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 */
struct mgcp_response *responses;
AST_MUTEX_DEFINE_STATIC(mgcp_reload_lock);
Mark Spencer
committed
static int mgcp_reloading = 0;
AST_MUTEX_DEFINE_STATIC(gatelock);
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 *callernum, char *callername);
static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs);
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, unsigned int ident, struct mgcp_request *resp);
Mark Spencer
committed
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[]);
static int has_voicemail(struct mgcp_endpoint *p)
{
return ast_app_has_voicemail(p->mailbox, NULL);
}
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 resend_response(struct mgcp_subchannel *sub, struct mgcp_response *resp)
{
struct mgcp_endpoint *p = sub->parent;
int res;
char iabuf[INET_ADDRSTRLEN];
if (mgcpdebug) {
Mark Spencer
committed
ast_verbose("Retransmitting:\n%s\n to %s:%d\n", resp->buf, ast_inet_ntoa(iabuf, sizeof(iabuf), p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
}
res = __mgcp_xmit(p->parent, resp->buf, resp->len);
if (res > 0)
res = 0;
return res;
}
static int send_response(struct mgcp_subchannel *sub, struct mgcp_request *req)
struct mgcp_endpoint *p = sub->parent;
char iabuf[INET_ADDRSTRLEN];
Mark Spencer
committed
ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, ast_inet_ntoa(iabuf, sizeof(iabuf), 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 transaction %u\n",
Mark Spencer
committed
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);
}
static void mgcp_queue_frame(struct mgcp_subchannel *sub, struct ast_frame *f)
{
for(;;) {
if (sub->owner) {
if (!ast_mutex_trylock(&sub->owner->lock)) {
ast_queue_frame(sub->owner, f);
ast_mutex_unlock(&sub->owner->lock);
} else {
ast_mutex_unlock(&sub->lock);
usleep(1);
ast_mutex_lock(&sub->lock);
}
} else
break;
}
}
static void mgcp_queue_hangup(struct mgcp_subchannel *sub)
{
for(;;) {
if (sub->owner) {
if (!ast_mutex_trylock(&sub->owner->lock)) {
ast_queue_hangup(sub->owner);
ast_mutex_unlock(&sub->owner->lock);
} else {
ast_mutex_unlock(&sub->lock);
usleep(1);
ast_mutex_lock(&sub->lock);
}
} else
break;
}
}
static void mgcp_queue_control(struct mgcp_subchannel *sub, int control)
{
struct ast_frame f = { AST_FRAME_CONTROL, };
f.subclass = control;
return mgcp_queue_frame(sub, &f);
}
Mark Spencer
committed
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 %u on [%s]\n", cur->retrans, cur->seqno, gw->name);
Mark Spencer
committed
}
__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 %u on [%s]\n", cur->seqno, gw->name);
Mark Spencer
committed
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
693
694
695
696
697
698
699
700
701
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
int res = 0;
struct mgcp_request **queue, *q, *r, *t;
char iabuf[INET_ADDRSTRLEN];
Mark Spencer
committed
ast_mutex_t *l;
Mark Spencer
committed
ast_log(LOG_DEBUG, "Slow sequence is %d\n", p->slowsequence);
if (p->slowsequence) {
queue = &p->cmd_queue;
l = &p->cmd_queue_lock;
ast_mutex_lock(l);
} else
Mark Spencer
committed
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
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
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,
Mark Spencer
committed
ast_inet_ntoa(iabuf, sizeof(iabuf), p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
Mark Spencer
committed
}
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,
Mark Spencer
committed
ast_inet_ntoa(iabuf, sizeof(iabuf), p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
Mark Spencer
committed
}
}
/* 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;
char tone[50]="";
char *distinctive_ring = NULL;
struct varshead *headp;
struct ast_var_t *current;
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_call(%s)\n", ast->name);
}
sub = ast->pvt->pvt;
headp=&ast->varshead;
AST_LIST_TRAVERSE(headp,current,entries) {
/* Check whether there is a ALERT_INFO variable */
if (strcasecmp(ast_var_name(current),"ALERT_INFO")==0) {
distinctive_ring = ast_var_value(current);
}
}
+
ast_mutex_lock(&sub->lock);
switch (p->hookstate) {
case MGCP_OFFHOOK:
snprintf(tone, sizeof(tone), "L/wt");
break;
case MGCP_ONHOOK:
default:
if (distinctive_ring && !ast_strlen_zero(distinctive_ring)) {
snprintf(tone, sizeof(tone), "L/r%s", distinctive_ring);
ast_verbose(VERBOSE_PREFIX_2 "MGCP distinctive ring %s\n", tone);
} else {
snprintf(tone, sizeof(tone), "L/rg");
ast_verbose(VERBOSE_PREFIX_2 "MGCP default ring\n");
}
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);
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->cid.cid_num, ast->cid.cid_name);
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;
}
ast_queue_control(ast, AST_CONTROL_RINGING);
return res;
}
static int mgcp_hangup(struct ast_channel *ast)
{
struct mgcp_subchannel *sub = ast->pvt->pvt;
struct mgcp_endpoint *p = sub->parent;
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 (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) {
Mark Spencer
committed
ast_log(LOG_DEBUG, "Invalid magic. MGCP subchannel freed up already.\n");
return 0;
}
ast_mutex_lock(&sub->lock);
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name);
}
Mark Spencer
committed
if ((p->dtmfmode & MGCP_DTMF_INBAND) && p->dsp) {
Mark Spencer
committed
/* SC: check whether other channel is active. */
Mark Spencer
committed
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;
}
sub->owner = NULL;
if (strlen(sub->cxident)) {
transmit_connection_del(sub);
if ((sub == p->sub) && sub->next->owner) {
if (p->hookstate == MGCP_OFFHOOK) {
Mark Spencer
committed
if (sub->next->owner && ast_bridged_channel(sub->next->owner)) {
transmit_notify_request_with_callerid(p->sub, "L/wt", ast_bridged_channel(sub->next->owner)->cid.cid_num, ast_bridged_channel(sub->next->owner)->cid.cid_name);
}
} 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);
Mark Spencer
committed
if (sub->next->owner && ast_bridged_channel(sub->next->owner)) {
transmit_notify_request_with_callerid(p->sub, "L/rg", ast_bridged_channel(sub->next->owner)->cid.cid_num, ast_bridged_channel(sub->next->owner)->cid.cid_name);
}
}
} else if ((sub == p->sub->next) && p->hookstate == MGCP_OFFHOOK) {
transmit_notify_request(sub, "L/v");
} else if (p->hookstate == MGCP_OFFHOOK) {
transmit_notify_request(sub, "L/ro");
} else {
transmit_notify_request(sub, "");
}
sub->alreadygone = 0;
sub->outgoing = 0;
sub->cxmode = MGCP_CX_INACTIVE;
memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
if (sub->rtp) {