2006-07-10 12:43:11 +02:00
/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
2006-07-04 16:07:08 +02:00
* All rights reserved .
*
* Redistribution and use in source and binary forms ,
* with or without modification , are permitted provided
* that the following conditions are met :
*
* Redistributions of source code must retain the above
* copyright notice , this list of conditions and the
* following disclaimer .
*
* Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES ,
* INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ,
* WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE .
*/
# include "libssh2_priv.h"
# include <errno.h>
# include <fcntl.h>
# ifndef WIN32
# include <unistd.h>
# endif
# include <openssl/evp.h>
# include <openssl/rand.h>
/* Needed for struct iovec on some platforms */
# ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
# endif
# ifdef HAVE_POLL
# include <sys / poll.h>
# else
# ifdef HAVE_SELECT
# ifdef HAVE_SYS_SELECT_H
# include <sys / select.h>
# else
# include <sys / time.h>
# include <sys / types.h>
# endif
# endif
# endif
/* {{{ libssh2_packet_queue_listener
* Queue a connection request for a listener
*/
inline int libssh2_packet_queue_listener ( LIBSSH2_SESSION * session , unsigned char * data , unsigned long datalen )
{
/* Look for a matching listener */
unsigned char * s = data + ( sizeof ( " forwarded-tcpip " ) - 1 ) + 5 ;
unsigned long packet_len = 17 + ( sizeof ( " Forward not requested " ) - 1 ) ;
unsigned char * p , packet [ 17 + ( sizeof ( " Forward not requested " ) - 1 ) ] ;
/* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
LIBSSH2_LISTENER * l = session - > listeners ;
char failure_code = 1 ; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */
unsigned long sender_channel , initial_window_size , packet_size ;
unsigned char * host , * shost ;
unsigned long port , sport , host_len , shost_len ;
sender_channel = libssh2_ntohu32 ( s ) ; s + = 4 ;
initial_window_size = libssh2_ntohu32 ( s ) ; s + = 4 ;
packet_size = libssh2_ntohu32 ( s ) ; s + = 4 ;
host_len = libssh2_ntohu32 ( s ) ; s + = 4 ;
host = s ; s + = host_len ;
port = libssh2_ntohu32 ( s ) ; s + = 4 ;
shost_len = libssh2_ntohu32 ( s ) ; s + = 4 ;
shost = s ; s + = shost_len ;
sport = libssh2_ntohu32 ( s ) ; s + = 4 ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Remote received connection from %s:%ld to %s:%ld " , shost , sport , host , port ) ;
# endif
while ( l ) {
2006-07-10 12:43:11 +02:00
if ( ( l - > port = = port ) & &
2006-07-04 16:07:08 +02:00
( strlen ( l - > host ) = = host_len ) & &
( memcmp ( l - > host , host , host_len ) = = 0 ) ) {
/* This is our listener */
LIBSSH2_CHANNEL * channel , * last_queued = l - > queue ;
if ( l - > queue_maxsize & &
( l - > queue_maxsize < = l - > queue_size ) ) {
/* Queue is full */
failure_code = 4 ; /* SSH_OPEN_RESOURCE_SHORTAGE */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Listener queue full, ignoring " ) ;
# endif
break ;
}
2006-07-10 12:43:11 +02:00
channel = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_CHANNEL ) ) ;
2006-07-04 16:07:08 +02:00
if ( ! channel ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate a channel for new connection " , 0 ) ;
failure_code = 4 ; /* SSH_OPEN_RESOURCE_SHORTAGE */
break ;
}
memset ( channel , 0 , sizeof ( LIBSSH2_CHANNEL ) ) ;
channel - > session = session ;
channel - > channel_type_len = sizeof ( " forwarded-tcpip " ) - 1 ;
2006-07-10 12:43:11 +02:00
channel - > channel_type = LIBSSH2_ALLOC ( session , channel - > channel_type_len + 1 ) ;
2006-07-04 16:07:08 +02:00
if ( ! channel - > channel_type ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate a channel for new connection " , 0 ) ;
LIBSSH2_FREE ( session , channel ) ;
failure_code = 4 ; /* SSH_OPEN_RESOURCE_SHORTAGE */
break ;
}
memcpy ( channel - > channel_type , " forwarded-tcpip " , channel - > channel_type_len + 1 ) ;
channel - > remote . id = sender_channel ;
channel - > remote . window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT ;
channel - > remote . window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT ;
channel - > remote . packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT ;
channel - > local . id = libssh2_channel_nextid ( session ) ;
channel - > local . window_size_initial = initial_window_size ;
channel - > local . window_size = initial_window_size ;
channel - > local . packet_size = packet_size ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Connection queued: channel %lu/%lu win %lu/%lu packet %lu/%lu " ,
channel - > local . id , channel - > remote . id ,
channel - > local . window_size , channel - > remote . window_size ,
channel - > local . packet_size , channel - > remote . packet_size ) ;
# endif
p = packet ;
* ( p + + ) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION ;
libssh2_htonu32 ( p , channel - > remote . id ) ; p + = 4 ;
libssh2_htonu32 ( p , channel - > local . id ) ; p + = 4 ;
libssh2_htonu32 ( p , channel - > remote . window_size_initial ) ; p + = 4 ;
libssh2_htonu32 ( p , channel - > remote . packet_size ) ; p + = 4 ;
if ( libssh2_packet_write ( session , packet , 17 ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send channel open confirmation " , 0 ) ;
return - 1 ;
}
/* Link the channel into the end of the queue list */
if ( ! last_queued ) {
l - > queue = channel ;
return 0 ;
}
while ( last_queued - > next ) last_queued = last_queued - > next ;
last_queued - > next = channel ;
channel - > prev = last_queued ;
l - > queue_size + + ;
return 0 ;
}
l = l - > next ;
}
/* We're not listening to you */
{
p = packet ;
* ( p + + ) = SSH_MSG_CHANNEL_OPEN_FAILURE ;
libssh2_htonu32 ( p , sender_channel ) ; p + = 4 ;
libssh2_htonu32 ( p , failure_code ) ; p + = 4 ;
libssh2_htonu32 ( p , sizeof ( " Forward not requested " ) - 1 ) ; p + = 4 ;
memcpy ( s , " Forward not requested " , sizeof ( " Forward not requested " ) - 1 ) ; p + = sizeof ( " Forward not requested " ) - 1 ;
libssh2_htonu32 ( p , 0 ) ;
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send open failure " , 0 ) ;
return - 1 ;
}
return 0 ;
}
}
/* }}} */
/* {{{ libssh2_packet_x11_open
* Accept a forwarded X11 connection
*/
inline int libssh2_packet_x11_open ( LIBSSH2_SESSION * session , unsigned char * data , unsigned long datalen )
{
int failure_code = 2 ; /* SSH_OPEN_CONNECT_FAILED */
unsigned char * s = data + ( sizeof ( " x11 " ) - 1 ) + 5 ;
unsigned long packet_len = 17 + ( sizeof ( " X11 Forward Unavailable " ) - 1 ) ;
unsigned char * p , packet [ 17 + ( sizeof ( " X11 Forward Unavailable " ) - 1 ) ] ;
/* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
LIBSSH2_CHANNEL * channel ;
unsigned long sender_channel , initial_window_size , packet_size ;
unsigned char * shost ;
unsigned long sport , shost_len ;
sender_channel = libssh2_ntohu32 ( s ) ; s + = 4 ;
initial_window_size = libssh2_ntohu32 ( s ) ; s + = 4 ;
packet_size = libssh2_ntohu32 ( s ) ; s + = 4 ;
shost_len = libssh2_ntohu32 ( s ) ; s + = 4 ;
shost = s ; s + = shost_len ;
sport = libssh2_ntohu32 ( s ) ; s + = 4 ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " X11 Connection Received from %s:%ld on channel %lu " , shost , sport , sender_channel ) ;
# endif
if ( session - > x11 ) {
2006-07-10 12:43:11 +02:00
channel = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_CHANNEL ) ) ;
2006-07-04 16:07:08 +02:00
if ( ! channel ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate a channel for new connection " , 0 ) ;
failure_code = 4 ; /* SSH_OPEN_RESOURCE_SHORTAGE */
goto x11_exit ;
}
memset ( channel , 0 , sizeof ( LIBSSH2_CHANNEL ) ) ;
channel - > session = session ;
channel - > channel_type_len = sizeof ( " x11 " ) - 1 ;
2006-07-10 12:43:11 +02:00
channel - > channel_type = LIBSSH2_ALLOC ( session , channel - > channel_type_len + 1 ) ;
2006-07-04 16:07:08 +02:00
if ( ! channel - > channel_type ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate a channel for new connection " , 0 ) ;
LIBSSH2_FREE ( session , channel ) ;
failure_code = 4 ; /* SSH_OPEN_RESOURCE_SHORTAGE */
goto x11_exit ;
}
memcpy ( channel - > channel_type , " x11 " , channel - > channel_type_len + 1 ) ;
channel - > remote . id = sender_channel ;
channel - > remote . window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT ;
channel - > remote . window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT ;
channel - > remote . packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT ;
channel - > local . id = libssh2_channel_nextid ( session ) ;
channel - > local . window_size_initial = initial_window_size ;
channel - > local . window_size = initial_window_size ;
channel - > local . packet_size = packet_size ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " X11 Connection established: channel %lu/%lu win %lu/%lu packet %lu/%lu " ,
channel - > local . id , channel - > remote . id ,
channel - > local . window_size , channel - > remote . window_size ,
channel - > local . packet_size , channel - > remote . packet_size ) ;
# endif
p = packet ;
* ( p + + ) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION ;
libssh2_htonu32 ( p , channel - > remote . id ) ; p + = 4 ;
libssh2_htonu32 ( p , channel - > local . id ) ; p + = 4 ;
libssh2_htonu32 ( p , channel - > remote . window_size_initial ) ; p + = 4 ;
libssh2_htonu32 ( p , channel - > remote . packet_size ) ; p + = 4 ;
if ( libssh2_packet_write ( session , packet , 17 ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send channel open confirmation " , 0 ) ;
return - 1 ;
}
/* Link the channel into the session */
if ( session - > channels . tail ) {
session - > channels . tail - > next = channel ;
channel - > prev = session - > channels . tail ;
} else {
session - > channels . head = channel ;
channel - > prev = NULL ;
}
channel - > next = NULL ;
session - > channels . tail = channel ;
/* Pass control to the callback, they may turn right around and free the channel, or actually use it */
2006-07-10 12:43:11 +02:00
LIBSSH2_X11_OPEN ( channel , shost , sport ) ;
2006-07-04 16:07:08 +02:00
return 0 ;
} else {
failure_code = 4 ; /* SSH_OPEN_RESOURCE_SHORTAGE */
}
x11_exit :
p = packet ;
* ( p + + ) = SSH_MSG_CHANNEL_OPEN_FAILURE ;
libssh2_htonu32 ( p , sender_channel ) ; p + = 4 ;
libssh2_htonu32 ( p , failure_code ) ; p + = 4 ;
libssh2_htonu32 ( p , sizeof ( " X11 Forward Unavailable " ) - 1 ) ; p + = 4 ;
memcpy ( s , " X11 Forward Unavailable " , sizeof ( " X11 Forward Unavailable " ) - 1 ) ; p + = sizeof ( " X11 Forward Unavailable " ) - 1 ;
libssh2_htonu32 ( p , 0 ) ;
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send open failure " , 0 ) ;
return - 1 ;
}
return 0 ;
}
/* }}} */
/* {{{ libssh2_packet_new
* Create a new packet and attach it to the brigade
*/
static int libssh2_packet_add ( LIBSSH2_SESSION * session , unsigned char * data , size_t datalen , int macstate )
{
LIBSSH2_PACKET * packet ;
unsigned long data_head = 0 ;
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Packet type %d received, length=%d " , ( int ) data [ 0 ] , ( int ) datalen ) ;
# endif
if ( macstate = = LIBSSH2_MAC_INVALID ) {
if ( session - > macerror ) {
2006-07-10 12:43:11 +02:00
if ( LIBSSH2_MACERROR ( session , data , datalen ) = = 0 ) {
2006-07-04 16:07:08 +02:00
/* Calling app has given the OK, Process it anyway */
macstate = LIBSSH2_MAC_CONFIRMED ;
} else {
libssh2_error ( session , LIBSSH2_ERROR_INVALID_MAC , " Invalid Message Authentication Code received " , 0 ) ;
if ( session - > ssh_msg_disconnect ) {
LIBSSH2_DISCONNECT ( session , SSH_DISCONNECT_MAC_ERROR , " Invalid MAC received " , sizeof ( " Invalid MAC received " ) - 1 , " " , 0 ) ;
}
return - 1 ;
}
} else {
libssh2_error ( session , LIBSSH2_ERROR_INVALID_MAC , " Invalid Message Authentication Code received " , 0 ) ;
if ( session - > ssh_msg_disconnect ) {
LIBSSH2_DISCONNECT ( session , SSH_DISCONNECT_MAC_ERROR , " Invalid MAC received " , sizeof ( " Invalid MAC received " ) - 1 , " " , 0 ) ;
}
return - 1 ;
}
}
/* A couple exceptions to the packet adding rule: */
switch ( data [ 0 ] ) {
case SSH_MSG_DISCONNECT :
{
char * message , * language ;
int reason , message_len , language_len ;
reason = libssh2_ntohu32 ( data + 1 ) ;
message_len = libssh2_ntohu32 ( data + 5 ) ;
2006-07-10 12:43:11 +02:00
message = data + 9 ; /* packet_type(1) + reason(4) + message_len(4) */
2006-07-04 16:07:08 +02:00
language_len = libssh2_ntohu32 ( data + 9 + message_len ) ;
/* This is where we hack on the data a little,
* Use the MSB of language_len to to a terminating NULL ( In all liklihood it is already )
* Shift the language tag back a byte ( In all likelihood it ' s zero length anyway
* Store a NULL in the last byte of the packet to terminate the language string
* With the lengths passed this isn ' t * REALLY * necessary , but it ' s " kind "
*/
message [ message_len ] = ' \0 ' ;
2006-07-10 12:43:11 +02:00
language = data + 9 + message_len + 3 ;
2006-07-04 16:07:08 +02:00
if ( language_len ) {
memcpy ( language , language + 1 , language_len ) ;
}
language [ language_len ] = ' \0 ' ;
if ( session - > ssh_msg_disconnect ) {
LIBSSH2_DISCONNECT ( session , reason , message , message_len , language , language_len ) ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Disconnect(%d): %s(%s) " , reason , message , language ) ;
# endif
LIBSSH2_FREE ( session , data ) ;
session - > socket_state = LIBSSH2_SOCKET_DISCONNECTED ;
return - 1 ;
}
break ;
case SSH_MSG_IGNORE :
/* As with disconnect, back it up one and add a trailing NULL */
memcpy ( data + 4 , data + 5 , datalen - 5 ) ;
data [ datalen ] = ' \0 ' ;
if ( session - > ssh_msg_ignore ) {
2006-07-10 12:43:11 +02:00
LIBSSH2_IGNORE ( session , data + 4 , datalen - 5 ) ;
2006-07-04 16:07:08 +02:00
}
LIBSSH2_FREE ( session , data ) ;
return 0 ;
break ;
case SSH_MSG_DEBUG :
{
int always_display = data [ 0 ] ;
char * message , * language ;
int message_len , language_len ;
message_len = libssh2_ntohu32 ( data + 2 ) ;
2006-07-10 12:43:11 +02:00
message = data + 6 ; /* packet_type(1) + display(1) + message_len(4) */
2006-07-04 16:07:08 +02:00
language_len = libssh2_ntohu32 ( data + 6 + message_len ) ;
/* This is where we hack on the data a little,
* Use the MSB of language_len to to a terminating NULL ( In all liklihood it is already )
* Shift the language tag back a byte ( In all likelihood it ' s zero length anyway
* Store a NULL in the last byte of the packet to terminate the language string
* With the lengths passed this isn ' t * REALLY * necessary , but it ' s " kind "
*/
message [ message_len ] = ' \0 ' ;
2006-07-10 12:43:11 +02:00
language = data + 6 + message_len + 3 ;
2006-07-04 16:07:08 +02:00
if ( language_len ) {
memcpy ( language , language + 1 , language_len ) ;
}
language [ language_len ] = ' \0 ' ;
if ( session - > ssh_msg_debug ) {
LIBSSH2_DEBUG ( session , always_display , message , message_len , language , language_len ) ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
/* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Debug Packet: %s " , message ) ;
# endif
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
break ;
case SSH_MSG_CHANNEL_EXTENDED_DATA :
data_head + = 4 ; /* streamid(4) */
case SSH_MSG_CHANNEL_DATA :
data_head + = 9 ; /* packet_type(1) + channelno(4) + datalen(4) */
{
LIBSSH2_CHANNEL * channel = libssh2_channel_locate ( session , libssh2_ntohu32 ( data + 1 ) ) ;
if ( ! channel ) {
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_UNKNOWN , " Packet received for unknown channel, ignoring " , 0 ) ;
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
{
unsigned long stream_id = 0 ;
if ( data [ 0 ] = = SSH_MSG_CHANNEL_EXTENDED_DATA ) {
stream_id = libssh2_ntohu32 ( data + 5 ) ;
}
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " %d bytes received for channel %lu/%lu stream #%lu " , ( int ) ( datalen - data_head ) , channel - > local . id , channel - > remote . id , stream_id ) ;
}
# endif
if ( ( channel - > remote . extended_data_ignore_mode = = LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE ) & & ( data [ 0 ] = = SSH_MSG_CHANNEL_EXTENDED_DATA ) ) {
/* Pretend we didn't receive this */
LIBSSH2_FREE ( session , data ) ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Ignoring extended data and refunding %d bytes " , ( int ) ( datalen - 13 ) ) ;
# endif
/* Adjust the window based on the block we just freed */
libssh2_channel_receive_window_adjust ( channel , datalen - 13 , 0 ) ;
return 0 ;
}
/* REMEMBER! remote means remote as source of data, NOT remote window! */
if ( channel - > remote . packet_size < ( datalen - data_head ) ) {
/* Spec says we MAY ignore bytes sent beyond packet_size */
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED , " Packet contains more data than we offered to receive, truncating " , 0 ) ;
datalen = channel - > remote . packet_size + data_head ;
}
if ( channel - > remote . window_size < = 0 ) {
/* Spec says we MAY ignore bytes sent beyond window_size */
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED , " The current receive window is full, data ignored " , 0 ) ;
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
/* Reset EOF status */
channel - > remote . eof = 0 ;
if ( ( datalen - data_head ) > channel - > remote . window_size ) {
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED , " Remote sent more data than current window allows, truncating " , 0 ) ;
datalen = channel - > remote . window_size + data_head ;
} else {
/* Now that we've received it, shrink our window */
channel - > remote . window_size - = datalen - data_head ;
}
}
break ;
case SSH_MSG_CHANNEL_EOF :
{
LIBSSH2_CHANNEL * channel = libssh2_channel_locate ( session , libssh2_ntohu32 ( data + 1 ) ) ;
if ( ! channel ) {
/* We may have freed already, just quietly ignore this... */
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " EOF received for channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
channel - > remote . eof = 1 ;
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
break ;
case SSH_MSG_CHANNEL_REQUEST :
{
if ( libssh2_ntohu32 ( data + 5 ) = = sizeof ( " exit-status " ) - 1
& & ! memcmp ( " exit-status " , data + 9 , sizeof ( " exit-status " ) - 1 ) ) {
/* we've got "exit-status" packet. Set the session value */
LIBSSH2_CHANNEL * channel = libssh2_channel_locate ( session , libssh2_ntohu32 ( data + 1 ) ) ;
if ( channel ) {
channel - > exit_status = libssh2_ntohu32 ( data + 9 + sizeof ( " exit-status " ) ) ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Exit status %lu received for channel %lu/%lu " , channel - > exit_status , channel - > local . id , channel - > remote . id ) ;
# endif
}
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
}
break ;
case SSH_MSG_CHANNEL_CLOSE :
{
LIBSSH2_CHANNEL * channel = libssh2_channel_locate ( session , libssh2_ntohu32 ( data + 1 ) ) ;
if ( ! channel ) {
/* We may have freed already, just quietly ignore this... */
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Close received for channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
channel - > remote . close = 1 ;
/* TODO: Add a callback for this */
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
break ;
case SSH_MSG_CHANNEL_OPEN :
if ( ( datalen > = ( sizeof ( " forwarded-tcpip " ) + 4 ) ) & &
( ( sizeof ( " forwarded-tcpip " ) - 1 ) = = libssh2_ntohu32 ( data + 1 ) ) & &
( memcmp ( data + 5 , " forwarded-tcpip " , sizeof ( " forwarded-tcpip " ) - 1 ) = = 0 ) ) {
int retval = libssh2_packet_queue_listener ( session , data , datalen ) ;
LIBSSH2_FREE ( session , data ) ;
return retval ;
}
if ( ( datalen > = ( sizeof ( " x11 " ) + 4 ) ) & &
( ( sizeof ( " x11 " ) - 1 ) = = libssh2_ntohu32 ( data + 1 ) ) & &
( memcmp ( data + 5 , " x11 " , sizeof ( " x11 " ) - 1 ) = = 0 ) ) {
int retval = libssh2_packet_x11_open ( session , data , datalen ) ;
LIBSSH2_FREE ( session , data ) ;
return retval ;
}
break ;
case SSH_MSG_CHANNEL_WINDOW_ADJUST :
{
LIBSSH2_CHANNEL * channel = libssh2_channel_locate ( session , libssh2_ntohu32 ( data + 1 ) ) ;
unsigned long bytestoadd = libssh2_ntohu32 ( data + 5 ) ;
if ( channel & & bytestoadd ) {
channel - > local . window_size + = bytestoadd ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu " , channel - > local . id , channel - > remote . id , bytestoadd , channel - > local . window_size ) ;
# endif
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
break ;
}
2006-07-10 12:43:11 +02:00
packet = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_PACKET ) ) ;
2006-07-04 16:07:08 +02:00
memset ( packet , 0 , sizeof ( LIBSSH2_PACKET ) ) ;
packet - > data = data ;
packet - > data_len = datalen ;
packet - > data_head = data_head ;
packet - > mac = macstate ;
packet - > brigade = & session - > packets ;
packet - > next = NULL ;
if ( session - > packets . tail ) {
packet - > prev = session - > packets . tail ;
packet - > prev - > next = packet ;
session - > packets . tail = packet ;
} else {
session - > packets . head = packet ;
session - > packets . tail = packet ;
packet - > prev = NULL ;
}
if ( data [ 0 ] = = SSH_MSG_KEXINIT & & ! ( session - > state & LIBSSH2_STATE_EXCHANGING_KEYS ) ) {
/* Remote wants new keys
* Well , it ' s already in the brigade ,
* let ' s just call back into ourselves
*/
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Renegotiating Keys " ) ;
# endif
libssh2_kex_exchange ( session , 1 ) ;
/* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */
}
return 0 ;
}
/* }}} */
/* {{{ libssh2_blocking_read
* Force a blocking read , regardless of socket settings
*/
static int libssh2_blocking_read ( LIBSSH2_SESSION * session , unsigned char * buf , size_t count )
{
size_t bytes_read = 0 ;
# if !defined(HAVE_POLL) && !defined(HAVE_SELECT)
int polls = 0 ;
# endif
# ifndef WIN32
fcntl ( session - > socket_fd , F_SETFL , 0 ) ;
# else
{
u_long block = FALSE ;
ioctlsocket ( session - > socket_fd , FIONBIO , & block ) ;
}
# endif
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Blocking read: %d bytes " , ( int ) count ) ;
# endif
while ( bytes_read < count ) {
int ret ;
2006-07-10 12:43:11 +02:00
ret = recv ( session - > socket_fd , buf + bytes_read , count - bytes_read , LIBSSH2_SOCKET_RECV_FLAGS ( session ) ) ;
2006-07-04 16:07:08 +02:00
if ( ret < 0 ) {
# ifdef WIN32
switch ( WSAGetLastError ( ) ) {
case WSAEWOULDBLOCK : errno = EAGAIN ; break ;
2006-07-10 12:43:11 +02:00
case WSAENOTSOCK : errno = EBADF ; break ;
2006-07-04 16:07:08 +02:00
case WSAENOTCONN :
2006-07-10 12:43:11 +02:00
case WSAECONNABORTED : errno = ENOTCONN ; break ;
2006-07-04 16:07:08 +02:00
case WSAEINTR : errno = EINTR ; break ;
}
# endif
if ( errno = = EAGAIN ) {
# ifdef HAVE_POLL
struct pollfd read_socket ;
read_socket . fd = session - > socket_fd ;
read_socket . events = POLLIN ;
if ( poll ( & read_socket , 1 , 30000 ) < = 0 ) {
return - 1 ;
}
# elif defined(HAVE_SELECT)
fd_set read_socket ;
struct timeval timeout ;
FD_ZERO ( & read_socket ) ;
FD_SET ( session - > socket_fd , & read_socket ) ;
timeout . tv_sec = 30 ;
timeout . tv_usec = 0 ;
if ( select ( session - > socket_fd + 1 , & read_socket , NULL , NULL , & timeout ) < = 0 ) {
return - 1 ;
}
# else
if ( polls + + > LIBSSH2_SOCKET_POLL_MAXLOOPS ) {
return - 1 ;
}
usleep ( LIBSSH2_SOCKET_POLL_UDELAY ) ;
# endif /* POLL/SELECT/SLEEP */
continue ;
}
if ( errno = = EINTR ) {
continue ;
}
2006-07-10 12:43:11 +02:00
if ( ( errno = = EBADF ) | | ( errno = = EIO ) | | ( errno = = ENOTCONN ) ) {
2006-07-04 16:07:08 +02:00
session - > socket_state = LIBSSH2_SOCKET_DISCONNECTED ;
}
return - 1 ;
}
if ( ret = = 0 ) continue ;
bytes_read + = ret ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Blocking read: %d bytes actually read " , ( int ) bytes_read ) ;
# endif
return bytes_read ;
}
/* }}} */
/* {{{ libssh2_packet_read
* Collect a packet into the input brigade
* block only controls whether or not to wait for a packet to start ,
* Once a packet starts , libssh2 will block until it is complete
* Returns packet type added to input brigade ( 0 if nothing added ) , or - 1 on failure
*/
int libssh2_packet_read ( LIBSSH2_SESSION * session , int should_block )
{
int packet_type = - 1 ;
if ( session - > socket_state = = LIBSSH2_SOCKET_DISCONNECTED ) {
return 0 ;
}
# ifndef WIN32
fcntl ( session - > socket_fd , F_SETFL , O_NONBLOCK ) ;
# else
{
u_long non_block = TRUE ;
ioctlsocket ( session - > socket_fd , FIONBIO , & non_block ) ;
}
# endif
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Checking for packet: will%s block " , should_block ? " " : " not " ) ;
# endif
if ( session - > state & LIBSSH2_STATE_NEWKEYS ) {
/* Temporary Buffer
* The largest blocksize ( currently ) is 32 , the largest MAC ( currently ) is 20
*/
unsigned char block [ 2 * 32 ] , * payload , * s , tmp [ 6 ] ;
long read_len ;
unsigned long blocksize = session - > remote . crypt - > blocksize ;
unsigned long packet_len , payload_len ;
int padding_len ;
int macstate ;
int free_payload = 1 ;
/* Safely ignored in CUSTOM cipher mode */
EVP_CIPHER_CTX * ctx = ( EVP_CIPHER_CTX * ) session - > remote . crypt_abstract ;
/* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this
* For now , all blocksize sizes are 8 +
*/
if ( should_block ) {
read_len = libssh2_blocking_read ( session , block , blocksize ) ;
} else {
2006-07-10 12:43:11 +02:00
read_len = recv ( session - > socket_fd , block , 1 , LIBSSH2_SOCKET_RECV_FLAGS ( session ) ) ;
2006-07-04 16:07:08 +02:00
if ( read_len < = 0 ) {
return 0 ;
}
read_len + = libssh2_blocking_read ( session , block + read_len , blocksize - read_len ) ;
}
2006-07-10 12:43:11 +02:00
if ( read_len < blocksize ) {
2006-07-04 16:07:08 +02:00
return ( session - > socket_state = = LIBSSH2_SOCKET_DISCONNECTED ) ? 0 : - 1 ;
}
if ( session - > remote . crypt - > flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP ) {
EVP_Cipher ( ctx , block + blocksize , block , blocksize ) ;
memcpy ( block , block + blocksize , blocksize ) ;
} else {
if ( session - > remote . crypt - > crypt ( session , block , & session - > remote . crypt_abstract ) ) {
libssh2_error ( session , LIBSSH2_ERROR_DECRYPT , " Error decrypting packet preamble " , 0 ) ;
return - 1 ;
}
}
packet_len = libssh2_ntohu32 ( block ) ;
padding_len = block [ 4 ] ;
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Processing packet %lu bytes long (with %lu bytes padding) " , packet_len , padding_len ) ;
# endif
memcpy ( tmp , block , 5 ) ; /* Use this for MAC later */
payload_len = packet_len - 1 ; /* padding_len(1) */
/* Sanity Check */
if ( ( payload_len > LIBSSH2_PACKET_MAXPAYLOAD ) | |
( ( packet_len + 4 ) % blocksize ) ) {
/* If something goes horribly wrong during the decryption phase, just bailout and die gracefully */
session - > socket_state = LIBSSH2_SOCKET_DISCONNECTED ;
libssh2_error ( session , LIBSSH2_ERROR_PROTO , " Fatal protocol error, invalid payload size " , 0 ) ;
return - 1 ;
}
2006-07-10 12:43:11 +02:00
s = payload = LIBSSH2_ALLOC ( session , payload_len ) ;
2006-07-04 16:07:08 +02:00
memcpy ( s , block + 5 , blocksize - 5 ) ;
s + = blocksize - 5 ;
2006-07-10 12:43:11 +02:00
while ( ( s - payload ) < payload_len ) {
2006-07-04 16:07:08 +02:00
read_len = libssh2_blocking_read ( session , block , blocksize ) ;
2006-07-10 12:43:11 +02:00
if ( read_len < blocksize ) {
2006-07-04 16:07:08 +02:00
LIBSSH2_FREE ( session , payload ) ;
return - 1 ;
}
if ( session - > remote . crypt - > flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP ) {
EVP_Cipher ( ctx , block + blocksize , block , blocksize ) ;
memcpy ( s , block + blocksize , blocksize ) ;
} else {
if ( session - > remote . crypt - > crypt ( session , block , & session - > remote . crypt_abstract ) ) {
libssh2_error ( session , LIBSSH2_ERROR_DECRYPT , " Error decrypting packet preamble " , 0 ) ;
LIBSSH2_FREE ( session , payload ) ;
return - 1 ;
}
memcpy ( s , block , blocksize ) ;
}
s + = blocksize ;
}
read_len = libssh2_blocking_read ( session , block , session - > remote . mac - > mac_len ) ;
if ( read_len < session - > remote . mac - > mac_len ) {
LIBSSH2_FREE ( session , payload ) ;
return - 1 ;
}
/* Calculate MAC hash */
session - > remote . mac - > hash ( session , block + session - > remote . mac - > mac_len , session - > remote . seqno , tmp , 5 , payload , payload_len , & session - > remote . mac_abstract ) ;
2006-07-10 12:43:11 +02:00
macstate = ( strncmp ( block , block + session - > remote . mac - > mac_len , session - > remote . mac - > mac_len ) = = 0 ) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID ;
2006-07-04 16:07:08 +02:00
session - > remote . seqno + + ;
/* Ignore padding */
payload_len - = padding_len ;
if ( session - > remote . comp & &
strcmp ( session - > remote . comp - > name , " none " ) ) {
/* Decompress */
unsigned char * data ;
unsigned long data_len ;
if ( session - > remote . comp - > comp ( session , 0 , & data , & data_len , LIBSSH2_PACKET_MAXDECOMP , & free_payload , payload , payload_len , & session - > remote . comp_abstract ) ) {
LIBSSH2_FREE ( session , payload ) ;
return - 1 ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed) " , data_len , payload_len ) ;
# endif
if ( free_payload ) {
LIBSSH2_FREE ( session , payload ) ;
payload = data ;
payload_len = data_len ;
} else {
if ( data = = payload ) {
/* It's not to be freed, because the compression layer reused payload,
* So let ' s do the same !
*/
payload_len = data_len ;
} else {
/* No comp_method actually lets this happen, but let's prepare for the future */
LIBSSH2_FREE ( session , payload ) ;
/* We need a freeable struct otherwise the brigade won't know what to do with it */
2006-07-10 12:43:11 +02:00
payload = LIBSSH2_ALLOC ( session , data_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! payload ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for copy of uncompressed data " , 0 ) ;
return - 1 ;
}
memcpy ( payload , data , data_len ) ;
payload_len = data_len ;
}
}
}
2006-07-10 12:43:11 +02:00
packet_type = payload [ 0 ] ;
2006-07-04 16:07:08 +02:00
libssh2_packet_add ( session , payload , payload_len , macstate ) ;
} else { /* No cipher active */
unsigned char * payload ;
unsigned char buf [ 24 ] ;
unsigned long buf_len , payload_len ;
unsigned long packet_length ;
unsigned long padding_length ;
if ( should_block ) {
buf_len = libssh2_blocking_read ( session , buf , 5 ) ;
} else {
2006-07-10 12:43:11 +02:00
buf_len = recv ( session - > socket_fd , buf , 1 , LIBSSH2_SOCKET_RECV_FLAGS ( session ) ) ;
2006-07-04 16:07:08 +02:00
if ( buf_len < = 0 ) {
return 0 ;
}
buf_len + = libssh2_blocking_read ( session , buf , 5 - buf_len ) ;
}
if ( buf_len < 5 ) {
/* Something bad happened */
return - 1 ;
}
packet_length = libssh2_ntohu32 ( buf ) ;
padding_length = buf [ 4 ] ;
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Processing plaintext packet %lu bytes long (with %lu bytes padding) " , packet_length , padding_length ) ;
# endif
payload_len = packet_length - padding_length - 1 ; /* padding_length(1) */
2006-07-10 12:43:11 +02:00
payload = LIBSSH2_ALLOC ( session , payload_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! payload ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for copy of plaintext data " , 0 ) ;
return - 1 ;
}
2006-07-10 12:43:11 +02:00
if ( libssh2_blocking_read ( session , payload , payload_len ) < payload_len ) {
2006-07-04 16:07:08 +02:00
return ( session - > socket_state = = LIBSSH2_SOCKET_DISCONNECTED ) ? 0 : - 1 ;
}
while ( padding_length ) {
int l ;
/* Flush padding */
l = libssh2_blocking_read ( session , buf , padding_length ) ;
if ( l > 0 )
padding_length - = l ;
else
break ;
}
2006-07-10 12:43:11 +02:00
packet_type = payload [ 0 ] ;
2006-07-04 16:07:08 +02:00
/* MACs don't exist in non-encrypted mode */
libssh2_packet_add ( session , payload , payload_len , LIBSSH2_MAC_CONFIRMED ) ;
session - > remote . seqno + + ;
}
return packet_type ;
}
/* }}} */
/* {{{ libssh2_packet_ask
* Scan the brigade for a matching packet type , optionally poll the socket for a packet first
*/
int libssh2_packet_ask_ex ( LIBSSH2_SESSION * session , unsigned char packet_type , unsigned char * * data , unsigned long * data_len ,
unsigned long match_ofs , const unsigned char * match_buf , unsigned long match_len , int poll_socket )
{
LIBSSH2_PACKET * packet = session - > packets . head ;
if ( poll_socket ) {
if ( libssh2_packet_read ( session , 0 ) < 0 ) {
return - 1 ;
}
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Looking for packet of type: %d " , ( int ) packet_type ) ;
# endif
while ( packet ) {
if ( packet - > data [ 0 ] = = packet_type & &
( packet - > data_len > = ( match_ofs + match_len ) ) & &
( ! match_buf | | ( memcmp ( packet - > data + match_ofs , match_buf , match_len ) = = 0 ) ) ) {
* data = packet - > data ;
* data_len = packet - > data_len ;
if ( packet - > prev ) {
packet - > prev - > next = packet - > next ;
} else {
session - > packets . head = packet - > next ;
}
if ( packet - > next ) {
packet - > next - > prev = packet - > prev ;
} else {
session - > packets . tail = packet - > prev ;
}
LIBSSH2_FREE ( session , packet ) ;
return 0 ;
}
packet = packet - > next ;
}
return - 1 ;
}
/* }}} */
/* {{{ libssh2_packet_askv
* Scan for any of a list of packet types in the brigade , optionally poll the socket for a packet first
*/
int libssh2_packet_askv_ex ( LIBSSH2_SESSION * session , unsigned char * packet_types , unsigned char * * data , unsigned long * data_len ,
unsigned long match_ofs , const unsigned char * match_buf , unsigned long match_len , int poll_socket )
{
2006-07-10 12:43:11 +02:00
int i , packet_types_len = strlen ( packet_types ) ;
2006-07-04 16:07:08 +02:00
for ( i = 0 ; i < packet_types_len ; i + + ) {
if ( 0 = = libssh2_packet_ask_ex ( session , packet_types [ i ] , data , data_len , match_ofs , match_buf , match_len , i ? 0 : poll_socket ) ) {
return 0 ;
}
}
return - 1 ;
}
/* }}} */
/* {{{ libssh2_packet_require
* Loops libssh2_packet_read ( ) until the packet requested is available
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
*/
int libssh2_packet_require_ex ( LIBSSH2_SESSION * session , unsigned char packet_type , unsigned char * * data , unsigned long * data_len ,
unsigned long match_ofs , const unsigned char * match_buf , unsigned long match_len )
{
if ( libssh2_packet_ask_ex ( session , packet_type , data , data_len , match_ofs , match_buf , match_len , 0 ) = = 0 ) {
/* A packet was available in the packet brigade */
return 0 ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Blocking until packet of type %d becomes available " , ( int ) packet_type ) ;
# endif
while ( session - > socket_state = = LIBSSH2_SOCKET_CONNECTED ) {
int ret = libssh2_packet_read ( session , 1 ) ;
if ( ret < 0 ) {
return - 1 ;
}
if ( ret = = 0 ) continue ;
if ( packet_type = = ret ) {
/* Be lazy, let packet_ask pull it out of the brigade */
return libssh2_packet_ask_ex ( session , packet_type , data , data_len , match_ofs , match_buf , match_len , 0 ) ;
}
}
/* Only reached if the socket died */
return - 1 ;
}
/* }}} */
2006-07-10 12:43:11 +02:00
/* {{{ libssh2_packet_burn
* Loops libssh2_packet_read ( ) until any packet is available and promptly discards it
* Used during KEX exchange to discard badly guessed KEX_INIT packets
*/
int libssh2_packet_burn ( LIBSSH2_SESSION * session )
{
unsigned char * data ;
unsigned long data_len ;
char all_packets [ 255 ] ;
int i ;
for ( i = 1 ; i < 256 ; i + + ) all_packets [ i - 1 ] = i ;
if ( libssh2_packet_askv_ex ( session , all_packets , & data , & data_len , 0 , NULL , 0 , 0 ) = = 0 ) {
i = data [ 0 ] ;
/* A packet was available in the packet brigade, burn it */
LIBSSH2_FREE ( session , data ) ;
return i ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Blocking until packet becomes available to burn " ) ;
# endif
while ( session - > socket_state = = LIBSSH2_SOCKET_CONNECTED ) {
int ret = libssh2_packet_read ( session , 1 ) ;
if ( ret < 0 ) {
return - 1 ;
}
if ( ret = = 0 ) continue ;
/* Be lazy, let packet_ask pull it out of the brigade */
if ( 0 = = libssh2_packet_ask_ex ( session , ret , & data , & data_len , 0 , NULL , 0 , 0 ) ) {
/* Smoke 'em if you got 'em */
LIBSSH2_FREE ( session , data ) ;
return ret ;
}
}
/* Only reached if the socket died */
return - 1 ;
}
/* }}} */
2006-07-04 16:07:08 +02:00
/* {{{ libssh2_packet_requirev
* Loops libssh2_packet_read ( ) until one of a list of packet types requested is available
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
* packet_types is a null terminated list of packet_type numbers
*/
int libssh2_packet_requirev_ex ( LIBSSH2_SESSION * session , unsigned char * packet_types , unsigned char * * data , unsigned long * data_len ,
unsigned long match_ofs , const unsigned char * match_buf , unsigned long match_len )
{
if ( libssh2_packet_askv_ex ( session , packet_types , data , data_len , match_ofs , match_buf , match_len , 0 ) = = 0 ) {
/* One of the packets listed was available in the packet brigade */
return 0 ;
}
while ( session - > socket_state ! = LIBSSH2_SOCKET_DISCONNECTED ) {
int ret = libssh2_packet_read ( session , 1 ) ;
if ( ret < 0 ) {
return - 1 ;
}
if ( ret = = 0 ) {
continue ;
}
2006-07-10 12:43:11 +02:00
if ( strchr ( packet_types , ret ) ) {
2006-07-04 16:07:08 +02:00
/* Be lazy, let packet_ask pull it out of the brigade */
return libssh2_packet_askv_ex ( session , packet_types , data , data_len , match_ofs , match_buf , match_len , 0 ) ;
}
}
/* Only reached if the socket died */
return - 1 ;
}
/* }}} */
/* {{{ libssh2_packet_write
* Send a packet , encrypting it and adding a MAC code if necessary
* Returns 0 on success , non - zero on failure
*/
int libssh2_packet_write ( LIBSSH2_SESSION * session , unsigned char * data , unsigned long data_len )
{
unsigned long packet_length = data_len + 1 ;
unsigned long block_size = ( session - > state & LIBSSH2_STATE_NEWKEYS ) ? session - > local . crypt - > blocksize : 8 ;
/* At this point packet_length doesn't include the packet_len field itself */
unsigned long padding_length ;
int free_data = 0 ;
unsigned char buf [ 246 ] ; /* 6 byte header plus max padding size(240) */
# ifdef LIBSSH2_DEBUG_TRANSPORT
{
/* Show a hint of what's being sent */
char excerpt [ 32 ] ;
int ex_len = 0 , db_ofs = 0 ;
for ( ; ex_len < 24 & & db_ofs < data_len ; ex_len + = 3 , db_ofs + + ) snprintf ( excerpt + ex_len , 4 , " %02X " , data [ db_ofs ] ) ;
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Sending packet type %d, length=%lu, %s " , ( int ) data [ 0 ] , data_len , excerpt ) ;
}
# endif
if ( ( session - > state & LIBSSH2_STATE_NEWKEYS ) & &
strcmp ( session - > local . comp - > name , " none " ) ) {
if ( session - > local . comp - > comp ( session , 1 , & data , & data_len , LIBSSH2_PACKET_MAXCOMP , & free_data , data , data_len , & session - > local . comp_abstract ) ) {
return - 1 ;
}
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Compressed payload to %lu bytes " , data_len ) ;
# endif
}
# ifndef WIN32
fcntl ( session - > socket_fd , F_SETFL , 0 ) ;
# else
{
u_long non_block = FALSE ;
ioctlsocket ( session - > socket_fd , FIONBIO , & non_block ) ;
}
# endif
packet_length = data_len + 1 ; /* padding_length(1) -- MAC doesn't count -- Padding to be added soon */
padding_length = block_size - ( ( packet_length + 4 ) % block_size ) ;
if ( padding_length < 4 ) {
padding_length + = block_size ;
}
/* TODO: Maybe add 1 or 2 times block_size to padding_length randomly -- shake things up a bit... */
packet_length + = padding_length ;
libssh2_htonu32 ( buf , packet_length ) ;
buf [ 4 ] = padding_length ;
# ifdef LIBSSH2_DEBUG_TRANSPORT
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Sending packet with total length %lu (%lu bytes padding) " , packet_length , padding_length ) ;
# endif
if ( session - > state & LIBSSH2_STATE_NEWKEYS ) {
/* Encryption is in effect */
unsigned char * encbuf , * s ;
int ret ;
/* Safely ignored in CUSTOM cipher mode */
EVP_CIPHER_CTX * ctx = ( EVP_CIPHER_CTX * ) session - > local . crypt_abstract ;
/* include packet_length(4) itself and room for the hash at the end */
2006-07-10 12:43:11 +02:00
encbuf = LIBSSH2_ALLOC ( session , 4 + packet_length + session - > local . mac - > mac_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! encbuf ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate encryption buffer " , 0 ) ;
if ( free_data ) {
LIBSSH2_FREE ( session , data ) ;
}
return - 1 ;
}
/* Copy packet to encoding buffer */
memcpy ( encbuf , buf , 5 ) ;
memcpy ( encbuf + 5 , data , data_len ) ;
RAND_bytes ( encbuf + 5 + data_len , padding_length ) ;
if ( free_data ) {
LIBSSH2_FREE ( session , data ) ;
}
/* Calculate MAC hash */
session - > local . mac - > hash ( session , encbuf + 4 + packet_length , session - > local . seqno , encbuf , 4 + packet_length , NULL , 0 , & session - > local . mac_abstract ) ;
/* Encrypt data */
2006-07-10 12:43:11 +02:00
for ( s = encbuf ; ( s - encbuf ) < ( 4 + packet_length ) ; s + = session - > local . crypt - > blocksize ) {
2006-07-04 16:07:08 +02:00
if ( session - > local . crypt - > flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP ) {
EVP_Cipher ( ctx , buf , s , session - > local . crypt - > blocksize ) ;
memcpy ( s , buf , session - > local . crypt - > blocksize ) ;
} else {
session - > local . crypt - > crypt ( session , s , & session - > local . crypt_abstract ) ;
}
}
session - > local . seqno + + ;
/* Send It */
2006-07-10 12:43:11 +02:00
ret = ( ( 4 + packet_length + session - > local . mac - > mac_len ) = = send ( session - > socket_fd , encbuf , 4 + packet_length + session - > local . mac - > mac_len , LIBSSH2_SOCKET_SEND_FLAGS ( session ) ) ) ? 0 : - 1 ;
2006-07-04 16:07:08 +02:00
/* Cleanup environment */
LIBSSH2_FREE ( session , encbuf ) ;
return ret ;
} else { /* LIBSSH2_ENDPOINT_CRYPT_NONE */
/* Simplified write for non-encrypted mode */
struct iovec data_vector [ 3 ] ;
/* Using vectors means we don't have to alloc a new buffer -- a byte saved is a byte earned
* No MAC during unencrypted phase
*/
2006-07-10 12:43:11 +02:00
data_vector [ 0 ] . iov_base = buf ;
2006-07-04 16:07:08 +02:00
data_vector [ 0 ] . iov_len = 5 ;
data_vector [ 1 ] . iov_base = ( char * ) data ;
data_vector [ 1 ] . iov_len = data_len ;
2006-07-10 12:43:11 +02:00
data_vector [ 2 ] . iov_base = buf + 5 ;
2006-07-04 16:07:08 +02:00
data_vector [ 2 ] . iov_len = padding_length ;
session - > local . seqno + + ;
/* Ignore this, it can't actually happen :) */
if ( free_data ) {
LIBSSH2_FREE ( session , data ) ;
}
2006-07-10 12:43:11 +02:00
return ( ( packet_length + 4 ) = = writev ( session - > socket_fd , data_vector , 3 ) ) ? 0 : 1 ;
2006-07-04 16:07:08 +02:00
}
}
/* }}} */