2009-03-23 10 views
3

J'ai un client HTTP en Python qui doit utiliser TLS. J'ai non seulement besoin de pour effectuer des connexions cryptées, mais aussi pour récupérer des informations à partir de la machine distante , telle que l'émetteur du certificat. Je dois faire connexion à de nombreux serveurs HTTP, souvent mal comporté, donc j'ai absolument besoin d'avoir un délai d'attente. Avec des connexions non-TLS, mysocket.settimeout(5) fait ce que je veux.Connexion TLS avec des délais (et quelques autres difficultés)

Parmi les nombreux TLS modules Python:

python-gnutls ne permet pas d'utiliser setTimeout() sur les sockets parce que il utilise des sockets non bloquants:

gnutls.errors.OperationWouldBlock: Function was interrupted. 

python-openssl a un problème similaire:

OpenSSL.SSL.WantReadError 

Le SSL module de la bibliothèque standard ne fonctionne pas avec Python 2.5.

D'autres bibliothèques comme TLSlite ne donnent apparemment pas accès à les métadonnées du certificat.

Le programme est enfilé, donc je ne peux pas utiliser de signaux. J'ai besoin de contrôle détaillé sur la boîte de dialogue HTTP, donc je ne peux pas utiliser une bibliothèque standard comme urllib2. Contexte: c'est le le projet d'enquête DNSwitness. Filetage SO pertinents: Timeout on a Python function call et How to limit execution time of a function call in Python.

Répondre

1

Bien que je ne l'ai jamais utilisé exactement dans ce but, Twisted devrait faire ce que vous voulez. Le seul inconvénient est que c'est une bibliothèque assez grande, et vous aurez également besoin d'installer PyOpenSSL (Twisted en dépend). Si vous ne l'avez jamais utilisé auparavant, l'architecture de rappel de Twisted peut prendre un peu de temps pour s'y habituer (vous voulez vraiment lire les tutoriels avant de commencer). Mis à part cela, il est conçu autour de l'idée de gérer un grand nombre de connexions, il permet bien sûr de spécifier les délais d'attente, les reconnexions, etc., et vous pouvez récupérer les informations du certificat (voir here).

1

Je suppose que les problèmes que vous rencontrez sont les suivants, vous ouvrez une connexion en utilisant PyOpenSSL et vous obtenez toujours une exception WantReadError. Et vous ne pouvez pas distinguer entre cette erreur et un délai d'expiration. Prenons l'exemple suivant:

#!/usr/bin/python 

import OpenSSL 
import socket 
import struct 

context = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.settimeout(5) 
connection = OpenSSL.SSL.Connection(context,s) 
connection.connect(("www.gmail.com",443)) 

# Put the socket in blocking mode 
connection.setblocking(1) 

# Set the timeout using the setsockopt 
tv = struct.pack('ii', int(6), int(0)) 
connection.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, tv) 

print "Connected to " , connection.getpeername() 
print "Sate " , connection.state_string() 

while True: 
    try: 
     connection.do_handshake() 
     break 
    except OpenSSL.SSL.WantReadError: 
     print "Exception" 
     pass 
print "Sate " , connection.state_string() 


print connection.send("koekoek\r\n") 


while True: 
    try: 
     recvstr = connection.recv(1024) 
     break 
    except OpenSSL.SSL.WantReadError: 
     print "Exception" 
     pass 

print recvstr 

Cela ouvrira une connexion SSL à gmail, envoyer une chaîne non valide, lisez la réponse et l'imprimer. Notez que: * la connexion est explicitement définie sur le mode de blocage * le délai d'attente recv est explicitement défini sur dans ce cas 6 secondes.

Maintenant, quel sera le comportement, lorsque le délai d'attente se produit, l'exception WantReadError sera thornw, dans ce cas, après avoir attendu 6 secondes. (Vous pouvez supprimer la valeur True pour éviter la nouvelle tentative, mais dans ce cas, je les ai ajouté pour les tests). Le délai d'attente défini sur le socket semble uniquement être valide dans l'appel connect().Une alternative serait de conserver les sockets en mode non bloquant, ce qui est probablement valable pour le cas GNUTLS, mais aussi pour effectuer vous-même le chronométrage, vous avez le temps de lancer l'appel, et dans le cas vrai, essayez : excepté WantReadError vous effectuez la vérification à chaque fois pour voir si vous n'avez pas attendu trop longtemps.

+0

Mais est-il pas une sorte d'attente occupé, très coûteux pour le processeur? – bortzmeyer

+0

Vous pouvez toujours insérer un sleep, céder le scheduler, ou faire autre chose. Et ce n'est pas comme si vous passiez une longue période dans la boucle, les bons serveurs essayent de répondre assez rapidement. –

0

Une solution simple pourrait être de changer le type de prise en fonction de l'opération. Je l'ai testé cela avec gnutls et cela a fonctionné:

  1. Do settimeout() sur la prise avant de faire connecter() sur la prise nue enveloppée par gnutls, de cette façon connect() est soumise au délai d'attente que vous vouliez.
  2. Assurez-vous de supprimer le délai d'attente avec settimeout (Aucun) ou setblocking (1) AVANT poignée de main GnuTLS()
Questions connexes