github.com/chwjbn/xclash@v0.2.0/component/dhcp/dhcp.go (about)

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