Skip to content

Commit 2d75c99

Browse files
committed
6.32 By EbraSha
Improving UDP traffic forwarding performance
1 parent 987e2df commit 2d75c99

File tree

4 files changed

+336
-11
lines changed

4 files changed

+336
-11
lines changed

Core/go.mod

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ module Abdal_4iProto_Client
33
go 1.24
44

55
require (
6-
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 // indirect
6+
github.com/charmbracelet/bubbletea v1.3.5
7+
github.com/charmbracelet/lipgloss v1.1.0
8+
github.com/google/gopacket v1.1.19
9+
golang.org/x/crypto v0.39.0
10+
golang.org/x/net v0.21.0
11+
)
12+
13+
require (
714
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
8-
github.com/charmbracelet/bubbletea v1.3.5 // indirect
915
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
10-
github.com/charmbracelet/lipgloss v1.1.0 // indirect
1116
github.com/charmbracelet/x/ansi v0.8.0 // indirect
1217
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
1318
github.com/charmbracelet/x/term v0.2.1 // indirect
@@ -21,8 +26,6 @@ require (
2126
github.com/muesli/termenv v0.16.0 // indirect
2227
github.com/rivo/uniseg v0.4.7 // indirect
2328
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
24-
golang.org/x/crypto v0.39.0 // indirect
25-
golang.org/x/net v0.21.0 // indirect
2629
golang.org/x/sync v0.15.0 // indirect
2730
golang.org/x/sys v0.33.0 // indirect
2831
golang.org/x/text v0.26.0 // indirect

Core/go.sum

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
2-
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
31
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
42
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
53
github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
@@ -16,6 +14,8 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ
1614
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
1715
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
1816
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
17+
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
18+
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
1919
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
2020
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
2121
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -35,15 +35,31 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
3535
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
3636
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
3737
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
38+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
39+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
3840
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
3941
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
42+
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
43+
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
44+
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
45+
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
46+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
47+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
4048
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
4149
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
50+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
4251
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
4352
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
53+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
54+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4455
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4556
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
4657
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
4758
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
59+
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
60+
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
61+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
4862
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
4963
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
64+
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
65+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

Core/ip_defrag.go

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
/*
2+
**********************************************************************
3+
* -------------------------------------------------------------------
4+
* Project Name : Abdal 4iProto Client
5+
* File Name : ip_defrag.go
6+
* Author : Ebrahim Shafiei (EbraSha)
7+
* Email : Prof.Shafiei@Gmail.com
8+
* Created On : 2025-12-14 15:17:47
9+
* Description : IP packet defragmentation module using gopacket for handling fragmented UDP packets in SOCKS5 proxy
10+
* -------------------------------------------------------------------
11+
*
12+
* "Coding is an engaging and beloved hobby for me. I passionately and insatiably pursue knowledge in cybersecurity and programming."
13+
* – Ebrahim Shafiei
14+
*
15+
**********************************************************************
16+
*/
17+
18+
package main
19+
20+
import (
21+
"fmt"
22+
"net"
23+
"os"
24+
"runtime"
25+
"sync"
26+
"syscall"
27+
28+
"github.com/google/gopacket"
29+
"github.com/google/gopacket/ip4defrag"
30+
"github.com/google/gopacket/layers"
31+
"golang.org/x/net/ipv4"
32+
)
33+
34+
// IP_HDRINCL constant for Windows (value is 2)
35+
const IP_HDRINCL = 2
36+
37+
// IPDefragmenter handles IP packet defragmentation for UDP packets
38+
type IPDefragmenter struct {
39+
defragger *ip4defrag.IPv4Defragmenter
40+
mu sync.Mutex
41+
}
42+
43+
// NewIPDefragmenter creates a new IP defragmenter instance
44+
func NewIPDefragmenter() *IPDefragmenter {
45+
return &IPDefragmenter{
46+
defragger: ip4defrag.NewIPv4Defragmenter(),
47+
}
48+
}
49+
50+
// DefragmentIPPacket processes an IP packet and returns the defragmented packet if complete
51+
func (d *IPDefragmenter) DefragmentIPPacket(ipPacket []byte) ([]byte, error) {
52+
d.mu.Lock()
53+
defer d.mu.Unlock()
54+
55+
// Parse the IP packet
56+
packet := gopacket.NewPacket(ipPacket, layers.LayerTypeIPv4, gopacket.Default)
57+
ipLayer := packet.Layer(layers.LayerTypeIPv4)
58+
if ipLayer == nil {
59+
return nil, fmt.Errorf("not an IPv4 packet")
60+
}
61+
62+
ip, _ := ipLayer.(*layers.IPv4)
63+
64+
// Check if packet is fragmented
65+
isFragmented := (ip.Flags&layers.IPv4MoreFragments != 0) || (ip.FragOffset > 0)
66+
if !isFragmented {
67+
// Not fragmented, return as is
68+
return ipPacket, nil
69+
}
70+
71+
// Defragment the packet
72+
defragged, err := d.defragger.DefragIPv4(ip)
73+
if err != nil {
74+
return nil, fmt.Errorf("defragmentation error: %w", err)
75+
}
76+
77+
if defragged == nil {
78+
// Packet is still being assembled, return nil to indicate incomplete
79+
return nil, nil
80+
}
81+
82+
// Serialize the defragmented packet
83+
buf := gopacket.NewSerializeBuffer()
84+
opts := gopacket.SerializeOptions{
85+
FixLengths: true,
86+
ComputeChecksums: true,
87+
}
88+
err = defragged.SerializeTo(buf, opts)
89+
if err != nil {
90+
return nil, fmt.Errorf("failed to serialize defragmented packet: %w", err)
91+
}
92+
93+
return buf.Bytes(), nil
94+
}
95+
96+
// UDPPacketReader reads UDP packets from a raw socket with IP defragmentation support
97+
type UDPPacketReader struct {
98+
defragger *IPDefragmenter
99+
rawConn *ipv4.PacketConn
100+
udpConn *net.UDPConn
101+
useRaw bool
102+
localUDPPort int
103+
}
104+
105+
// NewUDPPacketReader creates a new UDP packet reader with defragmentation support
106+
// Attempts to use raw socket for IP-level packet capture, falls back to regular UDP if unavailable
107+
// Note: Raw sockets require administrator privileges on Windows
108+
func NewUDPPacketReader(udpConn *net.UDPConn) (*UDPPacketReader, error) {
109+
reader := &UDPPacketReader{
110+
defragger: NewIPDefragmenter(),
111+
udpConn: udpConn,
112+
useRaw: false,
113+
localUDPPort: udpConn.LocalAddr().(*net.UDPAddr).Port,
114+
}
115+
116+
// Try to create a raw socket for IP-level packet capture
117+
// This requires administrator privileges on Windows
118+
rawSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_IP)
119+
if err != nil {
120+
// Raw socket not available (likely no admin privileges), fall back to regular UDP
121+
// OS will handle defragmentation, but we can't intercept fragmented packets
122+
return reader, nil
123+
}
124+
125+
// Set socket options to receive IP headers
126+
// Use platform-specific constant for IP_HDRINCL
127+
var ipHdrIncl int
128+
if runtime.GOOS == "windows" {
129+
ipHdrIncl = IP_HDRINCL
130+
} else {
131+
// On Unix systems, try to use syscall.IP_HDRINCL if available
132+
// If not available, we'll skip this option
133+
ipHdrIncl = 1 // Try a generic value, may not work on all systems
134+
}
135+
136+
err = syscall.SetsockoptInt(rawSocket, syscall.IPPROTO_IP, ipHdrIncl, 1)
137+
if err != nil {
138+
syscall.Close(rawSocket)
139+
return reader, nil
140+
}
141+
142+
// Create a file from the socket descriptor
143+
file := os.NewFile(uintptr(rawSocket), "raw-socket")
144+
if file == nil {
145+
syscall.Close(rawSocket)
146+
return reader, nil
147+
}
148+
149+
// Create a packet connection from the file (not regular connection)
150+
packetConn, err := net.FilePacketConn(file)
151+
file.Close() // Close the file, we have the connection now
152+
if err != nil {
153+
syscall.Close(rawSocket)
154+
return reader, nil
155+
}
156+
157+
// Create ipv4.PacketConn from the packet connection
158+
rawConn := ipv4.NewPacketConn(packetConn)
159+
if rawConn == nil {
160+
packetConn.Close()
161+
return reader, nil
162+
}
163+
164+
reader.rawConn = rawConn
165+
reader.useRaw = true
166+
return reader, nil
167+
}
168+
169+
// ReadUDPPacket reads a complete UDP packet, handling fragmentation if needed
170+
func (r *UDPPacketReader) ReadUDPPacket() ([]byte, *net.UDPAddr, error) {
171+
if r.useRaw && r.rawConn != nil {
172+
// Use raw socket with defragmentation
173+
buf := make([]byte, 65535)
174+
for {
175+
n, cm, src, err := r.rawConn.ReadFrom(buf)
176+
if err != nil {
177+
return nil, nil, err
178+
}
179+
180+
// Extract source IP from control message or address
181+
var srcIP net.IP
182+
if cm != nil && cm.Src != nil {
183+
// cm.Src is net.IP (which is []byte)
184+
srcIP = cm.Src
185+
} else if src != nil {
186+
if ipAddr, ok := src.(*net.IPAddr); ok {
187+
srcIP = ipAddr.IP
188+
} else if udpAddr, ok := src.(*net.UDPAddr); ok {
189+
srcIP = udpAddr.IP
190+
}
191+
}
192+
193+
// Parse IP packet
194+
packet := gopacket.NewPacket(buf[:n], layers.LayerTypeIPv4, gopacket.Default)
195+
ipLayer := packet.Layer(layers.LayerTypeIPv4)
196+
if ipLayer == nil {
197+
continue
198+
}
199+
200+
ip, _ := ipLayer.(*layers.IPv4)
201+
202+
// Use source IP from packet if not available from control message
203+
if srcIP == nil {
204+
srcIP = ip.SrcIP
205+
}
206+
207+
// Check if packet is fragmented
208+
isFragmented := (ip.Flags&layers.IPv4MoreFragments != 0) || (ip.FragOffset > 0)
209+
if isFragmented {
210+
// Defragment the packet
211+
defragged, err := r.defragger.DefragmentIPPacket(buf[:n])
212+
if err != nil {
213+
// Error during defragmentation, skip this packet
214+
continue
215+
}
216+
if defragged == nil {
217+
// Still assembling, skip this packet and wait for more fragments
218+
continue
219+
}
220+
221+
// Re-parse the defragmented packet
222+
packet = gopacket.NewPacket(defragged, layers.LayerTypeIPv4, gopacket.Default)
223+
ipLayer = packet.Layer(layers.LayerTypeIPv4)
224+
if ipLayer == nil {
225+
continue
226+
}
227+
ip, _ = ipLayer.(*layers.IPv4)
228+
}
229+
230+
// Extract UDP layer
231+
udpLayer := packet.Layer(layers.LayerTypeUDP)
232+
if udpLayer == nil {
233+
continue
234+
}
235+
236+
udp, _ := udpLayer.(*layers.UDP)
237+
238+
// Check if this UDP packet is for our port
239+
if int(udp.DstPort) != r.localUDPPort {
240+
continue
241+
}
242+
243+
// Extract source address
244+
clientAddr := &net.UDPAddr{
245+
IP: srcIP,
246+
Port: int(udp.SrcPort),
247+
}
248+
249+
// Return the UDP payload (SOCKS5 packet)
250+
return udp.Payload, clientAddr, nil
251+
}
252+
}
253+
254+
// Fall back to regular UDP socket reading
255+
// The OS should handle defragmentation, but we'll use larger buffer
256+
buf := make([]byte, 65535)
257+
n, clientAddr, err := r.udpConn.ReadFromUDP(buf)
258+
if err != nil {
259+
return nil, nil, err
260+
}
261+
return buf[:n], clientAddr, nil
262+
}
263+
264+
// Close closes the packet reader
265+
func (r *UDPPacketReader) Close() error {
266+
if r.rawConn != nil {
267+
r.rawConn.Close()
268+
}
269+
return nil
270+
}

0 commit comments

Comments
 (0)