blob: 856434f67614e3bab90b26cf6dd5141d9583a367 [file] [log] [blame]
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +00001# Test the support for SSL and sockets
2
3import sys
4import unittest
5from test import test_support
6import socket
7import errno
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +00008import subprocess
9import time
10import os
11import pprint
Bill Janssen296a59d2007-09-16 22:06:00 +000012import urllib, urlparse
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000013import shutil
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000014import traceback
15
Bill Janssen296a59d2007-09-16 22:06:00 +000016from BaseHTTPServer import HTTPServer
17from SimpleHTTPServer import SimpleHTTPRequestHandler
18
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000019# Optionally test SSL support, if we have it in the tested platform
20skip_expected = False
21try:
22 import ssl
23except ImportError:
24 skip_expected = True
25
26CERTFILE = None
Bill Janssen296a59d2007-09-16 22:06:00 +000027SVN_PYTHON_ORG_ROOT_CERT = None
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000028
Bill Janssen98d19da2007-09-10 21:51:02 +000029TESTPORT = 10025
Neal Norwitz3e533c22007-08-27 01:03:18 +000030
31def handle_error(prefix):
32 exc_format = ' '.join(traceback.format_exception(*sys.exc_info()))
Bill Janssen98d19da2007-09-10 21:51:02 +000033 if test_support.verbose:
34 sys.stdout.write(prefix + exc_format)
Neal Norwitz3e533c22007-08-27 01:03:18 +000035
36
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000037class BasicTests(unittest.TestCase):
38
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000039 def testSSLconnect(self):
Christian Heimes6c29be52008-01-19 16:39:27 +000040 if not test_support.is_resource_enabled('network'):
41 return
Bill Janssen296a59d2007-09-16 22:06:00 +000042 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
43 cert_reqs=ssl.CERT_NONE)
44 s.connect(("svn.python.org", 443))
45 c = s.getpeercert()
46 if c:
47 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
48 s.close()
49
50 # this should fail because we have no verification certs
51 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
52 cert_reqs=ssl.CERT_REQUIRED)
53 try:
54 s.connect(("svn.python.org", 443))
55 except ssl.SSLError:
56 pass
57 finally:
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000058 s.close()
59
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000060
Bill Janssen98d19da2007-09-10 21:51:02 +000061 def testCrucialConstants(self):
62 ssl.PROTOCOL_SSLv2
63 ssl.PROTOCOL_SSLv23
64 ssl.PROTOCOL_SSLv3
65 ssl.PROTOCOL_TLSv1
66 ssl.CERT_NONE
67 ssl.CERT_OPTIONAL
68 ssl.CERT_REQUIRED
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000069
Bill Janssen98d19da2007-09-10 21:51:02 +000070 def testRAND(self):
71 v = ssl.RAND_status()
72 if test_support.verbose:
73 sys.stdout.write("\n RAND_status is %d (%s)\n"
74 % (v, (v and "sufficient randomness") or
75 "insufficient randomness"))
Guido van Rossume4729332007-08-26 19:35:09 +000076 try:
Bill Janssen98d19da2007-09-10 21:51:02 +000077 ssl.RAND_egd(1)
78 except TypeError:
79 pass
Guido van Rossume4729332007-08-26 19:35:09 +000080 else:
Bill Janssen98d19da2007-09-10 21:51:02 +000081 print "didn't raise TypeError"
82 ssl.RAND_add("this is a random string", 75.0)
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000083
Bill Janssen98d19da2007-09-10 21:51:02 +000084 def testParseCert(self):
85 # note that this uses an 'unofficial' function in _ssl.c,
86 # provided solely for this test, to exercise the certificate
87 # parsing code
88 p = ssl._ssl._test_decode_cert(CERTFILE, False)
89 if test_support.verbose:
90 sys.stdout.write("\n" + pprint.pformat(p) + "\n")
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +000091
Bill Janssen296a59d2007-09-16 22:06:00 +000092 def testDERtoPEM(self):
93
94 pem = open(SVN_PYTHON_ORG_ROOT_CERT, 'r').read()
95 d1 = ssl.PEM_cert_to_DER_cert(pem)
96 p2 = ssl.DER_cert_to_PEM_cert(d1)
97 d2 = ssl.PEM_cert_to_DER_cert(p2)
98 if (d1 != d2):
99 raise test_support.TestFailed("PEM-to-DER or DER-to-PEM translation failed")
100
101
102class NetworkTests(unittest.TestCase):
103
104 def testConnect(self):
Bill Janssen296a59d2007-09-16 22:06:00 +0000105 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
106 cert_reqs=ssl.CERT_NONE)
107 s.connect(("svn.python.org", 443))
108 c = s.getpeercert()
109 if c:
110 raise test_support.TestFailed("Peer cert %s shouldn't be here!")
111 s.close()
112
113 # this should fail because we have no verification certs
114 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
115 cert_reqs=ssl.CERT_REQUIRED)
116 try:
117 s.connect(("svn.python.org", 443))
118 except ssl.SSLError:
119 pass
120 finally:
121 s.close()
122
123 # this should succeed because we specify the root cert
124 s = ssl.wrap_socket(socket.socket(socket.AF_INET),
125 cert_reqs=ssl.CERT_REQUIRED,
126 ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
127 try:
128 s.connect(("svn.python.org", 443))
129 except ssl.SSLError, x:
130 raise test_support.TestFailed("Unexpected exception %s" % x)
131 finally:
132 s.close()
133
134 def testFetchServerCert(self):
135
136 pem = ssl.get_server_certificate(("svn.python.org", 443))
137 if not pem:
138 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
139
140 try:
141 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=CERTFILE)
142 except ssl.SSLError:
143 #should fail
144 pass
145 else:
146 raise test_support.TestFailed("Got server certificate %s for svn.python.org!" % pem)
147
148 pem = ssl.get_server_certificate(("svn.python.org", 443), ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
149 if not pem:
150 raise test_support.TestFailed("No server certificate on svn.python.org:443!")
151 if test_support.verbose:
152 sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem)
153
154
Bill Janssen98d19da2007-09-10 21:51:02 +0000155try:
156 import threading
157except ImportError:
158 _have_threads = False
159else:
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000160
Bill Janssen98d19da2007-09-10 21:51:02 +0000161 _have_threads = True
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000162
Bill Janssen98d19da2007-09-10 21:51:02 +0000163 class ThreadedEchoServer(threading.Thread):
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000164
Bill Janssen98d19da2007-09-10 21:51:02 +0000165 class ConnectionHandler(threading.Thread):
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000166
Bill Janssen98d19da2007-09-10 21:51:02 +0000167 """A mildly complicated class, because we want it to work both
168 with and without the SSL wrapper around the socket connection, so
169 that we can test the STARTTLS functionality."""
170
171 def __init__(self, server, connsock):
172 self.server = server
173 self.running = False
174 self.sock = connsock
175 self.sock.setblocking(1)
176 self.sslconn = None
177 threading.Thread.__init__(self)
178 self.setDaemon(True)
179
180 def wrap_conn (self):
181 try:
182 self.sslconn = ssl.wrap_socket(self.sock, server_side=True,
183 certfile=self.server.certificate,
184 ssl_version=self.server.protocol,
185 ca_certs=self.server.cacerts,
186 cert_reqs=self.server.certreqs)
187 except:
188 if self.server.chatty:
189 handle_error("\n server: bad connection attempt from " +
190 str(self.sock.getpeername()) + ":\n")
191 if not self.server.expect_bad_connects:
192 # here, we want to stop the server, because this shouldn't
193 # happen in the context of our test case
194 self.running = False
195 # normally, we'd just stop here, but for the test
196 # harness, we want to stop the server
197 self.server.stop()
198 return False
199
200 else:
201 if self.server.certreqs == ssl.CERT_REQUIRED:
202 cert = self.sslconn.getpeercert()
203 if test_support.verbose and self.server.chatty:
204 sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n")
205 cert_binary = self.sslconn.getpeercert(True)
206 if test_support.verbose and self.server.chatty:
207 sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n")
208 cipher = self.sslconn.cipher()
209 if test_support.verbose and self.server.chatty:
210 sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n")
211 return True
212
213 def read(self):
214 if self.sslconn:
215 return self.sslconn.read()
216 else:
217 return self.sock.recv(1024)
218
219 def write(self, bytes):
220 if self.sslconn:
221 return self.sslconn.write(bytes)
222 else:
223 return self.sock.send(bytes)
224
225 def close(self):
226 if self.sslconn:
227 self.sslconn.close()
228 else:
229 self.sock.close()
230
231 def run (self):
232 self.running = True
233 if not self.server.starttls_server:
234 if not self.wrap_conn():
235 return
236 while self.running:
237 try:
238 msg = self.read()
239 if not msg:
240 # eof, so quit this handler
241 self.running = False
242 self.close()
243 elif msg.strip() == 'over':
244 if test_support.verbose and self.server.connectionchatty:
245 sys.stdout.write(" server: client closed connection\n")
246 self.close()
247 return
248 elif self.server.starttls_server and msg.strip() == 'STARTTLS':
249 if test_support.verbose and self.server.connectionchatty:
250 sys.stdout.write(" server: read STARTTLS from client, sending OK...\n")
251 self.write("OK\n")
252 if not self.wrap_conn():
253 return
254 else:
255 if (test_support.verbose and
256 self.server.connectionchatty):
257 ctype = (self.sslconn and "encrypted") or "unencrypted"
258 sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n"
259 % (repr(msg), ctype, repr(msg.lower()), ctype))
260 self.write(msg.lower())
261 except ssl.SSLError:
262 if self.server.chatty:
263 handle_error("Test server failure:\n")
264 self.close()
265 self.running = False
266 # normally, we'd just stop here, but for the test
267 # harness, we want to stop the server
268 self.server.stop()
269 except:
270 handle_error('')
271
272 def __init__(self, port, certificate, ssl_version=None,
273 certreqs=None, cacerts=None, expect_bad_connects=False,
274 chatty=True, connectionchatty=False, starttls_server=False):
275 if ssl_version is None:
276 ssl_version = ssl.PROTOCOL_TLSv1
277 if certreqs is None:
278 certreqs = ssl.CERT_NONE
279 self.certificate = certificate
280 self.protocol = ssl_version
281 self.certreqs = certreqs
282 self.cacerts = cacerts
283 self.expect_bad_connects = expect_bad_connects
284 self.chatty = chatty
285 self.connectionchatty = connectionchatty
286 self.starttls_server = starttls_server
287 self.sock = socket.socket()
288 self.flag = None
289 if hasattr(socket, 'SO_REUSEADDR'):
290 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
291 if hasattr(socket, 'SO_REUSEPORT'):
292 self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
293 self.sock.bind(('127.0.0.1', port))
294 self.active = False
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000295 threading.Thread.__init__(self)
Bill Janssen98d19da2007-09-10 21:51:02 +0000296 self.setDaemon(False)
297
298 def start (self, flag=None):
299 self.flag = flag
300 threading.Thread.start(self)
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000301
302 def run (self):
Bill Janssen98d19da2007-09-10 21:51:02 +0000303 self.sock.settimeout(0.5)
304 self.sock.listen(5)
305 self.active = True
306 if self.flag:
307 # signal an event
308 self.flag.set()
309 while self.active:
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000310 try:
Bill Janssen98d19da2007-09-10 21:51:02 +0000311 newconn, connaddr = self.sock.accept()
312 if test_support.verbose and self.chatty:
313 sys.stdout.write(' server: new connection from '
314 + str(connaddr) + '\n')
315 handler = self.ConnectionHandler(self, newconn)
316 handler.start()
317 except socket.timeout:
318 pass
319 except KeyboardInterrupt:
320 self.stop()
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000321 except:
Bill Janssen98d19da2007-09-10 21:51:02 +0000322 if self.chatty:
323 handle_error("Test server failure:\n")
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000324
Bill Janssen98d19da2007-09-10 21:51:02 +0000325 def stop (self):
326 self.active = False
327 self.sock.close()
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000328
Bill Janssen296a59d2007-09-16 22:06:00 +0000329
330 class AsyncoreHTTPSServer(threading.Thread):
331
332 class HTTPSServer(HTTPServer):
333
334 def __init__(self, server_address, RequestHandlerClass, certfile):
335
336 HTTPServer.__init__(self, server_address, RequestHandlerClass)
337 # we assume the certfile contains both private key and certificate
338 self.certfile = certfile
339 self.active = False
Neal Norwitz0098c9d2008-03-09 19:03:42 +0000340 self.active_lock = threading.Lock()
Bill Janssen296a59d2007-09-16 22:06:00 +0000341 self.allow_reuse_address = True
342
343 def get_request (self):
344 # override this to wrap socket with SSL
345 sock, addr = self.socket.accept()
346 sslconn = ssl.wrap_socket(sock, server_side=True,
347 certfile=self.certfile)
348 return sslconn, addr
349
350 # The methods overridden below this are mainly so that we
351 # can run it in a thread and be able to stop it from another
352 # You probably wouldn't need them in other uses.
353
354 def server_activate(self):
355 # We want to run this in a thread for testing purposes,
356 # so we override this to set timeout, so that we get
357 # a chance to stop the server
358 self.socket.settimeout(0.5)
359 HTTPServer.server_activate(self)
360
361 def serve_forever(self):
362 # We want this to run in a thread, so we use a slightly
363 # modified version of "forever".
364 self.active = True
Neal Norwitz0098c9d2008-03-09 19:03:42 +0000365 while 1:
Bill Janssen296a59d2007-09-16 22:06:00 +0000366 try:
Neal Norwitz0098c9d2008-03-09 19:03:42 +0000367 # We need to lock while handling the request.
368 # Another thread can close the socket after self.active
369 # has been checked and before the request is handled.
370 # This causes an exception when using the closed socket.
371 with self.active_lock:
372 if not self.active:
373 break
374 self.handle_request()
Bill Janssen296a59d2007-09-16 22:06:00 +0000375 except socket.timeout:
376 pass
377 except KeyboardInterrupt:
378 self.server_close()
379 return
380 except:
Neal Norwitz0098c9d2008-03-09 19:03:42 +0000381 sys.stdout.write(''.join(traceback.format_exception(*sys.exc_info())))
382 break
Bill Janssen296a59d2007-09-16 22:06:00 +0000383
384 def server_close(self):
385 # Again, we want this to run in a thread, so we need to override
386 # close to clear the "active" flag, so that serve_forever() will
387 # terminate.
Neal Norwitz0098c9d2008-03-09 19:03:42 +0000388 with self.active_lock:
389 HTTPServer.server_close(self)
390 self.active = False
Bill Janssen296a59d2007-09-16 22:06:00 +0000391
392 class RootedHTTPRequestHandler(SimpleHTTPRequestHandler):
393
394 # need to override translate_path to get a known root,
395 # instead of using os.curdir, since the test could be
396 # run from anywhere
397
398 server_version = "TestHTTPS/1.0"
399
400 root = None
401
402 def translate_path(self, path):
403 """Translate a /-separated PATH to the local filename syntax.
404
405 Components that mean special things to the local file system
406 (e.g. drive or directory names) are ignored. (XXX They should
407 probably be diagnosed.)
408
409 """
410 # abandon query parameters
411 path = urlparse.urlparse(path)[2]
412 path = os.path.normpath(urllib.unquote(path))
413 words = path.split('/')
414 words = filter(None, words)
415 path = self.root
416 for word in words:
417 drive, word = os.path.splitdrive(word)
418 head, word = os.path.split(word)
419 if word in self.root: continue
420 path = os.path.join(path, word)
421 return path
422
423 def log_message(self, format, *args):
424
425 # we override this to suppress logging unless "verbose"
426
427 if test_support.verbose:
428 sys.stdout.write(" server (%s, %d, %s):\n [%s] %s\n" %
429 (self.server.server_name,
430 self.server.server_port,
431 self.request.cipher(),
432 self.log_date_time_string(),
433 format%args))
434
435
436 def __init__(self, port, certfile):
437 self.flag = None
438 self.active = False
439 self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0]
440 self.server = self.HTTPSServer(
441 ('', port), self.RootedHTTPRequestHandler, certfile)
442 threading.Thread.__init__(self)
443 self.setDaemon(True)
444
445 def __str__(self):
446 return '<%s %s:%d>' % (self.__class__.__name__,
447 self.server.server_name,
448 self.server.server_port)
449
450 def start (self, flag=None):
451 self.flag = flag
452 threading.Thread.start(self)
453
454 def run (self):
455 self.active = True
456 if self.flag:
457 self.flag.set()
458 self.server.serve_forever()
459 self.active = False
460
461 def stop (self):
462 self.active = False
463 self.server.server_close()
464
465
Bill Janssen98d19da2007-09-10 21:51:02 +0000466 def badCertTest (certfile):
467 server = ThreadedEchoServer(TESTPORT, CERTFILE,
468 certreqs=ssl.CERT_REQUIRED,
469 cacerts=CERTFILE, chatty=False)
470 flag = threading.Event()
471 server.start(flag)
472 # wait for it to start
473 flag.wait()
474 # try to connect
475 try:
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000476 try:
Bill Janssen98d19da2007-09-10 21:51:02 +0000477 s = ssl.wrap_socket(socket.socket(),
478 certfile=certfile,
479 ssl_version=ssl.PROTOCOL_TLSv1)
480 s.connect(('127.0.0.1', TESTPORT))
481 except ssl.SSLError, x:
Neal Norwitz9eb9b102007-08-27 01:15:33 +0000482 if test_support.verbose:
Bill Janssen98d19da2007-09-10 21:51:02 +0000483 sys.stdout.write("\nSSLError is %s\n" % x[1])
484 else:
485 raise test_support.TestFailed(
486 "Use of invalid cert should have failed!")
487 finally:
488 server.stop()
489 server.join()
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000490
Bill Janssen98d19da2007-09-10 21:51:02 +0000491 def serverParamsTest (certfile, protocol, certreqs, cacertsfile,
492 client_certfile, client_protocol=None, indata="FOO\n",
493 chatty=True, connectionchatty=False):
494
495 server = ThreadedEchoServer(TESTPORT, certfile,
496 certreqs=certreqs,
497 ssl_version=protocol,
498 cacerts=cacertsfile,
499 chatty=chatty,
500 connectionchatty=connectionchatty)
501 flag = threading.Event()
502 server.start(flag)
503 # wait for it to start
504 flag.wait()
505 # try to connect
506 if client_protocol is None:
507 client_protocol = protocol
508 try:
509 try:
510 s = ssl.wrap_socket(socket.socket(),
511 certfile=client_certfile,
512 ca_certs=cacertsfile,
513 cert_reqs=certreqs,
514 ssl_version=client_protocol)
515 s.connect(('127.0.0.1', TESTPORT))
516 except ssl.SSLError, x:
517 raise test_support.TestFailed("Unexpected SSL error: " + str(x))
518 except Exception, x:
519 raise test_support.TestFailed("Unexpected exception: " + str(x))
520 else:
521 if connectionchatty:
522 if test_support.verbose:
523 sys.stdout.write(
524 " client: sending %s...\n" % (repr(indata)))
525 s.write(indata)
526 outdata = s.read()
527 if connectionchatty:
528 if test_support.verbose:
529 sys.stdout.write(" client: read %s\n" % repr(outdata))
530 if outdata != indata.lower():
531 raise test_support.TestFailed(
532 "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n"
533 % (outdata[:min(len(outdata),20)], len(outdata),
534 indata[:min(len(indata),20)].lower(), len(indata)))
535 s.write("over\n")
536 if connectionchatty:
537 if test_support.verbose:
538 sys.stdout.write(" client: closing connection.\n")
Bill Janssen98d19da2007-09-10 21:51:02 +0000539 s.close()
540 finally:
541 server.stop()
542 server.join()
543
544 def tryProtocolCombo (server_protocol,
545 client_protocol,
546 expectedToWork,
Bill Janssene3f1d7d2007-09-11 01:09:19 +0000547 certsreqs=None):
548
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000549 if certsreqs is None:
Bill Janssene3f1d7d2007-09-11 01:09:19 +0000550 certsreqs = ssl.CERT_NONE
Bill Janssen98d19da2007-09-10 21:51:02 +0000551
552 if certsreqs == ssl.CERT_NONE:
553 certtype = "CERT_NONE"
554 elif certsreqs == ssl.CERT_OPTIONAL:
555 certtype = "CERT_OPTIONAL"
556 elif certsreqs == ssl.CERT_REQUIRED:
557 certtype = "CERT_REQUIRED"
558 if test_support.verbose:
559 formatstr = (expectedToWork and " %s->%s %s\n") or " {%s->%s} %s\n"
560 sys.stdout.write(formatstr %
561 (ssl.get_protocol_name(client_protocol),
562 ssl.get_protocol_name(server_protocol),
563 certtype))
564 try:
565 serverParamsTest(CERTFILE, server_protocol, certsreqs,
566 CERTFILE, CERTFILE, client_protocol, chatty=False)
567 except test_support.TestFailed:
568 if expectedToWork:
569 raise
570 else:
571 if not expectedToWork:
572 raise test_support.TestFailed(
573 "Client protocol %s succeeded with server protocol %s!"
574 % (ssl.get_protocol_name(client_protocol),
575 ssl.get_protocol_name(server_protocol)))
576
577
578 class ConnectedTests(unittest.TestCase):
579
580 def testRudeShutdown(self):
581
582 listener_ready = threading.Event()
583 listener_gone = threading.Event()
584
585 # `listener` runs in a thread. It opens a socket listening on
586 # PORT, and sits in an accept() until the main thread connects.
587 # Then it rudely closes the socket, and sets Event `listener_gone`
588 # to let the main thread know the socket is gone.
589 def listener():
590 s = socket.socket()
Bill Janssen119c7a62007-09-10 23:41:24 +0000591 if hasattr(socket, 'SO_REUSEADDR'):
592 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Bill Janssen98d19da2007-09-10 21:51:02 +0000593 if hasattr(socket, 'SO_REUSEPORT'):
594 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
Bill Janssen296a59d2007-09-16 22:06:00 +0000595 s.bind(('127.0.0.1', TESTPORT))
Bill Janssen98d19da2007-09-10 21:51:02 +0000596 s.listen(5)
597 listener_ready.set()
598 s.accept()
599 s = None # reclaim the socket object, which also closes it
600 listener_gone.set()
601
602 def connector():
603 listener_ready.wait()
604 s = socket.socket()
Bill Janssen119c7a62007-09-10 23:41:24 +0000605 s.connect(('127.0.0.1', TESTPORT))
Bill Janssen98d19da2007-09-10 21:51:02 +0000606 listener_gone.wait()
607 try:
608 ssl_sock = ssl.wrap_socket(s)
609 except socket.sslerror:
610 pass
611 else:
612 raise test_support.TestFailed(
613 'connecting to closed SSL socket should have failed')
614
615 t = threading.Thread(target=listener)
616 t.start()
617 connector()
618 t.join()
619
620 def testEcho (self):
621
622 if test_support.verbose:
623 sys.stdout.write("\n")
624 serverParamsTest(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE,
625 CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1,
626 chatty=True, connectionchatty=True)
627
628 def testReadCert(self):
629
630 if test_support.verbose:
631 sys.stdout.write("\n")
632 s2 = socket.socket()
633 server = ThreadedEchoServer(TESTPORT, CERTFILE,
634 certreqs=ssl.CERT_NONE,
635 ssl_version=ssl.PROTOCOL_SSLv23,
636 cacerts=CERTFILE,
637 chatty=False)
638 flag = threading.Event()
639 server.start(flag)
640 # wait for it to start
641 flag.wait()
642 # try to connect
643 try:
644 try:
645 s = ssl.wrap_socket(socket.socket(),
646 certfile=CERTFILE,
647 ca_certs=CERTFILE,
648 cert_reqs=ssl.CERT_REQUIRED,
649 ssl_version=ssl.PROTOCOL_SSLv23)
650 s.connect(('127.0.0.1', TESTPORT))
651 except ssl.SSLError, x:
652 raise test_support.TestFailed(
653 "Unexpected SSL error: " + str(x))
654 except Exception, x:
655 raise test_support.TestFailed(
656 "Unexpected exception: " + str(x))
657 else:
658 if not s:
659 raise test_support.TestFailed(
660 "Can't SSL-handshake with test server")
661 cert = s.getpeercert()
662 if not cert:
663 raise test_support.TestFailed(
664 "Can't get peer certificate.")
665 cipher = s.cipher()
666 if test_support.verbose:
667 sys.stdout.write(pprint.pformat(cert) + '\n')
668 sys.stdout.write("Connection cipher is " + str(cipher) + '.\n')
669 if not cert.has_key('subject'):
670 raise test_support.TestFailed(
671 "No subject field in certificate: %s." %
672 pprint.pformat(cert))
673 if ((('organizationName', 'Python Software Foundation'),)
674 not in cert['subject']):
675 raise test_support.TestFailed(
676 "Missing or invalid 'organizationName' field in certificate subject; "
Neal Norwitz0098c9d2008-03-09 19:03:42 +0000677 "should be 'Python Software Foundation'.")
Bill Janssen98d19da2007-09-10 21:51:02 +0000678 s.close()
679 finally:
680 server.stop()
681 server.join()
682
683 def testNULLcert(self):
684 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
685 "nullcert.pem"))
686 def testMalformedCert(self):
687 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
688 "badcert.pem"))
689 def testMalformedKey(self):
690 badCertTest(os.path.join(os.path.dirname(__file__) or os.curdir,
691 "badkey.pem"))
692
693 def testProtocolSSL2(self):
694 if test_support.verbose:
695 sys.stdout.write("\n")
696 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True)
697 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL)
698 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED)
699 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True)
700 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False)
701 tryProtocolCombo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False)
702
703 def testProtocolSSL23(self):
704 if test_support.verbose:
705 sys.stdout.write("\n")
706 try:
707 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True)
708 except test_support.TestFailed, x:
709 # this fails on some older versions of OpenSSL (0.9.7l, for instance)
710 if test_support.verbose:
711 sys.stdout.write(
712 " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n"
713 % str(x))
714 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True)
715 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True)
716 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True)
717
718 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
719 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL)
720 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
721
722 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
723 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED)
724 tryProtocolCombo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
725
726 def testProtocolSSL3(self):
727 if test_support.verbose:
728 sys.stdout.write("\n")
729 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True)
730 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL)
731 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED)
732 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False)
733 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False)
734 tryProtocolCombo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False)
735
736 def testProtocolTLS1(self):
737 if test_support.verbose:
738 sys.stdout.write("\n")
739 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True)
740 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL)
741 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED)
742 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False)
743 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False)
744 tryProtocolCombo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False)
745
746 def testSTARTTLS (self):
747
748 msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4")
749
750 server = ThreadedEchoServer(TESTPORT, CERTFILE,
751 ssl_version=ssl.PROTOCOL_TLSv1,
752 starttls_server=True,
753 chatty=True,
754 connectionchatty=True)
755 flag = threading.Event()
756 server.start(flag)
757 # wait for it to start
758 flag.wait()
759 # try to connect
760 wrapped = False
761 try:
762 try:
763 s = socket.socket()
764 s.setblocking(1)
765 s.connect(('127.0.0.1', TESTPORT))
766 except Exception, x:
767 raise test_support.TestFailed("Unexpected exception: " + str(x))
768 else:
769 if test_support.verbose:
770 sys.stdout.write("\n")
771 for indata in msgs:
772 if test_support.verbose:
Bill Janssen296a59d2007-09-16 22:06:00 +0000773 sys.stdout.write(
774 " client: sending %s...\n" % repr(indata))
Bill Janssen98d19da2007-09-10 21:51:02 +0000775 if wrapped:
776 conn.write(indata)
777 outdata = conn.read()
778 else:
779 s.send(indata)
780 outdata = s.recv(1024)
Bill Janssen296a59d2007-09-16 22:06:00 +0000781 if (indata == "STARTTLS" and
782 outdata.strip().lower().startswith("ok")):
Bill Janssen98d19da2007-09-10 21:51:02 +0000783 if test_support.verbose:
Bill Janssen296a59d2007-09-16 22:06:00 +0000784 sys.stdout.write(
785 " client: read %s from server, starting TLS...\n"
786 % repr(outdata))
Bill Janssen98d19da2007-09-10 21:51:02 +0000787 conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
788
789 wrapped = True
790 else:
791 if test_support.verbose:
Bill Janssen296a59d2007-09-16 22:06:00 +0000792 sys.stdout.write(
793 " client: read %s from server\n" % repr(outdata))
Bill Janssen98d19da2007-09-10 21:51:02 +0000794 if test_support.verbose:
795 sys.stdout.write(" client: closing connection.\n")
796 if wrapped:
797 conn.write("over\n")
Bill Janssen98d19da2007-09-10 21:51:02 +0000798 else:
799 s.send("over\n")
800 s.close()
801 finally:
802 server.stop()
803 server.join()
804
Bill Janssen296a59d2007-09-16 22:06:00 +0000805 def testAsyncore(self):
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000806
Bill Janssen296a59d2007-09-16 22:06:00 +0000807 server = AsyncoreHTTPSServer(TESTPORT, CERTFILE)
808 flag = threading.Event()
809 server.start(flag)
810 # wait for it to start
811 flag.wait()
812 # try to connect
813 try:
814 if test_support.verbose:
815 sys.stdout.write('\n')
Bill Janssenbf10c472007-09-16 23:16:46 +0000816 d1 = open(CERTFILE, 'rb').read()
Bill Janssen296a59d2007-09-16 22:06:00 +0000817 d2 = ''
818 # now fetch the same data from the HTTPS server
819 url = 'https://127.0.0.1:%d/%s' % (
820 TESTPORT, os.path.split(CERTFILE)[1])
821 f = urllib.urlopen(url)
822 dlen = f.info().getheader("content-length")
823 if dlen and (int(dlen) > 0):
824 d2 = f.read(int(dlen))
825 if test_support.verbose:
826 sys.stdout.write(
827 " client: read %d bytes from remote server '%s'\n"
828 % (len(d2), server))
829 f.close()
830 except:
831 msg = ''.join(traceback.format_exception(*sys.exc_info()))
832 if test_support.verbose:
833 sys.stdout.write('\n' + msg)
834 raise test_support.TestFailed(msg)
835 else:
836 if not (d1 == d2):
837 raise test_support.TestFailed(
838 "Couldn't fetch data from HTTPS server")
839 finally:
840 server.stop()
841 server.join()
Neal Norwitz7fc8e292007-08-26 18:50:39 +0000842
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000843
Bill Janssen119c7a62007-09-10 23:41:24 +0000844def findtestsocket(start, end):
845 def testbind(i):
846 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
847 try:
848 s.bind(("127.0.0.1", i))
849 except:
850 return 0
851 else:
852 return 1
853 finally:
854 s.close()
855
856 for i in range(start, end):
857 if testbind(i) and testbind(i+1):
858 return i
859 return 0
860
861
Neal Norwitz9eb9b102007-08-27 01:15:33 +0000862def test_main(verbose=False):
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000863 if skip_expected:
Bill Janssenffe576d2007-09-05 00:46:27 +0000864 raise test_support.TestSkipped("No SSL support")
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000865
Bill Janssen296a59d2007-09-16 22:06:00 +0000866 global CERTFILE, TESTPORT, SVN_PYTHON_ORG_ROOT_CERT
Guido van Rossumba8c5652007-08-27 17:19:42 +0000867 CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir,
Bill Janssen296a59d2007-09-16 22:06:00 +0000868 "keycert.pem")
869 SVN_PYTHON_ORG_ROOT_CERT = os.path.join(
870 os.path.dirname(__file__) or os.curdir,
871 "https_svn_python_org_root.pem")
872
873 if (not os.path.exists(CERTFILE) or
874 not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT)):
Bill Janssen98d19da2007-09-10 21:51:02 +0000875 raise test_support.TestFailed("Can't read certificate files!")
Bill Janssen119c7a62007-09-10 23:41:24 +0000876 TESTPORT = findtestsocket(10025, 12000)
877 if not TESTPORT:
878 raise test_support.TestFailed("Can't find open port to test servers on!")
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000879
880 tests = [BasicTests]
881
Bill Janssen296a59d2007-09-16 22:06:00 +0000882 if test_support.is_resource_enabled('network'):
883 tests.append(NetworkTests)
884
Bill Janssen98d19da2007-09-10 21:51:02 +0000885 if _have_threads:
886 thread_info = test_support.threading_setup()
Bill Janssen296a59d2007-09-16 22:06:00 +0000887 if thread_info and test_support.is_resource_enabled('network'):
Bill Janssen98d19da2007-09-10 21:51:02 +0000888 tests.append(ConnectedTests)
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000889
Bill Janssen98d19da2007-09-10 21:51:02 +0000890 test_support.run_unittest(*tests)
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000891
Bill Janssen98d19da2007-09-10 21:51:02 +0000892 if _have_threads:
893 test_support.threading_cleanup(*thread_info)
Guido van Rossum4f2c3dd2007-08-25 15:08:43 +0000894
895if __name__ == "__main__":
896 test_main()