docker testing

This commit is contained in:
Suyono 2025-04-21 18:37:11 +10:00
parent 59a91d29fd
commit 01553569ba
9 changed files with 433 additions and 3 deletions

View File

@ -251,13 +251,18 @@ func RunUDP(wg *sync.WaitGroup, ctx context.Context, cfg abstract.UDPConnectionC
udpReadLoop:
for {
if n, addr, err = client.ReadFrom(buf); err != nil {
select {
case <-ctx.Done():
break udpReadLoop
default:
}
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
//continue udpReadLoop
}
select {

View File

@ -0,0 +1,36 @@
package slicewriter
import "io"
type SliceWriter struct {
off int
buf []byte
}
func (w *SliceWriter) Write(p []byte) (int, error) {
if len(p)+w.off <= len(w.buf) {
copy(w.buf[w.off:], p)
w.off += len(p)
return len(p), nil
}
space := len(w.buf) - w.off
copy(w.buf[:w.off], p[:space])
w.off += space
return space, io.ErrShortWrite
}
func (w *SliceWriter) Reset() {
w.off = 0
}
func (w *SliceWriter) Bytes() []byte {
return w.buf[:w.off]
}
func NewSliceWriter(b []byte) *SliceWriter {
return &SliceWriter{
off: 0,
buf: b,
}
}

View File

@ -1 +1,122 @@
package main
import (
"fmt"
"gitea.suyono.dev/suyono/netbounce/cmd/slicewriter"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"net"
"time"
)
/*
Copyright 2025 Suyono <suyono3484@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
func main() {
parseFlags()
log.Debug().Msgf("Sending messages to %s server: %s", viper.GetString("protocol"), viper.GetString("server"))
sendMessages()
}
func sendMessages() {
switch viper.GetString("protocol") {
case "udp":
sendUDP()
case "tcp":
sendTCP()
default:
log.Fatal().Str("protocol", viper.GetString("protocol")).Msg("Unknown protocol")
}
}
func sendTCP() {
var (
conn net.Conn
err error
buf, b []byte
)
if conn, err = net.Dial("tcp", viper.GetString("server")); err != nil {
log.Fatal().Err(err).Msg("Failed to connect to server")
}
defer func() {
_ = conn.Close()
}()
buf = make([]byte, 4096)
for {
sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "client %s | %v | %s", viper.GetString("name"), time.Now(), viper.GetString("message")); err != nil {
log.Error().Err(err).Msg("Failed to build client message")
return
}
b = sb.Bytes()
if _, err = conn.Write(b); err != nil {
log.Error().Err(err).Msg("Failed to send client message")
time.Sleep(viper.GetDuration("sleep"))
continue
}
//TODO: read the server reply
}
}
func sendUDP() {
//TODO: implement this part
}
func parseFlags() {
pflag.String("name", "", "client name")
pflag.IntP("number", "n", 5, "number of messages to send; default 5; set to 0 for infinite")
pflag.DurationP("sleep", "s", 10*time.Millisecond, "sleep time between requests; default 10ms")
pflag.StringP("message", "m", "message from client", "message to send")
pflag.Bool("debug", false, "run in debug mode")
pflag.String("tcp", "", "tcp server address")
pflag.String("udp", "", "udp server address")
pflag.Parse()
_ = viper.BindPFlags(pflag.CommandLine)
if !viper.IsSet("name") {
log.Fatal().Msg("server name is required")
}
if viper.IsSet("tcp") && viper.IsSet("udp") {
log.Fatal().Msg("cannot use tcp and udp at once")
}
if !viper.IsSet("tcp") && !viper.IsSet("udp") {
log.Fatal().Msg("server address is required")
}
if viper.IsSet("tcp") {
viper.Set("protocol", "tcp")
viper.Set("server", viper.GetString("tcp"))
}
if viper.IsSet("udp") {
viper.Set("protocol", "udp")
viper.Set("server", viper.GetString("udp"))
}
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if viper.GetBool("debug") {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
}

View File

@ -1 +1,228 @@
package main
/*
Copyright 2025 Suyono <suyono3484@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import (
"context"
"fmt"
"gitea.suyono.dev/suyono/netbounce/cmd/slicewriter"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"golang.org/x/sys/unix"
"net"
"os/signal"
"sync"
"time"
)
func main() {
//zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
parseFlags()
log.Debug().Msg("Starting server")
ctx, cancel := signal.NotifyContext(context.Background(), unix.SIGINT, unix.SIGTERM)
defer cancel()
wg := &sync.WaitGroup{}
openPorts(wg, ctx)
wg.Wait()
}
func parseFlags() {
pflag.String("name", "", "server name")
pflag.Bool("debug", false, "run in debug mode")
pflag.StringSlice("tcp", []string{}, "tcp listen address")
pflag.StringSlice("udp", []string{}, "udp listen address")
pflag.Parse()
_ = viper.BindPFlags(pflag.CommandLine)
if !viper.IsSet("name") {
log.Fatal().Msg("server name is required")
}
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if viper.GetBool("debug") {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
}
func openPorts(wg *sync.WaitGroup, ctx context.Context) {
tcpPorts := viper.GetStringSlice("tcp")
for _, tcpPort := range tcpPorts {
wg.Add(1)
go listen(ctx, wg, tcpPort)
}
udpPorts := viper.GetStringSlice("udp")
for _, udpPort := range udpPorts {
wg.Add(1)
go bindUDP(ctx, wg, udpPort)
}
}
func ClosePacket(ctx context.Context, conn net.PacketConn) {
<-ctx.Done()
_ = conn.Close()
}
func bindUDP(ctx context.Context, wg *sync.WaitGroup, address string) {
defer wg.Done()
var (
conn net.PacketConn
err error
buf, b []byte
n int
addr net.Addr
)
log.Debug().Str("address", address).Msg("binding socket for UDP")
if conn, err = net.ListenPacket("udp", address); err != nil {
panic(fmt.Errorf("failed to bind udp address: %v", err))
}
go ClosePacket(ctx, conn)
buf = make([]byte, 4096)
udpLoop:
for {
if n, addr, err = conn.ReadFrom(buf); err != nil && n == 0 {
select {
case <-ctx.Done():
break udpLoop
default:
}
log.Error().Err(err).Msg("failed to read packet")
continue udpLoop
}
log.Info().Str("client", addr.String()).Msgf("received message: %s", buf[:n])
select {
case <-ctx.Done():
break udpLoop
default:
}
sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "server: %s | UDP | %v", viper.GetString("name"), time.Now()); err != nil {
log.Error().Err(err).Msg("build server message")
}
b = sb.Bytes()
if n, err = conn.WriteTo(b, addr); err != nil {
select {
case <-ctx.Done():
break udpLoop
default:
}
log.Error().Err(err).Str("client", addr.String()).Msg("failed to write packet")
continue udpLoop
}
if n != len(b) {
log.Debug().Str("client", addr.String()).Msg("incomplete packet sent")
}
log.Info().Str("client", addr.String()).Msg("packet received and replied")
}
}
func CloseListener(ctx context.Context, listener net.Listener) {
<-ctx.Done()
_ = listener.Close()
}
func listen(ctx context.Context, wg *sync.WaitGroup, address string) {
defer wg.Done()
var (
listener net.Listener
err error
conn net.Conn
)
if listener, err = net.Listen("tcp", address); err != nil {
log.Error().Err(err).Str("address", address).Msg("failed to listen")
return
}
go CloseListener(ctx, listener)
tcpIncoming:
for {
if conn, err = listener.Accept(); err != nil {
select {
case <-ctx.Done():
break tcpIncoming
default:
}
log.Error().Err(err).Str("address", address).Msg("failed to accept connection")
continue tcpIncoming
}
wg.Add(1)
go handleTCP(ctx, wg, conn)
}
}
func CloseConnection(ctx context.Context, conn net.Conn) {
<-ctx.Done()
_ = conn.Close()
}
func handleTCP(ctx context.Context, wg *sync.WaitGroup, conn net.Conn) {
defer wg.Done()
defer func() {
_ = conn.Close()
}()
var (
buf, b []byte
err error
n int
)
buf = make([]byte, 4096)
addr := conn.RemoteAddr()
cctx, cancel := context.WithCancel(ctx)
go CloseConnection(cctx, conn)
defer cancel()
for {
if n, err = conn.Read(buf); err != nil {
log.Error().Err(err).Str("client", addr.String()).Msg("failed to read data from TCP connection")
return
}
log.Info().Str("client", addr.String()).Msgf("received message: %s", buf[:n])
sb := slicewriter.NewSliceWriter(buf)
if _, err = fmt.Fprintf(sb, "server: %s | TCP | %v", viper.GetString("name"), time.Now()); err != nil {
log.Error().Err(err).Msg("build server message")
}
b = sb.Bytes()
if _, err = conn.Write(b); err != nil {
log.Error().Err(err).Str("client", addr.String()).Msg("failed to write data to TCP connection")
return
}
}
}

View File

@ -1 +1,16 @@
FROM suyono/wingmate:v0.2.0-bookworm AS wingmate
FROM golang:1.24-bookworm AS builder
ADD . .
RUN go build -v gitea.suyono.dev/suyono/netbounce/cmd/netbounce
FROM ubuntu:24.04
COPY --from=builder /go/netbounce /usr/local/bin/netbounce
COPY --from=wingmate /usr/local/bin/wingmate /usr/local/bin/entry.sh /usr/local/bin/
COPY wingmate/ /etc/wingmate
COPY example/ /etc/netbounce
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
CMD ["/usr/local/bin/wingmate"]

View File

@ -0,0 +1,11 @@
FROM golang:1.24-alpine AS builder
ADD . .
RUN go build -v gitea.suyono.dev/suyono/netbounce/cmd/test-client
FROM alpine:3.21
RUN apk add tzdata && ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime
COPY --from=builder /go/test-client /usr/local/bin/test-client
CMD ["/usr/local/bin/test-server"]

View File

@ -0,0 +1,11 @@
FROM golang:1.24-alpine AS builder
ADD . .
RUN go build -v gitea.suyono.dev/suyono/netbounce/cmd/test-server
FROM alpine:3.21
RUN apk add tzdata && ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime
COPY --from=builder /go/test-server /usr/local/bin/test-server
CMD ["/usr/local/bin/test-server"]

View File

@ -2,11 +2,11 @@ connection:
service1:
type: tcp
listen: ":10022"
backend: "jumper:10022"
backend: "test-server:10022"
service2:
type: udp
listen: ":10149"
backend: "jumper:10149"
backend: "test-server:10149"
timeout: 1h

4
wingmate/wingmate.yaml Normal file
View File

@ -0,0 +1,4 @@
service:
netbounce:
command:
- "/usr/local/bin/netbounce"