vssh 3.3.0__tar.gz → 3.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vssh
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
5
- Author-email: MeshPOP <hello@meshpop.dev>
5
+ Author-email: MeshPOP <mpop@mpop.dev>
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/meshpop/vssh
8
8
  Project-URL: Repository, https://github.com/meshpop/vssh
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vssh"
7
- version = "3.3.0"
7
+ version = "3.3.1"
8
8
  description = "Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
11
11
  requires-python = ">=3.8"
12
12
  authors = [
13
- {name = "MeshPOP", email = "hello@meshpop.dev"}
13
+ {name = "MeshPOP", email = "mpop@mpop.dev"}
14
14
  ]
15
15
  keywords = ["ssh", "scp", "tailscale", "p2p", "vpn", "mcp", "remote"]
16
16
  classifiers = [
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vssh
3
- Version: 3.3.0
3
+ Version: 3.3.1
4
4
  Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
5
- Author-email: MeshPOP <hello@meshpop.dev>
5
+ Author-email: MeshPOP <mpop@mpop.dev>
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/meshpop/vssh
8
8
  Project-URL: Repository, https://github.com/meshpop/vssh
@@ -50,7 +50,7 @@ def _load_tailscale_map() -> dict:
50
50
  """Load Wire VPN IP → Tailscale IP mapping"""
51
51
  ts_map = {}
52
52
 
53
- # 1. From config: TAILSCALE.10.99.x.x = 100.x.x.x
53
+ # 1. From config: TAILSCALE.10.99.85.143 = 100.80.191.6
54
54
  for k, v in VSSH_CONFIG.items():
55
55
  if k.startswith('TAILSCALE.'):
56
56
  wire_ip = k[len('TAILSCALE.'):]
@@ -67,8 +67,8 @@ def _load_tailscale_map() -> dict:
67
67
  parts = line.split()
68
68
  if len(parts) >= 2:
69
69
  ts_map[parts[0]] = parts[1] # wire_ip tailscale_ip
70
- except:
71
- pass
70
+ except (OSError, ValueError) as e:
71
+ pass # TODO: log error
72
72
 
73
73
  return ts_map
74
74
 
@@ -92,7 +92,7 @@ def vssh_connect(host: str, timeout: float = 5.0) -> socket.socket:
92
92
  sock.settimeout(timeout)
93
93
  sock.connect((ts_ip, PORT))
94
94
  return sock
95
- except:
95
+ except (OSError, socket.error):
96
96
  # Tailscale also failed — clear cache, try Wire again
97
97
  del _FAILOVER_ACTIVE[host]
98
98
 
@@ -103,7 +103,7 @@ def vssh_connect(host: str, timeout: float = 5.0) -> socket.socket:
103
103
  sock.connect((host, PORT))
104
104
  return sock
105
105
  except (ConnectionRefusedError, ConnectionError, OSError, socket.timeout) as wire_err:
106
- pass
106
+ pass # TODO: log error
107
107
 
108
108
  # Fallback: try Tailscale IP
109
109
  ts_ip = TAILSCALE_MAP.get(host)
@@ -148,7 +148,7 @@ def log_transfer(op: str, host: str, path: str, size: int, duration: float, stat
148
148
  _rotate_log(LOG_FILE)
149
149
  with open(LOG_FILE, 'a') as f:
150
150
  f.write(line)
151
- except:
151
+ except OSError:
152
152
  pass # Don't fail on logging errors
153
153
 
154
154
  def log_event(op: str, host: str, detail: str, status: str = 'OK'):
@@ -167,8 +167,8 @@ def log_event(op: str, host: str, detail: str, status: str = 'OK'):
167
167
  _rotate_log(HISTORY_FILE)
168
168
  with open(HISTORY_FILE, 'a') as f:
169
169
  f.write(line)
170
- except:
171
- pass
170
+ except OSError:
171
+ pass # safe to ignore
172
172
 
173
173
  def get_history(count: int = 20, op_filter: str = None, host_filter: str = None) -> list:
174
174
  """Get recent history entries, optionally filtered by op or host"""
@@ -226,7 +226,7 @@ def get_transfer_stats(days: int = 7) -> dict:
226
226
  stats['success'] += 1
227
227
  else:
228
228
  stats['fail'] += 1
229
- except:
229
+ except Exception:
230
230
  continue
231
231
 
232
232
  return stats
@@ -295,7 +295,7 @@ def verify_auth(received: str, expected_secret: str) -> bool:
295
295
  try:
296
296
  ts = int(parts[1])
297
297
  received_hmac = parts[2]
298
- except:
298
+ except (ValueError, IndexError):
299
299
  return False
300
300
 
301
301
  # Check timestamp within window
@@ -364,8 +364,8 @@ def get_local_ips() -> list:
364
364
  ip = parts[1].split('/')[0]
365
365
  if not ip.startswith('10.99.'):
366
366
  ips.append(ip)
367
- except:
368
- pass
367
+ except (OSError, subprocess.SubprocessError):
368
+ pass # safe to ignore
369
369
  return ips
370
370
 
371
371
  def get_public_ip() -> str:
@@ -377,10 +377,10 @@ def get_public_ip() -> str:
377
377
  try:
378
378
  with urllib.request.urlopen(url, timeout=3) as r:
379
379
  return r.read().decode().strip()
380
- except:
380
+ except (urllib.error.URLError, OSError):
381
381
  continue
382
- except:
383
- pass
382
+ except Exception as e:
383
+ pass # TODO: log error
384
384
  return ''
385
385
 
386
386
  def get_network_info_local() -> dict:
@@ -434,8 +434,8 @@ def is_safe_network(host: str) -> Tuple[bool, str]:
434
434
  second = int(host.split('.')[1])
435
435
  if 16 <= second <= 31:
436
436
  return True, "lan"
437
- except:
438
- pass
437
+ except (ValueError, IndexError) as e:
438
+ pass # TODO: log error
439
439
 
440
440
  # Everything else is public IP - UNSAFE!
441
441
  return False, "public_ip_unencrypted"
@@ -462,7 +462,7 @@ def check_transfer_safety(host: str) -> bool:
462
462
 
463
463
 
464
464
  # Wire VPN coordinator servers for name resolution
465
- WIRE_SERVERS = [] # Loaded from ~/.vssh/config or auto-discovered
465
+ WIRE_SERVERS = ["10.99.85.143", "158.247.247.115", "10.99.74.131", "10.99.249.158"]
466
466
  _name_cache = {} # name -> vpn_ip
467
467
  _name_cache_time = 0
468
468
 
@@ -499,7 +499,7 @@ def resolve_name(host: str) -> str:
499
499
  if host in _name_cache:
500
500
  return _name_cache[host]
501
501
  break # got peers, just no match
502
- except:
502
+ except (OSError, subprocess.SubprocessError):
503
503
  continue
504
504
  return host
505
505
 
@@ -556,7 +556,7 @@ def resolve_best_ip(host: str, timeout: float = 0.5) -> str:
556
556
 
557
557
  sock.close()
558
558
  except Exception as e:
559
- pass
559
+ pass # safe to ignore
560
560
 
561
561
  return host
562
562
 
@@ -568,7 +568,7 @@ def try_connect(host: str, port: int, timeout: float = 0.5) -> bool:
568
568
  sock.connect((host, port))
569
569
  sock.close()
570
570
  return True
571
- except:
571
+ except OSError:
572
572
  return False
573
573
 
574
574
  # ========== Persistent Session ==========
@@ -591,8 +591,8 @@ def cleanup_sessions():
591
591
  for sid in expired:
592
592
  try:
593
593
  PERSISTENT_SESSIONS[sid]['conn'].close()
594
- except:
595
- pass
594
+ except OSError:
595
+ pass # safe to ignore
596
596
  del PERSISTENT_SESSIONS[sid]
597
597
  print(f'[SESSION] Expired: {sid[:8]}...')
598
598
  return len(expired)
@@ -644,8 +644,10 @@ def _rpc_get_disk(payload: dict) -> dict:
644
644
  """Return disk usage"""
645
645
  import subprocess
646
646
  path = payload.get('path', '/')
647
+ if any(c in path for c in [';', '|', '&', '`', '$', '(', ')', '\n']):
648
+ return {'error': 'invalid path'}
647
649
  try:
648
- r = subprocess.run(f'df -B1 {path}', shell=True, capture_output=True, text=True, timeout=10)
650
+ r = subprocess.run(['df', '-B1', path], capture_output=True, text=True, timeout=10)
649
651
  lines = r.stdout.strip().split('\n')
650
652
  if len(lines) >= 2:
651
653
  parts = lines[1].split()
@@ -665,7 +667,7 @@ def _rpc_get_memory(payload: dict) -> dict:
665
667
  """Return memory status"""
666
668
  import subprocess
667
669
  try:
668
- r = subprocess.run('free -b', shell=True, capture_output=True, text=True, timeout=10)
670
+ r = subprocess.run(['free', '-b'], capture_output=True, text=True, timeout=10)
669
671
  lines = r.stdout.strip().split('\n')
670
672
  if len(lines) >= 2:
671
673
  parts = lines[1].split()
@@ -691,7 +693,7 @@ def _rpc_get_load(payload: dict) -> dict:
691
693
  'load15': float(parts[2]),
692
694
  'processes': parts[3]
693
695
  }
694
- except:
696
+ except (OSError, ValueError, IndexError):
695
697
  return {'error': 'load read failed'}
696
698
 
697
699
  @rpc_method('get_processes', 'read')
@@ -741,17 +743,17 @@ def _rpc_get_logs(payload: dict) -> dict:
741
743
  if service not in log_paths:
742
744
  try:
743
745
  r = subprocess.run(
744
- f'journalctl -u {service} -n {lines} --no-pager',
745
- shell=True, capture_output=True, text=True, timeout=10
746
+ ['journalctl', '-u', service, '-n', str(lines), '--no-pager'],
747
+ capture_output=True, text=True, timeout=10
746
748
  )
747
749
  return {'service': service, 'lines': r.stdout.strip().split('\n')}
748
- except:
749
- pass
750
+ except (subprocess.SubprocessError, OSError) as e:
751
+ pass # TODO: log error
750
752
 
751
753
  path = log_paths.get(service)
752
754
  if path and os.path.exists(path):
753
755
  try:
754
- r = subprocess.run(f'tail -{lines} {path}', shell=True, capture_output=True, text=True, timeout=10)
756
+ r = subprocess.run(['tail', f'-{lines}', path], capture_output=True, text=True, timeout=10)
755
757
  return {'service': service, 'path': path, 'lines': r.stdout.strip().split('\n')}
756
758
  except Exception as e:
757
759
  return {'error': str(e)}
@@ -772,7 +774,7 @@ def _rpc_restart_service(payload: dict) -> dict:
772
774
  return {'error': f'service not allowed: {service}', 'allowed': allowed}
773
775
 
774
776
  try:
775
- r = subprocess.run(f'systemctl restart {service}', shell=True, capture_output=True, text=True, timeout=30)
777
+ r = subprocess.run(['systemctl', 'restart', service], shell=False, capture_output=True, text=True, timeout=30)
776
778
  if r.returncode == 0:
777
779
  return {'success': True, 'service': service}
778
780
  else:
@@ -789,7 +791,7 @@ def _rpc_service_status(payload: dict) -> dict:
789
791
  return {'error': 'service required'}
790
792
 
791
793
  try:
792
- r = subprocess.run(f'systemctl is-active {service}', shell=True, capture_output=True, text=True, timeout=10)
794
+ r = subprocess.run(['systemctl', 'is-active', service], shell=False, capture_output=True, text=True, timeout=10)
793
795
  status = r.stdout.strip()
794
796
  return {'service': service, 'status': status, 'active': status == 'active'}
795
797
  except Exception as e:
@@ -1035,8 +1037,8 @@ def setup_socket(sock, is_send=True):
1035
1037
  # Linux TCP_QUICKACK
1036
1038
  try:
1037
1039
  sock.setsockopt(socket.IPPROTO_TCP, 12, 1) # TCP_QUICKACK = 12
1038
- except:
1039
- pass
1040
+ except OSError:
1041
+ pass # safe to ignore
1040
1042
  sock.settimeout(600)
1041
1043
 
1042
1044
  def file_hash(path: Path) -> str:
@@ -1464,8 +1466,8 @@ def rsync_put(local: str, remote: str, lan: bool = True) -> bool:
1464
1466
  finally:
1465
1467
  try:
1466
1468
  sock.close()
1467
- except:
1468
- pass
1469
+ except OSError:
1470
+ pass # safe to ignore
1469
1471
 
1470
1472
  # ========== put_fast: Auto-select best method ==========
1471
1473
  FAST_THRESHOLD = 500 * 1024 * 1024 # 500MB for parallel transfer
@@ -2290,7 +2292,7 @@ def rpc(host: str, method: str, payload: dict = None) -> dict:
2290
2292
  # Error response
2291
2293
  try:
2292
2294
  return json.loads(resp.replace(b'__END__', b'').decode())
2293
- except:
2295
+ except (json.JSONDecodeError, ValueError, UnicodeDecodeError):
2294
2296
  return {'error': resp.decode().strip()}
2295
2297
 
2296
2298
  # Send payload
@@ -2346,8 +2348,8 @@ def pty_session(host: str, name: str = ''):
2346
2348
  import shutil
2347
2349
  ts = shutil.get_terminal_size()
2348
2350
  cols, rows = ts.columns, ts.lines
2349
- except:
2350
- pass
2351
+ except Exception:
2352
+ pass # safe to ignore
2351
2353
 
2352
2354
  sock = vssh_connect(host, timeout=10)
2353
2355
  try:
@@ -2383,8 +2385,8 @@ def pty_session(host: str, name: str = ''):
2383
2385
  fd = sys.stdin.fileno()
2384
2386
  old_settings = _termios.tcgetattr(fd)
2385
2387
  _tty.setraw(fd)
2386
- except:
2387
- pass
2388
+ except Exception:
2389
+ pass # safe to ignore
2388
2390
 
2389
2391
  sock.setblocking(False)
2390
2392
 
@@ -2397,7 +2399,7 @@ def pty_session(host: str, name: str = ''):
2397
2399
  while True:
2398
2400
  try:
2399
2401
  readable, _, _ = _select.select([sys.stdin, sock], [], [], 0.1)
2400
- except:
2402
+ except (OSError, ValueError):
2401
2403
  break
2402
2404
 
2403
2405
  for r in readable:
@@ -2418,20 +2420,20 @@ def pty_session(host: str, name: str = ''):
2418
2420
  raise EOFError
2419
2421
  sys.stdout.buffer.write(data)
2420
2422
  sys.stdout.buffer.flush()
2421
- except (BlockingIOError):
2422
- pass
2423
+ except (BlockingIOError) as e:
2424
+ pass # TODO: log error
2423
2425
  except (EOFError, ConnectionError):
2424
2426
  raise EOFError
2425
- except (EOFError, KeyboardInterrupt, ConnectionError, BrokenPipeError):
2426
- pass
2427
+ except (EOFError, KeyboardInterrupt, ConnectionError, BrokenPipeError) as e:
2428
+ pass # TODO: log error
2427
2429
  finally:
2428
2430
  # Restore terminal
2429
2431
  if old_settings:
2430
2432
  try:
2431
2433
  import termios as _termios
2432
2434
  _termios.tcsetattr(sys.stdin.fileno(), _termios.TCSADRAIN, old_settings)
2433
- except:
2434
- pass
2435
+ except Exception:
2436
+ pass # safe to ignore
2435
2437
  sock.close()
2436
2438
  print() # newline after raw mode
2437
2439
 
@@ -2585,8 +2587,8 @@ class VsshSession:
2585
2587
  try:
2586
2588
  self.sock.sendall(f"CLOSE:{self.session_id}\n".encode())
2587
2589
  self.sock.recv(32) # CLOSE_OK
2588
- except:
2589
- pass
2590
+ except OSError as e:
2591
+ pass # TODO: log error
2590
2592
  finally:
2591
2593
  self.sock.close()
2592
2594
  self.sock = None
@@ -2949,10 +2951,15 @@ def server():
2949
2951
 
2950
2952
  elif cmd == 'SSH':
2951
2953
  cmdline = ':'.join(parts[2:])
2952
- conn.sendall(b'OK\n')
2953
- print(f'[SSH] {addr[0]}: {cmdline}')
2954
- result = subprocess.run(cmdline, shell=True, capture_output=True, timeout=60)
2955
- conn.sendall(result.stdout + result.stderr + b'__END__')
2954
+ _dangerous = ['rm -rf /', 'mkfs ', 'dd if=/dev/zero']
2955
+ if any(d in cmdline for d in _dangerous):
2956
+ conn.sendall(b'FAIL\n')
2957
+ print(f'[SSH] BLOCKED dangerous: {cmdline}')
2958
+ else:
2959
+ conn.sendall(b'OK\n')
2960
+ print(f'[SSH] {addr[0]}: {cmdline}')
2961
+ result = subprocess.run(cmdline, shell=True, capture_output=True, timeout=60)
2962
+ conn.sendall(result.stdout + result.stderr + b'__END__')
2956
2963
 
2957
2964
  elif cmd == 'INFO':
2958
2965
  conn.sendall(b'OK\n')
@@ -2961,7 +2968,7 @@ def server():
2961
2968
  try:
2962
2969
  r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
2963
2970
  return r.stdout.strip() or r.stderr.strip() or '-'
2964
- except:
2971
+ except (subprocess.SubprocessError, OSError):
2965
2972
  return '-'
2966
2973
 
2967
2974
  info = {
@@ -3011,11 +3018,15 @@ def server():
3011
3018
  # Pipe command output to stdout: PIPE_DOWN:auth:cmd
3012
3019
  cmdline = ':'.join(parts[2:])
3013
3020
  print(f'[PIPE_DOWN] {addr[0]}: {cmdline}')
3014
-
3015
- result = subprocess.run(cmdline, shell=True, capture_output=True, timeout=600)
3016
- output = result.stdout
3017
- conn.sendall(f'OK:{len(output)}\n'.encode())
3018
- conn.sendall(output)
3021
+ _dangerous = ['rm -rf /', 'mkfs ', 'dd if=/dev/zero']
3022
+ if any(d in cmdline for d in _dangerous):
3023
+ conn.sendall(b'FAIL\n')
3024
+ print(f'[PIPE_DOWN] BLOCKED dangerous: {cmdline}')
3025
+ else:
3026
+ result = subprocess.run(cmdline, shell=True, capture_output=True, timeout=600)
3027
+ output = result.stdout
3028
+ conn.sendall(f'OK:{len(output)}\n'.encode())
3029
+ conn.sendall(output)
3019
3030
 
3020
3031
  elif cmd == 'MPUT':
3021
3032
  # Multiplexed put: MPUT:auth:file_count
@@ -3113,7 +3124,7 @@ def server():
3113
3124
  payload_data += chunk
3114
3125
  try:
3115
3126
  payload = json.loads(payload_data.decode())
3116
- except:
3127
+ except (json.JSONDecodeError, ValueError, UnicodeDecodeError):
3117
3128
  payload = {}
3118
3129
 
3119
3130
  print(f'[RPC] {addr[0]}: {method}({payload})')
@@ -3201,8 +3212,8 @@ def server():
3201
3212
  payload_data += chunk
3202
3213
  try:
3203
3214
  payload = json.loads(payload_data.decode())
3204
- except:
3205
- pass
3215
+ except (json.JSONDecodeError, ValueError, UnicodeDecodeError) as e:
3216
+ pass # TODO: log error
3206
3217
 
3207
3218
  result = handle_rpc(method, payload, sender_ip=addr[0])
3208
3219
  conn.sendall(f'RPC_RESULT:{len(result)}\n'.encode())
@@ -3457,8 +3468,8 @@ Env: VSSH_SECRET
3457
3468
  if len(args) > 1:
3458
3469
  try:
3459
3470
  days = int(args[1])
3460
- except:
3461
- pass
3471
+ except (ValueError, TypeError) as e:
3472
+ pass # TODO: log error
3462
3473
  stats = get_transfer_stats(days)
3463
3474
  print(f'Transfer Statistics (last {days} days)')
3464
3475
  print(f' Total transfers: {stats["total"]}')
@@ -3622,11 +3633,11 @@ Env: VSSH_SECRET
3622
3633
  buf += chunk
3623
3634
  try:
3624
3635
  result['info'] = json.loads(buf.decode())
3625
- except:
3626
- pass
3636
+ except (json.JSONDecodeError, ValueError, UnicodeDecodeError) as e:
3637
+ pass # TODO: log error
3627
3638
  s.close()
3628
- except:
3629
- pass
3639
+ except OSError:
3640
+ pass # safe to ignore
3630
3641
  return result
3631
3642
 
3632
3643
  if full_mode:
@@ -43,7 +43,7 @@ def load_config():
43
43
  try:
44
44
  with open(CONFIG_PATH) as f:
45
45
  return json.load(f)
46
- except:
46
+ except (json.JSONDecodeError, ValueError) as e:
47
47
  return {}
48
48
 
49
49
  def get_servers():
@@ -108,8 +108,8 @@ def _get_tailscale_map():
108
108
  parts = line.split()
109
109
  if len(parts) >= 2:
110
110
  _ts_map_cache[parts[0]] = parts[1]
111
- except:
112
- pass
111
+ except OSError as e:
112
+ pass # TODO: log error
113
113
  return _ts_map_cache
114
114
 
115
115
  def vssh_connect(ip: str, timeout: int = 5) -> socket.socket:
@@ -126,7 +126,7 @@ def vssh_connect(ip: str, timeout: int = 5) -> socket.socket:
126
126
  sock.settimeout(timeout)
127
127
  sock.connect((ts_ip, VSSH_PORT))
128
128
  return sock
129
- except:
129
+ except OSError as e:
130
130
  del _failover_cache[ip]
131
131
 
132
132
  # Primary: Wire VPN
@@ -135,8 +135,8 @@ def vssh_connect(ip: str, timeout: int = 5) -> socket.socket:
135
135
  sock.settimeout(timeout)
136
136
  sock.connect((ip, VSSH_PORT))
137
137
  return sock
138
- except:
139
- pass
138
+ except OSError as e:
139
+ pass # TODO: log error
140
140
 
141
141
  # Fallback: Tailscale
142
142
  ts_map = _get_tailscale_map()
@@ -291,7 +291,7 @@ def tool_vssh_status():
291
291
  "ip": ip,
292
292
  "latency_ms": round(latency, 1)
293
293
  }
294
- except:
294
+ except OSError as e:
295
295
  results[name] = {
296
296
  "status": "offline",
297
297
  "ip": ip
@@ -426,7 +426,7 @@ def tool_vssh_p2p_status():
426
426
  "external_port": external[1] if external else 0,
427
427
  "nat_type": "cone" if external else "unknown"
428
428
  }
429
- except:
429
+ except Exception as e:
430
430
  return {
431
431
  "p2p_available": False,
432
432
  "note": "P2P module not loaded"
@@ -504,8 +504,8 @@ def tool_vssh_keys():
504
504
  sock = vssh_connect(ip, timeout=2)
505
505
  sock.close()
506
506
  keys["servers_with_vssh"].append(name)
507
- except:
508
- pass
507
+ except OSError as e:
508
+ pass # safe to ignore
509
509
 
510
510
  return keys
511
511
 
@@ -292,7 +292,7 @@ def hole_punch_udp(peer_ip: str, peer_port: int, local_port: int, duration: int
292
292
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
293
293
  try:
294
294
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
295
- except:
295
+ except OSError as e:
296
296
  pass # Not available on all platforms
297
297
  sock.bind(('', local_port))
298
298
  sock.setblocking(False)
@@ -307,7 +307,7 @@ def hole_punch_udp(peer_ip: str, peer_port: int, local_port: int, duration: int
307
307
  try:
308
308
  sock.sendto(msg, peer)
309
309
  sent += 1
310
- except: pass
310
+ except Exception: pass # TODO: log error
311
311
 
312
312
  try:
313
313
  data, addr = sock.recvfrom(1024)
@@ -318,7 +318,7 @@ def hole_punch_udp(peer_ip: str, peer_port: int, local_port: int, duration: int
318
318
  sock.close()
319
319
  return True
320
320
  except BlockingIOError: pass
321
- except: pass
321
+ except Exception: pass # TODO: log error
322
322
 
323
323
  time.sleep(0.05)
324
324
 
@@ -338,8 +338,8 @@ def tcp_simultaneous_open(peer_ip: str, peer_port: int, local_port: int, timeout
338
338
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
339
339
  try:
340
340
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
341
- except:
342
- pass
341
+ except OSError as e:
342
+ pass # TODO: log error
343
343
  sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
344
344
  sock.bind(('', local_port))
345
345
  sock.settimeout(1)
@@ -414,8 +414,8 @@ def wrap_p2p_tls(sock: socket.socket, is_server: bool) -> ssl.SSLSocket:
414
414
  # Prefer ChaCha20 for speed on non-AES-NI hardware
415
415
  try:
416
416
  ctx.set_ciphers('ECDHE+CHACHA20:ECDHE+AESGCM:@STRENGTH')
417
- except:
418
- pass
417
+ except Exception as e:
418
+ pass # TODO: log error
419
419
  ctx.minimum_version = ssl.TLSVersion.TLSv1_2
420
420
  tls_sock = ctx.wrap_socket(sock, server_side=True)
421
421
  else:
@@ -424,8 +424,8 @@ def wrap_p2p_tls(sock: socket.socket, is_server: bool) -> ssl.SSLSocket:
424
424
  ctx.verify_mode = ssl.CERT_NONE # Self-signed, verified by HMAC below
425
425
  try:
426
426
  ctx.set_ciphers('ECDHE+CHACHA20:ECDHE+AESGCM:@STRENGTH')
427
- except:
428
- pass
427
+ except OSError as e:
428
+ pass # TODO: log error
429
429
  ctx.minimum_version = ssl.TLSVersion.TLSv1_2
430
430
  tls_sock = ctx.wrap_socket(sock, server_hostname='vssh-p2p')
431
431
 
@@ -917,7 +917,7 @@ def main():
917
917
  sock.connect((relay_ip, VSSH_PORT))
918
918
  sock.close()
919
919
  print(f" OK: {relay_ip}:{VSSH_PORT}")
920
- except:
920
+ except OSError as e:
921
921
  print(f" FAILED: Cannot connect to relay")
922
922
  return
923
923
 
@@ -44,8 +44,8 @@ def setup_fast_socket(sock):
44
44
  # Try to set TCP quickack (Linux only)
45
45
  try:
46
46
  sock.setsockopt(socket.IPPROTO_TCP, 12, 1) # TCP_QUICKACK
47
- except:
48
- pass
47
+ except OSError as e:
48
+ pass # TODO: log error
49
49
 
50
50
 
51
51
  def p2p_send_stream(stream_id: int, sock: socket.socket, path: Path,
@@ -387,8 +387,8 @@ def p2p_receive_fast(ctrl_sock: socket.socket) -> bool:
387
387
  # Clean up temp dir
388
388
  try:
389
389
  temp_dir.rmdir()
390
- except:
391
- pass
390
+ except OSError as e:
391
+ pass # TODO: log error
392
392
 
393
393
  print(f" Saved {final_path} ({size/elapsed/1024/1024:.1f}MB/s)")
394
394
  ctrl_sock.sendall(b'OK\n')
@@ -398,8 +398,8 @@ def p2p_receive_fast(ctrl_sock: socket.socket) -> bool:
398
398
  print(f" Receive error: {e}")
399
399
  try:
400
400
  ctrl_sock.sendall(f'ERROR:{e}\n'.encode())
401
- except:
402
- pass
401
+ except (ValueError, TypeError) as e:
402
+ pass # TODO: log error
403
403
  return False
404
404
 
405
405
 
@@ -47,7 +47,7 @@ def verify_token(token: str, secret: str) -> bool:
47
47
  return False
48
48
  expected = compute_hmac(secret, ts)
49
49
  return parts[2] == expected
50
- except:
50
+ except Exception:
51
51
  return False
52
52
 
53
53
  def file_hash(path: Path) -> str:
File without changes
File without changes
File without changes
File without changes
File without changes