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;
}
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) {
/* SC: check whether other channel is active. */
if (!sub->next->owner) {
Mark Spencer
committed
if (p->dtmfmode & MGCP_DTMF_HYBRID)
p->dtmfmode &= ~MGCP_DTMF_INBAND;
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;
}
if ((sub == p->sub) && sub->next->owner) {
if (p->hookstate == MGCP_OFFHOOK) {
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);
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, "");
}
ast->tech_pvt = NULL;
sub->alreadygone = 0;
sub->outgoing = 0;
sub->cxmode = MGCP_CX_INACTIVE;
memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
if (sub->rtp) {
ast_rtp_destroy(sub->rtp);
sub->rtp = NULL;
Mark Spencer
committed
/* SC: Decrement use count */
Mark Spencer
committed
usecnt--;
ast_mutex_unlock(&usecnt_lock);
ast_update_use_count();
Mark Spencer
committed
if ((p->hookstate == MGCP_ONHOOK) && (!sub->next->rtp)) {
p->hidecallerid = 0;
if (p->hascallwaiting && !p->callwaiting) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Enabling call waiting on %s\n", ast->name);
p->callwaiting = -1;
}
if (has_voicemail(p)) {
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s set vmwi(+)\n",
ast->name, p->name, p->parent->name);
}
transmit_notify_request(sub, "L/vmwi(+)");
} else {
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s set vmwi(-)\n",
ast->name, p->name, p->parent->name);
}
transmit_notify_request(sub, "L/vmwi(-)");
}
}
ast_mutex_unlock(&sub->lock);
return 0;
}
static int mgcp_show_endpoints(int fd, int argc, char *argv[])
{
struct mgcp_gateway *g;
struct mgcp_endpoint *e;
int hasendpoints = 0;
char iabuf[INET_ADDRSTRLEN];
ast_mutex_lock(&gatelock);
g = gateways;
while(g) {
e = g->endpoints;
Mark Spencer
committed
ast_cli(fd, "Gateway '%s' at %s (%s)\n", g->name, g->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), g->addr.sin_addr) : ast_inet_ntoa(iabuf, sizeof(iabuf), g->defaddr.sin_addr), g->dynamic ? "Dynamic" : "Static");
/* JS: Don't show wilcard endpoint */
if (strcmp(e->name, g->wcardep) !=0)
ast_cli(fd, " -- '%s@%s in '%s' is %s\n", e->name, g->name, e->context, e->sub->owner ? "active" : "idle");
hasendpoints = 1;
e = e->next;
}
if (!hasendpoints) {
ast_cli(fd, " << No Endpoints Defined >> ");
}
g = g->next;
}
ast_mutex_unlock(&gatelock);
return RESULT_SUCCESS;
}
static char show_endpoints_usage[] =
"Usage: mgcp show endpoints\n"
" Lists all endpoints known to the MGCP (Media Gateway Control Protocol) subsystem.\n";
static struct ast_cli_entry cli_show_endpoints =
{ { "mgcp", "show", "endpoints", NULL }, mgcp_show_endpoints, "Show defined MGCP endpoints", show_endpoints_usage };
static int mgcp_audit_endpoint(int fd, int argc, char *argv[])
{
struct mgcp_gateway *g;
struct mgcp_endpoint *e;
int found = 0;
/* split the name into parts by null */
ename = argv[3];
gname = ename;
while (*gname) {
if (*gname == '@') {
*gname = 0;
gname++;
break;
}
gname++;
}
if (gname[0] == '[')
gname++;
if ((c = strrchr(gname, ']')))
*c = '\0';
ast_mutex_lock(&gatelock);
if (!strcasecmp(g->name, gname)) {
e = g->endpoints;
while(e) {
if (!strcasecmp(e->name, ename)) {
found = 1;
transmit_audit_endpoint(e);
break;
}
e = e->next;
}
if (found) {
break;
}
}
g = g->next;
}
if (!found) {
ast_cli(fd, " << Could not find endpoint >> ");
ast_mutex_unlock(&gatelock);
return RESULT_SUCCESS;
}
static char audit_endpoint_usage[] =
"Usage: mgcp audit endpoint <endpointid>\n"
" Lists the capabilities of an endpoint in the MGCP (Media Gateway Control Protocol) subsystem.\n"
" mgcp debug MUST be on to see the results of this command.\n";
static struct ast_cli_entry cli_audit_endpoint =
{ { "mgcp", "audit", "endpoint", NULL }, mgcp_audit_endpoint, "Audit specified MGCP endpoint", audit_endpoint_usage };
static int mgcp_answer(struct ast_channel *ast)
{
int res = 0;
struct mgcp_subchannel *sub = ast->tech_pvt;
struct mgcp_endpoint *p = sub->parent;
sub->cxmode = MGCP_CX_SENDRECV;
if (!sub->rtp) {
start_rtp(sub);
} else {
transmit_modify_request(sub);
}
/* SC: verbose level check */
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_answer(%s) on %s@%s-%d\n",
ast->name, p->name, p->parent->name, sub->id);
}
if (ast->_state != AST_STATE_UP) {
ast_setstate(ast, AST_STATE_UP);
if (option_debug)
ast_log(LOG_DEBUG, "mgcp_answer(%s)\n", ast->name);
transmit_notify_request(sub, "");
transmit_modify_request(sub);
static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub)
/* Retrieve audio/etc from channel. Assumes sub->lock is already held. */
/* Don't send RFC2833 if we're not supposed to */
if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833))
return &ast_null_frame;
/* We already hold the channel lock */
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != sub->owner->nativeformats) {
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
sub->owner->nativeformats = f->subclass;
Mark Spencer
committed
ast_set_read_format(sub->owner, sub->owner->readformat);
ast_set_write_format(sub->owner, sub->owner->writeformat);
/* Courtesy fearnor aka alex@pilosoft.com */
if ((sub->parent->dtmfmode & MGCP_DTMF_INBAND) && (sub->parent->dsp)) {
ast_log(LOG_NOTICE, "MGCP ast_dsp_process\n");
f = ast_dsp_process(sub->owner, sub->parent->dsp, f);
}
}
}
return f;
}
static struct ast_frame *mgcp_read(struct ast_channel *ast)
struct mgcp_subchannel *sub = ast->tech_pvt;
ast_mutex_lock(&sub->lock);
ast_mutex_unlock(&sub->lock);
}
static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame)
{
struct mgcp_subchannel *sub = ast->tech_pvt;
int res = 0;
if (frame->frametype != AST_FRAME_VOICE) {
if (frame->frametype == AST_FRAME_IMAGE)
return 0;
else {
ast_log(LOG_WARNING, "Can't send %d type frames with MGCP write\n", frame->frametype);
return 0;
}
} else {
if (!(frame->subclass & ast->nativeformats)) {
ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
return -1;
}
}
ast_mutex_lock(&sub->lock);
if ((sub->parent->sub == sub) || !sub->parent->singlepath) {
if (sub->rtp) {
res = ast_rtp_write(sub->rtp, frame);
}
ast_mutex_unlock(&sub->lock);
Mark Spencer
committed
static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
struct mgcp_subchannel *sub = newchan->tech_pvt;
ast_log(LOG_NOTICE, "mgcp_fixup(%s, %s)\n", oldchan->name, newchan->name);
ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
return 0;
}
static int mgcp_senddigit(struct ast_channel *ast, char digit)
{
struct mgcp_subchannel *sub = ast->tech_pvt;
char tmp[4];
tmp[0] = 'D';
tmp[1] = '/';
tmp[2] = digit;
tmp[3] = '\0';
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
/*!
* \brief mgcp_devicestate: channel callback for device status monitoring
* \param data tech/resource name of MGCP device to query
*
* Callback for device state management in channel subsystem
* to obtain device status (up/down) of a specific MGCP endpoint
*
* \return device status result (from devicestate.h) AST_DEVICE_INVALID (not available) or AST_DEVICE_UNKNOWN (available but unknown state)
*/
static int mgcp_devicestate(void *data)
{
struct mgcp_gateway *g;
struct mgcp_endpoint *e = NULL;
char *tmp, *endpt, *gw;
int ret = AST_DEVICE_INVALID;
endpt = ast_strdupa(data);
if ((tmp = strchr(endpt, '@'))) {
*tmp++ = '\0';
gw = tmp;
} else
goto error;
ast_mutex_lock(&gatelock);
g = gateways;
while (g) {
if (strcasecmp(g->name, gw) == 0) {
e = g->endpoints;
break;
}
g = g->next;
}
if (!e)
goto error;
while (e) {
if (strcasecmp(e->name, endpt) == 0)
break;
e = e->next;
}
if (!e)
goto error;
/*
* As long as the gateway/endpoint is valid, we'll
* assume that the device is available and its state
* can be tracked.
*/
ret = AST_DEVICE_UNKNOWN;
error:
ast_mutex_unlock(&gatelock);
return ret;
}
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
switch (ind) {
case AST_CONTROL_HANGUP:
return "Other end has hungup";
case AST_CONTROL_RING:
return "Local ring";
case AST_CONTROL_RINGING:
return "Remote end is ringing";
case AST_CONTROL_ANSWER:
return "Remote end has answered";
case AST_CONTROL_BUSY:
return "Remote end is busy";
case AST_CONTROL_TAKEOFFHOOK:
return "Make it go off hook";
case AST_CONTROL_OFFHOOK:
return "Line is off hook";
case AST_CONTROL_CONGESTION:
return "Congestion (circuits busy)";
case AST_CONTROL_FLASH:
return "Flash hook";
case AST_CONTROL_WINK:
return "Wink";
case AST_CONTROL_OPTION:
return "Set a low-level option";
case AST_CONTROL_RADIO_KEY:
return "Key Radio";
case AST_CONTROL_RADIO_UNKEY:
return "Un-Key Radio";
}
return "UNKNOWN";
static int mgcp_indicate(struct ast_channel *ast, int ind)
{
struct mgcp_subchannel *sub = ast->tech_pvt;
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP asked to indicate %d '%s' condition on channel %s\n",
ind, control2str(ind), ast->name);
}
Mark Spencer
committed
#ifdef DLINK_BUGGY_FIRMWARE
transmit_notify_request(sub, "rt");
#else
transmit_notify_request(sub, "G/rt");
Mark Spencer
committed
#endif
transmit_notify_request(sub, "L/bz");
transmit_notify_request(sub, "G/cg");
break;
default:
ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
ast_mutex_unlock(&sub->lock);
return res;
static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state)
struct mgcp_endpoint *i = sub->parent;
tmp->tech = &mgcp_tech;
tmp->nativeformats = i->capability;
if (!tmp->nativeformats)
tmp->nativeformats = capability;
fmt = ast_best_codec(tmp->nativeformats);
ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
if (sub->rtp)
tmp->fds[0] = ast_rtp_fd(sub->rtp);
Mark Spencer
committed
if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) {
i->dsp = ast_dsp_new();
ast_dsp_set_features(i->dsp,DSP_FEATURE_DTMF_DETECT);
/* SC: this is to prevent clipping of dtmf tones during dsp processing */
ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_NOQUELCH);
ast_setstate(tmp, state);
if (state == AST_STATE_RING)
tmp->rings = 1;
tmp->writeformat = fmt;
tmp->rawwriteformat = fmt;
tmp->rawreadformat = fmt;
tmp->tech_pvt = sub;
ast_string_field_set(tmp, language, i->language);
ast_string_field_set(tmp, accountcode, i->accountcode);
if (i->amaflags)
tmp->amaflags = i->amaflags;
sub->owner = tmp;
ast_mutex_lock(&usecnt_lock);
ast_mutex_unlock(&usecnt_lock);
tmp->callgroup = i->callgroup;
tmp->pickupgroup = i->pickupgroup;
ast_string_field_set(tmp, call_forward, i->call_forward);
strncpy(tmp->context, i->context, sizeof(tmp->context)-1);
strncpy(tmp->exten, i->exten, sizeof(tmp->exten)-1);
if (!ast_strlen_zero(i->cid_num))
tmp->cid.cid_num = strdup(i->cid_num);
if (!ast_strlen_zero(i->cid_name))
tmp->cid.cid_name = strdup(i->cid_name);
if (!i->adsi)
tmp->adsicpe = AST_ADSI_UNAVAILABLE;
tmp->priority = 1;
if (state != AST_STATE_DOWN) {
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
ast_hangup(tmp);
tmp = NULL;
}
}
/* SC: verbose level check */
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_new(%s) created in state: %s\n",
tmp->name, ast_state2str(state));
}
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
static char* get_sdp_by_line(char* line, char *name, int nameLen)
{
if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') {
char* r = line + nameLen + 1;
while (*r && (*r < 33)) ++r;
return r;
}
return "";
static char *get_sdp(struct mgcp_request *req, char *name)
{
int x;
int len = strlen(name);
char *r;
for (x=0; x<req->lines; x++) {
r = get_sdp_by_line(req->line[x], name, len);
if (r[0] != '\0') return r;
}
return "";
static void sdpLineNum_iterator_init(int* iterator)
{
*iterator = 0;
static char* get_sdp_iterate(int* iterator, struct mgcp_request *req, char *name)
{
int len = strlen(name);
char *r;
while (*iterator < req->lines) {
r = get_sdp_by_line(req->line[(*iterator)++], name, len);
if (r[0] != '\0') return r;
}
return "";
}
static char *__get_header(struct mgcp_request *req, char *name, int *start)
{
int x;
int len = strlen(name);
char *r;
for (x=*start;x<req->headers;x++) {
if (!strncasecmp(req->header[x], name, len) &&
(req->header[x][len] == ':')) {
r = req->header[x] + len + 1;
while(*r && (*r < 33))
r++;
*start = x+1;
return r;
}
}
/* Don't return NULL, so get_header is always a valid pointer */
return "";
}
static char *get_header(struct mgcp_request *req, char *name)
{
int start = 0;
return __get_header(req, name, &start);
}
/*! \brief get_csv: (SC:) get comma separated value */
static char *get_csv(char *c, int *len, char **next)
{
char *s;
*next = NULL, *len = 0;
if (!c) return NULL;
while (*c && (*c < 33 || *c == ','))
c++;
s = c;
while (*c && (*c >= 33 && *c != ','))
c++, (*len)++;
*next = c;
if (*len == 0)
s = NULL, *next = NULL;
return s;
}
static struct mgcp_subchannel *find_subchannel_and_lock(char *name, int msgid, struct sockaddr_in *sin)
char iabuf[INET_ADDRSTRLEN];
if (name) {
strncpy(tmp, name, sizeof(tmp) - 1);
at = strchr(tmp, '@');
if (!at) {
ast_log(LOG_NOTICE, "Endpoint '%s' has no at sign!\n", name);
return NULL;
}
*at = '\0';
at++;
}
ast_mutex_lock(&gatelock);
if (at && (at[0] == '[')) {
at++;
c = strrchr(at, ']');
if (c)
*c = '\0';
}
if ((!name || !strcasecmp(g->name, at)) &&
(sin || g->addr.sin_addr.s_addr || g->defaddr.sin_addr.s_addr)) {
/* Found the gateway. If it's dynamic, save it's address -- now for the endpoint */
if (sin && g->dynamic && name) {
if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) ||
(g->addr.sin_port != sin->sin_port)) {
memcpy(&g->addr, sin, sizeof(g->addr));
if (ast_ouraddrfor(&g->addr.sin_addr, &g->ourip))
memcpy(&g->ourip, &__ourip, sizeof(g->ourip));
Mark Spencer
committed
ast_verbose(VERBOSE_PREFIX_3 "Registered MGCP gateway '%s' at %s port %d\n", g->name, ast_inet_ntoa(iabuf, sizeof(iabuf), g->addr.sin_addr), ntohs(g->addr.sin_port));
/* SC: not dynamic, check if the name matches */
else if (name) {
if (strcasecmp(g->name, at)) {
g = g->next;
continue;
}
}
/* SC: not dynamic, no name, check if the addr matches */
else if (!name && sin) {
Mark Spencer
committed
if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) ||
(g->addr.sin_port != sin->sin_port)) {
g = g->next;
continue;
}
} else {
g = g->next;
continue;
}
/* SC */
ast_log(LOG_DEBUG, "Searching on %s@%s for subchannel\n",
p->name, g->name);
if (msgid) {
Mark Spencer
committed
#if 0 /* SC: new transport mech */
ast_log(LOG_DEBUG, "Searching on %s@%s-%d for subchannel with lastout: %d\n",
p->name, g->name, sub->id, msgid);
if (sub->lastout == msgid) {
ast_log(LOG_DEBUG, "Found subchannel sub%d to handle request %d sub->lastout: %d\n",
sub->id, msgid, sub->lastout);
found = 1;
break;
}
sub = sub->next;
} while (sub != p->sub);
if (found) {
break;
}
Mark Spencer
committed
#endif
/* SC */
sub = p->sub;
found = 1;
/* SC */
break;
} else if (name && !strcasecmp(p->name, tmp)) {
ast_log(LOG_DEBUG, "Coundn't determine subchannel, assuming current master %s@%s-%d\n",
p->name, g->name, p->sub->id);
sub = p->sub;
found = 1;
break;
}
p = p->next;
ast_mutex_unlock(&gatelock);
ast_log(LOG_NOTICE, "Endpoint '%s' not found on gateway '%s'\n", tmp, at);
else
ast_log(LOG_NOTICE, "Gateway '%s' (and thus its endpoint '%s') does not exist\n", at, tmp);
}
}
}
static void parse(struct mgcp_request *req)
{
/* Divide fields by NULL's */
char *c;
int f = 0;
c = req->data;
/* First header starts immediately */
req->header[f] = c;
while(*c) {
if (*c == '\n') {
/* We've got a new header */
*c = 0;
#if 0
printf("Header: %s (%d)\n", req->header[f], strlen(req->header[f]));
#endif
/* Line by itself means we're now in content */
c++;
break;
}
if (f >= MGCP_MAX_HEADERS - 1) {
ast_log(LOG_WARNING, "Too many MGCP headers...\n");
} else
f++;
req->header[f] = c + 1;
} else if (*c == '\r') {
/* Ignore but eliminate \r's */
*c = 0;
}
c++;
}
/* Check for last header */
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
f++;
req->headers = f;
/* Now we process any mime content */
f = 0;
req->line[f] = c;
while(*c) {
if (*c == '\n') {
/* We've got a new line */
*c = 0;
#if 0
printf("Line: %s (%d)\n", req->line[f], strlen(req->line[f]));
#endif
if (f >= MGCP_MAX_LINES - 1) {
ast_log(LOG_WARNING, "Too many SDP lines...\n");
} else
f++;
req->line[f] = c + 1;
} else if (*c == '\r') {
/* Ignore and eliminate \r's */
*c = 0;
}
c++;
}
/* Check for last line */
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
f++;
req->lines = f;
/* Parse up the initial header */
c = req->header[0];
while(*c && *c < 33) c++;
/* First the verb */
req->verb = c;
while(*c && (*c > 32)) c++;
if (*c) {
*c = '\0';
c++;
while(*c && (*c < 33)) c++;
req->identifier = c;
while(*c && (*c > 32)) c++;
if (*c) {
*c = '\0';
c++;
while(*c && (*c < 33)) c++;
req->endpoint = c;
while(*c && (*c > 32)) c++;
if (*c) {
*c = '\0';
c++;
while(*c && (*c < 33)) c++;
req->version = c;
while(*c && (*c > 32)) c++;
while(*c && (*c < 33)) c++;
while(*c && (*c > 32)) c++;
*c = '\0';
}
}
}
if (mgcpdebug) {
ast_verbose("Verb: '%s', Identifier: '%s', Endpoint: '%s', Version: '%s'\n",
req->verb, req->identifier, req->endpoint, req->version);
ast_verbose("%d headers, %d lines\n", req->headers, req->lines);
}
if (*c)
ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c);
}
static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req)
int peercapability, peerNonCodecCapability;
struct ast_hostent ahp; struct hostent *hp;
struct mgcp_endpoint *p = sub->parent;
/* Get codec and RTP info from SDP */
m = get_sdp(req, "m");
c = get_sdp(req, "c");
if (ast_strlen_zero(m) || ast_strlen_zero(c)) {
ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
return -1;
}
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
return -1;
}
/* XXX This could block for a long time, and block the main thread! XXX */
hp = ast_gethostbyname(host, &ahp);
if (!hp) {
ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
return -1;
}
if (sscanf(m, "audio %d RTP/AVP %n", &portno, &len) != 1) {
ast_log(LOG_WARNING, "Unable to determine port number for RTP in '%s'\n", m);
return -1;
}
sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
sin.sin_port = htons(portno);
Mark Spencer
committed
printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
/* Scan through the RTP payload types specified in a "m=" line: */
while (!ast_strlen_zero(codecs)) {
if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
if (codec_count)
break;
ast_log(LOG_WARNING, "Error in codec string '%s' at '%s'\n", m, codecs);
ast_rtp_set_m_type(sub->rtp, codec);
/* Next, scan through each "a=rtpmap:" line, noting each */
/* specified RTP payload type (with corresponding MIME subtype): */
sdpLineNum_iterator_init(&iterator);
while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2)
continue;
/* Note: should really look at the 'freq' and '#chans' params too */
ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype);
}
/* Now gather all of the codecs that were asked for: */
ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability);
ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n",
capability, peercapability, p->capability);
ast_verbose("Non-codec capabilities: us - %d, them - %d, combined - %d\n",
nonCodecCapability, peerNonCodecCapability, p->nonCodecCapability);
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
if (!p->capability) {
ast_log(LOG_WARNING, "No compatible codecs!\n");
return -1;
}
return 0;
}
static int add_header(struct mgcp_request *req, char *var, char *value)
{
if (req->len >= sizeof(req->data) - 4) {
ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
return -1;
}
if (req->lines) {
ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
return -1;
}
req->header[req->headers] = req->data + req->len;
snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s: %s\r\n", var, value);
req->len += strlen(req->header[req->headers]);
if (req->headers < MGCP_MAX_HEADERS)
req->headers++;
else {
ast_log(LOG_WARNING, "Out of header space\n");
return -1;
}
return 0;
}
static int add_line(struct mgcp_request *req, char *line)
{
if (req->len >= sizeof(req->data) - 4) {
ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
return -1;
}
if (!req->lines) {
/* Add extra empty return */
snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n");
req->len += strlen(req->data + req->len);
}
req->line[req->lines] = req->data + req->len;
snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
req->len += strlen(req->line[req->lines]);
if (req->lines < MGCP_MAX_LINES)
req->lines++;
else {
ast_log(LOG_WARNING, "Out of line space\n");
return -1;
}
return 0;
}
static int init_resp(struct mgcp_request *req, char *resp, struct mgcp_request *orig, char *resprest)
{
/* Initialize a response */
if (req->headers || req->len) {
ast_log(LOG_WARNING, "Request already initialized?!?\n");
return -1;
}
req->header[req->headers] = req->data + req->len;
snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %s %s\r\n", resp, orig->identifier, resprest);
req->len += strlen(req->header[req->headers]);
if (req->headers < MGCP_MAX_HEADERS)
req->headers++;
else
ast_log(LOG_WARNING, "Out of header space\n");
return 0;
}
static int init_req(struct mgcp_endpoint *p, struct mgcp_request *req, char *verb)
{
/* Initialize a response */
if (req->headers || req->len) {
ast_log(LOG_WARNING, "Request already initialized?!?\n");
return -1;
}
req->header[req->headers] = req->data + req->len;
/* SC: check if we need brackets around the gw name */
if (p->parent->isnamedottedip)
snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
else
snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
req->len += strlen(req->header[req->headers]);
if (req->headers < MGCP_MAX_HEADERS)
req->headers++;
else
ast_log(LOG_WARNING, "Out of header space\n");
return 0;
}
static int respprep(struct mgcp_request *resp, struct mgcp_endpoint *p, char *msg, struct mgcp_request *req, char *msgrest)
{
memset(resp, 0, sizeof(*resp));
init_resp(resp, msg, req, msgrest);
return 0;
}
static int reqprep(struct mgcp_request *req, struct mgcp_endpoint *p, char *verb)
{
memset(req, 0, sizeof(struct mgcp_request));
oseq++;
if (oseq > 999999999)
oseq = 1;
init_req(p, req, verb);
return 0;
}
static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest)
struct mgcp_endpoint *p = sub->parent;
struct mgcp_response *mgr;
mgr = malloc(sizeof(struct mgcp_response) + resp.len + 1);
if (mgr) {
/* Store MGCP response in case we have to retransmit */
memset(mgr, 0, sizeof(struct mgcp_response));
sscanf(req->identifier, "%d", &mgr->seqno);
time(&mgr->whensent);
mgr->len = resp.len;
memcpy(mgr->buf, resp.data, resp.len);
mgr->buf[resp.len] = '\0';
mgr->next = p->parent->responses;
static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp *rtp)
{
int len;
int codec;
char costr[80];
struct sockaddr_in sin;
char v[256];
char s[256];
char o[256];
char c[256];
char t[256];
char iabuf[INET_ADDRSTRLEN];
struct mgcp_endpoint *p = sub->parent;
/* XXX We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us XXX */
ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
return -1;
}
if (rtp) {
ast_rtp_get_peer(rtp, &dest);
} else {
if (sub->tmpdest.sin_addr.s_addr) {
dest.sin_addr = sub->tmpdest.sin_addr;
dest.sin_port = sub->tmpdest.sin_port;
memset(&sub->tmpdest, 0, sizeof(sub->tmpdest));
} else {
dest.sin_addr = p->parent->ourip;
dest.sin_port = sin.sin_port;
}
ast_verbose("We're at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->parent->ourip), ntohs(sin.sin_port));
}
Mark Spencer
committed
snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
Mark Spencer
committed
snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
snprintf(t, sizeof(t), "t=0 0\r\n");
snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port));
for (x = 1; x <= AST_FORMAT_MAX_AUDIO; x <<= 1) {
ast_verbose("Answering with capability %d\n", x);
codec = ast_rtp_lookup_code(sub->rtp, 1, x);
strncat(m, costr, sizeof(m) - strlen(m) - 1);
snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x));
strncat(a, costr, sizeof(a) - strlen(a) - 1);
for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
if (p->nonCodecCapability & x) {
if (mgcpdebug) {
ast_verbose("Answering with non-codec capability %d\n", x);
}
codec = ast_rtp_lookup_code(sub->rtp, 0, x);
if (codec > -1) {
snprintf(costr, sizeof(costr), " %d", codec);
strncat(m, costr, sizeof(m) - strlen(m) - 1);
snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x));
strncat(a, costr, sizeof(a) - strlen(a) - 1);
if (x == AST_RTP_DTMF) {
/* Indicate we support DTMF... Not sure about 16,
but MSN supports it so dang it, we will too... */
snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n", codec);
strncat(a, costr, sizeof(a) - strlen(a) - 1);
}
}
}
}
strncat(m, "\r\n", sizeof(m) - strlen(m) - 1);
len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a);
snprintf(costr, sizeof(costr), "%d", len);
add_line(resp, v);
add_line(resp, o);
add_line(resp, s);
add_line(resp, c);
add_line(resp, t);
add_line(resp, m);
add_line(resp, a);
return 0;
}
static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs)
{
struct mgcp_request resp;
char local[256];
char tmp[80];
int x;
struct mgcp_endpoint *p = sub->parent;
capability = p->capability;
if (codecs)
capability = codecs;
if (ast_strlen_zero(sub->cxident) && rtp) {
/* We don't have a CXident yet, store the destination and
wait a bit */
ast_rtp_get_peer(rtp, &sub->tmpdest);
snprintf(local, sizeof(local), "p:20");
for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x));
strncat(local, tmp, sizeof(local) - strlen(local) - 1);
add_header(&resp, "C", sub->callid);
add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
/* SC: X header should not be sent. kept for compatibility */
add_header(&resp, "X", sub->txident);
add_header(&resp, "I", sub->cxident);
/*add_header(&resp, "S", "");*/
add_sdp(&resp, sub, rtp);
/* SC: fill in new fields */
resp.cmd = MGCP_CMD_MDCX;
resp.trid = oseq;
Mark Spencer
committed
return send_request(p, sub, &resp, oseq); /* SC */
static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp)
{
struct mgcp_request resp;
char local[256];
char tmp[80];
int x;
struct mgcp_endpoint *p = sub->parent;
snprintf(local, sizeof(local), "p:20");
for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) {
if (p->capability & x) {
snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x));
strncat(local, tmp, sizeof(local) - strlen(local) - 1);
if (mgcpdebug) {
ast_verbose(VERBOSE_PREFIX_3 "Creating connection for %s@%s-%d in cxmode: %s callid: %s\n",
p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid);
}
add_header(&resp, "C", sub->callid);
add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
/* SC: X header should not be sent. kept for compatibility */
add_header(&resp, "X", sub->txident);
/*add_header(&resp, "S", "");*/
add_sdp(&resp, sub, rtp);
/* SC: fill in new fields */
resp.cmd = MGCP_CMD_CRCX;
resp.trid = oseq;
Mark Spencer
committed
return send_request(p, sub, &resp, oseq); /* SC */
static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone)
Loading
Loading full blame...