# How the Shadowsocks Windows UDP Relay Handles DNS Queries and Packet Forwarding

> Learn how the Shadowsocks Windows UDP relay encrypts and forwards DNS queries and UDP packets to a remote server for resolution and return, ensuring secure tunneling. Get the details here.

- Repository: [shadowsocks/shadowsocks-windows](https://github.com/shadowsocks/shadowsocks-windows)
- Tags: internals
- Published: 2026-03-05

---

**Shadowsocks-Windows forwards all UDP traffic—including DNS queries—through its `UDPRelay` service by encrypting payloads via `IEncryptor` and tunneling them to a remote Shadowsocks server, which resolves the destination and returns responses through the same encrypted path.**

The shadowsocks-windows repository implements a transparent UDP relay that processes DNS queries as standard UDP datagrams. In [`shadowsocks-csharp/Controller/Service/UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/UDPRelay.cs), the `UDPRelay` class coordinates with `Listener` and `UDPHandler` to encrypt, forward, and decrypt packets without distinguishing between DNS and other UDP traffic.

## UDP Relay Architecture Components

The UDP relay system consists of three primary components working in sequence:

1. **Listener** ([`shadowsocks-csharp/Controller/Service/Listener.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/Listener.cs)): Accepts raw UDP packets from local SOCKS5 clients and dispatches them to registered services.

2. **UDPRelay** ([`shadowsocks-csharp/Controller/Service/UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/UDPRelay.cs)): Creates and manages `UDPHandler` instances for each client session, coordinating encryption and remote transmission.

3. **IEncryptor** ([`shadowsocks-csharp/Encryption/IEncryptor.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Encryption/IEncryptor.cs)): Provides `EncryptUDP` and `DecryptUDP` methods used by handlers to secure payloads using algorithms like **AES-128-CBC** or **ChaCha20-Poly1305**.

## The UDP Packet Forwarding Flow

### Step 1: Local Listener Receipt

When a SOCKS5 client sends the first UDP packet following a **UDP-ASSOCIATE** request, the `Listener` creates a `UDPState` object and iterates through registered services until `UDPRelay.Handle` returns `true`. This dispatch logic appears in [`Listener.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Listener.cs) at lines 14-30 and 88-90.

The `Handle` method in [`UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/UDPRelay.cs) (lines 28-48) checks for an existing `UDPHandler` for the client's IP/port or creates a new one, then passes the raw packet for processing.

### Step 2: Handler Encryption and Remote Transmission

Inside `UDPHandler.Send` (lines 96-106 of [`UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/UDPRelay.cs)), the relay strips the first three bytes of the SOCKS5 UDP header and encrypts the remaining payload:

```csharp
// Inside UDPHandler.Send – encrypt + forward
byte[] dataIn  = new byte[length - 3];                // strip SOCKS5 header
Array.Copy(data, 3, dataIn, 0, length - 3);
byte[] dataOut = new byte[65536];
int outlen;
encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen);
_remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint);

```

The encrypted data is sent to the remote Shadowsocks server endpoint defined in [`Server.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Server.cs), which decrypts the packet and forwards it to the actual destination—whether a DNS resolver (port 53) or any other UDP service.

### Step 3: DNS Resolution at the Remote Server

DNS queries receive no special treatment in the client-side code. The remote Shadowsocks server receives the encrypted DNS query, performs the lookup (or forwards it upstream), and transmits the response back through the same UDP socket.

## Return Path and Response Decryption

When the reply arrives from the remote server, `UDPHandler.RecvFromCallback` (lines 119-135 of [`UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/UDPRelay.cs)) processes the incoming data:

```csharp
// Inside UDPHandler.RecvFromCallback – decrypt + return
int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint);
byte[] dataOut = new byte[bytesRead];
encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen);

byte[] sendBuf = new byte[outlen + 3];               // re‑add SOCKS5 header
Array.Copy(dataOut, 0, sendBuf, 3, outlen);
_local?.SendTo(sendBuf, outlen + 3, 0, _localEndPoint);

```

The method decrypts the payload using `IEncryptor.DecryptUDP`, restores the three-byte SOCKS5 UDP header, and transmits the result to the original local client via `_local.SendTo`.

## Why DNS Queries Work Transparently

Because the UDP relay operates at the transport layer without inspecting packet contents, DNS queries (typically sent to port 53) flow through the same encryption pipeline as any other UDP traffic. The SOCKS5 client—whether a browser, `dig` command, or system resolver—sends a standard UDP-ASSOCIATE request, and the relay encrypts and tunnels the query without modification.

## Summary

- **All UDP traffic is equal**: The `UDPRelay` treats DNS queries identically to other UDP packets, providing uniform encryption and forwarding.
- **Three-layer architecture**: `Listener` accepts packets, `UDPRelay` manages handlers, and `UDPHandler` performs encryption/decryption via `IEncryptor`.
- **Header manipulation**: The first three bytes of the SOCKS5 UDP header are stripped before encryption and restored after decryption.
- **Remote resolution**: The Shadowsocks server handles actual DNS resolution; the Windows client merely tunnels the request.
- **Stateful handlers**: Each client IP/port combination gets a dedicated `UDPHandler` instance to maintain session state.

## Frequently Asked Questions

### Does Shadowsocks-Windows treat DNS queries differently from other UDP traffic?

No. According to the source code in [`UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/UDPRelay.cs), DNS queries are processed as standard UDP packets. The relay strips the SOCKS5 header, encrypts the payload, and forwards it to the remote server without inspecting the destination port or payload content.

### What encryption methods does the UDP relay support?

The relay supports all methods defined in [`IEncryptor.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/IEncryptor.cs) and implemented in [`EncryptorFactory.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/EncryptorFactory.cs), including AES-128-CBC, AES-256-GCM, and ChaCha20-Poly1305. The specific method is determined by the server configuration and applied via `EncryptUDP` and `DecryptUDP` calls in `UDPHandler`.

### Where is the SOCKS5 UDP header stripped and re-added?

In [`shadowsocks-csharp/Controller/Service/UDPRelay.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/UDPRelay.cs), the `UDPHandler.Send` method (lines 96-106) strips the first three bytes before encryption, while `UDPHandler.RecvFromCallback` (lines 119-135) re-adds them after decryption and before returning the packet to the local client.

### How does the relay handle multiple concurrent UDP connections?

The `UDPRelay.Handle` method maintains a dictionary of `UDPHandler` instances keyed by client IP and port. When a packet arrives from a new endpoint, it creates a fresh handler; existing endpoints reuse their handler, allowing the relay to manage multiple concurrent UDP associations simultaneously.