/************************************************************************/ /* SISCO SOFTWARE MODULE HEADER *****************************************/ /************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 1986 - 2001, All Rights Reserved */ /* */ /* PROPRIETARY AND CONFIDENTIAL */ /* */ /* MODULE NAME : ms_size.c */ /* PRODUCT(S) : MMSEASE */ /* */ /* MODULE DESCRIPTION : */ /* This module contains support functions for estimating maximum */ /* size of Variable Access service PDUs (Read, Write, and InfoRpt).*/ /* */ /* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */ /* */ /* MODIFICATION LOG : */ /* Date Who Rev Comments */ /* -------- --- ------ ------------------------------------------- */ /* 04/14/03 JRB 09 Eliminate compiler warnings. */ /* 04/02/03 JRB 08 Add UTF8string support (see RT_UTF8_STRING). */ /* 11/14/01 EJV 07 Added support for new MMS type UtcTime: */ /* ms_data_size: added case for RT_UTC_TIME */ /* 10/18/00 RKR 06 Changed RT_UNSIGNED in ms_data_size */ /* 09/15/00 MDE 05 Fixed MMS-EASE compile error */ /* 09/07/00 MDE 04 Added MMS-LITE support */ /* 02/10/98 MDE 03 Now don't use runtime type loops element */ /* 08/15/97 MDE 02 Minor cleanup */ /* 06/09/97 MDE 01 Changed Runtime Type, and it's use */ /* 04/02/97 DTL 7.00 MMSEASE 7.0 release. See MODL70.DOC for */ /* history. */ /************************************************************************/ #include "glbtypes.h" #include "sysincs.h" #include "glbsem.h" #include "mmsdefs.h" #include "mms_pvar.h" #include "mms_vvar.h" #include "asn1defs.h" #if defined(MMS_LITE) #include "mvl_defs.h" #endif /************************************************************************/ /* For debug version, use a static pointer to avoid duplication of */ /* __FILE__ strings. */ #if defined(DEBUG_SISCO) && defined(S_MT_SUPPORT) static ST_CHAR *thisFileName = __FILE__; #endif static ST_LONG address_size (VAR_ACC_ADDR *address, ST_BOOLEAN req); static ST_LONG obj_name_size (OBJECT_NAME *name, ST_BOOLEAN req); static ST_LONG ms_data_size (RUNTIME_TYPE *rt_ptr, ST_INT rt_num, ST_INT alt_len, ST_UCHAR *alt_ptr, ST_BOOLEAN req); static ST_INT arr_loop_level; static ST_INT *arr_loops; /************************************************************************/ /************************************************************************/ /* To allow the user to specify either MMS-EASE or UNKNOWN PDU style, */ /* the following definitions & data structures are used. */ /* See ms_size.doc for information on how these were derived. */ ST_INT ms_req_bld_id = UNKNOWN_BUILDER; ST_INT ms_resp_bld_id = UNKNOWN_BUILDER; /* Because the MMS-EASE ASN.1 tools are a bit conservitive, need to */ /* provide for a little extra overhead. */ ST_INT ms_size_oh_pad = ASN1_MAX_ELEMENT_OVERHEAD; ST_LONG large_el_oh[] = { 6L, /* MMS-EASE is PDU Builder */ 8L /* UNKNOWN PDU Builder */ }; ST_LONG med_el_oh[] = { 4L, /* MMS-EASE is PDU Builder */ 8L /* UNKNOWN PDU Builder */ }; ST_LONG small_el_oh[] = { 2L, /* MMS-EASE is PDU Builder */ 8L /* UNKNOWN PDU Builder */ }; #define LARGE_EL_REQ_OH large_el_oh[ms_req_bld_id] #define MED_EL_REQ_OH med_el_oh[ms_req_bld_id] #define SMALL_EL_REQ_OH small_el_oh[ms_req_bld_id] #define LARGE_EL_RESP_OH large_el_oh[ms_resp_bld_id] #define MED_EL_RESP_OH med_el_oh[ms_resp_bld_id] #define SMALL_EL_RESP_OH small_el_oh[ms_resp_bld_id] /************************************************************************/ /************************************************************************/ /* Overhead for various types of MMS ASN.1 data elements */ #define CONF_REQ_PDU_OH (ms_size_oh_pad+MED_EL_REQ_OH+SMALL_EL_REQ_OH+4) #define CONF_RESP_PDU_OH (ms_size_oh_pad+MED_EL_RESP_OH+SMALL_EL_RESP_OH+4) #define UNCONF_PDU_OH (ms_size_oh_pad+MED_EL_REQ_OH) /* Overhead for empty PDU's of various types */ #define READ_REQ_OH (CONF_REQ_PDU_OH + (MED_EL_REQ_OH * 2)) #define READ_RESP_OH (CONF_RESP_PDU_OH + (MED_EL_RESP_OH * 2)) #define WRITE_REQ_OH (CONF_REQ_PDU_OH + (MED_EL_REQ_OH * 2)) #define WRITE_RESP_OH (CONF_RESP_PDU_OH + MED_EL_RESP_OH) #define INFO_REQ_OH (UNCONF_PDU_OH + (MED_EL_REQ_OH * 2)) /* Overhead for various components of interest */ #define SPEC_IN_RSLT_SIZE (MED_EL_REQ_OH + 1) /* Object name overhead */ #define VMD_OBJ_NAME_REQ_OH SMALL_EL_REQ_OH #define DOM_OBJ_NAME_REQ_OH (MED_EL_REQ_OH + (SMALL_EL_REQ_OH * 2)) #define AA_OBJ_NAME_REQ_OH SMALL_EL_REQ_OH #define VMD_OBJ_NAME_RESP_OH SMALL_EL_RESP_OH #define DOM_OBJ_NAME_RESP_OH (MED_EL_RESP_OH + (SMALL_EL_RESP_OH * 2)) #define AA_OBJ_NAME_RESP_OH SMALL_EL_RESP_OH #define GENTIME_LEN 18L /************************************************************************/ /************************************************************************/ /************************************************************************/ /* ms_init_va_size */ /* This function is called from the user as a first step in estimating */ /* the maximum size of both the request and response PDUs of any of the */ /* Variable Access services (Read, Write, and InformationReport). This */ /* function initializes the values pointed to by req_size and resp_size */ /* to the overhead in the PDU (before determining the size of the data */ /* and specification of each variable to be included). The companion */ /* function ms_va_size is then called for each variable to add on the */ /* estimated size attributable to that variable. This function returns */ /* 0 for success, else an error code. */ /************************************************************************/ ST_RET ms_init_va_size (ST_INT op, ST_UCHAR spec_in_rslt, ST_INT16 var_acc_tag, OBJECT_NAME *vl_name, ST_LONG *req_size, ST_LONG *resp_size) { ST_RET ret; S_LOCK_COMMON_RESOURCES (); ret = SD_SUCCESS; switch (op) { case MMSOP_READ: /* initialize sizes to include all overhead for basic request */ *req_size = READ_REQ_OH; *resp_size = READ_RESP_OH; /* To send request for spec in result takes extra bytes, do does */ /* sending the VA spec in the response. */ if (spec_in_rslt) { *req_size += SPEC_IN_RSLT_SIZE; *resp_size += MED_EL_RESP_OH; } break; case MMSOP_WRITE: /* initialize sizes to include all overhead for basic request */ *req_size = WRITE_REQ_OH; *resp_size = WRITE_RESP_OH; break; case MMSOP_INFO_RPT: /* initialize sizes to include all overhead for basic request */ *req_size = INFO_REQ_OH; *resp_size = 0; break; default : ret = MVE_WRONG_OP; } /* If the variable specification is a named list, add in the name, else */ /* add in the sequence of sequence constructor overhead */ /* Note that this is done here, NOT in later calls to ms_va_size */ if (var_acc_tag == VAR_ACC_NAMEDLIST) /* list of variables case */ *req_size += MED_EL_REQ_OH + obj_name_size (vl_name,SD_TRUE); else *req_size += MED_EL_REQ_OH; /* seq of seq */ S_UNLOCK_COMMON_RESOURCES (); return (ret); } /************************************************************************/ /* ms_va_size */ /* This function is called from the user repetitively for each variable */ /* expected to be included in a Read, Write, or InfoRpt PDU. Note that */ /* the function ms_init_va_size should be called first to initialize */ /* the two outputs req_size and resp_size. The values pointed to by */ /* req_size and resp_size are incremented by the appropriate amount, */ /* so that after this function has been called for all variables, they */ /* represent valid estimates of the size of the request and response */ /* PDUs for the given service (indicated by op). In some cases, the */ /* type of the variable may be indicated both by the vl_ptr argument */ /* and by the type argument; in this case, both must still be specified */ /* and it is up to the user to verify that they are equal. This */ /* function returns 0 for success, else an error code. */ /* */ /* NOTE: In the case of accessing a variable list object (as opposed */ /* to a list of variable objects), a variable_list structure must */ /* be built and filled in appropriately, with var_spec_tag set to */ /* 5. (Otherwise, the value pointed to by req_size and, in */ /* certain cases, the value pointed to by resp_size will be */ /* much larger than necessary. */ /************************************************************************/ #if !defined(MMS_LITE) ST_RET ms_va_size (ST_INT op, ST_UCHAR spec_in_rslt, ST_INT16 var_acc_tag, VARIABLE_LIST *vl_ptr, NAMED_TYPE *type, ST_LONG *req_size, ST_LONG *resp_size) #else ST_RET ms_va_size (ST_INT op, ST_UCHAR spec_in_rslt, ST_INT16 var_acc_tag, VARIABLE_LIST *vl_ptr, ST_INT type_id, ST_LONG *req_size, ST_LONG *resp_size) #endif { ST_LONG size; ST_INT alt_acc_len; RUNTIME_TYPE *rt; ST_INT rt_num; #if defined(MMS_LITE) ST_INT rc; #endif #if !defined(MMS_LITE) rt = type->rt_head; rt_num = type->rt_num; #else rc = mvl_get_runtime (type_id, &rt, &rt_num); if (rc != SD_SUCCESS) return (rc); #endif S_LOCK_COMMON_RESOURCES (); /* non functional at this time */ if (vl_ptr->alt_access_pres) alt_acc_len = vl_ptr->alt_access.len; else alt_acc_len = 0; /* get the size of the data element based on the MMS type and add the */ /* data element size to the target PDU */ if (op == MMSOP_READ) /* read response carries data */ { size = ms_data_size (rt, rt_num, alt_acc_len, vl_ptr->alt_access.data,SD_FALSE); *resp_size += size; } else /* WRITE or INFO */ { size = ms_data_size (rt, rt_num, alt_acc_len, vl_ptr->alt_access.data,SD_TRUE); *req_size += size; if (op == MMSOP_WRITE) /* Write response carries result */ *resp_size += SMALL_EL_RESP_OH + 1; /* assume failure (+1) */ } if (size <= 0) /* verify the data size was OK */ { S_UNLOCK_COMMON_RESOURCES (); return (MVE_TYPENAME); } /* Now calculate the size of the variable access spec for this var. */ /* Only req'd if var_acc_tag is list of vars (list name added already) */ if (var_acc_tag == VAR_ACC_VARLIST) { size = MED_EL_REQ_OH; switch (vl_ptr->var_spec.var_spec_tag) { case VA_SPEC_NAMED : size += obj_name_size (&vl_ptr->var_spec.vs.name,SD_TRUE); break; #if !defined(MMS_LITE) case VA_SPEC_DESCRIBED : /* described gets address & type */ size += type->asn1len + address_size (&vl_ptr->var_spec.vs.address,SD_TRUE); break; #endif case VA_SPEC_ADDRESSED : size += address_size (&vl_ptr->var_spec.vs.address,SD_TRUE); break; case VA_SPEC_SCATTERED : /* not currently supported */ case VA_SPEC_INVALIDATED : default: S_UNLOCK_COMMON_RESOURCES (); return (MVE_VM_SERVICE_NOTSUPP); } /* not really supported at this time, leave in anyway */ if (vl_ptr->alt_access_pres) size += (ST_LONG) alt_acc_len; /* Now we have the variable specification size, add to the request */ *req_size += size; /* Now, if spec in result requested, calculate VA spec size for resp */ if (op == MMSOP_READ && spec_in_rslt) { size = MED_EL_RESP_OH; switch (vl_ptr->var_spec.var_spec_tag) { case VA_SPEC_NAMED : size += obj_name_size (&vl_ptr->var_spec.vs.name,SD_FALSE); break; #if !defined(MMS_LITE) case VA_SPEC_DESCRIBED : /* described gets address & type */ size += type->asn1len + address_size (&vl_ptr->var_spec.vs.address,SD_FALSE); break; #endif case VA_SPEC_ADDRESSED : size += address_size (&vl_ptr->var_spec.vs.address,SD_FALSE); break; case 5: /* variable list object case */ S_UNLOCK_COMMON_RESOURCES (); return (SD_SUCCESS); /* No variable spec. req'd */ break; case VA_SPEC_SCATTERED : /* not currently supported */ case VA_SPEC_INVALIDATED : default: S_UNLOCK_COMMON_RESOURCES (); return (MVE_VM_SERVICE_NOTSUPP); } *resp_size += size; } } /* if VAR_ACC_VARLIST */ /* Now, if WRITE, need to add write result */ S_UNLOCK_COMMON_RESOURCES (); return (SD_SUCCESS); } /************************************************************************/ /* obj_name_size */ /* This function is called from ms_init_va_size and ms_va_size to */ /* calculate (exactly) the size that a particular object name will take */ /* up in a request or response PDU for the Read, Write, and InfoReport */ /* services. Returns the size of the object name, or 0 if there is a */ /* problem. */ /************************************************************************/ static ST_LONG obj_name_size (OBJECT_NAME *name, ST_BOOLEAN req) { ST_LONG oh; switch (name->object_tag) { case VMD_SPEC : if (req) /* if calculating for a request */ oh = VMD_OBJ_NAME_REQ_OH; else oh = VMD_OBJ_NAME_RESP_OH; return (oh + (ST_LONG) strlen (name->obj_name.vmd_spec)); break; case DOM_SPEC : if (req) /* if calculating for a request */ oh = DOM_OBJ_NAME_REQ_OH; else oh = DOM_OBJ_NAME_RESP_OH; return (oh + (ST_LONG) (strlen (name->obj_name.item_id) + strlen (name->domain_id))); break; case AA_SPEC : if (req) /* if calculating for a request */ oh = AA_OBJ_NAME_REQ_OH; else oh = AA_OBJ_NAME_RESP_OH; return (oh + (ST_LONG) strlen (name->obj_name.aa_spec)); break; } mms_op_err = MVE_OBJECT_SCOPE; return (0L); /* indicates error condition */ } /************************************************************************/ /* address_size */ /* This function is called from ms_va_size to calculate (exactly) the */ /* size that a particular address will take up in a request or response */ /* PDU for the Read, Write, and InfoReport services. Returns the size */ /* of the address, or 0 if there is a problem. */ /************************************************************************/ static ST_LONG address_size (VAR_ACC_ADDR *address, ST_BOOLEAN req) { ST_LONG size; ST_LONG numaddr; switch (address->addr_tag) { case NUM_ADDR : if (req) /* if calculating for a request */ size = SMALL_EL_REQ_OH; else size = SMALL_EL_RESP_OH; numaddr = address->addr.num_addr; if (numaddr < 128L) size += 1; else if (numaddr < 32768L) size += 2; else if (numaddr < 8388608L) size += 3; else size += 4; break; case SYM_ADDR : if (req) /* if calculating for a request */ size = MED_EL_REQ_OH; else size = MED_EL_RESP_OH; size += strlen (address->addr.sym_addr); break; case UNCON_ADDR : if (req) /* if calculating for a request */ size = MED_EL_REQ_OH; else size = MED_EL_RESP_OH; size += address->addr.unc_addr.unc_len; break; default: mms_op_err = MVE_ADDR; return (0); } return (size); } /************************************************************************/ /************************************************************************/ /************************************************************************/ /* ms_data_size */ /* This function is called from the user or the MMS-EASE virtual */ /* machine to estimate the size that the data corresponding to a */ /* particular type will take up in a request or response PDU for the */ /* Read, Write, and InfoReport services. Returns the size of the data, */ /* or 0 if there is a problem. (A rough error code can be found in */ /* mms_op_err; a more detailed one in asn1_pdu_dec_err.) */ /* */ /* Note: Alternate access may be used to modify a type, thereby */ /* affecting the size of the corresponding data. However, */ /* the alternate access inputs are disregarded at this time. */ /* This will be fixed in a later revision. */ /************************************************************************/ static ST_LONG ms_data_size (RUNTIME_TYPE *rt_ptr, ST_INT rt_num, ST_INT alt_len, ST_UCHAR *alt_ptr, ST_BOOLEAN req) { ST_LONG size; RUNTIME_TYPE *rt_end; ST_LONG med_oh; ST_LONG small_oh; ST_UINT ellen; ST_LONG len; ST_INT arr_loop_buf[ASN1_MAX_LEVEL]; ST_UINT el_size; /* num bytes to encode data */ arr_loop_level = 0; arr_loops = arr_loop_buf; if (req) /* if calculating for a request */ { med_oh = MED_EL_REQ_OH; small_oh = SMALL_EL_REQ_OH; } else /* calculating for response */ { med_oh = MED_EL_RESP_OH; small_oh = SMALL_EL_RESP_OH; } rt_end = rt_ptr + rt_num; /* end block */ size = 0; while (rt_ptr < rt_end) { if (rt_ptr->el_tag == RT_ARR_END) /* treat case of array ending */ { /* loops set at start of array */ if (--arr_loops[arr_loop_level] > 0) /* if need to do next ar elmnt */ rt_ptr -= rt_ptr->u.arr.num_rt_blks; /* mv rt_ptr to start of arr */ else --arr_loop_level; } ellen = abs (rt_ptr->u.p.el_len); /* make sure positive */ switch (rt_ptr->el_tag) { case RT_ARR_START : /* arrays don't have to be aligned */ ++arr_loop_level; arr_loops[arr_loop_level] = rt_ptr->u.arr.num_elmnts; /* let fall through to STR_MMSOP_START */ case RT_STR_START : /* structure starting */ size += med_oh; /* add in data element overhead */ break; case RT_UNSIGNED : /* unsigned integer */ size += small_oh + ellen + 1; /* high order bit set needs 0x00*/ break; case RT_UTC_TIME : /* UTC time */ case RT_BINARY_TIME : /* binary time */ case RT_INTEGER : /* integer */ case RT_FLOATING_POINT : /* float */ size += small_oh + ellen; break; case RT_VISIBLE_STRING : /* visible string */ case RT_OCTET_STRING : /* octet string */ size += ellen; if (ellen < 128) size += small_oh; else size += med_oh; break; case RT_GENERAL_TIME : /* generalized time */ size += small_oh + GENTIME_LEN; break; case RT_BOOL : /* boolean */ size += small_oh +1; break; case RT_BCD : /* bcd */ size += small_oh + ellen; break; case RT_BIT_STRING : /* bit string */ len = 1 + CALC_BIT_LEN (ellen); if (len < 128) size += small_oh + len; /* add in data element overhead */ else size += med_oh + len; /* add in data element overhead */ break; case RT_UTF8_STRING : /* Unicode UTF8 string */ el_size = ellen*4; /* Could take 4 bytes per char */ size += el_size; if (el_size < 128) size += small_oh; else size += med_oh; break; case RT_ARR_END : /* array done */ case RT_STR_END : /* structure done */ break; default : /* should not be any other tag */ return (0); break; } rt_ptr++; /* point to next block */ } /* while (rt blocks) */ return (size); }