Files
sam3/datagram.go

378 lines
11 KiB
Go
Raw Normal View History

2015-10-15 17:21:11 -04:00
package sam3
import (
"bytes"
"errors"
"fmt"
2015-10-15 17:21:11 -04:00
"net"
"sync"
2015-10-15 17:21:11 -04:00
"time"
2024-11-20 23:32:26 -05:00
"github.com/sirupsen/logrus"
2024-11-09 11:54:54 -05:00
"github.com/go-i2p/i2pkeys"
"github.com/go-i2p/sam3/common"
2015-10-15 17:21:11 -04:00
)
// The DatagramSession implements net.PacketConn. It works almost like ordinary
// UDP, except that datagrams may be at most 31kB large. These datagrams are
2016-02-10 17:54:17 -05:00
// also end-to-end encrypted, signed and includes replay-protection. And they
2015-10-15 17:21:11 -04:00
// are also built to be surveillance-resistant (yey!).
type DatagramSession struct {
2019-06-12 23:32:35 -04:00
samAddr string // address to the sam bridge (ipv4:port)
id string // tunnel name
conn net.Conn // connection to sam bridge
keys i2pkeys.I2PKeys // i2p destination keys
remoteAddr *i2pkeys.I2PAddr // optional remote I2P address
common.UDPSession
*DatagramOptions
2015-10-15 17:21:11 -04:00
}
2016-02-10 17:54:17 -05:00
// Creates a new datagram session. udpPort is the UDP port SAM is listening on,
2015-10-15 17:21:11 -04:00
// and if you set it to zero, it will use SAMs standard UDP port.
func (s *SAM) NewDatagramSession(id string, keys i2pkeys.I2PKeys, options []string, udpPort int, datagramOptions ...DatagramOptions) (*DatagramSession, error) {
2024-10-15 12:39:20 -04:00
log.WithFields(logrus.Fields{
"id": id,
"udpPort": udpPort,
}).Debug("Creating new DatagramSession")
udpSessionConfig := &common.UDPSessionConfig{
Port: udpPort,
ParentConn: s.conn,
Log: log,
DefaultPort: 7655,
AllowZeroPort: true,
// Add required session parameters
Style: "DATAGRAM",
FromPort: "0", // Allow dynamic port assignment
ToPort: "0",
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
2015-10-15 17:21:11 -04:00
}
udpconn, err := common.NewUDPSession(udpSessionConfig)
2015-10-15 17:21:11 -04:00
if err != nil {
log.WithError(err).Error("Failed to create UDP session")
2015-10-15 17:21:11 -04:00
return nil, err
}
_, lport, err := net.SplitHostPort(udpconn.Conn.LocalAddr().String())
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Failed to get local port")
s.Close()
return nil, err
}
conn, err := s.newGenericSession("DATAGRAM", id, keys, options, []string{" PORT=" + lport})
2015-10-15 17:21:11 -04:00
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Failed to create generic session")
2015-10-15 17:21:11 -04:00
return nil, err
}
if len(datagramOptions) > 0 {
return &DatagramSession{
samAddr: s.address,
id: id,
conn: conn,
keys: keys,
UDPSession: *udpconn,
DatagramOptions: &datagramOptions[0],
}, nil
}
2024-10-15 12:39:20 -04:00
log.WithField("id", id).Info("DatagramSession created successfully")
// return &DatagramSession{s.address, id, conn, udpconn, keys, rUDPAddr, nil, nil}, nil
return &DatagramSession{
samAddr: s.address,
id: id,
conn: conn,
keys: keys,
UDPSession: *udpconn,
}, nil
2015-10-15 17:21:11 -04:00
}
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) B32() string {
2024-10-15 12:39:20 -04:00
b32 := s.keys.Addr().Base32()
log.WithField("b32", b32).Debug("Generated B32 address")
return b32
}
2024-11-22 20:35:39 -05:00
func (s *DatagramSession) Dial(net, addr string) (*DatagramSession, error) {
2024-10-15 12:39:20 -04:00
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing address")
netaddr, err := s.Lookup(addr)
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Lookup failed")
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialRemote(net, addr string) (net.PacketConn, error) {
2024-10-15 12:39:20 -04:00
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing remote address")
netaddr, err := s.Lookup(addr)
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Lookup failed")
return nil, err
}
return s.DialI2PRemote(net, netaddr)
}
func (s *DatagramSession) DialI2PRemote(net string, addr net.Addr) (*DatagramSession, error) {
2024-10-15 12:39:20 -04:00
log.WithFields(logrus.Fields{
"net": net,
"addr": addr,
}).Debug("Dialing I2P remote address")
switch addr.(type) {
case *i2pkeys.I2PAddr:
s.remoteAddr = addr.(*i2pkeys.I2PAddr)
case i2pkeys.I2PAddr:
i2paddr := addr.(i2pkeys.I2PAddr)
s.remoteAddr = &i2paddr
}
return s, nil
}
2019-06-12 23:32:35 -04:00
func (s *DatagramSession) RemoteAddr() net.Addr {
2024-10-15 12:39:20 -04:00
log.WithField("remoteAddr", s.remoteAddr).Debug("Getting remote address")
2019-06-12 23:32:35 -04:00
return s.remoteAddr
}
2016-02-10 17:54:17 -05:00
// Reads one datagram sent to the destination of the DatagramSession. Returns
2015-10-15 17:21:11 -04:00
// the number of bytes read, from what address it was sent, or an error.
// implements net.PacketConn
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
2024-10-15 12:39:20 -04:00
log.Debug("Reading datagram")
// Use sync.Pool for buffers
bufPool := sync.Pool{
New: func() interface{} {
return make([]byte, len(b)+4096)
},
}
buf := bufPool.Get().([]byte)
defer bufPool.Put(buf)
2016-02-10 17:54:17 -05:00
2015-10-15 17:21:11 -04:00
for {
// very basic protection: only accept incomming UDP messages from the IP of the SAM bridge
var saddr *net.UDPAddr
n, saddr, err = s.UDPSession.Conn.ReadFromUDP(buf)
2015-10-15 17:21:11 -04:00
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Failed to read from UDP")
return 0, i2pkeys.I2PAddr(""), err
2015-10-15 17:21:11 -04:00
}
if bytes.Equal(saddr.IP, s.UDPSession.RemoteAddr.IP) {
2015-10-15 17:21:11 -04:00
continue
}
break
}
2024-11-24 01:08:43 -05:00
i := bytes.IndexByte(buf, byte(' '))
2015-10-15 17:21:11 -04:00
if i > 4096 || i > n {
2024-10-15 12:39:20 -04:00
log.Error("Could not parse incoming message remote address")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address.")
2015-10-15 17:21:11 -04:00
}
raddr, err := i2pkeys.NewI2PAddrFromString(string(buf[:i]))
2015-10-15 17:21:11 -04:00
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Could not parse incoming message remote address")
return 0, i2pkeys.I2PAddr(""), errors.New("Could not parse incomming message remote address: " + err.Error())
2015-10-15 17:21:11 -04:00
}
// shift out the incomming address to contain only the data received
2016-02-10 17:54:17 -05:00
if (n - i + 1) > len(b) {
2015-10-15 17:21:11 -04:00
copy(b, buf[i+1:i+1+len(b)])
2016-02-10 17:54:17 -05:00
return n - (i + 1), raddr, errors.New("Datagram did not fit into your buffer.")
2015-10-15 17:21:11 -04:00
} else {
copy(b, buf[i+1:n])
2024-10-15 12:39:20 -04:00
log.WithField("bytesRead", n-(i+1)).Debug("Datagram read successfully")
2016-02-10 17:54:17 -05:00
return n - (i + 1), raddr, nil
2015-10-15 17:21:11 -04:00
}
}
func (s *DatagramSession) Accept() (net.Conn, error) {
2024-10-15 12:39:20 -04:00
log.Debug("Accept called on DatagramSession")
return s, nil
}
2019-06-12 23:32:35 -04:00
func (s *DatagramSession) Read(b []byte) (n int, err error) {
2024-10-15 12:39:20 -04:00
log.Debug("Reading from DatagramSession")
2019-06-12 23:32:35 -04:00
rint, _, rerr := s.ReadFrom(b)
return rint, rerr
}
2024-11-22 20:35:39 -05:00
const (
MAX_DATAGRAM_SIZE = 31744 // Max reliable size
RECOMMENDED_SIZE = 11264 // 11KB recommended max
)
2016-02-10 17:54:17 -05:00
// Sends one signed datagram to the destination specified. At the time of
2015-10-15 17:21:11 -04:00
// writing, maximum size is 31 kilobyte, but this may change in the future.
// Implements net.PacketConn.
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) WriteTo(b []byte, addr net.Addr) (n int, err error) {
log.WithFields(logrus.Fields{
"addr": addr,
"datagramLen": len(b),
}).Debug("Writing datagram")
if len(b) > MAX_DATAGRAM_SIZE {
return 0, errors.New("datagram exceeds maximum size")
}
// Use chunking for anything above recommended size
if len(b) > RECOMMENDED_SIZE {
return s.writeChunked(b, addr)
}
// Single message path
if s.DatagramOptions != nil {
return s.writeToWithOptions(b, addr.(i2pkeys.I2PAddr))
}
2024-12-08 13:22:39 -05:00
header := []byte(fmt.Sprintf("3.1 %s %s\n", s.id, addr.(i2pkeys.I2PAddr).String()))
msg := append(header, b...)
n, err = s.UDPSession.Conn.WriteToUDP(msg, s.UDPSession.RemoteAddr)
if err != nil {
log.WithError(err).Error("Failed to write to UDP")
} else {
log.WithField("bytesWritten", n).Debug("Datagram written successfully")
}
return n, err
2024-11-23 14:19:02 -05:00
}
func (s *DatagramSession) writeChunked(b []byte, addr net.Addr) (total int, err error) {
chunkSize := RECOMMENDED_SIZE - 256 // Allow for header overhead
chunks := (len(b) + chunkSize - 1) / chunkSize
log.WithFields(logrus.Fields{
"totalSize": len(b),
"chunks": chunks,
}).Debug("Splitting datagram into chunks")
for i := 0; i < chunks; i++ {
start := i * chunkSize
end := start + chunkSize
if end > len(b) {
end = len(b)
}
chunk := b[start:end]
var n int
// Single write path that handles both cases
if s.DatagramOptions != nil {
n, err = s.writeToWithOptions(chunk, addr.(i2pkeys.I2PAddr))
} else {
2024-12-08 13:22:39 -05:00
header := []byte(fmt.Sprintf("3.1 %s %s %d %d\n", s.id, addr.(i2pkeys.I2PAddr).String(), i, chunks))
msg := append(header, chunk...)
n, err = s.UDPSession.Conn.WriteToUDP(msg, s.UDPSession.RemoteAddr)
}
if err != nil {
return total, fmt.Errorf("chunk %d/%d failed: %w", i+1, chunks, err)
}
total += n
if i < chunks-1 {
time.Sleep(50 * time.Millisecond)
}
}
return total, nil
2015-10-15 17:21:11 -04:00
}
type DatagramOptions struct {
SendTags int
TagThreshold int
Expires int
SendLeaseset bool
}
2024-11-22 20:35:39 -05:00
func (s *DatagramSession) writeToWithOptions(b []byte, addr i2pkeys.I2PAddr) (n int, err error) {
2024-12-08 13:22:39 -05:00
header := []byte(fmt.Sprintf("3.3 %s %s", s.id, addr.String()))
if s.DatagramOptions != nil {
if s.DatagramOptions.SendTags > 0 {
2024-12-08 13:22:39 -05:00
header = append(header, []byte(fmt.Sprintf(" SEND_TAGS=%d", s.DatagramOptions.SendTags))...)
}
if s.DatagramOptions.TagThreshold > 0 {
2024-12-08 13:22:39 -05:00
header = append(header, []byte(fmt.Sprintf(" TAG_THRESHOLD=%d", s.DatagramOptions.TagThreshold))...)
}
if s.DatagramOptions.Expires > 0 {
2024-12-08 13:22:39 -05:00
header = append(header, []byte(fmt.Sprintf(" EXPIRES=%d", s.DatagramOptions.Expires))...)
}
if s.DatagramOptions.SendLeaseset {
header = append(header, []byte(" SEND_LEASESET=true")...)
}
}
2024-12-08 13:22:39 -05:00
header = append(header, '\n')
msg := append(header, b...)
return s.UDPSession.Conn.WriteToUDP(msg, s.UDPSession.RemoteAddr)
}
2019-06-12 23:32:35 -04:00
func (s *DatagramSession) Write(b []byte) (int, error) {
2024-10-15 12:39:20 -04:00
log.WithField("dataLen", len(b)).Debug("Writing to DatagramSession")
2019-06-12 23:32:35 -04:00
return s.WriteTo(b, s.remoteAddr)
}
2015-10-15 17:21:11 -04:00
// Closes the DatagramSession. Implements net.PacketConn
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) Close() error {
2024-10-15 12:39:20 -04:00
log.Debug("Closing DatagramSession")
2015-10-15 17:21:11 -04:00
err := s.conn.Close()
err2 := s.UDPSession.Conn.Close()
2015-10-15 17:21:11 -04:00
if err != nil {
2024-10-15 12:39:20 -04:00
log.WithError(err).Error("Failed to close connection")
2015-10-15 17:21:11 -04:00
return err
}
2024-10-15 12:39:20 -04:00
if err2 != nil {
log.WithError(err2).Error("Failed to close UDP connection")
}
2015-10-15 17:21:11 -04:00
return err2
}
2018-09-09 01:33:24 -04:00
// Returns the I2P destination of the DatagramSession.
func (s *DatagramSession) LocalI2PAddr() i2pkeys.I2PAddr {
2024-10-15 12:39:20 -04:00
addr := s.keys.Addr()
log.WithField("localI2PAddr", addr).Debug("Getting local I2P address")
return addr
2015-10-15 17:21:11 -04:00
}
// Implements net.PacketConn
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) LocalAddr() net.Addr {
return s.LocalI2PAddr()
}
func (s *DatagramSession) Addr() net.Addr {
return s.LocalI2PAddr()
}
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) Lookup(name string) (a net.Addr, err error) {
2024-10-15 12:39:20 -04:00
log.WithField("name", name).Debug("Looking up address")
var sam *SAM
sam, err = NewSAM(s.samAddr)
if err == nil {
defer sam.Close()
a, err = sam.Lookup(name)
}
2024-10-15 12:39:20 -04:00
log.WithField("address", a).Debug("Lookup successful")
return
}
2016-02-10 17:54:17 -05:00
// Sets read and write deadlines for the DatagramSession. Implements
2015-10-15 17:21:11 -04:00
// net.PacketConn and does the same thing. Setting write deadlines for datagrams
// is seldom done.
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) SetDeadline(t time.Time) error {
2024-10-15 12:39:20 -04:00
log.WithField("deadline", t).Debug("Setting deadline")
return s.UDPSession.Conn.SetDeadline(t)
2015-10-15 17:21:11 -04:00
}
// Sets read deadline for the DatagramSession. Implements net.PacketConn
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) SetReadDeadline(t time.Time) error {
2024-10-15 12:39:20 -04:00
log.WithField("readDeadline", t).Debug("Setting read deadline")
return s.UDPSession.Conn.SetReadDeadline(t)
2015-10-15 17:21:11 -04:00
}
// Sets the write deadline for the DatagramSession. Implements net.Packetconn.
2019-02-09 17:01:29 -05:00
func (s *DatagramSession) SetWriteDeadline(t time.Time) error {
2024-10-15 12:39:20 -04:00
log.WithField("writeDeadline", t).Debug("Setting write deadline")
return s.UDPSession.Conn.SetWriteDeadline(t)
2015-10-15 17:21:11 -04:00
}
2019-07-30 14:50:14 -04:00
func (s *DatagramSession) SetWriteBuffer(bytes int) error {
2024-10-15 12:39:20 -04:00
log.WithField("bytes", bytes).Debug("Setting write buffer")
return s.UDPSession.Conn.SetWriteBuffer(bytes)
2019-07-30 14:50:14 -04:00
}