You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
4.6 KiB
230 lines
4.6 KiB
3 years ago
|
package neo
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
"sync"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// Net is virtual "net" package, implements mesh of peers.
|
||
|
type Net struct {
|
||
|
peers map[string]*PacketConn
|
||
|
}
|
||
|
|
||
|
type packet struct {
|
||
|
buf []byte
|
||
|
addr net.Addr
|
||
|
}
|
||
|
|
||
|
// PacketConn simulates mesh peer of Net.
|
||
|
type PacketConn struct {
|
||
|
packets chan packet
|
||
|
addr net.Addr
|
||
|
net *Net
|
||
|
|
||
|
closedMux sync.Mutex
|
||
|
closed bool
|
||
|
|
||
|
mux sync.Mutex
|
||
|
deadline notifier
|
||
|
readDeadline notifier
|
||
|
writeDeadline notifier
|
||
|
}
|
||
|
|
||
|
func addrKey(a net.Addr) string {
|
||
|
if u, ok := a.(*net.UDPAddr); ok {
|
||
|
return "udp/" + u.String()
|
||
|
}
|
||
|
return a.Network() + "/" + a.String()
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) ok() bool {
|
||
|
if c == nil {
|
||
|
return false
|
||
|
}
|
||
|
c.closedMux.Lock()
|
||
|
defer c.closedMux.Unlock()
|
||
|
return !c.closed
|
||
|
}
|
||
|
|
||
|
var ErrDeadline = errors.New("deadline")
|
||
|
|
||
|
// ReadFrom reads a packet from the connection,
|
||
|
// copying the payload into p.
|
||
|
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||
|
if !c.ok() {
|
||
|
return 0, nil, syscall.EINVAL
|
||
|
}
|
||
|
|
||
|
c.mux.Lock()
|
||
|
deadline := c.deadline
|
||
|
readDeadline := c.readDeadline
|
||
|
c.mux.Unlock()
|
||
|
|
||
|
select {
|
||
|
case pp := <-c.packets:
|
||
|
return copy(p, pp.buf), pp.addr, nil
|
||
|
case <-readDeadline:
|
||
|
return 0, nil, ErrDeadline
|
||
|
case <-deadline:
|
||
|
return 0, nil, ErrDeadline
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// WriteTo writes a packet with payload p to addr.
|
||
|
func (c *PacketConn) WriteTo(p []byte, a net.Addr) (n int, err error) {
|
||
|
if !c.ok() {
|
||
|
return 0, syscall.EINVAL
|
||
|
}
|
||
|
|
||
|
c.mux.Lock()
|
||
|
deadline := c.deadline
|
||
|
writeDeadline := c.writeDeadline
|
||
|
c.mux.Unlock()
|
||
|
|
||
|
select {
|
||
|
case c.net.peers[addrKey(a)].packets <- packet{
|
||
|
addr: c.addr,
|
||
|
buf: append([]byte{}, p...),
|
||
|
}:
|
||
|
return len(p), nil
|
||
|
case <-writeDeadline:
|
||
|
return 0, ErrDeadline
|
||
|
case <-deadline:
|
||
|
return 0, ErrDeadline
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) LocalAddr() net.Addr { return c.addr }
|
||
|
|
||
|
// Close closes the connection.
|
||
|
func (c *PacketConn) Close() error {
|
||
|
if !c.ok() {
|
||
|
return syscall.EINVAL
|
||
|
}
|
||
|
c.closedMux.Lock()
|
||
|
defer c.closedMux.Unlock()
|
||
|
if c.closed {
|
||
|
return syscall.EINVAL
|
||
|
}
|
||
|
c.closed = true
|
||
|
close(c.packets)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type notifier chan struct{}
|
||
|
|
||
|
func simpleDeadline(t time.Time) notifier {
|
||
|
deadline := make(notifier)
|
||
|
go func() {
|
||
|
now := time.Now()
|
||
|
<-time.After(t.Sub(now))
|
||
|
close(deadline)
|
||
|
}()
|
||
|
|
||
|
return deadline
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) SetDeadline(t time.Time) error {
|
||
|
if !c.ok() {
|
||
|
return syscall.EINVAL
|
||
|
}
|
||
|
c.mux.Lock()
|
||
|
c.deadline = simpleDeadline(t)
|
||
|
c.mux.Unlock()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
||
|
if !c.ok() {
|
||
|
return syscall.EINVAL
|
||
|
}
|
||
|
c.mux.Lock()
|
||
|
c.readDeadline = simpleDeadline(t)
|
||
|
c.mux.Unlock()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||
|
if !c.ok() {
|
||
|
return syscall.EINVAL
|
||
|
}
|
||
|
c.mux.Lock()
|
||
|
c.writeDeadline = simpleDeadline(t)
|
||
|
c.mux.Unlock()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type NetAddr struct {
|
||
|
Net string
|
||
|
Address string
|
||
|
}
|
||
|
|
||
|
func (n NetAddr) Network() string { return n.Net }
|
||
|
func (n NetAddr) String() string { return n.Address }
|
||
|
|
||
|
// ResolveUDPAddr returns an address of UDP end point.
|
||
|
func (n *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) {
|
||
|
a := &net.UDPAddr{
|
||
|
Port: 0,
|
||
|
IP: net.IPv4(127, 0, 0, 1),
|
||
|
}
|
||
|
host, port, err := net.SplitHostPort(address)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if a.IP = net.ParseIP(host); a.IP == nil {
|
||
|
// Probably we should use virtual DNS here.
|
||
|
return nil, errors.New("bad IP")
|
||
|
}
|
||
|
if a.Port, err = strconv.Atoi(port); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return a, nil
|
||
|
}
|
||
|
|
||
|
// ListenPacket announces on the local network address.
|
||
|
func (n *Net) ListenPacket(network, address string) (net.PacketConn, error) {
|
||
|
if network != "udp4" && network != "udp" && network != "udp6" {
|
||
|
return nil, errors.New("bad net")
|
||
|
}
|
||
|
a, err := n.ResolveUDPAddr(network, address)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
pc := &PacketConn{
|
||
|
net: n,
|
||
|
addr: a,
|
||
|
packets: make(chan packet, 10),
|
||
|
}
|
||
|
n.peers[addrKey(a)] = pc
|
||
|
return pc, nil
|
||
|
}
|
||
|
|
||
|
// NAT implements facility for Network Address Translation simulation.
|
||
|
//
|
||
|
// Basic example:
|
||
|
// [ A ] <-----> [ NAT1 ] <-----> [ NAT2 ] <-----> [ B ]
|
||
|
// IPa IPa' IPb' IPb
|
||
|
//
|
||
|
// 1) A sends packet P with dst = IPb'
|
||
|
// 2) NAT1 receives packet P and changes it's src to IPa',
|
||
|
// sending it to NAT2 from IPa'.
|
||
|
// 3) NAT2 receives packet P from IPa' to IPb', does a lookup to
|
||
|
// NAT translation table and finds association IPb' <-> IPb.
|
||
|
// Then it sends packet P to B.
|
||
|
// 4) B receives packet P from NAT2, observing that it has src = IPa'.
|
||
|
//
|
||
|
// Now B can repeat steps 1-4 and send packet back.
|
||
|
//
|
||
|
// IPa = 10.5.0.1:30000
|
||
|
// IPa' = 83.30.100.1:23100
|
||
|
// IPb' = 91.10.100.1:13000
|
||
|
// IPb = 10.1.0.1:20000
|
||
|
type NAT struct {
|
||
|
// TODO(ar): implement
|
||
|
}
|