/*****************************************************************************/
/*
                                HTTP2watch.c


VERSION HISTORY
---------------
27-OCT-2023  MGD  refinements using https://github.com/summerwind/h2spec
14-FEB-2019  MGD  Http2WatchFrame() relax WATCH from module to regular
17-AUG-2015  MGD  initial
*/
/*****************************************************************************/

#ifdef WASD_VMS_V7
#  undef __VMS_VER
#  define __VMS_VER 70000000
#  undef __CRTL_VER
#  define __CRTL_VER 70000000
#else
#  ifdef WASD_VMS_V7
#     undef _VMS__V6__SOURCE
#     define _VMS__V6__SOURCE
#     undef __VMS_VER
#     define __VMS_VER 70000000
#     undef __CRTL_VER
#     define __CRTL_VER 70000000
#   endif
#endif

#include <stdio.h>
#include <ctype.h>

#include "wasd.h"

#define WASD_MODULE "HTTP2WATCH"

/******************/
/* global storage */
/******************/

/********************/
/* external storage */
/********************/

extern char  *Http2ErrorArray [];
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
*/

void Http2WatchFrame
(
HTTP2_STRUCT *h2ptr,
uchar *FramePtr,
uchar *DataPtr,
uint BufferLength,
BOOL magic
)
{
   uint  flags, flen, ident, padlen, size, type;
   uchar  *csptr, *fptr, *sptr, *tptr;
   char  flstr [64];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
      WatchThis (WATCHITM(h2ptr), WATCH_MOD_HTTP2, "Http2WatchFrame()");

   fptr = FramePtr;
   HTTP2_GET_24 (fptr, flen);
   HTTP2_GET_8 (fptr, type);
   HTTP2_GET_8 (fptr, flags);
   HTTP2_GET_32 (fptr, ident);
   if (ident) ident &= 0x7fffffff;

   flstr[0] = '\0';
   size = sizeof(flstr);

   switch (type)
   {
      case HTTP2_FRAME_DATA :
           if (magic)
              tptr = "DATA (magic)";
           else
              tptr = "DATA";
           if (flags & HTTP2_FLAG_END_STR)
              strzcat (flstr, " END-STREAM", size);
           if (flags & HTTP2_FLAG_PADDED)
           {
              HTTP2_GET_8 (fptr, padlen);
              strzcat (flstr, " PADDED:", size);
              for (sptr = flstr; *sptr; sptr++);
              sprintf (sptr, "%d", padlen);
           }
           if (flags & ~(HTTP2_FLAG_END_STR |
                         HTTP2_FLAG_PADDED))
              strzcat (flstr, " ?", size);
           break;
      case HTTP2_FRAME_HEADERS :
           tptr = "HEADERS";
           if (flags & HTTP2_FLAG_END_STR)
              strzcat (flstr, " END-STREAM", size);
           if (flags & HTTP2_FLAG_END_HEAD)
              strzcat (flstr, " END-HEADERS", size);
           if (flags & HTTP2_FLAG_PADDED)
           {
              HTTP2_GET_8 (fptr, padlen);
              strzcat (flstr, " PADDED:", size);
              for (sptr = flstr; *sptr; sptr++);
              sprintf (sptr, "%d", padlen);
           }
           if (flags & HTTP2_FLAG_PRIORITY)
              strzcat (flstr, " PRIORITY", size);
           if (flags & ~(HTTP2_FLAG_END_STR |
                         HTTP2_FLAG_END_HEAD |
                         HTTP2_FLAG_PADDED |
                         HTTP2_FLAG_PRIORITY))
              strzcat (flstr, " ?", size);
           break;
      case HTTP2_FRAME_PRIORITY :
           tptr = "PRIORITY";
           break;
      case HTTP2_FRAME_RST_STREAM :
           tptr = "RST_STREAM";
           break;
      case HTTP2_FRAME_SETTINGS :
           tptr = "SETTINGS";
           if (flags & HTTP2_FLAG_ACK)
              strzcat (flstr, " ACKNOWLEDGE", size);
           if (flags & ~HTTP2_FLAG_ACK)
              strzcat (flstr, " ?", size);
           break;
      case HTTP2_FRAME_PUSH_PROMISE :
           tptr = "PUSH_PROMISE";
           if (flags & HTTP2_FLAG_END_HEAD)
              strzcat (flstr, " END-HEADERS", size);
           if (flags & HTTP2_FLAG_PADDED)
           {
              HTTP2_GET_8 (fptr, padlen);
              strzcat (flstr, " PADDED:", size);
              for (sptr = flstr; *sptr; sptr++);
              sprintf (sptr, "%d", padlen);
           }
           if (flags & ~(HTTP2_FLAG_END_HEAD |
                         HTTP2_FLAG_PADDED))
              strzcat (flstr, " ?", size);
           break;
      case HTTP2_FRAME_PING :
           tptr = "PING";
           if (flags & HTTP2_FLAG_ACK)
              strzcat (flstr, " ACKNOWLEDGE", size);
           if (flags & ~HTTP2_FLAG_ACK)
              strzcat (flstr, " ?", size);
           break;
      case HTTP2_FRAME_GOAWAY :
           tptr = "GOAWAY";
           break;
      case HTTP2_FRAME_WINDOW_UPDATE :
           tptr = "WINDOW_UPDATE";
           break;
      case HTTP2_FRAME_CONTINUATION :
           tptr = "CONTINUATION";
           if (flags & HTTP2_FLAG_END_HEAD)
              strzcat (flstr, " END-HEADERS", size);
           if (flags & ~HTTP2_FLAG_END_HEAD)
              strzcat (flstr, " ?", size);
           break;
      default :
           tptr = "?";
   }

   if (flags && !flstr[0]) strzcpy (flstr, " ?", sizeof(flstr));

   if (FramePtr >= h2ptr->ReadBufferPtr &&
       FramePtr < h2ptr->ReadBufferPtr + h2ptr->ReadBufferSize)
      csptr = "client";
   else
      csptr = "server";

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
"FRAME !AZ length:!UL type:!UL !AZ flags:0x!2XL!AZ ident:!UL",
                 csptr, flen, type, tptr, flags, flstr, ident);

   switch (type)
   {
      case HTTP2_FRAME_DATA :
           if (DataPtr) fptr = DataPtr;
           if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
              Http2WatchData (h2ptr, ident, flags, fptr, flen, BufferLength);
           break;
      case HTTP2_FRAME_HEADERS :
      case HTTP2_FRAME_CONTINUATION :
           Http2WatchHeaders (h2ptr, ident, flags, fptr, flen, BufferLength);
           break;
      case HTTP2_FRAME_PRIORITY :
           Http2WatchPriority (h2ptr, ident, fptr, flen);
           break;
      case HTTP2_FRAME_RST_STREAM :
           Http2WatchRstStream (h2ptr, ident, fptr, flen);
           break;
      case HTTP2_FRAME_SETTINGS :
           Http2WatchSettings (h2ptr, csptr, fptr, flen);
           break;
      case HTTP2_FRAME_PUSH_PROMISE :
           Http2WatchPushPromise (h2ptr, ident, flags, fptr, flen);
           break;
      case HTTP2_FRAME_PING :
           Http2WatchPing (h2ptr, fptr, flen);
           break;
      case HTTP2_FRAME_GOAWAY :
           Http2WatchGoAway (h2ptr, fptr, flen);
           break;
      case HTTP2_FRAME_WINDOW_UPDATE :
           Http2WatchWindowUpdate (h2ptr, ident, fptr, flen);
           break;
      default :
           WatchThis (WATCHITM(h2ptr), WATCH_HTTP2, "FRAME error!");
   }
}

/*****************************************************************************/
/*
*/

void Http2WatchData
(
HTTP2_STRUCT *h2ptr,
uint ident,
uint flags,
uchar *fptr,
uint flen,
uint blen
)
{
   uint  padlen;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
   {
      if (flags & HTTP2_FLAG_PADDED)
         HTTP2_GET_8 (fptr, padlen)
      else
         padlen = 0;

      WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
                 "DATA padding:!UL length:!UL", padlen, flen - padlen);

      /* dump only as many bytes as we have */
///      WatchDataDump (fptr, blen >= flen - padlen ? flen - padlen : blen);
   }
}

/*****************************************************************************/
/*
*/

void Http2WatchGoAway
(
HTTP2_STRUCT *h2ptr,
uchar *fptr,
uint flen
)
{
   uint  addlen, lastid, error;

   /*********/
   /* begin */
   /*********/

   HTTP2_GET_32 (fptr, lastid);
   lastid &= 0x7fffffff;
   HTTP2_GET_32 (fptr, error);

   addlen = flen - sizeof(uint) - sizeof(uint);

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
              "GOAWAY lastident:!UL error:!UL \"!AZ\"!AZ!#AZ!AZ",
              lastid, error, Http2ErrorString(error),
              addlen ? " \"" : "", addlen, fptr,
              addlen ? "\"" : "");
}

/*****************************************************************************/
/*
*/

void Http2WatchHeaders
(
HTTP2_STRUCT *h2ptr,
uint ident,
uint flags,
uchar *fptr,
uint flen,
uint blen
)
{
   uint  hbflen,
         padlen,
         depend,
         weight;
   uchar  *bptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
   {
      bptr = fptr;
      if (flags & HTTP2_FLAG_PADDED)
         HTTP2_GET_8 (fptr, padlen)
      else
         padlen = 0;
      if (flags & HTTP2_FLAG_PRIORITY)
      {
         HTTP2_GET_32 (fptr, depend)
         HTTP2_GET_8 (fptr, weight)
      }
      else
         depend = weight = 0;

      WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
"HEADERS ends:!&B endh:!&B pad:!&B prio:!&B \
padding:!UL depend:!UL (ex:!&B) header:!UL (!UL)",
                 flags & HTTP2_FLAG_END_STR,
                 flags & HTTP2_FLAG_END_HEAD,
                 flags & HTTP2_FLAG_PADDED,
                 flags & HTTP2_FLAG_PRIORITY,
                 padlen, depend, depend & 0x80000000, flen - padlen, blen);

      /* dump only as many bytes as we have */
      WatchDataDump (fptr, blen >= flen - padlen ? flen - padlen : blen);
   }
}

/*****************************************************************************/
/*
*/

void Http2WatchPing
(
HTTP2_STRUCT *h2ptr,
uchar *fptr,
uint flen
)
{
   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
      WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
                 "PING !2XL!2XL!2XL!2XL!2XL!2XL!2XL!2XL",
                 fptr[0],fptr[1],fptr[2],fptr[3],
                 fptr[4],fptr[5],fptr[6],fptr[7]);
}

/*****************************************************************************/
/*
*/

void Http2WatchPriority
(
HTTP2_STRUCT *h2ptr,
uint ident,
uchar *fptr,
uint flen
)
{
   uint  depend, weight;

   /*********/
   /* begin */
   /*********/

   HTTP2_GET_32 (fptr, depend)
   HTTP2_GET_8 (fptr, weight)

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
              "PRIORITY depend:!UL (ex:!UL) weight:!UL",
              depend & 0x7fffffff, depend & 0x80000000, weight+1);
}

/*****************************************************************************/
/*
*/

void Http2WatchPushPromise
(
HTTP2_STRUCT *h2ptr,
uint ident,
uint flags,
uchar *fptr,
uint flen
)
{
   uint  hbflen, padlen, promid;

   /*********/
   /* begin */
   /*********/

   if (flags & HTTP2_FLAG_PADDED)
      HTTP2_GET_8 (fptr, padlen)
   else
      padlen = 0;

   HTTP2_GET_32 (fptr, promid);
   promid &= 0x7fffffff;

   hbflen = flen - sizeof(uchar) - sizeof(uint) - sizeof(uchar) - padlen;

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
              "PUSH_PROMISE padding:!UL promid:!UL header:!UL",
              padlen, promid, hbflen);

   if (WATCHMOD (h2ptr, WATCH_MOD_HTTP2))
      WatchData (fptr, hbflen);
}

/*****************************************************************************/
/*
*/

void Http2WatchRstStream
(
HTTP2_STRUCT *h2ptr,
uint ident,
uchar *fptr,
uint flen
)
{
   uint  error;

   /*********/
   /* begin */
   /*********/

   HTTP2_GET_32 (fptr, error);

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
              "RST_STREAM ident:!UL error:!UL \"!AZ\"",
              ident, error, Http2ErrorString(error));
}

/*****************************************************************************/
/*
*/

void Http2WatchSettings
(
HTTP2_STRUCT *h2ptr,
uchar *csptr,
uchar *fptr,
uint flen
)
{
   uint  setting, value;
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   for (;(int)flen >= 6; flen -= 6)
   {
      HTTP2_GET_16 (fptr, setting);
      HTTP2_GET_32 (fptr, value);

      switch (setting)
      {
         case HTTP2_SETTING_MAX_HEAD_TABLE_SIZE :
              cptr = "header table size"; break;
         case HTTP2_SETTING_ENABLE_PUSH :
              cptr = "server push enabled"; break;
         case HTTP2_SETTING_MAX_CONC_STREAMS :
              cptr = "max concurrent streams"; break;
         case HTTP2_SETTING_INIT_WIN_SIZE :
              cptr = "initial window size"; break;
         case HTTP2_SETTING_MAX_FRAME_SIZE :
              cptr = "max frame size"; break;
         case HTTP2_SETTING_MAX_HEAD_LIST_SIZE :
              cptr = "max header list size"; break;
         default : cptr = "(unknown)";
      }

      WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
                 "SETTING !AZ !AZ: !UL", csptr, cptr, value);
   }

   if (flen) WatchThis (WATCHITM(h2ptr), WATCH_HTTP2, "SETTING error!!");
}

/*****************************************************************************/
/*
*/

void Http2WatchWindowUpdate
(
HTTP2_STRUCT *h2ptr,
uint ident,
uchar *fptr,
uint flen
)
{
   uint  value;

   /*********/
   /* begin */
   /*********/

   HTTP2_GET_32 (fptr, value);
   value &= 0x7fffffff;

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2,
              "WINDOW_UPDATE ident:!UL value:!UL", ident, value);
}

/*****************************************************************************/
/*
*/

void Http2WatchError
(
HTTP2_STRUCT *h2ptr,
uint error
)
{
   char  *sptr;

   /*********/
   /* begin */
   /*********/

   if (error < HTTP2_ERROR_COUNT)
      sptr = Http2ErrorArray[error];
   else
      sptr = "?";

   WatchThis (WATCHITM(h2ptr), WATCH_HTTP2, "ERROR !UL !AZ", error, sptr);
}

/*****************************************************************************/

