* first bits of decoding facility information elements
[asterisk/asterisk.git] / channels / misdn / fac.c
index 383a60f..19bfbf7 100644 (file)
 
-#include "isdn_lib_intern.h"
-#include "isdn_lib.h"
-
-#include "string.h"
-
-
-
-
-#define CENTREX_ID      0xa1
-#define CALLDEFLECT_ID      0xa1
-
-/**
-   This file covers the encoding and decoding of facility messages and
-   facility information elements.
-
-   There will be 2 Functions as Interface:
-   
-   fac_enc( char **ntmsg, msg_t * msg, enum facility_type type,  union facility fac, struct misdn_bchannel *bc)
-   fac_dec( unsigned char *p, Q931_info_t *qi, enum facility_type *type,  union facility *fac, struct misdn_bchannel *bc);
-
-   Those will either read the union facility or fill it.
-
-   internally, we will have deconding and encoding functions for each facility
-   IE.
-   
-**/
-
-
-/* support stuff */
-static void strnncpy(unsigned char *dest, unsigned char *src, int len, int dst_len)
+#include "fac.h"
+#include "asn1.h"
+
+#if 0
++-------------------------------
+| IE_IDENTIFIER
++-------------------------------
+| {length}
++-------------------------------
+|   +---------------------------
+|   | SERVICE_DISCRIMINATOR
+|   +---------------------------
+|   | COMPONENT_TYPE_TAG
+|   +---------------------------
+|   | {length}
+|   +---------------------------
+|   |  +-----------------------
+|   |   | INVOKE_IDENTIFIER_TAG (0x2)
+|   |   +-----------------------
+|   |   | {length}              (0x1)
+|   |   +-----------------------
+|   |   | {value}               (odd integer 0-127)
+|   |   +-----------------------
+|   |   +-----------------------
+|   |   | OPERATION_VALUE_TAG   (0x2)
+|   |   +-----------------------
+|   |   | {length}              (0x1)
+|   |   +-----------------------
+|   |   | {value}
+|   |   +-----------------------
+|   |  +-----------------------
+|   |  | ASN.1 data
++---+---+-----------------------
+#endif
+
+enum {
+       SUPPLEMENTARY_SERVICE   = 0x91,
+} SERVICE_DISCRIMINATOR;
+
+enum {
+       INVOKE                                  = 0xa1,
+       RETURN_RESULT                   = 0xa2,
+       RETURN_ERROR                    = 0xa3,
+       REJECT                                  = 0xa4,
+} COMPONENT_TYPE_TAG;
+
+enum {
+       INVOKE_IDENTIFIER               = 0x02,
+       LINKED_IDENTIFIER               = 0x80,
+       NULL_IDENTIFIER                 = 0x05,
+} INVOKE_IDENTIFIER_TAG;
+
+enum {
+       OPERATION_VALUE                 = 0x02,
+} OPERATION_VALUE_TAG;
+
+enum {
+       VALUE_QUERY                     = 0x8c,
+       SET_VALUE                               = 0x8d,
+       REQUEST_FEATURE                 = 0x8f,
+       ABORT                                   = 0xbe,
+       REDIRECT_CALL                   = 0xce,
+       CALLING_PARTY_TO_HOLD   = 0xcf,
+       CALLING_PARTY_FROM_HOLD = 0x50,
+       DROP_TARGET_PARTY               = 0xd1,
+       USER_DATA_TRANSFER              = 0xd3,
+       APP_SPECIFIC_STATUS     = 0xd2,
+
+       /* not from document */
+       CALL_DEFLECT                    = 0x0d,
+       AOC                                     = 0x22,
+} OPERATION_CODE;
+
+enum {
+       Q931_IE_TAG                     = 0x40,
+} ARGUMENT_TAG;
+
+#ifdef FACILITY_DEBUG
+#define FAC_DUMP(fac,len,bc) fac_dump(fac,len,bc)
+#include <ctype.h>
+static void fac_dump (__u8 *facility, unsigned int fac_len, struct misdn_bchannel *bc)
 {
-       if (len > dst_len-1)
-               len = dst_len-1;
-       strncpy((char *)dest, (char *)src, len);
-       dest[len] = '\0';
+       int i;
+       cb_log(0, bc->port, "    --- facility dump start. length:%d\n", fac_len);
+       for (i = 0; i < fac_len; ++i)
+               if (isprint(facility[i]))
+                       cb_log(0, bc->port, "    --- %d: %04p (char:%c)\n", i, facility[i], facility[i]);
+               else
+                       cb_log(0, bc->port, "    --- %d: %04p\n", i, facility[i]);
+       cb_log(0, bc->port, "    --- facility dump end\n");
 }
+#else
+#define FAC_DUMP(fac,len,bc)
+#endif
 
+/*
+** Facility Encoding
+*/
 
-
-
-/**********************/
-/*** FACILITY STUFF ***/
-/**********************/
-
-
-/* IE_FACILITY */
-void enc_ie_facility(unsigned char **ntmode, msg_t *msg, unsigned char *facility, int facility_len, int nt, struct misdn_bchannel *bc)
+static int enc_fac_calldeflect (__u8 *dest, char *number, int pres)
 {
-       unsigned char *p;
-       Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
-       int l;
-
-
-       if (!facility || facility_len<=0)
-       {
-               return;
-       }
-
+       __u8 *body_len,
+                *p = dest,
+                *seq1, *seq2;
+
+       *p++ = SUPPLEMENTARY_SERVICE;
+       *p++ = INVOKE;
+
+       body_len = p++;
+
+       p += _enc_int(p, 0x1 /* some odd integer in (0..127) */, INVOKE_IDENTIFIER);
+       p += _enc_int(p, CALL_DEFLECT, OPERATION_VALUE);
+       p += enc_sequence_start(p, &seq1);
+         p += enc_sequence_start(p, &seq2);
+           p += _enc_num_string(p, number, strlen(number), ASN1_TAG_CONTEXT_SPECIFIC);
+         p += enc_sequence_end(p, seq2);
+         p += enc_bool(p, pres);
+    p += enc_sequence_end(p, seq1);
        
-       l = facility_len;
-       p = msg_put(msg, l+2);
-       if (nt)
-               *ntmode = p+1;
-       else
-               qi->QI_ELEMENT(facility) = p - (unsigned char *)qi - sizeof(Q931_info_t);
-       p[0] = IE_FACILITY;
-       p[1] = l;
-       memcpy(p+2, facility, facility_len);
+       *body_len = p - &body_len[1];
+       
+       return p - dest;
 }
 
-
-/* facility for siemens CENTEX (known parts implemented only) */
-void enc_ie_facility_centrex(unsigned char **ntmode, msg_t *msg, unsigned char *cnip, int setup, int nt, struct misdn_bchannel *bc)
+static void enc_ie_facility (__u8 **ntmode, msg_t *msg, __u8 *facility, int facility_len, struct misdn_bchannel *bc)
 {
-       unsigned char centrex[256];
-       int i = 0;
-
-       if (!cnip)
-               return;
-
-       /* centrex facility */
-       centrex[i++] = FACILITY_CENTREX;
-       centrex[i++] = CENTREX_ID;
+       __u8 *ie_fac;
+       
+       Q931_info_t *qi;
 
-       /* cnip */
-       if (strlen((char *)cnip) > 15)
-       {
-/*             if (options.deb & DEBUG_PORT) */
-               cb_log(1,0,"%s: CNIP/CONP text too long (max 13 chars), cutting.\n", __FUNCTION__);
-               cnip[15] = '\0';
-       }
-       /*  dunno what the 8 bytes mean */
-       if (setup)
-       {
-               centrex[i++] = 0x17;
-               centrex[i++] = 0x02;
-               centrex[i++] = 0x02;
-               centrex[i++] = 0x44;
-               centrex[i++] = 0x18;
-               centrex[i++] = 0x02;
-               centrex[i++] = 0x01;
-               centrex[i++] = 0x09;
-       } else
-       {
-               centrex[i++] = 0x18;
-               centrex[i++] = 0x02;
-               centrex[i++] = 0x02;
-               centrex[i++] = 0x81;
-               centrex[i++] = 0x09;
-               centrex[i++] = 0x02;
-               centrex[i++] = 0x01;
-               centrex[i++] = 0x0a;
+       ie_fac = msg_put(msg, facility_len + 2);
+       if (bc->nt) {
+               *ntmode = ie_fac + 1;
+       } else {
+               qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN);
+               qi->QI_ELEMENT(facility) = ie_fac - (__u8 *)qi - sizeof(Q931_info_t);
        }
 
-       centrex[i++] = 0x80;
-       centrex[i++] = strlen((char *)cnip);
-       strcpy((char *)(&centrex[i]), (char *)cnip);
-       i += strlen((char *)cnip);
-       cb_log(4,0,"    cnip='%s'\n", cnip);
+       ie_fac[0] = IE_FACILITY;
+       ie_fac[1] = facility_len;
+       memcpy(ie_fac + 2, facility, facility_len);
 
-       /* encode facility */
-       enc_ie_facility(ntmode, msg, centrex, i, nt , bc);
+       FAC_DUMP(ie_fac, facility_len + 2, bc);
 }
 
-void dec_ie_facility_centrex(unsigned char *p, Q931_info_t *qi, unsigned char *centrex, int facility_len, unsigned char *cnip, int cnip_len, int nt, struct misdn_bchannel *bc)
+void fac_enc (__u8 **ntmsg, msg_t *msg, enum facility_type type,  union facility fac, struct misdn_bchannel *bc)
 {
+       __u8 facility[256];
+       int len;
 
-       int i = 0;
-       *cnip = '\0';
-       
-       if (facility_len >= 2)
-       {
-               if (centrex[i++] != FACILITY_CENTREX)
-                       return;
-               if (centrex[i++] != CENTREX_ID)
-                       return;
-       }
-
-       /* loop sub IEs of facility */
-       while(facility_len > i+1)
-       {
-               if (centrex[i+1]+i+1 > facility_len)
-               {
-                       printf("%s: ERROR: short read of centrex facility.\n", __FUNCTION__);
-                       return;
-               }
-               switch(centrex[i])
-               {
-               case 0x80:
-                       strnncpy(cnip, &centrex[i+2], centrex[i+1], cnip_len);
-                       cb_log(4,0,"    CENTREX cnip='%s'\n", cnip);
-                       break;
-               }
-               i += 1+centrex[i+1];
+       switch (type) {
+       case FACILITY_CALLDEFLECT:
+               len = enc_fac_calldeflect(facility, fac.calldeflect_nr, 1);
+               enc_ie_facility(ntmsg, msg, facility, len, bc);
+               break;
+       case FACILITY_CENTREX:
+       case FACILITY_NONE:
+               break;
        }
 }
 
+/*
+** Facility Decoding
+*/
 
+static int dec_fac_calldeflect (__u8 *p, int len, struct misdn_bchannel *bc)
+{
+       __u8 *end = p + len;
+       int offset,
+               pres;
 
+       if ((offset = dec_sequence(p, end)) < 0)
+               return -1;
+       p += offset;
 
-/* facility for CALL Deflect (known parts implemented only) */
-void enc_ie_facility_calldeflect(unsigned char **ntmode, msg_t *msg, unsigned char *nr, int nt, struct misdn_bchannel *bc)
-{
-       unsigned char fac[256];
+       if ((offset = dec_sequence(p, end)) < 0)
+               return -1;
+       p += offset;
        
-       if (!nr)
-               return;
+       if ((offset = dec_num_string(p, end, bc->fac.calldeflect_nr)) < 0)
+               return -1;
+       p += offset;
 
-       int len = strlen(nr);
-       /* calldeflect facility */
-       
-       /* cnip */
-       if (strlen((char *)nr) > 15)
-       {
-/*             if (options.deb & DEBUG_PORT) */
-               cb_log(1,0,"%s: NR text too long (max 13 chars), cutting.\n", __FUNCTION__);
-               nr[15] = '\0';
-       }
-       
-       fac[0]=FACILITY_CALLDEFLECT;    // ..
-       fac[1]=CALLDEFLECT_ID;
-       fac[2]=0x0f + len;      // strlen destination + 15 = 26
-       fac[3]=0x02;
-       fac[4]=0x01;
-       //fac[5]=0x70;
-       fac[5]=0x09;
-       fac[6]=0x02;
-       fac[7]=0x01;
-       fac[8]=0x0d;
-       fac[9]=0x30;
-       fac[10]=0x07 + len;     // strlen destination + 7 = 18
-       fac[11]=0x30;   // ...hm 0x30
-       fac[12]=0x02+ len;      // strlen destination + 2       
-       fac[13]=0x80;   // CLIP
-       fac[14]= len;   //  strlen destination 
-       
-       memcpy((unsigned char *)fac+15,nr,len);
-       fac[15+len]=0x01; //sending complete
-       fac[16+len]=0x01;
-       fac[17+len]=0x80;
-       
-       enc_ie_facility(ntmode, msg, fac, 17+len +1 , nt , bc);
-}
+       if ((offset = dec_bool(p, end, &pres)) < 0)
+               return -1;
+
+       cb_log(0, 0, "CALLDEFLECT: dest:%s pres:%s (not implemented yet)\n", bc->fac.calldeflect_nr, pres ? "yes" : "no");
+       bc->fac_type = FACILITY_CALLDEFLECT;
 
+       return 0;
+}
 
-void dec_ie_facility_calldeflect(unsigned char *p, Q931_info_t *qi, unsigned char *fac, int fac_len, unsigned char *cd_nr,  int nt, struct misdn_bchannel *bc)
+void fac_dec (__u8 *p, Q931_info_t *qi, enum facility_type *type,  union facility *fac, struct misdn_bchannel *bc)
 {
-       *cd_nr = '\0';
-       
-       if (fac_len >= 15)
-       {
-               if (fac[0] != FACILITY_CALLDEFLECT)
-                       return;
-               if (fac[1] != CALLDEFLECT_ID)
-                       return;
-       } else {
-               cb_log(1,bc->port, "IE too short: FAC_CALLDEFLECT\n");
-               return ;
-       }
-       
-       
-       
-       {
-               int dest_len=fac[2]-0x0f;
-               
-               if (dest_len <0 || dest_len > 15) {
-                       cb_log(1,bc->port, "IE is garbage: FAC_CALLDEFLECT\n");
-                       return ;
-               }
-               
-               if (fac_len < 15+dest_len) {
-                       cb_log(1,bc->port, "IE too short: FAC_CALLDEFLECT\n");
-                       return ;
-               }
-               
-               memcpy(cd_nr, &fac[15],dest_len);
-               cd_nr[dest_len]=0;
-               
-               cb_log(5,bc->port, "--> IE CALLDEFLECT NR: %s\n",cd_nr);
+       int len,
+               offset,
+               inner_len,
+               invoke_id,
+               op_tag,
+               op_val;
+       __u8 *end,
+                                 *begin = p;
+
+       if (!bc->nt) {
+               if (qi->QI_ELEMENT(facility))
+                       p = (__u8 *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1;
+               else
+                       p = NULL;
        }
-}
+       if (!p)
+               return;
 
+       offset = dec_len (p, &len);
+       if (offset < 0) {
+               cb_log(0, bc->port, "Could not decode FACILITY: dec_len failed!\n");
+               return;
+       }
+       p += offset;
+       end = p + len;
 
+       FAC_DUMP(p, len, bc);
 
-void fac_enc( unsigned char **ntmsg, msg_t * msg, enum facility_type type,  union facility fac, struct misdn_bchannel *bc)
-{
-       switch (type) {
-       case FACILITY_CENTREX:
-       {
-               int setup=0;
-               enc_ie_facility_centrex(ntmsg, msg, fac.cnip, setup, bc->nt, bc);
-       }
-               break;
-       case FACILITY_CALLDEFLECT:
-               enc_ie_facility_calldeflect(ntmsg, msg, fac.calldeflect_nr, bc->nt, bc);
-               break;
-       default:
-               cb_log(1,0,"Don't know how handle this facility: %d\n", type);
+       if (len < 3 || p[0] != SUPPLEMENTARY_SERVICE || p[1] != INVOKE) {
+               cb_log(0, bc->port, "Could not decode FACILITY: invalid or not supported!\n");
+               return;
        }
-}
+       p += 2;
 
-void fac_dec( unsigned char *p, Q931_info_t *qi, enum facility_type *type,  union facility *fac, struct misdn_bchannel *bc)
-{
-       int i, fac_len=0;
-       unsigned char facility[256];
+       offset = dec_len (p, &inner_len);
+       if (offset < 0) {
+               cb_log(0, bc->port, "Could not decode FACILITY: failed parsing inner length!\n");
+               return;
+       }
+       p += offset;
 
-       if (!bc->nt)
-       {
-               p = NULL;
-               if (qi->QI_ELEMENT(facility))
-                       p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1;
+       offset = dec_int (p, end, &invoke_id);
+       if (offset < 0) {
+               cb_log(0, bc->port, "Could not decode FACILITY: failed parsing invoke identifier!\n");
+               return;
        }
-       if (!p)
+       p += offset;
+
+       offset = _dec_int (p, end, &op_val, &op_tag);
+       if (offset < 0) {
+               cb_log(0, bc->port, "Could not decode FACILITY: failed parsing operation value!\n");
                return;
-       
-       fac_len = p[0] & 0xff;
+       }
+       p += offset;
 
-       memcpy(facility, p+1, fac_len);
-       
-       switch(facility[0]) {
-       case FACILITY_CENTREX:
-       {
-               int cnip_len=15;
-               
-               dec_ie_facility_centrex(p, qi,facility, fac_len, fac->cnip, cnip_len, bc->nt, bc);
-               
-               *type=FACILITY_CENTREX;
+       if (op_tag != OPERATION_VALUE || offset != 3) {
+               cb_log(0, bc->port, "Could not decode FACILITY: operation value tag 0x%x unknown!\n", op_tag);
+               return;
        }
-       break;
-       case FACILITY_CALLDEFLECT:
-               dec_ie_facility_calldeflect(p, qi,facility, fac_len, fac->calldeflect_nr,  bc->nt, bc);
-               
-               *type=FACILITY_CALLDEFLECT;
+
+       switch (op_val) {
+       case CALL_DEFLECT:
+               cb_log(0, bc->port, "FACILITY: Call Deflect\n");
+               dec_fac_calldeflect(p, len - (p - begin) + 1, bc);
+               break;
+       case AOC:
+               cb_log(0, bc->port, "FACILITY: AOC\n");
                break;
        default:
-               cb_log(3, bc->port, "Unknown Facility received: ");
-               i = 0;
-               while(i < fac_len)
-               {
-                       cb_log(3, bc->port, " %02x", facility[i]);
-                       i++;
-               }
-               cb_log(3, bc->port, "    facility\n");
-               
-               *type=FACILITY_NONE;
+               cb_log(0, bc->port, "FACILITY unknown: operation value 0x%x, ignoring ...\n", op_val);
        }
-       
-       
 }
-
-/*** FACILITY END **/
-