diff --git a/.vscode/settings.json b/.vscode/settings.json index 1a81e9a..821bf2a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,5 @@ "editor.formatOnSave": true, "modulename": "${workspaceFolderBasename}", "distname": "${workspaceFolderBasename}", - "moduleversion": "1.0.20" + "moduleversion": "1.0.21" } \ No newline at end of file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 083d079..7eada13 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,11 @@ # pygnssutils Release Notes +### RELEASE CANDIDATE 1.0.21 + +ENHANCEMENTS: + +1. Add SSL support to gnssntripclient. + ### RELEASE 1.0.20 FIXES: diff --git a/pyproject.toml b/pyproject.toml index 35b9fc1..587e98e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "pygnssutils" authors = [{ name = "semuadmin", email = "semuadmin@semuconsulting.com" }] maintainers = [{ name = "semuadmin", email = "semuadmin@semuconsulting.com" }] description = "GNSS Command Line Utilities" -version = "1.0.20" +version = "1.0.21" license = { file = "LICENSE" } readme = "README.md" requires-python = ">=3.8" @@ -34,6 +34,7 @@ classifiers = [ ] dependencies = [ + "certifi>=2024.0.0", "paho-mqtt>=1.6.0", "pyserial>=3.5", "pyspartn>=0.2.0", diff --git a/src/pygnssutils/_version.py b/src/pygnssutils/_version.py index 775f255..dd19086 100644 --- a/src/pygnssutils/_version.py +++ b/src/pygnssutils/_version.py @@ -8,4 +8,4 @@ :license: BSD 3-Clause """ -__version__ = "1.0.20" +__version__ = "1.0.21" diff --git a/src/pygnssutils/gnssntripclient.py b/src/pygnssutils/gnssntripclient.py index 1fbc38d..509957b 100644 --- a/src/pygnssutils/gnssntripclient.py +++ b/src/pygnssutils/gnssntripclient.py @@ -26,6 +26,7 @@ import os import socket +import ssl from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser from base64 import b64encode from datetime import datetime, timedelta @@ -34,6 +35,7 @@ from threading import Event, Thread from time import sleep +from certifi import where as findcacerts from pynmeagps import GET, NMEAMessage from pyrtcm import RTCMMessageError, RTCMParseError, RTCMTypeError from pyubx2 import ERR_IGNORE, RTCM3_PROTOCOL, UBXReader @@ -453,8 +455,16 @@ def _read_thread( scopeid = int(settings["scopeid"]) mountpoint = settings["mountpoint"] ggainterval = int(settings["ggainterval"]) + conn = format_conn(settings["ipprot"], server, port, flowinfo, scopeid) with socket.socket(settings["ipprot"], socket.SOCK_STREAM) as self._socket: + if port == 443: + # context = ssl.create_default_context() + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.load_verify_locations(findcacerts()) + self._socket = context.wrap_socket( + self._socket, server_hostname=server + ) self._socket.settimeout(TIMEOUT) self._socket.connect(conn) self._socket.sendall(self._formatGET(settings)) @@ -485,6 +495,20 @@ def _read_thread( ): stopevent.set() self._connected = False + except ssl.SSLCertVerificationError as err: + tip = ( + f" - try using '{server[4:]}' rather than '{server}' for the NTRIP caster URL" + if "certificate is not valid for 'www." in err.strerror + else ( + f" - try adding the NTRIP caster URL SSL certificate to {findcacerts()}" + if "unable to get local issuer certificate" in err.strerror + else "" + ) + ) + print(f"SSL Certificate Verification Error{tip}\n{err}") + stopevent.set() + self._connected = False + self._app_update_status(False, (f"Error!: {err.strerror[0:32]}", "red")) def _do_header(self, sock: socket, stopevent: Event, output: object) -> str: """