/* (c) Copyright 2012 - 2018 Anton Sviridenko https://picapica.im 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 3 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, see . */ #ifndef PICA_CLIENT_H #define PICA_CLIENT_H #include #include "PICA_id.h" #ifdef WIN32 #define NOCRYPT #include #include #include #if defined(__MINGW32__) || defined(HAVE_STDINT_H) #include #else typedef unsigned __int8 uint8_t; typedef unsigned __int32 uint32_t; typedef unsigned __int16 uint16_t; typedef unsigned __int64 uint64_t; #endif typedef u_long in_addr_t; typedef u_short in_port_t; #else #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #ifdef PICA_MULTITHREADED #include #endif #define SOCKET int #define SOCKET_ERROR -1 #endif #include #include #include #include #include #include #include #include #include #ifdef PICA_MULTITHREADED #define OPENSSL_THREAD_DEFINES #include #if defined(OPENSSL_THREADS) // thread support enabled #else #error "THREAD SUPPORT IS NOT ENABLED IN OPENSSL!" #endif #endif //#define PICA_MSGOK 3 //#define PICA_MSGUTF8 2 #define PICA_NEWCHAN 1 #define PICA_OK 0 #define PICA_ERRINVARG -1 #define PICA_ERRNOMEM -2 #define PICA_ERRSSL -3 #define PICA_ERRSOCK -4 #define PICA_ERRSERV -5 //#define PICA_ERRVERDIFFER -6 #define PICA_ERRNOTFOUND -7 #define PICA_ERRNOPEERCERT -8 #define PICA_ERRINVPEERCERT -9 #define PICA_ERRDISCONNECT -10 #define PICA_ERRINVCERT -11 #define PICA_ERRMSGSIZE -12 #define PICA_ERRDNSRESOLVE -13 #define PICA_ERRPROTOOLD -14 #define PICA_ERRPROTONEW -15 #define PICA_ERRINVFILENAME -16 #define PICA_ERRFILETRANSFERINPROGRESS -17 #define PICA_ERRFILETRANSFERNOTINPROGRESS -18 #define PICA_ERRFILEFRAGMENTCROSSEDSIZE -19 #define PICA_ERRFILEOPEN -20 #define PICA_ERRFILEIO -21 #define PICA_ERRINVPKEYPASSPHRASE -22 #define PICA_ERRINVPKEYFILE -23 #define PICA_ERRTIMEDOUT -24 //#define PICA_CHNMSGBUFLEN 104 #define PICA_CHANREADBUFSIZE 65536 #define PICA_CHANWRITEBUFSIZE 4096 #define PICA_CONNREADBUFSIZE 65536 #define PICA_CONNWRITEBUFSIZE 4096 #define PICA_FILEFRAGMENTSIZE 3 * 16384 //max size of TLS record #define PICA_CHANSENDFILESTATE_IDLE 0 #define PICA_CHANSENDFILESTATE_SENTREQ 20 #define PICA_CHANSENDFILESTATE_SENDING 22 #define PICA_CHANSENDFILESTATE_PAUSED 23 #define PICA_CHANRECVFILESTATE_IDLE 0 #define PICA_CHANRECVFILESTATE_RECEIVING 24 #define PICA_CHANRECVFILESTATE_PAUSED 25 #define PICA_CHANRECVFILESTATE_WAITACCEPT 26 #define PICA_C2C_ACTIVATE_TIMEOUT 30 #define PICA_C2C_INCOMING 0 #define PICA_C2C_OUTGOING 1 #define PICA_DIRECTC2C_INCOMING 0 #define PICA_DIRECTC2C_OUTGOING 1 struct PICA_c2n; struct PICA_c2c; struct PICA_acc { /** * SHA224 hash of user's certificate in DER format */ unsigned char id[PICA_ID_SIZE]; SSL_CTX* ctx; SSL_CTX* anon_ctx; const char *pkey_file; const char *cert_file; int (*password_cb)(char *buf, int size, int rwflag, void *userdata); }; enum PICA_c2n_state { PICA_C2N_STATE_NEW = 0, PICA_C2N_STATE_CONNECTING, PICA_C2N_STATE_WAITINGANONTLS, PICA_C2N_STATE_WAITINGREP, PICA_C2N_STATE_WAITINGANONTLSSHUTDOWN, PICA_C2N_STATE_WAITINGTLS, PICA_C2N_STATE_CONNECTED }; enum PICA_directc2c_config { PICA_DIRECTC2C_CFG_DISABLED, PICA_DIRECTC2C_CFG_CONNECTONLY, PICA_DIRECTC2C_CFG_ALLOWINCOMING }; struct PICA_c2n { struct PICA_acc *acc; //#warning "sockaddr!" struct sockaddr_in srv_addr; // sockaddr !! SOCKET sck_comm; SSL* ssl_comm; enum PICA_c2n_state state; enum PICA_directc2c_config directc2c_config; int multilogin_policy; uint64_t multilogin_last_timestamp; struct PICA_listener *directc2c_listener; unsigned char *read_buf; unsigned char *write_buf; unsigned int read_pos; unsigned int write_pos; unsigned int write_buflen; unsigned int write_sslbytestowrite; struct PICA_c2c *chan_list_head; struct PICA_c2c *chan_list_end; int init_resp_ok; unsigned char node_ver_major, node_ver_minor; int disconnect_on_empty_write_buf; int want_write; }; enum PICA_c2c_state { PICA_C2C_STATE_NEW = 0, PICA_C2C_STATE_CONNECTING, PICA_C2C_STATE_WAITINGANONTLS, PICA_C2C_STATE_CONNID, PICA_C2C_STATE_WAITINGANONTLSSHUTDOWN, PICA_C2C_STATE_WAITINGTLS, PICA_C2C_STATE_WAITINGREP, PICA_C2C_STATE_WAITINGC2CPROTOVER, PICA_C2C_STATE_ACTIVE }; enum PICA_directc2c_state { PICA_DIRECTC2C_STATE_INACTIVE, PICA_DIRECTC2C_STATE_CONNECTING, PICA_DIRECTC2C_STATE_FAILEDTOCONNECT, PICA_DIRECTC2C_STATE_WAITINGINCOMING, PICA_DIRECTC2C_STATE_ACTIVE }; enum PICA_directc2c_connection_state { PICA_DIRECTC2C_CONNSTATE_NEW = 0, PICA_DIRECTC2C_CONNSTATE_CONNECTING, PICA_DIRECTC2C_CONNSTATE_WAITINGTLS, PICA_DIRECTC2C_CONNSTATE_ACTIVE, PICA_DIRECTC2C_CONNSTATE_FAILED }; struct PICA_directc2c { int is_outgoing; SOCKET sck; SSL *ssl; X509 *peer_cert; int want_write; uint8_t *addrlist; int addrpos; struct sockaddr_in addr; enum PICA_directc2c_connection_state state; struct PICA_directc2c *next; }; struct PICA_c2c { const struct PICA_acc *acc; struct PICA_c2n *conn; unsigned char peer_id[PICA_ID_SIZE]; SOCKET sck_data; SSL *ssl; struct PICA_directc2c *direct; int outgoing; X509 *peer_cert; unsigned char *read_buf; unsigned char *write_buf; unsigned int read_pos; unsigned int write_pos; unsigned int write_buflen; unsigned int write_sslbytestowrite; struct PICA_c2c *next; struct PICA_c2c *prev; enum PICA_c2c_state state; enum PICA_directc2c_state directc2c_state; time_t timestamp; int sendfilestate; uint64_t sendfile_size; uint64_t sendfile_pos; FILE *sendfile_stream; int recvfilestate; uint64_t recvfile_size; uint64_t recvfile_pos; FILE *recvfile_stream; /** this flag should be set when connection * must be closed after sending last pushed packet */ int disconnect_on_empty_write_buf; int switched_to_directc2c_write; int switched_to_directc2c_read; int want_write; unsigned int directc2c_write_barrier_pos; unsigned int directc2c_write_before_barrier_pos; }; struct PICA_listener { const struct PICA_acc *acc; SOCKET sck_listener; int public_port; int local_port; in_addr_t public_addr_ipv4; const char *public_addr_dns; struct PICA_directc2c *accepted_connections; }; struct PICA_client_callbacks { /** * New text message received */ void (*newmsg_cb)(const unsigned char *peer_id, const char* msgbuf, unsigned int nb, int type); /** * Message delivery confirmed */ void (*msgok_cb)(const unsigned char *peer_id); /** * Client-to-client connection established */ void (*c2c_established_cb)(const unsigned char *peer_id, const char *ciphersuitename); /** * Failed to establish client-to-client connection */ void (*c2c_failed_cb)(const unsigned char *peer_id); /** Received incoming client-to-client connection request \return action to perform \retval 0 - reject request \retval non-zero - accept request */ int (*accept_cb)(const unsigned char *caller_id); /** * Requested client is unavailable or rejected request */ void (*notfound_cb)(const unsigned char *callee_id); /** * Client-to-client connection is closed */ void (*c2c_closed_cb)(const unsigned char *peer_id, int reason); /** * Received list of node addresses. Callback is called for * each node address entry. * \param type address type, PICA_PROTO_NEWNODE_{IPV4,IPV6,DNS} * \param addr_bin address in binary form, IPv4 or IPv6 in network byte order, DNS - zero-terminated string, same as addr_str * \param addr_str address in string format, zero-terminated * \param port listening port of node, in host byte order */ void (*nodelist_cb)(int type, void *addr_bin, const char *addr_str, unsigned int port); /** * Peer'c certificate in PEM format. Callback must compare passed cert with * previously stored cert of this peer (if it exists). * \return verification result * \retval 1 - verification success * \retval 0 - verification failed */ int (*peer_cert_verify_cb)(const unsigned char *peer_id, const char *cert_pem, unsigned int nb); /** * Received request of file transfer. * \return action to perform * \retval 0 - reject file * \retval 1 - accept file * \retval 2 - postpone decision. File can be accepted later by calling PICA_accept_file() */ int (*accept_file_cb)(const unsigned char *peer_id, uint64_t file_size, const char *filename, unsigned int filename_size); void (*accepted_file_cb)(const unsigned char *peer_id); void (*denied_file_cb)(const unsigned char *peer_id); void (*file_progress_cb)(const unsigned char *peer_id, uint64_t sent, uint64_t received); void (*file_control_cb)(const unsigned char *peer_id, unsigned int sender_cmd, unsigned int receiver_cmd); void (*file_finished_cb)(const unsigned char *peer_id, int sending); void (*c2n_established_cb)(struct PICA_c2n *c2n); void (*c2n_failed_cb)(struct PICA_c2n *c2n, int error); void (*c2n_closed_cb)(struct PICA_c2n *c2n, int error); void (*listener_error_cb)(struct PICA_listener *lst, int errorcode); void (*multilogin_cb)(uint64_t timestamp, void *addr_bin, const char *addr_str, uint16_t port); void (*direct_c2c_established_cb)(const unsigned char *peer_id, const char *ciphersuitename); }; #ifdef __cplusplus extern "C" { #endif /** * Read certificate from cert_file in PEM format, store id in buffer pointed by id * \param [in] cert_file string with certificate file name * \param [out] id buffer to store ID, PICA_ID_SIZE octets */ int PICA_get_id_from_cert_file(const char *cert_file, unsigned char *id); /** * Read certificate from C string in PEM format, store id in buffer pointed by id. * \param [in] cert_pem - certificate in PEM format * \param [out] id buffer to store ID, PICA_ID_SIZE octets */ int PICA_get_id_from_cert_string(const char *cert_pem, unsigned char *id); int PICA_client_init(struct PICA_client_callbacks *clcbs); int PICA_new_c2n(const struct PICA_acc *acc, const char *nodeaddr, unsigned int port, enum PICA_directc2c_config direct_c2c_mode, int multilogin, struct PICA_listener *l, struct PICA_c2n **ci); int PICA_new_c2c(struct PICA_c2n *ci, const unsigned char *peer_id, struct PICA_listener *l, struct PICA_c2c **chn); /** * Opens socket to listen for incoming direct c2c connections bypassing nodes * \param acc - pointer to opened account instance * \param public_addr - DNS name or IP address string, * it is an address that will be used for connecting from global Internet. * Should be set to router's external IP if computer running pica-client is located behind the NAT. * \param public_port - TCP port for incoming connections from global Internet * \param local_port - TCP port that will be actually opened on computer listening for direct c2c connections * \param l - address of pointer that will be filled with address of created PICA_listener structure */ int PICA_new_listener(const struct PICA_acc *acc, const char *public_addr, int public_port, int local_port, struct PICA_listener **l); int PICA_open_acc(const char *cert_file, const char *pkey_file, const char *dh_param_file, int (*password_cb)(char *buf, int size, int rwflag, void *userdata), struct PICA_acc **acc); //connections, listeners - NULL-terminated arrays of pointers to appropriate structures // timeout - timeout in milliseconds int PICA_event_loop(struct PICA_c2n **connections, int timeout); int PICA_send_msg(struct PICA_c2c *chn, char *buf, unsigned int len); //filename - ASCII or UTF-8 encoded string int PICA_send_file(struct PICA_c2c *chn, const char *filepath); int PICA_accept_file(struct PICA_c2c *chan, char *filename, unsigned int filenamesize); int PICA_deny_file(struct PICA_c2c *chan); // if sending != 0 then pause sending file, else pause receiving of file int PICA_pause_file(struct PICA_c2c *chan, int sending); int PICA_resume_file(struct PICA_c2c *chan, int sending); int PICA_cancel_file(struct PICA_c2c *chan, int sending); void PICA_close_c2c(struct PICA_c2c *chn); void PICA_close_c2n(struct PICA_c2n *cid); void PICA_close_directc2c(struct PICA_directc2c *d); void PICA_close_listener(struct PICA_listener *l); void PICA_close_acc(struct PICA_acc *a); #ifdef __cplusplus } #endif #endif