seleccione y ssl en python

Tengo una aplicación de servidor que utiliza select.select (), y ahora estoy intentando agregarle SSL, sin embargo, cuando escucho los sockets “sin procesar”, aparece el siguiente error:

ValueError: file descriptor cannot be a negative integer (-1) 

así que pensé que usaría los flujos ssl devueltos por ssl.wrap_socket en select en su lugar. Al hacerlo, no devuelve ningún error, pero tampoco funciona. No estoy realmente seguro de cuál es el problema, he investigado mucho y he encontrado publicaciones con problemas similares, pero no he encontrado ningún problema. solución a esto todavía.

Realmente aprecio cualquier ayuda.

El uso de sockets SSL con select() no es tan sencillo como puede parecer al principio. Si bien funcionan bien con eso en el sentido de que no produce un error cuando lo das, si los usas como sockets normales, es probable que te encuentres con alguna rareza tarde o temprano.

Como select() necesita un descriptor de archivo, obtendrá el socket sin formato. Pero incluso si el socket en bruto se vuelve legible, eso no significa que obtendrá datos del socket SSL. Deberá usar sockets no bloqueadores (lo cual es una buena idea al usar select() ) e ignorarlo si lanza SSL_ERROR_WANT_READ (el equivalente de SSL de EWOULDBLOCK ).

Otro problema es que si escribe 2048 bytes en la conexión en el otro extremo, el select() en su final devuelve. Pero si solo lee 1024 bytes desde el socket SSL, es posible que el socket SSL lea internamente más datos, y la siguiente select() no se devolverá aunque haya más datos para leer, posiblemente bloqueando la conexión. Esto se debe a que el socket sin procesar, que es lo que usa select() , no tiene datos, ya que ya está en los buffers del socket SSL.

La primera solución que viene a la mente sería leer más datos hasta que la lectura arroje SSL_ERROR_WANT_READ , vaciando así el búfer. Sin embargo, si el otro extremo genera datos más rápido de lo que puede procesarlos, esto terminará matando de hambre a todas sus otras conexiones hasta que ésta termine de generar datos.

Puede ver cuántos datos almacenados en búfer tiene el socket SSL llamando a sslsock.pending() . Un mejor enfoque, entonces, sería hacer primero una lectura para cierta cantidad de datos, verificar la cantidad de datos pendientes y emitir una segunda lectura para exactamente esa cantidad de datos, vaciando así el búfer sin causar más lecturas.

La página del SSL_pending() para SSL_pending() (la función C detrás de la escena) también dice esto:

SSL_pending () tiene en cuenta solo los bytes del registro TLS / SSL que se está procesando actualmente (si corresponde). Si se establece el indicador read_ahead del objeto SSL, es posible que se hayan leído bytes de protocolo adicionales que contengan más registros TLS / SSL; estos son ignorados por SSL_pending ()

Por lo que entiendo, esto significa que si se establece read_ahead , deberías repetir el segundo paso hasta que SSL_pending() devuelva 0. Estoy bastante seguro de que Python no establece read_ahead , pero es mejor prevenir que lamentar, así que He incluido el bucle en el código de ejemplo.

No estoy muy familiarizado con esto, pero algo como esto debería funcionar:

 # Put the SSL socket to non-blocking mode sslsock.setblocking(0) while True: r, w, e = select.select([sslsock], [], []) if sslsock in r: try: data = sslsock.recv(1024) except ssl.SSLError as e: # Ignore the SSL equivalent of EWOULDBLOCK, but re-raise other errors if e.errno != ssl.SSL_ERROR_WANT_READ: raise continue # No data means end of file if not data: break # Drain the SSL socket's internal buffer. # If you want to remove the loop, make sure you don't call recv() # with a 0 length, since that could cause a read to the raw socket. data_left = sslsock.pending() while data_left: data += sslsock.recv(data_left) data_left = sslsock.pending() # Process the data process(data) 

Como señaló Marius, select.select () funciona con sockets SSL, todavía no sé qué causó mi error silencioso, pero salté mi arma cuando pensé que era SSL + select (). Así se responde esta pregunta.