现在搞 p2p 很简单了

rkonfj:

我是 https://github.com/sigcn/pg 仓库的作者。 经过几个月的努力,现在用 PG 写 P2P 网络程序似乎很简单了。 发帖希望

  1. 得到一些关于 P2P 开发库 api 更好的设计思路。易用性和扩展性。
  2. 有感兴趣的朋友会基于 PG 开发一些实用的 P2P 应用。

这是一个访问虚拟网络内节点 HTTP 服务的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package main

import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"time"

"github.com/sigcn/pg/disco"
"github.com/sigcn/pg/langs"
"github.com/sigcn/pg/p2p"
"github.com/sigcn/pg/peermap/network"
"github.com/sigcn/pg/vpn"
"github.com/sigcn/pg/vpn/nic"
"github.com/sigcn/pg/vpn/nic/gvisor"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
)

var (
server = "wss://openpg.in/pg"
secretFile = "psns.json"
_, ip4, _ = net.ParseCIDR("100.99.0.27/24")
)

// prepareSecret 准备 JSONSecret 用于加入 PG 网络
func prepareSecret() error {
fetchSecret := func() error {
join, err := network.JoinOIDC("", server)
if err != nil {
return err
}
fmt.Println("Open the following link to authenticate")
fmt.Println(join.AuthURL())
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
secret, err := join.Wait(ctx)
cancel()
if err != nil {
panic(err)
}
f, err := os.Create(secretFile)
if err != nil {
return err
}
json.NewEncoder(f).Encode(secret)
return nil
}

f, err := os.Open(secretFile)
if err != nil {
return fetchSecret()
}
var secret disco.NetworkSecret
if err := json.NewDecoder(f).Decode(&secret); err != nil {
return err
}
if time.Now().After(secret.Expire) {
return fetchSecret()
}
return nil
}

// startVPN 启动 VPN (gVisor + p2p)
func startVPN(ctx context.Context) *gvisor.GvisorCard {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
})

vnic := nic.VirtualNIC{NIC: &gvisor.GvisorCard{Stack: s, Config: nic.Config{IPv4: ip4.String()}}}
packetConn := langs.Must(p2p.ListenPacket(
&disco.Server{Secret: &disco.FileSecretStore{StoreFilePath: secretFile}, URL: server},
p2p.ListenPeerUp(func(pi disco.PeerID, v url.Values) {
vnic.AddPeer(nic.Peer{Addr: pi, IPv4: v.Get("alias1"), IPv6: v.Get("alias2"), Meta: v})
}),
p2p.ListenPeerSecure(),
p2p.PeerAlias1(ip4.IP.String()),
))

go vpn.New(vpn.Config{MTU: 1371}).Run(ctx, &vnic, packetConn)
return vnic.NIC.(*gvisor.GvisorCard)
}

func main() {
// 获取 JSONSecret
if err := prepareSecret(); err != nil {
panic(err)
}

// 启动 gVisor VPN
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
gvisorCard := startVPN(ctx)

// HTTP 请求
cli := http.Client{
Transport: &http.Transport{DialContext: gvisorCard.DialContext},
Timeout: 15 * time.Second,
}

r := langs.Must(cli.Get("http://100.99.0.2"))
defer r.Body.Close()
io.Copy(os.Stdout, r.Body)
}