<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <array>
#include "MozQuic.h"
#include "MozQuicInternal.h"
#include "MozQuicStream.h"
#include "NSSHelper.h"

#include "assert.h"
#include "netinet/ip.h"
#include "stdlib.h"
#include "unistd.h"
#include "time.h"
#include "sys/time.h"
#include <string.h>
#include <fcntl.h>
#include "prerror.h"
#include "ufloat16.h"

namespace mozquic  {

// when this set is updated, look at versionOK() and
// GenerateVersionNegotiation()
static const uint32_t kMozQuicVersion1 = 0xf123f0c5; // 0xf123f0c* reserved for mozquic
static const uint32_t kMozQuicIetfID5 = 0xff000005;
}

#ifdef __cplusplus
extern "C" {
#endif
  static bool mozQuicInit = false;

  int mozquic_new_connection(mozquic_connection_t **outConnection,
                             mozquic_config_t *inConfig)
  {
    if (!mozQuicInit) {
      int rv = mozquic::NSSHelper::Init(nullptr);
      if (rv != MOZQUIC_OK) {
        return rv;
      }
    }

    if (!outConnection || !inConfig) {
      return MOZQUIC_ERR_INVALID;
    }

    if (!inConfig->originName) {
      return MOZQUIC_ERR_INVALID;
    }

    mozquic::MozQuic *q = new mozquic::MozQuic(inConfig->handleIO);
    if (!q) {
      return MOZQUIC_ERR_GENERAL;
    }
    *outConnection = (void *)q;

    q->SetClosure(inConfig->closure);
    q->SetLogger(inConfig->logging_callback);
    q->SetTransmiter(inConfig->send_callback);
    q->SetReceiver(inConfig->recv_callback);
    q->SetHandshakeInput(inConfig->handshake_input);
    q->SetErrorCB(inConfig->error_callback);
    q->SetConnEventCB(inConfig->connection_event_callback);
    q->SetOriginPort(inConfig->originPort);
    q->SetOriginName(inConfig->originName);
    if (inConfig->greaseVersionNegotiation) {
      q->GreaseVersionNegotiation();
    }
    if (inConfig->tolerateBadALPN) {
      q->SetTolerateBadALPN();
    }
    if (inConfig->preferMilestoneVersion) {
      q->PreferMilestoneVersion();
    }
    if (inConfig->ignorePKI) {
      q->SetIgnorePKI();
    }
    return MOZQUIC_OK;
  }

  int mozquic_destroy_connection(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->Destroy(0, "");
    return MOZQUIC_OK;
  }

  int mozquic_start_client(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->StartClient();
  }

  int mozquic_start_server(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->StartServer();
  }

  int mozquic_start_new_stream(mozquic_stream_t **outStream,
                               mozquic_connection_t *conn, void *data,
                               uint32_t amount,
                               int fin)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    mozquic::MozQuicStreamPair *stream;
    int rv = self->StartNewStream(&stream, data, amount, fin);
    if (!rv) {
      *outStream = (void *)stream;
    }
    return rv;
  }

  int mozquic_send(mozquic_stream_t *stream, void *data, uint32_t amount,
                   int fin)
  {
    mozquic::MozQuicStreamPair *self(reinterpret_cast<mozquic::MozQuicStreamPair *>(stream));
    int rv = self->Write((const unsigned char *)data, amount, fin);
    if (fin && self->Done()) {
      self->mMozQuic->DeleteStream(self->mStreamID);
    }
    return rv;
  }

  int mozquic_end_stream(mozquic_stream_t *stream)
  {
     mozquic::MozQuicStreamPair *self(reinterpret_cast<mozquic::MozQuicStreamPair *>(stream));
     int rv = self->EndStream();
     if (self->Done()) {
      self->mMozQuic->DeleteStream(self->mStreamID);
    }
    return rv;
  }

  int mozquic_recv(mozquic_stream_t *stream, void *data, uint32_t avail,
                   uint32_t *amount, int *fin)
  {
    mozquic::MozQuicStreamPair *self(reinterpret_cast<mozquic::MozQuicStreamPair *>(stream));
    bool f;
    uint32_t a;
    int rv = self->Read((unsigned char *)data, avail, a, f);
    *fin = f;
    *amount = a;
    if (f && self->Done()) {
      self->mMozQuic->DeleteStream(self->mStreamID);
    }
    return rv;
  }

  int mozquic_set_event_callback(mozquic_connection_t *conn, int (*fx)(mozquic_connection_t *, uint32_t event, void * param))
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->SetConnEventCB(fx);
    return MOZQUIC_OK;
  }

  int mozquic_set_event_callback_closure(mozquic_connection_t *conn,
                                         void *closure)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->SetClosure(closure);
    return MOZQUIC_OK;
  }
  
  int mozquic_IO(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->IO();
  }

  mozquic_socket_t mozquic_osfd(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->GetFD();
  }

  void mozquic_setosfd(mozquic_connection_t *conn, mozquic_socket_t fd)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->SetFD(fd);
  }

  void mozquic_handshake_output(mozquic_connection_t *conn,
                                unsigned char *data, uint32_t data_len)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->HandshakeOutput(data, data_len);
  }

  void mozquic_handshake_complete(mozquic_connection_t *conn, uint32_t errCode,
                                  struct mozquic_handshake_info *keyInfo)
  {
    assert(false);
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->HandshakeComplete(errCode, keyInfo);
  }

  int mozquic_nss_config(char *dir) 
  {
    if (mozQuicInit) {
      return MOZQUIC_ERR_GENERAL;
    }
    mozQuicInit = true;
    if (!dir) {
      return MOZQUIC_ERR_INVALID;
    }

    return mozquic::NSSHelper::Init(dir);
  }

  int mozquic_check_peer(mozquic_connection_t *conn, uint32_t deadlineMs)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->CheckPeer(deadlineMs);
  }
  
#ifdef __cplusplus
}
#endif

namespace mozquic  {

static const uint32_t kMozQuicVersionGreaseC = 0xfa1a7a3a;
static const uint32_t kMozQuicVersionGreaseS = 0xea0a6a2a;
static const uint32_t kFNV64Size = 8;

#define FRAME_FIN_BIT 0x20

MozQuic::MozQuic(bool handleIO)
  : mFD(MOZQUIC_SOCKET_BAD)
  , mHandleIO(handleIO)
  , mIsClient(true)
  , mIsChild(false)
  , mReceivedServerClearText(false)
  , mIgnorePKI(false)
  , mTolerateBadALPN(false)
  , mIsLoopback(false)
  , mConnectionState(STATE_UNINITIALIZED)
  , mOriginPort(-1)
  , mVersion(kMozQuicVersion1)
  , mConnectionID(0)
  , mNextTransmitPacketNumber(0)
  , mOriginalTransmitPacketNumber(0)
  , mNextRecvPacketNumber(0)
  , mClosure(this)
  , mLogCallback(nullptr)
  , mTransmitCallback(nullptr)
  , mReceiverCallback(nullptr)
  , mHandshakeInput(nullptr)
  , mErrorCB(nullptr)
  , mConnEventCB(nullptr)
  , mNextStreamId(1)
  , mNextRecvStreamId(1)
  , mParent(nullptr)
  , mAlive(this)
  , mTimestampConnBegin(0)
  , mPingDeadline(0)
  , mDecodedOK(false)
{
  assert(!handleIO); // todo
  unsigned char seed[4];
  if (SECSuccess != PK11_GenerateRandom(seed, sizeof(seed))) {
    // major badness!
    srandom(Timestamp() & 0xffffffff);
  } else {
    srandom(seed[0] << 24 | seed[1] << 16 | seed[2] << 8 | seed[3]);
  }
  memset(&mPeer, 0, sizeof(mPeer));
}

MozQuic::~MozQuic()
{
  if (!mIsChild && (mFD != MOZQUIC_SOCKET_BAD)) {
    close(mFD);
  }
}

void
MozQuic::Destroy(uint32_t code, const char *reason)
{
  Shutdown(code, reason);
  mAlive = nullptr;
}

uint32_t
MozQuic::CheckPeer(uint32_t deadline)
{
  if (mPingDeadline) {
    return MOZQUIC_OK;
  }
  if ((mConnectionState != CLIENT_STATE_CONNECTED) &&
      (mConnectionState != SERVER_STATE_CONNECTED)) {
    fprintf(stderr,"check peer not connected\n");
    return MOZQUIC_ERR_GENERAL;
  }

  mPingDeadline = Timestamp() + deadline;

  unsigned char plainPkt[kMozQuicMTU];
  unsigned char cipherPkt[kMozQuicMTU];
  uint32_t used;

  CreateShortPacketHeader(plainPkt, kMozQuicMTU, used);
  assert(used == 13);
  plainPkt[used] = FRAME_TYPE_PING;
  used++;

  uint32_t room = kMozQuicMTU - used - 16;
  uint32_t usedByAck = 0;
  if (AckPiggyBack(plainPkt + used, mNextTransmitPacketNumber, room, keyPhase1Rtt, usedByAck) == MOZQUIC_OK) {
    if (usedByAck) {
      fprintf(stderr,"Handy-Ack adds to ping packet %lX by %d\n", mNextTransmitPacketNumber, usedByAck);
    }
    used += usedByAck;
  }
  
  // 13 bytes of aead, 1 ping frame byte. result is 16 longer for aead tag
  uint32_t written = 0;
  memcpy(cipherPkt, plainPkt, 13);
  uint32_t rv = mNSSHelper->EncryptBlock(plainPkt, 13, plainPkt + 13, used - 13,
                                         mNextTransmitPacketNumber, cipherPkt + 13, kMozQuicMTU - 13, written);
  mNextTransmitPacketNumber++;
  Transmit(cipherPkt, written + 13, nullptr);

  return MOZQUIC_OK;
}
  
void
MozQuic::Shutdown(uint32_t code, const char *reason)
{
  if (mParent) {
    for (auto iter = mParent->mChildren.begin(); iter != mParent->mChildren.end(); ++iter) {
      if ((*iter).get() == this) {
          mParent->mChildren.erase(iter);
          break;
      }
    }    
  }

  if ((mConnectionState != CLIENT_STATE_CONNECTED) &&
      (mConnectionState != SERVER_STATE_CONNECTED)) {
    mConnectionState = mIsClient ? CLIENT_STATE_CLOSED : SERVER_STATE_CLOSED;
    return;
  }
  if (!mIsChild && !mIsClient) {
    // this is the listener.. it does not send packets
    return;
  }
  
  fprintf(stderr, "sending shutdown as %lx\n", mNextTransmitPacketNumber);

  unsigned char plainPkt[kMozQuicMTU];
  unsigned char cipherPkt[kMozQuicMTU];
  uint16_t tmp16;
  uint32_t tmp32;

  // todo before merge - this can't be inlined here
  // what if not kp 0 TODO
  // todo when transport params allow truncate id, the connid might go
  // short header with connid kp = 0, 4 bytes of packetnumber
  uint32_t used;
  CreateShortPacketHeader(plainPkt, kMozQuicMTU - 16, used);

  plainPkt[used] = FRAME_TYPE_CLOSE;
  used++;
  tmp32 = htonl(code);
  memcpy(plainPkt + used, &tmp32, 4);
  used += 4;

  size_t reasonLen = strlen(reason);
  if (reasonLen > (kMozQuicMTU - 16 - used - 2)) {
    reasonLen = kMozQuicMTU - 16 - used - 2;
  }
  tmp16 = htons(reasonLen);
  memcpy(plainPkt + used, &tmp16, 2);
  used += 2;
  if (reasonLen) {
    memcpy(plainPkt + used, reason, reasonLen);
    used += reasonLen;
  }

  // 13 bytes of aead, 7 + reasonLen of data. result is 16 longer for aead tag
  uint32_t written = 0;
  memcpy(cipherPkt, plainPkt, 13);
  uint32_t rv = mNSSHelper->EncryptBlock(plainPkt, 13, plainPkt + 13, 7 + reasonLen,
                                         mNextTransmitPacketNumber, cipherPkt + 13, kMozQuicMTU - 13, written);
  fprintf(stderr,"encrypt rv=%d inputlen=%d (+ %d of aead) outputlen=%d pktheaderLen =%d\n",
          rv, 7 + reasonLen, 13, written, 13);
  mNextTransmitPacketNumber++;
  Transmit(cipherPkt, written + 13, nullptr);

  mConnectionState = mIsClient ? CLIENT_STATE_CLOSED : SERVER_STATE_CLOSED;
}

void
MozQuic::GreaseVersionNegotiation()
{
  assert(mConnectionState == STATE_UNINITIALIZED);
  fprintf(stderr,"applying version grease\n");
  mVersion = kMozQuicVersionGreaseC;
}

void
MozQuic::PreferMilestoneVersion()
{
  assert(mConnectionState == STATE_UNINITIALIZED);
  mVersion = kMozQuicIetfID5;
}

bool
MozQuic::IgnorePKI()
{
  return mIgnorePKI || mIsLoopback;
}

int
MozQuic::StartClient()
{
  assert(!mHandleIO); // todo
  mIsClient = true;
  mNextStreamId = 1;
  mNextRecvStreamId = 2;
  mNSSHelper.reset(new NSSHelper(this, mTolerateBadALPN, mOriginName.get(), true));
  mStream0.reset(new MozQuicStreamPair(0, this, this));

  mConnectionState = CLIENT_STATE_1RTT;
  for (int i=0; i < 4; i++) {
    mConnectionID = mConnectionID << 16;
    mConnectionID = mConnectionID | (random() & 0xffff);
  }
  for (int i=0; i < 2; i++) {
    mNextTransmitPacketNumber = mNextTransmitPacketNumber << 16;
    mNextTransmitPacketNumber = mNextTransmitPacketNumber | (random() & 0xffff);
  }
  mNextTransmitPacketNumber &= 0x7fffffff; // 31 bits
  mOriginalTransmitPacketNumber = mNextTransmitPacketNumber;

  if (mFD == MOZQUIC_SOCKET_BAD) {
    // the application did not pass in its own fd
    mFD = socket(AF_INET, SOCK_DGRAM, 0); // todo blocking getaddrinfo
    fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK);
    struct addrinfo *outAddr;
    if (getaddrinfo(mOriginName.get(), nullptr, nullptr, &outAddr) != 0) {
      return MOZQUIC_ERR_GENERAL;
    }

    if (outAddr->ai_family == AF_INET) {
      ((struct sockaddr_in *) outAddr->ai_addr)->sin_port = htons(mOriginPort);
      if ((ntohl(((struct sockaddr_in *) outAddr->ai_addr)->sin_addr.s_addr) & 0xff000000) == 0x7f000000) {
        mIsLoopback = true;
      }
    } else if (outAddr->ai_family == AF_INET6) {
      ((struct sockaddr_in6 *) outAddr->ai_addr)->sin6_port = htons(mOriginPort);
      const void *ptr1 = &in6addr_loopback.s6_addr;
      const void *ptr2 = &((struct sockaddr_in6 *) outAddr->ai_addr)->sin6_addr.s6_addr;
      if (!memcmp(ptr1, ptr2, 16)) {
        mIsLoopback = true;
      }
    }
    
    connect(mFD, outAddr->ai_addr, outAddr->ai_addrlen);
    freeaddrinfo(outAddr);
  }
  mTimestampConnBegin = Timestamp();

  return MOZQUIC_OK;
}

int
MozQuic::StartServer()
{
  assert(!mHandleIO); // todo
  mIsClient = false;
  mNextStreamId = 2;
  mNextRecvStreamId = 1;

  mConnectionState = SERVER_STATE_LISTEN;
  assert (!mHandshakeInput); // todo
  return Bind();
}

int
MozQuic::StartNewStream(MozQuicStreamPair **outStream, const void *data, uint32_t amount, bool fin)
{
  *outStream = new MozQuicStreamPair(mNextStreamId, this, this);
  mStreams.insert( { mNextStreamId, *outStream } );
  mNextStreamId += 2;
  if ( amount || fin) {
    return (*outStream)->Write((const unsigned char *)data, amount, fin);
  }
  return MOZQUIC_OK;
}

void
MozQuic::SetOriginName(const char *name) 
{
  mOriginName.reset(new char[strlen(name) + 1]);
  strcpy (mOriginName.get(), name);
}

int
MozQuic::Bind()
{
  if (mFD != MOZQUIC_SOCKET_BAD) {
    return MOZQUIC_OK;
  }
  mFD = socket(AF_INET, SOCK_DGRAM, 0); // todo v6 and non 0 addr
  fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK);
  struct sockaddr_in sin;
  memset (&sin, 0, sizeof (sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(mOriginPort);
  bind(mFD, (const sockaddr *)&sin, sizeof (sin)); // todo err check
  listen(mFD, 1000); // todo err
  return MOZQUIC_OK;
}

MozQuic *
MozQuic::FindSession(uint64_t cid)
{
  assert (!mIsChild);
  if (mIsClient) {
    return mConnectionID == cid ? this : nullptr;
  }

  auto i = mConnectionHash.find(cid);
  if (i == mConnectionHash.end()) {
    Log((char *)"find session could not find id in hash");
    return nullptr;
  }
  return (*i).second;
}

static uint64_t
fnv1a(unsigned char *p, uint32_t len) 
{
  const uint64_t prime = 1099511628211UL;
  uint64_t hash = 14695981039346656037UL;
  for (uint32_t i = 0; i < len; ++i) {
    hash ^= p[i];
    hash *= prime;
  }
  return hash;
}

bool
MozQuic::IntegrityCheck(unsigned char *pkt, uint32_t pktSize) 
{
  assert (pkt[0] & 0x80);
  assert (((pkt[0] & 0x7f) == PACKET_TYPE_CLIENT_INITIAL) ||
          ((pkt[0] & 0x7f) == PACKET_TYPE_SERVER_STATELESS_RETRY) ||
          ((pkt[0] & 0x7f) == PACKET_TYPE_SERVER_CLEARTEXT) ||
          ((pkt[0] & 0x7f) == PACKET_TYPE_CLIENT_CLEARTEXT));
  if (pktSize < (kFNV64Size + 17)) {
    RaiseError(MOZQUIC_ERR_GENERAL, (char *)"hash err");
    return false;
  }
  uint64_t hash = fnv1a(pkt, pktSize - kFNV64Size);
  uint64_t recvdHash;
  memcpy(&recvdHash, pkt + pktSize - kFNV64Size, kFNV64Size);
  recvdHash = PR_ntohll(recvdHash);
  bool rv = recvdHash == hash;
  if (!rv) {
    Log((char *)"integrity error");
  }
  return rv;
}

uint32_t
MozQuic::Intake()
{
  if (mIsChild) {
    // parent does all fd reading
    return MOZQUIC_OK;
  }
  // check state
  assert (mConnectionState == SERVER_STATE_LISTEN ||
          mConnectionState == SERVER_STATE_1RTT ||
          mConnectionState == SERVER_STATE_CLOSED ||
          mConnectionState == CLIENT_STATE_CONNECTED ||
          mConnectionState == CLIENT_STATE_1RTT ||
          mConnectionState == CLIENT_STATE_CLOSED);
  uint32_t rv = MOZQUIC_OK;
  
  unsigned char pkt[kMozQuicMSS];
  bool sendAck;
  do {
    uint32_t pktSize = 0;
    sendAck = false;
    struct sockaddr_in client;
    rv = Recv(pkt, kMozQuicMSS, pktSize, &client);
    if (rv != MOZQUIC_OK || !pktSize) {
      return rv;
    }

    // dispatch to the right MozQuic class.
    MozQuic *session = this; // default

    if (!(pkt[0] & 0x80)) {
      ShortHeaderData tmpShortHeader(pkt, pktSize, 0);
      if (pktSize < tmpShortHeader.mHeaderSize) {
        return rv;
      }
      fprintf(stderr,"SHORTFORM PACKET RECVD id=%lx size=%d\n", tmpShortHeader.mConnectionID, pktSize);
      session = FindSession(tmpShortHeader.mConnectionID);
      if (!session) {
        rv = MOZQUIC_ERR_GENERAL;
      }
      ShortHeaderData shortHeader(pkt, pktSize, session->mNextRecvPacketNumber);
      rv = session->ProcessGeneral(pkt, pktSize,
                                   shortHeader.mHeaderSize, shortHeader.mPacketNumber, sendAck);
      if (rv == MOZQUIC_OK) {
        session->Acknowledge(shortHeader.mPacketNumber, keyPhase1Rtt);
      }
        
    } else {
      if (pktSize < 17) {
        return rv;
      }
      LongHeaderData longHeader(pkt, pktSize);

      fprintf(stderr,"LONG PACKET RECVD pktnum=%lX version=%X len=%d connid=%lx\n",
              longHeader.mPacketNumber, longHeader.mVersion, pktSize, longHeader.mConnectionID);

      if (!(VersionOK(longHeader.mVersion) ||
            (mIsClient && longHeader.mType == PACKET_TYPE_VERSION_NEGOTIATION && longHeader.mVersion == mVersion))) {
        // todo this could really be an amplifier
        session->GenerateVersionNegotiation(longHeader, &client);
        continue;
      }

      switch (longHeader.mType) {
      case PACKET_TYPE_VERSION_NEGOTIATION:
        // do not do integrity check (nop)
        break;
      case PACKET_TYPE_CLIENT_INITIAL:
      case PACKET_TYPE_SERVER_CLEARTEXT:
        if (!IntegrityCheck(pkt, pktSize)) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        break;
      case PACKET_TYPE_SERVER_STATELESS_RETRY:
        if (!IntegrityCheck(pkt, pktSize)) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        assert(false); // todo mvp
        break;
      case PACKET_TYPE_CLIENT_CLEARTEXT:
        if (!IntegrityCheck(pkt, pktSize)) {
          rv = MOZQUIC_ERR_GENERAL;
          break;
        }
        session = FindSession(longHeader.mConnectionID);
        if (!session) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        break;

      case PACKET_TYPE_1RTT_PROTECTED_KP0:
        session = FindSession(longHeader.mConnectionID);
        if (!session) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        break;
        
      default:
        // reject anything that is not a cleartext packet (not right, but later)
        Log((char *)"recv1rtt unexpected type");
        // todo this could actually be out of order protected packet even in handshake
        // and ideally would be queued. for now we rely on retrans
        // todo
        rv = MOZQUIC_ERR_GENERAL;
        break;
      }

      if (!session || rv != MOZQUIC_OK) {
        fprintf(stderr, "unable to find connection for packet\n");
        continue;
      }

      switch (longHeader.mType) {
      case PACKET_TYPE_VERSION_NEGOTIATION: // version negotiation
        rv = session->ProcessVersionNegotiation(pkt, pktSize, longHeader);
        // do not ack
        break;
      case PACKET_TYPE_CLIENT_INITIAL:
        rv = session->ProcessClientInitial(pkt, pktSize, &client, longHeader, &session, sendAck);
        // ack after processing - find new session
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhaseUnprotected);
        }
        break;
      case PACKET_TYPE_SERVER_STATELESS_RETRY:
        // do not ack
        // todo mvp
        break;
      case PACKET_TYPE_SERVER_CLEARTEXT:
        rv = session->ProcessServerCleartext(pkt, pktSize, longHeader, sendAck);
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhaseUnprotected);
        }
        break;
      case PACKET_TYPE_CLIENT_CLEARTEXT:
        rv = session->ProcessClientCleartext(pkt, pktSize, longHeader, sendAck);
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhaseUnprotected);
        }
        break;
      case PACKET_TYPE_1RTT_PROTECTED_KP0:
        rv = session->ProcessGeneral(pkt, pktSize, 17, longHeader.mPacketNumber, sendAck);
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhase1Rtt);
        }
        break;

      default:
        assert(false);
        break;
      }
    }
    if ((rv == MOZQUIC_OK) && sendAck) {
      rv = session->MaybeSendAck();
    }
  } while (rv == MOZQUIC_OK);

  return rv;
}

int
MozQuic::IO()
{
  uint32_t code;
  std::shared_ptr<MozQuic> deleteProtector(mAlive);

  Intake();
  RetransmitTimer();
  ClearOldInitialConnetIdsTimer();
  Flush();

  if (mIsClient) {
    switch (mConnectionState) {
    case CLIENT_STATE_1RTT:
      code = Client1RTT();
      if (code != MOZQUIC_OK) {
        return code;
      }
      break;
    case CLIENT_STATE_CONNECTED:
    case CLIENT_STATE_CLOSED:
    case SERVER_STATE_CLOSED:
      break;
    default:
      assert(false);
      // todo
    }
  } else {
    if (mConnectionState == SERVER_STATE_1RTT) {
      code = Server1RTT();
      if (code != MOZQUIC_OK) {
        return code;
      }
    }
    if (!mIsChild) {
      ssize_t len = mChildren.size();
      for (auto iter = mChildren.begin();
           len == mChildren.size() && iter != mChildren.end(); ++iter) {
        (*iter)->IO();
      }
    }
  }

  if (mPingDeadline && mConnEventCB && mPingDeadline >= Timestamp()) {
    mPingDeadline = 0;
    mConnEventCB(mClosure, MOZQUIC_EVENT_ERROR, this);
  }
  
  if (mConnEventCB) {
    mConnEventCB(mClosure, MOZQUIC_EVENT_IO, this);
  }
  return MOZQUIC_OK;
}

void
MozQuic::Log(char *msg) 
{<commit_msg>fix problem with ping timeout test<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <array>
#include "MozQuic.h"
#include "MozQuicInternal.h"
#include "MozQuicStream.h"
#include "NSSHelper.h"

#include "assert.h"
#include "netinet/ip.h"
#include "stdlib.h"
#include "unistd.h"
#include "time.h"
#include "sys/time.h"
#include <string.h>
#include <fcntl.h>
#include "prerror.h"
#include "ufloat16.h"

namespace mozquic  {

// when this set is updated, look at versionOK() and
// GenerateVersionNegotiation()
static const uint32_t kMozQuicVersion1 = 0xf123f0c5; // 0xf123f0c* reserved for mozquic
static const uint32_t kMozQuicIetfID5 = 0xff000005;
}

#ifdef __cplusplus
extern "C" {
#endif
  static bool mozQuicInit = false;

  int mozquic_new_connection(mozquic_connection_t **outConnection,
                             mozquic_config_t *inConfig)
  {
    if (!mozQuicInit) {
      int rv = mozquic::NSSHelper::Init(nullptr);
      if (rv != MOZQUIC_OK) {
        return rv;
      }
    }

    if (!outConnection || !inConfig) {
      return MOZQUIC_ERR_INVALID;
    }

    if (!inConfig->originName) {
      return MOZQUIC_ERR_INVALID;
    }

    mozquic::MozQuic *q = new mozquic::MozQuic(inConfig->handleIO);
    if (!q) {
      return MOZQUIC_ERR_GENERAL;
    }
    *outConnection = (void *)q;

    q->SetClosure(inConfig->closure);
    q->SetLogger(inConfig->logging_callback);
    q->SetTransmiter(inConfig->send_callback);
    q->SetReceiver(inConfig->recv_callback);
    q->SetHandshakeInput(inConfig->handshake_input);
    q->SetErrorCB(inConfig->error_callback);
    q->SetConnEventCB(inConfig->connection_event_callback);
    q->SetOriginPort(inConfig->originPort);
    q->SetOriginName(inConfig->originName);
    if (inConfig->greaseVersionNegotiation) {
      q->GreaseVersionNegotiation();
    }
    if (inConfig->tolerateBadALPN) {
      q->SetTolerateBadALPN();
    }
    if (inConfig->preferMilestoneVersion) {
      q->PreferMilestoneVersion();
    }
    if (inConfig->ignorePKI) {
      q->SetIgnorePKI();
    }
    return MOZQUIC_OK;
  }

  int mozquic_destroy_connection(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->Destroy(0, "");
    return MOZQUIC_OK;
  }

  int mozquic_start_client(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->StartClient();
  }

  int mozquic_start_server(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->StartServer();
  }

  int mozquic_start_new_stream(mozquic_stream_t **outStream,
                               mozquic_connection_t *conn, void *data,
                               uint32_t amount,
                               int fin)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    mozquic::MozQuicStreamPair *stream;
    int rv = self->StartNewStream(&stream, data, amount, fin);
    if (!rv) {
      *outStream = (void *)stream;
    }
    return rv;
  }

  int mozquic_send(mozquic_stream_t *stream, void *data, uint32_t amount,
                   int fin)
  {
    mozquic::MozQuicStreamPair *self(reinterpret_cast<mozquic::MozQuicStreamPair *>(stream));
    int rv = self->Write((const unsigned char *)data, amount, fin);
    if (fin && self->Done()) {
      self->mMozQuic->DeleteStream(self->mStreamID);
    }
    return rv;
  }

  int mozquic_end_stream(mozquic_stream_t *stream)
  {
     mozquic::MozQuicStreamPair *self(reinterpret_cast<mozquic::MozQuicStreamPair *>(stream));
     int rv = self->EndStream();
     if (self->Done()) {
      self->mMozQuic->DeleteStream(self->mStreamID);
    }
    return rv;
  }

  int mozquic_recv(mozquic_stream_t *stream, void *data, uint32_t avail,
                   uint32_t *amount, int *fin)
  {
    mozquic::MozQuicStreamPair *self(reinterpret_cast<mozquic::MozQuicStreamPair *>(stream));
    bool f;
    uint32_t a;
    int rv = self->Read((unsigned char *)data, avail, a, f);
    *fin = f;
    *amount = a;
    if (f && self->Done()) {
      self->mMozQuic->DeleteStream(self->mStreamID);
    }
    return rv;
  }

  int mozquic_set_event_callback(mozquic_connection_t *conn, int (*fx)(mozquic_connection_t *, uint32_t event, void * param))
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->SetConnEventCB(fx);
    return MOZQUIC_OK;
  }

  int mozquic_set_event_callback_closure(mozquic_connection_t *conn,
                                         void *closure)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->SetClosure(closure);
    return MOZQUIC_OK;
  }
  
  int mozquic_IO(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->IO();
  }

  mozquic_socket_t mozquic_osfd(mozquic_connection_t *conn)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->GetFD();
  }

  void mozquic_setosfd(mozquic_connection_t *conn, mozquic_socket_t fd)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->SetFD(fd);
  }

  void mozquic_handshake_output(mozquic_connection_t *conn,
                                unsigned char *data, uint32_t data_len)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->HandshakeOutput(data, data_len);
  }

  void mozquic_handshake_complete(mozquic_connection_t *conn, uint32_t errCode,
                                  struct mozquic_handshake_info *keyInfo)
  {
    assert(false);
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    self->HandshakeComplete(errCode, keyInfo);
  }

  int mozquic_nss_config(char *dir) 
  {
    if (mozQuicInit) {
      return MOZQUIC_ERR_GENERAL;
    }
    mozQuicInit = true;
    if (!dir) {
      return MOZQUIC_ERR_INVALID;
    }

    return mozquic::NSSHelper::Init(dir);
  }

  int mozquic_check_peer(mozquic_connection_t *conn, uint32_t deadlineMs)
  {
    mozquic::MozQuic *self(reinterpret_cast<mozquic::MozQuic *>(conn));
    return self->CheckPeer(deadlineMs);
  }
  
#ifdef __cplusplus
}
#endif

namespace mozquic  {

static const uint32_t kMozQuicVersionGreaseC = 0xfa1a7a3a;
static const uint32_t kMozQuicVersionGreaseS = 0xea0a6a2a;
static const uint32_t kFNV64Size = 8;

#define FRAME_FIN_BIT 0x20

MozQuic::MozQuic(bool handleIO)
  : mFD(MOZQUIC_SOCKET_BAD)
  , mHandleIO(handleIO)
  , mIsClient(true)
  , mIsChild(false)
  , mReceivedServerClearText(false)
  , mIgnorePKI(false)
  , mTolerateBadALPN(false)
  , mIsLoopback(false)
  , mConnectionState(STATE_UNINITIALIZED)
  , mOriginPort(-1)
  , mVersion(kMozQuicVersion1)
  , mConnectionID(0)
  , mNextTransmitPacketNumber(0)
  , mOriginalTransmitPacketNumber(0)
  , mNextRecvPacketNumber(0)
  , mClosure(this)
  , mLogCallback(nullptr)
  , mTransmitCallback(nullptr)
  , mReceiverCallback(nullptr)
  , mHandshakeInput(nullptr)
  , mErrorCB(nullptr)
  , mConnEventCB(nullptr)
  , mNextStreamId(1)
  , mNextRecvStreamId(1)
  , mParent(nullptr)
  , mAlive(this)
  , mTimestampConnBegin(0)
  , mPingDeadline(0)
  , mDecodedOK(false)
{
  assert(!handleIO); // todo
  unsigned char seed[4];
  if (SECSuccess != PK11_GenerateRandom(seed, sizeof(seed))) {
    // major badness!
    srandom(Timestamp() & 0xffffffff);
  } else {
    srandom(seed[0] << 24 | seed[1] << 16 | seed[2] << 8 | seed[3]);
  }
  memset(&mPeer, 0, sizeof(mPeer));
}

MozQuic::~MozQuic()
{
  if (!mIsChild && (mFD != MOZQUIC_SOCKET_BAD)) {
    close(mFD);
  }
}

void
MozQuic::Destroy(uint32_t code, const char *reason)
{
  Shutdown(code, reason);
  mAlive = nullptr;
}

uint32_t
MozQuic::CheckPeer(uint32_t deadline)
{
  if (mPingDeadline) {
    return MOZQUIC_OK;
  }
  if ((mConnectionState != CLIENT_STATE_CONNECTED) &&
      (mConnectionState != SERVER_STATE_CONNECTED)) {
    fprintf(stderr,"check peer not connected\n");
    return MOZQUIC_ERR_GENERAL;
  }

  mPingDeadline = Timestamp() + deadline;

  unsigned char plainPkt[kMozQuicMTU];
  unsigned char cipherPkt[kMozQuicMTU];
  uint32_t used;

  CreateShortPacketHeader(plainPkt, kMozQuicMTU, used);
  assert(used == 13);
  plainPkt[used] = FRAME_TYPE_PING;
  used++;

  uint32_t room = kMozQuicMTU - used - 16;
  uint32_t usedByAck = 0;
  if (AckPiggyBack(plainPkt + used, mNextTransmitPacketNumber, room, keyPhase1Rtt, usedByAck) == MOZQUIC_OK) {
    if (usedByAck) {
      fprintf(stderr,"Handy-Ack adds to ping packet %lX by %d\n", mNextTransmitPacketNumber, usedByAck);
    }
    used += usedByAck;
  }
  
  // 13 bytes of aead, 1 ping frame byte. result is 16 longer for aead tag
  uint32_t written = 0;
  memcpy(cipherPkt, plainPkt, 13);
  uint32_t rv = mNSSHelper->EncryptBlock(plainPkt, 13, plainPkt + 13, used - 13,
                                         mNextTransmitPacketNumber, cipherPkt + 13, kMozQuicMTU - 13, written);
  mNextTransmitPacketNumber++;
  Transmit(cipherPkt, written + 13, nullptr);

  return MOZQUIC_OK;
}
  
void
MozQuic::Shutdown(uint32_t code, const char *reason)
{
  if (mParent) {
    for (auto iter = mParent->mChildren.begin(); iter != mParent->mChildren.end(); ++iter) {
      if ((*iter).get() == this) {
          mParent->mChildren.erase(iter);
          break;
      }
    }    
  }

  if ((mConnectionState != CLIENT_STATE_CONNECTED) &&
      (mConnectionState != SERVER_STATE_CONNECTED)) {
    mConnectionState = mIsClient ? CLIENT_STATE_CLOSED : SERVER_STATE_CLOSED;
    return;
  }
  if (!mIsChild && !mIsClient) {
    // this is the listener.. it does not send packets
    return;
  }
  
  fprintf(stderr, "sending shutdown as %lx\n", mNextTransmitPacketNumber);

  unsigned char plainPkt[kMozQuicMTU];
  unsigned char cipherPkt[kMozQuicMTU];
  uint16_t tmp16;
  uint32_t tmp32;

  // todo before merge - this can't be inlined here
  // what if not kp 0 TODO
  // todo when transport params allow truncate id, the connid might go
  // short header with connid kp = 0, 4 bytes of packetnumber
  uint32_t used;
  CreateShortPacketHeader(plainPkt, kMozQuicMTU - 16, used);

  plainPkt[used] = FRAME_TYPE_CLOSE;
  used++;
  tmp32 = htonl(code);
  memcpy(plainPkt + used, &tmp32, 4);
  used += 4;

  size_t reasonLen = strlen(reason);
  if (reasonLen > (kMozQuicMTU - 16 - used - 2)) {
    reasonLen = kMozQuicMTU - 16 - used - 2;
  }
  tmp16 = htons(reasonLen);
  memcpy(plainPkt + used, &tmp16, 2);
  used += 2;
  if (reasonLen) {
    memcpy(plainPkt + used, reason, reasonLen);
    used += reasonLen;
  }

  // 13 bytes of aead, 7 + reasonLen of data. result is 16 longer for aead tag
  uint32_t written = 0;
  memcpy(cipherPkt, plainPkt, 13);
  uint32_t rv = mNSSHelper->EncryptBlock(plainPkt, 13, plainPkt + 13, 7 + reasonLen,
                                         mNextTransmitPacketNumber, cipherPkt + 13, kMozQuicMTU - 13, written);
  fprintf(stderr,"encrypt rv=%d inputlen=%d (+ %d of aead) outputlen=%d pktheaderLen =%d\n",
          rv, 7 + reasonLen, 13, written, 13);
  mNextTransmitPacketNumber++;
  Transmit(cipherPkt, written + 13, nullptr);

  mConnectionState = mIsClient ? CLIENT_STATE_CLOSED : SERVER_STATE_CLOSED;
}

void
MozQuic::GreaseVersionNegotiation()
{
  assert(mConnectionState == STATE_UNINITIALIZED);
  fprintf(stderr,"applying version grease\n");
  mVersion = kMozQuicVersionGreaseC;
}

void
MozQuic::PreferMilestoneVersion()
{
  assert(mConnectionState == STATE_UNINITIALIZED);
  mVersion = kMozQuicIetfID5;
}

bool
MozQuic::IgnorePKI()
{
  return mIgnorePKI || mIsLoopback;
}

int
MozQuic::StartClient()
{
  assert(!mHandleIO); // todo
  mIsClient = true;
  mNextStreamId = 1;
  mNextRecvStreamId = 2;
  mNSSHelper.reset(new NSSHelper(this, mTolerateBadALPN, mOriginName.get(), true));
  mStream0.reset(new MozQuicStreamPair(0, this, this));

  mConnectionState = CLIENT_STATE_1RTT;
  for (int i=0; i < 4; i++) {
    mConnectionID = mConnectionID << 16;
    mConnectionID = mConnectionID | (random() & 0xffff);
  }
  for (int i=0; i < 2; i++) {
    mNextTransmitPacketNumber = mNextTransmitPacketNumber << 16;
    mNextTransmitPacketNumber = mNextTransmitPacketNumber | (random() & 0xffff);
  }
  mNextTransmitPacketNumber &= 0x7fffffff; // 31 bits
  mOriginalTransmitPacketNumber = mNextTransmitPacketNumber;

  if (mFD == MOZQUIC_SOCKET_BAD) {
    // the application did not pass in its own fd
    mFD = socket(AF_INET, SOCK_DGRAM, 0); // todo blocking getaddrinfo
    fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK);
    struct addrinfo *outAddr;
    if (getaddrinfo(mOriginName.get(), nullptr, nullptr, &outAddr) != 0) {
      return MOZQUIC_ERR_GENERAL;
    }

    if (outAddr->ai_family == AF_INET) {
      ((struct sockaddr_in *) outAddr->ai_addr)->sin_port = htons(mOriginPort);
      if ((ntohl(((struct sockaddr_in *) outAddr->ai_addr)->sin_addr.s_addr) & 0xff000000) == 0x7f000000) {
        mIsLoopback = true;
      }
    } else if (outAddr->ai_family == AF_INET6) {
      ((struct sockaddr_in6 *) outAddr->ai_addr)->sin6_port = htons(mOriginPort);
      const void *ptr1 = &in6addr_loopback.s6_addr;
      const void *ptr2 = &((struct sockaddr_in6 *) outAddr->ai_addr)->sin6_addr.s6_addr;
      if (!memcmp(ptr1, ptr2, 16)) {
        mIsLoopback = true;
      }
    }
    
    connect(mFD, outAddr->ai_addr, outAddr->ai_addrlen);
    freeaddrinfo(outAddr);
  }
  mTimestampConnBegin = Timestamp();

  return MOZQUIC_OK;
}

int
MozQuic::StartServer()
{
  assert(!mHandleIO); // todo
  mIsClient = false;
  mNextStreamId = 2;
  mNextRecvStreamId = 1;

  mConnectionState = SERVER_STATE_LISTEN;
  assert (!mHandshakeInput); // todo
  return Bind();
}

int
MozQuic::StartNewStream(MozQuicStreamPair **outStream, const void *data, uint32_t amount, bool fin)
{
  *outStream = new MozQuicStreamPair(mNextStreamId, this, this);
  mStreams.insert( { mNextStreamId, *outStream } );
  mNextStreamId += 2;
  if ( amount || fin) {
    return (*outStream)->Write((const unsigned char *)data, amount, fin);
  }
  return MOZQUIC_OK;
}

void
MozQuic::SetOriginName(const char *name) 
{
  mOriginName.reset(new char[strlen(name) + 1]);
  strcpy (mOriginName.get(), name);
}

int
MozQuic::Bind()
{
  if (mFD != MOZQUIC_SOCKET_BAD) {
    return MOZQUIC_OK;
  }
  mFD = socket(AF_INET, SOCK_DGRAM, 0); // todo v6 and non 0 addr
  fcntl(mFD, F_SETFL, fcntl(mFD, F_GETFL, 0) | O_NONBLOCK);
  struct sockaddr_in sin;
  memset (&sin, 0, sizeof (sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(mOriginPort);
  bind(mFD, (const sockaddr *)&sin, sizeof (sin)); // todo err check
  listen(mFD, 1000); // todo err
  return MOZQUIC_OK;
}

MozQuic *
MozQuic::FindSession(uint64_t cid)
{
  assert (!mIsChild);
  if (mIsClient) {
    return mConnectionID == cid ? this : nullptr;
  }

  auto i = mConnectionHash.find(cid);
  if (i == mConnectionHash.end()) {
    Log((char *)"find session could not find id in hash");
    return nullptr;
  }
  return (*i).second;
}

static uint64_t
fnv1a(unsigned char *p, uint32_t len) 
{
  const uint64_t prime = 1099511628211UL;
  uint64_t hash = 14695981039346656037UL;
  for (uint32_t i = 0; i < len; ++i) {
    hash ^= p[i];
    hash *= prime;
  }
  return hash;
}

bool
MozQuic::IntegrityCheck(unsigned char *pkt, uint32_t pktSize) 
{
  assert (pkt[0] & 0x80);
  assert (((pkt[0] & 0x7f) == PACKET_TYPE_CLIENT_INITIAL) ||
          ((pkt[0] & 0x7f) == PACKET_TYPE_SERVER_STATELESS_RETRY) ||
          ((pkt[0] & 0x7f) == PACKET_TYPE_SERVER_CLEARTEXT) ||
          ((pkt[0] & 0x7f) == PACKET_TYPE_CLIENT_CLEARTEXT));
  if (pktSize < (kFNV64Size + 17)) {
    RaiseError(MOZQUIC_ERR_GENERAL, (char *)"hash err");
    return false;
  }
  uint64_t hash = fnv1a(pkt, pktSize - kFNV64Size);
  uint64_t recvdHash;
  memcpy(&recvdHash, pkt + pktSize - kFNV64Size, kFNV64Size);
  recvdHash = PR_ntohll(recvdHash);
  bool rv = recvdHash == hash;
  if (!rv) {
    Log((char *)"integrity error");
  }
  return rv;
}

uint32_t
MozQuic::Intake()
{
  if (mIsChild) {
    // parent does all fd reading
    return MOZQUIC_OK;
  }
  // check state
  assert (mConnectionState == SERVER_STATE_LISTEN ||
          mConnectionState == SERVER_STATE_1RTT ||
          mConnectionState == SERVER_STATE_CLOSED ||
          mConnectionState == CLIENT_STATE_CONNECTED ||
          mConnectionState == CLIENT_STATE_1RTT ||
          mConnectionState == CLIENT_STATE_CLOSED);
  uint32_t rv = MOZQUIC_OK;
  
  unsigned char pkt[kMozQuicMSS];
  bool sendAck;
  do {
    uint32_t pktSize = 0;
    sendAck = false;
    struct sockaddr_in client;
    rv = Recv(pkt, kMozQuicMSS, pktSize, &client);
    if (rv != MOZQUIC_OK || !pktSize) {
      return rv;
    }

    // dispatch to the right MozQuic class.
    MozQuic *session = this; // default

    if (!(pkt[0] & 0x80)) {
      ShortHeaderData tmpShortHeader(pkt, pktSize, 0);
      if (pktSize < tmpShortHeader.mHeaderSize) {
        return rv;
      }
      fprintf(stderr,"SHORTFORM PACKET RECVD id=%lx size=%d\n", tmpShortHeader.mConnectionID, pktSize);
      session = FindSession(tmpShortHeader.mConnectionID);
      if (!session) {
        rv = MOZQUIC_ERR_GENERAL;
      }
      ShortHeaderData shortHeader(pkt, pktSize, session->mNextRecvPacketNumber);
      rv = session->ProcessGeneral(pkt, pktSize,
                                   shortHeader.mHeaderSize, shortHeader.mPacketNumber, sendAck);
      if (rv == MOZQUIC_OK) {
        session->Acknowledge(shortHeader.mPacketNumber, keyPhase1Rtt);
      }
        
    } else {
      if (pktSize < 17) {
        return rv;
      }
      LongHeaderData longHeader(pkt, pktSize);

      fprintf(stderr,"LONG PACKET RECVD pktnum=%lX version=%X len=%d connid=%lx\n",
              longHeader.mPacketNumber, longHeader.mVersion, pktSize, longHeader.mConnectionID);

      if (!(VersionOK(longHeader.mVersion) ||
            (mIsClient && longHeader.mType == PACKET_TYPE_VERSION_NEGOTIATION && longHeader.mVersion == mVersion))) {
        // todo this could really be an amplifier
        session->GenerateVersionNegotiation(longHeader, &client);
        continue;
      }

      switch (longHeader.mType) {
      case PACKET_TYPE_VERSION_NEGOTIATION:
        // do not do integrity check (nop)
        break;
      case PACKET_TYPE_CLIENT_INITIAL:
      case PACKET_TYPE_SERVER_CLEARTEXT:
        if (!IntegrityCheck(pkt, pktSize)) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        break;
      case PACKET_TYPE_SERVER_STATELESS_RETRY:
        if (!IntegrityCheck(pkt, pktSize)) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        assert(false); // todo mvp
        break;
      case PACKET_TYPE_CLIENT_CLEARTEXT:
        if (!IntegrityCheck(pkt, pktSize)) {
          rv = MOZQUIC_ERR_GENERAL;
          break;
        }
        session = FindSession(longHeader.mConnectionID);
        if (!session) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        break;

      case PACKET_TYPE_1RTT_PROTECTED_KP0:
        session = FindSession(longHeader.mConnectionID);
        if (!session) {
          rv = MOZQUIC_ERR_GENERAL;
        }
        break;
        
      default:
        // reject anything that is not a cleartext packet (not right, but later)
        Log((char *)"recv1rtt unexpected type");
        // todo this could actually be out of order protected packet even in handshake
        // and ideally would be queued. for now we rely on retrans
        // todo
        rv = MOZQUIC_ERR_GENERAL;
        break;
      }

      if (!session || rv != MOZQUIC_OK) {
        fprintf(stderr, "unable to find connection for packet\n");
        continue;
      }

      switch (longHeader.mType) {
      case PACKET_TYPE_VERSION_NEGOTIATION: // version negotiation
        rv = session->ProcessVersionNegotiation(pkt, pktSize, longHeader);
        // do not ack
        break;
      case PACKET_TYPE_CLIENT_INITIAL:
        rv = session->ProcessClientInitial(pkt, pktSize, &client, longHeader, &session, sendAck);
        // ack after processing - find new session
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhaseUnprotected);
        }
        break;
      case PACKET_TYPE_SERVER_STATELESS_RETRY:
        // do not ack
        // todo mvp
        break;
      case PACKET_TYPE_SERVER_CLEARTEXT:
        rv = session->ProcessServerCleartext(pkt, pktSize, longHeader, sendAck);
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhaseUnprotected);
        }
        break;
      case PACKET_TYPE_CLIENT_CLEARTEXT:
        rv = session->ProcessClientCleartext(pkt, pktSize, longHeader, sendAck);
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhaseUnprotected);
        }
        break;
      case PACKET_TYPE_1RTT_PROTECTED_KP0:
        rv = session->ProcessGeneral(pkt, pktSize, 17, longHeader.mPacketNumber, sendAck);
        if (rv == MOZQUIC_OK) {
          session->Acknowledge(longHeader.mPacketNumber, keyPhase1Rtt);
        }
        break;

      default:
        assert(false);
        break;
      }
    }
    if ((rv == MOZQUIC_OK) && sendAck) {
      rv = session->MaybeSendAck();
    }
  } while (rv == MOZQUIC_OK);

  return rv;
}

int
MozQuic::IO()
{
  uint32_t code;
  std::shared_ptr<MozQuic> deleteProtector(mAlive);

  Intake();
  RetransmitTimer();
  ClearOldInitialConnetIdsTimer();
  Flush();

  if (mIsClient) {
    switch (mConnectionState) {
    case CLIENT_STATE_1RTT:
      code = Client1RTT();
      if (code != MOZQUIC_OK) {
        return code;
      }
      break;
    case CLIENT_STATE_CONNECTED:
    case CLIENT_STATE_CLOSED:
    case SERVER_STATE_CLOSED:
      break;
    default:
      assert(false);
      // todo
    }
  } else {
    if (mConnectionState == SERVER_STATE_1RTT) {
      code = Server1RTT();
      if (code != MOZQUIC_OK) {
        return code;
      }
    }
    if (!mIsChild) {
      ssize_t len = mChildren.size();
      for (auto iter = mChildren.begin();
           len == mChildren.size() && iter != mChildren.end(); ++iter) {
        (*iter)->IO();
      }
    }
  }

  if (mPingDeadline && mConnEventCB && mPingDeadline < Timestamp()) {
    fprintf(stderr,"deadline expired set at %ld now %ld\n", mPingDeadline, Timestamp());
    mPingDeadline = 0;
    mConnEventCB(mClosure, MOZQUIC_EVENT_ERROR, this);
  }
  
  if (mConnEventCB) {
    mConnEventCB(mClosure, MOZQUIC_EVENT_IO, this);
  }
  return MOZQUIC_OK;
}

void
MozQuic::Log(char *msg) 
{
{'fraction_non_alphanumeric': 0.12069003600379917, 'fraction_numerical': 0.016742871026881365, 'mean_word_length': 3.2052758684748284, 'pattern_counts': {'":': 0, '<': 74, '<?xml version=': 0, '>': 220, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '443809', 'n_tokens_mistral': 17172, 'n_tokens_neox': 16622, 'n_words': 3240}