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
Этот коммит содержится в:
родитель
ac4fd09177
Коммит
3edfd105b3
@ -326,6 +326,8 @@ struct ssh_session {
|
|||||||
/* the states are used by the nonblocking stuff to remember */
|
/* the states are used by the nonblocking stuff to remember */
|
||||||
/* where it was before being interrupted */
|
/* where it was before being interrupted */
|
||||||
int packet_state;
|
int packet_state;
|
||||||
|
int dh_handshake_state;
|
||||||
|
STRING *dh_server_signature; //information used by dh_handshake.
|
||||||
|
|
||||||
KEX server_kex;
|
KEX server_kex;
|
||||||
KEX client_kex;
|
KEX client_kex;
|
||||||
@ -413,7 +415,7 @@ int packet_send(SSH_SESSION *session);
|
|||||||
int packet_read(SSH_SESSION *session);
|
int packet_read(SSH_SESSION *session);
|
||||||
int packet_translate(SSH_SESSION *session);
|
int packet_translate(SSH_SESSION *session);
|
||||||
int packet_wait(SSH_SESSION *session,int type,int blocking);
|
int packet_wait(SSH_SESSION *session,int type,int blocking);
|
||||||
|
int packet_flush(SSH_SESSION *session, int enforce_blocking);
|
||||||
/* connect.c */
|
/* connect.c */
|
||||||
SSH_SESSION *ssh_session_new();
|
SSH_SESSION *ssh_session_new();
|
||||||
int ssh_connect_host(SSH_SESSION *session, const char *host,const char
|
int ssh_connect_host(SSH_SESSION *session, const char *host,const char
|
||||||
|
@ -104,53 +104,84 @@ int ssh_send_banner(SSH_SESSION *session,int server){
|
|||||||
return 0;
|
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){
|
static int dh_handshake(SSH_SESSION *session){
|
||||||
STRING *e,*f,*pubkey,*signature;
|
STRING *e,*f,*pubkey,*signature;
|
||||||
|
int ret;
|
||||||
|
switch(session->dh_handshake_state){
|
||||||
|
case DH_STATE_INIT:
|
||||||
packet_clear_out(session);
|
packet_clear_out(session);
|
||||||
buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT);
|
buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT);
|
||||||
dh_generate_x(session);
|
dh_generate_x(session);
|
||||||
dh_generate_e(session);
|
dh_generate_e(session);
|
||||||
e=dh_get_e(session);
|
e=dh_get_e(session);
|
||||||
buffer_add_ssh_string(session->out_buffer,e);
|
buffer_add_ssh_string(session->out_buffer,e);
|
||||||
packet_send(session);
|
ret=packet_send(session);
|
||||||
free(e);
|
free(e);
|
||||||
if(packet_wait(session,SSH2_MSG_KEXDH_REPLY,1))
|
session->dh_handshake_state=DH_STATE_INIT_TO_SEND;
|
||||||
return -1;
|
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);
|
pubkey=buffer_get_ssh_string(session->in_buffer);
|
||||||
if(!pubkey){
|
if(!pubkey){
|
||||||
ssh_set_error(session,SSH_FATAL,"No public key in packet");
|
ssh_set_error(session,SSH_FATAL,"No public key in packet");
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
dh_import_pubkey(session,pubkey);
|
dh_import_pubkey(session,pubkey);
|
||||||
f=buffer_get_ssh_string(session->in_buffer);
|
f=buffer_get_ssh_string(session->in_buffer);
|
||||||
if(!f){
|
if(!f){
|
||||||
ssh_set_error(session,SSH_FATAL,"No F number in packet");
|
ssh_set_error(session,SSH_FATAL,"No F number in packet");
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
dh_import_f(session,f);
|
dh_import_f(session,f);
|
||||||
free(f);
|
free(f);
|
||||||
if(!(signature=buffer_get_ssh_string(session->in_buffer))){
|
if(!(signature=buffer_get_ssh_string(session->in_buffer))){
|
||||||
ssh_set_error(session,SSH_FATAL,"No signature in packet");
|
ssh_set_error(session,SSH_FATAL,"No signature in packet");
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
|
session->dh_server_signature=signature;
|
||||||
dh_build_k(session);
|
dh_build_k(session);
|
||||||
packet_wait(session,SSH2_MSG_NEWKEYS,1);
|
// send the MSG_NEWKEYS
|
||||||
ssh_say(2,"Got SSH_MSG_NEWKEYS\n");
|
|
||||||
packet_clear_out(session);
|
packet_clear_out(session);
|
||||||
buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS);
|
buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS);
|
||||||
packet_send(session);
|
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");
|
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);
|
make_sessionid(session);
|
||||||
/* set the cryptographic functions for the next crypto (it is needed for generate_session_keys for key lenghts) */
|
/* set the cryptographic functions for the next crypto */
|
||||||
|
/* (it is needed for generate_session_keys for key lenghts) */
|
||||||
if(crypt_set_algorithms(session))
|
if(crypt_set_algorithms(session))
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
generate_session_keys(session);
|
generate_session_keys(session);
|
||||||
/* verify the host's signature. XXX do it sooner */
|
/* verify the host's signature. XXX do it sooner */
|
||||||
|
signature=session->dh_server_signature;
|
||||||
|
session->dh_server_signature=NULL;
|
||||||
if(signature_verify(session,signature)){
|
if(signature_verify(session,signature)){
|
||||||
free(signature);
|
free(signature);
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
free(signature); /* forget it for now ... */
|
free(signature); /* forget it for now ... */
|
||||||
/* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */
|
/* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */
|
||||||
@ -159,7 +190,14 @@ static int dh_handshake(SSH_SESSION *session){
|
|||||||
/* XXX later, include a function to change keys */
|
/* XXX later, include a function to change keys */
|
||||||
session->current_crypto=session->next_crypto;
|
session->current_crypto=session->next_crypto;
|
||||||
session->next_crypto=crypto_new();
|
session->next_crypto=crypto_new();
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
/* not reached */
|
||||||
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ssh_service_request(SSH_SESSION *session,char *service){
|
int ssh_service_request(SSH_SESSION *session,char *service){
|
||||||
@ -185,13 +223,13 @@ int ssh_connect(SSH_SESSION *session){
|
|||||||
SSH_OPTIONS *options=session->options;
|
SSH_OPTIONS *options=session->options;
|
||||||
if(!session->options){
|
if(!session->options){
|
||||||
ssh_set_error(session,SSH_FATAL,"Must set options before connect");
|
ssh_set_error(session,SSH_FATAL,"Must set options before connect");
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
session->client=1;
|
session->client=1;
|
||||||
ssh_crypto_init();
|
ssh_crypto_init();
|
||||||
if(options->fd==-1 && !options->host){
|
if(options->fd==-1 && !options->host){
|
||||||
ssh_set_error(session,SSH_FATAL,"Hostname required");
|
ssh_set_error(session,SSH_FATAL,"Hostname required");
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
}
|
}
|
||||||
if(options->fd != -1)
|
if(options->fd != -1)
|
||||||
fd=options->fd;
|
fd=options->fd;
|
||||||
|
@ -335,19 +335,19 @@ int packet_translate(SSH_SESSION *session){
|
|||||||
|
|
||||||
static int atomic_write(int fd, void *buffer, int len){
|
static int atomic_write(int fd, void *buffer, int len){
|
||||||
int written;
|
int written;
|
||||||
do {
|
while(len >0) {
|
||||||
written=write(fd,buffer,len);
|
written=write(fd,buffer,len);
|
||||||
if(written==0 || written==-1)
|
if(written==0 || written==-1)
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
len-=written;
|
len-=written;
|
||||||
buffer+=written;
|
buffer+=written;
|
||||||
} while (len > 0);
|
}
|
||||||
return SSH_OK;
|
return SSH_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* when doing a nonblocking write, you should issue the packet_write only once, then
|
/* 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 */
|
* 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 except, can_write;
|
||||||
int w;
|
int w;
|
||||||
ssh_fd_poll(session,&can_write,&except); /* internally sets data_to_write */
|
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 */
|
/* 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),
|
if(atomic_write(session->fd,buffer_get_rest(session->out_socket_buffer),
|
||||||
buffer_get_rest_len(session->out_socket_buffer))){
|
buffer_get_rest_len(session->out_socket_buffer))){
|
||||||
session->data_to_write=0;
|
session->data_to_write=0;
|
||||||
@ -392,6 +396,16 @@ int packet_blocking_flush(SSH_SESSION *session){
|
|||||||
return SSH_OK; // no data pending
|
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 */
|
/* this function places the outgoing packet buffer into an outgoing socket buffer */
|
||||||
static int socket_write(SSH_SESSION *session){
|
static int socket_write(SSH_SESSION *session){
|
||||||
if(!session->out_socket_buffer){
|
if(!session->out_socket_buffer){
|
||||||
@ -596,15 +610,17 @@ static int packet_wait1(SSH_SESSION *session,int type,int blocking){
|
|||||||
}
|
}
|
||||||
#endif /* HAVE_SSH1 */
|
#endif /* HAVE_SSH1 */
|
||||||
static int packet_wait2(SSH_SESSION *session,int type,int blocking){
|
static int packet_wait2(SSH_SESSION *session,int type,int blocking){
|
||||||
|
int ret;
|
||||||
while(1){
|
while(1){
|
||||||
if(packet_read2(session))
|
ret=packet_read2(session);
|
||||||
return -1;
|
if(ret != SSH_OK)
|
||||||
|
return ret;
|
||||||
if(packet_translate(session))
|
if(packet_translate(session))
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
switch(session->in_packet.type){
|
switch(session->in_packet.type){
|
||||||
case SSH2_MSG_DISCONNECT:
|
case SSH2_MSG_DISCONNECT:
|
||||||
packet_parse(session);
|
packet_parse(session);
|
||||||
return -1;
|
return SSH_ERROR;
|
||||||
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
||||||
case SSH2_MSG_CHANNEL_DATA:
|
case SSH2_MSG_CHANNEL_DATA:
|
||||||
case SSH2_MSG_CHANNEL_EXTENDED_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_EOF:
|
||||||
case SSH2_MSG_CHANNEL_CLOSE:
|
case SSH2_MSG_CHANNEL_CLOSE:
|
||||||
packet_parse(session);
|
packet_parse(session);
|
||||||
break;;
|
break;
|
||||||
case SSH2_MSG_IGNORE:
|
case SSH2_MSG_IGNORE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if(type && (type != session->in_packet.type)){
|
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);
|
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)
|
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){
|
int packet_wait(SSH_SESSION *session, int type, int block){
|
||||||
#ifdef HAVE_SSH1
|
#ifdef HAVE_SSH1
|
||||||
|
9
sample.c
9
sample.c
@ -319,7 +319,7 @@ void do_sftp(SSH_SESSION *session){
|
|||||||
ssh_say(0,"Error reading file : %s\n",ssh_get_error(session));
|
ssh_say(0,"Error reading file : %s\n",ssh_get_error(session));
|
||||||
sftp_file_close(fichier);
|
sftp_file_close(fichier);
|
||||||
sftp_file_close(to);
|
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);
|
to=sftp_open(sftp,"/tmp/grosfichier",O_WRONLY|O_CREAT,NULL);
|
||||||
for(i=0;i<1000;++i){
|
for(i=0;i<1000;++i){
|
||||||
len=sftp_write(to,data,8000);
|
len=sftp_write(to,data,8000);
|
||||||
@ -331,7 +331,7 @@ void do_sftp(SSH_SESSION *session){
|
|||||||
sftp_file_close(to);
|
sftp_file_close(to);
|
||||||
/* close the sftp session */
|
/* close the sftp session */
|
||||||
sftp_free(sftp);
|
sftp_free(sftp);
|
||||||
printf("session sftp terminщe\n");
|
printf("session sftp termin<EFBFBD>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int auth_kbdint(SSH_SESSION *session){
|
int auth_kbdint(SSH_SESSION *session){
|
||||||
@ -463,6 +463,11 @@ int main(int argc, char **argv){
|
|||||||
memset(password,0,strlen(password));
|
memset(password,0,strlen(password));
|
||||||
}
|
}
|
||||||
ssh_say(1,"Authentication success\n");
|
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(!sftp){
|
||||||
if(!cmds[0])
|
if(!cmds[0])
|
||||||
shell(session);
|
shell(session);
|
||||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user