Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ALPN support #648

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/paho/mqtt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ def tls_set_context(self, context=None):
if hasattr(context, 'check_hostname'):
self._tls_insecure = not context.check_hostname

def tls_set(self, ca_certs=None, certfile=None, keyfile=None, cert_reqs=None, tls_version=None, ciphers=None, keyfile_password=None):
def tls_set(self, ca_certs=None, certfile=None, keyfile=None, cert_reqs=None, tls_version=None, ciphers=None, keyfile_password=None, alpn_protocols=None):
"""Configure network encryption and authentication options. Enables SSL/TLS support.

ca_certs : a string path to the Certificate Authority certificate files
Expand Down Expand Up @@ -808,6 +808,11 @@ def tls_set(self, ca_certs=None, certfile=None, keyfile=None, cert_reqs=None, tl
if ciphers is not None:
context.set_ciphers(ciphers)

if alpn_protocols is not None:
if not getattr(ssl, "HAS_ALPN", None):
raise ValueError("SSL library has no support for ALPN")
context.set_alpn_protocols(alpn_protocols)

self.tls_set_context(context)

if cert_reqs != ssl.CERT_NONE:
Expand Down
54 changes: 54 additions & 0 deletions test/lib/08-ssl-connect-alpn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3

# Test whether a client produces a correct connect and subsequent disconnect when using SSL.
# Client must provide a certificate.

# The client should connect to port 1888 with keepalive=60, clean session set,
# and client id 08-ssl-connect-alpn
# It should use the CA certificate ssl/all-ca.crt for verifying the server.
# The test will send a CONNACK message to the client with rc=0. Upon receiving
# the CONNACK and verifying that rc=0, the client should send a DISCONNECT
# message. If rc!=0, the client should exit with an error.
#
# Additionally, the secure socket must have been negotiated with the "paho-test-protocol"

import context
import paho_test
from paho_test import ssl

context.check_ssl()

rc = 1
keepalive = 60
connect_packet = paho_test.gen_connect("08-ssl-connect-alpn", keepalive=keepalive)
connack_packet = paho_test.gen_connack(rc=0)
disconnect_packet = paho_test.gen_disconnect()

ssock = paho_test.create_server_socket_ssl(cert_reqs=ssl.CERT_REQUIRED, alpn_protocols=["paho-test-protocol"])

client = context.start_client()

try:
(conn, address) = ssock.accept()
conn.settimeout(10)

paho_test.expect_packet(conn, "connect", connect_packet)
conn.send(connack_packet)

paho_test.expect_packet(conn, "disconnect", disconnect_packet)
rc = 0

if getattr(ssl, "HAS_ALPN"):
negotiated_protocol = conn.selected_alpn_protocol()
if negotiated_protocol != "paho-test-protocol":
raise Exception(
"Unexpected protocol '{}'".format(negotiated_protocol)
)

conn.close()
finally:
client.terminate()
client.wait()
ssock.close()

exit(rc)
1 change: 1 addition & 0 deletions test/lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ test :
$(PYTHON) ./08-ssl-bad-cacert.py python/08-ssl-bad-cacert.test
$(PYTHON) ./08-ssl-connect-cert-auth-pw.py python/08-ssl-connect-cert-auth-pw.test
$(PYTHON) ./08-ssl-connect-cert-auth.py python/08-ssl-connect-cert-auth.test
$(PYTHON) ./08-ssl-connect-alpn.py python/08-ssl-connect-alpn.test
$(PYTHON) ./08-ssl-connect-no-auth.py python/08-ssl-connect-no-auth.test
39 changes: 39 additions & 0 deletions test/lib/python/08-ssl-connect-alpn.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python3

import sys

import paho.mqtt.client as mqtt

if sys.version_info < (2, 7, 9):
print("WARNING: SSL/TLS not supported on Python 2.6")
exit(0)

import ssl

if not getattr(ssl, "HAS_ALPN"):
print("ALPN not supported in this version of Python")
exit(0)


def on_connect(mqttc, obj, flags, rc):
if rc != 0:
exit(rc)
else:
mqttc.disconnect()


def on_disconnect(mqttc, obj, rc):
obj = rc


run = -1
mqttc = mqtt.Client("08-ssl-connect-alpn", run)
mqttc.tls_set("../ssl/all-ca.crt", "../ssl/client.crt", "../ssl/client.key", alpn_protocols=["paho-test-protocol"])
mqttc.on_connect = on_connect
mqttc.on_disconnect = on_disconnect

mqttc.connect("localhost", 1888)
while run == -1:
mqttc.loop()

exit(run)
20 changes: 14 additions & 6 deletions test/paho_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def create_server_socket():
return sock


def create_server_socket_ssl(*args, **kwargs):
def create_server_socket_ssl(cert_reqs=None, alpn_protocols=None):
if ssl is None:
raise RuntimeError

Expand All @@ -44,10 +44,18 @@ def create_server_socket_ssl(*args, **kwargs):

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ssock = ssl.wrap_socket(
sock, ca_certs="../ssl/all-ca.crt",
keyfile="../ssl/server.key", certfile="../ssl/server.crt",
server_side=True, ssl_version=ssl_version, **kwargs)

context = ssl.SSLContext(ssl_version)
if cert_reqs is not None:
context.options |= cert_reqs

if alpn_protocols is not None:
context.set_alpn_protocols(alpn_protocols)

context.load_verify_locations(cafile="../ssl/all-ca.crt")
context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key")

ssock = context.wrap_socket(sock, server_side=True)
ssock.settimeout(10)
ssock.bind(('', 1888))
ssock.listen(5)
Expand All @@ -63,7 +71,7 @@ def expect_packet(sock, name, expected):
packet_recvd = b""
try:
while len(packet_recvd) < rlen:
data = sock.recv(rlen-len(packet_recvd))
data = sock.recv(rlen - len(packet_recvd))
if len(data) == 0:
break
packet_recvd += data
Expand Down