455 lines
15 KiB
C++
455 lines
15 KiB
C++
#ifndef NETWORK_HPP
|
|
#define NETWORK_HPP
|
|
|
|
#include <map>
|
|
|
|
#include <pro.h>
|
|
|
|
#ifdef __NT__
|
|
# if !defined(AF_MAX)
|
|
# include <ws2tcpip.h>
|
|
# 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 <netinet/in.h>
|
|
# include <netinet/tcp.h>
|
|
# include <arpa/inet.h>
|
|
# include <netdb.h>
|
|
# 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 <sys/socket.h>
|
|
# include <netinet/in.h>
|
|
#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<client_handler_t *, qthread_t> 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_t> 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
|