Skip to content

Commit

Permalink
Basic chat intercepting proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
ChillerDragon committed Jun 21, 2024
0 parents commit 91a63c8
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Go

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22'

- name: Build
run: go build -v ./...

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22'

- name: Test
run: go test -v ./...

format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22'

- name: Format
run: diff -u <(echo -n) <(gofmt -d ./)
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
teeworlds_client
teeworlds
proxy

14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# teeworlds proxy

WORK IN PROGRESS

## run the proxy

```
go build
./proxy -H 127.0.0.1 -P 8303 -p 8333
```

Start a teeworlds 0.7 server on port 8303 on your machine.
Connect to 8333 with your 0.7 client and you will be proxied to the 8303 server.

7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/teeworlds-go/proxy

go 1.22.3

require github.com/teeworlds-go/teeworlds v0.0.0-20240621015046-db550ce86fda

require github.com/teeworlds-go/huffman v1.0.0 // indirect
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/teeworlds-go/huffman v1.0.0 h1:XSNMNAJZb+njNrPACsxcDrrLDXTGjZZt35FqLAuHi4I=
github.com/teeworlds-go/huffman v1.0.0/go.mod h1:kjaXpL6C6xL7CM+tWPNYjdEgVZB2GumKhx7rCDdXArU=
github.com/teeworlds-go/teeworlds v0.0.0-20240621015046-db550ce86fda h1:Ho08Zo5Co+t5VfX4wVqiEDHxxDD3F+52AYne/Gs7oj0=
github.com/teeworlds-go/teeworlds v0.0.0-20240621015046-db550ce86fda/go.mod h1:aKhyLQAZ0cXv3e6ivNMiolKLbycQlDTIq9Yldn7klWM=
219 changes: 219 additions & 0 deletions proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// Implementation of a UDP proxy

package main

import (
"flag"
"fmt"
"log"
"net"
"os"
"strings"
"sync"

"github.com/teeworlds-go/teeworlds/messages7"
"github.com/teeworlds-go/teeworlds/network7"
"github.com/teeworlds-go/teeworlds/protocol7"
)

// Information maintained for each client/server connection
type Connection struct {
ClientAddr *net.UDPAddr // Address of the client
ServerConn *net.UDPConn // UDP connection to server
}

// Generate a new connection by opening a UDP connection to the server
func NewConnection(srvAddr, cliAddr *net.UDPAddr) *Connection {
conn := new(Connection)
conn.ClientAddr = cliAddr
srvudp, err := net.DialUDP("udp", nil, srvAddr)
if checkreport(1, err) {
return nil
}
conn.ServerConn = srvudp
return conn
}

// Global state
// Connection used by clients as the proxy server
var ProxyConn *net.UDPConn

// Address of server
var ServerAddr *net.UDPAddr

// Mapping from client addresses (as host:port) to connection
var ClientDict map[string]*Connection = make(map[string]*Connection)

// Mutex used to serialize access to the dictionary
var dmutex *sync.Mutex = new(sync.Mutex)

func setup(hostport string, port int) bool {
// Set up Proxy
saddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
if checkreport(1, err) {
return false
}
pudp, err := net.ListenUDP("udp", saddr)
if checkreport(1, err) {
return false
}
ProxyConn = pudp
Vlogf(2, "Proxy serving on port %d\n", port)

// Get server address
srvaddr, err := net.ResolveUDPAddr("udp", hostport)
if checkreport(1, err) {
return false
}
ServerAddr = srvaddr
Vlogf(2, "Connected to server at %s\n", hostport)
return true
}

func dlock() {
dmutex.Lock()
}

func dunlock() {
dmutex.Unlock()
}

// Go routine which manages connection from server to single client
func RunConnection(conn *Connection, twconn *protocol7.Connection) {
var buffer [1500]byte
for {
// Read from server
n, err := conn.ServerConn.Read(buffer[0:])
if checkreport(1, err) {
continue
}

srvMsg := buffer[0:n]
result, err := twconn.OnPacket(srvMsg)
if err != nil {
panic(err)
}
// example of inspecting incoming trafic
for i, msg := range result.Packet.Messages {
if msg.MsgId() == network7.MsgGameSvChat {
var chat *messages7.SvChat
var ok bool
if chat, ok = result.Packet.Messages[i].(*messages7.SvChat); ok {
fmt.Printf("got chat msg: %s\n", chat.Message)
chat.Message = "capitalism."

// modify chat if this was a proxy
result.Packet.Messages[i] = chat

srvMsg = result.Packet.Pack(twconn)
}
}
}

// Relay it to client
_, err = ProxyConn.WriteToUDP(srvMsg, conn.ClientAddr)
if checkreport(1, err) {
continue
}
Vlogf(3, "Relayed '%x' from server to %s.\n",
buffer[0:n], conn.ClientAddr.String())
}
}

// Routine to handle inputs to Proxy port
func RunProxy() {
var buffer [1500]byte
for {
n, cliaddr, err := ProxyConn.ReadFromUDP(buffer[0:])
if checkreport(1, err) {
continue
}
Vlogf(3, "Read '%x' from client %s\n",
buffer[0:n], cliaddr.String())
saddr := cliaddr.String()
dlock()
conn, found := ClientDict[saddr]
if !found {
conn = NewConnection(ServerAddr, cliaddr)
if conn == nil {
dunlock()
continue
}
ClientDict[saddr] = conn
dunlock()
Vlogf(2, "Created new connection for client %s\n", saddr)

twconn := &protocol7.Connection{
ClientToken: [4]byte{0x01, 0x02, 0x03, 0x04},
ServerToken: [4]byte{0xff, 0xff, 0xff, 0xff},
Ack: 0,
Players: make([]protocol7.Player, network7.MaxClients),
}

// Fire up routine to manage new connection
go RunConnection(conn, twconn)
} else {
Vlogf(5, "Found connection for client %s\n", saddr)
dunlock()
}
// Relay to server
_, err = conn.ServerConn.Write(buffer[0:n])
if checkreport(1, err) {
continue
}
}
}

var verbosity int = 6

// Log result if verbosity level high enough
func Vlogf(level int, format string, v ...interface{}) {
if level <= verbosity {
log.Printf(format, v...)
}
}

// Handle errors
func checkreport(level int, err error) bool {
if err == nil {
return false
}
Vlogf(level, "Error: %s", err.Error())
return true
}

func main() {
var ihelp *bool = flag.Bool("h", false, "Show help information")
var ipport *int = flag.Int("p", 8333, "Proxy port")
var isport *int = flag.Int("P", 8303, "Server port")
var ishost *string = flag.String("H", "localhost", "Server address")
var iverb *int = flag.Int("v", 1, "Verbosity (0-6)")
// var idrop *float64 = flag.Float64("d", 0.0, "Packet drop rate")
flag.Parse()
verbosity = *iverb
if *ihelp {
flag.Usage()
os.Exit(0)
}
if flag.NArg() > 0 {
ok := true
fields := strings.Split(flag.Arg(0), ":")
ok = ok && len(fields) == 2
if ok {
*ishost = fields[0]
n, err := fmt.Sscanf(fields[1], "%d", isport)
ok = ok && n == 1 && err == nil
}
if !ok {
flag.Usage()
os.Exit(0)
}
}
hostport := fmt.Sprintf("%s:%d", *ishost, *isport)
Vlogf(3, "Proxy port = %d, Server address = %s\n",
*ipport, hostport)
if setup(hostport, *ipport) {
RunProxy()
}
os.Exit(0)
}

0 comments on commit 91a63c8

Please sign in to comment.