Hey,

I just wanted to ask if there is any update.

Thanks!

Hey John,

I prepared a minimal example which shows the behavior I described earlier.

#include <sys/socket.h>
#include <arpa/inet.h>

#include <fcntl.h>
#include <wolfssh/ssh.h>
#include <errno.h>
#include <sys/epoll.h>

#define SSH_SERVER_ADDR "192.168.188.69"
#define SSH_USER "myUsername"
#define SSH_PASS "myPassword"

int sock = 0;
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;

struct epoll_event events[5];

enum states
{
    CONNECTING,
    LOGIN,
    DONE
};

int currState = CONNECTING;

int setup_socket();

int connect_sock();

int get_socket_error();

int init_ssh();

int authCb(byte authType, WS_UserAuthData* authData, void* userData);

int login();

int main()
{
    // create nonblocking socket
    if(setup_socket() < 0)
    {
        perror("Setting up socket failed");
        if(sock > 0)
            close(sock);
        exit(0);
    }

    // call non-blocking connect
    if(connect_sock() < 0)
    {
        perror("Connecting failed");
        close(sock);
        exit(0);
    }

    // initialize wolfssh
    if(init_ssh() < 0)
    {
        printf("ssh error: %d", wolfSSH_get_error(ssh));
        if(ctx)
            wolfSSH_CTX_free(ctx);
        if(ssh)
            wolfSSH_free(ssh);
        close(sock);
        exit(0);
    }

    // setup epoll
    int epoll = epoll_create(1);

    struct epoll_event event;
    event.events = EPOLLIN | EPOLLOUT;
    event.data.fd = sock;
    if(epoll_ctl(epoll, EPOLL_CTL_ADD, sock, &event) < 0)
    {
        perror("epoll_ctl");
        wolfSSH_CTX_free(ctx);
        wolfSSH_free(ssh);
        close(sock);
        exit(0);
    }

    int nfds;
    int notDone = 1;
    while(notDone)
    {
        nfds = epoll_wait(epoll, events, sizeof(events), 3);
        if(nfds == -1)
        {
            perror("epoll_wait");
            break;
        }

        int ret;
        for(int n = 0; n < nfds; ++n)
        {
            if(events[n].data.fd == sock && (events[n].events & (EPOLLIN | EPOLLOUT)))
            {
                switch(currState)
                {
                    case CONNECTING:
                        ret = get_socket_error();
                        if(ret != 0 && ret != EINPROGRESS)
                        {
                            printf("Failed to connect, %s\n", strerror(ret));
                            currState = DONE;
                        }
                        else if(ret != EINPROGRESS)
                        {
                            printf("Connected\n");
                            currState = LOGIN;
                        }
                        else
                        {
                            currState = CONNECTING;
                        }
                        break;
                    case LOGIN:
                        ret = login();
                        if(ret == WS_SUCCESS)
                        {
                            printf("Login success");
                            currState = DONE;
                        }
                        else if(ret == WS_WANT_READ || ret == WS_WANT_WRITE)
                        {
                            // repeat calling login()
                            currState = LOGIN;
                        }
                        else
                        {
                            printf("Login failed, code %d", wolfSSH_get_error(ssh));
                            currState = DONE;
                        }
                        break;
                    case DONE:
                        notDone = 0;
                        break;
                }
            }
        }
    }

    wolfSSH_shutdown(ssh);
    wolfSSH_CTX_free(ctx);
    wolfSSH_free(ssh);
    close(sock);

    return 0;
}

int setup_socket()
{
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
        return -1;

    // set non-blocking
    if(fcntl(sock, F_SETFL, O_NONBLOCK | fcntl(sock, F_GETFL, 0)) < 0)
        return -1;

    return 0;
}

int connect_sock()
{
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(22);
    inet_pton(AF_INET, SSH_SERVER_ADDR, &(addr.sin_addr));

    if(connect(sock, (struct sockaddr*)(&addr), sizeof(addr)) < 0 && errno != EINPROGRESS)
        return -1;

    return 0;
}

int get_socket_error()
{
    int err;
    socklen_t size = sizeof(err);
    int ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &size);
    if(ret < 0)
        return ret;

    return err;
}

int init_ssh()
{
    wolfSSH_Init();

    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
    if(!ctx)
        return -1;

    ssh = wolfSSH_new(ctx);
    if(!ssh)
        return -1;

    int ret = wolfSSH_set_fd(ssh, sock);
    if(ret != WS_SUCCESS)
        return -1;

    // set channel to shell
    ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SHELL, NULL, 0);
    if(ret != WS_SUCCESS)
        return -1;

    // set auth callback
    wolfSSH_SetUserAuth(ctx, authCb);
    // set callback data
    wolfSSH_SetUserAuthCtx(ssh, NULL);
    // set username
    wolfSSH_SetUsername(ssh, SSH_USER);

    return 0;
}

int authCb(byte authType, WS_UserAuthData* authData, void* userData)
{

    if(authType == WOLFSSH_USERAUTH_PASSWORD)
    {
        authData->sf.password.password = (byte*)(SSH_PASS);
        authData->sf.password.passwordSz = strlen(SSH_PASS);
    }
    else
    {
        return WOLFSSH_USERAUTH_REJECTED;
    }

    return WOLFSSH_USERAUTH_SUCCESS;
}

int login()
{
    int ret = wolfSSH_connect(ssh);

    // Here comes th odd part. If you remove the call to usleep(),
    // WS_OVERFLOW_E is returned by wolfssh.
    // With usleep it works perfectly.
    usleep(10000);

    if(ret != WS_SUCCESS)
    {
        return wolfSSH_get_error(ssh);
    }

    return WS_SUCCESS;
}

Thank you very much!

Hi Eric,

Thanks for your reply. I am compiling for x86 & x64 architectures, but I am using uclibc. As I said it actually works if I add a call to sleep() after wolfSSH_connect() and sometimes if I enable the wolfssh debug log.

Hey,

I'm having trouble figuring out how to use wolfSSH_connect() with epoll. wolfSSH_connect() is called again on every EPOLLIN/EPOLLOUT event if the previous call to the function returned WS_WANT_READ/WS_WANT_WRITE. But I always get an WS_OVERFLOW_E error after the server sent his DH Key Exchange Reply.

It works if I add a delay of about 100ms after (not before!) the call to wolfSSH_connect(), but that somehow defeats the purpose of using non-blocking sockets.

Am I using the function in a wrong way?