pax_global_header00006660000000000000000000000064147511563540014525gustar00rootroot0000000000000052 comment=e97087efd9b388c427522dffdb765f0642cc6be8 dhcpdns-0.0.7/000077500000000000000000000000001475115635400131545ustar00rootroot00000000000000dhcpdns-0.0.7/.github/000077500000000000000000000000001475115635400145145ustar00rootroot00000000000000dhcpdns-0.0.7/.github/workflows/000077500000000000000000000000001475115635400165515ustar00rootroot00000000000000dhcpdns-0.0.7/.github/workflows/CI.yml000066400000000000000000000014721475115635400175730ustar00rootroot00000000000000name: Native Build and Test on: [push, pull_request] jobs: build: name: dhcpdns-${{matrix.os}} runs-on: ${{matrix.os}} # run native test strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: 1 - name: Build run: | cd cli go build -ldflags="all=-s -w" -trimpath - name: Test Windows if: runner.os == 'Windows' run: ./cli/cli -k 3 -n 30 - name: Test Non-Windows if: runner.os != 'Windows' run: sudo ./cli/cli -k 3 -n 30 - name: Upload uses: actions/upload-artifact@v4 with: name: dhcpdns-${{github.head_ref || github.ref_name}}-${{matrix.os}} path: cli/cli* dhcpdns-0.0.7/.gitignore000066400000000000000000000004151475115635400151440ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ dhcpdns-0.0.7/LICENSE000066400000000000000000000020541475115635400141620ustar00rootroot00000000000000MIT License Copyright (c) 2023 lifenjoiner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dhcpdns-0.0.7/cli/000077500000000000000000000000001475115635400137235ustar00rootroot00000000000000dhcpdns-0.0.7/cli/build.bat000066400000000000000000000006511475115635400155140ustar00rootroot00000000000000@echo off setlocal ENABLEDELAYEDEXPANSION pushd %~dp0 ::-code-:: call :Build windows amd64 call :Build linux amd64 ::Android call :Build linux arm64 ::Mac call :Build darwin amd64 call :Build freebsd amd64 call :Build solaris amd64 ::-code-:: :Exit popd endlocal exit /b :Build setlocal set GOOS=%1 set GOARCH=%2 set OutDir=dhcpdns-%1-%2 echo %OutDir% go build -ldflags="all=-s -w" -trimpath -o %OutDir%/ endlocal exit /b dhcpdns-0.0.7/cli/main.go000066400000000000000000000023261475115635400152010ustar00rootroot00000000000000// Copyright 2023-now by lifenjoiner. All rights reserved. // Use of this source code is governed by a MIT license // that can be found in the LICENSE file. // The CLI. package main import ( "flag" "log" "time" "github.com/lifenjoiner/dhcpdns" ) func detect(d *dhcpdns.Detector) { log.Printf("Targeting: %v", d.RemoteIPPort) _ = d.Detect() n, ip, DNS, err := d.Status() log.Printf("Constancy: %v", n) if err != nil { log.Printf("Error: %v", err) if ip != "" { if n > 9 { log.Print("Seems can't get DHCP DNS") return } else { log.Print("Maybe DHCP temporarily failed, keep the last results") } } } log.Printf("Active local IP: %v", ip) for _, dnsi := range DNS { log.Printf("DHCP DNS: %v", dnsi.String()) } } // `Serve` acts like a daemon. func main() { var n, k int flag.IntVar(&n, "n", -1, "Detecting rounds") flag.IntVar(&k, "k", 9, "Keep rounds for the same active IP") flag.Parse() d4 := &dhcpdns.Detector{RemoteIPPort: "8.8.8.8:80"} d6 := &dhcpdns.Detector{RemoteIPPort: "[2001:4860:4860::8888]:80"} for ; n != 0; n-- { if n%k == 0 { d4.SetNewRound() d6.SetNewRound() } detect(d4) detect(d6) if n == 1 { break } time.Sleep(10 * time.Second) } } dhcpdns-0.0.7/dhcpdns.go000066400000000000000000000325011475115635400151270ustar00rootroot00000000000000// Copyright 2023-now by lifenjoiner. All rights reserved. // Use of this source code is governed by a MIT license // that can be found in the LICENSE file. // Package dhcpdns gets the DHCP/DHCPv6 DNS. package dhcpdns import ( "crypto/rand" "errors" //"log" "net" "runtime" "sync" "time" ) const ( dhcpv4MessageSizeMax = 576 dhcpv6MessageSizeComm = 1024 ) var ( err4InvalidParam = errors.New("invalid DHCPv4 parameters") err4NotReply = errors.New("not DHCPv4 reply") err4TidNotMatch = errors.New("DHCPv4 TID not match") ) var ( errInvalidReply = errors.New("invalid reply") errNoDNSFound = errors.New("no DNS found") ) var ( err6InterfaceNotRunning = errors.New("interface is not running") err6InvalidParam = errors.New("invalid DHCPv6 parameters") err6NoLLUAFound = errors.New("no link-local address found") err6NotReply = errors.New("not DHCPv6 Reply") err6TidNotMatch = errors.New("DHCPv6 TID not match") ) var ( errIsLLA = errors.New("unsupported link-local address") errIsTeredo = errors.New("unsupported Teredo Tunneling Pseudo-Interface") ) // Sample messages, https://wiki.wireshark.org/SampleCaptures.md // GetDNSFromReply4 gets DNS from a DHCP reply message. func GetDNSFromReply4(msg []byte, tid []byte) (ip []net.IP, err error) { n := len(msg) if n < 241 || len(tid) < 4 { err = err4InvalidParam return } if msg[0] != 0x02 { err = err4NotReply return } if msg[4] != tid[0] || msg[5] != tid[1] || msg[6] != tid[2] || msg[7] != tid[3] { err = err4TidNotMatch return } m := 240 for m < n { opt := msg[m] if opt == 255 { // END break } m++ if m < n { i := m + 1 m += 1 + int(msg[m]) if m <= n { if opt == 6 { // DHCP DNS for i+4 <= m { ip = append(ip, msg[i:i+4]) i += 4 } break } continue } } err = errInvalidReply break } if len(ip) == 0 { err = errNoDNSFound //log.Printf("%x", msg) } return } // GetDNSByIPv4 sends DHCP message and return the DNS. // ip is the reaching out IP. func GetDNSByIPv4(ip string) (dns []net.IP, err error) { ipAddr, ifi, err := getOutboundParams(ip) if err != nil { return nil, err } //log.Printf("Receiving addr Zone: %v", ipAddr.Zone) // Windows (WSL2) can't choose the right IP. pc, err := reuseListenPacket("udp4", ip+":68") if err != nil { return nil, err } // Minimal DHCP message // We prefer to be reached by a broadcast than unicast relpy, in case of there is the OS DHCP deamon binding. // https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6 // INIT-REBOOT: https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.2 dhcpMsg := []byte{ 0x01, // message type 0x01, // hardware type: Ethernet 0x06, // hardware address length: Ethernet 0x00, // hops 0x48, 0x59, 0x58, 0x27, // transaction id 0x00, 0x00, // seconds elasped 0x80, 0x00, // flags: BROADCAST. Unicast may not be received. 0x00, 0x00, 0x00, 0x00, // client ip: ciaddr 0x00, 0x00, 0x00, 0x00, // your ip: yiaddr 0x00, 0x00, 0x00, 0x00, // server ip: siaddr 0x00, 0x00, 0x00, 0x00, // relay ip: giaddr // client MAC: https://gitlab.com/wireshark/wireshark/-/raw/master/manuf 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // client hardware address padding // ServerHostName 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BootFileName 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // magic cookie: DHCP 0x63, 0x82, 0x53, 0x63, // 240B // Options 0x35, 0x01, 0x03, // DHCPREQUEST. DHCPDISCOVER may cause the server to release the OFFER. 0x32, 0x04, 0xc0, 0xa8, 0x01, 0x04, // Requested IP address for `INIT-REBOOT` 0x37, 0x01, 0x06, // Parameter Request List: DNS 0x3d, 0x07, 0x01, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Client Identifier 0xff, // END // padding: min length of 300 bytes per RFC951 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } // new transaction id tid := dhcpMsg[4:8] _, _ = rand.Read(tid) // MAC. On devices (Android) with both IPv6 and IPv6 available, MAC would be nil. copy(dhcpMsg[28:28+16], ifi.HardwareAddr) // Requested IP address copy(dhcpMsg[245:245+4], ipAddr.IP.To4()) // The DHCP server of VMware NAT mode requires Client identifier. m := len(ifi.HardwareAddr) //log.Printf("MAC[%v]: %v", m, ifi.HardwareAddr) if m > 0 { copy(dhcpMsg[255:255+m], ifi.HardwareAddr) dhcpMsg[253] = byte(m&0xff) + 1 dhcpMsg[255+m] = 0xff } rAddr := &net.UDPAddr{IP: net.IPv4bcast, Port: 67} _ = pc.SetDeadline(time.Now().Add(2 * time.Second)) _, err = pc.WriteTo(dhcpMsg, rAddr) if err != nil { // defer doesn't work on reassignment _ = pc.Close() return nil, err } // Prefer broadcast: // (*nix) may have a deamon binding the local IPPort and the gateway IPPort. // If so and the server replies with a broadcast to the local IPPort, rather than IPv4bcast, // it may not be received on some OS. if ipAddr.Zone != "" { _ = pc.Close() pc, err = reuseListenPacket("udp4", ":68") if err != nil { return nil, err } } //log.Printf("Receiving addr: %v", pc.LocalAddr()) buf := make([]byte, dhcpv4MessageSizeMax) _ = pc.SetDeadline(time.Now().Add(2 * time.Second)) n, _, err := pc.ReadFrom(buf[:]) _ = pc.Close() if err != nil { return nil, err } //log.Printf("Received from: %v", rAddr2) dns, err = GetDNSFromReply4(buf[:n], tid) return } // Required. func getOutboundParams(ip string) (*net.IPAddr, *net.Interface, error) { ipAddr, err := net.ResolveIPAddr("ip", ip) if err != nil { return nil, nil, err } is6 := ipAddr.IP.To4() == nil ift, err := net.Interfaces() if err != nil { return nil, nil, err } for _, ifi := range ift { addrs, err := ifi.Addrs() if err != nil { continue } var ipUnicast net.IP var got bool for _, addr := range addrs { ipa, ok := addr.(*net.IPNet) if !ok { continue } ipi := ipa.IP if ipi.Equal(ipAddr.IP) { got = true } if is6 && ipi.To4() == nil && ipi.IsLinkLocalUnicast() { ipUnicast = ipi } //log.Printf("%v: %v", ifi.Name, ipi) } if got { // https://www.kernel.org/doc/html/latest/networking/operstates.html if ifi.Flags&net.FlagRunning == net.FlagRunning { if ipUnicast != nil { ipAddr.IP = ipUnicast } // Bind fe80::/10 and ListenUDP needs Zone on *nix. if ipAddr.Zone == "" && runtime.GOOS != "windows" { ipAddr.Zone = ifi.Name } return ipAddr, &ifi, nil } return nil, nil, err6InterfaceNotRunning } } return nil, nil, err6NoLLUAFound } func readBigEndianUint16(b []byte) uint16 { return uint16(b[0])<<8&0xff00 | uint16(b[1]) } // GetDNSFromReply6 gets DNS from a DHCPv6 REPLY message. // https://datatracker.ietf.org/doc/html/rfc3646 func GetDNSFromReply6(msg []byte, tid []byte) (ip []net.IP, err error) { n := len(msg) if n < 7 || len(tid) < 3 { err = err6InvalidParam return } if msg[0] != 0x07 { err = err6NotReply return } if msg[1] != tid[0] || msg[2] != tid[1] || msg[3] != tid[2] { err = err6TidNotMatch return } m := 4 for m+2 <= n { opt := readBigEndianUint16(msg[m : m+2]) m += 2 if m+2 < n { i := m + 2 m += 2 + int(readBigEndianUint16(msg[m:m+2])) if m <= n { if opt == 23 { // DHCPv6 DNS for i+16 <= m { ip = append(ip, msg[i:i+16]) i += 16 } break } continue } } err = errInvalidReply break } if len(ip) == 0 { err = errNoDNSFound } return } // GetDNSByIPv6 sends DHCPv6 INFORMATION-REQUEST message and return the DNS. // ip is the reaching out IP. func GetDNSByIPv6(ip string) (dns []net.IP, err error) { ipAddr, _, err := getOutboundParams(ip) if err != nil { return nil, err } pc, err := reuseListenPacket("udp6", "["+ipAddr.String()+"]:546") if err != nil { return nil, err } //log.Printf("Receiving addr: %v", pc.LocalAddr()) // Minimal INFORMATION-REQUEST message // https://en.wikipedia.org/wiki/DHCPv6 // INFORMATION-REQUEST (11): // https://datatracker.ietf.org/doc/html/rfc8415#section-18.2.6 // https://datatracker.ietf.org/doc/html/rfc8415#section-8 dhcpv6Msg := []byte{ 0x0b, // message type 0x48, 0x59, 0x58, // transaction id // Options // Elapsed Time Option: https://datatracker.ietf.org/doc/html/rfc8415#section-21.9 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, // option request: INF_MAX_RT, Information Refresh Time, DNS 0x00, 0x06, 0x00, 0x06, 0x00, 0x53, 0x00, 0x20, 0x00, 0x17, // Client Identifier Option: https://datatracker.ietf.org/doc/html/rfc8415#section-21.2 // anonymity profile DUID-LLT: https://datatracker.ietf.org/doc/html/rfc7844#section-4.3 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x26, 0xeb, 0x58, 0x35, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, } // new transaction id tid := dhcpv6Msg[1:4] _, _ = rand.Read(tid) rAddr := &net.UDPAddr{IP: net.IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x02}, Port: 547} _ = pc.SetDeadline(time.Now().Add(2 * time.Second)) _, err = pc.WriteTo(dhcpv6Msg, rAddr) if err != nil { return nil, err } buf := make([]byte, dhcpv6MessageSizeComm) _ = pc.SetDeadline(time.Now().Add(2 * time.Second)) n, _, err := pc.ReadFrom(buf[:]) _ = pc.Close() if err != nil { return nil, err } dns, err = GetDNSFromReply6(buf[:n], tid) return } // Detector holds the parameters and results. // // if err == nil { // if lastActiveIP != "" { // // got DNS // } else { // // uninitialized // } // } else if lastActiveIP == "" { // // offline/invalid // } else if constancy > x { // // treat as (switched to a network that) can't get DNS // } else { // // treat as temporarily failed // } type Detector struct { sync.RWMutex got bool // RemoteIPPort is the remote IPPort to detect within UDP. // Won't send any data to it. `Dial` in UDP only detects if the network is available. RemoteIPPort string lastActiveIP string dns []net.IP err error constancy int } func detect(d *Detector) (string, []net.IP, error) { c, err := net.Dial("udp", d.RemoteIPPort) if err != nil { return "", nil, err } ipPort := c.LocalAddr().String() _ = c.Close() ip, _, err := net.SplitHostPort(ipPort) if err != nil { return "", nil, err } if ip[:7] == "2001:0:" { // https://en.wikipedia.org/wiki/Teredo_tunneling#IPv6_addressing err = errIsTeredo } else if ip[:6] == "fe80::" || ip[:7] == "169.254" { // Only detect valid network. https://www.wikiwand.com/en/Link-local_address err = errIsLLA } var dns []net.IP if err == nil && (!d.got || d.lastActiveIP != ip) { if ipPort[0] == '[' { dns, err = GetDNSByIPv6(ip) } else { dns, err = GetDNSByIPv4(ip) } } return ip, dns, err } // Detect the DNS from the active interface which is adopted to connect to the provided IPPort address. // The last active IP is used to reduce traffic or defense. // If got and IP hasn't changed, skip sending DHCP messages as soft detecting. func (d *Detector) Detect() error { ip, dns, err := detect(d) d.Lock() if err == nil { if len(dns) > 0 { d.dns = dns } d.got = true } if d.lastActiveIP == ip && isTheSameErr(err, d.err) { d.constancy++ } else { d.constancy = 1 } d.lastActiveIP = ip d.err = err d.Unlock() return err } // SetNewRound sets a new force detecting. func (d *Detector) SetNewRound() { d.Lock() d.got = false d.Unlock() } // Status gets the detected results. func (d *Detector) Status() (constancy int, ip string, dns []net.IP, err error) { d.RLock() constancy = d.constancy ip = d.lastActiveIP dns = d.dns err = d.err d.RUnlock() return } // Serve periodically detects the DNS as a daemon. // cycle is the soft detecting rounds following a force detecting. sleep is in seconds. func (d *Detector) Serve(cycle, sleep int) { var i int if cycle <= 0 { cycle = 9 } if sleep <= 0 { sleep = 10 } for { if i%cycle == 0 { d.SetNewRound() } _ = d.Detect() i++ time.Sleep(time.Duration(sleep) * time.Second) } } func isTheSameErr(err1, err2 error) bool { return err1 == err2 || (err1 != nil && err2 != nil && err1.Error() == err2.Error()) } dhcpdns-0.0.7/go.mod000066400000000000000000000000571475115635400142640ustar00rootroot00000000000000module github.com/lifenjoiner/dhcpdns go 1.20 dhcpdns-0.0.7/readme.md000066400000000000000000000005631475115635400147370ustar00rootroot00000000000000`dhcpdns` is a golang package to get the DHCP DNS. It helps programs to use the upstream DNS easily when your network changed. ## Usage and Demo [cli](./cli) detects the DHCPv6/DHCPv4 DNS repeatedly. Enter cli dir in CLI: ``` go build cli ``` It will need root privileges on operating systems other than Windows. ## Homepage https://github.com/lifenjoiner/dhcpdns dhcpdns-0.0.7/sockopt_reuse0.go000066400000000000000000000013121475115635400164450ustar00rootroot00000000000000// Copyright 2023-now by lifenjoiner. All rights reserved. // Use of this source code is governed by a MIT license // that can be found in the LICENSE file. //go:build windows || (js && wasm) // +build windows js,wasm package dhcpdns import ( "net" ) // SO_REUSEADDR and SO_REUSEPORT: https://stackoverflow.com/questions/14388706/ // `SO_REUSEADDR` doesn't really work for this on Windows, if `DHCP Client` service occupies the port! // https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse // On Windows, the 1st bind receives the reply data. func reuseListenPacket(network, address string) (net.PacketConn, error) { return net.ListenPacket(network, address) } dhcpdns-0.0.7/sockopt_reuse1.go000066400000000000000000000012711475115635400164520ustar00rootroot00000000000000// Copyright 2023-now by lifenjoiner. All rights reserved. // Use of this source code is governed by a MIT license // that can be found in the LICENSE file. //go:build solaris // +build solaris package dhcpdns import ( "context" "net" "syscall" ) // No SO_REUSEPORT implemented. Doesn't work for SO_EXCLBIND on Solaris. func reuseListenPacket(network, address string) (net.PacketConn, error) { lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) }) }, } return lc.ListenPacket(context.Background(), network, address) } dhcpdns-0.0.7/sockopt_reuse2_1.go000066400000000000000000000015261475115635400166760ustar00rootroot00000000000000// Copyright 2023-now by lifenjoiner. All rights reserved. // Use of this source code is governed by a MIT license // that can be found in the LICENSE file. //go:build (linux && 386) || (linux && amd64) || (linux && arm) // +build linux,386 linux,amd64 linux,arm package dhcpdns import ( "context" "net" "syscall" ) const SO_REUSEPORT = 0xf func reuseListenPacket(network, address string) (net.PacketConn, error) { lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) // SO_REUSEPORT Requires same UID for security reason. _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEPORT, 1) }) }, } return lc.ListenPacket(context.Background(), network, address) } dhcpdns-0.0.7/sockopt_reuse2_2.go000066400000000000000000000015271475115635400167000ustar00rootroot00000000000000// Copyright 2023-now by lifenjoiner. All rights reserved. // Use of this source code is governed by a MIT license // that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || (linux && !386 && !amd64 && !arm) // +build aix darwin dragonfly freebsd netbsd openbsd linux,!386,!amd64,!arm package dhcpdns import ( "context" "net" "syscall" ) func reuseListenPacket(network, address string) (net.PacketConn, error) { lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) _ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) }) }, } return lc.ListenPacket(context.Background(), network, address) }