[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

How use ssh_select after ­fail in ssh_bind_accept?


Hello, 

I have question about using of ssh_select.
It's required to wait events on several descriptors (ssh and not ssh). The system select or ssh_select can be used for this purpose.

But I have problem with error cases after ssh_bind_accept(). Common algorithm is:

while ( ssh_select( descrs) == SSH_OK)

{

   if (ssh_listen_descr is set)

   {

       ssh_bind_accept()

       // create session etc

   }

   else if ( other descr)

  {

     ....

  } 

}

When ssh_bind_accept() returns error (for example: 'too many open files' or 'Can't open file'), then ssh_select() will return read event on listening ssh descriptor always. Event the client has stopped already.

How I can process an error of ssh_bind_accept()  to avoid endless events in ssh_select() ?

Code example in attach. To reproduce problem try to connect to port=2222.

Note: example will create many files 'delmeXXX' in current dir.

Example of output:

Max open file count was reached: can't open file delme252 (253 files opened). errno=24, strerror: Too many open files
Wait for ssh_select
ssh_select result: 0
ssh_bind_accept failed: Error opening ./ssh_host_dsa_key: Too many open files
Can't accept connection
Wait for ssh_select
ssh_select result: 0
ssh_bind_accept failed: Error opening ./ssh_host_dsa_key: Too many open files
Can't accept connection
Wait for ssh_select
ssh_select result: 0
ssh_bind_accept failed: Error opening ./ssh_host_dsa_key: Too many open files
Can't accept connection
Wait for ssh_select
ssh_select result: 0
...

So, select have endless read event on litening FD.

--

Regards,

Andrey
/*

CC ./server.cc -I/home/shamanin/tmp/libssh/include -I/home/shamanin/tmp/libssh/openssl/inc /home/shamanin/tmp/libssh/libssh_static.a /home/shamanin/tmp/libssh/openssl/lib/libssl.a /home/shamanin/tmp/libssh/openssl/lib/libcrypto.a -lsocket -lz -lnsl -lresolv -lposix4 /usr/sfw/lib/gcc/sparc-sun-solaris2.10/3.4.3/libgcc.a

*/

#include <libssh/libssh.h>
#include <libssh/server.h>
#include <stdio.h>
#include <sys/time.h>
#include <strings.h>
#include <errno.h>

const int bindPort = 2222;
const char* bindIp = "0.0.0.0";

const char* dsaKeyFile = "./ssh_host_dsa_key";
const char* rsaKeyFile = "./ssh_host_rsa_key";

int setKeys(ssh_bind bind)
{
   if (ssh_bind_options_set(bind, SSH_BIND_OPTIONS_DSAKEY, dsaKeyFile) < 0)
   {
      printf("Cannot set DSA key file\n");
      return -1;
   }

   if (ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSAKEY, rsaKeyFile) < 0)
   {
      printf("Cannot set RSA key file\n");
      return -1;
   }

   return 0;
}

int accept_ssh_connection(ssh_bind bind, ssh_session &retSession)
{
   if ((retSession = ssh_new()) == NULL)
   {
      printf("Can't create ssh session\n");
      return -1;
   }

   int ret = ssh_bind_accept(bind, retSession);  // this call should be failed in test
   if (ret != SSH_OK)
   {
      ssh_disconnect(retSession);
      ssh_free(retSession);
      retSession = 0;
      printf("ssh_bind_accept failed: %s\n", ssh_get_error(bind));
      return -1;
   }

   
   return 0;
}

int select_loop(ssh_bind bind, ssh_session &retSession)
{
   ssh_channel channels[1], outchannels[1];
   fd_set fds;
   int maxfd;
   struct timeval timeout;
   int listenFd = ssh_bind_get_fd(bind);

   timeout.tv_sec=300000; // long time
   timeout.tv_usec=0;

   channels[0] = 0;
   outchannels[0] = 0;
   FD_ZERO(&fds);
   FD_SET(listenFd,&fds);  // in real application fds will contain additional descriptors of other sockets
   
   maxfd = listenFd + 1;

   int ret = SSH_OK;
   while (ret == SSH_OK)
   {
      printf("Wait for ssh_select\n");
      ret = ssh_select(channels,outchannels,maxfd,&fds,&timeout);
      printf("ssh_select result: %d\n", ret);

      if (ret == SSH_OK && FD_ISSET(listenFd, &fds)) // is event on SSH listening descriptor
      {
         if (accept_ssh_connection(bind, retSession) < 0)
         {            
            printf("Can't accept connection\n");

            {
               char buf[32];
               int rres = read(listenFd, buf, sizeof(buf));
               printf("read %d bytes from failed socket\n", rres);
            }

            continue;  // we can't accept incomming SSH connection for any reason, but we should listen descriptors for other events
            // problem is: ssh_select returns event for listening descriptor always, after fail in ssh_bind_accept
            //return -1;
         }
         else
         {
            printf("Connection was accepted\n");
         }
      }
   }
   return 0;
}


void openFiles()
{
   for(int i=0; i<10000; ++i)
   {
      char fname[128];
      snprintf(fname, sizeof(fname), "delme%d", i);

      FILE* fp = fopen(fname, "w");
      if (!fp)
      {
         int err = errno;
         printf("Max open file count was reached: can't open file %s (%d files opened). errno=%d, strerror: %s\n", fname, i+1, err, strerror(err));

         if (i < 10)
            printf("++++ Few files were opened. May be test was reproducced incorrectly.\n");

         return;
      }      
   }
}

int main()
{
   ssh_bind    bind = 0;
   ssh_session session = 0;
   ssh_channel channel = 0;

   if(ssh_init() != 0)
   {
      printf("Can't init libssh\n");
      return -1;
   }

   if ((bind = ssh_bind_new()) == NULL)
   {
      printf("ssh_bind_new error: %s\n",ssh_get_error(bind));
      return -1;
   }
   if (ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, bindIp) < 0 ||
       ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT, (void*) &bindPort) < 0)
   {
      printf("ssh_bind_options_set error: %s\n",ssh_get_error(bind));
      return -1;
   }

   if (setKeys(bind) < 0)
      return -1;
   
   if (ssh_bind_listen(bind) < 0)
   {
      printf("ssh_bind_listen error: %s\n",ssh_get_error(bind));
      return -1;
   }

   openFiles();

   select_loop(bind, session);

   
   ssh_finalize();
   return 0;
}

Archive administrator: postmaster@lists.cynapses.org