78 lines
1.8 KiB
Go
78 lines
1.8 KiB
Go
package ping
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/netip"
|
|
"os"
|
|
"runtime"
|
|
|
|
"github.com/sagernet/sing-tun"
|
|
"github.com/sagernet/sing/common/buf"
|
|
"github.com/sagernet/sing/common/control"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/sing/common/logger"
|
|
)
|
|
|
|
var _ tun.DirectRouteDestination = (*Destination)(nil)
|
|
|
|
type Destination struct {
|
|
ctx context.Context
|
|
logger logger.ContextLogger
|
|
routeContext tun.DirectRouteContext
|
|
conn *Conn
|
|
}
|
|
|
|
func ConnectDestination(ctx context.Context, logger logger.ContextLogger, controlFunc control.Func, address netip.Addr, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
|
var (
|
|
conn *Conn
|
|
err error
|
|
)
|
|
switch runtime.GOOS {
|
|
case "darwin", "ios", "windows":
|
|
conn, err = Connect(ctx, logger, false, controlFunc, address)
|
|
default:
|
|
conn, err = Connect(ctx, logger, true, controlFunc, address)
|
|
if errors.Is(err, os.ErrPermission) {
|
|
conn, err = Connect(ctx, logger, false, controlFunc, address)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d := &Destination{
|
|
ctx: ctx,
|
|
logger: logger,
|
|
routeContext: routeContext,
|
|
conn: conn,
|
|
}
|
|
go d.loopRead()
|
|
return d, nil
|
|
}
|
|
|
|
func (d *Destination) loopRead() {
|
|
for {
|
|
buffer := buf.NewPacket()
|
|
err := d.conn.ReadIP(buffer)
|
|
if err != nil {
|
|
buffer.Release()
|
|
if !E.IsClosed(err) {
|
|
d.logger.ErrorContext(d.ctx, E.Cause(err, "receive ICMP echo reply"))
|
|
}
|
|
return
|
|
}
|
|
err = d.routeContext.WritePacket(buffer.Bytes())
|
|
if err != nil {
|
|
d.logger.ErrorContext(d.ctx, E.Cause(err, "write ICMP echo reply"))
|
|
}
|
|
buffer.Release()
|
|
}
|
|
}
|
|
|
|
func (d *Destination) WritePacket(packet *buf.Buffer) error {
|
|
return d.conn.WriteIP(packet)
|
|
}
|
|
|
|
func (d *Destination) Close() error {
|
|
return d.conn.Close()
|
|
}
|