Changeset 11153 in ntrip for trunk/BNC/tests
- Timestamp:
- Oct 31, 2026, 11:50:15 PM (6 days ago)
- Location:
- trunk/BNC/tests
- Files:
-
- 1 added
- 2 edited
-
README.md (modified) (2 diffs)
-
tools/cred_check.py (added)
-
tools/fakecaster.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/BNC/tests/README.md
r11152 r11153 32 32 | `tools/ssl_check.py` | TLS checks of the Ntrip 2s client and upload (not part of `run_tests.py`) | 33 33 | `tools/net_check.py` | UDP/RTP sender filter and upload status checks on loopback (not part of `run_tests.py`) | 34 | `tools/cred_check.py` | credentials files (`~/.netrc`, BNC format) for download and upload (not part of `run_tests.py`) | 34 35 | `work/` | scratch output, not versioned | 35 36 … … 179 180 as broken. Takes about 2 min. 180 181 182 ## Credentials check 183 184 ``` 185 tests/tools/cred_check.py [--bnc PATH] 186 ``` 187 188 BNC against `tools/fakecaster.py --auth user:password` (other credentials 189 rejected), each scenario with its own `HOME`: password in the configuration 190 file (hint logged), `~/.netrc` with two users for the host, `$NETRC` with 191 `machine host:port` preferred, BNC format file (`credentialsFile`) with exact 192 port preferred over `*` and a quoted password, URL without user (no 193 credentials sent), user without entry, `~/.netrc` readable for others and the 194 netrc `default` entry (not used), Ntrip 1 download, Ntrip 2 upload and 195 Ntrip 1 upload without user. Takes about 2 min. 196 181 197 ## Adding a replay case 182 198 -
trunk/BNC/tests/tools/fakecaster.py
r11130 r11153 16 16 a GET request (BNC as Ntrip client) is answered with the RTCM bytes of the 17 17 given raw file. With --reply an upload is answered with this text instead 18 of the 200 status (e.g. a rejection), and the connection is closed. Failed 18 of the 200 status (e.g. a rejection), and the connection is closed. With 19 --auth user:password (repeatable, ':password' for an Ntrip 1 upload) only 20 these credentials are accepted, others get 401 / 'ERROR - Bad Password'. 21 The user received ("-" without user, "none" without credentials) and failed 19 22 TLS handshakes are reported on stdout. 20 23 """ 21 24 22 25 import argparse 26 import base64 23 27 import os 24 28 import socket … … 70 74 71 75 72 def serve(conn, store, timeout, tls=None, served=None, reply=None): 76 def credentials(header): 77 """(user, password) of a request header, None without credentials.""" 78 lines = header.split(b"\r\n") 79 if lines[0].startswith(b"SOURCE"): 80 parts = lines[0].split(b" ") 81 return "", parts[1].decode("latin-1") if len(parts) > 1 else "" 82 for line in lines[1:]: 83 key, _, value = line.partition(b":") 84 value = value.strip() 85 if key.strip().lower() == b"authorization" and value.lower().startswith(b"basic "): 86 user, _, password = base64.b64decode(value[6:]).decode("latin-1").partition(":") 87 return user, password 88 return None 89 90 91 def serve(conn, store, timeout, tls=None, served=None, reply=None, auth=None): 73 92 conn.settimeout(timeout) 74 93 buf = b"" … … 88 107 header, _, pending = buf.partition(b"\r\n\r\n") 89 108 chunked = b"chunked" in header.lower() 109 cred = credentials(header) 110 ok = auth is None or cred in auth 111 print("auth %s %s" % ("none" if cred is None else (cred[0] or "-"), 112 "accepted" if ok else "rejected"), flush=True) 113 if not ok: 114 conn.sendall(b"ERROR - Bad Password\r\n" if header.startswith(b"SOURCE") 115 else b"HTTP/1.1 401 Unauthorized\r\n\r\n") 116 store.rejected += 1 117 return 90 118 if header.startswith(b"GET") and served is not None: 91 conn.sendall(b"HTTP/1.1 200 OK\r\nNtrip-Version: Ntrip/2.0\r\n" 92 b"Content-Type: gnss/data\r\nConnection: close\r\n\r\n" + served) 119 if b"ntrip-version" in header.lower(): 120 conn.sendall(b"HTTP/1.1 200 OK\r\nNtrip-Version: Ntrip/2.0\r\n" 121 b"Content-Type: gnss/data\r\nConnection: close\r\n\r\n" + served) 122 else: # Ntrip 1 123 conn.sendall(b"ICY 200 OK\r\n\r\n" + served) 93 124 store.served += len(served) 94 125 return … … 134 165 ap.add_argument("--tls-client-ca", help="require a client certificate of this CA") 135 166 ap.add_argument("--serve", help="raw file whose RTCM bytes are sent on GET") 167 ap.add_argument("--auth", action="append", 168 help="accepted user:password (repeatable), others rejected") 136 169 ap.add_argument("--reply", help="answer to uploads instead of 200 ('\\r\\n' escapes allowed)") 137 170 args = ap.parse_args() … … 147 180 if args.reply is not None: 148 181 reply = args.reply.encode("latin-1").decode("unicode_escape").encode("latin-1") 182 auth = None 183 if args.auth: 184 auth = [tuple(a.split(":", 1)) for a in args.auth] 149 185 served = None 150 186 if args.serve: … … 166 202 except socket.timeout: 167 203 break 168 t = threading.Thread(target=serve, args=(conn, store, args.timeout, tls, served, reply), 204 t = threading.Thread(target=serve, args=(conn, store, args.timeout, tls, served, reply, auth), 169 205 daemon=True) 170 206 t.start() … … 177 213 if served is not None: 178 214 print("served %d bytes" % store.served, flush=True) 179 if reply is not None: 180 print("%d upload(s) answered with --reply" % store.rejected, flush=True)215 if reply is not None or auth is not None: 216 print("%d connection(s) rejected" % store.rejected, flush=True) 181 217 return 0 182 218
Note:
See TracChangeset
for help on using the changeset viewer.
