2007-02-02 16:21:20 +00:00
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
2004-12-07 21:17:20 +00: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>
2007-04-12 22:20:18 +00:00
# ifdef HAVE_UNISTD_H
2004-12-07 21:17:20 +00:00
# include <unistd.h>
2005-01-03 22:46:15 +00:00
# endif
2004-12-07 21:17:20 +00:00
2007-04-12 22:20:18 +00:00
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# endif
# ifdef HAVE_INTTYPES_H
# include <inttypes.h>
# endif
2005-03-21 21:26:08 +00:00
/* Needed for struct iovec on some platforms */
# ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
# endif
2004-12-29 19:26:28 +00:00
2007-02-02 16:21:20 +00:00
# include <sys/types.h>
2006-11-14 01:30:39 +00:00
2004-12-29 19:26:28 +00:00
/* {{{ libssh2_packet_queue_listener
* Queue a connection request for a listener
*/
2007-03-27 13:02:01 +00:00
static inline int libssh2_packet_queue_listener ( LIBSSH2_SESSION * session ,
unsigned char * data ,
unsigned long datalen )
2004-12-29 19:26:28 +00:00
{
2007-05-28 17:56:08 +00:00
/* Look for a matching listener */
unsigned char * s = data + ( sizeof ( " forwarded-tcpip " ) - 1 ) + 5 ;
/* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
unsigned long packet_len = 17 + ( sizeof ( " Forward not requested " ) - 1 ) ;
unsigned char * p , packet [ 17 + ( sizeof ( " Forward not requested " ) - 1 ) ] ;
LIBSSH2_LISTENER * l = session - > listeners ;
char failure_code = 1 ; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */
uint32_t sender_channel , initial_window_size , packet_size ;
unsigned char * host , * shost ;
uint32_t port , sport , host_len , shost_len ;
( void ) datalen ;
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 ;
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Remote received connection from %s:%ld to %s:%ld " , shost , sport , host , port ) ;
while ( l ) {
if ( ( l - > port = = ( int ) port ) & &
( 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 */
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Listener queue full, ignoring " ) ;
break ;
}
channel = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_CHANNEL ) ) ;
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 ;
channel - > channel_type = LIBSSH2_ALLOC ( session , channel - > channel_type_len + 1 ) ;
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 ;
_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 ) ;
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 ;
2007-02-02 16:21:20 +00:00
return 0 ;
2007-05-28 17:56:08 +00:00
}
while ( last_queued - > next ) last_queued = last_queued - > next ;
last_queued - > next = channel ;
channel - > prev = last_queued ;
l - > queue_size + + ;
return 0 ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
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 ;
}
2004-12-29 19:26:28 +00:00
}
/* }}} */
2005-01-06 00:51:30 +00:00
/* {{{ libssh2_packet_x11_open
* Accept a forwarded X11 connection
*/
2007-03-27 13:02:01 +00:00
static inline int libssh2_packet_x11_open ( LIBSSH2_SESSION * session ,
unsigned char * data , unsigned long datalen )
2005-01-06 00:51:30 +00:00
{
2007-05-28 17:56:08 +00:00
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 ;
( void ) datalen ;
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 ;
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " X11 Connection Received from %s:%ld on channel %lu " , shost , sport , sender_channel ) ;
if ( session - > x11 ) {
channel = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_CHANNEL ) ) ;
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 ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
memset ( channel , 0 , sizeof ( LIBSSH2_CHANNEL ) ) ;
channel - > session = session ;
channel - > channel_type_len = sizeof ( " x11 " ) - 1 ;
channel - > channel_type = LIBSSH2_ALLOC ( session , channel - > channel_type_len + 1 ) ;
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 ;
_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 ) ;
2007-02-02 16:21:20 +00:00
p = packet ;
2007-05-28 17:56:08 +00:00
* ( 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 ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
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
*/
LIBSSH2_X11_OPEN ( channel , ( char * ) shost , sport ) ;
2007-02-02 16:21:20 +00:00
return 0 ;
2007-05-28 17:56:08 +00:00
} 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 ;
2005-01-06 00:51:30 +00:00
}
/* }}} */
2004-12-29 19:26:28 +00:00
2004-12-07 21:17:20 +00:00
/* {{{ libssh2_packet_new
* Create a new packet and attach it to the brigade
*/
2007-02-02 16:21:20 +00:00
int libssh2_packet_add ( LIBSSH2_SESSION * session , unsigned char * data , size_t datalen , int macstate )
2004-12-07 21:17:20 +00:00
{
2007-05-28 17:56:08 +00:00
LIBSSH2_PACKET * packet ;
unsigned long data_head = 0 ;
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Packet type %d received, length=%d " , ( int ) data [ 0 ] , ( int ) datalen ) ;
if ( macstate = = LIBSSH2_MAC_INVALID ) {
if ( session - > macerror ) {
if ( LIBSSH2_MACERROR ( session , ( char * ) data , datalen ) = = 0 ) {
/* 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 ) ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
LIBSSH2_FREE ( session , data ) ;
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 ) ;
}
LIBSSH2_FREE ( session , data ) ;
return - 1 ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
}
/* 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 ) ;
message = ( char * ) data + 9 ; /* packet_type(1) + reason(4) + message_len(4) */
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 ' ;
language = ( char * ) data + 9 + message_len + 3 ;
if ( language_len ) {
memcpy ( language , language + 1 , language_len ) ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
language [ language_len ] = ' \0 ' ;
if ( session - > ssh_msg_disconnect ) {
LIBSSH2_DISCONNECT ( session , reason , message , message_len , language , language_len ) ;
}
_libssh2_debug ( session , LIBSSH2_DBG_TRANS , " Disconnect(%d): %s(%s) " , reason , message , language ) ;
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 ) {
LIBSSH2_IGNORE ( session , ( char * ) data + 4 , datalen - 5 ) ;
}
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 ) ;
message = ( char * ) data + 6 ; /* packet_type(1) + display(1) + message_len(4) */
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 ' ;
language = ( char * ) data + 6 + message_len + 3 ;
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 ) ;
}
/* _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 ) ;
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 ;
2007-02-02 16:21:20 +00:00
}
2007-02-02 23:23:36 +00:00
# ifdef LIBSSH2DEBUG
2007-05-28 17:56:08 +00:00
{
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 ) ;
}
2005-05-11 05:11:31 +00:00
# endif
2007-05-28 17:56:08 +00:00
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 ) ;
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Ignoring extended data and refunding %d bytes " , ( int ) ( datalen - 13 ) ) ;
/* 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 ;
}
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " EOF received for channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
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 " ) ) ;
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Exit status %lu received for channel %lu/%lu " , channel - > exit_status , channel - > local . id , channel - > remote . id ) ;
}
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 ;
}
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Close received for channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
channel - > remote . close = 1 ;
channel - > remote . eof = 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 ;
}
_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 ) ;
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
break ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
packet = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_PACKET ) ) ;
if ( ! packet ) {
_libssh2_debug ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for LIBSSH2_PACKET " ) ;
LIBSSH2_FREE ( session , data ) ;
return - 1 ;
}
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
*/
_libssh2_debug ( session , LIBSSH2_DBG_TRANS ,
" Renegotiating Keys " ) ;
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 ;
2004-12-07 21:17:20 +00:00
}
/* }}} */
2007-02-02 16:21:20 +00:00
/* {{{ libssh2_packet_ask
* Scan the brigade for a matching packet type , optionally poll the socket for
* a packet first
2004-12-07 21:17:20 +00:00
*/
2007-02-02 16:21:20 +00:00
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 )
2004-12-07 21:17:20 +00:00
{
2007-05-28 17:56:08 +00:00
LIBSSH2_PACKET * packet = session - > packets . head ;
if ( poll_socket ) {
libssh2pack_t rc = libssh2_packet_read ( session ) ;
if ( ( rc < 0 ) & & ! packet ) {
return rc ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
}
_libssh2_debug ( session , LIBSSH2_DBG_TRANS ,
" Looking for packet of type: %d " , ( int ) packet_type ) ;
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 ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
packet = packet - > next ;
}
return - 1 ;
2004-12-07 21:17:20 +00:00
}
/* }}} */
2007-02-02 16:21:20 +00:00
/* {{{ libssh2_packet_askv
* Scan for any of a list of packet types in the brigade , optionally poll the
* socket for a packet first
2004-12-07 21:17:20 +00:00
*/
2007-02-02 16:21:20 +00:00
int libssh2_packet_askv_ex ( LIBSSH2_SESSION * session ,
2007-04-17 18:12:41 +00:00
const unsigned char * packet_types ,
2007-02-02 16:21:20 +00:00
unsigned char * * data ,
unsigned long * data_len ,
unsigned long match_ofs ,
const unsigned char * match_buf ,
unsigned long match_len , int poll_socket )
2004-12-07 21:17:20 +00:00
{
2007-05-28 17:56:08 +00:00
int i , packet_types_len = strlen ( ( char * ) packet_types ) ;
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 ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
}
return - 1 ;
2004-12-07 21:17:20 +00:00
}
/* }}} */
2007-02-02 16:21:20 +00:00
/* {{{ waitsocket
* Returns
* negative on error
* > 0 on incoming data
* 0 on timeout
*
* FIXME : convert to use poll on systems that have it .
2004-12-07 21:17:20 +00:00
*/
2007-02-02 16:21:20 +00:00
int libssh2_waitsocket ( LIBSSH2_SESSION * session ,
2007-05-28 17:56:08 +00:00
long seconds )
2004-12-07 21:17:20 +00:00
{
2007-05-28 17:56:08 +00:00
struct timeval timeout ;
int rc ;
fd_set fd ;
timeout . tv_sec = seconds ;
timeout . tv_usec = 0 ;
FD_ZERO ( & fd ) ;
FD_SET ( session - > socket_fd , & fd ) ;
rc = select ( session - > socket_fd + 1 , & fd , NULL , NULL , & timeout ) ;
return rc ;
2005-04-01 06:11:34 +00:00
}
2004-12-07 21:17:20 +00:00
/* {{{ libssh2_packet_require
2007-02-02 16:21:20 +00:00
* [ BLOCKING ] for LIBSSH2_READ_TIMEOUT seconds
2004-12-07 21:17:20 +00:00
* Loops libssh2_packet_read ( ) until the packet requested is available
2005-04-01 06:11:34 +00:00
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
2007-02-02 16:21:20 +00:00
*
* Returns negative on error
* Returns 0 when it has taken care of the requested packet .
2004-12-07 21:17:20 +00:00
*/
2007-02-02 16:21:20 +00:00
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 )
2004-12-07 21:17:20 +00:00
{
2007-05-28 17:56:08 +00:00
time_t start = time ( NULL ) ;
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 ;
}
_libssh2_debug ( session , LIBSSH2_DBG_TRANS ,
" Blocking until packet of type %d becomes available " ,
( int ) packet_type ) ;
while ( session - > socket_state = = LIBSSH2_SOCKET_CONNECTED ) {
libssh2pack_t ret = libssh2_packet_read ( session ) ;
if ( ( ret < 0 ) & & ( ret ! = PACKET_EAGAIN ) ) {
/* an error which is not just because of blocking */
return ret ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
if ( packet_type = = ret ) {
/* Be lazy, let packet_ask pull it out of the
brigade */
ret = libssh2_packet_ask_ex ( session , packet_type ,
data , data_len , match_ofs ,
match_buf , match_len , 0 ) ;
return ret ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
else if ( ret < = 0 ) {
/* nothing available, wait until data arrives or
we time out */
long left = LIBSSH2_READ_TIMEOUT -
( time ( NULL ) - start ) ;
if ( ( left < = 0 ) | |
( libssh2_waitsocket ( session , left ) < = 0 ) ) {
return PACKET_TIMEOUT ;
}
}
}
/* Only reached if the socket died */
return - 1 ;
2004-12-07 21:17:20 +00:00
}
/* }}} */
2006-06-22 18:45:29 +00: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 )
{
2007-05-28 17:56:08 +00:00
unsigned char * data ;
unsigned long data_len ;
unsigned 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 ;
}
_libssh2_debug ( session , LIBSSH2_DBG_TRANS ,
" Blocking until packet becomes available to burn " ) ;
while ( session - > socket_state = = LIBSSH2_SOCKET_CONNECTED ) {
int ret = libssh2_packet_read ( session ) ;
if ( ret < 0 ) {
return ret ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
if ( ret = = 0 ) {
/* FIXME: this might busyloop */
continue ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
/* 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-06-22 18:45:29 +00:00
}
/* }}} */
2005-04-01 06:11:34 +00:00
/* {{{ libssh2_packet_requirev
2007-02-02 16:21:20 +00:00
* [ BLOCKING ]
* Loops libssh2_packet_read ( ) until one of a list of packet types requested is
* available
2005-04-01 06:11:34 +00:00
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
* packet_types is a null terminated list of packet_type numbers
*/
2004-12-07 21:17:20 +00:00
2007-02-02 16:21:20 +00:00
int libssh2_packet_requirev_ex ( LIBSSH2_SESSION * session ,
2007-04-17 18:12:41 +00:00
const unsigned char * packet_types ,
2007-02-02 16:21:20 +00:00
unsigned char * * data , unsigned long * data_len ,
unsigned long match_ofs ,
const unsigned char * match_buf ,
unsigned long match_len )
2005-05-11 05:11:31 +00:00
{
2007-05-28 17:56:08 +00:00
time_t start = time ( NULL ) ;
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 ) ;
if ( ( ret < 0 ) & & ( ret ! = PACKET_EAGAIN ) ) {
return ret ;
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
if ( ret < = 0 ) {
long left = LIBSSH2_READ_TIMEOUT -
( time ( NULL ) - start ) ;
if ( ( left < = 0 ) | |
( libssh2_waitsocket ( session , left ) < = 0 ) ) {
return PACKET_TIMEOUT ;
}
2007-02-02 16:21:20 +00:00
}
2007-05-28 17:56:08 +00:00
if ( strchr ( ( char * ) packet_types , ret ) ) {
/* 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 ;
2004-12-07 21:17:20 +00:00
}
/* }}} */