[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [patch]: Stop connector socket-to-channel EOF flooding


On 01.04.19 13:29, g4-lisz@xxxxxxxxxxxx wrote:

> Hi there,
>
> I'm using connectors for a direct-tcp client. So this creates two
> connectors FD in --> channel out and vice versa.
>
> Now when the socket forwarding peer (not the ssh server) closes the
> connection, i.e. reading on the socket returns 0 = EOF, we end up in
> some loop with sending EOFs on the channel, until finally the client
> receives a channel close message.
>
I attached some test code to reproduce this.

Edit line 14 to your needs. It should connect to a service which does
not dicsonnecet the connenction from its side. I.e. SSH is fine. A
webserver will not do because it disconnects after answering the request.

Start a local sshd with 

shell1>  /usr/sbin/sshd -d -d -p 2222

shell2> ./fwconnector username localhost 2222

shell3> ssh admin@localhost -p 2022
Welcome to Debian...
~: exit

On shell1 you will see the > 20 lines of "channel 0: rcvd eof"

Cheers,
Till


#include <libssh/libssh.h>
#include <libssh/callbacks.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>

#include <stdlib.h>
#include <stdio.h> 
#include <errno.h>
#include <string.h>

const char *remote_desthost = "mysshserver.com"; /* resolved by the remote server */
int remote_destport = 22;

const char *local_listenip = "127.0.0.1";
int local_listenport = 2022;

char *ssh_host;
int ssh_port;
char *ssh_user;

static void channel_eof_cb(ssh_session session, ssh_channel channel, void *userdata) {
	(void) userdata;
	printf("Channel got EOF.\n");
	ssh_channel_send_eof(channel);
}

static void select_loop(ssh_session session, ssh_channel channel, int fd)
{
    ssh_connector connector_in, connector_out, connector_err;
    
	struct ssh_channel_callbacks_struct cb = {
		.channel_eof_function = channel_eof_cb
	};
	ssh_callbacks_init(&cb);
	ssh_set_channel_callbacks(channel, &cb);

    ssh_event event = ssh_event_new();

    /* stdin */
    connector_in = ssh_connector_new(session);
    ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDOUT);
    ssh_connector_set_in_fd(connector_in, fd);
    ssh_event_add_connector(event, connector_in);

    /* stdout */
    connector_out = ssh_connector_new(session);
    ssh_connector_set_out_fd(connector_out, fd);
    ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDOUT);
    ssh_event_add_connector(event, connector_out);

    /* stderr */
    /*connector_err = ssh_connector_new(session);
    ssh_connector_set_out_fd(connector_err, 2);
    ssh_connector_set_in_channel(connector_err, channel, SSH_CONNECTOR_STDERR);
    ssh_event_add_connector(event, connector_err);*/

    while (ssh_channel_is_open(channel)) {
        ssh_event_dopoll(event, 5000);
        printf("Loop alive - ssh_channel_is_eof: %d\n", ssh_channel_is_eof(channel));
    }
    ssh_event_remove_connector(event, connector_in);
    ssh_event_remove_connector(event, connector_out);
    //ssh_event_remove_connector(event, connector_err);

    ssh_connector_free(connector_in);
    ssh_connector_free(connector_out);
    //ssh_connector_free(connector_err);

    ssh_event_free(event);
}

int forward_tunnel(ssh_session session) {
	struct sockaddr_in sin;
	int listensock = -1;
	int forwardsock = -1;
	unsigned sout_len = 0;
	ssh_channel channel;
	int rc;

	listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listensock == -1) {
		fprintf(stderr, "Error creating socket\n");
		goto shutdown;
	}

	sin.sin_family = AF_INET;
	sin.sin_port = htons(local_listenport);
	if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(local_listenip))) {
		fprintf(stderr, "Invalid local IP address\n");
		goto shutdown;
	}

	if (-1 == bind(listensock, (struct sockaddr *)&sin,
		  sizeof(struct sockaddr_in))) {
		perror("Failed to connect");
		goto shutdown;
	}

	if (-1 == listen(listensock,1)) {
		perror("Failed to listen");
		goto shutdown;
	}
	
	fprintf(stdout,
		"Accepting connections on %s:%d\n",
		local_listenip, local_listenport);

	/* Setting session to non-blocking IO */
	//ssh_set_blocking(session, 0);
	
	while(1) {
		sout_len = 0;
		forwardsock = accept(listensock, NULL, &sout_len);
		if (forwardsock < 0) {
			perror("accept failed");
			continue;
		}

		channel = ssh_channel_new(session);
		if (channel == NULL) {
			fprintf(stderr, "Couldn't allocate channel: %s\n",
							ssh_get_error(session));
			close(forwardsock);
			continue;
		}
		
		fprintf(stdout, "Forwarding connection on remote %s:%d to %s:%d\n",
			remote_desthost, remote_destport, local_listenip, local_listenport);

		rc = ssh_channel_open_forward(channel,
									remote_desthost, remote_destport,
									local_listenip, local_listenport);
		if (rc != SSH_OK) {
			fprintf(stderr, "Couldn't open forwarding channel %d: %s\n",
							rc, ssh_get_error(session));
			goto shutdown;
		}

		//ssh_channel_set_blocking(channel, 1);
		select_loop(session, channel, forwardsock);
		//ssh_channel_set_blocking(channel, 0);
		
		ssh_channel_free(channel);
		close(forwardsock);
		
		fprintf(stdout, "New round\n");
	}

shutdown:
	close(listensock);
	return rc;
}

int main(int argc, char **argv)
{
	ssh_session my_ssh_session;
	int verbosity = SSH_LOG_FUNCTIONS;
	int rc;
	
	if (argc < 4) {
		printf("Usage: %s <user> <host> <port>\n", argv[0]);
		exit(0);
	}
	ssh_user = argv[1];
	ssh_host = argv[2];
	ssh_port = atoi(argv[3]);
	
	my_ssh_session = ssh_new();
	if (my_ssh_session == NULL)
		exit(-1);

	ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, ssh_host);
	ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, ssh_user);
	ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
	ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &ssh_port);

	rc = ssh_connect(my_ssh_session);
	if (rc != SSH_OK)
	{
		fprintf(stderr, "Error connecting to localhost: %s\n",
						ssh_get_error(my_ssh_session));
		goto finish;
	}
	
	printf("We are connected.\n");
	
	rc = ssh_userauth_publickey_auto(my_ssh_session, NULL, NULL);
	if (rc != SSH_AUTH_SUCCESS)
	{
		fprintf(stderr, "Authentication failed: %s\n",
						ssh_get_error(my_ssh_session));
		goto finish;
	}
	
	printf("We are authenticated.\n");
	
	forward_tunnel(my_ssh_session);

finish:

	ssh_disconnect(my_ssh_session);
	ssh_free(my_ssh_session);
}

Follow-Ups:
Re: [patch]: Stop connector socket-to-channel EOF floodingTilo Eckert <tilo.eckert@xxxxxxx>
Archive administrator: postmaster@lists.cynapses.org