Skip to content

Instantly share code, notes, and snippets.

@ericflo
Last active April 17, 2024 21:27
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save ericflo/7dcf4179c315d8bd714c to your computer and use it in GitHub Desktop.
Save ericflo/7dcf4179c315d8bd714c to your computer and use it in GitHub Desktop.
Golang TCP Proxy
package proxy
import (
"io"
"net"
"sync"
log "github.com/Sirupsen/logrus"
)
type Proxy struct {
from string
to string
done chan struct{}
log *log.Entry
}
func NewProxy(from, to string) *Proxy {
return &Proxy{
from: from,
to: to,
done: make(chan struct{}),
log: log.WithFields(log.Fields{
"from": from,
"to": to,
}),
}
}
func (p *Proxy) Start() error {
p.log.Infoln("Starting proxy")
listener, err := net.Listen("tcp", p.from)
if err != nil {
return err
}
go p.run(listener)
return nil
}
func (p *Proxy) Stop() {
p.log.Infoln("Stopping proxy")
if p.done == nil {
return
}
close(p.done)
p.done = nil
}
func (p *Proxy) run(listener net.Listener) {
for {
select {
case <-p.done:
return
default:
connection, err := listener.Accept()
if err == nil {
go p.handle(connection)
} else {
p.log.WithField("err", err).Errorln("Error accepting conn")
}
}
}
}
func (p *Proxy) handle(connection net.Conn) {
p.log.Debugln("Handling", connection)
defer p.log.Debugln("Done handling", connection)
defer connection.Close()
remote, err := net.Dial("tcp", p.to)
if err != nil {
p.log.WithField("err", err).Errorln("Error dialing remote host")
return
}
defer remote.Close()
wg := &sync.WaitGroup{}
wg.Add(2)
go p.copy(remote, connection, wg)
go p.copy(connection, remote, wg)
wg.Wait()
}
func (p *Proxy) copy(from, to net.Conn, wg *sync.WaitGroup) {
defer wg.Done()
select {
case <-p.done:
return
default:
if _, err := io.Copy(to, from); err != nil {
p.log.WithField("err", err).Errorln("Error from copy")
p.Stop()
return
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment