WIP: backend udp

This commit is contained in:
2025-04-20 12:20:36 +10:00
parent d0b3cbf967
commit 7f69553ead
4 changed files with 395 additions and 7 deletions

223
bounce.go
View File

@@ -17,18 +17,32 @@ package netbounce
*/
import (
"context"
"fmt"
"net"
"sync"
"gitea.suyono.dev/suyono/netbounce/abstract"
"gitea.suyono.dev/suyono/netbounce/config"
"github.com/rs/zerolog/log"
)
func Bounce() error {
const (
CONNECTION = "connection"
DIRECTION = "direction"
CLIENT_TO_BACKEND = "client-to-backend"
BACKEND_TO_CLIENT = "backend-to-client"
BUF_SIZE = 4096
)
func Bounce(ctx context.Context) error {
var (
names []string
err error
cc abstract.ConnectionConfig
)
wg := &sync.WaitGroup{}
if names, err = config.ListConnection(); err != nil {
return fmt.Errorf("bounce: connection list: %w", err)
}
@@ -37,20 +51,219 @@ func Bounce() error {
return fmt.Errorf("bounce: connection get %s: %w", name, err)
}
wg.Add(1)
if cc.Type() == abstract.TCP {
go RunTCP(cc.AsTCP())
go RunTCP(wg, ctx, cc.AsTCP())
} else {
go RunUDP(cc.AsUDP())
go RunUDP(wg, ctx, cc.AsUDP())
}
}
return nil
}
func RunTCP(cfg abstract.TCPConnectionConfig) {
func RunTCP(wg *sync.WaitGroup, ctx context.Context, cfg abstract.TCPConnectionConfig) {
defer wg.Done()
var (
err error
l net.Listener
accepted chan net.Conn
)
if l, err = net.Listen("tcp", cfg.Listen()); err != nil {
panic(fmt.Errorf("failed to listen tcp: %w", err))
}
accepted = make(chan net.Conn)
go func(ctx context.Context, cfg abstract.TCPConnectionConfig, listener net.Listener, acceptChan chan<- net.Conn) {
defer log.Info().Msgf("go routine for accepting connection quited: %s", cfg.Name())
var (
err error
c net.Conn
)
acceptIncoming:
for {
if c, err = listener.Accept(); err != nil {
log.Error().Err(err).Msg("accept TCP connection")
continue
}
select {
case acceptChan <- c:
case <-ctx.Done():
break acceptIncoming
}
}
}(ctx, cfg, l, accepted)
connStarter:
for {
select {
case c := <-accepted:
wg.Add(1)
go makeTCPConnection(wg, ctx, cfg, c)
case <-ctx.Done():
break connStarter
}
}
}
func RunUDP(cfg abstract.UDPConnectionConfig) {
func makeTCPConnection(wg *sync.WaitGroup, ctx context.Context, cfg abstract.TCPConnectionConfig, conn net.Conn) {
defer wg.Done()
var (
backend net.Conn
err error
)
if backend, err = net.Dial("tcp", cfg.Backend()); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Msg("connection to backend failed")
return
}
ctxConn, cancelConn := context.WithCancel(ctx)
wg.Add(1)
go tcpClient2Backend(wg, ctxConn, cfg, cancelConn, conn, backend)
wg.Add(1)
go tcpBackend2Client(wg, ctxConn, cfg, cancelConn, backend, conn)
<-ctxConn.Done()
_ = backend.Close()
_ = conn.Close()
}
func tcpBackend2Client(wg *sync.WaitGroup, ctx context.Context, cfg abstract.TCPConnectionConfig, cf context.CancelFunc, backend, client net.Conn) {
defer wg.Done()
var (
buf []byte
n, wn int
cancel context.CancelFunc
err error
)
cancel = cf
buf = make([]byte, BUF_SIZE)
backendRead:
for {
if n, err = backend.Read(buf); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Str(DIRECTION, BACKEND_TO_CLIENT).Msg("tcp read error")
cancel()
break backendRead
}
select {
case <-ctx.Done():
break backendRead
default:
}
if wn, err = client.Write(buf[:n]); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Str(DIRECTION, BACKEND_TO_CLIENT).Msg("tcp read error")
cancel()
break backendRead
}
if wn != n {
log.Error().Err(fmt.Errorf("mismatch length between read and write")).Str(CONNECTION, cfg.Name()).Str(DIRECTION, BACKEND_TO_CLIENT).Msg("tcp read problem")
cancel()
break backendRead
}
select {
case <-ctx.Done():
break backendRead
default:
}
}
}
func tcpClient2Backend(wg *sync.WaitGroup, ctx context.Context, cfg abstract.TCPConnectionConfig, cf context.CancelFunc, client, backend net.Conn) {
defer wg.Done()
var (
buf []byte
n, wn int
cancel context.CancelFunc
err error
)
cancel = cf
buf = make([]byte, BUF_SIZE)
clientRead:
for {
if n, err = client.Read(buf); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Str(DIRECTION, CLIENT_TO_BACKEND).Msg("tcp read error")
cancel()
break clientRead
}
select {
case <-ctx.Done():
break clientRead
default:
}
if wn, err = backend.Write(buf[:n]); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Str(DIRECTION, CLIENT_TO_BACKEND).Msg("tcp write error")
cancel()
break clientRead
}
if wn != n {
log.Error().Err(fmt.Errorf("mismatch length between read and write")).Str(CONNECTION, cfg.Name()).Str(DIRECTION, CLIENT_TO_BACKEND).Msg("tcp write problem")
cancel()
break clientRead
}
select {
case <-ctx.Done():
break clientRead
default:
}
}
}
func RunUDP(wg *sync.WaitGroup, ctx context.Context, cfg abstract.UDPConnectionConfig) {
defer wg.Done()
var (
client net.PacketConn
buf []byte
addr net.Addr
err error
n int
backend *backendUDP
)
if client, err = net.ListenPacket("udp", cfg.Listen()); err != nil {
panic(fmt.Errorf("failed to bind for UDP %s: %w", cfg.Name(), err))
}
wg.Add(1)
backend = initUDP(wg, ctx, cfg, client)
buf = make([]byte, BUF_SIZE)
udpReadLoop:
for {
if n, addr, err = client.ReadFrom(buf); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Str(DIRECTION, CLIENT_TO_BACKEND).Msg("udp read error")
continue udpReadLoop
}
if err = backend.Send(ctx, addr.String(), buf[:n]); err != nil {
log.Error().Err(err).Str(CONNECTION, cfg.Name()).Str(DIRECTION, CLIENT_TO_BACKEND).Msg("send udp message")
continue udpReadLoop
}
select {
case <-ctx.Done():
break udpReadLoop
default:
}
}
}