Created
October 8, 2012 15:58
-
-
Save mike-zhang/3853251 to your computer and use it in GitHub Desktop.
Implementation of a UDP proxy in Golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
} |
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
how to sent client IP address at server?