/*
 * Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>
 * Copyright (c) 2023-2024 Simone Rossetto <simros85@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 * 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
 *  its contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#if !defined(__ESP_WIREGUARD__H__)
#define __ESP_WIREGUARD__H__

#ifdef __cplusplus
extern "C" {
#endif

#include "esp_wireguard_err.h"

#include <stdint.h>
#include <time.h>
#include <lwip/netif.h>

#define WG_KEY_LEN  (32)
#define WG_B64_KEY_LEN (4 * ((WG_KEY_LEN + 2) / 3))

typedef uint8_t wg_key_t[WG_KEY_LEN];

typedef struct {
    /* interface config */
    wg_key_t    private_key2;           /**< private key generated by wg genkey. Required. */
    uint16_t    listen_port;            /**< a 16-bit port for listening */
    uint32_t    fw_mark;                /**< a 32-bit fwmark for outgoing packets */
    /* peer config */
    wg_key_t    public_key2;            /**< public key calculated by wg pubkey from a private key. Required. */
    wg_key_t    preshared_key2;         /**< preshared key generated by wg genpsk. */
    ip_addr_t   address2;               /**< a local IP address. */
    ip_addr_t   subnet;                 /**< a subnet mask of the local IP address. */
    ip_addr_t   netmask2;               /**< the global subnet for the netif. */
    const char* endpoint;               /**< an endpoint IP address or hostname. */
    ip_addr_t   endpoint_ip;            /**< endpoint IP address (internal use, resolved through dns query) */
    uint16_t    port;                   /**< a port number of remote endpoint. Default is 51820. */
    uint16_t    persistent_keepalive;   /**< a seconds interval, between 1 and 65535 inclusive, of how often to send an
                                             authenticated empty packet to the peer for the purpose of keeping a stateful
                                             firewall or NAT mapping valid persistently. Set zero to disable the feature.
                                             Default is zero. */
} wireguard_config_t;

typedef struct {
    wireguard_config_t* config;        /**< a pointer to wireguard config */
    struct netif*       netif;         /**< a pointer to configured netif */
} wireguard_ctx_t;

/**
 * @brief Initialize WireGuard
 *
 * Call this function to initialize the context of WireGuard.
 *
 * Do not call this function multiple times.
 *
 * To connect to other peer, use `esp_wireguard_disconnect()`, and
 * `esp_wireguard_init()` with a new configuration. To reconnect to
 * the same peer just use `esp_wireguard_disconnect()` and then
 * `esp_wireguard_connect()`.
 *
 * @param       config WireGuard configuration.
 * @param[out]  ctx Context of WireGuard.
 *
 * @return
 *      - ESP_OK: Successfully initilized WireGuard interface.
 *      - ESP_ERR_INVALID_ARG: given argument is invalid.
 *      - ESP_ERR_INVALID_STATE: hostname dns resolution cannot start
 *      - ESP_FAIL: Other error.
 */
esp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx);

/**
 * @brief Create a WireGuard interface and start establishing the connection
 *        to the peer.
 *
 * Call this function to start establishing the connection. Note that `ESP_OK`
 * does not mean the connection is established. To see if the connection is
 * established, or the peer is up, use `esp_wireguard_peer_is_up()`.
 *
 * Do not call this function multiple times.
 *
 * @param ctx Context of WireGuard.
 * @return
 *      - ESP_OK on success.
 *      - ESP_ERR_INVALID_ARG if input arguments are invalid
 *      - ESP_ERR_RETRY dns query still ongoing for endpoint hostname resolution (retry connection)
 *      - ESP_ERR_INVALID_IP if endpoint IP address is missing or invalid (dns query failed)
 *      - ESP_FAIL on failure.
 */
esp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx);

/**
 * @brief Test if the peer is up.
 * @param ctx Context of WireGuard
 * @return
 *      - ESP_OK on peer up.
 *      - ESP_ERR_INVALID_ARG if ctx is NULL.
 *      - ESP_FAIL on peer still down.
 */
esp_err_t esp_wireguard_peer_is_up(const wireguard_ctx_t *ctx);

/**
 * @brief Get timestamp of the latest handshake (with seconds resolution since unix epoch)
 * @param ctx Context of WireGuard
 * @param result the output timestamp
 * @return
 *      - ESP_OK on success
 *      - ESP_FAIL if no handshake already completed
 *      - ESP_ERR_INVALID_ARG if ctx is NULL
 *      - ESP_ERR_INVALID_STATE if data inside ctx is not valid
 */
esp_err_t esp_wireguard_latest_handshake(const wireguard_ctx_t *ctx, time_t *result);

/**
 * @brief Add new allowed IP/mask to the list of allowed ip/mask
 * @param ctx Context of WireGuard
 * @param allowed_ip The new IP to be allowed through tunnel
 * @param allowed_ip_mask The mask of the new IP
 * @return
 *      - ESP_OK on success
 *      - ESP_FAIL if the adding failed
 *      - ESP_ERR_INVALID_ARG if ctx, allowed_ip or allowed_ip_mask are invalid or empty
 *      - ESP_ERR_INVALID_STATE if data inside ctx is not valid
 */
esp_err_t esp_wireguard_add_allowed_ip(const wireguard_ctx_t *ctx, const ip_addr_t& allowed_ip, const ip_addr_t& allowed_ip_mask);

/**
 * @brief Disconnect from the peer
 *
 * @param ctx Context of WireGuard.
 * @return
 *      - ESP_OK on success.
 */
esp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx);

#ifdef __cplusplus
}
#endif

#endif
// vim: expandtab tabstop=4
