/* * Copyright (C) 2004-2005 by Objective Systems, Inc. * * This software is furnished under an open source license and may be * used and copied only in accordance with the terms of this license. * The text of the license may generally be found in the root * directory of this installation in the COPYING file. It * can also be viewed online at the following URL: * * http://www.obj-sys.com/open/license.html * * Any redistributions of this file including modified versions must * maintain this copyright notice. * *****************************************************************************/ #include "chan_ooh323.h" /*** MODULEINFO no ***/ /* Defaults */ #define DEFAULT_CONTEXT "default" #define DEFAULT_H323ID "Asterisk PBX" #define DEFAULT_LOGFILE "/var/log/asterisk/h323_log" #define DEFAULT_H323ACCNT "ast_h323" /* Flags */ #define H323_SILENCESUPPRESSION (1<<0) #define H323_GKROUTED (1<<1) #define H323_TUNNELING (1<<2) #define H323_FASTSTART (1<<3) #define H323_OUTGOING (1<<4) #define H323_ALREADYGONE (1<<5) #define H323_NEEDDESTROY (1<<6) #define H323_DISABLEGK (1<<7) /* Channel description */ static const char type[] = "OOH323"; static const char tdesc[] = "Objective Systems H323 Channel Driver"; static const char config[] = "ooh323.conf"; /* Channel Definition */ static struct ast_channel *ooh323_request(const char *type, int format, void *data, int *cause); static int ooh323_digit_begin(struct ast_channel *ast, char digit); static int ooh323_digit_end(struct ast_channel *ast, char digit, unsigned int duration); static int ooh323_call(struct ast_channel *ast, char *dest, int timeout); static int ooh323_hangup(struct ast_channel *ast); static int ooh323_answer(struct ast_channel *ast); static struct ast_frame *ooh323_read(struct ast_channel *ast); static int ooh323_write(struct ast_channel *ast, struct ast_frame *f); static int ooh323_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); static int ooh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static enum ast_rtp_get_result ooh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); static enum ast_rtp_get_result ooh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); static int ooh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active); static void print_codec_to_cli(int fd, struct ast_codec_pref *pref); #if 0 static void ast_ooh323c_exit(); #endif static const struct ast_channel_tech ooh323_tech = { .type = type, .description = tdesc, .capabilities = -1, .requester = ooh323_request, .send_digit_begin = ooh323_digit_begin, .send_digit_end = ooh323_digit_end, .call = ooh323_call, .hangup = ooh323_hangup, .answer = ooh323_answer, .read = ooh323_read, .write = ooh323_write, .exception = ooh323_read, .indicate = ooh323_indicate, .fixup = ooh323_fixup, .send_html = 0, .bridge = ast_rtp_bridge, }; static struct ast_rtp_protocol ooh323_rtp = { .type = type, .get_rtp_info = ooh323_get_rtp_peer, .get_vrtp_info = ooh323_get_vrtp_peer, .set_rtp_peer = ooh323_set_rtp_peer }; /* H.323 channel private structure */ static struct ooh323_pvt { ast_mutex_t lock; /* Channel private lock */ struct ast_rtp *rtp; struct ast_rtp *vrtp; /* Placeholder for now */ struct ast_channel *owner; /* Master Channel */ time_t lastrtptx; time_t lastrtprx; unsigned int flags; unsigned int call_reference; char *callToken; char *username; char *host; char *callerid_name; char *callerid_num; char caller_h323id[AST_MAX_EXTENSION]; char caller_dialedDigits[AST_MAX_EXTENSION]; char caller_email[AST_MAX_EXTENSION]; char caller_url[256]; char callee_h323id[AST_MAX_EXTENSION]; char callee_dialedDigits[AST_MAX_EXTENSION]; char callee_email[AST_MAX_EXTENSION]; char callee_url[AST_MAX_EXTENSION]; int port; int readformat; /* negotiated read format */ int writeformat; /* negotiated write format */ int capability; struct ast_codec_pref prefs; int dtmfmode; char exten[AST_MAX_EXTENSION]; /* Requested extension */ char context[AST_MAX_EXTENSION]; /* Context where to start */ char accountcode[256]; /* Account code */ int nat; int amaflags; struct ast_dsp *vad; struct ooh323_pvt *next; /* Next entity */ } *iflist = NULL; /* Protect the channel/interface list (ooh323_pvt) */ AST_MUTEX_DEFINE_STATIC(iflock); /* Profile of H.323 user registered with PBX*/ struct ooh323_user{ ast_mutex_t lock; char name[256]; char context[AST_MAX_EXTENSION]; int incominglimit; unsigned inUse; char accountcode[20]; int amaflags; int capability; struct ast_codec_pref prefs; int dtmfmode; int rtptimeout; int mUseIP; /* Use IP address or H323-ID to search user */ char mIP[20]; struct ooh323_user *next; }; /* Profile of valid asterisk peers */ struct ooh323_peer{ ast_mutex_t lock; char name[256]; unsigned outgoinglimit; unsigned outUse; int capability; struct ast_codec_pref prefs; char accountcode[20]; int amaflags; int dtmfmode; int mFriend; /* indicates defined as friend */ char ip[20]; int port; char *h323id; /* H323-ID alias, which asterisk will register with gk to reach this peer*/ char *email; /* Email alias, which asterisk will register with gk to reach this peer*/ char *url; /* url alias, which asterisk will register with gk to reach this peer*/ char *e164; /* e164 alias, which asterisk will register with gk to reach this peer*/ int rtptimeout; struct ooh323_peer *next; }; /* List of H.323 users known to PBX */ static struct ast_user_list { struct ooh323_user *users; ast_mutex_t lock; } userl; static struct ast_peer_list { struct ooh323_peer *peers; ast_mutex_t lock; } peerl; /* Mutex to protect H.323 reload process */ static int h323_reloading = 0; AST_MUTEX_DEFINE_STATIC(h323_reload_lock); /* Mutex to protect usage counter */ static int usecnt = 0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); AST_MUTEX_DEFINE_STATIC(ooh323c_cmd_lock); /* stack callbacks */ int onAlerting(ooCallData *call); int onNewCallCreated(ooCallData *call); int onCallEstablished(ooCallData *call); int onCallCleared(ooCallData *call); static char gLogFile[256] = DEFAULT_LOGFILE; static int gPort = 1720; static char gIP[20]; static char gCallerID[AST_MAX_EXTENSION] = DEFAULT_H323ID; static struct ooAliases *gAliasList; static int gCapability = AST_FORMAT_ULAW; static struct ast_codec_pref gPrefs; static int gDTMFMode = H323_DTMF_RFC2833; static char gGatekeeper[100]; static enum RasGatekeeperMode gRasGkMode = RasNoGatekeeper; static int gIsGateway = 0; static int gFastStart = 1; static int gTunneling = 1; static int gMediaWaitForConnect = 0; static int gTOS = 0; static int gRTPTimeout = 60; static char gAccountcode[80] = DEFAULT_H323ACCNT; static int gAMAFLAGS; static char gContext[AST_MAX_EXTENSION] = DEFAULT_CONTEXT; static int gIncomingLimit = 4; static int gOutgoingLimit = 4; OOBOOL gH323Debug = FALSE; static struct ooh323_config { int mTCPPortStart; int mTCPPortEnd; } ooconfig; /** Asterisk RTP stuff*/ static struct sched_context *sched; static struct io_context *io; /* 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); /* 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; static struct ast_channel *ooh323_new(struct ooh323_pvt *i, int state, const char *host) { struct ast_channel *ch = NULL; int fmt; if (gH323Debug) ast_verbose("--- ooh323_new - %s\n", host); /* Don't hold a h323 pvt lock while we allocate a channel */ ast_mutex_unlock(&i->lock); ch = ast_channel_alloc(1, state, i->callerid_num, i->callerid_name, i->accountcode, i->exten, i->context, i->amaflags, "OOH323/%s-%08x", host, (unsigned int)(unsigned long) i); ast_mutex_lock(&i->lock); if (ch) { ast_channel_lock(ch); ch->tech = &ooh323_tech; ch->nativeformats = i->capability; fmt = ast_best_codec(ch->nativeformats); ch->fds[0] = ast_rtp_fd(i->rtp); ch->fds[1] = ast_rtcp_fd(i->rtp); if (state == AST_STATE_RING) ch->rings = 1; ch->adsicpe = AST_ADSI_UNAVAILABLE; ch->writeformat = fmt; ch->rawwriteformat = fmt; ch->readformat = fmt; ch->rawreadformat = fmt; ch->tech_pvt = i; i->owner = ch; /* Allocate dsp for in-band DTMF support */ if (i->dtmfmode & H323_DTMF_INBAND) { i->vad = ast_dsp_new(); ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT); } ast_mutex_lock(&usecnt_lock); usecnt++; ast_mutex_unlock(&usecnt_lock); /* Notify the module monitors that use count for resource has changed*/ ast_update_use_count(); ast_copy_string(ch->context, i->context, sizeof(ch->context)); ast_copy_string(ch->exten, i->exten, sizeof(ch->exten)); ch->priority = 1; if (i->callerid_name) { ch->cid.cid_name = strdup(i->callerid_name); } if (i->callerid_num) { ch->cid.cid_num = strdup(i->callerid_num); } if (!ast_test_flag(i, H323_OUTGOING)) { if (!ast_strlen_zero(i->caller_h323id)) { pbx_builtin_setvar_helper(ch, "_CALLER_H323ID", i->caller_h323id); } if (!ast_strlen_zero(i->caller_dialedDigits)) { pbx_builtin_setvar_helper(ch, "_CALLER_H323DIALEDDIGITS", i->caller_dialedDigits); } if (!ast_strlen_zero(i->caller_email)) { pbx_builtin_setvar_helper(ch, "_CALLER_H323EMAIL", i->caller_email); } if (!ast_strlen_zero(i->caller_url)) { pbx_builtin_setvar_helper(ch, "_CALLER_H323URL", i->caller_url); } } if (!ast_strlen_zero(i->accountcode)) ast_string_field_set(ch, accountcode, i->accountcode); if (i->amaflags) ch->amaflags = i->amaflags; ast_setstate(ch, state); if (state != AST_STATE_DOWN) { if (ast_pbx_start(ch)) { ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ch->name); ast_channel_unlock(ch); ast_hangup(ch); ch = NULL; } } } else ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); if (ch) ast_channel_unlock(ch); if (gH323Debug) ast_verbose("+++ h323_new\n"); return ch; } static struct ooh323_pvt *ooh323_alloc(int callref, char *callToken) { struct ooh323_pvt *pvt = NULL; struct in_addr ipAddr; if (gH323Debug) ast_verbose("--- ooh323_alloc\n"); if (!(pvt = ast_calloc(1, sizeof(*pvt)))) { ast_log(LOG_ERROR, "Couldn't allocate private ooh323 structure\n"); return NULL; } ast_mutex_init(&pvt->lock); ast_mutex_lock(&pvt->lock); if (!inet_aton(gIP, &ipAddr)) { ast_log(LOG_ERROR, "Invalid OOH323 driver ip address\n"); ast_mutex_unlock(&pvt->lock); ast_mutex_destroy(&pvt->lock); free(pvt); return NULL; } if (!(pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, ipAddr))) { ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno)); ast_mutex_unlock(&pvt->lock); ast_mutex_destroy(&pvt->lock); free(pvt); return NULL; } ast_rtp_setqos(pvt->rtp, gTOS, 0, "ooh323"); pvt->call_reference = callref; if (callToken) pvt->callToken = strdup(callToken); /* whether to use gk for this call */ if (gRasGkMode == RasNoGatekeeper) OO_SETFLAG(pvt->flags, H323_DISABLEGK); pvt->dtmfmode = gDTMFMode; ast_copy_string(pvt->context, gContext, sizeof(pvt->context)); ast_copy_string(pvt->accountcode, gAccountcode, sizeof(pvt->accountcode)); pvt->amaflags = gAMAFLAGS; pvt->capability = gCapability; memcpy(&pvt->prefs, &gPrefs, sizeof(pvt->prefs)); ast_mutex_unlock(&pvt->lock); /* Add to interface list */ ast_mutex_lock(&iflock); pvt->next = iflist; iflist = pvt; ast_mutex_unlock(&iflock); if (gH323Debug) ast_verbose("+++ ooh323_alloc\n"); return pvt; } /* Possible data values - peername, exten/peername, exten@ip */ static struct ast_channel *ooh323_request(const char *type, int format, void *data, int *cause) { struct ast_channel *chan = NULL; struct ooh323_pvt *p = NULL; struct ooh323_peer *peer = NULL; char *dest = NULL; char *ext = NULL; char tmp[256]; char formats[512]; int oldformat; int port = 0; if (gH323Debug) ast_verbose("--- ooh323_request - data %s format %s\n", (char*)data, ast_getformatname_multiple(formats,512,format)); oldformat = format; format &= AST_FORMAT_AUDIO_MASK; if (!format) { ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format " "'%d'\n", format); return NULL; } p = ooh323_alloc(0,0); /* Initial callRef is zero */ if (!p) { ast_log(LOG_WARNING, "Unable to build pvt data for '%s'\n", (char*)data); return NULL; } ast_mutex_lock(&p->lock); /* This is an outgoing call, since ooh323_request is called */ ast_set_flag(p, H323_OUTGOING); ast_copy_string(tmp, data, sizeof(data)); dest = strchr(tmp, '/'); if (dest) { *dest = '\0'; dest++; ext = tmp; } else if ((dest = strchr(tmp, '@'))) { *dest = '\0'; dest++; ext = tmp; } else { dest = tmp; ext = NULL; } #if 0 if ((sport = strchr(dest, ':'))) { *sport = '\0'; sport++; port = atoi(sport); } #endif if (dest) { peer = find_peer(dest, port); } else{ ast_log(LOG_ERROR, "Destination format is not supported\n"); return NULL; } if (peer) { p->username = strdup(peer->name); p->host = strdup(peer->ip); p->port = peer->port; /* Disable gk as we are going to call a known peer*/ OO_SETFLAG(p->flags, H323_DISABLEGK); if (ext) ast_copy_string(p->exten, ext, sizeof(p->exten)); if (peer->capability & format) { p->capability = peer->capability & format; } else { p->capability = peer->capability; } memcpy(&p->prefs, &peer->prefs, sizeof(struct ast_codec_pref)); p->dtmfmode = peer->dtmfmode; ast_copy_string(p->accountcode, peer->accountcode, sizeof(p->accountcode)); p->amaflags = peer->amaflags; } else { p->dtmfmode = gDTMFMode; p->capability = gCapability; memcpy(&p->prefs, &gPrefs, sizeof(struct ast_codec_pref)); p->username = strdup(dest); p->host = strdup(dest); if (port > 0) { p->port = port; } if (ext) { ast_copy_string(p->exten, ext, sizeof(p->exten)); } } chan = ooh323_new(p, AST_STATE_DOWN, p->username); ast_mutex_unlock(&p->lock); if (!chan) { ast_mutex_lock(&iflock); ooh323_destroy(p); ast_mutex_unlock(&iflock); } restart_monitor(); if (gH323Debug) ast_verbose("+++ ooh323_request\n"); return chan; } static struct ooh323_pvt* find_call(ooCallData *call) { struct ooh323_pvt *p; if (gH323Debug) ast_verbose("--- find_call\n"); ast_mutex_lock(&iflock); for (p = iflist; p; p = p->next) { if (p->callToken && !strcmp(p->callToken, call->callToken)) { break; } } ast_mutex_unlock(&iflock); if (gH323Debug) ast_verbose("+++ find_call\n"); return p; } struct ooh323_user *find_user(const char * name, const char* ip) { struct ooh323_user *user; if (gH323Debug) ast_verbose("--- find_user\n"); ast_mutex_lock(&userl.lock); for (user = userl.users; user; user = user->next) { if (ip && user->mUseIP && !strcmp(user->mIP, ip)) { break; } if (name && !strcmp(user->name, name)) { break; } } ast_mutex_unlock(&userl.lock); if (gH323Debug) ast_verbose("+++ find_user\n"); return user; } struct ooh323_peer *find_friend(const char *name, int port) { struct ooh323_peer *peer; if (gH323Debug) ast_verbose("--- find_friend \"%s\"\n", name); ast_mutex_lock(&peerl.lock); for (peer = peerl.peers; peer; peer = peer->next) { if (gH323Debug) { ast_verbose(" comparing with \"%s\"\n", peer->ip); } if (!strcmp(peer->ip, name)) { if (port <= 0 || (port > 0 && peer->port == port)) { break; } } } ast_mutex_unlock(&peerl.lock); if (gH323Debug) { if (peer) { ast_verbose(" found matching friend\n"); } ast_verbose("+++ find_friend \"%s\"\n", name); } return peer; } struct ooh323_peer *find_peer(const char * name, int port) { struct ooh323_peer *peer; if (gH323Debug) ast_verbose("--- find_peer \"%s\"\n", name); ast_mutex_lock(&peerl.lock); for (peer = peerl.peers; peer; peer = peer->next) { if (gH323Debug) { ast_verbose(" comparing with \"%s\"\n", peer->ip); } if (!strcasecmp(peer->name, name)) break; if (peer->h323id && !strcasecmp(peer->h323id, name)) break; if (peer->e164 && !strcasecmp(peer->e164, name)) break; /* if (!strcmp(peer->ip, name)) { if (port > 0 && peer->port == port) { break; } else if (port <= 0) { break; } } */ } ast_mutex_unlock(&peerl.lock); if (gH323Debug) { if (peer) { ast_verbose(" found matching peer\n"); } ast_verbose("+++ find_peer \"%s\"\n", name); } return peer; } static int ooh323_digit_begin(struct ast_channel *chan, char digit) { char dtmf[2]; struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt; if (gH323Debug) ast_verbose("--- ooh323_digit_begin\n"); if (!p) { ast_log(LOG_ERROR, "No private structure for call\n"); return -1; } ast_mutex_lock(&p->lock); if (p->rtp && (p->dtmfmode & H323_DTMF_RFC2833)) { ast_rtp_senddigit_begin(p->rtp, digit); } else if (((p->dtmfmode & H323_DTMF_Q931) || (p->dtmfmode & H323_DTMF_H245ALPHANUMERIC) || (p->dtmfmode & H323_DTMF_H245SIGNAL))) { dtmf[0] = digit; dtmf[1] = '\0'; ast_mutex_lock(&ooh323c_cmd_lock); ooSendDTMFDigit(p->callToken, dtmf); ast_mutex_unlock(&ooh323c_cmd_lock); } ast_mutex_unlock(&p->lock); if (gH323Debug) ast_verbose("+++ ooh323_digit_begin\n"); return 0; } static int ooh323_digit_end(struct ast_channel *chan, char digit, unsigned int duration) { struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt; if (gH323Debug) ast_verbose("--- ooh323_digit_end\n"); if (!p) { ast_log(LOG_ERROR, "No private structure for call\n"); return -1; } ast_mutex_lock(&p->lock); if (p->rtp && (p->dtmfmode & H323_DTMF_RFC2833)) ast_rtp_senddigit_end(p->rtp, digit); ast_mutex_unlock(&p->lock); if (gH323Debug) ast_verbose("+++ ooh323_digit_end\n"); return 0; } static int ooh323_call(struct ast_channel *ast, char *dest, int timeout) { struct ooh323_pvt *p = ast->tech_pvt; char destination[256]; int res = 0; const char *val = NULL; ooCallOptions opts = { .fastStart = TRUE, .tunneling = TRUE, .disableGk = TRUE, .callMode = OO_CALLMODE_AUDIOCALL }; if (gH323Debug) ast_verbose("--- ooh323_call- %s\n", dest); if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "ooh323_call called on %s, neither down nor " "reserved\n", ast->name); return -1; } ast_mutex_lock(&p->lock); ast_set_flag(p, H323_OUTGOING); if (ast->cid.cid_num) { if (p->callerid_num) { free(p->callerid_num); } p->callerid_num = strdup(ast->cid.cid_num); } if (ast->cid.cid_name) { if (p->callerid_name) { free(p->callerid_name); } p->callerid_name = strdup(ast->cid.cid_name); } else{ ast->cid.cid_name = strdup(gCallerID); if (p->callerid_name) { free(p->callerid_name); } p->callerid_name = strdup(ast->cid.cid_name); } /* Retrieve vars */ if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323ID"))) { ast_copy_string(p->caller_h323id, val, sizeof(p->caller_h323id)); } if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323DIALEDDIGITS"))) { ast_copy_string(p->caller_dialedDigits, val, sizeof(p->caller_dialedDigits)); if (!p->callerid_num) { p->callerid_num = strdup(val); } } if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323EMAIL"))) { ast_copy_string(p->caller_email, val, sizeof(p->caller_email)); } if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323URL"))) { ast_copy_string(p->caller_url, val, sizeof(p->caller_url)); } if (!(p->callToken = (char*)malloc(AST_MAX_EXTENSION))) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Failed to allocate memory for callToken\n"); return -1; /* TODO: need to clean/hangup?? */ } if (p->host && p->port != 0) snprintf(destination, sizeof(destination), "%s:%d", p->host, p->port); else if (p->host) snprintf(destination, sizeof(destination), "%s", p->host); else ast_copy_string(destination, dest, sizeof(destination)); ast_mutex_lock(&ooh323c_cmd_lock); if (OO_TESTFLAG(p->flags, H323_DISABLEGK)) res = ooMakeCall(destination, p->callToken, AST_MAX_EXTENSION, &opts); else res = ooMakeCall(destination, p->callToken, AST_MAX_EXTENSION, NULL); ast_mutex_unlock(&ooh323c_cmd_lock); ast_mutex_unlock(&p->lock); if (res != OO_OK) { ast_log(LOG_ERROR, "Failed to make call\n"); return -1; /* TODO: cleanup */ } if (gH323Debug) ast_verbose("+++ ooh323_call\n"); return 0; } static int ooh323_hangup(struct ast_channel *ast) { struct ooh323_pvt *p = ast->tech_pvt; if (gH323Debug) ast_verbose("--- ooh323_hangup\n"); if (p) { ast_mutex_lock(&p->lock); if (gH323Debug) ast_verbose(" hanging %s\n", p->username); ast->tech_pvt = NULL; if (!ast_test_flag(p, H323_ALREADYGONE)) { ast_mutex_lock(&ooh323c_cmd_lock); ooHangCall(p->callToken, ooh323_convert_hangupcause_asteriskToH323(p->owner->hangupcause)); ast_mutex_unlock(&ooh323c_cmd_lock); ast_set_flag(p, H323_ALREADYGONE); /* ast_mutex_unlock(&p->lock); */ } else { ast_set_flag(p, H323_NEEDDESTROY); } /* detach channel here */ if (p->owner) { p->owner->tech_pvt = NULL; p->owner = NULL; } ast_mutex_unlock(&p->lock); ast_mutex_lock(&usecnt_lock); usecnt--; ast_mutex_unlock(&usecnt_lock); /* Notify the module monitors that use count for resource has changed */ ast_update_use_count(); } else { ast_log(LOG_ERROR, "No call to hangup\n" ); return -1; } if (gH323Debug) ast_verbose("+++ ooh323_hangup\n"); return 0; } static int ooh323_answer(struct ast_channel *ast) { struct ooh323_pvt *p = ast->tech_pvt; if (gH323Debug) ast_verbose("--- ooh323_answer\n"); ast_mutex_lock(&p->lock); if (ast->_state != AST_STATE_UP) { ast_channel_lock(ast); ast_setstate(ast, AST_STATE_UP); ast_debug(1, "ooh323_answer(%s)\n", ast->name); ast_channel_unlock(ast); ast_mutex_lock(&ooh323c_cmd_lock); ooAnswerCall(p->callToken); ast_mutex_unlock(&ooh323c_cmd_lock); } ast_mutex_unlock(&p->lock); if (gH323Debug) ast_verbose("+++ ooh323_answer\n"); return 0; } static struct ast_frame *ooh323_read(struct ast_channel *ast) { struct ast_frame *fr; static struct ast_frame null_frame = { AST_FRAME_NULL, }; struct ooh323_pvt *p = ast->tech_pvt; ast_mutex_lock(&p->lock); if (p->rtp) fr = ooh323_rtp_read(ast, p); else fr = &null_frame; /* time(&p->lastrtprx); */ ast_mutex_unlock(&p->lock); return fr; } static int ooh323_write(struct ast_channel *ast, struct ast_frame *f) { struct ooh323_pvt *p = ast->tech_pvt; int res = 0; if (f->frametype == AST_FRAME_VOICE) { if (!(f->subclass & ast->nativeformats)) { ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native " "formats is %d (read/write = %d/%d)\n", f->subclass, ast->nativeformats, ast->readformat, ast->writeformat); return 0; } if (p) { ast_mutex_lock(&p->lock); if (p->rtp) res = ast_rtp_write(p->rtp, f); ast_mutex_unlock(&p->lock); } } else if (f->frametype == AST_FRAME_IMAGE) { return 0; } else { ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", f->frametype); return 0; } return res; } static int ooh323_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) { struct ooh323_pvt *p = (struct ooh323_pvt *) ast->tech_pvt; char *callToken = (char *)NULL; ast_mutex_lock(&p->lock); callToken = (p->callToken ? strdup(p->callToken) : NULL); ast_mutex_unlock(&p->lock); if (!callToken) { if (gH323Debug) ast_verbose(" ooh323_indicate - No callToken\n"); return -1; } if (gH323Debug) ast_verbose("----- ooh323_indicate %d on call %s\n", condition, callToken); switch (condition) { case AST_CONTROL_CONGESTION: if (!ast_test_flag(p, H323_ALREADYGONE)) { ast_mutex_lock(&ooh323c_cmd_lock); ooHangCall(callToken, OO_REASON_LOCAL_CONGESTED); ast_mutex_unlock(&ooh323c_cmd_lock); ast_set_flag(p, H323_ALREADYGONE); } break; case AST_CONTROL_BUSY: if (!ast_test_flag(p, H323_ALREADYGONE)) { ast_mutex_lock(&ooh323c_cmd_lock); ooHangCall(callToken, OO_REASON_LOCAL_BUSY); ast_mutex_unlock(&ooh323c_cmd_lock); ast_set_flag(p, H323_ALREADYGONE); } break; case AST_CONTROL_HOLD: ast_moh_start(ast, data, NULL); break; case AST_CONTROL_UNHOLD: ast_moh_stop(ast); break; case AST_CONTROL_PROCEEDING: case AST_CONTROL_RINGING: case AST_CONTROL_PROGRESS: case -1: break; default: ast_log(LOG_WARNING, "Don't know how to indicate condition %d on %s\n", condition, callToken); } if (gH323Debug) ast_verbose("++++ ooh323_indicate %d on %s\n", condition, callToken); return -1; } static int ooh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct ooh323_pvt *p = newchan->tech_pvt; if (gH323Debug) ast_verbose("--- ooh323c ooh323_fixup\n"); ast_mutex_lock(&p->lock); if (p->owner != oldchan) { ast_log(LOG_WARNING, "Old channel wasn't %p but was %p\n", oldchan, p->owner); ast_mutex_unlock(&p->lock); return -1; } if (p->owner == oldchan) { p->owner = newchan; } else { p->owner = oldchan; } ast_mutex_unlock(&p->lock); if (gH323Debug) ast_verbose("+++ ooh323c ooh323_fixup \n"); return 0; } void ooh323_set_write_format(ooCallData *call, int fmt) { #if 0 struct ooh323_pvt *p = NULL; char formats[512]; #ifdef print_debug printf("--- ooh323_update_writeformat %s\n", ast_getformatname_multiple(formats,512, fmt)); #endif p = find_call(call); if (!p) { ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken); return; } ast_mutex_lock(&p->lock); p->writeformat = fmt; ast_mutex_unlock(&p->lock); if (p->owner) { printf("Writeformat before update %s\n", ast_getformatname_multiple(formats,512, p->owner->writeformat)); ast_set_write_format(p->owner, fmt); } else ast_log(LOG_ERROR, "No owner found\n"); #ifdef print_debug printf("+++ ooh323_update_writeformat\n"); #endif #endif } void ooh323_set_read_format(ooCallData *call, int fmt) { #if 0 struct ooh323_pvt *p = NULL; char formats[512]; #ifdef print_debug printf("--- ooh323_update_readformat %s\n", ast_getformatname_multiple(formats,512, fmt)); #endif p = find_call(call); if (!p) { ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken); return; } ast_mutex_lock(&p->lock); p->readformat = fmt; ast_mutex_unlock(&p->lock); ast_set_read_format(p->owner, fmt); #ifdef print_debug printf("+++ ooh323_update_readformat\n"); #endif #endif } int onAlerting(ooCallData *call) { struct ooh323_pvt *p = NULL; struct ast_channel *c = NULL; if (gH323Debug) ast_verbose("--- onAlerting %s\n", call->callToken); if (!(p = find_call(call))) { ast_log(LOG_ERROR, "No matching call found\n"); return -1; } ast_mutex_lock(&p->lock); if (!ast_test_flag(p, H323_OUTGOING)) { if (!(c = ooh323_new(p, AST_STATE_RING, p->username))) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Could not create ast_channel\n"); return -1; } ast_mutex_unlock(&p->lock); } else { if (!p->owner) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Channel has no owner\n"); return 0; } c = p->owner; ast_mutex_unlock(&p->lock); ast_channel_lock(c); ast_setstate(c, AST_STATE_RINGING); ast_channel_unlock(c); ast_queue_control(c, AST_CONTROL_RINGING); } if (gH323Debug) ast_verbose("+++ onAlerting %s\n", call->callToken); return OO_OK; } /** * Callback for sending digits from H.323 up to asterisk * */ int ooh323_onReceivedDigit(OOH323CallData *call, const char *digit) { struct ooh323_pvt *p = NULL; struct ast_frame f; int res; ast_debug(1, "Received Digit: %c\n", digit[0]); p = find_call(call); if (!p) { ast_log(LOG_ERROR, "Failed to find a matching call.\n"); return -1; } if (!p->owner) { ast_log(LOG_ERROR, "Channel has no owner\n"); return -1; } ast_mutex_lock(&p->lock); memset(&f, 0, sizeof(f)); f.frametype = AST_FRAME_DTMF; f.subclass = digit[0]; f.datalen = 0; f.samples = 800; f.offset = 0; f.data.ptr = NULL; f.mallocd = 0; f.src = "SEND_DIGIT"; ast_mutex_unlock(&p->lock); res = ast_queue_frame(p->owner, &f); return res; } int ooh323_onReceivedSetup(ooCallData *call, Q931Message *pmsg) { struct ooh323_pvt *p = NULL; struct ooh323_user *user = NULL; ooAliases *alias = NULL; char *at = NULL; char number [OO_MAX_NUMBER_LENGTH]; if (gH323Debug) ast_verbose("--- ooh323_onReceivedSetup %s\n", call->callToken); if (!(p = ooh323_alloc(call->callReference, call->callToken))) { ast_log(LOG_ERROR, "Failed to create a new call.\n"); return -1; } ast_mutex_lock(&p->lock); ast_clear_flag(p, H323_OUTGOING); if (call->remoteDisplayName) { p->callerid_name = strdup(call->remoteDisplayName); } if (ooCallGetCallingPartyNumber(call, number, OO_MAX_NUMBER_LENGTH) == OO_OK) { p->callerid_num = strdup(number); } if (call->remoteAliases) { for (alias = call->remoteAliases; alias; alias = alias->next) { if (alias->type == T_H225AliasAddress_h323_ID) { if (!p->callerid_name) { p->callerid_name = strdup(alias->value); } ast_copy_string(p->caller_h323id, alias->value, sizeof(p->caller_h323id)); } else if (alias->type == T_H225AliasAddress_dialedDigits) { if (!p->callerid_num) { p->callerid_num = strdup(alias->value); } ast_copy_string(p->caller_dialedDigits, alias->value, sizeof(p->caller_dialedDigits)); } else if (alias->type == T_H225AliasAddress_email_ID) { ast_copy_string(p->caller_email, alias->value, sizeof(p->caller_email)); } else if (alias->type == T_H225AliasAddress_url_ID) { ast_copy_string(p->caller_url, alias->value, sizeof(p->caller_url)); } } } number[0] = '\0'; if (ooCallGetCalledPartyNumber(call, number, OO_MAX_NUMBER_LENGTH) == OO_OK) { ast_copy_string(p->exten, number, sizeof(p->exten)); } else { update_our_aliases(call, p); if (!ast_strlen_zero(p->callee_dialedDigits)) { ast_copy_string(p->exten, p->callee_dialedDigits, sizeof(p->exten)); } else if (!ast_strlen_zero(p->callee_h323id)) { ast_copy_string(p->exten, p->callee_h323id, sizeof(p->exten)); } else if (!ast_strlen_zero(p->callee_email)) { ast_copy_string(p->exten, p->callee_email, sizeof(p->exten)); if ((at = strchr(p->exten, '@'))) { *at = '\0'; } } } /* if no extension found, set to default 's' */ if (ast_strlen_zero(p->exten)) { ast_copy_string(p->exten, "s", sizeof(p->exten)); } if (!p->callerid_name) { p->callerid_name = strdup(call->remoteIP); } if (p->callerid_name) { if ((user = find_user(p->callerid_name, call->remoteIP))) { ast_mutex_lock(&user->lock); p->username = strdup(user->name); ast_copy_string(p->context, user->context, sizeof(p->context)); ast_copy_string(p->accountcode, user->accountcode, sizeof(p->accountcode)); p->amaflags = user->amaflags; p->capability = user->capability; memcpy(&p->prefs, &user->prefs, sizeof(struct ast_codec_pref)); p->dtmfmode = user->dtmfmode; /* Since, call is coming from a pbx user, no need to use gk */ OO_SETFLAG(p->flags, H323_DISABLEGK); OO_SETFLAG(call->flags, OO_M_DISABLEGK); ast_mutex_unlock(&user->lock); } } ooh323c_set_capability_for_call(call, &p->prefs, p->capability, p->dtmfmode); configure_local_rtp(p, call); ast_mutex_unlock(&p->lock); if (gH323Debug) ast_verbose("+++ ooh323_onReceivedSetup - Determined context %s, " "extension %s\n", p->context, p->exten); return OO_OK; } int onNewCallCreated(ooCallData *call) { struct ooh323_pvt *p = NULL; int i = 0; if (gH323Debug) ast_verbose("--- onNewCallCreated %s\n", call->callToken); if (!strcmp(call->callType, "outgoing")) { p = find_call(call); if (!p) { ast_log(LOG_ERROR, "No matching call found for outgoing call\n"); return -1; } ast_mutex_lock(&p->lock); if (p->callerid_name) { ooCallSetCallerId(call, p->callerid_name); } if (p->callerid_num) { i = 0; while (*(p->callerid_num + i) != '\0') { if (!isdigit(*(p->callerid_num + i))) { break; } i++; } if (*(p->callerid_num + i) == '\0') { ooCallSetCallingPartyNumber(call, p->callerid_num); } else { if (!p->callerid_name) { ooCallSetCallerId(call, p->callerid_num); } } } if (!ast_strlen_zero(p->caller_h323id)) ooCallAddAliasH323ID(call, p->caller_h323id); if (!ast_strlen_zero(p->caller_dialedDigits)) { if (gH323Debug) { ast_verbose("Setting dialed digits %s\n", p->caller_dialedDigits); } ooCallAddAliasDialedDigits(call, p->caller_dialedDigits); } else if (p->callerid_num) { if (ooIsDailedDigit(p->callerid_num)) { if (gH323Debug) { ast_verbose("setting callid number %s\n", p->callerid_num); } ooCallAddAliasDialedDigits(call, p->callerid_num); } else if (ast_strlen_zero(p->caller_h323id)) { ooCallAddAliasH323ID(call, p->callerid_num); } } if (!ast_strlen_zero(p->exten)) { if (ooIsDailedDigit(p->exten)) { ooCallSetCalledPartyNumber(call, p->exten); ooCallAddRemoteAliasDialedDigits(call, p->exten); } else { ooCallAddRemoteAliasH323ID(call, p->exten); } } if (gH323Debug) { char prefsBuf[256]; ast_codec_pref_string(&p->prefs, prefsBuf, sizeof(prefsBuf)); ast_verbose(" Outgoing call %s(%s) - Codec prefs - %s\n", p->username?p->username:"NULL", call->callToken, prefsBuf); } ooh323c_set_capability_for_call(call, &p->prefs, p->capability, p->dtmfmode); configure_local_rtp(p, call); ast_mutex_unlock(&p->lock); } if (gH323Debug) ast_verbose("+++ onNewCallCreated %s\n", call->callToken); return OO_OK; } int onCallEstablished(ooCallData *call) { struct ooh323_pvt *p = NULL; if (gH323Debug) ast_verbose("--- onCallEstablished %s\n", call->callToken); if (!(p = find_call(call))) { ast_log(LOG_ERROR, "Failed to find a matching call.\n"); return -1; } ast_mutex_lock(&p->lock); if (!p->owner) { ast_mutex_unlock(&p->lock); ast_log(LOG_ERROR, "Channel has no owner\n"); return -1; } while (ast_channel_trylock(p->owner)) { ast_debug(1,"Failed to grab lock, trying again\n"); ast_mutex_unlock(&p->lock); usleep(1); ast_mutex_lock(&p->lock); } if (p->owner->_state != AST_STATE_UP) { ast_setstate(p->owner, AST_STATE_UP); } ast_channel_unlock(p->owner); if (ast_test_flag(p, H323_OUTGOING)) { struct ast_channel* c = p->owner; ast_mutex_unlock(&p->lock); ast_queue_control(c, AST_CONTROL_ANSWER); } else { ast_mutex_unlock(&p->lock); } if (gH323Debug) ast_verbose("+++ onCallEstablished %s\n", call->callToken); return OO_OK; } int onCallCleared(ooCallData *call) { struct ooh323_pvt *p = NULL; int ownerLock = 0; if (gH323Debug) ast_verbose("--- onCallCleared %s \n", call->callToken); p = find_call(call); if (!p) { return 0; } ast_mutex_lock(&p->lock); while (p->owner) { if (ast_channel_trylock(p->owner)) { ooTrace(OOTRCLVLINFO, "Failed to grab lock, trying again\n"); ast_debug(1,"Failed to grab lock, trying again\n"); ast_mutex_unlock(&p->lock); usleep(1); ast_mutex_lock(&p->lock); } else { ownerLock = 1; break; } } if (ownerLock) { if (!ast_test_flag(p, H323_ALREADYGONE)) { /* NOTE: Channel is not detached yet */ ast_set_flag(p, H323_ALREADYGONE); p->owner->hangupcause = ooh323_convert_hangupcause_h323ToAsterisk(call->callEndReason); p->owner->_softhangup |= AST_SOFTHANGUP_DEV; ast_channel_unlock(p->owner); ast_queue_hangup(p->owner); ast_mutex_unlock(&p->lock); return OO_OK; } ast_channel_unlock(p->owner); } ast_set_flag(p, H323_NEEDDESTROY); ast_mutex_unlock(&p->lock); if (gH323Debug) ast_verbose("+++ onCallCleared\n"); return OO_OK; } #if 0 static void ooh323_delete_user(struct ooh323_user *user) { struct ooh323_user *prev = NULL, *cur = NULL; if (gH323Debug) ast_verbose("--- ooh323_delete_user\n"); if (user) { cur = userl.users; ast_mutex_lock(&userl.lock); while (cur) { if (cur == user) break; prev = cur; cur = cur->next; } if (cur) { if (prev) prev->next = cur->next; else userl.users = cur->next; } ast_mutex_unlock(&userl.lock); free(user); } if (gH323Debug) ast_verbose("+++ ooh323_delete_user\n"); } #endif void ooh323_delete_peer(struct ooh323_peer *peer) { struct ooh323_peer *prev = NULL, *cur = NULL; if (gH323Debug) ast_verbose("--- ooh323_delete_peer\n"); if (peer) { ast_mutex_lock(&peerl.lock); for (cur = peerl.peers; cur; prev = cur, cur = cur->next) { if (cur == peer) { break; } } if (cur) { if (prev) { prev->next = cur->next; } else { peerl.peers = cur->next; } } ast_mutex_unlock(&peerl.lock); if (peer->h323id) free(peer->h323id); if (peer->email) free(peer->email); if (peer->url) free(peer->url); if (peer->e164) free(peer->e164); free(peer); } if (gH323Debug) ast_verbose("+++ ooh323_delete_peer\n"); } static struct ooh323_user *build_user(const char *name, struct ast_variable *v) { struct ooh323_user *user = NULL; if (gH323Debug) ast_verbose("--- build_user\n"); user = ast_calloc(1, sizeof(*user)); if (user) { ast_mutex_init(&user->lock); ast_copy_string(user->name, name, sizeof(user->name)); user->capability = gCapability; memcpy(&user->prefs, &gPrefs, sizeof(user->prefs)); user->rtptimeout = gRTPTimeout; user->dtmfmode = gDTMFMode; /* set default context */ ast_copy_string(user->context, gContext, sizeof(user->context)); ast_copy_string(user->accountcode, gAccountcode, sizeof(user->accountcode)); user->amaflags = gAMAFLAGS; while (v) { if (!strcasecmp(v->name, "context")) { ast_copy_string(user->context, v->value, sizeof(user->context)); } else if (!strcasecmp(v->name, "incominglimit")) { user->incominglimit = atoi(v->value); if (user->incominglimit < 0) user->incominglimit = 0; } else if (!strcasecmp(v->name, "accountcode")) { ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode)); } else if (!strcasecmp(v->name, "rtptimeout")) { user->rtptimeout = atoi(v->value); if (user->rtptimeout < 0) user->rtptimeout = gRTPTimeout; } else if (!strcasecmp(v->name, "disallow")) { ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 0); } else if (!strcasecmp(v->name, "allow")) { const char* tcodecs = v->value; if (!strcasecmp(v->value, "all")) { tcodecs = "ulaw,alaw,g729,g723,gsm"; } ast_parse_allow_disallow(&user->prefs, &user->capability, tcodecs, 1); } else if (!strcasecmp(v->name, "amaflags")) { user->amaflags = ast_cdr_amaflags2int(v->value); } else if (!strcasecmp(v->name, "ip")) { ast_copy_string(user->mIP, v->value, sizeof(user->mIP)); user->mUseIP = 1; } else if (!strcasecmp(v->name, "dtmfmode")) { if (!strcasecmp(v->value, "rfc2833")) user->dtmfmode = H323_DTMF_RFC2833; else if (!strcasecmp(v->value, "q931keypad")) user->dtmfmode = H323_DTMF_Q931; else if (!strcasecmp(v->value, "h245alphanumeric")) user->dtmfmode = H323_DTMF_H245ALPHANUMERIC; else if (!strcasecmp(v->value, "h245signal")) user->dtmfmode = H323_DTMF_H245SIGNAL; } v = v->next; } } if (gH323Debug) ast_verbose("+++ build_user\n"); return user; } static struct ooh323_peer *build_peer(const char *name, struct ast_variable *v, int friend_type) { struct ooh323_peer *peer = NULL; if (gH323Debug) ast_verbose("--- build_peer\n"); peer = ast_calloc(1, sizeof(*peer)); if (peer) { memset(peer, 0, sizeof(struct ooh323_peer)); ast_mutex_init(&peer->lock); ast_copy_string(peer->name, name, sizeof(peer->name)); peer->capability = gCapability; memcpy(&peer->prefs, &gPrefs, sizeof(struct ast_codec_pref)); peer->rtptimeout = gRTPTimeout; ast_copy_string(peer->accountcode, gAccountcode, sizeof(peer->accountcode)); peer->amaflags = gAMAFLAGS; peer->dtmfmode = gDTMFMode; if (0 == friend_type) { peer->mFriend = 1; } while (v) { if (!strcasecmp(v->name, "h323id")) { if (!(peer->h323id = ast_strdup(v->value))) { ast_log(LOG_ERROR, "Could not allocate memory for h323id of " "peer %s\n", name); ooh323_delete_peer(peer); return NULL; } } else if (!strcasecmp(v->name, "e164")) { if (!(peer->e164 = ast_strdup(v->value))) { ast_log(LOG_ERROR, "Could not allocate memory for e164 of " "peer %s\n", name); ooh323_delete_peer(peer); return NULL; } } else if (!strcasecmp(v->name, "email")) { if (!(peer->email = ast_strdup(v->value))) { ast_log(LOG_ERROR, "Could not allocate memory for email of " "peer %s\n", name); ooh323_delete_peer(peer); return NULL; } } else if (!strcasecmp(v->name, "url")) { if (!(peer->url = ast_strdup(v->value))) { ast_log(LOG_ERROR, "Could not allocate memory for h323id of " "peer %s\n", name); ooh323_delete_peer(peer); return NULL; } } else if (!strcasecmp(v->name, "port")) { peer->port = atoi(v->value); } else if (!strcasecmp(v->name, "ip")) { ast_copy_string(peer->ip, v->value, sizeof(peer->ip)); } else if (!strcasecmp(v->name, "outgoinglimit")) { if ((peer->outgoinglimit = atoi(v->value)) < 0) { peer->outgoinglimit = 0; } } else if (!strcasecmp(v->name, "accountcode")) { ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode)); } else if (!strcasecmp(v->name, "rtptimeout")) { if ((peer->rtptimeout = atoi(v->value)) < 0) { peer->rtptimeout = gRTPTimeout; } } else if (!strcasecmp(v->name, "disallow")) { ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0); } else if (!strcasecmp(v->name, "allow")) { const char* tcodecs = v->value; if (!strcasecmp(v->value, "all")) { tcodecs = "ulaw,alaw,g729,g723,gsm"; } ast_parse_allow_disallow(&peer->prefs, &peer->capability, tcodecs, 1); } else if (!strcasecmp(v->name, "amaflags")) { peer->amaflags = ast_cdr_amaflags2int(v->value); } else if (!strcasecmp(v->name, "dtmfmode")) { if (!strcasecmp(v->value, "rfc2833")) peer->dtmfmode = H323_DTMF_RFC2833; else if (!strcasecmp(v->value, "q931keypad")) peer->dtmfmode = H323_DTMF_Q931; else if (!strcasecmp(v->value, "h245alphanumeric")) peer->dtmfmode = H323_DTMF_H245ALPHANUMERIC; else if (!strcasecmp(v->value, "h245signal")) peer->dtmfmode = H323_DTMF_H245SIGNAL; } v = v->next; } } if (gH323Debug) ast_verbose("+++ build_peer\n"); return peer; } static int ooh323_do_reload(void) { if (gH323Debug) { ast_verbose("--- ooh323_do_reload\n"); } reload_config(1); if (gH323Debug) { ast_verbose("+++ ooh323_do_reload\n"); } return 0; } #if 0 /*--- h323_reload: Force reload of module from cli ---*/ static int ooh323_reload(int fd, int argc, char *argv[]) { if (gH323Debug) ast_verbose("--- ooh323_reload\n"); ast_mutex_lock(&h323_reload_lock); if (h323_reloading) { ast_verbose("Previous OOH323 reload not yet done\n"); } else { h323_reloading = 1; } ast_mutex_unlock(&h323_reload_lock); restart_monitor(); if (gH323Debug) ast_verbose("+++ ooh323_reload\n"); return 0; } #endif #if 0 static int reload(void *mod) { return ooh323_reload(0, 0, NULL); } #endif int reload_config(int reload) { int format; struct ooAliases *pNewAlias = NULL; struct ast_config *cfg; struct ast_variable *v; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; struct ooh323_user *user = NULL; struct ooh323_peer *peer = NULL; char *cat; const char *utype; if (gH323Debug) ast_verbose("--- reload_config\n"); cfg = ast_config_load((char*)config, config_flags); /* We *must* have a config file otherwise stop immediately */ if (!cfg) { ast_log(LOG_NOTICE, "Unable to load config %s, OOH323 disabled\n", config); return 1; } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) return RESULT_SUCCESS; if (reload) { delete_users(); delete_peers(); } /* Inintialize everything to default */ strcpy(gLogFile, DEFAULT_LOGFILE); gPort = 1720; gIP[0] = '\0'; strcpy(gCallerID, DEFAULT_H323ID); gCapability = AST_FORMAT_ULAW; memset(&gPrefs, 0, sizeof(struct ast_codec_pref)); gDTMFMode = H323_DTMF_RFC2833; gRasGkMode = RasNoGatekeeper; gGatekeeper[0] = '\0'; gRTPTimeout = 60; strcpy(gAccountcode, DEFAULT_H323ACCNT); gFastStart = 1; gTunneling = 1; gTOS = 0; strcpy(gContext, DEFAULT_CONTEXT); gAliasList = NULL; gMediaWaitForConnect = 0; ooconfig.mTCPPortStart = 12030; ooconfig.mTCPPortEnd = 12230; v = ast_variable_browse(cfg, "general"); while (v) { if (!strcasecmp(v->name, "port")) { gPort = (int)strtol(v->value, NULL, 10); } else if (!strcasecmp(v->name, "bindaddr")) { ast_copy_string(gIP, v->value, sizeof(gIP)); } else if (!strcasecmp(v->name, "h225portrange")) { char* endlimit = 0; char temp[256]; ast_copy_string(temp, v->value, sizeof(temp)); endlimit = strchr(temp, ','); if (endlimit) { *endlimit = '\0'; endlimit++; ooconfig.mTCPPortStart = atoi(temp); ooconfig.mTCPPortEnd = atoi(endlimit); if (ooH323EpSetTCPPortRange(ooconfig.mTCPPortStart, ooconfig.mTCPPortEnd) == OO_FAILED) { ast_log(LOG_ERROR, "h225portrange: Failed to set range\n"); } } else { ast_log(LOG_ERROR, "h225portrange: Invalid format, separate port range with \",\"\n"); } } else if (!strcasecmp(v->name, "gateway")) { gIsGateway = ast_true(v->value); } else if (!strcasecmp(v->name, "faststart")) { gFastStart = ast_true(v->value); if (gFastStart) ooH323EpEnableFastStart(); else ooH323EpDisableFastStart(); } else if (!strcasecmp(v->name, "mediawaitforconnect")) { gMediaWaitForConnect = ast_true(v->value); if (gMediaWaitForConnect) ooH323EpEnableMediaWaitForConnect(); else ooH323EpDisableMediaWaitForConnect(); } else if (!strcasecmp(v->name, "h245tunneling")) { gTunneling = ast_true(v->value); if (gTunneling) ooH323EpEnableH245Tunneling(); else ooH323EpDisableH245Tunneling(); } else if (!strcasecmp(v->name, "h323id")) { pNewAlias = malloc(sizeof(*pNewAlias)); if (!pNewAlias) { ast_log(LOG_ERROR, "Failed to allocate memory for h323id alias\n"); return 1; } pNewAlias->type = T_H225AliasAddress_h323_ID; pNewAlias->value = strdup(v->value); pNewAlias->next = gAliasList; gAliasList = pNewAlias; pNewAlias = NULL; } else if (!strcasecmp(v->name, "e164")) { pNewAlias = malloc(sizeof(*pNewAlias)); if (!pNewAlias) { ast_log(LOG_ERROR, "Failed to allocate memory for e164 alias\n"); return 1; } pNewAlias->type = T_H225AliasAddress_dialedDigits; pNewAlias->value = strdup(v->value); pNewAlias->next = gAliasList; gAliasList = pNewAlias; pNewAlias = NULL; } else if (!strcasecmp(v->name, "email")) { pNewAlias = malloc(sizeof(*pNewAlias)); if (!pNewAlias) { ast_log(LOG_ERROR, "Failed to allocate memory for email alias\n"); return 1; } pNewAlias->type = T_H225AliasAddress_email_ID; pNewAlias->value = strdup(v->value); pNewAlias->next = gAliasList; gAliasList = pNewAlias; pNewAlias = NULL; } else if (!strcasecmp(v->name, "callerid")) { ast_copy_string(gCallerID, v->value, sizeof(gCallerID)); } else if (!strcasecmp(v->name, "incominglimit")) { gIncomingLimit = atoi(v->value); } else if (!strcasecmp(v->name, "outgoinglimit")) { gOutgoingLimit = atoi(v->value); } else if (!strcasecmp(v->name, "gatekeeper")) { if (!strcasecmp(v->value, "DISABLE")) { gRasGkMode = RasNoGatekeeper; } else if (!strcasecmp(v->value, "DISCOVER")) { gRasGkMode = RasDiscoverGatekeeper; } else { gRasGkMode = RasUseSpecificGatekeeper; ast_copy_string(gGatekeeper, v->value, sizeof(gGatekeeper)); } } else if (!strcasecmp(v->name, "logfile")) { ast_copy_string(gLogFile, v->value, sizeof(gLogFile)); } else if (!strcasecmp(v->name, "context")) { ast_copy_string(gContext, v->value, sizeof(gContext)); ast_verb(3, " == Setting default context to %s\n", gContext); } else if (!strcasecmp(v->name, "rtptimeout")) { gRTPTimeout = atoi(v->value); if (gRTPTimeout <= 0) gRTPTimeout = 60; } else if (!strcasecmp(v->name, "tos")) { if (sscanf(v->value, "%i", &format) == 1) gTOS = format & 0xff; else if (!strcasecmp(v->value, "lowdelay")) gTOS = IPTOS_LOWDELAY; else if (!strcasecmp(v->value, "throughput")) gTOS = IPTOS_THROUGHPUT; else if (!strcasecmp(v->value, "reliability")) gTOS = IPTOS_RELIABILITY; else if (!strcasecmp(v->value, "mincost")) gTOS = IPTOS_MINCOST; else if (!strcasecmp(v->value, "none")) gTOS = 0; else ast_log(LOG_WARNING, "Invalid tos value at line %d, should be " "'lowdelay', 'throughput', 'reliability', " "'mincost', or 'none'\n", v->lineno); } else if (!strcasecmp(v->name, "amaflags")) { gAMAFLAGS = ast_cdr_amaflags2int(v->value); } else if (!strcasecmp(v->name, "accountcode")) { ast_copy_string(gAccountcode, v->value, sizeof(gAccountcode)-1); } else if (!strcasecmp(v->name, "disallow")) { ast_parse_allow_disallow(&gPrefs, &gCapability, v->value, 0); } else if (!strcasecmp(v->name, "allow")) { const char* tcodecs = v->value; if (!strcasecmp(v->value, "all")) { tcodecs = "ulaw,alaw,g729,g723,gsm"; } ast_parse_allow_disallow(&gPrefs, &gCapability, tcodecs, 1); } else if (!strcasecmp(v->name, "dtmfmode")) { if (!strcasecmp(v->value, "inband")) gDTMFMode = H323_DTMF_INBAND; else if (!strcasecmp(v->value, "rfc2833")) gDTMFMode = H323_DTMF_RFC2833; else if (!strcasecmp(v->value, "q931keypad")) gDTMFMode = H323_DTMF_Q931; else if (!strcasecmp(v->value, "h245alphanumeric")) gDTMFMode = H323_DTMF_H245ALPHANUMERIC; else if (!strcasecmp(v->value, "h245signal")) gDTMFMode = H323_DTMF_H245SIGNAL; else { ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", v->value); gDTMFMode = H323_DTMF_RFC2833; } } v = v->next; } for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) { if (strcasecmp(cat, "general")) { int friend_type = 0; utype = ast_variable_retrieve(cfg, cat, "type"); if (utype) { friend_type = strcasecmp(utype, "friend"); if (!strcmp(utype, "user") || 0 == friend_type) { user = build_user(cat, ast_variable_browse(cfg, cat)); if (user) { ast_mutex_lock(&userl.lock); user->next = userl.users; userl.users = user; ast_mutex_unlock(&userl.lock); } else { ast_log(LOG_WARNING, "Failed to build user %s\n", cat); } } if (!strcasecmp(utype, "peer") || 0 == friend_type) { peer = build_peer(cat, ast_variable_browse(cfg, cat), friend_type); if (peer) { ast_mutex_lock(&peerl.lock); peer->next = peerl.peers; peerl.peers = peer; ast_mutex_unlock(&peerl.lock); } else { ast_log(LOG_WARNING, "Failed to build peer %s\n", cat); } } } } } ast_config_destroy(cfg); /* Determine ip address if neccessary */ if (ast_strlen_zero(gIP)) { ooGetLocalIPAddress(gIP); if (!strcmp(gIP, "127.0.0.1")) { ast_log(LOG_NOTICE, "Failed to determine local ip address. Please " "specify it in ooh323.conf. OOH323 Disabled\n"); return 1; } } if (gH323Debug) ast_verbose("+++ reload_config\n"); return 0; } static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char ip_port[30]; struct ooh323_peer *prev = NULL, *peer = NULL; switch (cmd) { case CLI_INIT: e->command = "ooh323 show peer"; e->usage = "Usage: ooh323 show peer \n" " List details of specific OOH323 peer.\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 4) return CLI_SHOWUSAGE; ast_mutex_lock(&peerl.lock); peer = peerl.peers; while (peer) { ast_mutex_lock(&peer->lock); if (!strcmp(peer->name, a->argv[3])) break; else { prev = peer; peer = peer->next; ast_mutex_unlock(&prev->lock); } } if (peer) { snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port); ast_cli(a->fd, "%-15.15s%s\n", "Name: ", peer->name); ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); print_codec_to_cli(a->fd, &peer->prefs); ast_cli(a->fd, ")\n"); ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); if (peer->dtmfmode & H323_DTMF_RFC2833) ast_cli(a->fd, "%s\n", "rfc2833"); else if (peer->dtmfmode & H323_DTMF_Q931) ast_cli(a->fd, "%s\n", "q931keypad"); else if (peer->dtmfmode & H323_DTMF_H245ALPHANUMERIC) ast_cli(a->fd, "%s\n", "h245alphanumeric"); else if (peer->dtmfmode & H323_DTMF_H245SIGNAL) ast_cli(a->fd, "%s\n", "h245signal"); else ast_cli(a->fd, "%s\n", "unknown"); ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode); ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(peer->amaflags)); ast_cli(a->fd, "%-15.15s%s\n", "Ip:Port: ", ip_port); ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit); ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout); ast_mutex_unlock(&peer->lock); } else { ast_cli(a->fd, "Peer %s not found\n", a->argv[3]); ast_cli(a->fd, "\n"); } ast_mutex_unlock(&peerl.lock); return CLI_SUCCESS; } static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char ip_port[30]; char formats[512]; struct ooh323_peer *prev = NULL, *peer = NULL; #define FORMAT "%-15.15s %-15.15s %-23.23s %-s\n" switch (cmd) { case CLI_INIT: e->command = "ooh323 show peers"; e->usage = "Usage: ooh323 show peers\n" " Lists all known OOH323 peers.\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) return CLI_SHOWUSAGE; ast_cli(a->fd, FORMAT, "Name", "Accountcode", "ip:port", "Formats"); ast_mutex_lock(&peerl.lock); peer = peerl.peers; while (peer) { ast_mutex_lock(&peer->lock); snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port); ast_cli(a->fd, FORMAT, peer->name, peer->accountcode, ip_port, ast_getformatname_multiple(formats, sizeof(formats), peer->capability)); prev = peer; peer = peer->next; ast_mutex_unlock(&prev->lock); } ast_mutex_unlock(&peerl.lock); #undef FORMAT return CLI_SUCCESS; } /*! \brief Print codec list from preference to CLI/manager */ static void print_codec_to_cli(int fd, struct ast_codec_pref *pref) { int x, codec; for (x = 0; x < 32; x++) { codec = ast_codec_pref_index(pref, x); if (!codec) break; ast_cli(fd, "%s", ast_getformatname(codec)); ast_cli(fd, ":%d", pref->framing[x]); if (x < 31 && ast_codec_pref_index(pref, x + 1)) ast_cli(fd, ","); } if (!x) ast_cli(fd, "none"); } static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ooh323_user *prev = NULL, *user = NULL; switch (cmd) { case CLI_INIT: e->command = "ooh323 show user"; e->usage = "Usage: ooh323 show user \n" " List details of specific OOH323 user.\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 4) return CLI_SHOWUSAGE; ast_mutex_lock(&userl.lock); user = userl.users; while (user) { ast_mutex_lock(&user->lock); if (!strcmp(user->name, a->argv[3])) break; else { prev = user; user = user->next; ast_mutex_unlock(&prev->lock); } } if (user) { ast_cli(a->fd, "%-15.15s%s\n", "Name: ", user->name); ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "("); print_codec_to_cli(a->fd, &user->prefs); ast_cli(a->fd, ")\n"); ast_cli(a->fd, "%-15.15s", "DTMF Mode: "); if (user->dtmfmode & H323_DTMF_RFC2833) ast_cli(a->fd, "%s\n", "rfc2833"); else if (user->dtmfmode & H323_DTMF_Q931) ast_cli(a->fd, "%s\n", "q931keypad"); else if (user->dtmfmode & H323_DTMF_H245ALPHANUMERIC) ast_cli(a->fd, "%s\n", "h245alphanumeric"); else if (user->dtmfmode & H323_DTMF_H245SIGNAL) ast_cli(a->fd, "%s\n", "h245signal"); else ast_cli(a->fd, "%s\n", "unknown"); ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode); ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(user->amaflags)); ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context); ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit); ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", user->rtptimeout); ast_mutex_unlock(&user->lock); } else { ast_cli(a->fd, "User %s not found\n", a->argv[3]); ast_cli(a->fd, "\n"); } ast_mutex_unlock(&userl.lock); return CLI_SUCCESS; } static char *handle_cli_ooh323_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char formats[512]; struct ooh323_user *prev = NULL, *user = NULL; #define FORMAT1 "%-15.15s %-15.15s %-15.15s %-s\n" switch (cmd) { case CLI_INIT: e->command = "ooh323 show users"; e->usage = "Usage: ooh323 show users \n" " Lists all known OOH323 users.\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) return CLI_SHOWUSAGE; ast_cli(a->fd, FORMAT1, "Username", "Accountcode", "Context", "Formats"); ast_mutex_lock(&userl.lock); user = userl.users; while (user) { ast_mutex_lock(&user->lock); ast_cli(a->fd, FORMAT1, user->name, user->accountcode, user->context, ast_getformatname_multiple(formats, 512, user->capability)); prev = user; user = user->next; ast_mutex_unlock(&prev->lock); } ast_mutex_unlock(&userl.lock); #undef FORMAT1 return CLI_SUCCESS; } static char *handle_cli_ooh323_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { switch (cmd) { case CLI_INIT: e->command = "ooh323 set debug [off]"; e->usage = "Usage: ooh323 set debug [off]\n" " Enables/Disables debugging of OOH323 channel driver\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc < 3 || a->argc > 4) return CLI_SHOWUSAGE; if (a->argc == 4 && strcasecmp(a->argv[3], "off")) return CLI_SHOWUSAGE; gH323Debug = (a->argc == 4) ? FALSE : TRUE; ast_cli(a->fd, "OOH323 Debugging %s\n", gH323Debug ? "Enabled" : "Disabled"); return CLI_SUCCESS; } #if 0 static int ooh323_show_channels(int fd, int argc, char *argv[]) { return RESULT_SUCCESS; } #endif static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { char value[512]; ooAliases *pAlias = NULL, *pAliasNext = NULL;; switch (cmd) { case CLI_INIT: e->command = "ooh323 show config"; e->usage = "Usage: ooh323 show config\n" " Shows global configuration of H.323 channel driver\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) return CLI_SHOWUSAGE; snprintf(value, sizeof(value), "%s:%d", gIP, gPort); ast_cli(a->fd, "\nObjective Open H.323 Channel Driver's Config:\n"); ast_cli(a->fd, "%-20s%s\n", "IP:Port: ", value); ast_cli(a->fd, "%-20s%s\n", "FastStart", gFastStart?"yes":"no"); ast_cli(a->fd, "%-20s%s\n", "Tunneling", gTunneling?"yes":"no"); ast_cli(a->fd, "%-20s%s\n", "CallerId", gCallerID); ast_cli(a->fd, "%-20s%s\n", "MediaWaitForConnect", gMediaWaitForConnect ? "yes" : "no"); #if 0 { extern OOH323EndPoint gH323ep; ast_cli(a->fd, "%-20s%s\n", "FASTSTART", (OO_TESTFLAG(gH323ep.flags, OO_M_FASTSTART) != 0) ? "yes" : "no"); ast_cli(a->fd, "%-20s%s\n", "TUNNELING", (OO_TESTFLAG(gH323ep.flags, OO_M_TUNNELING) != 0) ? "yes" : "no"); ast_cli(a->fd, "%-20s%s\n", "MEDIAWAITFORCONN", (OO_TESTFLAG(gH323ep.flags, OO_M_MEDIAWAITFORCONN) != 0) ? "yes" : "no"); } #endif if (gRasGkMode == RasNoGatekeeper) snprintf(value, sizeof(value), "%s", "No Gatekeeper"); else if (gRasGkMode == RasDiscoverGatekeeper) snprintf(value, sizeof(value), "%s", "Discover"); else snprintf(value, sizeof(value), "%s", gGatekeeper); ast_cli(a->fd, "%-20s%s\n", "Gatekeeper:", value); ast_cli(a->fd, "%-20s%s\n", "H.323 LogFile:", gLogFile); ast_cli(a->fd, "%-20s%s\n", "Context:", gContext); ast_cli(a->fd, "%-20s%s\n", "Capability:", ast_getformatname_multiple(value, sizeof(value), gCapability)); ast_cli(a->fd, "%-20s", "DTMF Mode: "); if (gDTMFMode & H323_DTMF_RFC2833) ast_cli(a->fd, "%s\n", "rfc2833"); else if (gDTMFMode & H323_DTMF_Q931) ast_cli(a->fd, "%s\n", "q931keypad"); else if (gDTMFMode & H323_DTMF_H245ALPHANUMERIC) ast_cli(a->fd, "%s\n", "h245alphanumeric"); else if (gDTMFMode & H323_DTMF_H245SIGNAL) ast_cli(a->fd, "%s\n", "h245signal"); else ast_cli(a->fd, "%s\n", "unknown"); ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode); ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS)); pAlias = gAliasList; if (pAlias) ast_cli(a->fd, "%-20s\n", "Aliases: "); while (pAlias) { pAliasNext = pAlias->next; if (pAliasNext) { ast_cli(a->fd, "\t%-30s\t%-30s\n", pAlias->value, pAliasNext->value); pAlias = pAliasNext->next; } else { ast_cli(a->fd, "\t%-30s\n", pAlias->value); pAlias = pAlias->next; } } return CLI_SUCCESS; } static struct ast_cli_entry cli_ooh323[] = { AST_CLI_DEFINE(handle_cli_ooh323_set_debug, "Enable/Disable OOH323 debugging"), AST_CLI_DEFINE(handle_cli_ooh323_show_config, "Show details on global configuration of H.323 channel driver"), AST_CLI_DEFINE(handle_cli_ooh323_show_peer, "Show details on specific OOH323 peer"), AST_CLI_DEFINE(handle_cli_ooh323_show_peers, "Show defined OOH323 peers"), AST_CLI_DEFINE(handle_cli_ooh323_show_user, "Show details on specific OOH323 user"), AST_CLI_DEFINE(handle_cli_ooh323_show_users, "Show defined OOH323 users"), }; static int load_module(void) { int res; struct ooAliases * pNewAlias = NULL; struct ooh323_peer *peer = NULL; OOH225MsgCallbacks h225Callbacks = {0, 0, 0, 0}; OOH323CALLBACKS h323Callbacks = { .onNewCallCreated = onNewCallCreated, .onAlerting = onAlerting, .onIncomingCall = NULL, .onOutgoingCall = NULL, .onCallEstablished = onCallEstablished, .onCallCleared = onCallCleared, .openLogicalChannels = NULL, .onReceivedDTMF = &ooh323_onReceivedDigit }; ast_log(LOG_NOTICE, "---------------------------------------------------------------------------------\n" "--- ******* IMPORTANT NOTE ***********\n" "---\n" "--- This module is currently unsupported. Use it at your own risk.\n" "---\n" "---------------------------------------------------------------------------------\n"); h225Callbacks.onReceivedSetup = &ooh323_onReceivedSetup; userl.users = NULL; ast_mutex_init(&userl.lock); peerl.peers = NULL; ast_mutex_init(&peerl.lock); #if 0 ast_register_atexit(&ast_ooh323c_exit); #endif if (!(sched = sched_context_create())) { ast_log(LOG_WARNING, "Unable to create schedule context\n"); } if (!(io = io_context_create())) { ast_log(LOG_WARNING, "Unable to create I/O context\n"); } if (!(res = reload_config(0))) { /* Make sure we can register our OOH323 channel type */ if (ast_channel_register(&ooh323_tech)) { ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); return 0; } ast_rtp_proto_register(&ooh323_rtp); ast_cli_register_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry)); /* fire up the H.323 Endpoint */ if (OO_OK != ooH323EpInitialize(OO_CALLMODE_AUDIOCALL, gLogFile)) { ast_log(LOG_ERROR, "Failed to initialize OOH323 endpoint-OOH323 Disabled\n"); return 1; } if (gIsGateway) ooH323EpSetAsGateway(); ooH323EpDisableAutoAnswer(); ooH323EpSetH225MsgCallbacks(h225Callbacks); ooH323EpSetTraceLevel(OOTRCLVLDBGC); ooH323EpSetLocalAddress(gIP, gPort); ooH323EpSetCallerID(gCallerID); /* Set aliases if any */ for (pNewAlias = gAliasList; pNewAlias; pNewAlias = pNewAlias->next) { switch (pNewAlias->type) { case T_H225AliasAddress_h323_ID: ooH323EpAddAliasH323ID(pNewAlias->value); break; case T_H225AliasAddress_dialedDigits: ooH323EpAddAliasDialedDigits(pNewAlias->value); break; case T_H225AliasAddress_email_ID: ooH323EpAddAliasEmailID(pNewAlias->value); break; } } ast_mutex_lock(&peerl.lock); peer = peerl.peers; while (peer) { if (peer->h323id) ooH323EpAddAliasH323ID(peer->h323id); if (peer->email) ooH323EpAddAliasEmailID(peer->email); if (peer->e164) ooH323EpAddAliasDialedDigits(peer->e164); if (peer->url) ooH323EpAddAliasURLID(peer->url); peer = peer->next; } ast_mutex_unlock(&peerl.lock); if (gMediaWaitForConnect) ooH323EpEnableMediaWaitForConnect(); else ooH323EpDisableMediaWaitForConnect(); /* Fast start and tunneling options */ if (gFastStart) ooH323EpEnableFastStart(); else ooH323EpDisableFastStart(); if (!gTunneling) ooH323EpDisableH245Tunneling(); /* Gatekeeper */ if (gRasGkMode == RasUseSpecificGatekeeper) ooGkClientInit(gRasGkMode, gGatekeeper, 0); else if (gRasGkMode == RasDiscoverGatekeeper) ooGkClientInit(gRasGkMode, 0, 0); /* Register callbacks */ ooH323EpSetH323Callbacks(h323Callbacks); /* Add endpoint capabilities */ if (ooh323c_set_capability(&gPrefs, gCapability, gDTMFMode) < 0) { ast_log(LOG_ERROR, "Capabilities failure for OOH323. OOH323 Disabled.\n"); return 1; } /* Create H.323 listener */ if (ooCreateH323Listener() != OO_OK) { ast_log(LOG_ERROR, "OOH323 Listener Creation failure. OOH323 DISABLED\n"); ooH323EpDestroy(); return 1; } if (ooh323c_start_stack_thread() < 0) { ast_log(LOG_ERROR, "Failed to start OOH323 stack thread. OOH323 DISABLED\n"); ooH323EpDestroy(); return 1; } /* And start the monitor for the first time */ restart_monitor(); } return 0; } static void *do_monitor(void *data) { int res; int reloading; struct ooh323_pvt *h323 = NULL; time_t t; for (;;) { struct ooh323_pvt *h323_next; /* 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) { ast_verb(1, "Reloading H.323\n"); ooh323_do_reload(); } /* Check for interfaces needing to be killed */ ast_mutex_lock(&iflock); time(&t); h323 = iflist; while (h323) { h323_next = h323->next; /* TODO: Need to add rtptimeout keepalive support */ if (ast_test_flag(h323, H323_NEEDDESTROY)) { ooh323_destroy (h323); } h323 = h323_next; } ast_mutex_unlock(&iflock); pthread_testcancel(); /* Wait for sched or io */ res = ast_sched_wait(sched); if ((res < 0) || (res > 1000)) { res = 1000; } res = ast_io_wait(io, res); pthread_testcancel(); ast_mutex_lock(&monlock); if (res >= 0) { ast_sched_runq(sched); } ast_mutex_unlock(&monlock); } /* Never reached */ return NULL; } int restart_monitor(void) { pthread_attr_t attr; /* If we're supposed to be stopped -- stay stopped */ if (monitor_thread == AST_PTHREADT_STOP) return 0; if (ast_mutex_lock(&monlock)) { ast_log(LOG_WARNING, "Unable to lock monitor\n"); return -1; } if (monitor_thread == pthread_self()) { ast_mutex_unlock(&monlock); ast_log(LOG_WARNING, "Cannot kill myself\n"); return -1; } if (monitor_thread != AST_PTHREADT_NULL) { /* Wake up the thread */ pthread_kill(monitor_thread, SIGURG); } 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; } } ast_mutex_unlock(&monlock); return 0; } int ooh323_destroy(struct ooh323_pvt *p) { /* NOTE: Assumes iflock already acquired */ struct ooh323_pvt *prev = NULL, *cur = NULL; if (gH323Debug) { ast_verbose("--- ooh323_destroy \n"); if (p) ast_verbose(" Destroying %s\n", p->username); } cur = iflist; while (cur) { if (cur == p) { break; } prev = cur; cur = cur->next; } if (cur) { ast_mutex_lock(&cur->lock); if (prev) prev->next = cur->next; else iflist = cur->next; if (cur->callToken) { free(cur->callToken); cur->callToken = 0; } if (cur->username) { free(cur->username); cur->username = 0; } if (cur->host) { free(cur->host); cur->host = 0; } if (cur->callerid_name) { free(cur->callerid_name); cur->callerid_name = 0; } if (cur->callerid_num) { free(cur->callerid_num); cur->callerid_num = 0; } if (cur->rtp) { ast_rtp_destroy(cur->rtp); cur->rtp = 0; } /* Unlink us from the owner if we have one */ if (cur->owner) { ast_channel_lock(cur->owner); ast_debug(1, "Detaching from %s\n", cur->owner->name); cur->owner->tech_pvt = NULL; ast_channel_unlock(cur->owner); cur->owner = NULL; } if (cur->vad) { ast_dsp_free(cur->vad); cur->vad = NULL; } ast_mutex_unlock(&cur->lock); ast_mutex_destroy(&cur->lock); free(cur); } if (gH323Debug) ast_verbose("+++ ooh323_destroy\n"); return 0; } int delete_peers() { struct ooh323_peer *cur = NULL, *prev = NULL; ast_mutex_lock(&peerl.lock); cur = peerl.peers; while (cur) { prev = cur; cur = cur->next; ast_mutex_destroy(&prev->lock); if (prev->h323id) free(prev->h323id); if (prev->email) free(prev->email); if (prev->url) free(prev->url); if (prev->e164) free(prev->e164); free(prev); if (cur == peerl.peers) { break; } } peerl.peers = NULL; ast_mutex_unlock(&peerl.lock); return 0; } int delete_users() { struct ooh323_user *cur = NULL, *prev = NULL; ast_mutex_lock(&userl.lock); cur = userl.users; while (cur) { prev = cur; cur = cur->next; ast_mutex_destroy(&prev->lock); free(prev); if (cur == userl.users) { break; } } userl.users = NULL; ast_mutex_unlock(&userl.lock); return 0; } static int unload_module(void) { struct ooh323_pvt *p; struct ooAliases *cur = NULL, *prev = NULL; if (gH323Debug) { ast_verbose("--- ooh323 unload_module \n"); } /* First, take us out of the channel loop */ ast_cli_unregister_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry)); ast_rtp_proto_unregister(&ooh323_rtp); ast_channel_unregister(&ooh323_tech); #if 0 ast_unregister_atexit(&ast_ooh323c_exit); #endif if (gH323Debug) { ast_verbose(" unload_module - hanging up all interfaces\n"); } if (!ast_mutex_lock(&iflock)) { /* 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 (gH323Debug) { ast_verbose(" unload_module - stopping monitor thread\n"); } if (monitor_thread != AST_PTHREADT_NULL) { if (!ast_mutex_lock(&monlock)) { if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) { pthread_cancel(monitor_thread); 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; } } if (gH323Debug) { ast_verbose(" unload_module - stopping stack thread\n"); } ooh323c_stop_stack_thread(); if (gH323Debug) { ast_verbose(" unload_module - freeing up memory used by interfaces\n"); } if (!ast_mutex_lock(&iflock)) { struct ooh323_pvt *pl; /* Destroy all the interfaces and free their memory */ p = iflist; while (p) { pl = p; p = p->next; /* Free associated memory */ ooh323_destroy(pl); } iflist = NULL; ast_mutex_unlock(&iflock); } else { ast_log(LOG_WARNING, "Unable to lock the interface list\n"); return -1; } if (gH323Debug) { ast_verbose(" unload_module - deleting users\n"); } delete_users(); if (gH323Debug) { ast_verbose(" unload_module - deleting peers\n"); } delete_peers(); if (gH323Debug) { ast_verbose(" unload_module - Freeing up alias list\n"); } cur = gAliasList; while (cur) { prev = cur; cur = cur->next; free(prev->value); free(prev); } gAliasList = NULL; if (gH323Debug) { ast_verbose(" unload_module- destroying OOH323 endpoint \n"); } ooH323EpDestroy(); if (gH323Debug) { ast_verbose("+++ ooh323 unload_module \n"); } return 0; } static enum ast_rtp_get_result ooh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) { struct ooh323_pvt *p = NULL; enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; if (!(p = (struct ooh323_pvt *) chan->tech_pvt)) return AST_RTP_GET_FAILED; *rtp = p->rtp; if (!(p->rtp)) { return AST_RTP_GET_FAILED; } res = AST_RTP_TRY_NATIVE; return res; } static enum ast_rtp_get_result ooh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) { struct ooh323_pvt *p = NULL; enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; if (!(p = (struct ooh323_pvt *) chan->tech_pvt)) return AST_RTP_GET_FAILED; *rtp = p->vrtp; if (!(p->rtp)) { return AST_RTP_GET_FAILED; } res = AST_RTP_TRY_NATIVE; return res; } int ooh323_update_capPrefsOrderForCall (ooCallData *call, struct ast_codec_pref *prefs) { int i = 0; int codec = ast_codec_pref_index(prefs, i); ooResetCapPrefs(call); while (codec) { ooAppendCapToCapPrefs(call, ooh323_convertAsteriskCapToH323Cap(codec)); codec = ast_codec_pref_index(prefs, ++i); } return 0; } int ooh323_convertAsteriskCapToH323Cap(int cap) { char formats[512]; switch (cap) { case AST_FORMAT_ULAW: return OO_G711ULAW64K; case AST_FORMAT_ALAW: return OO_G711ALAW64K; case AST_FORMAT_GSM: return OO_GSMFULLRATE; case AST_FORMAT_G729A: return OO_G729A; case AST_FORMAT_G723_1: return OO_G7231; case AST_FORMAT_H263: return OO_H263VIDEO; default: ast_log(LOG_NOTICE, "Don't know how to deal with mode %s\n", ast_getformatname_multiple(formats, sizeof(formats), cap)); return -1; } } static int ooh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active) { /* XXX Deal with Video */ struct ooh323_pvt *p; struct sockaddr_in them; struct sockaddr_in us; int mode; if (gH323Debug) ast_verbose("--- ooh323_set_peer - %s\n", chan->name); if (!rtp) { return 0; } mode = ooh323_convertAsteriskCapToH323Cap(chan->writeformat); p = (struct ooh323_pvt *) chan->tech_pvt; if (!p) { ast_log(LOG_ERROR, "No Private Structure, this is bad\n"); return -1; } ast_rtp_get_peer(rtp, &them); ast_rtp_get_us(rtp, &us); return 0; } int configure_local_rtp(struct ooh323_pvt *p, ooCallData *call) { struct sockaddr_in us; ooMediaInfo mediaInfo; int x, format = 0; if (gH323Debug) ast_verbose("--- configure_local_rtp\n"); if (p->rtp) { ast_rtp_codec_setpref(p->rtp, &p->prefs); } /* figure out our local RTP port and tell the H.323 stack about it*/ ast_rtp_get_us(p->rtp, &us); ast_copy_string(mediaInfo.lMediaIP, ast_inet_ntoa(us.sin_addr), sizeof(mediaInfo.lMediaIP)); mediaInfo.lMediaPort = ntohs(us.sin_port); mediaInfo.lMediaCntrlPort = mediaInfo.lMediaPort +1; for (x = 0; 0 != (format = ast_codec_pref_index(&p->prefs, x)); x++) { strcpy(mediaInfo.dir, "transmit"); mediaInfo.cap = ooh323_convertAsteriskCapToH323Cap(format); ooAddMediaInfo(call, mediaInfo); strcpy(mediaInfo.dir, "receive"); ooAddMediaInfo(call, mediaInfo); if (mediaInfo.cap == OO_G729A) { strcpy(mediaInfo.dir, "transmit"); mediaInfo.cap = OO_G729; ooAddMediaInfo(call, mediaInfo); strcpy(mediaInfo.dir, "receive"); ooAddMediaInfo(call, mediaInfo); } } if (gH323Debug) ast_verbose("+++ configure_local_rtp\n"); return 1; } void setup_rtp_connection(ooCallData *call, const char *remoteIp, int remotePort) { struct ooh323_pvt *p = NULL; struct sockaddr_in them; if (gH323Debug) ast_verbose("--- setup_rtp_connection\n"); /* Find the call or allocate a private structure if call not found */ p = find_call(call); if (!p) { ast_log(LOG_ERROR, "Something is wrong: rtp\n"); return; } them.sin_family = AF_INET; them.sin_addr.s_addr = inet_addr(remoteIp); /* only works for IPv4 */ them.sin_port = htons(remotePort); ast_rtp_set_peer(p->rtp, &them); if (gH323Debug) { ast_verbose("+++ setup_rtp_connection\n"); } return; } void close_rtp_connection(ooCallData *call) { struct ooh323_pvt *p = NULL; if (gH323Debug) { ast_verbose("--- close_rtp_connection\n"); } p = find_call(call); if (!p) { ast_log(LOG_ERROR, "Couldn't find matching call to close rtp connection\n"); return; } ast_mutex_lock(&p->lock); if (p->rtp) { ast_rtp_stop(p->rtp); } ast_mutex_unlock(&p->lock); if (gH323Debug) { ast_verbose("+++ close_rtp_connection\n"); } return; } int update_our_aliases(ooCallData *call, struct ooh323_pvt *p) { int updated = -1; ooAliases *psAlias = NULL; if (!call->ourAliases) return updated; for (psAlias = call->ourAliases; psAlias; psAlias = psAlias->next) { if (psAlias->type == T_H225AliasAddress_h323_ID) { ast_copy_string(p->callee_h323id, psAlias->value, sizeof(p->callee_h323id)); updated = 1; } if (psAlias->type == T_H225AliasAddress_dialedDigits) { ast_copy_string(p->callee_dialedDigits, psAlias->value, sizeof(p->callee_dialedDigits)); updated = 1; } if (psAlias->type == T_H225AliasAddress_url_ID) { ast_copy_string(p->callee_url, psAlias->value, sizeof(p->callee_url)); updated = 1; } if (psAlias->type == T_H225AliasAddress_email_ID) { ast_copy_string(p->callee_email, psAlias->value, sizeof(p->callee_email)); updated = 1; } } return updated; } struct ast_frame *ooh323_rtp_read(struct ast_channel *ast, struct ooh323_pvt *p) { /* Retrieve audio/etc from channel. Assumes p->lock is already held. */ struct ast_frame *f; static struct ast_frame null_frame = { AST_FRAME_NULL, }; switch (ast->fdno) { case 0: f = ast_rtp_read(p->rtp); /* RTP Audio */ break; case 1: f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */ break; case 2: f = ast_rtp_read(p->vrtp); /* RTP Video */ break; case 3: f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ break; default: f = &null_frame; } /* Don't send RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF) && !(p->dtmfmode & H323_DTMF_RFC2833)) { return &null_frame; } if (p->owner) { /* We already hold the channel lock */ if (f->frametype == AST_FRAME_VOICE) { if (f->subclass != p->owner->nativeformats) { ast_debug(1, "Oooh, format changed to %d\n", f->subclass); p->owner->nativeformats = f->subclass; ast_set_read_format(p->owner, p->owner->readformat); ast_set_write_format(p->owner, p->owner->writeformat); } if ((p->dtmfmode & H323_DTMF_INBAND) && p->vad) { f = ast_dsp_process(p->owner, p->vad, f); if (f && (f->frametype == AST_FRAME_DTMF)) { ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass); } } } } return f; } int ooh323_convert_hangupcause_asteriskToH323(int cause) { switch (cause) { case AST_CAUSE_CALL_REJECTED: return OO_REASON_REMOTE_REJECTED; case AST_CAUSE_UNALLOCATED: return OO_REASON_NOUSER; case AST_CAUSE_BUSY: return OO_REASON_REMOTE_BUSY; case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: return OO_REASON_NOCOMMON_CAPABILITIES; case AST_CAUSE_CONGESTION: return OO_REASON_REMOTE_BUSY; case AST_CAUSE_NO_ANSWER: return OO_REASON_REMOTE_NOANSWER; case AST_CAUSE_NORMAL: return OO_REASON_REMOTE_CLEARED; case AST_CAUSE_FAILURE: default: return OO_REASON_UNKNOWN; } return 0; } int ooh323_convert_hangupcause_h323ToAsterisk(int cause) { switch (cause) { case OO_REASON_REMOTE_REJECTED: return AST_CAUSE_CALL_REJECTED; case OO_REASON_NOUSER: return AST_CAUSE_UNALLOCATED; case OO_REASON_REMOTE_BUSY: case OO_REASON_LOCAL_BUSY: return AST_CAUSE_BUSY; case OO_REASON_NOCOMMON_CAPABILITIES: /* No codecs approved */ return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; case OO_REASON_REMOTE_CONGESTED: case OO_REASON_LOCAL_CONGESTED: return AST_CAUSE_CONGESTION; case OO_REASON_REMOTE_NOANSWER: return AST_CAUSE_NO_ANSWER; case OO_REASON_UNKNOWN: case OO_REASON_INVALIDMESSAGE: case OO_REASON_TRANSPORTFAILURE: return AST_CAUSE_FAILURE; case OO_REASON_REMOTE_CLEARED: return AST_CAUSE_NORMAL; default: return AST_CAUSE_NORMAL; } /* Never reached */ return 0; } #if 0 void ast_ooh323c_exit() { ooGkClientDestroy(); } #endif AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Objective Systems H323 Channel");