From 6c19246b9dccbcfa013c0588e6d7ee4d54d9c2e2 Mon Sep 17 00:00:00 2001 From: MelwareDE Date: Sun, 11 Feb 2007 16:01:32 +0000 Subject: [PATCH] - Added QSIG patch from Mario Goegel. --- Makefile | 2 +- README.qsig | 63 ++++++ capi.conf | 1 + chan_capi.c | 22 ++ chan_capi.h | 7 + chan_capi_qsig.h | 189 +++++++++++++++++ chan_capi_qsig_core.c | 474 ++++++++++++++++++++++++++++++++++++++++++ chan_capi_qsig_ecma.c | 129 ++++++++++++ 8 files changed, 886 insertions(+), 1 deletion(-) create mode 100644 README.qsig create mode 100644 chan_capi_qsig.h create mode 100644 chan_capi_qsig_core.c create mode 100644 chan_capi_qsig_ecma.c diff --git a/Makefile b/Makefile index 03cfc19..5c9a86f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.qsig b/README.qsig new file mode 100644 index 0000000..8786b97 --- /dev/null +++ b/README.qsig @@ -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 + + Copyright (C) 2006-2007 Mario Goegel + Mario Goegel + +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. + diff --git a/capi.conf b/capi.conf index a937f01..03a1bb2 100644 --- a/capi.conf +++ b/capi.conf @@ -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. diff --git a/chan_capi.c b/chan_capi.c index 39658dc..8648ab9 100644 --- a/chan_capi.c +++ b/chan_capi.c @@ -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; diff --git a/chan_capi.h b/chan_capi.h index 47ef5ca..8370876 100644 --- a/chan_capi.h +++ b/chan_capi.h @@ -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 diff --git a/chan_capi_qsig.h b/chan_capi_qsig.h new file mode 100644 index 0000000..3d2b247 --- /dev/null +++ b/chan_capi_qsig.h @@ -0,0 +1,189 @@ +/* + * (QSIG) + * + * Implementation of QSIG extensions for CHAN_CAPI + * + * Copyright 2006-2007 (c) Mario Goegel + * + * Mario Goegel + * + * 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 diff --git a/chan_capi_qsig_core.c b/chan_capi_qsig_core.c new file mode 100644 index 0000000..a83376b --- /dev/null +++ b/chan_capi_qsig_core.c @@ -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 + * Mario Goegel + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#include +#include +#include + +#include +#include +#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)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; +} diff --git a/chan_capi_qsig_ecma.c b/chan_capi_qsig_ecma.c new file mode 100644 index 0000000..8679b22 --- /dev/null +++ b/chan_capi_qsig_ecma.c @@ -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 + * Mario Goegel + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + */ + +#include +#include +#include + +#include +#include +#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; +}