Skip to content

Commit 2359f3a

Browse files
committed
http proxy
1 parent a174009 commit 2359f3a

File tree

4 files changed

+166
-3
lines changed

4 files changed

+166
-3
lines changed

client/client.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"bufio"
45
"crypto/tls"
56
"errors"
67
"io"
@@ -37,6 +38,7 @@ func (c *Client) Serve() error {
3738
return err
3839
}
3940
log.Printf("Client starts to listen socks5://%s", listener.Addr().String())
41+
log.Printf("Client starts to listen http://%s", listener.Addr().String())
4042

4143
for {
4244
conn, err := listener.Accept()
@@ -45,8 +47,41 @@ func (c *Client) Serve() error {
4547
continue
4648
}
4749

48-
go c.handler(conn)
50+
go func() {
51+
br := bufio.NewReader(conn)
52+
handler, err := probeProtocol(br)
53+
if err != nil {
54+
conn.Close()
55+
log.Printf("Probe protocol failed: %s", err)
56+
return
57+
}
58+
59+
handler(c, &bufferedConn{conn, br})
60+
}()
61+
}
62+
}
63+
64+
func probeProtocol(br *bufio.Reader) (func(*Client, net.Conn), error) {
65+
b, err := br.Peek(1)
66+
if err != nil {
67+
return nil, err
4968
}
69+
70+
switch b[0] {
71+
case socks.Version:
72+
return (*Client).socks5Handler, nil
73+
default:
74+
return (*Client).httpHandler, nil
75+
}
76+
}
77+
78+
type bufferedConn struct {
79+
net.Conn
80+
br *bufio.Reader
81+
}
82+
83+
func (c *bufferedConn) Read(b []byte) (int, error) {
84+
return c.br.Read(b)
5085
}
5186

5287
var protocol2wrapper = map[string]func(*Client, net.Conn) net.Conn{

client/http.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ import (
88
"fmt"
99
"io"
1010
"io/ioutil"
11+
"log"
1112
"net"
1213
"net/http"
1314
"net/url"
15+
16+
"github.com/luyuhuang/subsocks/socks"
17+
"github.com/luyuhuang/subsocks/utils"
1418
)
1519

1620
func (c *Client) wrapHTTPS(conn net.Conn) net.Conn {
@@ -21,6 +25,130 @@ func (c *Client) wrapHTTP(conn net.Conn) net.Conn {
2125
return newHTTPWrapper(conn, c)
2226
}
2327

28+
func isValidHTTPProxyRequest(req *http.Request) bool {
29+
if req.URL.Host == "" {
30+
return false
31+
}
32+
if req.Method != http.MethodConnect && req.URL.Scheme != "http" {
33+
return false
34+
}
35+
return true
36+
}
37+
38+
func httpReply(statusCode int, status string) *http.Response {
39+
return &http.Response{
40+
ProtoMajor: 1,
41+
ProtoMinor: 1,
42+
StatusCode: statusCode,
43+
Status: status,
44+
}
45+
}
46+
47+
func (c *Client) httpHandler(conn net.Conn) {
48+
defer conn.Close()
49+
50+
req, err := http.ReadRequest(bufio.NewReader(conn))
51+
if err != nil {
52+
log.Printf("[http] read HTTP request failed: %s", err)
53+
return
54+
}
55+
defer req.Body.Close()
56+
57+
if !isValidHTTPProxyRequest(req) {
58+
log.Printf("[http] invalid http proxy request: %v", req)
59+
httpReply(http.StatusBadRequest, "").Write(conn)
60+
return
61+
}
62+
63+
host := req.URL.Hostname()
64+
addr := req.URL.Host
65+
if req.URL.Port() == "" {
66+
addr = net.JoinHostPort(addr, "80")
67+
}
68+
69+
var nextHop net.Conn
70+
var isProxy bool
71+
if rule := c.Rules.getRule(host); rule == ruleProxy {
72+
log.Printf(`[http] dial server to connect %s for %s`, addr, conn.RemoteAddr())
73+
74+
isProxy = true
75+
nextHop, err = c.dialServer()
76+
if err != nil {
77+
log.Printf(`[http] dial server failed: %s`, err)
78+
httpReply(http.StatusServiceUnavailable, "").Write(conn)
79+
return
80+
}
81+
82+
} else {
83+
log.Printf(`[http] dial %s for %s`, addr, conn.RemoteAddr())
84+
85+
nextHop, err = net.Dial("tcp", addr)
86+
if err != nil {
87+
if rule == ruleAuto {
88+
log.Printf(`[http] dial %s failed, dial server for %s`, addr, conn.RemoteAddr())
89+
90+
isProxy = true
91+
nextHop, err = c.dialServer()
92+
if err != nil {
93+
log.Printf(`[http] dial server failed: %s`, err)
94+
httpReply(http.StatusServiceUnavailable, "").Write(conn)
95+
return
96+
}
97+
c.Rules.setAsProxy(host)
98+
99+
} else {
100+
log.Printf(`[http] dial remote failed: %s`, err)
101+
httpReply(http.StatusServiceUnavailable, "").Write(conn)
102+
return
103+
}
104+
}
105+
106+
}
107+
defer nextHop.Close()
108+
109+
var dash rune
110+
if isProxy {
111+
socksAddr, _ := socks.NewAddr(addr)
112+
if err = socks.NewRequest(socks.CmdConnect, socksAddr).Write(nextHop); err != nil {
113+
log.Printf(`[http] send request failed: %s`, err)
114+
httpReply(http.StatusServiceUnavailable, "").Write(conn)
115+
return
116+
}
117+
if r, e := socks.ReadReply(nextHop); e != nil {
118+
log.Printf(`[http] read reply failed: %s`, err)
119+
httpReply(http.StatusServiceUnavailable, "").Write(conn)
120+
return
121+
} else if r.Rep != socks.Succeeded {
122+
log.Printf(`[http] server connect failed: %q`, r)
123+
httpReply(http.StatusServiceUnavailable, "").Write(conn)
124+
return
125+
}
126+
127+
dash = '-'
128+
} else {
129+
dash = '='
130+
}
131+
132+
if req.Method == http.MethodConnect {
133+
if err = httpReply(http.StatusOK, "200 Connection Established").Write(conn); err != nil {
134+
log.Printf(`[http] write reply failed: %s`, err)
135+
return
136+
}
137+
} else {
138+
req.Header.Del("Proxy-Connection")
139+
if err = req.Write(nextHop); err != nil {
140+
log.Printf(`[http] relay request failed: %s`, err)
141+
return
142+
}
143+
}
144+
145+
log.Printf(`[http] tunnel established %s <%c> %s`, conn.RemoteAddr(), dash, addr)
146+
if err := utils.Transport(conn, nextHop); err != nil {
147+
log.Printf(`[http] transport failed: %s`, err)
148+
}
149+
log.Printf(`[http] tunnel disconnected %s >%c< %s`, conn.RemoteAddr(), dash, addr)
150+
}
151+
24152
type httpWrapper struct {
25153
net.Conn
26154
client *Client

client/socks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func (c *Client) wrapSocks(conn net.Conn) net.Conn {
1515
return conn
1616
}
1717

18-
func (c *Client) handler(conn net.Conn) {
18+
func (c *Client) socks5Handler(conn net.Conn) {
1919
defer conn.Close()
2020

2121
// select method

subsocks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package main
22

33
// Version of subsocks
4-
const Version = "0.2.2"
4+
const Version = "0.3.0"
55

66
var needsTLS = map[string]bool{
77
"https": true,

0 commit comments

Comments
 (0)