Skip to content

Instantly share code, notes, and snippets.

@mike-zhang
Created October 8, 2012 15:58
Show Gist options
  • Star 58 You must be signed in to star a gist
  • Fork 13 You must be signed in to fork a gist
  • Save mike-zhang/3853251 to your computer and use it in GitHub Desktop.
Save mike-zhang/3853251 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)
}
@lipoiq
Copy link

lipoiq commented May 16, 2015

how to sent client IP address at server?

@DineshGuptaa
Copy link

Hi,
I didn't see anywhere connection is closed. Is connection will remain open after go routine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment