/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (c) 2020 Infineon Technologies AG
 * All rights reserved.
 */

#ifdef HAVE_CONFIG_H
#include "config.h" // IWYU pragma: keep
#endif

#include <inttypes.h>   // for uint8_t, uint32_t, uint64_t
#include <netinet/in.h> // for htonl, ntohl
#include <stdio.h>      // for NULL, size_t, fprintf, ssize_t
#include <stdlib.h>     // for calloc, free, EXIT_SUCCESS
#include <string.h>     // for strcmp, memcpy
#include <sys/stat.h>   // for mode_t
#include <time.h>       // for clockid_t, timespec, CLOCK_...

#include "../helper/cmocka_all.h"        // for will_return, assert_int_equal
#include "tss2-tcti/tcti-common.h"       // for TSS2_TCTI_COMMON_CONTEXT
#include "tss2-tcti/tcti-pcap-builder.h" // for ENV_PCAP_FILE, pcap_buider_ctx
#include "tss2-tcti/tcti-pcap.h"         // for TSS2_TCTI_PCAP_CONTEXT, TCT...
#include "tss2_common.h"                 // for TSS2_RC_SUCCESS, TSS2_RC
#include "tss2_tcti.h"                   // for TSS2_TCTI_CONTEXT, TSS2_TCT...
#include "tss2_tcti_pcap.h"              // for Tss2_Tcti_Pcap_Init
#include "tss2_tpm2_types.h"             // for TPM2_RC_SUCCESS

#define LOGMODULE tests
#include "util/log.h"

struct timespec;

#define EXIT_SKIP 77

#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define _LE32TOH(a, b, c, d) d, c, b, a
#define _LE16TOH(a, b)       b, a
#else
#define _LE32TOH(a, b, c, d) a, b, c, d
#define _LE16TOH(a, b)       a, b
#endif

#define TCTI_STUB_CONF             "stub"
#define TCTI_PCAP_ENV_VAR          "pcap_env_var"
#define TCTI_PCAP_FILE             "pcap_file"
#define TCTI_PCAP_FD               0x01234567
#define TCTI_PCAP_IP_HOST          0x01, 0x02, 0x03, 0x04
#define TCTI_PCAP_IP_TPM           0x05, 0x06, 0x07, 0x08
#define TCTI_PCAP_IP_HOST_L        0x0102
#define TCTI_PCAP_IP_HOST_H        0x0304
#define TCTI_PCAP_IP_TPM_L         0x0506
#define TCTI_PCAP_IP_TPM_H         0x0708
#define TCTI_PCAP_TCP_SEQ_HOST     0x00, 0x11, 0x22, 0x33
#define TCTI_PCAP_TCP_SEQ_TPM      0xaa, 0xbb, 0xcc, 0xdd
#define TCTI_PCAP_TCP_SEQ_HOST_INT 0x00112233
#define TCTI_PCAP_TCP_SEQ_TPM_INT  0xaabbccdd
#define TCTI_PCAP_HOST_PORT_BYTES  0xcd, 0xef
#define TCTI_PCAP_TIMESTAMP_SEC    ((uint64_t)0x0001020304050607 / 1000000)
#define TCTI_PCAP_TIMESTAMP_NSEC   (((uint64_t)0x0001020304050607 % 1000000) * 1000)
#define TCTI_PCAP_TIMESTAMP_BYTES  _LE32TOH(0x03, 0x02, 0x01, 0x00), _LE32TOH(0x07, 0x06, 0x05, 0x04)

static const uint8_t pcap_header[] = {
    /* section header block */
    _LE32TOH(0x0a, 0x0d, 0x0d, 0x0a), _LE32TOH(0x1c, 0x00, 0x00, 0x00),
    _LE32TOH(0x4d, 0x3c, 0x2b, 0x1a), _LE16TOH(0x01, 0x00), 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, _LE32TOH(0x1c, 0x00, 0x00, 0x00),
    /* interface description block */
    _LE32TOH(0x01, 0x00, 0x00, 0x00), _LE32TOH(0x14, 0x00, 0x00, 0x00), _LE16TOH(0xE4, 0x00), 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, _LE32TOH(0x14, 0x00, 0x00, 0x00)
};

static uint8_t pcap_rx_epb_data[] = {
    /* enhanced packet block header */
    _LE32TOH(0x06, 0x00, 0x00, 0x00), _LE32TOH(0x4c, 0x00, 0x00, 0x00), 0x00, 0x00, 0x00, 0x00,
    TCTI_PCAP_TIMESTAMP_BYTES, _LE32TOH(0x2b, 0x00, 0x00, 0x00), _LE32TOH(0x2b, 0x00, 0x00, 0x00),
    /* ipv4 header */
    0x45, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x40, 0x00, 0xff, 0x06, 0x00, 0x00, TCTI_PCAP_IP_TPM,
    TCTI_PCAP_IP_HOST,
    /* tcp header */
    0x09, 0x11, 0xc3, 0x50, TCTI_PCAP_TCP_SEQ_TPM, TCTI_PCAP_TCP_SEQ_HOST, 0x50, 0x10, 0xff, 0xff,
    0x00, 0x00, 0x00, 0x00,
    /* data */
    0x00, 0x01, 0x02,
    /* epb padding */
    0x00,
    /* epb footer */
    _LE32TOH(0x4c, 0x00, 0x00, 0x00)
};

static uint8_t pcap_tx_epb_data[] = {
    /* enhanced packet block header */
    _LE32TOH(0x06, 0x00, 0x00, 0x00), _LE32TOH(0x4c, 0x00, 0x00, 0x00), 0x00, 0x00, 0x00, 0x00,
    TCTI_PCAP_TIMESTAMP_BYTES, _LE32TOH(0x2b, 0x00, 0x00, 0x00), _LE32TOH(0x2b, 0x00, 0x00, 0x00),
    /* ipv4 header */
    0x45, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x40, 0x00, 0xff, 0x06, 0x00, 0x00, TCTI_PCAP_IP_HOST,
    TCTI_PCAP_IP_TPM,
    /* tcp header */
    0xc3, 0x50, 0x09, 0x11, TCTI_PCAP_TCP_SEQ_HOST, TCTI_PCAP_TCP_SEQ_TPM, 0x50, 0x10, 0xff, 0xff,
    0x00, 0x00, 0x00, 0x00,
    /* data */
    0x00, 0x01, 0x02,
    /* epb padding */
    0x00,
    /* epb footer */
    _LE32TOH(0x4c, 0x00, 0x00, 0x00)
};

typedef struct {
    TSS2_TCTI_COMMON_CONTEXT common;
} TSS2_TCTI_STUB_CONTEXT;

TSS2_RC
tcti_stub_transmit(TSS2_TCTI_CONTEXT *tcti_ctx, size_t size, const uint8_t *cmd_buf) {
    ssize_t  ret = mock_type(ssize_t);
    size_t   expected_size = mock_type(int);
    uint8_t *expected_buf = mock_type(uint8_t *);

    assert_int_equal(expected_size, size);
    assert_memory_equal(expected_buf, cmd_buf, size);

    return ret;
}

TSS2_RC
tcti_stub_receive(TSS2_TCTI_CONTEXT *tctiContext,
                  size_t            *response_size,
                  uint8_t           *response_buffer,
                  int32_t            timeout) {
    ssize_t ret = mock_type(ssize_t);
    *response_size = mock_type(int);
    uint8_t *buf_in;

    /* partial read */
    if (response_buffer == NULL) {
        return ret;
    }

    buf_in = mock_type(uint8_t *);
    memcpy(response_buffer, buf_in, *response_size);

    return ret;
}

TSS2_RC
tcti_stub_cancel(TSS2_TCTI_CONTEXT *tctiContext) { return mock_type(ssize_t); }

TSS2_RC
tcti_stub_set_locality(TSS2_TCTI_CONTEXT *tctiContext, uint8_t locality) {
    ssize_t ret = mock_type(ssize_t);
    uint8_t expected_locality = mock_type(int);

    assert_int_equal(expected_locality, locality);

    return ret;
}

TSS2_RC
tcti_stub_get_poll_handles(TSS2_TCTI_CONTEXT     *tctiContext,
                           TSS2_TCTI_POLL_HANDLE *handles,
                           size_t                *num_handles) {
    ssize_t ret = mock_type(ssize_t);

    return ret;
}

TSS2_RC
Tss2_TctiLdr_Initialize(const char *nameConf, TSS2_TCTI_CONTEXT **tctiContext) {
    TSS2_TCTI_STUB_CONTEXT   *tcti_pcap;
    TSS2_TCTI_COMMON_CONTEXT *tcti_common;

    *tctiContext = NULL;

    if (nameConf != NULL && strcmp(nameConf, TCTI_STUB_CONF) == 0) {
        /* create and return stub tcti */
        tcti_pcap = calloc(1, sizeof(TSS2_TCTI_STUB_CONTEXT));
        tcti_common = (TSS2_TCTI_COMMON_CONTEXT *)tcti_pcap;

        TSS2_TCTI_MAGIC(tcti_common) = TCTI_PCAP_MAGIC;
        TSS2_TCTI_VERSION(tcti_common) = TCTI_VERSION;
        TSS2_TCTI_TRANSMIT(tcti_common) = tcti_stub_transmit;
        TSS2_TCTI_RECEIVE(tcti_common) = tcti_stub_receive;
        TSS2_TCTI_FINALIZE(tcti_common) = NULL;
        TSS2_TCTI_CANCEL(tcti_common) = tcti_stub_cancel;
        TSS2_TCTI_GET_POLL_HANDLES(tcti_common) = tcti_stub_get_poll_handles;
        TSS2_TCTI_SET_LOCALITY(tcti_common) = tcti_stub_set_locality;
        TSS2_TCTI_MAKE_STICKY(tcti_common) = NULL;
        tcti_common->state = TCTI_STATE_TRANSMIT;

        *tctiContext = (TSS2_TCTI_CONTEXT *)tcti_pcap;

        return TPM2_RC_SUCCESS;
    } else {
        /* return mocked rc */
        return mock_type(int);
    }
}

void
Tss2_TctiLdr_Finalize(TSS2_TCTI_CONTEXT **tctiContext) {
    free(*tctiContext);
}

char *__real_getenv(const char *name);

char *
__wrap_getenv(const char *name) {
    if (name != NULL && strcmp(name, ENV_PCAP_FILE) == 0) {
        return TCTI_PCAP_FILE;
    }

    return __real_getenv(name);
}

int
__wrap_rand(void) {
    return mock_type(int);
}

int
__wrap_clock_gettime(clockid_t clk_id, struct timespec *tp) {
    if (clk_id == CLOCK_REALTIME) {
        tp->tv_sec = TCTI_PCAP_TIMESTAMP_SEC;
        tp->tv_nsec = TCTI_PCAP_TIMESTAMP_NSEC;
        return EXIT_SUCCESS;
    }

    return EXIT_FAILURE;
}

int
__wrap___clock_gettime64(clockid_t clk_id, struct timespec *tp) {
    return __wrap_clock_gettime(clk_id, tp);
}

int __real_open(const char *pathname, int flags, mode_t mode);

int
__wrap_open(const char *pathname, int flags, mode_t mode) {
    if (pathname != NULL && strcmp(pathname, TCTI_PCAP_FILE) == 0) {
        return mock_type(int);
    } else {
        return __real_open(pathname, flags, mode);
    }
}

ssize_t __real_read(int fd, void *buffer, size_t buffer_size);

ssize_t
__wrap_read(int fd, void *buffer, size_t buffer_size) {
    uint8_t *buf_in;
    ssize_t  ret;

    if (fd == TCTI_PCAP_FD) {
        ret = mock_type(ssize_t);
        buf_in = mock_type(uint8_t *);

        memcpy(buffer, buf_in, ret);
        return ret;
    } else {
        return __real_read(fd, buffer, buffer_size);
    }
}

int __real_close(int fd);

int
__wrap_close(int fd) {
    if (fd == TCTI_PCAP_FD) {
        return mock_type(int);
    } else {
        return __real_close(fd);
    }
}

ssize_t __real_write(int fd, void *buffer, size_t buffer_size);

ssize_t
__wrap_write(int fd, void *buffer, size_t buffer_size) {
    size_t   expected_size;
    uint8_t *expected_buffer;
    ssize_t  ret;

    if (fd == TCTI_PCAP_FD) {
        ret = mock_type(int);
        expected_size = mock_type(int);
        expected_buffer = mock_type(uint8_t *);

        fprintf(stderr, "exp: ");
        for (size_t i = 0; i < expected_size; i++)
            fprintf(stderr, "%02x", ((uint8_t *)expected_buffer)[i]);
        fprintf(stderr, "\n");

        fprintf(stderr, "got: ");
        for (size_t i = 0; i < buffer_size; i++)
            fprintf(stderr, "%02x", ((uint8_t *)buffer)[i]);
        fprintf(stderr, "\n");

        assert_int_equal(expected_size, buffer_size);
        assert_memory_equal(expected_buffer, buffer, buffer_size);

        return ret;
    } else {
        return __real_write(fd, buffer, buffer_size);
    }
}

static void
update_tcp_seq(void *data, uint32_t size) {
    const size_t offset = 52;
    uint32_t     seq_no;

    seq_no = *((uint32_t *)(data + offset));

    seq_no = ntohl(seq_no);
    seq_no += size;
    seq_no = htonl(seq_no);

    *((uint32_t *)(data + offset)) = seq_no;
}

static void
tcti_pcap_init_context_and_size_null_test(void **state) {
    TSS2_RC rc;

    rc = Tss2_Tcti_Pcap_Init(NULL, NULL, NULL);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_VALUE);
}

static void
tcti_pcap_init_size_test(void **state) {
    size_t  tcti_size = 0;
    TSS2_RC rc;

    rc = Tss2_Tcti_Pcap_Init(NULL, &tcti_size, NULL);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
    assert_int_equal(tcti_size, sizeof(TSS2_TCTI_PCAP_CONTEXT));
}

static void
tcti_pcap_init_tctildr_fail_test(void **state) {
    size_t                 tcti_size = 0;
    TSS2_TCTI_PCAP_CONTEXT tcti_pcap = { 0 };
    TSS2_TCTI_CONTEXT     *tcti = (TSS2_TCTI_CONTEXT *)&tcti_pcap;
    TSS2_RC                rc = TSS2_RC_SUCCESS;

    will_return(Tss2_TctiLdr_Initialize, TSS2_TCTI_RC_MEMORY);
    rc = Tss2_Tcti_Pcap_Init(tcti, &tcti_size, NULL);
    assert_int_equal(rc, TSS2_TCTI_RC_MEMORY);
    assert_int_equal(tcti_pcap.pcap_builder.fd, 0);
}

static void
tcti_pcap_init_open_fail_test(void **state) {
    size_t             tcti_size = 0;
    TSS2_RC            ret = TSS2_RC_SUCCESS;
    TSS2_TCTI_CONTEXT *tcti = NULL;
    TSS2_RC            rc = TSS2_RC_SUCCESS;

    ret = Tss2_Tcti_Pcap_Init(NULL, &tcti_size, NULL);
    assert_true(ret == TSS2_RC_SUCCESS);

    tcti = calloc(1, tcti_size);
    assert_non_null(tcti);

    will_return(__wrap_rand, 0); /* host ip */
    will_return(__wrap_rand, 0);
    will_return(__wrap_rand, 0); /* tpm ip */
    will_return(__wrap_rand, 0);
    will_return(__wrap_rand, 0); /* host sequence no */
    will_return(__wrap_rand, 0); /* tpm sequence no */
    will_return(__wrap_open, -1);
    rc = Tss2_Tcti_Pcap_Init(tcti, &tcti_size, TCTI_STUB_CONF);
    assert_int_equal(rc, TSS2_TCTI_RC_IO_ERROR);

    free(tcti);
}

static void
tcti_pcap_init_write_fail_test(void **state) {
    size_t             tcti_size = 0;
    TSS2_RC            ret = TSS2_RC_SUCCESS;
    TSS2_TCTI_CONTEXT *tcti = NULL;
    TSS2_RC            rc = TSS2_RC_SUCCESS;

    ret = Tss2_Tcti_Pcap_Init(NULL, &tcti_size, NULL);
    assert_true(ret == TSS2_RC_SUCCESS);

    tcti = calloc(1, tcti_size);
    assert_non_null(tcti);

    will_return(__wrap_rand, 0); /* host ip */
    will_return(__wrap_rand, 0);
    will_return(__wrap_rand, 0); /* tpm ip */
    will_return(__wrap_rand, 0);
    will_return(__wrap_rand, 0); /* host sequence no */
    will_return(__wrap_rand, 0); /* tpm sequence no */
    will_return(__wrap_open, TCTI_PCAP_FD);
    will_return(__wrap_write, -1);
    will_return(__wrap_write, sizeof(pcap_header));
    will_return(__wrap_write, pcap_header);
    will_return(__wrap_close, 0); /* close pcap */
    rc = Tss2_Tcti_Pcap_Init(tcti, &tcti_size, TCTI_STUB_CONF);
    assert_int_equal(rc, TSS2_TCTI_RC_IO_ERROR);

    free(tcti);
}

/* Setup functions to create the context for the pcap TCTI */
static int
tcti_pcap_setup(void **state) {
    size_t             tcti_size = 0;
    TSS2_RC            ret = TSS2_RC_SUCCESS;
    TSS2_TCTI_CONTEXT *tcti = NULL;

    ret = Tss2_Tcti_Pcap_Init(NULL, &tcti_size, NULL);
    assert_true(ret == TSS2_RC_SUCCESS);

    tcti = calloc(1, tcti_size);
    assert_non_null(tcti);

    will_return(__wrap_rand, TCTI_PCAP_IP_HOST_L); /* host ip */
    will_return(__wrap_rand, TCTI_PCAP_IP_HOST_H);
    will_return(__wrap_rand, TCTI_PCAP_IP_TPM_L); /* tpm ip */
    will_return(__wrap_rand, TCTI_PCAP_IP_TPM_H);
    will_return(__wrap_rand, TCTI_PCAP_TCP_SEQ_HOST_INT); /* host sequence no */
    will_return(__wrap_rand, TCTI_PCAP_TCP_SEQ_TPM_INT);  /* tpm sequence no */
    will_return(__wrap_open, TCTI_PCAP_FD);
    will_return(__wrap_write, sizeof(pcap_header));
    will_return(__wrap_write, sizeof(pcap_header));
    will_return(__wrap_write, pcap_header);
    ret = Tss2_Tcti_Pcap_Init(tcti, &tcti_size, TCTI_STUB_CONF);
    assert_true(ret == TSS2_RC_SUCCESS);

    *state = tcti;
    return 0;
}

static int
tcti_pcap_teardown(void **state) {
    TSS2_TCTI_CONTEXT *tcti = (TSS2_TCTI_CONTEXT *)*state;

    will_return(__wrap_close, EXIT_SUCCESS);
    Tss2_Tcti_Finalize(tcti);
    free(tcti);

    return 0;
}

static void
tcti_pcap_receive_test(void **state) {
    TSS2_TCTI_CONTEXT        *ctx = (TSS2_TCTI_CONTEXT *)*state;
    TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast(ctx);
    size_t                    partial_read_size = 123;
    uint8_t                   mock_response_buffer[] = { 0x00, 0x01, 0x02 };
    size_t                    mock_response_size = sizeof(mock_response_buffer);
    uint8_t                   response_buffer[100] = { 0 };
    size_t                    response_size = sizeof(response_buffer);
    TSS2_RC                   rc;

    tcti_common->state = TCTI_STATE_RECEIVE;

    /* have tcti_common_receive_checks fail */
    rc = Tss2_Tcti_Receive(ctx, NULL, NULL, TSS2_TCTI_TIMEOUT_BLOCK);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_REFERENCE);

    /* partial read: give back response_size of 123 */
    will_return(tcti_stub_receive, TSS2_RC_SUCCESS);
    will_return(tcti_stub_receive, partial_read_size);
    rc = Tss2_Tcti_Receive(ctx, &response_size, NULL, /* NULL buffer */
                           TSS2_TCTI_TIMEOUT_BLOCK);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
    assert_int_equal(response_size, partial_read_size);

    /* underlying read fails */
    will_return(tcti_stub_receive, TSS2_TCTI_RC_IO_ERROR);
    will_return(tcti_stub_receive, 0);
    will_return(tcti_stub_receive, NULL);
    /* receive fails, it will not log to pcap */
    rc = Tss2_Tcti_Receive(ctx, &response_size, response_buffer, TSS2_TCTI_TIMEOUT_BLOCK);
    assert_int_equal(rc, TSS2_TCTI_RC_IO_ERROR);

    /* read successfully  */
    will_return(tcti_stub_receive, TSS2_RC_SUCCESS);
    will_return(tcti_stub_receive, mock_response_size);
    will_return(tcti_stub_receive, mock_response_buffer);
    will_return(__wrap_write, sizeof(pcap_rx_epb_data));
    will_return(__wrap_write, sizeof(pcap_rx_epb_data));
    will_return(__wrap_write, pcap_rx_epb_data);
    rc = Tss2_Tcti_Receive(ctx, &response_size, response_buffer, TSS2_TCTI_TIMEOUT_BLOCK);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
    assert_int_equal(response_size, mock_response_size);
    assert_memory_equal(response_buffer, mock_response_buffer, response_size);

    /* read successfully, but fail to write to pcap  */
    tcti_common->state = TCTI_STATE_RECEIVE;
    update_tcp_seq(pcap_rx_epb_data, mock_response_size);
    will_return(tcti_stub_receive, TSS2_RC_SUCCESS);
    will_return(tcti_stub_receive, mock_response_size);
    will_return(tcti_stub_receive, mock_response_buffer);
    will_return(__wrap_write, -1); /* fail to write to pcap */
    will_return(__wrap_write, sizeof(pcap_rx_epb_data));
    will_return(__wrap_write, pcap_rx_epb_data);
    rc = Tss2_Tcti_Receive(ctx, &response_size, response_buffer, TSS2_TCTI_TIMEOUT_BLOCK);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
    assert_int_equal(response_size, mock_response_size);
    assert_memory_equal(response_buffer, mock_response_buffer, response_size);
}

static void
tcti_pcap_transmit_test(void **state) {
    TSS2_TCTI_CONTEXT        *ctx = (TSS2_TCTI_CONTEXT *)*state;
    TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast(ctx);
    uint8_t                   mock_transmit_buffer[] = { 0x00, 0x01, 0x02 };
    size_t                    mock_transmit_size = sizeof(mock_transmit_buffer);
    TSS2_RC                   rc;

    tcti_common->state = TCTI_STATE_TRANSMIT;

    /* have tcti_common_transmit_checks fail (cmd_buf = NULL) */
    rc = Tss2_Tcti_Transmit(ctx, 0, NULL);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_REFERENCE);

    /* underlying tranmit fails*/
    will_return(tcti_stub_transmit, TSS2_TCTI_RC_IO_ERROR);
    will_return(tcti_stub_transmit, mock_transmit_size);   /* assert size */
    will_return(tcti_stub_transmit, mock_transmit_buffer); /* assert buf */
    /* transmit fails, but it still logs to pcap */
    will_return(__wrap_write, sizeof(pcap_tx_epb_data));
    will_return(__wrap_write, sizeof(pcap_tx_epb_data));
    will_return(__wrap_write, pcap_tx_epb_data);
    rc = Tss2_Tcti_Transmit(ctx, mock_transmit_size,
                            mock_transmit_buffer); /* have tcti_common_transmit_checks fail */
    assert_int_equal(rc, TSS2_TCTI_RC_IO_ERROR);

    /* transmit successfully */
    update_tcp_seq(pcap_tx_epb_data, mock_transmit_size);
    will_return(tcti_stub_transmit, TSS2_RC_SUCCESS);
    will_return(tcti_stub_transmit, mock_transmit_size);   /* assert size */
    will_return(tcti_stub_transmit, mock_transmit_buffer); /* assert buf */
    will_return(__wrap_write, sizeof(pcap_tx_epb_data));
    will_return(__wrap_write, sizeof(pcap_tx_epb_data));
    will_return(__wrap_write, pcap_tx_epb_data);
    rc = Tss2_Tcti_Transmit(ctx, mock_transmit_size, mock_transmit_buffer);
    assert_int_equal(rc, TSS2_RC_SUCCESS);

    /* transmit successfully, but fail to write to pcap */
    tcti_common->state = TCTI_STATE_TRANSMIT;
    update_tcp_seq(pcap_tx_epb_data, mock_transmit_size);
    will_return(tcti_stub_transmit, TSS2_RC_SUCCESS);
    will_return(tcti_stub_transmit, mock_transmit_size);   /* assert size */
    will_return(tcti_stub_transmit, mock_transmit_buffer); /* assert buf */
    will_return(__wrap_write, -1);                         /* fail to write to pcap */
    will_return(__wrap_write, sizeof(pcap_tx_epb_data));
    will_return(__wrap_write, pcap_tx_epb_data);
    rc = Tss2_Tcti_Transmit(ctx, mock_transmit_size, mock_transmit_buffer);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
}

static void
tcti_pcap_cancel_test(void **state) {
    TSS2_TCTI_CONTEXT        *ctx = (TSS2_TCTI_CONTEXT *)*state;
    TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast(ctx);
    TSS2_RC                   rc;

    tcti_common->state = TCTI_STATE_RECEIVE;

    /* have tcti_common_cancel_checks fail */
    rc = Tss2_Tcti_Cancel(NULL);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_REFERENCE);

    /* cancel successfully */
    will_return(tcti_stub_cancel, TSS2_RC_SUCCESS);
    /* cancel will not write to pcap */
    rc = Tss2_Tcti_Cancel(ctx);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
}

static void
tcti_pcap_set_locality_test(void **state) {
    TSS2_TCTI_CONTEXT        *ctx = (TSS2_TCTI_CONTEXT *)*state;
    TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast(ctx);
    uint8_t                   mock_locality = 3;
    TSS2_RC                   rc;

    tcti_common->state = TCTI_STATE_TRANSMIT;

    /* have tcti_common_set_locality_checks fail */
    rc = Tss2_Tcti_SetLocality(NULL, 0);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_REFERENCE);

    /* set_locality successfully */
    will_return(tcti_stub_set_locality, TSS2_RC_SUCCESS);
    will_return(tcti_stub_set_locality, mock_locality);
    /* set_locality will not write to pcap */
    rc = Tss2_Tcti_SetLocality(ctx, mock_locality);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
}

static void
tcti_pcap_get_poll_handles_test(void **state) {
    TSS2_TCTI_CONTEXT        *ctx = (TSS2_TCTI_CONTEXT *)*state;
    TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_common_context_cast(ctx);
    TSS2_TCTI_POLL_HANDLE     handles[5] = { 0 };
    size_t                    num_handles = sizeof(handles);
    TSS2_RC                   rc;

    tcti_common->state = TCTI_STATE_TRANSMIT;

    /* have tcti_common_get_poll_handles_checks fail */
    rc = Tss2_Tcti_GetPollHandles(NULL, NULL, 0);
    assert_int_equal(rc, TSS2_TCTI_RC_BAD_REFERENCE);

    /* get_poll_handles successfully */
    will_return(tcti_stub_get_poll_handles, TSS2_RC_SUCCESS);
    /* get_poll_handles will not write to pcap */
    rc = Tss2_Tcti_GetPollHandles(ctx, handles, &num_handles);
    assert_int_equal(rc, TSS2_RC_SUCCESS);
}

int
main(int argc, char *argv[]) {
#if _FILE_OFFSET_BITS == 64
    // Would produce cmocka error
    LOG_WARNING("_FILE_OFFSET == 64 would produce cmocka errors.");
    return EXIT_SKIP;
#endif

    const struct CMUnitTest tests[] = {
        cmocka_unit_test(tcti_pcap_init_context_and_size_null_test),
        cmocka_unit_test(tcti_pcap_init_size_test),
        cmocka_unit_test(tcti_pcap_init_tctildr_fail_test),
        cmocka_unit_test(tcti_pcap_init_open_fail_test),
        cmocka_unit_test(tcti_pcap_init_write_fail_test),
        cmocka_unit_test_setup_teardown(tcti_pcap_receive_test, tcti_pcap_setup,
                                        tcti_pcap_teardown),
        cmocka_unit_test_setup_teardown(tcti_pcap_transmit_test, tcti_pcap_setup,
                                        tcti_pcap_teardown),
        cmocka_unit_test_setup_teardown(tcti_pcap_cancel_test, tcti_pcap_setup, tcti_pcap_teardown),
        cmocka_unit_test_setup_teardown(tcti_pcap_set_locality_test, tcti_pcap_setup,
                                        tcti_pcap_teardown),
        cmocka_unit_test_setup_teardown(tcti_pcap_get_poll_handles_test, tcti_pcap_setup,
                                        tcti_pcap_teardown),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
