Skip to content

Commit fa3266a

Browse files
committed
Integrate with Device Detection LLM API
1 parent 609af10 commit fa3266a

File tree

1 file changed

+99
-7
lines changed

1 file changed

+99
-7
lines changed

common.py

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,112 @@
11
import datetime
2+
import os
23
import time
34
import threading
45
import json
56
import functools
6-
7+
import requests
8+
import libinspector
79

810
config_file_name = 'config.json'
911
config_lock = threading.Lock()
1012
config_dict = {}
1113

1214

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+
13110
def get_human_readable_time(timestamp=None):
14111
"""
15112
Convert a timestamp to a human-readable time format.
@@ -25,7 +122,6 @@ def get_human_readable_time(timestamp=None):
25122
return datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
26123

27124

28-
29125
def initialize_config_dict():
30126
"""
31127
Initialize the configuration dictionary by reading from the config file.
@@ -43,7 +139,6 @@ def initialize_config_dict():
43139
config_dict['app_start_time'] = time.time()
44140

45141

46-
47142
def config_get(key, default=None):
48143
"""
49144
Get a configuration value.
@@ -69,7 +164,6 @@ def config_get(key, default=None):
69164
raise KeyError(f"Key '{key}' not found in configuration.")
70165

71166

72-
73167
def config_get_prefix(key_prefix):
74168
"""
75169
Get all configuration values that start with a given prefix.
@@ -91,7 +185,6 @@ def config_get_prefix(key_prefix):
91185
}
92186

93187

94-
95188
def config_set(key, value):
96189
"""
97190
Set a configuration value.
@@ -131,9 +224,8 @@ def get_device_custom_name(mac_address):
131224
return device_custom_name
132225

133226

134-
135227
@functools.cache
136-
def get_sql_query(sql_file_name):
228+
def get_sql_query(sql_file_name: str) -> str:
137229
"""
138230
Get the SQL query from a file.
139231

0 commit comments

Comments
 (0)