WIP: test (+ more todos)

This commit is contained in:
Suyono 2025-05-11 13:02:54 +10:00
parent 9eeda34e19
commit 72745cf268
4 changed files with 104 additions and 43 deletions

View File

@ -19,11 +19,12 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"golang.org/x/sys/unix"
"net" "net"
"os/signal" "os/signal"
"time" "time"
"golang.org/x/sys/unix"
"gitea.suyono.dev/suyono/netbounce/cmd/slicewriter" "gitea.suyono.dev/suyono/netbounce/cmd/slicewriter"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -38,18 +39,19 @@ func main() {
defer cancel() defer cancel()
parseFlags() parseFlags()
log.Debug().Msgf("Sending messages to %s server: %s", viper.GetString(PROTOCOL), viper.GetString(SERVER)) log.Debug().Caller().Msgf("Sending messages to %s server: %s", viper.GetString(PROTOCOL), viper.GetString(SERVER))
sendMessages(ctx) sendMessages(ctx)
} }
const ( const (
SERVER = "server" SERVER = "server"
PROTOCOL = "protocol" PROTOCOL = "protocol"
UDP = "udp" UDP = "udp"
TCP = "tcp" TCP = "tcp"
NAME = "name" NAME = "name"
MESSAGE = "message" MESSAGE = "message"
SLEEP = "sleep" SLEEP = "sleep"
READTIMEOUT = "read-timeout"
) )
func sendMessages(ctx context.Context) { func sendMessages(ctx context.Context) {
@ -59,7 +61,7 @@ func sendMessages(ctx context.Context) {
case "tcp": case "tcp":
sendTCP(ctx) sendTCP(ctx)
default: default:
log.Fatal().Str(PROTOCOL, viper.GetString(PROTOCOL)).Msg("Unknown protocol") log.Fatal().Caller().Str(PROTOCOL, viper.GetString(PROTOCOL)).Msg("Unknown protocol")
} }
} }
@ -72,7 +74,7 @@ func sendTCP(ctx context.Context) {
) )
if conn, err = net.Dial(TCP, viper.GetString(SERVER)); err != nil { if conn, err = net.Dial(TCP, viper.GetString(SERVER)); err != nil {
log.Fatal().Err(err).Msg("Failed to connect to server") log.Fatal().Caller().Err(err).Msg("Failed to connect to server")
} }
defer func() { defer func() {
_ = conn.Close() _ = conn.Close()
@ -86,18 +88,21 @@ func sendTCP(ctx context.Context) {
for gLimit.isContinue(ctx) { for gLimit.isContinue(ctx) {
sb := slicewriter.NewSliceWriter(buf) sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString(NAME), time.Now(), viper.GetString(MESSAGE)); err != nil { if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString(NAME), time.Now(), viper.GetString(MESSAGE)); err != nil {
log.Fatal().Err(err).Msg("Failed to build client message") log.Fatal().Caller().Err(err).Msg("Failed to build client message")
} }
b = sb.Bytes() b = sb.Bytes()
if _, err = conn.Write(b); err != nil { if _, err = conn.Write(b); err != nil {
log.Fatal().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message") log.Fatal().Caller().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message")
} }
if err = conn.SetReadDeadline(time.Now().Add(viper.GetDuration(READTIMEOUT))); err != nil {
log.Fatal().Caller().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message")
}
if n, err = conn.Read(buf); err != nil { if n, err = conn.Read(buf); err != nil {
log.Fatal().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("read from the server") log.Fatal().Caller().Err(err).Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msg("read from the server")
} }
log.Info().Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msgf("%s", buf[:n]) log.Info().Caller().Str(PROTOCOL, TCP).Str(SERVER, viper.GetString(SERVER)).Msgf("%s", buf[:n])
time.Sleep(viper.GetDuration(SLEEP)) time.Sleep(viper.GetDuration(SLEEP))
} }
@ -105,21 +110,29 @@ func sendTCP(ctx context.Context) {
func sendUDP(ctx context.Context) { func sendUDP(ctx context.Context) {
var ( var (
addr *net.UDPAddr addr, laddr *net.UDPAddr
conn *net.UDPConn conn *net.UDPConn
rAddr net.Addr rAddr net.Addr
err error err error
buf, b []byte buf, b []byte
n int n int
) )
if addr, err = net.ResolveUDPAddr(UDP, viper.GetString(SERVER)); err != nil { if addr, err = net.ResolveUDPAddr(UDP, viper.GetString(SERVER)); err != nil {
log.Fatal().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("udp resolve address") log.Fatal().Caller().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("udp resolve server address")
} }
if conn, err = net.DialUDP(UDP, nil, addr); err != nil { if laddr, err = net.ResolveUDPAddr(UDP, ""); err != nil {
log.Fatal().Err(err).Str("server", viper.GetString(SERVER)).Msg("dial server udp") log.Fatal().Caller().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("udp resolve local/self address")
} }
//TODO: log address to be bound by client
//TODO: use un-connected socket of UDP, replace all net.DialUDP() with net.ListenUDP()
// In Go, binding for UDP using ListenUDP. Confusing!!
if conn, err = net.ListenUDP(UDP, laddr); err != nil {
log.Fatal().Caller().Err(err).Str(SERVER, viper.GetString(SERVER)).Msg("fail to bind local/self address")
}
defer func() { defer func() {
_ = conn.Close() _ = conn.Close()
}() }()
@ -132,24 +145,28 @@ func sendUDP(ctx context.Context) {
for gLimit.isContinue(ctx) { for gLimit.isContinue(ctx) {
sb := slicewriter.NewSliceWriter(buf) sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString(NAME), time.Now(), viper.GetString(MESSAGE)); err != nil { if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString(NAME), time.Now(), viper.GetString(MESSAGE)); err != nil {
log.Fatal().Err(err).Msg("Failed to build client message") log.Fatal().Caller().Err(err).Msg("Failed to build client message")
} }
b = sb.Bytes() b = sb.Bytes()
if _, err = conn.WriteTo(b, addr); err != nil { if _, err = conn.WriteTo(b, addr); err != nil {
log.Fatal().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message") log.Fatal().Caller().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("Failed to send client message")
} }
if err = conn.SetReadDeadline(time.Now().Add(viper.GetDuration(READTIMEOUT))); err != nil {
log.Error().Caller().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("set read timeout on the socket")
}
if n, rAddr, err = conn.ReadFrom(b); err != nil { if n, rAddr, err = conn.ReadFrom(b); err != nil {
log.Fatal().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("read from server") log.Fatal().Caller().Err(err).Str(PROTOCOL, UDP).Str(SERVER, viper.GetString(SERVER)).Msg("read from server")
} }
log.Info().Str(PROTOCOL, UDP).Str(SERVER, rAddr.String()).Msgf("%s", buf[:n]) log.Info().Caller().Str(PROTOCOL, UDP).Str(SERVER, rAddr.String()).Msgf("%s", buf[:n])
} }
} }
func parseFlags() { func parseFlags() {
pflag.String(NAME, "", "client name") pflag.String(NAME, "", "client name")
pflag.IntP("number", "n", 5, "number of messages to send; default 5; set to 0 for infinite") pflag.IntP("number", "n", 5, "number of messages to send; default 5; set to 0 for infinite")
pflag.Duration(READTIMEOUT, 5*time.Second, "read timeout; default 5 seconds")
pflag.DurationP(SLEEP, "s", 10*time.Millisecond, "sleep time between requests; default 10ms") pflag.DurationP(SLEEP, "s", 10*time.Millisecond, "sleep time between requests; default 10ms")
pflag.StringP(MESSAGE, "m", "message from client", "message to send") pflag.StringP(MESSAGE, "m", "message from client", "message to send")
pflag.Bool("debug", false, "run in debug mode") pflag.Bool("debug", false, "run in debug mode")
@ -162,15 +179,15 @@ func parseFlags() {
gLimit = makeCounter(viper.GetInt("number")) gLimit = makeCounter(viper.GetInt("number"))
if !viper.IsSet(NAME) { if !viper.IsSet(NAME) {
log.Fatal().Msg("server name is required") log.Fatal().Caller().Msg("server name is required")
} }
if viper.IsSet(TCP) && viper.IsSet(UDP) { if viper.IsSet(TCP) && viper.IsSet(UDP) {
log.Fatal().Msg("cannot use tcp and udp at once") log.Fatal().Caller().Msg("cannot use tcp and udp at once")
} }
if !viper.IsSet(TCP) && !viper.IsSet(UDP) { if !viper.IsSet(TCP) && !viper.IsSet(UDP) {
log.Fatal().Msg("server address is required") log.Fatal().Caller().Msg("--tcp or --udp is required, use one of them not both.")
} }
if viper.IsSet(TCP) { if viper.IsSet(TCP) {
@ -198,7 +215,7 @@ func makeCounter(limit int) *counter {
log.Fatal().Msg("number must be > 0") log.Fatal().Msg("number must be > 0")
} }
return &counter{limit: limit, tick: 0} return &counter{limit: limit, tick: -1}
} }
func (c *counter) isContinue(ctx context.Context) bool { func (c *counter) isContinue(ctx context.Context) bool {
@ -213,9 +230,5 @@ func (c *counter) isContinue(ctx context.Context) bool {
} }
c.tick++ c.tick++
if c.tick < c.limit { return c.tick < c.limit
return true
}
return false
} }

View File

@ -19,16 +19,17 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"os/signal"
"sync"
"time"
"gitea.suyono.dev/suyono/netbounce/cmd/slicewriter" "gitea.suyono.dev/suyono/netbounce/cmd/slicewriter"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"net"
"os/signal"
"sync"
"time"
) )
func main() { func main() {
@ -43,6 +44,13 @@ func main() {
openPorts(wg, ctx) openPorts(wg, ctx)
wg.Wait() wg.Wait()
select {
case <-ctx.Done():
log.Debug().Caller().Msg("received signal to quit")
default:
log.Debug().Caller().Msg("unexpected exit")
}
} }
func parseFlags() { func parseFlags() {
@ -94,7 +102,7 @@ func bindUDP(ctx context.Context, wg *sync.WaitGroup, address string) {
addr net.Addr addr net.Addr
) )
log.Debug().Str("address", address).Msg("binding socket for UDP") log.Debug().Caller().Msgf("binding socket for UDP on %v", address)
if conn, err = net.ListenPacket("udp", address); err != nil { if conn, err = net.ListenPacket("udp", address); err != nil {
panic(fmt.Errorf("failed to bind udp address: %v", err)) panic(fmt.Errorf("failed to bind udp address: %v", err))
} }
@ -158,6 +166,7 @@ func listen(ctx context.Context, wg *sync.WaitGroup, address string) {
conn net.Conn conn net.Conn
) )
log.Debug().Caller().Msgf("listeng for TCP on %v", address)
if listener, err = net.Listen("tcp", address); err != nil { if listener, err = net.Listen("tcp", address); err != nil {
log.Error().Err(err).Str("address", address).Msg("failed to listen") log.Error().Err(err).Str("address", address).Msg("failed to listen")
return return

View File

@ -0,0 +1,39 @@
name: bounce-test
services:
server:
build:
context: .
dockerfile: docker/Dockerfile-test-server
no_cache: true
tty: true
hostname: server
command: [
"/usr/local/bin/test-server", "--name", "server", "--debug",
"--udp", "0.0.0.0:10010",
"--tcp", "0.0.0.0:10020"
]
client-udp:
build:
context: .
dockerfile: docker/Dockerfile-test-client
no_cache: true
tty: true
hostname: client
command: [
"/usr/local/bin/test-client", "--debug", "--name", "client-udp",
"--udp", "server:10010"
]
client-tcp:
build:
context: .
dockerfile: docker/Dockerfile-test-client
no_cache: true
tty: true
hostname: client
command: [
"/usr/local/bin/test-client", "--debug", "--name", "client-tcp",
"--tcp", "server:10020"
]

View File

@ -8,4 +8,4 @@ FROM alpine:3.21
RUN apk add tzdata && ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime RUN apk add tzdata && ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime
COPY --from=builder /go/test-client /usr/local/bin/test-client COPY --from=builder /go/test-client /usr/local/bin/test-client
CMD ["/usr/local/bin/test-server"] CMD ["/usr/local/bin/test-client"]