Skip to content

Client Server Communication

Bubori Attila edited this page Aug 11, 2026 · 2 revisions

Client-Server Communication

BigLan clients (Windows BigLanService) talk to the server over two independent channels, each with its own direction, port, and authentication mechanism:

graph LR
    C[BigLanService client] -- "HTTPS :443<br/>client → server<br/>API Token" --> S[BigLan server]
    S -- "TCP :8080<br/>server → client<br/>per-workstation AES key" --> C
Loading
Status/Event Reporting Console (Remote Commands)
Port 443 (HTTPS) 8080 (TCP)
Initiated by Client Server
Auth API Token (shared secret) Per-workstation AES key (ws_keys)
Endpoint POST /api/v2ApiController::payload() WorkstationsController::command()

1. Status & Event Reporting (Port 443, API Token)

Endpoint

All client → server traffic goes through a single endpoint: POST /api/v2, handled by ApiController::payload().

Authentication

Every request must include a token field (the plaintext API Token). This is checked in ApiController::__construct(), before payload() runs — meaning if the token check fails, none of the event/action logic below ever executes:

  1. hash('sha256', $plainToken) is compared against api_tokens.token_hash
  2. Rejected ("ERROR") if: token not found, is_active = 0, or expires_at is in the past (which also deactivates it)
  3. If max_uses is set, uses_count is incremented; once it reaches max_uses, the token is deactivated
  4. last_used_at is updated on every valid request
  5. One-time binding: on the first request that includes a real wsid, the token is bound to that workstation (tokenable_type = "Workstations", tokenable_id = $wsid). This binding is not updated again afterwards — if the same token is shared across many workstations (e.g. a bulk-deployment token), tokenable_id only ever reflects the first machine that used it, not all of them.

Note: the API Token authenticates a sender, not a specific machine. Workstation identity is established independently — see Workstation Identification below.

Request dispatch

payload() branches on two possible top-level fields:

  • action — used for the Command Center polling flow (getWaitingCommandsCounter, getCommand, saveCommandResult, update)
  • event — used for all state/status reporting (heartbeat, boot, shutdown, etc.)

For event-type requests, unless the event is in a fixed exemption list (identification, basic, refresh, heartbeat, external, print, register, key exchange), it is first logged via storeEvent() (feeds the workstation's Events tab), and then dispatched:

Event(s) sent by client Handler method Purpose
identification identifyWorkstation() Matches/creates the workstation by hardware identifiers — see below
key exchange keyExchange() Stores the client's AES key for the Console channel — see Section 2
basic refresh() Full hardware/OS inventory refresh
external external() Dispatches to sub-savers: IPs, DNS, user accounts, printers, monitors, memory, disks (see saveIPAddresses(), saveUserAccounts(), etc.)
shutdown, service stopped, suspend shutdown() Marks workstation offline, resets connection flags
boot, resume boot() Marks workstation online, sets startup_at
lock, unlock, logon, logoff idle() Toggles the idle flag
heartbeat heartbeat() Updates heartbeat timestamp (sent every minute)
anydesk connected/disconnected anydesk() Toggles AnyDesk security-risk flag
teamviewer connected/disconnected teamviewer() Toggles TeamViewer security-risk flag
vnc connected/disconnected vnc() Toggles VNC security-risk flag
usb connected/removed usb() Increments/decrements the connected-USB-storage counter
print printing() Records a print job/statistic

Workstation Identification

identifyWorkstation($uuid, $board, $product, $mac, $hostname) is the mechanism that decides which database row a client's data belongs to — independent of the API Token used:

  1. Four identifiers are validated by regex: uuid, product_serial, mboard_serial, first_mac
  2. Each is looked up individually against existing workstations rows
  3. If any single identifier matches exactly one existing row, that row's ID is reused (with a score recording how many of the 4 identifiers matched, out of 4 = 100%)
  4. If none match any existing row, a new workstations row is created
  5. If some identifiers match zero rows and none match exactly, the request is rejected ("ERROR") — this is the "at least 25% must be unique" rule from the User Guide

2. Console — Remote Command Execution (Port 8080, per-workstation AES key)

Unlike Section 1, this channel is server-initiated: the server opens an outbound TCP connection to the workstation. It does not use the API Token at all — it uses a separate, per-workstation AES-256 key.

Key exchange (setup)

sequenceDiagram
    participant C as BigLanService client
    participant S as BigLan server

    Note over C: On service start, client generates<br/>a random 32-char password
    C->>S: event: "key exchange" (token, wsid, key=password)
    Note over S: keyExchange() in ApiController
    S-->>S: Encrypt password with MASTER_KEY,<br/>store in ws_keys (unique per wsid)
    S-->>C: OK
Loading
  • Client-side: Service1.cs, static password field (generated once per service run) → AES key = SHA256(password), used by the client's own TcpListener on port 8080
  • Server-side: ApiController::keyExchange() — stores the password (not the derived key) encrypted with MASTER_KEY in ws_keys.encryption_key, one row per wsid (unique constraint)

Sending a command

sequenceDiagram
    participant U as Admin (Console UI)
    participant S as BigLan server
    participant C as BigLanService client

    U->>S: Submit command (Workstation → Console tab)
    Note over S: WorkstationsController::command()
    S-->>S: Load ws_keys row for wsid, decrypt with MASTER_KEY
    S-->>S: Derive AES key = SHA256(password),<br/>encrypt command (fixed IV)
    S->>C: fsockopen(workstation_ip, 8080), send encrypted command
    C-->>C: Decrypt, execute via cmd/PowerShell
    C-->>S: Encrypted result
    S-->>U: Display result in Console UI
Loading
  • Requires write-workstation-command permission
  • Every command attempt is logged to ws_control_logs (WsControlLog) regardless of outcome
  • The Console tab is only shown at all if the workstation's IP falls inside a subnet registered under IP Table → New Subnet — see Known Limitations
  • Connection timeout is 15 seconds (fsockopen 4th argument); if the workstation's OS firewall silently drops the connection (the common case with the Windows Firewall, which doesn't send a "connection refused" but just drops the packet), the request will visibly hang for close to 15 seconds before failing

Known caveats

  • The AES IV is a fixed constant (0x30 × 16) on both the client and server side, not randomized per message — see Known Limitations for the security implication
  • This channel requires the workstation to be reachable on the same registered subnet as the server (LAN only) and the Windows Firewall must allow inbound TCP 8080 on the client — neither is automatic; the installer does not create a firewall rule
  • Because this channel is independent of the API Token, it still works even if the workstation's API Token has been revoked — it can be used to push a new token remotely via the Change-Token console command

Clone this wiki locally