/* 
 * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#ifndef __PJ_SSL_SOCK_H__
#define __PJ_SSL_SOCK_H__

/**
 * @file ssl_sock.h
 * @brief Secure socket
 */

#include <pj/ioqueue.h>
#include <pj/sock.h>
#include <pj/sock_qos.h>


PJ_BEGIN_DECL

/**
 * @defgroup PJ_SSL_SOCK Secure socket I/O
 * @brief Secure socket provides security on socket operation using standard
 * security protocols such as SSL and TLS.
 * @ingroup PJ_IO
 * @{
 *
 * Secure socket wraps normal socket and applies security features, i.e: 
 * privacy and data integrity, on the socket traffic, using standard security
 * protocols such as SSL and TLS.
 *
 * Secure socket employs active socket operations, which is similar to (and
 * described more detail) in \ref PJ_ACTIVESOCK.
 */


 /**
 * This opaque structure describes the secure socket.
 */
typedef struct pj_ssl_sock_t pj_ssl_sock_t;


/**
 * Opaque declaration of endpoint certificate or credentials. This may contains
 * certificate, private key, and trusted Certificate Authorities list.
 */
typedef struct pj_ssl_cert_t pj_ssl_cert_t;


/**
 * Bitwise flag for SSL certificate verification.
 */
typedef enum pj_ssl_cert_verify_flag_t
{
    /**
     * No error in verification.
     */
    PJ_SSL_CERT_ESUCCESS                                = 0,

    /**
     * The issuer certificate cannot be found.
     */
    PJ_SSL_CERT_EISSUER_NOT_FOUND                       = (1 << 0),

    /**
     * The certificate is untrusted.
     */
    PJ_SSL_CERT_EUNTRUSTED                              = (1 << 1),

    /**
     * The certificate has expired or not yet valid.
     */
    PJ_SSL_CERT_EVALIDITY_PERIOD                        = (1 << 2),

    /**
     * One or more fields of the certificate cannot be decoded due to
     * invalid format.
     */
    PJ_SSL_CERT_EINVALID_FORMAT                         = (1 << 3),

    /**
     * The certificate cannot be used for the specified purpose.
     */
    PJ_SSL_CERT_EINVALID_PURPOSE                        = (1 << 4),

    /**
     * The issuer info in the certificate does not match to the (candidate) 
     * issuer certificate, e.g: issuer name not match to subject name
     * of (candidate) issuer certificate.
     */
    PJ_SSL_CERT_EISSUER_MISMATCH                        = (1 << 5),

    /**
     * The CRL certificate cannot be found or cannot be read properly.
     */
    PJ_SSL_CERT_ECRL_FAILURE                            = (1 << 6),

    /**
     * The certificate has been revoked.
     */
    PJ_SSL_CERT_EREVOKED                                = (1 << 7),

    /**
     * The certificate chain length is too long.
     */
    PJ_SSL_CERT_ECHAIN_TOO_LONG                         = (1 << 8),

    /**
     * The certificate signature is created using a weak hashing algorithm.
     */
    PJ_SSL_CERT_EWEAK_SIGNATURE                         = (1 << 9),

    /**
     * The server identity does not match to any identities specified in 
     * the certificate, e.g: subjectAltName extension, subject common name.
     * This flag will only be set by application as SSL socket does not 
     * perform server identity verification.
     */
    PJ_SSL_CERT_EIDENTITY_NOT_MATCH                     = (1 << 30),

    /**
     * Unknown verification error.
     */
    PJ_SSL_CERT_EUNKNOWN                                = (1 << 31)

} pj_ssl_cert_verify_flag_t;


/**
 * Type of SSL certificate name.
 */
typedef enum pj_ssl_cert_name_type
{
    PJ_SSL_CERT_NAME_UNKNOWN = 0,
    PJ_SSL_CERT_NAME_RFC822,
    PJ_SSL_CERT_NAME_DNS,
    PJ_SSL_CERT_NAME_URI,
    PJ_SSL_CERT_NAME_IP
} pj_ssl_cert_name_type;


/**
 * Field type for looking up SSL certificate in the certificate stores.
 */
typedef enum pj_ssl_cert_lookup_type
{
    /**
     * No certificate to be looked up.
     */
    PJ_SSL_CERT_LOOKUP_NONE,

    /**
     * Lookup by subject, this will lookup any first certificate whose
     * subject containing the specified keyword. Note that subject may not
     * be unique in the store, so the lookup may end up selecting a wrong
     * certificate.
     */
    PJ_SSL_CERT_LOOKUP_SUBJECT,

    /**
     * Lookup by fingerprint/thumbprint (SHA1 hash), this will lookup
     * any first certificate whose fingerprint matching the specified
     * keyword. The keyword is an array of hash octets.
     */
    PJ_SSL_CERT_LOOKUP_FINGERPRINT,

    /**
     * Lookup by friendly name, this will lookup any first certificate
     * whose friendly name containing the specified keyword. Note that
     * friendly name may not be unique in the store, so the lookup may end up
     * selecting a wrong certificate.
     */
    PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME

} pj_ssl_cert_lookup_type;


/**
 * Describe structure of certificate lookup criteria.
 */
typedef struct pj_ssl_cert_lookup_criteria
{
    /**
     * Certificate field type to look.
     */
    pj_ssl_cert_lookup_type type;

    /*
     * Keyword to match on the field specified in \a type.
     */
    pj_str_t                keyword;

} pj_ssl_cert_lookup_criteria;


/**
 * The SSL certificate buffer.
 */
typedef pj_str_t pj_ssl_cert_buffer;


/**
 * Data type of the direct SSL certificate.
 */
typedef enum pj_ssl_cert_direct_type
{
    /**
     * No data.
     */
    PJ_SSL_CERT_DIRECT_NONE = 0,

    /**
     * Data is OpenSSL EVP_PKEY.
     */
    PJ_SSL_CERT_DIRECT_OPENSSL_EVP_PKEY = 1,

    /**
     * Data is OpenSSL X509 certificate.
     */
    PJ_SSL_CERT_DIRECT_OPENSSL_X509_CERT = 2,

} pj_ssl_cert_direct_type;


/**
 * The direct SSL certificate. Instead of loading certificate from file,
 * buffer, or OS certificate store, application can provide direct access
 * to the backend specific certificate data using this structure.
 */
typedef struct pj_ssl_cert_direct
{
    /**
     * Bit flag of the type of direct certificate data, see
     * \ref pj_ssl_cert_direct_type.
     */
    unsigned type;
    
    /**
     * Pointer to backend specific private key object, e.g: OpenSSL EVP_PKEY.
     */
    void *privkey;

    /**
     * Pointer to backend specific certificate object, e.g: OpenSSL X509.
     */
    void *cert;

} pj_ssl_cert_direct;


/**
 * Describe structure of certificate info.
 */
typedef struct pj_ssl_cert_info {

    unsigned    version;            /**< Certificate version    */

    pj_uint8_t  serial_no[20];      /**< Serial number, array of
                                         octets, first index is
                                         MSB                    */

    struct {
        pj_str_t        cn;         /**< Common name            */
        pj_str_t        info;       /**< One line subject, fields
                                         are separated by slash, e.g:
                                         "CN=sample.org/OU=HRD" */
    } subject;                      /**< Subject                */

    struct {
        pj_str_t        cn;         /**< Common name            */
        pj_str_t        info;       /**< One line subject, fields
                                         are separated by slash.*/
    } issuer;                       /**< Issuer                 */

    struct {
        pj_time_val     start;      /**< Validity start         */
        pj_time_val     end;        /**< Validity end           */
        pj_bool_t       gmt;        /**< Flag if validity date/time 
                                         use GMT                */
    } validity;                     /**< Validity               */

    struct {
        unsigned        cnt;        /**< # of entry             */
        struct {
            pj_ssl_cert_name_type type;
                                    /**< Name type              */
            pj_str_t    name;       /**< The name               */
        } *entry;                   /**< Subject alt name entry */
    } subj_alt_name;                /**< Subject alternative
                                         name extension         */

    pj_str_t raw;                   /**< Raw certificate in PEM format, only
                                         available for remote certificate. */

    struct {
        unsigned        cnt;        /**< # of entry     */
        pj_str_t       *cert_raw;
    } raw_chain;

} pj_ssl_cert_info;


/**
 * Create credential from files. TLS server application can provide multiple
 * certificates (RSA, ECC, and DSA) by supplying certificate name with "_rsa"
 * suffix, e.g: "pjsip_rsa.pem", the library will automatically check for
 * other certificates with "_ecc" and "_dsa" suffix.
 *
 * @param pool          The pool.
 * @param CA_file       The file of trusted CA list.
 * @param cert_file     The file of certificate.
 * @param privkey_file  The file of private key.
 * @param privkey_pass  The password of private key, if any.
 * @param p_cert        Pointer to credential instance.
 *                      If the credential instance is NULL, a new credential
 *                      instance will be created, otherwise the specified
 *                      credential instance will be used.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
                                                 const pj_str_t *CA_file,
                                                 const pj_str_t *cert_file,
                                                 const pj_str_t *privkey_file,
                                                 const pj_str_t *privkey_pass,
                                                 pj_ssl_cert_t **p_cert);

/**
 * Create credential from files. TLS server application can provide multiple
 * certificates (RSA, ECC, and DSA) by supplying certificate name with "_rsa"
 * suffix, e.g: "pjsip_rsa.pem", the library will automatically check for
 * other certificates with "_ecc" and "_dsa" suffix.
 *
 * This is the same as pj_ssl_cert_load_from_files() but also
 * accepts an additional param CA_path to load CA certificates from
 * a directory.
 *
 * @param pool          The pool.
 * @param CA_file       The file of trusted CA list.
 * @param CA_path       The path to a directory of trusted CA list.
 * @param cert_file     The file of certificate.
 * @param privkey_file  The file of private key.
 * @param privkey_pass  The password of private key, if any.
 * @param p_cert        Pointer to credential instance.
 *                      If the credential instance is NULL, a new credential
 *                      instance will be created, otherwise the specified
 *                      credential instance will be used.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files2(
                                                pj_pool_t *pool,
                                                const pj_str_t *CA_file,
                                                const pj_str_t *CA_path,
                                                const pj_str_t *cert_file,
                                                const pj_str_t *privkey_file,
                                                const pj_str_t *privkey_pass,
                                                pj_ssl_cert_t **p_cert);


/**
 * Create credential from data buffer. The certificate expected is in 
 * PEM format.
 *
 * @param pool          The pool.
 * @param CA_buf        The buffer of trusted CA list.
 * @param cert_buf      The buffer of certificate.
 * @param privkey_buf   The buffer of private key.
 * @param privkey_pass  The password of private key, if any.
 * @param p_cert        Pointer to credential instance.
 *                      If the credential instance is NULL, a new credential
 *                      instance will be created, otherwise the specified
 *                      credential instance will be used.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
                                        const pj_ssl_cert_buffer *CA_buf,
                                        const pj_ssl_cert_buffer *cert_buf,
                                        const pj_ssl_cert_buffer *privkey_buf,
                                        const pj_str_t *privkey_pass,
                                        pj_ssl_cert_t **p_cert);


/**
 * Create credential from OS certificate store, this function will lookup
 * certificate using the specified criterias.
 *
 * Currently this is used by Windows Schannel backend only, it will lookup
 * in the Current User store first, if no certificate with the specified
 * criteria is not found, it will lookup in the Local Machine store.
 *
 * Note that for manual verification (e.g: when pj_ssl_sock_param.verify_peer
 * is disabled), the backend will provide pre-verification result against
 * trusted CA certificates in Current User store only (will not check CA
 * certificates in the Local Machine store).
 *
 * @param pool          The pool.
 * @param criteria      The lookup criteria.
 * @param p_cert        Pointer to credential instance.
 *                      If the credential instance is NULL, a new credential
 *                      instance will be created, otherwise the specified
 *                      credential instance will be used.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cert_load_from_store(
                                pj_pool_t *pool,
                                const pj_ssl_cert_lookup_criteria *criteria,
                                pj_ssl_cert_t **p_cert);


/**
 * Create credential from the backend specific objects.
 *
 * For example, application can use OpenSSL ENGINE to load a private key from
 * a hardware device, and then provide the EVP_PKEY instance to be used by
 * OpenSSL backend via this function.
 *
 * Application should maintain the objects lifetime until the credential
 * instance is no longer used (e.g: SSL socket is destroyed).
 * In OpenSSL version 3, the SSL socket utilizes the reference counting
 * feature to manage object lifetimes. Specifically,
 * pj_ssl_sock_set_certificate() increments the reference count and
 * pj_ssl_close() decrements it.
 *
 * @param pool          The pool.
 * @param cert_direct   The backend specific objects.
 * @param p_cert        Pointer to credential instance.
 *                      If the credential instance is NULL, a new credential
 *                      instance will be created, otherwise the specified
 *                      credential instance will be used.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cert_load_direct(
                                pj_pool_t *pool,
                                pj_ssl_cert_direct *cert_direct,
                                pj_ssl_cert_t **p_cert);


/**
 * Dump SSL certificate info.
 *
 * @param ci            The certificate info.
 * @param indent        String for left indentation.
 * @param buf           The buffer where certificate info will be printed on.
 * @param buf_size      The buffer size.
 *
 * @return              The length of the dump result, or -1 when buffer size
 *                      is not sufficient.
 */
PJ_DECL(pj_ssize_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
                                          const char *indent,
                                          char *buf,
                                          pj_size_t buf_size);


/**
 * Get SSL certificate verification error messages from verification status.
 *
 * @param verify_status The SSL certificate verification status.
 * @param error_strings Array of strings to receive the verification error 
 *                      messages.
 * @param count         On input it specifies maximum error messages should be
 *                      retrieved. On output it specifies the number of error
 *                      messages retrieved.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cert_get_verify_status_strings(
                                                 pj_uint32_t verify_status, 
                                                 const char *error_strings[],
                                                 unsigned *count);

/** 
 * Wipe out the keys in the SSL certificate. 
 *
 * @param cert          The SSL certificate. 
 *
 */
PJ_DECL(void) pj_ssl_cert_wipe_keys(pj_ssl_cert_t *cert);


/** 
 * Cipher suites enumeration.
 */
typedef enum pj_ssl_cipher {

    /* Unsupported cipher */
    PJ_TLS_UNKNOWN_CIPHER                       = -1,

    /* NULL */
    PJ_TLS_NULL_WITH_NULL_NULL                  = 0x00000000,

    /* TLS/SSLv3 */
    PJ_TLS_RSA_WITH_NULL_MD5                    = 0x00000001,
    PJ_TLS_RSA_WITH_NULL_SHA                    = 0x00000002,
    PJ_TLS_RSA_WITH_NULL_SHA256                 = 0x0000003B,
    PJ_TLS_RSA_WITH_RC4_128_MD5                 = 0x00000004,
    PJ_TLS_RSA_WITH_RC4_128_SHA                 = 0x00000005,
    PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA            = 0x0000000A,
    PJ_TLS_RSA_WITH_AES_128_CBC_SHA             = 0x0000002F,
    PJ_TLS_RSA_WITH_AES_256_CBC_SHA             = 0x00000035,
    PJ_TLS_RSA_WITH_AES_128_CBC_SHA256          = 0x0000003C,
    PJ_TLS_RSA_WITH_AES_256_CBC_SHA256          = 0x0000003D,
    PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA         = 0x0000000D,
    PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA         = 0x00000010,
    PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA        = 0x00000013,
    PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA        = 0x00000016,
    PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA          = 0x00000030,
    PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA          = 0x00000031,
    PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA         = 0x00000032,
    PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA         = 0x00000033,
    PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA          = 0x00000036,
    PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA          = 0x00000037,
    PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA         = 0x00000038,
    PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA         = 0x00000039,
    PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256       = 0x0000003E,
    PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256       = 0x0000003F,
    PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256      = 0x00000040,
    PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256      = 0x00000067,
    PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256       = 0x00000068,
    PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256       = 0x00000069,
    PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256      = 0x0000006A,
    PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256      = 0x0000006B,
    PJ_TLS_DH_anon_WITH_RC4_128_MD5             = 0x00000018,
    PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA        = 0x0000001B,
    PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA         = 0x00000034,
    PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA         = 0x0000003A,
    PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256      = 0x0000006C,
    PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256      = 0x0000006D,

    PJ_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384  = 0x0000c02c,
    PJ_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384    = 0x0000c030,
    PJ_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384      = 0x000000a3,
    PJ_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384      = 0x0000009f,
    PJ_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256  = 0x0000c02b,
    PJ_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256    = 0x0000c02f,

    /* TLS 1.3 cipher suites */
    PJ_TLS_AES_128_GCM_SHA256                   = 0x00001301,
    PJ_TLS_AES_256_GCM_SHA384                   = 0x00001302,
    PJ_TLS_CHACHA20_POLY1305_SHA256             = 0x00001303,

    /* TLS (deprecated) */
    PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5           = 0x00000003,
    PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5       = 0x00000006,
    PJ_TLS_RSA_WITH_IDEA_CBC_SHA                = 0x00000007,
    PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA        = 0x00000008,
    PJ_TLS_RSA_WITH_DES_CBC_SHA                 = 0x00000009,
    PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA     = 0x0000000B,
    PJ_TLS_DH_DSS_WITH_DES_CBC_SHA              = 0x0000000C,
    PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA     = 0x0000000E,
    PJ_TLS_DH_RSA_WITH_DES_CBC_SHA              = 0x0000000F,
    PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA    = 0x00000011,
    PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA             = 0x00000012,
    PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA    = 0x00000014,
    PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA             = 0x00000015,
    PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5       = 0x00000017,
    PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA    = 0x00000019,
    PJ_TLS_DH_anon_WITH_DES_CBC_SHA             = 0x0000001A,

    /* SSLv3 */
    PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA           = 0x0000001C,
    PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA   = 0x0000001D,
    PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA        = 0x0000001E,
    
    /* SSLv2 */
    PJ_SSL_CK_RC4_128_WITH_MD5                  = 0x00010080,
    PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5         = 0x00020080,
    PJ_SSL_CK_RC2_128_CBC_WITH_MD5              = 0x00030080,
    PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5     = 0x00040080,
    PJ_SSL_CK_IDEA_128_CBC_WITH_MD5             = 0x00050080,
    PJ_SSL_CK_DES_64_CBC_WITH_MD5               = 0x00060040,
    PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5         = 0x000700C0

} pj_ssl_cipher;


/**
 * Get cipher list supported by SSL/TLS backend.
 *
 * @param ciphers       The ciphers buffer to receive cipher list.
 * @param cipher_num    Maximum number of ciphers to be received.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
                                                  unsigned *cipher_num);


/**
 * Check if the specified cipher is supported by SSL/TLS backend.
 *
 * @param cipher        The cipher.
 *
 * @return              PJ_TRUE when supported.
 */
PJ_DECL(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher);


/**
 * Get cipher name string.
 *
 * @param cipher        The cipher.
 *
 * @return              The cipher name or NULL if cipher is not recognized/
 *                      supported.
 */
PJ_DECL(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher);


/**
 * Get cipher ID from cipher name string. Note that on different backends
 * (e.g. OpenSSL or Symbian implementation), cipher names may not be
 * equivalent for the same cipher ID.
 *
 * @param cipher_name   The cipher name string.
 *
 * @return              The cipher ID or PJ_TLS_UNKNOWN_CIPHER if the cipher
 *                      name string is not recognized/supported.
 */
PJ_DECL(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name);

/**
 * Elliptic curves enumeration.
 */
typedef enum pj_ssl_curve
{
        PJ_TLS_UNKNOWN_CURVE            = 0,
        PJ_TLS_CURVE_SECT163K1          = 1,
        PJ_TLS_CURVE_SECT163R1          = 2,
        PJ_TLS_CURVE_SECT163R2          = 3,
        PJ_TLS_CURVE_SECT193R1          = 4,
        PJ_TLS_CURVE_SECT193R2          = 5,
        PJ_TLS_CURVE_SECT233K1          = 6,
        PJ_TLS_CURVE_SECT233R1          = 7,
        PJ_TLS_CURVE_SECT239K1          = 8,
        PJ_TLS_CURVE_SECT283K1          = 9,
        PJ_TLS_CURVE_SECT283R1          = 10,
        PJ_TLS_CURVE_SECT409K1          = 11,
        PJ_TLS_CURVE_SECT409R1          = 12,
        PJ_TLS_CURVE_SECT571K1          = 13,
        PJ_TLS_CURVE_SECT571R1          = 14,
        PJ_TLS_CURVE_SECP160K1          = 15,
        PJ_TLS_CURVE_SECP160R1          = 16,
        PJ_TLS_CURVE_SECP160R2          = 17,
        PJ_TLS_CURVE_SECP192K1          = 18,
        PJ_TLS_CURVE_SECP192R1          = 19,
        PJ_TLS_CURVE_SECP224K1          = 20,
        PJ_TLS_CURVE_SECP224R1          = 21,
        PJ_TLS_CURVE_SECP256K1          = 22,
        PJ_TLS_CURVE_SECP256R1          = 23,
        PJ_TLS_CURVE_SECP384R1          = 24,
        PJ_TLS_CURVE_SECP521R1          = 25,
        PJ_TLS_CURVE_BRAINPOOLP256R1    = 26,
        PJ_TLS_CURVE_BRAINPOOLP384R1    = 27,
        PJ_TLS_CURVE_BRAINPOOLP512R1    = 28,
        PJ_TLS_CURVE_ARBITRARY_EXPLICIT_PRIME_CURVES    = 0XFF01,
        PJ_TLS_CURVE_ARBITRARY_EXPLICIT_CHAR2_CURVES    = 0XFF02
} pj_ssl_curve;

/**
 * Get curve list supported by SSL/TLS backend.
 *
 * @param curves        The curves buffer to receive curve list.
 * @param curve_num     Maximum number of curves to be received.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_curve_get_availables(pj_ssl_curve curves[],
                                                 unsigned *curve_num);

/**
 * Check if the specified curve is supported by SSL/TLS backend.
 *
 * @param curve         The curve.
 *
 * @return              PJ_TRUE when supported.
 */
PJ_DECL(pj_bool_t) pj_ssl_curve_is_supported(pj_ssl_curve curve);


/**
 * Get curve name string.
 *
 * @param curve         The curve.
 *
 * @return              The curve name or NULL if curve is not recognized/
 *                      supported.
 */
PJ_DECL(const char*) pj_ssl_curve_name(pj_ssl_curve curve);

/**
 * Get curve ID from curve name string. Note that on different backends
 * (e.g. OpenSSL or Symbian implementation), curve names may not be
 * equivalent for the same curve ID.
 *
 * @param curve_name    The curve name string.
 *
 * @return              The curve ID or PJ_TLS_UNKNOWN_CURVE if the curve
 *                      name string is not recognized/supported.
 */
PJ_DECL(pj_ssl_curve) pj_ssl_curve_id(const char *curve_name);

/**
 * Entropy enumeration
 */
typedef enum pj_ssl_entropy
{
        PJ_SSL_ENTROPY_NONE     = 0,    /**< None */
        PJ_SSL_ENTROPY_EGD      = 1,    /**< EGD */
        PJ_SSL_ENTROPY_RANDOM   = 2,    /**< Random */
        PJ_SSL_ENTROPY_URANDOM  = 3,    /**< Urandom */
        PJ_SSL_ENTROPY_FILE     = 4,    /**< File */
        PJ_SSL_ENTROPY_UNKNOWN  = 0x0F  /**< Unknown */
} pj_ssl_entropy_t;

/**
 * This structure contains the callbacks to be called by the secure socket.
 */
typedef struct pj_ssl_sock_cb
{
    /**
     * This callback is called when a data arrives as the result of
     * pj_ssl_sock_start_read().
     *
     * @param ssock     The secure socket.
     * @param data      The buffer containing the new data, if any. If 
     *                  the status argument is non-PJ_SUCCESS, this 
     *                  argument may be NULL.
     * @param size      The length of data in the buffer.
     * @param status    The status of the read operation. This may contain
     *                  non-PJ_SUCCESS for example when the TCP connection
     *                  has been closed. In this case, the buffer may
     *                  contain left over data from previous callback which
     *                  the application may want to process.
     * @param remainder If application wishes to leave some data in the 
     *                  buffer (common for TCP applications), it should 
     *                  move the remainder data to the front part of the 
     *                  buffer and set the remainder length here. The value
     *                  of this parameter will be ignored for datagram
     *                  sockets.
     *
     * @return          PJ_TRUE if further read is desired, and PJ_FALSE 
     *                  when application no longer wants to receive data.
     *                  Application may destroy the secure socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_data_read)(pj_ssl_sock_t *ssock,
                              void *data,
                              pj_size_t size,
                              pj_status_t status,
                              pj_size_t *remainder);
    /**
     * This callback is called when a packet arrives as the result of
     * pj_ssl_sock_start_recvfrom().
     *
     * @param ssock     The secure socket.
     * @param data      The buffer containing the packet, if any. If 
     *                  the status argument is non-PJ_SUCCESS, this 
     *                  argument will be set to NULL.
     * @param size      The length of packet in the buffer. If 
     *                  the status argument is non-PJ_SUCCESS, this 
     *                  argument will be set to zero.
     * @param src_addr  Source address of the packet.
     * @param addr_len  Length of the source address.
     * @param status    This contains
     *
     * @return          PJ_TRUE if further read is desired, and PJ_FALSE 
     *                  when application no longer wants to receive data.
     *                  Application may destroy the secure socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_data_recvfrom)(pj_ssl_sock_t *ssock,
                                  void *data,
                                  pj_size_t size,
                                  const pj_sockaddr_t *src_addr,
                                  int addr_len,
                                  pj_status_t status);

    /**
     * This callback is called when data has been sent.
     *
     * @param ssock     The secure socket.
     * @param send_key  Key associated with the send operation.
     * @param sent      If value is positive non-zero it indicates the
     *                  number of data sent. When the value is negative,
     *                  it contains the error code which can be retrieved
     *                  by negating the value (i.e. status=-sent).
     *
     * @return          Application may destroy the secure socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_data_sent)(pj_ssl_sock_t *ssock,
                              pj_ioqueue_op_key_t *send_key,
                              pj_ssize_t sent);

    /**
     * This callback is called when new connection arrives as the result
     * of pj_ssl_sock_start_accept(). If the status of accept operation is
     * needed use on_accept_complete2 instead of this callback.
     *
     * @param ssock     The secure socket.
     * @param newsock   The new incoming secure socket.
     * @param src_addr  The source address of the connection.
     * @param addr_len  Length of the source address.
     *
     * @return          PJ_TRUE if further accept() is desired, and PJ_FALSE
     *                  when application no longer wants to accept incoming
     *                  connection. Application may destroy the secure socket
     *                  in the callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_accept_complete)(pj_ssl_sock_t *ssock,
                                    pj_ssl_sock_t *newsock,
                                    const pj_sockaddr_t *src_addr,
                                    int src_addr_len);
    /**
     * This callback is called when new connection arrives as the result
     * of pj_ssl_sock_start_accept().
     *
     * @param asock     The active socket.
     * @param newsock   The new incoming socket.
     * @param src_addr  The source address of the connection.
     * @param addr_len  Length of the source address.
     * @param status    The status of the accept operation. This may contain
     *                  non-PJ_SUCCESS for example when the TCP listener is in
     *                  bad state for example on iOS platform after the
     *                  application waking up from background.
     *
     * @return          PJ_TRUE if further accept() is desired, and PJ_FALSE
     *                  when application no longer wants to accept incoming
     *                  connection. Application may destroy the active socket
     *                  in the callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_accept_complete2)(pj_ssl_sock_t *ssock,
                                     pj_ssl_sock_t *newsock,
                                     const pj_sockaddr_t *src_addr,
                                     int src_addr_len, 
                                     pj_status_t status);

    /**
     * This callback is called when pending connect operation has been
     * completed.
     *
     * @param ssock     The secure socket.
     * @param status    The connection result. If connection has been
     *                  successfully established, the status will contain
     *                  PJ_SUCCESS.
     *
     * @return          Application may destroy the secure socket in the
     *                  callback and return PJ_FALSE here. 
     */
    pj_bool_t (*on_connect_complete)(pj_ssl_sock_t *ssock,
                                     pj_status_t status);
    
    /**
     * This callback is called when certificate verification is being done.
     * Certification info can be obtained from #pj_ssl_sock_info. Currently
     * it's only implemented for OpenSSL backend.
     *
     * If this is set, the callback will always be invoked, even when peer
     * verification is disabled (pj_ssl_sock_param.verify_peer set to
     * PJ_FALSE).
     *
     * @param ssock     The secure socket.
     * @param is_server PJ_TRUE to indicate an incoming connection.
     *
     * @return          Return PJ_TRUE if verification is successful. 
     *                  If verification failed, then the connection will be 
     *                  dropped immediately.
     * 
     */
    pj_bool_t (*on_verify_cb)(pj_ssl_sock_t *ssock, pj_bool_t is_server);

} pj_ssl_sock_cb;


/** 
 * Enumeration of secure socket protocol types.
 * This can be combined using bitwise OR operation.
 */
typedef enum pj_ssl_sock_proto
{
    /**
     * Default protocol of backend.
     * Typically this will be set to all supported non-deprecated protocols,
     * which, currently is TLSv1.2 and TLSv1.3.
     */   
    PJ_SSL_SOCK_PROTO_DEFAULT = 0,

    /** 
     * SSLv2.0 protocol.
     * This protocol has been deprecated.
     */
    PJ_SSL_SOCK_PROTO_SSL2    = (1 << 0),

    /** 
     * SSLv3.0 protocol.
     * This protocol has been deprecated.
     */
    PJ_SSL_SOCK_PROTO_SSL3    = (1 << 1),

    /**
     * TLSv1.0 protocol.
     * This protocol has been deprecated.
     */
    PJ_SSL_SOCK_PROTO_TLS1    = (1 << 2),

    /** 
     * TLSv1.1 protocol.
     * This protocol has been deprecated.
     */
    PJ_SSL_SOCK_PROTO_TLS1_1  = (1 << 3),

    /**
     * TLSv1.2 protocol.
     */
    PJ_SSL_SOCK_PROTO_TLS1_2  = (1 << 4),

    /**
     * TLSv1.3 protocol.
     */
    PJ_SSL_SOCK_PROTO_TLS1_3  = (1 << 5),

    /** 
     * This protocol has been deprecated.
     */
    PJ_SSL_SOCK_PROTO_SSL23   = (1 << 16) - 1,

    /**
     * This will enable all the backend's supported protocols.
     */
    PJ_SSL_SOCK_PROTO_ALL     = (1 << 16) - 1,

    /**
     * DTLSv1.0 protocol.         
     */
    PJ_SSL_SOCK_PROTO_DTLS1   = (1 << 16),

} pj_ssl_sock_proto;


/**
 * Definition of secure socket info structure.
 */
typedef struct pj_ssl_sock_info 
{
    /**
     * Describes whether secure socket connection is established, i.e: TLS/SSL 
     * handshaking has been done successfully.
     */
    pj_bool_t established;

    /**
     * Describes secure socket protocol being used, see #pj_ssl_sock_proto. 
     * Use bitwise OR operation to combine the protocol type.
     */
    pj_uint32_t proto;

    /**
     * Describes cipher suite being used, this will only be set when connection
     * is established.
     */
    pj_ssl_cipher cipher;

    /**
     * Describes local address.
     */
    pj_sockaddr local_addr;

    /**
     * Describes remote address.
     */
    pj_sockaddr remote_addr;
   
    /**
     * Describes active local certificate info.
     */
    pj_ssl_cert_info *local_cert_info;
   
    /**
     * Describes active remote certificate info.
     */
    pj_ssl_cert_info *remote_cert_info;

    /**
     * Status of peer certificate verification.
     */
    pj_uint32_t         verify_status;

    /**
     * Last native error returned by the backend.
     */
    unsigned long       last_native_err;

    /**
     * Group lock assigned to the ioqueue key.
     */
    pj_grp_lock_t *grp_lock;

    /**
     * Native TLS/SSL instance of the backend. Currently only available for
     * OpenSSL backend (this will contain the OpenSSL "SSL" instance).
     */
    void *native_ssl;

} pj_ssl_sock_info;


/**
 * Definition of secure socket creation parameters.
 */
typedef struct pj_ssl_sock_param
{
    /**
     * Optional group lock to be assigned to the ioqueue key.
     *
     * Note that when a secure socket listener is configured with a group
     * lock, any new secure socket of an accepted incoming connection
     * will have its own group lock created automatically by the library,
     * this group lock can be queried via pj_ssl_sock_get_info() in the info
     * field pj_ssl_sock_info::grp_lock.
     */
    pj_grp_lock_t *grp_lock;

    /**
     * Specifies socket address family, either pj_AF_INET() and pj_AF_INET6().
     *
     * Default is pj_AF_INET().
     */
    int sock_af;

    /**
     * Specify socket type, either pj_SOCK_DGRAM() or pj_SOCK_STREAM().
     *
     * Default is pj_SOCK_STREAM().
     */
    int sock_type;

    /**
     * Specify the ioqueue to use. Secure socket uses the ioqueue to perform
     * active socket operations, see \ref PJ_ACTIVESOCK for more detail.
     */
    pj_ioqueue_t *ioqueue;

    /**
     * Specify the timer heap to use. Secure socket uses the timer to provide
     * auto cancelation on asynchronous operation when it takes longer time 
     * than specified timeout period, e.g: security negotiation timeout.
     */
    pj_timer_heap_t *timer_heap;

    /**
     * Specify secure socket callbacks, see #pj_ssl_sock_cb.
     */
    pj_ssl_sock_cb cb;

    /**
     * Specify secure socket user data.
     */
    void *user_data;

    /**
     * Specify security protocol to use, see #pj_ssl_sock_proto. Use bitwise OR 
     * operation to combine the protocol type.
     *
     * Default is PJ_SSL_SOCK_PROTO_DEFAULT.
     */
    pj_uint32_t proto;

    /**
     * Number of concurrent asynchronous operations that is to be supported
     * by the secure socket. This value only affects socket receive and
     * accept operations -- the secure socket will issue one or more 
     * asynchronous read and accept operations based on the value of this
     * field. Setting this field to more than one will allow more than one
     * incoming data or incoming connections to be processed simultaneously
     * on multiprocessor systems, when the ioqueue is polled by more than
     * one threads.
     *
     * The default value is 1.
     */
    unsigned async_cnt;

    /**
     * The ioqueue concurrency to be forced on the socket when it is 
     * registered to the ioqueue. See #pj_ioqueue_set_concurrency() for more
     * info about ioqueue concurrency.
     *
     * When this value is -1, the concurrency setting will not be forced for
     * this socket, and the socket will inherit the concurrency setting of 
     * the ioqueue. When this value is zero, the secure socket will disable
     * concurrency for the socket. When this value is +1, the secure socket
     * will enable concurrency for the socket.
     *
     * The default value is -1.
     */
    int concurrency;

    /**
     * If this option is specified, the secure socket will make sure that
     * asynchronous send operation with stream oriented socket will only
     * call the callback after all data has been sent. This means that the
     * secure socket will automatically resend the remaining data until
     * all data has been sent.
     *
     * Please note that when this option is specified, it is possible that
     * error is reported after partial data has been sent. Also setting
     * this will disable the ioqueue concurrency for the socket.
     *
     * Default value is 1.
     */
    pj_bool_t whole_data;

    /**
     * Specify buffer size for sending operation. Buffering sending data
     * is used for allowing application to perform multiple outstanding 
     * send operations. Whenever application specifies this setting too
     * small, sending operation may return PJ_ENOMEM.
     *  
     * Default value is 8192 bytes.
     */
    pj_size_t send_buffer_size;

    /**
     * Specify buffer size for receiving encrypted (and perhaps compressed)
     * data on underlying socket. This setting is unused on Symbian, since 
     * SSL/TLS Symbian backend, CSecureSocket, can use application buffer 
     * directly.
     *
     * Default value is 1500.
     */
    pj_size_t read_buffer_size;

    /**
     * Number of ciphers contained in the specified cipher preference. 
     * If this is set to zero, then the cipher list used will be determined
     * by the backend default (for OpenSSL backend, setting 
     * PJ_SSL_SOCK_OSSL_CIPHERS will be used).
     */
    unsigned ciphers_num;

    /**
     * Ciphers and order preference. If empty, then default cipher list and
     * its default order of the backend will be used.
     */
    pj_ssl_cipher *ciphers;

    /**
     * Number of curves contained in the specified curve preference.
     * If this is set to zero, then default curve list of the backend
     * will be used.
     *
     * Default: 0 (zero).
     */
    unsigned curves_num;

    /**
     * Curves and order preference. The #pj_ssl_curve_get_availables()
     * can be used to check the available curves supported by backend.
     */
    pj_ssl_curve *curves;

    /**
     * The supported signature algorithms. Set the sigalgs string
     * using this form:
     * "<DIGEST>+<ALGORITHM>:<DIGEST>+<ALGORITHM>"
     * Digests are: "RSA", "DSA" or "ECDSA"
     * Algorithms are: "MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512"
     * Example: "ECDSA+SHA256:RSA+SHA256"
     */
    pj_str_t    sigalgs;

    /**
     * Reseed random number generator.
     * For type #PJ_SSL_ENTROPY_FILE, parameter \a entropy_path
     * must be set to a file.
     * For type #PJ_SSL_ENTROPY_EGD, parameter \a entropy_path
     * must be set to a socket.
     *
     * Default value is PJ_SSL_ENTROPY_NONE.
    */
    pj_ssl_entropy_t    entropy_type;

    /**
     * When using a file/socket for entropy #PJ_SSL_ENTROPY_EGD or
     * #PJ_SSL_ENTROPY_FILE, \a entropy_path must contain the path
     * to entropy socket/file.
     *
     * Default value is an empty string.
     */
    pj_str_t            entropy_path;

    /**
     * Security negotiation timeout. If this is set to zero (both sec and 
     * msec), the negotiation doesn't have a timeout.
     *
     * Default value is zero.
     */
    pj_time_val timeout;

    /**
     * Specify whether endpoint should verify peer certificate.
     *
     * Default value is PJ_FALSE.
     */
    pj_bool_t verify_peer;
    
    /**
     * When secure socket is acting as server (handles incoming connection),
     * it will require the client to provide certificate.
     *
     * Default value is PJ_FALSE.
     */
    pj_bool_t require_client_cert;

    /**
     * Server name indication. When secure socket is acting as client 
     * (perform outgoing connection) and the server may host multiple
     * 'virtual' servers at a single underlying network address, setting
     * this will allow client to tell the server a name of the server
     * it is contacting. This must be set to hostname and literal IP addresses
     * are not allowed.
     *
     * Default value is zero/not-set.
     */
    pj_str_t server_name;

    /**
     * Specify if SO_REUSEADDR should be used for listening socket. This
     * option will only be used with accept() operation.
     *
     * Default is PJ_FALSE.
     */
    pj_bool_t reuse_addr;

    /**
     * QoS traffic type to be set on this transport. When application wants
     * to apply QoS tagging to the transport, it's preferable to set this
     * field rather than \a qos_param fields since this is more portable.
     *
     * Default value is PJ_QOS_TYPE_BEST_EFFORT.
     */
    pj_qos_type qos_type;

    /**
     * Set the low level QoS parameters to the transport. This is a lower
     * level operation than setting the \a qos_type field and may not be
     * supported on all platforms.
     *
     * By default all settings in this structure are disabled.
     */
    pj_qos_params qos_params;

    /**
     * Specify if the transport should ignore any errors when setting the QoS
     * traffic type/parameters.
     *
     * Default: PJ_TRUE
     */
    pj_bool_t qos_ignore_error;

    /**
     * Specify options to be set on the transport. 
     *
     * For GnuTLS backend, by default TCP_NODELAY will be set. For other
     * SSL backends, the default is no options.
     * 
     */
    pj_sockopt_params sockopt_params;

    /**
     * Specify if the transport should ignore any errors when setting the 
     * sockopt parameters.
     *
     * Default: PJ_TRUE
     * 
     */
    pj_bool_t sockopt_ignore_error;

    /**
     * Specify if should set close-on-exec flag for socket.
     *
     * Default: PJ_TRUE
    */
    pj_bool_t sock_cloexec;

    /**
     * Specify if renegotiation is enabled for TLSv1.2 or earlier.
     * 
     * Default: PJ_TRUE
     */
    pj_bool_t enable_renegotiation;

} pj_ssl_sock_param;


/**
 * The parameter for pj_ssl_sock_start_connect2().
 */
typedef struct pj_ssl_start_connect_param {
    /**
     * The pool to allocate some internal data for the operation.
     */
    pj_pool_t *pool;

    /**
     * Local address.
     */
    const pj_sockaddr_t *localaddr;

    /**
     * Port range for socket binding, relative to the start port number
     * specified in \a localaddr. This is only applicable when the start port
     * number is non zero.
     */
    pj_uint16_t local_port_range;

    /**
     * Remote address.
     */
    const pj_sockaddr_t *remaddr;

    /**
     * Length of buffer containing above addresses.
     */
    int addr_len;

} pj_ssl_start_connect_param;


/**
 * Initialize the secure socket parameters for its creation with 
 * the default values.
 *
 * @param param         The parameter to be initialized.
 */
PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param);


/**
 * Duplicate pj_ssl_sock_param.
 *
 * @param pool  Pool to allocate memory.
 * @param dst   Destination parameter.
 * @param src   Source parameter.
 */
PJ_DECL(void) pj_ssl_sock_param_copy(pj_pool_t *pool, 
                                     pj_ssl_sock_param *dst,
                                     const pj_ssl_sock_param *src);


/**
 * Create secure socket instance.
 *
 * @param pool          The pool for allocating secure socket instance.
 * @param param         The secure socket parameter, see #pj_ssl_sock_param.
 * @param p_ssock       Pointer to secure socket instance to be created.
 *
 * @return              PJ_SUCCESS when successful.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool,
                                        const pj_ssl_sock_param *param,
                                        pj_ssl_sock_t **p_ssock);


/**
 * Set secure socket certificate or credentials. Credentials may include 
 * certificate, private key and trusted Certification Authorities list. 
 * Normally, server socket must provide certificate (and private key).
 * Socket client may also need to provide certificate in case requested
 * by the server.
 *
 * @param ssock         The secure socket instance.
 * @param pool          The pool.
 * @param cert          The endpoint certificate/credentials, see
 *                      #pj_ssl_cert_t.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(
                                            pj_ssl_sock_t *ssock,
                                            pj_pool_t *pool,
                                            const pj_ssl_cert_t *cert);


/**
 * Close and destroy the secure socket.
 *
 * @param ssock         The secure socket.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock);


/**
 * Associate arbitrary data with the secure socket. Application may
 * inspect this data in the callbacks and associate it with higher
 * level processing.
 *
 * @param ssock         The secure socket.
 * @param user_data     The user data to be associated with the secure
 *                      socket.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
                                               void *user_data);

/**
 * Retrieve the user data previously associated with this secure
 * socket.
 *
 * @param ssock         The secure socket.
 *
 * @return              The user data.
 */
PJ_DECL(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock);


/**
 * Retrieve the local address and port used by specified secure socket.
 *
 * @param ssock         The secure socket.
 * @param info          The info buffer to be set, see #pj_ssl_sock_info.
 *
 * @return              PJ_SUCCESS on successful.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_get_info(pj_ssl_sock_t *ssock,
                                          pj_ssl_sock_info *info);


/**
 * Starts read operation on this secure socket. This function will create
 * \a async_cnt number of buffers (the \a async_cnt parameter was given
 * in \a pj_ssl_sock_create() function) where each buffer is \a buff_size
 * long. The buffers are allocated from the specified \a pool. Once the 
 * buffers are created, it then issues \a async_cnt number of asynchronous
 * \a recv() operations to the socket and returns back to caller. Incoming
 * data on the socket will be reported back to application via the 
 * \a on_data_read() callback.
 *
 * Application only needs to call this function once to initiate read
 * operations. Further read operations will be done automatically by the
 * secure socket when \a on_data_read() callback returns non-zero. 
 *
 * @param ssock         The secure socket.
 * @param pool          Pool used to allocate buffers for incoming data.
 * @param buff_size     The size of each buffer, in bytes.
 * @param flags         Flags to be given to pj_ioqueue_recv().
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock,
                                            pj_pool_t *pool,
                                            unsigned buff_size,
                                            pj_uint32_t flags);

/**
 * Same as #pj_ssl_sock_start_read(), except that the application
 * supplies the buffers for the read operation so that the acive socket
 * does not have to allocate the buffers.
 *
 * @param ssock         The secure socket.
 * @param pool          Pool used to allocate buffers for incoming data.
 * @param buff_size     The size of each buffer, in bytes.
 * @param readbuf       Array of packet buffers, each has buff_size size.
 * @param flags         Flags to be given to pj_ioqueue_recv().
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_read2(pj_ssl_sock_t *ssock,
                                             pj_pool_t *pool,
                                             unsigned buff_size,
                                             void *readbuf[],
                                             pj_uint32_t flags);

/**
 * Same as pj_ssl_sock_start_read(), except that this function is used
 * only for datagram sockets, and it will trigger \a on_data_recvfrom()
 * callback instead.
 *
 * @param ssock         The secure socket.
 * @param pool          Pool used to allocate buffers for incoming data.
 * @param buff_size     The size of each buffer, in bytes.
 * @param flags         Flags to be given to pj_ioqueue_recvfrom().
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom(pj_ssl_sock_t *ssock,
                                                pj_pool_t *pool,
                                                unsigned buff_size,
                                                pj_uint32_t flags);

/**
 * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
 * operation takes the buffer from the argument rather than creating
 * new ones.
 *
 * @param ssock         The secure socket.
 * @param pool          Pool used to allocate buffers for incoming data.
 * @param buff_size     The size of each buffer, in bytes.
 * @param readbuf       Array of packet buffers, each has buff_size size.
 * @param flags         Flags to be given to pj_ioqueue_recvfrom().
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom2(pj_ssl_sock_t *ssock,
                                                 pj_pool_t *pool,
                                                 unsigned buff_size,
                                                 void *readbuf[],
                                                 pj_uint32_t flags);

/**
 * Send data using the socket.
 *
 * @param ssock         The secure socket.
 * @param send_key      The operation key to send the data, which is useful
 *                      if application wants to submit multiple pending
 *                      send operations and want to track which exact data 
 *                      has been sent in the \a on_data_sent() callback.
 * @param data          The data to be sent. This data must remain valid
 *                      until the data has been sent.
 * @param size          The size of the data.
 * @param flags         Flags to be given to pj_ioqueue_send().
 *
 * @return              PJ_SUCCESS if data has been sent immediately, or
 *                      PJ_EPENDING if data cannot be sent immediately or
 *                      PJ_ENOMEM when sending buffer could not handle all
 *                      queued data, see \a send_buffer_size. The callback
 *                      \a on_data_sent() will be called when data is actually
 *                      sent. Any other return value indicates error condition.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock,
                                      pj_ioqueue_op_key_t *send_key,
                                      const void *data,
                                      pj_ssize_t *size,
                                      unsigned flags);

/**
 * Send datagram using the socket.
 *
 * @param ssock         The secure socket.
 * @param send_key      The operation key to send the data, which is useful
 *                      if application wants to submit multiple pending
 *                      send operations and want to track which exact data 
 *                      has been sent in the \a on_data_sent() callback.
 * @param data          The data to be sent. This data must remain valid
 *                      until the data has been sent.
 * @param size          The size of the data.
 * @param flags         Flags to be given to pj_ioqueue_send().
 * @param addr          The destination address.
 * @param addr_len      Length of buffer containing destination address.
 *
 * @return              PJ_SUCCESS if data has been sent immediately, or
 *                      PJ_EPENDING if data cannot be sent immediately. In
 *                      this case the \a on_data_sent() callback will be
 *                      called when data is actually sent. Any other return
 *                      value indicates error condition.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_sendto(pj_ssl_sock_t *ssock,
                                        pj_ioqueue_op_key_t *send_key,
                                        const void *data,
                                        pj_ssize_t *size,
                                        unsigned flags,
                                        const pj_sockaddr_t *addr,
                                        int addr_len);


/**
 * Starts asynchronous socket accept() operations on this secure socket. 
 * This function will issue \a async_cnt number of asynchronous \a accept() 
 * operations to the socket and returns back to caller. Incoming
 * connection on the socket will be reported back to application via the
 * \a on_accept_complete() callback.
 *
 * Application only needs to call this function once to initiate accept()
 * operations. Further accept() operations will be done automatically by 
 * the secure socket when \a on_accept_complete() callback returns non-zero.
 *
 * @param ssock         The secure socket.
 * @param pool          Pool used to allocate some internal data for the
 *                      operation.
 * @param local_addr    Local address to bind on.
 * @param addr_len      Length of buffer containing local address.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_accept(pj_ssl_sock_t *ssock,
                                              pj_pool_t *pool,
                                              const pj_sockaddr_t *local_addr,
                                              int addr_len);


/**
 * Same as #pj_ssl_sock_start_accept(), but application can provide
 * a secure socket parameter, which will be used to create a new secure
 * socket reported in \a on_accept_complete() callback when there is
 * an incoming connection.
 *
 * @param ssock         The secure socket.
 * @param pool          Pool used to allocate some internal data for the
 *                      operation.
 * @param local_addr    Local address to bind on.
 * @param addr_len      Length of buffer containing local address.
 * @param newsock_param Secure socket parameter for new accepted sockets.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
                          pj_pool_t *pool,
                          const pj_sockaddr_t *local_addr,
                          int addr_len,
                          const pj_ssl_sock_param *newsock_param);


/**
 * Starts asynchronous socket connect() operation and SSL/TLS handshaking 
 * for this socket. Once the connection is done (either successfully or not),
 * the \a on_connect_complete() callback will be called.
 *
 * @param ssock         The secure socket.
 * @param pool          The pool to allocate some internal data for the
 *                      operation.
 * @param localaddr     Local address.
 * @param remaddr       Remote address.
 * @param addr_len      Length of buffer containing above addresses.
 *
 * @return              PJ_SUCCESS if connection can be established immediately
 *                      or PJ_EPENDING if connection cannot be established 
 *                      immediately. In this case the \a on_connect_complete()
 *                      callback will be called when connection is complete. 
 *                      Any other return value indicates error condition.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock,
                                               pj_pool_t *pool,
                                               const pj_sockaddr_t *localaddr,
                                               const pj_sockaddr_t *remaddr,
                                               int addr_len);

/**
 * Same as #pj_ssl_sock_start_connect(), but application can provide a 
 * \a port_range parameter, which will be used to bind the socket to 
 * random port.
 *
 * @param ssock         The secure socket.
 *
 * @param connect_param The parameter, refer to \a pj_ssl_start_connect_param.
 *
 * @return              PJ_SUCCESS if connection can be established immediately
 *                      or PJ_EPENDING if connection cannot be established 
 *                      immediately. In this case the \a on_connect_complete()
 *                      callback will be called when connection is complete. 
 *                      Any other return value indicates error condition.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_start_connect2(
                              pj_ssl_sock_t *ssock,
                              pj_ssl_start_connect_param *connect_param);

/**
 * Starts SSL/TLS renegotiation over an already established SSL connection
 * for this socket. This operation is performed transparently, no callback 
 * will be called once the renegotiation completed successfully. However, 
 * when the renegotiation fails, the connection will be closed and callback
 * \a on_data_read() will be invoked with non-PJ_SUCCESS status code.
 *
 * @param ssock         The secure socket.
 *
 * @return              PJ_SUCCESS if renegotiation is completed immediately,
 *                      or PJ_EPENDING if renegotiation has been started and
 *                      waiting for completion, or the appropriate error code 
 *                      on failure.
 */
PJ_DECL(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock);

/**
 * @}
 */

PJ_END_DECL

#endif  /* __PJ_SSL_SOCK_H__ */
