How to communicate with multiple clients in a UDP server in go? - go

I have a server that transfers files over UDP and it works well when with only one client at the time, but if other client tries to connect, the following error is thrown
listen udp :20000: bind: Only one usage of each socket address
(protocol/network address/port) is normally permitted.
This is my code in main:
...
fmt.Println("Input the number of connections")
var number int
fmt.Scanln(&number)
var wg sync.WaitGroup
wg.Add(number)
lAdd, err := net.ResolveUDPAddr("udp", PORT)
if err != nil {
fmt.Println(err)
return
}
for i:=0; i < number; i++ {
go startConnection(lAdd, file, i, wg)
}
wg.Wait()
This is the code of startConnection:
func startConnection (lAdd * net.UDPAddr, archivo string, i int, wg sync.WaitGroup) {
defer wg.Done()
conn, err := net.ListenUDP("udp", lAdd)
if err != nil {
logger.Println("There was an error: ", err)
return
}
defer conn.Close()
sendFile (conn, add, archivo, i)
}

A server should call Listen only once to start the listener. At that point, it is listening. This is where TCP and UDP diverge, because of the nature of the protocols.
ListenTCP returns a TCPListener, which can be used to Accept connections as they come in. Each connection can then send and receive data.
ListenUDP returns a UDPConn, the same as DialUDP, because UDP is a connectionless protocol. You just read and write to the UDPConn. UDP packet headers include the source and target information, so you can use ReadFrom to read data and see who sent it, and WriteTo to respond to a specific client.
This article has a pretty thorough rundown of implementing UDP connectivity in Go: https://ops.tips/blog/udp-client-and-server-in-go/

Related

How to drop a UDP connection between server and client from server side in Golang?

I'm trying to find a standard way to close a connection between a client and server in a UDP connection.
Currently, I came up with the following solution, however, I'm not sure whether this is an idiomatic way or not?
Basically what I'm doing here on the server-side (handleClient function) is to conn.WriteTo(nil, Addr) which write nil to the UDP address. on the client side I check if the read() function retrieved any data or not, in case the number of the reading byte is zero, the client gives up on reading.
if n ==0 || err != nil {
break
}
Here is my simplified Server.go file:
func handleClient(conn *net.UDPConn) {
b := make([]byte, 1024)
n, Addr, err := conn.ReadFrom(b[0:])
if err != nil {
panic(err)
}
fmt.Println("read: ", n, " bytes", Addr.String())
// write the data back to the client, just for debug purpose
conn.WriteTo(b[0:n], Addr)
// let the client know that server has no more data
conn.WriteTo(nil, Addr)
}
And this is my client.go file
conn, err := net.Dial("udp", ":14000")
if err != nil {
panic(err)
}
defer conn.Close()
conn.Write([]byte("I'm client "))
for {
b := make([]byte, 512)
n, err := conn.Read(b)
if n ==0 || err != nil {
fmt.Println(">", n, err)
break
}
fmt.Println(string(b))
}
fmt.Println("finished.")
There is no standard way to do this. UDP is a stateless protocol so there is no "connection" like you would have with a TCP connection. DNS for example has no concept of a state, a client sends a request and the server will respond, no state is maintained, this is usually the selling point of UDP.
The applications are responsible for maintain state, so if you want a stateful protocol on top of UDP you also have to handle closing such a connection yourself. You might take inspiration from FTP or TFTP on how to implement this. Or consider using a stateful transport layer protocol like TCP or QUIC to handle stateful connections for you.

UDP listener seems to hang after being closed until another packet is being received

While practicing a bit on concurrency with go, I faced a problem.
I am making a dns filtering server, it receives a dns packet, and act as a recursive server with caching and its own records.
Starting the server works all great, but when I try to close the listener to shut the server down, it just hangs until there is another packet arriving.
So what I have been trying to do is run another anonymous function that receives a message from a channel, close the listener, clear cache and exit from the server goroutine.
Related code:
func StartDNSServer(recursiveServer, ip string, port int, ch chan bool) {
stopChn := make(chan bool)
//Listen on UDP Port
addr := net.UDPAddr{
Port: port,
IP: net.ParseIP(ip),
}
fmt.Println("[*] Starting dns server...")
u, err := net.ListenUDP("udp", &addr)
//u.ReadFrom is a blocking command, so if we try to stop the dns server it wont detect the data in ch until there is a new dns packet.
// anonymous function to close the udp listener to realease the blocking.
go func() {
select {
case <-ch:
stopChn <- false
u.Close()
}
}()
defer u.Close()
if err != nil {
log.Panic("Could not listen on given port, maybe another application is using it?")
}
fmt.Println("[*] Started!")
// Wait to get request on that port
for {
select {
case <-stopChn:
fmt.Println("[*] Deleting records from cache.")
Cache.DeleteExpired()
fmt.Println("[*] Stopping dns server")
runtime.Goexit()
default:
tmp := make([]byte, 1024)
_, addr, _ := u.ReadFrom(tmp)
clientAddr := addr
packet := gopacket.NewPacket(tmp, layers.LayerTypeDNS, gopacket.Default)
dnsPacket := packet.Layer(layers.LayerTypeDNS)
tcp, _ := dnsPacket.(*layers.DNS)
go handleDNS(u, clientAddr, tcp, recursiveServer)
continue
}
}
}
From what I managed to get, the select statement under the infinite for loop is entering the default section, and then waiting for a packet, after that its handling the packet, it is "noticing" that the stopChn has received a signal, and shutting down.
So if this is really the case, I then tried to change the stopChn := make(chan bool) into a buffered channel stopChn := make(chan bool, 1).
Now it really does trigger the desired action immediately, but panics with the error:
panic: runtime error: index out of range [0] with length 0
goroutine 16 [running]:
/C/Users/moti_/OneDrive/Desktop/GoDNSFilter/internals/dnsserver.handleDNS(0xc000006660,
0x0, 0x0, 0xc0001ea180, 0xa98f34, 0x7)
C:/Users/moti_/OneDrive/Desktop/GoDNSFilter/internals/dnsserver/utils.go:32
+0x75b created by /C/Users/moti_/OneDrive/Desktop/GoDNSFilter/internals/dnsserver.StartDNSServer
C:/Users/moti_/OneDrive/Desktop/GoDNSFilter/internals/dnsserver/utils.go:151
+0x2be
I looked for it online, and some answers said that I might be sending too many signals into the stopChn but it would be blocking, not panicking..
Not sure if I am approaching this correctly, any help would be appreciated!
So, After #mh-cbon helped me a lot, heres the solution if any one ever gonna face the same problem.
As said before, I cannot close one goroutine from another, so I had to use 2 channels, one for the not blocked goroutine, which will close the listener and then clear out cache and all.
Still got to compare error with error vars, but for solution its enough for now.
func StartDNSServer(recursiveServer, ip string, port int, ch chan bool) {
//Listen on UDP Port
addr := net.UDPAddr{
Port: port,
IP: net.ParseIP(ip),
}
fmt.Println("[*] Starting dns server...")
u, err := net.ListenUDP("udp", &addr)
//u.ReadFrom is a blocking command, so if we try to stop the dns server it wont detect the data in ch until there is a new dns packet.
// anonymous function to close the udp listener to realease the blocking.
go func() {
select {
case <-ch:
u.Close()
}
}()
defer u.Close()
if err != nil {
log.Panic("Could not listen on given port, maybe another application is using it?")
}
fmt.Println("[*] Started!")
// Wait to get request on that port
for {
tmp := make([]byte, 1024)
_, addr, err := u.ReadFrom(tmp)
if err != nil {
if strings.Contains(err.Error(), "use of closed network connection") {
fmt.Println("[*] Deleting records from cache.")
Cache.DeleteExpired()
fmt.Println("[*] Stopping dns server")
return
}
clientAddr := addr
packet := gopacket.NewPacket(tmp, layers.LayerTypeDNS, gopacket.Default)
dnsPacket := packet.Layer(layers.LayerTypeDNS)
tcp, _ := dnsPacket.(*layers.DNS)
go handleDNS(u, clientAddr, tcp, recursiveServer)
continue
}
}
}

Can't read UDP reply (golang)

I'm working on a Go program that sends out a UDP broadcast to query existence of devices on the local network and then reads the replies. Using Wireshark I confirm that the packet is broadcast and that the single device on (my) network replies (ten times, in fact) but my application blocks on the read as if it does not see the incoming packet. Here is the code:
func Discover(timeout int) ([]string, error) {
inBuf := make([]byte, 1024)
devices := make([]string, 0)
var readLen int
var fromAddr *net.UDPAddr
// get server connection
server := fmt.Sprintf("%s:%d", bcastIP, udpDiscoverPort) // "255.255.255.255", 10000
serverAddr, err = net.ResolveUDPAddr("udp", server)
checkErr(err)
ourAddr, err = net.ResolveUDPAddr("udp", "192.168.1.132:10000")
checkErr(err)
conn, err = net.DialUDP("udp", ourAddr, serverAddr)
checkErr(err)
defer conn.Close()
// send the Discover message
discoverMsg := []byte(magic)
discoverMsg = append(discoverMsg, discovery...)
sendLen, err := conn.Write(discoverMsg)
checkErr(err)
fmt.Println("Sent", sendLen, "bytes")
// read one reply
readLen, fromAddr, err = conn.ReadFromUDP(inBuf)
fmt.Println("Read ", readLen, "bytesfrom ", fromAddr)
txtutil.Dump(string(inBuf[:readLen]))
return devices, nil
}
checkErr(err) prints a diagnostic and exits if err is not nil, BTW.
The information in the replies looks like:
Internet Protocol Version 4, Src: 192.168.1.126 (192.168.1.126), Dst: 192.168.1.132 (192.168.1.132)
User Datagram Protocol, Src Port: ndmp (10000), Dst Port: ndmp (10000)
I have tried "0.0.0.0:10000", ":10000" and "127.0.0.1:10000" in place of "192.168.1.132:10000" and none seem to make any difference.
Any suggestions as to what I'm doing wrong are welcome!
You need to use ListenUDP instead of DialUDP. When you use DialUDP, it creates a "connected" UDP port, and only packets originating from the remote address are returned on read.
conn, err = net.ListenUDP("udp", ourAddr)
Since the connection doesn't have a default destination, you will also need to use WriteTo* methods to send packets:
sendLen, err := conn.WriteToUDP(discoverMsg, serverAddr)

ListenUDP, a one way street?

When I run this code an incoming UDP packet gets read in, however no packet gets sent back out. Why is this? (I verified this fact with wireshark). I want to be able to communicate two ways over a UDP connection, how do I achieve this with golang?
//Node 1
func main() {
addr := net.UDPAddr{
Port: 7000,
IP: net.ParseIP("127.0.0.1"),
}
conn, err := net.ListenUDP("udp", &addr)
defer conn.Close()
if err != nil {
panic(err)
}
for {
b := make([]byte, 10)
conn.Read(b)
fmt.Println(string(b[:]))
conn.Write([]byte("sending back"))
}
}
func main() {
sock, _ := net.Dial("udp", "127.0.0.1:7000")
buf := make([]byte, 10)
sock.Write([]byte("first send"))
sock.Read(buf)
fmt.Println(string(buf[:]))
}
Remember, UDP is connection-less. When you call conn.Write, your listener doesn't know where to send the packet. In your server code, you should be using UDPConn.ReadFromUDP and UDPConn.WriteToUDP to obtain and specify the client address, as mentioned in the documentation:
The returned connection's ReadFrom and WriteTo methods can be used to receive and send UDP packets with per-packet addressing.
Your modified Node 1 loop could then look something like the following:
for {
b := make([]byte, 10)
n, clientAddr, _ := conn.ReadFromUDP(b) // TODO: error check
fmt.Println(string(b[:n]))
conn.WriteToUDP([]byte("sending back"), clientAddr)
}

How to reliably unlink() a Unix domain socket in Go programming language

I have a Go program hosting a simple HTTP service on localhost:8080 so I can connect my public nginx host to it via the proxy_pass directive, as a reverse proxy to serve part of my site's requests. This is all working great, no problems there.
I want to convert the Go program to host the HTTP service on a Unix domain socket instead of a local TCP socket for improved security and to reduce the unnecessary protocol overhead of TCP.
PROBLEM:
The problem is that Unix domain sockets cannot be reused once they are bind() to, even after program termination. The second time (and every time after) I run the Go program it exits with a fatal error "address already in use".
Common practice is to unlink() Unix domain sockets (i.e. remove the file) when the server shuts down. However, this is tricky in Go. My first attempt was to use the defer statement in my main func (see below), but it is not getting run if I interrupt the process with a signal like CTRL-C. I suppose this is to be expected. Disappointing, but not unexpected.
QUESTION: Is there a best practice on how to unlink() the socket when the server process shuts down (either gracefully or ungracefully)?
Here's part of my func main() that starts the server listening for reference:
// Create the HTTP server listening on the requested socket:
l, err := net.Listen("unix", "/tmp/mysocket")
if err != nil {
log.Fatal(err)
} else {
// Unix sockets must be unlink()ed before being reused again.
// Unfortunately, this defer is not run when a signal is received, e.g. CTRL-C.
defer func() {
os.Remove("/tmp/mysocket")
}()
log.Fatal(http.Serve(l, http.HandlerFunc(indexHtml)))
}
Here is the complete solution I used. The code I posted in my question was a simplified version for clear demonstration purposes.
// Create the socket to listen on:
l, err := net.Listen(socketType, socketAddr)
if err != nil {
log.Fatal(err)
return
}
// Unix sockets must be unlink()ed before being reused again.
// Handle common process-killing signals so we can gracefully shut down:
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
go func(c chan os.Signal) {
// Wait for a SIGINT or SIGKILL:
sig := <-c
log.Printf("Caught signal %s: shutting down.", sig)
// Stop listening (and unlink the socket if unix type):
l.Close()
// And we're done:
os.Exit(0)
}(sigc)
// Start the HTTP server:
log.Fatal(http.Serve(l, http.HandlerFunc(indexHtml)))
I sure hope this is good and effective Go code that would make the Go authors proud. It certainly looks so to me. If it is not, that would be embarrassing on my part. :)
For anyone curious, this is part of https://github.com/JamesDunne/go-index-html which is a simple HTTP directory listing generator with some extra features that web servers don't give you out of the box.
You can end your main func with the signal handler and spawn separate go routines for your other tasks instead. That way, you can leverage the defer mechanism and handle all (signal-based or not) shut downs cleanly:
func main() {
// Create the HTTP server listening on the requested socket:
l, err := net.Listen("unix", "/tmp/mysocket")
if err != nil {
log.Fatal(err)
return
}
// Just work with defer here; this works as long as the signal handling
// happens in the main Go routine.
defer l.Close()
// Make sure the server does not block the main
go func() {
log.Fatal(http.Serve(l, http.HandlerFunc(indexHtml)))
}()
// Use a buffered channel so we don't miss any signals
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
// Block until a signal is received.
s := <-c
fmt.Println("Got signal:", s)
// ...and exit, running all the defer statements
}
In modern Go, you may use the syscall.Unlink() - docs here:
import (
"net"
"syscall"
...
)
...
socketpath := "/tmp/somesocket"
// carry on with your socket creation:
addr, err := net.ResolveUnixAddr("unixgram", socketpath)
if err != nil {
return err;
}
// always remove the named socket from the fs if its there
err = syscall.Unlink(socketpath)
if err != nil {
// not really important if it fails
log.Error("Unlink()",err)
}
// carry on with socket bind()
conn, err := net.ListenUnixgram("unixgram", addr);
if err != nil {
return err;
}

Resources