2009-10-16 16 views
5

Estoy buscando un ejemplo de cómo usar libssh2 para configurar el reenvío de puertos ssh. Miré la API, pero hay muy poca documentación en el área de reenvío de puertos.Código de ejemplo de libssh2 utilizado para el reenvío de puertos

Por ejemplo, cuando se utiliza PuTTY's plink existe el puerto remoto para escuchar, pero también el puerto local al que se debe enviar el tráfico. ¿Es responsabilidad de los desarrolladores configurar esto? ¿Alguien puede dar un ejemplo de cómo hacer esto?

También, un ejemplo donde el puerto remoto se lleva a un puerto local sería útil. ¿Uso libssh2_channel_direct_tcpip_ex()?

Estoy dispuesto a poner una recompensa si es necesario para obtener un par de ejemplos prácticos de esto.

Respuesta

6

La clave para hacer que el reenvío de puertos libssh2 funcione fue descubrir que básicamente solo le proporciona los datos que ingresaron a ese puerto. En realidad, debe enviar los datos a un puerto local que abra:

(Tenga en cuenta que este código aún no se ha completado, no se ha verificado ningún error y el resultado no es correcto, pero da un esquema general de cómo lograr esto.)

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 Para que esto funcione, se necesitan las últimas compilaciones SVN de libssh2. Hubo errores en versiones anteriores que impedían el uso de reenvío de puertos.

+0

Parece que está pidiendo un puerto de reenvío, pero su función se titula claramente como "reverse_port_forward". ¿Este código de ejemplo trae un puerto local a un puerto de servidor, o un puerto de servidor a un puerto local? – Ralphleon

5

El código fuente libssh2 incluye desde hace algunos años un ejemplo de direct_tcpip.c que demuestra cómo crear canales SSH directos-tcpip, y desde la semana pasada un ejemplo de forward-tcpip.c que demuestra cómo crear canales SSH forward-tcpip .

direct-tcpip es lo que usa ssh -L, y forward-tcpip es lo que ssh -R usa.

Siempre es responsabilidad de los usuarios de libssh2 tratar los datos reales. libssh2 se encarga de los canales SSH y nada más. Puede beneficiarse significativamente al estudiar los SSH RFC, en particular el RFC 4254, para obtener más información acerca de qué es exactamente lo que cada tipo de canal le promete y, por lo tanto, qué puede esperar de libssh2.

Cuestiones relacionadas