- Added QSIG patch from Mario Goegel.
This commit is contained in:
parent
92c3f4afc6
commit
6c19246b9d
2
Makefile
2
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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
22
chan_capi.c
22
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue