Newer
Older
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005
*
* 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
* 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 This file is part of the chan_h323 driver for Asterisk
* \par See also
* \arg Config_h323
*
* \ingroup channel_drivers
#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
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Kevin P. Fleming
committed
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/utils.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"
#include "asterisk/causes.h"
#include "asterisk/stringfields.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;
Jeremy McNamara
committed
hangup_cb on_hangup;
Jeremy McNamara
committed
setcapabilities_cb on_setcapabilities;
/* global debug flag */
/** Variables required by Asterisk */
static const char desc[] = "The NuFone Network's Open H.323 Channel Driver";
static const char tdesc[] = "The NuFone Network's Open H.323 Channel Driver";
static const char config[] = "h323.conf";
static char default_context[AST_MAX_CONTEXT] = "default";
Jeremy McNamara
committed
static struct sockaddr_in bindaddr;
#define GLOBAL_CAPABILITY (AST_FORMAT_G723_1 | AST_FORMAT_GSM | AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_G729A | AST_FORMAT_H261)
static int h323_signalling_port = 1720;
static int gatekeeper_disable = 1;
static int gatekeeper_discover = 0;
static int gkroute = 0;
/* Find user by alias (h.323 id) is default, alternative is the incomming call's source IP address*/
static int tos = 0;
static char secret[50];
static unsigned int unique = 0;
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 nonCodecCapability; /* non-audio capability */
int outgoing; /* Outgoing or incoming call? */
char exten[AST_MAX_EXTENSION]; /* Requested extension */
char context[AST_MAX_CONTEXT]; /* 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 */
struct ast_rtp *rtp; /* RTP Session */
struct ast_dsp *vad; /* Used for in-band DTMF detection */
Jeremy McNamara
committed
int nativeformats; /* Codec formats supported by a channel */
int needhangup; /* Send hangup when Asterisk is ready */
int hangupcause; /* Hangup cause from OpenH323 layer */
int newstate; /* Pending state change */
int newcontrol; /* Pending control to send */
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 struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause);
static int oh323_digit(struct ast_channel *c, char digit);
static int oh323_call(struct ast_channel *c, char *dest, int timeout);
static int oh323_hangup(struct ast_channel *c);
static int oh323_answer(struct ast_channel *c);
static struct ast_frame *oh323_read(struct ast_channel *c);
static int oh323_write(struct ast_channel *c, struct ast_frame *frame);
static int oh323_indicate(struct ast_channel *c, int condition);
static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static const struct ast_channel_tech oh323_tech = {
.description = tdesc,
.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
Mark Spencer
committed
.properties = AST_CHAN_TP_WANTSJITTER,
.requester = oh323_request,
.send_digit = oh323_digit,
.call = oh323_call,
.hangup = oh323_hangup,
.answer = oh323_answer,
.read = oh323_read,
.write = oh323_write,
.indicate = oh323_indicate,
.fixup = oh323_fixup,
/* disable, for now */
#if 0
.bridge = ast_rtp_bridge,
#endif
};
/* Channel and private structures should be already locked */
static void __oh323_update_info(struct ast_channel *c, struct oh323_pvt *pvt)
Jeremy McNamara
committed
{
if (c->nativeformats != pvt->nativeformats) {
if (h323debug)
ast_log(LOG_DEBUG, "Preparing %s for new native format\n", c->name);
c->nativeformats = pvt->nativeformats;
ast_set_read_format(c, c->readformat);
ast_set_write_format(c, c->writeformat);
}
if (pvt->needhangup) {
if (h323debug)
ast_log(LOG_DEBUG, "Process pending hangup for %s\n", c->name);
c->_softhangup |= AST_SOFTHANGUP_DEV;
c->hangupcause = pvt->hangupcause;
ast_queue_hangup(c);
pvt->needhangup = 0;
pvt->newstate = pvt->newcontrol = -1;
}
if (pvt->newstate >= 0) {
ast_setstate(c, pvt->newstate);
pvt->newstate = -1;
}
if (pvt->newcontrol >= 0) {
ast_queue_control(c, pvt->newcontrol);
pvt->newcontrol = -1;
Jeremy McNamara
committed
}
}
/* Only channel structure should be locked */
static void oh323_update_info(struct ast_channel *c)
{
struct oh323_pvt *pvt = c->tech_pvt;
if (pvt) {
ast_mutex_lock(&pvt->lock);
__oh323_update_info(c, pvt);
ast_mutex_unlock(&pvt->lock);
}
Jeremy McNamara
committed
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
}
static void cleanup_call_details(call_details_t *cd)
{
if (cd->call_token) {
free(cd->call_token);
cd->call_token = NULL;
}
if (cd->call_source_aliases) {
free(cd->call_source_aliases);
cd->call_source_aliases = NULL;
}
if (cd->call_dest_alias) {
free(cd->call_dest_alias);
cd->call_dest_alias = NULL;
}
if (cd->call_source_name) {
free(cd->call_source_name);
cd->call_source_name = NULL;
}
if (cd->call_source_e164) {
free(cd->call_source_e164);
cd->call_source_e164 = NULL;
}
if (cd->call_dest_e164) {
free(cd->call_dest_e164);
cd->call_dest_e164 = NULL;
}
if (cd->sourceIp) {
free(cd->sourceIp);
cd->sourceIp = NULL;
}
}
static void __oh323_destroy(struct oh323_pvt *pvt)
{
struct oh323_pvt *cur, *prev = NULL;
if (pvt->rtp) {
ast_rtp_destroy(pvt->rtp);
Jeremy McNamara
committed
/* Free dsp used for in-band DTMF detection */
if (pvt->vad) {
ast_dsp_free(pvt->vad);
Jeremy McNamara
committed
}
cleanup_call_details(&pvt->cd);
Jeremy McNamara
committed
/* Unlink us from the owner if we have one */
if (pvt->owner) {
ast_mutex_lock(&pvt->owner->lock);
ast_log(LOG_DEBUG, "Detaching from %s\n", pvt->owner->name);
pvt->owner->tech_pvt = NULL;
ast_mutex_unlock(&pvt->owner->lock);
if (cur == pvt) {
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(&pvt->lock);
free(pvt);
}
static void oh323_destroy(struct oh323_pvt *pvt)
__oh323_destroy(pvt);
}
/**
* Send (play) the specified digit to the channel.
*
*/
static int oh323_digit(struct ast_channel *c, char digit)
{
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
char *token;
ast_log(LOG_ERROR, "No private structure?! This is bad\n");
Jeremy McNamara
committed
return -1;
ast_mutex_lock(&pvt->lock);
if (pvt->rtp && (pvt->options.dtmfmode & H323_DTMF_RFC2833)) {
/* out-of-band DTMF */
if (h323debug) {
ast_log(LOG_DEBUG, "Sending out-of-band digit %c on %s\n", digit, c->name);
}
ast_rtp_senddigit(pvt->rtp, digit);
} else {
/* in-band DTMF */
if (h323debug) {
ast_log(LOG_DEBUG, "Sending inband digit %c on %s\n", digit, c->name);
}
token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
h323_send_tone(token, digit);
if (token) {
free(token);
}
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->lock);
oh323_update_info(c);
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->tech_pvt;
char addr[INET_ADDRSTRLEN];
char called_addr[1024];
if (h323debug) {
ast_log(LOG_DEBUG, "Calling to %s on %s\n", dest, c->name);
}
Jeremy McNamara
committed
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;
}
ast_mutex_lock(&pvt->lock);
if (usingGk) {
if (ast_strlen_zero(pvt->exten)) {
strncpy(called_addr, dest, sizeof(called_addr));
} else {
snprintf(called_addr, sizeof(called_addr), "%s@%s", pvt->exten, dest);
}
} else {
ast_inet_ntoa(addr, sizeof(addr), pvt->sa.sin_addr);
res = htons(pvt->sa.sin_port);
if (ast_strlen_zero(pvt->exten)) {
snprintf(called_addr, sizeof(called_addr), "%s:%d", addr, res);
} else {
snprintf(called_addr, sizeof(called_addr), "%s@%s:%d", pvt->exten, addr, res);
Jeremy McNamara
committed
}
Jeremy McNamara
committed
}
Jeremy McNamara
committed
/* make sure null terminated */
called_addr[sizeof(called_addr) - 1] = '\0';
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
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Placing outgoing call to %s, %d\n", called_addr, pvt->options.dtmfcodec);
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
res = h323_make_call(called_addr, &(pvt->cd), &pvt->options);
ast_log(LOG_NOTICE, "h323_make_call failed(%s)\n", c->name);
Jeremy McNamara
committed
oh323_update_info(c);
return 0;
}
static int oh323_answer(struct ast_channel *c)
{
int res;
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
Jeremy McNamara
committed
char *token;
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "Answering on %s\n", c->name);
ast_mutex_lock(&pvt->lock);
token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
ast_mutex_unlock(&pvt->lock);
res = h323_answering_call(token, 0);
if (token)
free(token);
oh323_update_info(c);
return res;
}
static int oh323_hangup(struct ast_channel *c)
{
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
int q931cause = AST_CAUSE_NORMAL_CLEARING;
Jeremy McNamara
committed
char *call_token;
if (h323debug)
ast_log(LOG_DEBUG, "Hanging up call %s\n", c->name);
if (!c->tech_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);
pvt->owner = NULL;
c->tech_pvt = NULL;
if (c->hangupcause) {
q931cause = c->hangupcause;
} else {
const 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 */
Jeremy McNamara
committed
if (!pvt->alreadygone && !pvt->hangupcause) {
call_token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
if (call_token) {
/* Release lock to eliminate deadlock */
ast_mutex_unlock(&pvt->lock);
if (h323_clear_call(call_token, q931cause)) {
ast_log(LOG_DEBUG, "ClearCall failed.\n");
}
free(call_token);
ast_mutex_lock(&pvt->lock);
Jeremy McNamara
committed
}
Jeremy McNamara
committed
pvt->needdestroy = 1;
Jeremy McNamara
committed
if (usecnt < 0) {
ast_log(LOG_WARNING, "Usecnt < 0\n");
Jeremy McNamara
committed
}
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
ast_update_use_count();
static struct ast_frame *oh323_rtp_read(struct oh323_pvt *pvt)
/* Retrieve audio/etc from channel. Assumes pvt->lock is already held. */
/* Only apply it for the first packet, we just need the correct ip/port */
if (pvt->options.nat) {
ast_rtp_setnat(pvt->rtp, pvt->options.nat);
pvt->options.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->options.dtmfmode & H323_DTMF_RFC2833)) {
return &ast_null_frame;
if (pvt->owner) {
/* We already hold the channel lock */
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != pvt->owner->nativeformats) {
Jeremy McNamara
committed
/* Try to avoid deadlock */
if (ast_mutex_trylock(&pvt->owner->lock)) {
ast_log(LOG_NOTICE, "Format changed but channel is locked. Ignoring frame...\n");
return &ast_null_frame;
Jeremy McNamara
committed
}
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
pvt->owner->nativeformats = f->subclass;
Jeremy McNamara
committed
pvt->nativeformats = f->subclass;
ast_set_read_format(pvt->owner, pvt->owner->readformat);
ast_set_write_format(pvt->owner, pvt->owner->writeformat);
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->owner->lock);
if ((pvt->options.dtmfmode & H323_DTMF_INBAND) && pvt->vad) {
if (!ast_mutex_trylock(&pvt->owner->lock)) {
f = ast_dsp_process(pvt->owner,pvt->vad,f);
ast_mutex_unlock(&pvt->owner->lock);
}
else
ast_log(LOG_NOTICE, "Unable to process inband DTMF while channel is locked\n");
if (f &&(f->frametype == AST_FRAME_DTMF)) {
ast_log(LOG_DEBUG, "Received in-band digit %c.\n", f->subclass);
static struct ast_frame *oh323_read(struct ast_channel *c)
struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt;
ast_mutex_lock(&pvt->lock);
__oh323_update_info(c, pvt);
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->tech_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);
Jeremy McNamara
committed
return 0;
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->tech_pvt;
Jeremy McNamara
committed
char *token = (char *)NULL;
Jeremy McNamara
committed
Jeremy McNamara
committed
ast_mutex_lock(&pvt->lock);
token = (pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL);
ast_mutex_unlock(&pvt->lock);
if (h323debug)
ast_log(LOG_DEBUG, "OH323: Indicating %d on %s\n", condition, token);
Jeremy McNamara
committed
switch(condition) {
case AST_CONTROL_RINGING:
Jeremy McNamara
committed
if (c->_state == AST_STATE_RING || c->_state == AST_STATE_RINGING) {
Jeremy McNamara
committed
h323_send_alerting(token);
Jeremy McNamara
committed
break;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
if (token)
free(token);
Jeremy McNamara
committed
return -1;
case AST_CONTROL_PROGRESS:
if (c->_state != AST_STATE_UP) {
Jeremy McNamara
committed
h323_send_progress(token);
Jeremy McNamara
committed
break;
Jeremy McNamara
committed
if (token)
free(token);
Jeremy McNamara
committed
return -1;
case AST_CONTROL_BUSY:
if (c->_state != AST_STATE_UP) {
Jeremy McNamara
committed
h323_answering_call(token, 1);
ast_mutex_lock(&pvt->lock);
pvt->alreadygone = 1;
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV);
Jeremy McNamara
committed
if (token)
free(token);
Jeremy McNamara
committed
return -1;
case AST_CONTROL_CONGESTION:
if (c->_state != AST_STATE_UP) {
Jeremy McNamara
committed
h323_answering_call(token, 1);
ast_mutex_lock(&pvt->lock);
pvt->alreadygone = 1;
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV);
Jeremy McNamara
committed
if (token)
free(token);
Jeremy McNamara
committed
return -1;
Jeremy McNamara
committed
if (token)
free(token);
Jeremy McNamara
committed
return -1;
Jeremy McNamara
committed
ast_log(LOG_WARNING, "Don't know how to indicate condition %d on %s\n", condition, token);
if (token)
free(token);
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "OH323: Indicated %d on %s\n", condition, token);
if (token)
free(token);
oh323_update_info(c);
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->tech_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);
/* Private structure should be locked on a call */
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);
Josh Roberson
committed
ch->tech = &oh323_tech;
ast_string_field_build(ch, name, "H323/%s", host);
ch->nativeformats = pvt->options.capability;
Jeremy McNamara
committed
if (!ch->nativeformats) {
ch->nativeformats = global_options.capability;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
pvt->nativeformats = ch->nativeformats;
fmt = ast_best_codec(ch->nativeformats);
ch->fds[0] = ast_rtp_fd(pvt->rtp);
Jeremy McNamara
committed
if (state == AST_STATE_RING) {
Jeremy McNamara
committed
}
ch->rawwriteformat = fmt;
ch->rawreadformat = fmt;
if (pvt->options.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->tech_pvt = pvt;
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)) {
ast_string_field_set(ch, accountcode, pvt->accountcode);
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 (!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 (!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));
Jeremy McNamara
committed
pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0,bindaddr.sin_addr);
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);
if (!pvt->cd.call_token) {
ast_log(LOG_ERROR, "Not enough memory to alocate call token\n");
return NULL;
}
memset((char *)(pvt->cd).call_token, 0, 128);
pvt->cd.call_reference = callid;
memcpy(&pvt->options, &global_options, sizeof(pvt->options));
if (pvt->options.dtmfmode & H323_DTMF_RFC2833) {
pvt->nonCodecCapability |= AST_RTP_DTMF;
} else {
pvt->nonCodecCapability &= ~AST_RTP_DTMF;
strncpy(pvt->context, default_context, sizeof(pvt->context) - 1);
pvt->newstate = pvt->newcontrol = -1;
pvt->next = iflist;
iflist = pvt;
return pvt;
static struct oh323_pvt *find_call_locked(int call_reference, const char *token)
struct oh323_pvt *pvt;
pvt = iflist;
while(pvt) {
if (!pvt->needdestroy && ((signed int)pvt->cd.call_reference == call_reference)) {
/* Found the call */
if ((token != NULL) && (!strcmp(pvt->cd.call_token, token))) {
ast_mutex_lock(&pvt->lock);
ast_mutex_unlock(&iflock);
return pvt;
} else if (token == NULL) {
ast_log(LOG_WARNING, "Call Token is NULL\n");
ast_mutex_lock(&pvt->lock);
ast_mutex_unlock(&iflock);
return pvt;
}
}
pvt = pvt->next;
}
ast_mutex_unlock(&iflock);
static int update_state(struct oh323_pvt *pvt, int state, int signal)
{
if (!pvt)
return 0;
if (pvt->owner && !ast_mutex_trylock(&pvt->owner->lock)) {
if (state >= 0)
ast_setstate(pvt->owner, state);
if (signal >= 0)
ast_queue_control(pvt->owner, signal);
return 1;
}
else {
if (state >= 0)
pvt->newstate = state;
if (signal >= 0)
pvt->newcontrol = signal;
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) {
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++;
memcpy(&pvt->options, &p->options, sizeof(pvt->options));
if (pvt->rtp) {
ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", pvt->options.nat);
ast_rtp_setnat(pvt->rtp, pvt->options.nat);
if (pvt->options.dtmfmode) {
if (pvt->options.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;
Jeremy McNamara
committed
}
}
ast_mutex_unlock(&peerl.lock);
if (!p && !found) {
hostn = peer;
if (port) {
portno = atoi(port);
} else {
portno = h323_signalling_port;
}
hp = ast_gethostbyname(hostn, &ahp);
if (hp) {
Jeremy McNamara
committed
memcpy(&pvt->options, &global_options, sizeof(pvt->options));
memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr));