diff --git a/tcp.c b/tcp.c index d82803c..891164c 100644 --- a/tcp.c +++ b/tcp.c @@ -2,12 +2,12 @@ rdesktop: A Remote Desktop Protocol client. Protocol services - TCP layer Copyright (C) Matthew Chapman 1999-2005 - + 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 2 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 @@ -18,6 +18,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifndef _WIN32 #include /* select read write close */ #include /* socket connect setsockopt */ #include /* timeval */ @@ -26,19 +27,52 @@ #include /* TCP_NODELAY */ #include /* inet_addr */ #include /* errno */ +#endif + #include "rdesktop.h" +#ifdef _WIN32 +#define socklen_t int +#define TCP_CLOSE(_sck) closesocket(_sck) +#define TCP_STRERROR "tcp error" +#define TCP_BLOCKS (WSAGetLastError() == WSAEWOULDBLOCK) +#else +#define TCP_CLOSE(_sck) close(_sck) +#define TCP_STRERROR strerror(errno) +#define TCP_BLOCKS (errno == EWOULDBLOCK) +#endif + #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long) -1) #endif -static int sock; -static struct stream in; +static int g_sock; +static struct stream g_in; #ifndef WITH_SCARD -static struct stream out; +static struct stream g_out; #endif int g_tcp_port_rdp = TCP_PORT_RDP; +/* wait till socket is ready to write or timeout */ +static BOOL +tcp_can_send(int sck, int millis) +{ + fd_set wfds; + struct timeval time; + int sel_count; + + time.tv_sec = millis / 1000; + time.tv_usec = (millis * 1000) % 1000000; + FD_ZERO(&wfds); + FD_SET(sck, &wfds); + sel_count = select(sck + 1, 0, &wfds, 0, &time); + if (sel_count > 0) + { + return True; + } + return False; +} + /* Initialise TCP transport data packet */ STREAM tcp_init(uint32 maxlen) @@ -49,7 +83,7 @@ tcp_init(uint32 maxlen) scard_lock(SCARD_LOCK_TCP); result = scard_tcp_init(); #else - result = &out; + result = &g_out; #endif if (maxlen > result->size) @@ -78,13 +112,20 @@ tcp_send(STREAM s) #endif while (total < length) { - sent = send(sock, s->data + total, length - total, 0); + sent = send(g_sock, s->data + total, length - total, 0); if (sent <= 0) { - error("send: %s\n", strerror(errno)); - return; + if (sent == -1 && TCP_BLOCKS) + { + tcp_can_send(g_sock, 100); + sent = 0; + } + else + { + error("send: %s\n", TCP_STRERROR); + return; + } } - total += sent; } #ifdef WITH_SCARD @@ -96,19 +137,19 @@ tcp_send(STREAM s) STREAM tcp_recv(STREAM s, uint32 length) { - unsigned int new_length, end_offset, p_offset; + uint32 new_length, end_offset, p_offset; int rcvd = 0; if (s == NULL) { /* read into "new" stream */ - if (length > in.size) + if (length > g_in.size) { - in.data = (uint8 *) xrealloc(in.data, length); - in.size = length; + g_in.data = (uint8 *) xrealloc(g_in.data, length); + g_in.size = length; } - in.end = in.p = in.data; - s = ∈ + g_in.end = g_in.p = g_in.data; + s = &g_in; } else { @@ -127,15 +168,22 @@ tcp_recv(STREAM s, uint32 length) while (length > 0) { - if (!ui_select(sock)) + if (!ui_select(g_sock)) /* User quit */ return NULL; - rcvd = recv(sock, s->end, length, 0); + rcvd = recv(g_sock, s->end, length, 0); if (rcvd < 0) { - error("recv: %s\n", strerror(errno)); - return NULL; + if (rcvd == -1 && TCP_BLOCKS) + { + rcvd = 0; + } + else + { + error("recv: %s\n", TCP_STRERROR); + return NULL; + } } else if (rcvd == 0) { @@ -154,7 +202,8 @@ tcp_recv(STREAM s, uint32 length) BOOL tcp_connect(char *server) { - int true_value = 1; + socklen_t option_len; + uint32 option_value; #ifdef IPv6 @@ -175,22 +224,22 @@ tcp_connect(char *server) } ressave = res; - sock = -1; + g_sock = -1; while (res) { - sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (!(sock < 0)) + g_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (!(g_sock < 0)) { - if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) + if (connect(g_sock, res->ai_addr, res->ai_addrlen) == 0) break; - close(sock); - sock = -1; + TCP_CLOSE(g_sock); + g_sock = -1; } res = res->ai_next; } freeaddrinfo(ressave); - if (sock == -1) + if (g_sock == -1) { error("%s: unable to connect\n", server); return False; @@ -211,34 +260,46 @@ tcp_connect(char *server) return False; } - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + if ((g_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - error("socket: %s\n", strerror(errno)); + error("socket: %s\n", TCP_STRERROR); return False; } servaddr.sin_family = AF_INET; - servaddr.sin_port = htons(g_tcp_port_rdp); + servaddr.sin_port = htons((uint16) g_tcp_port_rdp); - if (connect(sock, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) < 0) + if (connect(g_sock, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) < 0) { - error("connect: %s\n", strerror(errno)); - close(sock); + error("connect: %s\n", TCP_STRERROR); + TCP_CLOSE(g_sock); return False; } #endif /* IPv6 */ - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &true_value, sizeof(true_value)); + option_value = 1; + option_len = sizeof(option_value); + setsockopt(g_sock, IPPROTO_TCP, TCP_NODELAY, (void *) &option_value, option_len); + /* receive buffer must be a least 16 K */ + if (getsockopt(g_sock, SOL_SOCKET, SO_RCVBUF, (void *) &option_value, &option_len) == 0) + { + if (option_value < (1024 * 16)) + { + option_value = 1024 * 16; + option_len = sizeof(option_value); + setsockopt(g_sock, SOL_SOCKET, SO_RCVBUF, (void *) &option_value, option_len); + } + } - in.size = 4096; - in.data = (uint8 *) xmalloc(in.size); + g_in.size = 4096; + g_in.data = (uint8 *) xmalloc(g_in.size); #ifdef WITH_SCARD scard_tcp_connect(); #else - out.size = 4096; - out.data = (uint8 *) xmalloc(out.size); + g_out.size = 4096; + g_out.data = (uint8 *) xmalloc(g_out.size); #endif return True; @@ -248,7 +309,7 @@ tcp_connect(char *server) void tcp_disconnect(void) { - close(sock); + TCP_CLOSE(g_sock); } char * @@ -257,9 +318,9 @@ tcp_get_address() static char ipaddr[32]; struct sockaddr_in sockaddr; socklen_t len = sizeof(sockaddr); - if (getsockname(sock, (struct sockaddr *) &sockaddr, &len) == 0) + if (getsockname(g_sock, (struct sockaddr *) &sockaddr, &len) == 0) { - unsigned char *ip = (unsigned char *) &sockaddr.sin_addr; + uint8 *ip = (uint8 *) &sockaddr.sin_addr; sprintf(ipaddr, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); } else @@ -272,35 +333,35 @@ tcp_get_address() void tcp_reset_state(void) { - sock = -1; /* reset socket */ + g_sock = -1; /* reset socket */ /* Clear the incoming stream */ - if (in.data != NULL) - xfree(in.data); - in.p = NULL; - in.end = NULL; - in.data = NULL; - in.size = 0; - in.iso_hdr = NULL; - in.mcs_hdr = NULL; - in.sec_hdr = NULL; - in.rdp_hdr = NULL; - in.channel_hdr = NULL; + if (g_in.data != NULL) + xfree(g_in.data); + g_in.p = NULL; + g_in.end = NULL; + g_in.data = NULL; + g_in.size = 0; + g_in.iso_hdr = NULL; + g_in.mcs_hdr = NULL; + g_in.sec_hdr = NULL; + g_in.rdp_hdr = NULL; + g_in.channel_hdr = NULL; /* Clear the outgoing stream(s) */ #ifdef WITH_SCARD scard_tcp_reset_state(); #else - if (out.data != NULL) - xfree(out.data); - out.p = NULL; - out.end = NULL; - out.data = NULL; - out.size = 0; - out.iso_hdr = NULL; - out.mcs_hdr = NULL; - out.sec_hdr = NULL; - out.rdp_hdr = NULL; - out.channel_hdr = NULL; + if (g_out.data != NULL) + xfree(g_out.data); + g_out.p = NULL; + g_out.end = NULL; + g_out.data = NULL; + g_out.size = 0; + g_out.iso_hdr = NULL; + g_out.mcs_hdr = NULL; + g_out.sec_hdr = NULL; + g_out.rdp_hdr = NULL; + g_out.channel_hdr = NULL; #endif }