| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934 |
- package ike
- import (
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "net"
- "strconv"
- "strings"
- "sync"
- "time"
- "vocat/internal/vowifi"
- )
- type datagramTransport interface {
- LocalAddr() *net.UDPAddr
- RemoteAddr() *net.UDPAddr
- Float(context.Context) error
- RoundTrip(context.Context, []byte) ([]byte, error)
- SendESP(context.Context, []byte) error
- ReceiveESP(context.Context, []byte) (int, error)
- SendSessionPacket(context.Context, []byte, bool) error
- ReceiveSessionPacket(context.Context, []byte) (int, bool, error)
- Close() error
- }
- type transportConfig struct {
- Resolver *net.Resolver
- Dialer *net.Dialer
- Timeout time.Duration
- }
- func newDatagramTransport(
- ctx context.Context,
- config transportConfig,
- route vowifi.ProxyRoute,
- host string,
- ) (datagramTransport, error) {
- if config.Resolver == nil {
- config.Resolver = net.DefaultResolver
- }
- if config.Dialer == nil {
- config.Dialer = &net.Dialer{}
- }
- if config.Timeout <= 0 {
- config.Timeout = 12 * time.Second
- }
- addresses, err := resolveEPDG(ctx, config.Resolver, host)
- if err != nil {
- return nil, fmt.Errorf("ike: resolve ePDG: %w", err)
- }
- var remoteIPs []net.IP
- for _, address := range addresses {
- candidate := address.IP.To4()
- if candidate == nil {
- candidate = address.IP.To16()
- }
- if candidate == nil {
- continue
- }
- duplicate := false
- for _, existing := range remoteIPs {
- if existing.Equal(candidate) {
- duplicate = true
- break
- }
- }
- if !duplicate {
- remoteIPs = append(remoteIPs, append(net.IP(nil), candidate...))
- }
- }
- if len(remoteIPs) == 0 {
- return nil, errors.New("ike: ePDG did not resolve to an IP address")
- }
- remotes := make([]*net.UDPAddr, 0, len(remoteIPs))
- for _, remoteIP := range remoteIPs {
- remotes = append(remotes, &net.UDPAddr{IP: remoteIP, Port: 500})
- }
- switch route.Mode {
- case "", vowifi.ProxyModeDirect:
- return newDirectUDP(ctx, config, remotes[0])
- case vowifi.ProxyModeSOCKS5:
- return newSOCKS5UDP(ctx, config, route, remotes)
- default:
- return nil, fmt.Errorf("ike: unsupported proxy mode %q", route.Mode)
- }
- }
- func roundTripDatagram(
- ctx context.Context,
- timeout time.Duration,
- write func([]byte) error,
- read func([]byte, time.Time) (int, error),
- packet []byte,
- ) ([]byte, error) {
- if ctx == nil {
- ctx = context.Background()
- }
- deadline := time.Now().Add(timeout)
- if callerDeadline, ok := ctx.Deadline(); ok && callerDeadline.Before(deadline) {
- deadline = callerDeadline
- }
- retransmit := []time.Duration{500 * time.Millisecond, time.Second, 2 * time.Second, 4 * time.Second}
- buffer := make([]byte, 65535)
- var lastErr error
- for _, interval := range retransmit {
- if err := ctx.Err(); err != nil {
- return nil, err
- }
- if err := write(packet); err != nil {
- return nil, err
- }
- attemptDeadline := time.Now().Add(interval)
- if deadline.Before(attemptDeadline) {
- attemptDeadline = deadline
- }
- for time.Now().Before(attemptDeadline) {
- if err := ctx.Err(); err != nil {
- return nil, err
- }
- n, err := read(buffer, attemptDeadline)
- if err == nil {
- return append([]byte(nil), buffer[:n]...), nil
- }
- if timeoutError, ok := err.(net.Error); ok && timeoutError.Timeout() {
- lastErr = err
- break
- }
- return nil, err
- }
- if !time.Now().Before(deadline) {
- break
- }
- }
- if lastErr == nil {
- lastErr = context.DeadlineExceeded
- }
- return nil, fmt.Errorf("ike: UDP exchange timed out: %w", lastErr)
- }
- type directUDP struct {
- mu sync.Mutex
- readMu sync.Mutex
- writeMu sync.Mutex
- config transportConfig
- conn *net.UDPConn
- remote *net.UDPAddr
- floated bool
- }
- func newDirectUDP(ctx context.Context, config transportConfig, remote *net.UDPAddr) (*directUDP, error) {
- transport := &directUDP{config: config, remote: cloneUDPAddr(remote)}
- if err := transport.dial(ctx, false); err != nil {
- return nil, err
- }
- return transport, nil
- }
- func (transport *directUDP) dial(ctx context.Context, bind4500 bool) error {
- dialer := *transport.config.Dialer
- if bind4500 {
- localIP := net.IP(nil)
- if transport.conn != nil {
- if current, ok := transport.conn.LocalAddr().(*net.UDPAddr); ok {
- localIP = append(net.IP(nil), current.IP...)
- }
- }
- dialer.LocalAddr = &net.UDPAddr{IP: localIP, Port: 4500}
- }
- connection, err := dialer.DialContext(ctx, "udp", transport.remote.String())
- if err != nil && bind4500 {
- dialer.LocalAddr = nil
- connection, err = dialer.DialContext(ctx, "udp", transport.remote.String())
- }
- if err != nil {
- return fmt.Errorf("ike: dial ePDG UDP: %w", err)
- }
- udp, ok := connection.(*net.UDPConn)
- if !ok {
- _ = connection.Close()
- return errors.New("ike: UDP dialer returned a non-UDP connection")
- }
- old := transport.conn
- transport.conn = udp
- if old != nil {
- _ = old.Close()
- }
- return nil
- }
- func (transport *directUDP) LocalAddr() *net.UDPAddr {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.conn == nil {
- return nil
- }
- address, _ := transport.conn.LocalAddr().(*net.UDPAddr)
- return cloneUDPAddr(address)
- }
- func (transport *directUDP) RemoteAddr() *net.UDPAddr {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- return cloneUDPAddr(transport.remote)
- }
- func (transport *directUDP) Float(ctx context.Context) error {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.floated {
- return nil
- }
- transport.remote.Port = 4500
- if err := transport.dial(ctx, true); err != nil {
- return err
- }
- transport.floated = true
- return nil
- }
- func (transport *directUDP) RoundTrip(ctx context.Context, packet []byte) ([]byte, error) {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.conn == nil {
- return nil, errors.New("ike: UDP transport is closed")
- }
- requestHeader, _, err := parseIKEPacket(packet)
- if err != nil {
- return nil, fmt.Errorf("ike: invalid outbound packet: %w", err)
- }
- wirePacket := packet
- if transport.floated {
- wirePacket = append([]byte{0, 0, 0, 0}, packet...)
- }
- write := func(value []byte) error {
- if err := transport.conn.SetWriteDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return err
- }
- _, err := transport.conn.Write(value)
- return err
- }
- read := func(buffer []byte, attemptDeadline time.Time) (int, error) {
- for {
- if err := transport.conn.SetReadDeadline(attemptDeadline); err != nil {
- return 0, err
- }
- n, err := transport.conn.Read(buffer)
- if err != nil {
- return 0, err
- }
- if transport.floated {
- // IKE and ESP legitimately share UDP/4500. An ESP packet can
- // arrive immediately before the IKE response that completes
- // CHILD_SA setup; discard it here and keep the same absolute
- // attempt deadline while waiting for marked IKE.
- if !hasNonESPMarker(buffer[:n]) {
- continue
- }
- copy(buffer, buffer[4:n])
- n -= 4
- }
- if !ikeResponseMatchesRequest(buffer[:n], requestHeader) {
- continue
- }
- return n, nil
- }
- }
- return roundTripDatagram(ctx, transport.config.Timeout, write, read, wirePacket)
- }
- func (transport *directUDP) SendESP(ctx context.Context, packet []byte) error {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.conn == nil || !transport.floated {
- return errors.New("ike: ESP relay requires an active UDP/4500 transport")
- }
- if len(packet) < 8 {
- return errors.New("ike: ESP packet is too short")
- }
- if err := transport.conn.SetWriteDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return err
- }
- _, err := transport.conn.Write(packet)
- return err
- }
- func (transport *directUDP) ReceiveESP(ctx context.Context, buffer []byte) (int, error) {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.conn == nil || !transport.floated {
- return 0, errors.New("ike: ESP relay requires an active UDP/4500 transport")
- }
- if err := transport.conn.SetReadDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return 0, err
- }
- n, err := transport.conn.Read(buffer)
- if err != nil {
- return 0, err
- }
- if n >= 4 && buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0 && buffer[3] == 0 {
- return 0, errors.New("ike: received an IKE packet on the ESP relay")
- }
- return n, nil
- }
- func (transport *directUDP) SendSessionPacket(ctx context.Context, packet []byte, ike bool) error {
- transport.mu.Lock()
- connection := transport.conn
- floated := transport.floated
- transport.mu.Unlock()
- if connection == nil {
- return errors.New("ike: UDP transport is closed")
- }
- wire := packet
- if floated {
- if ike {
- wire = append([]byte{0, 0, 0, 0}, packet...)
- }
- } else if !ike {
- return errors.New("ike: ESP is not UDP encapsulated on an un-floated transport")
- }
- transport.writeMu.Lock()
- defer transport.writeMu.Unlock()
- if err := connection.SetWriteDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return err
- }
- _, err := connection.Write(wire)
- return err
- }
- func (transport *directUDP) ReceiveSessionPacket(ctx context.Context, buffer []byte) (int, bool, error) {
- transport.mu.Lock()
- connection := transport.conn
- floated := transport.floated
- transport.mu.Unlock()
- if connection == nil {
- return 0, false, errors.New("ike: UDP transport is closed")
- }
- transport.readMu.Lock()
- defer transport.readMu.Unlock()
- if err := connection.SetReadDeadline(deadlineFor(ctx, time.Second)); err != nil {
- return 0, false, err
- }
- n, err := connection.Read(buffer)
- if err != nil {
- return 0, false, err
- }
- if !floated {
- return n, true, nil
- }
- if n >= 4 && buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0 && buffer[3] == 0 {
- copy(buffer, buffer[4:n])
- return n - 4, true, nil
- }
- return n, false, nil
- }
- func (transport *directUDP) Close() error {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.conn == nil {
- return nil
- }
- err := transport.conn.Close()
- transport.conn = nil
- return err
- }
- type socks5UDP struct {
- mu sync.Mutex
- readMu sync.Mutex
- writeMu sync.Mutex
- config transportConfig
- control net.Conn
- udp *net.UDPConn
- relay *net.UDPAddr
- remote *net.UDPAddr
- remotes []*net.UDPAddr
- floated bool
- }
- func newSOCKS5UDP(
- ctx context.Context,
- config transportConfig,
- route vowifi.ProxyRoute,
- remotes []*net.UDPAddr,
- ) (*socks5UDP, error) {
- if len(remotes) == 0 || remotes[0] == nil {
- return nil, errors.New("ike: SOCKS5 transport requires an ePDG destination")
- }
- proxyAddress := strings.TrimSpace(route.Address)
- if _, _, err := net.SplitHostPort(proxyAddress); err != nil {
- return nil, fmt.Errorf("ike: invalid SOCKS5 proxy address: %w", err)
- }
- control, err := config.Dialer.DialContext(ctx, "tcp", proxyAddress)
- if err != nil {
- return nil, fmt.Errorf("ike: connect SOCKS5 proxy %s: %w", proxyAddress, err)
- }
- fail := func(cause error) (*socks5UDP, error) {
- _ = control.Close()
- return nil, cause
- }
- if err := control.SetDeadline(deadlineFor(ctx, config.Timeout)); err != nil {
- return fail(err)
- }
- methods := []byte{0}
- if route.Username != "" {
- methods = append(methods, 2)
- }
- greeting := append([]byte{5, byte(len(methods))}, methods...)
- if _, err := control.Write(greeting); err != nil {
- return fail(fmt.Errorf("ike: SOCKS5 greeting: %w", err))
- }
- var selection [2]byte
- if _, err := io.ReadFull(control, selection[:]); err != nil {
- return fail(fmt.Errorf("ike: SOCKS5 method selection: %w", err))
- }
- if selection[0] != 5 {
- return fail(errors.New("ike: SOCKS5 proxy returned an invalid version"))
- }
- switch selection[1] {
- case 0:
- case 2:
- if err := socksUserPassword(control, route.Username, route.Password); err != nil {
- return fail(err)
- }
- default:
- return fail(fmt.Errorf("ike: SOCKS5 proxy selected unsupported authentication method %d", selection[1]))
- }
- if _, err := control.Write([]byte{5, 3, 0, 1, 0, 0, 0, 0, 0, 0}); err != nil {
- return fail(fmt.Errorf("ike: SOCKS5 UDP ASSOCIATE request: %w", err))
- }
- relay, err := readSOCKS5Reply(ctx, control, config.Resolver)
- if err != nil {
- return fail(err)
- }
- if relay.IP == nil || relay.IP.IsUnspecified() {
- if peer, ok := control.RemoteAddr().(*net.TCPAddr); ok {
- relay.IP = append(net.IP(nil), peer.IP...)
- }
- }
- udpConnection, err := net.DialUDP("udp", nil, relay)
- if err != nil {
- return fail(fmt.Errorf("ike: dial SOCKS5 UDP relay: %w", err))
- }
- _ = control.SetDeadline(time.Time{})
- return &socks5UDP{
- config: config,
- control: control,
- udp: udpConnection,
- relay: relay,
- remote: cloneUDPAddr(remotes[0]),
- remotes: cloneUDPAddrs(remotes),
- }, nil
- }
- func socksUserPassword(connection net.Conn, username, password string) error {
- if len(username) > 255 || len(password) > 255 {
- return errors.New("ike: SOCKS5 username or password exceeds 255 bytes")
- }
- request := []byte{1, byte(len(username))}
- request = append(request, username...)
- request = append(request, byte(len(password)))
- request = append(request, password...)
- if _, err := connection.Write(request); err != nil {
- return fmt.Errorf("ike: SOCKS5 credential exchange: %w", err)
- }
- var response [2]byte
- if _, err := io.ReadFull(connection, response[:]); err != nil {
- return fmt.Errorf("ike: SOCKS5 credential response: %w", err)
- }
- if response[0] != 1 || response[1] != 0 {
- return errors.New("ike: SOCKS5 authentication failed")
- }
- return nil
- }
- func readSOCKS5Reply(ctx context.Context, connection net.Conn, resolver *net.Resolver) (*net.UDPAddr, error) {
- var header [4]byte
- if _, err := io.ReadFull(connection, header[:]); err != nil {
- return nil, fmt.Errorf("ike: SOCKS5 UDP ASSOCIATE response: %w", err)
- }
- if header[0] != 5 || header[1] != 0 || header[2] != 0 {
- return nil, fmt.Errorf("ike: SOCKS5 UDP ASSOCIATE rejected with code %d", header[1])
- }
- ip, name, err := readSOCKSAddress(connection, header[3])
- if err != nil {
- return nil, err
- }
- var encodedPort [2]byte
- if _, err := io.ReadFull(connection, encodedPort[:]); err != nil {
- return nil, fmt.Errorf("ike: SOCKS5 relay port: %w", err)
- }
- if ip == nil && name != "" {
- if resolver == nil {
- resolver = net.DefaultResolver
- }
- addresses, err := resolver.LookupIPAddr(ctx, name)
- if err != nil || len(addresses) == 0 {
- return nil, fmt.Errorf("ike: resolve SOCKS5 UDP relay domain %q: %w", name, err)
- }
- ip = addresses[0].IP
- }
- return &net.UDPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(encodedPort[:]))}, nil
- }
- func readSOCKSAddress(reader io.Reader, kind byte) (net.IP, string, error) {
- switch kind {
- case 1:
- ip := make(net.IP, net.IPv4len)
- if _, err := io.ReadFull(reader, ip); err != nil {
- return nil, "", err
- }
- return ip, "", nil
- case 4:
- ip := make(net.IP, net.IPv6len)
- if _, err := io.ReadFull(reader, ip); err != nil {
- return nil, "", err
- }
- return ip, "", nil
- case 3:
- var length [1]byte
- if _, err := io.ReadFull(reader, length[:]); err != nil {
- return nil, "", err
- }
- name := make([]byte, int(length[0]))
- if _, err := io.ReadFull(reader, name); err != nil {
- return nil, "", err
- }
- return nil, string(name), nil
- default:
- return nil, "", fmt.Errorf("ike: unsupported SOCKS5 address type %d", kind)
- }
- }
- func (transport *socks5UDP) LocalAddr() *net.UDPAddr {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.udp == nil {
- return nil
- }
- address, _ := transport.udp.LocalAddr().(*net.UDPAddr)
- return cloneUDPAddr(address)
- }
- func (transport *socks5UDP) RemoteAddr() *net.UDPAddr {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- return cloneUDPAddr(transport.remote)
- }
- func (transport *socks5UDP) Float(_ context.Context) error {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- transport.remote.Port = 4500
- transport.floated = true
- return nil
- }
- func (transport *socks5UDP) RoundTrip(ctx context.Context, packet []byte) ([]byte, error) {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.udp == nil {
- return nil, errors.New("ike: SOCKS5 UDP transport is closed")
- }
- requestHeader, _, err := parseIKEPacket(packet)
- if err != nil {
- return nil, fmt.Errorf("ike: invalid outbound packet: %w", err)
- }
- // Carrier ePDG hostnames commonly return several gateways. A SOCKS5
- // egress can reach a different subset than the local host, so an initial
- // timeout on one address must not make the entire hostname unavailable.
- // Once a gateway answers, keep it pinned for the lifetime of the IKE SA.
- if !transport.floated && requestHeader.Exchange == exchangeIKEInit && requestHeader.MessageID == 0 && len(transport.remotes) > 1 {
- var lastErr error
- for _, candidate := range transport.remotes {
- transport.remote = cloneUDPAddr(candidate)
- response, attemptErr := transport.roundTripLocked(ctx, packet, requestHeader)
- if attemptErr == nil {
- return response, nil
- }
- lastErr = attemptErr
- if ctx.Err() != nil || !isNetworkTimeout(attemptErr) {
- return nil, attemptErr
- }
- }
- return nil, fmt.Errorf("ike: all %d resolved ePDG addresses timed out: %w", len(transport.remotes), lastErr)
- }
- return transport.roundTripLocked(ctx, packet, requestHeader)
- }
- func (transport *socks5UDP) roundTripLocked(ctx context.Context, packet []byte, requestHeader ikeHeader) ([]byte, error) {
- wireIKE := packet
- if transport.floated {
- wireIKE = append([]byte{0, 0, 0, 0}, packet...)
- }
- datagram, err := marshalSOCKS5Datagram(transport.remote, wireIKE)
- if err != nil {
- return nil, err
- }
- write := func(value []byte) error {
- if err := transport.udp.SetWriteDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return err
- }
- _, err := transport.udp.Write(value)
- return err
- }
- read := func(buffer []byte, attemptDeadline time.Time) (int, error) {
- for {
- payload, err := readExpectedSOCKS5Datagram(
- transport.udp,
- transport.remote,
- buffer,
- attemptDeadline,
- )
- if err != nil {
- return 0, err
- }
- if transport.floated {
- // The relay can deliver ESP before the marked IKE response on
- // the same UDP/4500 association. Do not accept it as IKE, and
- // do not abort the exchange; keep waiting within the original
- // deadline.
- if !hasNonESPMarker(payload) {
- continue
- }
- payload = payload[4:]
- }
- if !ikeResponseMatchesRequest(payload, requestHeader) {
- continue
- }
- copy(buffer, payload)
- return len(payload), nil
- }
- }
- return roundTripDatagram(ctx, transport.config.Timeout, write, read, datagram)
- }
- func isNetworkTimeout(err error) bool {
- var networkError net.Error
- return errors.As(err, &networkError) && networkError.Timeout()
- }
- func (transport *socks5UDP) SendESP(ctx context.Context, packet []byte) error {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.udp == nil || !transport.floated {
- return errors.New("ike: SOCKS5 ESP relay requires an active UDP/4500 association")
- }
- if len(packet) < 8 {
- return errors.New("ike: ESP packet is too short")
- }
- datagram, err := marshalSOCKS5Datagram(transport.remote, packet)
- if err != nil {
- return err
- }
- if err := transport.udp.SetWriteDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return err
- }
- _, err = transport.udp.Write(datagram)
- return err
- }
- func (transport *socks5UDP) ReceiveESP(ctx context.Context, buffer []byte) (int, error) {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- if transport.udp == nil || !transport.floated {
- return 0, errors.New("ike: SOCKS5 ESP relay requires an active UDP/4500 association")
- }
- wire := make([]byte, len(buffer)+32)
- payload, err := readExpectedSOCKS5Datagram(
- transport.udp,
- transport.remote,
- wire,
- deadlineFor(ctx, transport.config.Timeout),
- )
- if err != nil {
- return 0, err
- }
- if len(payload) >= 4 && payload[0] == 0 && payload[1] == 0 && payload[2] == 0 && payload[3] == 0 {
- return 0, errors.New("ike: received an IKE packet on the SOCKS5 ESP relay")
- }
- if len(payload) > len(buffer) {
- return 0, io.ErrShortBuffer
- }
- copy(buffer, payload)
- return len(payload), nil
- }
- func (transport *socks5UDP) SendSessionPacket(ctx context.Context, packet []byte, ike bool) error {
- transport.mu.Lock()
- connection := transport.udp
- floated := transport.floated
- remote := cloneUDPAddr(transport.remote)
- transport.mu.Unlock()
- if connection == nil || !floated {
- return errors.New("ike: SOCKS5 session transport is not on UDP/4500")
- }
- wire := packet
- if ike {
- wire = append([]byte{0, 0, 0, 0}, packet...)
- }
- datagram, err := marshalSOCKS5Datagram(remote, wire)
- if err != nil {
- return err
- }
- transport.writeMu.Lock()
- defer transport.writeMu.Unlock()
- if err := connection.SetWriteDeadline(deadlineFor(ctx, transport.config.Timeout)); err != nil {
- return err
- }
- _, err = connection.Write(datagram)
- return err
- }
- func (transport *socks5UDP) ReceiveSessionPacket(ctx context.Context, buffer []byte) (int, bool, error) {
- transport.mu.Lock()
- connection := transport.udp
- floated := transport.floated
- remote := cloneUDPAddr(transport.remote)
- transport.mu.Unlock()
- if connection == nil || !floated {
- return 0, false, errors.New("ike: SOCKS5 session transport is not on UDP/4500")
- }
- wire := make([]byte, len(buffer)+32)
- transport.readMu.Lock()
- defer transport.readMu.Unlock()
- payload, err := readExpectedSOCKS5Datagram(
- connection,
- remote,
- wire,
- deadlineFor(ctx, time.Second),
- )
- if err != nil {
- return 0, false, err
- }
- isIKE := len(payload) >= 4 && payload[0] == 0 && payload[1] == 0 && payload[2] == 0 && payload[3] == 0
- if isIKE {
- payload = payload[4:]
- }
- if len(payload) > len(buffer) {
- return 0, false, io.ErrShortBuffer
- }
- copy(buffer, payload)
- return len(payload), isIKE, nil
- }
- func (transport *socks5UDP) Close() error {
- transport.mu.Lock()
- defer transport.mu.Unlock()
- var errs []error
- if transport.udp != nil {
- if err := transport.udp.Close(); err != nil {
- errs = append(errs, err)
- }
- transport.udp = nil
- }
- if transport.control != nil {
- if err := transport.control.Close(); err != nil {
- errs = append(errs, err)
- }
- transport.control = nil
- }
- return errors.Join(errs...)
- }
- // readExpectedSOCKS5Datagram discards valid datagrams attributed to a
- // different destination until the caller's deadline. A retransmitted
- // IKE_SA_INIT response from UDP/500 can legitimately remain queued after the
- // transport floats to UDP/4500; it must not be accepted as the current
- // exchange, but it is not a reason to abort the authenticated session either.
- func readExpectedSOCKS5Datagram(
- connection *net.UDPConn,
- remote *net.UDPAddr,
- wire []byte,
- deadline time.Time,
- ) ([]byte, error) {
- if connection == nil || remote == nil {
- return nil, errors.New("ike: SOCKS5 UDP transport is closed")
- }
- for {
- if err := connection.SetReadDeadline(deadline); err != nil {
- return nil, err
- }
- n, err := connection.Read(wire)
- if err != nil {
- return nil, err
- }
- payload, source, err := parseSOCKS5Datagram(wire[:n])
- if err != nil {
- return nil, err
- }
- if source.IP.Equal(remote.IP) && source.Port == remote.Port {
- return payload, nil
- }
- }
- }
- func hasNonESPMarker(packet []byte) bool {
- return len(packet) >= 4 &&
- packet[0] == 0 &&
- packet[1] == 0 &&
- packet[2] == 0 &&
- packet[3] == 0
- }
- func ikeResponseMatchesRequest(
- packet []byte,
- request ikeHeader,
- ) bool {
- response, _, err := parseIKEPacket(packet)
- if err != nil {
- return false
- }
- if response.InitiatorSPI != request.InitiatorSPI ||
- response.Exchange != request.Exchange ||
- response.MessageID != request.MessageID ||
- response.Flags&flagResponse == 0 ||
- response.Flags&flagInitiator != 0 {
- return false
- }
- var zeroSPI [8]byte
- if request.ResponderSPI == zeroSPI {
- return response.ResponderSPI != zeroSPI
- }
- return response.ResponderSPI == request.ResponderSPI
- }
- func marshalSOCKS5Datagram(remote *net.UDPAddr, payload []byte) ([]byte, error) {
- if remote == nil || remote.IP == nil || remote.Port < 1 || remote.Port > 65535 {
- return nil, errors.New("ike: invalid SOCKS5 UDP destination")
- }
- result := []byte{0, 0, 0}
- if ip4 := remote.IP.To4(); ip4 != nil {
- result = append(result, 1)
- result = append(result, ip4...)
- } else if ip16 := remote.IP.To16(); ip16 != nil {
- result = append(result, 4)
- result = append(result, ip16...)
- } else {
- return nil, errors.New("ike: SOCKS5 UDP destination is not an IP address")
- }
- var port [2]byte
- binary.BigEndian.PutUint16(port[:], uint16(remote.Port))
- result = append(result, port[:]...)
- result = append(result, payload...)
- return result, nil
- }
- func parseSOCKS5Datagram(encoded []byte) ([]byte, *net.UDPAddr, error) {
- if len(encoded) < 4 || encoded[0] != 0 || encoded[1] != 0 {
- return nil, nil, errors.New("ike: malformed SOCKS5 UDP datagram")
- }
- if encoded[2] != 0 {
- return nil, nil, errors.New("ike: fragmented SOCKS5 UDP datagrams are unsupported")
- }
- offset := 4
- var ip net.IP
- switch encoded[3] {
- case 1:
- if offset+4 > len(encoded) {
- return nil, nil, errors.New("ike: truncated SOCKS5 IPv4 address")
- }
- ip = append(net.IP(nil), encoded[offset:offset+4]...)
- offset += 4
- case 4:
- if offset+16 > len(encoded) {
- return nil, nil, errors.New("ike: truncated SOCKS5 IPv6 address")
- }
- ip = append(net.IP(nil), encoded[offset:offset+16]...)
- offset += 16
- case 3:
- if offset >= len(encoded) {
- return nil, nil, errors.New("ike: truncated SOCKS5 domain length")
- }
- length := int(encoded[offset])
- offset++
- if offset+length > len(encoded) {
- return nil, nil, errors.New("ike: truncated SOCKS5 domain")
- }
- addresses, err := net.LookupIP(string(encoded[offset : offset+length]))
- if err != nil || len(addresses) == 0 {
- return nil, nil, errors.New("ike: cannot resolve SOCKS5 UDP response domain")
- }
- ip = addresses[0]
- offset += length
- default:
- return nil, nil, errors.New("ike: unsupported SOCKS5 UDP address type")
- }
- if offset+2 > len(encoded) {
- return nil, nil, errors.New("ike: truncated SOCKS5 UDP port")
- }
- port := int(binary.BigEndian.Uint16(encoded[offset : offset+2]))
- offset += 2
- return append([]byte(nil), encoded[offset:]...), &net.UDPAddr{IP: ip, Port: port}, nil
- }
- func deadlineFor(ctx context.Context, maximum time.Duration) time.Time {
- deadline := time.Now().Add(maximum)
- if ctx != nil {
- if caller, ok := ctx.Deadline(); ok && caller.Before(deadline) {
- return caller
- }
- }
- return deadline
- }
- func cloneUDPAddr(address *net.UDPAddr) *net.UDPAddr {
- if address == nil {
- return nil
- }
- return &net.UDPAddr{IP: append(net.IP(nil), address.IP...), Port: address.Port, Zone: address.Zone}
- }
- func cloneUDPAddrs(addresses []*net.UDPAddr) []*net.UDPAddr {
- result := make([]*net.UDPAddr, 0, len(addresses))
- for _, address := range addresses {
- if address != nil {
- result = append(result, cloneUDPAddr(address))
- }
- }
- return result
- }
- func parsePort(value string) (int, error) {
- port, err := strconv.Atoi(value)
- if err != nil || port < 1 || port > 65535 {
- return 0, errors.New("ike: invalid UDP port")
- }
- return port, nil
- }
|