Add standalone, pure Go port forwarding
This commit is contained in:
		
							
								
								
									
										54
									
								
								net.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								net.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					   Copyright 2016-2017 gtalent2@gmail.com
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   This Source Code Form is subject to the terms of the Mozilla Public
 | 
				
			||||||
 | 
					   License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
				
			||||||
 | 
					   file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						CONN_ACTIVE = iota
 | 
				
			||||||
 | 
						CONN_DISCONNECTED
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ConnStatus struct {
 | 
				
			||||||
 | 
						Status int
 | 
				
			||||||
 | 
						Err    error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func portForward(wanConn *net.TCPConn, lanIp, port string, connStatus chan ConnStatus) {
 | 
				
			||||||
 | 
						done := make(chan error)
 | 
				
			||||||
 | 
						log.Print("Proxy: Connecting to ", lanIp+":"+port)
 | 
				
			||||||
 | 
						lanConn, err := net.Dial("tcp", lanIp+":"+port)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Print("Proxy: LAN dial error:", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go forwardConn(wanConn, lanConn, done)
 | 
				
			||||||
 | 
						go forwardConn(lanConn, wanConn, done)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < 2; i++ {
 | 
				
			||||||
 | 
							err = <-done
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Print("Proxy:", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wanConn.Close()
 | 
				
			||||||
 | 
						lanConn.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						connStatus <- ConnStatus{Status: CONN_DISCONNECTED, Err: err}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func forwardConn(writer, reader net.Conn, done chan error) {
 | 
				
			||||||
 | 
						_, err := io.Copy(writer, reader)
 | 
				
			||||||
 | 
						done <- err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,51 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
   Copyright 2016-2017 gtalent2@gmail.com
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   This Source Code Form is subject to the terms of the Mozilla Public
 | 
					 | 
				
			||||||
   License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					 | 
				
			||||||
   file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func addPortForward(ruleName, ip, port string) {
 | 
					 | 
				
			||||||
	pfrule := "\"rdr pass on $dospin_ext_if proto { tcp, udp } from any to any port {" + port + "} -> " + ip + "\""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	in, err := exec.Command("pfctl", "-a", "\"dospin_"+ruleName+"\"", "-f", "-").StdinPipe()
 | 
					 | 
				
			||||||
	defer in.Close()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Println("Port Forwarding:", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, err = in.Write([]byte(pfrule))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Println("Port Forwarding:", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func rmPortForward(ruleName string) {
 | 
					 | 
				
			||||||
	_, err := exec.Command("pfctl", "-a", "\"dospin_"+ruleName+"\"", "-F", "rules").Output()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Println("Port Forwarding:", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func portUsageCount(ports ...int) int {
 | 
					 | 
				
			||||||
	cmd := "sockstat"
 | 
					 | 
				
			||||||
	args := []string{"-4c"}
 | 
					 | 
				
			||||||
	for _, v := range ports {
 | 
					 | 
				
			||||||
		args = append(args, "-p")
 | 
					 | 
				
			||||||
		args = append(args, strconv.Itoa(v))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	out, err := exec.Command(cmd, args...).Output()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Println("Port Usage Check: Could not run \""+cmd+"\":", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return bytes.Count(out, []byte{'\n'}) - 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										32
									
								
								net_linux.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								net_linux.go
									
									
									
									
									
								
							@@ -1,32 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
   Copyright 2016-2017 gtalent2@gmail.com
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   This Source Code Form is subject to the terms of the Mozilla Public
 | 
					 | 
				
			||||||
   License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					 | 
				
			||||||
   file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func addPortForward(ruleName, localIp, remoteIp, port string) {
 | 
					 | 
				
			||||||
	log.Println("Setting up port", port, "to", remoteIp)
 | 
					 | 
				
			||||||
	cmdOut, err := exec.Command("iptables", "-t", "nat", "-A", "PREROUTING", "-p", "tcp", "--dport", port, "-j", "DNAT", "--to-destination", remoteIp+":"+port).Output()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Println("iptables error:", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	log.Println(cmdOut)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmdOut, err = exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-p", "tcp", "-d", localIp, "--dport", port, "-j", "SNAT", "--to-source", remoteIp).Output()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Println("iptables error:", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	log.Println("iptables", "-t", "nat", "-A", "POSTROUTING", "-p", "tcp", "-d", localIp, "--dport", port, "-j", "SNAT", "--to-source", remoteIp)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func rmPortForward(ruleName string) {
 | 
					 | 
				
			||||||
	log.Print("Port forwarding not currently implemented for Linux/iptables")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										23
									
								
								net_test.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								net_test.go
									
									
									
									
									
								
							@@ -1,23 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
   Copyright 2016-2017 gtalent2@gmail.com
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   This Source Code Form is subject to the terms of the Mozilla Public
 | 
					 | 
				
			||||||
   License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					 | 
				
			||||||
   file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"net"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestPortCount(t *testing.T) {
 | 
					 | 
				
			||||||
	port := 49214
 | 
					 | 
				
			||||||
	// listen on some port that nothing should be using for the sake of the test
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		addr, _ := net.ResolveTCPAddr("tcp", "0.0.0.0:"+strconv.Itoa(port))
 | 
					 | 
				
			||||||
		net.ListenTCP("tcp", addr)
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -31,12 +32,14 @@ type ServerHandler interface {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ServerManager struct {
 | 
					type ServerManager struct {
 | 
				
			||||||
	name       string
 | 
						name              string
 | 
				
			||||||
	ports      []int
 | 
						ports             []int
 | 
				
			||||||
	in         chan serverManagerEvent
 | 
						in                chan serverManagerEvent
 | 
				
			||||||
	done       chan interface{}
 | 
						done              chan interface{}
 | 
				
			||||||
	usageScore int // spin down server when this reaches 0
 | 
						connStatus        chan ConnStatus
 | 
				
			||||||
	server     ServerHandler
 | 
						lastKeepAliveTime time.Time
 | 
				
			||||||
 | 
						usageScore        int // spin down server when this reaches 0
 | 
				
			||||||
 | 
						server            ServerHandler
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewServerManager(name string, server ServerHandler, settings Settings) *ServerManager {
 | 
					func NewServerManager(name string, server ServerHandler, settings Settings) *ServerManager {
 | 
				
			||||||
@@ -58,11 +61,22 @@ func NewServerManager(name string, server ServerHandler, settings Settings) *Ser
 | 
				
			|||||||
func (me *ServerManager) Serve() {
 | 
					func (me *ServerManager) Serve() {
 | 
				
			||||||
	// TODO: see if server is currently up, and setup port forwarding if so
 | 
						// TODO: see if server is currently up, and setup port forwarding if so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fiveMin := time.Duration(5) * time.Minute
 | 
				
			||||||
 | 
						ticker := time.NewTicker(fiveMin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// event loop
 | 
						// event loop
 | 
				
			||||||
	for running := true; running; {
 | 
						for running := true; running; {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
 | 
							case status := <-me.connStatus:
 | 
				
			||||||
 | 
								if status.Status == CONN_ACTIVE {
 | 
				
			||||||
 | 
									me.lastKeepAliveTime = time.Now()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		case action := <-me.in:
 | 
							case action := <-me.in:
 | 
				
			||||||
			running = me.serveAction(action)
 | 
								running = me.serveAction(action)
 | 
				
			||||||
 | 
							case <-ticker.C:
 | 
				
			||||||
 | 
								if time.Since(me.lastKeepAliveTime) > fiveMin {
 | 
				
			||||||
 | 
									me.Spindown()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,7 +94,7 @@ func (me *ServerManager) Spinup(c *net.TCPConn) {
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 Sends the serve loop a spindown message.
 | 
					 Sends the serve loop a spindown message.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
func (me *ServerManager) Spindown(c *net.TCPConn) {
 | 
					func (me *ServerManager) Spindown() {
 | 
				
			||||||
	me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINDOWN}
 | 
						me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINDOWN}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,18 +109,6 @@ func (me *ServerManager) Done() {
 | 
				
			|||||||
	<-me.done
 | 
						<-me.done
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (me *ServerManager) addPortForwards(localIp, remoteIp string) {
 | 
					 | 
				
			||||||
	log.Println("Ports:", me.ports)
 | 
					 | 
				
			||||||
	for _, p := range me.ports {
 | 
					 | 
				
			||||||
		port := strconv.Itoa(p)
 | 
					 | 
				
			||||||
		addPortForward(me.name, localIp, remoteIp, port)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (me *ServerManager) rmPortForwards() {
 | 
					 | 
				
			||||||
	rmPortForward(me.name)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (me *ServerManager) setupListener(port int) {
 | 
					func (me *ServerManager) setupListener(port int) {
 | 
				
			||||||
	portStr := strconv.Itoa(port)
 | 
						portStr := strconv.Itoa(port)
 | 
				
			||||||
	addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+portStr)
 | 
						addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+portStr)
 | 
				
			||||||
@@ -125,14 +127,9 @@ func (me *ServerManager) setupListener(port int) {
 | 
				
			|||||||
				conn, err := l.AcceptTCP()
 | 
									conn, err := l.AcceptTCP()
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					log.Print("Could not accept TCP connection:", err)
 | 
										log.Print("Could not accept TCP connection:", err)
 | 
				
			||||||
				} else {
 | 
									} else { // connection accepted
 | 
				
			||||||
					// connection accepted
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// spinup machine
 | 
										// spinup machine
 | 
				
			||||||
					me.Spinup(conn)
 | 
										me.Spinup(conn)
 | 
				
			||||||
 | 
					 | 
				
			||||||
					// close existing connection, not doing anything with it
 | 
					 | 
				
			||||||
					conn.Close()
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -146,16 +143,15 @@ func (me *ServerManager) serveAction(event serverManagerEvent) bool {
 | 
				
			|||||||
		targetIp, err := me.server.Spinup(me.name)
 | 
							targetIp, err := me.server.Spinup(me.name)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			log.Println("ServerManager: Got IP for", me.name+":", targetIp)
 | 
								log.Println("ServerManager: Got IP for", me.name+":", targetIp)
 | 
				
			||||||
			localIp := event.tcpConn.LocalAddr().String()
 | 
								wanAddr := event.tcpConn.LocalAddr().String()
 | 
				
			||||||
			me.addPortForwards(localIp, targetIp)
 | 
								_, port, _ := net.SplitHostPort(wanAddr)
 | 
				
			||||||
 | 
								go portForward(event.tcpConn, targetIp, port, me.connStatus)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			log.Println("ServerManager: Could not spin up "+me.name+":", err)
 | 
								log.Println("ServerManager: Could not spin up "+me.name+":", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case SERVERMANAGER_SPINDOWN:
 | 
						case SERVERMANAGER_SPINDOWN:
 | 
				
			||||||
		err := me.server.Spindown(me.name)
 | 
							err := me.server.Spindown(me.name)
 | 
				
			||||||
		if err == nil {
 | 
							if err != nil {
 | 
				
			||||||
			me.rmPortForwards()
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			log.Println("ServerManager: Could not spin down "+me.name+":", err)
 | 
								log.Println("ServerManager: Could not spin down "+me.name+":", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case SERVERMANAGER_STOP:
 | 
						case SERVERMANAGER_STOP:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
   Copyright 2016 gtalent2@gmail.com
 | 
					   Copyright 2016-2017 gtalent2@gmail.com
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   This Source Code Form is subject to the terms of the Mozilla Public
 | 
					   This Source Code Form is subject to the terms of the Mozilla Public
 | 
				
			||||||
   License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					   License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user