超时的TLS连接(以及其他一些困难)

时间:2009-03-23 20:36:34

标签: python ssl

我在Python中有一个需要使用TLS的HTTP客户端。我不仅需要 建立加密连接,但也从中检索信息 远程机器,例如证书颁发者。我需要做 连接到许多HTTP服务器,通常表现不佳,所以我绝对 需要暂停。使用非TLS连接, mysocket.settimeout(5)做我想做的事。

在众多TLS Python模块中:

python-gnutls不允许在套接字上使用settimeout(),因为 它使用非阻塞套接字:

gnutls.errors.OperationWouldBlock: Function was interrupted.

python-openssl也有类似的问题:

OpenSSL.SSL.WantReadError

标准库的SSL module不适用于Python 2.5。

TLSlite等其他库显然无法访问 证书的元数据。

程序是线程化的,所以我不能使用信号。我需要详细说明 控制HTTP对话框,所以我不能使用像urllib2这样的标准库。

背景:这是 调查项目DNSwitness。相关的SO主题:Timeout on a Python function callHow to limit execution time of a function call in Python

4 个答案:

答案 0 :(得分:1)

虽然我从未将其用于此目的,Twisted应该做你想要的。唯一的缺点是它是一个相当大的库,你还需要安装PyOpenSSL(Twisted取决于它)。如果你之前从未使用过,那么Twisted基于回调的架构可能会让你习惯(你真的希望在开始之前阅读教程)。

除此之外,它是围绕管理大量连接的想法而设计的,当然它允许您指定超时,重新连接等,并且您可以检索证书信息(请参阅here)。

答案 1 :(得分:1)

我假设您遇到的问题如下,您正在使用PyOpenSSL打开连接,并且您总是遇到WantReadError异常。而你无法区分这个错误和超时。请考虑以下示例:

#!/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 

这将打开与gmail的SSL连接,发送无效字符串,读取响应并打印它。注意: *连接明确设置为阻塞模式 * recv超时在这种情况下明确设置为6秒。

现在行为是什么,当超时发生时,WantReadError异常将是thornw,在这种情况下等待6秒后。 (你可以删除while,以避免重试,但在这种情况下,我添加它们进行测试)。套接字上设置的超时似乎仅在connect()调用中有效。

另一种方法是将套接字保持在非阻塞模式,这可能适用于GNUTLS情况,也就是自己执行计时,你得到启动呼叫的时间,并且在while时,尝试:除了WantReadError,你每次都要进行检查,看看你是否还没有等待太长时间。

答案 2 :(得分:0)

我还建议使用Twisted,并使用M2Crypto作为TLS parts

答案 3 :(得分:0)

一个简单的解决方案可能是根据操作更改套接字类型。我用gnutls对它进行了测试,结果很有效:

  1. 在对gnutls包装的裸插槽上执行connect()之前,先在套接字上执行settimeout(),这样就可以根据需要使用connect()。
  2. 确保使用settimeout(无)或setblocking(1)在GnuTLS握手()
  3. 之前删除超时