/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	1997 - 2003, All Rights Reserved				*/
/*									*/
/*			PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : cosp_dec.c						*/
/* PRODUCT(S)  : MOSI Stack (over TP4)					*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*		This file implements the decoding of COSP SPDUs.	*/
/*									*/
/*  For information see the:						*/
/*	ISO 8326 "Information processing systems - Open Systems		*/
/*	  Interconnection - Basic connection oriented session service	*/
/*	  definition.							*/
/*	ISO 8327 "Information processing systems - Open Systems		*/
/*	  Interconnection - Basic connection oriented session protocol	*/
/*	  specification.						*/
/*	ISO 8327/ADD.2 (Draft for Version2).				*/
/*									*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/*			cosp_dec_cn_ac					*/
/*			cosp_dec_rf					*/
/*			cosp_dec_fn_dn					*/
/*			cosp_dec_ab					*/
/*			cosp_dec_dt					*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 08/19/03  EJV    08     Allow for ACCEPT  User Data <= COSP_MAX_UDATA*/
/* 06/05/02  JRB    07     Allow Called SSEL=NULL on connect if		*/
/*			   configured SSEL=NULL.			*/
/* 04/26/01  EJV    06     Correction to version check in CN/AC.	*/
/*			   Corrected spelling.				*/
/* 09/13/99  MDE    05     Added SD_CONST modifiers			*/
/* 01/08/99  EJV    04     Added decoding of param 17 in FINISH SPDU.	*/
/*			   Make sure param 17 is extracted properly.	*/
/* 08/13/98  JRB    03     Lint cleanup.				*/
/* 05/27/97  JRB   7.00    MMSEASE 7.0 release.				*/
/* 03/20/97  EJV    02     Enhanced logging.				*/
/* 01/17/97  EJV    01     Created					*/
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;	/* Define for SLOG mechanism	*/
#endif

#include "acse2.h"

#include "cosp_log.h"
#include "cosp.h"
#include "cosp_usr.h"


	/*--------------------------------------*/
	/*	Local function prototypes	*/
	/*--------------------------------------*/

static ST_UINT16  cosp_dec_len   (ST_UCHAR *buf, ST_UINT *bytes);
static ST_RET cosp_validate_cn_ac (COSP_CN_AC *dec_par, ST_UCHAR spdu_type);


/************************************************************************/
/*			cosp_dec_cn_ac					*/
/*----------------------------------------------------------------------*/
/* Function to decode a CONNECT or ACCEPT SPDU.				*/
/* Note if User Data are present in received SPDU then the length in the*/
/* dec_par->udata_len will be > 0. The dec_par->udata_ptr points into	*/
/* the spdu_buf (it is not an allocated pointer).			*/
/* 									*/
/* Parameters:								*/
/*  COSP_CN_AC		*dec_par	ptr to decoded parameters struct*/
/*  char        	*spdu_buf	Pointer to received SPDU buf	*/
/*  ST_UINT     	 spdu_len	Length of received SPDU		*/
/*  ST_UCHAR		 spdu_type	SPDU type to decode (CN, AC)	*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_dec_cn_ac (COSP_CN_AC *dec_par, char *spdu_buf, ST_UINT spdu_len, 
			ST_UCHAR spdu_type)
{
ST_RET		ret;
ST_UCHAR	       *dec_buf;
ST_UINT	dec_len;
ST_UCHAR		param_code;
ST_UINT	param_len;
ST_UINT	group_len;
ST_UINT	len_bytes;	/* number of bytes for encoded len (1 or 3) */
ST_UINT	idx;

  ret = SD_SUCCESS;

  dec_buf = (ST_UCHAR *) spdu_buf;
  dec_len = 0;
  idx = 0;

  /*-----------------------------------------------------*/
  /* set defaults in case params are not present in SPDU */
  /*-----------------------------------------------------*/

  memset (dec_par, 0, sizeof (COSP_CN_AC));
  dec_par->ver_num        = COSP_VER1;		/*!default ver is Version1 */
  dec_par->ses_urequir [0]= 0x03;		/* bits 9-16: 9 and 10 set */
  dec_par->ses_urequir [1]= 0x49;		/* bits 1-8: 1,4 and 7 set */

  /* in addition memset is equivalent to following con params settings */
  /* dec_par->prot_option         = 0;   rcv extended concatination not supported	*/
  /* dec_par->initiator_tsdu_size = 0;   SSDU segmenting from initiator not supported	*/
  /* dec_par->responder_tsdu_size = 0;   SSDU segmenting from responder not supported	*/
  /* dec_par->loc_ssel [0]        = 0;   called SSEL is NULL				*/
  /* dec_par->rem_ssel [0]        = 0;   calling SSEL is NULL				*/
  /* dec_par->udata_len           = 0;   User Data length  				*/
  /* dec_par->udata_ptr           = 0;   User Data pointer (points into spdu_buf!)	*/

  /* we will use the first while loop to avoid excesive nesting of if...else... */

  while (SD_TRUE)
    {
    /* NOTE: we should never return back from bottom of the loop here!!! */

    /* the decoded len should be comparable with spdu_len passed to this func */
    dec_len = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);
    if (dec_len + 1+len_bytes != spdu_len)
      {
      ret = COSP_ERR_DEC_INV_LEN;
      COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid SPDU len=%u or decoded len=%u.",
        spdu_len, dec_len + 1+len_bytes);
      break;
      }
    idx += 1+len_bytes;		/* 1 for SPDU code */

    /* decode all connect parameters */
    while (dec_len > 0)
      {
      param_code = dec_buf [idx];
      param_len  = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);

      /* signal error if param_len is greater then decoding length left */
      if (param_len + 1+len_bytes > dec_len)
        {
        ret = COSP_ERR_DEC_INV_LEN;
        COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid parameter len=%u (length left to decode len=%u).",
          param_len + 1+len_bytes, dec_len);
	break;
	}
      dec_len -= 1+len_bytes;
      idx     += 1+len_bytes;

      /* do not try to decode if param_len is 0 (param is empty)	*/
      if (param_len == 0)
        continue;

      switch (param_code)
        {
	case  1:		/* PGI - Connection Identifier group 	*/
	  /* we will ignore this group (skip all decoding) */
	break;

	case  5:		/* PGI - Connect/Accept Item group	*/
          group_len = param_len;
	  while (group_len > 0)
	    {
            param_code = dec_buf [idx];
            param_len  = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);

            /* signal error if param_len is greater then decoding length left */
            if (param_len + 1+len_bytes > group_len)
              {
              ret = COSP_ERR_DEC_INV_LEN;
              COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid parameter len=%u (PGI-5 group length left to decode len=%u).",
                param_len + 1+len_bytes, group_len);
              break;
              }
            dec_len   -= 1+len_bytes;
            group_len -= 1+len_bytes;
            idx       += 1+len_bytes;

            /* do not try to decode if param_len is 0 (param is empty)	*/
            if (param_len == 0)
              continue;

            switch (param_code)
              {
              case  19:			/* PI - Protocol Options	*/
		if (param_len == 1)
                  dec_par->prot_option = dec_buf [idx];
		else
                  {
                  ret = COSP_ERR_DEC_INV_LEN;
                  COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI-19 parameter len=%u (should be len=1).",
                    param_len);
                  }
	      break;

	      case  21:			/* PI - TSDU Max Size		*/
		if (param_len == 4)
		  {
                  dec_par->initiator_tsdu_size = 
		    ((ST_UINT16) dec_buf [idx]   << 8) | (ST_UINT16) dec_buf [idx+1];
                  dec_par->responder_tsdu_size =
		    ((ST_UINT16) dec_buf [idx+2] << 8) | (ST_UINT16) dec_buf [idx+3];
		  }
		else
                  {
                  ret = COSP_ERR_DEC_INV_LEN;
                  COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI-21 parameter len=%u (should be len=4).",
                    param_len);
                  }
	      break;

	      case  22:			/* PI - Version Number		*/
		if (param_len == 1)
                  dec_par->ver_num = dec_buf [idx];
		else
                  {
                  ret = COSP_ERR_DEC_INV_LEN;
                  COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI-22 parameter len=%u (should be len=1)",
                    param_len);
                  }
	      break;

	      case  23:			/* PI - Initial Serial Number	*/
                /* we will ignore this param (skip all decoding) */
	      break;

	      case  26:			/* PI - Token Setting Item	*/
                /* we will ignore this param (skip all decoding) */
	      break;

	      default:
	        ret = COSP_ERR_DEC_INV_PI_CODE;
                COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI code=%d in PGI-5",
                  param_code);
	      } /* end of switch */

	    if (ret == SD_SUCCESS)
	      {
	      /* adjust processing vars by the length of param processed */
              dec_len   -= param_len;
              group_len -= param_len;
              idx       += param_len;
	      }
	    else
	      break;	/* error, exit this loop */
	    } /* end of while (group_len > 0) */

	  param_len = 0;	/* dec_len & idx already adjusted */
	break;

        case  16:			/* PI - Token Item	*/
	  if (spdu_type == COSP_SI_ACCEPT)
            /* we will ignore this param (skip all decoding) */
            ;
	  else
            {
	    ret = COSP_ERR_DEC_INV_PI_CODE;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI code=%d", param_code);
            }
	break;

	case  20:	/* PI -  Session User Requirements param */
	  if (param_len == 2)
	    {
            dec_par->ses_urequir [0]= dec_buf [idx];	/* bits 9-16	*/
            dec_par->ses_urequir [1]= dec_buf [idx+1];	/* bits 1-8	*/
	    }
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI-20 parameter len=%u (should be len=2).",
              param_len);
            }
	break;

	case  51:	/* PI -  Calling Session Selector param */
          if (param_len <= MAX_SSEL_LEN) /* 16 */
            {
            if (spdu_type == COSP_SI_CONNECT)
              {
              dec_par->rem_ssel [0] = (ST_UCHAR) param_len;
              memcpy (&dec_par->rem_ssel [1], &dec_buf [idx], param_len);
              }
            else
              {
              /* if COSP_SI_ACCEPT then same as Calling in S-CONNECT */
              dec_par->loc_ssel [0] = (ST_UCHAR) param_len;
              memcpy (&dec_par->loc_ssel [1], &dec_buf [idx], param_len);
	      }
	    }
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid PI-51 parameter len=%u (should be len<=%u).",
              param_len, MAX_SSEL_LEN);
            }
	break;

	case  52:	/* PI - Called(CN)/Responding(AC) Session Selector param */
	  if (param_len <= MAX_SSEL_LEN) /* 16 */
	    {
            if (spdu_type == COSP_SI_CONNECT)
              {
              dec_par->loc_ssel [0] = (ST_UCHAR) param_len;
	      memcpy (&dec_par->loc_ssel [1], &dec_buf [idx], param_len);
	      }
	    else
	      {
              /* if COSP_SI_ACCEPT then Responder SSEL */
              dec_par->rem_ssel [0] = (ST_UCHAR) param_len;
	      memcpy (&dec_par->rem_ssel [1], &dec_buf [idx], param_len);
	      }
	    }
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid PI-52 parameter len=%u (should be len<=%u).",
              param_len, MAX_SSEL_LEN);
            }
	break;

	case  193:	/* PGI - User Data - used only if len <=512 in CONNECT and	*/
                        /* len up to 65539 total SPDU size in ACCEPT (ISO 8327/DAD2)	*/
          if ((spdu_type == COSP_SI_CONNECT && param_len <= 512) ||
	      (spdu_type == COSP_SI_ACCEPT  && param_len <= COSP_MAX_UDATA))
            {
            dec_par->udata_len = param_len;
            dec_par->udata_ptr = &dec_buf [idx];
            }
          else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid PI-193 (User Data) parameter len=%u (max should be len<=%u).",
              param_len, (spdu_type == COSP_SI_CONNECT) ? 512 : COSP_MAX_UDATA);
            }
	break;

	case  194:	/* PGI - Extended User Data - used if len>512	*/
          if (param_len <= COSP_MAX_UDATA_CON)	/* and len<=10240 	*/
            {
            dec_par->udata_len = param_len;
            dec_par->udata_ptr = &dec_buf [idx];
            }
          else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: invalid PI-194 (User Data) parameter len=%u (should be len<=%u).",
              param_len, COSP_MAX_UDATA_CON);
            }
	break;

	default:
	  ret = COSP_ERR_DEC_INV_PI_CODE;
          COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: invalid PI code=%d", param_code);
	} /* end of switch */

      /* adjust processing vars by the length of param processed */
      if (ret == SD_SUCCESS)
        {
        dec_len -= param_len;
        idx     += param_len;
	}
      else
        break;
      } /* end while len to decode */

    break;	/* this is end of decoding, exit loop unconditionally */
    }  /* end of while (SD_TRUE) loop */

  if (ret == SD_SUCCESS)
    /* validate the received SPDU */
    ret = cosp_validate_cn_ac (dec_par, spdu_type);

  return (ret);
}


/************************************************************************/
/*			cosp_dec_rf					*/
/*----------------------------------------------------------------------*/
/* Function to decode a REFUSE SPDU.					*/
/* Note if User Data are present in received SPDU then the length in the*/
/* dec_par->udata_len will be > 0. The dec_par->udata_ptr points into	*/
/* the spdu_buf (it is not an allocated pointer).			*/
/* 									*/
/* Parameters:								*/
/*  COSP_RF		*dec_par	ptr to decoded parameters struct*/
/*  char        	*spdu_buf	Pointer to received SPDU buf	*/
/*  ST_UINT     	 spdu_len	Length of received SPDU		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_dec_rf (COSP_RF *dec_par, char *spdu_buf, ST_UINT spdu_len)
{
ST_RET		ret;
ST_UCHAR	       *dec_buf;
ST_UINT	dec_len;
ST_UCHAR		param_code;
ST_UINT	param_len;
ST_UINT	len_bytes;	/* number of bytes for encoded len (1 or 3) */
ST_UINT  	idx;

  ret = SD_SUCCESS;

  dec_buf = (ST_UCHAR *) spdu_buf;
  dec_len = 0;
  idx = 0;

  /*-----------------------------------------------------*/
  /* set defaults in case params are not present in SPDU */
  /*-----------------------------------------------------*/

  memset (dec_par, 0, sizeof (COSP_RF));
  dec_par->disconnect          = 1;		/* default disconnect transport	*/
  dec_par->ver_num             = COSP_VER1;	/* default is Version1		*/
  dec_par->ses_urequir [0]     = 0x03;		/* bits 9-16: 9 and 10 set	*/
  dec_par->ses_urequir [1]     = 0x49;		/* bits 1-8: 1,4 and 7 set	*/

  /* in addition memset is equivalent to following con params settings	*/
  /* dec_par->reason	     = 0;	rejected by called SS (SS-user?)*/
  /* dec_par->udata_len      = 0;	Length of User Data		*/
  /* dec_par->udata_ptr      = 0;       Pointer to User Data (points into spdu_buf!)	*/

  /* we will use the first while loop to avoid excesive nesting of if...else... */

  while (SD_TRUE)
    {
    /* NOTE: we should never return back from bottom of the loop here!!! */

    /* the decoded len should be comparable with spdu_len passed to this func */
    dec_len = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);
    if (dec_len + 1+len_bytes != spdu_len)
      {
      ret = COSP_ERR_DEC_INV_LEN;
      COSP_LOG_ERR2 ("COSP-ERROR: Decoding RF: invalid SPDU len=%u or decoded len=%u.",
        spdu_len, dec_len + 1+len_bytes);
      break;
      }
    idx += 1+len_bytes;		/* 1 for RF SPDU code */

    /* decode all REFUSE parameters */
    while (dec_len > 0)
      {
      param_code = dec_buf [idx];
      param_len  = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);

      /* signal error if param_len is greater then decoding length left */
      if (param_len + 1+len_bytes > dec_len)
        {
        ret = COSP_ERR_DEC_INV_LEN;
        COSP_LOG_ERR2 ("COSP-ERROR: Decoding RF: invalid parameter len=%u (length left to decode len=%u).",
          param_len + 1+len_bytes, dec_len);
	break;
	}
      dec_len -= 1+len_bytes;
      idx     += 1+len_bytes;

      /* do not try to decode if param_len is 0 (param is empty)	*/
      if (param_len == 0)
        continue;

      switch (param_code)
        {
	case  1:		/* PGI - Connection Identifier group 	*/
	  /* we will ignore this group (skip all decoding) */
	break;

        case  17:			/* PI - Transport Disconnect	*/
	  if (param_len == 1)
            dec_par->disconnect = (ST_BOOLEAN)(dec_buf [idx] & 0x01);
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding RF: invalid PI-17 parameter len=%u (should be len=1).",
              param_len);
            }
	break;

	case  20:	/* PI -  Session User Requirements param */
	  if (param_len == 2)
	    {
            dec_par->ses_urequir [0]= dec_buf [idx];	/* bits 9-16	*/
            dec_par->ses_urequir [1]= dec_buf [idx+1]; /* bits 1-8	*/
	    }
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding RF: invalid PI-20 parameter len=%u (should be len=2).",
              param_len);
            }
	break;

        case  22:			/* PI - Version Number		*/
	  if (param_len == 1)
            dec_par->ver_num = dec_buf [idx];
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding RF: invalid PI-22 parameter len=%u (should be len=1).",
              param_len);
            }
	break;

	case  50:			/* PI -  Reason Code param */
          dec_par->reason = dec_buf [idx];	/* refuse reason code	*/

          if (param_len > 1)
            {
            dec_par->udata_len = param_len-1;
            dec_par->udata_ptr = &dec_buf [idx+1];
            }
	break;

	default:
	  ret = COSP_ERR_DEC_INV_PI_CODE;
          COSP_LOG_ERR1 ("COSP-ERROR: Decoding RF: invalid PI code=%d", param_code);
	} /* end of switch */

      /* adjust processing vars by the length of param processed */
      if (ret == SD_SUCCESS)
        {
        dec_len -= param_len;
        idx     += param_len;
	}
      else
        break;
      } /* end while len to decode */

    break;	/* this is end of decoding, exit loop unconditionally */
    }  /* end of while (SD_TRUE) loop */

  /* validate the received SPDU */
  if (ret == SD_SUCCESS)
    {
    /* User Data may be present only if refuse reason=2	*/
    if (dec_par->reason != COSP_RF_REASON_U_REJECT)
      if (dec_par->udata_len > 0)
        {
        ret = COSP_ERR_DEC_INV_RF_UDATA;
        COSP_LOG_ERR1 ("COSP-ERROR: Decoding RF: User Data present (len=%u) when Refuse Reason!=2",
          dec_par->udata_len);
        }
    }

  return (ret);
}


/************************************************************************/
/*			cosp_dec_fn_dn					*/
/*----------------------------------------------------------------------*/
/* Function to decode a FINISH or DISCONNECT SPDU.			*/
/* Note if User Data are present in received SPDU then the length in the*/
/* dec_par->udata_len will be > 0. The dec_par->udata_ptr points into	*/
/* the spdu_buf (it is not an allocated pointer).			*/
/* 									*/
/* Parameters:								*/
/*  COSP_FN_DN		*dec_par	Ptr to decoded parameters struct*/
/*  char        	*spdu_buf	Pointer to received SPDU buf	*/
/*  ST_UINT     	 spdu_len	Length of received SPDU		*/
/*  ST_UCHAR		 spdu_type	SPDU type to decode (FN, DN)	*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_dec_fn_dn (COSP_FN_DN *dec_par, char *spdu_buf, ST_UINT spdu_len,
			ST_UCHAR spdu_type)
{
ST_RET		ret;
ST_UCHAR	       *dec_buf;
ST_UINT	dec_len;
ST_UCHAR		param_code;
ST_UINT	param_len;
ST_UINT	len_bytes;	/* number of bytes for encoded len (1 or 3) */
ST_UINT  	idx;

  ret = SD_SUCCESS;

  dec_buf = (ST_UCHAR *) spdu_buf;
  dec_len = 0;
  idx = 0;

  /*-----------------------------------------------------*/
  /* set defaults in case params are not present in SPDU */
  /*-----------------------------------------------------*/

  memset (dec_par, 0, sizeof (COSP_FN_DN));
  if (spdu_type == COSP_SI_FINISH)
    dec_par->disconnect        = 1;	/* default disconnect transport	*/

  /* in addition memset is equivalent to following con params settings	*/
  /* dec_par->udata_len      = 0;	Length of User Data		*/
  /* dec_par->udata_ptr      = 0;       Pointer to User Data (points into spdu_buf!)	*/

  /* we will use the first while loop to avoid excesive nesting of if...else... */

  while (SD_TRUE)
    {
    /* NOTE: we should never return back from bottom of the loop here!!! */

    /* the decoded len should be comparable with spdu_len passed to this func */
    dec_len = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);
    if (dec_len + 1+len_bytes != spdu_len)
      {
      ret = COSP_ERR_DEC_INV_LEN;
      COSP_LOG_ERR2 ("COSP-ERROR: Decoding FN/DN: invalid SPDU len=%u or decoded len=%u.",
        spdu_len, dec_len + 1+len_bytes);
      break;
      }
    idx += 1+len_bytes;		/* 1 for FN/DN SPDU code */

    /* decode all FINISH / DISCONNECT parameters */
    while (dec_len > 0)
      {
      param_code = dec_buf [idx];
      param_len  = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);

      /* signal error if param_len is greater then decoding length left */
      if (param_len + 1+len_bytes > dec_len)
        {
        ret = COSP_ERR_DEC_INV_LEN;
        COSP_LOG_ERR2 ("COSP-ERROR: Decoding FN/DN: invalid parameter len=%u (length left to decode len=%u).",
          param_len + 1+len_bytes, dec_len);
	break;
	}
      dec_len -= 1+len_bytes;
      idx     += 1+len_bytes;

      /* do not try to decode if param_len is 0 (param is empty)	*/
      if (param_len == 0)
        continue;

      switch (param_code)
        {
	case  17:	/* PI - Transport Disconnect */
          /* this param is valid only in FINISH SPDU */
          if (spdu_type == COSP_SI_FINISH)
            {
            dec_par->disconnect = (ST_BOOLEAN)(dec_buf [idx] & 0x01);
            if (dec_par->disconnect != 1)
              {
              /* we do NOT support reusing transport connection */
              ret = COSP_ERR_DEC_INV_PI_CODE;
              COSP_LOG_ERR1 ("COSP-ERROR: Decoding FN: PI code=%d (keep transport connection not supported)", param_code);
              }
            }
          else
            {
            ret = COSP_ERR_DEC_INV_PI_CODE;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding DN: invalid PI code=%d", param_code);
            }
	break;

	case  193:	/* PGI - User Data group */
          dec_par->udata_len = param_len;
          dec_par->udata_ptr = &dec_buf [idx];
	break;

	default:
	  ret = COSP_ERR_DEC_INV_PI_CODE;
          COSP_LOG_ERR1 ("COSP-ERROR: Decoding FN/DN: invalid PI code=%d", param_code);
	} /* end of switch */

      /* adjust processing vars by the length of param processed */
      if (ret == SD_SUCCESS)
        {
        dec_len -= param_len;
        idx     += param_len;
	}
      else
        break;
      } /* end while len to decode */

    break;	/* this is end of decoding, exit loop unconditionally */
    }  /* end of while (SD_TRUE) loop */

  /* validate the received SPDU */
  if (ret == SD_SUCCESS)
    {
    if (dec_par->udata_len == 0)
      {
      ret = COSP_ERR_INV_UDATA_LEN;
      COSP_LOG_ERR0 ("COSP-ERROR: Decoding FN/DN: invalid User Data len=0");
      }
    }

  return (ret);
}


/************************************************************************/
/*			cosp_dec_ab					*/
/*----------------------------------------------------------------------*/
/* Function to decode an ABORT SPDU.					*/
/* Note if User Data are present in received SPDU then the length in the*/
/* dec_par->udata_len will be > 0. The dec_par->udata_ptr points into	*/
/* the spdu_buf (it is not an allocated pointer).			*/
/* 									*/
/* Parameters:								*/
/*  COSP_AB		*dec_par	Ptr to decoded parameters struct*/
/*  char        	*spdu_buf	Pointer to received SPDU buf	*/
/*  ST_UINT     	 spdu_len	Length of received SPDU		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_dec_ab (COSP_AB *dec_par, char *spdu_buf, ST_UINT spdu_len)
{
ST_RET		ret;
ST_UCHAR	       *dec_buf;
ST_UINT	dec_len;
ST_UCHAR		param_code;
ST_UINT	param_len;
ST_UINT	len_bytes;	/* number of bytes for encoded len (1 or 3) */
ST_UINT  	idx;

  ret = SD_SUCCESS;

  dec_buf = (ST_UCHAR *) spdu_buf;
  dec_len = 0;
  idx = 0;

  /*-----------------------------------------------------*/
  /* set defaults in case params are not present in SPDU */
  /*-----------------------------------------------------*/

  memset (dec_par, 0, sizeof (COSP_AB));
  dec_par->disconnect        = 1;	/* default disconnect transport	*/

  /* in addition memset is equivalent to following con params settings	*/
  /* dec_par->reason	     = 0;	undefined reason		*/
  /* dec_par->reflect_par_len= 0;	Len of  implementation defined protocol err code*/
  /* dec_par->reflect_par [9]= 0;	Buf for implementation defined protocol err code*/
  /* dec_par->udata_len      = 0;	Length of User Data		*/
  /* dec_par->udata_ptr      = 0;       Pointer to User Data (points into spdu_buf!)	*/

  /* we will use the first while loop to avoid excesive nesting of if...else... */

  while (SD_TRUE)
    {
    /* NOTE: we should never return back from bottom of the loop here!!! */

    /* the decoded len should be comparable with spdu_len passed to this func */
    dec_len = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);
    if (dec_len + 1+len_bytes != spdu_len)
      {
      ret = COSP_ERR_DEC_INV_LEN;
      COSP_LOG_ERR2 ("COSP-ERROR: Decoding AB: invalid SPDU len=%u or decoded len=%u.",
        spdu_len, dec_len + 1+len_bytes);
      break;
      }
    idx += 1+len_bytes;		/* 1 for AB SPDU code */

    /* decode all ABORT parameters */
    while (dec_len > 0)
      {
      param_code = dec_buf [idx];
      param_len  = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);

      /* signal error if param_len is greater then decoding length left */
      if (param_len + 1+len_bytes > dec_len)
        {
        ret = COSP_ERR_DEC_INV_LEN;
        COSP_LOG_ERR2 ("COSP-ERROR: Decoding AB: invalid parameter len=%u (length left to decode len=%u).",
          param_len + 1+len_bytes, dec_len);
	break;
	}
      dec_len -= 1+len_bytes;
      idx     += 1+len_bytes;

      /* do not try to decode if param_len is 0 (param is empty)	*/
      if (param_len == 0)
        continue;

      switch (param_code)
        {
	case  17:		/* PI - Transport Disconnect */
	  if (param_len == 1)
	    {
            dec_par->disconnect = (ST_BOOLEAN) (dec_buf [idx] & 0x01);
            dec_par->reason     = (ST_UCHAR)   (dec_buf [idx] & 0x1E);
            }
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding AB: invalid PI-17 parameter len=%u (should be len=1).",
              param_len);
            }
	break;

	case  49:			/* PI -  Reflect param */
	  if (param_len <= 9)
	    {
            dec_par->reflect_par_len = param_len;
            memcpy (dec_par->reflect_par, &dec_buf [idx], param_len);
	    }
	  else
            {
            ret = COSP_ERR_DEC_INV_LEN;
            COSP_LOG_ERR1 ("COSP-ERROR: Decoding AB: invalid PI-49 parameter len=%u (should be len<=9).",
              param_len);
            }
	break;

	case  193:	/* PGI - User Data group */
          dec_par->udata_len = param_len;
          dec_par->udata_ptr = &dec_buf [idx];
	break;

	default:
	  ret = COSP_ERR_DEC_INV_PI_CODE;
          COSP_LOG_ERR1 ("COSP-ERROR: Decoding AB: invalid PI code=%d", param_code);
	} /* end of switch */

      /* adjust processing vars by the length of param processed */
      if (ret == SD_SUCCESS)
        {
        dec_len -= param_len;
        idx     += param_len;
	}
      else
        break;
      } /* end while len to decode */

    break;	/* this is end of decoding, exit loop unconditionally */
    }  /* end of while (SD_TRUE) loop */

  /* validate the received SPDU */
  if (ret == SD_SUCCESS)
    {
    if (dec_par->reason == COSP_AB_REASON_PROT_ERROR)
      if (dec_par->reflect_par_len == 0)
        {
        ret = COSP_ERR_DEC_INV_AB_RP;
        COSP_LOG_ERR0 ("COSP-ERROR: Decoding AB: invalid Reflect Parameter len=0");
        }

    if (dec_par->reason == COSP_AB_REASON_USER_ABORT)
      if (dec_par->udata_len == 0)
        {
        ret = COSP_ERR_INV_UDATA_LEN;
        COSP_LOG_ERR0 ("COSP-ERROR: Decoding AB: invalid User Data len=0");
        }
    }

  return (ret);
}


/************************************************************************/
/*			cosp_dec_dt					*/
/*----------------------------------------------------------------------*/
/* Function to decode an DATA SPDU.					*/
/* Note if User Data are present in received SPDU then the length in the*/
/* dec_par->udata_len will be > 0. The dec_par->udata_ptr points into	*/
/* the spdu_buf (it is not an allocated pointer).			*/
/* 									*/
/* Parameters:								*/
/*  COSP_DT		*dec_par	Ptr to decoded parameters struct*/
/*  char        	*spdu_buf	Pointer to received SPDU buf	*/
/*  ST_UINT     	 spdu_len	Length of received SPDU		*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if successful				*/
/*	error code		otherwise				*/
/************************************************************************/
ST_RET cosp_dec_dt (COSP_DT *dec_par, char *spdu_buf, ST_UINT spdu_len)
{
ST_RET		ret;
ST_UCHAR	       *dec_buf;
ST_UINT	dec_len;
ST_UINT	len_bytes;	/* number of bytes for encoded len (1 or 3) */
ST_UINT  	idx;

  ret = SD_SUCCESS;

  dec_buf = (ST_UCHAR *) spdu_buf;
  dec_len = 0;
  idx = 0;

  /*-----------------------------------------------------*/
  /* set defaults in case params are not present in SPDU */
  /*-----------------------------------------------------*/

  memset (dec_par, 0, sizeof (COSP_DT));

  /* memset is equivalent to following con params settings	*/
  /* dec_par->udata_len      = 0;	Length of User Data		*/
  /* dec_par->udata_ptr      = 0;       Pointer to User Data (points into spdu_buf!)	*/

  /*--------------------------------------------------------------------*/
  /* the protocol calls for GIVE-TOKEN and DT SPDU to be concatinated	*/
  /* even if we do not use the token mechanism.				*/
  /*--------------------------------------------------------------------*/

  dec_len = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);
  if (dec_len == 0)
    {
    idx += 1+len_bytes;
    /* token mechanism not used, decode DT */
    if (dec_buf [idx] == COSP_SI_DATA)
      {
      /*------------------------------------------------------------*/
      /* note that Enclosure Item PI should not be present because	*/
      /* we do not support segmenting (see CONNECT)			*/
      /*------------------------------------------------------------*/

      /* get the params length, should be 0, we do not send/rcv any params in DT */
      dec_len = (ST_UINT) cosp_dec_len (&dec_buf [idx+1], &len_bytes);
      if (dec_len == 0)
        {
        idx += 1+len_bytes;  /* 1 for DT SPDU code and 1 byte for params len LI */

        dec_par->udata_len = spdu_len - (2 + 1+len_bytes);
        dec_par->udata_ptr = &dec_buf [idx];
        }
      else
        {
        ret = COSP_ERR_DEC_INV_LEN;
        COSP_LOG_ERR0 ("COSP-ERROR: Decoding DT: Decoding of parameters in DATA SPDU not supported.");
        }
      }
    else
      {
      ret = COSP_ERR_DEC_INV_SPDU;
      COSP_LOG_ERR1 ("COSP-ERROR: Decoding DT: Invalid SPDU code=%u detected after Token SPDU.",
        (ST_UINT) dec_buf [idx]);
      }
    }
  else
    {
    ret = COSP_ERR_DEC_INV_LEN;
    COSP_LOG_ERR0 ("COSP-ERROR: Decoding DT: Parameters in Token SPDU not supported.");
    }

  return (ret);
}


/************************************************************************/
/*			cosp_dec_len					*/
/*----------------------------------------------------------------------*/
/* Function to decode a parameter's length (8-bit or 16-bit value).	*/
/* 									*/
/* Parameters:								*/
/*  ST_UCHAR	*buf		Pointer to len in received SPDU buf	*/
/*  ST_UINT	*bytes		Pointer where to return number of	*/
/*				bytes for decoded field			*/
/*	  								*/
/* Return:								*/
/*	ST_UINT16			the length of a parameter		*/
/************************************************************************/
static ST_UINT16 cosp_dec_len (ST_UCHAR *buf, ST_UINT *bytes)
{
ST_UINT16   len;

  if (buf [0] == 0xFF)
    {
    /* len >254      high order byte          low order byte		*/
    len  = ((ST_UINT16) buf [1] << 8) | (ST_UINT16) buf [2];	
    *bytes = 3;
    }
  else
    {
    /* len <=254 */
    len  = (ST_UINT16) buf [0];
    *bytes = 1;
    }

  return (len);
}


/************************************************************************/
/*			cosp_validate_cn_ac				*/
/*----------------------------------------------------------------------*/
/* Function to validate parameters in a CONNECT or ACCEPT SPDU.		*/
/* 									*/
/* Parameters:								*/
/*  COSP_CN_AC		*dec_par	ptr to decoded parameters struct*/
/*  ST_UCHAR		 spdu_type	SPDU type to validate (CN, AC)	*/
/*	  								*/
/* Return:								*/
/*	SD_SUCCESS (0)		if validation successful		*/
/*	error code		otherwise				*/
/************************************************************************/
static ST_RET cosp_validate_cn_ac (COSP_CN_AC *dec_par, ST_UCHAR spdu_type)
{
  if (spdu_type == COSP_SI_CONNECT)
    {
    /* check if CONNECT SPDU is addressed to us */
    if (memcmp (dec_par->loc_ssel, cosp_only_ssel, cosp_only_ssel [0]+1)!=0)
      {
      COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: Invalid Called SSEL (len=%d).",
        (ST_INT) dec_par->loc_ssel [0]);
      COSP_LOG_ERRH((ST_INT) dec_par->loc_ssel [0], &dec_par->loc_ssel [1]);
      return (COSP_ERR_DEC_INV_LOC_SSEL);
      }
    }
  else
    {
    /* ACCEPT SPDU, check the local SSEL if present */
    if (dec_par->loc_ssel [0] > 0)
      {
      if (dec_par->loc_ssel [0] != cosp_only_ssel [0] ||
          memcmp (&dec_par->loc_ssel [1], &cosp_only_ssel [1], cosp_only_ssel [0])
            != 0)
        {
        COSP_LOG_ERR1 ("COSP-ERROR: Decoding CN/AC: Invalid Calling SSEL (len=%d).",
          (ST_INT) dec_par->loc_ssel [0]);
        COSP_LOG_ERRH((ST_INT) dec_par->loc_ssel [0], &dec_par->loc_ssel [1]);
        return (COSP_ERR_DEC_INV_LOC_SSEL);
        }
      }
    }

  /* currently we support Version 2 of the Session Protocol		*/
  if (spdu_type == COSP_SI_CONNECT)
    {
    /* CONNECT, check if one of choices is COSP_VER2 */
    if ((dec_par->ver_num & COSP_VER2) == 0)
      {
      COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: Session Protocol Version '%d' not supported (supported ver=%d).",
        dec_par->ver_num, COSP_VER2);
      return (COSP_ERR_DEC_INV_PROT_VER);
      }
    }
  else
    {
    /* ACCEPT, make sure COSP_VER2 was accepted	*/
    if (dec_par->ver_num != COSP_VER2)
      {
      COSP_LOG_ERR2 ("COSP-ERROR: Decoding CN/AC: Session Protocol Version '%d' not supported (supported ver=%d).",
        dec_par->ver_num, COSP_VER2);
      return (COSP_ERR_DEC_INV_PROT_VER);
      }
    }

  /* make sure extended concatination is not set (we do not support it)*/
  if (dec_par->prot_option != 0)
    {
    COSP_LOG_ERR0 ("COSP-ERROR: Decoding CN/AC: Extended Concatination not supported.");
    return (COSP_ERR_DEC_INV_PROT_OPT);
    }

  /* make sure that segmenting of SSDUs was not specified */
  if (dec_par->initiator_tsdu_size != 0 ||
      dec_par->responder_tsdu_size != 0)
    {
    COSP_LOG_ERR0 ("COSP-ERROR: Decoding CN/AC: SSDUs segmenting not supported.");
    return (COSP_ERR_DEC_INV_SEG);
    }

  if (spdu_type == COSP_SI_CONNECT)
    {
    /* we support only Duplex Functional Units, make sure it is available*/
    if (!(dec_par->ses_urequir [1] & 0x02))	/* is bit2 set?	*/
      {
      COSP_LOG_ERR0 ("COSP-ERROR: Decoding CN/AC: Duplex Functional Unit not proposed (Session User Requirements).");
      return (COSP_ERR_DEC_INV_FUN_UNITS);
      }
    }
  else
    {
    /* ACCEPT, make sure only Duplex Functional Units (FU) has been selected */
    if (dec_par->ses_urequir [0] != 0 ||	/* bits 9-16: must be 0	*/
        dec_par->ses_urequir [1] != 0x02)	/* bits 1-8:  bit2 set	*/
      {
      COSP_LOG_ERR0 ("COSP-ERROR: Decoding CN/AC: Only Duplex Functional Unit supported (Session User Requirements).");
      return (COSP_ERR_DEC_INV_FUN_UNITS);
      }
    }

  return (SD_SUCCESS);
}


