// net_app.h : network applications // // Author: Gongbing // // Create: 2019-07-22 #pragma once #include "net_base.h" //typedef struct ip_option_information { // UCHAR Ttl; // UCHAR Tos; // UCHAR Flags; // UCHAR OptionsSize; // PUCHAR OptionsData; //} IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION; //typedef struct icmp_echo_reply { // struct sockaddr_in Address; // ULONG Status; // ULONG RoundTripTime; // USHORT DataSize; // USHORT Reserved; // PVOID Data; // struct ip_option_information Options; //} ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY; //#define IPAddr struct sockaddr_in namespace net_app { enum web_format { WEB_FMT_UNKNOWN = 0, WEB_FMT_STREAM, // binary stream ... WEB_FMT_ANSI, WEB_FMT_UTF8, WEB_FMT_UNICODE, WEB_FMT_DECIDE_BY_CONTENT, // text/html should decide by content }; __declspec(novtable) struct IAcceptClient : public ref_util::IRef { COM_API_DECLARE(long, client_accepted(SOCKET s, const char* ip, unsigned port)); COM_API_DECLARE(void, on_server_exited(unsigned exit_code)); }; __declspec(novtable) struct ITcpSvrSocket : public ref_util::IRef { COM_API_DECLARE(long, listen_at(unsigned short port, const char* ip = NULL/*INADDR_ANY*/, bool reuse_addr = true)); COM_API_DECLARE(long, stop(void)); COM_API_DECLARE(long, listen_port(void)); COM_API_DECLARE(SOCKET, listen_socket(void)); }; __declspec(novtable) struct IAsyncTcpSvr : public ITcpSvrSocket { // following two functions return ERROR_FILE_HANDLE_REVOKED if the peer is used in transmit file COM_API_DECLARE(long, read_from_peer(void* peer_key)); // DO NOT call this until you stop the read with return -1 in IPeerManager::on_data_arrived, server will invoke this automately COM_API_DECLARE(long, write_2_peer(void* peer_key, const char* data, size_t data_len, bool inner_buffer = false/*whether the data buffer is returned by request_sent_buffer, if failed and iner_buffer is 'true', the 'data' would be freed !!!*/)); COM_API_DECLARE(long, close_peer(void* peer_key, int* peer_ref)); COM_API_DECLARE(long, set_peer_data(void* peer_key, int index, void* data)); COM_API_DECLARE(long, get_peer_data(void* peer_key, int index, void** data)); COM_API_DECLARE(long, peer_address(void* peer_key, char ip_buf[IP_BUF_LEN], unsigned* port)); COM_API_DECLARE(long, send_file_content(void* peer_key, HANDLE file)); // NOTE: this method only used for transmitting file content, all pre- or post-task should done by caller !!! COM_API_DECLARE(size_t, io_buffer_size(bool in_buf)); // add on 2017-08-26 for add an existing connecting into client queue (DO NOT add it which is already in queue !!!) // after this, you should operate the peer in IPeerManager. // following two apis return peer_key COM_API_DECLARE(void*, add_2_client_queue(SOCKET s)); COM_API_DECLARE(void*, create_async_tcp_client(const char* ip, unsigned short port, unsigned conn_timeout_seconds = 0, const char* reuse_ip = NULL, unsigned reuse_port = 0)); // for reducing the times of memory copying, request memory from inner - added on 2019-07-28 COM_API_DECLARE(char*, request_sent_buffer(size_t bytes)); COM_API_DECLARE(void, free_unsent_buffer(void* buf)); // free the returned value of request_sent_buffer which has NOT called write_2_peer with inner_buffer is 'true' yet!!! }; __declspec(novtable) struct IAsyncTcpHatcher : public ref_util::IRef { COM_API_DECLARE(void*, connect(const char* ip, int port, const char* reuse_ip = NULL, unsigned reuse_port = 0)); COM_API_DECLARE(int, write(void* client, const char* buf, size_t data_len, bool inner_buf)); //COM_API_DECLARE(void*, add_2_client_queue(SOCKET s)); COM_API_DECLARE(int, close_client(void* client)); COM_API_DECLARE(int, destroy(void)); COM_API_DECLARE(long, send_file_content(void* client, HANDLE file)); // NOTE: this method only used for transmitting file content, all pre- or post-task should done by caller !!! COM_API_DECLARE(bool, get_address(void* client, char* ip/*at least with 40 bytes!!!*/, int* port, bool svr = true)); }; __declspec(novtable) struct IAsyncTcpHandler : public ref_util::IRef { COM_API_DECLARE(long, on_data_arrived(void* client, const char* data, size_t data_len, bool* rcv_file/*[in] - whether in receiving file for transmit_file, [out] - stop(true) or continue(true) receiving file*/)); // return how many bytes used, -1 to stop receiving !!! COM_API_DECLARE(bool, on_data_sent(void* client, char* data, size_t data_len, size_t data_buf_len/*for reuse inner_buffer*/)); // return whether to keep the data buffer in use. true - use again, false - default value, should free it COM_API_DECLARE(void, on_closed(void* client, int err)); COM_API_DECLARE(void, on_file_content_sent(void* client, HANDLE file, ULONGLONG bytes, int error)); // response for IAsyncTcpSvr::send_file_content }; __declspec(novtable) struct IPeerManager : public ref_util::IRef { COM_API_DECLARE(void, on_peer_connected(void* peer_key)); COM_API_DECLARE(void, on_server_exited(unsigned exit_code)); COM_API_DECLARE(long, on_data_arrived(void* peer_key, const char* data, size_t data_len, bool* rcv_file/*[in] - whether in receiving file for transmit_file, [out] - stop(true) or continue(true) receiving file*/)); // return how many bytes used, -1 to stop receiving !!! COM_API_DECLARE(bool, on_data_sent(void* peer_key, char* data, size_t data_len, size_t data_buf_len/*for reuse inner_buffer*/)); // return whether to keep the data buffer in use. true - use again, false - default value, should free it COM_API_DECLARE(void, on_peer_error(void* peer_key, unsigned err_code)); COM_API_DECLARE(void, on_peer_closed(void* peer_key)); COM_API_DECLARE(bool, is_peer_timeout(void* peer_key, int elapsed_seconds/*elapsed seconds from last active*/)); COM_API_DECLARE(void, on_file_content_sent(void* peer_key, HANDLE file, ULONGLONG bytes, int error)); // response for IAsyncTcpSvr::send_file_content }; __declspec(novtable) struct IBlockTcp : public ref_util::IRef { COM_API_DECLARE(SOCKET, attach(SOCKET s)); COM_API_DECLARE(SOCKET, detach(void)); COM_API_DECLARE(SOCKET, socket_handle(void)); COM_API_DECLARE(int, re_connect(const char* dot_ip = NULL, unsigned short port = 0)); COM_API_DECLARE(int, set_timeout(unsigned *read_timeout/*milliseconds*/, unsigned *write_timeout/*milliseconds*/)); COM_API_DECLARE(int, read(char* buf, size_t buf_len, size_t* read_bytes)); COM_API_DECLARE(int, write(const char* buf, size_t data_len, size_t* wrote)); COM_API_DECLARE(int, last_error(void)); COM_API_DECLARE(long, send_file_content(HANDLE file)); // NOTE: this method only used for transmitting file content, all pre- or post-task should done by caller !!! COM_API_DECLARE(void, get_address(char* ip/*at least with 40 bytes!!!*/, int* port, bool svr = true)); COM_API_DECLARE(int, close(void)); }; __declspec(novtable) struct IUdpSvrHandler : public ref_util::IRef { COM_API_DECLARE(size_t,on_udp_data_arrived(const char* data, size_t data_len, const char* ip, unsigned port)); // return used data bytes COM_API_DECLARE(bool, on_udp_data_sent(char* data, size_t data_len, size_t data_buf_len/*for reuse inner_buffer*/, const char* ip, unsigned port)); // return whether to keep the data buffer in use. true - use again, false - default value, should free it COM_API_DECLARE(void, on_udp_server_exit(unsigned err)); }; __declspec(novtable) struct IUdpServer : public ref_util::IRef { COM_API_DECLARE(int, start(unsigned short port)); COM_API_DECLARE(int, send_to(const char* buf, size_t len, const char* ip, unsigned port, bool inner_buffer = false/*whether the data buffer is returned by request_sent_buffer*/)); COM_API_DECLARE(int, broad_cast(const char* buf, size_t len, unsigned short port, bool inner_buffer = false, const char* broad_ip_seg = NULL)); COM_API_DECLARE(int, stop(void)); // for reducing the times of memory copying, request memory from inner - added on 2019-07-28 COM_API_DECLARE(char*, request_sent_buffer(size_t bytes)); COM_API_DECLARE(void, free_unsent_buffer(void* buf)); // free the returned value of request_sent_buffer which has NOT called write_2_peer with inner_buffer is 'true' yet!!! }; typedef struct _set_cookie // all members and its' buffers will be in the same buffer with the head { char *name; char *val; char *path; char *expire; char *domain; bool http_only; bool https_only; // true - secure 1; false - secure 0 }SETCOOKIE, *LPSETCOOKIE; enum _add_head_flag { ADD_HEAD_ADD = 1, // new item added if the key was not existing, or else failed ADD_HEAD_NEW, // new item added however the key is existing already ADD_HEAD_REPLACE, // new item added if the key was not existing, or else replace it with new val ADD_HEAD_APPEND_COMMA, // like ADD_HEAD_REPLACE but append new val to rear with comma(,) if the key was existing ADD_HEAD_APPEND_SEMIC, // like ADD_HEAD_REPLACE but append new val to rear with semicolon(;) if the key was existing }; enum _cookie_index { COOKIE_INDEX_ALL = -1, }; #ifdef _USE_HTTP_CALLBACK_ __declspec(novtable) struct IHttpCallback : public ref_util::IRef { COM_API_DECLARE(void, on_response_header_ok(int response_code, unsigned __int64 cont_len, bool cont_chunked, bool cont_zipped)); COM_API_DECLARE(int, on_response_content(const char* content, size_t len)); // return 0 to continue, other to stop COM_API_DECLARE(int, on_url_jump(int jump_code, const char* utf8_new_url)); // return 0 to continue, other to stop }; #endif __declspec(novtable) struct IHttp : public ref_util::IRef // all methods are thread unsafe, all history cookies will occur in last request { #ifndef _USE_HTTP_CALLBACK_ COM_API_DECLARE(void, set_response_data_callback(inter_module_data::set_data setdata, void* param)); #endif COM_API_DECLARE(int, add_header(const char* utf8_key, const char* utf8_val, _add_head_flag flag = ADD_HEAD_REPLACE)); // 0 - ok; ERROR_ALREADY_EXISTS. must call before open_url or post_data COM_API_DECLARE(int, remove_header(const char* utf8_key/*NULL to clear all extra headers*/)); // 0 - ok; ERROR_NOT_FOUND. must call before open_url or post_data COM_API_DECLARE(int, clear_cookie(void)); // all history cookies would be added on the last request, call this to clear all cookies COM_API_DECLARE(int, open_url(const char* url, const char* cookie, const char* agent, bool get = true, const char* content = NULL, size_t content_len = 0)); COM_API_DECLARE(int, post_data(const char* url, const char* data, unsigned __int64 data_len, const char* utf8_file_tag, const char* utf8_file_name, const char* cookie, const char* agent)); COM_API_DECLARE(int, get_response_code(void)); COM_API_DECLARE(int, get_set_cookie(LPSETCOOKIE buf/*format: cookie1\0cookie2\0\0*/, size_t* len/*[in] - bytes of buf, [out] - string length in buf or minimum buffer size if buf was NULL*/ , int index = 0/*if index == COOKIE_INDEX_ALL, then return all cookies in ONE string and put it in buf*/)); // 0 - ok; ERROR_NOT_FOUND COM_API_DECLARE(int, get_jump_url(char* buf, size_t* len/*[in] - bytes of buf, [out] - string length in buf or minimum buffer size if buf was NULL*/)); // 0 - ok; ERROR_NOT_FOUND COM_API_DECLARE(int, get_response_all_header(char* buf, size_t* len/*[in] - bytes of buf, [out] - string length in buf or minimum buffer size if buf was NULL*/)); // 0 - ok; ERROR_NOT_FOUND COM_API_DECLARE(unsigned __int64, get_response_content_length(void)); COM_API_DECLARE(enum web_format, get_content_format(void)); COM_API_DECLARE(void, close(void)); }; PORT_API(ITcpSvrSocket*) create_tcp_server_socket(IAcceptClient* ac, bool ipv6 = false); enum _svr_ip_type { SVR_TYPE_NONE = 0, SVR_TYPE_IPV4_ONLY = 1, SVR_TYPE_IPV6_ONLY, SVR_TYPE_IPV4_AND_IPV6, }; PORT_API(IAsyncTcpSvr*) create_async_tcp_server(IPeerManager* peer, _svr_ip_type svr_type = SVR_TYPE_IPV4_ONLY, int buf_size_in = 0, int buf_size_out = 0, int desired_threads = 0, unsigned allow_cpu_mask = -1); // for blocking tcp would blocks following operation, this function will set r/w timeout to be 9 seconds; PORT_API(IBlockTcp*) connect_2_server(const char* ip, unsigned short port, unsigned conn_timeout_seconds = 0, const char* reuse_ip = NULL, unsigned reuse_port = 0); PORT_API(IBlockTcp*) connect_2_server_by_host(const char* host, unsigned short port, unsigned conn_timeout_seconds = 0, const char* reuse_ip = NULL, unsigned reuse_port = 0); PORT_API(IAsyncTcpHatcher*) create_async_tcp_hatcher(IAsyncTcpHandler* handler); PORT_API(IUdpServer*) create_udp_server(IUdpSvrHandler* handler, _svr_ip_type type = SVR_TYPE_IPV4_ONLY); #ifdef _USE_HTTP_CALLBACK_ PORT_API(IHttp*) create_http(IHttpCallback* cb); #else PORT_API(IHttp*) create_http(void); #endif // trace route by ICMPxxx function, to be completed ... // parameter of result: // data: ip, NULL when over // len: milliseconds, -1 is timeout, or error code when over // total: hops count, unused when over // flag: DATA_FLAG_FINAL in hops, DATA_FLAG_INSTAR in beginning information, and DATA_FLAG_OVER for over // param: 'param' PORT_API(int) trace_route(const char* www, inter_module_data::set_data result, void* param, int max_hops = 30); PORT_API(int) get_default_gateway(char gateway[IP_BUF_LEN]); PORT_API(int) get_mac(const char* dot_ip, char *mac, int* mac_bytes/*in - bytes of 'mac', out - result bytes in 'mac'*/); // enum all ips in given subnet ... // parameter of result: // data: ip, NULL when over // len: bytes of 'data' // total: unused // flag: DATA_FLAG_FINAL // param: same as 'param' PORT_API(int) enum_all_ips_in_subnet(const char* dot_ip, const char* dot_subnet, inter_module_data::set_data result, void* param); PORT_API(int) parse_cookie(const char* cookie_str, LPSETCOOKIE lpcookie, size_t size/*size of lpcookie must great than sizeof(SETCOOKIE) + lstrlenA(cookie_str) * 2*/); PORT_API(int) cookie_for_url(LPSETCOOKIE lpcookie, const char* url, char* cookie, size_t* len); PORT_API(int) http_post(const char* url, const char* content, size_t length, const char* utf8_file_tag, const char* utf8_file_name = NULL, const char* cookie = NULL, const char* agent = NULL); PORT_API(int) http_download(const char* url, inter_module_data::set_data setdata, void* param, const char* cookie = NULL, const char* agent = NULL, const char* extra_data = 0, size_t extra_data_len = 0); PORT_API(enum web_format) web_content_format(const char* web_cont, size_t bytes, UINT* cp = NULL/*to receive the code page*/); // function: send file by 'TransmitFile' for more efficiency, the function is blocked until all content transfered or error happens if parameter 'lpovl' was NULL // file: an openning file handle, suggest use flag with 'FILE_FLAG_SEQUENTIAL_SCAN' // s: an connection socket // packet_size: bytes of every packet // lpovl: asynchronous OVERLAPPED data. if you bind the socket on an IOCP, the IOCP will receive a write complete notify // pre: the content sent to remote before the file content // pre_bytes: bytes of 'pre' content // rear: the content sent to remote after all file contents transfered // rear_bytes: bytes of 'rear' content // // return: error code. PORT_API(int) transmit_file(HANDLE file, SOCKET s, size_t bytes_to_sent = 0/*max up to INT_MAX - 1*/, size_t packet_size = 4096, LPOVERLAPPED lpovl = NULL, void* pre = NULL, size_t pre_bytes = 0, void* rear = NULL, size_t rear_bytes = 0); PORT_API(HANDLE) open_file_for_sending(const wchar_t* local_file); enum { NIC_CHANGED = 0, // MibParameterNotification, NIC_ADDED = 1, // MibAddInstance, NIC_DELETED = 2, // MibDeleteInstance, }; // parameter of notify: // data: ip, this is the final ip // len: index of network interface // total_len: NIC_CHANGED - parameter changed; NIC_ADDED - new NIC added; NIC_DELETED - NIC deleted // flag: be inter_module_data::DATA_FLAG_FINAL always PORT_API(void*) reg_interface_notification(inter_module_data::set_data notify, void* param); // free the returned object by unreg_interface_notification PORT_API(void) unreg_interface_notification(void* key/*returned by reg_interface_notification()*/); }