Newer
Older
*
* OpenH323 Channel Driver for ASTERISK PBX.
* By Jeremy McNamara
* For The NuFone Network
*
* chan_h323 has been derived from code created by
* Michael Manousos and Mark Spencer
*
* This file is part of the chan_h323 driver for Asterisk
*
* chan_h323 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* chan_h323 is distributed WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/param.h>
#if defined(BSD)
#ifndef IPTOS_MINCOST
#define IPTOS_MINCOST 0x02
#endif
#endif
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <asterisk/lock.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/config.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/dsp.h>
Mark Spencer
committed
#include <asterisk/causes.h>
send_digit_cb on_send_digit;
Jeremy McNamara
committed
on_rtp_cb on_external_rtp_create;
start_rtp_cb on_start_rtp_channel;
setup_incoming_cb on_incoming_call;
setup_outbound_cb on_outgoing_call;
chan_ringing_cb on_chan_ringing;
con_established_cb on_connection_established;
clear_con_cb on_connection_cleared;
answer_call_cb on_answer_call;
Jeremy McNamara
committed
progress_cb on_progress;
rfc2833_cb on_set_rfc2833_payload;
/* global debug flag */
/** Variables required by Asterisk */
static char *type = "H323";
static char *desc = "The NuFone Network's Open H.323 Channel Driver";
static char *tdesc = "The NuFone Network's Open H.323 Channel Driver";
static char default_context[AST_MAX_EXTENSION] = "default";
Jeremy McNamara
committed
static struct sockaddr_in bindaddr;
static int h323_signalling_port = 1720;
static int gatekeeper_disable = 1;
static int gatekeeper_discover = 0;
static int gkroute = 0;
/* Assume we can native bridge by default */
static int bridging = 1;
/* Find user by alias (h.323 id) is default, alternative is the incomming call's source IP address*/
static int capability = AST_FORMAT_ULAW;
static int tos = 0;
static int dtmfmode = H323_DTMF_RFC2833;
static char secret[50];
Jeremy McNamara
committed
static call_options_t global_options;
/** Private structure of a OpenH323 channel */
struct oh323_pvt {
call_options_t options; /* Options to be used during call setup */
int alreadygone; /* Whether or not we've already been destroyed by our peer */
int needdestroy; /* if we need to be destroyed */
call_details_t cd; /* Call details */
struct ast_channel *owner; /* Who owns us */
struct sockaddr_in sa; /* Our peer */
struct sockaddr_in redirip; /* Where our RTP should be going if not to us */
int capability; /* audio capability */
int nonCodecCapability; /* non-audio capability */
int outgoing; /* Outgoing or incoming call? */
int nat; /* Are we talking to a NAT EP?*/
int bridge; /* Determine of we should native bridge or not*/
char exten[AST_MAX_EXTENSION]; /* Requested extension */
char context[AST_MAX_EXTENSION]; /* Context where to start */
Jeremy McNamara
committed
char cid_num[80]; /* Caller*id number, if available */
char cid_name[80]; /* Caller*id name, if available */
char rdnis[80]; /* Referring DNIS, if available */
int amaflags; /* AMA Flags */
int dtmfmode; /* What DTMF Mode is being used */
struct ast_dsp *vad; /* Used for in-band DTMF detection */
struct oh323_pvt *next; /* Next channel in list */
} *iflist = NULL;
static struct ast_user_list {
struct oh323_user *users;
static struct ast_peer_list {
struct oh323_peer *peers;
static struct ast_alias_list {
struct oh323_alias *aliases;
/** Asterisk RTP stuff */
static struct sched_context *sched;
static struct io_context *io;
/** Protect the interface list (oh323_pvt) */
/** Usage counter and associated lock */
Jeremy McNamara
committed
static int usecnt = 0;
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
/* 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(monlock);
/* Protect the H.323 capabilities list, to avoid more than one channel to set the capabilities simultaneaously in the h323 stack. */
Jeremy McNamara
committed
/* Protect the reload process */
AST_MUTEX_DEFINE_STATIC(h323_reload_lock);
static int h323_reloading = 0;
/* 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;
Jeremy McNamara
committed
static int h323_do_reload(void);
static void __oh323_destroy(struct oh323_pvt *p)
{
struct oh323_pvt *cur, *prev = NULL;
if (p->rtp) {
ast_rtp_destroy(p->rtp);
}
/* Unlink us from the owner if we have one */
if (p->owner) {
ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
p->owner->pvt->pvt = NULL;
}
cur = iflist;
while(cur) {
if (cur == p) {
if (prev)
prev->next = cur->next;
else
iflist = cur->next;
break;
}
prev = cur;
cur = cur->next;
}
if (!cur) {
ast_log(LOG_WARNING, "%p is not in list?!?! \n", cur);
Mark Spencer
committed
} else {
ast_mutex_destroy(&p->lock);
Mark Spencer
committed
}
}
static void oh323_destroy(struct oh323_pvt *p)
{
}
static struct oh323_alias *build_alias(char *name, struct ast_variable *v)
{
struct oh323_alias *alias;
alias = (struct oh323_alias *)malloc(sizeof(struct oh323_alias));
if (alias) {
memset(alias, 0, sizeof(struct oh323_alias));
strncpy(alias->name, name, sizeof(alias->name) - 1);
while (v) {
if (!strcasecmp(v->name, "e164")) {
strncpy(alias->e164, v->value, sizeof(alias->e164) - 1);
} else if (!strcasecmp(v->name, "prefix")) {
strncpy(alias->prefix, v->value, sizeof(alias->prefix) - 1);
} else if (!strcasecmp(v->name, "context")) {
strncpy(alias->context, v->value, sizeof(alias->context) - 1);
} else if (!strcasecmp(v->name, "secret")) {
strncpy(alias->secret, v->value, sizeof(alias->secret) - 1);
if (strcasecmp(v->value, "h323")) {
ast_log(LOG_WARNING, "Keyword %s does not make sense in type=h323\n", v->value);
}
v = v->next;
}
}
return alias;
}
static struct oh323_user *build_user(char *name, struct ast_variable *v)
{
struct oh323_user *user;
int format;
user = (struct oh323_user *)malloc(sizeof(struct oh323_user));
if (user) {
memset(user, 0, sizeof(struct oh323_user));
strncpy(user->name, name, sizeof(user->name) - 1);
Jeremy McNamara
committed
user->options.dtmfcodec = 101;
user->bridge = bridging;
strncpy(user->context, default_context, sizeof(user->context) - 1);
while(v) {
if (!strcasecmp(v->name, "context")) {
strncpy(user->context, v->value, sizeof(user->context) - 1);
} else if (!strcasecmp(v->name, "bridge")) {
user->bridge = ast_true(v->value);
} else if (!strcasecmp(v->name, "nat")) {
user->nat = ast_true(v->value);
} else if (!strcasecmp(v->name, "noFastStart")) {
Jeremy McNamara
committed
user->options.noFastStart = ast_true(v->value);
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
Jeremy McNamara
committed
user->options.noH245Tunneling = ast_true(v->value);
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
Jeremy McNamara
committed
user->options.noSilenceSuppression = ast_true(v->value);
} else if (!strcasecmp(v->name, "secret")) {
strncpy(user->secret, v->value, sizeof(user->secret) - 1);
} else if (!strcasecmp(v->name, "callerid")) {
strncpy(user->callerid, v->value, sizeof(user->callerid) - 1);
} else if (!strcasecmp(v->name, "accountcode")) {
strncpy(user->accountcode, v->value, sizeof(user->accountcode) - 1);
Jeremy McNamara
committed
} else if (!strcasecmp(v->name, "progress_setup")) {
int progress_setup = atoi(v->value);
if ((progress_setup != 0) &&
(progress_setup != 1) &&
(progress_setup != 3) &&
(progress_setup != 8)) {
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", progress_setup, v->lineno);
progress_setup = 0;
}
user->options.progress_setup = progress_setup;
} else if (!strcasecmp(v->name, "progress_alert")) {
int progress_alert = atoi(v->value);
if ((progress_alert != 0) &&
(progress_alert != 8)) {
ast_log(LOG_WARNING, "Invalud value %d for progress_alert at line %d, assuming 0\n", progress_alert, v->lineno);
progress_alert = 0;
}
user->options.progress_alert = progress_alert;
} else if (!strcasecmp(v->name, "progress_audio")) {
user->options.progress_audio = ast_true(v->value);
} else if (!strcasecmp(v->name, "dtmfcodec")) {
user->options.dtmfcodec = atoi(v->value);
} else if (!strcasecmp(v->name, "host")) {
if (!strcasecmp(v->value, "dynamic")) {
ast_log(LOG_ERROR, "A dynamic host on a type=user does not make any sense\n");
free(user);
return NULL;
} else if (ast_get_ip(&user->addr, v->value)) {
free(user);
return NULL;
}
/* Let us know we need to use ip authentication */
user->host = 1;
} else if (!strcasecmp(v->name, "amaflags")) {
format = ast_cdr_amaflags2int(v->value);
if (format < 0) {
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
} else {
user->amaflags = format;
}
}
v = v->next;
}
}
return user;
}
static struct oh323_peer *build_peer(char *name, struct ast_variable *v)
{
struct oh323_peer *peer;
struct oh323_peer *prev;
struct ast_ha *oldha = NULL;
Jeremy McNamara
committed
int format;
peer = peerl.peers;
while(peer) {
if (!strcasecmp(peer->name, name)) {
break;
}
prev = peer;
peer = peer->next;
}
if (peer) {
found++;
/* Already in the list, remove it and it will be added back (or FREE'd) */
if (prev) {
prev->next = peer->next;
} else {
peerl.peers = peer->next;
}
peer = (struct oh323_peer*)malloc(sizeof(struct oh323_peer));
memset(peer, 0, sizeof(struct oh323_peer));
}
if (peer) {
if (!found) {
strncpy(peer->name, name, sizeof(peer->name) - 1);
peer->addr.sin_port = htons(h323_signalling_port);
peer->addr.sin_family = AF_INET;
oldha = peer->ha;
peer->ha = NULL;
peer->addr.sin_family = AF_INET;
peer->capability = capability;
Jeremy McNamara
committed
peer->options.dtmfcodec = 101;
peer->dtmfmode = H323_DTMF_RFC2833;
if (!strcasecmp(v->name, "bridge")) {
} else if (!strcasecmp(v->name, "nat")) {
peer->nat = ast_true(v->value);
} else if (!strcasecmp(v->name, "noFastStart")) {
Jeremy McNamara
committed
peer->options.noFastStart = ast_true(v->value);
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
Jeremy McNamara
committed
peer->options.noH245Tunneling = ast_true(v->value);
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
Jeremy McNamara
committed
peer->options.noSilenceSuppression = ast_true(v->value);
} else if (!strcasecmp(v->name, "progress_setup")) {
int progress_setup = atoi(v->value);
if ((progress_setup != 0) &&
(progress_setup != 1) &&
(progress_setup != 3) &&
(progress_setup != 8)) {
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", progress_setup, v->lineno);
progress_setup = 0;
}
peer->options.progress_setup = progress_setup;
} else if (!strcasecmp(v->name, "progress_alert")) {
int progress_alert = atoi(v->value);
if ((progress_alert != 0) &&
(progress_alert != 8)) {
ast_log(LOG_WARNING, "Invalid value %d for progress_alert at line %d, assuming 0\n", progress_alert, v->lineno);
progress_alert = 0;
}
peer->options.progress_alert = progress_alert;
} else if (!strcasecmp(v->name, "progress_audio")) {
peer->options.progress_audio = ast_true(v->value);
} else if (!strcasecmp(v->name, "dtmfcodec")) {
peer->options.dtmfcodec = atoi(v->value);
} else if (!strcasecmp(v->name, "dtmfmode")) {
if (!strcasecmp(v->value, "inband")) {
peer->dtmfmode = H323_DTMF_INBAND;
} else if (!strcasecmp(v->value, "rfc2833")) {
peer->dtmfmode = H323_DTMF_RFC2833;
} else {
ast_log(LOG_WARNING, "Unknown DTMF Mode %s, using RFC2833\n", v->value);
peer->dtmfmode = H323_DTMF_RFC2833;
}
Jeremy McNamara
committed
} else if (!strcasecmp(v->name, "allow")) {
format = ast_getformatbyname(v->value);
if (format < 1) {
ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
} else {
peer->capability |= format;
}
} else if (!strcasecmp(v->name, "disallow")) {
format = ast_getformatbyname(v->value);
if (format < 1) {
ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
} else {
peer->capability |= ~format;
}
} else if (!strcasecmp(v->name, "host")) {
if (!strcasecmp(v->value, "dynamic")) {
ast_log(LOG_ERROR, "Dynamic host configuration not implemented.\n");
free(peer);
return NULL;
}
if (ast_get_ip(&peer->addr, v->value)) {
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Could not determine IP for %s\n", v->value);
} else if (!strcasecmp(v->name, "port")) {
peer->addr.sin_port = htons(atoi(v->value));
}
v=v->next;
}
}
return peer;
}
/**
* Send (play) the specified digit to the channel.
*
*/
static int oh323_digit(struct ast_channel *c, char digit)
{
struct oh323_pvt *p = (struct oh323_pvt *) c->pvt->pvt;
if (p && p->rtp && (p->dtmfmode & H323_DTMF_RFC2833)) {
ast_rtp_senddigit(p->rtp, digit);
}
/* If in-band DTMF is desired, send that */
return 0;
}
/**
* Make a call over the specified channel to the specified
* destination.
* Returns -1 on error, 0 on success.
*/
static int oh323_call(struct ast_channel *c, char *dest, int timeout)
{
int res = 0;
struct oh323_pvt *pvt = (struct oh323_pvt *)c->pvt->pvt;
char addr[INET_ADDRSTRLEN];
char called_addr[1024];
if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "Line is already in use (%s)\n", c->name);
return -1;
}
/* Clear and then set the address to call */
memset(addr, 0, sizeof(addr));
if (usingGk) {
Jeremy McNamara
committed
memcpy(addr, dest, strlen(addr));
pvt->options.port = h323_signalling_port;
} else {
ast_inet_ntoa(addr, sizeof(addr), pvt->sa.sin_addr);
pvt->options.port = htons(pvt->sa.sin_port);
Jeremy McNamara
committed
}
if (c->cid.cid_num) {
strncpy(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num));
}
if (c->cid.cid_name) {
strncpy(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name));
}
Jeremy McNamara
committed
/* indicate that this is an outgoing call */
pvt->outgoing = 1;
Jeremy McNamara
committed
if (pvt->exten) {
snprintf(called_addr, sizeof(called_addr), "%s@%s:%d", pvt->exten, addr, pvt->options.port);
Jeremy McNamara
committed
} else {
snprintf(called_addr, sizeof(called_addr), "%s:%d",addr, pvt->options.port);
Jeremy McNamara
committed
}
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Placing outgoing call to %s, %d\n", called_addr, pvt->options.dtmfcodec);
res = h323_make_call(called_addr, &(pvt->cd), &pvt->options);
ast_log(LOG_NOTICE, "h323_make_call failed(%s)\n", c->name);
return -1;
}
return 0;
}
static int oh323_answer(struct ast_channel *c)
{
int res;
struct oh323_pvt *pvt = (struct oh323_pvt *) c->pvt->pvt;
res = h323_answering_call(pvt->cd.call_token, 0);
return res;
}
static int oh323_hangup(struct ast_channel *c)
{
struct oh323_pvt *pvt = (struct oh323_pvt *) c->pvt->pvt;
int q931cause = AST_CAUSE_NORMAL_CLEARING;
if (!c->pvt->pvt) {
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
return 0;
}
ast_mutex_lock(&pvt->lock);
if (pvt->owner != c) {
ast_log(LOG_WARNING, "Huh? We aren't the owner?\n");
ast_mutex_unlock(&pvt->lock);
/* Free dsp used for in-band DTMF detection */
if (pvt->vad) {
ast_dsp_free(pvt->vad);
Jeremy McNamara
committed
}
pvt->owner = NULL;
if (c->hangupcause) {
q931cause = c->hangupcause;
} else {
char *cause = pbx_builtin_getvar_helper(c, "DIALSTATUS");
if (cause) {
if (!strcmp(cause, "CONGESTION")) {
q931cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
} else if (!strcmp(cause, "BUSY")) {
q931cause = AST_CAUSE_USER_BUSY;
} else if (!strcmp(cause, "CHANISUNVAIL")) {
q931cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
} else if (!strcmp(cause, "NOANSWER")) {
q931cause = AST_CAUSE_NO_ANSWER;
} else if (!strcmp(cause, "CANCEL")) {
q931cause = AST_CAUSE_CALL_REJECTED;
}
}
}
/* Start the process if it's not already started */
if (!pvt->alreadygone) {
if (h323_clear_call((pvt->cd).call_token, q931cause)) {
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "ClearCall failed.\n");
}
pvt->needdestroy = 1;
Jeremy McNamara
committed
if (usecnt < 0) {
ast_log(LOG_WARNING, "Usecnt < 0\n");
Jeremy McNamara
committed
}
ast_mutex_unlock(&pvt->lock);
static struct ast_frame *oh323_rtp_read(struct oh323_pvt *pvt)
/* Retrieve audio/etc from channel. Assumes pvt->lock is already held. */
struct ast_frame *f;
static struct ast_frame null_frame = { AST_FRAME_NULL, };
/* Only apply it for the first packet, we just need the correct ip/port */
if (pvt->nat) {
ast_rtp_setnat(pvt->rtp,pvt->nat);
pvt->nat = 0;
}
f = ast_rtp_read(pvt->rtp);
/* Don't send RFC2833 if we're not supposed to */
if (f && (f->frametype == AST_FRAME_DTMF) && !(pvt->dtmfmode & H323_DTMF_RFC2833)) {
if (pvt->owner) {
/* We already hold the channel lock */
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != pvt->owner->nativeformats) {
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
pvt->owner->nativeformats = f->subclass;
ast_set_read_format(pvt->owner, pvt->owner->readformat);
ast_set_write_format(pvt->owner, pvt->owner->writeformat);
if (pvt->dtmfmode & H323_DTMF_INBAND) {
f = ast_dsp_process(pvt->owner,pvt->vad,f);
if (f->frametype == AST_FRAME_DTMF) {
ast_log(LOG_DEBUG, "Received in-band digit %c.\n", f->subclass);
}
}
return f;
}
static struct ast_frame *oh323_read(struct ast_channel *c)
{
struct ast_frame *fr;
struct oh323_pvt *pvt = (struct oh323_pvt *)c->pvt->pvt;
ast_mutex_lock(&pvt->lock);
fr = oh323_rtp_read(pvt);
ast_mutex_unlock(&pvt->lock);
return fr;
}
static int oh323_write(struct ast_channel *c, struct ast_frame *frame)
{
struct oh323_pvt *pvt = (struct oh323_pvt *) c->pvt->pvt;
int res = 0;
if (frame->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Can't send %d type frames with H323 write\n", frame->frametype);
return 0;
}
} else {
if (!(frame->subclass & c->nativeformats)) {
Jeremy McNamara
committed
ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
frame->subclass, c->nativeformats, c->readformat, c->writeformat);
return -1;
if (pvt) {
ast_mutex_lock(&pvt->lock);
if (pvt->rtp) {
res = ast_rtp_write(pvt->rtp, frame);
ast_mutex_unlock(&pvt->lock);
}
return res;
}
static int oh323_indicate(struct ast_channel *c, int condition)
{
struct oh323_pvt *pvt = (struct oh323_pvt *) c->pvt->pvt;
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "OH323: Indicating %d on %s\n", condition, pvt->cd.call_token);
switch(condition) {
case AST_CONTROL_RINGING:
Jeremy McNamara
committed
if (c->_state == AST_STATE_RING || c->_state == AST_STATE_RINGING) {
h323_send_alerting(pvt->cd.call_token);
Jeremy McNamara
committed
break;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
return -1;
case AST_CONTROL_PROGRESS:
if (c->_state != AST_STATE_UP) {
h323_send_progress(pvt->cd.call_token);
Jeremy McNamara
committed
break;
Jeremy McNamara
committed
return -1;
case AST_CONTROL_BUSY:
if (c->_state != AST_STATE_UP) {
h323_answering_call(pvt->cd.call_token, 1);
pvt->alreadygone = 1;
Jeremy McNamara
committed
ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV);
Jeremy McNamara
committed
return -1;
case AST_CONTROL_CONGESTION:
if (c->_state != AST_STATE_UP) {
h323_answering_call(pvt->cd.call_token, 1);
pvt->alreadygone = 1;
Jeremy McNamara
committed
ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV);
Jeremy McNamara
committed
return -1;
Jeremy McNamara
committed
return -1;
default:
ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition);
return -1;
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
return -1;
static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
struct oh323_pvt *pvt = (struct oh323_pvt *) newchan->pvt->pvt;
ast_mutex_lock(&pvt->lock);
if (pvt->owner != oldchan) {
ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, pvt->owner);
pvt->owner = newchan;
ast_mutex_unlock(&pvt->lock);
static struct ast_channel *oh323_new(struct oh323_pvt *pvt, int state, const char *host)
Jeremy McNamara
committed
/* Don't hold a oh323_pvt lock while we allocate a chanel */
ast_mutex_unlock(&pvt->lock);
ch = ast_channel_alloc(1);
Jeremy McNamara
committed
/* Update usage counter */
ast_mutex_lock(&usecnt_lock);
usecnt++;
ast_mutex_unlock(&usecnt_lock);
ast_update_use_count();
Jeremy McNamara
committed
ast_mutex_lock(&pvt->lock);
snprintf(ch->name, sizeof(ch->name), "H323/%s", host);
ch->nativeformats = pvt->capability;
Jeremy McNamara
committed
if (!ch->nativeformats) {
Jeremy McNamara
committed
}
fmt = ast_best_codec(ch->nativeformats);
ch->type = type;
ch->fds[0] = ast_rtp_fd(pvt->rtp);
Jeremy McNamara
committed
if (state == AST_STATE_RING) {
Jeremy McNamara
committed
}
ch->writeformat = fmt;
ch->pvt->rawwriteformat = fmt;
ch->readformat = fmt;
ch->pvt->rawreadformat = fmt;
if (pvt->dtmfmode & H323_DTMF_INBAND) {
pvt->vad = ast_dsp_new();
ast_dsp_set_features(pvt->vad, DSP_FEATURE_DTMF_DETECT);
Jeremy McNamara
committed
/* Register channel functions. */
ch->pvt->pvt = pvt;
ch->pvt->send_digit = oh323_digit;
ch->pvt->call = oh323_call;
ch->pvt->hangup = oh323_hangup;
ch->pvt->answer = oh323_answer;
ch->pvt->read = oh323_read;
ch->pvt->write = oh323_write;
ch->pvt->indicate = oh323_indicate;
ch->pvt->fixup = oh323_fixup;
/* disable, for now */
#if 0
ch->pvt->bridge = ast_rtp_bridge;
#endif
pvt->owner = ch;
strncpy(ch->context, pvt->context, sizeof(ch->context) - 1);
strncpy(ch->exten, pvt->exten, sizeof(ch->exten) - 1);
Jeremy McNamara
committed
if (!ast_strlen_zero(pvt->accountcode)) {
strncpy(ch->accountcode, pvt->accountcode, sizeof(ch->accountcode) - 1);
Jeremy McNamara
committed
}
if (pvt->amaflags) {
ch->amaflags = pvt->amaflags;
Jeremy McNamara
committed
}
if (!ast_strlen_zero(pvt->cid_num)) {
ch->cid.cid_num = strdup(pvt->cid_num);
} else if (pvt->cd.call_source_e164 && !ast_strlen_zero(pvt->cd.call_source_e164)) {
ch->cid.cid_num = strdup(pvt->cd.call_source_e164);
Jeremy McNamara
committed
}
if (!ast_strlen_zero(pvt->cid_name)) {
ch->cid.cid_name = strdup(pvt->cid_name);
} else if (pvt->cd.call_source_e164 && !ast_strlen_zero(pvt->cd.call_source_name)) {
ch->cid.cid_name = strdup(pvt->cd.call_source_name);
Jeremy McNamara
committed
}
if (!ast_strlen_zero(pvt->rdnis)) {
ch->cid.cid_rdnis = strdup(pvt->rdnis);
}
if (!ast_strlen_zero(pvt->exten) && strcmp(pvt->exten, "s")) {
ch->cid.cid_dnid = strdup(pvt->exten);
}
ast_setstate(ch, state);
if (ast_pbx_start(ch)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ch->name);
ast_hangup(ch);
ch = NULL;
Jeremy McNamara
committed
} else {
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
Jeremy McNamara
committed
}
}
static struct oh323_pvt *oh323_alloc(int callid)
{
struct oh323_pvt *pvt;
pvt = (struct oh323_pvt *) malloc(sizeof(struct oh323_pvt));
if (!pvt) {
ast_log(LOG_ERROR, "Couldn't allocate private structure. This is bad\n");
return NULL;
}
memset(pvt, 0, sizeof(struct oh323_pvt));
pvt->rtp = ast_rtp_new(sched, io, 1, 0);
if (!pvt->rtp) {
ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno));
ast_rtp_settos(pvt->rtp, tos);
ast_mutex_init(&pvt->lock);
/* Ensure the call token is allocated */
if ((pvt->cd).call_token == NULL) {
(pvt->cd).call_token = (char *)malloc(128);
memset((char *)(pvt->cd).call_token, 0, 128);
if (!pvt->cd.call_token) {
ast_log(LOG_ERROR, "Not enough memory to alocate call token\n");
return NULL;
}
pvt->cd.call_reference = callid;
pvt->bridge = bridging;
pvt->dtmfmode = dtmfmode;
if (pvt->dtmfmode & H323_DTMF_RFC2833) {
pvt->nonCodecCapability |= AST_RTP_DTMF;
strncpy(pvt->context, default_context, sizeof(pvt->context) - 1);
pvt->next = iflist;
iflist = pvt;
return pvt;
static struct oh323_pvt *find_call(int call_reference, const char *token)
struct oh323_pvt *pvt;
pvt = iflist;
while(pvt) {
if ((signed int)pvt->cd.call_reference == call_reference) {
if ((token != NULL) && (!strcmp(pvt->cd.call_token, token))) {
ast_mutex_unlock(&iflock);
return pvt;
Jeremy McNamara
committed
} else if (token == NULL) {
ast_log(LOG_DEBUG, "Call Token is NULL\n");
ast_mutex_unlock(&iflock);
return pvt;
pvt = pvt->next;
}
ast_mutex_unlock(&iflock);
struct oh323_user *find_user(const call_details_t cd)
{
struct oh323_user *u;
char iabuf[INET_ADDRSTRLEN];
u = userl.users;
Jeremy McNamara
committed
if (userbyalias) {
while(u) {
if (!strcasecmp(u->name, cd.call_source_aliases)) {
break;
}
u = u->next;
}
} else {
while(u) {
if (!strcasecmp(cd.sourceIp, ast_inet_ntoa(iabuf, sizeof(iabuf), u->addr.sin_addr))) {
break;
}
u = u->next;
}
}
return u;
}
struct oh323_peer *find_peer(const char *peer, struct sockaddr_in *sin)
{
struct oh323_peer *p = NULL;
Jeremy McNamara
committed
static char iabuf[INET_ADDRSTRLEN];
p = peerl.peers;
if (peer) {
while(p) {
if (!strcasecmp(p->name, peer)) {
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Found peer %s by name\n", peer);
break;
}
p = p->next;
}
} else {
/* find by sin */
Jeremy McNamara
committed
if (sin) {
while (p) {
if ((!inaddrcmp(&p->addr, sin)) ||
(p->addr.sin_addr.s_addr == sin->sin_addr.s_addr)) {
ast_log(LOG_DEBUG, "Found peer %s/%s by addr\n", peer, ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr));
break;
}
p = p->next;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
if (!p) {
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Could not find peer %s by name or address\n", peer);
Jeremy McNamara
committed
}
return p;
}
static int create_addr(struct oh323_pvt *pvt, char *opeer)
struct hostent *hp;
struct ast_hostent ahp;
struct oh323_peer *p;
int found = 0;
char *port;
char *hostn;
char peer[256] = "";
strncpy(peer, opeer, sizeof(peer) - 1);
port = strchr(peer, ':');
if (port) {
*port = '\0';
port++;
}
pvt->sa.sin_family = AF_INET;
ast_mutex_lock(&peerl.lock);
p = find_peer(peer, NULL);
if (p) {
found++;
pvt->capability = p->capability;
pvt->nat = p->nat;
if (pvt->rtp) {
ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", pvt->nat);
ast_rtp_setnat(pvt->rtp, pvt->nat);
Jeremy McNamara
committed
memcpy(&pvt->options, &p->options, sizeof(pvt->options));
if (p->dtmfmode) {
pvt->dtmfmode = p->dtmfmode;
if (pvt->dtmfmode & H323_DTMF_RFC2833) {
pvt->nonCodecCapability |= AST_RTP_DTMF;
pvt->nonCodecCapability &= ~AST_RTP_DTMF;
if (p->addr.sin_addr.s_addr) {
pvt->sa.sin_addr = p->addr.sin_addr;
pvt->sa.sin_port = p->addr.sin_port;