package obfsvpn import ( "bytes" "context" "crypto/rand" "encoding/hex" "fmt" "log" "net" pt "git.torproject.org/pluggable-transports/goptlib.git" "github.com/xtaci/kcp-go" "gitlab.com/yawning/obfs4.git/common/ntor" "gitlab.com/yawning/obfs4.git/transports/base" "gitlab.com/yawning/obfs4.git/transports/obfs4" ) const ( netKCP = "kcp" ) // ListenConfig contains options for listening to an address. // If Seed is not set it defaults to a randomized value. // If StateDir is not set the current working directory is used. type ListenConfig struct { ListenConfig net.ListenConfig NodeID *ntor.NodeID PrivateKey *ntor.PrivateKey PublicKey string Seed [ntor.KeySeedLength]byte StateDir string } // NewListenConfig // perhaps this is redundant, but using the same json format than ss for debug. func NewListenConfig(nodeIDStr, privKeyStr, pubKeyStr, seedStr, stateDir string) (*ListenConfig, error) { var err error var seed [ntor.KeySeedLength]byte var nodeID *ntor.NodeID private := new(ntor.PrivateKey) if nodeID, err = ntor.NodeIDFromHex(nodeIDStr); err != nil { return nil, err } raw, err := hex.DecodeString(privKeyStr) if err != nil { return nil, err } log.Println("DEBUG len private ley:", len(raw)) // TODO raise invalid error if len not right copy(private[:], raw) s, err := hex.DecodeString(seedStr) if err != nil { return nil, err } copy(seed[:], s) lc := &ListenConfig{ NodeID: nodeID, PrivateKey: private, PublicKey: pubKeyStr, Seed: seed, StateDir: stateDir, } return lc, nil } // NewListenConfigCert creates a listener config by unpacking the node ID from // its certificate. // The private key must still be specified. func NewListenConfigCert(cert string) (*ListenConfig, error) { nodeID, _, err := unpackCert(cert) if err != nil { return nil, err } return &ListenConfig{ NodeID: nodeID, }, nil } // Wrap takes an existing net.Listener and wraps it in a listener that is // configured to perform the ntor handshake and copy data through the obfuscated conn. // Values from the inner net.ListenConfig are ignored. func (lc *ListenConfig) Wrap(ctx context.Context, ln net.Listener) (*Listener, error) { args := make(pt.Args) args.Add("node-id", lc.NodeID.Hex()) args.Add("private-key", lc.PrivateKey.Hex()) seed := ntor.KeySeed{} if bytes.Equal(lc.Seed[:], seed[:]) { _, err := rand.Read(seed[:]) if err != nil { return nil, err } } else { seed = lc.Seed } args.Add("drbg-seed", hex.EncodeToString(seed[:])) args.Add("public-key", lc.PublicKey) fmt.Println("pubkey:", lc.PublicKey) sf, err := (&obfs4.Transport{}).ServerFactory(lc.StateDir, &args) if err != nil { return nil, err } return &Listener{sf: sf, ln: ln}, nil } // Listen listens on the local network address. // See func net.Dial for a description of the network and address parameters. func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (*Listener, error) { var ln net.Listener var err error switch network { case netKCP: log.Println("kcp listen on", address) ln, err = kcp.Listen(address) if err != nil { return nil, err } default: ln, err = lc.ListenConfig.Listen(ctx, network, address) if err != nil { return nil, err } } return lc.Wrap(ctx, ln) } // Listener is a network listener that accepts obfuscated connections and // performs the ntor handshake on them. type Listener struct { sf base.ServerFactory ln net.Listener } // Accept waits for and returns the next connection to the listener. func (l *Listener) Accept() (net.Conn, error) { conn, err := l.ln.Accept() if err != nil { return nil, err } conn, err = l.sf.WrapConn(conn) return conn, err } // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. func (l *Listener) Close() error { return l.ln.Close() } // Addr returns the listener's network address. func (l *Listener) Addr() net.Addr { return l.ln.Addr() }