/
tunnel.go
92 lines (77 loc) · 2.2 KB
/
tunnel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package nanoagent
import (
"fmt"
"io"
"net"
"net/http"
"strings"
"syscall"
"github.com/nanobox-io/nanobox/util"
"github.com/nanobox-io/nanobox/util/display"
)
func Tunnel(key, location, port, name string) error {
if port == "0" {
return fmt.Errorf("port 0 is not a valid port - that component has nothing to tunnel to")
}
// establish a connection and just leave it open.
req, err := http.NewRequest("POST", fmt.Sprintf("/tunnel?key=%s", key), nil)
if err != nil {
return fmt.Errorf("failed to generate a request for nanoagent: %s", err.Error())
}
// set noproxy because this connection allows more multiple connections
// to use the tunnel
req.Header.Set("X-NOPROXY", "true")
conn, err := connect(req, location)
if err != nil {
return err
}
defer conn.Close()
if port == "22" {
port = "2022"
}
// setup a tcp listener
serv, err := net.Listen("tcp4", fmt.Sprintf(":%s", port))
if err != nil {
err2 := util.Err{
Code: "TUNNEL",
Message: err.Error(),
}
if strings.Contains(err.Error(), "address already in use") || err == syscall.EADDRINUSE {
display.PortInUse(port)
err2.Code = "USER"
err2.Suggest = fmt.Sprintf("It appears your local port (%s) is in use. Please specify a different port with '-p'.", port)
}
if strings.Contains(err.Error(), "bind: permission denied") || err == syscall.EACCES {
display.PortPrivileged(port)
err2.Code = "USER"
err2.Suggest = fmt.Sprintf("It appears you don't have permission to use port '%s'. Please specify a different port with '-p'.", port)
}
return util.ErrorAppend(err2, "failed to setup tcp listener")
}
display.TunnelEstablished(name, port)
// handle connections
for {
conn, err := serv.Accept()
if err != nil {
return fmt.Errorf("failed to accept client connection: %s", err.Error())
}
go handleConnection(conn, key, location)
}
return nil
}
func handleConnection(conn net.Conn, key, location string) {
req, err := http.NewRequest("POST", fmt.Sprintf("/tunnel?key=%s", key), nil)
if err != nil {
return
}
remoteConn, err := connect(req, location)
if err != nil {
return
}
defer remoteConn.Close()
go io.Copy(conn, remoteConn)
_, err = io.Copy(remoteConn, conn)
if err != nil {
return
}
}