p2ptunnel/p2pforwarder/forwarder.go
chenjia404 0861d112d8 0.0.8:
支持自定义p2p端口
降低默认连接节点数
2023-05-02 17:24:01 +08:00

283 lines
5.5 KiB
Go

package p2pforwarder
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"sync"
"time"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/routing"
routing2 "github.com/libp2p/go-libp2p/p2p/discovery/routing"
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
"github.com/libp2p/go-libp2p/p2p/security/noise"
"github.com/libp2p/go-libp2p"
dht "github.com/libp2p/go-libp2p-kad-dht"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/peer"
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
"github.com/sparkymat/appdir"
)
const (
protocolTypeTCP byte = 0x00
protocolTypeUDP byte = 0x01
)
// Forwarder - instance of P2P Forwarder
type Forwarder struct {
host host.Host
openPorts *openPortsStore
portsSubscriptions map[peer.ID]chan *portsManifest
portsSubscriptionsMux sync.Mutex
portsSubscribers map[peer.ID]struct{}
portsSubscribersMux sync.Mutex
}
type openPortsStore struct {
tcp *openPortsStoreMap
udp *openPortsStoreMap
}
type openPortsStoreMap struct {
ports map[uint16]context.Context
mux sync.Mutex
}
func newOpenPortsStore() *openPortsStore {
return &openPortsStore{
tcp: &openPortsStoreMap{
ports: map[uint16]context.Context{},
},
udp: &openPortsStoreMap{
ports: map[uint16]context.Context{},
},
}
}
// NewForwarder - instances Forwarder and connects it to libp2p network
func NewForwarder(p2p_port int) (*Forwarder, context.CancelFunc, error) {
priv, err := loadUserPrivKey()
if err != nil {
return nil, nil, err
}
ctx, cancel := context.WithCancel(context.Background())
h, err := createLibp2pHost(ctx, priv, p2p_port)
if err != nil {
cancel()
return nil, nil, err
}
for _, value := range h.Addrs() {
fmt.Println("multiaddr:" + value.String())
}
f := &Forwarder{
host: h,
openPorts: newOpenPortsStore(),
portsSubscriptions: make(map[peer.ID]chan *portsManifest),
portsSubscribers: make(map[peer.ID]struct{}),
}
setDialHandler(f)
setPortsSubHandler(f)
return f, cancel, nil
}
func loadUserPrivKey() (priv crypto.PrivKey, err error) {
krPath, err := appdir.AppInfo{
Author: "nickname32",
Name: "P2P Forwarder",
}.ConfigPath("keypair")
if err != nil {
return nil, err
}
pkFile, err := os.Open(krPath)
if err == nil {
defer pkFile.Close()
b, err := ioutil.ReadAll(pkFile)
if err != nil {
return nil, err
}
priv, err = crypto.UnmarshalPrivateKey(b)
if err != nil {
return nil, err
}
return priv, nil
}
if !os.IsNotExist(err) {
return nil, err
}
priv, _, err = crypto.GenerateKeyPair(crypto.Ed25519, -1)
if err != nil {
return nil, err
}
b, err := crypto.MarshalPrivateKey(priv)
if err != nil {
return nil, err
}
err = os.MkdirAll(filepath.Dir(krPath), os.ModePerm)
if err != nil {
return nil, err
}
newPkFile, err := os.Create(krPath)
if err != nil {
return nil, err
}
_, err = newPkFile.Write(b)
if err != nil {
return nil, err
}
err = newPkFile.Close()
if err != nil {
return nil, err
}
return priv, nil
}
const Protocol = "/p2ptunnel/0.1"
func createLibp2pHost(ctx context.Context, priv crypto.PrivKey, p2p_port int) (host.Host, error) {
var d *dht.IpfsDHT
connmgr, _ := connmgr.NewConnManager(
10, // Lowwater
100, // HighWater,
connmgr.WithGracePeriod(time.Minute),
)
var h, err = libp2p.New(
libp2p.Identity(priv),
libp2p.ListenAddrStrings(
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", p2p_port),
fmt.Sprintf("/ip6/::/tcp/%d", p2p_port),
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d/ws", p2p_port),
fmt.Sprintf("/ip6/::/tcp/%d/ws", p2p_port),
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1", p2p_port),
fmt.Sprintf("/ip6/::/udp/%d/quic-v1", p2p_port),
fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1/webtransport", p2p_port),
fmt.Sprintf("/ip6/::/udp/%d/quic-v1/webtransport", p2p_port),
),
libp2p.DefaultTransports,
libp2p.Security(noise.ID, noise.New),
libp2p.Security(libp2ptls.ID, libp2ptls.New),
libp2p.NATPortMap(),
libp2p.EnableNATService(),
libp2p.ConnectionManager(connmgr),
libp2p.EnableRelay(),
libp2p.EnableRelayService(),
libp2p.DefaultPeerstore,
libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
var err error
d, err = dht.New(ctx, h, dht.BootstrapPeers(dht.GetDefaultBootstrapPeerAddrInfos()...))
return d, err
}),
)
if err != nil {
return nil, err
}
// This connects to public bootstrappers
for _, addr := range dht.DefaultBootstrapPeers {
pi, err := peer.AddrInfoFromP2pAddr(addr)
if err != nil {
panic(err)
}
h.Connect(ctx, *pi)
}
err = d.Bootstrap(ctx)
if err != nil {
return nil, err
}
d1 := routing2.NewRoutingDiscovery(d)
go func() {
_, err = d1.Advertise(ctx, Protocol)
if err != nil {
log.Println(err)
}
}()
go func() {
peerChan, err := d1.FindPeers(ctx, Protocol)
if err != nil {
panic(err)
}
for dhtPeer := range peerChan {
if dhtPeer.ID == h.ID() {
continue
}
if h.Network().Connectedness(dhtPeer.ID) != network.Connected {
h.Connect(ctx, dhtPeer)
}
}
time.Sleep(time.Second * 10)
}()
return h, err
}
// ID returns id of Forwarder
func (f *Forwarder) ID() string {
return f.host.ID().String()
}
var onErrFn = func(err error) {
println(err.Error())
}
var onInfoFn = func(str string) {
println(str)
}
// OnError sets function which be called on error inside this package
func OnError(fn func(error)) {
if fn == nil {
return
}
onErrFn = fn
}
// OnInfo sets function which be called on information inside this package
func OnInfo(fn func(string)) {
if fn == nil {
return
}
onInfoFn = fn
}