📄 ssh.c
字号:
if( attributes->atime )
flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
if( attributes->mtime )
flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
writeUint32( stream, flags );
sputc( stream, attributes->isDirectory ? \
SSH_FILETYPE_DIRECTORY : SSH_FILETYPE_REGULAR );
/* Write the optional attributes */
if( attributes->size != CRYPT_UNUSED )
writeUint64( stream, attributes->size );
if( attributes->permissions != CRYPT_UNUSED )
writeUint32( stream, attributes->permissions );
if( attributes->ctime )
writeUint64Time( stream, attributes->ctime );
if( attributes->atime )
writeUint64Time( stream, attributes->atime );
if( attributes->mtime )
writeUint64Time( stream, attributes->mtime );
}
return( sGetStatus( stream ) );
}
/* Read/write SFTP status:
uint32 id
uint32 error/status code
string error message (ISO-10646 UTF-8 [RFC-2279])
string language tag (as defined in [RFC-1766]) */
static int sizeofStatus( const char *sshStatusString )
{
return( UINT32_SIZE + UINT32_SIZE + \
( UINT32_SIZE + strlen( sshStatusString ) ) + \
UINT32_SIZE );
}
static int readStatus( STREAM *stream, SFTP_INFO *info )
{
static const struct {
const int sftpStatus, cryptlibStatus;
} sftpStatusMap[] = {
{ SSH_FX_OK, CRYPT_OK },
{ SSH_FX_EOF, CRYPT_ERROR_COMPLETE },
{ SSH_FX_NO_SUCH_FILE, CRYPT_ERROR_NOTFOUND },
{ SSH_FX_PERMISSION_DENIED, CRYPT_ERROR_PERMISSION },
{ SSH_FX_FAILURE, CRYPT_ERROR_FAILED },
{ SSH_FX_BAD_MESSAGE, CRYPT_ERROR_BADDATA },
{ SSH_FX_NO_CONNECTION, CRYPT_ERROR_FAILED },
{ SSH_FX_CONNECTION_LOST, CRYPT_ERROR_FAILED },
{ SSH_FX_OP_UNSUPPORTED, CRYPT_ERROR_NOTAVAIL },
{ SSH_FX_INVALID_HANDLE, CRYPT_ERROR_BADDATA },
{ SSH_FX_NO_SUCH_PATH, CRYPT_ERROR_NOTFOUND },
{ SSH_FX_FILE_ALREADY_EXISTS, CRYPT_ERROR_DUPLICATE },
{ SSH_FX_WRITE_PROTECT, CRYPT_ERROR_PERMISSION },
{ SSH_FX_NO_MEDIA, CRYPT_ERROR_FAILED },
{ CRYPT_ERROR, CRYPT_ERROR_FAILED }
};
int value, i, status;
/* Read the status info and make sure that it's valid */
value = readUint32( stream );
status = readUint32( stream );
if( cryptStatusError( status ) )
return( status );
if( value != info->id )
return( CRYPT_ERROR_BADDATA );
/* Translate the SFTP status into a cryptlib status */
for( i = 0; sftpStatusMap[ i ].sftpStatus != CRYPT_ERROR && \
sftpStatusMap[ i ].sftpStatus != status; i++ );
status = sftpStatusMap[ i ].cryptlibStatus;
return( status );
}
static int writeStatus( STREAM *stream, SFTP_INFO *info, const int sshStatus,
const char *sshStatusString )
{
writeUint32( stream, info->id );
writeUint32( stream, sshStatus );
writeString32( stream, sshStatusString, strlen( sshStatusString ) );
return( writeString32( stream, "", 0 ) );
}
static int readSftpPacket( const CRYPT_SESSION cryptSession, void *buffer,
const int bufSize )
{
int bytesCopied, status;
status = cryptPopData( cryptSession, buffer, BUFFER_SIZE, &bytesCopied );
if( cryptStatusError( status ) )
{
printf( "SVR: Couldn't read data from SFTP client, status %d, line "
"%d.\n", status, __LINE__ );
return( status );
}
return( bytesCopied > 0 ? bytesCopied : CRYPT_ERROR_UNDERFLOW );
}
static int writeSftpPacket( const CRYPT_SESSION cryptSession, const void *data,
const int length )
{
int bytesCopied, status;
status = cryptPushData( cryptSession, data, length, &bytesCopied );
if( cryptStatusOK( status ) )
status = cryptFlushData( cryptSession );
if( cryptStatusError( status ) )
{
printf( "SVR: Couldn't write data to SFTP client, status %d, line "
"%d.\n", status, __LINE__ );
return( status );
}
if( bytesCopied < length )
{
printf( "SVR: Only wrote %d of %d bytes of SFTP data, line %d.\n",
bytesCopied, length, __LINE__ );
return( status );
}
return( CRYPT_OK );
}
static int sendAck( const CRYPT_SESSION cryptSession, SFTP_INFO *sftpInfo )
{
STREAM stream;
BYTE buffer[ 128 ];
int length;
/* Ack an SFTP packet */
sMemOpen( &stream, buffer, 128 );
writeUint32( &stream, 1 + sizeofStatus( "" ) );
sputc( &stream, SSH_FXP_STATUS );
writeStatus( &stream, sftpInfo, SSH_FX_OK, "" );
length = stell( &stream );
sMemDisconnect( &stream );
return( writeSftpPacket( cryptSession, buffer, length ) );
}
int sftpServer( const CRYPT_SESSION cryptSession )
{
STREAM stream;
SFTP_ATTRS sftpAttrs;
SFTP_INFO sftpInfo;
BYTE buffer[ BUFFER_SIZE ], nameBuffer[ 128 ];
time_t xferTime;
long xferCount = 0, dataLength;
int length, value, status;
cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_READTIMEOUT, 30 );
memset( &sftpInfo, 0, sizeof( SFTP_INFO ) );
/* Read the client's FXP_INIT and send our response */
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
value = sgetc( &stream );
if( ( length != 1 + 4 ) || ( value != SSH_FXP_INIT ) )
return( CRYPT_ERROR_BADDATA );
sftpInfo.version = readUint32( &stream );
sMemDisconnect( &stream );
printf( "SVR: Client supports SFTP version %d.\n", sftpInfo.version );
sMemOpen( &stream, buffer, BUFFER_SIZE );
writeUint32( &stream, 1 + 4 );
sputc( &stream, SSH_FXP_VERSION );
writeUint32( &stream, 3 );
length = stell( &stream );
sMemDisconnect( &stream );
status = writeSftpPacket( cryptSession, buffer, length );
if( cryptStatusError( status ) )
return( status );
/* Read the client's FXP_OPEN and send our response */
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status ) )
{
printExtError( cryptSession, "SVR: Attempt to read data from "
"client", status, __LINE__ );
return( status );
}
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
value = sgetc( &stream );
if( value == SSH_FXP_STAT )
{
/* See what the client is after */
sftpInfo.id = readUint32( &stream );
length = readUint32( &stream );
sread( &stream, nameBuffer, length );
sMemDisconnect( &stream );
nameBuffer[ length ] = '\0';
printf( "SVR: Client tried to stat file '%s'.\n", nameBuffer );
if( strcmp( nameBuffer, "." ) )
{
puts( "SVR: Don't know how to respond to stat request for this "
"file." );
return( CRYPT_ERROR_NOTAVAIL );
}
/* Send back a dummy response */
memset( &sftpAttrs, 0, sizeof( SFTP_ATTRS ) );
sftpAttrs.isDirectory = TRUE;
sftpAttrs.permissions = 0777;
sftpAttrs.size = CRYPT_UNUSED;
sftpAttrs.atime = sftpAttrs.ctime = sftpAttrs.mtime = time( NULL );
length = sizeofAttributes( &sftpAttrs, sftpInfo.version );
sMemOpen( &stream, buffer, BUFFER_SIZE );
writeUint32( &stream, 1 + UINT32_SIZE + length );
sputc( &stream, SSH_FXP_ATTRS );
writeUint32( &stream, sftpInfo.id );
writeAttributes( &stream, &sftpAttrs, sftpInfo.version );
length = stell( &stream );
sMemDisconnect( &stream );
status = writeSftpPacket( cryptSession, buffer, length );
if( cryptStatusError( status ) )
return( status );
/* See what they want next */
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status ) )
{
printExtError( cryptSession, "SVR: Attempt to read data from "
"client", status, __LINE__ );
return( status );
}
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
value = sgetc( &stream );
}
if( value == SSH_FXP_OPEN )
{
/* See what the client is after */
sftpInfo.id = readUint32( &stream );
length = readUint32( &stream );
sread( &stream, nameBuffer, length );
value = readUint32( &stream );
readAttributes( &stream, &sftpAttrs, sftpInfo.version );
sMemDisconnect( &stream );
nameBuffer[ length ] = '\0';
printf( "Client tried to open file '%s', mode %02X, length %d.\n",
nameBuffer, value, sftpAttrs.size );
/* Putty for some reason tries to open the current directory for
create (rather than the filename), and bails out when it gets a
permission-denied. So I guess we tell it to go ahead... */
sMemOpen( &stream, buffer, BUFFER_SIZE );
writeUint32( &stream, 1 + UINT32_SIZE + ( UINT32_SIZE + 1 ) );
sputc( &stream, SSH_FXP_HANDLE );
writeUint32( &stream, sftpInfo.id );
writeUint32( &stream, 1 );
sputc( &stream, 1 );
length = stell( &stream );
sMemDisconnect( &stream );
status = writeSftpPacket( cryptSession, buffer, length );
if( cryptStatusError( status ) )
return( status );
}
/* Now we're in the write loop... */
xferTime = time( NULL );
dataLength = 0;
while( TRUE )
{
/* See what they want next */
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status ) )
{
printExtError( cryptSession, "SVR: Attempt to read data from "
"client", status, __LINE__ );
return( status );
}
if( status < 1 )
{
printf( "SVR: Read 0 bytes from client.\n" );
return( CRYPT_ERROR_UNDERFLOW );
}
if( dataLength > 0 )
{
xferCount += status;
dataLength -= status;
printf( "SRV: -------- : %d.\r", xferCount );
if( dataLength <= 0 )
break;
continue;
}
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
if( status < BUFFER_SIZE && ( length != status - UINT32_SIZE ) )
{
printf( "Didn't read complete packet, length = %d, byte count = "
"%d.\n", length, status - UINT32_SIZE );
}
value = sgetc( &stream );
if( value != SSH_FXP_WRITE )
break;
sftpInfo.id = readUint32( &stream );
readString32( &stream, nameBuffer, &length, 128 );
value = readUint64( &stream );
dataLength = readUint32( &stream );
printf( "SRV: %8d : %d.\r", value, length );
xferCount += status - stell( &stream );
dataLength -= status - stell( &stream );
sMemDisconnect( &stream );
/* Ack the write */
if( dataLength <= 0 )
{
status = sendAck( cryptSession, &sftpInfo );
if( cryptStatusError( status ) )
return( status );
}
}
xferTime = time( NULL ) - xferTime;
printf( "Transfer time = %d seconds, %ld bytes, %d bytes/sec.\n",
xferTime, xferCount, xferCount / xferTime );
/* Clean up */
if( value != SSH_FXP_CLOSE )
{
printf( "SVR: Client sent unexpected packet %d.\n", value );
return( CRYPT_ERROR_BADDATA );
}
sftpInfo.id = readUint32( &stream );
status = sendAck( cryptSession, &sftpInfo );
if( cryptStatusError( status ) )
return( status );
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( status == CRYPT_ERROR_COMPLETE )
{
puts( "SVR: Client has closed the channel." );
return( CRYPT_OK );
}
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
value = sgetc( &stream );
return( CRYPT_OK );
}
#define SFTP_DATA_AMOUNT ( 1024 * 1024 )
int sftpClient( const CRYPT_SESSION cryptSession )
{
STREAM stream;
SFTP_ATTRS sftpAttrs;
SFTP_INFO sftpInfo;
BYTE buffer[ BUFFER_SIZE ];
long totalLength = SFTP_DATA_AMOUNT;
int length, value, status;
cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_READTIMEOUT, 30 );
memset( &sftpInfo, 0, sizeof( SFTP_INFO ) );
/* Send our FXP_INIT and read back the response */
sMemOpen( &stream, buffer, BUFFER_SIZE );
writeUint32( &stream, 1 + 4 );
sputc( &stream, SSH_FXP_INIT );
writeUint32( &stream, 3 );
length = stell( &stream );
sMemDisconnect( &stream );
status = writeSftpPacket( cryptSession, buffer, length );
if( cryptStatusError( status ) )
return( status );
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
value = sgetc( &stream );
if( ( length != 1 + 4 ) || ( value != SSH_FXP_VERSION ) )
return( CRYPT_ERROR_BADDATA );
sftpInfo.version = readUint32( &stream );
sMemDisconnect( &stream );
printf( "Server supports SFTP version %d.\n", sftpInfo.version );
/* Open the file to transfer */
memset( &sftpAttrs, 0, sizeof( SFTP_ATTRS ) );
sftpAttrs.permissions = 0777;
sftpAttrs.size = CRYPT_UNUSED;
sftpAttrs.atime = sftpAttrs.ctime = sftpAttrs.mtime = time( NULL );
length = sizeofAttributes( &sftpAttrs, sftpInfo.version );
sMemOpen( &stream, buffer, BUFFER_SIZE );
writeUint32( &stream, 1 + UINT32_SIZE + ( UINT32_SIZE + 8 ) + UINT32_SIZE + length );
sputc( &stream, SSH_FXP_OPEN );
writeUint32( &stream, 1 );
writeString32( &stream, "test.dat", 8 );
writeUint32( &stream, SSH_FXF_CREAT | SSH_FXF_WRITE );
writeAttributes( &stream, &sftpAttrs, sftpInfo.version );
length = stell( &stream );
sMemDisconnect( &stream );
status = writeSftpPacket( cryptSession, buffer, length );
if( cryptStatusError( status ) )
return( status );
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -