- Added QSIG patch from Mario Goegel.

This commit is contained in:
MelwareDE 2007-02-11 16:01:32 +00:00
parent 92c3f4afc6
commit 6c19246b9d
8 changed files with 886 additions and 1 deletions

View File

@ -91,7 +91,7 @@ INSTALL=install
SHAREDOS=chan_capi.so
OBJECTS=chan_capi.o c20msg.o chan_capi_rtp.o
OBJECTS=chan_capi.o c20msg.o chan_capi_rtp.o chan_capi_qsig_core.o chan_capi_qsig_ecma.o
CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations

63
README.qsig Normal file
View File

@ -0,0 +1,63 @@
(CAPI*) chan_capi a Common ISDN API 2.0 implementation
for Asterisk/OpenPBX
QSIG Extension for chan_capi
Copyright (C) 2005-2007 Cytronics & Melware
Armin Schindler <armin@melware.de>
Copyright (C) 2006-2007 Mario Goegel
Mario Goegel <m.goegel@gmx.de>
This program is free software and may be modified and distributed under
the terms of the GNU Public License. There is _NO_ warranty for this!
Thanks go to the debuggers, bugfixers and contributors :)
===========================================================================
None yet - you will be welcome here :-)
(...and all the others that have been forgotten...) :-)
No support for Asterisk 1.0.x any more, you need at least
Asterisk 1.2.x
What is Q.SIG
=============
Q.SIG is an protocoll extension for ISDN.
It is mainly used on connecting PBXs of different PBX vendors, which allows
better interoperability.
As example there can be a name of an extension transferred between different
PBXs, which is with standard ISDN not possible.
These extensions will be transmitted as encoded facility information elements.
To use Q.SIG with asterisk, you'll need a card like Eicon DIVA Server
(BRI like PRI), which supports QSIG. Maybe others do also work, let me now.
The QSIG support includes:
==========================
- Name presentation on Call SETUP incoming like outgoing
Future Targets:
===============
- better name handling - there are still some case which are wrongly handled
- check code for buffer overflows
- Path Replacement
- CCBS
- AOC
- ...
How to use:
===========
simply enable Q.SIG with following line in your capi.conf interface:
qsig=on
Take care that you enable this only for interfaces, where the other end
understands the Q.SIG protocoll. If not, then these switches may reject the
entire call, because of wrong facility contents.
Later this will change to qsig=off or qsig=1..x where we can support some
pbx manufacturer specific operations.

View File

@ -67,4 +67,5 @@ devices=2 ;number of concurrent calls (b-channels) on this controller
;jb..... ;with Asterisk 1.4 you can configure jitterbuffer,
;see Asterisk documentation for all jb* setting available.
;mohinterpret=default ;Asterisk 1.4: default music on hold class when placed on hold.
;qsig=on ;enable use of Q.SIG extensions.

View File

@ -89,6 +89,7 @@ OPENPBX_FILE_VERSION("$HeadURL$", "$Revision$")
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_rtp.h"
#include "chan_capi_qsig.h"
#endif
#ifdef PBX_IS_OPBX
@ -1454,6 +1455,7 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
char *dsa = NULL;
char callingsubaddress[AST_MAX_EXTENSION];
char calledsubaddress[AST_MAX_EXTENSION];
int doqsig;
_cmsg CMSG;
MESSAGE_EXCHANGE_ERROR error;
@ -1465,6 +1467,7 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
i->doB3 = CAPI_B3_DONT;
i->doOverlap = 0;
memset(i->overlapdigits, 0, sizeof(i->overlapdigits));
doqsig = i->doqsig;
/* parse the parameters */
while ((param) && (*param)) {
@ -1489,6 +1492,11 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
cc_log(LOG_WARNING, "Default CID already set in '%s'\n", idest);
use_defaultcid = 1;
break;
case 'q': /* disable QSIG */
cc_verbose(4, 0, VERBOSE_PREFIX_4 "%s: QSIG extensions for this call disabled\n",
i->vname);
doqsig = 0;
break;
default:
cc_log(LOG_WARNING, "Unknown parameter '%c' in '%s', ignoring.\n",
*param, idest);
@ -1577,6 +1585,12 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
bchaninfo[2] = 0x0;
CONNECT_REQ_BCHANNELINFORMATION(&CMSG) = (_cstruct)bchaninfo; /* 0 */
if (doqsig) {
char *facilityarray = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE);
cc_qsig_add_call_setup_data(facilityarray, i);
CONNECT_REQ_FACILITYDATAARRAY(&CMSG) = facilityarray;
}
cc_mutex_lock(&i->lock);
i->outgoing = 1;
@ -3914,6 +3928,11 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
pbx_builtin_setvar_helper(i->owner, "ANI2", buffer);
pbx_builtin_setvar_helper(i->owner, "SECONDCALLERID", buffer);
*/
if (i->qsigfeat == QSIG_ENABLED) {
cc_qsig_handle_capiind(CONNECT_IND_FACILITYDATAARRAY(CMSG), i);
}
if ((i->isdnmode == CAPI_ISDNMODE_MSN) && (i->immediate)) {
/* if we don't want to wait for SETUP/SENDING-COMPLETE in MSN mode */
start_pbx_on_match(i, PLCI, HEADER_MSGNUM(CMSG));
@ -5197,6 +5216,8 @@ int mkif(struct cc_capi_conf *conf)
tmp->doDTMF = conf->softdtmf;
tmp->capability = conf->capability;
tmp->qsigfeat = conf->qsigfeat;
tmp->next = iflist; /* prepend */
iflist = tmp;
cc_verbose(2, 0, VERBOSE_PREFIX_3 "capi %c %s (%s:%s) contr=%d devs=%d EC=%d,opt=%d,tail=%d\n",
@ -5797,6 +5818,7 @@ static int conf_interface(struct cc_capi_conf *conf, struct ast_variable *v)
CONF_TRUE(conf->es, "echosquelch", 1)
CONF_TRUE(conf->bridge, "bridge", 1)
CONF_TRUE(conf->ntmode, "ntmode", 1)
CONF_TRUE(conf->qsigfeat, "qsig", 1)
if (!strcasecmp(v->name, "callgroup")) {
conf->callgroup = ast_get_group(v->value);
continue;

View File

@ -36,6 +36,8 @@
#define RTP_HEADER_SIZE 12
#define CAPI_MAX_FACILITYDATAARRAY_SIZE 300
#ifndef CONNECT_RESP_GLOBALCONFIGURATION
#define CC_HAVE_NO_GLOBALCONFIGURATION
#warning If you dont update your libcapi20, some fax features are not available
@ -374,6 +376,9 @@ struct capi_pvt {
int codec;
unsigned int timestamp;
/* Q.SIG features */
int qsigfeat;
/*! Next channel in list */
struct capi_pvt *next;
};
@ -415,6 +420,7 @@ struct cc_capi_conf {
int es;
int bridge;
int amaflags;
int qsigfeat;
unsigned int faxsetting;
ast_group_t callgroup;
ast_group_t pickupgroup;
@ -463,6 +469,7 @@ struct cc_capi_controller {
#define CAPI_ETSI_IE_CAUSE 0x08
#define CAPI_ETSI_IE_PROGRESS_INDICATOR 0x1e
#define CAPI_ETSI_IE_CALLED_PARTY_NUMBER 0x70
#define CAPI_ETSI_IE_FACILITY 0x1c
/* ETIS 300 102-1 message types */
#define CAPI_ETSI_ALERTING 0x01

189
chan_capi_qsig.h Normal file
View File

@ -0,0 +1,189 @@
/*
* (QSIG)
*
* Implementation of QSIG extensions for CHAN_CAPI
*
* Copyright 2006-2007 (c) Mario Goegel
*
* Mario Goegel <m.goegel@gmx.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#ifndef PBX_QSIG_H
#define PBX_QSIG_H
#define QSIG_DISABLED 0x00
#define QSIG_ENABLED 0x01
#define QSIG_TYPE_DEFAULT 0x00 /* use only common features */
#define QSIG_TYPE_ALCATEL 0x01 /* use additional Alcatel features */
#define QSIG_TYPE_HICOM 0x02 /* use additional Hicom features */
#define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */
#define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */
#define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */
#define Q932_PROTOCOL_GAT 0x16
#define Q932_PROTOCOL_EXTENSIONS 0x1F
#define COMP_TYPE_INVOKE 0xa1 /* Invoke component */
#define COMP_TYPE_DISCR_SS 0x91 /* Supplementary service descriptor - ROSE PROTOCOL */
#define COMP_TYPE_NFE 0xaa /* Network Facility Extensions (ECMA-165) */
#define COMP_TYPE_APDU_INTERP 0x8b /* APDU Interpration Type (0 DISCARD, 1 CLEARCALL-IF-UNKNOWN, 2 REJECT-APDU) */
#define COMP_TYPE_RETURN_RESULT 0xA2
#define COMP_TYPE_RETURN_ERROR 0xA3
#define COMP_TYPE_REJECT 0xA4
#define APDUINTERPRETATION_IGNORE 0x00
#define APDUINTERPRETATION_CLEARCALL 0x01
#define APDUINTERPRETATION_REJECT 0x02
/* ASN.1 Identifier Octet - Data types */
#define ASN1_TYPE_MASK 0x1f
#define ASN1_BOOLEAN 0x01
#define ASN1_INTEGER 0x02
#define ASN1_BITSTRING 0x03
#define ASN1_OCTETSTRING 0x04
#define ASN1_NULL 0x05
#define ASN1_OBJECTIDENTIFIER 0x06
#define ASN1_OBJECTDESCRIPTOR 0x07
#define ASN1_EXTERN 0x08
#define ASN1_REAL 0x09
#define ASN1_ENUMERATED 0x0a
#define ASN1_EMBEDDEDPDV 0x0b
#define ASN1_UTF8STRING 0x0c
#define ASN1_RELATIVEOBJECTID 0x0d
/* 0x0e & 0x0f are reserved for future ASN.1 editions */
#define ASN1_SEQUENCE 0x10
#define ASN1_SET 0x11
#define ASN1_NUMERICSTRING 0x12
#define ASN1_PRINTABLESTRING 0x13
#define ASN1_TELETEXSTRING 0x14
#define ASN1_IA5STRING 0x16
#define ASN1_UTCTIME 0x17
#define ASN1_GENERALIZEDTIME 0x18
#define CNIP_CALLINGNAME 0x00 /* Name-Types defined in ECMA-164 */
#define CNIP_CALLEDNAME 0x01
#define CNIP_CONNECTEDNAME 0x02
#define CNIP_BUSYNAME 0x03
#define CNIP_NAMEPRESALLOW 0x80
#define CNIP_NAMEPRESRESTR 0xA0
#define CNIP_NAMEPRESUNAVAIL 0xC0
#define CNIP_NAMEUSERPROVIDED 0x00 /* Name is User-provided, unvalidated */
#define CNIP_NAMEUSERPROVIDEDV 0x01 /* Name is User-provided and validated */
#define CCQSIG__ECMA__NAMEPRES 1000 /* Setting an own constant for ECMA Operation/Namepresentation, others will follow */
/*
* INVOKE Data struct, contains data for further operations
*/
struct cc_qsig_invokedata {
int len; /* invoke length */
int offset; /* where does the invoke start in facility array */
int id; /* id from sent Invoke Number */
int apdu_interpr; /* What To Do with unknown Operation? */
int descr_type; /* component descriptor is of ASN.1 Datatype (0x02 Integer, 0x06 Object Identifier) */
int type; /* when component is Integer */
int oid_len;
unsigned char oid_bin[20]; /* when component is Object Identifier then save here the binary oid */
int datalen; /* invoke struct len */
unsigned char data[255]; /* invoke */
};
/*
* NFE Entity Address - contains destination informations for following INVOKE's
*/
struct cc_qsig_entityaddr { /* In case of AnyPINX */
int partynum; /* private,public,etc a5=private */
int ton; /* Type of Number */
unsigned char *num; /* EntityNumber */
};
/*
* Network Facility Extensions struct - to which pbx does the INVOKE belong to
*/
struct cc_qsig_nfe {
int src_entity; /* Call is coming from PBX (End|Any) */
int dst_entity; /* Call destination is */
struct cc_qsig_entityaddr src_addr; /* additional infos (PBX identifier) */
struct cc_qsig_entityaddr dst_addr; /* same here for destination */
};
/***************** QSIG Core Functions */
/* Create an default QSIG Facility Array */
extern int cc_qsig_build_facility_struct(unsigned char * buf, unsigned int *idx, int apdu_interpr, struct cc_qsig_nfe *nfe);
extern int cc_qsig_add_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke);
/* Returns an String from ASN.1 Encoded String */
extern unsigned int cc_qsig_asn1_get_string(unsigned char *buf, int buflen, unsigned char *data);
/* Returns an Integer from ASN.1 Encoded Integer */
extern unsigned int cc_qsig_asn1_get_integer(unsigned char *data, int *idx);
/* Returns an Human Readable OID from ASN.1 Encoded OID */
extern unsigned char cc_qsig_asn1_get_oid(unsigned char *data, int *idx);
/* Check if OID is ECMA-ISDN (1.3.12.9.*) */
extern signed int cc_qsig_asn1_check_ecma_isdn_oid(unsigned char *data, int len);
/*
* Valid QSIG-Facility?
* Returns 0 if not
*/
extern unsigned int cc_qsig_check_facility(unsigned char *data, int *idx, int *apduval);
/*
* Is this an INVOKE component?
* when not return -1, set idx to next byte (length of component?)
* *** check idx in this case, that we are not out of range - maybe we got an unknown component then
* when it is an invoke, return invoke length and set idx to first byte of component
*/
extern signed int cc_qsig_check_invoke(unsigned char *data, int *idx);
/*
* Get Invoke ID
* returns current index
* idx points to next byte in array
*/
extern signed int cc_qsig_get_invokeid(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke);
/* fill the Invoke struct with all the invoke data */
extern signed int cc_qsig_fill_invokestruct(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke, int apduval);
/*
* Handles incoming Facilities on CAPI Indications
*/
extern unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i);
/*
* Handles outgoing Facilies on Call SETUP
*/
extern unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i);
/* Identify an INVOKE and return our own Ident Integer (CCQSIG__*) */
extern signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke);
extern unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
/* ECMA QSIG Functions */
/* Handle Operation: 1.3.12.9.0-3 ECMA/ISDN/NAMEPRESENTATION */
extern void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
#endif

474
chan_capi_qsig_core.c Normal file
View File

@ -0,0 +1,474 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel
*
* Armin Schindler <armin@melware.de>
* Mario Goegel <m.goegel@gmx.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <asterisk/channel.h>
#include <asterisk/options.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_qsig.h"
// Returns an String from ASN.1 Encoded String
unsigned int cc_qsig_asn1_add_string(unsigned char *buf, int *idx, char *data, int datalen)
{
int myidx=*idx;
if ((1 + datalen + (*idx) ) > sizeof(*buf)) {
// String exceeds buffer size
return -1;
}
buf[myidx++] = datalen;
memcpy(&buf[myidx], data, datalen);
myidx += 1 + datalen;
*idx = myidx;
return 0;
}
// Returns an String from ASN.1 Encoded String
unsigned int cc_qsig_asn1_get_string(unsigned char *buf, int buflen, unsigned char *data)
{
int strsize;
int myidx=0;
strsize = data[myidx++];
if (strsize > buflen)
strsize = buflen - 1;
memcpy(buf, &data[myidx], strsize);
// cc_verbose(1, 1, VERBOSE_PREFIX_4 " get_string length %i\n", strsize);
return strsize;
}
unsigned int cc_qsig_asn1_add_integer(unsigned char *buf, int *idx, int value)
{
int myidx = *idx;
int intlen = 1;
if ((unsigned int)value > (unsigned int)0xFFFF)
return -1; // no support at the moment
if (value > 255)
intlen++; // we need 2 bytes
buf[myidx++] = ASN1_INTEGER;
buf[myidx++] = intlen;
if (intlen > 1) {
buf[myidx++] = (unsigned char)(value >> 8);
buf[myidx++] = (unsigned char)(value - 0xff00);
} else {
buf[myidx++] = (unsigned char)value;
}
*idx = myidx;
return 0;
}
// Returns an Integer from ASN.1 Encoded Integer
unsigned int cc_qsig_asn1_get_integer(unsigned char *data, int *idx)
{ // TODO: not conform with negative Integers
int myidx = *idx;
int intlen;
int temp;
intlen = data[myidx++];
if ((intlen < 1) || (intlen > 2)) { // i don't know if there's a bigger Integer as 16bit -> read specs
cc_verbose(1, 1, VERBOSE_PREFIX_3 "ASN1Decode: Size of ASN.1 Integer not supported: %i\n", intlen);
*idx = myidx + intlen;
return 0;
}
temp = (char)data[myidx++];
if (intlen == 2) {
temp=(temp << 8) + data[myidx++];
}
*idx = myidx;
return temp;
}
// Returns an Human Readable OID from ASN.1 Encoded OID
unsigned char cc_qsig_asn1_get_oid(unsigned char *data, int *idx)
{
// TODO: Add code
return 0;
}
// Check if OID is ECMA-ISDN (1.3.12.9.*)
signed int cc_qsig_asn1_check_ecma_isdn_oid(unsigned char *data, int len)
{
// 1.3 .12 .9
if ((data[0] == 0x2B) && (data[1] == 0x0C) && (data[2] == 0x09))
return 0;
return -1;
}
// This function simply updates the length informations of the facility struct
void cc_qsig_update_facility_length(unsigned char * buf, unsigned int idx)
{
buf[0] = idx;
buf[2] = idx-2;
}
// Create Invoke Struct
int cc_qsig_build_facility_struct(unsigned char * buf, unsigned int *idx, int apdu_interpr, struct cc_qsig_nfe *nfe)
{
int myidx = 1; // we start with Index 1 - Byte 0 is Length of Facilitydataarray
buf[myidx++] = 0x1c;
buf[myidx++] = 0; // Byte 2 length of Facilitydataarray
buf[myidx++] = COMP_TYPE_DISCR_SS; // QSIG Facility
// TODO: Outsource following struct to an separate function
buf[myidx++] = COMP_TYPE_NFE; // Network Facility Extension
buf[myidx++] = 6; // NFE Size hardcoded - not good
buf[myidx++] = 0x80; // Source Entity
buf[myidx++] = 0x01;
buf[myidx++] = 0x00; // End PINX hardcoded
buf[myidx++] = 0x82; // Dest. Entity
buf[myidx++] = 0x01;
buf[myidx++] = 0x00; // End PINX hardcoded
buf[myidx++] = COMP_TYPE_APDU_INTERP; // How to interpret this APDU
buf[myidx++] = 0x01; // Length
buf[myidx++] = apdu_interpr;
// Here will follow now the Invoke
*idx = myidx;
cc_qsig_update_facility_length(buf, myidx);
return 0;
}
// Add invoke to buf
int cc_qsig_add_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke)
{
int myidx = *idx;
int invlenidx;
int result;
buf[myidx++] = COMP_TYPE_INVOKE;
invlenidx = myidx; // save the Invoke length index for later
buf[myidx++] = 0;
result = cc_qsig_asn1_add_integer(buf, &myidx, invoke->id);
if (result) {
cc_log(LOG_ERROR, "QSIG: Cannot add invoke, identifier is not encoded!\n");
return -1;
}
switch (invoke->descr_type) {
case ASN1_INTEGER:
result = cc_qsig_asn1_add_integer(buf, &myidx, invoke->type);
if (result) {
cc_log(LOG_ERROR, "QSIG: Cannot add invoke, identifier is not encoded!\n");
return -1;
}
break;
case ASN1_OBJECTIDENTIFIER:
if ((invoke->oid_len < 1) || (invoke->oid_len > 20)) {
cc_log(LOG_ERROR, "QSIG: Cannot add invoke, OID is too big!\n");
return -1;
}
buf[myidx++] = ASN1_OBJECTIDENTIFIER;
buf[myidx++] = invoke->oid_len;
memcpy(&buf[myidx], invoke->oid_bin, invoke->oid_len);
myidx += invoke->oid_len;
break;
default:
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unknown Invoke Type, not encoded (%i)\n", invoke->descr_type);
return -1;
break;
}
if (invoke->datalen > 0) { // may be no error, if there's no data
memcpy(&buf[myidx], invoke->data, invoke->datalen);
myidx += invoke->datalen;
}
buf[invlenidx] = myidx-1;
cc_qsig_update_facility_length(buf, myidx - 1);
*idx = myidx;
return 0;
}
// Valid QSIG-Facility?
// Returns 0 if not
unsigned int cc_qsig_check_facility(unsigned char *data, int *idx, int *apduval)
{
int myidx = *idx;
// First byte after Facility Length
if (data[myidx] == (unsigned char)(0x80 | Q932_PROTOCOL_ROSE)) {
myidx++;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: ROSE Supplementary Services\n");
if (data[myidx++] == (unsigned char)COMP_TYPE_NFE) {
// Todo: Check Entities?
myidx = myidx + data[myidx] + 1;
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (idc #1 %i)\n",idx);
if ((data[myidx++] == (unsigned char)COMP_TYPE_APDU_INTERP)) {
myidx = myidx + data[myidx];
*apduval = data[myidx];
// ToDo: implement real reject or clear call ?
*idx = ++myidx;
return 1;
}
}
}
return 0;
}
// Is this an INVOKE component?
// when not return -1, set idx to next byte (length of component?)
// *** check idx in this case, that we are not out of range - maybe we got an unknown component then
// when it is an invoke, return invoke length and set idx to first byte of component
signed int cc_qsig_check_invoke(unsigned char *data, int *idx)
{
int myidx = *idx;
if (data[myidx] == (unsigned char)COMP_TYPE_INVOKE) {
// is an INVOKE_IDENT
*idx = myidx + 1; // Set index to first byte of component
// cc_verbose(1, 1, VERBOSE_PREFIX_4 "CONNECT_IND (Invoke Length %i)\n", data[myidx+1]);
return data[myidx + 1]; // return component length
}
*idx = ++myidx;
return -1; // what to do now? got no Invoke
}
// Get Invoke ID
// returns current index
// idx points to next byte in array
signed int cc_qsig_get_invokeid(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke)
{
int myidx;
int invidtype = 0;
int invlen = 0;
int invoffset;
int temp = 0;
myidx = *idx;
invoffset = myidx;
invlen = data[myidx++];
if (invlen > 0) {
invoke->len = invlen; // set Length of Invoke struct
invoke->offset = invoffset; // offset in Facility Array, where the Invoke Data starts
invidtype = data[myidx++]; // Get INVOKE Id Type
if (invidtype != ASN1_INTEGER) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unknown Invoke Identifier Type 0x%#x\n", invidtype);
return -1;
}
*idx = myidx;
temp = cc_qsig_asn1_get_integer(data, idx);
invoke->id = temp;
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (Invoke Identifier %#x)\n", temp);
// *idx=myidx; // Set by cc_qsig_asn1get_integer
}
return 0;
}
// fill the Invoke struct with all the invoke data
signed int cc_qsig_fill_invokestruct(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke, int apduval)
{
int myidx = *idx;
int invoptyp;
int temp;
int temp2;
int datalen;
invoptyp = data[myidx++]; // Invoke Operation Type 0x02=INTEGER, 0x06=OID
switch (invoptyp) {
case ASN1_INTEGER:
invoke->apdu_interpr = apduval;
temp = cc_qsig_asn1_get_integer(data, idx);
invoke->descr_type = ASN1_INTEGER;
invoke->type = temp;
myidx++; // component length
//datalen=data[myidx++]; // maybe correct, better we calculate the datalength
temp2 = (invoke->len) + (invoke->offset) + 1; // Array End = Invoke Length + Invoke Offset +1
datalen = temp2 - myidx;
if (datalen > 255) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen);
datalen = 255;
}
invoke->datalen = datalen;
memcpy(invoke->data, &data[myidx], datalen); // copy data of Invoke Operation
myidx = myidx + datalen; // points to next INVOKE component, if there's any
*idx = myidx;
break;
case ASN1_OBJECTIDENTIFIER:
invoke->apdu_interpr = apduval;
invoke->descr_type = ASN1_OBJECTIDENTIFIER;
temp = data[myidx++]; // Length of OID
if (temp > 20) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation OID Size (max 20 Bytes): %i\n", temp);
temp = 20;
}
// TODO: Maybe we decode the OID here and be verbose - have to write cc_qsig_asn1get_oid
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (OID, Length %i)\n", temp);
invoke->oid_len = temp;
memcpy(invoke->oid_bin, &data[myidx], temp); // Copy OID to separate array
myidx = myidx + temp; // Set index to next information
temp2 = (invoke->len) + (invoke->offset) + 1; // Array End = Invoke Length + Invoke Offset +1
datalen = temp2 - myidx;
if (datalen > 255) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen);
datalen = 255;
}
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (OID, Datalength %i)\n",datalen);
invoke->datalen = datalen;
memcpy(invoke->data, &data[myidx], datalen); // copy data of Invoke Operation
myidx = myidx + datalen; // points to next INVOKE component, if there's any
*idx = myidx;
break;
default:
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unknown INVOKE Operation Type: %i\n", invoptyp);
temp2 = (invoke->len) + (invoke->offset) + 1; // Array End = Invoke Length + Invoke Offset +1
datalen = temp2 - myidx;
if (datalen > 255) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unsupported INVOKE Operation Size (max 255 Bytes): %i\n", datalen);
datalen = 255;
}
*idx = datalen; // Set index to next INVOKE, if there's any
return -1;
break;
}
return 0; // No problems
}
// Identify an INVOKE and return our own Ident Integer (CCQSIG__*)
signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke)
{
int invokedescrtype = 0;
int datalen;
// cc_verbose(1, 1, VERBOSE_PREFIX_4 "CONNECT_IND (Ident Invoke %i)\n", invoke->descr_type);
switch (invoke->descr_type) {
case ASN1_INTEGER:
invokedescrtype = 1;
break;
case ASN1_OBJECTIDENTIFIER:
invokedescrtype = 2;
datalen = invoke->oid_len;
if ((datalen) == 4) {
if (!cc_qsig_asn1_check_ecma_isdn_oid(invoke->oid_bin, datalen)) {
switch (invoke->oid_bin[3]) {
case 0: // ECMA QSIG Name Presentation
return CCQSIG__ECMA__NAMEPRES;
default: // Unknown Operation
cc_verbose(1, 1, VERBOSE_PREFIX_4 "QSIG: Unhandled ECMA-ISDN QSIG INVOKE (%i)\n", invoke->oid_bin[3]);
return 0;
}
}
}
break;
default:
cc_verbose(1, 1, VERBOSE_PREFIX_3 "QSIG: Unidentified INVOKE OP\n");
break;
}
return 0;
}
unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
{
switch (invokeident) {
case CCQSIG__ECMA__NAMEPRES:
cc_qsig_op_ecma_isdn_namepres(invoke, i);
break;
default:
break;
}
return 0;
}
/*
* Handles incoming Indications from CAPI
*/
unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i)
{
int faclen;
int facidx = 2;
int action_unkn_apdu; // What to do with unknown Invoke-APDUs (0=Ignore, 1=clear call, 2=reject APDU)
int invoke_len; // Length of Invoke APDU
unsigned int invoke_op; // Invoke Operation ID
struct cc_qsig_invokedata invoke;
int invoketmp1;
if (data) {
faclen=data[facidx];
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND (Got Facility IE, Length=%#x)\n", faclen);
facidx++;
if (cc_qsig_check_facility(data, &facidx, &action_unkn_apdu)) {
// cc_verbose(1, 1, VERBOSE_PREFIX_3 "CONNECT_IND ROSE Supplementary Services (APDU Interpretation: %i)\n", action_unkn_apdu);
while ((facidx-1)<faclen) {
invoke_len=cc_qsig_check_invoke(data, &facidx);
if (invoke_len>0) {
if (cc_qsig_get_invokeid(data, &facidx, &invoke)==0) {
invoketmp1=cc_qsig_fill_invokestruct(data, &facidx, &invoke, action_unkn_apdu);
invoke_op=cc_qsig_identifyinvoke(&invoke);
cc_qsig_handle_invokeoperation(invoke_op, &invoke, i);
}
} else {
// Not an Invoke
}
}
}
}
return 0;
}
/*
* Handles outgoing Facilies on Call SETUP
*/
unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i)
{
/* TODO: Check buffers */
struct cc_qsig_invokedata invoke;
struct cc_qsig_nfe nfe;
unsigned int dataidx;
cc_qsig_build_facility_struct(data, &dataidx, APDUINTERPRETATION_IGNORE, &nfe);
cc_qsig_encode_ecma_name_invoke(data, &dataidx, &invoke, i);
cc_qsig_add_invoke(data, &dataidx, &invoke);
return 0;
}

129
chan_capi_qsig_ecma.c Normal file
View File

@ -0,0 +1,129 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel
*
* Armin Schindler <armin@melware.de>
* Mario Goegel <m.goegel@gmx.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <asterisk/channel.h>
#include <asterisk/options.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_qsig.h"
/* Handle Operation: 1.3.12.9.0-3 ECMA/ISDN/NAMEPRESENTATION */
void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
{
char callername[50]; /* ECMA defines max length to 50 */
unsigned int namelength = 0;
unsigned int namesetlength = 0;
unsigned int charset = 1;
unsigned int namepres;
unsigned int nametype;
unsigned int datalength;
int myidx = 0;
cc_verbose(1, 1, VERBOSE_PREFIX_4 "Handling Name Operation (id# %#x)\n", invoke->id);
datalength = invoke->datalen;
namepres = (invoke->data[myidx] & 0xF0); /* Name Presentation or Restriction */
nametype = (invoke->data[myidx++] & 0x0F); /* Type of Name-Struct */
switch (nametype) {
case 0: /* Simple Name */
case 2: /* [LENGTH] [STRING] */
namelength = cc_qsig_asn1_get_string((unsigned char *)callername, sizeof(callername), &invoke->data[myidx]);
callername[namelength] = 0;
break;
case 1: /* Nameset */
case 3: /* [LENGTH] [BIT-STRING] [LENGTH] [STRING] [INTEGER] [LENGTH] [VALUE] */
namesetlength = invoke->data[myidx++];
if (invoke->data[myidx++] == ASN1_OCTETSTRING) {
/* should be so */
namelength = cc_qsig_asn1_get_string((unsigned char *)callername, sizeof(callername), &invoke->data[myidx]);
callername[namelength] = 0;
myidx += invoke->data[myidx-1]; /* is this safe? */
} else {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " Namestruct not ECMA conform (String expected)\n");
break;
}
if (invoke->data[myidx++] == ASN1_INTEGER) {
charset=cc_qsig_asn1_get_integer(invoke->data, &myidx);
} else {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " Namestruct not ECMA conform (Integer expected)\n");
}
break;
case 4: /* Name not available */
break;
case 7: /* Namepres. restricted NULL - don't understand ECMA-164, Page 5 */
break;
}
if (namelength > 0) {
/* TODO: Maybe we do some charset conversions */
i->owner->cid.cid_name = strdup(callername);
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " callers name length: %i, \"%s\"\n", namelength, callername); */
}
}
int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
{
const unsigned char oid[] = {0x2b,0x0c,0x09,0x00}; /* 1.3.12.9.0 */
int oid_len = sizeof(oid);
unsigned char namebuf[50];
unsigned char data[255];
int dataidx = 0;
int namelen = 0;
/*TODO: write something */
namelen = strlen(i->owner->cid.cid_name);
if (namelen < 1) { /* There's no name available, try to take Interface-Name */
if (strlen(i->name) >= 1) {
if (namelen > 50)
namelen = 50;
namelen = strlen(i->name);
memcpy(namebuf, i->name, namelen);
}
} else {
if (namelen > 50)
namelen = 50;
memcpy(namebuf, i->owner->cid.cid_name, namelen);
}
invoke->id = 1;
invoke->descr_type = ASN1_OBJECTIDENTIFIER;
invoke->oid_len = oid_len;
memcpy(invoke->oid_bin, oid, oid_len);
if (namelen>0) {
data[dataidx++] = 0x80; /* We send only simple Name, Namepresentation allowed */
data[dataidx++] = namelen;
memcpy(&data[dataidx], namebuf, namelen);
dataidx += namelen;
} else {
data[dataidx++] = 0x84; /* Name not available */
data[dataidx++] = 0;
}
invoke->datalen = dataidx;
memcpy(invoke->data, data, dataidx);
/* qsig_add_invoke(buf, idx, invoke); */
return 0;
}