A fast, modern anti-censorship VPN client for iOS featuring DNS tunneling with support for multiple protocols. Built with Flutter and a Go tunnel core bridged via gomobile.
DNSly is a legitimate anti-censorship tool designed to help users in countries with internet censorship access the free internet. It is comparable to Tor, Psiphon, and dnstt. This project does not target, exploit, or attack any systems — it is a client-side privacy tool used voluntarily by end users.
Join us for updates, support, and discussions:
If DNSly has been useful to you, consider supporting development:
| Bitcoin (BTC) | Tether (USDT — TRC20) |
|---|---|
bc1qq7hxnfvr0gn7cfd5h8dskgk0mhrmuleqnmgylx |
TQTpqyqXsaTM57xoHV3mTsFU3vntZGtxFW |
DNSly supports multiple tunnel types with optional chaining:
| Tunnel Type | Protocol | Description |
|---|---|---|
| VayDNS | DNS/TXT | Optimized DNS tunneling — encodes TCP as DNS queries |
| VayDNS + SSH | DNS + SSH | VayDNS with SSH chaining for extra encryption |
| VayDNS + SOCKS5 | DNS + SOCKS5 | VayDNS with upstream SOCKS5 relay |
| SSH | SSH | Standalone SSH dynamic port forwarding |
| SOCKS5 Relay | SOCKS5 | Transparent passthrough to an upstream proxy |
DNS Transport Options:
| Mode | Port | Protocol |
|---|---|---|
| Classic UDP | 53 | Plain UDP |
| TCP | 53 | DNS over TCP |
| DoT | 853 | DNS-over-TLS (RFC 7858) |
| DoH | 443 | DNS-over-HTTPS POST (RFC 8484) |
- iOS VPN Integration: System-level packet interception via iOS NetworkExtension (PacketTunnel)
- Multiple Tunnel Types: VayDNS, SSH, SOCKS5, and hybrid combinations
- DNS Transport Selection: Choose UDP, TCP, DoT, or DoH for DNS resolution
- SSH Tunneling: Chain SSH through VayDNS or use standalone SSH dynamic port forwarding
- SSH Key Auth: Authenticate with password or PEM private key
- SSH Cipher Selection: Choose between AES-256-GCM, AES-128-GCM, and ChaCha20-Poly1305
- Multiple Profiles: Create and manage multiple server configurations
- QR Import/Export: Share profiles as QR codes — useful when internet is restricted
- Encrypted Profiles: Password-protect profile exports with AES-256-CBC encryption; also import upstream
slipnet-enc://AES-256-GCM payloads - DNS Scanner: Parallel resolver latency testing to find the fastest working DNS server
- Real-time Stats: Live bytes in/out, uptime, and latency tracking
- Profile Server: Built-in Go HTTP server for distributing profiles to devices
- SlipNet Codec: Compatible with
slipnet://andslipnet-enc://URI formats - Dark Mode: Full system-wide dark theme support
- Cross-platform: Flutter codebase targets iOS (primary), Android, macOS, Linux, Windows
Device traffic is captured by Tun2Socks via the iOS PacketTunnel extension and routed to a local SOCKS5 listener. The Go core handles the tunnel:
iOS PacketTunnel Extension
↓
Tun2Socks (userspace)
↓
Local SOCKS5 (127.0.0.1:PORT) ← Go tunnel engine
↓
┌────┴────────────────────┐
│ TunnelType selector │
└────┬──────┬──────┬──────┘
│ │ │
VayDNS SSH SOCKS5
Tunnel Proxy Relay
Each TCP connection is assigned a random 4-byte session ID. Data is chunked, Base32-encoded, and embedded in DNS QNAMEs:
Upload query QNAME:
{base32(sid[4] ‖ seq[4] ‖ [isSyn:1] ‖ payload)}.d.{domain}.
Download poll QNAME:
{base32(sid[4] ‖ recvSeq[4])}.r.{domain}.
| Field | Size | Description |
|---|---|---|
sid |
4 bytes | Random session ID per TCP connection |
seq |
4 bytes | Big-endian uint32, increments per chunk |
isSyn |
1 byte | 0x01 on first chunk only (carries target address) |
payload |
≤120 bytes | Application data fragment |
Download polling interval: 80 ms
Profiles are exported in two formats:
Plain:
slipnet://tunnelType@host:port?name=...&domain=...&dnsTransport=...&...
Encrypted (SlipNet official AES-256-GCM):
slipnet-enc://BASE64_DATA
BASE64_DATA decodes to raw bytes:
- version byte:
0x01 - IV: 12-byte AES-GCM nonce
- ciphertext + tag: the remaining bytes are AES-GCM ciphertext followed by a 16-byte authentication tag
The payload is decrypted with AES-256-GCM using SlipNet's built-in 32-byte application key. In this official format, the password is not used to derive the AES key; it is only a usage-control check in the SlipNet app.
Legacy app-encrypted export:
slipnet-enc://base64(envelope)
envelope = {
"v": 2,
"iv": "<base64 16-byte random IV>",
"ct": "<base64 AES-256-CBC ciphertext>",
"meta": { name, server, domain, ... } ← plaintext preview fields
}
Key derivation: SHA256(password) → 32-byte AES key
This repository supports both the official upstream slipnet-enc:// AES-256-GCM format and the legacy password-protected envelope format.
- iOS 15.0 or higher
- Xcode 15 or later
- Flutter SDK (stable channel)
- Go 1.21+ with gomobile (
go install golang.org/x/mobile/cmd/gomobile@latest)
cd go
gomobile init
gomobile bind -target=ios -o ../ios/Frameworks/Tunnel.xcframework ./tunnelflutter pub getflutter run --releaseNote: The iOS PacketTunnel extension requires a real device with a paid Apple Developer account for VPN entitlements. The simulator can run the UI but the actual tunnel requires a device.
cd server
go build -o dnsly-server .
./dnsly-servergo/
tunnel/
tunnel.go # Transport selector, gomobile public API
dns_tunnel.go # VayDNS: UDP/TCP/DoH/DoT, chunking, polling
ssh_proxy.go # SSH dynamic port forwarding (-D equivalent)
socks_relay.go # SOCKS5 transparent relay
go.mod
server/
main.go # REST API, profile storage, iOS VPN config gen
go.mod
lib/
app.dart # Root widget, BLoC providers
models/
profile.dart # Core data model (all tunnel variants)
blocs/
connection/ # Connection lifecycle, stats, native bridge
profile/ # CRUD, import (plain + encrypted), activation
dns_scanner/ # Parallel resolver latency probe
screens/
home/ # Connection status, connect/disconnect
profiles/ # Profile list and management
dns_scanner/ # Resolver benchmark UI
settings/ # App settings
services/
profile_repository.dart # Hive persistence
vpn_platform_service.dart # MethodChannel / EventChannel iOS bridge
utils/
slipnet_codec.dart # Encode/decode, AES-CBC, legacy v1 compat
ios/
Runner/ # iOS app target
DNSly/ # PacketTunnel network extension
Frameworks/ # Built Go framework (Tunnel.xcframework)
test/
slipnet_enc_upstream_test.dart
upstream_slipnet_decoder_test.dart
golang.org/x/crypto— SSH, TLS, AEAD primitivesgolang.org/x/net— DNS packet handlinggolang.org/x/mobile— gomobile bind for iOS framework generation- Transport: UDP 53 · TCP 53 · DoT 853 · DoH 443
flutter_bloc ^9.1.1— BLoC state managementhive_flutter ^1.1.0+shared_preferences ^2.2.2— local persistencemobile_scanner ^7.2.0+qr_flutter ^4.1.0— QR import/exportencrypt ^5.0.3— AES-256-CBC profile encryptioncrypto ^3.0.6— SHA-256 key derivationcryptography ^2.7.0— additional cipher supportuuid ^4.2.1— profile ID generation
- Standard library only
- REST API + Bearer token auth
- iOS
NEVPNProtocolconfig generation
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is released under the MIT License. See LICENSE for details.





