Skip to content

Commit 1252437

Browse files
committed
feat: initial implementation
1 parent 8bed89b commit 1252437

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

app.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package caddy_libp2p_listener
2+
3+
import (
4+
"github.com/caddyserver/caddy/v2"
5+
"github.com/caddyserver/caddy/v2/caddyconfig"
6+
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
7+
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
8+
"go.uber.org/zap"
9+
"strconv"
10+
)
11+
12+
func init() {
13+
caddy.RegisterModule(App{})
14+
httpcaddyfile.RegisterGlobalOption("libp2p", parseAppConfig)
15+
}
16+
17+
// App is the libp2p Caddy app used to configure libp2p nodes.
18+
type App struct {
19+
// PrivateKey is the location of the private key as a PEM file
20+
PrivateKey string `json:"private_key,omitempty" caddy:"namespace=libp2p.key"`
21+
22+
// AdvertiseAmino indicates if the peer's addresses should be adverised in the Amino DHT
23+
// This is particularly useful for WebTransport and WebRTC which have their certificate hashes rotate
24+
// even while their peerID remains the same
25+
AdvertiseAmino bool `json:"advertise_amino,omitempty" caddy:"namespace=libp2p.advertise_amino"`
26+
27+
logger *zap.Logger
28+
}
29+
30+
func (App) CaddyModule() caddy.ModuleInfo {
31+
return caddy.ModuleInfo{
32+
ID: "libp2p",
33+
New: func() caddy.Module { return new(App) },
34+
}
35+
}
36+
37+
func (t *App) Provision(ctx caddy.Context) error {
38+
t.logger = ctx.Logger(t)
39+
return nil
40+
}
41+
42+
func (t *App) Start() error {
43+
return nil
44+
}
45+
46+
func (t *App) Stop() error {
47+
return nil
48+
}
49+
50+
func parseAppConfig(d *caddyfile.Dispenser, _ any) (any, error) {
51+
app := &App{}
52+
if !d.Next() {
53+
return app, d.ArgErr()
54+
55+
}
56+
57+
for d.NextBlock(0) {
58+
val := d.Val()
59+
60+
switch val {
61+
case "advertise_amino":
62+
if d.NextArg() {
63+
v, err := strconv.ParseBool(d.Val())
64+
if err != nil {
65+
return nil, d.WrapErr(err)
66+
}
67+
app.AdvertiseAmino = v
68+
} else {
69+
app.AdvertiseAmino = true
70+
}
71+
case "private_key":
72+
if !d.NextArg() {
73+
return nil, d.ArgErr()
74+
}
75+
app.PrivateKey = d.Val()
76+
default:
77+
return nil, d.Errf("unrecognized directive: %s", d.Val())
78+
}
79+
}
80+
return httpcaddyfile.App{
81+
Name: "libp2p",
82+
Value: caddyconfig.JSON(app, nil),
83+
}, nil
84+
}
85+
86+
var (
87+
_ caddy.App = (*App)(nil)
88+
_ caddy.Provisioner = (*App)(nil)
89+
)

module.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package caddy_libp2p_listener
2+
3+
import (
4+
"context"
5+
"crypto/ed25519"
6+
"crypto/x509"
7+
"encoding/pem"
8+
"fmt"
9+
"github.com/caddyserver/caddy/v2"
10+
"github.com/libp2p/go-libp2p"
11+
dht "github.com/libp2p/go-libp2p-kad-dht"
12+
"github.com/libp2p/go-libp2p/core/crypto"
13+
"github.com/libp2p/go-libp2p/core/host"
14+
"github.com/libp2p/go-libp2p/core/routing"
15+
libp2phttp "github.com/libp2p/go-libp2p/p2p/http"
16+
"github.com/libp2p/go-libp2p/p2p/net/gostream"
17+
libp2pwebrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc"
18+
"github.com/multiformats/go-multiaddr"
19+
"net"
20+
"os"
21+
"strconv"
22+
"strings"
23+
)
24+
25+
func init() {
26+
//caddy.RegisterModule(new(LL))
27+
caddy.RegisterNetwork("multiaddr", registerMultiaddrURI)
28+
}
29+
30+
func registerMultiaddrURI(ctx context.Context, network, addr string, cfg net.ListenConfig) (any, error) {
31+
cctx, ok := ctx.(caddy.Context)
32+
if !ok {
33+
return nil, fmt.Errorf("context is not a caddy.Context: %T", ctx)
34+
}
35+
36+
if network != "multiaddr" {
37+
return nil, fmt.Errorf("multiaddr URI network only handles multiaddr URIs")
38+
}
39+
40+
lastColon := strings.LastIndex(addr, ":")
41+
var port int
42+
var err error
43+
if lastColon != len(addr)-1 {
44+
port, err = strconv.Atoi(addr[lastColon+1:])
45+
if err != nil {
46+
return nil, fmt.Errorf("invalid port %w", err)
47+
}
48+
}
49+
addr = addr[:lastColon]
50+
51+
ma, err := multiaddr.NewMultiaddr(addr)
52+
if err != nil {
53+
return nil, fmt.Errorf("could not parse multiaddr: %w", err)
54+
}
55+
56+
// TODO: check port matches first one in multiaddr
57+
_ = port
58+
59+
/*
60+
ai, err := peer.AddrInfoFromP2pAddr(ma)
61+
if err != nil {
62+
return nil, fmt.Errorf("only libp2p multiaddrs currently supported: %w", err)
63+
}
64+
65+
if len(ai.Addrs) == 0 {
66+
return nil, fmt.Errorf("must listen on a supported address type")
67+
}
68+
*/
69+
70+
appIface, err := cctx.App("libp2p")
71+
if err != nil {
72+
return nil, err
73+
}
74+
app := appIface.(*App)
75+
var sk crypto.PrivKey
76+
if app.PrivateKey != "" {
77+
privFile, err := os.ReadFile(app.PrivateKey)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
pemBlock, rest := pem.Decode(privFile)
83+
if pemBlock == nil {
84+
return nil, fmt.Errorf("PEM block not found in input data:\n%s", rest)
85+
}
86+
87+
if pemBlock.Type != "PRIVATE KEY" {
88+
return nil, fmt.Errorf("expected PRIVATE KEY type in PEM block but got: %s", pemBlock.Type)
89+
}
90+
91+
stdKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
92+
if err != nil {
93+
return nil, fmt.Errorf("parsing PKCS8 format: %w", err)
94+
}
95+
96+
// In case ed25519.PrivateKey is returned we need the pointer for
97+
// conversion to libp2p keys
98+
if ed25519KeyPointer, ok := stdKey.(ed25519.PrivateKey); ok {
99+
stdKey = &ed25519KeyPointer
100+
}
101+
102+
sk, _, err = crypto.KeyPairFromStdKey(stdKey)
103+
if err != nil {
104+
return nil, fmt.Errorf("converting std Go key to libp2p key: %w", err)
105+
}
106+
}
107+
108+
opts := []libp2p.Option{libp2p.ListenAddrs(ma), libp2p.DefaultTransports, libp2p.Transport(libp2pwebrtc.New)}
109+
if sk != nil {
110+
opts = append(opts, libp2p.Identity(sk))
111+
}
112+
if app.AdvertiseAmino {
113+
opts = append(opts, libp2p.Routing(func(host host.Host) (routing.PeerRouting, error) {
114+
r, err := dht.New(ctx, host, dht.Mode(dht.ModeClient))
115+
return r, err
116+
}))
117+
}
118+
119+
h, err := libp2p.New(opts...)
120+
if err != nil {
121+
return nil, err
122+
}
123+
124+
fmt.Println(h.ID())
125+
fmt.Println(h.Addrs())
126+
127+
return gostream.Listen(h, libp2phttp.ProtocolIDForMultistreamSelect)
128+
}
129+
130+
/*
131+
type LL struct {
132+
}
133+
134+
func (L LL) CaddyModule() caddy.ModuleInfo {
135+
return caddy.ModuleInfo{
136+
ID: "caddy.listeners.libp2p",
137+
New: func() caddy.Module {
138+
return new(LL)
139+
},
140+
}
141+
}
142+
*/

0 commit comments

Comments
 (0)