Skip to content
Snippets Groups Projects
chan_mgcp.c 128 KiB
Newer Older
Mark Spencer's avatar
Mark Spencer committed
/*
 * Asterisk -- An open source telephony toolkit.
Mark Spencer's avatar
Mark Spencer committed
 *
 * Copyright (C) 1999 - 2006, Digium, Inc.
Mark Spencer's avatar
Mark Spencer committed
 *
 * Mark Spencer <markster@digium.com>
Mark Spencer's avatar
Mark Spencer committed
 *
 * 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.
 *
Mark Spencer's avatar
Mark Spencer committed
 * 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>
 *
Russell Bryant's avatar
Russell Bryant committed
 * \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
*/

/* 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's avatar
Mark Spencer committed
#include <stdio.h>
#include <string.h>
#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>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
Kevin P. Fleming's avatar
Kevin P. Fleming committed
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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

/*
 * 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	*/
Mark Spencer's avatar
Mark Spencer committed
#define MGCPDUMPER
#define DEFAULT_EXPIRY	120
#define MAX_EXPIRY	3600
#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_INBAND	(1 << 1)
#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 */
/* 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"
/* 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 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 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 relaxdtmf = 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 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;

Mark Spencer's avatar
Mark Spencer committed
static int usecnt =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;

Mark Spencer's avatar
Mark Spencer 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);
Mark Spencer's avatar
Mark Spencer committed

/* 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;
Mark Spencer's avatar
Mark Spencer committed

static int restart_monitor(void);

static int capability = AST_FORMAT_ULAW;
static int nonCodecCapability = AST_RTP_DTMF;
static char ourhost[MAXHOSTNAMELEN];
Mark Spencer's avatar
Mark Spencer committed
static struct in_addr __ourip;
static int ourport;

static int mgcpdebug = 0;

Mark Spencer's avatar
Mark Spencer committed
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
Mark Spencer's avatar
Mark Spencer committed

struct mgcp_request {
	int len;
	char *verb;
	char *identifier;
	char *endpoint;
	char *version;
	int headers;			/*!< MGCP Headers */
Mark Spencer's avatar
Mark Spencer committed
	char *header[MGCP_MAX_HEADERS];
	int lines;			/*!< SDP Content */
Mark Spencer's avatar
Mark Spencer committed
	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's avatar
Mark Spencer committed
static struct mgcp_pkt {
	int retrans;
	struct mgcp_endpoint *owner;
	int packetlen;
	char data[MGCP_MAX_PACKET];
	struct mgcp_pkt *next;
} *packets = NULL;	
/*! \brief mgcp_message: MGCP message for queuing up */
struct mgcp_message {
	struct mgcp_endpoint *owner_ep;
	struct mgcp_subchannel *owner_sub;
	int retrans;
	unsigned long expire;
	unsigned int seqno;
	int len;
	struct mgcp_message *next;
#define RESPONSE_TIMEOUT 30	/* in seconds */

struct mgcp_response {
	time_t whensent;
	int len;
	int seqno;
	struct mgcp_response *next;
#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.
	 */
	char magic[6]; 
	int id;
	struct ast_channel *owner;
	struct mgcp_endpoint *parent;
	struct ast_rtp *rtp;
	struct sockaddr_in tmpdest;
	char txident[80]; /* FIXME SC: txident is replaced by rqnt_ident in endpoint. 
			This should be obsoleted */
	char cxident[80];
	char callid[80];
	time_t lastouttime;
	int lastout;
	int cxmode;
	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;
	int messagepending;
	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
Mark Spencer's avatar
Mark Spencer committed

struct mgcp_endpoint {
Mark Spencer's avatar
Mark Spencer committed
	char name[80];
	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 */
Mark Spencer's avatar
Mark Spencer committed
	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 mailbox[AST_MAX_EXTENSION];
	char musicclass[MAX_MUSICCLASS];
	char curtone[80];			/*!< Current tone */
	ast_group_t callgroup;
	ast_group_t pickupgroup;
	int callwaiting;
	int hascallwaiting;
	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? */
Mark Spencer's avatar
Mark Spencer committed
	int hascallerid;
	int hidecallerid;
Mark Spencer's avatar
Mark Spencer committed
	int amaflags;
	int type;
	int slowsequence;			/*!< MS: Sequence the endpoint as a whole */
Mark Spencer's avatar
Mark Spencer committed
	int group;
	int iseq; /*!< Not used? */
	int lastout; /*!< tracking this on the subchannels.  Is it needed here? */
	int needdestroy; /*!< Not used? */
Mark Spencer's avatar
Mark Spencer committed
	int capability;
	int nonCodecCapability;
	int onhooktime;
	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 */
	struct mgcp_request *cmd_queue;  /*!< SC: pending commands other than RQNT */
	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 */
Mark Spencer's avatar
Mark Spencer committed
	struct mgcp_endpoint *next;
	struct mgcp_gateway *parent;
};

static struct mgcp_gateway {
Mark Spencer's avatar
Mark Spencer committed
	/* A gateway containing one or more endpoints */
	char name[80];
	int isnamedottedip; /*!< SC: is the name FQDN or dotted ip */
Mark Spencer's avatar
Mark Spencer committed
	struct sockaddr_in addr;
	struct sockaddr_in defaddr;
Mark Spencer's avatar
Mark Spencer committed
	struct in_addr ourip;
	int dynamic;
	int expire;		/*!< XXX Should we ever expire dynamic registrations? XXX */
Mark Spencer's avatar
Mark Spencer committed
	struct mgcp_endpoint *endpoints;
	struct ast_ha *ha;
	time_t lastouttime;
	int lastout;
/* 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;
Mark Spencer's avatar
Mark Spencer committed
	struct mgcp_gateway *next;
} *gateways;

AST_MUTEX_DEFINE_STATIC(mgcp_reload_lock);
/*! \brief gatelock: mutex for gateway/endpoint lists */
AST_MUTEX_DEFINE_STATIC(gatelock);
Mark Spencer's avatar
Mark Spencer committed

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);
Mark Spencer's avatar
Mark Spencer committed
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);
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,
	.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;
	if (!ast_strlen_zero(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;
	}
	dump_cmd_queues(NULL, sub); /* SC */
/* SC: modified for new transport mechanism */
static int __mgcp_xmit(struct mgcp_gateway *gw, char *data, int len)
Mark Spencer's avatar
Mark Spencer committed
{
	int res;
		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));
Mark Spencer's avatar
Mark Spencer committed
	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];
		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)
Mark Spencer's avatar
Mark Spencer committed
{
	struct mgcp_endpoint *p = sub->parent;
Mark Spencer's avatar
Mark Spencer committed
	int res;
	char iabuf[INET_ADDRSTRLEN];
	if (mgcpdebug) {
		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));
	res = __mgcp_xmit(p->parent, req->data, req->len);
Mark Spencer's avatar
Mark Spencer committed
	if (res > 0)
		res = 0;
	return res;
}

/* SC: modified for new transport framework */
static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p)
	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", 
				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);
}

	struct mgcp_gateway *gw = (struct mgcp_gateway *)data;
	struct mgcp_message *cur, *exq = NULL, *w, *prev;
	int res = 0;
	/* 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);
			}
			__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);
			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, 
                            char *data, int len, unsigned int seqno)
{
	struct mgcp_message *msg = malloc(sizeof(struct mgcp_message) + len);
	struct mgcp_message *cur;
	struct mgcp_gateway *gw = ((p && p->parent) ? p->parent : NULL);
 	struct timeval tv;

	if (!msg) {
		return -1;
	}
	if (!gw) {
		return -1;
	}
	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);
	}
	msg->seqno = seqno;
	msg->next = NULL;
	msg->len = len;
	memcpy(msg->buf, data, msg->len);
	ast_mutex_lock(&gw->msgs_lock);
	if (cur) {
		while(cur->next)
			cur = cur->next;
		cur->next = msg;

	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 */
	} else
		ast_log(LOG_DEBUG, "Deferring transmission of transaction %d\n", seqno);
/* 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's avatar
Mark Spencer committed
{
	int res = 0;
	struct mgcp_request **queue, *q, *r, *t;
	char iabuf[INET_ADDRSTRLEN];
	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 {
		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;
Mark Spencer's avatar
Mark Spencer committed
}

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);
	}
	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;
	}
Mark Spencer's avatar
Mark Spencer committed
	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_mutex_unlock(&sub->lock);
Mark Spencer's avatar
Mark Spencer committed
		return -1;
	}

	res = 0;
	sub->outgoing = 1;
	sub->cxmode = MGCP_CX_RECVONLY;
Mark Spencer's avatar
Mark Spencer committed
	if (p->type == TYPE_LINE) {
		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);
Mark Spencer's avatar
Mark Spencer committed
		ast_setstate(ast, AST_STATE_RINGING);
		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);
		}
Mark Spencer's avatar
Mark Spencer committed
	} else {
		ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n");
		res = -1;
	}
	ast_mutex_unlock(&sub->lock);
	ast_queue_control(ast, AST_CONTROL_RINGING);
Mark Spencer's avatar
Mark Spencer committed
	return res;
}

static int mgcp_hangup(struct ast_channel *ast)
{
	struct mgcp_subchannel *sub = ast->tech_pvt;
	struct mgcp_endpoint *p = sub->parent;

	if (option_debug) {
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name);
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
		return 0;
	}
	if (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) {
		ast_log(LOG_DEBUG, "Invalid magic. MGCP subchannel freed up already.\n");
		return 0;