lanscape 2.2.1a1__py3-none-any.whl → 2.3.0b1__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.
@@ -138,18 +138,29 @@ class Device(BaseModel):
138
138
  @timeout_enforcer(enforcer_timeout, False)
139
139
  def do_test():
140
140
  for attempt in range(port_config.retries + 1):
141
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
142
- sock.settimeout(port_config.timeout)
141
+ sock = None
143
142
  try:
143
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
144
+ sock.settimeout(port_config.timeout)
144
145
  result = sock.connect_ex((self.ip, port))
145
146
  if result == 0:
146
147
  if port not in self.ports:
147
148
  self.ports.append(port)
148
149
  return True
150
+ except OSError as e:
151
+ # Handle socket creation failures (e.g., "Too many open files")
152
+ # Log and continue to retry if attempts remain
153
+ log = logging.getLogger('Device.test_port')
154
+ log.debug(f"OSError on {self.ip}:{port} attempt {attempt + 1}: {e}")
149
155
  except Exception:
150
156
  pass # Connection failed, try again if retries remain
151
157
  finally:
152
- sock.close()
158
+ # Always close socket if it was created
159
+ if sock is not None:
160
+ try:
161
+ sock.close()
162
+ except Exception:
163
+ pass # Ignore errors during cleanup
153
164
 
154
165
  # Wait before retry (except on last attempt)
155
166
  if attempt < port_config.retries:
@@ -154,7 +154,9 @@ def get_port_probes(port: int, strategy: ServiceScanStrategy):
154
154
 
155
155
  def scan_service(ip: str, port: int, cfg: ServiceScanConfig) -> str:
156
156
  """
157
- Synchronous function that attempts to identify the service running on a given port.
157
+ Synchronous function that attempts to identify the service
158
+ running on a given port.
159
+ TODO: This is AI slop and needs to be reworked properly.
158
160
  """
159
161
 
160
162
  async def _async_scan_service(
@@ -183,5 +185,38 @@ def scan_service(ip: str, port: int, cfg: ServiceScanConfig) -> str:
183
185
  log.debug(traceback.format_exc())
184
186
  return "Unknown"
185
187
 
186
- # Use asyncio.run to execute the asynchronous logic synchronously
187
- return asyncio.run(_async_scan_service(ip, port, cfg=cfg))
188
+ # Create and properly manage event loop to avoid file descriptor leaks
189
+ # Using new_event_loop + explicit close is safer in threaded environments
190
+ # than asyncio.run() which can leave resources open under heavy load
191
+ loop = None
192
+ try:
193
+ try:
194
+ # Try to get existing loop first (if running in async context)
195
+ loop = asyncio.get_running_loop()
196
+ # If we're already in an async context, just await directly
197
+ return asyncio.run_coroutine_threadsafe(
198
+ _async_scan_service(ip, port, cfg=cfg), loop
199
+ ).result(timeout=cfg.timeout + 5)
200
+ except RuntimeError:
201
+ # No running loop, create a new one
202
+ loop = asyncio.new_event_loop()
203
+ asyncio.set_event_loop(loop)
204
+ try:
205
+ return loop.run_until_complete(_async_scan_service(ip, port, cfg=cfg))
206
+ finally:
207
+ # Clean up the loop properly
208
+ try:
209
+ # Cancel all remaining tasks
210
+ pending = asyncio.all_tasks(loop)
211
+ for task in pending:
212
+ task.cancel()
213
+ # Run loop once more to process cancellations
214
+ if pending:
215
+ loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
216
+ except Exception:
217
+ pass
218
+ finally:
219
+ loop.close()
220
+ except Exception as e:
221
+ log.error(f"Event loop error scanning {ip}:{port}: {e}")
222
+ return "Unknown"
@@ -21,6 +21,20 @@ def get_port_lists():
21
21
  return jsonify(PortManager().get_port_lists())
22
22
 
23
23
 
24
+ @api_bp.route('/api/port/list/summary', methods=['GET'])
25
+ def get_port_lists_summary():
26
+ """Get port list names with their port counts."""
27
+ manager = PortManager()
28
+ summaries = []
29
+ for name in manager.get_port_lists():
30
+ ports = manager.get_port_list(name) or {}
31
+ summaries.append({
32
+ 'name': name,
33
+ 'count': len(ports)
34
+ })
35
+ return jsonify(summaries)
36
+
37
+
24
38
  @api_bp.route('/api/port/list/<port_list>', methods=['GET'])
25
39
  def get_port_list(port_list):
26
40
  """
@@ -564,8 +564,8 @@ input[type="range"] {
564
564
  /* Service Strategy Select Styles */
565
565
  .service-strategy-wrapper {
566
566
  position: relative;
567
- display: inline-block;
568
- width: 60%; /* Narrower than port list */
567
+ display: block;
568
+ width: 100%;
569
569
  }
570
570
 
571
571
  .service-strategy {
@@ -139,15 +139,26 @@ function getScanConfig() {
139
139
  }
140
140
 
141
141
  function getPortLists(callback=null) {
142
- $.get('/api/port/list', function(data) {
143
- const customSelectDropdown = $('#port_list');
142
+ const customSelectDropdown = $('#port_list');
143
+
144
+ const renderOptions = (items) => {
144
145
  customSelectDropdown.empty();
145
-
146
- // Populate the dropdown with the options
147
- data.forEach(function(portList) {
148
- customSelectDropdown.append('<option>' + portList + '</option>');
146
+ items.forEach((item) => {
147
+ const name = item.name || item;
148
+ const count = item.count;
149
+ const label = count !== undefined ? `${name} (${count} ports)` : name;
150
+ customSelectDropdown.append(`<option value="${name}">${label}</option>`);
149
151
  });
152
+ };
153
+
154
+ $.get('/api/port/list/summary', function(data) {
155
+ renderOptions(data || []);
150
156
  if (callback) callback();
157
+ }).fail(function() {
158
+ $.get('/api/port/list', function(data) {
159
+ renderOptions(data || []);
160
+ if (callback) callback();
161
+ });
151
162
  });
152
163
  }
153
164
 
@@ -201,7 +201,7 @@
201
201
  <div id="section-service-scan" class="form-group mt-2 config-section div-hide">
202
202
  <h6>Service Scanning</h6>
203
203
  <div class="row">
204
- <div class="col">
204
+ <div class="col-12">
205
205
  <label for="service_lookup_type" class="form-label">Strategy</label>
206
206
  <div class="service-strategy-wrapper">
207
207
  <select id="service_lookup_type" class="service-strategy">
@@ -211,11 +211,13 @@
211
211
  </select>
212
212
  </div>
213
213
  </div>
214
- <div class="col">
214
+ </div>
215
+ <div class="row mt-2">
216
+ <div class="col-12 col-md-6">
215
217
  <label for="service_timeout" class="form-label">Timeout (sec)</label>
216
218
  <input type="number" step="0.1" id="service_timeout" class="form-control">
217
219
  </div>
218
- <div class="col">
220
+ <div class="col-12 col-md-6 mt-2 mt-md-0">
219
221
  <label for="service_max_concurrent_probes" class="form-label">Max Concurrent Probes</label>
220
222
  <input type="number" id="service_max_concurrent_probes" class="form-control">
221
223
  </div>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lanscape
3
- Version: 2.2.1a1
3
+ Version: 2.3.0b1
4
4
  Summary: A python based local network scanner
5
5
  Author-email: Michael Dennis <michael@dipduo.com>
6
6
  License-Expression: MIT
@@ -31,6 +31,8 @@ Requires-Dist: pytest>=8.0; extra == "dev"
31
31
  Requires-Dist: pytest-cov>=5.0; extra == "dev"
32
32
  Requires-Dist: pytest-xdist>=3.0; extra == "dev"
33
33
  Requires-Dist: openai>=1.0.0; extra == "dev"
34
+ Requires-Dist: pylint>=3.0; extra == "dev"
35
+ Requires-Dist: autopep8>=2.0; extra == "dev"
34
36
  Dynamic: license-file
35
37
 
36
38
  # LANscape
@@ -8,11 +8,11 @@ lanscape/core/errors.py,sha256=QTf42UzR9Zxj1t1mdwfLvZIp0c9a5EItELOdCR7kTmE,1322
8
8
  lanscape/core/ip_parser.py,sha256=kn5H4ERitLnreRAqifWphwbxdjItGqwu50lsMCPDMcA,3474
9
9
  lanscape/core/logger.py,sha256=nzo6J8UdlMdhRkOJEDOIHKztoE3Du8PQZad7ixvNgeM,2534
10
10
  lanscape/core/mac_lookup.py,sha256=PxBSMe3wEVDtivCsh5NclSAguZz9rqdAS7QshBiuWvM,3519
11
- lanscape/core/net_tools.py,sha256=Ht8TkLnKf-f6E1_AkTCde-yuiwUNI57TCXke0nIFi0o,21303
11
+ lanscape/core/net_tools.py,sha256=o5A7e-YHC7VKp0fHJccrhY404qlEDV84P3-fhr8bsbc,21925
12
12
  lanscape/core/port_manager.py,sha256=3_ROOb6JEiB0NByZVtADuGcldFkgZwn1RKtvwgs9AIk,4479
13
13
  lanscape/core/runtime_args.py,sha256=2vIqRrcWr-NHRSBlZGrxh1PdkPY0ytkPguu8KZqy2L8,2543
14
14
  lanscape/core/scan_config.py,sha256=A2ZKXqXKW9nrP6yLb7b9b3XqSY_cQB3LZ5K0LVCSebE,11114
15
- lanscape/core/service_scan.py,sha256=wTDxOdazOsbI0hwCBR__4UCmB2RIbl2pw3F2YUW9aaE,6428
15
+ lanscape/core/service_scan.py,sha256=umDVOoCNNVJhWMtxDV-rn7hcS7t_V073srSMPTl5gMo,7943
16
16
  lanscape/core/subnet_scan.py,sha256=PtSOk92dK05-reyr8LBkOXaI15qpYnar5nDqALCX1tQ,14850
17
17
  lanscape/core/version_manager.py,sha256=eGjyKgsv31QO0W26se9pPQ1TwmEN8qn37dHULtoocqc,2841
18
18
  lanscape/resources/mac_addresses/convert_csv.py,sha256=hvlyLs0XmuuhBuvXBNRGP1cKJzYVRSf8VfUJ1VqROms,1189
@@ -30,13 +30,13 @@ lanscape/ui/main.py,sha256=KXVPfHbKbMP2mMvJ3e2nqdmMTbTadEfuZ7tP4SIDIqE,4334
30
30
  lanscape/ui/shutdown_handler.py,sha256=HrEnWrdYSzLDVsPgD8tf9FgtAwQrZNECDu6wnEs27EY,1704
31
31
  lanscape/ui/blueprints/__init__.py,sha256=EjPtaR5Nh17pGiOVYTJULVNaZntpFZOiYyep8rBWAiE,265
32
32
  lanscape/ui/blueprints/api/__init__.py,sha256=5Z4Y7B36O-bNFenpomfuNhPuJ9dW_MC0TPUU3pCFVfA,103
33
- lanscape/ui/blueprints/api/port.py,sha256=KlfLrAmlTqN44I1jLVDy-mWOcor4kL-b0Cl4AcTMCys,1976
33
+ lanscape/ui/blueprints/api/port.py,sha256=enqJ87hpVHSTBoaV9bJ7IgsvHrWDTga704wV33dtwUg,2384
34
34
  lanscape/ui/blueprints/api/scan.py,sha256=2rsW4xkI4X2Q2ocwaE469aU1VxQ3xHuBRjD9xE36WdI,3326
35
35
  lanscape/ui/blueprints/api/tools.py,sha256=jr9gt0VhvBFgJ61MLgNIM6hin-MUmJLGdmStPx3e3Yc,2432
36
36
  lanscape/ui/blueprints/web/__init__.py,sha256=NvgnjP0X4LwqVhSEyh5RUzoG45N44kHK1MEFlfvBxTg,118
37
37
  lanscape/ui/blueprints/web/routes.py,sha256=f5TzfTzelJ_erslyBXTOpFr4BlIfB1Mb1ye6ioH7IL0,4534
38
38
  lanscape/ui/static/lanscape.webmanifest,sha256=07CqA-PQsO35KJD8R96sI3Pxix6UuBjijPDCuy9vM3s,446
39
- lanscape/ui/static/css/style.css,sha256=9IQru_W0WUpcxNJuHbqwjfxLOC5n1EeGcxf_kDVY3jc,21232
39
+ lanscape/ui/static/css/style.css,sha256=dOlmk0pt03TGFgx2TixTRALX_VpYX9eGDLTFJoUlovA,21196
40
40
  lanscape/ui/static/img/ico/android-chrome-192x192.png,sha256=JmFT6KBCCuoyxMV-mLNtF9_QJbVBvfWPUizKN700fi8,18255
41
41
  lanscape/ui/static/img/ico/android-chrome-512x512.png,sha256=88Jjx_1-4XAnZYz64KP6FdTl_kYkNG2_kQIKteQwSh4,138055
42
42
  lanscape/ui/static/img/ico/apple-touch-icon.png,sha256=tEJlLwBZtF4v-NC90YCfRJQ2prTsF4i3VQLK_hnv2Mw,16523
@@ -49,7 +49,7 @@ lanscape/ui/static/js/layout-sizing.js,sha256=U2dsyJi-YKpOpudu3kg2whiU4047ghzDTY
49
49
  lanscape/ui/static/js/main.js,sha256=Bgb5Ld_UPWzBVzxWkLD0kJGjOoLJyzni2TKE6Uk99VA,7428
50
50
  lanscape/ui/static/js/on-tab-close.js,sha256=3icxYWlLpY81iLoW7kQTJeWQ3UnyyboG0dESHF2wLPQ,1376
51
51
  lanscape/ui/static/js/quietReload.js,sha256=8POH0t1KVzGCJas9fNsOVbBG-ULwZT2wiS14URgkeAU,702
52
- lanscape/ui/static/js/scan-config.js,sha256=TiUwWWUGqqQbC3ZJnQCNQpoR492RXA3jLkALe6Nzl5Q,7726
52
+ lanscape/ui/static/js/scan-config.js,sha256=nQ2dWkgpf8yer-lCMTZ25DBqOuWt0SIyzzfdP_fS-bE,8086
53
53
  lanscape/ui/static/js/shutdown-server.js,sha256=Mx8UGmmktHaCK7DL8TVUxah6VEcN0wwLFfhbCId-K8U,453
54
54
  lanscape/ui/static/js/subnet-info.js,sha256=osZM6CGs-TC5QpBJWkNWCtXNOKzjyIiWKHwKi4vlDf8,559
55
55
  lanscape/ui/static/js/subnet-selector.js,sha256=2YKCAuKU2Ti1CmJrqi4_vNTD2LQbxx7chIDqND_1eAY,358
@@ -61,16 +61,16 @@ lanscape/ui/templates/scan.html,sha256=00QX2_1S_1wGzk42r00LjEkJvoioCLs6JgjOibi6r
61
61
  lanscape/ui/templates/shutdown.html,sha256=iXVCq2yl5TjZfNFl4esbDJra3gJA2VQpae0jj4ipy9w,701
62
62
  lanscape/ui/templates/core/head.html,sha256=zP1RkTYuaKCC6RtnSEHFKPw3wKWfSyV0HZg5XsAxWik,719
63
63
  lanscape/ui/templates/core/scripts.html,sha256=rSRi4Ut8iejajMPhOc5bzEz-Z3EHxpj_3PxwwyyhmTQ,640
64
- lanscape/ui/templates/scan/config.html,sha256=X95xayNtJrRUeGUlPbtUjd1228hH6re9BT6jcbaSnl8,14704
64
+ lanscape/ui/templates/scan/config.html,sha256=_XrumonSY0i2Zr8nlHB4UmFUwLc0Wx-yNXqr7sNZ2z8,14806
65
65
  lanscape/ui/templates/scan/device-detail.html,sha256=3N0WcdnWopbSFwsnKogBaHOYsLMAfKBZdkP7HQG4vLA,4794
66
66
  lanscape/ui/templates/scan/export.html,sha256=Nvs_unojzT3qhN_ZnEgYHou2C9wqWGr3dVr2UiLnYjY,749
67
67
  lanscape/ui/templates/scan/ip-table-row.html,sha256=iSW3PYev3_k7pxTZUJUboqDUgdhsWNOrYxysYismyI0,1255
68
68
  lanscape/ui/templates/scan/ip-table.html,sha256=AT2ZvCPYdKl-XJiAkEAawPOVuQw-w0MXumGQTr3zyKM,926
69
69
  lanscape/ui/templates/scan/overview.html,sha256=xWj9jWDPg2KcPLvS8fnSins23_UXjKCdb2NJwNG2U2Q,1176
70
70
  lanscape/ui/templates/scan/scan-error.html,sha256=wmAYQ13IJHUoO8fAGNDjMvNml7tu4rsIU3Vav71ETlA,999
71
- lanscape-2.2.1a1.dist-info/licenses/LICENSE,sha256=VLoE0IrNTIc09dFm7hMN0qzk4T3q8V0NaPcFQqMemDs,1070
72
- lanscape-2.2.1a1.dist-info/METADATA,sha256=INz2lnO8UO83zXGFZZhF7Ghz2tHM0iqMjYNOKiDvWPU,3615
73
- lanscape-2.2.1a1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
- lanscape-2.2.1a1.dist-info/entry_points.txt,sha256=evxSxUikFa1OEd4e0Boky9sLH87HdgM0YqB_AbB2HYc,51
75
- lanscape-2.2.1a1.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
76
- lanscape-2.2.1a1.dist-info/RECORD,,
71
+ lanscape-2.3.0b1.dist-info/licenses/LICENSE,sha256=VLoE0IrNTIc09dFm7hMN0qzk4T3q8V0NaPcFQqMemDs,1070
72
+ lanscape-2.3.0b1.dist-info/METADATA,sha256=r89dv1bzcmNA9z_DEK8bHa4n9CX28wqpiRyKUVpEGzI,3703
73
+ lanscape-2.3.0b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
+ lanscape-2.3.0b1.dist-info/entry_points.txt,sha256=evxSxUikFa1OEd4e0Boky9sLH87HdgM0YqB_AbB2HYc,51
75
+ lanscape-2.3.0b1.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
76
+ lanscape-2.3.0b1.dist-info/RECORD,,