dial_linux.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. //go:build linux
  2. // +build linux
  3. package main
  4. import (
  5. "syscall"
  6. "golang.org/x/sys/unix"
  7. )
  8. // dialerControl prepares a syscall.RawConn for a future bind-before-connect by
  9. // setting the IP_BIND_ADDRESS_NO_PORT socket option.
  10. //
  11. // On Linux, setting the IP_BIND_ADDRESS_NO_PORT socket option helps conserve
  12. // ephemeral ports when binding to a specific IP addresses before connecting
  13. // (bind before connect), by not assigning the port number when bind is called,
  14. // but waiting until connect. But problems arise if there are multiple processes
  15. // doing bind-before-connect, and some of them use IP_BIND_ADDRESS_NO_PORT and
  16. // some of them do not. When there is a mix, the ones that do will have their
  17. // ephemeral ports reserved by the ones that do not, leading to EADDRNOTAVAIL
  18. // errors.
  19. //
  20. // tor does bind-before-connect when the OutboundBindAddress option is set in
  21. // torrc. Since version 0.4.7.13 (January 2023), tor sets
  22. // IP_BIND_ADDRESS_NO_PORT unconditionally on platforms that support it, and
  23. // therefore we must do the same, to avoid EADDRNOTAVAIL errors.
  24. //
  25. // # References
  26. //
  27. // https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/40201#note_2839472
  28. // https://forum.torproject.net/t/tor-relays-inet-csk-bind-conflict/5757/10
  29. // https://blog.cloudflare.com/how-to-stop-running-out-of-ephemeral-ports-and-start-to-love-long-lived-connections/
  30. // https://blog.cloudflare.com/the-quantum-state-of-a-tcp-port/
  31. // https://forum.torproject.net/t/stable-release-0-4-5-16-and-0-4-7-13/6216
  32. func dialerControl(network, address string, c syscall.RawConn) error {
  33. var sockErr error
  34. err := c.Control(func(fd uintptr) {
  35. sockErr = syscall.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_BIND_ADDRESS_NO_PORT, 1)
  36. })
  37. if err == nil {
  38. err = sockErr
  39. }
  40. return err
  41. }