From 91fa723b31fd3cd885d7f3dec8bc9d7e01d3bd41 Mon Sep 17 00:00:00 2001 From: MikalaiR Date: Tue, 29 Mar 2016 11:06:00 +0000 Subject: Telegram: netlib network pt. 2 git-svn-id: http://svn.miranda-ng.org/main/trunk@16561 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Telegram/src/stdafx.h | 1 + protocols/Telegram/src/t_network.cpp | 564 +++++++++++++++++++++++++++++++++-- protocols/Telegram/src/t_network.h | 47 ++- 3 files changed, 575 insertions(+), 37 deletions(-) (limited to 'protocols/Telegram') diff --git a/protocols/Telegram/src/stdafx.h b/protocols/Telegram/src/stdafx.h index d83f501535..3393f840e7 100644 --- a/protocols/Telegram/src/stdafx.h +++ b/protocols/Telegram/src/stdafx.h @@ -9,6 +9,7 @@ #include #include #include +#include #include diff --git a/protocols/Telegram/src/t_network.cpp b/protocols/Telegram/src/t_network.cpp index 3804a25cf4..8837883a72 100644 --- a/protocols/Telegram/src/t_network.cpp +++ b/protocols/Telegram/src/t_network.cpp @@ -2,7 +2,7 @@ #define PING_TIMEOUT 10 -static void start_ping_timer(connection *c); +/*static void start_ping_timer(connection *c); void __stdcall ping_alarm(PVOID arg, BOOLEAN TimerOrWaitFired) { @@ -34,19 +34,19 @@ static void stop_ping_timer(struct connection *c) } -int mtgln_write_out(struct connection *c, const void *_data, int len) -{ - return Netlib_Send(c->hNetlibConn, (char*)_data, len, 0); +int mtgln_write_out(struct connection *c, const void *_data, int len) +{ + return Netlib_Send(c->hNetlibConn, (char*)_data, len, 0); } -int mtgln_read_in(struct connection *c, void *_data, int len) -{ - return Netlib_Recv(c->hNetlibConn, (char*)_data, len, 0); +int mtgln_read_in(struct connection *c, void *_data, int len) +{ + return Netlib_Recv(c->hNetlibConn, (char*)_data, len, 0); } -int mtgln_read_in_lookup(struct connection *c, void *_data, int len) -{ - return Netlib_Recv(c->hNetlibConn, (char*)_data, len, MSG_PEEK); +int mtgln_read_in_lookup(struct connection *c, void *_data, int len) +{ + return Netlib_Recv(c->hNetlibConn, (char*)_data, len, MSG_PEEK); } connection* mtgln_create_connection(struct tgl_state *TLS, const char *host, int port, struct tgl_session *session, struct tgl_dc *dc, struct mtproto_methods *methods) @@ -55,8 +55,8 @@ connection* mtgln_create_connection(struct tgl_state *TLS, const char *host, int conn->TLS = TLS; conn->ip = mir_strdup(host); conn->port = port; - conn->dc = dc; - conn->session = session; + conn->dc = dc; + conn->session = session; conn->methods = methods; NETLIBOPENCONNECTION nloc = { sizeof(nloc) }; @@ -78,7 +78,490 @@ connection* mtgln_create_connection(struct tgl_state *TLS, const char *host, int return conn; } +static void incr_out_packet_num(struct connection *c) { +} + +static struct tgl_dc *get_dc(struct connection *c) { + return c->dc; +} + +static struct tgl_session *get_session(struct connection *c) { + return c->session; +} + +static void tgln_free(struct connection *c) +{ + Netlib_CloseHandle(c->hNetlibConn); + mir_free(c->ip); + delete c; +} + +void mtgln_flush_out(struct connection *c) { +}*/ + +#define vlogprintf(verbosity_level,...) \ +do { \ +if (TLS->verbosity >= verbosity_level) {\ + TLS->callback.logprintf(__VA_ARGS__); \ +} \ +} while (0) + +#define assert(x) x + + + +static void fail_connection(struct connection *c); + +#define PING_TIMEOUT 10 + +static void start_ping_timer(struct connection *c); + +void __stdcall ping_alarm(PVOID arg, BOOLEAN TimerOrWaitFired) +{ + struct connection *c = (connection*)arg; + if (tglt_get_double_time() - c->last_receive_time > 6 * PING_TIMEOUT) + { + c->TLS->callback.logprintf("fail connection: reason: ping timeout"); + } + else if (tglt_get_double_time() - c->last_receive_time > 3 * PING_TIMEOUT) + { + tgl_do_send_ping(c->TLS, c); + start_ping_timer(c); + } + else + { + start_ping_timer(c); + } +} + +static void start_ping_timer(connection *c) +{ + CreateTimerQueueTimer(&c->ping_timer, hQueue, ping_alarm, c, PING_TIMEOUT * 1000, 0, 0); +} + +static void stop_ping_timer(struct connection *c) +{ + DeleteTimerQueueTimer(hQueue, c->ping_timer, 0); + c->ping_timer = 0; +} + +static void restart_connection(struct connection *c); + +void __stdcall fail_alarm(PVOID arg, BOOLEAN TimerOrWaitFired) +{ + struct connection *c = (connection*)arg; + c->in_fail_timer = 0; + restart_connection(c); +} + +static void start_fail_timer(struct connection *c) { + if (c->in_fail_timer) { return; } + CreateTimerQueueTimer(&c->in_fail_timer, hQueue, fail_alarm, c, PING_TIMEOUT * 1000, 0, 0); +} + +connection_buffer *new_connection_buffer(int size) { + connection_buffer *b = (connection_buffer*)talloc0(sizeof(*b)); + b->start = (PBYTE)talloc(size); + b->end = b->start + size; + b->rptr = b->wptr = b->start; + return b; +} + +static void delete_connection_buffer(struct connection_buffer *b) { + tfree(b->start, b->end - b->start); + tfree(b, sizeof (*b)); +} + +int mtgln_write_out(struct connection *c, const void *_data, int len) +{ + struct tgl_state *TLS = c->TLS; + vlogprintf(E_DEBUG, "write_out: %d bytes\n", len); + const unsigned char *data = (PBYTE)_data; + if (!len) { return 0; } + assert(len > 0); + int x = 0; + if (!c->out_bytes) { + NotifyEventHooks(c->write_ev, (WPARAM)c); + } + if (!c->out_head) { + struct connection_buffer *b = new_connection_buffer(1 << 20); + c->out_head = c->out_tail = b; + } + while (len) { + if (c->out_tail->end - c->out_tail->wptr >= len) { + memcpy(c->out_tail->wptr, data, len); + c->out_tail->wptr += len; + c->out_bytes += len; + return x + len; + } + else { + int y = c->out_tail->end - c->out_tail->wptr; + assert(y < len); + memcpy(c->out_tail->wptr, data, y); + x += y; + len -= y; + data += y; + struct connection_buffer *b = new_connection_buffer(1 << 20); + c->out_tail->next = b; + b->next = 0; + c->out_tail = b; + c->out_bytes += y; + } + } + return x; +} + +int tgln_read_in(struct connection *c, void *_data, int len) { + unsigned char *data = (PBYTE)_data; + if (!len) { return 0; } + assert(len > 0); + if (len > c->in_bytes) { + len = c->in_bytes; + } + int x = 0; + while (len) { + int y = c->in_head->wptr - c->in_head->rptr; + if (y > len) { + memcpy(data, c->in_head->rptr, len); + c->in_head->rptr += len; + c->in_bytes -= len; + return x + len; + } + else { + memcpy(data, c->in_head->rptr, y); + c->in_bytes -= y; + x += y; + data += y; + len -= y; + void *old = c->in_head; + c->in_head = c->in_head->next; + if (!c->in_head) { + c->in_tail = 0; + } + delete_connection_buffer((connection_buffer*)old); + } + } + return x; +} + +int tgln_read_in_lookup(struct connection *c, void *_data, int len) { + unsigned char *data = (PBYTE)_data; + if (!len || !c->in_bytes) { return 0; } + assert(len > 0); + if (len > c->in_bytes) { + len = c->in_bytes; + } + int x = 0; + struct connection_buffer *b = c->in_head; + while (len) { + int y = b->wptr - b->rptr; + if (y >= len) { + memcpy(data, b->rptr, len); + return x + len; + } + else { + memcpy(data, b->rptr, y); + x += y; + data += y; + len -= y; + b = b->next; + } + } + return x; +} + +static void try_write(struct connection *c); + +void tgln_flush_out(struct connection *c) +{ +// try_write(c); +} + +#define MAX_CONNECTIONS 100 +static struct connection *Connections[MAX_CONNECTIONS]; +static int max_connection_fd; + +static void rotate_port(struct connection *c) { + switch (c->port) { + case 443: + c->port = 80; + break; + case 80: + c->port = 25; + break; + case 25: + c->port = 443; + break; + } +} + +static void try_read(struct connection *c); +static void try_write(struct connection *c); + + +void __stdcall conn_try_read(void *param, BYTE){ + struct connection *c = (connection*)param; + struct tgl_state *TLS = c->TLS; + vlogprintf(E_DEBUG + 1, "Try read. Fd = %d\n", c->hNetlibConn); + try_read(c); +} + +static int conn_try_write(WPARAM w, LPARAM) +{ + struct connection *c = (connection*)w; + struct tgl_state *TLS = c->TLS; + if (c->state == conn_connecting) { + c->state = conn_ready; + c->methods->ready(TLS, c); + } + try_write(c); + if (c->out_bytes) { + NotifyEventHooks(c->write_ev, (WPARAM)c); + } + return 0; +} + +struct connection *tgln_create_connection(struct tgl_state *TLS, const char *host, int port, struct tgl_session *session, struct tgl_dc *dc, struct mtproto_methods *methods) +{ + struct connection *c = new connection; + c->TLS = TLS; + c->ip = tstrdup(host); + c->port = port; + + + NETLIBOPENCONNECTION nloc = { sizeof(nloc) }; + nloc.szHost = c->ip; + nloc.wPort = port; + + if (!(c->hNetlibConn = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)((MirTLS*)TLS)->m_proto->m_hNetlibUser, (LPARAM)&nloc))) + { + delete c; + return 0; + } + + c->state = conn_connecting; + c->last_receive_time = tglt_get_double_time(); + c->flags = 0; + + char szTmp[2048] = "TGL_NET_TMPHOOK/"; + char *dst = &szTmp[strlen(szTmp)]; + MUUID uuid; + CoCreateGuid((GUID*)&uuid); + mir_snprintf(dst, 2048 - strlen(szTmp) - 1, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + uuid.a, uuid.b, uuid.c, uuid.d[0], uuid.d[1], uuid.d[2], uuid.d[3], uuid.d[4], uuid.d[5], uuid.d[6], uuid.d[7]); + + c->write_ev = CreateHookableEvent(szTmp); + HookEvent(szTmp, conn_try_write); + + + + + + //CoCreateGuid((GUID*)&uuid); + //mir_snprintf(dst, 2048 - strlen(szTmp) - 1, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + // uuid.a, uuid.b, uuid.c, uuid.d[0], uuid.d[1], uuid.d[2], uuid.d[3], uuid.d[4], uuid.d[5], uuid.d[6], uuid.d[7]); + // + //c->read_ev = CreateHookableEvent(szTmp); + //HookEvent(szTmp, conn_try_read); + + + start_ping_timer(c); + + c->dc = dc; + c->session = session; + c->methods = methods; + + char byte = 0xef; + assert(mtgln_write_out(c, &byte, 1) == 1); + tgln_flush_out(c); + + CreateTimerQueueTimer(&c->write_ev, hQueue, conn_try_read, c, 1, 5000, 0); + + return c; +} + +static void restart_connection(struct connection *c) +{ + struct tgl_state *TLS = c->TLS; + + if (c->last_connect_time == time(0)) { + start_fail_timer(c); + return; + } + + c->state = conn_connecting; + c->last_receive_time = tglt_get_double_time(); + + Netlib_CloseHandle(c->hNetlibConn); + + NETLIBOPENCONNECTION nloc = { sizeof(nloc) }; + nloc.szHost = c->ip; + nloc.wPort = c->port; + + c->hNetlibConn = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)((MirTLS*)TLS)->m_proto->m_hNetlibUser, (LPARAM)&nloc); + + char byte = 0xef; + assert(mtgln_write_out(c, &byte, 1) == 1); + tgln_flush_out(c); +} + +static void fail_connection(struct connection *c) +{ + struct tgl_state *TLS = c->TLS; + if (c->state == conn_ready || c->state == conn_connecting) { + stop_ping_timer(c); + } + +// DestroyHookableEvent(c->read_ev); + + rotate_port(c); + struct connection_buffer *b = c->out_head; + while (b) { + struct connection_buffer *d = b; + b = b->next; + delete_connection_buffer(d); + } + b = c->in_head; + while (b) { + struct connection_buffer *d = b; + b = b->next; + delete_connection_buffer(d); + } + c->out_head = c->out_tail = c->in_head = c->in_tail = 0; + c->state = conn_failed; + c->out_bytes = c->in_bytes = 0; + + Netlib_CloseHandle(c->hNetlibConn); + + vlogprintf(E_NOTICE, "Lost connection to server... %s:%d\n", c->ip, c->port); + restart_connection(c); +} + +//extern FILE *log_net_f; +static void try_write(struct connection *c) +{ + struct tgl_state *TLS = c->TLS; + vlogprintf(E_DEBUG, "try write: fd = %d\n", c->hNetlibConn); + int x = 0; + while (c->out_head) { + int r = Netlib_Send(c->hNetlibConn, (char*)c->out_head->rptr, c->out_head->wptr - c->out_head->rptr, MSG_RAW); + if (r != SOCKET_ERROR) { + + x += r; + c->out_head->rptr += r; + if (c->out_head->rptr != c->out_head->wptr) { + break; + } + struct connection_buffer *b = c->out_head; + c->out_head = b->next; + if (!c->out_head) { + c->out_tail = 0; + } + delete_connection_buffer(b); + } + else { + if (/*WSAGetLastError() != EAGAIN && */WSAGetLastError() != WSAEWOULDBLOCK) { + vlogprintf(E_NOTICE, "fail_connection: write_error %s\n", GetLastErrorStr(WSAGetLastError())); + fail_connection(c); + return; + } + else { + break; + } + } + } + vlogprintf(E_DEBUG, "Sent %d bytes to %d\n", x, c->hNetlibConn); + c->out_bytes -= x; +} + +static void try_rpc_read(struct connection *c) { + assert(c->in_head); + struct tgl_state *TLS = c->TLS; + + while (1) { + if (c->in_bytes < 1) { return; } + unsigned len = 0; + unsigned t = 0; + assert(tgln_read_in_lookup(c, &len, 1) == 1); + if (len >= 1 && len <= 0x7e) { + if (c->in_bytes < (int)(1 + 4 * len)) { return; } + } + else { + if (c->in_bytes < 4) { return; } + assert(tgln_read_in_lookup(c, &len, 4) == 4); + len = (len >> 8); + if (c->in_bytes < (int)(4 + 4 * len)) { return; } + len = 0x7f; + } + + if (len >= 1 && len <= 0x7e) { + assert(tgln_read_in(c, &t, 1) == 1); + assert(t == len); + assert(len >= 1); + } + else { + assert(len == 0x7f); + assert(tgln_read_in(c, &len, 4) == 4); + len = (len >> 8); + assert(len >= 1); + } + len *= 4; + int op; + assert(tgln_read_in_lookup(c, &op, 4) == 4); + if (c->methods->execute(TLS, c, op, len) < 0) { + return; + } + } +} + +static void try_read(struct connection *c) { + struct tgl_state *TLS = c->TLS; + vlogprintf(E_DEBUG, "try read: fd = %d\n", c->hNetlibConn); + if (!c->in_tail) { + c->in_head = c->in_tail = new_connection_buffer(1 << 20); + } +#ifdef EVENT_V1 + struct timeval tv = { 5, 0 }; + event_add(c->read_ev, &tv); +#endif + int x = 0; + while (1) { + int r = Netlib_Recv(c->hNetlibConn, (char*)c->in_tail->wptr, c->in_tail->end - c->in_tail->wptr, MSG_RAW); + if (r != SOCKET_ERROR) { + c->last_receive_time = tglt_get_double_time(); + stop_ping_timer(c); + start_ping_timer(c); + } + if (r >= 0) { + c->in_tail->wptr += r; + x += r; + if (c->in_tail->wptr != c->in_tail->end) { + break; + } + struct connection_buffer *b = new_connection_buffer(1 << 20); + c->in_tail->next = b; + c->in_tail = b; + } + else { + if (/*WSAGetLastError() != EAGAIN &&*/ WSAGetLastError() != WSAEWOULDBLOCK) { + vlogprintf(E_NOTICE, "fail_connection: read_error %s\n", GetLastErrorStr(WSAGetLastError())); + fail_connection(c); + return; + } + else { + break; + } + } + } + vlogprintf(E_DEBUG, "Received %d bytes from %d\n", x, c->hNetlibConn); + c->in_bytes += x; + if (x) { + try_rpc_read(c); + } +} + static void incr_out_packet_num(struct connection *c) { + c->out_packet_num++; } static struct tgl_dc *get_dc(struct connection *c) { @@ -87,28 +570,49 @@ static struct tgl_dc *get_dc(struct connection *c) { static struct tgl_session *get_session(struct connection *c) { return c->session; +} + +static void tgln_free(struct connection *c) { + if (c->ip) { tfree_str(c->ip); } + if (c->ping_ev) { } + if (c->fail_ev) { } +// if (c->read_ev) { DestroyHookableEvent(c->read_ev); } + if (c->write_ev) { DestroyHookableEvent(c->write_ev); } + + struct connection_buffer *b = c->out_head; + while (b) { + struct connection_buffer *d = b; + b = b->next; + delete_connection_buffer(d); + } + b = c->in_head; + while (b) { + struct connection_buffer *d = b; + b = b->next; + delete_connection_buffer(d); + } + + + if (c->hNetlibConn) Netlib_CloseHandle(c->hNetlibConn); + delete c; } -static void tgln_free(struct connection *c) -{ - Netlib_CloseHandle(c->hNetlibConn); - mir_free(c->ip); - delete c; -} -void mtgln_flush_out(struct connection *c) { -} -struct tgl_net_methods mtgl_conn_methods = { - mtgln_write_out, - mtgln_read_in, - mtgln_read_in_lookup, - mtgln_flush_out, - incr_out_packet_num, - tgln_free, - get_dc, - get_session, - mtgln_create_connection + + + + +struct tgl_net_methods mtgl_conn_methods = { + mtgln_write_out, + tgln_read_in, + tgln_read_in_lookup, + tgln_flush_out, + incr_out_packet_num, + tgln_free, + get_dc, + get_session, + tgln_create_connection }; void CTelegramProto::InitNetwork() diff --git a/protocols/Telegram/src/t_network.h b/protocols/Telegram/src/t_network.h index eaf90ed31d..3859f562e4 100644 --- a/protocols/Telegram/src/t_network.h +++ b/protocols/Telegram/src/t_network.h @@ -1,15 +1,48 @@ -struct connection +enum conn_state { + conn_none, + conn_connecting, + conn_ready, + conn_failed, + conn_stopped +}; + +struct connection : public MZeroedObject { - char *ip; - int port; - int flags; + char *ip; + int port; + int flags; enum conn_state state; - struct mtproto_methods *methods; - struct tgl_state *TLS; - struct tgl_session *session; + struct mtproto_methods *methods; + struct tgl_state *TLS; + struct tgl_session *session; struct tgl_dc *dc; void *extra; double last_receive_time; HANDLE hNetlibConn; HANDLE ping_timer; + HANDLE in_fail_timer; + + struct connection_buffer *in_head; + struct connection_buffer *in_tail; + struct connection_buffer *out_head; + struct connection_buffer *out_tail; + + int in_bytes; + int out_bytes; + int packet_num; + int out_packet_num; + + + HANDLE ping_ev, fail_ev, write_ev, read_ev; + + time_t last_connect_time; + +}; + +struct connection_buffer { + unsigned char *start; + unsigned char *end; + unsigned char *rptr; + unsigned char *wptr; + struct connection_buffer *next; }; \ No newline at end of file -- cgit v1.2.3