Merge pull request #28 from Control-D-Inc/release-branch-v1.1.2

Release branch v1.1.2
This commit is contained in:
Cuong Manh Le
2023-03-16 22:35:09 +07:00
committed by GitHub
8 changed files with 135 additions and 27 deletions

40
.goreleaser-qf.yaml Normal file
View File

@@ -0,0 +1,40 @@
before:
hooks:
- go mod tidy
builds:
- id: ctrld
env:
- CGO_ENABLED=0
flags:
- -trimpath
ldflags:
- -s -w
goos:
- darwin
- linux
- windows
goarch:
- amd64
- arm64
tags:
- qf
main: ./cmd/ctrld
hooks:
post: /bin/sh ./scripts/upx.sh {{ .Path }}
archives:
- format_overrides:
- goos: windows
format: zip
strip_parent_binary_folder: true
wrap_in_directory: true
files:
- README.md
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'

View File

@@ -70,7 +70,7 @@ func initCLI() {
rootCmd := &cobra.Command{
Use: "ctrld",
Short: strings.TrimLeft(rootShortDesc, "\n"),
Version: "1.1.1",
Version: "1.1.2",
}
rootCmd.PersistentFlags().CountVarP(
&verbose,
@@ -112,6 +112,7 @@ func initCLI() {
if err := v.Unmarshal(&cfg); err != nil {
log.Fatalf("failed to unmarshal config: %v", err)
}
fmt.Println("starting ctrld...")
// Wait for network up.
if !ctrldnet.Up() {
log.Fatal("network is not up yet")

View File

@@ -12,6 +12,7 @@ import (
"time"
"github.com/miekg/dns"
"golang.org/x/sync/errgroup"
"github.com/Control-D-Inc/ctrld"
"github.com/Control-D-Inc/ctrld/internal/dnscache"
@@ -20,7 +21,7 @@ import (
const staleTTL = 60 * time.Second
func (p *prog) serveUDP(listenerNum string) error {
func (p *prog) serveDNS(listenerNum string) error {
listenerConfig := p.cfg.Listener[listenerNum]
// make sure ip is allocated
if allocErr := p.allocateIP(listenerConfig.IP); allocErr != nil {
@@ -55,27 +56,38 @@ func (p *prog) serveUDP(listenerNum string) error {
}
})
// On Windows, there's no easy way for disabling/removing IPv6 DNS resolver, so we check whether we can
// listen on ::1, then spawn a listener for receiving DNS requests.
if runtime.GOOS == "windows" && ctrldnet.SupportsIPv6ListenLocal() {
go func() {
g := new(errgroup.Group)
for _, proto := range []string{"udp", "tcp"} {
proto := proto
// On Windows, there's no easy way for disabling/removing IPv6 DNS resolver, so we check whether we can
// listen on ::1, then spawn a listener for receiving DNS requests.
if runtime.GOOS == "windows" && ctrldnet.SupportsIPv6ListenLocal() {
g.Go(func() error {
s := &dns.Server{
Addr: net.JoinHostPort("::1", strconv.Itoa(listenerConfig.Port)),
Net: proto,
Handler: handler,
}
if err := s.ListenAndServe(); err != nil {
mainLog.Error().Err(err).Msg("could not serving on ::1")
}
return nil
})
}
g.Go(func() error {
s := &dns.Server{
Addr: net.JoinHostPort("::1", strconv.Itoa(listenerConfig.Port)),
Net: "udp",
Addr: net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port)),
Net: proto,
Handler: handler,
}
if err := s.ListenAndServe(); err != nil {
mainLog.Error().Err(err).Msg("could not serving on ::1")
mainLog.Error().Err(err).Msgf("could not listen and serve on: %s", s.Addr)
return err
}
}()
return nil
})
}
s := &dns.Server{
Addr: net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port)),
Net: "udp",
Handler: handler,
}
return s.ListenAndServe()
return g.Wait()
}
func (p *prog) upstreamFor(ctx context.Context, defaultUpstreamNum string, lc *ctrld.ListenerConfig, addr net.Addr, domain string) ([]string, bool) {
@@ -330,6 +342,7 @@ func ttlFromMsg(msg *dns.Msg) uint32 {
}
var osUpstreamConfig = &ctrld.UpstreamConfig{
Name: "OS resolver",
Type: ctrld.ResolverTypeOS,
Name: "OS resolver",
Type: ctrld.ResolverTypeOS,
Timeout: 2000,
}

View File

@@ -85,7 +85,7 @@ func (p *prog) run() {
}
addr := net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port))
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, addr)
err := p.serveUDP(listenerNum)
err := p.serveDNS(listenerNum)
if err != nil && !defaultConfigWritten && cdUID == "" {
mainLog.Fatal().Err(err).Msgf("Unable to start dns proxy on listener.%s", listenerNum)
return
@@ -109,7 +109,7 @@ func (p *prog) run() {
p.cfg.Service.AllocateIP = true
p.preRun()
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, net.JoinHostPort(ip, strconv.Itoa(port)))
if err := p.serveUDP(listenerNum); err != nil {
if err := p.serveDNS(listenerNum); err != nil {
mainLog.Fatal().Err(err).Msgf("Unable to start dns proxy on listener.%s", listenerNum)
return
}

View File

@@ -165,11 +165,15 @@ func (uc *UpstreamConfig) SetupBootstrapIP() {
return ""
}
resolver := &osResolver{nameservers: nameservers()}
resolver := &osResolver{nameservers: availableNameservers()}
resolver.nameservers = append([]string{net.JoinHostPort(bootstrapDNS, "53")}, resolver.nameservers...)
ProxyLog.Debug().Msgf("Resolving %q using bootstrap DNS %q", uc.Domain, resolver.nameservers)
timeoutMs := 2000
if uc.Timeout > 0 && uc.Timeout < timeoutMs {
timeoutMs = uc.Timeout
}
do := func(dnsType uint16) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(uc.Timeout)*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMs)*time.Millisecond)
defer cancel()
m := new(dns.Msg)
m.SetQuestion(uc.Domain+".", dnsType)
@@ -203,7 +207,7 @@ func (uc *UpstreamConfig) SetupBootstrapIP() {
uc.nextBootstrapIP.Add(1)
// If this is an ipv6, and ipv6 is not available, don't use it as bootstrap ip.
if !ctrldnet.IPv6Available(ctx) && ctrldnet.IsIPv6(ip) {
if !ctrldnet.SupportsIPv6() && ctrldnet.IsIPv6(ip) {
continue
}
uc.BootstrapIP = ip
@@ -349,3 +353,18 @@ func defaultPortFor(typ string) string {
}
return "53"
}
func availableNameservers() []string {
nss := nameservers()
n := 0
for _, ns := range nss {
ip, _, _ := net.SplitHostPort(ns)
// skipping invalid entry or ipv6 nameserver if ipv6 not available.
if ip == "" || (ctrldnet.IsIPv6(ip) && !ctrldnet.SupportsIPv6()) {
continue
}
nss[n] = ns
n++
}
return nss[:n]
}

View File

@@ -28,6 +28,13 @@ var Dialer = &net.Dialer{
},
}
const probeStackTimeout = 2 * time.Second
var probeStackDialer = &net.Dialer{
Resolver: Dialer.Resolver,
Timeout: probeStackTimeout,
}
var (
stackOnce atomic.Pointer[sync.Once]
ipv4Enabled bool
@@ -41,12 +48,12 @@ func init() {
}
func supportIPv4() bool {
_, err := Dialer.Dial("tcp4", net.JoinHostPort(controldIPv4Test, "80"))
_, err := probeStackDialer.Dial("tcp4", net.JoinHostPort(controldIPv4Test, "80"))
return err == nil
}
func supportIPv6(ctx context.Context) bool {
_, err := Dialer.DialContext(ctx, "tcp6", net.JoinHostPort(controldIPv6Test, "80"))
_, err := probeStackDialer.DialContext(ctx, "tcp6", net.JoinHostPort(controldIPv6Test, "80"))
return err == nil
}
@@ -61,7 +68,7 @@ func supportListenIPv6Local() bool {
func probeStack() {
b := backoff.NewBackoff("probeStack", func(format string, args ...any) {}, time.Minute)
for {
if _, err := Dialer.Dial("udp", bootstrapDNS); err == nil {
if _, err := probeStackDialer.Dial("udp", bootstrapDNS); err == nil {
hasNetworkUp = true
break
} else {

24
internal/net/net_test.go Normal file
View File

@@ -0,0 +1,24 @@
package net
import (
"context"
"testing"
"time"
)
func TestProbeStackTimeout(t *testing.T) {
done := make(chan struct{})
started := make(chan struct{})
go func() {
defer close(done)
close(started)
supportIPv6(context.Background())
}()
<-started
select {
case <-time.After(probeStackTimeout + time.Second):
t.Error("probeStack timeout is not enforce")
case <-done:
}
}

View File

@@ -18,6 +18,10 @@ case "$binary" in
echo >&2 "upx does not work with windows arm/arm64 binary yet"
exit 0
;;
*_darwin_*)
echo >&2 "upx claims to work with darwin binary, but testing show that it is broken"
exit 0
;;
esac
upx -- "$binary"