/**
 * @file session_p.h
 * @author Radek Krejci <rkrejci@cesnet.cz>
 * @author Michal Vasko <mvasko@cesnet.cz>
 * @brief libnetconf2 session manipulation
 *
 * @copyright
 * Copyright (c) 2017 - 2025 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#ifndef NC_SESSION_PRIVATE_H_
#define NC_SESSION_PRIVATE_H_

#define _GNU_SOURCE

#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/types.h>

#include <libyang/libyang.h>

#include "compat.h"
#include "config.h"
#include "session_client.h"
#include "session_server.h"
#include "session_server_ch.h"

#ifdef NC_ENABLED_SSH_TLS

#include "session_wrapper.h"

#endif /* NC_ENABLED_SSH_TLS */

extern struct nc_server_opts server_opts;

#define client_opts nc_client_context_location()->opts
#define client_unix_opts nc_client_context_location()->unix_opts
#define client_ssh_opts nc_client_context_location()->ssh_opts
#define client_ssh_ch_opts nc_client_context_location()->ssh_ch_opts
#define client_tls_opts nc_client_context_location()->tls_opts
#define client_tls_ch_opts nc_client_context_location()->tls_ch_opts

/**
 * Size added to the reader buffer on realloc.
 * Used only for NETCONF 1.0 messages (always the hello message).
 */
#define NC_READ_BUF_SIZE_STEP 512

/**
 * Maximum size of a written framing message chunk (spec max is 2^32 - 1).
 */
#define NC_WRITE_CHUNK_SIZE_MAX 1024 * 10

/**
 * Maximum size of a read framing message chunk.
 */
#define NC_READ_CHUNK_SIZE_MAX 1024 * 1024 * 10

/**
 * Sleep time in usec to wait between nc_recv_notif() calls.
 */
#define NC_CLIENT_NOTIF_THREAD_SLEEP 10000

/**
 * Timeout in msec for transport-related data to arrive (ssh_handle_key_exchange(), SSL_accept(), SSL_connect()).
 * It can be quite a lot on slow machines (waiting for TLS cert-to-name resolution, ...).
 */
#define NC_TRANSPORT_TIMEOUT 10000

/**
 * Timeout in msec for acquiring a lock of a session (used with a condition, so higher numbers could be required
 * only in case of extreme concurrency).
 */
#define NC_SESSION_LOCK_TIMEOUT 500

/**
 * Timeout in msec for acquiring a lock of a session that is supposed to be freed.
 */
#define NC_SESSION_FREE_LOCK_TIMEOUT 1000

/**
 * Timeout in msec to poll for SSH channel EOF response when closing a session.
 */
#define NC_SESSION_FREE_SSH_POLL_EOF_TIMEOUT 100

/**
 * Timeout in msec for a thread to wait for its turn to work with a pollsession structure.
 */
#define NC_PS_QUEUE_TIMEOUT 5000

/**
 * @brief Maximum time (in seconds) to wait for a pending configuration
 * update to complete before rejecting a new update request.
 */
#define NC_SERVER_CONFIG_UPDATE_WAIT_TIMEOUT_SEC 5

/**
 * Time slept in msec if no endpoint was created for a running Call Home client.
 */
#define NC_CH_NO_ENDPT_WAIT 1000

/**
 * Time slept in msec between Call Home thread session idle timeout checks.
 */
#define NC_CH_THREAD_IDLE_TIMEOUT_SLEEP 1000

/**
 * Timeout in msec for a Call Home socket to establish its connection.
 */
#define NC_CH_CONNECT_TIMEOUT 500

/**
 * @brief Timeout in msec for acquiring the hello_lock
 * (iterating through all YANG modules + building capability strings)
 */
#define NC_HELLO_LOCK_TIMEOUT 5000

/**
 * @brief Timeout in msec for acquiring the ch_threads_lock
 * (simple array iteration and thread signaling operations)
 */
#define NC_CH_THREADS_LOCK_TIMEOUT 1000

/**
 * @brief Timeout in msec for acquiring the Call Home thread cond_lock
 * (short critical sections used to signal thread termination and gate condition waits)
 */
#define NC_CH_COND_LOCK_TIMEOUT 1000

/**
 * @brief Timeout in msec for acquiring the certificate expiration notification lock
 * (short critical sections for flag updates and condition signaling)
 */
#define NC_CERT_EXP_LOCK_TIMEOUT 1000

/**
 * @brief Timeout in msec for acquiring the config_lock
 * (socket binding and Call Home client dispatching can involve network operations)
 */
#define NC_CONFIG_LOCK_TIMEOUT 10000

/**
 * @brief Timeout in msec for acquiring session's ch_lock
 * (just simple session flag checks and updates)
 */
#define NC_SESSION_CH_LOCK_TIMEOUT 1000

/**
 * @brief Timeout in msec for acquiring the pollsession's lock
 * (only O(n) array manipulation, where n is number of sessions (small usually))
 */
#define NC_PS_LOCK_TIMEOUT 1000

/**
 * @brief Timeout in msec for acquiring the notification status lock
 * (short critical sections for incrementing/decrementing notification status)
 */
#define NC_SESSION_NTF_STATUS_LOCK_TIMEOUT 500

/**
 * Number of sockets kept waiting to be accepted.
 */
#define NC_REVERSE_QUEUE 5

/**
 * Time slept in msec in each cycle of the client monitoring thread.
 */
#define NC_CLIENT_MONITORING_BACKOFF 200

/**
 * Timeout in msec for acquiring a lock of a client monitoring thread.
 */
#define NC_CLIENT_MONITORING_LOCK_TIMEOUT 500

/**
 * TLS key log file environment variable name.
 */
#define NC_TLS_KEYLOGFILE_ENV "SSLKEYLOGFILE"

#define NC_VERSION_10_ENDTAG "]]>]]>"
#define NC_VERSION_10_ENDTAG_LEN 6

/**
 * Enumeration of diff operation types.
 */
enum nc_operation {
    NC_OP_UNKNOWN = 0,
    NC_OP_NONE,
    NC_OP_CREATE,
    NC_OP_DELETE,
    NC_OP_REPLACE
};

/**
 * @brief Enumeration of rwlock mode.
 */
enum nc_rwlock_mode {
    NC_RWLOCK_NONE = 0,     /**< Lock not held */
    NC_RWLOCK_READ,         /**< Read lock mode */
    NC_RWLOCK_WRITE         /**< Write lock mode */
};

/**
 * Enumeration of key or certificate store type.
 */
enum nc_store_type {
    NC_STORE_UNKNOWN = 0,   /**< unknown store type */
    NC_STORE_LOCAL,         /**< key/certificate is stored locally in the ietf-netconf-server YANG data */
    NC_STORE_KEYSTORE,      /**< key/certificate is stored externally in a keystore module YANG data */
    NC_STORE_TRUSTSTORE,    /**< key/certificate is stored externally in a truststore module YANG data */
    NC_STORE_SYSTEM         /**< key/certificate is managed by the system */
};

#ifdef NC_ENABLED_SSH_TLS

#include <curl/curl.h>
#include <libssh/libssh.h>

/**
 * Timeout set for libssh (s).
 */
#define NC_SSH_TIMEOUT 10

/**
 * @brief Enumeration of SSH public key formats.
 */
enum nc_pubkey_format {
    NC_PUBKEY_FORMAT_UNKNOWN = 0, /**< Unknown format */
    NC_PUBKEY_FORMAT_SSH, /**< see RFC 4253, section 6.6 */
    NC_PUBKEY_FORMAT_X509 /**< see RFC 5280 sec. 4.1.2.7 */
};

/**
 * @brief Enumeration of private key file formats.
 */
enum nc_privkey_format {
    NC_PRIVKEY_FORMAT_UNKNOWN = 0,  /**< Unknown format */
    NC_PRIVKEY_FORMAT_RSA,          /**< PKCS1 RSA format */
    NC_PRIVKEY_FORMAT_EC,           /**< SEC1 EC format */
    NC_PRIVKEY_FORMAT_X509,         /**< X509 (PKCS8) format */
    NC_PRIVKEY_FORMAT_OPENSSH       /**< OpenSSH format */
};

/**
 * @brief Enumeration of SSH keyboard-interactive authentication methods.
 */
enum nc_kbdint_auth_method {
    NC_KBDINT_AUTH_METHOD_NONE = 0,     /**< Keyboard-interactive authentication disabled. */
    NC_KBDINT_AUTH_METHOD_SYSTEM        /**< Keyboard-interactive authentication is left to the system (PAM, shadow, etc.). */
};

/**
 * @brief Certificate representation.
 */
struct nc_certificate {
    char *name; /**< Optional identifier of the certificate. */
    char *data; /**< Base-64 encoded certificate. */
};

/**
 * @brief Public key representation.
 */
struct nc_public_key {
    char *name;                     /**< Optional identifier of the public key. */
    enum nc_pubkey_format type;     /**< Type of the public key. */
    char *data;                     /**< Base-64 encoded public key. */
};

/**
 * @brief Private key representation.
 */
struct nc_private_key {
    enum nc_privkey_format type;    /**< Type of the private key. */
    char *data;                     /**< Base-64 encoded private key. */
};

/**
 * @brief Asymmetric key pair representation.
 */
struct nc_asymmetric_key {
    char *name;                             /**< Optional identifier of the key pair. */

    struct nc_public_key pubkey;            /**< Public key. */
    struct nc_private_key privkey;          /**< Private key. */
};

/**
 * @brief Store of trusted certificates and public keys.
 */
struct nc_truststore {
    /**
     * @brief Bag of certificates that should share the same purpose.
     */
    struct nc_certificate_bag {
        char *name;                     /**< Identifier of the certificate bag. */
        char *description;              /**< Optional description of the certificate bag. */
        struct nc_certificate *certs;   /**< Certificates in the bag (sized-array, see libyang docs). */
    } *cert_bags;                       /**< Certificate bags (sized-array, see libyang docs). */

    /**
     * @brief Bag of public keys that should share the same purpose.
     */
    struct nc_public_key_bag {
        char *name;                     /**< Identifier of the public key bag. */
        char *description;              /**< Optional description of the public key bag. */
        struct nc_public_key *pubkeys;  /**< Public keys in the bag (sized-array, see libyang docs). */
    } *pubkey_bags;                     /**< Public key bags (sized-array, see libyang docs). */
};

/**
 * @brief Keystore YANG module representation.
 */
struct nc_keystore {
    struct nc_keystore_entry {
        struct nc_asymmetric_key asym_key;      /**< Stored asymmetric key. */
        struct nc_certificate *certs;           /**< Certificates associated with the asymmetric key (sized-array, see libyang docs). */
    } *entries;                                 /**< Asymmetric key + certs entries (sized-array, see libyang docs). */
};

/**
 * @brief Tracks the state of a client's authentication.
 */
struct nc_auth_state {
    int methods;            /**< Bit field of authentication methods that the user supports. */
    int method_count;       /**< Number of authentication methods that the user supports. */
    int success_methods;    /**< Bit field of authentication methods that the user successfully authenticated with. */
    int success_count;      /**< Number of authentication methods that the user successfully authenticated with. */
};

/**
 * @brief Client authorized to connect to the server over SSH.
 */
struct nc_auth_client {
    char *username;                             /**< Identifier of the client. */

    enum nc_store_type pubkey_store;            /**< Specifies how/where the client's public keys are stored. */
    union {
        struct nc_public_key *pubkeys;          /**< Locally-defined public keys (sized-array, see libyang docs). */
        char *ts_ref;                           /**< Name of the referenced truststore public key bag. */
    };

    char *password;                             /**< Password for password authentication. */
    time_t password_last_modified;              /**< Time of the last password modification. */

    enum nc_kbdint_auth_method kbdint_method;   /**< Keyboard-interactive authentication method,
                                                  *  may be extended by e.g. a union for each method when needed. */
    int none_enabled;                           /**< Whether "none" authentication is enabled for this user. */
};

/**
 * @brief Server's SSH host key.
 */
struct nc_hostkey {
    char *name;                         /**<  Identifier of the hostkey. */

    enum nc_store_type store;           /**< Specifies how/where the key is stored. */
    union {
        struct nc_asymmetric_key key;   /**< Locally-defined key. */
        char *ks_ref;                   /**< Name of the referenced keystore key. */
    };
};

/**
 * @brief Server options for configuring the SSH transport protocol.
 */
struct nc_server_ssh_opts {
    struct nc_hostkey *hostkeys;            /**< Server's hostkeys (sized-array, see libyang docs). */

    struct nc_auth_client *auth_clients;    /**< Server's authorized clients (sized-array, see libyang docs). */

    char *referenced_endpt_name;            /**< Reference to another endpoint (used for client authentication). */

    char *hostkey_algs;                     /**< Used hostkey algorithms (comma-separated list). */
    char *encryption_algs;                  /**< Used encryption algorithms (comma-separated list). */
    char *kex_algs;                         /**< Used key exchange algorithms (comma-separated list). */
    char *mac_algs;                         /**< Used MAC algorithms (comma-separated list). */

    char *banner;                           /**< SSH banner message, sent before authentication. */

    uint16_t auth_timeout;                  /**< Authentication timeout. */
};

/**
 * @brief Storing downloaded data via CURL.
 */
struct nc_curl_data {
    unsigned char *data;    /**< Downloaded data */
    size_t size;            /**< Size of downloaded data */
};

/**
 * @brief Cert-to-name entries.
 */
struct nc_ctn {
    uint32_t id;                        /**< ID of the entry, the lower the higher priority */
    char *fingerprint;                  /**< Fingerprint of the entry */
    NC_TLS_CTN_MAPTYPE map_type;        /**< Specifies how to get the username from the certificate */
    char *name;                         /**< Username for this entry */
    struct nc_ctn *next;                /**< Linked-list reference to the next entry */
};

/**
 * @brief Enumeration of supported TLS versions.
 */
enum nc_tls_version {
    NC_TLS_VERSION_NONE = 0,    /**< Unknown TLS version */
    NC_TLS_VERSION_1_2,         /**< TLS version 1.2 */
    NC_TLS_VERSION_1_3          /**< TLS version 1.3 */
};

/**
 * @brief Server's TLS client authentication configuration.
 */
struct nc_server_tls_client_auth {
    enum nc_store_type ca_certs_store;      /**< Specifies how/where the CA certificates are stored. */
    union {
        struct nc_certificate *ca_certs;    /**< Locally-defined CA certificates (sized-array, see libyang docs). */
        char *ca_cert_bag_ts_ref;           /**< Name of the referenced truststore certificate bag. */
    };

    enum nc_store_type ee_certs_store;      /**< Specifies how/where the end-entity certificates are stored. */
    union {
        struct nc_certificate *ee_certs;    /**< Locally-defined end-entity certificates (sized-array, see libyang docs). */
        char *ee_cert_bag_ts_ref;           /**< Name of the referenced truststore certificate bag. */
    };
};

/**
 * @brief Server options for configuring the TLS transport protocol.
 */
struct nc_server_tls_opts {
    enum nc_store_type cert_store;          /**< Specifies how/where the server's key and certificate are stored. */
    union {
        struct {
            struct nc_asymmetric_key key;   /**< Locally-defined key. */
            struct nc_certificate cert;     /**< Locally-defined certificate. */
        } local;                            /**< Local definition of the server's key and certificate. */
        struct {
            char *asym_key_ref;             /**< Name of the referenced keystore asymmetric key. */
            char *cert_ref;                 /**< Name of the referenced keystore certificate. */
        } keystore;                         /**< Keystore reference of the server's key and certificate. */
    };

    struct nc_server_tls_client_auth client_auth;   /**< Client authentication configuration. */

    struct nc_ctn *ctn;                 /**< Cert-to-name entries linked list */

    char *referenced_endpt_name;        /**< Reference to another endpoint (used for client authentication). */

    enum nc_tls_version min_version;    /**< Minimum supported TLS version. */
    enum nc_tls_version max_version;    /**< Maximum supported TLS version. */

    char *cipher_suites;                /**< Allowed cipher suites (colon-separated list). */
};

#endif /* NC_ENABLED_SSH_TLS */

/**
 * @brief TCP keepalives configuration.
 */
struct nc_keepalives {
    int enabled;                /**< Indicates that keepalives are enabled. */
    uint16_t idle_time;         /**< Idle timeout. */
    uint16_t max_probes;        /**< Maximum number of probes. */
    uint16_t probe_interval;    /**< Probe interval. */
};

/**
 * @brief Enumeration of UNIX socket path types.
 */
enum nc_unix_socket_path_type {
    NC_UNIX_SOCKET_PATH_UNKNOWN = 0,    /**< Unknown or uninitialized path type. */
    NC_UNIX_SOCKET_PATH_HIDDEN,         /**< Socket path is hidden from YANG; configured via API. */
    NC_UNIX_SOCKET_PATH_FILE            /**< Socket path is explicitly configured in YANG. */
};

/**
 * @brief Server options for configuring the UNIX transport protocol.
 */
struct nc_server_unix_opts {
    enum nc_unix_socket_path_type path_type;    /**< Method used to determine the socket path. */

    mode_t mode;                                /**< Socket file permissions (defaults to rw-rw----). */
    uid_t uid;                                  /**< Owner of the socket file. */
    gid_t gid;                                  /**< Group owner of the socket file. */

    struct nc_server_unix_user_mapping {
        char *system_user;              /**< System username for authentication. */
        char **allowed_users;           /**< Permitted NETCONF usernames (sized-array, see libyang docs). */
    } *user_mappings;                   /**< Username mappings for the UNIX socket connection (sized-array, see libyang docs). */
};

/**
 * @brief Stores information about a server endpoint binding.
 */
struct nc_bind {
    char *address;  /**< Either IPv4/IPv6 address or path to UNIX socket. */
    uint16_t port;  /**< Either port number or 0 for UNIX socket. */
    int sock;       /**< Socket file descriptor, -1 if not created yet. */
};

struct nc_client_unix_opts {
    char *username;
};

#ifdef NC_ENABLED_SSH_TLS

/* number of all supported authentication methods */
#define NC_SSH_AUTH_COUNT 3

struct nc_client_ssh_opts {
    char *knownhosts_path;  /**< path to known_hosts file */
    NC_SSH_KNOWNHOSTS_MODE knownhosts_mode; /**< implies whether to check known_hosts or not */

    /* SSH authentication method preferences */
    struct {
        NC_SSH_AUTH_TYPE type;
        int16_t value;
    } auth_pref[NC_SSH_AUTH_COUNT];

    /* SSH key pairs */
    struct {
        char *pubkey_path;
        char *privkey_path;
        int8_t privkey_crypt;
    } *keys;
    uint16_t key_count;

    /* SSH authentication callbacks */
    char *(*auth_password)(const char *, const char *, void *);
    char *(*auth_interactive)(const char *, const char *, const char *, int, void *);
    char *(*auth_privkey_passphrase)(const char *, void *);

    /* private data for the callbacks */
    void *auth_password_priv;
    void *auth_interactive_priv;
    void *auth_privkey_passphrase_priv;

    char *username;
};

struct nc_client_tls_opts {
    char *cert_path;
    char *key_path;

    char *ca_file;
    char *ca_dir;
};

#endif /* NC_ENABLED_SSH_TLS */

/**
 * @brief Stores data for the client monitoring thread.
 */
struct nc_client_monitoring_thread_arg {
    struct nc_session **sessions;   /**< Array of monitored sessions. */
    uint16_t session_count;         /**< Number of monitored sessions. */

    struct pollfd *pfds;            /**< Array of poll file descriptors corresponding to the monitored sessions. */
    nfds_t pfd_count;               /**< Number of poll file descriptors. */

    pthread_t tid;                  /**< Thread ID of the monitoring thread. */
    int thread_running;             /**< Flag representing the runningness of the monitoring thread. */
    pthread_mutex_t lock;           /**< Monitoring thread data lock. */

    nc_client_monitoring_clb clb;   /**< Callback called when a monitored session is terminated by the server. */
    void *clb_data;                 /**< Data passed to the callback. */
    void (*clb_free_data)(void *);  /**< Callback to free the user data. */
};

/* ACCESS unlocked */
struct nc_client_opts {
    char *schema_searchpath;
    int auto_context_fill_disabled;
    ly_module_imp_clb schema_clb;
    void *schema_clb_data;
    struct nc_keepalives ka;

    char **capabilities;            /**< Array of custom client capabilities. */
    uint32_t capabilities_count;    /**< Count of capabilities. */

    struct nc_bind *ch_binds;

    struct {
        NC_TRANSPORT_IMPL ti;
        char *hostname;
    } *ch_binds_aux;
    uint16_t ch_bind_count;

    struct nc_client_monitoring_thread_arg monitoring_thread_data; /**< Data of the monitoring thread. */
};

/* ACCESS unlocked */
struct nc_client_context {
    unsigned int refcount;
    struct nc_client_opts opts;

    struct nc_client_unix_opts unix_opts;

#ifdef NC_ENABLED_SSH_TLS
    struct nc_client_ssh_opts ssh_opts;
    struct nc_client_ssh_opts ssh_ch_opts;

    struct nc_client_tls_opts tls_opts;
    struct nc_client_tls_opts tls_ch_opts;
#endif /* NC_ENABLED_SSH_TLS */
};

#ifdef NC_ENABLED_SSH_TLS

/**
 * @brief Stores time information used for creating certificate expiration intervals.
 */
struct nc_cert_exp_time {
    int months;
    int weeks;
    int days;
    int hours;
};

/**
 * @brief Stores information about a certificate expiration notification.
 */
struct nc_cert_expiration {
    time_t *starts_of_intervals;    /**< Array of the starting times of the certificate expiration notification intervals. */
    int current_interval;           /**< Index of the current interval. */

    time_t expiration_time;         /**< Time of the certificate expiration. */
    time_t notif_time;              /**< Time of the next notification. */

    char *xpath;                    /**< XPath to the certificate. */
};

/**
 * @brief Certificate expiration notification thread data.
 */
struct nc_cert_exp_notif_thread_arg {
    nc_cert_exp_notif_clb clb;      /**< Callback called when a certificate expiration notification is ready to be sent. */
    void *clb_data;                 /**< Data passed to the callback. */
    void (*clb_free_data)(void *);  /**< Callback to free the user data. */
};

/**
 * @brief Auxiliary structure used for creating the XPaths to the certificates.
 */
struct nc_cert_path_aux {
    const char *ch_client_name;
    const char *endpt_name;
    const char *ca_cert_name;
    const char *ee_cert_name;
    const char *ks_askey_name;
    const char *ks_cert_name;
    const char *ts_cbag_name;
    const char *ts_cert_name;
};

/**
 * @brief Update the values of the nc_cert_path_aux members.
 */
#define NC_CERT_EXP_UPDATE_CERT_PATH(cp, ch_client, endpt, ca_cert, \
                ee_cert, ks_askey, ks_cert, ts_cbag, ts_cert) \
    (cp)->ch_client_name = (ch_client); \
    (cp)->endpt_name = (endpt); \
    (cp)->ca_cert_name = (ca_cert); \
    (cp)->ee_cert_name = (ee_cert); \
    (cp)->ks_askey_name = (ks_askey); \
    (cp)->ks_cert_name = (ks_cert); \
    (cp)->ts_cbag_name = (ts_cbag); \
    (cp)->ts_cert_name = (ts_cert)

#endif /* NC_ENABLED_SSH_TLS */

/**
 * @brief Call Home client thread data.
 */
struct nc_server_ch_thread_arg {
    char *client_name;

    const struct ly_ctx *(*acquire_ctx_cb)(void *cb_data);  /**< acquiring libyang context cb */
    void (*release_ctx_cb)(void *cb_data);                  /**< releasing libyang context cb */
    void *ctx_cb_data;                                      /**< acq/rel cb data */
    int (*new_session_cb)(const char *client_name, struct nc_session *new_session, void *user_data);    /**< creating new session cb */
    void *new_session_cb_data;                              /**< new session cb data */
    void (*new_session_fail_cb)(const char *client_name, const char *endpt_name, uint8_t max_attempts,
            uint8_t cur_attempt, void *user_data);          /**< failed to create a new session cb */
    void *new_session_fail_cb_data;                         /**< new session fail cb data */

    pthread_t tid;              /**< Thread ID of the Call Home client thread. */
    ATOMIC_T thread_running;    /**< Non-zero while the Call Home thread is running. Atomic for lock-free checks,
                                     but also used with the condition variable under cond_lock for signalling. */
    pthread_mutex_t cond_lock;  /**< Condition's lock used for signalling the thread to terminate */
    pthread_cond_t cond;        /**< Condition used for signalling the thread to terminate */
};

struct nc_server_config {
    uint16_t idle_timeout;  /**< Idle timeout of the server sessions. */

    char **ignored_modules; /**< Names of YANG modules that are not reported in the server <hello> message (sized-array, see libyang docs). */

    struct nc_endpt {
        char *name;                 /**< Identifier of the endpoint. */

        struct nc_bind *binds;      /**< Listening binds of the endpoint (sized-array, see libyang docs). */

        struct nc_keepalives ka;    /**< TCP keepalives configuration. */

        NC_TRANSPORT_IMPL ti;       /**< Transport implementation of the endpoint. */
        union {
#ifdef NC_ENABLED_SSH_TLS
            struct nc_server_ssh_opts *ssh;     /**< SSH transport options. */
            struct nc_server_tls_opts *tls;     /**< TLS transport options. */
#endif /* NC_ENABLED_SSH_TLS */
            struct nc_server_unix_opts *unix;   /**< UNIX socket transport options. */
        } opts;
    } *endpts;      /**< Listening endpoints (sized-array, see libyang docs). */

    struct nc_ch_client {
        char *name;                         /**< Identifier of the Call Home client. */

        struct nc_ch_endpt {
            char *name;                     /**< Identifier of the Call Home endpoint. */

            char *src_addr;                 /**< IP address to bind to when connecting to a Call Home client. */
            uint16_t src_port;              /**< Port to bind to when connecting to a Call Home client. */
            char *dst_addr;                 /**< IP address of the Call Home client. */
            uint16_t dst_port;              /**< Port of the Call Home client. */

            int sock_pending;               /**< Socket file descriptor of the pending connection to the Call Home client. */
            struct nc_keepalives ka;        /**< Keepalives configuration data for the Call Home endpoint. */

            NC_TRANSPORT_IMPL ti;           /**< Transport implementation of the Call Home endpoint. */
            union {
#ifdef NC_ENABLED_SSH_TLS
                struct nc_server_ssh_opts *ssh;     /**< SSH transport options for the Call Home endpoint. */
                struct nc_server_tls_opts *tls;     /**< TLS transport options for the Call Home endpoint. */
#endif /* NC_ENABLED_SSH_TLS */
            } opts;
        } *ch_endpts;                       /**< Call Home endpoints (sized-array, see libyang docs). */

        NC_CH_CONN_TYPE conn_type;          /**< Type of the Call Home connection. */
        struct {
            uint16_t period;                /**< Period of a periodic Call Home connection in seconds. */
            time_t anchor_time;             /**< Anchor time of a periodic Call Home connection. */
            uint16_t idle_timeout;          /**< Idle timeout of a periodic Call Home connection in seconds. */
        };

        NC_CH_START_WITH start_with;        /**< How to select the Call Home endpoint to connect to. */
        uint8_t max_attempts;               /**< Maximum number of attempts to connect to the given Call Home endpoint. */
        uint16_t max_wait;                  /**< Maximum time to wait for a Call Home connection in seconds. */

        struct nc_server_ch_thread_arg *thread; /**< Call Home client thread data, if dispatched. */
    } *ch_clients;                          /**< Call Home clients (sized-array, see libyang docs). */

#ifdef NC_ENABLED_SSH_TLS
    struct nc_keystore keystore;        /**< Asymmetric and symmetric key storage used for server identity. */
    struct nc_truststore truststore;    /**< Public keys and certificates storage used for client authentication. */

    /**
     * @brief Time intervals for certificate expiration notifications.
     *
     * See libnetconf2-netconf-server YANG module for more details.
     */
    struct nc_cert_exp_time_interval {
        struct nc_cert_exp_time anchor; /**< Lower bound of the given interval. */
        struct nc_cert_exp_time period; /**< Period of the given interval. */
    } *cert_exp_notif_intervals;        /**< Certificate expiration notification intervals (sized-array, see libyang docs). */
#endif /* NC_ENABLED_SSH_TLS */
};

struct nc_server_opts {
    /* ACCESS locked - hello lock - separate lock to not always hold config_lock */
    pthread_rwlock_t hello_lock;    /**< Needs to be held while the server <hello> message is being generated. */

    NC_WD_MODE wd_basic_mode;       /**< With-defaults basic mode of the server. */
    int wd_also_supported;          /**< Bitmap of with-defaults modes that are also supported by the server. */
    char **capabilities;            /**< Array of server's capabilities. */
    uint32_t capabilities_count;    /**< Number of server's capabilities. */

    char *(*content_id_clb)(void *user_data);   /**< Callback for generating content_id for ietf-yang-library data. */
    void *content_id_data;                      /**< Data passed to the content_id_clb callback. */
    void (*content_id_data_free)(void *data);   /**< Callback to free the content_id_data. */

    /* ACCESS locked - options modified by YANG data/API - WRITE lock
     *               - options read when accepting sessions - READ lock */
    pthread_rwlock_t config_lock;       /**< Lock for the server configuration. */
    struct nc_server_config config;     /**< YANG Server configuration. */

#ifdef NC_ENABLED_SSH_TLS
    char *authkey_path_fmt;             /**< Path to users' public keys that may contain tokens with special meaning. */
    char *pam_config_name;              /**< PAM configuration file name. */
    char *ssh_protocol_string;          /**< SSH protocol identification string. */
    int (*interactive_auth_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data);
    void *interactive_auth_data;
    void (*interactive_auth_data_free)(void *data);

    int (*user_verify_clb)(const struct nc_session *session);

    /**
     * @brief Data for automatically dispatching Call Home clients.
     */
    struct {
        nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb;     /**< Acquiring libyang context callback. */
        nc_server_ch_session_release_ctx_cb release_ctx_cb;     /**< Releasing libyang context callback. */
        void *ctx_cb_data;                                      /**< Data passed to the callbacks above. */
        nc_server_ch_new_session_cb new_session_cb;             /**< New session callback. */
        void *new_session_cb_data;                              /**< Data passed to the new_session_cb callback. */
        nc_server_ch_new_session_fail_cb new_session_fail_cb;   /**< New session fail callback, */
        void *new_session_fail_cb_data;                         /**< Data passed to the new_session_fail_cb callback. */
    } ch_dispatch_data;

    struct {
        pthread_t tid;              /**< Thread ID of the certificate expiration notification thread. */
        int thread_running;         /**< Flag representing the runningness of the cert exp notification thread. */
        pthread_mutex_t lock;       /**< Certificate expiration notification thread's data and cond lock. */
        pthread_cond_t cond;        /**< Condition for the certificate expiration notification thread. */
    } cert_exp_notif;
#endif /* NC_ENABLED_SSH_TLS */

    /**
     * @brief Entry for a UNIX socket path configured via API (Hidden).
     *
     * These paths are not part of the YANG configuration and are managed manually by the application.
     */
    struct nc_server_unix_path_entry {
        char *endpt_name;       /**< Name of the endpoint using this path. */
        char *path;             /**< The actual filesystem path for the socket. */
    } *unix_paths;  /**< Array of UNIX socket paths set via API (sized-array, see libyang docs). */
    char *unix_socket_dir;    /**< Base directory for UNIX sockets created by the server. */

    /* ACCESS unlocked */
    ATOMIC_T new_session_id;

#ifdef NC_ENABLED_SSH_TLS
    /* ACCESS unlocked - set from env */
    FILE *tls_keylog_file;                  /**< File to log TLS secrets to. */
#endif

    /* ACCESS locked - separate lock to allow concurrent reads of the config while an update is being applied */
    pthread_mutex_t config_update_lock; /**< Lock for synchronizing configuration updates, used to wait for pending
                                             configuration update to complete before accepting a new one. */
};

/**
 * @brief Type of the session
 */
typedef enum {
    NC_CLIENT,        /**< client side */
    NC_SERVER         /**< server side */
} NC_SIDE;

/**
 * @brief Container to serialize RPC messages
 */
struct nc_msg_cont {
    struct ly_in *msg;
    NC_MSG_TYPE type;         /**< can be either NC_MSG_REPLY or NC_MSG_NOTIF */
    struct nc_msg_cont *next;
};

/**
 * @brief NETCONF session structure
 */
struct nc_session {
    NC_STATUS status;            /**< status of the session */
    NC_SESSION_TERM_REASON term_reason; /**< reason of termination, if status is NC_STATUS_INVALID */
    uint32_t killed_by;          /**< session responsible for termination, if term_reason is NC_SESSION_TERM_KILLED */
    NC_SIDE side;                /**< side of the session: client or server */

    /* NETCONF data */
    uint32_t id;                 /**< NETCONF session ID (session-id-type) */
    NC_PROT_VERSION version;     /**< NETCONF protocol version */
    char **cpblts;               /**< NETCONF capabilities received from the other end */

    /* Transport implementation */
    NC_TRANSPORT_IMPL ti_type;   /**< transport implementation type to select items from ti union */
    pthread_mutex_t *io_lock;    /**< input/output lock, note that in case of libssh TI, it will be shared with
                                      other NETCONF sessions on the same SSH session (but different SSH channel) */

    union {
        struct {
            int in;              /**< input file descriptor */
            int out;             /**< output file descriptor */
        } fd;                    /**< NC_TI_FD transport implementation structure */
        struct {
            int sock;            /**< socket file descriptor */
        } unixsock;              /**< NC_TI_UNIX transport implementation structure */
#ifdef NC_ENABLED_SSH_TLS
        struct {
            ssh_channel channel;
            ssh_session session;
            struct nc_session *next; /**< pointer to the next NETCONF session on the same
                                          SSH session, but different SSH channel. If no such session exists, it is NULL.
                                          otherwise there is a ring list of the NETCONF sessions */
        } libssh;

        struct {
            void *session;
            void *config;
            struct nc_tls_ctx ctx;
        } tls;
#endif /* NC_ENABLED_SSH_TLS */
    } ti;                          /**< transport implementation data */
    char *username;
    char *host;
    uint16_t port;
    char *path;                    /**< socket path in case of unix socket */

    /* other */
    struct ly_ctx *ctx;            /**< libyang context of the session */
    void *data;                    /**< arbitrary user data */
    uint8_t flags;                 /**< various flags of the session */

/* shared flags */
#define NC_SESSION_SHAREDCTX 0x01   /**< context is shared */
#define NC_SESSION_CALLHOME 0x02    /**< session is Call Home and ch_lock is initialized */
#define NC_SESSION_CH_THREAD 0x04   /**< protected by ch_lock */

/* client flags */
#define NC_SESSION_CLIENT_NOT_STRICT 0x08   /**< some server modules failed to load so the data from
                                                 them will be ignored - not use strict flag for parsing */
#define NC_SESSION_CLIENT_MONITORED 0x40    /**< session is being monitored by the client monitoring thread */

/* server flags */
#ifdef NC_ENABLED_SSH_TLS
#define NC_SESSION_SSH_AUTHENTICATED 0x10   /**< SSH session authenticated */
#define NC_SESSION_SSH_SUBSYS_NETCONF 0x20  /**< netconf subsystem requested */
#endif

    union {
        struct {
            /* client side only data */
            uint64_t msgid;
            pthread_mutex_t msgs_lock;     /**< lock for the msgs buffer */
            struct nc_msg_cont *msgs;      /**< queue for messages received of different type than expected */
            ATOMIC_T ntf_thread_count;     /**< number of running notification threads */
            ATOMIC_T ntf_thread_running;   /**< flag whether there are notification threads for this session running or not */
        } client;
        struct {
            /* server side only data */
            struct timespec session_start;  /**< real time the session was created */
            time_t last_rpc;                /**< monotonic time (seconds) the last RPC was received on this session */

            pthread_mutex_t ntf_status_lock;    /**< lock for ntf_status */
            uint32_t ntf_status;                /**< flag (count) whether the session is subscribed to notifications */

            pthread_mutex_t rpc_lock;    /**< lock indicating RPC processing, this lock is always locked before io_lock!! */
            pthread_cond_t rpc_cond;     /**< RPC condition (tied with rpc_lock and rpc_inuse) */
            int rpc_inuse;               /**< variable indicating whether there is RPC being processed or not (tied with
                                              rpc_cond and rpc_lock) */

            pthread_mutex_t ch_lock;       /**< Call Home thread lock */
            pthread_cond_t ch_cond;        /**< Call Home thread condition */

#ifdef NC_ENABLED_SSH_TLS
            uint16_t ssh_auth_attempts;    /**< number of failed SSH authentication attempts */
            void *client_cert;                /**< TLS client certificate if used for authentication */
#endif /* NC_ENABLED_SSH_TLS */
        } server;
    } opts;
};

enum nc_ps_session_state {
    NC_PS_STATE_NONE = 0,      /**< session is not being worked with */
    NC_PS_STATE_BUSY,          /**< session is being polled or communicated on (and locked) */
    NC_PS_STATE_INVALID        /**< session is invalid and was already returned by another poll */
};

struct nc_ps_session {
    struct nc_session *session;
    enum nc_ps_session_state state;
};

/* ACCESS locked */
struct nc_pollsession {
    struct nc_ps_session **sessions;
    uint16_t session_count;
    uint16_t last_event_session;

    pthread_cond_t cond;
    pthread_mutex_t lock;
    uint8_t queue[NC_PS_QUEUE_SIZE]; /**< round buffer, queue is empty when queue_len == 0 */
    uint8_t queue_begin;             /**< queue starts on queue[queue_begin] */
    uint8_t queue_len;               /**< queue ends on queue[(queue_begin + queue_len - 1) % NC_PS_QUEUE_SIZE] */
};

struct nc_ntf_thread_arg {
    struct nc_session *session;
    nc_notif_dispatch_clb notif_clb;
    void *user_data;

    void (*free_data)(void *);
};

#ifdef NC_ENABLED_SSH_TLS

/**
 * @brief PAM callback arguments.
 */
struct nc_pam_thread_arg {
    ssh_message msg;                /**< libssh message */
    struct nc_session *session;     /**< NETCONF session */
};

/**
 * @brief Converts private key format to a string.
 * This string is the same, as in a PKCS#1 Private key format, meaning
 * ---- BEGIN (string) PRIVATE KEY ----. The string can be empty for some types.
 *
 * @param[in] format Private key format.
 * @return String representing the private key or NULL.
 */
const char *nc_privkey_format_to_str(enum nc_privkey_format format);

/**
 * @brief Checks if the given base64 belongs to a public key in the SubjectPublicKeyInfo format.
 *
 * @param[in] b64 Base64 encoded data.
 *
 * @return -1 on error, 0 if it is not SPKI public key, 1 if it is a public key in the SPKI format.
 */
int nc_is_pk_subject_public_key_info(const char *b64);

/**
 * @brief Import a Base64 DER encoded certificate data.
 *
 * @param[in] data Base64 DER encoded certificate data.
 * @return Imported certificate on success, NULL on error.
 */
void * nc_base64der_to_cert(const char *data);

#endif /* NC_ENABLED_SSH_TLS */

/**
 * @brief Start monitoring a session.
 *
 * @param[in] session Session to start monitoring.
 *
 * @return 0 on success, 1 on error.
 */
int nc_client_monitoring_session_start(struct nc_session *session);

/**
 * @brief Stop monitoring a session.
 *
 * @param[in] session Session to stop monitoring.
 * @param[in] lock Whether to lock the thread data while removing the session from the list.
 */
void nc_client_monitoring_session_stop(struct nc_session *session, int lock);

/**
 * @brief Stop all notification threads for a client session.
 *
 * The function asks threads to stop and waits for their termination for at
 * most ::NC_SESSION_FREE_LOCK_TIMEOUT milliseconds.
 *
 * @param[in] session Session to stop notification threads for.
 */
void nc_client_notification_threads_stop(struct nc_session *session);

/**
 * @brief Free messages stored in the client session's message queue.
 *
 * @param[in] session Session to free messages for.
 */
void nc_client_msgs_free(struct nc_session *session);

/**
 * @brief Get current client context.
 *
 * @return Client context.
 */
struct nc_client_context *nc_client_context_location(void);

void *nc_realloc(void *ptr, size_t size);

/**
 * @brief Get the UNIX socket path for the given endpoint.
 *
 * @param[in] endpt Endpoint to get the socket path for.
 * @return Socket path, NULL on error.
 */
char *nc_server_unix_get_socket_path(const struct nc_endpt *endpt);

/**
 * @brief Bind and listen on a socket for the given endpoint and its bind.
 *
 * @param[in] endpt Endpoint the bind belongs to.
 * @param[in] bind Bind to bind and listen for.
 * @return 0 on success, 1 on error.
 */
int nc_server_bind_and_listen(struct nc_endpt *endpt, struct nc_bind *bind);

/**
 * @brief Free server configuration data (only YANG config data).
 *
 * @param[in] config Server configuration to free.
 */
void nc_server_config_free(struct nc_server_config *config);

/**
 * @brief Get passwd entry for UID or a user.
 *
 * @param[in] uid UID to use, if @p username is NULL.
 * @param[in] username User name to use, if not NULL.
 * @param[in] pwd_buf passwd buffer.
 * @param[in,out] buf String buffer.
 * @param[in,out] buf_size Size of @p buf.
 * @return Found passwd entry, NULL on error.
 */
struct passwd *nc_getpw(uid_t uid, const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size);

/**
 * @brief Get group entry for GID or a group name.
 *
 * @param[in] gid GID to use, if @p grpname is NULL.
 * @param[in] grpname Group name to use, if not NULL.
 * @param[in] grp_buf group buffer.
 * @param[in,out] buf String buffer.
 * @param[in,out] buf_size Size of @p buf.
 * @return Found group entry, NULL on error.
 */
struct group *nc_getgr(gid_t gid, const char *grpname, struct group *grp_buf, char **buf, size_t *buf_size);

/**
 * @brief Get current clock (uses COMPAT_CLOCK_ID) time with an offset.
 *
 * @param[out] ts Current time offset by @p add_ms.
 * @param[in] add_ms Number of milliseconds to add.
 */
void nc_timeouttime_get(struct timespec *ts, uint32_t add_ms);

/**
 * @brief Get the time difference.
 *
 * @param[in] ts1 First timespec structure to compare.
 * @param[in] ts2 Second timespec structure to compare.
 * @return Time difference in milliseconds, positive if @p ts1 > @p ts2, negative if @p ts1 < @p ts2.
 */
int32_t nc_time_diff(const struct timespec *ts1, const struct timespec *ts2);

/**
 * @brief Get time difference based on the current time (uses COMPAT_CLOCK_ID).
 *
 * @param[in] ts Timespec structure holding real time from which the current time is going to be subtracted.
 * @return Time difference in milliseconds.
 */
int32_t nc_timeouttime_cur_diff(const struct timespec *ts);

/**
 * @brief Get current CLOCK_REALTIME time.
 *
 * @param[out] ts Current real time.
 */
void nc_realtime_get(struct timespec *ts);

/**
 * @brief Perform poll(2) with signal support and timeout adjustment.
 *
 * @param[in] pfd Poll structure to use.
 * @param[in] pfd_count Count of FD in @p pfd.
 * @param[in] timeout_ms Timeout to use.
 * @return poll(2) return.
 */
int nc_poll(struct pollfd *pfd, uint16_t pfd_count, int timeout);

/**
 * @brief Enables/disables TCP keepalives.
 *
 * @param[in] sock Socket to set this option for.
 * @param[in] ka Keepalives to set.
 * @return 0 on success, -1 on fail.
 */
int nc_sock_configure_ka(int sock, const struct nc_keepalives *ka);

struct nc_session *nc_new_session(NC_SIDE side, int shared_ti);

int nc_session_rpc_lock(struct nc_session *session, int timeout, const char *func);

int nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func);

/**
 * @brief Lock a pthread_rwlock with timeout support.
 *
 * @param[in] rwlock Lock to be acquired.
 * @param[in] mode Lock mode (NC_RWLOCK_READ for read lock, NC_RWLOCK_WRITE for write lock).
 * @param[in] timeout Timeout in milliseconds:
 *                    - Positive value: Wait up to this many milliseconds for the lock.
 *                    - 0: Try to acquire the lock without waiting (trylock).
 *                    - Negative value: Wait indefinitely for the lock.
 * @param[in] func_name Caller function name for logging purposes.
 * @return 1 on success (lock acquired);
 * @return 0 on timeout;
 * @return -1 on error.
 */
int nc_rwlock_lock(pthread_rwlock_t *rwlock, enum nc_rwlock_mode mode, int timeout, const char *func_name);

/**
 * @brief Unlock a previously locked pthread_rwlock.
 *
 * This function unlocks a previously locked pthread_rwlock regardless of whether
 * it was locked in read or write mode.
 *
 * @param[in] rwlock Lock to be unlocked.
 * @param[in] func_name Caller function name for logging purposes.
 */
void nc_rwlock_unlock(pthread_rwlock_t *rwlock, const char *func_name);

/**
 * @brief Lock a pthread_mutex with timeout support.
 *
 * @param[in] mutex Mutex to be acquired.
 * @param[in] timeout Timeout in milliseconds:
 *                    - Positive value: Wait up to this many milliseconds for the lock.
 *                    - 0: Try to acquire the lock without waiting (trylock).
 *                    - Negative value: Wait indefinitely for the lock.
 * @param[in] func_name Caller function name for logging purposes.
 * @return 1 on success (lock acquired);
 * @return 0 on timeout;
 * @return -1 on error.
 */
int nc_mutex_lock(pthread_mutex_t *mutex, int timeout, const char *func_name);

/**
 * @brief Unlock a previously locked pthread_mutex.
 *
 * @param[in] mutex Mutex to be unlocked.
 * @param[in] func_name Caller function name for logging purposes.
 */
void nc_mutex_unlock(pthread_mutex_t *mutex, const char *func_name);

int nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func);

int nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func);

int nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);

/**
 * @brief Fill libyang context in @p session. Context models are based on the stored session
 *        capabilities. If the server does not support \<get-schema\>, the models are searched
 *        for in the directory set using nc_client_schema_searchpath().
 *
 * @param[in] session Session to create the context for.
 * @return 0 on success, 1 on some missing schemas, -1 on error.
 */
int nc_ctx_check_and_fill(struct nc_session *session);

/**
 * @brief Perform NETCONF handshake on @p session.
 *
 * @param[in] session NETCONF session to use.
 * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message parsing fail
 * (server-side only), NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other error.
 */
NC_MSG_TYPE nc_handshake_io(struct nc_session *session);

/**
 * @brief Perform NETCONF handshake on a server-side Call Home session.
 *
 * @param[in] session NETCONF session to use.
 * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message parsing fail,
 * NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other error.
 */
NC_MSG_TYPE nc_ch_handshake_io(struct nc_session *session);

/**
 * @brief Bind a socket to an address and a port.
 *
 * @param[in] sock Socket to bind.
 * @param[in] address Address to bind to.
 * @param[in] port Port to bind to.
 * @param[in] is_ipv4 Whether the address is IPv4 or IPv6.
 * @return 0 on success, -1 on error.
 */
int nc_sock_bind_inet(int sock, const char *address, uint16_t port, int is_ipv4);

/**
 * @brief Create a socket connection.
 *
 * @param[in] src_addr Address to connect from.
 * @param[in] src_port Port to connect from.
 * @param[in] dst_addr Address to connect to.
 * @param[in] dst_port Port to connect to.
 * @param[in] timeout_ms Timeout in ms for blocking the connect + select call (-1 for infinite).
 * @param[in] ka Keepalives parameters.
 * @param[in,out] sock_pending Previous pending socket. If set, equal to -1, and the connection is still in progress
 * after @p timeout, it is set to the pending socket but -1 is returned. If NULL, the socket is closed on timeout.
 * @param[out] ip_host Optional parameter with string IP address of the connected host.
 * @return Connected socket or -1 on error.
 */
int nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms,
        struct nc_keepalives *ka, int *sock_pending, char **ip_host);

/**
 * @brief Accept a new socket connection.
 *
 * @param[in] sock Listening socket.
 * @param[in] timeout Timeout in milliseconds.
 * @param[out] peer_host Host the new connection was initiated from. Can be NULL.
 * @param[out] peer_port Port the new connection is connected on. Can be NULL.
 * @return Connected socket with the new connection, -1 on error.
 */
int nc_sock_accept(int sock, int timeout, char **peer_host, uint16_t *peer_port);

/**
 * @brief Create a listening socket (AF_INET or AF_INET6).
 *
 * @param[in] address IP address to listen on.
 * @param[in] port Port to listen on.
 * @return Listening socket, -1 on error.
 */
int nc_sock_listen_inet(const char *address, uint16_t port);

/**
 * @brief Accept a new connection on any of the given Call Home binds.
 *
 * @param[in] binds Call Home binds to accept on.
 * @param[in] bind_count Number of @p binds.
 * @param[in] timeout Timeout for accepting.
 * @param[out] host Host of the remote peer. Can be NULL.
 * @param[out] port Port of the new connection. Can be NULL.
 * @param[out] bind_idx Index of the bind that was accepted. Can be NULL.
 * @param[out] sock Accepted socket, if any.
 * @return -1 on error, 0 on timeout, 1 if a socket was accepted.
 */
int nc_server_ch_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host,
        uint16_t *port, uint16_t *bind_idx, int *sock);

/**
 * @brief Establish a UNIX transport session.
 *
 * @param[in] session NETCONF session for logging.
 * @param[in] sock Connected socket to use.
 * @param[in] username NETCONF username to use.
 * @param[in] timeout_ms Timeout for writing the username.
 * @return -1 on failure; 0 on timeout; 1 on success.
 */
int nc_connect_unix_session(struct nc_session *session, int sock, const char *username, int timeout_ms);

/**
 * @brief Gets a listening endpoint based on its name.
 *
 * @param[in] name The name of the endpoint.
 * @param[out] endpt Pointer to the endpoint structure.
 * @return 0 on success, 1 on failure.
 */
int nc_server_endpt_get(const char *name, struct nc_endpt **endpt);

/**
 * @brief Add a client Call Home bind, listen on it.
 *
 * @param[in] address Address to bind to.
 * @param[in] port Port to bind to.
 * @param[in] hostname Expected server hostname, may be NULL.
 * @param[in] ti Transport to use.
 * @return 0 on success, -1 on error.
 */
int nc_client_ch_add_bind_listen(const char *address, uint16_t port, const char *hostname, NC_TRANSPORT_IMPL ti);

/**
 * @brief Remove a client Call Home bind, stop listening on it.
 *
 * @param[in] address Address of the bind. NULL matches any address.
 * @param[in] port Port of the bind. 0 matches all ports.
 * @param[in] ti Transport of the bind. 0 matches all transports.
 * @return 0 on success, -1 on no matches found.
 */
int nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti);

/**
 * @brief Connect to a listening NETCONF client using Call Home.
 *
 * @param[in] host Hostname to connect to.
 * @param[in] port Port to connect to.
 * @param[in] ti Transport fo the connection.
 * @param[out] session New Call Home session.
 * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
 *         parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
 */
NC_MSG_TYPE nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);

#ifdef NC_ENABLED_SSH_TLS

/**
 * @brief Stop a dispatched Call Home client thread, if such thread was dispatched for the given client.
 *
 * @warning The caller MUST hold both WRITE config lock and CONFIG APPLY mutex when calling this function.
 *
 * @param[in] ch_client Call Home client to stop the thread for, can be NULL.
 * @return 0 if the thread was successfully stopped, 1 on error.
 */
int nc_session_server_ch_client_dispatch_stop(struct nc_ch_client *ch_client);

/**
 * @brief Dispatch a thread connecting to a listening NETCONF client and creating Call Home sessions.
 *
 * @note The config WRITE lock MUST be held.
 *
 * @param[in] ch_client Call Home client to dispatch the thread for.
 * @param[in] acquire_ctx_cb Callback for acquiring new session context.
 * @param[in] release_ctx_cb Callback for releasing session context.
 * @param[in] ctx_cb_data Arbitrary user data passed to @p acquire_ctx_cb and @p release_ctx_cb.
 * @param[in] new_session_cb Callback called for every established session on the client.
 * @param[in] new_session_cb_data Arbitrary user data passed to @p new_session_cb.
 * @return 0 if the thread was successfully created, -1 on error.
 */
int _nc_connect_ch_client_dispatch(struct nc_ch_client *ch_client, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb,
        nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb,
        void *new_session_cb_data);

/**
 * @brief Accept a server Call Home connection on a socket.
 *
 * @param[in] sock Socket with a new connection.
 * @param[in] host Hostname of the server.
 * @param[in] port Port of the server.
 * @param[in] ctx Context for the session. Can be NULL.
 * @param[in] timeout Transport operations timeout in msec.
 * @return New session, NULL on error.
 */
struct nc_session *nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout);

/**
 * @brief Establish SSH transport on a socket.
 *
 * @param[in] session Session structure of the new connection.
 * @param[in] sock Socket of the new connection, closed if not set to the session.
 * @param[in] timeout Transport operations timeout in msec (not SSH authentication one).
 * @return 1 on success, 0 on timeout, -1 on error.
 */
int nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opts, int sock, int timeout);

/**
 * @brief Process a SSH message.
 *
 * @param[in] session Session structure of the connection.
 * @param[in] opts Endpoint SSH options on which the session was created.
 * @param[in] msg SSH message itself.
 * @param[in] auth_state State of the authentication.
 * @return 0 if the message was handled, 1 if it is left up to libssh.
 */
int nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg, struct nc_auth_state *auth_state);

void nc_client_ssh_destroy_opts(void);
void _nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts);

struct nc_session *nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx,
        int timeout, const char *peername);

/**
 * @brief Establish TLS transport on a socket.
 *
 * @param[in] session Session structure of the new connection.
 * @param[in] sock Socket of the new connection.
 * @param[in] timeout Transport operations timeout in msec.
 * @return 1 on success, 0 on timeout, -1 on error.
 */
int nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opts, int sock, int timeout);

void nc_client_tls_destroy_opts(void);
void _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts);

/**
 * @brief Fetch CRLs from the x509v3 CRLDistributionPoints extension.
 *
 * @param[in] leaf_cert Server/client certificate.
 * @param[in] cert_store CA/EE certificates store.
 * @param[out] crl_store Created CRL store.
 * @return 0 on success, 1 on error.
 */
int nc_session_tls_crl_from_cert_ext_fetch(void *leaf_cert, void *cert_store, void **crl_store);

#endif /* NC_ENABLED_SSH_TLS */

/**
 * @brief Check whether a module is not ignored by the server.
 *
 * @param[in] mod_name Module name to check.
 * @param[in] config_locked Whether the configuration lock is already held or should be acquired in this function.
 * @return Whether the module is ignored.
 */
int nc_server_is_mod_ignored(const char *mod_name, int config_locked);

/**
 * Functions
 * - io.c
 */

/**
 * @brief Read message from the wire.
 *
 * Accepts hello, rpc, rpc-reply and notification. Received string is transformed into
 * libyang XML tree and the message type is detected from the top level element.
 *
 * @param[in] session NETCONF session from which the message is being read.
 * @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
 *            zero value causes to return immediately.
 * @param[out] msg Input handled with the NETCONF message (application layer data).
 * @return 1 on success.
 * @return 0 on timeout.
 * @return -1 on error.
 * @return -2 on malformed message error.
 */
int nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg);

/**
 * @brief Read a message from the wire.
 *
 * @param[in] session NETCONF session from which the message is being read.
 * @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
 * zero value causes to return immediately.
 * @param[in] passing_io_lock True if @p session IO lock is already held.
 * @param[in,out] buf Buffer with the NETCONF message (application layer data).
 * @param[in,out] buf_len Length of @p buf.
 * @return Number of message characters read on success.
 * @return 0 on timeout.
 * @return -1 on error.
 * @return -2 on malformed message error.
 */
int nc_read_msg_io(struct nc_session *session, int io_timeout, int passing_io_lock, char **buf, uint32_t *buf_len);

struct nc_wclb_arg {
    struct nc_session *session;
    char buf[NC_WRITE_CHUNK_SIZE_MAX];
    uint32_t len;
};

/**
 * @brief Write callback buffering the data in a write structure.
 *
 * @param[in] arg Write structure used for buffering.
 * @param[in] buf Buffer to write, NULL causes flush of any buffered data.
 * @param[in] count Count of bytes to write from @p buf.
 * @param[in] xmlcontent Whether the data are actually printed as part of an XML in which case they need to be encoded.
 * @return Number of written bytes.
 * @return -1 on error.
 */
int nc_write_clb(struct nc_wclb_arg *arg, const void *buf, uint32_t count, int xmlcontent);

/**
 * @brief Write message into wire.
 *
 * @param[in] session NETCONF session to which the message will be written.
 * @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
 *            zero value causes to return immediately.
 * @param[in] type The type of the message to write, specified as #NC_MSG_TYPE value. According to the type, the
 * specific additional parameters are required or accepted:
 * - #NC_MSG_RPC
 *   - `struct lyd_node *op;` - operation (content of the \<rpc/\> to be sent. Required parameter.
 *   - `const char *attrs;` - additional attributes to be added into the \<rpc/\> element. Required parameter.
 * - #NC_MSG_REPLY
 *   - `struct lyd_node_opaq *rpc_envp;` - parsed envelopes of the RPC to reply to. Required parameter.
 *   - `struct nc_server_reply *reply;` - RPC reply. Required parameter.
 * - #NC_MSG_NOTIF
 *   - `struct nc_server_notif *notif;` - notification object. Required parameter.
 * - #NC_MSG_HELLO
 *   - `const char **capabs;` - capabilities array ended with NULL. Required parameter.
 *   - `uint32_t *sid;` - session ID to be included in the hello message. Optional parameter.
 * - #NC_MSG_RAW
 *   - `const char *msg;` - string message to print. Required parameter.
 *   - `uint32_t msg_len;` - message length. Required parameter.
 *
 * @return Type of the written message. #NC_MSG_WOULDBLOCK is returned if timeout is positive
 * (or zero) value and IO lock could not be acquired in that time. #NC_MSG_ERROR is
 * returned on error and #NC_MSG_NONE is never returned by this function.
 */
NC_MSG_TYPE nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...);

/**
 * @brief Check whether a session is still connected (on transport layer).
 *
 * @param[in] session Session to check.
 * @return 1 if connected, 0 if not.
 */
int nc_session_is_connected(const struct nc_session *session);

#endif /* NC_SESSION_PRIVATE_H_ */
