From c6e9565127d5980d9f57928b36e8ca424197ca9e Mon Sep 17 00:00:00 2001 From: Mike Gabriel Date: Wed, 15 Jun 2016 10:29:12 +0200 Subject: nxcomp/Loop.cpp: Add Unix file socket support for proxy <-> proxy connection. --- nxcomp/ChannelEndPoint.cpp | 143 ++++++++-- nxcomp/ChannelEndPoint.h | 11 +- nxcomp/Loop.cpp | 675 ++++++++++++++++++++++++++++++++------------- 3 files changed, 607 insertions(+), 222 deletions(-) diff --git a/nxcomp/ChannelEndPoint.cpp b/nxcomp/ChannelEndPoint.cpp index cb35e52d2..3e3053927 100644 --- a/nxcomp/ChannelEndPoint.cpp +++ b/nxcomp/ChannelEndPoint.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "ChannelEndPoint.h" @@ -31,8 +32,21 @@ ChannelEndPoint::ChannelEndPoint(const char *spec) : defaultTCPPort_(0), defaultTCPInterface_(0), - defaultUnixPath_(NULL) { - spec_ = (spec ? strdup(spec) : NULL); + defaultUnixPath_(NULL), spec_(NULL) { + setSpec(spec); +} + +ChannelEndPoint::~ChannelEndPoint() +{ + char *unixPath = NULL; + + if (getUnixPath(&unixPath)) + { + struct stat st; + lstat(unixPath, &st); + if(S_ISSOCK(st.st_mode)) + unlink(unixPath); + } } void @@ -40,21 +54,92 @@ ChannelEndPoint::setSpec(const char *spec) { if (spec_) free(spec_); if (spec && strlen(spec)) + { spec_ = strdup(spec); + isUnix_ = getUnixPath(); + isTCP_ = getTCPHostAndPort(); + } else + { spec_ = NULL; + isUnix_ = false; + isTCP_ = false; + } } void -ChannelEndPoint::setSpec(int port) { +ChannelEndPoint::setSpec(long port) { if (port >= 0) { char tmp[20]; - sprintf(tmp, "%d", port); + sprintf(tmp, "%ld", port); setSpec(tmp); } + else { + disable(); + } +} + +void +ChannelEndPoint::setSpec(const char *hostName, long port) { + int length; + + if (spec_) free(spec_); + isUnix_ = false; + isTCP_ = false; + + if (hostName && strlen(hostName) && port >= 1) + { + length = snprintf(NULL, 0, "tcp:%s:%ld", hostName, port); + spec_ = (char *)calloc(length + 1, sizeof(char)); + snprintf(spec_, length+1, "tcp:%s:%ld", hostName, port); + isTCP_ = true; + } else setSpec((char*)NULL); } +bool +ChannelEndPoint::getSpec(char **socketUri) const { + + if (socketUri) *socketUri = NULL; + + char *unixPath = NULL; + char *hostName = NULL; + long port = -1; + + char *newSocketUri = NULL; + int length = -1; + + if (getUnixPath(&unixPath)) + { + length = snprintf(NULL, 0, "unix:%s", unixPath); + } + else if (getTCPHostAndPort(&hostName, &port)) + { + length = snprintf(NULL, 0, "tcp:%s:%ld", hostName, port); + } + + if (length > 0) + { + newSocketUri = (char *)calloc(length + 1, sizeof(char)); + if (isUnixSocket()) + snprintf(newSocketUri, length+1, "unix:%s", unixPath); + else + snprintf(newSocketUri, length+1, "tcp:%s:%ld", hostName, port); + + if (socketUri) + *socketUri = strdup(newSocketUri); + } + + free(newSocketUri); + free(unixPath); + free(hostName); + + if (*socketUri != '\0') + return true; + + return false; +} + void ChannelEndPoint::setDefaultTCPPort(long port) { defaultTCPPort_ = port; @@ -76,10 +161,12 @@ ChannelEndPoint::setDefaultUnixPath(char *path) { } void -ChannelEndPoint::disable() { setSpec("0"); } +ChannelEndPoint::disable() { + setSpec("0"); +} bool -ChannelEndPoint::specIsPort(long *port) const { +ChannelEndPoint::getPort(long *port) const { if (port) *port = 0; long p = -1; if (spec_) { @@ -101,7 +188,7 @@ ChannelEndPoint::getUnixPath(char **unixPath) const { long p; char *path = NULL; - if (specIsPort(&p)) { + if (getPort(&p)) { if (p != 1) return false; } else if (spec_ && (strncmp("unix:", spec_, 5) == 0)) { @@ -122,6 +209,11 @@ ChannelEndPoint::getUnixPath(char **unixPath) const { return true; } +bool +ChannelEndPoint::isUnixSocket() const { + return isUnix_; +} + // FIXME!!! static const char * getComputerName() { @@ -158,7 +250,7 @@ ChannelEndPoint::getTCPHostAndPort(char **host, long *port) const { if (host) *host = NULL; if (port) *port = 0; - if (specIsPort(&p)) { + if (getPort(&p)) { h_len = 0; } else if (spec_ && (strncmp("tcp:", spec_, 4) == 0)) { @@ -194,8 +286,8 @@ ChannelEndPoint::getTCPHostAndPort(char **host, long *port) const { } bool -ChannelEndPoint::enabled() const { - return (getUnixPath() || getTCPHostAndPort()); +ChannelEndPoint::isTCPSocket() const { + return isTCP_; } long ChannelEndPoint::getTCPPort() const { @@ -204,9 +296,16 @@ long ChannelEndPoint::getTCPPort() const { return -1; } +bool +ChannelEndPoint::enabled() const { + return (isUnixSocket() || isTCPSocket()); +} + bool ChannelEndPoint::validateSpec() { - return (specIsPort() || getUnixPath() || getTCPHostAndPort()); + isTCP_ = getTCPHostAndPort(); + isUnix_ = getUnixPath(); + return ( getPort() || isUnix_ || isTCP_ ); } ChannelEndPoint &ChannelEndPoint::operator=(const ChannelEndPoint &other) { @@ -219,26 +318,24 @@ ChannelEndPoint &ChannelEndPoint::operator=(const ChannelEndPoint &other) { old = spec_; spec_ = (other.spec_ ? strdup(other.spec_) : NULL); free(old); + isUnix_ = getUnixPath(); + isTCP_ = getTCPHostAndPort(); return *this; } std::ostream& operator<<(std::ostream& os, const ChannelEndPoint& endPoint) { if (endPoint.enabled()) { - char *unixPath, *host; - long port; - if (endPoint.getUnixPath(&unixPath)) { - os << "unix:" << unixPath; - free(unixPath); + char* endPointSpec = NULL; + if (endPoint.getSpec(&endPointSpec)) + { + os << endPointSpec; + free(endPointSpec); } - else if (endPoint.getTCPHostAndPort(&host, &port)) { - os << "tcp:" << host << ":" << port; - free(host); - } - else { + else os << "(invalid)"; - } } - else { + else + { os << "(disabled)"; } return os; diff --git a/nxcomp/ChannelEndPoint.h b/nxcomp/ChannelEndPoint.h index 5b4e75345..901952e37 100644 --- a/nxcomp/ChannelEndPoint.h +++ b/nxcomp/ChannelEndPoint.h @@ -33,25 +33,32 @@ class ChannelEndPoint int defaultTCPInterface_; // 0=localhost, otherwise IP of public interface. char *defaultUnixPath_; char *spec_; + bool isUnix_; + bool isTCP_; - bool specIsPort(long *port = NULL) const; + bool getPort(long *port = NULL) const; public: ChannelEndPoint(const char *spec = NULL); + ~ChannelEndPoint(); ChannelEndPoint &operator=(const ChannelEndPoint &other); bool enabled() const; bool disabled() { return !enabled(); } void disable(); void setSpec(const char *spec); - void setSpec(int port); + void setSpec(long port); + void setSpec(const char *hostName, long port); + bool getSpec(char **socketUri) const; void setDefaultTCPPort(long port); void setDefaultTCPInterface(int publicInterface); void setDefaultUnixPath(char *path); bool getUnixPath(char **path = NULL) const; + bool isUnixSocket() const; bool getTCPHostAndPort(char **hostname = NULL, long *port = NULL) const; long getTCPPort() const; + bool isTCPSocket() const; bool validateSpec(); }; diff --git a/nxcomp/Loop.cpp b/nxcomp/Loop.cpp index 450c477c6..6bec5e30d 100644 --- a/nxcomp/Loop.cpp +++ b/nxcomp/Loop.cpp @@ -239,7 +239,7 @@ struct sockaddr_un // should connect to remote. // -#define WE_INITIATE_CONNECTION (*connectHost != '\0') +#define WE_INITIATE_CONNECTION (connectSocket.enabled()) // // Is true if we must provide our credentials @@ -255,7 +255,7 @@ struct sockaddr_un // #define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \ - listenPort != -1) + listenSocket.enabled()) // // You must define FLUSH in Misc.h if @@ -440,7 +440,7 @@ static int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr, static int ListenConnection(ChannelEndPoint &endPoint, const char *label); static int ListenConnectionTCP(const char *host, long port, const char *label); -static int ListenConnectionUnix(const char *unixPath, const char *label); +static int ListenConnectionUnix(const char *path, const char *label); static int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label); static int AcceptConnection(int fd, int domain, const char *label); @@ -448,8 +448,11 @@ static int AcceptConnection(int fd, int domain, const char *label); // Other convenience functions. // -static int WaitForRemote(int portNum); -static int ConnectToRemote(const char *const hostName, int portNum); +static int PrepareProxyConnectionTCP(char** hostName, long int* portNum, int* timeout, int* proxyFD, int* reason); +static int PrepareProxyConnectionUnix(char** path, int* timeout, int* proxyFD, int* reason); + +static int WaitForRemote(ChannelEndPoint &socketAddress); +static int ConnectToRemote(ChannelEndPoint &socketAddress); static int SendProxyOptions(int fd); static int SendProxyCaches(int fd); @@ -514,7 +517,7 @@ static int ParsePackOption(const char *opt); // given on the command line. // -static int ParseHostOption(const char *opt, char *host, int &port); +static int ParseHostOption(const char *opt, char *host, long &port); // // Translate a font server port specification @@ -944,7 +947,6 @@ static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 }; // Other parameters. // -static char connectHost[DEFAULT_STRING_LENGTH] = { 0 }; static char acceptHost[DEFAULT_STRING_LENGTH] = { 0 }; static char displayHost[DEFAULT_STRING_LENGTH] = { 0 }; static char authCookie[DEFAULT_STRING_LENGTH] = { 0 }; @@ -963,13 +965,19 @@ static sockaddr *xServerAddr = NULL; static unsigned int xServerAddrLength = 0; // -// The port where the local proxy will await -// the peer connection or where the remote -// proxy will be contacted. +// The representation of a Unix socket path or +// a bind address, denoting where the local proxy +// will await the peer connection. +// + +static ChannelEndPoint listenSocket; + +// +// The TCP host and port or Unix file socket where +// the remote proxy will be contacted. // -static int listenPort = -1; -static int connectPort = -1; +static ChannelEndPoint connectSocket; // // Helper channels are disabled by default. @@ -3355,58 +3363,88 @@ int InitBeforeNegotiation() int SetupProxyConnection() { + if (proxyFD == -1) { + + char *socketUri = NULL; + + // Let's make sure, the default value for listenSocket is properly set. Doing this + // here, because we have to make sure that we call it after the connectSocket + // declaration is really really complete. + + if (listenSocket.disabled() && connectSocket.disabled()) + { + char listenPortValue[20] = { 0 }; + sprintf(listenPortValue, "%ld", (long)(proxyPort + DEFAULT_NX_PROXY_PORT_OFFSET)); + + SetAndValidateChannelEndPointArg("local", "listen", listenPortValue, listenSocket); + } + + #ifdef TEST + connectSocket.getSpec(&socketUri); + *logofs << "Loop: connectSocket is "<< ( connectSocket.enabled() ? "enabled" : "disabled") << ". " + << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "") << "'.\n" << logofs_flush; + listenSocket.getSpec(&socketUri); + *logofs << "Loop: listenSocket is "<< ( listenSocket.enabled() ? "enabled" : "disabled") << ". " + << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "") << "'.\n" << logofs_flush; + free(socketUri); + socketUri = NULL; + #endif + if (WE_INITIATE_CONNECTION) { - if (connectPort < 0) + if (connectSocket.getSpec(&socketUri)) { - connectPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort; - } - - #ifdef TEST - *logofs << "Loop: Going to connect to " << connectHost - << ":" << connectPort << ".\n" << logofs_flush; - #endif + #ifdef TEST + *logofs << "Loop: Going to connect to '" << socketUri + << "'.\n" << logofs_flush; + #endif + free(socketUri); - proxyFD = ConnectToRemote(connectHost, connectPort); + proxyFD = ConnectToRemote(connectSocket); - #ifdef TEST - *logofs << "Loop: Connected to remote proxy on FD#" - << proxyFD << ".\n" << logofs_flush; - #endif + #ifdef TEST + *logofs << "Loop: Connected to remote proxy on FD#" + << proxyFD << ".\n" << logofs_flush; + #endif - cerr << "Info" << ": Connection to remote proxy '" << connectHost - << ":" << connectPort << "' established.\n"; + cerr << "Info" << ": Connected to remote proxy on FD#" + << proxyFD << ".\n"; + } } else { - if (listenPort < 0) + + if (listenSocket.isTCPSocket() && (listenSocket.getTCPPort() < 0)) { - listenPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort; + listenSocket.setSpec(DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort); } - #ifdef TEST - *logofs << "Loop: Going to wait for connection on port " - << listenPort << ".\n" << logofs_flush; - #endif + if (listenSocket.getSpec(&socketUri)) + { + #ifdef TEST + *logofs << "Loop: Going to wait for connection at '" + << socketUri << "'.\n" << logofs_flush; + #endif + free(socketUri); - proxyFD = WaitForRemote(listenPort); + proxyFD = WaitForRemote(listenSocket); - #ifdef TEST + #ifdef TEST + if (WE_LISTEN_FORWARDER) + { + *logofs << "Loop: Connected to remote forwarder on FD#" + << proxyFD << ".\n" << logofs_flush; + } + else + { + *logofs << "Loop: Connected to remote proxy on FD#" + << proxyFD << ".\n" << logofs_flush; + } + #endif - if (WE_LISTEN_FORWARDER) - { - *logofs << "Loop: Connected to remote forwarder on FD#" - << proxyFD << ".\n" << logofs_flush; } - else - { - *logofs << "Loop: Connected to remote proxy on FD#" - << proxyFD << ".\n" << logofs_flush; - } - - #endif } } #ifdef TEST @@ -3422,8 +3460,11 @@ int SetupProxyConnection() // to reduce startup time. Option will // later be disabled if needed. // + // either listenSocket or connectSocket is used here... + + if(listenSocket.isTCPSocket() || connectSocket.isTCPSocket()) - SetNoDelay(proxyFD, 1); + SetNoDelay(proxyFD, 1); // // We need non-blocking input since the @@ -4404,11 +4445,13 @@ int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label) goto SetupSocketError; } - if (SetReuseAddress(newFD) < 0) - { - // SetReuseAddress already warns with an error - goto SetupSocketError; - } + + if (addr->sa_family == AF_INET) + if (SetReuseAddress(newFD) < 0) + { + // SetReuseAddress already warns with an error + goto SetupSocketError; + } if (bind(newFD, addr, addrlen) == -1) { @@ -4427,12 +4470,12 @@ int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label) if (listen(newFD, 8) == -1) { #ifdef PANIC - *logofs << "Loop: PANIC! Call to bind failed for " << label + *logofs << "Loop: PANIC! Call to listen failed for " << label << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif - cerr << "Error" << ": Call to bind failed for " << label + cerr << "Error" << ": Call to listen failed for " << label << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; @@ -5445,7 +5488,6 @@ void CleanupLocal() *unixSocketName = '\0'; - *connectHost = '\0'; *acceptHost = '\0'; *displayHost = '\0'; *authCookie = '\0'; @@ -5460,8 +5502,8 @@ void CleanupLocal() xServerAddr = NULL; - listenPort = -1; - connectPort = -1; + listenSocket.disable(); + connectSocket.disable(); cupsPort.disable(); auxPort.disable(); @@ -6592,62 +6634,79 @@ void ResetTimer() } // -// Open TCP socket to listen for remote proxy and -// block until remote connects. If successful close -// the listening socket and return FD on which the -// other party is connected. +// Open TCP or UNIX file socket to listen for remote proxy +// and block until remote connects. If successful close +// the listening socket and return FD on which the other +// party is connected. // -int WaitForRemote(int portNum) +int WaitForRemote(ChannelEndPoint &socketAddress) { char hostLabel[DEFAULT_STRING_LENGTH] = { 0 }; + char *socketUri = NULL; int retryAccept = -1; int proxyFD = -1; int newFD = -1; - // - // Get IP address of host to be awaited. - // - int acceptIPAddr = 0; - if (*acceptHost != '\0') + if (socketAddress.isTCPSocket()) { - acceptIPAddr = GetHostAddress(acceptHost); - if (acceptIPAddr == 0) + // + // Get IP address of host to be awaited. + // + + if (*acceptHost != '\0') { - #ifdef PANIC - *logofs << "Loop: PANIC! Cannot accept connections from unknown host '" - << acceptHost << "'.\n" << logofs_flush; - #endif + acceptIPAddr = GetHostAddress(acceptHost); - cerr << "Error" << ": Cannot accept connections from unknown host '" - << acceptHost << "'.\n"; + if (acceptIPAddr == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Cannot accept connections from unknown host '" + << acceptHost << "'.\n" << logofs_flush; + #endif - goto WaitForRemoteError; + cerr << "Error" << ": Cannot accept connections from unknown host '" + << acceptHost << "'.\n"; + + goto WaitForRemoteError; + } + strcpy(hostLabel, "any host"); + } + else + { + snprintf(hostLabel, sizeof(hostLabel), "'%s'", acceptHost); + } + + if (loopbackBind) + { + long bindPort; + if (socketAddress.getTCPHostAndPort(NULL, &bindPort)) + socketAddress.setSpec("localhost", bindPort); } - strcpy(hostLabel, "any host"); } + else if (socketAddress.isUnixSocket()) + strcpy(hostLabel, "this host"); else - { - snprintf(hostLabel, sizeof(hostLabel), "'%s'", acceptHost); - } + strcpy(hostLabel, "unknown origin (something went wrong!!!)"); + - proxyFD = ListenConnectionTCP(((loopbackBind || (control->ProxyMode == proxy_server)) ? "localhost" : "*"), - portNum, "NX"); + proxyFD = ListenConnection(socketAddress, "NX"); + socketAddress.getSpec(&socketUri); #ifdef TEST *logofs << "Loop: Waiting for connection from " - << hostLabel << " on port '" << portNum + << hostLabel << " on socket '" << socketUri << "'.\n" << logofs_flush; #endif - cerr << "Info" << ": Waiting for connection from " - << hostLabel << " on port '" << portNum + << hostLabel << " on socket '" << socketUri << "'.\n"; + free(socketUri); // // How many times to loop waiting for connections @@ -6702,12 +6761,19 @@ int WaitForRemote(int portNum) } else if (result > 0 && FD_ISSET(proxyFD, &readSet)) { - sockaddr_in newAddr; - - socklen_t addrLen = sizeof(sockaddr_in); - newFD = accept(proxyFD, (sockaddr *) &newAddr, &addrLen); + sockaddr_in newAddrINET; + if (socketAddress.isUnixSocket()) + { + socklen_t addrLen = sizeof(sockaddr_un); + newFD = accept(proxyFD, NULL, &addrLen); + } + else if (socketAddress.isTCPSocket()) + { + socklen_t addrLen = sizeof(sockaddr_in); + newFD = accept(proxyFD, (sockaddr *) &newAddrINET, &addrLen); + } if (newFD == -1) { #ifdef PANIC @@ -6722,47 +6788,82 @@ int WaitForRemote(int portNum) goto WaitForRemoteError; } - char *connectedHost = inet_ntoa(newAddr.sin_addr); - - if (*acceptHost == '\0' || (int) newAddr.sin_addr.s_addr == acceptIPAddr) + if (socketAddress.isUnixSocket()) { - #ifdef TEST - - unsigned int connectedPort = ntohs(newAddr.sin_port); - *logofs << "Loop: Accepted connection from '" << connectedHost - << "' with port '" << connectedPort << "'.\n" + char * unixPath = NULL; + socketAddress.getUnixPath(&unixPath); + #ifdef TEST + *logofs << "Loop: Accepted connection from this host on Unix file socket '" + << unixPath << "'.\n" << logofs_flush; #endif - cerr << "Info" << ": Accepted connection from '" - << connectedHost << "'.\n"; + cerr << "Info" << ": Accepted connection from this host on Unix file socket '" + << unixPath << "'.\n"; + free(unixPath); break; } - else + else if (socketAddress.isTCPSocket()) { - #ifdef PANIC - *logofs << "Loop: WARNING! Refusing connection from '" << connectedHost - << "' on port '" << portNum << "'.\n" << logofs_flush; - #endif - cerr << "Warning" << ": Refusing connection from '" - << connectedHost << "'.\n"; - } + char *connectedHost = inet_ntoa(newAddrINET.sin_addr); - // - // Not the best way to elude a DOS attack... - // + if (*acceptHost == '\0' || (int) newAddrINET.sin_addr.s_addr == acceptIPAddr) + { + + #ifdef TEST - sleep(5); + unsigned int connectedPort = ntohs(newAddrINET.sin_port); + + *logofs << "Loop: Accepted connection from '" << connectedHost + << "' with port '" << connectedPort << "'.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Accepted connection from '" + << connectedHost << "'.\n"; + + break; + } + else + { + #ifdef PANIC + *logofs << "Loop: WARNING! Refusing connection from '" << connectedHost + << "' on port '" << socketAddress.getTCPPort() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Refusing connection from '" + << connectedHost << "'.\n"; + } + + // + // Not the best way to elude a DOS attack... + // + + sleep(5); + + close(newFD); + + } - close(newFD); } if (--retryAccept == 0) { - if (*acceptHost == '\0') + if (socketAddress.isUnixSocket()) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection via Unix file socket from this host " + << "could not be established.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection via Unix file socket from this host " + << "could not be established.\n"; + } + else if (*acceptHost == '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Connection with remote host " @@ -6804,43 +6905,194 @@ WaitForRemoteError: HandleCleanup(); } -// -// Connect to remote proxy. If successful -// return FD of connection, else return -1. -// - -int ConnectToRemote(const char *const hostName, int portNum) +int PrepareProxyConnectionTCP(char** hostName, long int* portNum, int* timeout, int* proxyFD, int* reason) { - int proxyFD = -1; - int remoteIPAddr = GetHostAddress(hostName); + if (!proxyFD) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). " + << "'proxyFD' must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionTCP). " + << "'proxyFD' must not be a NULL pointer.\n"; + + return -1; + } + + if (!reason) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). " + << "'reason' must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionTCP). " + << "'reason' must not be a NULL pointer.\n"; + + return -1; + } + int remoteIPAddr = GetHostAddress(*hostName); if (remoteIPAddr == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Unknown remote host '" - << hostName << "'.\n" << logofs_flush; + << *hostName << "'.\n" << logofs_flush; #endif - - cerr << "Error" << ": Unknown remote host '" - << hostName << "'.\n"; + cerr << "Error" << ": Unknown remote host '" + << *hostName << "'.\n"; HandleCleanup(); } #ifdef TEST *logofs << "Loop: Connecting to remote host '" - << hostName << ":" << portNum << "'.\n" + << *hostName << ":" << *portNum << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Connecting to remote host '" - << hostName << ":" << portNum << "'.\n" + << *hostName << ":" << *portNum << "'.\n" << logofs_flush; + *proxyFD = -1; + *reason = -1; + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(*portNum); + addr.sin_addr.s_addr = remoteIPAddr; + + *proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); + *reason = EGET(); + + if (*proxyFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n"; + return -1; + + } + else if (SetReuseAddress(*proxyFD) < 0) + { + return -1; + } + + // + // Ensure operation is timed out + // if there is a network problem. + // + + if (timeout) + SetTimer(*timeout); + else + SetTimer(20000); + + int result = connect(*proxyFD, (sockaddr *) &addr, sizeof(sockaddr_in)); + + *reason = EGET(); + + ResetTimer(); + + return result; + +} + +int PrepareProxyConnectionUnix(char** path, int* timeout, int* proxyFD, int* reason) +{ + + if (!proxyFD) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). " + << "proxyFD must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionUnix). " + << "proxyFD must not be a NULL pointer.\n"; + + return -1; + } + + if (!reason) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). " + << "'reason' must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionUnix). " + << "'reason' must not be a NULL pointer.\n"; + + return -1; + } + + /* FIXME: Add socket file existence and permission checks */ + + *proxyFD = -1; + *reason = -1; + + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, *path, 108 - 1); + + *proxyFD = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); + *reason = EGET(); + + if (*proxyFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n"; + + return -1; + } + + // + // Ensure operation is timed out + // if there is a network problem. + // + + if (timeout) + SetTimer(*timeout); + else + SetTimer(20000); + + int result = connect(*proxyFD, (sockaddr *) &addr, sizeof(sockaddr_un)); + + *reason = EGET(); + + ResetTimer(); + + return result; +} + +// +// Connect to remote proxy. If successful +// return FD of connection, else return -1. +// + +int ConnectToRemote(ChannelEndPoint &socketAddress) +{ + // // How many times we retry to connect to remote - // host in case of failure? + // host / Unix domain socket in case of failure? // int retryConnect = control -> OptionProxyRetryConnect; @@ -6858,39 +7110,16 @@ int ConnectToRemote(const char *const hostName, int portNum) T_timestamp lastRetry = getNewTimestamp(); - sockaddr_in addr; + int result = -1; + int reason = -1; + int proxyFD = -1; - addr.sin_family = AF_INET; - addr.sin_port = htons(portNum); - addr.sin_addr.s_addr = remoteIPAddr; + char *hostName = NULL; + long int portNum = -1; + char *unixPath = NULL; for (;;) { - proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); - - if (proxyFD == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to socket failed. " - << "Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to socket failed. " - << "Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - goto ConnectToRemoteError; - } - else if (SetReuseAddress(proxyFD) < 0) - { - goto ConnectToRemoteError; - } - - // - // Ensure operation is timed out - // if there is a network problem. - // #ifdef DEBUG *logofs << "Loop: Timer set to " << connectTimeout / 1000 @@ -6899,13 +7128,10 @@ int ConnectToRemote(const char *const hostName, int portNum) << "'.\n" << logofs_flush; #endif - SetTimer(connectTimeout); - - int result = connect(proxyFD, (sockaddr *) &addr, sizeof(sockaddr_in)); - - int reason = EGET(); - - ResetTimer(); + if (socketAddress.getUnixPath(&unixPath)) + result = PrepareProxyConnectionUnix(&unixPath, &connectTimeout, &proxyFD, &reason); + else if (socketAddress.getTCPHostAndPort(&hostName, &portNum)) + result = PrepareProxyConnectionTCP(&hostName, &portNum, &connectTimeout, &proxyFD, &reason); if (result < 0) { @@ -6919,24 +7145,40 @@ int ConnectToRemote(const char *const hostName, int portNum) { ESET(reason); - #ifdef PANIC - *logofs << "Loop: PANIC! Connection to '" << hostName - << ":" << portNum << "' failed. Error is " - << EGET() << " '" << ESTR() << "'.\n" - << logofs_flush; - #endif + if (socketAddress.isUnixSocket()) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection to Unix file socket '" + << unixPath << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif - cerr << "Error" << ": Connection to '" << hostName - << ":" << portNum << "' failed. Error is " - << EGET() << " '" << ESTR() << "'.\n"; + cerr << "Error" << ": Connection to Unix file socket '" + << unixPath << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection to '" << hostName + << ":" << portNum << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection to '" << hostName + << ":" << portNum << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + } goto ConnectToRemoteError; } else { #ifdef TEST - *logofs << "Loop: Sleeping " << retryTimeout - << " Ms before retrying.\n" + *logofs << "Loop: Sleeping " << retryTimeout + << " ms before retrying.\n" << logofs_flush; #endif @@ -7005,10 +7247,20 @@ int ConnectToRemote(const char *const hostName, int portNum) ESET(reason); #ifdef TEST - *logofs << "Loop: Connection to '" << hostName - << ":" << portNum << "' failed with error '" - << ESTR() << "'. Retrying.\n" - << logofs_flush; + if (unixPath[0] != '\0' ) + { + *logofs << "Loop: Connection to Unix socket file '" + << unixPath << "' failed with error '" + << ESTR() << "'. Retrying.\n" + << logofs_flush; + } + else + { + *logofs << "Loop: Connection to '" << hostName + << ":" << portNum << "' failed with error '" + << ESTR() << "'. Retrying.\n" + << logofs_flush; + } #endif } else @@ -8234,6 +8486,9 @@ int ParseEnvironmentOptions(const char *env, int force) name = strtok(nextOpts, "="); + char connectHost[DEFAULT_STRING_LENGTH] = { 0 }; + long connectPort = -1; + while (name) { value = strtok(NULL, ","); @@ -8254,6 +8509,7 @@ int ParseEnvironmentOptions(const char *env, int force) } else if (strcasecmp(name, "link") == 0) { + if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); @@ -8315,26 +8571,29 @@ int ParseEnvironmentOptions(const char *env, int force) } else if (strcasecmp(name, "listen") == 0) { - if (*connectHost != '\0') + char *socketUri = NULL; + if (connectSocket.getSpec(&socketUri)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't handle 'listen' and 'connect' parameters " << "at the same time.\n" << logofs_flush; *logofs << "Loop: PANIC! Refusing 'listen' parameter with 'connect' being '" - << connectHost << "'.\n" << logofs_flush; + << socketUri << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't handle 'listen' and 'connect' parameters " << "at the same time.\n"; cerr << "Error" << ": Refusing 'listen' parameter with 'connect' being '" - << connectHost << "'.\n"; + << socketUri << "'.\n"; + free(socketUri); return -1; } - listenPort = ValidateArg("local", name, value); + SetAndValidateChannelEndPointArg("local", name, value, listenSocket); + } else if (strcasecmp(name, "loopback") == 0) { @@ -8342,22 +8601,24 @@ int ParseEnvironmentOptions(const char *env, int force) } else if (strcasecmp(name, "accept") == 0) { - if (*connectHost != '\0') + char *socketUri = NULL; + if (connectSocket.getSpec(&socketUri)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't handle 'accept' and 'connect' parameters " << "at the same time.\n" << logofs_flush; *logofs << "Loop: PANIC! Refusing 'accept' parameter with 'connect' being '" - << connectHost << "'.\n" << logofs_flush; + << socketUri << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't handle 'accept' and 'connect' parameters " << "at the same time.\n"; cerr << "Error" << ": Refusing 'accept' parameter with 'connect' being '" - << connectHost << "'.\n"; + << socketUri << "'.\n"; + free(socketUri); return -1; } @@ -8383,8 +8644,12 @@ int ParseEnvironmentOptions(const char *env, int force) return -1; } - - strncpy(connectHost, value, DEFAULT_STRING_LENGTH - 1); + if ((strncmp(value, "tcp:", 4) == 0) || (strncmp(value, "unix:", 5) == 0)) + SetAndValidateChannelEndPointArg("local", name, value, connectSocket); + else + // if the "connect" parameter does not start with "unix:" or "tcp:" assume + // old parameter usage style (providing hostname string only). + strcpy(connectHost, value); } else if (strcasecmp(name, "port") == 0) { @@ -8842,6 +9107,17 @@ int ParseEnvironmentOptions(const char *env, int force) } // End of while (name) ... + // Assemble the connectSocket channel end point if parameter values have been old-school... + if (connectSocket.disabled() && (connectHost[0] != '\0') && (proxyPort > 0 || connectPort > 0)) + { + if (connectPort < 0) + connectPort = proxyPort + DEFAULT_NX_PROXY_PORT_OFFSET; + + char tcpHostAndPort[DEFAULT_STRING_LENGTH] = { 0 }; + sprintf(tcpHostAndPort, "tcp:%s:%ld", connectHost, connectPort); + SetAndValidateChannelEndPointArg("local", name, tcpHostAndPort, connectSocket); + } + #ifdef TEST *logofs << "Loop: Completed parsing of string '" << env << "'.\n" << logofs_flush; @@ -9082,16 +9358,21 @@ int ParseCommandLineOptions(int argc, const char **argv) // command line at the connecting side. // - if (ParseHostOption(nextArg, connectHost, connectPort) > 0) - { - // - // Assume port is at a proxied display offset. - // + char *cHost; + long cPort; - proxyPort = connectPort; + if (connectSocket.getTCPHostAndPort(&cHost, &cPort) && (ParseHostOption(nextArg, cHost, cPort) > 0)) + { + // + // Assume port is at a proxied display offset. + // - connectPort += DEFAULT_NX_PROXY_PORT_OFFSET; - } + proxyPort = cPort; + + cPort += DEFAULT_NX_PROXY_PORT_OFFSET; + connectSocket.setSpec(cHost, cPort); + + } else if (ParseEnvironmentOptions(nextArg, 1) < 0) { return -1; @@ -13195,7 +13476,7 @@ int ParseBitrateOption(const char *opt) return 1; } -int ParseHostOption(const char *opt, char *host, int &port) +int ParseHostOption(const char *opt, char *host, long &port) { #ifdef TEST *logofs << "Loop: Trying to parse options string '" << opt -- cgit v1.2.3