mirror of
https://github.com/soypat/lneto.git
synced 2026-08-14 20:03:45 +00:00
187 lines
4.6 KiB
Go
187 lines
4.6 KiB
Go
package internet
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"log/slog"
|
|
"net/netip"
|
|
"time"
|
|
|
|
"github.com/soypat/lneto/ipv4"
|
|
"github.com/soypat/lneto/ipv6"
|
|
"github.com/soypat/lneto/tcp"
|
|
)
|
|
|
|
type TCPConn struct {
|
|
h tcp.Handler
|
|
remoteAddr []byte
|
|
logger
|
|
|
|
rdead time.Time
|
|
wdead time.Time
|
|
lastTx time.Time
|
|
lastRx time.Time
|
|
}
|
|
type TCPConnConfig struct {
|
|
RxBuf []byte
|
|
TxBuf []byte
|
|
TxPacketQueueSize int
|
|
Logger *slog.Logger
|
|
}
|
|
|
|
func (conn *TCPConn) Configure(config *TCPConnConfig) (err error) {
|
|
err = conn.h.SetBuffers(config.TxBuf, config.RxBuf, config.TxPacketQueueSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn.logger.log = config.Logger
|
|
return nil
|
|
}
|
|
|
|
// LocalPort returns the local port on which the socket is listening or connected to.
|
|
func (conn *TCPConn) LocalPort() uint16 { return conn.h.LocalPort() }
|
|
|
|
// RemotePort returns the port of the incoming remote connection. Is non-zero if connection is established.
|
|
func (conn *TCPConn) RemotePort() uint16 { return conn.h.RemotePort() }
|
|
|
|
// State returns the TCP state of the socket.
|
|
func (conn *TCPConn) State() tcp.State { return conn.h.State() }
|
|
|
|
// BufferedInput returns the number of bytes in the socket's receive/input buffer.
|
|
func (conn *TCPConn) BufferedInput() int { return conn.h.BufferedInput() }
|
|
|
|
// OpenActive opens a connection to a remote peer with a known IP address and port combination.
|
|
// iss is the initial send sequence number which is ideally a random number which is far away from the last sequence number used on a connection to the same host.
|
|
func (conn *TCPConn) OpenActive(remote netip.AddrPort, localPort uint16, iss tcp.Value) error {
|
|
err := conn.h.OpenActive(localPort, remote.Port(), iss)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn.reset(conn.h)
|
|
raddr := remote.Addr()
|
|
if raddr.Is4() {
|
|
addr4 := raddr.As4()
|
|
conn.remoteAddr = append(conn.remoteAddr[:0], addr4[:]...)
|
|
} else if raddr.Is6() {
|
|
addr6 := raddr.As16()
|
|
conn.remoteAddr = append(conn.remoteAddr[:0], addr6[:]...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OpenListen opens a passive connection which listens for the first SYN packet to be received on a local port.
|
|
// iss is the initial send sequence number which is usually a randomly chosen number.
|
|
func (conn *TCPConn) OpenListen(localPort uint16, iss tcp.Value) error {
|
|
err := conn.h.OpenListen(localPort, iss)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn.reset(conn.h)
|
|
return nil
|
|
}
|
|
|
|
func (conn *TCPConn) RecvIP(buf []byte, off int) (err error) {
|
|
conn.trace("tcpconn.Recv:start")
|
|
if off >= len(buf) {
|
|
return errors.New("bad offset in TCPConn.Recv")
|
|
}
|
|
raddr, err := getIPAddr(buf[:off])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if conn.isRaddrSet() && !bytes.Equal(conn.remoteAddr, raddr) {
|
|
return errors.New("IP addr mismatch on TCPConn")
|
|
}
|
|
err = conn.h.Recv(buf[off:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !conn.isRaddrSet() && conn.h.RemotePort() != 0 {
|
|
conn.remoteAddr = append(conn.remoteAddr[:0], raddr...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (conn *TCPConn) HandleIP(buf []byte, off int) (n int, err error) {
|
|
if len(conn.remoteAddr) == 0 {
|
|
return 0, errors.New("unset IP address")
|
|
}
|
|
raddr, err := getIPAddr(buf[:off])
|
|
if err != nil {
|
|
return 0, err
|
|
} else if len(raddr) != len(conn.remoteAddr) {
|
|
return 0, errors.New("mismatched IP version")
|
|
}
|
|
n, err = conn.h.Send(buf[off:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
err = setDstAddr(buf[:off], conn.remoteAddr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (conn *TCPConn) Send(response []byte) (n int, err error) {
|
|
conn.trace("tcpconn.Send:start")
|
|
return conn.h.Send(response)
|
|
}
|
|
|
|
func getIPAddr(buf []byte) (addr []byte, err error) {
|
|
switch buf[0] >> 4 {
|
|
case 4:
|
|
ifrm4, err := ipv4.NewFrame(buf)
|
|
if err != nil {
|
|
return addr, err
|
|
}
|
|
addr = ifrm4.SourceAddr()[:]
|
|
case 6:
|
|
ifrm6, err := ipv6.NewFrame(buf)
|
|
if err != nil {
|
|
return addr, err
|
|
}
|
|
addr = ifrm6.SourceAddr()[:]
|
|
default:
|
|
err = errors.New("unsupported IP version")
|
|
}
|
|
return addr, err
|
|
}
|
|
|
|
func setDstAddr(buf []byte, addr []byte) (err error) {
|
|
var dstaddr []byte
|
|
switch buf[0] >> 4 {
|
|
case 4:
|
|
ifrm4, err := ipv4.NewFrame(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dstaddr = ifrm4.DestinationAddr()[:]
|
|
case 6:
|
|
ifrm6, err := ipv6.NewFrame(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dstaddr = ifrm6.DestinationAddr()[:]
|
|
default:
|
|
err = errors.New("unsupported IP version")
|
|
}
|
|
if err == nil && len(dstaddr) != len(addr) {
|
|
return errors.New("invalid ip version to setDstAddr")
|
|
}
|
|
copy(dstaddr, addr)
|
|
return nil
|
|
}
|
|
|
|
func (conn *TCPConn) isRaddrSet() bool {
|
|
return len(conn.remoteAddr) != 0
|
|
}
|
|
|
|
func (conn *TCPConn) reset(h tcp.Handler) {
|
|
*conn = TCPConn{
|
|
h: h,
|
|
remoteAddr: conn.remoteAddr[:0],
|
|
logger: conn.logger,
|
|
}
|
|
}
|