Skip to main content

ObfuscatorServer

ObfuscatorServer is the server-side peer for ObfuscatorClient. It uses the same reversible payload transform and must be configured with the same method, key, and header options.

In the current implementation, the only supported method is XOR. This node is a traffic-shaping and obfuscation layer, not an encryption layer.

The option-off path remains stateless. TLS-like framing uses normal-line receive state for stream reassembly, while exact packet lines remain stateless.

Typical Placement​

Simple TCP path:

client side: TcpListener -> ObfuscatorClient -> TcpConnector
server side: TcpListener -> ObfuscatorServer -> TcpConnector

UDP path:

client side: UdpListener -> ObfuscatorClient -> UdpConnector
server side: UdpListener -> ObfuscatorServer -> UdpConnector

Raw packet path:

packet transport -> ObfuscatorServer -> RawSocket

Use the same skip and tls_record_header settings as the client side.

What It Does​

  • Requires settings.method: "xor".
  • Requires settings.xor_key.
  • Applies XOR to upstream payload bytes before forwarding to next.
  • Optionally prepends a 5-byte TLS-like record header after upstream XOR.
  • Optionally strips the 5-byte TLS-like header before downstream XOR.
  • Applies XOR to downstream payload bytes before forwarding to the previous node.
  • Can leave the IPv4 header, or IPv4 plus TCP/UDP header, unobfuscated.
  • Marks packet-line payloads for checksum recalculation after changing bytes.
  • Keeps bounded receive state only for ordinary streams with TLS-like framing enabled.

The server implementation mirrors the client implementation. The client/server names describe deployment role; the transform itself is symmetric.

Configuration Example​

{
"name": "obfuscator-server",
"type": "ObfuscatorServer",
"settings": {
"method": "xor",
"xor_key": 90,
"skip": "transport",
"tls_record_header": true
},
"next": "service-node"
}

Matching client:

{
"name": "obfuscator-client",
"type": "ObfuscatorClient",
"settings": {
"method": "xor",
"xor_key": 90,
"skip": "transport",
"tls_record_header": true
},
"next": "transport-node"
}

Required Fields​

Top-level fields:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "ObfuscatorServer".
settingsobjectRequired. Must contain method and xor_key.
nextstringRequired in normal use. The next node receives transformed upstream payloads.

Required settings fields:

FieldTypeDescription
methodstringMust be "xor" in the current implementation.
xor_keyintegerXOR key. The value is stored as one byte, so values outside 0..255 are converted to uint8_t.

Optional Settings​

SettingTypeDefaultDescription
skipstring"none"Leading packet headers to leave clear before XOR. Accepted values are "none", "ipv4", and "transport".
tls_record_headerbooleanfalseAdds/removes a 5-byte TLS-like application-data record header on the transport-facing payload.
tls_headerbooleanunsetDeprecated compatibility alias for tls_record_header. If present, it is accepted and a warning is logged.

Skip Modes​

skip controls how many leading bytes are not XORed:

ValueBehavior
"none"XOR the whole payload.
"ipv4"If the payload starts with a valid IPv4 packet, leave the IPv4 header clear and XOR bytes after it.
"transport"If the payload starts with a valid IPv4 TCP/UDP packet, leave the IPv4 header and TCP/UDP header clear. For other IPv4 protocols, leave only the IPv4 header clear.

If the payload is too short or not a valid IPv4 packet, the skip length becomes 0 and the whole payload is XORed.

For malformed or incomplete TCP/UDP headers under skip: "transport", the implementation falls back to leaving only the IPv4 header clear.

TLS-Like Record Header​

When tls_record_header is enabled, transport-facing payloads are sent as:

17 03 03 + length(2 bytes, big-endian) + obfuscated payload

The bytes imitate a TLS application-data record header:

FieldValue
content type23 / 0x17
legacy version0x0303
lengthobfuscated payload length, 16-bit big-endian

On the receive path, the header is validated and stripped before XOR runs.

warning

This is not TLS. It has no handshake, no certificates, no SNI, no ALPN, no encryption, and no authentication. It only imitates the shape of a post-handshake TLS application-data record.

Stream framing behavior:

  • Split large deliveries into records of at most 65,535 body bytes and apply XOR/skip per record.
  • Reassemble fragmented headers/bodies and parse coalesced records, retaining partial tails.
  • Send one encoded or decoded aggregate per batch, preserving FIFO without waiting for Resume.
  • Forward every Pause/Resume in both directions, including repeats; retain only incomplete records.
  • Bound retained input by a fixed 2,162,692 bytes (2 MiB + 65,540), independent of pool sizes, RAM profile and splice settings. The 65,535-byte record-body limit is separate.
  • Close malformed or overflowing ordinary streams and release state on Finish.

Exact packet lines still require one complete record per packet. Truncated, mismatched, malformed, or oversized datagrams retain the existing drop policy; this mode adds no datagram fragmentation or cross-packet reassembly.

Direction Behavior​

Incoming callbackBehavior
upstream PayloadXORs according to skip, optionally prepends the TLS-like header, marks packet-line checksum recalculation, then forwards to next.
downstream PayloadOptionally validates and strips the TLS-like header, XORs according to skip, marks packet-line checksum recalculation, then forwards to the previous node.
Init in either directionInitializes normal-line receive state when framing is enabled, then forwards in the same direction.
Finish in either directionReleases normal-line receive state, then forwards away from the sender.
Est, Pause, ResumeForwards transparently in the same direction, including repeats.

Exact packet lines carry no receive state. For framed normal lines, protocol or admission failure destroys local state, finishes the opposite side, then the source side recorded during Init. The nodes remain active for XOR and record processing on every payload; they do not permanently bypass themselves.

Buffer And Padding Notes​

Node metadata advertises:

required_padding_left = 5
layer_group = kNodeLayerAnything

The 5 bytes are for the optional TLS-like header. If a buffer does not have enough left padding when tls_record_header is enabled, the node clones the payload into a new buffer with enough padding, recycles the original buffer, and then prepends the header.

When the current line is the worker packet line, the node sets sbufSetRecalculateChecksum(buf, true) on the surviving buffer after transforming payload bytes.

Implementation Notes​

The XOR helper has optimized paths:

  • AVX2 when available
  • aligned 64-bit chunk processing on 64-bit builds
  • aligned 32-bit or byte-wise fallback otherwise

These paths change performance only; visible behavior is still byte-wise XOR with the configured one-byte key.

Practical Notes​

  • ObfuscatorServer and ObfuscatorClient must use the same method, xor_key, skip, and tls_record_header settings.
  • This is obfuscation, not cryptographic security.
  • skip: "none" is simplest for ordinary byte streams.
  • skip: "transport" is useful for raw IPv4 TCP/UDP packets whose IP and transport headers need to remain readable.
  • If you only need secure encryption, use EncryptionClient and EncryptionServer instead.

Node Metadata​

Source-backed metadata:

This node explicitly disables splice for the entire chain through kNodeFlagBlocksSplice, even if kNodeFlagSupportsSplice is also set.

PropertyValue
node flagskNodeFlagBlocksSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayerAnything
layer_group_prev_nodekNodeLayerSameAsNext
layer_group_next_nodekNodeLayerSameAsPrev
required_padding_left5 bytes