core: Add OnionConnector for outbound connections
This commit is contained in:
parent
ec8d6f5430
commit
fc0c6b3c95
|
@ -0,0 +1,86 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"golang.org/x/net/context"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// XXX on failures, decide how long to wait before trying again
|
||||
// XXX limit and queue global attempts?
|
||||
// XXX circ/stream/fetch monitoring
|
||||
|
||||
type OnionConnector struct {
|
||||
Network *Network
|
||||
NeverGiveUp bool
|
||||
AttemptCount int
|
||||
}
|
||||
|
||||
// Attempt to connect to 'address', which must be a .onion address and port,
|
||||
// using the Network from this OnionConnector instance.
|
||||
//
|
||||
// If NeverGiveUp is set, failed connections will be retried automatically,
|
||||
// with appropriate backoff periods, and an error is only returned in fatal
|
||||
// situations. The backoff is defined by AttemptCount on the OnionConnector
|
||||
// instance -- it is not reset after Connect returns.
|
||||
//
|
||||
// If the Network is not ready, this function will wait and monitor the
|
||||
// Network's status.
|
||||
//
|
||||
// Context can be used to set a timeout, deadline, or cancel function for
|
||||
// the connection attempt.
|
||||
func (oc *OnionConnector) Connect(address string, c context.Context) (net.Conn, error) {
|
||||
if oc.Network == nil {
|
||||
return nil, errors.New("No network configured for connector")
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(address)
|
||||
if err != nil || !strings.HasSuffix(host, ".onion") {
|
||||
return nil, errors.New("Invalid address")
|
||||
}
|
||||
|
||||
options := &net.Dialer{
|
||||
Cancel: c.Done(),
|
||||
Timeout: 0, // XXX should use timeout
|
||||
}
|
||||
|
||||
for {
|
||||
// XXX This waits to know SOCKS info, but does not wait for connection
|
||||
// ready state; should it?
|
||||
proxy, err := oc.Network.WaitForProxyDialer(options, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := proxy.Dial("tcp", address)
|
||||
if err != nil {
|
||||
if c.Err() != nil {
|
||||
return nil, c.Err()
|
||||
}
|
||||
if !oc.NeverGiveUp {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("Connection attempt to %s failed: %s", address, err)
|
||||
if err := oc.Backoff(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (oc *OnionConnector) Backoff(c context.Context) error {
|
||||
oc.AttemptCount++
|
||||
// XXX This should actually do backoff and be less dumb
|
||||
waitCtx, finish := context.WithTimeout(c, 10*time.Second)
|
||||
defer finish()
|
||||
<-waitCtx.Done()
|
||||
return c.Err()
|
||||
}
|
Loading…
Reference in New Issue