问题
I'm trying to use Python to test a web server. I have nearly no experience with Python, but encouraged to use it because its easy to learn and simple to do things with (someone else's opinion, not mine at the moment). The script I am using is:
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")
The error is:
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'
I've also tried using servername
, name
, hostname
and sni
with no joy.
The Python docs don't mention SNI (TLS/SSL wrapper for socket objects and SSL wiki page). But I know the patch for SNI and server_hostname
was incorporated 4 years ago in 2010 (Add a *server_hostname* argument to SSLContext.wrap_socket, changeset 65593:846c0e1342d0).
The equivalent OpenSSL call I need access to is SSL_set_tlsext_host_name
.
How do I specify the SNI hostname? (Eventually, I'll need to set it to an arbitrary name because I am testing a proxy).
回答1:
The patch you're mentioning is for Python 3.2, and you're using Python 2.7. Issue 5639 also seems to indicate there is no plan to back-port SNI support for Python 2.7.
You could wrap the socket with pyOpenSSL instead (its Connection
class has a set_tlsext_host_name since version 0.13. (I'm not sure which version comes with Debian 7.3, you might want to set up a virtualenv and upgrade to a newer version locally, if needed.)
There is an SNI example is the pyOpenSSL repository.
If you want your usage of wrap_socket
to be more compatible with the trick were you replace the value of sock
in an httplib
connection, you could have a look at how urllib3 does this with pyOpenSSL. Essentially, it creates an OpenSSL.SSL.Connection
from an existing socket, but since that connection isn't compatible with a socket, it wraps it into a class that implements the required method.
(By the way, in Python 2.7, urllib
, urllib2
and httpconnection
don't do any certificate verification at all, unless you implement it yourself by wrapping their sockets.)
EDIT:
Here is a version of your code that should work with Python 3.2. Unfortunately, the server_name
parameter isn't in the plain ssl.wrap_socket
, only in SSLContext.wrap_socket
, but you can use SSLSocket
directly.
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)
回答2:
First connect, then wrap the socket.
import socket, ssl
sock = socket.create_connection( ('localhost', 443) )
sock = ssl.wrap_socket(sock, ca_certs="./pki/signing-dss-cert.pem", cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1)
In case of python2 I also use the following hack sometimes (because the httplib.HTTPSConnection
documentation says that it doesn't perform any kind of checking on the https server cert):
import urllib, httplib, ssl
PATH = urllib.quote(u'/'.encode('utf-8'))
conn = httplib.HTTPConnection('www.google.com', 443)
conn.connect()
try:
conn.sock = ssl.wrap_socket(conn.sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=CA_BUNDLE_FILE, ssl_version=VERSION)
conn.request('GET', PATH)
resp = conn.getresponse()
print resp.status
print resp.read()
finally:
conn.close()
Note that if you want to communicate with the server then using a http client is often much easier than playing around with raw sockets.
回答3:
My env is:requests==2.7.0, python-2.7.5-34.el7.x86_64, gevent==1.0.2
Change python version to python-2.7.5-18.el7_1.1.x86_64, problem is solved.
on 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
pakages can search on google.
来源:https://stackoverflow.com/questions/22387651/wrap-socket-got-an-unexpected-keyword-argument-server-hostname