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 <openssl/rand.h>
# ifndef WIN32
# include <unistd.h>
# endif
/* {{{ libssh2_channel_nextid
* Determine the next channel ID we can use at our end
*/
unsigned long libssh2_channel_nextid ( LIBSSH2_SESSION * session )
{
unsigned long id = session - > next_channel ;
LIBSSH2_CHANNEL * channel ;
channel = session - > channels . head ;
while ( channel ) {
if ( channel - > local . id > id ) {
id = channel - > local . id ;
}
channel = channel - > next ;
}
/* This is a shortcut to avoid waiting for close packets on channels we've forgotten about,
* This * could * be a problem if we request and close 4 billion or so channels in too rapid succession
* for the remote end to respond , but the worst case scenario is that some data meant for another channel
* Gets picked up by the new one . . . . Pretty unlikely all told . . .
*/
session - > next_channel = id + 1 ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Allocated new channel ID#%lu " , id ) ;
# endif
return id ;
}
/* }}} */
/* {{{ libssh2_channel_locate
* Locate a channel pointer by number
*/
LIBSSH2_CHANNEL * libssh2_channel_locate ( LIBSSH2_SESSION * session , unsigned long channel_id )
{
LIBSSH2_CHANNEL * channel = session - > channels . head ;
while ( channel ) {
if ( channel - > local . id = = channel_id ) {
return channel ;
}
channel = channel - > next ;
}
return NULL ;
}
/* }}} */
# define libssh2_channel_add(session, channel) \
{ \
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 ) ; \
( channel ) - > session = ( session ) ; \
}
/* {{{ libssh2_channel_open_session
* Establish a generic session channel
*/
LIBSSH2_API LIBSSH2_CHANNEL * libssh2_channel_open_ex ( LIBSSH2_SESSION * session , const char * channel_type , int channel_type_len , int window_size , int packet_size ,
const char * message , int message_len )
{
unsigned char reply_codes [ 3 ] = { SSH_MSG_CHANNEL_OPEN_CONFIRMATION , SSH_MSG_CHANNEL_OPEN_FAILURE , 0 } ;
LIBSSH2_CHANNEL * channel = NULL ;
unsigned long local_channel = libssh2_channel_nextid ( session ) ;
unsigned char * s , * packet = NULL ;
unsigned long packet_len = channel_type_len + message_len + 17 ; /* packet_type(1) + channel_type_len(4) + sender_channel(4) +
window_size ( 4 ) + packet_size ( 4 ) */
unsigned char * data = NULL ;
unsigned long data_len ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Opening Channel - win %d pack %d " , window_size , packet_size ) ;
# endif
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 space for channel data " , 0 ) ;
return NULL ;
}
memset ( channel , 0 , sizeof ( LIBSSH2_CHANNEL ) ) ;
channel - > channel_type_len = channel_type_len ;
2006-07-10 12:43:11 +02:00
channel - > channel_type = LIBSSH2_ALLOC ( session , channel_type_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! channel - > channel_type ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Failed allocating memory for channel type name " , 0 ) ;
LIBSSH2_FREE ( session , channel ) ;
return NULL ;
}
memcpy ( channel - > channel_type , channel_type , channel_type_len ) ;
/* REMEMBER: local as in locally sourced */
channel - > local . id = local_channel ;
channel - > remote . window_size = window_size ;
channel - > remote . window_size_initial = window_size ;
channel - > remote . packet_size = packet_size ;
libssh2_channel_add ( session , channel ) ;
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate temporary space for packet " , 0 ) ;
return NULL ;
}
* ( s + + ) = SSH_MSG_CHANNEL_OPEN ;
libssh2_htonu32 ( s , channel_type_len ) ; s + = 4 ;
memcpy ( s , channel_type , channel_type_len ) ; s + = channel_type_len ;
libssh2_htonu32 ( s , local_channel ) ; s + = 4 ;
libssh2_htonu32 ( s , window_size ) ; s + = 4 ;
libssh2_htonu32 ( s , packet_size ) ; s + = 4 ;
if ( message & & message_len ) {
memcpy ( s , message , message_len ) ; s + = message_len ;
}
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send channel-open request " , 0 ) ;
goto channel_error ;
}
if ( libssh2_packet_requirev_ex ( session , reply_codes , & data , & data_len , 1 , packet + 5 + channel_type_len , 4 ) ) {
goto channel_error ;
}
if ( data [ 0 ] = = SSH_MSG_CHANNEL_OPEN_CONFIRMATION ) {
channel - > remote . id = libssh2_ntohu32 ( data + 5 ) ;
channel - > local . window_size = libssh2_ntohu32 ( data + 9 ) ;
channel - > local . window_size_initial = libssh2_ntohu32 ( data + 9 ) ;
channel - > local . packet_size = libssh2_ntohu32 ( data + 13 ) ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Connection Established - ID: %lu/%lu win: %lu/%lu pack: %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
LIBSSH2_FREE ( session , packet ) ;
LIBSSH2_FREE ( session , data ) ;
return channel ;
}
if ( data [ 0 ] = = SSH_MSG_CHANNEL_OPEN_FAILURE ) {
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_FAILURE , " Channel open failure " , 0 ) ;
}
channel_error :
if ( data ) {
LIBSSH2_FREE ( session , data ) ;
}
if ( packet ) {
LIBSSH2_FREE ( session , packet ) ;
}
if ( channel ) {
unsigned char channel_id [ 4 ] ;
LIBSSH2_FREE ( session , channel - > channel_type ) ;
if ( channel - > next ) {
channel - > next - > prev = channel - > prev ;
}
if ( channel - > prev ) {
channel - > prev - > next = channel - > next ;
}
if ( session - > channels . head = = channel ) {
session - > channels . head = channel - > next ;
}
if ( session - > channels . tail = = channel ) {
session - > channels . tail = channel - > prev ;
}
/* Clear out packets meant for this channel */
libssh2_htonu32 ( channel_id , channel - > local . id ) ;
while ( ( libssh2_packet_ask_ex ( session , SSH_MSG_CHANNEL_DATA , & data , & data_len , 1 , channel_id , 4 , 1 ) > = 0 ) | |
( libssh2_packet_ask_ex ( session , SSH_MSG_CHANNEL_EXTENDED_DATA , & data , & data_len , 1 , channel_id , 4 , 1 ) > = 0 ) ) {
LIBSSH2_FREE ( session , data ) ;
}
LIBSSH2_FREE ( session , channel ) ;
}
return NULL ;
}
/* }}} */
/* {{{ libssh2_channel_direct_tcpip_ex
* Tunnel TCP / IP connect through the SSH session to direct host / port
*/
LIBSSH2_API LIBSSH2_CHANNEL * libssh2_channel_direct_tcpip_ex ( LIBSSH2_SESSION * session , char * host , int port , char * shost , int sport )
{
LIBSSH2_CHANNEL * channel ;
unsigned char * message , * s ;
unsigned long host_len = strlen ( host ) , shost_len = strlen ( shost ) ;
unsigned long message_len = host_len + shost_len + 16 ; /* host_len(4) + port(4) + shost_len(4) + sport(4) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Requesting direct-tcpip session to from %s:%d to %s:%d " , shost , sport , host , port ) ;
# endif
2006-07-10 12:43:11 +02:00
s = message = LIBSSH2_ALLOC ( session , message_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! message ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for direct-tcpip connection " , 0 ) ;
return NULL ;
}
libssh2_htonu32 ( s , host_len ) ; s + = 4 ;
memcpy ( s , host , host_len ) ; s + = host_len ;
libssh2_htonu32 ( s , port ) ; s + = 4 ;
libssh2_htonu32 ( s , shost_len ) ; s + = 4 ;
memcpy ( s , shost , shost_len ) ; s + = shost_len ;
libssh2_htonu32 ( s , sport ) ; s + = 4 ;
2006-07-10 12:43:11 +02:00
channel = libssh2_channel_open_ex ( session , " direct-tcpip " , sizeof ( " direct-tcpip " ) - 1 , LIBSSH2_CHANNEL_WINDOW_DEFAULT , LIBSSH2_CHANNEL_PACKET_DEFAULT , message , message_len ) ;
2006-07-04 16:07:08 +02:00
LIBSSH2_FREE ( session , message ) ;
return channel ;
}
/* }}} */
/* {{{ libssh2_channel_forward_listen_ex
* Bind a port on the remote host and listen for connections
*/
LIBSSH2_API LIBSSH2_LISTENER * libssh2_channel_forward_listen_ex ( LIBSSH2_SESSION * session , char * host , int port , int * bound_port , int queue_maxsize )
{
unsigned char * packet , * s , * data , reply_codes [ 3 ] = { SSH_MSG_REQUEST_SUCCESS , SSH_MSG_REQUEST_FAILURE , 0 } ;
unsigned long data_len ;
unsigned long host_len = ( host ? strlen ( host ) : ( sizeof ( " 0.0.0.0 " ) - 1 ) ) ;
unsigned long packet_len = host_len + ( sizeof ( " tcpip-forward " ) - 1 ) + 14 ;
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Requesting tcpip-forward session for %s:%d " , host , port ) ;
# endif
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memeory for setenv packet " , 0 ) ;
return NULL ;
}
* ( s + + ) = SSH_MSG_GLOBAL_REQUEST ;
libssh2_htonu32 ( s , sizeof ( " tcpip-forward " ) - 1 ) ; s + = 4 ;
memcpy ( s , " tcpip-forward " , sizeof ( " tcpip-forward " ) - 1 ) ; s + = sizeof ( " tcpip-forward " ) - 1 ;
* ( s + + ) = 0xFF ; /* want_reply */
libssh2_htonu32 ( s , host_len ) ; s + = 4 ;
memcpy ( s , host ? host : " 0.0.0.0 " , host_len ) ; s + = host_len ;
libssh2_htonu32 ( s , port ) ; s + = 4 ;
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send global-request packet for forward listen request " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return NULL ;
}
LIBSSH2_FREE ( session , packet ) ;
if ( libssh2_packet_requirev ( session , reply_codes , & data , & data_len ) ) {
return NULL ;
}
if ( data [ 0 ] = = SSH_MSG_REQUEST_SUCCESS ) {
LIBSSH2_LISTENER * listener ;
2006-07-10 12:43:11 +02:00
listener = LIBSSH2_ALLOC ( session , sizeof ( LIBSSH2_LISTENER ) ) ;
2006-07-04 16:07:08 +02:00
if ( ! listener ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for listener queue " , 0 ) ;
LIBSSH2_FREE ( session , data ) ;
return NULL ;
}
memset ( listener , 0 , sizeof ( LIBSSH2_LISTENER ) ) ;
listener - > session = session ;
2006-07-10 12:43:11 +02:00
listener - > host = LIBSSH2_ALLOC ( session , host_len + 1 ) ;
2006-07-04 16:07:08 +02:00
if ( ! listener - > host ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for listener queue " , 0 ) ;
LIBSSH2_FREE ( session , listener ) ;
LIBSSH2_FREE ( session , data ) ;
return NULL ;
}
memcpy ( listener - > host , host ? host : " 0.0.0.0 " , host_len ) ;
listener - > host [ host_len ] = 0 ;
if ( data_len > = 5 & & ! port ) {
listener - > port = libssh2_ntohu32 ( data + 1 ) ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Dynamic tcpip-forward port allocated: %d " , listener - > port ) ;
# endif
} else {
listener - > port = port ;
}
listener - > queue_size = 0 ;
listener - > queue_maxsize = queue_maxsize ;
listener - > next = session - > listeners ;
listener - > prev = NULL ;
if ( session - > listeners ) {
session - > listeners - > prev = listener ;
}
session - > listeners = listener ;
if ( bound_port ) {
* bound_port = listener - > port ;
}
LIBSSH2_FREE ( session , data ) ;
return listener ;
}
if ( data [ 0 ] = = SSH_MSG_REQUEST_FAILURE ) {
LIBSSH2_FREE ( session , data ) ;
libssh2_error ( session , LIBSSH2_ERROR_REQUEST_DENIED , " Unable to complete request for forward-listen " , 0 ) ;
return NULL ;
}
return NULL ;
}
/* }}} */
/* {{{ libssh2_channel_forward_cancel
* Stop listening on a remote port and free the listener
* Toss out any pending ( un - accept ( ) ed ) connections
*/
LIBSSH2_API int libssh2_channel_forward_cancel ( LIBSSH2_LISTENER * listener )
{
LIBSSH2_SESSION * session = listener - > session ;
LIBSSH2_CHANNEL * queued = listener - > queue ;
unsigned char * packet , * s ;
unsigned long host_len = strlen ( listener - > host ) ;
unsigned long packet_len = host_len + 14 + sizeof ( " cancel-tcpip-forward " ) - 1 ;
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Cancelling tcpip-forward session for %s:%d " , listener - > host , listener - > port ) ;
# endif
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memeory for setenv packet " , 0 ) ;
return - 1 ;
}
* ( s + + ) = SSH_MSG_GLOBAL_REQUEST ;
libssh2_htonu32 ( s , sizeof ( " cancel-tcpip-forward " ) - 1 ) ; s + = 4 ;
memcpy ( s , " cancel-tcpip-forward " , sizeof ( " cancel-tcpip-forward " ) - 1 ) ; s + = sizeof ( " cancel-tcpip-forward " ) - 1 ;
* ( s + + ) = 0x00 ; /* want_reply */
libssh2_htonu32 ( s , host_len ) ; s + = 4 ;
memcpy ( s , listener - > host , host_len ) ; s + = host_len ;
libssh2_htonu32 ( s , listener - > port ) ; s + = 4 ;
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send global-request packet for forward listen request " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return - 1 ;
}
LIBSSH2_FREE ( session , packet ) ;
while ( queued ) {
LIBSSH2_CHANNEL * next = queued - > next ;
libssh2_channel_free ( queued ) ;
queued = next ;
}
LIBSSH2_FREE ( session , listener - > host ) ;
if ( listener - > next ) {
listener - > next - > prev = listener - > prev ;
}
if ( listener - > prev ) {
listener - > prev - > next = listener - > next ;
} else {
session - > listeners = listener - > next ;
}
LIBSSH2_FREE ( session , listener ) ;
return 0 ;
}
/* }}} */
/* {{{ libssh2_channel_forward_accept
* Accept a connection
*/
LIBSSH2_API LIBSSH2_CHANNEL * libssh2_channel_forward_accept ( LIBSSH2_LISTENER * listener )
{
while ( libssh2_packet_read ( listener - > session , 0 ) > 0 ) ;
if ( listener - > queue ) {
LIBSSH2_SESSION * session = listener - > session ;
LIBSSH2_CHANNEL * channel ;
channel = listener - > queue ;
listener - > queue = listener - > queue - > next ;
if ( listener - > queue ) {
listener - > queue - > prev = NULL ;
}
channel - > prev = NULL ;
channel - > next = session - > channels . head ;
session - > channels . head = channel ;
if ( channel - > next ) {
channel - > next - > prev = channel ;
} else {
session - > channels . tail = channel ;
}
listener - > queue_size - - ;
return channel ;
}
return NULL ;
}
/* }}} */
/* {{{ libssh2_channel_setenv_ex
* Set an environment variable prior to requesting a shell / program / subsystem
*/
LIBSSH2_API int libssh2_channel_setenv_ex ( LIBSSH2_CHANNEL * channel , char * varname , int varname_len , char * value , int value_len )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char * s , * packet , * data , reply_codes [ 3 ] = { SSH_MSG_CHANNEL_SUCCESS , SSH_MSG_CHANNEL_FAILURE , 0 } , local_channel [ 4 ] ;
unsigned long data_len ;
unsigned long packet_len = varname_len + value_len + 21 ; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" +
want_reply ( 1 ) + varname_len ( 4 ) + value_len ( 4 ) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Setting remote environment variable: %s=%s on channel %lu/%lu " , varname , value , channel - > local . id , channel - > remote . id ) ;
# endif
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memeory for setenv packet " , 0 ) ;
return - 1 ;
}
* ( s + + ) = SSH_MSG_CHANNEL_REQUEST ;
libssh2_htonu32 ( s , channel - > remote . id ) ; s + = 4 ;
libssh2_htonu32 ( s , sizeof ( " env " ) - 1 ) ; s + = 4 ;
memcpy ( s , " env " , sizeof ( " env " ) - 1 ) ; s + = sizeof ( " env " ) - 1 ;
* ( s + + ) = 0xFF ;
libssh2_htonu32 ( s , varname_len ) ; s + = 4 ;
memcpy ( s , varname , varname_len ) ; s + = varname_len ;
libssh2_htonu32 ( s , value_len ) ; s + = 4 ;
memcpy ( s , value , value_len ) ; s + = value_len ;
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send channel-request packet for setenv request " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return - 1 ;
}
LIBSSH2_FREE ( session , packet ) ;
libssh2_htonu32 ( local_channel , channel - > local . id ) ;
if ( libssh2_packet_requirev_ex ( session , reply_codes , & data , & data_len , 1 , local_channel , 4 ) ) {
return - 1 ;
}
if ( data [ 0 ] = = SSH_MSG_CHANNEL_SUCCESS ) {
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
LIBSSH2_FREE ( session , data ) ;
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED , " Unable to complete request for channel-setenv " , 0 ) ;
return - 1 ;
}
/* }}} */
/* {{{ libssh2_channel_request_pty_ex
* Duh . . . Request a PTY
*/
LIBSSH2_API int libssh2_channel_request_pty_ex ( LIBSSH2_CHANNEL * channel , char * term , int term_len ,
char * modes , int modes_len ,
int width , int height ,
int width_px , int height_px )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char * s , * packet , * data , reply_codes [ 3 ] = { SSH_MSG_CHANNEL_SUCCESS , SSH_MSG_CHANNEL_FAILURE , 0 } , local_channel [ 4 ] ;
unsigned long data_len ;
unsigned long packet_len = term_len + modes_len + 41 ; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) +
term_len ( 4 ) + width ( 4 ) + height ( 4 ) + width_px ( 4 ) + height_px ( 4 ) + modes_len ( 4 ) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Allocating tty on channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for pty-request " , 0 ) ;
return - 1 ;
}
* ( s + + ) = SSH_MSG_CHANNEL_REQUEST ;
libssh2_htonu32 ( s , channel - > remote . id ) ; s + = 4 ;
libssh2_htonu32 ( s , sizeof ( " pty-req " ) - 1 ) ; s + = 4 ;
memcpy ( s , " pty-req " , sizeof ( " pty-req " ) - 1 ) ; s + = sizeof ( " pty-req " ) - 1 ;
* ( s + + ) = 0xFF ;
libssh2_htonu32 ( s , term_len ) ; s + = 4 ;
if ( term ) {
memcpy ( s , term , term_len ) ; s + = term_len ;
}
libssh2_htonu32 ( s , width ) ; s + = 4 ;
libssh2_htonu32 ( s , height ) ; s + = 4 ;
libssh2_htonu32 ( s , width_px ) ; s + = 4 ;
libssh2_htonu32 ( s , height_px ) ; s + = 4 ;
libssh2_htonu32 ( s , modes_len ) ; s + = 4 ;
if ( modes ) {
memcpy ( s , modes , modes_len ) ; s + = modes_len ;
}
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send pty-request packet " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return - 1 ;
}
LIBSSH2_FREE ( session , packet ) ;
libssh2_htonu32 ( local_channel , channel - > local . id ) ;
if ( libssh2_packet_requirev_ex ( session , reply_codes , & data , & data_len , 1 , local_channel , 4 ) ) {
return - 1 ;
}
if ( data [ 0 ] = = SSH_MSG_CHANNEL_SUCCESS ) {
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
LIBSSH2_FREE ( session , data ) ;
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED , " Unable to complete request for channel request-pty " , 0 ) ;
return - 1 ;
}
/* }}} */
/* Keep this an even number */
# define LIBSSH2_X11_RANDOM_COOKIE_LEN 32
/* {{{ libssh2_channel_x11_req_ex
* Request X11 forwarding
*/
LIBSSH2_API int libssh2_channel_x11_req_ex ( LIBSSH2_CHANNEL * channel , int single_connection , char * auth_proto , char * auth_cookie , int screen_number )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char * s , * packet , * data , reply_codes [ 3 ] = { SSH_MSG_CHANNEL_SUCCESS , SSH_MSG_CHANNEL_FAILURE , 0 } , local_channel [ 4 ] ;
unsigned long data_len ;
unsigned long proto_len = auth_proto ? strlen ( auth_proto ) : ( sizeof ( " MIT-MAGIC-COOKIE-1 " ) - 1 ) ;
unsigned long cookie_len = auth_cookie ? strlen ( auth_cookie ) : LIBSSH2_X11_RANDOM_COOKIE_LEN ;
unsigned long packet_len = proto_len + cookie_len + 30 ; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) +
single_cnx ( 1 ) + proto_len ( 4 ) + cookie_len ( 4 ) + screen_num ( 4 ) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Requesting x11-req for channel %lu/%lu: single=%d proto=%s cookie=%s screen=%d " ,
channel - > local . id , channel - > remote . id , single_connection ,
auth_proto ? auth_proto : " MIT-MAGIC-COOKIE-1 " ,
auth_cookie ? auth_cookie : " <random> " , screen_number ) ;
# endif
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for pty-request " , 0 ) ;
return - 1 ;
}
* ( s + + ) = SSH_MSG_CHANNEL_REQUEST ;
libssh2_htonu32 ( s , channel - > remote . id ) ; s + = 4 ;
libssh2_htonu32 ( s , sizeof ( " x11-req " ) - 1 ) ; s + = 4 ;
memcpy ( s , " x11-req " , sizeof ( " x11-req " ) - 1 ) ; s + = sizeof ( " x11-req " ) - 1 ;
* ( s + + ) = 0xFF ; /* want_reply */
* ( s + + ) = single_connection ? 0xFF : 0x00 ;
libssh2_htonu32 ( s , proto_len ) ; s + = 4 ;
memcpy ( s , auth_proto ? auth_proto : " MIT-MAGIC-COOKIE-1 " , proto_len ) ;
s + = proto_len ;
libssh2_htonu32 ( s , cookie_len ) ; s + = 4 ;
if ( auth_cookie ) {
memcpy ( s , auth_cookie , cookie_len ) ;
} else {
int i ;
char buffer [ LIBSSH2_X11_RANDOM_COOKIE_LEN / 2 ] ;
2006-07-10 12:43:11 +02:00
RAND_bytes ( buffer , LIBSSH2_X11_RANDOM_COOKIE_LEN / 2 ) ;
2006-07-04 16:07:08 +02:00
for ( i = 0 ; i < ( LIBSSH2_X11_RANDOM_COOKIE_LEN / 2 ) ; i + + ) {
2006-07-10 12:43:11 +02:00
snprintf ( s + ( i * 2 ) , 2 , " %02X " , buffer [ i ] ) ;
2006-07-04 16:07:08 +02:00
}
}
s + = cookie_len ;
libssh2_htonu32 ( s , screen_number ) ; s + = 4 ;
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send x11-req packet " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return - 1 ;
}
LIBSSH2_FREE ( session , packet ) ;
libssh2_htonu32 ( local_channel , channel - > local . id ) ;
if ( libssh2_packet_requirev_ex ( session , reply_codes , & data , & data_len , 1 , local_channel , 4 ) ) {
return - 1 ;
}
if ( data [ 0 ] = = SSH_MSG_CHANNEL_SUCCESS ) {
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
LIBSSH2_FREE ( session , data ) ;
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED , " Unable to complete request for channel x11-req " , 0 ) ;
return - 1 ;
}
/* }}} */
/* {{{ libssh2_channel_process_startup
* Primitive for libssh2_channel_ ( shell | exec | subsystem )
*/
LIBSSH2_API int libssh2_channel_process_startup ( LIBSSH2_CHANNEL * channel , const char * request , int request_len , const char * message , int message_len )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char * s , * packet , * data , reply_codes [ 3 ] = { SSH_MSG_CHANNEL_SUCCESS , SSH_MSG_CHANNEL_FAILURE , 0 } , local_channel [ 4 ] ;
unsigned long data_len ;
unsigned long packet_len = request_len + 10 ; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
if ( message ) {
packet_len + = message_len + 4 ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " starting request(%s) on channel %lu/%lu, message=%s " , request , channel - > local . id , channel - > remote . id , message ) ;
# endif
2006-07-10 12:43:11 +02:00
s = packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocate memory for channel-process request " , 0 ) ;
return - 1 ;
}
* ( s + + ) = SSH_MSG_CHANNEL_REQUEST ;
libssh2_htonu32 ( s , channel - > remote . id ) ; s + = 4 ;
libssh2_htonu32 ( s , request_len ) ; s + = 4 ;
memcpy ( s , request , request_len ) ; s + = request_len ;
* ( s + + ) = 0xFF ;
if ( message ) {
libssh2_htonu32 ( s , message_len ) ; s + = 4 ;
memcpy ( s , message , message_len ) ; s + = message_len ;
}
if ( libssh2_packet_write ( session , packet , packet_len ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send channel request " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return - 1 ;
}
LIBSSH2_FREE ( session , packet ) ;
libssh2_htonu32 ( local_channel , channel - > local . id ) ;
if ( libssh2_packet_requirev_ex ( session , reply_codes , & data , & data_len , 1 , local_channel , 4 ) ) {
return - 1 ;
}
if ( data [ 0 ] = = SSH_MSG_CHANNEL_SUCCESS ) {
LIBSSH2_FREE ( session , data ) ;
return 0 ;
}
LIBSSH2_FREE ( session , data ) ;
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED , " Unable to complete request for channel-process-startup " , 0 ) ;
return - 1 ;
}
/* }}} */
/* {{{ libssh2_channel_set_blocking
* Set a channel ' s blocking mode on or off , similar to a socket ' s fcntl ( fd , F_SETFL , O_NONBLOCK ) ; type command
*/
LIBSSH2_API void libssh2_channel_set_blocking ( LIBSSH2_CHANNEL * channel , int blocking )
{
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( channel - > session , LIBSSH2_DBG_CONN , " Setting blocking mode on channel %lu/%lu to %d " , channel - > local . id , channel - > remote . id , blocking ) ;
# endif
channel - > blocking = blocking ;
}
/* }}} */
/* {{{ libssh2_channel_flush_ex
* Flush data from one ( or all ) stream
* Returns number of bytes flushed , or - 1 on failure
*/
LIBSSH2_API int libssh2_channel_flush_ex ( LIBSSH2_CHANNEL * channel , int streamid )
{
LIBSSH2_PACKET * packet = channel - > session - > packets . head ;
unsigned long refund_bytes = 0 , flush_bytes = 0 ;
while ( packet ) {
LIBSSH2_PACKET * next = packet - > next ;
unsigned char packet_type = packet - > data [ 0 ] ;
if ( ( ( packet_type = = SSH_MSG_CHANNEL_DATA ) | | ( packet_type = = SSH_MSG_CHANNEL_EXTENDED_DATA ) ) & &
( libssh2_ntohu32 ( packet - > data + 1 ) = = channel - > local . id ) ) {
/* It's our channel at least */
unsigned long packet_stream_id = ( packet_type = = SSH_MSG_CHANNEL_DATA ) ? 0 : libssh2_ntohu32 ( packet - > data + 5 ) ;
if ( ( streamid = = LIBSSH2_CHANNEL_FLUSH_ALL ) | |
2006-07-10 12:43:11 +02:00
( ( packet_type = = SSH_MSG_CHANNEL_EXTENDED_DATA ) & & ( ( streamid = = LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA ) | | ( streamid = = packet_stream_id ) ) ) | |
2006-07-04 16:07:08 +02:00
( ( packet_type = = SSH_MSG_CHANNEL_DATA ) & & ( streamid = = 0 ) ) ) {
int bytes_to_flush = packet - > data_len - packet - > data_head ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( channel - > session , LIBSSH2_DBG_CONN , " Flushing %d bytes of data from stream %lu on channel %lu/%lu " , bytes_to_flush ,
packet_stream_id , channel - > local . id , channel - > remote . id ) ;
# endif
/* It's one of the streams we wanted to flush */
refund_bytes + = packet - > data_len - 13 ;
flush_bytes + = bytes_to_flush ;
LIBSSH2_FREE ( channel - > session , packet - > data ) ;
if ( packet - > prev ) {
packet - > prev - > next = packet - > next ;
} else {
channel - > session - > packets . head = packet - > next ;
}
if ( packet - > next ) {
packet - > next - > prev = packet - > prev ;
} else {
channel - > session - > packets . tail = packet - > prev ;
}
LIBSSH2_FREE ( channel - > session , packet ) ;
}
}
packet = next ;
}
if ( refund_bytes ) {
libssh2_channel_receive_window_adjust ( channel , refund_bytes , 0 ) ;
}
return flush_bytes ;
}
/* }}} */
/* {{{ libssh2_channel_get_exit_status
* Return the channel ' s program exit status
*/
LIBSSH2_API int libssh2_channel_get_exit_status ( LIBSSH2_CHANNEL * channel )
{
return channel - > exit_status ;
}
/* }}} */
/* {{{ libssh2_channel_receive_window_adjust
* Adjust the receive window for a channel by adjustment bytes
* If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0
* The adjustment amount will be queued for a later packet
*
* Returns the new size of the receive window ( as understood by remote end )
*/
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust ( LIBSSH2_CHANNEL * channel , unsigned long adjustment , unsigned char force )
{
unsigned char adjust [ 9 ] ; /* packet_type(1) + channel(4) + adjustment(4) */
if ( ! force & & ( adjustment + channel - > adjust_queue < LIBSSH2_CHANNEL_MINADJUST ) ) {
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( channel - > session , LIBSSH2_DBG_CONN , " Queing %lu bytes for receive window adjustment for channel %lu/%lu " , adjustment , channel - > local . id , channel - > remote . id ) ;
# endif
channel - > adjust_queue + = adjustment ;
return channel - > remote . window_size ;
}
if ( ! adjustment & & ! channel - > adjust_queue ) {
return channel - > remote . window_size ;
}
adjustment + = channel - > adjust_queue ;
channel - > adjust_queue = 0 ;
/* Adjust the window based on the block we just freed */
adjust [ 0 ] = SSH_MSG_CHANNEL_WINDOW_ADJUST ;
libssh2_htonu32 ( adjust + 1 , channel - > remote . id ) ;
libssh2_htonu32 ( adjust + 5 , adjustment ) ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( channel - > session , LIBSSH2_DBG_CONN , " Adjusting window %lu bytes for data flushed from channel %lu/%lu " , adjustment , channel - > local . id , channel - > remote . id ) ;
# endif
if ( libssh2_packet_write ( channel - > session , adjust , 9 ) ) {
libssh2_error ( channel - > session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send transfer-window adjustment packet, deferring " , 0 ) ;
channel - > adjust_queue = adjustment ;
} else {
channel - > remote . window_size + = adjustment ;
}
return channel - > remote . window_size ;
}
/* }}} */
/* {{{ libssh2_channel_handle_extended_data
* How should extended data look to the calling app ?
* Keep it in separate channels [ _read ( ) _read_stdder ( ) ] ? ( NORMAL )
* Merge the extended data to the standard data ? [ everything via _read ( ) ] ? ( MERGE )
* Ignore it entirely [ toss out packets as they come in ] ? ( IGNORE )
*/
LIBSSH2_API void libssh2_channel_handle_extended_data ( LIBSSH2_CHANNEL * channel , int ignore_mode )
{
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( channel - > session , LIBSSH2_DBG_CONN , " Setting channel %lu/%lu handle_extended_data mode to %d " , channel - > local . id , channel - > remote . id , ignore_mode ) ;
# endif
channel - > remote . extended_data_ignore_mode = ignore_mode ;
if ( ignore_mode = = LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE ) {
libssh2_channel_flush_ex ( channel , LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA ) ;
}
}
/* }}} */
/* {{{ libssh2_channel_read_ex
* Read data from a channel
*/
LIBSSH2_API int libssh2_channel_read_ex ( LIBSSH2_CHANNEL * channel , int stream_id , char * buf , size_t buflen )
{
LIBSSH2_SESSION * session = channel - > session ;
int bytes_read = 0 , blocking_read = 0 ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Attempting to read %d bytes from channel %lu/%lu stream #%d " , ( int ) buflen , channel - > local . id , channel - > remote . id , stream_id ) ;
# endif
do {
LIBSSH2_PACKET * packet ;
/* Process any waiting packets */
while ( libssh2_packet_read ( session , blocking_read ) > 0 ) blocking_read = 0 ;
packet = session - > packets . head ;
2006-07-10 12:43:11 +02:00
while ( packet & & ( bytes_read < buflen ) ) {
2006-07-04 16:07:08 +02:00
/* In case packet gets destroyed during this iteration */
LIBSSH2_PACKET * next = packet - > next ;
/* Either we asked for a specific extended data stream (and data was available),
* or the standard stream ( and data was available ) ,
* or the standard stream with extended_data_merge enabled and data was available
*/
2006-07-10 12:43:11 +02:00
if ( ( stream_id & & ( packet - > data [ 0 ] = = SSH_MSG_CHANNEL_EXTENDED_DATA ) & & ( channel - > local . id = = libssh2_ntohu32 ( packet - > data + 1 ) ) & & ( stream_id = = libssh2_ntohu32 ( packet - > data + 5 ) ) ) | |
2006-07-04 16:07:08 +02:00
( ! stream_id & & ( packet - > data [ 0 ] = = SSH_MSG_CHANNEL_DATA ) & & ( channel - > local . id = = libssh2_ntohu32 ( packet - > data + 1 ) ) ) | |
( ! stream_id & & ( packet - > data [ 0 ] = = SSH_MSG_CHANNEL_EXTENDED_DATA ) & & ( channel - > local . id = = libssh2_ntohu32 ( packet - > data + 1 ) ) & & ( channel - > remote . extended_data_ignore_mode = = LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE ) ) ) {
int want = buflen - bytes_read ;
int unlink_packet = 0 ;
2006-07-10 12:43:11 +02:00
if ( want > = ( packet - > data_len - packet - > data_head ) ) {
2006-07-04 16:07:08 +02:00
want = packet - > data_len - packet - > data_head ;
unlink_packet = 1 ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Reading %d of buffered data from %lu/%lu/%d " , want , channel - > local . id , channel - > remote . id , stream_id ) ;
# endif
memcpy ( buf + bytes_read , packet - > data + packet - > data_head , want ) ;
packet - > data_head + = want ;
bytes_read + = want ;
if ( unlink_packet ) {
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 - > data ) ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Unlinking empty packet buffer from channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
libssh2_channel_receive_window_adjust ( channel , packet - > data_len - ( stream_id ? 13 : 9 ) , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
}
}
packet = next ;
}
blocking_read = 1 ;
} while ( channel - > blocking & & ( bytes_read = = 0 ) & & ! channel - > remote . close ) ;
if ( channel - > blocking & & ( bytes_read = = 0 ) ) {
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_CLOSED , " Remote end has closed this channel " , 0 ) ;
}
return bytes_read ;
}
/* }}} */
/* {{{ libssh2_channel_write_ex
* Send data to a channel
*/
LIBSSH2_API int libssh2_channel_write_ex ( LIBSSH2_CHANNEL * channel , int stream_id , const char * buf , size_t buflen )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char * packet ;
unsigned long packet_len , bufwrote = 0 ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Writing %d bytes on channel %lu/%lu, stream #%d " , ( int ) buflen , channel - > local . id , channel - > remote . id , stream_id ) ;
# endif
if ( channel - > local . close ) {
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_CLOSED , " We've already closed this channel " , 0 ) ;
return - 1 ;
}
if ( channel - > local . eof ) {
libssh2_error ( session , LIBSSH2_ERROR_CHANNEL_EOF_SENT , " EOF has already been sight, data might be ignored " , 0 ) ;
}
if ( ! channel - > blocking & & ( channel - > local . window_size < = 0 ) ) {
/* Can't write anything */
return 0 ;
}
packet_len = buflen + ( stream_id ? 13 : 9 ) ; /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */
2006-07-10 12:43:11 +02:00
packet = LIBSSH2_ALLOC ( session , packet_len ) ;
2006-07-04 16:07:08 +02:00
if ( ! packet ) {
libssh2_error ( session , LIBSSH2_ERROR_ALLOC , " Unable to allocte space for data transmission packet " , 0 ) ;
return - 1 ;
}
while ( buflen > 0 ) {
size_t bufwrite = buflen ;
unsigned char * s = packet ;
* ( s + + ) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA ;
libssh2_htonu32 ( s , channel - > remote . id ) ; s + = 4 ;
if ( stream_id ) {
libssh2_htonu32 ( s , stream_id ) ; s + = 4 ;
}
/* twiddle our thumbs until there's window space available */
while ( channel - > local . window_size < = 0 ) {
/* Don't worry -- This is never hit unless it's a blocking channel anyway */
if ( libssh2_packet_read ( session , 1 ) < 0 ) {
/* Error occured, disconnect? */
return - 1 ;
}
}
/* Don't exceed the remote end's limits */
/* REMEMBER local means local as the SOURCE of the data */
if ( bufwrite > channel - > local . window_size ) {
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Splitting write block due to %lu byte window_size on %lu/%lu/%d " , channel - > local . window_size , channel - > local . id , channel - > remote . id , stream_id ) ;
# endif
bufwrite = channel - > local . window_size ;
}
if ( bufwrite > channel - > local . packet_size ) {
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Splitting write block due to %lu byte packet_size on %lu/%lu/%d " , channel - > local . packet_size , channel - > local . id , channel - > remote . id , stream_id ) ;
# endif
bufwrite = channel - > local . packet_size ;
}
libssh2_htonu32 ( s , bufwrite ) ; s + = 4 ;
memcpy ( s , buf , bufwrite ) ; s + = bufwrite ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Sending %d bytes on channel %lu/%lu, stream_id=%d " , ( int ) bufwrite , channel - > local . id , channel - > remote . id , stream_id ) ;
# endif
if ( libssh2_packet_write ( session , packet , s - packet ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send channel data " , 0 ) ;
LIBSSH2_FREE ( session , packet ) ;
return - 1 ;
}
/* Shrink local window size */
channel - > local . window_size - = bufwrite ;
/* Adjust buf for next iteration */
buflen - = bufwrite ;
buf + = bufwrite ;
bufwrote + = bufwrite ;
if ( ! channel - > blocking ) {
break ;
}
}
LIBSSH2_FREE ( session , packet ) ;
return bufwrote ;
}
/* }}} */
/* {{{ libssh2_channel_send_eof
* Send EOF on channel
*/
LIBSSH2_API int libssh2_channel_send_eof ( LIBSSH2_CHANNEL * channel )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char packet [ 5 ] ; /* packet_type(1) + channelno(4) */
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Sending EOF on channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
packet [ 0 ] = SSH_MSG_CHANNEL_EOF ;
libssh2_htonu32 ( packet + 1 , channel - > remote . id ) ;
if ( libssh2_packet_write ( session , packet , 5 ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send EOF on channel " , 0 ) ;
return - 1 ;
}
channel - > local . eof = 1 ;
return 0 ;
}
/* }}} */
/* {{{ libssh2_channel_eof
* Read channel ' s eof status
*/
LIBSSH2_API int libssh2_channel_eof ( LIBSSH2_CHANNEL * channel )
{
LIBSSH2_SESSION * session = channel - > session ;
LIBSSH2_PACKET * packet = session - > packets . head ;
while ( packet ) {
if ( ( ( packet - > data [ 0 ] = = SSH_MSG_CHANNEL_DATA ) | | ( packet - > data [ 0 ] = = SSH_MSG_CHANNEL_EXTENDED_DATA ) ) & &
( channel - > local . id = = libssh2_ntohu32 ( packet - > data + 1 ) ) ) {
/* There's data waiting to be read yet, mask the EOF status */
return 0 ;
}
packet = packet - > next ;
}
return channel - > remote . eof ;
}
/* }}} */
/* {{{ libssh2_channel_close
* Close a channel
*/
LIBSSH2_API int libssh2_channel_close ( LIBSSH2_CHANNEL * channel )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char packet [ 5 ] ;
if ( channel - > local . close ) {
/* Already closed, act like we sent another close, even though we didn't... shhhhhh */
return 0 ;
}
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Closing channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
if ( channel - > close_cb ) {
LIBSSH2_CHANNEL_CLOSE ( session , channel ) ;
}
channel - > local . close = 1 ;
packet [ 0 ] = SSH_MSG_CHANNEL_CLOSE ;
libssh2_htonu32 ( packet + 1 , channel - > remote . id ) ;
if ( libssh2_packet_write ( session , packet , 5 ) ) {
libssh2_error ( session , LIBSSH2_ERROR_SOCKET_SEND , " Unable to send close-channel request " , 0 ) ;
return - 1 ;
}
/* TODO: Wait up to a timeout value for a CHANNEL_CLOSE to come back, to avoid the problem alluded to in channel_nextid */
return 0 ;
}
/* }}} */
/* {{{ libssh2_channel_wait_closed
* Awaiting channel close after EOF
*/
LIBSSH2_API int libssh2_channel_wait_closed ( LIBSSH2_CHANNEL * channel )
{
LIBSSH2_SESSION * session = channel - > session ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Awaiting close of channel %lu/%lu " , channel - > local . id , channel - > remote . id ) ;
# endif
/* while channel is not closed, read more
* packets from the network .
* Either or channel will be closed
* or network timeout will occur
*/
while ( ! channel - > remote . close & & libssh2_packet_read ( session , 1 ) > 0 )
;
return 1 ;
}
/* }}} */
/* {{{ libssh2_channel_free
* Make sure a channel is closed , then remove the channel from the session and free its resource ( s )
*/
LIBSSH2_API int libssh2_channel_free ( LIBSSH2_CHANNEL * channel )
{
LIBSSH2_SESSION * session = channel - > session ;
unsigned char channel_id [ 4 ] , * data ;
unsigned long data_len ;
# ifdef LIBSSH2_DEBUG_CONNECTION
_libssh2_debug ( session , LIBSSH2_DBG_CONN , " Freeing channel %lu/%lu resources " , channel - > local . id , channel - > remote . id ) ;
# endif
/* Allow channel freeing even when the socket has lost its connection */
if ( ! channel - > local . close & & ( session - > socket_state = = LIBSSH2_SOCKET_CONNECTED ) & &
libssh2_channel_close ( channel ) ) {
return - 1 ;
}
/* channel->remote.close *might* not be set yet, Well...
* We ' ve sent the close packet , what more do you want ?
* Just let packet_add ignore it when it finally arrives
*/
/* Clear out packets meant for this channel */
libssh2_htonu32 ( channel_id , channel - > local . id ) ;
while ( ( libssh2_packet_ask_ex ( session , SSH_MSG_CHANNEL_DATA , & data , & data_len , 1 , channel_id , 4 , 1 ) > = 0 ) | |
( libssh2_packet_ask_ex ( session , SSH_MSG_CHANNEL_EXTENDED_DATA , & data , & data_len , 1 , channel_id , 4 , 1 ) > = 0 ) ) {
LIBSSH2_FREE ( session , data ) ;
}
/* free "channel_type" */
if ( channel - > channel_type ) {
LIBSSH2_FREE ( session , channel - > channel_type ) ;
}
/* Unlink from channel brigade */
if ( channel - > prev ) {
channel - > prev - > next = channel - > next ;
} else {
session - > channels . head = channel - > next ;
}
if ( channel - > next ) {
channel - > next - > prev = channel - > prev ;
} else {
session - > channels . tail = channel - > prev ;
}
LIBSSH2_FREE ( session , channel ) ;
return 0 ;
}
/* }}} */
/* {{{ libssh2_channel_window_read_ex
* Check the status of the read window
* Returns the number of bytes which the remote end may send without overflowing the window limit
* read_avail ( if passed ) will be populated with the number of bytes actually available to be read
* window_size_initial ( if passed ) will be populated with the window_size_initial as defined by the channel_open request
*/
LIBSSH2_API unsigned long libssh2_channel_window_read_ex ( LIBSSH2_CHANNEL * channel , unsigned long * read_avail , unsigned long * window_size_initial )
{
if ( window_size_initial ) {
* window_size_initial = channel - > remote . window_size_initial ;
}
if ( read_avail ) {
unsigned long bytes_queued = 0 ;
LIBSSH2_PACKET * packet = channel - > session - > packets . head ;
while ( packet ) {
unsigned char packet_type = packet - > data [ 0 ] ;
if ( ( ( packet_type = = SSH_MSG_CHANNEL_DATA ) | | ( packet_type = = SSH_MSG_CHANNEL_EXTENDED_DATA ) ) & &
( libssh2_ntohu32 ( packet - > data + 1 ) = = channel - > local . id ) ) {
bytes_queued + = packet - > data_len - packet - > data_head ;
}
packet = packet - > next ;
}
* read_avail = bytes_queued ;
}
return channel - > remote . window_size ;
}
/* }}} */
/* {{{ libssh2_channel_window_write_ex
* Check the status of the write window
* Returns the number of bytes which may be safely writen on the channel without blocking
* window_size_initial ( if passed ) will be populated with the size of the initial window as defined by the channel_open request
*/
LIBSSH2_API unsigned long libssh2_channel_window_write_ex ( LIBSSH2_CHANNEL * channel , unsigned long * window_size_initial )
{
if ( window_size_initial ) {
/* For locally initiated channels this is very often 0, so it's not *that* useful as information goes */
* window_size_initial = channel - > local . window_size_initial ;
}
return channel - > local . window_size ;
}
/* }}} */