Skip to content
This repository has been archived by the owner on Apr 9, 2020. It is now read-only.

Porting ss-redir to go #473

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
script/http
bin
.idea
.vscode/
135 changes: 121 additions & 14 deletions cmd/shadowsocks-httpget/httpget.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"errors"
"flag"
"fmt"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
Expand Down Expand Up @@ -57,7 +58,11 @@ func get(connid int, uri, serverAddr string, rawAddr []byte, cipher *ss.Cipher,
}()
tr := &http.Transport{
Dial: func(_, _ string) (net.Conn, error) {
return ss.DialWithRawAddr(rawAddr, serverAddr, cipher.Copy())
if cipher != nil {
return ss.DialWithRawAddr(rawAddr, serverAddr, cipher.Copy())
}

return dialSocks5(string(rawAddr), serverAddr)
},
}

Expand All @@ -76,9 +81,94 @@ func get(connid int, uri, serverAddr string, rawAddr []byte, cipher *ss.Cipher,
}
}

func dialSocks5(targetAddr, proxy string) (conn net.Conn, err error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be added into shadowsocks package

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

readAll := func(conn net.Conn) (resp []byte, err error) {
resp = make([]byte, 1024)
Timeout := 5 * time.Second
if err := conn.SetReadDeadline(time.Now().Add(Timeout)); err != nil {
return nil, err
}
n, err := conn.Read(resp)
resp = resp[:n]
return
}
sendReceive := func(conn net.Conn, req []byte) (resp []byte, err error) {
Timeout := 5 * time.Second
if err := conn.SetWriteDeadline(time.Now().Add(Timeout)); err != nil {
return nil, err
}
_, err = conn.Write(req)
if err != nil {
return
}
resp, err = readAll(conn)
return
}

conn, err = net.Dial("tcp", proxy)
if err != nil {
return
}

// version identifier/method selection request
req := []byte{
5, // version number
1, // number of methods
0, // method 0: no authentication (only anonymous access supported for now)
}
resp, err := sendReceive(conn, req)
if err != nil {
return
} else if len(resp) != 2 {
err = errors.New("server does not respond properly")
return
} else if resp[0] != 5 {
err = errors.New("server does not support Socks 5")
return
} else if resp[1] != 0 { // no auth
err = errors.New("socks method negotiation failed")
return
}

// detail request
host, portStr, err := net.SplitHostPort(targetAddr)
if err != nil {
return nil, err
}
portInt, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil, err
}
port := uint16(portInt)

req = []byte{
5, // version number
1, // connect command
0, // reserved, must be zero
3, // address type, 3 means domain name
byte(len(host)), // address length
}
req = append(req, []byte(host)...)
req = append(req, []byte{
byte(port >> 8), // higher byte of destination port
byte(port), // lower byte of destination port (big endian)
}...)
resp, err = sendReceive(conn, req)
if err != nil {
return
} else if len(resp) != 10 {
err = errors.New("server does not respond properly")
} else if resp[1] != 0 {
err = errors.New("can't complete SOCKS5 connection")
}

return
}

func main() {
flag.StringVar(&config.server, "s", "127.0.0.1", "server:port")
flag.IntVar(&config.port, "p", 0, "server:port")
server := flag.String("s", "127.0.0.1", "server:port")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keey the code style

proxy := flag.String("ss", "127.0.0.1", "proxy:port")
flag.IntVar(&config.port, "p", 0, "port")
flag.IntVar(&config.core, "core", 1, "number of CPU cores to use")
flag.StringVar(&config.passwd, "k", "", "password")
flag.StringVar(&config.method, "m", "", "encryption method, use empty string or rc4")
Expand All @@ -89,8 +179,17 @@ func main() {

flag.Parse()

if config.server == "" || config.port == 0 || config.passwd == "" || len(flag.Args()) != 1 {
fmt.Printf("Usage: %s -s <server> -p <port> -k <password> <url>\n", os.Args[0])
config.server = "127.0.0.1"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default config can be set with flag

var connectProxy bool
if *server != config.server {
config.server = *server
} else {
config.server = *proxy
connectProxy = true
}

if config.port == 0 || !connectProxy && config.passwd == "" || len(flag.Args()) != 1 {
fmt.Printf("Usage: %s -s[s] <server> -p <port> -k <password> <url>\n", os.Args[0])
os.Exit(1)
}

Expand All @@ -104,11 +203,6 @@ func main() {
uri = "http://" + uri
}

cipher, err := ss.NewCipher(config.method, config.passwd)
if err != nil {
fmt.Println("Error creating cipher:", err)
os.Exit(1)
}
serverAddr := net.JoinHostPort(config.server, strconv.Itoa(config.port))

parsedURL, err := url.Parse(uri)
Expand All @@ -122,10 +216,23 @@ func main() {
} else {
host = parsedURL.Host
}
// fmt.Println(host)
rawAddr, err := ss.RawAddr(host)
if err != nil {
panic("Error getting raw address.")

rawAddr := []byte(host)
var cipher *ss.Cipher
if !connectProxy {
if config.method == "" {
config.method = "aes-256-cfb"
}

cipher, err = ss.NewCipher(config.method, config.passwd)
if err != nil {
fmt.Println("Error creating cipher:", err)
os.Exit(1)
}
rawAddr, err = ss.RawAddr(host)
if err != nil {
panic("Error getting raw address.")
}
}

done := make(chan []time.Duration)
Expand Down
138 changes: 138 additions & 0 deletions cmd/shadowsocks-local/guess.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file should be in shadowsocks package but not the client main package

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


import (
"strings"
)

const (
DefaultPortForHttp = 80
DefaultPortForTls = 443

SERVER_NAME_LEN = 256
TLS_HEADER_LEN = 5
TLS_HANDSHAKE_CONTENT_TYPE = 0x16
TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 0x01
)

func parseHttpHeader(buf string) string {
for _, l := range strings.Split(buf, "\r\n") {
if strings.HasPrefix(l, "Host:") {
return strings.TrimSpace(l[5:])
}
}

return ""
}

func parseTlsHeader(buf string) string {
slen := len(buf)
if slen < TLS_HEADER_LEN {
return ""
}

if buf[0] != TLS_HANDSHAKE_CONTENT_TYPE {
return ""
}

tlsVersionMajor, tlsVersionMinor := buf[1], buf[2]
if tlsVersionMajor < 3 {
return ""
}

l := int(uint(buf[3])<<8 + uint(buf[4]) + TLS_HEADER_LEN)
if slen < l {
return ""
}

buf = buf[:l]
slen = len(buf)
pos := TLS_HEADER_LEN
if slen < pos+1 {
return ""
}

if buf[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO {
return ""
}

/* Skip past fixed length records:
* 1 Handshake Type
* 3 Length
* 2 Version (again)
* 32 Random
* to Session ID Length
*/
pos += 38

if pos+1 > slen {
return ""
}
pos += int(1 + uint(buf[pos]))

if pos+2 > slen {
return ""
}
pos += int(2 + uint(buf[pos])<<8 + uint(buf[pos+1]))

if pos+1 > slen {
return ""
}
pos += int(1 + uint(buf[pos]))

if pos == slen && tlsVersionMajor == 3 && tlsVersionMinor == 0 {
return ""
}

if pos+2 > slen {
return ""
}
l = int(uint(buf[pos])<<8 + uint(buf[pos+1]))
pos += 2
if pos+l > slen {
return ""
}

return parseExtensions(buf[pos : pos+l])
}

func parseExtensions(buf string) string {
var pos, l int
slen := len(buf)

for pos+4 <= slen {
l = int(uint(buf[pos+2])<<8 + uint(buf[pos+3]))
if buf[pos] == 0x00 && buf[pos+1] == 0x00 {
if pos+4+l > slen {
return ""
}

return parseServerNameExtension(buf[pos+4 : pos+4+l])
}
pos += 4 + l
}

return ""
}

func parseServerNameExtension(buf string) string {
var l int
slen := len(buf)
pos := 2

for pos+3 < slen {
l = int(uint(buf[pos+1])<<8 + uint(buf[pos+2]))
if pos+3+l > slen {
return ""
}

switch buf[pos] {
case 0x00:
return buf[pos+3 : pos+3+l]
default:
}
pos += 3 + l
}

return ""
}

Loading