Add the shared packages for the sharks stack
model, tunnel, wg, authtoken, netguard, docker and dotenv, split out of the original single repository so Hammerhead, Blacktip and Bamboo can live in their own repos and import them. README extended; LICENSE unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c7c2ae8f8d
commit
2259580adf
16 files changed
+1359
-1
No files matched your search
@@ -0,0 +1,105 @@
|
||||
// Package tunnel holds what blacktip and bamboo agree on inside the WireGuard
|
||||
// tunnel: the target header that starts each proxied connection, and a helper
|
||||
// that pipes two connections together.
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ports inside the WireGuard network where a site agent accepts proxied
|
||||
// connections. TCP streams start with a Header line; UDP datagrams are
|
||||
// prefixed as described in blacktip's udpTunnel.
|
||||
const (
|
||||
ProxyTCPPort = 9111
|
||||
ProxyUDPPort = 9112
|
||||
MailTCPPort = 9113 // on the edge: a site's SMTP relay hands it mail to deliver
|
||||
)
|
||||
|
||||
// MailMsg is one request from a site's SMTP relay to the edge's mail port. The
|
||||
// stream carries one mail transaction: "rcpt" (the first one also carries the
|
||||
// sender and makes the edge connect to the recipient's server), more "rcpt",
|
||||
// then "data" and "body". The edge answers each with a MailReply line.
|
||||
type MailMsg struct {
|
||||
Op string `json:"op"` // rcpt, data, body, rset, quit
|
||||
Egress string `json:"egress,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
Len int `json:"len,omitempty"` // body: that many bytes follow this line (the message, unstuffed)
|
||||
}
|
||||
|
||||
type MailReply struct {
|
||||
Code int `json:"code"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// MaxMessage is the largest message the relay accepts.
|
||||
const MaxMessage = 50 << 20
|
||||
|
||||
// ReadLine reads up to '\n' one byte at a time, so nothing after it is consumed.
|
||||
func ReadLine(r io.Reader) (string, error) {
|
||||
var out []byte
|
||||
one := make([]byte, 1)
|
||||
for len(out) < 4096 {
|
||||
if _, err := io.ReadFull(r, one); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if one[0] == '\n' {
|
||||
return string(out), nil
|
||||
}
|
||||
out = append(out, one[0])
|
||||
}
|
||||
return "", io.ErrShortBuffer
|
||||
}
|
||||
|
||||
type Header struct {
|
||||
Proto string `json:"proto"` // "tcp" or "udp"
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
func WriteHeader(w io.Writer, h Header) error {
|
||||
b, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(append(b, '\n'))
|
||||
return err
|
||||
}
|
||||
|
||||
func ReadHeader(r *bufio.Reader) (Header, error) {
|
||||
var h Header
|
||||
line, err := r.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return h, err
|
||||
}
|
||||
return h, json.Unmarshal(line, &h)
|
||||
}
|
||||
|
||||
type closeWriter interface{ CloseWrite() error }
|
||||
|
||||
// Proxy copies both directions between a and b and closes both when done.
|
||||
// aR and bR are the readers for each side (they may be buffered wrappers).
|
||||
// It returns the bytes moved a->b and b->a.
|
||||
func Proxy(a io.ReadWriteCloser, aR io.Reader, b io.ReadWriteCloser, bR io.Reader) (aToB, bToA int64) {
|
||||
var wg sync.WaitGroup
|
||||
cp := func(dst io.ReadWriteCloser, src io.Reader, n *int64) {
|
||||
defer wg.Done()
|
||||
*n, _ = io.Copy(dst, src)
|
||||
if cw, ok := dst.(closeWriter); ok {
|
||||
cw.CloseWrite()
|
||||
} else {
|
||||
dst.Close()
|
||||
}
|
||||
}
|
||||
wg.Add(2)
|
||||
go cp(b, aR, &aToB)
|
||||
go cp(a, bR, &bToA)
|
||||
wg.Wait()
|
||||
a.Close()
|
||||
b.Close()
|
||||
return
|
||||
}
|
||||
Reference in new issue
Block a user