dhcp.go 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package dhcp
  2. import (
  3. "context"
  4. "errors"
  5. "net"
  6. "net/netip"
  7. "github.com/metacubex/mihomo/common/nnip"
  8. "github.com/metacubex/mihomo/component/iface"
  9. "github.com/insomniacslk/dhcp/dhcpv4"
  10. )
  11. var (
  12. ErrNotResponding = errors.New("DHCP not responding")
  13. ErrNotFound = errors.New("DNS option not found")
  14. )
  15. func ResolveDNSFromDHCP(context context.Context, ifaceName string) ([]netip.Addr, error) {
  16. conn, err := ListenDHCPClient(context, ifaceName)
  17. if err != nil {
  18. return nil, err
  19. }
  20. defer func() {
  21. _ = conn.Close()
  22. }()
  23. result := make(chan []netip.Addr, 1)
  24. ifaceObj, err := iface.ResolveInterface(ifaceName)
  25. if err != nil {
  26. return nil, err
  27. }
  28. discovery, err := dhcpv4.NewDiscovery(ifaceObj.HardwareAddr, dhcpv4.WithBroadcast(true), dhcpv4.WithRequestedOptions(dhcpv4.OptionDomainNameServer))
  29. if err != nil {
  30. return nil, err
  31. }
  32. go receiveOffer(conn, discovery.TransactionID, result)
  33. _, err = conn.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67})
  34. if err != nil {
  35. return nil, err
  36. }
  37. select {
  38. case r, ok := <-result:
  39. if !ok {
  40. return nil, ErrNotFound
  41. }
  42. return r, nil
  43. case <-context.Done():
  44. return nil, ErrNotResponding
  45. }
  46. }
  47. func receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []netip.Addr) {
  48. defer close(result)
  49. buf := make([]byte, dhcpv4.MaxMessageSize)
  50. for {
  51. n, _, err := conn.ReadFrom(buf)
  52. if err != nil {
  53. return
  54. }
  55. pkt, err := dhcpv4.FromBytes(buf[:n])
  56. if err != nil {
  57. continue
  58. }
  59. if pkt.MessageType() != dhcpv4.MessageTypeOffer {
  60. continue
  61. }
  62. if pkt.TransactionID != id {
  63. continue
  64. }
  65. dns := pkt.DNS()
  66. l := len(dns)
  67. if l == 0 {
  68. return
  69. }
  70. dnsAddr := make([]netip.Addr, l)
  71. for i := 0; i < l; i++ {
  72. dnsAddr[i] = nnip.IpToAddr(dns[i])
  73. }
  74. result <- dnsAddr
  75. return
  76. }
  77. }