Hi expert,

We have a daemon application on HP-UX 11iv3 which uses libssh2 to connect
to sftp server for file transfer.
The application works well with cc: HP C/aC++ B3910B A.06.20 but core dump
due to SIGPIPE error when compiled with another machine with cc: HP C/aC++
B3910B A.06.25.

>From stack trace, the problem seems to be due to libssh2_session_disconnect
which eventually called libc read() function which is causing SIGPIPE and
core dump.

The scenario is as below:

1. Client create an sftp connection to sftp-server with idle timeout set.
2. During idle time, sftp server disconnect the connection.
3. When client has file to send, it check connection and reconnect if
connection already gone (by checking handle with opendir() ).
4. Prior to reconnect, it will need to clean up existing connection and
part of it is calling the libssh2_session_disconnect which then causing
SIGPIPE error.

This can be reproduce by using libssh2 example code with a bit of change to
wait for timeout (also attached here):
In this case, I set my sftpserver to timeout within 30sec idle time.

int main()
{

// initialization and so on...
. . .

// server timeout set to 30 sec.
fprintf(stderr, "Wait 60 sec for server timeout\n");
sleep(60);


    fprintf(stderr, "libssh2_sftp_open()!\n");
    /* Request a file via SFTP */
    sftp_handle =
        libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);

    if (!sftp_handle) {
        fprintf(stderr, "Unable to open file with SFTP\n");
        goto check_status;
    }
    fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n");
    do {
        char mem[1024];

        /* loop until we fail */
        fprintf(stderr, "libssh2_sftp_read()!\n");
        rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
        if (rc > 0) {
            write(1, mem, rc);
        } else {
            break;
        }
    } while (1);

check_status:
fprintf(stderr, "Check SFTP conn status\n");
connection_status(sock);

fprintf(stderr, "Shutting down sftp_session...\n");

    libssh2_sftp_shutdown(sftp_session);

  shutdown:

fprintf(stderr, "Session disconect...\n");
    libssh2_session_disconnect(session, "Normal Shutdown, Thank you for
playing");

fprintf(stderr, "Session free...\n");
    libssh2_session_free(session);

. . .

==============================

The result is:
libssh2_sftp_read()!
Wait 60 sec for server timeout
libssh2_sftp_opendir()!
Unable to open dir "." with SFTP
Check SFTP conn status
Connection seems to be alive (fcntl: 0), continuing check..Connection is
lost (recv -1, 232, Connection reset by peer)Shutting down sftp_session...
Session disconect...
test_sftp> $

** There's no "Session free" test as it core dump.

gdb output:
---------------
Program received signal SIGPIPE, Broken pipe
  si_code: 0 - .
0xc000000000436810:0 in _send_sys+0x30 () from /usr/lib/hpux64/libc.so.1
(gdb) bt
#0  0xc000000000436810:0 in _send_sys+0x30 () from /usr/lib/hpux64/libc.so.1
#1  0xc00000000044ccc0:0 in send+0xe0 () from /usr/lib/hpux64/libc.so.1
#2  0xc0000000014bee90:0 in _libssh2_transport_write+0x8d0 ()
   from /home/xxxx/bin/libssh2.so.1
#3  0xc0000000014a2ef0:0 in session_disconnect+0x3c0 ()
   from /home/xxxx/bin/libssh2.so.1
#4  0xc0000000014a3030:0 in libssh2_session_disconnect_ex+0x50 ()
. . .

Now, the main question, should libssh2 actually handle this kind of
behaviour when libssh2_session_disconnect is called (a bug?) or should
client application skip calling libssh2_session_disconnect if the session
already timed out by the server? Will it be safe for client to skip calling
this function if it detected connection already unavailable in a sense that
will this cause any other issues such as memory leak etc?

This is tested for 1.2.7 & 1.2.8 with the same behavior... and this seems
to happen with SunOS cc: Sun C 5.9 SunOS_sparc Patch 124867-01 2007/07/12
as well.

Hope to get some advice soon...

Thanks & Regards,
zaideeabu
/*
 * $Id: sftp.c,v 1.17 2009/04/28 10:35:30 bagder Exp $
 *
 * Sample showing how to do SFTP transfers.
 *
 * The sample code has default values for host name, user name, password
 * and path to copy, but you can specify them on the command line like:
 *
 * "sftp 192.168.0.1 user password /tmp/secrets -p|-i|-k"
 */

#include "libssh2_config.h"
#include <libssh2.h>
#include <libssh2_sftp.h>

#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
# ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>


const char *keyfile1="~/.ssh/id_rsa.pub";
const char *keyfile2="~/.ssh/id_rsa";
const char *username="username";
const char *password="password";
const char *sftppath="/tmp/TEST";


static void kbd_callback(const char *name, int name_len, 
             const char *instruction, int instruction_len, int num_prompts,
             const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
             LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
             void **abstract)
{
    int i;
    size_t n;
    char buf[1024];
    (void)abstract;

    printf("Performing keyboard-interactive authentication.\n");

    printf("Authentication name: '");
    fwrite(name, 1, name_len, stdout);
    printf("'\n");

    printf("Authentication instruction: '");
    fwrite(instruction, 1, instruction_len, stdout);
    printf("'\n");

    printf("Number of prompts: %d\n\n", num_prompts);

    for (i = 0; i < num_prompts; i++) {
        printf("Prompt %d from server: '", i);
        fwrite(prompts[i].text, 1, prompts[i].length, stdout);
        printf("'\n");

        printf("Please type response: ");
        fgets(buf, sizeof(buf), stdin);
        n = strlen(buf);
        while (n > 0 && strchr("\r\n", buf[n - 1]))
          n--;
        buf[n] = 0;

        responses[i].text = strdup(buf);
        responses[i].length = n;

        printf("Response %d from user is '", i);
        fwrite(responses[i].text, 1, responses[i].length, stdout);
        printf("'\n\n");
    }

    printf("Done. Sending keyboard-interactive responses to server now.\n");
}

int connection_status(int socket) {

    int ret;
    char buf[10];
    int oldflags;

    oldflags = fcntl(socket, F_GETFL);
    ret = fcntl(socket, F_SETFL, oldflags | O_NONBLOCK);
    if( ret == -1 ) {
        fprintf(stderr, "Connection is lost (fcntl: -1, %d, %s)", errno, strerror(errno));
        /*_set_error(session_info, SFTP_OK, "Connection is lost (fcntl: -1, %d, %s)", errno, strerror(errno)); */
        fcntl(socket, F_SETFL, oldflags);
        return 0;
    }

    fprintf(stderr, "Connection seems to be alive (fcntl: %d), continuing check..", ret);

    /* Upon successful completion, recv() shall return the length of the message in bytes.
     * If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0.
     * Otherwise, -1 shall be returned and errno set to indicate the error.
     */
    ret = recv(socket, buf, 9, MSG_PEEK);
    if( ret == -1 ) {
        if( errno == EWOULDBLOCK || errno == EAGAIN ) {
            fprintf(stderr, "Connection is alive (recv: -1, EWOULDBLOCK/EAGAIN)");
            fcntl(socket, F_SETFL, oldflags);
        } else {
            fprintf(stderr, "Connection is lost (recv -1, %d, %s)", errno, strerror(errno));
 /*           _set_error(session_info, SFTP_OK, "Connection is lost (recv -1, %d, %s)", errno, strerror(errno)); */
            fcntl(socket, F_SETFL, oldflags);
            return 0;
        }
    } else if ( ret == 0 ) {
        fprintf(stderr, "Connection is lost (recv: 0)");
  /*     _set_error(session_info, SFTP_OK, "Peer has performed an orderly shutdown (recv 0)"); */
        fcntl(socket, F_SETFL, oldflags);
        return 0;
    } else {
        fprintf(stderr, "Connection is alive (recv: %d)", ret);
        fcntl(socket, F_SETFL, oldflags);
    }
    return 1;
}


int main(int argc, char *argv[])
{
    unsigned long hostaddr;
    int sock, i, auth_pw = 0;
    struct sockaddr_in sin;
    const char *fingerprint;
    char *userauthlist;
    LIBSSH2_SESSION *session;
    int rc;
    LIBSSH2_SFTP *sftp_session;
    LIBSSH2_SFTP_HANDLE *sftp_handle;

#ifdef WIN32
    WSADATA wsadata;

    WSAStartup(MAKEWORD(2,0), &wsadata);
#endif

    if (argc > 1) {
        hostaddr = inet_addr(argv[1]);
    } else {
        hostaddr = htonl(0x7F000001);
    }

    if(argc > 2) {
        username = argv[2];
    }
    if(argc > 3) {
        password = argv[3];
    }
    if(argc > 4) {
        sftppath = argv[4];
    }

    rc = libssh2_init (0);
    if (rc != 0) {
        fprintf (stderr, "libssh2 initialization failed (%d)\n", rc);
        return 1;
    }

    /*
     * The application code is responsible for creating the socket
     * and establishing the connection
     */
    sock = socket(AF_INET, SOCK_STREAM, 0);

    sin.sin_family = AF_INET;
    sin.sin_port = htons(22);
    sin.sin_addr.s_addr = hostaddr;
    if (connect(sock, (struct sockaddr*)(&sin),
                sizeof(struct sockaddr_in)) != 0) {
        fprintf(stderr, "failed to connect!\n");
        return -1;
    }

    /* Create a session instance
     */
    session = libssh2_session_init();
    if(!session)
        return -1;

    /* Since we have set non-blocking, tell libssh2 we are blocking */
    libssh2_session_set_blocking(session, 1);

    /* ... start it up. This will trade welcome banners, exchange keys,
     * and setup crypto, compression, and MAC layers
     */
    rc = libssh2_session_startup(session, sock);
    if(rc) {
        fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
        return -1;
    }

    /* At this point we havn't yet authenticated.  The first thing to do
     * is check the hostkey's fingerprint against our known hosts Your app
     * may have it hard coded, may go to a file, may present it to the
     * user, that's your call
     */
    fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
    fprintf(stderr, "Fingerprint: ");
    for(i = 0; i < 20; i++) {
        fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
    }
    fprintf(stderr, "\n");

    /* check what authentication methods are available */
    userauthlist = libssh2_userauth_list(session, username, strlen(username));
    printf("Authentication methods: %s\n", userauthlist);
    if (strstr(userauthlist, "password") != NULL) {
        auth_pw |= 1;
    }
    if (strstr(userauthlist, "keyboard-interactive") != NULL) {
        auth_pw |= 2;
    }
    if (strstr(userauthlist, "publickey") != NULL) {
        auth_pw |= 4;
    }

    /* if we got an 4. argument we set this option if supported */ 
    if(argc > 5) {
        if ((auth_pw & 1) && !strcasecmp(argv[5], "-p")) {
            auth_pw = 1;
        }
        if ((auth_pw & 2) && !strcasecmp(argv[5], "-i")) {
            auth_pw = 2;
        }
        if ((auth_pw & 4) && !strcasecmp(argv[5], "-k")) {
            auth_pw = 4;
        }
    }

    if (auth_pw & 1) {
        /* We could authenticate via password */
        if (libssh2_userauth_password(session, username, password)) {
            fprintf(stderr, "Authentication by password failed.\n");
            goto shutdown;
        }
    } else if (auth_pw & 2) {
        /* Or via keyboard-interactive */
        if (libssh2_userauth_keyboard_interactive(session, username, &kbd_callback) ) {
            printf("\tAuthentication by keyboard-interactive failed!\n");
            goto shutdown;
        } else {
            printf("\tAuthentication by keyboard-interactive succeeded.\n");
        }
    } else if (auth_pw & 4) {
        /* Or by public key */
        if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) {
            printf("\tAuthentication by public key failed!\n");
            goto shutdown;
        } else {
            printf("\tAuthentication by public key succeeded.\n");
        }
    } else {
        printf("No supported authentication methods found!\n");
        goto shutdown;
    }

    fprintf(stderr, "libssh2_sftp_init()!\n");
    sftp_session = libssh2_sftp_init(session);

    if (!sftp_session) {
        fprintf(stderr, "Unable to init SFTP session\n");
        goto shutdown;
    }

    fprintf(stderr, "libssh2_sftp_open()!\n");
    /* Request a file via SFTP */
    sftp_handle =
        libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);

    if (!sftp_handle) {
        fprintf(stderr, "Unable to open file with SFTP: %ld\n",
                libssh2_sftp_last_error(sftp_session));
        goto shutdown;
    }
    fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n");
    do {
        char mem[1024];

        /* loop until we fail */
        fprintf(stderr, "libssh2_sftp_read()!\n");
        rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
        if (rc > 0) {
            write(1, mem, rc);
        } else {
            break;
        }
    } while (1);

    libssh2_sftp_close(sftp_handle);

fprintf(stderr, "Wait 60 sec for server timeout\n");
sleep(60);


    fprintf(stderr, "libssh2_sftp_open()!\n");
    /* Request a file via SFTP */
    sftp_handle =
        libssh2_sftp_open(sftp_session, sftppath, LIBSSH2_FXF_READ, 0);

    if (!sftp_handle) {
        fprintf(stderr, "Unable to open file with SFTP\n");
        goto check_status;
    }
    fprintf(stderr, "libssh2_sftp_open() is done, now receive data!\n");
    do {
        char mem[1024];

        /* loop until we fail */
        fprintf(stderr, "libssh2_sftp_read()!\n");
        rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));
        if (rc > 0) {
            write(1, mem, rc);
        } else {
            break;
        }
    } while (1);

check_status:
fprintf(stderr, "Check SFTP conn status\n");
connection_status(sock);

fprintf(stderr, "Shutting down sftp_session...\n");

    libssh2_sftp_shutdown(sftp_session);

  shutdown:

fprintf(stderr, "Session disconect...\n");
    libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");

fprintf(stderr, "Session free...\n");
    libssh2_session_free(session);

#ifdef WIN32
    closesocket(sock);
#else
    close(sock);
#endif
    fprintf(stderr, "all done\n");

    libssh2_exit();

    return 0;
}
_______________________________________________
libssh2-devel http://cool.haxx.se/cgi-bin/mailman/listinfo/libssh2-devel

Reply via email to