#ifndef NETWORK_HPP #define NETWORK_HPP #include #include #ifdef __NT__ # if !defined(AF_MAX) # include # endif # define SYSTEM "Windows" # define socklen_t int # define SHUT_RD SD_RECEIVE # define SHUT_WR SD_SEND # define SHUT_RDWR SD_BOTH #else // not NT, i.e. UNIX # include # include # include # include # define closesocket(s) close(s) # define SOCKET size_t # define INVALID_SOCKET size_t(-1) # define SOCKET_ERROR (-1) # if defined(__LINUX__) # if defined(__ARM__) # if defined(__ANDROID__) # define SYSTEM "Android" # else # define SYSTEM "ARM Linux" # endif # else # if defined(__ANDROID__) # define SYSTEM "Android x86" # else # define SYSTEM "Linux" # endif # endif // linux debugger cannot be multithreaded because it uses thread_db. // i doubt that this library is meant to be used with multiple // applications simultaneously. # define __SINGLE_THREADED_SERVER__ # elif defined(__MAC__) # define SYSTEM "Mac OS X" # else # error "Unknown platform" # endif # include # include #endif #ifndef __X86__ # define _SYSBITS " 64-bit" #else # define _SYSBITS " 32-bit" #endif #ifdef TESTABLE_BUILD # ifdef __EA64__ # define SYSBITS _SYSBITS " (sizeof ea=64)" # else # define SYSBITS _SYSBITS " (sizeof ea=32)" # endif #else # define SYSBITS _SYSBITS #endif #ifdef __SINGLE_THREADED_SERVER__ # define __SERVER_TYPE__ "ST" #else # define __SERVER_TYPE__ "MT" #endif #define TIMEOUT (1000/25) // timeout for polling (ms) #define TIMEOUT_INFINITY -1 #define RECV_HELLO_TIMEOUT 1000 // timeout for the first packet (ms) #define RECV_TIMEOUT_PERIOD 10000 // timeout for recv (ms) // bidirectional codes (client <-> server) enum base_packet_id_t { RPC_OK = 0, // response: function call succeeded RPC_UNK, // response: unknown function code RPC_MEM, // response: no memory base_packet_id_last }; #define RPC_OPEN 3 // server->client: i'm ready, the very first packet #define RPC_EVENT 4 // server->client: debug event ready, followed by debug_event #define RPC_EVOK 5 // client->server: event processed (in response to RPC_EVENT) #define RPC_CANCELLED 6 // client->server: operation was cancelled by the user // we need EVOK to handle the situation when the debug // event was detected by the server during polling and // was sent to the client using RPC_EVENT but client has not received it yet // and requested GET_DEBUG_EVENT. In this case we should not // call remote_get_debug_event() but instead force the client // to use the event sent by RPC_EVENT. // In other words, if the server has sent RPC_EVENT but has not // received RPC_EVOK, it should fail all GET_DEBUG_EVENTS. // client->server codes #define RPC_INIT 10 #define RPC_TERM 11 #define RPC_GET_PROCESSES 12 #define RPC_START_PROCESS 13 #define RPC_EXIT_PROCESS 14 #define RPC_ATTACH_PROCESS 15 #define RPC_DETACH_PROCESS 16 #define RPC_GET_DEBUG_EVENT 17 #define RPC_PREPARE_TO_PAUSE_PROCESS 18 #define RPC_STOPPED_AT_DEBUG_EVENT 19 #define RPC_CONTINUE_AFTER_EVENT 20 #define RPC_TH_SUSPEND 21 #define RPC_TH_CONTINUE 22 #define RPC_SET_RESUME_MODE 23 #define RPC_GET_MEMORY_INFO 24 #define RPC_READ_MEMORY 25 #define RPC_WRITE_MEMORY 26 #define RPC_UPDATE_BPTS 27 #define RPC_UPDATE_LOWCNDS 28 #define RPC_EVAL_LOWCND 29 #define RPC_ISOK_BPT 30 #define RPC_READ_REGS 31 #define RPC_WRITE_REG 32 #define RPC_GET_SREG_BASE 33 #define RPC_SET_EXCEPTION_INFO 34 #define RPC_OPEN_FILE 35 #define RPC_CLOSE_FILE 36 #define RPC_READ_FILE 37 #define RPC_WRITE_FILE 38 #define RPC_IOCTL 39 // both client and the server may send this packet #define RPC_UPDATE_CALL_STACK 40 #define RPC_APPCALL 41 #define RPC_CLEANUP_APPCALL 42 #define RPC_REXEC 43 #define RPC_GET_SCATTERED_IMAGE 44 #define RPC_GET_IMAGE_UUID 45 #define RPC_GET_SEGM_START 46 #define RPC_BIN_SEARCH 47 // server->client codes #define RPC_SET_DEBUG_NAMES 50 #define RPC_SYNC_STUB 51 #define RPC_ERROR 52 #define RPC_MSG 53 #define RPC_WARNING 54 #define RPC_HANDLE_DEBUG_EVENT 55 #define RPC_REPORT_IDC_ERROR 56 #define RPC_IMPORT_DLL 57 #pragma pack(push, 1) struct PACKED rpc_packet_t { // fields are always sent in the network order uint32 length; // length of the packet (do not count length & code) uchar code; // function code }; CASSERT(sizeof(rpc_packet_t) == 5); #pragma pack(pop) enum rpc_notification_type_t { rnt_unknown = 0, rnt_msg, rnt_warning, rnt_error, }; #define DEFINE_ONE_NOTIFICATION_FUNCTION(FuncName, NotifCode, RpcEngineInst) \ AS_PRINTF(2, 3) void FuncName(const char *format, ...) \ { \ va_list va; \ va_start(va, format); \ dvnotif(NotifCode, RpcEngineInst, format, va); \ va_end(va); \ } #define DEFINE_ALL_NOTIFICATION_FUNCTIONS(RpcEngineInst) \ DEFINE_ONE_NOTIFICATION_FUNCTION(dmsg, 0, RpcEngineInst) \ DEFINE_ONE_NOTIFICATION_FUNCTION(dwarning, 1, RpcEngineInst) \ DEFINE_ONE_NOTIFICATION_FUNCTION(derror, -1, RpcEngineInst) class rpc_engine_t; //------------------------------------------------------------------------- AS_PRINTF(2, 0) ssize_t dvnotif_client( int code, const char *format, va_list va); #ifdef __NT__ # define IRSERR_TIMEOUT WAIT_TIMEOUT #else # define IRSERR_TIMEOUT ETIME #endif #define IRSERR_CANCELLED -0xE5CA7E // escape #define IRSERR_SKIP_ITER -0x5217 // skip recv() in rpc_engine_t's recv_data loop //------------------------------------------------------------------------- // idarpc_stream_t //------------------------------------------------------------------------- // the idarpc_stream_t structure is not defined. // it is used as an opaque type provided by the transport level. // the transport level defines its own local type for it. struct idarpc_stream_t; idarpc_stream_t *irs_new(bool use_tls=false); bool irs_init_client(idarpc_stream_t *irs, const char *hostname, int port_number); bool irs_init_server( idarpc_stream_t *irs, const char *hostname, int port_number, const char *certchain=nullptr, const char *privkey=nullptr); bool irs_accept(idarpc_stream_t *irs, idarpc_stream_t *listener); bool irs_handshake(idarpc_stream_t *irs, int timeout_ms = -1); int irs_ready(idarpc_stream_t *irs, int timeout_ms = -1); ssize_t irs_recv(idarpc_stream_t *irs, void *buf, size_t n); ssize_t irs_send(idarpc_stream_t *irs, const void *buf, size_t n); void irs_term(idarpc_stream_t **pirs, int shutdown_flags = -1); int irs_get_error(idarpc_stream_t *irs); const char *irs_strerror(idarpc_stream_t *irs); bool irs_peername(idarpc_stream_t *irs, qstring *out, bool lookupname = true); bool irs_sockname(idarpc_stream_t *irs, qstring *out, bool lookupname = true); enum progress_loop_ctrl_t { plc_proceed, plc_skip_iter, plc_cancel, }; typedef progress_loop_ctrl_t irs_progress_cb_t(bool receiving, size_t processed, size_t total, void *); void irs_set_progress_cb(idarpc_stream_t *irs, int ms, irs_progress_cb_t cb, void *ud=NULL); struct irs_cancellable_op_t { idarpc_stream_t *irs; irs_cancellable_op_t(idarpc_stream_t *_irs, bool receiving, size_t goal=0); ~irs_cancellable_op_t(); void inc_progress(size_t progress); }; //------------------------------------------------------------------------- typedef qtime64_t utc_timestamp_t; //------------------------------------------------------------------------- // base_dispatcher_t + client_handler_t //------------------------------------------------------------------------- struct client_handler_t { FILE *channels[16]; idarpc_stream_t *irs; qstring peer_name; uint32 session_id; utc_timestamp_t session_start; bool verbose; void close_all_channels(); void clear_channels(); int find_free_channel() const; client_handler_t(idarpc_stream_t *_irs, bool _verbose); virtual ~client_handler_t(); virtual bool handle() = 0; // true - delete this virtual void shutdown_gracefully(int signum) = 0; //lint -sem(client_handler_t::term_irs,cleanup) void term_irs(); AS_PRINTF(2, 3) int lprintf(const char *format, ...) const; private: DECLARE_UNCOPYABLE(client_handler_t); }; //------------------------------------------------------------------------- struct client_handlers_list_t { typedef std::map storage_t; storage_t storage; virtual ~client_handlers_list_t() {} virtual void lock() {} virtual void unlock() {} virtual bool is_multi_threaded() const { return false; } }; //------------------------------------------------------------------------- struct mt_client_handlers_list_t : public client_handlers_list_t { qmutex_t mutex; mt_client_handlers_list_t() { mutex = qmutex_create(); QASSERT(1540, mutex != NULL); } virtual ~mt_client_handlers_list_t() { qmutex_free(mutex); } virtual void lock() override { qmutex_lock(mutex); } virtual void unlock() override { qmutex_unlock(mutex); } virtual bool is_multi_threaded() const override { return true; } }; //------------------------------------------------------------------------- struct base_dispatcher_t { ushort port_number; qstring ipv4_address; qstring certchain; qstring privkey; idarpc_stream_t *irs; client_handlers_list_t *clients_list; bool use_tls; bool verbose; base_dispatcher_t(bool multi_threaded);// : port_number(-1), irs(NULL), verbose(false) {} virtual ~base_dispatcher_t(); void dispatch(); virtual void collect_cliopts(cliopts_t *out); // void install_signal_handlers(); // virtual client_handler_t *new_client_handler(idarpc_stream_t *_irs) = 0; void delete_client_handler(client_handler_t *inst); virtual void shutdown_gracefully(int signum); private: void handle_session(client_handler_t *handler); void add_to_clients_list(client_handler_t *handler, qthread_t t); DECLARE_UNCOPYABLE(base_dispatcher_t); }; //------------------------------------------------------------------------- // packing/unpacking utils //------------------------------------------------------------------------- bytevec_t prepare_rpc_packet(uchar code); void finalize_packet(bytevec_t &pkt); //const char *get_rpc_name(int code); //------------------------------------------------------------------------- // rpc_engine_t //------------------------------------------------------------------------- #define VERBOSE_ENABLED #ifdef VERBOSE_ENABLED #define verb(x) do { if ( verbose ) msg x; } while(0) #define verb_eng(engine, x) do { if ( (engine)->verbose ) msg x; } while(0) #else #define verb(x) //msg x #define verb_eng(engine, x) #endif #define verbev(x) //msg x //------------------------------------------------------------------------- struct rpc_packet_data_t { uchar code; rpc_packet_data_t(uchar _code) : code(_code) {} virtual ~rpc_packet_data_t() {} virtual void serialize(bytevec_t *out, int version) const = 0; virtual bool deserialize(const uchar **ptr, size_t len, int version) = 0; }; //------------------------------------------------------------------------- typedef int ioctl_handler_t( class rpc_engine_t *rpc, int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize); //------------------------------------------------------------------------- typedef rpc_packet_data_t *rpc_packet_instantiator_t(const uchar *ptr, size_t len, int version); //------------------------------------------------------------------------- struct rpc_packet_type_desc_t { uchar code; const char *name; rpc_packet_instantiator_t *instantiate; }; DECLARE_TYPE_AS_MOVABLE(rpc_packet_type_desc_t); typedef qvector rpc_packet_type_desc_vec_t; //--------------------------------------------------------------------------- class rpc_engine_t { public: bool network_error; // pointer to the ioctl request handler, in case you // need to handle ioctl requests from the server. ioctl_handler_t *ioctl_handler; int recv_timeout; bool is_client; bool logged_in; protected: void register_packet_type_descs(const rpc_packet_type_desc_t *ptypes, size_t cnt); const rpc_packet_type_desc_t *find_packet_type_desc(int code) const; const rpc_packet_type_desc_t *find_packet_type_desc(const char *name) const; public: rpc_engine_t(bool is_client); virtual ~rpc_engine_t() {} int handle_ioctl_packet(bytevec_t &pkt, const uchar *ptr, const uchar *end); // low-level: deal with bytes, and don't handle "conversations". int send_data(bytevec_t &data); rpc_packet_t *recv_packet(); virtual rpc_packet_t *send_request_and_receive_reply(bytevec_t &pkt) = 0; virtual idarpc_stream_t *get_irs() const = 0; AS_PRINTF(3, 0) virtual ssize_t send_notif(int code, const char *format, va_list va); virtual bool get_broken_connection(void) { return false; } virtual void set_broken_connection(void) {}; int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize); void set_ioctl_handler(ioctl_handler_t *h) { ioctl_handler = h; } DEFINE_ALL_NOTIFICATION_FUNCTIONS(this); private: rpc_packet_type_desc_vec_t ptypes; int recv_data(void *out, size_t len); AS_PRINTF(3,0) static ssize_t dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va); }; //------------------------------------------------------------------------- AS_PRINTF(3, 0) ssize_t dvnotif_rpc( int code, rpc_engine_t *rpc, const char *format, va_list va); //--------------------------------------------------------------------------- AS_PRINTF(1, 0) int vlprintf(const char *format, va_list va); AS_PRINTF(1, 2) int lprintf(const char *format, ...); void set_lprintf_output(FILE *out); //--------------------------------------------------------------------------- size_t format_timestamp(char *buf, size_t bufsize, qtime64_t ts); #endif // NETWORK_HPP