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));
pvt->sa.sin_port = htons(portno);
return 0;
} else {
ast_log(LOG_WARNING, "No such host: %s\n", peer);
return -1;
}
} else if (!p) {
return -1;
} else {
return 0;
}
Jeremy McNamara
committed
static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause)
struct oh323_pvt *pvt;
char *dest = (char *)data;
Jeremy McNamara
committed
char *h323id = NULL;
char tmp[256], tmp1[256];
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "type=%s, format=%d, data=%s.\n", type, format, (char *)data);
pvt = oh323_alloc(0);
if (!pvt) {
ast_log(LOG_WARNING, "Unable to build pvt data for '%s'\n", (char *)data);
return NULL;
}
format &= ((AST_FORMAT_MAX_AUDIO << 1) - 1);
if (!format) {
ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
return NULL;
}
strncpy(tmp, dest, sizeof(tmp) - 1);
host = strchr(tmp, '@');
if (host) {
*host = '\0';
host++;
ext = tmp;
} else {
host = tmp;
ext = NULL;
}
strtok_r(host, "/", &(h323id));
if (!ast_strlen_zero(h323id)) {
Jeremy McNamara
committed
h323_set_id(h323id);
}
Jeremy McNamara
committed
strncpy(pvt->exten, ext, sizeof(pvt->exten) - 1);
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Extension: %s Host: %s\n", pvt->exten, host);
if (!usingGk) {
if (create_addr(pvt, host)) {
oh323_destroy(pvt);
return NULL;
}
else {
memcpy(&pvt->options, &global_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;
} else {
pvt->nonCodecCapability &= ~AST_RTP_DTMF;
}
}
}
Jeremy McNamara
committed
Jeremy McNamara
committed
ast_mutex_lock(&caplock);
/* Generate unique channel identifier */
snprintf(tmp1, sizeof(tmp1)-1, "%s-%u", host, ++unique);
tmp1[sizeof(tmp1)-1] = '\0';
Jeremy McNamara
committed
ast_mutex_unlock(&caplock);
ast_mutex_lock(&pvt->lock);
tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1);
ast_mutex_unlock(&pvt->lock);
oh323_destroy(pvt);
}
ast_update_use_count();
restart_monitor();
return tmpc;
}
Jeremy McNamara
committed
/** Find a call by alias */
Jeremy McNamara
committed
struct oh323_alias *find_alias(const char *source_aliases)
Jeremy McNamara
committed
struct oh323_alias *a;
Jeremy McNamara
committed
if (!strcasecmp(a->name, source_aliases)) {
Jeremy McNamara
committed
return a;
}
/**
* Callback for sending digits from H.323 up to asterisk
*
*/
int send_digit(unsigned call_reference, char digit, const char *token)
struct oh323_pvt *pvt;
int res;
ast_log(LOG_DEBUG, "Received Digit: %c\n", digit);
pvt = find_call_locked(call_reference, token);
if (!pvt) {
ast_log(LOG_ERROR, "Private structure not found in send_digit.\n");
return -1;
}
memset(&f, 0, sizeof(f));
f.frametype = AST_FRAME_DTMF;
f.subclass = digit;
f.datalen = 0;
f.samples = 800;
f.offset = 0;
f.data = NULL;
f.mallocd = 0;
f.src = "SEND_DIGIT";
res = ast_queue_frame(pvt->owner, &f);
ast_mutex_unlock(&pvt->lock);
return res;
Jeremy McNamara
committed
* Callback function used to inform the H.323 stack of the local rtp ip/port details
Jeremy McNamara
committed
struct rtp_info *external_rtp_create(unsigned call_reference, const char * token)
struct oh323_pvt *pvt;
Jeremy McNamara
committed
info = (struct rtp_info *)malloc(sizeof(struct rtp_info));
if (!info) {
ast_log(LOG_ERROR, "Unable to allocated info structure, this is very bad\n");
return NULL;
}
pvt = find_call_locked(call_reference, token);
if (!pvt) {
Jeremy McNamara
committed
free(info);
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Unable to find call %s(%d)\n", token, call_reference);
Jeremy McNamara
committed
/* figure out our local RTP port and tell the H.323 stack about it */
ast_rtp_get_us(pvt->rtp, &us);
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
ast_inet_ntoa(info->addr, sizeof(info->addr), us.sin_addr);
if (h323debug)
ast_log(LOG_DEBUG, "Sending RTP 'US' %s:%d\n", info->addr, info->port);
Jeremy McNamara
committed
/**
* Definition taken from rtp.c for rtpPayloadType because we need it here.
*/
struct rtpPayloadType {
int isAstFormat; /* whether the following code is an AST_FORMAT */
int code;
};
Jeremy McNamara
committed
/**
* Call-back function passing remote ip/port information from H.323 to asterisk
*
* Returns nothing
*/
Jeremy McNamara
committed
void setup_rtp_connection(unsigned call_reference, const char *remoteIp, int remotePort, const char *token, int pt)
Jeremy McNamara
committed
{
struct oh323_pvt *pvt;
Jeremy McNamara
committed
struct sockaddr_in them;
Jeremy McNamara
committed
struct rtpPayloadType rtptype;
Jeremy McNamara
committed
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "Setting up RTP connection for %s\n", token);
Jeremy McNamara
committed
/* Find the call or allocate a private structure if call not found */
pvt = find_call_locked(call_reference, token);
Jeremy McNamara
committed
if (!pvt) {
ast_log(LOG_ERROR, "Something is wrong: rtp\n");
return;
}
Jeremy McNamara
committed
if (pvt->alreadygone) {
ast_mutex_unlock(&pvt->lock);
return;
}
rtptype = ast_rtp_lookup_pt(pvt->rtp, pt);
pvt->nativeformats = rtptype.code;
if (pvt->owner && !ast_mutex_trylock(&pvt->owner->lock)) {
pvt->owner->nativeformats = pvt->nativeformats;
ast_set_read_format(pvt->owner, pvt->owner->readformat);
ast_set_write_format(pvt->owner, pvt->owner->writeformat);
if (pvt->options.progress_audio)
ast_queue_control(pvt->owner, AST_CONTROL_PROGRESS);
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->owner->lock);
}
else {
if (pvt->options.progress_audio)
pvt->newcontrol = AST_CONTROL_PROGRESS;
if (h323debug)
ast_log(LOG_DEBUG, "RTP connection preparation for %s is pending...\n", token);
}
Jeremy McNamara
committed
Jeremy McNamara
committed
them.sin_family = AF_INET;
/* only works for IPv4 */
them.sin_addr.s_addr = inet_addr(remoteIp);
Jeremy McNamara
committed
them.sin_port = htons(remotePort);
ast_rtp_set_peer(pvt->rtp, &them);
Jeremy McNamara
committed
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "RTP connection prepared for %s\n", token);
Jeremy McNamara
committed
Jeremy McNamara
committed
return;
}
/**
* Call-back function to signal asterisk that the channel has been answered
* Returns nothing
*/
void connection_made(unsigned call_reference, const char *token)
{
struct oh323_pvt *pvt;
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "Call %s answered\n", token);
pvt = find_call_locked(call_reference, token);
Jeremy McNamara
committed
if (!pvt) {
ast_log(LOG_ERROR, "Something is wrong: connection\n");
return;
}
Jeremy McNamara
committed
/* Inform asterisk about remote party connected only on outgoing calls */
if (!pvt->outgoing) {
ast_mutex_unlock(&pvt->lock);
return;
}
if (update_state(pvt, AST_STATE_UP, AST_CONTROL_ANSWER))
ast_mutex_unlock(&pvt->owner->lock);
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
return;
}
Jeremy McNamara
committed
int progress(unsigned call_reference, const char *token, int inband)
{
struct oh323_pvt *pvt;
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Received ALERT/PROGRESS message for %s tones\n", (inband ? "inband" : "self-generated"));
pvt = find_call_locked(call_reference, token);
if (!pvt) {
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Private structure not found in progress.\n");
return -1;
}
if (!pvt->owner) {
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
ast_log(LOG_ERROR, "No Asterisk channel associated with private structure.\n");
return -1;
}
if (update_state(pvt, -1, (inband ? AST_CONTROL_PROGRESS : AST_CONTROL_RINGING)))
ast_mutex_unlock(&pvt->owner->lock);
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
return 0;
}
/**
* Call-back function for incoming calls
*
* Returns 1 on success
*/
Jeremy McNamara
committed
call_options_t *setup_incoming_call(call_details_t *cd)
struct oh323_pvt *pvt;
struct oh323_user *user = NULL;
struct oh323_alias *alias = NULL;
char iabuf[INET_ADDRSTRLEN];
Jeremy McNamara
committed
if (h323debug)
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Setting up incoming call for %s\n", cd->call_token);
Jeremy McNamara
committed
Jeremy McNamara
committed
pvt = oh323_alloc(cd->call_reference);
if (!pvt) {
ast_log(LOG_ERROR, "Unable to allocate private structure, this is bad.\n");
Jeremy McNamara
committed
return NULL;
}
/* Populate the call details in the private structure */
Jeremy McNamara
committed
memcpy(&pvt->cd, cd, sizeof(pvt->cd));
memcpy(&pvt->options, &global_options, sizeof(pvt->options));
Jeremy McNamara
committed
ast_verbose(VERBOSE_PREFIX_3 "Setting up Call\n");
Jeremy McNamara
committed
ast_verbose(VERBOSE_PREFIX_3 "\tCall token: [%s]\n", pvt->cd.call_token);
ast_verbose(VERBOSE_PREFIX_3 "\tCalling party name: [%s]\n", pvt->cd.call_source_name);
ast_verbose(VERBOSE_PREFIX_3 "\tCalling party number: [%s]\n", pvt->cd.call_source_e164);
ast_verbose(VERBOSE_PREFIX_3 "\tCalled party name: [%s]\n", pvt->cd.call_dest_alias);
ast_verbose(VERBOSE_PREFIX_3 "\tCalled party number: [%s]\n", pvt->cd.call_dest_e164);
}
/* Decide if we are allowing Gatekeeper routed calls*/
Jeremy McNamara
committed
if ((!strcasecmp(cd->sourceIp, gatekeeper)) && (gkroute == -1) && (usingGk)) {
if (!ast_strlen_zero(cd->call_dest_e164)) {
strncpy(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten) - 1);
strncpy(pvt->context, default_context, sizeof(pvt->context) - 1);
Jeremy McNamara
committed
alias = find_alias(cd->call_dest_alias);
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Call for %s rejected, alias not found\n", cd->call_dest_alias);
Jeremy McNamara
committed
return NULL;
strncpy(pvt->exten, alias->name, sizeof(pvt->exten) - 1);
strncpy(pvt->context, alias->context, sizeof(pvt->context) - 1);
} else {
/* Either this call is not from the Gatekeeper
or we are not allowing gk routed calls */
Jeremy McNamara
committed
user = find_user(cd);
if (!ast_strlen_zero(pvt->cd.call_dest_e164)) {
Jeremy McNamara
committed
strncpy(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten) - 1);
Jeremy McNamara
committed
strncpy(pvt->exten, cd->call_dest_alias, sizeof(pvt->exten) - 1);
ast_log(LOG_ERROR, "Call from '%s' rejected due to no default context\n", pvt->cd.call_source_aliases);
Jeremy McNamara
committed
return NULL;
strncpy(pvt->context, default_context, sizeof(pvt->context) - 1);
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Sending %s to context [%s]\n", cd->call_source_aliases, pvt->context);
/* XXX: Is it really required??? */
#if 0
Jeremy McNamara
committed
memset(&pvt->options, 0, sizeof(pvt->options));
#endif
Jeremy McNamara
committed
} else {
Jeremy McNamara
committed
if (strcasecmp(cd->sourceIp, ast_inet_ntoa(iabuf, sizeof(iabuf), user->addr.sin_addr))) {
if (ast_strlen_zero(user->context)) {
if (ast_strlen_zero(default_context)) {
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Call from '%s' rejected due to non-matching IP address (%s) and no default context\n", user->name, cd->sourceIp);
Jeremy McNamara
committed
return NULL;
strncpy(pvt->context, default_context, sizeof(pvt->context) - 1);
strncpy(pvt->context, user->context, sizeof(pvt->context) - 1);
Jeremy McNamara
committed
}
pvt->exten[0] = 'i';
pvt->exten[1] = '\0';
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Call from '%s' rejected due to non-matching IP address (%s)s\n", user->name, cd->sourceIp);
return NULL; /* XXX: Hmmm... Why to setup context if we drop connection immediately??? */
strncpy(pvt->context, user->context, sizeof(pvt->context) - 1);
memcpy(&pvt->options, &user->options, sizeof(pvt->options));
if (!ast_strlen_zero(pvt->cd.call_dest_e164)) {
Jeremy McNamara
committed
strncpy(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten) - 1);
Jeremy McNamara
committed
strncpy(pvt->exten, cd->call_dest_alias, sizeof(pvt->exten) - 1);
strncpy(pvt->accountcode, user->accountcode, sizeof(pvt->accountcode) - 1);
Jeremy McNamara
committed
if (user->amaflags) {
pvt->amaflags = user->amaflags;
}
return &pvt->options;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
/**
* Call-back function to start PBX when OpenH323 ready to serve incoming call
*
* Returns 1 on success
*/
static int answer_call(unsigned call_reference, const char *token)
Jeremy McNamara
committed
{
struct oh323_pvt *pvt;
Jeremy McNamara
committed
struct ast_channel *c = NULL;
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "Preparing Asterisk to answer for %s\n", token);
Jeremy McNamara
committed
/* Find the call or allocate a private structure if call not found */
pvt = find_call_locked(call_reference, token);
if (!pvt) {
Jeremy McNamara
committed
ast_log(LOG_ERROR, "Something is wrong: answer_call\n");
return 0;
}
/* allocate a channel and tell asterisk about it */
c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token);
/* And release when done */
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
if (!c) {
ast_log(LOG_ERROR, "Couldn't create channel. This is bad\n");
return 0;
}
Jeremy McNamara
committed
return 1;
}
/**
* Call-back function to establish an outgoing H.323 call
*
* Returns 1 on success
*/
Jeremy McNamara
committed
int setup_outgoing_call(call_details_t *cd)
{
/* Use argument here or free it immediately */
cleanup_call_details(cd);
Jeremy McNamara
committed
/**
* Call-back function to signal asterisk that the channel is ringing
* Returns nothing
*/
void chan_ringing(unsigned call_reference, const char *token)
Jeremy McNamara
committed
{
Jeremy McNamara
committed
struct oh323_pvt *pvt;
Jeremy McNamara
committed
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "Ringing on %s\n", token);
pvt = find_call_locked(call_reference, token);
if (!pvt) {
ast_log(LOG_ERROR, "Something is wrong: ringing\n");
return;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
if (!pvt->owner) {
ast_mutex_unlock(&pvt->lock);
ast_log(LOG_ERROR, "Channel has no owner\n");
return;
}
if (update_state(pvt, AST_STATE_RINGING, AST_CONTROL_RINGING))
ast_mutex_unlock(&pvt->owner->lock);
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->lock);
return;
Jeremy McNamara
committed
}
/**
* Call-back function to cleanup communication
* Returns nothing,
*/
Jeremy McNamara
committed
static void cleanup_connection(unsigned call_reference, const char *call_token)
struct oh323_pvt *pvt;
Jeremy McNamara
committed
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Cleaning connection to %s\n", call_token);
Jeremy McNamara
committed
while (1) {
Jeremy McNamara
committed
pvt = find_call_locked(call_reference, call_token);
Jeremy McNamara
committed
if (!pvt) {
if (h323debug)
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "No connection for %s\n", call_token);
Jeremy McNamara
committed
return;
}
if (!pvt->owner || !ast_mutex_trylock(&pvt->owner->lock))
break;
#if 1
#ifdef DEBUG_THREADS
ast_log(LOG_NOTICE, "Avoiding H.323 destory deadlock on %s, locked at %ld/%d by %s (%s:%d)\n", call_token, pvt->owner->lock.thread[0], pvt->owner->lock.reentrancy, pvt->owner->lock.func[0], pvt->owner->lock.file[0], pvt->owner->lock.lineno[0]);
Jeremy McNamara
committed
#else
Jeremy McNamara
committed
ast_log(LOG_NOTICE, "Avoiding H.323 destory deadlock on %s\n", call_token);
Jeremy McNamara
committed
#endif
#endif
ast_mutex_unlock(&pvt->lock);
usleep(1);
if (pvt->rtp) {
Jeremy McNamara
committed
ast_rtp_destroy(pvt->rtp);
pvt->rtp = NULL;
Jeremy McNamara
committed
/* Free dsp used for in-band DTMF detection */
if (pvt->vad) {
ast_dsp_free(pvt->vad);
pvt->vad = NULL;
}
cleanup_call_details(&pvt->cd);
pvt->alreadygone = 1;
if (pvt->owner) {
Jeremy McNamara
committed
pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
ast_queue_hangup(pvt->owner);
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->owner->lock);
}
ast_mutex_unlock(&pvt->lock);
if (h323debug)
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Connection to %s cleaned\n", call_token);
Jeremy McNamara
committed
static void hangup_connection(unsigned int call_reference, const char *token, int cause)
{
struct oh323_pvt *pvt;
Jeremy McNamara
committed
ast_log(LOG_DEBUG, "Hanging up connection to %s with cause %d\n", token, cause);
pvt = find_call_locked(call_reference, token);
Jeremy McNamara
committed
if (!pvt) {
return;
}
if (pvt->owner && !ast_mutex_trylock(&pvt->owner->lock)) {
pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
pvt->owner->hangupcause = pvt->hangupcause = cause;
ast_queue_hangup(pvt->owner);
ast_mutex_unlock(&pvt->owner->lock);
}
else {
pvt->needhangup = 1;
pvt->hangupcause = cause;
ast_log(LOG_DEBUG, "Hangup for %s is pending\n", token);
}
ast_mutex_unlock(&pvt->lock);
}
Jeremy McNamara
committed
void set_dtmf_payload(unsigned call_reference, const char *token, int payload)
{
struct oh323_pvt *pvt;
Jeremy McNamara
committed
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "Setting DTMF payload to %d on %s\n", payload, token);
pvt = find_call_locked(call_reference, token);
Jeremy McNamara
committed
if (!pvt) {
return;
}
if (pvt->rtp) {
ast_rtp_set_rtpmap_type(pvt->rtp, payload, "audio", "telephone-event");
}
ast_mutex_unlock(&pvt->lock);
Jeremy McNamara
committed
if (h323debug)
ast_log(LOG_DEBUG, "DTMF payload on %s set to %d\n", token, payload);
Jeremy McNamara
committed
}
Jeremy McNamara
committed
static void set_local_capabilities(unsigned call_reference, const char *token)
{
struct oh323_pvt *pvt;
int capability, dtmfmode;
if (h323debug)
ast_log(LOG_DEBUG, "Setting capabilities for connection %s\n", token);
pvt = find_call_locked(call_reference, token);
if (!pvt)
return;
capability = pvt->options.capability;
dtmfmode = pvt->options.dtmfmode;
Jeremy McNamara
committed
ast_mutex_unlock(&pvt->lock);
h323_set_capabilities(token, capability, dtmfmode);
if (h323debug)
ast_log(LOG_DEBUG, "Capabilities for connection %s is set\n", token);
}
static void *do_monitor(void *data)
{
int res;
Jeremy McNamara
committed
int reloading;
Jeremy McNamara
committed
for(;;) {
Jeremy McNamara
committed
/* Check for a reload request */
ast_mutex_lock(&h323_reload_lock);
reloading = h323_reloading;
h323_reloading = 0;
ast_mutex_unlock(&h323_reload_lock);
if (reloading) {
if (option_verbose > 0) {
ast_verbose(VERBOSE_PREFIX_1 "Reloading H.323\n");
Jeremy McNamara
committed
}
Jeremy McNamara
committed
h323_do_reload();
}
/* Check for interfaces needing to be killed */
restartsearch:
oh323 = iflist;
while(oh323) {
if (oh323->needdestroy) {
__oh323_destroy(oh323);
goto restartsearch;
}
oh323 = oh323->next;
}
Jeremy McNamara
committed
pthread_testcancel();
/* Wait for sched or io */
res = ast_sched_wait(sched);
Jeremy McNamara
committed
if ((res < 0) || (res > 1000)) {
Jeremy McNamara
committed
}
Jeremy McNamara
committed
pthread_testcancel();
Jeremy McNamara
committed
if (res >= 0) {
Jeremy McNamara
committed
}
}
/* Never reached */
return NULL;
}
static int restart_monitor(void)
{
Jeremy McNamara
committed
pthread_attr_t attr;
/* If we're supposed to be stopped -- stay stopped */
Jeremy McNamara
committed
if (monitor_thread == AST_PTHREADT_STOP) {
Jeremy McNamara
committed
}
ast_log(LOG_WARNING, "Unable to lock monitor\n");
return -1;
}
if (monitor_thread == pthread_self()) {
ast_log(LOG_WARNING, "Cannot kill myself\n");
return -1;
}
if (monitor_thread && (monitor_thread != AST_PTHREADT_NULL)) {
/* Wake up the thread */
pthread_kill(monitor_thread, SIGURG);
Jeremy McNamara
committed
} else {
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* Start a new monitor */
if (ast_pthread_create(&monitor_thread, &attr, do_monitor, NULL) < 0) {
ast_mutex_unlock(&monlock);
ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
return -1;
}
return 0;
}
static int h323_do_trace(int fd, int argc, char *argv[])
{
h323_debug(1, atoi(argv[2]));
ast_cli(fd, "H.323 trace set to level %s\n", argv[2]);
return RESULT_SUCCESS;
}
static int h323_no_trace(int fd, int argc, char *argv[])
{
h323_debug(0,0);
ast_cli(fd, "H.323 trace disabled\n");
return RESULT_SUCCESS;
}
static int h323_do_debug(int fd, int argc, char *argv[])
{
h323debug = 1;
ast_cli(fd, "H323 debug enabled\n");
return RESULT_SUCCESS;
}
static int h323_no_debug(int fd, int argc, char *argv[])
{
h323debug = 0;
ast_cli(fd, "H323 Debug disabled\n");
return RESULT_SUCCESS;
}
static int h323_gk_cycle(int fd, int argc, char *argv[])
{
h323_gk_urq();
/* Possibly register with a GK */
if (!gatekeeper_disable) {
ast_log(LOG_ERROR, "Gatekeeper registration failed.\n");
}
}
return RESULT_SUCCESS;
Jeremy McNamara
committed
static int h323_ep_hangup(int fd, int argc, char *argv[])
{
Jeremy McNamara
committed
return RESULT_SHOWUSAGE;
Jeremy McNamara
committed
if (h323_soft_hangup(argv[2])) {
ast_verbose(VERBOSE_PREFIX_3 "Hangup succeeded on %s\n", argv[2]);
} else {
ast_verbose(VERBOSE_PREFIX_3 "Hangup failed for %s\n", argv[2]);
}
return RESULT_SUCCESS;
}
static int h323_tokens_show(int fd, int argc, char *argv[])
{
Jeremy McNamara
committed
return RESULT_SHOWUSAGE;
Jeremy McNamara
committed
h323_show_tokens();
return RESULT_SUCCESS;
}
static char trace_usage[] =
"Usage: h.323 trace <level num>\n"
" Enables H.323 stack tracing for debugging purposes\n";
static char no_trace_usage[] =
"Usage: h.323 no trace\n"
" Disables H.323 stack tracing for debugging purposes\n";
static char debug_usage[] =
"Usage: h.323 debug\n"
static char no_debug_usage[] =
"Usage: h.323 no debug\n"
static char show_codec_usage[] =
"Usage: h.323 show codec\n"
" Shows all enabled codecs\n";
static char show_cycle_usage[] =
"Usage: h.323 gk cycle\n"
" Manually re-register with the Gatekeper (Currently Disabled)\n";
Jeremy McNamara
committed
static char show_hangup_usage[] =
"Usage: h.323 hangup <token>\n"
" Manually try to hang up call identified by <token>\n";
static char show_tokens_usage[] =
"Usage: h.323 show tokens\n"
" Print out all active call tokens\n";
Jeremy McNamara
committed
static char h323_reload_usage[] =
"Usage: h323 reload\n"
" Reloads H.323 configuration from sip.conf\n";
static struct ast_cli_entry cli_trace =
{ { "h.323", "trace", NULL }, h323_do_trace, "Enable H.323 Stack Tracing", trace_usage };
static struct ast_cli_entry cli_no_trace =
{ { "h.323", "no", "trace", NULL }, h323_no_trace, "Disable H.323 Stack Tracing", no_trace_usage };
static struct ast_cli_entry cli_debug =
{ { "h.323", "debug", NULL }, h323_do_debug, "Enable H.323 debug", debug_usage };
{ { "h.323", "no", "debug", NULL }, h323_no_debug, "Disable H.323 debug", no_debug_usage };
static struct ast_cli_entry cli_show_codecs =
{ { "h.323", "show", "codecs", NULL }, h323_show_codec, "Show enabled codecs", show_codec_usage };
static struct ast_cli_entry cli_gk_cycle =
{ { "h.323", "gk", "cycle", NULL }, h323_gk_cycle, "Manually re-register with the Gatekeper", show_cycle_usage };
Jeremy McNamara
committed
static struct ast_cli_entry cli_hangup_call =
{ { "h.323", "hangup", NULL }, h323_ep_hangup, "Manually try to hang up a call", show_hangup_usage };
Jeremy McNamara
committed
static struct ast_cli_entry cli_show_tokens =
{ { "h.323", "show", "tokens", NULL }, h323_tokens_show, "Show all active call tokens", show_tokens_usage };
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
static int update_common_options(struct ast_variable *v, struct call_options *options)
{
unsigned int format;
int tmp;
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
options->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
options->capability &= ~format;
} else if (!strcasecmp(v->name, "dtmfmode")) {
if (!strcasecmp(v->value, "inband")) {
options->dtmfmode = H323_DTMF_INBAND;
} else if (!strcasecmp(v->value, "rfc2833")) {
options->dtmfmode = H323_DTMF_RFC2833;
} else {
ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", v->value);
options->dtmfmode = H323_DTMF_RFC2833;
}
} else if (!strcasecmp(v->name, "dtmfcodec")) {
tmp = atoi(v->value);
if (tmp < 96)
ast_log(LOG_WARNING, "Invalid global dtmfcodec value %s\n", v->value);
else
options->dtmfcodec = tmp;
} else if (!strcasecmp(v->name, "bridge")) {
options->bridge = ast_true(v->value);
} else if (!strcasecmp(v->name, "nat")) {
options->nat = ast_true(v->value);
} else if (!strcasecmp(v->name, "noFastStart")) {
options->noFastStart = ast_true(v->value);
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
options->noH245Tunneling = ast_true(v->value);
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
options->noSilenceSuppression = ast_true(v->value);
} else if (!strcasecmp(v->name, "progress_setup")) {
tmp = atoi(v->value);
if ((tmp != 0) && (tmp != 1) && (tmp != 3) && (tmp != 8)) {
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", tmp, v->lineno);
tmp = 0;
}
options->progress_setup = tmp;
} else if (!strcasecmp(v->name, "progress_alert")) {
tmp = atoi(v->value);
if ((tmp != 0) && (tmp != 8)) {
ast_log(LOG_WARNING, "Invalud value %d for progress_alert at line %d, assuming 0\n", tmp, v->lineno);
tmp = 0;
}
options->progress_alert = tmp;
} else if (!strcasecmp(v->name, "progress_audio")) {
options->progress_audio = ast_true(v->value);
} else
return 1;
return 0;
}
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
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);
} else {
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);
memcpy(&user->options, &global_options, sizeof(user->options));
/* Set default context */
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 (!update_common_options(v, &user->options)) {
/* dummy */
1900
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
} 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);
} 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;
int found=0;
prev = NULL;
ast_mutex_lock(&peerl.lock);
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;
}
ast_mutex_unlock(&peerl.lock);
} else {
ast_mutex_unlock(&peerl.lock);
peer = (struct oh323_peer*)malloc(sizeof(struct oh323_peer));
if (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;
memcpy(&peer->options, &global_options, sizeof(peer->options));
while(v) {
if (!update_common_options(v, &peer->options)) {
/* dummy */
} 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)) {
ast_log(LOG_ERROR, "Could not determine IP for %s\n", v->value);
free(peer);
return NULL;
}
} else if (!strcasecmp(v->name, "port")) {
peer->addr.sin_port = htons(atoi(v->value));
}
v=v->next;
}
}
return peer;
}
Jeremy McNamara
committed
{
int format;
struct ast_config *cfg;
struct ast_variable *v;
struct oh323_peer *peer = NULL;
struct oh323_user *user = NULL;
struct oh323_alias *alias = NULL;
struct ast_hostent ahp; struct hostent *hp;
/* We *must* have a config file otherwise stop immediately */
if (!cfg) {
ast_log(LOG_NOTICE, "Unable to load config %s, H.323 disabled\n", config);
/* fire up the H.323 Endpoint */
if (!h323_end_point_exist()) {
h323_end_point_create();
Jeremy McNamara
committed
memset(&global_options, 0, sizeof(global_options));
global_options.dtmfcodec = 101;
global_options.dtmfmode = H323_DTMF_RFC2833;
global_options.capability = GLOBAL_CAPABILITY;
global_options.bridge = 1; /* Do native bridging by default */
v = ast_variable_browse(cfg, "general");
while(v) {
/* Create the interface list */
if (!strcasecmp(v->name, "port")) {
h323_signalling_port = (int)strtol(v->value, NULL, 10);
} else if (!strcasecmp(v->name, "bindaddr")) {
if (!(hp = ast_gethostbyname(v->value, &ahp))) {
ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
} else {
memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
}
} else if (!strcasecmp(v->name, "tos")) {
Kevin P. Fleming
committed
if (sscanf(v->value, "%d", &format)) {
Jeremy McNamara
committed
} else if (!strcasecmp(v->value, "lowdelay")) {
Jeremy McNamara
committed
} else if (!strcasecmp(v->value, "throughput")) {
Jeremy McNamara
committed
} else if (!strcasecmp(v->value, "reliability")) {
Jeremy McNamara
committed
} else if (!strcasecmp(v->value, "mincost")) {
Jeremy McNamara
committed
} else if (!strcasecmp(v->value, "none")) {
Jeremy McNamara
committed
} else {
ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
Jeremy McNamara
committed
}
} else if (!strcasecmp(v->name, "gatekeeper")) {
if (!strcasecmp(v->value, "DISABLE")) {
gatekeeper_disable = 1;
usingGk = 0;
} else if (!strcasecmp(v->value, "DISCOVER")) {
gatekeeper_disable = 0;
gatekeeper_discover = 1;
usingGk = 1;
} else {
gatekeeper_disable = 0;
usingGk = 1;
strncpy(gatekeeper, v->value, sizeof(gatekeeper) - 1);
}
} else if (!strcasecmp(v->name, "secret")) {
} else if (!strcasecmp(v->name, "AllowGKRouted")) {
} else if (!strcasecmp(v->name, "context")) {
strncpy(default_context, v->value, sizeof(default_context) - 1);
ast_verbose(VERBOSE_PREFIX_2 "Setting default context to %s\n", default_context);
Jeremy McNamara
committed
} else if (!strcasecmp(v->name, "UserByAlias")) {
Jeremy McNamara
committed
userbyalias = ast_true(v->value);
} else if (!update_common_options(v, &global_options)) {
/* dummy */
v = v->next;
}
cat = ast_category_browse(cfg, NULL);
while(cat) {
if (strcasecmp(cat, "general")) {
utype = ast_variable_retrieve(cfg, cat, "type");
if (utype) {
Jeremy McNamara
committed
if (!strcasecmp(utype, "user")) {
user = build_user(cat, ast_variable_browse(cfg, cat));
if (user) {
user->next = userl.users;
userl.users = user;
Jeremy McNamara
committed
} else if (!strcasecmp(utype, "peer")) {
peer = build_peer(cat, ast_variable_browse(cfg, cat));
if (peer) {
ast_mutex_lock(&peerl.lock);
peer->next = peerl.peers;
peerl.peers = peer;
ast_mutex_unlock(&peerl.lock);
}
} else if (!strcasecmp(utype, "friend")) {
user = build_user(cat, ast_variable_browse(cfg, cat));
peer = build_peer(cat, ast_variable_browse(cfg, cat));
Jeremy McNamara
committed
if (user) {
ast_mutex_lock(&userl.lock);
user->next = userl.users;
userl.users = user;
ast_mutex_unlock(&userl.lock);
}
peer->next = peerl.peers;
peerl.peers = peer;
} else if (!strcasecmp(utype, "h323") || !strcasecmp(utype, "alias")) {
alias = build_alias(cat, ast_variable_browse(cfg, cat));
if (alias) {
alias->next = aliasl.aliases;
aliasl.aliases = alias;
}
} else {
ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config);
}
ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
}
cat = ast_category_browse(cfg, cat);
}
/* Register our H.323 aliases if any*/
while (alias) {
if (h323_set_alias(alias)) {
ast_log(LOG_ERROR, "Alias %s rejected by endpoint\n", alias->name);
return -1;
}
alias = alias->next;
}
return 0;
}
void delete_users(void)
{
struct oh323_user *user, *userlast;
struct oh323_peer *peer;
/* Delete all users */
for (user=userl.users;user;) {
userlast = user;
user=user->next;
free(userlast);
}
userl.users=NULL;
ast_mutex_unlock(&userl.lock);
ast_mutex_lock(&peerl.lock);
for (peer=peerl.peers;peer;) {
/* Assume all will be deleted, and we'll find out for sure later */
peer->delme = 1;
peer = peer->next;
}
}
void delete_aliases(void)
{
struct oh323_alias *alias, *aliaslast;
for (alias=aliasl.aliases;alias;) {
aliaslast = alias;
alias=alias->next;
free(aliaslast);
}
aliasl.aliases=NULL;
}
void prune_peers(void)
{
/* Prune peers who still are supposed to be deleted */
struct oh323_peer *peer, *peerlast, *peernext;
peerlast = NULL;
for (peer=peerl.peers;peer;) {
peernext = peer->next;
if (peer->delme) {
free(peer);
Jeremy McNamara
committed
static int h323_reload(int fd, int argc, char *argv[])
{
ast_mutex_lock(&h323_reload_lock);
if (h323_reloading) {
ast_verbose("Previous H.323 reload not yet done\n");
} else {
h323_reloading = 1;
}
Jeremy McNamara
committed
ast_mutex_unlock(&h323_reload_lock);
restart_monitor();
return 0;
Jeremy McNamara
committed
}
static int h323_do_reload(void)
delete_aliases();
prune_peers();
reload_config();
restart_monitor();
return 0;
Jeremy McNamara
committed
}
Jeremy McNamara
committed
int reload(void)
{
return h323_reload(0, 0, NULL);
Jeremy McNamara
committed
static struct ast_cli_entry cli_h323_reload =
{ { "h.323", "reload", NULL }, h323_reload, "Reload H.323 configuration", h323_reload_usage };
Jeremy McNamara
committed
static struct ast_rtp *oh323_get_rtp_peer(struct ast_channel *chan)
{
struct oh323_pvt *pvt;
pvt = (struct oh323_pvt *) chan->tech_pvt;
if (pvt && pvt->rtp && pvt->options.bridge) {
return pvt->rtp;
static struct ast_rtp *oh323_get_vrtp_peer(struct ast_channel *chan)
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
static char *convertcap(int cap)
{
switch (cap) {
case AST_FORMAT_G723_1:
return "G.723";
case AST_FORMAT_GSM:
return "GSM";
case AST_FORMAT_ULAW:
return "ULAW";
case AST_FORMAT_ALAW:
return "ALAW";
case AST_FORMAT_ADPCM:
return "G.728";
case AST_FORMAT_G729A:
return "G.729";
case AST_FORMAT_SPEEX:
return "SPEEX";
case AST_FORMAT_ILBC:
return "ILBC";
default:
ast_log(LOG_NOTICE, "Don't know how to deal with mode %d\n", cap);
return NULL;
}
}
static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
{
/* XXX Deal with Video */
struct oh323_pvt *pvt;
struct sockaddr_in them;
struct sockaddr_in us;
char iabuf[INET_ADDRSTRLEN];
pvt = (struct oh323_pvt *) chan->tech_pvt;
if (!pvt) {
ast_log(LOG_ERROR, "No Private Structure, this is bad\n");
return -1;
}
ast_rtp_get_peer(rtp, &them);
h323_native_bridge(pvt->cd.call_token, ast_inet_ntoa(iabuf, sizeof(iabuf), them.sin_addr), mode);
return 0;
}
static struct ast_rtp_protocol oh323_rtp = {
.get_rtp_info = oh323_get_rtp_peer,
.get_vrtp_info = oh323_get_vrtp_peer,
.set_rtp_peer= oh323_set_rtp_peer,
};
int load_module()
{
int res;
ast_mutex_init(&userl.lock);
ast_mutex_init(&peerl.lock);
ast_mutex_init(&aliasl.lock);
Jeremy McNamara
committed
sched = sched_context_create();
if (!sched) {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
}
io = io_context_create();
if (!io) {
ast_log(LOG_WARNING, "Unable to create I/O context\n");
}
if (res) {
return 0;
} else {
/* Make sure we can register our channel type */
if (ast_channel_register(&oh323_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class 'H323'\n");
h323_end_process();
return -1;
}
ast_cli_register(&cli_debug);
ast_cli_register(&cli_no_debug);
ast_cli_register(&cli_trace);
ast_cli_register(&cli_no_trace);
ast_cli_register(&cli_show_codecs);
Jeremy McNamara
committed
ast_cli_register(&cli_hangup_call);
ast_cli_register(&cli_show_tokens);
Jeremy McNamara
committed
ast_cli_register(&cli_h323_reload);
ast_rtp_proto_register(&oh323_rtp);
/* Register our callback functions */
h323_callback_register(setup_incoming_call,
setup_outgoing_call,
external_rtp_create,
setup_rtp_connection,
cleanup_connection,
chan_ringing,
connection_made,
send_digit,
answer_call,
progress,
set_dtmf_payload,
Jeremy McNamara
committed
hangup_connection,
set_local_capabilities);
if (h323_start_listener(h323_signalling_port, bindaddr)) {
ast_log(LOG_ERROR, "Unable to create H323 listener.\n");
return -1;
}
/* Possibly register with a GK */
if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) {
ast_log(LOG_ERROR, "Gatekeeper registration failed.\n");
}
}
/* And start the monitor for the first time */
restart_monitor();
}
return res;
}
int unload_module()
{
struct oh323_pvt *p, *pl;
Jeremy McNamara
committed
/* unregister commands */
ast_cli_unregister(&cli_debug);
ast_cli_unregister(&cli_no_debug);
ast_cli_unregister(&cli_trace);
ast_cli_unregister(&cli_no_trace);
ast_cli_unregister(&cli_show_codecs);
ast_cli_unregister(&cli_gk_cycle);
ast_cli_unregister(&cli_hangup_call);
ast_cli_unregister(&cli_show_tokens);
ast_cli_unregister(&cli_h323_reload);
Jeremy McNamara
committed
ast_rtp_proto_unregister(&oh323_rtp);
ast_channel_unregister(&oh323_tech);
Jeremy McNamara
committed
/* hangup all interfaces if they have an owner */
p = iflist;
while(p) {
if (p->owner) {
ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
}
p = p->next;
}
iflist = NULL;
ast_mutex_unlock(&iflock);
} else {
ast_log(LOG_WARNING, "Unable to lock the interface list\n");
return -1;
}
if (!ast_mutex_lock(&monlock)) {
if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
/* this causes a seg, anyone know why? */
pthread_kill(monitor_thread, SIGURG);
pthread_join(monitor_thread, NULL);
}
monitor_thread = AST_PTHREADT_STOP;
ast_mutex_unlock(&monlock);
} else {
ast_log(LOG_WARNING, "Unable to lock the monitor\n");
return -1;
}
/* destroy all the interfaces and free their memory */
p = iflist;
while(p) {
pl = p;
p = p->next;
/* free associated memory */
Mark Spencer
committed
ast_mutex_destroy(&pl->lock);
Loading
Loading full blame...