1
1

nonblocking support in dh_handshake() from client.c

some packet nonblocking fixes.
reenable sftp from the sample client.


git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@52 7dcaeef0-15fb-0310-b436-a5af3365683c
Этот коммит содержится в:
Aris Adamantiadis 2005-11-30 21:23:12 +00:00
родитель ac4fd09177
Коммит 3edfd105b3
4 изменённых файлов: 133 добавлений и 72 удалений

Просмотреть файл

@ -326,6 +326,8 @@ struct ssh_session {
/* the states are used by the nonblocking stuff to remember */
/* where it was before being interrupted */
int packet_state;
int dh_handshake_state;
STRING *dh_server_signature; //information used by dh_handshake.
KEX server_kex;
KEX client_kex;
@ -413,7 +415,7 @@ int packet_send(SSH_SESSION *session);
int packet_read(SSH_SESSION *session);
int packet_translate(SSH_SESSION *session);
int packet_wait(SSH_SESSION *session,int type,int blocking);
int packet_flush(SSH_SESSION *session, int enforce_blocking);
/* connect.c */
SSH_SESSION *ssh_session_new();
int ssh_connect_host(SSH_SESSION *session, const char *host,const char

Просмотреть файл

@ -104,62 +104,100 @@ int ssh_send_banner(SSH_SESSION *session,int server){
return 0;
}
#define DH_STATE_INIT 0
#define DH_STATE_INIT_TO_SEND 1
#define DH_STATE_INIT_SENT 2
#define DH_STATE_NEWKEYS_TO_SEND 3
#define DH_STATE_NEWKEYS_SENT 4
#define DH_STATE_FINISHED 5
static int dh_handshake(SSH_SESSION *session){
STRING *e,*f,*pubkey,*signature;
packet_clear_out(session);
buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT);
dh_generate_x(session);
dh_generate_e(session);
e=dh_get_e(session);
buffer_add_ssh_string(session->out_buffer,e);
packet_send(session);
free(e);
if(packet_wait(session,SSH2_MSG_KEXDH_REPLY,1))
return -1;
pubkey=buffer_get_ssh_string(session->in_buffer);
if(!pubkey){
ssh_set_error(session,SSH_FATAL,"No public key in packet");
return -1;
int ret;
switch(session->dh_handshake_state){
case DH_STATE_INIT:
packet_clear_out(session);
buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT);
dh_generate_x(session);
dh_generate_e(session);
e=dh_get_e(session);
buffer_add_ssh_string(session->out_buffer,e);
ret=packet_send(session);
free(e);
session->dh_handshake_state=DH_STATE_INIT_TO_SEND;
if(ret==SSH_ERROR)
return ret;
case DH_STATE_INIT_TO_SEND:
ret=packet_flush(session,0);
if(ret!=SSH_OK)
return ret; // SSH_ERROR or SSH_AGAIN
session->dh_handshake_state=DH_STATE_INIT_SENT;
case DH_STATE_INIT_SENT:
ret=packet_wait(session,SSH2_MSG_KEXDH_REPLY,1);
if(ret != SSH_OK)
return ret;
pubkey=buffer_get_ssh_string(session->in_buffer);
if(!pubkey){
ssh_set_error(session,SSH_FATAL,"No public key in packet");
return SSH_ERROR;
}
dh_import_pubkey(session,pubkey);
f=buffer_get_ssh_string(session->in_buffer);
if(!f){
ssh_set_error(session,SSH_FATAL,"No F number in packet");
return SSH_ERROR;
}
dh_import_f(session,f);
free(f);
if(!(signature=buffer_get_ssh_string(session->in_buffer))){
ssh_set_error(session,SSH_FATAL,"No signature in packet");
return SSH_ERROR;
}
session->dh_server_signature=signature;
dh_build_k(session);
// send the MSG_NEWKEYS
packet_clear_out(session);
buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS);
packet_send(session);
session->dh_handshake_state=DH_STATE_NEWKEYS_TO_SEND;
case DH_STATE_NEWKEYS_TO_SEND:
ret=packet_flush(session,0);
if(ret != SSH_OK)
return ret;
ssh_say(2,"SSH_MSG_NEWKEYS sent\n");
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
case DH_STATE_NEWKEYS_SENT:
ret=packet_wait(session,SSH2_MSG_NEWKEYS,1);
if(ret != SSH_OK)
return ret;
ssh_say(2,"Got SSH_MSG_NEWKEYS\n");
make_sessionid(session);
/* set the cryptographic functions for the next crypto */
/* (it is needed for generate_session_keys for key lenghts) */
if(crypt_set_algorithms(session))
return SSH_ERROR;
generate_session_keys(session);
/* verify the host's signature. XXX do it sooner */
signature=session->dh_server_signature;
session->dh_server_signature=NULL;
if(signature_verify(session,signature)){
free(signature);
return SSH_ERROR;
}
free(signature); /* forget it for now ... */
/* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */
if(session->current_crypto)
crypto_free(session->current_crypto);
/* XXX later, include a function to change keys */
session->current_crypto=session->next_crypto;
session->next_crypto=crypto_new();
session->dh_handshake_state=DH_STATE_FINISHED;
return SSH_OK;
default:
ssh_set_error(session,SSH_FATAL,"Invalid state in dh_handshake():%d",session->dh_handshake_state);
return SSH_ERROR;
}
dh_import_pubkey(session,pubkey);
f=buffer_get_ssh_string(session->in_buffer);
if(!f){
ssh_set_error(session,SSH_FATAL,"No F number in packet");
return -1;
}
dh_import_f(session,f);
free(f);
if(!(signature=buffer_get_ssh_string(session->in_buffer))){
ssh_set_error(session,SSH_FATAL,"No signature in packet");
return -1;
}
dh_build_k(session);
packet_wait(session,SSH2_MSG_NEWKEYS,1);
ssh_say(2,"Got SSH_MSG_NEWKEYS\n");
packet_clear_out(session);
buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS);
packet_send(session);
ssh_say(2,"SSH_MSG_NEWKEYS sent\n");
make_sessionid(session);
/* set the cryptographic functions for the next crypto (it is needed for generate_session_keys for key lenghts) */
if(crypt_set_algorithms(session))
return -1;
generate_session_keys(session);
/* verify the host's signature. XXX do it sooner */
if(signature_verify(session,signature)){
free(signature);
return -1;
}
free(signature); /* forget it for now ... */
/* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */
if(session->current_crypto)
crypto_free(session->current_crypto);
/* XXX later, include a function to change keys */
session->current_crypto=session->next_crypto;
session->next_crypto=crypto_new();
return 0;
/* not reached */
return SSH_ERROR;
}
int ssh_service_request(SSH_SESSION *session,char *service){
@ -185,13 +223,13 @@ int ssh_connect(SSH_SESSION *session){
SSH_OPTIONS *options=session->options;
if(!session->options){
ssh_set_error(session,SSH_FATAL,"Must set options before connect");
return -1;
return SSH_ERROR;
}
session->client=1;
ssh_crypto_init();
if(options->fd==-1 && !options->host){
ssh_set_error(session,SSH_FATAL,"Hostname required");
return -1;
return SSH_ERROR;
}
if(options->fd != -1)
fd=options->fd;

Просмотреть файл

@ -335,19 +335,19 @@ int packet_translate(SSH_SESSION *session){
static int atomic_write(int fd, void *buffer, int len){
int written;
do {
while(len >0) {
written=write(fd,buffer,len);
if(written==0 || written==-1)
return -1;
return SSH_ERROR;
len-=written;
buffer+=written;
} while (len > 0);
}
return SSH_OK;
}
/* when doing a nonblocking write, you should issue the packet_write only once, then
* do packet_nonblocking_flush() until you get a SSH_OK or a SSH_ERROR */
int packet_nonblocking_flush(SSH_SESSION *session){
static int packet_nonblocking_flush(SSH_SESSION *session){
int except, can_write;
int w;
ssh_fd_poll(session,&can_write,&except); /* internally sets data_to_write */
@ -375,7 +375,11 @@ int packet_nonblocking_flush(SSH_SESSION *session){
}
/* blocking packet flush */
int packet_blocking_flush(SSH_SESSION *session){
static int packet_blocking_flush(SSH_SESSION *session){
if(session->data_except)
return SSH_ERROR;
if(buffer_get_rest(session->out_socket_buffer)==0)
return SSH_OK;
if(atomic_write(session->fd,buffer_get_rest(session->out_socket_buffer),
buffer_get_rest_len(session->out_socket_buffer))){
session->data_to_write=0;
@ -392,6 +396,16 @@ int packet_blocking_flush(SSH_SESSION *session){
return SSH_OK; // no data pending
}
/* Write the the bufferized output. If the session is blocking, or enforce_blocking
* is set, the call may block. Otherwise, it won't block.
* return SSH°OK if everything has been sent, SSH_AGAIN if there are still things
* to send on buffer, SSH_ERROR if there is an error. */
int packet_flush(SSH_SESSION *session, int enforce_blocking){
if(enforce_blocking || session->blocking)
return packet_blocking_flush(session);
return packet_nonblocking_flush(session);
}
/* this function places the outgoing packet buffer into an outgoing socket buffer */
static int socket_write(SSH_SESSION *session){
if(!session->out_socket_buffer){
@ -596,15 +610,17 @@ static int packet_wait1(SSH_SESSION *session,int type,int blocking){
}
#endif /* HAVE_SSH1 */
static int packet_wait2(SSH_SESSION *session,int type,int blocking){
int ret;
while(1){
if(packet_read2(session))
return -1;
ret=packet_read2(session);
if(ret != SSH_OK)
return ret;
if(packet_translate(session))
return -1;
return SSH_ERROR;
switch(session->in_packet.type){
case SSH2_MSG_DISCONNECT:
packet_parse(session);
return -1;
return SSH_ERROR;
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
case SSH2_MSG_CHANNEL_DATA:
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
@ -612,20 +628,20 @@ static int packet_wait2(SSH_SESSION *session,int type,int blocking){
case SSH2_MSG_CHANNEL_EOF:
case SSH2_MSG_CHANNEL_CLOSE:
packet_parse(session);
break;;
break;
case SSH2_MSG_IGNORE:
break;
default:
if(type && (type != session->in_packet.type)){
ssh_set_error(session,SSH_FATAL,"waitpacket(): Received a %d type packet, was waiting for a %d\n",session->in_packet.type,type);
return -1;
return SSH_ERROR;
}
return 0;
return SSH_OK;
}
if(blocking==0)
return 0;
return SSH_OK; //shouldn't it return SSH_AGAIN here ?
}
return 0;
return SSH_OK;
}
int packet_wait(SSH_SESSION *session, int type, int block){
#ifdef HAVE_SSH1

Просмотреть файл

@ -319,7 +319,7 @@ void do_sftp(SSH_SESSION *session){
ssh_say(0,"Error reading file : %s\n",ssh_get_error(session));
sftp_file_close(fichier);
sftp_file_close(to);
printf("fichiers fermщs\n");
printf("fichiers ferm<EFBFBD>\n");
to=sftp_open(sftp,"/tmp/grosfichier",O_WRONLY|O_CREAT,NULL);
for(i=0;i<1000;++i){
len=sftp_write(to,data,8000);
@ -331,7 +331,7 @@ void do_sftp(SSH_SESSION *session){
sftp_file_close(to);
/* close the sftp session */
sftp_free(sftp);
printf("session sftp terminщe\n");
printf("session sftp termin<EFBFBD>\n");
}
int auth_kbdint(SSH_SESSION *session){
@ -463,6 +463,11 @@ int main(int argc, char **argv){
memset(password,0,strlen(password));
}
ssh_say(1,"Authentication success\n");
printf("%s\n",argv[0]);
if(strstr(argv[0],"sftp")){
sftp=1;
ssh_say(1,"doing sftp instead\n");
}
if(!sftp){
if(!cmds[0])
shell(session);