/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ /* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ /* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ /* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE.nxcomp which comes in the */ /* source distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ #include <sys/types.h> #include <sys/utsname.h> #if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) #include <netinet/in_systm.h> #endif #ifdef __sun #include <unistd.h> #include <sys/termios.h> #endif #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> // // System specific defines. // #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) #define SOL_IP IPPROTO_IP #endif #ifdef __sun #define INADDR_NONE ((unsigned int) -1) #endif // // The TIOCOUTQ ioctl is not implemented on Cygwin. // Note also that TIOCOUTQ and IPTOS_LOWDELAY are // disabled when running on MacOS/X. // #ifdef __CYGWIN32__ #define TIOCOUTQ ((unsigned int) -1) #endif // // NX includes. // #include "Misc.h" #include "Socket.h" // // Set verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Set this only once by querying OS details. // static int _kernelStep = -1; int GetKernelStep() { if (_kernelStep < 0) { // // At the moment only NX clients run on Win32 // and MacOS/X so we are not really interested // in the relevant OS dependent functions. // #if defined(__CYGWIN32__) || defined(__APPLE__) _kernelStep = 0; #else struct utsname buffer; if (uname(&buffer) < 0) { #ifdef WARNING *logofs << "Socket: WARNING! Failed to get system info. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; *logofs << "Socket: WARNING! Assuming lowest system support.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to get system info. Error is " << EGET() << " '" << ESTR() << "'.\n"; cerr << "Warning" << ": Assuming lowest system support.\n"; _kernelStep = 0; } else { #ifdef TEST *logofs << "Socket: System is '" << buffer.sysname << "' nodename '" << buffer.nodename << "' release '" << buffer.release << "'.\n" << logofs_flush; *logofs << "Socket: Version is '" << buffer.version << "' machine '" << buffer.machine << "'.\n" << logofs_flush; #endif // // Should test support on other operating systems. // if (strcmp(buffer.sysname, "Linux") == 0) { if (strncmp(buffer.release, "2.0.", 4) == 0 || strncmp(buffer.release, "2.2.", 4) == 0) { #ifdef TEST *logofs << "Socket: Assuming level 2 system support.\n" << logofs_flush; #endif _kernelStep = 2; } else { #ifdef TEST *logofs << "Socket: Assuming level 3 system support.\n" << logofs_flush; #endif _kernelStep = 3; } } else if (strcmp(buffer.sysname, "SunOS") == 0) { #ifdef TEST *logofs << "Socket: Assuming level 1 system support.\n" << logofs_flush; #endif _kernelStep = 1; } else { #ifdef TEST *logofs << "Socket: Assuming level 0 system support.\n" << logofs_flush; #endif _kernelStep = 0; } } #endif /* #if defined(__CYGWIN32__) || defined(__APPLE__) */ } return _kernelStep; } int SetReuseAddress(int fd) { int flag = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(flag)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_REUSEADDR flag on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_REUSEADDR flag on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_REUSEADDR flag on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } int SetNonBlocking(int fd, int value) { int flags = fcntl(fd, F_GETFL); if (flags >= 0) { if (value == 0) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; } } if (flags < 0 || fcntl(fd, F_SETFL, flags) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set O_NONBLOCK flag on FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set O_NONBLOCK flag on FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set O_NONBLOCK flag on FD#" << fd << " to " << value << ".\n" << logofs_flush; } #endif return 1; } int SetLingerTimeout(int fd, int timeout) { struct linger linger_value; if (timeout > 0) { linger_value.l_onoff = 1; linger_value.l_linger = timeout; } else { linger_value.l_onoff = 0; linger_value.l_linger = 0; } if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, sizeof(linger_value)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_LINGER values to " << linger_value.l_onoff << " and " << linger_value.l_linger << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_LINGER values to " << linger_value.l_onoff << " and " << linger_value.l_linger << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_LINGER values to " << linger_value.l_onoff << " and " << linger_value.l_linger << " on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } int SetSendBuffer(int fd, int size) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_SNDBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_SNDBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_SNDBUF on FD#" << fd << " to " << size << " bytes.\n" << logofs_flush; } #endif return 1; } int SetReceiveBuffer(int fd, int size) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_RCVBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_RCVBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_RCVBUF on FD#" << fd << " to " << size << " bytes.\n" << logofs_flush; } #endif return 1; } int SetNoDelay(int fd, int value) { int result = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); if (result == 0) { result = 1; } else if (result < 0) { // // Is it become a different error on // Mac OSX 10.4? // #if defined(__APPLE__) result = 0; #endif #if defined(__sun) if (EGET() == ENOPROTOOPT) { result = 0; } #endif #if !defined(__APPLE__) && !defined(__sun) if (EGET() == EOPNOTSUPP) { result = 0; } #endif } if (result < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set TCP_NODELAY flag on " << "FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set TCP_NODELAY flag on " << "FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; } #ifdef TEST else if (result == 0) { #ifdef TEST *logofs << "Socket: Option TCP_NODELAY not supported " << "on FD#" << fd << ".\n" << logofs_flush; #endif } else { *logofs << "Socket: Set TCP_NODELAY flag on FD#" << fd << " to " << value << ".\n" << logofs_flush; } #endif return result; } int SetKeepAlive(int fd) { int flag = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_KEEPALIVE flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_KEEPALIVE flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_KEEPALIVE flag on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } int SetLowDelay(int fd) { if (_kernelStep < 0) { GetKernelStep(); } switch (_kernelStep) { case 3: case 2: case 1: { int flag = IPTOS_LOWDELAY; if (setsockopt(fd, SOL_IP, IP_TOS, &flag, sizeof(flag)) < 0) { if (EGET() == EOPNOTSUPP) { #ifdef TEST *logofs << "Socket: Option IPTOS_LOWDELAY not supported " << "on FD#" << fd << ".\n" << logofs_flush; #endif return 0; } else { #ifdef WARNING *logofs << "Socket: WARNING! Failed to set IPTOS_LOWDELAY flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to set IPTOS_LOWDELAY flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } } #ifdef TEST else { *logofs << "Socket: Set IPTOS_LOWDELAY flag on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } default: { #ifdef TEST *logofs << "Socket: Option IPTOS_LOWDELAY not " << "supported on FD#" << fd << ".\n" << logofs_flush; #endif return 0; } } } int SetCloseOnExec(int fd) { if (fcntl(fd, F_SETFD, 1) != 0) { #ifdef TEST *logofs << "NXClient: PANIC! Cannot set close-on-exec " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot set close-on-exec on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } return 1; } int GetBytesReadable(int fd) { long readable = 0; // // It may fail, for example at session // shutdown. // if (ioctl(fd, FIONREAD, &readable) < 0) { #ifdef TEST *logofs << "Socket: PANIC! Failed to get bytes readable " << "from FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "Socket: Returning " << (int) readable << " bytes readable on FD#" << fd << ".\n" << logofs_flush; #endif return (int) readable; } int GetBytesWritable(int fd) { if (_kernelStep < 0) { GetKernelStep(); } long writable; switch (_kernelStep) { case 3: { // // TODO: Should query the real size // of the TCP write buffer. // writable = 16384 - GetBytesQueued(fd); if (writable < 0) { writable = 0; } break; } case 2: { if (ioctl(fd, TIOCOUTQ, (void *) &writable) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to get bytes writable " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to get bytes writable " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } break; } default: { #ifdef TEST *logofs << "Socket: Option TIOCOUTQ not supported " << "on FD#" << fd << ",\n" << logofs_flush; #endif // // TODO: Should query the real size // of the TCP write buffer. // writable = 16384; break; } } #ifdef TEST *logofs << "Socket: Returning " << writable << " bytes writable on FD#" << fd << ".\n" << logofs_flush; #endif return (int) writable; } int GetBytesQueued(int fd) { // // The TIOCOUTQ ioctl is not implemented on Cygwin // and returns the space available on Linux Kernels // 2.0 and 2.2 (like current MIPS for PS/2). // if (_kernelStep < 0) { GetKernelStep(); } long queued; switch (_kernelStep) { case 3: { if (ioctl(fd, TIOCOUTQ, (void *) &queued) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to get bytes queued " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to get bytes queued " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } break; } case 2: { // // TODO: Should query the real size // of the TCP write buffer. // queued = 16384 - GetBytesWritable(fd); if (queued < 0) { queued = 0; } break; } default: { #ifdef TEST *logofs << "Socket: Option TIOCOUTQ not supported " << "on FD#" << fd << ",\n" << logofs_flush; #endif queued = 0; break; } } #ifdef TEST *logofs << "Socket: Returning " << queued << " bytes queued on FD#" << fd << ".\n" << logofs_flush; #endif return (int) queued; } int GetHostAddress(const char *name) { hostent *host = gethostbyname(name); if (host == NULL) { // // On some Unices gethostbyname() doesn't // accept IP addresses, so try inet_addr. // IN_ADDR_T address = inet_addr(name); if (address == INADDR_NONE) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to resolve address of '" << name << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to resolve address of '" << name << "'.\n"; return 0; } return (int) address; } else { return (*((int *) host -> h_addr_list[0])); } }