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>
*
* \par See also
* \arg \ref Config_mgcp
*
* \ingroup channel_drivers
/* 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
*/
-- fixed reload_config() / do_monitor to stay responsive during reloads
*/
Mark Spencer
committed
Kevin P. Fleming
committed
#include <unistd.h>
Kevin P. Fleming
committed
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#include <stdlib.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>
#include "asterisk.h"
Kevin P. Fleming
committed
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/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/features.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/dsp.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.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.
*/
Mark Spencer
committed
#define DEFAULT_EXPIRY 120
#define MAX_EXPIRY 3600
#define CANREINVITE 1
#ifndef INADDR_NONE
#define INADDR_NONE (in_addr_t)(-1)
#endif
static const char desc[] = "Media Gateway Control Protocol (MGCP)";
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)
#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 */
#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
"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_MUSICCLASS] = "";
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 cur_signalling = -1;*/
/*static unsigned int cur_group = 0;*/
static ast_group_t cur_callergroup = 0;
static ast_group_t 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[AST_MAX_ACCOUNT_CODE] = "";
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
static pthread_t monitor_thread = AST_PTHREADT_NULL;
static int restart_monitor(void);
static int capability = AST_FORMAT_ULAW;
static int nonCodecCapability = AST_RTP_DTMF;
static char ourhost[MAXHOSTNAMELEN];
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 */
int lines; /*!< SDP Content */
char *line[MGCP_MAX_LINES];
char data[MGCP_MAX_PACKET];
int cmd; /*!< SC: int version of verb = command */
unsigned 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
*/
/*! \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];
#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 {
/* 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.
*/
Mark Spencer
committed
#define MGCP_SUBCHANNEL_MAGIC "!978!"
ast_mutex_t lock;
int id;
struct ast_channel *owner;
struct mgcp_endpoint *parent;
struct ast_rtp *rtp;
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
*/
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 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 call_forward[AST_MAX_EXTENSION]; /*!< Last Caller*ID */
char musicclass[MAX_MUSICCLASS];
char curtone[80]; /*!< Current tone */
ast_group_t callgroup;
ast_group_t pickupgroup;
int transfer;
int threewaycalling;
int dnd; /* How does this affect callwait? Do we just deny a mgcp_request if we're dnd? */
int dtmfmode;
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;
char rqnt_ident[80]; /*!< SC: request identifier */
struct mgcp_request *rqnt_queue; /*!< SC: pending RQNT commands */
Mark Spencer
committed
ast_mutex_t rqnt_queue_lock;
struct mgcp_request *cmd_queue; /*!< SC: pending commands other than RQNT */
Mark Spencer
committed
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];
int isnamedottedip; /*!< SC: 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;
Mark Spencer
committed
/* SC: obsolete
Mark Spencer
committed
int messagepending;
*/
/* JS: Wildcard endpoint name */
char wcardep[30];
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;
/*! \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_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 struct ast_channel *mgcp_request(const char *type, int format, 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);
static int mgcp_indicate(struct ast_channel *ast, int ind);
static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int mgcp_senddigit(struct ast_channel *ast, char digit);
static int mgcp_devicestate(void *data);
static const struct ast_channel_tech mgcp_tech = {
.description = tdesc,
.capabilities = AST_FORMAT_ULAW,
Mark Spencer
committed
.properties = AST_CHAN_TP_WANTSJITTER,
.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 = mgcp_senddigit,
.bridge = ast_rtp_bridge,
};
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;
}
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
/* 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));
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;
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;
Mark Spencer
committed
ast_log(LOG_NOTICE, "Removing message from %s transaction %u\n",
gw->name, cur->seqno);
w = cur;
cur = cur->next;
if (q) {
w->next = q;
} else {
w->next = NULL;
}
q = w;
} 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;
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
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);
}
__mgcp_xmit(gw, cur->buf, cur->len);
Mark Spencer
committed
prev = cur;
cur = cur->next;
} 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);
exq = exq->next;
free(cur);
}
Mark Spencer
committed
Mark Spencer
committed
}
/* SC: 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)
{
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;
}
if (!gw) {
return -1;
}
Mark Spencer
committed
/* SC
time(&t);
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",
gw->msgs ? gw->msgs->seqno : -1, (long) gw->lastouttime, (long) t);
dump_queue(sub->parent);
}
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
Mark Spencer
committed
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;
Mark Spencer
committed
if (gw->retransid == -1)
gw->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, (void *)gw);
}
ast_mutex_unlock(&gw->msgs_lock);
Mark Spencer
committed
/* SC
if (!gw->messagepending) {
gw->messagepending = 1;
gw->lastout = seqno;
Mark Spencer
committed
*/
/* 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)
int res = 0;
struct mgcp_request **queue, *q, *r, *t;
char iabuf[INET_ADDRSTRLEN];
Mark Spencer
committed
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);
819
820
821
822
823
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
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
} 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 */
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;
}
}
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,
ast_inet_ntoa(iabuf, sizeof(iabuf), 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,
ast_inet_ntoa(iabuf, sizeof(iabuf), 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[50] = "";
const 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->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);
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP distinctive callwait %s\n", tone);
}
} else {
snprintf(tone, sizeof(tone), "L/wt");
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_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);
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP distinctive ring %s\n", tone);
}
} else {
snprintf(tone, sizeof(tone), "L/rg");
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP default ring\n");
}
}
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);
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, ast->cid.cid_num, ast->cid.cid_name);
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;
}
ast_queue_control(ast, AST_CONTROL_RINGING);
return res;
}
static int mgcp_hangup(struct ast_channel *ast)
{
struct mgcp_subchannel *sub = ast->tech_pvt;
struct mgcp_endpoint *p = sub->parent;
ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name);
if (!ast->tech_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;