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

Re: Issues with Connector on net sockets


On 24.01.19 15:56, g4-lisz@xxxxxxxxxxxx wrote:

> Hi there
>
> I rewrote my socket forward client code so it uses the "new" connector
> API now. But sadly there are a few issues.
>
> First of all: Is this supposed to work with more than one channel in a
> single event loop?
>
> Here some pseudo code to understand what I'm doing:
>
> [...]
>
> Then there is a second issue when the remote side (connected to the
> socket on the server side) closes the connection. It seems that the
> connector does not  detect this, and the connection hangs forever. How
> can this be solved
>
I wrote some sample code to reproduce the second issue (attached). I
also found some solution to it: I added my own CB for handling the
channel EOF. All it does is sending back an EOF. Simple as that. This
seems to trigger the closing of the channel and also the net socket.

I probably could instead change the running condition of the main loop to:

while (ssh_channel_is_open(channel) && !ssh_channel_is_eof(channel)) {
   ....
}

But I'm not sure if everything gets shut down properly when the poll
loop ends before the channel has been closed.

Still, things do not work as expected when I try to add and remove more
channels to the event.

Regards,
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>

//#define USE_EOF_CB

const char *remote_listenhost = "google.com"; /* resolved by the remote server */
int remote_listenport = 80;

const char *local_destip = "127.0.0.1";
int local_destport = 20001;

char *ssh_host;
int ssh_port;
const char *ssh_user = "root";

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;

#ifdef USE_EOF_CB
    struct ssh_channel_callbacks_struct cb = {
        .channel_eof_function = channel_eof_cb};
    ssh_callbacks_init(&cb);
    ssh_set_channel_callbacks(channel, &cb);
#endif

    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_destport);
    if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(local_destip)))
    {
        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_destip, local_destport);

    /* 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_listenhost, remote_listenport, local_destip, local_destport);

        rc = ssh_channel_open_forward(channel,
                                      remote_listenhost, remote_listenport,
                                      local_destip, local_destport);
        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 < 3)
    {
        printf("Usage: %s <host> <port>\n", argv[0]);
        return EXIT_SUCCESS;
    }
    ssh_host = argv[1];
    ssh_port = atoi(argv[2]);

    my_ssh_session = ssh_new();
    if (my_ssh_session == NULL)
    {
        return EXIT_FAILURE;
    }

    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));

        ssh_free(my_ssh_session);
        return EXIT_FAILURE;
    }

    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));

        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return EXIT_FAILURE;
    }

    printf("We are authenticated.\n");

    rc = forward_tunnel(my_ssh_session);

    ssh_disconnect(my_ssh_session);
    ssh_free(my_ssh_session);

    return EXIT_SUCCESS; 
}

References:
Issues with Connector on net socketsg4-lisz@xxxxxxxxxxxx
Archive administrator: postmaster@lists.cynapses.org