diff --git a/net.go b/net.go new file mode 100644 index 0000000..e59ce2a --- /dev/null +++ b/net.go @@ -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 +} diff --git a/net_freebsd.go b/net_freebsd.go deleted file mode 100644 index a369d5b..0000000 --- a/net_freebsd.go +++ /dev/null @@ -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 -} diff --git a/net_linux.go b/net_linux.go deleted file mode 100644 index 1b5671e..0000000 --- a/net_linux.go +++ /dev/null @@ -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") -} diff --git a/net_test.go b/net_test.go deleted file mode 100644 index f8f8ff3..0000000 --- a/net_test.go +++ /dev/null @@ -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) - }() -} diff --git a/servermanager.go b/servermanager.go index aade57f..e71189c 100644 --- a/servermanager.go +++ b/servermanager.go @@ -11,6 +11,7 @@ import ( "log" "net" "strconv" + "time" ) const ( @@ -31,12 +32,14 @@ type ServerHandler interface { } type ServerManager struct { - name string - ports []int - in chan serverManagerEvent - done chan interface{} - usageScore int // spin down server when this reaches 0 - server ServerHandler + name string + ports []int + in chan serverManagerEvent + done chan interface{} + connStatus chan ConnStatus + lastKeepAliveTime time.Time + usageScore int // spin down server when this reaches 0 + server ServerHandler } 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() { // 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 for running := true; running; { select { + case status := <-me.connStatus: + if status.Status == CONN_ACTIVE { + me.lastKeepAliveTime = time.Now() + } case action := <-me.in: 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. */ -func (me *ServerManager) Spindown(c *net.TCPConn) { +func (me *ServerManager) Spindown() { me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINDOWN} } @@ -95,18 +109,6 @@ func (me *ServerManager) 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) { portStr := strconv.Itoa(port) addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+portStr) @@ -125,14 +127,9 @@ func (me *ServerManager) setupListener(port int) { conn, err := l.AcceptTCP() if err != nil { log.Print("Could not accept TCP connection:", err) - } else { - // connection accepted - + } else { // connection accepted // spinup machine 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) if err == nil { log.Println("ServerManager: Got IP for", me.name+":", targetIp) - localIp := event.tcpConn.LocalAddr().String() - me.addPortForwards(localIp, targetIp) + wanAddr := event.tcpConn.LocalAddr().String() + _, port, _ := net.SplitHostPort(wanAddr) + go portForward(event.tcpConn, targetIp, port, me.connStatus) } else { log.Println("ServerManager: Could not spin up "+me.name+":", err) } case SERVERMANAGER_SPINDOWN: err := me.server.Spindown(me.name) - if err == nil { - me.rmPortForwards() - } else { + if err != nil { log.Println("ServerManager: Could not spin down "+me.name+":", err) } case SERVERMANAGER_STOP: diff --git a/varstate.go b/varstate.go index d6a86ee..b19a2c9 100644 --- a/varstate.go +++ b/varstate.go @@ -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 License, v. 2.0. If a copy of the MPL was not distributed with this