Commit 6ea00158 authored by Stas Korobeynikov's avatar Stas Korobeynikov Committed by Pavel Vainerman

add nx proxy

parent 74109f5c
......@@ -95,7 +95,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
platform-pledge.o platform-tracing.o
SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
sshconnect.o sshconnect1.o sshconnect2.o mux.o
sshconnect.o sshconnect1.o sshconnect2.o mux.o proxy.o
SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
audit.o audit-bsm.o audit-linux.o platform.o \
......
/**************************************************************************/
/* */
/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */
/* */
/* NXSSH, NX protocol compression and NX extensions to this software */
/* are copyright of NoMachine. Redistribution and use of the present */
/* software is allowed according to terms specified in the file LICENSE */
/* which comes in the source distribution. */
/* */
/* Check http://www.nomachine.com/licensing.html for applicability. */
/* */
/* NX and NoMachine are trademarks of Medialogic S.p.A. */
/* */
/* All rights reserved. */
/* */
/**************************************************************************/
#include "includes.h"
#include "log.h"
#include "channels.h"
#include "xmalloc.h"
/*
* Used in NX network related functions.
*/
#include "NX.h"
#include "proxy.h"
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <netdb.h>
#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
#include <netinet/in_systm.h>
#endif
#include <netinet/in.h>
#ifdef __sun
#define INADDR_NONE ((unsigned int) -1)
#endif
/*
* Set here the requested log level.
*/
#define PANIC
#define WARNING
#undef TEST
#undef DEBUG
/*
* Print a line in the log if the time we
* spent inside the select or handling the
* messages exceeded a given time value.
*/
#undef TIME
/*
* The NX version we are advertising to the proxy.
*/
#define NX_ADVERTISE_VERSION "3.0.0"
/*
* Replace stdin and stdout upon the switch. This
* is not supported yet.
*/
#undef NX_REPLACE_STANDARD_DESCRIPTORS
/*
* Close selectively the read and write ends of
* the proxy and channel pipes in the proxy loop.
* This is just for reference as it is better to
* close all the descriptors at the first failure.
*/
#undef NX_SELECTIVELY_CLOSE_DESCRIPTORS
/*
* Use a well-known log file and share it with the
* proxy when the program is forwarding a SSHD con-
* nection to the agent. To be used only for test
* purposes.
*/
#undef NX_SHARE_BINDER_LOG
/*
* Do we check the standard input for the incoming
* switch command? This flag is bound to a command
* line parameter.
*/
int nx_check_switch = 0;
/*
* Did we receive the command? The switch is executed
* at the time the first channel receives the command,
* hence the port forwarding feature of SSH should be
* disabled when enabling the NX proxying. This is
* required to ensure that only the standard input has
* the possibility to perform the switch. Monitoring
* multiple concurrent channels is not implemented and
* would be of little use in the NX context.
*
* This is the typical layout of the file descriptors
* after the two proxies are connected through SSH:
*
* +-------+ +-------+
* | | | |
* | X |<---[12]--->| Proxy |<---[8,7]---> ...
* | | | |
* +-------+ +-------+
*
* +-------+
* ... <---[8,7]---|---[4]-in-->| |
* |<--[5]-out--| SSH |<--[3]--/
* +--[6]-err--| | /-----> SSHD
* | +-------+
* file
*/
int nx_switch_received = 0;
/*
* Do we redirect the debug log after the switch
* command? If so, we'll try to determine what is the
* log file used by the proxy and will append the out-
* put to the same log. This is useful when debugging
* the communication between nxssh and the NX trans-
* port. If the proxy log can't be determined, for
* example because the NX transport is not in use, the
* output will go to a well known file, but only if
* the NX_SHARE_BINDER_LOG is defined. This is useful
* when debugging the nxssh forwarding of the SSHD
* connection to the agent, as you can force the proxy
* to use the same file. For this to work, the file
* must be made writable by everybody. Beware that
* normally this forwarding process is run by nxserver
* as the nx user.
*/
int nx_switch_log = 0;
/*
* Parameters read from the switch command.
*/
char nx_switch_cookie[256] = { 0 };
char nx_switch_host[256] = { 0 };
int nx_switch_proxy = -1;
int nx_switch_port = -1;
int nx_switch_in = -1;
int nx_switch_out = -1;
char nx_switch_mode[256] = { 0 };
char nx_switch_options[1024] = { 0 };
int nx_switch_internal = -1;
int nx_switch_forward = -1;
/*
* The awaited switch command.
*/
const char *nx_switch_command = "NX> 299 Switch connection to: ";
/*
* Buffer the input while looking for the command.
* The buffering happens by flushing the input when
* a newline is received. This means that all input
* should be terminated with a newline or it will
* remain in the buffer and will never be sent to
* the packet interface.
*/
static int nx_check_input(Buffer *buffer, char *data, int *length, int limit);
static int nx_check_switch_command(Buffer *buffer, char *start);
static int nx_check_switch_parameters(char *command);
/*
* Parse the parameters from the switch command.
*/
static int nx_check_specifier_mode_and_options(char *command, char *mode, char *options);
static int nx_check_specifier_and_mode(char *command, char *mode);
static int nx_check_specifier_and_options(char *command, char *options);
static int nx_check_specifier(char *command);
static int nx_check_host_and_port(char *command, char *host, char *port);
static int nx_check_host_port_and_cookie(char *command, char *host, char *port, char *cookie);
static int nx_check_host_port_and_descriptors(char *command, char *host, char *port, char *in, char *out);
static int nx_check_port_and_accept(char *command, char *port, char *accept);
static int nx_check_descriptors(char *command, char *in, char *out);
/*
* Connect to the proxy over a socketpair or
* through a network connection.
*/
static int nx_open_internal_proxy_connection();
static int nx_open_external_proxy_connection();
/*
* Redirect the log output.
*/
static void nx_redirect_log_output();
/*
* Forward data read from the SSHD server to an
* agent running on the NX server side.
*/
void nx_run_server_side_loop(int proxy_fd);
/*
* Run a simple loop that lets the NX proxy on
* the client connect to an agent running on
* the NX server and make is manage the unenc-
* rypted connection.
*/
void nx_run_client_side_loop(int proxy_fd);
/*
* Buffers the standard input when waiting for the
* command without having established any channel.
*/
static Buffer nx_input_buffer;
/*
* Utilities to log in the NX format.
*/
static void nx_dump_buffer(Buffer *buffer);
static void nx_dump_string(char *string);
#if defined(DEBUG) || defined(TIME)
static char *nx_dump_timestamp();
static struct timeval nx_get_timestamp();
static int nx_diff_timestamp(struct timeval ts1, struct timeval ts2);
#endif
/*
* Catch signals used to interrupt the connect or
* detect a broken connection.
*/
static void nx_catch_timeout_signal(int number);
static void nx_catch_pipe_signal(int number);
/*
* Safe version of string functions. They are able
* to operate on strings delimited by a newline or
* a null.
*/
static char *nx_search_string_in_buffer(Buffer *buffer, char *start, const char *string);
static char *nx_remove_string_in_buffer(Buffer *buffer, char *start, int length);
static char *nx_search_newline_in_buffer(Buffer *buffer, char *start);
static char *nx_remove_newline_in_string(char *start, int length);
static char *nx_toupper_string(char *start);
/*
* This is currently unused. Not defined
* static to avoid the warning.
*/
char *nx_search_char_in_buffer(Buffer *buffer, char *start, int value);
/*
* Wait for the given amount of seconds.
*/
static void nx_wait_timeout(int seconds);
/*
* Utility used to set our preferred socket
* options. May alternatively use the SSH
* functions in misc.c.
*/
static int nx_set_nonblocking(int fd);
static int nx_set_blocking(int fd);
static int nx_set_nodelay(int fd);
static int nx_set_keepalive(int fd);
static int nx_set_lowdelay(int fd);
int nx_proxy_select(int maxfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
/*
* Check if we have switched the connection
* to the NX transport.
*/
if (nx_switch_internal == 1 && NXTransRunning(NX_FD_ANY) == 1)
{
fd_set t_readfds, t_writefds;
struct timeval t_timeout;
int n, r, e;
if (exceptfds != NULL)
{
fatal("NX> 280 Can't handle exception fds in select");
exit(1);
}
#ifdef TEST
debug("NX> 280 Going to run a new NX loop");
#endif
if (readfds == NULL)
{
FD_ZERO(&t_readfds);
readfds = &t_readfds;
}
if (writefds == NULL)
{
FD_ZERO(&t_writefds);
writefds = &t_writefds;
}
if (timeout == NULL)
{
t_timeout.tv_sec = 10;
t_timeout.tv_usec = 0;
timeout = &t_timeout;
}
n = maxfds;
/*
* If the transport is gone avoid
* sleeping until the timeout.
*/
if (NXTransPrepare(&n, readfds, writefds, timeout) != 0)
{
/*
* Merge the SSH descriptors with the NX
* descriptors and give a chance to the
* proxy to run its own loop.
*/
NXTransSelect(&r, &e, &n, readfds, writefds, timeout);
NXTransExecute(&r, &e, &n, readfds, writefds, timeout);
errno = e;
return r;
}
else
{
return 0;
}
}
else
{
#ifdef DEBUG
debug("NX> 280 The NX transport is not running");
#endif
return select(maxfds, readfds, writefds, exceptfds, timeout);
}
}
int nx_check_channel_input(Channel *channel, char *data, int *length, int limit)
{
debug("NX> 285 Going to check input for fd: %d", channel->rfd);
/*
* This allows forwarding of connections to
* the authentication agent but it is to be
* better investigated if this is the best
* way to proceed.
*/
if (strcmp(channel->ctype, "authentication agent connection") == 0)
{
debug("NX> 285 Detected authentication agent connection");
return 0;
}
/*
* It returns true if more data has been
* produced for the channel. The switch
* command is removed from the buffer.
*/
return nx_check_input(&channel->nx_buffer, data, length, limit);
}
int nx_check_standard_input()
{
char data[1024];
fd_set set;
int fd = fileno(stdin);
int result;
debug("NX> 285 Going to check input for fd: %d", fd);
FD_ZERO(&set);
for (;;)
{
FD_SET(fd, &set);
result = select(fd + 1, &set, NULL, NULL, NULL);
if (result < 0 && errno == EINTR)
{
continue;
}
if (result <= 0)
{
goto nx_check_standard_input_error;
}
for (;;)
{
result = read(fd, data, 1024 - 1);
if (result < 0 && errno == EINTR)
{
continue;
}
if (result <= 0)
{
goto nx_check_standard_input_error;
}
/*
* No echo performed. Just print a warning.
* Actually we should only receive the com-
* mand. Any other data is discarded.
*/
nx_check_input(&nx_input_buffer, data, &result, 1024 - 1);
if (result > 0)
{
*(data + result) = '\0';
nx_remove_newline_in_string(data, result);
error("NX> 289 Discarding spurious data: '%s'", data);
buffer_clear(&nx_input_buffer);
}
break;
}
break;
}
return 1;
nx_check_standard_input_error:
fatal("NX> 290 End of input received before switch command");
return -1;
}
int nx_check_input(Buffer *buffer, char *data, int *length, int limit)
{
char *start;
char *current;
char *end;
/*
* Append data to the buffer.
*/
if (buffer_len(buffer) + *length >= (16*1024 - 1))
{
error("\r\nNX> 288 Buffer length of: %d bytes exceeded",
16*1024);
fatal("NX> 290 Don't use the -B option with binary data");
}
debug("NX> 280 Adding: %d bytes to the temporary buffer",
*length);
buffer_append(buffer, data, *length);
debug("NX> 280 Dumping content of the temporary buffer:");
nx_dump_buffer(buffer);
/*
* Check if buffer contains the switch command.
*/
start = nx_search_string_in_buffer(buffer, NULL, nx_switch_command);
if (start != NULL)
{
debug("NX> 280 Switch command maybe found at position: %d",
(int) (start - (char *) buffer_ptr(buffer)));
/*
* Verify the host and port and wipe out
* the command from the buffer.
*/
nx_switch_received = nx_check_switch_command(buffer, start);
}
/*
* Place back the remaining data in
* the original buffer.
*/
current = data;
*length = 0;
for (;;)
{
start = buffer_ptr(buffer);
end = nx_search_newline_in_buffer(buffer, start);
if (end == NULL || *length + (end - start + 1) > limit)
{
break;
}
debug("NX> 280 Adding: %d bytes to the returned buffer",
(int) (end - start + 1));
buffer_get(buffer, current, end - start + 1);
current += end - start + 1;
*length += end - start + 1;
}
debug("NX> 280 Left: %d bytes in the temporary buffer",
buffer_len(buffer));
return 1;
}
int nx_check_switch_command(Buffer *buffer, char *start)
{
char *command;
char *end;
int length = buffer_len(buffer) - (start - (char *) buffer_ptr(buffer));
debug("NX> 280 Searching newline with: %d bytes remaining", length);
if ((end = nx_search_newline_in_buffer(buffer, start)) == NULL)
{
fatal("NX> 290 Can't find the terminating newline in buffer:");
return 0;
}
/*
* Copy the command string to a temporary.
*/
length = end - start;
debug("NX> 280 Checking command with length: %d", length);
command = xmalloc(length + 1);
if (command == NULL)
{
fatal("NX> 298 Out of memory checking the command string");
return -1;
}
memcpy(command, start, length);
*(command + length) = '\0';
debug("NX> 280 Analyzing command string:");
nx_dump_string(command);
/*
* Get rid of the command fingerprint from
* buffer until the terminating newline.
*/
nx_remove_string_in_buffer(buffer, start, length);
debug("NX> 280 Content of the temporary buffer:");
nx_dump_buffer(buffer);
if (nx_check_switch_parameters(command) <= 0)
{
/*
* Skip the fingerprint part in the
* error message.
*/
fatal("\r\nNX> 298 Parse error in the switch parameters");
return -1;
}
return 1;
}
int nx_check_switch_parameters(char *command)
{
char host[256] = { 0 };
char accept[256] = { 0 };
char cookie[256] = { 0 };
char mode[256] = { 0 };
char options[1024] = { 0 };
char port[16] = { 0 };
char in[16] = { 0 };
char out[16] = { 0 };
if (command == NULL || *command == '\0')
{
return -1;
}
else if (strlen(command) >= 256)
{
return -1;
}
debug("NX> 285 Searching switch parameters in command:");
nx_dump_string(command);
if (nx_check_specifier_mode_and_options(command, mode, options) <= 0 &&
nx_check_specifier_and_options(command, options) <= 0 &&
nx_check_specifier_and_mode(command, mode) <= 0 &&
nx_check_port_and_accept(command, port, accept) <= 0 &&
nx_check_host_port_and_descriptors(command, host, port, in, out) <= 0 &&
nx_check_descriptors(command, in, out) <= 0 &&
nx_check_host_port_and_cookie(command, host, port, cookie) <= 0 &&
nx_check_host_and_port(command, host, port) <= 0 &&
nx_check_specifier(command) <= 0)
{
return -1;
}
/*
* If at least host and port were given
* we need an outbound connection.
*/
if (*host != '\0' && *port != '\0')
{
strcpy(nx_switch_host, host);
nx_switch_port = atoi(port);
logit("NX> 285 Identified host: %s port: %d", nx_switch_host, nx_switch_port);
if (*cookie != '\0')
{
strcpy(nx_switch_cookie, cookie);
logit("NX> 285 Identified cookie: %s", nx_switch_cookie);
}
if (*in != '\0' && *out != '\0')
{
nx_switch_in = atoi(in);
nx_switch_out = atoi(out);
logit("NX> 285 Identified descriptors in: %d out: %d", nx_switch_in, nx_switch_out);
}
nx_switch_internal = 0;
}
else if (*in != '\0' && *out != '\0')
{
nx_switch_in = atoi(in);
nx_switch_out = atoi(out);
logit("NX> 285 Identified descriptors in: %d out: %d", nx_switch_in, nx_switch_out);
nx_switch_forward = 1;
}
else if (*accept != '\0' && *port != '\0')
{
strcpy(nx_switch_host, accept);
nx_switch_port = atoi(port);
logit("NX> 285 Identified port: %d accept: %s", nx_switch_port, nx_switch_host);
nx_switch_forward = 2;
}
else
{
logit("NX> 285 Identified internal connection");
if (*mode != '\0')
{
/*
* The mode "encrypted" is assumed to be the
* default and is left to the original empty
* string. Unencrypted connections are run
* by entering a different loop.
*/
if (strcmp("encrypted", mode) != 0 &&
strcmp("unencrypted", mode) != 0 &&
strcmp("default", mode) != 0)
{
error("NX> 290 Not supported mode: %s", mode);
return -1;
}
if (strcmp("unencrypted", mode) == 0)
{
strcpy(nx_switch_mode, mode);
logit("NX> 285 Identified mode: %s", nx_switch_mode);
}
else
{
logit("NX> 285 Using default mode encrypted");
}
}
if (*options != '\0')
{
strcpy(nx_switch_options, options);
logit("NX> 285 Identified options: %s", nx_switch_options);
}
nx_switch_internal = 1;
}
return 1;
}
int nx_check_host_port_and_descriptors(char *command, char *host, char *port, char *in, char *out)
{
char match[256] = { 0 };
int result;
/*
* Look for the hostname and port followed
* by the input and output fd descriptors.
*/
snprintf(match, 255, "%s%%128[^:]:%%5[0-9] in: %%5[0-9] out: %%5[0-9]", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, host, port, in, out);
if (result != 4)
{
return -1;
}
return 1;
}
int nx_check_port_and_accept(char *command, char *port, char *accept)
{
char match[256] = { 0 };
int result;
/*
* Look for the port.
*/
snprintf(match, 255, "%s SSH port: %%5[0-9] accept: %%128s", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, port, accept);
if (result != 2)
{
return -1;
}
return 1;
}
int nx_check_descriptors(char *command, char *in, char *out)
{
char match[256] = { 0 };
int result;
/*
* Look for the input and output fd descriptors.
*/
snprintf(match, 255, "%s SSH in: %%5[0-9] out: %%5[0-9]", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, in, out);
if (result != 2)
{
return -1;
}
return 1;
}
int nx_check_host_port_and_cookie(char *command, char *host, char *port, char *cookie)
{
char match[256] = { 0 };
int result;
/*
* Look for the hostname and port followed
* by the cookie specification.
*/
snprintf(match, 255, "%s%%128[^:]:%%5[0-9] cookie: %%32s", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, host, port, cookie);
if (result != 3)
{
return -1;
}
return 1;
}
int nx_check_host_and_port(char *command, char *host, char *port)
{
char match[256] = { 0 };
int result;
/*
* Look for the hostname and port.
*/
snprintf(match, 255, "%s%%128[^:]:%%5[0-9]", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, host, port);
if (result != 2)
{
return -1;
}
return 1;
}
int nx_check_specifier_mode_and_options(char *command, char *mode, char *options)
{
char match[256] = { 0 };
int result;
/*
* Look for the NX mode and options. Mode must be
* one of "default", "encrypted" or "unencrypted".
* Options are an arbitrary string that will have
* to make sense for NX.
*/
snprintf(match, 255, "%s NX mode: %%16s options: %%1023[^']", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, mode, options);
if (result != 2)
{
return -1;
}
return 1;
}
int nx_check_specifier_and_mode(char *command, char *mode)
{
char match[256] = { 0 };
int result;
/*
* Look for the NX mode. Options are assumed
* to be empty.
*/
snprintf(match, 255, "%s NX mode: %%16s", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, mode);
if (result != 1)
{
return -1;
}
return 1;
}
int nx_check_specifier_and_options(char *command, char *options)
{
char match[256] = { 0 };
int result;
/*
* Look for the NX options. Mode is assumed to be
* the current default.
*/
snprintf(match, 255, "%s NX options: %%1023s", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, options);
if (result != 1)
{
return -1;
}
return 1;
}
int nx_check_specifier(char *command)
{
char match[256] = { 0 };
char nx[256] = { 0 };
int result;
/*
* Look for the NX string.
*/
snprintf(match, 255, "%s%%2s", nx_switch_command);
debug("NX> 280 Searching with matching string:");
nx_dump_string(match);
result = sscanf(command, match, nx);
if (result != 1 || strcmp("NX", nx_toupper_string(nx)) != 0)
{
return -1;
}
return 1;
}
int nx_open_proxy_connection()
{
if (*nx_switch_host != '\0' && nx_switch_port != -1)
{
return nx_open_external_proxy_connection();
}
else if (nx_switch_internal == 1)
{
return nx_open_internal_proxy_connection();
}
else
{
fatal("\r\nNX> 297 Missing data to perform the switch");
return -1;
}
}
int nx_close_proxy_connection()
{
/*
* Yield the control to the proxy until the NX
* transport is gone.
*/
debug("NX> 280 Switch proxy is: %d", nx_switch_proxy);
if (nx_switch_proxy != -1)
{
debug("NX> 280 Waiting for the NX transport to terminate");
NXTransDestroy(nx_switch_proxy);
debug("NX> 280 NX transport successfully terminated");
}
return 1;
}
/*
* Let the proxy create a socketpair and receive
* back the end we'll use to communicate with it.
*/
int nx_open_internal_proxy_connection()
{
char *proxy_options;
int proxy_pair[2];
/*
* Options can be passed in the environment
* by the client by using an empty "options"
* switch parameter.
*/
if (*nx_switch_options != '\0')
{
proxy_options = nx_switch_options;
debug("NX> 280 Creating proxy with options: %s", nx_switch_options);
}
else
{
proxy_options = NULL;
debug("NX> 280 Creating proxy with null options");
}
/*
* Create a socketpair. We will use the
* proxy_pair[0] at our end and will
* pass the proxy_pair[1] to NX.
*/
if (socketpair(PF_UNIX, SOCK_STREAM, 0, proxy_pair) < 0)
{
fatal("NX> 290 Failure creating the socket pair: %s",
strerror(errno));
return -1;
}
/*
* If connection has to continue unencrypted
* reset the remote end. NX will create a new
* connection based on the proxy options.
*/
if (strcmp("unencrypted", nx_switch_mode) == 0)
{
close(proxy_pair[1]);
proxy_pair[1] = NX_FD_ANY;
}
else
{
/*
* Set the preferred options on our end of the
* socket. In the case of an unencrypted NX
* connection, the local end is just used as a
* place-holder. We will close it at the time
* we'll start the client loop.
*/
nx_set_socket_options(proxy_pair[0], 0);
}
debug("NX> 280 Using local end: %d remote end: %d",
proxy_pair[0], proxy_pair[1]);
if (NXTransCreate(proxy_pair[1], NX_MODE_SERVER, proxy_options) < 0)
{
fatal("NX> 290 Failed to create the internal connection");
return -1;
}
logit("NX> 280 Proxy opened with local: %d remote: %d",
proxy_pair[0], proxy_pair[1]);
/*
* Client-side support for memory-to-memory transport is not
* implemented in nxcomp, yet. This means that communication
* between nxcomp and nxssh takes place through a Unix pipe.
*
* if (strcmp("encrypted", nx_switch_mode) == 0)
* {
* NXTransAgent(proxy_pair);
* }
*/
debug("NX> 280 Should enable NX memory-to-memory agent transport");
nx_switch_proxy = proxy_pair[1];
return proxy_pair[0];
}
/*
* Connect to an external proxy process.
*/
int nx_open_external_proxy_connection()
{
int proxy_fd;
int remote_ip_addr;
void (*handler)(int) = signal(SIGALRM, nx_catch_timeout_signal);
/*
* Set how many times we retry to connect
* to the remote host in case of failure?
*/
int retry_connect = 4;
struct sockaddr_in addr;
struct hostent *host = gethostbyname(nx_switch_host);
int flag = 1;
int result;
/*
* We are reusing the timer. This is not
* optimal. Save at least the old value.
*/
int timer = alarm(0);
logit("\r\nNX> 291 Connecting to: %s:%d",
nx_switch_host, nx_switch_port);
if (host == NULL)
{
/*
* On some Unices gethostbyname() doesn't
* accept IP addresses, so try inet_addr.
*/
unsigned int address;
address = (unsigned int) inet_addr(nx_switch_host);
if (address == INADDR_NONE)
{
fatal("\r\nNX> 297 Can't resolve address of host: %s",
nx_switch_host);
goto nx_open_proxy_connection_error;
}
remote_ip_addr = (int) address;
}
else
{
remote_ip_addr = (*((int *) host -> h_addr_list[0]));
}
addr.sin_family = AF_INET;
addr.sin_port = htons(nx_switch_port);
addr.sin_addr.s_addr = remote_ip_addr;
for (;;)
{
proxy_fd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
if (proxy_fd == -1)
{
fatal("\r\nNX> 296 Can't create the connecting socket");
goto nx_open_proxy_connection_error;
}
else if (setsockopt(proxy_fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &flag, sizeof(flag)) < 0)
{
fatal("\r\nNX> 295 Can't set the SO_REUSEADDR on socket");
goto nx_open_proxy_connection_error;
}
/*
* Ensure operation is timed out
* if there is a network problem.
*/
alarm(retry_connect);
result = connect(proxy_fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
if (result < 0)
{
retry_connect--;
if (retry_connect > 0)
{
error("NX> 294 Connection to: %s:%d failed. Retrying",
nx_switch_host, nx_switch_port);
close(proxy_fd);
}
else
{
fatal("NX> 290 Failed connection to: %s:%d",
nx_switch_host, nx_switch_port);
goto nx_open_proxy_connection_error;
}
nx_wait_timeout(3);
}
else
{
break;
}
}
debug("NX> 294 Connected to proxy at: %s:%d",
nx_switch_host, nx_switch_port);
signal(SIGALRM, handler);
alarm(timer);
/*
* Set the preferred NX options on the socket.
*/
nx_set_socket_options(proxy_fd, 0);
return proxy_fd;
nx_open_proxy_connection_error:
signal(SIGALRM, handler);
alarm(timer);
return -1;
}
int nx_check_proxy_authentication(int proxy_fd)
{
if (*nx_switch_cookie != '\0')
{
char string[256];
/*
* String must be terminated with a space.
*/
snprintf(string, 255, "NXSSH-%s cookie=%s ", NX_ADVERTISE_VERSION,
nx_toupper_string(nx_switch_cookie));
/*
* This should not be required.
*/
*(string + 255) = '\0';
logit("NX> 285 Sending authentication cookie: %s",
nx_switch_cookie);
write(proxy_fd, string, strlen(string));
return 1;
}
return 0;
}
int nx_switch_client_side_descriptors(Channel *channel, int proxy_fd)
{
/*
* This is executed on the NX client side to
* reassign the SSH channel's descriptors to
* the socket we opened with the proxy.
*/
if (nx_switch_internal == 1 &&
strcmp("unencrypted", nx_switch_mode) == 0)
{
/*
* Enter the client-side I/O loop.
*/
nx_check_switch = 0;
nx_run_client_side_loop(proxy_fd);
logit("NX> 285 Client side loop completed");
exit(0);
}
else
{
/*
* This is executed for both "internal" and "normal"
* connections, the latter being connection obtained
* by attaching to a proxy listening on a TCP port.
* The "internal" connections will be managed in a
* special way, in clientloop.c:
*/
logit("NX> 285 Switching descriptors: %d and: %d to: %d",
channel->rfd, channel->wfd, proxy_fd);
if (dup2(proxy_fd, channel->rfd) < 0 || dup2(proxy_fd, channel->wfd) < 0)
{
fatal("\r\nNX> 292 Can't redirect I/O to channel descriptors");
}
close(proxy_fd);
/*
* Set again the preferred NX options on the
* involved fds after the dup2() even if this
* should not be required.
*/
nx_set_socket_options(channel->rfd, 0);
nx_set_socket_options(channel->wfd, 0);
logit("\r\nNX> 287 Redirected I/O to channel descriptors");
logit("NX> 280 Proxy in: %d out: %d transport in: %d out: %d",
channel->rfd, channel->wfd, nx_switch_proxy, nx_switch_proxy);
/*
* If requested, redirect the debug output to the
* error log used by the proxy. This can be useful
* when debugging the interaction between nxssh and
* the proxy.
*/
if (nx_switch_log == 1)
{
nx_redirect_log_output();
}
nx_check_switch = 0;
return 1;
}
}
void nx_switch_server_side_descriptors()
{
/*
* This is executed on the NX server side to
* forward the proxy I/O to the SSH daemon,
* where it will be encrypted. The standard
* error is left untouched. Our error output
* will go to the standard error we inherited
* from the NX server.
*/
int proxy_fd;
proxy_fd = nx_open_proxy_connection();
if (proxy_fd < 0)
{
fatal("NX> 290 Can't switch communication to: %s:%d",
nx_switch_host, nx_switch_port);
}
/*
* Check if we must authenticate to the proxy.
*/
nx_check_proxy_authentication(proxy_fd);
/*
* Redirect I/O to the provided descriptors.
*/
if (nx_switch_in >= 0 && nx_switch_out >= 0)
{
/*
* This has not been tested yet.
*/
#ifdef NX_REPLACE_STANDARD_DESCRIPTORS
if (dup2(nx_switch_in, fileno(stdin)) < 0)
{
fatal("NX> 290 Can't duplicate descriptor: %d to: %d error: '%s'",
nx_switch_in, fileno(stdin), strerror(errno));
}
else
{
logit("NX> 285 Duplicated descriptor: %d to: %d",
nx_switch_in, fileno(stdin));
}
if (dup2(nx_switch_out, fileno(stdout)) < 0)
{
fatal("NX> 290 Can't duplicate descriptor: %d to: %d error: '%s'",
nx_switch_out, fileno(stdout), strerror(errno));
}
else
{
logit("NX> 285 Duplicated descriptor: %d to: %d",
nx_switch_out, fileno(stdout));
}
#endif
}
else
{
fatal("NX> 290 Can't switch communication to in: %d out: %d",
nx_switch_in, nx_switch_out);
}
/*
* Enter the server-side I/O loop.
*/
nx_check_switch = 0;
nx_run_server_side_loop(proxy_fd);
}
int nx_switch_forward_descriptors(Channel *channel)
{
/*
* This is executed on the NX server side to
* tie together two descriptors, the one of
* the ssh connection from client and the one
* of the ssh connection to the node.
*/
if (nx_switch_in >= 0 && nx_switch_out >= 0)
{
if (dup2(nx_switch_in, channel->rfd) < 0)
{
fatal("NX> 290 Can't duplicate descriptor: %d to: %d error: '%s'",
nx_switch_in, channel->rfd, strerror(errno));
}
else
{
logit("NX> 285 Duplicated descriptor: %d to: %d",
nx_switch_in, channel->rfd);
}
if (dup2(nx_switch_out, channel->wfd) < 0)
{
fatal("NX> 290 Can't duplicate descriptor: %d to: %d error: '%s'",
nx_switch_out, channel->wfd, strerror(errno));
}
else
{
logit("NX> 285 Duplicated descriptor: %d to: %d",
nx_switch_out, channel->wfd);
}
}
else
{
fatal("NX> 290 Can't switch communication to in: %d out: %d",
nx_switch_in, nx_switch_out);
}
nx_check_switch = 0;
return 1;
}
int nx_switch_forward_port(Channel *channel)
{
/*
* This is executed on the NX server side to
* open a port and forward all data to the
* ssh connection to the node.
*/
int retry_accept = 4;
int proxy_fd = -1;
int new_fd = -1;
int flag = 1;
int remote_ip_addr;
struct sockaddr_in tcp_addr;
struct hostent *host = gethostbyname(nx_switch_host);
if (host == NULL)
{
/*
* On some Unices gethostbyname() doesn't
* accept IP addresses, so try inet_addr.
*/
unsigned int address;
address = (unsigned int) inet_addr(nx_switch_host);
if (address == INADDR_NONE)
{
fatal("\r\nNX> 297 Can't resolve address of host: %s",
nx_switch_host);
goto nx_switch_forward_port_error;
}
remote_ip_addr = (int) address;
}
else
{
remote_ip_addr = (*((int *) host -> h_addr_list[0]));
}
if (remote_ip_addr == 0)
{
fatal("\r\nNX> 297 Cannot accept connections from unknown host: %s",
nx_switch_host);
goto nx_switch_forward_port_error;
}
proxy_fd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
if (proxy_fd == -1)
{
fatal("\r\nNX> 297 Can't create socket error: %s",
strerror(errno));
goto nx_switch_forward_port_error;
}
else if (setsockopt(proxy_fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &flag, sizeof(flag)) < 0)
{
fatal("\r\nNX> 295 Can't set the SO_REUSEADDR on socket");
goto nx_switch_forward_port_error;
}
tcp_addr.sin_family = AF_INET;
tcp_addr.sin_port = htons(nx_switch_port);
tcp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(proxy_fd, (struct sockaddr *) &tcp_addr, sizeof(tcp_addr)) == -1)
{
fatal("\r\nNX> 297 Can't bind to port: %i error: %s",
nx_switch_port, strerror(errno));
goto nx_switch_forward_port_error;
}
if (listen(proxy_fd, 4) == -1)
{
fatal("\r\nNX> 297 Can't listen on port: %i error: %s",
nx_switch_port, strerror(errno));
goto nx_switch_forward_port_error;
}
logit("NX> 291 Waiting for remote connection on port: %d from: %s",
nx_switch_port, nx_switch_host);
for (;;)
{
fd_set readSet;
int result;
struct timeval selectTs;
FD_ZERO(&readSet);
FD_SET(proxy_fd, &readSet);
selectTs.tv_sec = 20;
selectTs.tv_usec = 0;
result = select(proxy_fd + 1, &readSet, NULL, NULL, &selectTs);
if (result == -1)
{
/*
* Don't restart the call on a interrupt.
* It is likely that the server wants us
* to terminate the procedure.
*
* if (errno == EINTR)
* {
* continue;
* }
*/
fatal("\r\nNX> 297 Call to select failed error: %s",
strerror(errno));
goto nx_switch_forward_port_error;
}
else if (result > 0 && FD_ISSET(proxy_fd, &readSet))
{
struct sockaddr_in newAddr;
char *connected_host = inet_ntoa(newAddr.sin_addr);
unsigned int connected_port = ntohs(newAddr.sin_port);
socklen_t addrLen = sizeof(struct sockaddr_in);
new_fd = accept(proxy_fd, (struct sockaddr *) &newAddr, &addrLen);
if (new_fd == -1)
{
fatal("\r\nNX> 297 Call to accept failed error: %s",
strerror(errno));
goto nx_switch_forward_port_error;
}
if ((int) newAddr.sin_addr.s_addr == remote_ip_addr)
{
#ifdef TEST
logit("NX> 291 Accepted connection from: %s with port: %d",
connected_host, connected_port);
#endif
break;
}
else
{
fatal("NX> 297 Refused connection from: %s with port: %d",
connected_host, connected_port);
goto nx_switch_forward_port_error;
}
/*
* Not the best way to elude a DOS attack.
*/
sleep(5);
close(new_fd);
}
if (--retry_accept == 0)
{
fatal("\r\nNX> 297 Connection with remote host: %s could not be established",
nx_switch_host);
goto nx_switch_forward_port_error;
}
}
close(proxy_fd);
nx_set_socket_options(new_fd, 0);
if (dup2(new_fd, channel->rfd) < 0 || dup2(new_fd, channel->wfd) < 0)
{
fatal("\r\nNX> 297 Can't redirect port to channel descriptors");
}
nx_check_switch = 0;
return 1;
nx_switch_forward_port_error:
close(proxy_fd);
return -1;
}
void nx_run_server_side_loop(int proxy_fd)
{
/*
* This loop is run on the NX server side when the program
* is run with the option -B. The loop just copies from the
* standard input to the proxy descriptor and from the proxy
* descriptor to to the standard output. The standard input
* and the standard output of the process are connected by
* the NX server to the SSHD's descriptors, so what this
* loop does is actually to connect the client side proxy
* to an agent running on the NX server.
*/
char data[64 * 1024];
fd_set set;
int proxy_in = dup(proxy_fd);
int proxy_out = dup(proxy_fd);
#ifdef NX_REPLACE_STANDARD_DESCRIPTORS
int channel_in = fileno(stdin);
int channel_out = fileno(stdout);
#else
int channel_in = nx_switch_in;
int channel_out = nx_switch_out;
#endif
int proxy_in_open = 1;
int channel_in_open = 1;
int in_fd;
int out_fd;
int failed_fd;
int selected;
int written;
int length;
int result;
#if defined(DEBUG) || defined(TIME)
struct timeval last_ts = nx_get_timestamp();
int diff_ts;
#endif
/*
* If requested, redirect the debug output.
*/
if (nx_switch_log == 1)
{
nx_redirect_log_output();
}
close(proxy_fd);
/*
* Set the preferred NX options on all the
* involved descriptors. We want blocking
* operations on the sockets so that we can
* rely on the NX proxy for the buffering
* and minimize the context switches.
*/
nx_set_socket_options(proxy_in, 1);
nx_set_socket_options(proxy_out, 1);
nx_set_socket_options(channel_in, 1);
nx_set_socket_options(channel_out, 1);
signal(SIGPIPE, nx_catch_pipe_signal);
logit("NX> 285 Entering the server side NX loop with proxy at: %s:%d",
nx_switch_host, nx_switch_port);
FD_ZERO(&set);
nx_run_server_side_loop_start:
while (proxy_in_open || channel_in_open)
{
selected = 0;
/*
* Put logging in #ifdef DEBUG to
* save the function calls.
*/
if (proxy_in_open)
{
/*
* Debug output is printed with logit(),
* here, to override the settings that
* might have been passed in the config
* files.
*/
#ifdef DEBUG
logit("NX> 280 Selecting proxy descriptor");
#endif
FD_SET(proxy_in, &set);
if (proxy_in >= selected)
{
selected = proxy_in + 1;
}
}
if (channel_in_open)
{
#ifdef DEBUG
logit("NX> 280 Selecting channel descriptor");
#endif
FD_SET(channel_in, &set);
if (channel_in >= selected)
{
selected = channel_in + 1;
}
}
/*
* We could exit when the first end is gone,
* anyway we try to keep the link up to send
* the pending data that would eventually
* come.
*/
#if defined(DEBUG) || defined(TIME)
diff_ts = nx_diff_timestamp(last_ts, nx_get_timestamp());
#ifdef TIME
if (diff_ts > 20)
{
logit("NX> 280 TIME! Spent: %d ms handling messages",
diff_ts);
}
#endif
last_ts = nx_get_timestamp();
logit("NX> 280 Entering select at: %s", nx_dump_timestamp());
#endif
selected = select(selected, &set, NULL, NULL, NULL);
#if defined(DEBUG) || defined(TIME)
diff_ts = nx_diff_timestamp(last_ts, nx_get_timestamp());
logit("NX> 280 Out of select with result: %d at: %s after: %d ms",
selected, nx_dump_timestamp(), diff_ts);
#ifdef TIME
if (diff_ts > 20)
{
logit("NX> 280 TIME! Spent: %d ms waiting for data",
diff_ts);
}
#endif
last_ts = nx_get_timestamp();
#endif
if (selected <= 0)
{
if (selected < 0)
{
#ifdef DEBUG
logit("NX> 280 Got error: %d in select: '%s' at: %s",
errno, strerror(errno), nx_dump_timestamp());
#endif
if (errno == EINTR)
{
continue;
}
}
error("NX> 280 Failed select on input descriptors");
goto nx_run_server_side_loop_end;
}
while (selected-- > 0)
{
if (FD_ISSET(channel_in, &set))
{
#ifdef DEBUG
logit("NX> 280 Channel in descriptor is selected");
#endif
in_fd = channel_in;
out_fd = proxy_out;
}
else
{
#ifdef DEBUG
logit("NX> 280 Proxy in descriptor is selected");
#endif
in_fd = proxy_in;
out_fd = channel_out;
}
FD_CLR(in_fd, &set);
for (;;)
{
#ifdef DEBUG
logit("NX> 280 Going to read from descriptor: %d at: %s",
in_fd, nx_dump_timestamp());
#endif
length = read(in_fd, data, sizeof(data));
#ifdef DEBUG
logit("NX> 280 Read: %d bytes from descriptor: %d at: %s",
length, in_fd, nx_dump_timestamp());
#endif
if (length <= 0)
{
if (length < 0)
{
#ifdef DEBUG
logit("NX> 280 Got error: %d in read: '%s' at: %s",
errno, strerror(errno), nx_dump_timestamp());
#endif
if (errno == EINTR)
{
continue;
}
}
#ifdef DEBUG
logit("NX> 280 Error reading from descriptor: %d at: %s",
in_fd, nx_dump_timestamp());
#endif
failed_fd = in_fd;
goto nx_run_server_side_loop_error;
}
written = 0;
for (;;)
{
result = write(out_fd, data + written, length - written);
if (result <= 0)
{
if (result < 0)
{
#ifdef DEBUG
logit("NX> 280 Got error: %d in write: '%s' at: %s",
errno, strerror(errno), nx_dump_timestamp());
#endif
if (errno == EINTR || errno == EAGAIN)
{
continue;
}
}
#ifdef DEBUG
logit("NX> 280 Error writing to descriptor: %d at: %s",
out_fd, nx_dump_timestamp());
#endif
failed_fd = out_fd;
goto nx_run_server_side_loop_error;
}
#ifdef DEBUG
logit("NX> 280 Written: %d bytes to descriptor: %d at: %s",
result, out_fd, nx_dump_timestamp());
#endif
written += result;
if (written == length)
{
break;
}
}
break;
}
}
}
nx_run_server_side_loop_end:
error("NX> 280 Exiting from the server side loop");
return;
nx_run_server_side_loop_error:
#ifdef NX_SELECTIVELY_CLOSE_DESCRIPTORS
if (failed_fd == proxy_in)
{
error("NX> 290 Failed read on proxy descriptor");
close(proxy_in);
close(channel_out);
proxy_in_open = 0;
}
else if (failed_fd == proxy_out)
{
error("NX> 290 Failed write on proxy descriptor");
close(proxy_out);
close(channel_in);
channel_in_open = 0;
}
else if (failed_fd == channel_in)
{
error("NX> 290 Failed read on channel descriptor");
close(proxy_out);
close(channel_in);
channel_in_open = 0;
}
else
{
error("NX> 290 Failed write on channel descriptor");
close(proxy_in);
close(channel_out);
proxy_in_open = 0;
}
#else /* #ifdef NX_SELECTIVELY_CLOSE_DESCRIPTORS */
if (failed_fd == proxy_in)
{
error("NX> 290 Failed read on proxy descriptor");
}
else if (failed_fd == proxy_out)
{
error("NX> 290 Failed write on proxy descriptor");
}
else if (failed_fd == channel_in)
{
error("NX> 290 Failed read on channel descriptor");
}
else
{
error("NX> 290 Failed write on channel descriptor");
}
close(proxy_in);
close(proxy_out);
close(channel_out);
close(channel_in);
proxy_in_open = 0;
channel_in_open = 0;
#endif /* #ifdef NX_SELECTIVELY_CLOSE_DESCRIPTORS */
signal(SIGPIPE, nx_catch_pipe_signal);
goto nx_run_server_side_loop_start;
}
void nx_run_client_side_loop(int proxy_fd)
{
/*
* Close our end of the proxy. The NX library
* will also close its end and will create a
* new unencrypted connection to the remote.
*/
struct timeval timeout;
close(proxy_fd);
debug("NX> 285 Entering the client side NX loop");
while (NXTransRunning(NX_FD_ANY))
{
#ifdef DEBUG
debug("NX> 280 Going to prepare a new NX loop");
#endif
timeout.tv_sec = 10;
timeout.tv_usec = 0;
/*
* Let the proxy run a new loop.
*/
NXTransContinue(&timeout);
#ifdef DEBUG
debug("NX> 280 Completed execution of the NX loop");
#endif
}
debug("NX> 285 Exiting from the client side loop");
return;
}
void nx_catch_timeout_signal(int number)
{
}
void nx_catch_pipe_signal(int number)
{
}
void nx_dump_buffer(Buffer *buffer)
{
unsigned int i;
unsigned int l;
unsigned char *p = buffer->buf;
char line[136];
debug("---");
for (i = buffer->offset, l = 0; i < buffer->end; i++, l++)
{
line[l] = p[i];
if (line[l] == '%')
{
line[l] = '?';
}
if (l == 134)
{
line[l] = '\\';
line[l + 1] = '\0';
debug(line);
l = 0;
}
}
line[l] = '\0';
if (line[0] != '\0')
{
debug(line);
}
debug("---");
}
void nx_dump_string(char *string)
{
int l;
char *p;
char line[136];
debug("---");
for (p = string, l = 0; *p != '\0'; p++, l++)
{
line[l] = *p;
if (line[l] == '%')
{
line[l] = '?';
}
if (l == 134)
{
line[l] = '\\';
line[l + 1] = '\0';
debug(line);
l = 0;
}
}
line[l] = '\0';
if (line[0] != '\0')
{
debug(line);
}
debug("---");
}
#if defined(DEBUG) || defined(TIME)
struct timeval nx_get_timestamp()
{
struct timeval ts;
gettimeofday(&ts, NULL);
return ts;
}
int nx_diff_timestamp(struct timeval ts1, struct timeval ts2)
{
long ms;
if (ts1.tv_sec == 0 && ts1.tv_usec == 0)
{
return -1;
}
/*
* Add 500 microseconds to round up
* to the nearest millisecond.
*/
ms = ((ts2.tv_sec * 1000 + (ts2.tv_usec + 500) / 1000) -
(ts1.tv_sec * 1000 + (ts1.tv_usec + 500) / 1000));
return (ms < 0 ? -1 : ms);
}
char *nx_dump_timestamp()
{
char ctime_new[25];
char *ctime_now;
struct timeval ts = nx_get_timestamp();
ctime_now = ctime((time_t *) &ts.tv_sec);
sprintf(ctime_new, "%.8s:%3.3f", ctime_now + 11,
(float) ts.tv_usec / 1000);
strncpy(ctime_now, ctime_new, 24);
return ctime_now;
}
#endif
char *nx_search_string_in_buffer(Buffer *buffer, char *start, const char *string)
{
char *end;
char *found;
int temporary;
if (start == NULL)
{
start = buffer_ptr(buffer);
}
/*
* Ensure buffer is either null-terminated
* or terminated by a newline.
*/
end = nx_search_newline_in_buffer(buffer, start);
if (end == NULL)
{
return NULL;
}
temporary = *end;
*end = '\0';
found = strstr(start, string);
*end = temporary;
return found;
}
char *nx_remove_string_in_buffer(Buffer *buffer, char *start, int length)
{
int left;
int right;
char *temporary = xmalloc(buffer_len(buffer));
if (temporary == NULL)
{
fatal("NX> 298 Out of memory creating the temporary string");
return NULL;
}
left = start - (char *) buffer_ptr(buffer);
debug("NX> 280 Lefthand remaining bytes are: %d", left);
memcpy(temporary, buffer_ptr(buffer), left);
debug("NX> 280 Righthand remaining bytes are: %d", left);
right = buffer_len(buffer) - left - length - 1;
memcpy(temporary + left, start + length + 1, right);
left += right;
debug("NX> 280 Total remaining bytes are: %d", left);
buffer_clear(buffer);
buffer_append(buffer, temporary, left);
xfree(temporary);
return buffer_ptr(buffer);
}
char *nx_search_char_in_buffer(Buffer *buffer, char *start, int value)
{
char *end;
char *found;
int temporary;
if (start == NULL)
{
start = buffer_ptr(buffer);
}
/*
* Ensure buffer is either null-terminated
* or terminated by a newline.
*/
end = nx_search_newline_in_buffer(buffer, start);
if (end == NULL)
{
return NULL;
}
temporary = *end;
*end = '\0';
found = strchr(start, value);
*end = temporary;
return found;
}
char *nx_search_newline_in_buffer(Buffer *buffer, char *start)
{
char *p;
char *end;
if (start == NULL)
{
start = buffer_ptr(buffer);
}
end = (char *) buffer_ptr(buffer) + buffer_len(buffer);
for (p = start; p < end; p++)
{
if (*p == '\n' || *p == '\r' || *p == '\0')
{
return p;
}
}
return NULL;
}
char *nx_remove_newline_in_string(char *start, int length)
{
char *p;
char *end = start + length;
for (p = start; p < end; p++)
{
if (*p == '\n' || *p == '\r')
{
*p = '\0';
}
}
return start;
}
char *nx_toupper_string(char *start)
{
char *p;
for (p = start; *p != '\0'; p++)
{
*p = toupper(*p);
}
return start;
}
void nx_wait_timeout(int seconds)
{
struct timeval timeout;
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
select(0, NULL, NULL, NULL, &timeout);
return;
}
void nx_set_socket_options(int fd, int blocking)
{
/*
* This is unused at the moment but declared
* static, so avoid the compiler warning.
*/
if (0)
{
nx_set_keepalive(fd);
}
if (blocking == 1)
{
nx_set_blocking(fd);
}
else
{
nx_set_nonblocking(fd);
}
nx_set_nodelay(fd);
nx_set_lowdelay(fd);
}
static int nx_set_nonblocking(int fd)
{
int flags;
debug("NX> 286 Setting O_NONBLOCK on descriptor: %d", fd);
flags = fcntl(fd, F_GETFL);
if (flags >= 0)
{
flags |= O_NONBLOCK;
}
if (flags < 0 || fcntl(fd, F_SETFL, flags) < 0)
{
error("NX> 286 Failed to set O_NONBLOCK on descriptor: %d", fd);
return -1;
}
debug("NX> 286 Set O_NONBLOCK on descriptor: %d", fd);
return 1;
}
static int nx_set_blocking(int fd)
{
int flags;
debug("NX> 286 Resetting O_NONBLOCK on descriptor: %d", fd);
flags = fcntl(fd, F_GETFL);
if (flags >= 0)
{
flags &= ~O_NONBLOCK;
}
if (flags < 0 || fcntl(fd, F_SETFL, flags) < 0)
{
error("NX> 286 Failed to reset O_NONBLOCK on descriptor: %d", fd);
return -1;
}
debug("NX> 286 Reset O_NONBLOCK on descriptor: %d", fd);
return 1;
}
static int nx_set_nodelay(int fd)
{
int result;
int flag = 1;
debug("NX> 286 Trying TCP_NODELAY on descriptor: %d", fd);
result = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
if (result == 0)
{
result = 1;
}
else if (result < 0)
{
#if defined(__APPLE__) || defined(__sun)
if (errno == ENOPROTOOPT)
{
result = 0;
}
#else
if (errno == EOPNOTSUPP)
{
result = 0;
}
#endif
}
if (result < 0)
{
error("NX> 286 Failed to set TCP_NODELAY on descriptor: %d", fd);
}
else if (result == 0)
{
debug("NX> 286 Option TCP_NODELAY not supported on: %d", fd);
}
else
{
debug("NX> 286 Set TCP_NODELAY on descriptor: %d", fd);
}
return result;
}
static int nx_set_keepalive(int fd)
{
int result;
int flag = 1;
debug("NX> 286 Trying SO_KEEPALIVE on descriptor: %d", fd);
result = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag));
if (result < 0)
{
error("NX> 286 Failed to set SO_KEEPALIVE on descriptor: %d", fd);
}
else
{
debug("NX> 286 Set SO_KEEPALIVE on descriptor: %d", fd);
}
return result;
}
static int nx_set_lowdelay(int fd)
{
int result;
int flag = IPTOS_LOWDELAY;
#if defined(__CYGWIN32__)
return 0;
#endif
debug("NX> 286 Trying IPTOS_LOWDELAY on descriptor: %d", fd);
result = setsockopt(fd, IPPROTO_IP, IP_TOS, &flag, sizeof(flag));
if (result == 0)
{
result = 1;
}
else if (result < 0)
{
#if defined(__APPLE__) || defined(__sun)
if (errno == ENOPROTOOPT)
{
result = 0;
}
#else
if (errno == EOPNOTSUPP)
{
result = 0;
}
#endif
}
if (result < 0)
{
error("NX> 286 Failed to set IPTOS_LOWDELAY on descriptor: %d", fd);
}
else if (result == 0)
{
debug("NX> 286 Option IPTOS_LOWDELAY not supported on: %d", fd);
}
else
{
debug("NX> 286 Set IPTOS_LOWDELAY on descriptor: %d", fd);
}
return result;
}
void nx_redirect_log_output()
{
FILE *file;
char name[1024];
const char *proxy_log;
logit("NX> 285 Enabling redirection of debug output");
/*
* Try to get the name of the file from
* the NX transport.
*/
proxy_log = NXTransFile(NX_FILE_ERRORS);
if (proxy_log != NULL)
{
logit("NX> 280 Using proxy log file: %s", proxy_log);
strncpy(name, proxy_log, 1023);
*(name + 1023) = '\0';
}
else
{
#ifdef NX_SHARE_BINDER_LOG
/*
* Use a well-known file so that you can force
* the proxy to open the same file. This file
* is also made writable by everybody. The for-
* warding process, in fact, is usually run as
* the nx user while the proxy runs as the real
* account.
*/
strcpy(name, "/tmp/errors");
logit("NX> 280 Using log file: %s", name);
#else
logit("NX> 280 Can't determine the name of the proxy log");
return;
#endif
}
file = fopen(name, "a");
if (file == NULL)
{
logit("NX> 280 Can't open log file: %s error is: %d, '%s'",
name, errno, strerror(errno));
}
else
{
if (dup2(fileno(file), fileno(stderr)) == -1)
{
logit("NX> 280 Can't redirect the log to file: %s", name);
}
}
}
const char *nx_get_environment(const char *name)
{
return getenv(name);
}
int nx_set_environment(const char *name, const char *value)
{
#ifdef __sun
static char buffer[1024];
snprintf(buffer, 1024 - 1, "%s=%s", name, value);
*(buffer + 1023) = '\0';
return putenv(buffer);
#else
return setenv(name, value, 1);
#endif
}
/**************************************************************************/
/* */
/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */
/* */
/* NXSSH, NX protocol compression and NX extensions to this software */
/* are copyright of NoMachine. Redistribution and use of the present */
/* software is allowed according to terms specified in the file LICENSE */
/* which comes in the source distribution. */
/* */
/* Check http://www.nomachine.com/licensing.html for applicability. */
/* */
/* NX and NoMachine are trademarks of Medialogic S.p.A. */
/* */
/* All rights reserved. */
/* */
/**************************************************************************/
/*
* Do we share the debug log with the proxy?
*/
extern int nx_proxy_log;
/*
* Do we check for the incoming command? This flag
* should be bound to a command line parameter.
*/
extern int nx_check_switch;
/*
* Did we receive the command? Switch is executed at
* the time the first channel receives the command,
* thus it is important to note that port forwarding
* should always be disabled when enabling the check.
*/
extern int nx_switch_received;
/*
* Parameters read from the switch command.
*/
extern char nx_switch_cookie[256];
extern char nx_switch_host[256];
extern int nx_switch_proxy;
extern int nx_switch_port;
extern int nx_switch_in;
extern int nx_switch_out;
extern char nx_switch_mode[256];
extern char nx_switch_options[1024];
extern int nx_switch_internal;
extern int nx_switch_forward;
/*
* Buffer the input while looking for the command.
* The buffering happens by flushing the input when
* a newline is received. This means that all input
* should be terminated with a newline or it will
* remain in the buffer and will never be sent to
* the packet interface.
*/
int nx_check_channel_input(Channel *channel, char *data, int *length, int limit);
/*
* Replace the select() with the version managing
* the NX descriptors.
*/
int nx_proxy_select(int maxfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
/*
* Connect to the NX transport.
*/
int nx_open_proxy_connection();
/*
* Wait for the NX transport to terminate.
*/
int nx_close_proxy_connection();
/*
* If cookie was passed, manage authentication.
*/
int nx_check_proxy_authentication(int proxy_fd);
/*
* Reassign the descriptors.
*/
int nx_switch_client_side_descriptors(Channel *channel, int proxy_fd);
/*
* Reassign the descriptors.
*/
int nx_switch_forward_descriptors(Channel *channel);
/*
* Forward port to remote sshd.
*/
int nx_switch_forward_port(Channel *channel);
/*
* Used in ssh.c to monitor the standard input
* until the switch command is received.
*/
int nx_check_standard_input();
/*
* Used in ssh.c. Connect to the proxy and run the
* restricted loop forwarding the traffic to the
* local proxy.
*/
void nx_switch_server_side_descriptors();
/*
* Set the preferred options for using the socket
* with NX.
*/
void nx_set_socket_options(int fd, int blocking);
/*
* Wrappers used to get and set the environment.
*/
const char *nx_get_environment(const char *name);
int nx_set_environment(const char *name, const char *value);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment