initial commit

This commit is contained in:
Dylan Parker 2023-01-08 03:26:04 -06:00
commit cfa445db14
11 changed files with 300 additions and 0 deletions

19
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "http server",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/http/main.go",
"env": {
"LILPROXY_TARGET": "localhost:9001",
"LILPROXY_PORT":":9000"
}
}
]
}

10
Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM golang:alpine
# Move to /dist directory as the place for resulting binary folder
WORKDIR /dist
# Copy binary from build to main folder
COPY ./main .
# Command to run when starting the container
ENTRYPOINT [ "/dist/main" ]

2
Makefile Normal file
View File

@ -0,0 +1,2 @@
go-build:
go build cmd/http/main.go

29
cmd/http/main.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"log"
"os"
"github.com/dgparker/lilproxy/pkg/udpproxy"
)
func main() {
target := os.Getenv("LILPROXY_TARGET")
if target == "" {
log.Fatal("env LILPROXY_TARGET required")
}
port := os.Getenv("LILPROXY_PORT")
if port == "" {
log.Fatal("env LILPROXY_PORT required")
}
c, err := udpproxy.New(port, target)
if err != nil {
log.Fatal(err)
}
log.Printf("lilproxy initialized")
log.Fatal(c.ListenAndServe())
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/dgparker/lilproxy
go 1.18

0
go.sum Normal file
View File

BIN
main Executable file

Binary file not shown.

66
pkg/udpproxy/session.go Normal file
View File

@ -0,0 +1,66 @@
package udpproxy
import (
"log"
"net"
"time"
)
type Session struct {
serverConn *net.UDPConn
proxyConn *net.UDPConn
caddr *net.UDPAddr
updateTime time.Time
}
func createSession(caddr *net.UDPAddr, raddr *net.UDPAddr, proxyConn *net.UDPConn) (*Session, error) {
serverConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
return nil, err
}
session := &Session{
serverConn: serverConn,
proxyConn: proxyConn,
caddr: caddr,
updateTime: time.Now(),
}
go session.listen()
return session, nil
}
func (s *Session) listen() error {
for {
buf := make([]byte, 1024)
_, err := s.serverConn.Read(buf)
if err != nil {
log.Println(err)
continue
}
go s.proxyFrom(buf)
}
}
func (s *Session) proxyFrom(buf []byte) error {
s.updateTime = time.Now()
_, err := s.proxyConn.WriteToUDP(buf, s.caddr)
if err != nil {
return err
}
return nil
}
func (s *Session) proxyTo(buf []byte) error {
s.updateTime = time.Now()
_, err := s.serverConn.Write(buf)
if err != nil {
log.Println(err)
return err
}
return nil
}

84
pkg/udpproxy/udpproxy.go Normal file
View File

@ -0,0 +1,84 @@
package udpproxy
import (
"log"
"net"
"sync"
"time"
)
type Client struct {
laddr *net.UDPAddr
raddr *net.UDPAddr
proxyConn *net.UDPConn
mutex sync.RWMutex
sessions map[string]*Session
}
func New(port, target string) (*Client, error) {
laddr, err := net.ResolveUDPAddr("udp", port)
if err != nil {
return nil, err
}
raddr, err := net.ResolveUDPAddr("udp", target)
if err != nil {
return nil, err
}
return &Client{
laddr: laddr,
raddr: raddr,
mutex: sync.RWMutex{},
sessions: map[string]*Session{},
}, nil
}
func (c *Client) ListenAndServe() error {
var err error
c.proxyConn, err = net.ListenUDP("udp", c.laddr)
if err != nil {
return err
}
go c.pruneSessions()
for {
buf := make([]byte, 2048)
_, caddr, err := c.proxyConn.ReadFromUDP(buf)
if err != nil {
log.Println(err)
}
session, found := c.sessions[caddr.String()]
if !found {
session, err = createSession(caddr, c.raddr, c.proxyConn)
if err != nil {
log.Println(err)
continue
}
c.sessions[caddr.String()] = session
}
go session.proxyTo(buf)
}
}
func (c *Client) pruneSessions() {
ticker := time.NewTicker(1 * time.Minute)
// the locks here could be abusive and i dont even know if this is a real
// problem but we definitely need to clean up stale sessions
for range ticker.C {
for _, session := range c.sessions {
c.mutex.RLock()
if time.Since(session.updateTime) > time.Minute*5 {
delete(c.sessions, session.caddr.String())
}
c.mutex.RUnlock()
}
}
}

View File

@ -0,0 +1,83 @@
package udpproxy
import (
"log"
"net"
"testing"
"time"
)
func TestSendAndReceive(t *testing.T) {
go runLilProxy()
go runUDPServer()
paddr, err := net.ResolveUDPAddr("udp", "localhost:9000")
if err != nil {
t.Fatal(err)
}
conn, err := net.DialUDP("udp", nil, paddr)
if err != nil {
t.Fatal(err)
}
go func() {
for {
buf := make([]byte, 2048)
_, _, err = conn.ReadFromUDP(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("response received: %s", string(buf))
}
}()
for {
time.Sleep(1 * time.Second)
_, err = conn.Write([]byte("hi\n"))
if err != nil {
log.Fatal(err)
}
}
}
func runLilProxy() {
port := ":9000"
target := "localhost:9001"
c, err := New(port, target)
if err != nil {
log.Fatal(err)
}
log.Fatal(c.ListenAndServe())
}
func runUDPServer() {
taddr, err := net.ResolveUDPAddr("udp", ":9001")
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", taddr)
if err != nil {
log.Fatal(err)
}
for {
buf := make([]byte, 2048)
_, caddr, err := conn.ReadFromUDP(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("request received: %s", string(buf))
_, err = conn.WriteToUDP([]byte("bye\n"), caddr)
if err != nil {
log.Fatal(err)
}
}
}

4
vendor/modules.txt vendored Normal file
View File

@ -0,0 +1,4 @@
# github.com/google/martian/v3 v3.3.2
## explicit; go 1.11
# golang.org/x/sys v0.3.0
## explicit; go 1.17