mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-12-23 09:55:39 +00:00
remove udp.go
This commit is contained in:
parent
38e7704161
commit
fad6f6b50e
@ -1,374 +0,0 @@
|
||||
package yggdrasil
|
||||
|
||||
// This communicates with peers via UDP
|
||||
// It's not as well tested or debugged as the TCP transport
|
||||
// It's intended to use UDP, so debugging/optimzing this is a high priority
|
||||
// TODO? use golang.org/x/net/ipv6.PacketConn's ReadBatch and WriteBatch?
|
||||
// To send all chunks of a message / recv all available chunks in one syscall
|
||||
// That might be faster on supported platforms, but it needs investigation
|
||||
// Chunks are currently murged, but outgoing messages aren't chunked
|
||||
// This is just to support chunking in the future, if it's needed and debugged
|
||||
// Basically, right now we might send UDP packets that are too large
|
||||
|
||||
// TODO remove old/unused code and better document live code
|
||||
|
||||
import "net"
|
||||
import "time"
|
||||
import "sync"
|
||||
import "fmt"
|
||||
|
||||
type udpInterface struct {
|
||||
core *Core
|
||||
sock *net.UDPConn // Or more general PacketConn?
|
||||
mutex sync.RWMutex // each conn has an owner goroutine
|
||||
conns map[connAddr]*connInfo
|
||||
}
|
||||
|
||||
type connAddr struct {
|
||||
ip [16]byte
|
||||
port int
|
||||
zone string
|
||||
}
|
||||
|
||||
func (c *connAddr) fromUDPAddr(u *net.UDPAddr) {
|
||||
copy(c.ip[:], u.IP.To16())
|
||||
c.port = u.Port
|
||||
c.zone = u.Zone
|
||||
}
|
||||
|
||||
func (c *connAddr) toUDPAddr() *net.UDPAddr {
|
||||
var u net.UDPAddr
|
||||
u.IP = make([]byte, 16)
|
||||
copy(u.IP, c.ip[:])
|
||||
u.Port = c.port
|
||||
u.Zone = c.zone
|
||||
return &u
|
||||
}
|
||||
|
||||
type connInfo struct {
|
||||
name string
|
||||
addr connAddr
|
||||
peer *peer
|
||||
linkIn chan []byte
|
||||
keysIn chan *udpKeys
|
||||
closeIn chan *udpKeys
|
||||
timeout int // count of how many heartbeats have been missed
|
||||
in func([]byte)
|
||||
out chan []byte
|
||||
countIn uint8
|
||||
countOut uint8
|
||||
chunkSize uint16
|
||||
}
|
||||
|
||||
type udpKeys struct {
|
||||
box boxPubKey
|
||||
sig sigPubKey
|
||||
}
|
||||
|
||||
func (iface *udpInterface) init(core *Core, addr string) (err error) {
|
||||
iface.core = core
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iface.sock, err = net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iface.conns = make(map[connAddr]*connInfo)
|
||||
go iface.reader()
|
||||
return
|
||||
}
|
||||
|
||||
func (iface *udpInterface) sendKeys(addr connAddr) {
|
||||
udpAddr := addr.toUDPAddr()
|
||||
msg := []byte{}
|
||||
msg = udp_encode(msg, 0, 0, 0, nil)
|
||||
msg = append(msg, iface.core.boxPub[:]...)
|
||||
msg = append(msg, iface.core.sigPub[:]...)
|
||||
iface.sock.WriteToUDP(msg, udpAddr)
|
||||
}
|
||||
|
||||
func (iface *udpInterface) sendClose(addr connAddr) {
|
||||
udpAddr := addr.toUDPAddr()
|
||||
msg := []byte{}
|
||||
msg = udp_encode(msg, 0, 1, 0, nil)
|
||||
msg = append(msg, iface.core.boxPub[:]...)
|
||||
msg = append(msg, iface.core.sigPub[:]...)
|
||||
iface.sock.WriteToUDP(msg, udpAddr)
|
||||
}
|
||||
|
||||
func udp_isKeys(msg []byte) bool {
|
||||
keyLen := 3 + boxPubKeyLen + sigPubKeyLen
|
||||
return len(msg) == keyLen && msg[0] == 0x00 && msg[1] == 0x00
|
||||
}
|
||||
|
||||
func udp_isClose(msg []byte) bool {
|
||||
keyLen := 3 + boxPubKeyLen + sigPubKeyLen
|
||||
return len(msg) == keyLen && msg[0] == 0x00 && msg[1] == 0x01
|
||||
}
|
||||
|
||||
func (iface *udpInterface) startConn(info *connInfo) {
|
||||
ticker := time.NewTicker(6 * time.Second)
|
||||
defer ticker.Stop()
|
||||
defer func() {
|
||||
// Cleanup
|
||||
iface.mutex.Lock()
|
||||
delete(iface.conns, info.addr)
|
||||
iface.mutex.Unlock()
|
||||
iface.core.peers.removePeer(info.peer.port)
|
||||
close(info.linkIn)
|
||||
close(info.keysIn)
|
||||
close(info.closeIn)
|
||||
close(info.out)
|
||||
iface.core.log.Println("Removing peer:", info.name)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case ks := <-info.closeIn:
|
||||
{
|
||||
if ks.box == info.peer.box && ks.sig == info.peer.sig {
|
||||
// TODO? secure this somehow
|
||||
// Maybe add a signature and sequence number (timestamp) to close and keys?
|
||||
return
|
||||
}
|
||||
}
|
||||
case ks := <-info.keysIn:
|
||||
{
|
||||
// FIXME? need signatures/sequence-numbers or something
|
||||
// Spoofers could lock out a peer with fake/bad keys
|
||||
if ks.box == info.peer.box && ks.sig == info.peer.sig {
|
||||
info.timeout = 0
|
||||
}
|
||||
}
|
||||
case <-ticker.C:
|
||||
{
|
||||
if info.timeout > 10 {
|
||||
return
|
||||
}
|
||||
info.timeout++
|
||||
iface.sendKeys(info.addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iface *udpInterface) handleClose(msg []byte, addr connAddr) {
|
||||
//defer util_putBytes(msg)
|
||||
var ks udpKeys
|
||||
_, _, _, bs := udp_decode(msg)
|
||||
switch {
|
||||
case !wire_chop_slice(ks.box[:], &bs):
|
||||
return
|
||||
case !wire_chop_slice(ks.sig[:], &bs):
|
||||
return
|
||||
}
|
||||
if ks.box == iface.core.boxPub {
|
||||
return
|
||||
}
|
||||
if ks.sig == iface.core.sigPub {
|
||||
return
|
||||
}
|
||||
iface.mutex.RLock()
|
||||
conn, isIn := iface.conns[addr]
|
||||
iface.mutex.RUnlock()
|
||||
if !isIn {
|
||||
return
|
||||
}
|
||||
func() {
|
||||
defer func() { recover() }()
|
||||
select {
|
||||
case conn.closeIn <- &ks:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) {
|
||||
//defer util_putBytes(msg)
|
||||
var ks udpKeys
|
||||
_, _, _, bs := udp_decode(msg)
|
||||
switch {
|
||||
case !wire_chop_slice(ks.box[:], &bs):
|
||||
return
|
||||
case !wire_chop_slice(ks.sig[:], &bs):
|
||||
return
|
||||
}
|
||||
if ks.box == iface.core.boxPub {
|
||||
return
|
||||
}
|
||||
if ks.sig == iface.core.sigPub {
|
||||
return
|
||||
}
|
||||
iface.mutex.RLock()
|
||||
conn, isIn := iface.conns[addr]
|
||||
iface.mutex.RUnlock()
|
||||
if !isIn {
|
||||
udpAddr := addr.toUDPAddr()
|
||||
// Check if we're authorized to connect to this key / IP
|
||||
// TODO monitor and always allow outgoing connections
|
||||
if !iface.core.peers.isAllowedEncryptionPublicKey(&ks.box) {
|
||||
// Allow unauthorized peers if they're link-local
|
||||
if !udpAddr.IP.IsLinkLocalUnicast() {
|
||||
return
|
||||
}
|
||||
}
|
||||
themNodeID := getNodeID(&ks.box)
|
||||
themAddr := address_addrForNodeID(themNodeID)
|
||||
themAddrString := net.IP(themAddr[:]).String()
|
||||
themString := fmt.Sprintf("%s@%s", themAddrString, udpAddr.String())
|
||||
conn = &connInfo{
|
||||
name: themString,
|
||||
addr: connAddr(addr),
|
||||
peer: iface.core.peers.newPeer(&ks.box, &ks.sig),
|
||||
linkIn: make(chan []byte, 1),
|
||||
keysIn: make(chan *udpKeys, 1),
|
||||
closeIn: make(chan *udpKeys, 1),
|
||||
out: make(chan []byte, 32),
|
||||
chunkSize: 576 - 60 - 8 - 3, // max safe - max ip - udp header - chunk overhead
|
||||
}
|
||||
if udpAddr.IP.IsLinkLocalUnicast() {
|
||||
ifce, err := net.InterfaceByName(udpAddr.Zone)
|
||||
if ifce != nil && err == nil {
|
||||
conn.chunkSize = uint16(ifce.MTU) - 60 - 8 - 3
|
||||
}
|
||||
}
|
||||
var inChunks uint8
|
||||
var inBuf []byte
|
||||
conn.in = func(bs []byte) {
|
||||
//defer util_putBytes(bs)
|
||||
chunks, chunk, count, payload := udp_decode(bs)
|
||||
if count != conn.countIn {
|
||||
if len(inBuf) > 0 {
|
||||
// Something went wrong
|
||||
// Forward whatever we have
|
||||
// Maybe the destination can do something about it
|
||||
msg := append(util_getBytes(), inBuf...)
|
||||
conn.peer.handlePacket(msg, conn.linkIn)
|
||||
}
|
||||
inChunks = 0
|
||||
inBuf = inBuf[:0]
|
||||
conn.countIn = count
|
||||
}
|
||||
if chunk <= chunks && chunk == inChunks+1 {
|
||||
inChunks += 1
|
||||
inBuf = append(inBuf, payload...)
|
||||
if chunks != chunk {
|
||||
return
|
||||
}
|
||||
msg := append(util_getBytes(), inBuf...)
|
||||
conn.peer.handlePacket(msg, conn.linkIn)
|
||||
inBuf = inBuf[:0]
|
||||
}
|
||||
}
|
||||
conn.peer.out = func(msg []byte) {
|
||||
defer func() { recover() }()
|
||||
select {
|
||||
case conn.out <- msg:
|
||||
conn.peer.updateQueueSize(1)
|
||||
default:
|
||||
util_putBytes(msg)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
var out []byte
|
||||
var chunks [][]byte
|
||||
for msg := range conn.out {
|
||||
chunks = chunks[:0]
|
||||
bs := msg
|
||||
for len(bs) > int(conn.chunkSize) {
|
||||
chunks, bs = append(chunks, bs[:conn.chunkSize]), bs[conn.chunkSize:]
|
||||
}
|
||||
chunks = append(chunks, bs)
|
||||
if len(chunks) > 255 {
|
||||
continue
|
||||
}
|
||||
for idx, bs := range chunks {
|
||||
nChunks, nChunk, count := uint8(len(chunks)), uint8(idx)+1, conn.countOut
|
||||
out = udp_encode(out[:0], nChunks, nChunk, count, bs)
|
||||
//iface.core.log.Println("DEBUG out:", nChunks, nChunk, count, len(bs))
|
||||
iface.sock.WriteToUDP(out, udpAddr)
|
||||
}
|
||||
conn.countOut += 1
|
||||
conn.peer.updateQueueSize(-1)
|
||||
util_putBytes(msg)
|
||||
}
|
||||
}()
|
||||
//*/
|
||||
conn.peer.close = func() { iface.sendClose(conn.addr) }
|
||||
iface.mutex.Lock()
|
||||
iface.conns[addr] = conn
|
||||
iface.mutex.Unlock()
|
||||
iface.core.log.Println("Adding peer:", conn.name)
|
||||
go iface.startConn(conn)
|
||||
go conn.peer.linkLoop(conn.linkIn)
|
||||
iface.sendKeys(conn.addr)
|
||||
}
|
||||
func() {
|
||||
defer func() { recover() }()
|
||||
select {
|
||||
case conn.keysIn <- &ks:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (iface *udpInterface) handlePacket(msg []byte, addr connAddr) {
|
||||
iface.mutex.RLock()
|
||||
if conn, isIn := iface.conns[addr]; isIn {
|
||||
conn.in(msg)
|
||||
}
|
||||
iface.mutex.RUnlock()
|
||||
}
|
||||
|
||||
func (iface *udpInterface) reader() {
|
||||
iface.core.log.Println("Listening for UDP on:", iface.sock.LocalAddr().String())
|
||||
bs := make([]byte, 65536) // This needs to be large enough for everything...
|
||||
for {
|
||||
n, udpAddr, err := iface.sock.ReadFromUDP(bs)
|
||||
//iface.core.log.Println("DEBUG: read:", bs[0], bs[1], bs[2], n)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
break
|
||||
}
|
||||
msg := bs[:n]
|
||||
var addr connAddr
|
||||
addr.fromUDPAddr(udpAddr)
|
||||
switch {
|
||||
case udp_isKeys(msg):
|
||||
var them address
|
||||
copy(them[:], udpAddr.IP.To16())
|
||||
if them.isValid() {
|
||||
continue
|
||||
}
|
||||
if udpAddr.IP.IsLinkLocalUnicast() {
|
||||
if len(iface.core.ifceExpr) == 0 {
|
||||
break
|
||||
}
|
||||
for _, expr := range iface.core.ifceExpr {
|
||||
if expr.MatchString(udpAddr.Zone) {
|
||||
iface.handleKeys(msg, addr)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case udp_isClose(msg):
|
||||
iface.handleClose(msg, addr)
|
||||
default:
|
||||
iface.handlePacket(msg, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func udp_decode(bs []byte) (chunks, chunk, count uint8, payload []byte) {
|
||||
if len(bs) >= 3 {
|
||||
chunks, chunk, count, payload = bs[0], bs[1], bs[2], bs[3:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func udp_encode(out []byte, chunks, chunk, count uint8, payload []byte) []byte {
|
||||
return append(append(out, chunks, chunk, count), payload...)
|
||||
}
|
Loading…
Reference in New Issue
Block a user