1
1
import datetime
2
+ import os
2
3
import time
3
4
import threading
4
5
import json
5
6
import functools
6
-
7
+ import requests
8
+ import libinspector
7
9
8
10
config_file_name = 'config.json'
9
11
config_lock = threading .Lock ()
10
12
config_dict = {}
11
13
12
14
15
+ def call_predict_api (mac_address : str , url = "https://dev-id-1.tailcedbd.ts.net/predict" ) -> dict :
16
+ """
17
+ Call the predicting API with the given fields.
18
+ This takes the MAC Address of an inspected device
19
+ and checks the `devices` table, where iot inspector core collected meta-data
20
+ based on SSDP discovery.
21
+
22
+ Please see Page 11 Table A.1. We explain how to get the data from IoT Inspector:
23
+ 1. oui_friendly: we use the OUI database from IoT Inspector Core
24
+ 2. dhcp_hostname: this is extracted from the 'devices' table, check meta-data and look for 'dhcp_hostname' key.
25
+ 3. remote_hostnames: IoT Inspector collects this information the DHCP hostname via either DNS or SNI
26
+
27
+ Args:
28
+ mac_address (str): The MAC address of the device we want to use AI to get more info about
29
+ url (str): The API endpoint.
30
+
31
+ Returns:
32
+ dict: The response text from the API.
33
+ """
34
+
35
+ db_conn , rwlock = libinspector .global_state .db_conn_and_lock
36
+
37
+ # Get the DHCP Hostname if found
38
+ sql = """
39
+ SELECT metadata_json FROM devices
40
+ WHERE mac_address = ?
41
+ """
42
+ with rwlock :
43
+ row = db_conn .execute (sql , (mac_address ,)).fetchone ()
44
+ if row :
45
+ metadata = json .loads (row ['metadata_json' ])
46
+ if 'dhcp_hostname' in metadata :
47
+ dhcp_hostname = metadata ['dhcp_hostname' ]
48
+ else :
49
+ dhcp_hostname = ""
50
+ if 'oui_vendor' in metadata :
51
+ oui_vendor = metadata ['oui_vendor' ]
52
+ else :
53
+ oui_vendor = ""
54
+ else :
55
+ dhcp_hostname = ""
56
+ oui_vendor = ""
57
+
58
+ # Get the remote hostname
59
+ sql = """
60
+ SELECT DISTINCT hostname
61
+ FROM (
62
+ SELECT src_hostname AS hostname FROM network_flows
63
+ WHERE src_mac_address = ? AND src_hostname IS NOT NULL
64
+ UNION
65
+ SELECT dest_hostname AS hostname FROM network_flows
66
+ WHERE dest_mac_address = ? AND dest_hostname IS NOT NULL
67
+ ) AS combined
68
+ """
69
+ with rwlock :
70
+ rows = db_conn .execute (sql , (mac_address , mac_address )).fetchall ()
71
+ hostnames = [row ['hostname' ] for row in rows if row ['hostname' ]]
72
+ remote_hostnames = '+' .join (hostnames ) if hostnames else ""
73
+
74
+ api_key = os .environ .get ("API_KEY" , "momo" )
75
+ headers = {
76
+ "Content-Type" : "application/json" ,
77
+ "x-api-key" : api_key
78
+ }
79
+ data = {
80
+ "fields" : {
81
+ "oui_friendly" : oui_vendor ,
82
+ "dhcp_hostname" : dhcp_hostname ,
83
+ "remote_hostnames" : remote_hostnames ,
84
+ "user_agent_info" : "" ,
85
+ "netdisco_info" : "" ,
86
+ "user_labels" : "" ,
87
+ "talks_to_ads" : False
88
+ }
89
+ }
90
+ try :
91
+ response = requests .post (url , headers = headers , json = data , timeout = 10 )
92
+ response .raise_for_status () # Raises HTTPError for bad responses
93
+ result = response .json () # May raise ValueError if not valid JSON
94
+ except requests .exceptions .RequestException as e :
95
+ # Handle network errors, timeouts, or HTTP errors
96
+ print (f"API request failed: { e } " )
97
+ return {}
98
+ except ValueError as e :
99
+ # Handle JSON decoding errors
100
+ print (f"Failed to decode JSON response: { e } " )
101
+ return {}
102
+
103
+ # Store the results in the config.json output
104
+ # The key is MAC Address, and the output is directly written
105
+ print ("API query successful!" )
106
+ config_set (f'device_details@{ mac_address } ' , result )
107
+ return response .json ()
108
+
109
+
13
110
def get_human_readable_time (timestamp = None ):
14
111
"""
15
112
Convert a timestamp to a human-readable time format.
@@ -25,7 +122,6 @@ def get_human_readable_time(timestamp=None):
25
122
return datetime .datetime .fromtimestamp (timestamp ).strftime ('%Y-%m-%d %H:%M:%S' )
26
123
27
124
28
-
29
125
def initialize_config_dict ():
30
126
"""
31
127
Initialize the configuration dictionary by reading from the config file.
@@ -43,7 +139,6 @@ def initialize_config_dict():
43
139
config_dict ['app_start_time' ] = time .time ()
44
140
45
141
46
-
47
142
def config_get (key , default = None ):
48
143
"""
49
144
Get a configuration value.
@@ -69,7 +164,6 @@ def config_get(key, default=None):
69
164
raise KeyError (f"Key '{ key } ' not found in configuration." )
70
165
71
166
72
-
73
167
def config_get_prefix (key_prefix ):
74
168
"""
75
169
Get all configuration values that start with a given prefix.
@@ -91,7 +185,6 @@ def config_get_prefix(key_prefix):
91
185
}
92
186
93
187
94
-
95
188
def config_set (key , value ):
96
189
"""
97
190
Set a configuration value.
@@ -131,9 +224,8 @@ def get_device_custom_name(mac_address):
131
224
return device_custom_name
132
225
133
226
134
-
135
227
@functools .cache
136
- def get_sql_query (sql_file_name ) :
228
+ def get_sql_query (sql_file_name : str ) -> str :
137
229
"""
138
230
Get the SQL query from a file.
139
231
0 commit comments