2020-02-11 03:26:04 +03:00
package udp
import (
"io"
"net"
2022-11-21 20:36:05 +03:00
"github.com/rs/zerolog/log"
2020-02-11 03:26:04 +03:00
)
// Proxy is a reverse-proxy implementation of the Handler interface.
type Proxy struct {
// TODO: maybe optimize by pre-resolving it at proxy creation time
target string
}
2020-05-11 13:06:07 +03:00
// NewProxy creates a new Proxy.
2020-02-11 03:26:04 +03:00
func NewProxy ( address string ) ( * Proxy , error ) {
return & Proxy { target : address } , nil
}
// ServeUDP implements the Handler interface.
func ( p * Proxy ) ServeUDP ( conn * Conn ) {
2023-02-15 13:29:28 +03:00
log . Debug ( ) . Msgf ( "Handling UDP stream from %s to %s" , conn . rAddr , p . target )
2020-02-11 03:26:04 +03:00
// needed because of e.g. server.trackedConnection
defer conn . Close ( )
connBackend , err := net . Dial ( "udp" , p . target )
if err != nil {
2023-02-15 13:29:28 +03:00
log . Error ( ) . Err ( err ) . Msg ( "Error while dialing backend" )
2020-02-11 03:26:04 +03:00
return
}
// maybe not needed, but just in case
defer connBackend . Close ( )
errChan := make ( chan error )
2021-11-09 17:12:07 +03:00
go connCopy ( conn , connBackend , errChan )
go connCopy ( connBackend , conn , errChan )
2020-02-11 03:26:04 +03:00
err = <- errChan
if err != nil {
2023-02-15 13:29:28 +03:00
log . Error ( ) . Err ( err ) . Msg ( "Error while handling UDP stream" )
2020-02-11 03:26:04 +03:00
}
<- errChan
}
2021-11-09 17:12:07 +03:00
func connCopy ( dst io . WriteCloser , src io . Reader , errCh chan error ) {
// The buffer is initialized to the maximum UDP datagram size,
// to make sure that the whole UDP datagram is read or written atomically (no data is discarded).
buffer := make ( [ ] byte , maxDatagramSize )
_ , err := io . CopyBuffer ( dst , src , buffer )
2020-02-11 03:26:04 +03:00
errCh <- err
if err := dst . Close ( ) ; err != nil {
2023-02-15 13:29:28 +03:00
log . Debug ( ) . Err ( err ) . Msg ( "Error while terminating UDP stream" )
2020-02-11 03:26:04 +03:00
}
}