wrap_socket() 收到意外的关键字参数 'server_hostname'?

3 投票
2 回答
9545 浏览
提问于 2025-04-17 22:11

我正在尝试用Python来测试一个网络服务器。我几乎没有Python的经验,但听说它很容易学,而且做事情也很简单(这不是我自己的看法,只是别人的意见)。我正在使用的脚本是:

s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.wrap_socket(s1,
                     ca_certs="./pki/signing-dss-cert.pem",
                     cert_reqs=ssl.CERT_REQUIRED,
                     ssl_version=ssl.PROTOCOL_TLSv1,
                     server_hostname="localhost")

s2.connect( ("localhost", 8443) )

s2.send("GET / ")
time.sleep(1)
s2.send("HTTP/1.1")

出现的错误是:

Traceback (most recent call last):
  File "./fetch.sh", line 10, in <module>
    server_hostname="localhost")
TypeError: wrap_socket() got an unexpected keyword argument 'server_hostname'

我还尝试过使用 servernamenamehostnamesni,但是都没有成功。

Python的文档里没有提到SNI(用于套接字对象的TLS/SSL封装SSL维基页面)。不过我知道,关于SNI和 server_hostname 的补丁在2010年就已经加入了(SSLContext.wrap_socket 添加一个 *server_hostname* 参数, 更改集 65593:846c0e1342d0)。

我需要访问的等效OpenSSL调用是 SSL_set_tlsext_host_name

我该如何指定SNI主机名呢?(最终,我需要把它设置为一个任意的名称,因为我在测试一个代理)。

2 个回答

0

我的环境是:requests==2.7.0,python-2.7.5-34.el7.x86_64,gevent==1.0.2

把Python版本换成python-2.7.5-18.el7_1.1.x86_64后,问题就解决了。

在CentOS上:

sudo rpm -Uvh --oldpackage python-devel-2.7.5-18.el7_1.1.x86_64.rpm  python-libs-2.7.5-18.el7_1.1.x86_64.rpm  python-2.7.5-18.el7_1.1.x86_64.rpm

可以在谷歌上搜索相关的包。

2

你提到的这个补丁是针对Python 3.2的,而你现在用的是Python 2.7。问题5639似乎也表明没有计划将SNI支持移植到Python 2.7上。

你可以考虑用pyOpenSSL来包装socket,它的Connection类从0.13版本开始就有一个set_tlsext_host_name方法。(我不太确定Debian 7.3自带哪个版本,你可以考虑设置一个虚拟环境,然后根据需要升级到更新的版本。)

在pyOpenSSL的库里有一个SNI示例

如果你希望你的wrap_socket用法能更好地兼容你在httplib连接中替换sock的做法,可以看看urllib3是如何用pyOpenSSL实现的。基本上,它是从一个现有的socket创建一个OpenSSL.SSL.Connection,但由于这个连接和socket不兼容,它会把它包装成一个实现所需方法的类。

(顺便提一下,在Python 2.7中,urlliburllib2httpconnection根本不进行任何证书验证,除非你自己通过包装它们的socket来实现。)

编辑:

这里有一个可以在Python 3.2中工作的代码版本。不幸的是,server_name参数不在普通的ssl.wrap_socket中,只有在SSLContext.wrap_socket里,但你可以直接使用SSLSocket

import socket
import ssl

CA_BUNDLE_FILE="/etc/ssl/certs/ca-certificates.crt"


HOST = "sni.velox.ch"
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = ssl.SSLSocket(sock=s1, ca_certs=CA_BUNDLE_FILE,
                     cert_reqs=ssl.CERT_REQUIRED,
                     ssl_version=ssl.PROTOCOL_TLSv1,
                     server_hostname=HOST)

s2.connect((HOST, 443))

s2.send(bytes("GET / HTTP/1.1\n", "UTF-8"))
# This might need to be modified when using another port
s2.send(bytes("Host: %s\n" % (HOST,), "UTF-8"))
s2.send(bytes("\n", "UTF-8"))

# Certainly not the best way to read the response, but it works.
while True:
    x = s2.read()
    if not x:
        break
    print(x)

撰写回答