bind_windows.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package dialer
  2. import (
  3. "context"
  4. "encoding/binary"
  5. "fmt"
  6. "net"
  7. "net/netip"
  8. "syscall"
  9. "unsafe"
  10. "github.com/metacubex/mihomo/component/iface"
  11. )
  12. const (
  13. IP_UNICAST_IF = 31
  14. IPV6_UNICAST_IF = 31
  15. )
  16. func bind4(handle syscall.Handle, ifaceIdx int) error {
  17. var bytes [4]byte
  18. binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx))
  19. idx := *(*uint32)(unsafe.Pointer(&bytes[0]))
  20. err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx))
  21. if err != nil {
  22. err = fmt.Errorf("bind4: %w", err)
  23. }
  24. return err
  25. }
  26. func bind6(handle syscall.Handle, ifaceIdx int) error {
  27. err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx)
  28. if err != nil {
  29. err = fmt.Errorf("bind6: %w", err)
  30. }
  31. return err
  32. }
  33. func bindControl(ifaceIdx int, rAddrPort netip.AddrPort) controlFn {
  34. return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {
  35. addrPort, err := netip.ParseAddrPort(address)
  36. if err == nil && !addrPort.Addr().IsGlobalUnicast() {
  37. return
  38. }
  39. var innerErr error
  40. err = c.Control(func(fd uintptr) {
  41. handle := syscall.Handle(fd)
  42. bind6err := bind6(handle, ifaceIdx)
  43. bind4err := bind4(handle, ifaceIdx)
  44. switch network {
  45. case "ip6", "tcp6":
  46. innerErr = bind6err
  47. case "ip4", "tcp4", "udp4":
  48. innerErr = bind4err
  49. case "udp6":
  50. // golang will set network to udp6 when listenUDP on wildcard ip (eg: ":0", "")
  51. if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil && rAddrPort.Addr().Unmap().Is4() {
  52. // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
  53. if bind4err != nil {
  54. innerErr = fmt.Errorf("%w (%s)", bind6err, bind4err)
  55. } else {
  56. innerErr = nil
  57. }
  58. } else {
  59. innerErr = bind6err
  60. }
  61. }
  62. })
  63. if innerErr != nil {
  64. err = innerErr
  65. }
  66. return
  67. }
  68. }
  69. func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, destination netip.Addr) error {
  70. ifaceObj, err := iface.ResolveInterface(ifaceName)
  71. if err != nil {
  72. return err
  73. }
  74. addControlToDialer(dialer, bindControl(ifaceObj.Index, netip.AddrPortFrom(destination, 0)))
  75. return nil
  76. }
  77. func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) {
  78. ifaceObj, err := iface.ResolveInterface(ifaceName)
  79. if err != nil {
  80. return "", err
  81. }
  82. addControlToListenConfig(lc, bindControl(ifaceObj.Index, rAddrPort))
  83. return address, nil
  84. }
  85. func ParseNetwork(network string, addr netip.Addr) string {
  86. return network
  87. }