Files
iroh-tracker/itcp/itcp.go

127 lines
2.7 KiB
Go

package itcp
import (
"net"
"fmt"
"sync"
"io"
"context"
iroh "git.coopcloud.tech/decentral1se/iroh-go"
"gomod.cblgh.org/cerca/util/eout"
)
type ITCP struct {
LocalListener net.Listener
Endpoint *iroh.Endpoint
ch chan string
port int
conns *sync.Map
}
// create an iroh endpoint
func NewITCP(alpn, secretBytes []byte, port int, conns *sync.Map) *ITCP {
initLogger()
var t ITCP
t.port = port
t.ch = make(chan string)
t.conns = conns
preset := iroh.PresetN0()
opts := iroh.EndpointOptions{
Preset: &preset,
Alpns: &[][]byte{alpn},
}
// static endpoint key being set
if len(secretBytes) > 0 {
opts.SecretKey = &secretBytes
}
endpoint, err := iroh.EndpointBind(opts)
if err := iroh.As(err); err != nil {
panic(err)
}
endpoint.Online()
fmt.Printf("online as %s\n", endpoint.Id())
ticket, err := iroh.EndpointTicketFromAddr(endpoint.Addr())
eout.Check(err, "format ticket")
fmt.Println("\nticket:")
fmt.Println(ticket, "\n")
t.Endpoint = endpoint
return &t
}
func (t *ITCP) GetHost() string {
return fmt.Sprintf(":%d", t.port)
}
func (t *ITCP) Start(ctx context.Context) {
tcphost := t.GetHost()
var err error
log(fmt.Sprintf("setting up local listener on %s", tcphost))
t.LocalListener, err = net.Listen("tcp", tcphost)
eout.Check(err, "set up local listener")
go t.handleIncoming(tcphost)
}
// implements net.Listener
func (t *ITCP) Accept() (net.Conn, error) {
c, err := t.LocalListener.Accept()
// receive endpoint id from t.handleIncoming goroutine
endpointId := <-t.ch
t.conns.Store(c, endpointId)
return c, err
}
// implements net.Listener
func (t *ITCP) Close() error {
// TODO (2026-10-21)
return t.LocalListener.Close()
}
// implements net.Listener
func (t *ITCP) Addr() net.Addr {
return t.LocalListener.Addr()
}
func (t *ITCP) handleIncoming(tcphost string) {
for {
incoming := t.Endpoint.AcceptNext()
log("incoming")
accepting, err := (*incoming).Accept()
if err := iroh.As(err); err != nil {
eout.Check(err, "incoming.Accept")
}
log("accepted")
conn, err := accepting.Connect()
if err := iroh.As(err); err != nil {
eout.Check(err, "incoming.Connect")
}
log("connected")
stream, err := conn.AcceptBi()
if err := iroh.As(err); err != nil {
eout.Check(err, "incoming.AcceptBi")
panic(err)
}
log("bidi stream")
// send endpoint public key to be received by accept handler
go func(endpointId string) {
t.ch <- endpointId
}(conn.RemoteId().String())
pipe := iroh.NewPipe(stream)
log("pre dial")
dialedConn, err := net.Dial("tcp", tcphost)
eout.Check(err, "net.dial")
go io.Copy(dialedConn, pipe)
go io.Copy(pipe, dialedConn)
log("post dial")
}
}