diff options
author | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2016-07-05 08:09:24 +0200 |
---|---|---|
committer | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2016-07-05 08:09:24 +0200 |
commit | 854f3fa49351ec72da432f195806162fcf5e5ddc (patch) | |
tree | 2366136004c46641fa72b8a717663c7ac08ac440 /nxcomp | |
parent | e0edae04db6dc189512b3a1eda7fe4516af56512 (diff) | |
parent | 5a22f2ec1fc5f40257bb61939f9cad4187e00ab8 (diff) | |
download | nx-libs-854f3fa49351ec72da432f195806162fcf5e5ddc.tar.gz nx-libs-854f3fa49351ec72da432f195806162fcf5e5ddc.tar.bz2 nx-libs-854f3fa49351ec72da432f195806162fcf5e5ddc.zip |
Merge branch 'sunweaver-pr/proxy-to-proxy-over-unix-socket' into 3.6.x
Attributes GH PR #142: https://github.com/ArcticaProject/nx-libs/pull/142
Reviewed by: Salvador Fandino <sfandino@yahoo.com> -- Mon, 04 Jul 2016 16:31:00 +0200
Diffstat (limited to 'nxcomp')
-rw-r--r-- | nxcomp/ChannelEndPoint.cpp | 143 | ||||
-rw-r--r-- | nxcomp/ChannelEndPoint.h | 11 | ||||
-rw-r--r-- | nxcomp/Loop.cpp | 961 |
3 files changed, 626 insertions, 489 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 <string.h> #include <stdlib.h> #include <stdio.h> +#include <sys/stat.h> #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 { @@ -205,8 +297,15 @@ long ChannelEndPoint::getTCPPort() const { } 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 5086db012..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 @@ -524,14 +527,6 @@ static int ParseHostOption(const char *opt, char *host, int &port); static int ParseFontPath(char *path); // -// Determine the interface where to listen for -// the remote proxy connection or the local -// forwarder. -// - -static int ParseListenOption(int &interface); - -// // Translate a pack method id in a literal. // @@ -952,9 +947,7 @@ 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 listenHost[DEFAULT_STRING_LENGTH] = { 0 }; static char displayHost[DEFAULT_STRING_LENGTH] = { 0 }; static char authCookie[DEFAULT_STRING_LENGTH] = { 0 }; @@ -972,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 int listenPort = -1; -static int connectPort = -1; +static ChannelEndPoint listenSocket; + +// +// The TCP host and port or Unix file socket where +// the remote proxy will be contacted. +// + +static ChannelEndPoint connectSocket; // // Helper channels are disabled by default. @@ -3364,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 : "<unset>") << "'.\n" << logofs_flush; + listenSocket.getSpec(&socketUri); + *logofs << "Loop: listenSocket is "<< ( listenSocket.enabled() ? "enabled" : "disabled") << ". " + << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "<unset>") << "'.\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 @@ -3431,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 @@ -3945,72 +3977,7 @@ int SetupTcpSocket() // Open TCP socket emulating local display. // - tcpFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); - - if (tcpFD == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to socket failed for TCP socket" - << ". Error is " << EGET() << " '" << ESTR() << "'.\n" - << logofs_flush; - #endif - - cerr << "Error" << ": Call to socket failed for TCP socket" - << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; - - HandleCleanup(); - } - else if (SetReuseAddress(tcpFD) < 0) - { - HandleCleanup(); - } - - unsigned int proxyPortTCP = X_TCP_PORT + proxyPort; - - sockaddr_in tcpAddr; - - tcpAddr.sin_family = AF_INET; - tcpAddr.sin_port = htons(proxyPortTCP); - if ( loopbackBind ) - { - tcpAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - } - else - { - tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); - } - - if (bind(tcpFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to bind failed for TCP port " - << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to bind failed for TCP port " - << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - HandleCleanup(); - } - - if (listen(tcpFD, 8) == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to listen failed for TCP port " - << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to listen failed for TCP port " - << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - HandleCleanup(); - } - - return 1; + return ListenConnectionTCP((loopbackBind ? "localhost" : "*"), X_TCP_PORT + proxyPort, "X11"); } int SetupUnixSocket() @@ -4019,90 +3986,40 @@ int SetupUnixSocket() // Open UNIX domain socket for display. // - unixFD = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); - - if (unixFD == -1) - { + if (!control->TempPath) { #ifdef PANIC - *logofs << "Loop: PANIC! Call to socket failed for UNIX domain" - << ". Error is " << EGET() << " '" << ESTR() << "'.\n" - << logofs_flush; + *logofs << "Loop: PANIC! Temporal path is null.\n" << logofs_flush; #endif - cerr << "Error" << ": Call to socket failed for UNIX domain" - << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; - + cerr << "Error" << ": Temporal path is null.\n"; HandleCleanup(); } - sockaddr_un unixAddr; - unixAddr.sun_family = AF_UNIX; - - char dirName[DEFAULT_STRING_LENGTH]; - - snprintf(dirName, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix", - control -> TempPath); + unsigned int required = snprintf(unixSocketName, DEFAULT_STRING_LENGTH, "%s/.X11-unix", control->TempPath); + if (required < sizeof(unixSocketName)) { - *(dirName + DEFAULT_STRING_LENGTH - 1) = '\0'; + // No need to execute the following actions conditionally + mkdir(unixSocketName, (0777 | S_ISVTX)); + chmod(unixSocketName, (0777 | S_ISVTX)); - struct stat dirStat; + required = snprintf(unixSocketName, DEFAULT_STRING_LENGTH, "%s/.X11-unix/X%d", control->TempPath, proxyPort); + if (required < sizeof(unixSocketName)) { - if ((stat(dirName, &dirStat) == -1) && (EGET() == ENOENT)) - { - mkdir(dirName, (0777 | S_ISVTX)); - chmod(dirName, (0777 | S_ISVTX)); + unixFD = ListenConnectionUnix(unixSocketName, "x11"); + if (unixFD >= 0) + chmod(unixSocketName, 0777); + return unixFD; + } } - snprintf(unixSocketName, DEFAULT_STRING_LENGTH - 1, "%s/X%d", - dirName, proxyPort); - - strncpy(unixAddr.sun_path, unixSocketName, 108); + unixSocketName[0] = '\0'; // Just in case! - #ifdef TEST - *logofs << "Loop: Assuming Unix socket with name '" - << unixAddr.sun_path << "'.\n" - << logofs_flush; + #ifdef PANIC + *logofs << "Loop: PANIC! path for unix socket is too long.\n" << logofs_flush; #endif - *(unixAddr.sun_path + 107) = '\0'; - - if (bind(unixFD, (sockaddr *) &unixAddr, sizeof(unixAddr)) == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to bind failed for UNIX domain socket " - << unixSocketName << ". Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to bind failed for UNIX domain socket " - << unixSocketName << ". Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - HandleCleanup(); - } - - if (listen(unixFD, 8) == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to listen failed for UNIX domain socket " - << unixSocketName << ". Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to listen failed for UNIX domain socket " - << unixSocketName << ". Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - HandleCleanup(); - } - - // - // Let any local user to gain access to socket. - // - - chmod(unixSocketName, 0777); - - return 1; + cerr << "Error" << ": path for Unix socket is too long.\n"; + HandleCleanup(); } // @@ -4528,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) { @@ -4551,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"; @@ -5569,9 +5488,7 @@ void CleanupLocal() *unixSocketName = '\0'; - *connectHost = '\0'; *acceptHost = '\0'; - *listenHost = '\0'; *displayHost = '\0'; *authCookie = '\0'; @@ -5585,8 +5502,8 @@ void CleanupLocal() xServerAddr = NULL; - listenPort = -1; - connectPort = -1; + listenSocket.disable(); + connectSocket.disable(); cupsPort.disable(); auxPort.disable(); @@ -6717,151 +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 listenIPAddr = -1; int proxyFD = -1; int newFD = -1; - // - // Get IP address of host to be awaited. - // - int acceptIPAddr = 0; - if (*acceptHost != '\0') - { - acceptIPAddr = GetHostAddress(acceptHost); - - if (acceptIPAddr == 0) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Cannot accept connections from unknown host '" - << acceptHost << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Cannot accept connections from unknown host '" - << acceptHost << "'.\n"; - - goto WaitForRemoteError; - } - } - - proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); - - if (proxyFD == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to socket failed for TCP socket. " - << "Error is " << EGET() << " '" << ESTR() << "'.\n" - << logofs_flush; - #endif - - cerr << "Error" << ": Call to socket failed for TCP socket. " - << "Error is " << EGET() << " '" << ESTR() << "'.\n"; - - goto WaitForRemoteError; - } - else if (SetReuseAddress(proxyFD) < 0) + if (socketAddress.isTCPSocket()) { - goto WaitForRemoteError; - } - listenIPAddr = 0; - - ParseListenOption(listenIPAddr); + // + // Get IP address of host to be awaited. + // - sockaddr_in tcpAddr; + if (*acceptHost != '\0') + { + acceptIPAddr = GetHostAddress(acceptHost); - tcpAddr.sin_family = AF_INET; - tcpAddr.sin_port = htons(portNum); + if (acceptIPAddr == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Cannot accept connections from unknown host '" + << acceptHost << "'.\n" << logofs_flush; + #endif - // - // Quick patch to run on MacOS/X where inet_addr("127.0.0.1") - // alone seems to fail to return a valid interface. It probably - // just needs a htonl() or something like that. - // - // TODO: We have to give another look at inet_addr("127.0.0.1") - // on the Mac. - // + cerr << "Error" << ": Cannot accept connections from unknown host '" + << acceptHost << "'.\n"; - #ifdef __APPLE__ + goto WaitForRemoteError; + } + strcpy(hostLabel, "any host"); + } + else + { + snprintf(hostLabel, sizeof(hostLabel), "'%s'", acceptHost); + } - if ( loopbackBind ) - { - tcpAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (loopbackBind) + { + long bindPort; + if (socketAddress.getTCPHostAndPort(NULL, &bindPort)) + socketAddress.setSpec("localhost", bindPort); + } } + else if (socketAddress.isUnixSocket()) + strcpy(hostLabel, "this host"); else - { - tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); - } + strcpy(hostLabel, "unknown origin (something went wrong!!!)"); - #else - - tcpAddr.sin_addr.s_addr = listenIPAddr; - - #endif - if (bind(proxyFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to bind failed for TCP port " - << portNum << ". Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to bind failed for TCP port " - << portNum << ". Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - goto WaitForRemoteError; - } - - if (listen(proxyFD, 4) == -1) - { - #ifdef PANIC - *logofs << "Loop: PANIC! Call to listen failed for TCP port " - << portNum << ". Error is " << EGET() << " '" << ESTR() - << "'.\n" << logofs_flush; - #endif - - cerr << "Error" << ": Call to listen failed for TCP port " - << portNum << ". Error is " << EGET() << " '" << ESTR() - << "'.\n"; - - goto WaitForRemoteError; - } - - if (*acceptHost != '\0') - { - strcat(hostLabel, "'"); - strcat(hostLabel, acceptHost); - strcat(hostLabel, "'"); - } - else - { - strcpy(hostLabel, "any host"); - } + 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 @@ -6916,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 @@ -6936,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) + { - sleep(5); + #ifdef TEST + + 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 " @@ -7018,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; @@ -7072,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 @@ -7113,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) { @@ -7133,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 @@ -7219,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 @@ -8448,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, ","); @@ -8468,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); @@ -8529,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) { @@ -8556,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; } @@ -8597,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) { @@ -9056,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; @@ -9296,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; @@ -13409,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 @@ -13676,40 +13743,6 @@ ParseFontPathError: return -1; } -int ParseListenOption(int &address) -{ - if (*listenHost == '\0') - { - // - // On the X client side listen on any address. - // On the X server side listen to the forwarder - // on localhost. - // - - if (control -> ProxyMode == proxy_server) - { - address = (int) inet_addr("127.0.0.1"); - } - else - { - if ( loopbackBind ) - { - address = htonl(INADDR_LOOPBACK); - } - else - { - address = htonl(INADDR_ANY); - } - } - } - else - { - address = inet_addr(listenHost); - } - - return 1; -} - int OpenLogFile(char *name, ostream *&stream) { if (name == NULL || *name == '\0') |