├── .gitignore ├── pipe.go ├── README.md ├── main.go ├── proxy.go └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | go-proxy-example 3 | -------------------------------------------------------------------------------- /pipe.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net" 6 | ) 7 | 8 | // Copy data between two connections. Return EOF on connection close. 9 | func Pipe(a, b net.Conn) error { 10 | done := make(chan error, 1) 11 | 12 | cp := func(r, w net.Conn) { 13 | n, err := io.Copy(r, w) 14 | logger.Debugf("copied %d bytes from %s to %s", n, r.RemoteAddr(), w.RemoteAddr()) 15 | done <- err 16 | } 17 | 18 | go cp(a, b) 19 | go cp(b, a) 20 | err1 := <-done 21 | err2 := <-done 22 | if err1 != nil { 23 | return err1 24 | } 25 | if err2 != nil { 26 | return err2 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example TCP Proxy 2 | ================= 3 | An example TCP proxy in Go. It listens on an address and proxies them to a 4 | single backend address. 5 | 6 | Build 7 | ----- 8 | If you have your GOPATH variable and directory structure set up correctly: 9 | go get . 10 | go build 11 | 12 | ... and if not: 13 | go get github.com/BlueDragonX/go-log 14 | go build 15 | 16 | Usage 17 | ----- 18 | You need to specify a -listen and -backend option. For example: 19 | 20 | go-proxy-example -listen=0.0.0.0:80 -backend=10.1.1.2:80 21 | 22 | This would proxy all local connections to port 80 to port 80 on the machine at 10.1.1.2. 23 | 24 | License 25 | ------- 26 | Copyright (c) 2015 Ryan Bourgeois. Licensed under BSD-Modified. See the [LICENSE][1] file for a copy of the license. 27 | 28 | [1]: https://raw.githubusercontent.com/BlueDragonX/go-proxy-example/master/LICENSE "LICENSE" 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/BlueDragonX/go-log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | ) 11 | 12 | var logger *log.Logger = log.NewOrExit() 13 | 14 | func main() { 15 | var level, listen, backend string 16 | flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 17 | flags.StringVar(&listen, "listen", "", "Listen for connections on this address.") 18 | flags.StringVar(&backend, "backend", "", "The address of the backend to forward to.") 19 | flags.StringVar(&level, "level", "info", "The logging level.") 20 | flags.Parse(os.Args[1:]) 21 | logger.SetLevel(log.NewLevel(level)) 22 | 23 | if listen == "" || backend == "" { 24 | fmt.Fprintln(os.Stderr, "listen and backend options required") 25 | os.Exit(1) 26 | } 27 | 28 | p := Proxy{Listen: listen, Backend: backend} 29 | 30 | sigs := make(chan os.Signal) 31 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 32 | go func() { 33 | <-sigs 34 | if err := p.Close(); err != nil { 35 | logger.Fatal(err.Error()) 36 | } 37 | }() 38 | 39 | if err := p.Run(); err != nil { 40 | logger.Fatal(err.Error()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | ) 7 | 8 | // Proxy connections from Listen to Backend. 9 | type Proxy struct { 10 | Listen string 11 | Backend string 12 | listener net.Listener 13 | } 14 | 15 | func (p *Proxy) Run() error { 16 | var err error 17 | if p.listener, err = net.Listen("tcp", p.Listen); err != nil { 18 | return err 19 | } 20 | 21 | wg := &sync.WaitGroup{} 22 | for { 23 | if conn, err := p.listener.Accept(); err == nil { 24 | wg.Add(1) 25 | go func() { 26 | defer wg.Done() 27 | p.handle(conn) 28 | }() 29 | } else { 30 | return nil 31 | } 32 | } 33 | wg.Wait() 34 | return nil 35 | } 36 | 37 | func (p *Proxy) Close() error { 38 | return p.listener.Close() 39 | } 40 | 41 | func (p *Proxy) handle(upConn net.Conn) { 42 | defer upConn.Close() 43 | logger.Infof("accepted: %s", upConn.RemoteAddr()) 44 | downConn, err := net.Dial("tcp", p.Backend) 45 | if err != nil { 46 | logger.Errorf("unable to connect to %s: %s", p.Backend, err) 47 | return 48 | } 49 | defer downConn.Close() 50 | if err := Pipe(upConn, downConn); err != nil { 51 | logger.Errorf("pipe failed: %s", err) 52 | } else { 53 | logger.Infof("disconnected: %s", upConn.RemoteAddr()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ryan Bourgeois 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | -Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | -Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | -Neither the name of the project nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | --------------------------------------------------------------------------------