Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, Digium, Inc.
* Mark Spencer <markster@digium.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.
*/
*
* \brief Implementation of Media Gateway Control Protocol
*
* \author Mark Spencer <markster@digium.com>
*
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Kevin P. Fleming
committed
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/signal.h>
#include <signal.h>
Kevin P. Fleming
committed
#include <netinet/in.h>
Kevin P. Fleming
committed
#include <netinet/in_systm.h>
#include <netinet/ip.h>
Kevin P. Fleming
committed
#include <arpa/inet.h>
Kevin P. Fleming
committed
#include <ctype.h>
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
Kevin P. Fleming
committed
#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/features.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/utils.h"
#include "asterisk/netsock.h"
Kevin P. Fleming
committed
#include "asterisk/causes.h"
#include "asterisk/dsp.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/abstract_jb.h"
Tilghman Lesher
committed
#include "asterisk/chanvars.h"
#include "asterisk/pktccops.h"
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.
*/
Mark Spencer
committed
#define DEFAULT_EXPIRY 120
#define MAX_EXPIRY 3600
Kevin P. Fleming
committed
#define DIRECTMEDIA 1
#ifndef INADDR_NONE
#define INADDR_NONE (in_addr_t)(-1)
#endif
/*! Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
.flags = 0,
.max_size = -1,
.resync_threshold = -1,
.impl = "",
.target_extra = -1,
};
static struct ast_jb_conf global_jbconf;
static const char tdesc[] = "Media Gateway Control Protocol (MGCP)";
static const char config[] = "mgcp.conf";
#define MGCP_DTMF_RFC2833 (1 << 0)
Mark Spencer
committed
#define MGCP_DTMF_HYBRID (1 << 2)
Kevin P. Fleming
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 */
#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit */
#define MAX_RETRANS 5 /*!< Try only 5 times for retransmissions */
Kevin P. Fleming
committed
/*! 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
Kevin P. Fleming
committed
/*! } */
static const char * const mgcp_cxmodes[] = {
"sendonly",
"recvonly",
"sendrecv",
"confrnce",
"inactive"
Kevin P. Fleming
committed
enum {
MGCP_CMD_EPCF,
MGCP_CMD_CRCX,
MGCP_CMD_MDCX,
MGCP_CMD_DLCX,
MGCP_CMD_RQNT,
MGCP_CMD_NTFY,
MGCP_CMD_AUEP,
MGCP_CMD_AUCX,
MGCP_CMD_RSIP
};
Mark Spencer
committed
static char context[AST_MAX_EXTENSION] = "default";
static char language[MAX_LANGUAGE] = "";
static char musicclass[MAX_MUSICCLASS] = "";
static char cid_num[AST_MAX_EXTENSION] = "";
static char cid_name[AST_MAX_EXTENSION] = "";
static int dtmfmode = 0;
static int ncs = 0;
static int pktcgatealloc = 0;
static int hangupongateremove = 0;
static ast_group_t cur_callergroup = 0;
static ast_group_t cur_pickupgroup = 0;
static struct {
unsigned int tos;
unsigned int tos_audio;
unsigned int cos;
unsigned int cos_audio;
} qos = { 0, 0, 0, 0 };
static int immediate = 0;
static int callwaiting = 0;
static int callreturn = 0;
Mark Spencer
committed
static int slowsequence = 0;
Kevin P. Fleming
committed
/*! This is for flashhook transfers */
static int transfer = 0;
static int cancallforward = 0;
static int singlepath = 0;
Kevin P. Fleming
committed
static int directmedia = DIRECTMEDIA;
static char accountcode[AST_MAX_ACCOUNT_CODE] = "";
static char mailbox[AST_MAX_EXTENSION];
static int amaflags = 0;
static int adsi = 0;
static unsigned int oseq;
Kevin P. Fleming
committed
/*! Wait up to 16 seconds for first digit (FXO logic) */
static int firstdigittimeout = 16000;
Kevin P. Fleming
committed
/*! How long to wait for following digits (FXO logic) */
static int gendigittimeout = 8000;
Kevin P. Fleming
committed
/*! How long to wait for an extra digit, if there is an ambiguous match */
static int matchdigittimeout = 3000;
Kevin P. Fleming
committed
/*! 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);
Kevin P. Fleming
committed
/*! This is the thread for the monitor which checks for input on the channels
static pthread_t monitor_thread = AST_PTHREADT_NULL;
static format_t capability = AST_FORMAT_ULAW;
static int nonCodecCapability = AST_RTP_DTMF;
static char ourhost[MAXHOSTNAMELEN];
static struct in_addr __ourip;
static int ourport;
static struct ast_sched_context *sched;
/*! 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 */
int lines; /*!< SDP Content */
char *line[MGCP_MAX_LINES];
char data[MGCP_MAX_PACKET];
Kevin P. Fleming
committed
int cmd; /*!< int version of verb = command */
unsigned int trid; /*!< int version of identifier = transaction id */
struct mgcp_request *next; /*!< next in the queue */
/*! \brief mgcp_message: MGCP message for queuing up */
struct mgcp_endpoint *owner_ep;
struct mgcp_subchannel *owner_sub;
int retrans;
unsigned long expire;
unsigned int seqno;
int len;
struct mgcp_message *next;
char buf[0];
Kevin P. Fleming
committed
#define RESPONSE_TIMEOUT 30 /*!< in seconds */
struct mgcp_response {
time_t whensent;
int len;
int seqno;
struct mgcp_response *next;
char buf[0];
#define MAX_SUBS 2
#define SUB_REAL 0
#define SUB_ALT 1
struct mgcp_subchannel {
/*! 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.
*/
Mark Spencer
committed
#define MGCP_SUBCHANNEL_MAGIC "!978!"
ast_mutex_t lock;
int id;
struct ast_channel *owner;
struct mgcp_endpoint *parent;
char txident[80]; /*! \todo FIXME txident is replaced by rqnt_ident in endpoint.
This should be obsoleted */
char cxident[80];
char callid[80];
int cxmode;
Kevin P. Fleming
committed
struct mgcp_request *cx_queue; /*!< pending CX commands */
ast_mutex_t cx_queue_lock; /*!< CX queue lock */
Kevin P. Fleming
committed
int iseq; /*!< Not used? RTP? */
int sdpsent;
struct cops_gate *gate;
Kevin P. Fleming
committed
struct mgcp_subchannel *next; /*!< for out circular linked list */
#define TYPE_TRUNK 1
#define TYPE_LINE 2
ast_mutex_t lock;
struct mgcp_subchannel *sub; /*!< Pointer to our current connection, channel and stuff */
char accountcode[AST_MAX_ACCOUNT_CODE];
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 number */
char cid_name[AST_MAX_EXTENSION]; /*!< Caller*ID name */
char lastcallerid[AST_MAX_EXTENSION]; /*!< Last Caller*ID */
char dtmf_buf[AST_MAX_EXTENSION]; /*!< place to collect digits be */
char call_forward[AST_MAX_EXTENSION]; /*!< Last Caller*ID */
char musicclass[MAX_MUSICCLASS];
char curtone[80]; /*!< Current tone */
char mailbox[AST_MAX_EXTENSION];
struct ast_event_sub *mwi_event_sub;
ast_group_t callgroup;
ast_group_t pickupgroup;
int transfer;
int threewaycalling;
Kevin P. Fleming
committed
int directmedia;
int dnd; /* How does this affect callwait? Do we just deny a mgcp_request if we're dnd? */
int dtmfmode;
int ncs;
int pktcgatealloc;
int hangupongateremove;
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 msgstate; /*!< voicemail message state */
int immediate;
int hookstate;
int adsi;
Kevin P. Fleming
committed
char rqnt_ident[80]; /*!< request identifier */
struct mgcp_request *rqnt_queue; /*!< pending RQNT commands */
Mark Spencer
committed
ast_mutex_t rqnt_queue_lock;
Kevin P. Fleming
committed
struct mgcp_request *cmd_queue; /*!< pending commands other than RQNT */
Mark Spencer
committed
ast_mutex_t cmd_queue_lock;
Kevin P. Fleming
committed
int delme; /*!< needed for reload */
int needaudit; /*!< 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 */
Tilghman Lesher
committed
struct ast_variable *chanvars; /*!< Variables to set for channel created by user */
struct mgcp_endpoint *next;
struct mgcp_gateway *parent;
};
/* A gateway containing one or more endpoints */
char name[80];
Kevin P. Fleming
committed
int isnamedottedip; /*!< is the name FQDN or dotted ip */
int expire; /*!< XXX Should we ever expire dynamic registrations? XXX */
struct mgcp_endpoint *endpoints;
struct ast_ha *ha;
Kevin P. Fleming
committed
/* obsolete
Mark Spencer
committed
int messagepending;
*/
Kevin P. Fleming
committed
/* Wildcard endpoint name */
Kevin P. Fleming
committed
struct mgcp_message *msgs; /*!< gw msg queue */
Kevin P. Fleming
committed
int retransid; /*!< retrans timer id */
int delme; /*!< needed for reload */
int realtime;
struct mgcp_response *responses;
} *gateways = NULL;
AST_MUTEX_DEFINE_STATIC(mgcp_reload_lock);
Mark Spencer
committed
static int mgcp_reloading = 0;
/*! \brief gatelock: mutex for gateway/endpoint lists */
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_connect(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_instance *rtp, format_t 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);
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 char *mgcp_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static int reload_config(int reload);
static struct ast_channel *mgcp_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
static int mgcp_call(struct ast_channel *ast, char *dest, int timeout);
static int mgcp_hangup(struct ast_channel *ast);
static int mgcp_answer(struct ast_channel *ast);
static struct ast_frame *mgcp_read(struct ast_channel *ast);
static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame);
Kevin P. Fleming
committed
static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int mgcp_senddigit_begin(struct ast_channel *ast, char digit);
static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int mgcp_devicestate(void *data);
static void add_header_offhook(struct mgcp_subchannel *sub, struct mgcp_request *resp, char *tone);
static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp_instance *rtp);
static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v);
static int mgcp_alloc_pktcgate(struct mgcp_subchannel *sub);
static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
Tilghman Lesher
committed
static struct ast_variable *add_var(const char *buf, struct ast_variable *list);
static struct ast_variable *copy_vars(struct ast_variable *src);
static const struct ast_channel_tech mgcp_tech = {
.description = tdesc,
.capabilities = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
.requester = mgcp_request,
.devicestate = mgcp_devicestate,
.call = mgcp_call,
.hangup = mgcp_hangup,
.answer = mgcp_answer,
.read = mgcp_read,
.write = mgcp_write,
.indicate = mgcp_indicate,
.fixup = mgcp_fixup,
.send_digit_begin = mgcp_senddigit_begin,
.send_digit_end = mgcp_senddigit_end,
};
static void mwi_event_cb(const struct ast_event *event, void *userdata)
{
/* This module does not handle MWI in an event-based manner. However, it
* subscribes to MWI for each mailbox that is configured so that the core
* knows that we care about it. Then, chan_mgcp will get the MWI from the
* event cache instead of checking the mailbox directly. */
}
static int has_voicemail(struct mgcp_endpoint *p)
{
int new_msgs;
struct ast_event *event;
char *mbox, *cntx;
cntx = mbox = ast_strdupa(p->mailbox);
strsep(&cntx, "@");
if (ast_strlen_zero(cntx))
cntx = "default";
event = ast_event_get_cached(AST_EVENT_MWI,
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mbox,
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cntx,
AST_EVENT_IE_END);
if (event) {
new_msgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
ast_event_destroy(event);
} else
new_msgs = ast_app_has_voicemail(p->mailbox, NULL);
return new_msgs;
}
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_debug(1, "Released sub %d of channel %s@%s\n", sub->id, p->name, p->parent->name);
}
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) {
Kevin P. Fleming
committed
/* modified for new transport mechanism */
Mark Spencer
committed
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));
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;
ast_debug(1, "Retransmitting:\n%s\n to %s:%d\n", resp->buf, ast_inet_ntoa(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;
ast_debug(1, "Transmitting:\n%s\n to %s:%d\n", req->data, ast_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;
}
Kevin P. Fleming
committed
/* modified for new transport framework */
Mark Spencer
committed
static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p)
Mark Spencer
committed
struct mgcp_message *cur, *q = NULL, *w, *prev;
for (prev = NULL, cur = gw->msgs; cur; prev = cur, cur = cur->next) {
Mark Spencer
committed
ast_log(LOG_NOTICE, "Removing message from %s transaction %u\n",
gw->name, cur->seqno);
w = cur;
if (q) {
w->next = q;
} else {
w->next = NULL;
}
q = w;
}
}
ast_mutex_unlock(&gw->msgs_lock);
while (q) {
cur = q;
q = q->next;
static void mgcp_queue_frame(struct mgcp_subchannel *sub, struct ast_frame *f)
{
Russell Bryant
committed
if (!ast_channel_trylock(sub->owner)) {
Russell Bryant
committed
ast_channel_unlock(sub->owner);
DEADLOCK_AVOIDANCE(&sub->lock);
}
}
static void mgcp_queue_hangup(struct mgcp_subchannel *sub)
{
Russell Bryant
committed
if (!ast_channel_trylock(sub->owner)) {
Michiel van Baak
committed
ast_queue_hangup(sub->owner);
Russell Bryant
committed
ast_channel_unlock(sub->owner);
DEADLOCK_AVOIDANCE(&sub->lock);
}
}
static void mgcp_queue_control(struct mgcp_subchannel *sub, int control)
{
struct ast_frame f = { AST_FRAME_CONTROL, { control } };
return mgcp_queue_frame(sub, &f);
}
static int retrans_pkt(const void *data)
Mark Spencer
committed
{
struct mgcp_gateway *gw = (struct mgcp_gateway *)data;
Mark Spencer
committed
struct mgcp_message *cur, *exq = NULL, *w, *prev;
Mark Spencer
committed
/* find out expired msgs */
ast_mutex_lock(&gw->msgs_lock);
Mark Spencer
committed
for (prev = NULL, cur = gw->msgs; cur; prev = cur, cur = cur->next) {
if (cur->retrans < MAX_RETRANS) {
cur->retrans++;
ast_debug(1, "Retransmitting #%d transaction %u on [%s]\n",
cur->retrans, cur->seqno, gw->name);
__mgcp_xmit(gw, cur->buf, cur->len);
} else {
if (prev)
prev->next = cur->next;
else
gw->msgs = cur->next;
Mark Spencer
committed
ast_log(LOG_WARNING, "Maximum retries exceeded for transaction %u on [%s]\n",
cur->seqno, gw->name);
Mark Spencer
committed
Mark Spencer
committed
if (exq) {
w->next = exq;
} else {
w->next = NULL;
}
exq = w;
}
}
Mark Spencer
committed
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);
Mark Spencer
committed
Mark Spencer
committed
}
Kevin P. Fleming
committed
/* modified for the new transaction mechanism */
static int mgcp_postrequest(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
char *data, int len, unsigned int seqno)
Tilghman Lesher
committed
struct mgcp_message *msg;
Tilghman Lesher
committed
struct mgcp_gateway *gw;
Mark Spencer
committed
if (!(msg = ast_malloc(sizeof(*msg) + len))) {
if (!(gw = ((p && p->parent) ? p->parent : NULL))) {
Tilghman Lesher
committed
ast_free(msg);
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
for (cur = gw->msgs; cur && cur->next; cur = cur->next);
Mark Spencer
committed
gw->msgs = msg;
Mark Spencer
committed
now = ast_tvnow();
msg->expire = now.tv_sec * 1000 + now.tv_usec / 1000 + DEFAULT_RETRANS;
Mark Spencer
committed
Tilghman Lesher
committed
if (gw->retransid == -1)
gw->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, (void *)gw);
ast_mutex_unlock(&gw->msgs_lock);
__mgcp_xmit(gw, msg->buf, msg->len);
/* XXX Should schedule retransmission XXX */
Kevin P. Fleming
committed
/* modified for new transport */
static int send_request(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
Mark Spencer
committed
struct mgcp_request *req, unsigned int seqno)
int res = 0;
struct mgcp_request **queue, *q, *r, *t;
ast_mutex_t *l;
Mark Spencer
committed
ast_debug(1, "Slow sequence is %d\n", p->slowsequence);
Mark Spencer
committed
if (p->slowsequence) {
queue = &p->cmd_queue;
l = &p->cmd_queue_lock;
ast_mutex_lock(l);
} else {
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 */
/* buggy sb5120 */
if (!sub->parent->ncs) {
while (q) {
r = q->next;
ast_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;
}
}
ast_log(LOG_WARNING, "Cannot post MGCP request: insufficient memory\n");
ast_mutex_unlock(l);
return -1;
}
memcpy(r, req, sizeof(*r));
ast_debug(1, "Posting Request:\n%s to %s:%d\n", req->data,
ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
res = mgcp_postrequest(p, sub, req->data, req->len, seqno);
} else {
ast_debug(1, "Queueing Request:\n%s to %s:%d\n", req->data,
ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
Kevin P. Fleming
committed
/* XXX 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[50] = "";
const char *distinctive_ring = NULL;
struct varshead *headp;
struct ast_var_t *current;
ast_debug(3, "MGCP mgcp_call(%s)\n", ast->name);
sub = ast->tech_pvt;
p = sub->parent;
headp = &ast->varshead;
AST_LIST_TRAVERSE(headp,current,entries) {
/* Check whether there is an 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:
if (!ast_strlen_zero(distinctive_ring)) {
snprintf(tone, sizeof(tone), "L/wt%s", distinctive_ring);
ast_debug(3, "MGCP distinctive callwait %s\n", tone);
ast_copy_string(tone, (p->ncs ? "L/wt1" : "L/wt"), sizeof(tone));
ast_debug(3, "MGCP normal callwait %s\n", tone);
break;
case MGCP_ONHOOK:
default:
if (!ast_strlen_zero(distinctive_ring)) {
snprintf(tone, sizeof(tone), "L/r%s", distinctive_ring);
ast_debug(3, "MGCP distinctive ring %s\n", tone);
Russell Bryant
committed
ast_copy_string(tone, "L/rg", sizeof(tone));
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);
ast_setstate(ast, AST_STATE_RINGING);
if (!sub->rtp) {
start_rtp(sub);
} else {
transmit_modify_request(sub);
}
if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(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,
S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, ""),
S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, ""));
if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(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->tech_pvt;
struct mgcp_endpoint *p = sub->parent;
ast_debug(1, "mgcp_hangup(%s)\n", ast->name);
if (!ast->tech_pvt) {
ast_debug(1, "Asked to hangup channel not connected\n");
if (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) {
ast_debug(1, "Invalid magic. MGCP subchannel freed up already.\n");
Mark Spencer
committed
return 0;
ast_debug(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) {
Kevin P. Fleming
committed
/* check whether other channel is active. */
Mark Spencer
committed
p->dtmfmode &= ~MGCP_DTMF_INBAND;
ast_debug(2, "MGCP free dsp on %s@%s\n", p->name, p->parent->name);
ast_dsp_free(p->dsp);
p->dsp = NULL;
}
/* for deleting gate */
if (p->pktcgatealloc && sub->gate) {
sub->gate->gate_open = NULL;
sub->gate->gate_remove = NULL;
sub->gate->got_dq_gi = NULL;
sub->gate->tech_pvt = NULL;
if (sub->gate->state == GATE_ALLOC_PROGRESS || sub->gate->state == GATE_ALLOCATED) {
ast_pktccops_gate_alloc(GATE_DEL, sub->gate, 0, 0, 0, 0, 0, 0, NULL, NULL);
} else {
sub->gate->deltimer = time(NULL) + 5;
}
sub->gate = NULL;
}
if ((sub == p->sub) && sub->next->owner) {
if (p->hookstate == MGCP_OFFHOOK) {
if (sub->next->owner && ast_bridged_channel(sub->next->owner)) {
bridged = ast_bridged_channel(sub->next->owner);
transmit_notify_request_with_callerid(p->sub, (p->ncs ? "L/wt1" : "L/wt"),
S_COR(bridged->caller.id.number.valid, bridged->caller.id.number.str, ""),
S_COR(bridged->caller.id.name.valid, bridged->caller.id.name.str, ""));
}
} 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 && ast_bridged_channel(sub->next->owner)) {
bridged = ast_bridged_channel(sub->next->owner);
transmit_notify_request_with_callerid(p->sub, "L/rg",
S_COR(bridged->caller.id.number.valid, bridged->caller.id.number.str, ""),
S_COR(bridged->caller.id.name.valid, bridged->caller.id.name.str, ""));
} else if ((sub == p->sub->next) && p->hookstate == MGCP_OFFHOOK) {
transmit_notify_request(sub, p->ncs ? "" : "L/v");
} else if (p->hookstate == MGCP_OFFHOOK) {
transmit_notify_request(sub, "L/ro");
} else {
transmit_notify_request(sub, "");
}
ast->tech_pvt = NULL;