diff --git a/common/iponly/conn.go b/common/iponly/conn.go new file mode 100644 index 00000000..86e9e99b --- /dev/null +++ b/common/iponly/conn.go @@ -0,0 +1,95 @@ +package iponly + +import ( + "net" + "os" + + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +type PacketConn struct { + N.NetPacketConn + logger logger.Logger +} + +func NewPacketConn(logger logger.Logger, conn net.PacketConn) *PacketConn { + return &PacketConn{ + NetPacketConn: bufio.NewPacketConn(conn), + logger: logger, + } +} + +func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + destination := M.SocksaddrFromNet(addr) + if !destination.IsIP() { + c.logger.Debug("dropped packet to non-IP destination ", destination) + return len(p), nil + } + return c.NetPacketConn.WriteTo(p, addr) +} + +func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { + if !destination.IsIP() { + buffer.Release() + c.logger.Debug("dropped packet to non-IP destination ", destination) + return nil + } + return c.NetPacketConn.WritePacket(buffer, destination) +} + +func (c *PacketConn) CreatePacketBatchWriter() (N.PacketBatchWriter, bool) { + writer, created := bufio.CreatePacketBatchWriter(c.NetPacketConn) + if !created { + return nil, false + } + return &packetBatchWriter{c, writer}, true +} + +func (c *PacketConn) CreateConnectedPacketBatchWriter() (N.ConnectedPacketBatchWriter, bool) { + return bufio.CreateConnectedPacketBatchWriter(c.NetPacketConn) +} + +func (c *PacketConn) ReaderReplaceable() bool { + return true +} + +func (c *PacketConn) Upstream() any { + return c.NetPacketConn +} + +type packetBatchWriter struct { + *PacketConn + writer N.PacketBatchWriter +} + +func (w *packetBatchWriter) WritePacketBatch(buffers []*buf.Buffer, destinations []M.Socksaddr) error { + if len(buffers) == 0 || len(buffers) != len(destinations) { + buf.ReleaseMulti(buffers) + return os.ErrInvalid + } + writeIndex := 0 + for index, destination := range destinations { + if !destination.IsIP() { + buffers[index].Release() + w.logger.Debug("dropped packet to non-IP destination ", destination) + continue + } + buffers[writeIndex] = buffers[index] + destinations[writeIndex] = destination + writeIndex++ + } + if writeIndex == 0 { + return nil + } + return w.writer.WritePacketBatch(buffers[:writeIndex], destinations[:writeIndex]) +} + +var ( + _ N.NetPacketConn = (*PacketConn)(nil) + _ N.PacketBatchWriteCreator = (*PacketConn)(nil) + _ N.ConnectedPacketBatchWriteCreator = (*PacketConn)(nil) +) diff --git a/protocol/openconnect/client.go b/protocol/openconnect/client.go index 2021d623..16216361 100644 --- a/protocol/openconnect/client.go +++ b/protocol/openconnect/client.go @@ -15,6 +15,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/iponly" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -587,16 +588,20 @@ func (e *Endpoint) ListenPacketWithDestination(ctx context.Context, destination if err != nil { return nil, netip.Addr{}, err } - return N.ListenSerial(ctx, e.device, destination, destinationAddresses) + packetConn, destinationAddress, err := N.ListenSerial(ctx, e.device, destination, destinationAddresses) + if err != nil { + return nil, netip.Addr{}, err + } + return iponly.NewPacketConn(e.logger, packetConn), destinationAddress, nil } packetConn, err := e.device.ListenPacket(ctx, destination) if err != nil { return nil, netip.Addr{}, err } if destination.IsIP() { - return packetConn, destination.Addr, nil + return iponly.NewPacketConn(e.logger, packetConn), destination.Addr, nil } - return packetConn, netip.Addr{}, nil + return iponly.NewPacketConn(e.logger, packetConn), netip.Addr{}, nil } func (e *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { diff --git a/protocol/openvpn/client.go b/protocol/openvpn/client.go index 1fa5906a..42715ef6 100644 --- a/protocol/openvpn/client.go +++ b/protocol/openvpn/client.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/iponly" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -791,16 +792,20 @@ func (c *ClientEndpoint) ListenPacketWithDestination(ctx context.Context, destin if err != nil { return nil, netip.Addr{}, err } - return N.ListenSerial(ctx, c.device, destination, destinationAddresses) + packetConn, destinationAddress, err := N.ListenSerial(ctx, c.device, destination, destinationAddresses) + if err != nil { + return nil, netip.Addr{}, err + } + return iponly.NewPacketConn(c.logger, packetConn), destinationAddress, nil } packetConn, err := c.device.ListenPacket(ctx, destination) if err != nil { return nil, netip.Addr{}, err } if destination.IsIP() { - return packetConn, destination.Addr, nil + return iponly.NewPacketConn(c.logger, packetConn), destination.Addr, nil } - return packetConn, netip.Addr{}, nil + return iponly.NewPacketConn(c.logger, packetConn), netip.Addr{}, nil } func (c *ClientEndpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { diff --git a/protocol/openvpn/server.go b/protocol/openvpn/server.go index 24e03698..9c4ee269 100644 --- a/protocol/openvpn/server.go +++ b/protocol/openvpn/server.go @@ -12,6 +12,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/iponly" "github.com/sagernet/sing-box/common/listener" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -753,16 +754,20 @@ func (s *ServerEndpoint) ListenPacketWithDestination(ctx context.Context, destin if err != nil { return nil, netip.Addr{}, err } - return N.ListenSerial(ctx, s.device, destination, destinationAddresses) + packetConn, destinationAddress, err := N.ListenSerial(ctx, s.device, destination, destinationAddresses) + if err != nil { + return nil, netip.Addr{}, err + } + return iponly.NewPacketConn(s.logger, packetConn), destinationAddress, nil } packetConn, err := s.device.ListenPacket(ctx, destination) if err != nil { return nil, netip.Addr{}, err } if destination.IsIP() { - return packetConn, destination.Addr, nil + return iponly.NewPacketConn(s.logger, packetConn), destination.Addr, nil } - return packetConn, netip.Addr{}, nil + return iponly.NewPacketConn(s.logger, packetConn), netip.Addr{}, nil } func (s *ServerEndpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index a4efb83b..4cd0ecb9 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -26,6 +26,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/iponly" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/log" @@ -858,7 +859,7 @@ func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination for _, address := range destinationAddresses { packetConn, packetErr := t.listenPacketWithAddress(ctx, M.SocksaddrFrom(address, destination.Port)) if packetErr == nil { - return packetConn, address, nil + return iponly.NewPacketConn(t.logger, packetConn), address, nil } errors = append(errors, packetErr) } @@ -869,9 +870,9 @@ func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination return nil, netip.Addr{}, err } if destination.IsIP() { - return packetConn, destination.Addr, nil + return iponly.NewPacketConn(t.logger, packetConn), destination.Addr, nil } - return packetConn, netip.Addr{}, nil + return iponly.NewPacketConn(t.logger, packetConn), netip.Addr{}, nil } func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 958e430e..27e73456 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -11,6 +11,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/sing-box/common/iponly" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -298,16 +299,20 @@ func (w *Endpoint) ListenPacketWithDestination(ctx context.Context, destination if err != nil { return nil, netip.Addr{}, err } - return N.ListenSerial(ctx, w.endpoint, destination, destinationAddresses) + packetConn, destinationAddress, err := N.ListenSerial(ctx, w.endpoint, destination, destinationAddresses) + if err != nil { + return nil, netip.Addr{}, err + } + return iponly.NewPacketConn(w.logger, packetConn), destinationAddress, nil } packetConn, err := w.endpoint.ListenPacket(ctx, destination) if err != nil { return nil, netip.Addr{}, err } if destination.IsIP() { - return packetConn, destination.Addr, nil + return iponly.NewPacketConn(w.logger, packetConn), destination.Addr, nil } - return packetConn, netip.Addr{}, nil + return iponly.NewPacketConn(w.logger, packetConn), netip.Addr{}, nil } func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {