2009-10-16 5 views
5

Я ищу пример использования libssh2 для настройки перенаправления портов ssh. Я просмотрел API, но очень мало способов документации в области переадресации портов.Пример кода libssh2, используемого для переадресации портов

Например, при использовании PuTTY plink есть удаленный порт для прослушивания, но также и локальный порт, на который должен быть отправлен трафик. Обязан ли разработчики установить это? Может ли кто-нибудь привести пример того, как это сделать?

Также будет полезен пример, когда удаленный порт подключен к локальному порту. Использую ли я libssh2_channel_direct_tcpip_ex()?

Я готов поднять щедрость, если потребуется, чтобы получить пару рабочих примеров этого.

ответ

6

Ключом к созданию переадресации портов libssh2 было обнаружение того, что он в основном просто дает вам данные, которые поступают в этот порт. Фактически вы отправляете данные на локальный порт, который вы открываете:

(Примечание. Этот код еще не завершен, проверка ошибок не выполняется, и приведение потока неверно, но оно дает общую схему о том, как это сделать.)

void reverse_port_forward(CMainDlg* dlg, addrinfo * hubaddr, std::string username, std::string password, int port) 
{ 
    int iretval; 
    unsigned long mode = 1; 
    int last_socket_err = 0; 
    int other_port = 0; 
    fd_set read_set, write_set; 

    SOCKET sshsock = socket(AF_INET, SOCK_STREAM, 0); 
    iretval = connect(sshsock, hubaddr->ai_addr, hubaddr->ai_addrlen); 
    if (iretval != 0) 
    ::PostQuitMessage(0); 

    LIBSSH2_SESSION * session = NULL; 
    session = libssh2_session_init(); 

    iretval = libssh2_session_startup(session, sshsock); 
    if (iretval) 
    ::PostQuitMessage(0); 

    iretval = libssh2_userauth_password(session, username.c_str(), password.c_str()); 

    dlg->m_track_status(dlg, 1, 0, "Authorized"); 

    LIBSSH2_LISTENER* listener = NULL; 
    listener = libssh2_channel_forward_listen_ex(session, "127.0.0.1", port, &other_port, 1); 
    if (!listener) 
    ::PostQuitMessage(0); 

    LIBSSH2_CHANNEL* channel = NULL; 

    ioctlsocket(sshsock, FIONBIO, &mode); 
    libssh2_session_set_blocking(session, 0); // non-blocking 
    int err = LIBSSH2_ERROR_EAGAIN; 
    while (err == LIBSSH2_ERROR_EAGAIN) 
    { 
    channel = libssh2_channel_forward_accept(listener); 
    if (channel) break; 
    err = libssh2_session_last_errno(session); 
    boost::this_thread::yield(); 
    } 

    if (channel) 
    { 
    char buf[MAX_BUF_LEN]; 
    char* chunk; 
    long bytes_read = 0; 
    long bytes_written = 0; 
    int total_set = 0; 
    timeval wait; 
    wait.tv_sec = 0; 
    wait.tv_usec = 2000; 

    sockaddr_in localhost; 
    localhost.sin_family = AF_INET; 
    localhost.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    localhost.sin_port = htons(5900); 
    SOCKET local_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    ioctlsocket(local_sock, FIONBIO, &mode); 
    iretval = connect(local_sock, (sockaddr*) &localhost, sizeof(localhost)); 
    if (iretval == SOCKET_ERROR) 
     iretval = WSAGetLastError(); 

    while (1) 
    { 
     bytes_read = libssh2_channel_read(channel, buf, MAX_BUF_LEN); 
     if (bytes_read >= 0){ 
     FD_ZERO(&read_set); 
     FD_ZERO(&write_set); 
     FD_SET(local_sock, &write_set); 

     // wait until the socket can be written to 
     while (select(0, &read_set, &write_set, NULL, &wait) < 1) 
      boost::this_thread::yield(); 

     if (FD_ISSET(local_sock, &write_set)) 
     { 
      FD_CLR(local_sock, &write_set); 
      chunk = buf; 

      // everything may not get written in this call because we're non blocking. So 
      // keep writing more data until we've emptied the buffer pointer. 
      while ((bytes_written = send(local_sock, chunk, bytes_read, 0)) < bytes_read) 
      { 
      // if it couldn't write anything because the buffer is full, bytes_written 
      // will be negative which won't help our pointer math much 
      if (bytes_written > 0) 
      { 
       chunk = buf + bytes_written; 
       bytes_read -= bytes_written; 
       if (bytes_read == 0) 
       break; 
      } 
      FD_ZERO(&read_set); 
      FD_ZERO(&write_set); 
      FD_SET(local_sock, &write_set); 

      // wait until the socket can be written to 
      while (select(0, &read_set, &write_set, NULL, &wait) < 1) 
       boost::this_thread::yield(); 
      } 

     } 
     } 

     FD_ZERO(&read_set); 
     FD_ZERO(&write_set); 
     FD_SET(local_sock, &read_set); 
     select(0, &read_set, &write_set, NULL, &wait); 
     if (FD_ISSET(local_sock, &read_set)) 
     { 
     FD_CLR(local_sock, &read_set); 
     bytes_read = recv(local_sock, buf, MAX_BUF_LEN, 0); 
     if (bytes_read >= 0) 
     { 
      while ((bytes_written = libssh2_channel_write_ex(channel, 0, buf, bytes_read)) == LIBSSH2_ERROR_EAGAIN) 
      boost::this_thread::yield(); 
     } 
     } 
     boost::this_thread::yield(); 
    } // while 
    } // if channel 
} 

PS Для выполнения этой работы требуются последние сборки SVN libssh2. В предыдущих версиях были ошибки, которые препятствовали использованию порта.

+0

Он, кажется, просил открыть порт, но ваша функция явно называется «reverse_port_forward». Приводит ли этот пример код локального порта к порту сервера или порт сервера к локальному порту? – Ralphleon

5

Исходный код библиотеки libssh2 включает в себя с нескольких лет пример direct_tcpip.c, который демонстрирует, как создавать каналы SSH прямого tcpip, а с прошлой недели - пример forward-tcpip.c, который демонстрирует, как создавать SSH-каналы forward-tcpip ,

direct-tcpip - это то, что использует ssh -L, а forward-tcpip - это то, что использует ssh -R.

Пользователь libssh2 всегда несет ответственность за фактические данные. libssh2 заботится о SSH-каналах и ничего больше. Вы можете значительно выиграть от изучения RFC SSH, в частности RFC 4254, чтобы узнать больше о том, что обещает вам каждый тип канала, и, следовательно, чего вы можете ожидать от libssh2.

Смежные вопросы