Merge pull request 'Introduce new GetInfo API for fetching information about ACN' (#26) from getinfo into master
continuous-integration/drone/push Build is pending Details
continuous-integration/drone/tag Build is pending Details

Reviewed-on: #26
This commit is contained in:
erinn 2022-01-17 20:07:30 +00:00
commit 04dec3238b
6 changed files with 124 additions and 6 deletions

2
acn.go
View File

@ -57,5 +57,7 @@ type ACN interface {
Callback() func(int, string)
GetInfo(onion string) (map[string]string, error)
Close()
}

View File

@ -6,6 +6,8 @@ import (
"net"
)
const acnError = "error initializing anonymous communication network"
// ErrorACN - a status-callback safe errored ACN. Use this when ACN construction goes wrong
// and you need a safe substitute that can later be replaced with a working ACN without impacting calling clients.
type ErrorACN struct {
@ -16,12 +18,16 @@ func (e ErrorACN) Callback() func(int, string) {
return e.statusCallbackCache
}
func (e *ErrorACN) GetInfo(addr string) (map[string]string, error) {
return nil, errors.New(acnError)
}
func (e ErrorACN) GetBootstrapStatus() (int, string) {
return -1, "error initializing tor"
return -1, acnError
}
func (e ErrorACN) WaitTillBootstrapped() error {
return errors.New("error initializing tor")
return errors.New(acnError)
}
func (e *ErrorACN) SetStatusCallback(callback func(int, string)) {
@ -32,19 +38,19 @@ func (e ErrorACN) Restart() {
}
func (e ErrorACN) Open(hostname string) (net.Conn, string, error) {
return nil, "", fmt.Errorf("error initializing tor")
return nil, "", fmt.Errorf(acnError)
}
func (e ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) {
return nil, fmt.Errorf("error initializing tor")
return nil, fmt.Errorf(acnError)
}
func (e ErrorACN) GetPID() (int, error) {
return -1, fmt.Errorf("error initializing tor")
return -1, fmt.Errorf(acnError)
}
func (e ErrorACN) GetVersion() string {
return "Error Initializing Tor"
return acnError
}
func (e ErrorACN) Close() {

View File

@ -38,6 +38,10 @@ func (ls *localListenService) Close() {
ls.l.Close()
}
func (lp *localProvider) GetInfo(addr string) (map[string]string, error) {
return nil, nil
}
// GetBootstrapStatus returns an int 0-100 on the percent the bootstrapping of the underlying network is at and an optional string message
func (lp *localProvider) GetBootstrapStatus() (int, string) {
return 100, "Done"

View File

@ -33,6 +33,10 @@ func (p *ProxyACN) ReplaceACN(acn ACN) {
p.acn = acn
}
func (p *ProxyACN) GetInfo(addr string) (map[string]string, error) {
return p.acn.GetInfo(addr)
}
func (p *ProxyACN) GetBootstrapStatus() (int, string) {
return p.acn.GetBootstrapStatus()
}

View File

@ -91,6 +91,77 @@ func (ols *onionListenService) Close() {
ols.os.Close()
}
func (tp *torProvider) GetInfo(onion string) (map[string]string, error) {
tp.lock.Lock()
defer tp.lock.Unlock()
circuits, streams, err := getCircuitInfo(tp.t.Control)
if err == nil {
var circuitID string
for _, stream := range streams {
if stream.Key == "stream-status" {
lines := strings.Split(stream.Val, "\n")
for _, line := range lines {
parts := strings.Split(line, " ")
// StreamID SP StreamStatus SP CircuitID SP Target CRLF
if len(parts) == 4 {
if strings.HasPrefix(parts[3], onion) {
circuitID = parts[2]
break
}
}
}
}
}
if circuitID == "" {
return nil, errors.New("could not find circuit")
}
var hops []string
for _, circuit := range circuits {
if circuit.Key == "circuit-status" {
lines := strings.Split(circuit.Val, "\n")
for _, line := range lines {
parts := strings.Split(line, " ")
// CIRCID SP STATUS SP PATH...
if len(parts) >= 3 && circuitID == parts[0] {
//log.Debugf("Found Circuit for Onion %v %v", onion, parts)
if parts[1] == "BUILT" {
circuitPath := strings.Split(parts[2], ",")
for _, hop := range circuitPath {
fingerprint := hop[1:41]
keyvals, err := tp.t.Control.GetInfo(fmt.Sprintf("ns/id/%s", fingerprint))
if err == nil && len(keyvals) == 1 {
lines = strings.Split(keyvals[0].Val, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "r") {
parts := strings.Split(line, " ")
if len(parts) > 6 {
keyvals, err := tp.t.Control.GetInfo(fmt.Sprintf("ip-to-country/%s", parts[6]))
if err == nil && len(keyvals) >= 1 {
hops = append(hops, fmt.Sprintf("%s:%s", strings.ToUpper(keyvals[0].Val), parts[6]))
} else {
hops = append(hops, fmt.Sprintf("%s:%s", "XX", parts[6]))
}
}
}
}
}
}
return map[string]string{"circuit": strings.Join(hops, ",")}, nil
}
}
}
}
}
}
return nil, err
}
// GetBootstrapStatus returns an int 0-100 on the percent the bootstrapping of the underlying network is at and an optional string message
// returns -1 on network disconnected
// returns -2 on error
@ -479,6 +550,15 @@ func (tp *torProvider) monitorRestart() {
}
}
func getCircuitInfo(controlport *control.Conn) ([]*control.KeyVal, []*control.KeyVal, error) {
circuits, cerr := controlport.GetInfo("circuit-status")
streams, serr := controlport.GetInfo("stream-status")
if cerr == nil && serr == nil {
return circuits, streams, nil
}
return nil, nil, errors.New("could not fetch circuits or streams")
}
func createFromExisting(controlport *control.Conn, datadir string) *tor.Tor {
t := &tor.Tor{
Process: nil,

View File

@ -43,6 +43,28 @@ func TestTorProvider(t *testing.T) {
t.Logf("progress: %v", progress)
}
// Test opening the OP Server
_, _, err = acn.Open("isbr2t6bflul2zyi6hjtnuezb2xvfr42svzjg2q3gyqfgg3wmnrbkkqd")
if err == nil {
info, err := acn.GetInfo("isbr2t6bflul2zyi6hjtnuezb2xvfr42svzjg2q3gyqfgg3wmnrbkkqd")
if err != nil {
t.Fatalf("could not find info for OP server %v", err)
}
cinfo, exists := info["circuit"]
if !exists || len(cinfo) == 0 {
t.Fatalf("could not find circuit info for OP server %v", err)
}
_, err = acn.GetInfo("not_a_real_onion")
if err == nil {
t.Fatalf("GetInfo for non existant onion should have errored")
}
} else {
t.Fatalf("could not connect to OP server %v", err)
}
// Should skip without blocking...
acn.Restart()
acn.Restart()