Add stub for later SNI support

This commit is contained in:
Michael Lazar 2020-05-19 00:25:22 -04:00
parent b7c3e43ac4
commit 56aea1b683
2 changed files with 35 additions and 6 deletions

View File

@ -94,7 +94,14 @@ class GeminiCertificateOptions(CertificateOptions):
https://github.com/twisted/twisted/blob/trunk/src/twisted/internet/_sslverify.py https://github.com/twisted/twisted/blob/trunk/src/twisted/internet/_sslverify.py
""" """
def verify_callback(self, conn, cert, errno, depth, preverify_ok): def verify_callback(
self,
conn: OpenSSL.SSL.Connection,
cert: OpenSSL.crypto.X509,
errno: int,
depth: int,
preverify_ok: int,
) -> bool:
""" """
Callback used by OpenSSL for client certificate verification. Callback used by OpenSSL for client certificate verification.
@ -106,7 +113,9 @@ class GeminiCertificateOptions(CertificateOptions):
conn.verified = preverify_ok conn.verified = preverify_ok
return True return True
def proto_select_callback(self, conn, protocols): def proto_select_callback(
self, conn: OpenSSL.SSL.Connection, protocols: typing.List[bytes]
) -> bytes:
""" """
Callback used by OpenSSL for ALPN support. Callback used by OpenSSL for ALPN support.
@ -118,6 +127,16 @@ class GeminiCertificateOptions(CertificateOptions):
else: else:
return b"" return b""
def sni_callback(self, conn: OpenSSL.SSL.Connection) -> None:
"""
Callback used by OpenSSL for SNI support.
We can inspect the servername requested by the client using
conn.get_servername(), and attach an appropriate context using
conn.set_context(new_context).
"""
pass
def __init__( def __init__(
self, self,
certfile: str, certfile: str,
@ -177,4 +196,6 @@ class GeminiCertificateOptions(CertificateOptions):
ctx.set_alpn_select_callback(self.proto_select_callback) ctx.set_alpn_select_callback(self.proto_select_callback)
ctx.set_alpn_protos(self._acceptableProtocols) ctx.set_alpn_protos(self._acceptableProtocols)
ctx.set_tlsext_servername_callback(self.sni_callback)
return ctx return ctx

View File

@ -15,7 +15,7 @@ context.check_hostname = False
context.verify_mode = ssl.CERT_NONE context.verify_mode = ssl.CERT_NONE
def fetch(url: str, host: str = None, port: str = None): def fetch(url, host=None, port=None, use_sni=False):
parsed_url = urllib.parse.urlparse(url) parsed_url = urllib.parse.urlparse(url)
if not parsed_url.scheme: if not parsed_url.scheme:
parsed_url = urllib.parse.urlparse(f"gemini://{url}") parsed_url = urllib.parse.urlparse(f"gemini://{url}")
@ -23,8 +23,10 @@ def fetch(url: str, host: str = None, port: str = None):
host = host or parsed_url.hostname host = host or parsed_url.hostname
port = port or parsed_url.port or 1965 port = port or parsed_url.port or 1965
server_hostname = host if use_sni else None
with socket.create_connection((host, port)) as sock: with socket.create_connection((host, port)) as sock:
with context.wrap_socket(sock) as ssock: with context.wrap_socket(sock, server_hostname=server_hostname) as ssock:
ssock.sendall((url + "\r\n").encode()) ssock.sendall((url + "\r\n").encode())
fp = ssock.makefile("rb", buffering=0) fp = ssock.makefile("rb", buffering=0)
data = fp.read(1024) data = fp.read(1024)
@ -44,12 +46,18 @@ def run_client():
) )
parser.add_argument("--certfile", help="Optional client certificate") parser.add_argument("--certfile", help="Optional client certificate")
parser.add_argument("--keyfile", help="Optional client key") parser.add_argument("--keyfile", help="Optional client key")
args = parser.parse_args() parser.add_argument("--alpn-protocol", help="Indicate the protocol using ALPN")
parser.add_argument(
"--use-sni", action="store_true", help="Specify the server hostname via SNI"
)
args = parser.parse_args()
if args.certfile: if args.certfile:
context.load_cert_chain(args.certfile, args.keyfile) context.load_cert_chain(args.certfile, args.keyfile)
if args.alpn_protocol:
context.set_alpn_protocols([args.alpn_protocol])
fetch(args.url, args.host, args.port) fetch(args.url, args.host, args.port, args.use_sni)
if __name__ == "__main__": if __name__ == "__main__":