portune 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

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.

Potentially problematic release.


This version of portune might be problematic. Click here for more details.

portune/portune.py CHANGED
@@ -78,7 +78,16 @@ div {
78
78
  text-align: right;
79
79
  padding-right: 8px;
80
80
  }
81
-
81
+ .desc {
82
+ text-overflow: ellipsis;
83
+ max-width: 300px;
84
+ white-space: nowrap;
85
+ overflow: hidden;
86
+ }
87
+ .desc:hover {
88
+ overflow: visible;
89
+ white-space: normal;
90
+ }
82
91
  .table-container {
83
92
  height: 90%;
84
93
  overflow-y: auto;
@@ -600,6 +609,7 @@ def parse_input_file(filename: str) -> List[Tuple[str, List[int]]]:
600
609
  A list of tuples, each containing:
601
610
  - hostname (str): The target hostname or IP address
602
611
  - ports (List[int]): List of ports to scan for that host
612
+ - desc (str): Optional description for the host
603
613
 
604
614
  Examples:
605
615
  Input file format:
@@ -615,19 +625,26 @@ def parse_input_file(filename: str) -> List[Tuple[str, List[int]]]:
615
625
  ('host3', [8080])
616
626
  ]
617
627
  """
618
- hosts = []
628
+ host_dict = {}
619
629
  with open(filename, 'r') as f:
620
630
  for line in f:
621
631
  line = line.strip().lower()
622
632
  if not line or line.startswith('#'):
623
633
  continue
624
- try:
625
- host, ports = line.split()
626
- port_list = [int(p) for p in ports.split(',')]
627
- hosts.append((host, port_list))
628
- except Exception:
629
- hosts.append((line, [22])) # Default port 22 if parsing fails
630
- continue
634
+ words = line.split()
635
+ fqdn = words[0]
636
+ ports = words[1] if len(words) > 1 else '22'
637
+ port_list = [int(p) for p in ports.split(',')]
638
+ desc = ' '.join(words[2:]).strip() if len(words) > 2 else ''
639
+ if fqdn in host_dict:
640
+ existing_ports, existing_desc = host_dict[fqdn]
641
+ host_dict[fqdn] = (list(set(existing_ports + port_list)), existing_desc or desc)
642
+ else:
643
+ host_dict[fqdn] = (port_list, desc)
644
+ hosts = []
645
+ for fqdn in host_dict:
646
+ ports, desc = host_dict[fqdn]
647
+ hosts.append((fqdn, sorted(ports), desc))
631
648
  return hosts
632
649
 
633
650
  def ping_host(ip: str, timeout: float = 2.0) -> bool:
@@ -698,7 +715,7 @@ def resolve_and_ping_host(hostname: str, timeout: float = 2.0, noping: bool = Fa
698
715
  except Exception:
699
716
  return hostname, {'ip': 'N/A', 'ping': False}
700
717
 
701
- def ping_hosts(hosts: List[Tuple[str, List[int]]],
718
+ def ping_hosts(hosts: List[Tuple[str, List[int], str]],
702
719
  timeout: float = 2.0,
703
720
  parallelism: int = 10,
704
721
  noping: bool = False) -> Dict[str, Dict[str, Union[str, bool]]]:
@@ -739,7 +756,7 @@ def ping_hosts(hosts: List[Tuple[str, List[int]]],
739
756
  with ThreadPoolExecutor(max_workers=parallelism) as executor:
740
757
  future_to_host = {
741
758
  executor.submit(resolve_and_ping_host, hostname, timeout, noping): hostname
742
- for hostname, _ in hosts
759
+ for hostname, _, _ in hosts
743
760
  }
744
761
 
745
762
  # Add callback to update progress bar when each future completes
@@ -760,7 +777,8 @@ def ping_hosts(hosts: List[Tuple[str, List[int]]],
760
777
 
761
778
  def check_port(hostname: str,
762
779
  port: int,
763
- host_info: Dict[str, Union[str, bool]],
780
+ host_info: Dict[str, Union[str, bool]],
781
+ desc: str,
764
782
  timeout: float = 2.0) -> Tuple[str, str, int, str, bool]:
765
783
  """Check if a specific TCP port is accessible on a host.
766
784
 
@@ -774,6 +792,7 @@ def check_port(hostname: str,
774
792
  - 'ip': IP address or 'N/A' if resolution failed
775
793
  - 'ping': Boolean indicating ping status
776
794
  - 'hostname': Optional resolved hostname from reverse DNS
795
+ desc: Optional description for the host
777
796
  timeout: Maximum time to wait for connection in seconds
778
797
 
779
798
  Returns:
@@ -783,6 +802,7 @@ def check_port(hostname: str,
783
802
  - port: The port number that was checked
784
803
  - status: Connection status (CONNECTED/TIMEOUT/REFUSED/UNREACHABLE)
785
804
  - ping: Boolean indicating if the host responded to ping
805
+ - desc: Optional description for the host
786
806
 
787
807
  Status meanings:
788
808
  CONNECTED: Successfully established TCP connection
@@ -792,7 +812,7 @@ def check_port(hostname: str,
792
812
  RESOLVE_FAIL: Could not resolve hostname to IP
793
813
  """
794
814
  if host_info['ip'] == 'N/A':
795
- return (hostname, host_info['ip'], port, 'RESOLVE_FAIL', host_info['ping'])
815
+ return (hostname, host_info['ip'], port, 'RESOLVE_FAIL', host_info['ping'], desc)
796
816
 
797
817
  # Use resolved hostname if available
798
818
  display_hostname = host_info.get('hostname', hostname)
@@ -802,16 +822,16 @@ def check_port(hostname: str,
802
822
  try:
803
823
  s.connect((host_info['ip'], port))
804
824
  s.close()
805
- return (display_hostname, host_info['ip'], port, 'CONNECTED', host_info['ping'])
825
+ return (display_hostname, host_info['ip'], port, 'CONNECTED', host_info['ping'], desc)
806
826
  except ConnectionAbortedError:
807
- return (display_hostname, host_info['ip'], port, 'CONNECTED', host_info['ping'])
827
+ return (display_hostname, host_info['ip'], port, 'CONNECTED', host_info['ping'], desc)
808
828
  except (TimeoutError, socket.timeout):
809
- return (display_hostname, host_info['ip'], port, 'TIMEOUT', host_info['ping'])
829
+ return (display_hostname, host_info['ip'], port, 'TIMEOUT', host_info['ping'], desc)
810
830
  except ConnectionRefusedError:
811
- return (display_hostname, host_info['ip'], port, 'REFUSED', host_info['ping'])
831
+ return (display_hostname, host_info['ip'], port, 'REFUSED', host_info['ping'], desc)
812
832
  except Exception:
813
833
  # Handle other network errors (filtered, network unreachable, etc)
814
- return (display_hostname, host_info['ip'], port, 'UNREACHABLE', host_info['ping'])
834
+ return (display_hostname, host_info['ip'], port, 'UNREACHABLE', host_info['ping'], desc)
815
835
 
816
836
  def send_email_report(
817
837
  output_file: str,
@@ -979,7 +999,7 @@ def compute_stats(
979
999
 
980
1000
 
981
1001
  # Collect VLAN statistics for timeouts
982
- for hostname, ip, port, status, _ in results:
1002
+ for hostname, ip, port, status, _, _ in results:
983
1003
  if ip != 'N/A':
984
1004
  try:
985
1005
  vlan = get_vlan_base(ip, bits)
@@ -993,7 +1013,7 @@ def compute_stats(
993
1013
  # Group results by hostname for host statistics
994
1014
  host_stats = defaultdict(lambda: {'statuses': [], 'ping': False})
995
1015
  for result in results:
996
- hostname, _, _, status, ping = result
1016
+ hostname, _, _, status, ping, _ = result
997
1017
  host_stats[hostname]['statuses'].append(status)
998
1018
  host_stats[hostname]['ping'] |= ping
999
1019
 
@@ -1020,7 +1040,7 @@ def compute_stats(
1020
1040
  })
1021
1041
  })
1022
1042
 
1023
- for hostname, ip, port, status, _ in results:
1043
+ for hostname, ip, port, status, _, _ in results:
1024
1044
  # For IP addresses, use VLAN/16 as domain
1025
1045
  try:
1026
1046
  socket.inet_aton(hostname)
@@ -1194,13 +1214,14 @@ def generate_html_report(
1194
1214
  <th>Port</th>
1195
1215
  <th>Status</th>
1196
1216
  <th>Ping</th>
1217
+ <th>Description</th>
1197
1218
  </tr>
1198
1219
  </thead>
1199
1220
  <tbody>
1200
1221
  ''')
1201
1222
 
1202
1223
  # Add result rows
1203
- for hostname, ip, port, status, ping in results:
1224
+ for hostname, ip, port, status, ping, desc in results:
1204
1225
  ping_status = 'UP' if ping else 'N/A' if noping else 'DOWN'
1205
1226
  ping_class = 'green' if ping else 'blue' if noping else 'red'
1206
1227
  status_class = 'green' if status == 'CONNECTED' else 'blue' if status == 'REFUSED' else 'red'
@@ -1212,6 +1233,7 @@ def generate_html_report(
1212
1233
  <td style="text-align: right;">{port}</td>
1213
1234
  <td style="text-align: center;"><span class="{status_class} status">{escape(status)}</span></td>
1214
1235
  <td style="text-align: center;"><span class="{ping_class} ping">{ping_status}</span></td>
1236
+ <td class="desc">{escape(str(desc))}</td>
1215
1237
  </tr>
1216
1238
  ''')
1217
1239
 
@@ -1374,7 +1396,7 @@ def format_table_output(
1374
1396
  table.append(separator)
1375
1397
 
1376
1398
  # Add data rows
1377
- for hostname, ip, port, status, ping in results:
1399
+ for hostname, ip, port, status, ping, desc in results:
1378
1400
  ping_status = 'UP' if ping else 'N/A' if noping else 'DOWN'
1379
1401
  table.append(row_format.format(
1380
1402
  str(hostname), widths['Hostname'],
@@ -1420,14 +1442,14 @@ def main():
1420
1442
  host_info = ping_hosts(hosts, args.timeout, args.parallelism, args.noping)
1421
1443
 
1422
1444
  # Calculate total tasks and initialize progress bar
1423
- total_tasks = sum(len(ports) for _, ports in hosts)
1445
+ total_tasks = sum(len(ports) for _, ports, _ in hosts)
1424
1446
  print(f"Preparing to scan {len(hosts)} hosts with {total_tasks} total ports...", file=sys.stderr)
1425
1447
 
1426
1448
  # Prepare tasks with pre-resolved data
1427
1449
  tasks = []
1428
- for hostname, ports in hosts:
1450
+ for hostname, ports, desc in hosts:
1429
1451
  for port in ports:
1430
- tasks.append((hostname, port, host_info[hostname]))
1452
+ tasks.append((hostname, port, host_info[hostname], desc))
1431
1453
 
1432
1454
  results = []
1433
1455
  lock = threading.Lock()
@@ -1436,8 +1458,8 @@ def main():
1436
1458
  progress_bar = ProgressBar(total_tasks, prefix='Scanning')
1437
1459
 
1438
1460
  with ThreadPoolExecutor(max_workers=args.parallelism) as executor:
1439
- future_to_task = {executor.submit(check_port, hostname, port, info, args.timeout): (hostname, port, info)
1440
- for hostname, port, info in tasks}
1461
+ future_to_task = {executor.submit(check_port, hostname, port, info, desc, args.timeout): (hostname, port, info)
1462
+ for hostname, port, info, desc in tasks}
1441
1463
 
1442
1464
  for future in as_completed(future_to_task):
1443
1465
  res = future.result()
portune/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.2'
21
- __version_tuple__ = version_tuple = (0, 1, 2)
20
+ __version__ = version = '0.1.3'
21
+ __version_tuple__ = version_tuple = (0, 1, 3)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portune
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Author: Franck Jouvanceau
6
6
  Maintainer: Franck Jouvanceau
@@ -0,0 +1,9 @@
1
+ portune/__init__.py,sha256=RfXuNfHBqfRt_z4IukwN1a0oeCXahuMOO8_eBt4T8NM,58
2
+ portune/portune.py,sha256=ZSZQlqJ7ebjSFJmO5cC5NHzNX6NwOHOEdEp0MG11B_g,62820
3
+ portune/version.py,sha256=NIzzV8ZM0W-CSLuEs1weG4zPrn_-8yr1AwwI1iuS6yo,511
4
+ portune-0.1.3.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
5
+ portune-0.1.3.dist-info/METADATA,sha256=E5QlqZso2uL0VnLdldXI73x_wG0EkgJCnF2pgnvXseM,2837
6
+ portune-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ portune-0.1.3.dist-info/entry_points.txt,sha256=6j7jAf5fOZrLPbVIs3z92R1tT891WyY9YxQ6OVnPKG0,80
8
+ portune-0.1.3.dist-info/top_level.txt,sha256=CITDikHhRKAsSOGmNJzD-xSp6D5iBhSr9ZS1qy8-SL0,8
9
+ portune-0.1.3.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- portune/__init__.py,sha256=RfXuNfHBqfRt_z4IukwN1a0oeCXahuMOO8_eBt4T8NM,58
2
- portune/portune.py,sha256=ODPpO7E9-pXoFRxKavyn22_b9dhVjxUp-aIxKhnxamY,61908
3
- portune/version.py,sha256=bSmADqydH8nBu-J4lG8UVuR7hnU_zcwhnSav2oQ0W0A,511
4
- portune-0.1.2.dist-info/licenses/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
5
- portune-0.1.2.dist-info/METADATA,sha256=qEkffZiY8znnWihHuYtJlzf7t4bRPFTPvCcajLqmYBs,2837
6
- portune-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- portune-0.1.2.dist-info/entry_points.txt,sha256=6j7jAf5fOZrLPbVIs3z92R1tT891WyY9YxQ6OVnPKG0,80
8
- portune-0.1.2.dist-info/top_level.txt,sha256=CITDikHhRKAsSOGmNJzD-xSp6D5iBhSr9ZS1qy8-SL0,8
9
- portune-0.1.2.dist-info/RECORD,,