Skip to content

Commit

Permalink
Add https ping @Release
Browse files Browse the repository at this point in the history
  • Loading branch information
wweir committed Jan 25, 2019
1 parent 712b689 commit ceedef4
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 36 deletions.
62 changes: 32 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,44 @@

The sower is a cross-platform intelligent transparent proxy tool base on DNS solution.

The first time you visit a new website, sower will detect if the domain in block list and add it in suggect list. So that, you do not need to care about the rules, sower will handle it in a intelligent way.

If you wanna enjoy the sower, you need to deploy sower on both server and client side.
On client side, sower listening UDP `53` and TCP `80`/`443` ports, so that you need run it with privileged.
On server side, it just listening to a port (default `5533`), parse and relay the request to target server.

Sower also provides an http(s) proxy, which listens to port `8080` by default. You can turn it off or use another port at any time.
Sower also provides an http(s) proxy listening on `:8080` by default. You can turn it off or use another port at any time.


## Installation
### Auto deploy
Auto deploy script support Linux server side and masOS/Linux client side.

```shell
$ bash -c "$(curl -s https://raw.githubusercontent.com/wweir/sower/master/deploy/install)"
```

Then modify the configuration file as needed and set `127.0.0.1` as your first domain name server.
In most situation, you just need to modify `/etc/resolv.conf`.

If you wanna uninstall sower, change `install` into `uninstall` and rerun the command.

### Manually deploy
1. Download the precompiled file from https://github.com/wweir/sower/releases
2. Decompression the file into a folder
3. Run `./sower -h` for help
5. Config domain name server
4. Config auto start

### Docker deploy
The auto build docker images are [wweir/sower](https://hub.docker.com/r/wweir/sower).

It is very simple to use it on the server side. Map the port and run it directly.

But the client is more troublesome and needs some understanding of the working mechanism of the sower.

The first time you visit a new website, sower will detect if the domain in block list and add it in suggect list. So that, you do not need to care about the rules, sower will handle it in a intelligent way. The only situation you should care about rules is some site network speed too slow.

## Architecture
For more detail, see [透明代理 Sower 技术剖析](https://wweir.cc/post/%E9%80%8F%E6%98%8E%E4%BB%A3%E7%90%86-sower-%E6%8A%80%E6%9C%AF%E5%89%96%E6%9E%90/)
```
request target servers
<-------------+ +------------->
Expand Down Expand Up @@ -51,33 +79,7 @@ http(s) proxy | +----------+ | |
blocked request normal request
```

## Installation
### Auto deploy
Auto deploy script support Linux server side and masOS/Linux client side.

```shell
$ bash -c "$(curl -s https://raw.githubusercontent.com/wweir/sower/master/deploy/install)"
```

Then modify the configuration file as needed and set `127.0.0.1` as your first domain name server.
In most situation, you just need to modify `/etc/resolv.conf`.

If you wanna uninstall sower, change `install` into `uninstall` and rerun the command.

### Manually deploy
1. Download the precompiled file from https://github.com/wweir/sower/releases
2. Decompression the file into a folder
3. Run `./sower -h` for help
5. Config domain name server
4. Config auto start

### Docker deploy
The auto build docker images are [wweir/sower](https://hub.docker.com/r/wweir/sower).

It is very simple to use it on the server side. Map the port and run it directly.

But the client is more troublesome and needs some understanding of the working mechanism of the sower.
For more detail, see [透明代理 Sower 技术剖析](https://wweir.cc/post/%E9%80%8F%E6%98%8E%E4%BB%A3%E7%90%86-sower-%E6%8A%80%E6%9C%AF%E5%89%96%E6%9E%90/)


## Todo
Expand Down
5 changes: 3 additions & 2 deletions dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func StartDNS(dnsServer, listenIP string) {
dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
// *Msg r has an TSIG record and it was validated
if r.IsTsig() != nil && w.TsigStatus() == nil {
r.SetTsig(r.Extra[len(r.Extra)-1].(*dns.TSIG).Hdr.Name, dns.HmacMD5, 300, time.Now().Unix())
lastTsig := r.Extra[len(r.Extra)-1].(*dns.TSIG)
r.SetTsig(lastTsig.Hdr.Name, dns.HmacMD5, 300, time.Now().Unix())
}

//https://stackoverflow.com/questions/4082081/requesting-a-and-aaaa-records-in-single-dns-query/4083071#4083071
Expand Down Expand Up @@ -82,7 +83,7 @@ func (i *intelliSuggest) GetOne(domain interface{}) (iface interface{}, e error)

for _, port := range i.ports {
// give local dial a hand, make it not so easy to be added into suggestions
util.HTTPPing(addr+port, addr, i.timeout/10)
util.HTTPPing(addr+port, addr, i.timeout/4)
localCh := util.HTTPPing(addr+port, addr, i.timeout)
remoteCh := util.HTTPPing(i.listenIP+port, addr, i.timeout)

Expand Down
90 changes: 86 additions & 4 deletions util/http_ping.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package util

import (
"bytes"
"crypto/tls"
"encoding/binary"
"errors"
"io"
"net"
"strings"
Expand All @@ -24,9 +28,21 @@ func httpPing(tcpAddr, domain string, timeout time.Duration) error {
}
defer conn.Close()

idx := strings.Index(tcpAddr, ":")
if idx <= 0 {
return errors.New("tcp address port is needed")
}

var msg []byte
switch tcpAddr[idx:] {
case ":80":
msg = []byte("TRACE / HTTP/1.1\r\nHost: " + domain + "\r\n\r\n")
case ":443":
msg = NewClientHelloSNIMsg(domain)
}

conn.SetDeadline(time.Now().Add(timeout))
if _, err = conn.Write(
[]byte("TRACE / HTTP/1.1\r\nHost: " + domain + "\r\n\r\n")); err != nil {
if _, err = conn.Write(msg); err != nil {
return err
}

Expand All @@ -35,10 +51,76 @@ func httpPing(tcpAddr, domain string, timeout time.Duration) error {
// err -> timeout: tcp package has been dropped
_, err = conn.Read(make([]byte, 1))
if err == io.EOF {
idx := strings.Index(tcpAddr, ":")
if idx >= 0 && tcpAddr[:idx] == domain {
if tcpAddr[:idx] == domain {
return nil
}
}
return err
}

type clientHelloSNI struct {
ContentType uint8
Version uint16
Length uint16
handshakeProtocol
}
type handshakeProtocol struct {
HandshakeType uint8
LengthExpand uint8
Length uint16
Version uint16
Random [32]byte
SessionIDLength uint8
CipherSuitesLength uint16
CipherSuite uint16
CompressionMethodsLength uint8
CompressionMethod uint8
ExtensionsLength uint16
extensionServerName
}
type extensionServerName struct {
Type uint16
Length uint16
serverNameIndicationExtension
}
type serverNameIndicationExtension struct {
ServerNameListlength uint16
ServerNameType uint8
ServerNamelength uint16
// ServerName []byte // Disable for fix length
}

func NewClientHelloSNIMsg(domain string) []byte {
length := uint16(len(domain))
msg := &clientHelloSNI{
ContentType: 0x16, // Content Type: Handshake (22)
Version: 0x0301, // Version: TLS 1.0 (0x0301)
Length: length + 56,
handshakeProtocol: handshakeProtocol{
HandshakeType: 0x01, // Handshake Type: Client Hello (1)
Length: length + 52,
Version: 0x0303, // Version: TLS 1.2 (0x0303)
Random: [32]byte{}, // [32]byte{},
SessionIDLength: 0x0, // Session ID Length: 0
CipherSuitesLength: 2, // Cipher Suites Length: 84
CipherSuite: tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
CompressionMethodsLength: 1, // Compression Methods Length: 1
CompressionMethod: 0x00, // Compression null
ExtensionsLength: length + 9,
extensionServerName: extensionServerName{
Type: 0x0000, // Type: server_name (0)
Length: length + 5,
serverNameIndicationExtension: serverNameIndicationExtension{
ServerNameListlength: length + 3,
ServerNameType: 0x00, // Server Name Type: host_name (0)
ServerNamelength: length,
},
},
},
}

buf := bytes.NewBuffer(make([]byte, 0, length+71))
binary.Write(buf, binary.BigEndian, msg)
buf.WriteString(domain)
return buf.Bytes()
}

0 comments on commit ceedef4

Please sign in to comment.