initial
Browse files- go.mod +5 -0
- go.sum +2 -0
- main.go +54 -0
- src/gotunnelme/get_assigned_url.go +45 -0
- src/gotunnelme/get_assigned_url_test.go +15 -0
- src/gotunnelme/tunnel.go +235 -0
- src/gotunnelme/tunnel_test.go +25 -0
go.mod
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module gotunnelme/go_localt
|
2 |
+
|
3 |
+
go 1.22.1
|
4 |
+
|
5 |
+
require github.com/NoahShen/gotunnelme v0.0.0-20180106044115-fbc9b0b77df8 // indirect
|
go.sum
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
github.com/NoahShen/gotunnelme v0.0.0-20180106044115-fbc9b0b77df8 h1:9PnWTGpbxbF4rwTL7q+5ozPyG/pr1NfXXaFTmLXkb5s=
|
2 |
+
github.com/NoahShen/gotunnelme v0.0.0-20180106044115-fbc9b0b77df8/go.mod h1:4/5P3Xn6TPHLWG1iHNwvob7WPT3+kr8wBcW/xueIqe4=
|
main.go
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package main
|
2 |
+
|
3 |
+
import (
|
4 |
+
"fmt"
|
5 |
+
"github.com/NoahShen/gotunnelme/src/gotunnelme"
|
6 |
+
"io/ioutil"
|
7 |
+
"net/http"
|
8 |
+
"os"
|
9 |
+
"strconv"
|
10 |
+
)
|
11 |
+
|
12 |
+
func getExternalIP() (string, error) {
|
13 |
+
resp, err := http.Get("http://ipinfo.io/ip")
|
14 |
+
if err != nil {
|
15 |
+
return "", err
|
16 |
+
}
|
17 |
+
defer resp.Body.Close()
|
18 |
+
ip, err := ioutil.ReadAll(resp.Body)
|
19 |
+
if err != nil {
|
20 |
+
return "", err
|
21 |
+
}
|
22 |
+
return string(ip), nil
|
23 |
+
}
|
24 |
+
|
25 |
+
func main() {
|
26 |
+
if len(os.Args) == 1 {
|
27 |
+
fmt.Fprintln(os.Stderr, "Usage: go_localt <local port>")
|
28 |
+
os.Exit(1)
|
29 |
+
}
|
30 |
+
localPort, err := strconv.Atoi(os.Args[1])
|
31 |
+
if err != nil {
|
32 |
+
fmt.Fprintln(os.Stderr, err)
|
33 |
+
os.Exit(1)
|
34 |
+
}
|
35 |
+
|
36 |
+
t := gotunnelme.NewTunnel()
|
37 |
+
url, err := t.GetUrl("")
|
38 |
+
if err != nil {
|
39 |
+
panic(err)
|
40 |
+
}
|
41 |
+
fmt.Println(url)
|
42 |
+
|
43 |
+
extIP, err := getExternalIP()
|
44 |
+
if err != nil {
|
45 |
+
panic(err)
|
46 |
+
}
|
47 |
+
fmt.Println(extIP)
|
48 |
+
|
49 |
+
err = t.CreateTunnel(localPort)
|
50 |
+
if err != nil {
|
51 |
+
panic(err)
|
52 |
+
}
|
53 |
+
t.StopTunnel()
|
54 |
+
}
|
src/gotunnelme/get_assigned_url.go
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package gotunnelme
|
2 |
+
|
3 |
+
import (
|
4 |
+
"encoding/json"
|
5 |
+
"fmt"
|
6 |
+
"io/ioutil"
|
7 |
+
"net/http"
|
8 |
+
)
|
9 |
+
|
10 |
+
const (
|
11 |
+
localtunnelServer = "http://localtunnel.me/"
|
12 |
+
)
|
13 |
+
|
14 |
+
type AssignedUrlInfo struct {
|
15 |
+
Id string `json:"id,omitempty"`
|
16 |
+
Url string `json:"url,omitempty"`
|
17 |
+
Port int `json:"port,omitempty"`
|
18 |
+
MaxConnCount int `json:"max_conn_count,omitempty"`
|
19 |
+
}
|
20 |
+
|
21 |
+
func GetAssignedUrl(assignedDomain string) (*AssignedUrlInfo, error) {
|
22 |
+
if len(assignedDomain) == 0 {
|
23 |
+
assignedDomain = "?new"
|
24 |
+
}
|
25 |
+
url := fmt.Sprintf(localtunnelServer+"%s", assignedDomain)
|
26 |
+
request, _ := http.NewRequest("GET", url, nil)
|
27 |
+
response, httpErr := http.DefaultClient.Do(request)
|
28 |
+
if httpErr != nil {
|
29 |
+
return nil, httpErr
|
30 |
+
}
|
31 |
+
defer response.Body.Close()
|
32 |
+
bytes, readErr := ioutil.ReadAll(response.Body)
|
33 |
+
if readErr != nil {
|
34 |
+
return nil, readErr
|
35 |
+
}
|
36 |
+
if Debug {
|
37 |
+
fmt.Printf("***GetAssignedUrl: %s\n", string(bytes))
|
38 |
+
}
|
39 |
+
|
40 |
+
assignedUrlInfo := &AssignedUrlInfo{}
|
41 |
+
if unmarshalErr := json.Unmarshal(bytes, assignedUrlInfo); unmarshalErr != nil {
|
42 |
+
return nil, unmarshalErr
|
43 |
+
}
|
44 |
+
return assignedUrlInfo, nil
|
45 |
+
}
|
src/gotunnelme/get_assigned_url_test.go
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package gotunnelme
|
2 |
+
|
3 |
+
import (
|
4 |
+
"fmt"
|
5 |
+
"testing"
|
6 |
+
)
|
7 |
+
|
8 |
+
func _TestGetAssignedUrl(t *testing.T) {
|
9 |
+
Debug = false
|
10 |
+
assignedUrlInfo, err := GetAssignedUrl("noah")
|
11 |
+
if err != nil {
|
12 |
+
t.Fatal(err)
|
13 |
+
}
|
14 |
+
fmt.Printf("assignedUrlInfo: %+v\n", assignedUrlInfo)
|
15 |
+
}
|
src/gotunnelme/tunnel.go
ADDED
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package gotunnelme
|
2 |
+
|
3 |
+
import (
|
4 |
+
"bufio"
|
5 |
+
"errors"
|
6 |
+
"fmt"
|
7 |
+
"io"
|
8 |
+
"net"
|
9 |
+
"net/http"
|
10 |
+
"net/url"
|
11 |
+
"os"
|
12 |
+
"strings"
|
13 |
+
)
|
14 |
+
|
15 |
+
var Debug = false
|
16 |
+
|
17 |
+
type TunnelConn struct {
|
18 |
+
remoteHost string
|
19 |
+
remotePort int
|
20 |
+
localPort int
|
21 |
+
remoteConn net.Conn
|
22 |
+
localConn net.Conn
|
23 |
+
errorChannel chan error
|
24 |
+
}
|
25 |
+
|
26 |
+
func NewTunnelConn(remoteHost string, remotePort, localPort int) *TunnelConn {
|
27 |
+
tunnelConn := &TunnelConn{}
|
28 |
+
tunnelConn.remoteHost = remoteHost
|
29 |
+
tunnelConn.remotePort = remotePort
|
30 |
+
tunnelConn.localPort = localPort
|
31 |
+
return tunnelConn
|
32 |
+
}
|
33 |
+
|
34 |
+
func (self *TunnelConn) Tunnel(replyCh chan<- int) error {
|
35 |
+
self.errorChannel = make(chan error, 1) // clear previous channel's message
|
36 |
+
remoteConn, remoteErr := self.connectRemote()
|
37 |
+
if remoteErr != nil {
|
38 |
+
if Debug {
|
39 |
+
fmt.Printf("Connect remote error[%s]!\n", remoteErr.Error())
|
40 |
+
}
|
41 |
+
replyCh <- -1
|
42 |
+
return remoteErr
|
43 |
+
}
|
44 |
+
|
45 |
+
if Debug {
|
46 |
+
fmt.Printf("Connect remote[%s:%d] successful!\n", self.remoteHost, self.remotePort)
|
47 |
+
}
|
48 |
+
|
49 |
+
localConn, localErr := self.connectLocal()
|
50 |
+
if localErr != nil {
|
51 |
+
if Debug {
|
52 |
+
fmt.Printf("Connect local error[%s]!\n", localErr.Error())
|
53 |
+
}
|
54 |
+
replyCh <- -1
|
55 |
+
return localErr
|
56 |
+
}
|
57 |
+
if Debug {
|
58 |
+
fmt.Printf("Connect local[:%d] successful!\n", self.localPort)
|
59 |
+
}
|
60 |
+
|
61 |
+
self.remoteConn = remoteConn
|
62 |
+
self.localConn = localConn
|
63 |
+
go func() {
|
64 |
+
var err error
|
65 |
+
for {
|
66 |
+
_, err = io.Copy(remoteConn, localConn)
|
67 |
+
if err != nil {
|
68 |
+
if Debug {
|
69 |
+
fmt.Printf("Stop copy form local to remote! error=[%v]\n", err)
|
70 |
+
}
|
71 |
+
break
|
72 |
+
}
|
73 |
+
}
|
74 |
+
self.errorChannel <- err
|
75 |
+
}()
|
76 |
+
go func() {
|
77 |
+
var err error
|
78 |
+
for {
|
79 |
+
_, err = io.Copy(localConn, remoteConn)
|
80 |
+
if err != nil {
|
81 |
+
if Debug {
|
82 |
+
fmt.Printf("Stop copy form remote to local! error=[%v]\n", err)
|
83 |
+
}
|
84 |
+
break
|
85 |
+
}
|
86 |
+
|
87 |
+
}
|
88 |
+
self.errorChannel <- err
|
89 |
+
}()
|
90 |
+
err := <-self.errorChannel
|
91 |
+
replyCh <- 1
|
92 |
+
return err
|
93 |
+
}
|
94 |
+
|
95 |
+
func (self *TunnelConn) StopTunnel() error {
|
96 |
+
if self.remoteConn != nil {
|
97 |
+
self.remoteConn.Close()
|
98 |
+
}
|
99 |
+
if self.localConn != nil {
|
100 |
+
self.localConn.Close()
|
101 |
+
}
|
102 |
+
return nil
|
103 |
+
}
|
104 |
+
|
105 |
+
func (self *TunnelConn) connectRemote() (net.Conn, error) {
|
106 |
+
remoteAddr := fmt.Sprintf("%s:%d", self.remoteHost, self.remotePort)
|
107 |
+
addr := remoteAddr
|
108 |
+
proxy := os.Getenv("HTTP_PROXY")
|
109 |
+
if proxy == "" {
|
110 |
+
proxy = os.Getenv("http_proxy")
|
111 |
+
}
|
112 |
+
if len(proxy) > 0 {
|
113 |
+
url, err := url.Parse(proxy)
|
114 |
+
if err == nil {
|
115 |
+
addr = url.Host
|
116 |
+
}
|
117 |
+
}
|
118 |
+
remoteConn, remoteErr := net.Dial("tcp", addr)
|
119 |
+
if remoteErr != nil {
|
120 |
+
return nil, remoteErr
|
121 |
+
}
|
122 |
+
|
123 |
+
if len(proxy) > 0 {
|
124 |
+
fmt.Fprintf(remoteConn, "CONNECT %s HTTP/1.1\r\n", remoteAddr)
|
125 |
+
fmt.Fprintf(remoteConn, "Host: %s\r\n", remoteAddr)
|
126 |
+
fmt.Fprintf(remoteConn, "\r\n")
|
127 |
+
br := bufio.NewReader(remoteConn)
|
128 |
+
req, _ := http.NewRequest("CONNECT", remoteAddr, nil)
|
129 |
+
resp, readRespErr := http.ReadResponse(br, req)
|
130 |
+
if readRespErr != nil {
|
131 |
+
return nil, readRespErr
|
132 |
+
}
|
133 |
+
if resp.StatusCode != 200 {
|
134 |
+
f := strings.SplitN(resp.Status, " ", 2)
|
135 |
+
return nil, errors.New(f[1])
|
136 |
+
}
|
137 |
+
|
138 |
+
if Debug {
|
139 |
+
fmt.Printf("Connect %s by proxy[%s].\n", remoteAddr, proxy)
|
140 |
+
}
|
141 |
+
}
|
142 |
+
return remoteConn, nil
|
143 |
+
}
|
144 |
+
|
145 |
+
func (self *TunnelConn) connectLocal() (net.Conn, error) {
|
146 |
+
localAddr := fmt.Sprintf("%s:%d", "localhost", self.localPort)
|
147 |
+
return net.Dial("tcp", localAddr)
|
148 |
+
}
|
149 |
+
|
150 |
+
type TunnelCommand int
|
151 |
+
|
152 |
+
const (
|
153 |
+
stopTunnelCmd TunnelCommand = 1
|
154 |
+
)
|
155 |
+
|
156 |
+
type Tunnel struct {
|
157 |
+
assignedUrlInfo *AssignedUrlInfo
|
158 |
+
localPort int
|
159 |
+
tunnelConns []*TunnelConn
|
160 |
+
cmdChan chan TunnelCommand
|
161 |
+
}
|
162 |
+
|
163 |
+
func NewTunnel() *Tunnel {
|
164 |
+
tunnel := &Tunnel{}
|
165 |
+
tunnel.cmdChan = make(chan TunnelCommand, 1)
|
166 |
+
return tunnel
|
167 |
+
}
|
168 |
+
|
169 |
+
func (self *Tunnel) startTunnel() error {
|
170 |
+
if err := self.checkLocalPort(); err != nil {
|
171 |
+
return err
|
172 |
+
}
|
173 |
+
url, parseErr := url.Parse(localtunnelServer)
|
174 |
+
if parseErr != nil {
|
175 |
+
return parseErr
|
176 |
+
}
|
177 |
+
replyCh := make(chan int, self.assignedUrlInfo.MaxConnCount)
|
178 |
+
remoteHost := url.Host
|
179 |
+
for i := 0; i < self.assignedUrlInfo.MaxConnCount; i++ {
|
180 |
+
tunnelConn := NewTunnelConn(remoteHost, self.assignedUrlInfo.Port, self.localPort)
|
181 |
+
self.tunnelConns[i] = tunnelConn
|
182 |
+
go tunnelConn.Tunnel(replyCh)
|
183 |
+
}
|
184 |
+
L:
|
185 |
+
for i := 0; i < self.assignedUrlInfo.MaxConnCount; i++ {
|
186 |
+
select {
|
187 |
+
case <-replyCh:
|
188 |
+
case cmd := <-self.cmdChan:
|
189 |
+
switch cmd {
|
190 |
+
case stopTunnelCmd:
|
191 |
+
break L
|
192 |
+
}
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
return nil
|
197 |
+
}
|
198 |
+
|
199 |
+
func (self *Tunnel) checkLocalPort() error {
|
200 |
+
localAddr := fmt.Sprintf("%s:%d", "localhost", self.localPort)
|
201 |
+
c, err := net.Dial("tcp", localAddr)
|
202 |
+
if err != nil {
|
203 |
+
return errors.New("can't connect local port!")
|
204 |
+
}
|
205 |
+
c.Close()
|
206 |
+
return nil
|
207 |
+
}
|
208 |
+
|
209 |
+
func (self *Tunnel) StopTunnel() {
|
210 |
+
if Debug {
|
211 |
+
fmt.Printf("Stop tunnel for localPort[%d]!\n", self.localPort)
|
212 |
+
}
|
213 |
+
self.cmdChan <- stopTunnelCmd
|
214 |
+
for _, tunnelCon := range self.tunnelConns {
|
215 |
+
tunnelCon.StopTunnel()
|
216 |
+
}
|
217 |
+
}
|
218 |
+
|
219 |
+
func (self *Tunnel) GetUrl(assignedDomain string) (string, error) {
|
220 |
+
if len(assignedDomain) == 0 {
|
221 |
+
assignedDomain = "?new"
|
222 |
+
}
|
223 |
+
assignedUrlInfo, err := GetAssignedUrl(assignedDomain)
|
224 |
+
if err != nil {
|
225 |
+
return "", err
|
226 |
+
}
|
227 |
+
self.assignedUrlInfo = assignedUrlInfo
|
228 |
+
self.tunnelConns = make([]*TunnelConn, assignedUrlInfo.MaxConnCount)
|
229 |
+
return assignedUrlInfo.Url, nil
|
230 |
+
}
|
231 |
+
|
232 |
+
func (self *Tunnel) CreateTunnel(localPort int) error {
|
233 |
+
self.localPort = localPort
|
234 |
+
return self.startTunnel()
|
235 |
+
}
|
src/gotunnelme/tunnel_test.go
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package gotunnelme
|
2 |
+
|
3 |
+
import (
|
4 |
+
"fmt"
|
5 |
+
"testing"
|
6 |
+
"time"
|
7 |
+
)
|
8 |
+
|
9 |
+
func TestTunnel(t *testing.T) {
|
10 |
+
Debug = true
|
11 |
+
tunnel := NewTunnel()
|
12 |
+
url, getUrlErr := tunnel.GetUrl("noah")
|
13 |
+
if getUrlErr != nil {
|
14 |
+
t.Fatal(getUrlErr)
|
15 |
+
}
|
16 |
+
fmt.Println("Get Url:", url)
|
17 |
+
go func() {
|
18 |
+
tunnelErr := tunnel.CreateTunnel(8787)
|
19 |
+
if tunnelErr != nil {
|
20 |
+
t.Fatal(tunnelErr)
|
21 |
+
}
|
22 |
+
}()
|
23 |
+
time.Sleep(30 * time.Second)
|
24 |
+
tunnel.StopTunnel()
|
25 |
+
}
|