Skip to content

Instantly share code, notes, and snippets.

@cbluth
Forked from mike-zhang/udpProxy.go
Created March 26, 2020 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cbluth/c7a04d9442960a4df4738cb8b4beb7fb to your computer and use it in GitHub Desktop.
Save cbluth/c7a04d9442960a4df4738cb8b4beb7fb to your computer and use it in GitHub Desktop.
Implementation of a UDP proxy in Golang
// Implementation of a UDP proxy
package main
import (
"flag"
"fmt"
"log"
"net"
"os"
"strings"
"sync"
)
// 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) {
var buffer [1500]byte
for {
// Read from server
n, err := conn.ServerConn.Read(buffer[0:])
if checkreport(1, err) {
continue
}
// Relay it to client
_, err = ProxyConn.WriteToUDP(buffer[0:n], conn.ClientAddr)
if checkreport(1, err) {
continue
}
Vlogf(3, "Relayed '%s' from server to %s.\n",
string(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 '%s' from client %s\n",
string(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)
// Fire up routine to manage new connection
go RunConnection(conn)
} 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", 6667, "Proxy port")
var isport *int = flag.Int("P", 6666, "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)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment