-
Notifications
You must be signed in to change notification settings - Fork 750
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package tcp | ||
|
||
import ( | ||
"github.com/apernet/OpenGFW/analyzer" | ||
"github.com/apernet/OpenGFW/ruleset/builtins/tor" | ||
) | ||
|
||
var _ analyzer.TCPAnalyzer = (*TorAnalyzer)(nil) | ||
|
||
type TorAnalyzer struct{ | ||
directory tor.TorDirectory | ||
} | ||
|
||
func (a *TorAnalyzer) Init() error { | ||
var err error | ||
a.directory, err = tor.GetOnionooDirectory() | ||
return err | ||
} | ||
|
||
func (a *TorAnalyzer) Name() string { | ||
return "tor" | ||
} | ||
|
||
// For now only TCP metadata is needed | ||
func (a *TorAnalyzer) Limit() int { | ||
return 1 | ||
} | ||
|
||
func (a *TorAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream { | ||
isRelay := a.directory.Query(info.DstIP, info.DstPort) | ||
return newTorStream(logger, isRelay) | ||
} | ||
|
||
type torStream struct { | ||
logger analyzer.Logger | ||
isRelay bool // Public relay identifier | ||
} | ||
|
||
func newTorStream(logger analyzer.Logger, isRelay bool) *torStream { | ||
return &torStream{logger: logger, isRelay: isRelay} | ||
} | ||
|
||
func (s *torStream) Feed(rev, start, end bool, skip int, data []byte) (u *analyzer.PropUpdate, done bool) { | ||
if skip != 0 { | ||
return nil, true | ||
} | ||
if len(data) == 0 { | ||
return nil, false | ||
} | ||
|
||
return &analyzer.PropUpdate{ | ||
Type: analyzer.PropUpdateReplace, | ||
M: analyzer.PropMap{ | ||
"relay": s.isRelay, | ||
}, | ||
}, true | ||
} | ||
|
||
func (s *torStream) Close(limited bool) *analyzer.PropUpdate { | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package tor | ||
|
||
import "net" | ||
|
||
type TorDirectory interface { | ||
Init() error | ||
Add(ip net.IP, port uint16) | ||
Query(ip net.IP, port uint16) bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package tor | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"strconv" | ||
"sync" | ||
) | ||
|
||
const ( | ||
onionooUrl = "https://onionoo.torproject.org/details" | ||
) | ||
|
||
var _ TorDirectory = (*OnionooDirectory)(nil) | ||
|
||
// Singleton instance | ||
var onionooInstance *OnionooDirectory | ||
var once sync.Once | ||
|
||
func GetOnionooDirectory() (*OnionooDirectory, error) { | ||
var err error | ||
// Singleton initialization | ||
once.Do(func() { | ||
onionooInstance = &OnionooDirectory{ | ||
directory: make(map[string]struct{}), | ||
} | ||
err = onionooInstance.Init() | ||
}) | ||
return onionooInstance, err | ||
} | ||
|
||
type OnionooDirectory struct { | ||
directory map[string]struct{} | ||
sync.RWMutex | ||
} | ||
|
||
// example detail entry | ||
// {..., "or_addresses":["195.15.242.99:9001","[2001:1600:10:100::201]:9001"], ...} | ||
|
||
type OnionooDetail struct { | ||
OrAddresses []string `json:"or_addresses"` | ||
} | ||
|
||
type OnionooResponse struct { | ||
Relays []OnionooDetail `json:"relays"` | ||
} | ||
|
||
func (d *OnionooDirectory) Init() error { | ||
response, err := d.downloadDirectory(onionooUrl) | ||
if err != nil { | ||
return err | ||
} | ||
for _, relay := range response.Relays { | ||
for _, address := range relay.OrAddresses { | ||
ipStr, portStr, err := net.SplitHostPort(address) | ||
if err != nil { | ||
continue | ||
} | ||
ip := net.ParseIP(ipStr) | ||
port, err := strconv.ParseUint(portStr, 10, 16) | ||
if ip != nil && err == nil { | ||
d.Add(ip, uint16(port)) | ||
} | ||
} | ||
} | ||
// TODO: log number of entries loaded | ||
return nil | ||
} | ||
|
||
func (d *OnionooDirectory) Add(ip net.IP, port uint16) { | ||
d.Lock() | ||
defer d.Unlock() | ||
addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10)) | ||
d.directory[addr] = struct{}{} | ||
} | ||
|
||
func (d *OnionooDirectory) Query(ip net.IP, port uint16) bool { | ||
d.RLock() | ||
defer d.RUnlock() | ||
addr := net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(port), 10)) | ||
_, exists := d.directory[addr] | ||
return exists | ||
} | ||
|
||
func (d *OnionooDirectory) downloadDirectory(url string) (*OnionooResponse, error) { | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return nil, fmt.Errorf("failed to fetch onionoo data: status code %d", resp.StatusCode) | ||
} | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var onionooResponse OnionooResponse | ||
err = json.Unmarshal(body, &onionooResponse) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse onionoo json response: %s", err) | ||
} | ||
|
||
return &onionooResponse, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters