lichao 发表于 2023-3-31 21:34:18

【Golang】编译iOS版dnscrypt-proxy

本帖最后由 lichao 于 2023-4-2 15:45 编辑


## 前言
  dnscrypt-proxy是一个开源github项目,用于加密dns请求,支持Win/Linux/MacOS/Android/iOS等平台,
官方却有iOS编译方法,但已经不兼容现有系统环境,因此笔者自行编译。项目地址<https://github.com/DNSCrypt/dnscrypt-proxy>

## 编译

和以往一样笔者位MacOS系统

1. 下载源码并进入dnscrypt-proxy/dnscrypt-proxy目录
2. 修改main.go,注意代码中包括了官方提到的防内存过高被杀进程的memorystatus_control,还有我自己发现的SIGURG的处理,此bug目前原因未知,进程启动没多久就会因为无法处理SIGURG而崩溃,google搜了下无所获,猜测是go的某个依赖库有问题,因此直接自己handle了该信号,可正常运行

```golang
package main

/*
#include <stdint.h>
int memorystatus_control(uint32_t command, int32_t pid, uint32_t flags, void *buffer, size_t buffersize);
*/
import "C"

import (
        crypto_rand "crypto/rand"
        "encoding/binary"
        "flag"
        "fmt"
        "math/rand"
        "os"
        "os/signal"
        "runtime"
        "sync"
        "syscall"

        "github.com/jedisct1/dlog"
        "github.com/kardianos/service"
)

const (
        AppVersion            = "2.1.4"
        DefaultConfigFileName = "dnscrypt-proxy.toml"
)

type App struct {
        wg    sync.WaitGroup
        quitchan struct{}
        proxy *Proxy
        flags *ConfigFlags
}

func handle_sigurg() {
        c := make(chan os.Signal)
        signal.Notify(c, syscall.SIGURG)
        go func() {
                for s := range c {
                        switch s {
                                case syscall.SIGURG:
                                        fmt.Println("SIGURG")
                                        break
                                default:
                                        break
                        }
                }
        }()
}

func main() {
        handle_sigurg()
        TimezoneSetup()
        dlog.Init("dnscrypt-proxy", dlog.SeverityNotice, "DAEMON")
        runtime.MemProfileRate = 0

        seed := make([]byte, 8)
        crypto_rand.Read(seed)
        rand.Seed(int64(binary.LittleEndian.Uint64(seed[:])))

        pwd, err := os.Getwd()
        if err != nil {
                dlog.Fatal("Unable to find the path to the current directory")
        }

        svcFlag := flag.String("service", "", fmt.Sprintf("Control the system service: %q", service.ControlAction))
        version := flag.Bool("version", false, "print current proxy version")
        flags := ConfigFlags{}
        flags.Resolve = flag.String("resolve", "", "resolve a DNS name (string can be <name> or <name>,<resolver address>)")
        flags.List = flag.Bool("list", false, "print the list of available resolvers for the enabled filters")
        flags.ListAll = flag.Bool("list-all", false, "print the complete list of available resolvers, ignoring filters")
        flags.JSONOutput = flag.Bool("json", false, "output list as JSON")
        flags.Check = flag.Bool("check", false, "check the configuration file and exit")
        flags.ConfigFile = flag.String("config", DefaultConfigFileName, "Path to the configuration file")
        flags.Child = flag.Bool("child", false, "Invokes program as a child process")
        flags.NetprobeTimeoutOverride = flag.Int("netprobe-timeout", 60, "Override the netprobe timeout")
        flags.ShowCerts = flag.Bool("show-certs", false, "print DoH certificate chain hashes")

        flag.Parse()

        if *version {
                fmt.Println(AppVersion)
                os.Exit(0)
        }
        if os.Getuid() == 0 {
                var MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT C.uint = 6;
                if C.memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_TASK_LIMIT, C.int(os.Getpid()), 0, nil, 0) != 0 {
                        dlog.Warn("Failed to disable iOS launchd's memory restrictions; loading a large blacklist, for example, may cause dnscrypt-proxy to be killed!")
                }
        }
        app := &App{
                flags: &flags,
        }

        svcConfig := &service.Config{
                Name:             "dnscrypt-proxy",
                DisplayName:      "DNSCrypt client proxy",
                Description:      "Encrypted/authenticated DNS proxy",
                WorkingDirectory: pwd,
                Arguments:      []string{"-config", *flags.ConfigFile},
        }
        svc, err := service.New(app, svcConfig)
        if err != nil {
                svc = nil
                dlog.Debug(err)
        }

        app.proxy = NewProxy()
        _ = ServiceManagerStartNotify()
        if len(*svcFlag) != 0 {
                if svc == nil {
                        dlog.Fatal("Built-in service installation is not supported on this platform")
                }
                if err := service.Control(svc, *svcFlag); err != nil {
                        dlog.Fatal(err)
                }
                if *svcFlag == "install" {
                        dlog.Notice("Installed as a service. Use `-service start` to start")
                } else if *svcFlag == "uninstall" {
                        dlog.Notice("Service uninstalled")
                } else if *svcFlag == "start" {
                        dlog.Notice("Service started")
                } else if *svcFlag == "stop" {
                        dlog.Notice("Service stopped")
                } else if *svcFlag == "restart" {
                        dlog.Notice("Service restarted")
                }
                return
        }
        if svc != nil {
                if err := svc.Run(); err != nil {
                        dlog.Fatal(err)
                }
        } else {
                app.Start(nil)
        }
}

func (app *App) Start(service service.Service) error {
        if service != nil {
                go func() {
                        app.AppMain()
                }()
        } else {
                app.AppMain()
        }
        return nil
}

func (app *App) AppMain() {
        if err := ConfigLoad(app.proxy, app.flags); err != nil {
                dlog.Fatal(err)
        }
        if err := PidFileCreate(); err != nil {
                dlog.Criticalf("Unable to create the PID file: %v", err)
        }
        if err := app.proxy.InitPluginsGlobals(); err != nil {
                dlog.Fatal(err)
        }
        app.quit = make(chan struct{})
        app.wg.Add(1)
        app.proxy.StartProxy()
        runtime.GC()
        <-app.quit
        dlog.Notice("Quit signal received...")
        app.wg.Done()
}

func (app *App) Stop(service service.Service) error {
        PidFileRemove()
        dlog.Notice("Stopped.")
        return nil
}
```

3. 如官方说明添加dnscrypt-proxy.plist

```plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>get-task-allow</key>
    <true/>
    <key>proc_info-allow</key>
    <true/>
    <key>task_for_pid-allow</key>
    <true/>
        <key>com.apple.private.network.reserved-port</key>
        <true/>
    <key>com.apple.security.network.client</key>
    <true/>
    <key>com.apple.security.network.server</key>
    <true/>
    <key>com.apple.developer.networking.networkextension</key>
    <true/>
    <key>com.apple.network.multipath-tcp</key>
    <true/>
    <key>platform-application</key>
    <true/>
    <key>com.apple.private.security.container-required</key>
    <false/>
</dict>
</plist>
```

4. 添加build.sh如下

```bash
#!/bin/bash
export GOROOT=$(go env GOROOT)
CGO_ENABLED=1 GOOS=ios GOARCH=arm64 CC="$GOROOT/misc/ios/clangwrap.sh" CXX="$GOROOT/misc/ios/clangwrap.sh" \
CGO_CFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=9.0 -fembed-bitcode -arch arm64" \
CGO_CXXFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=9.0 -fembed-bitcode -arch arm64" \
CGO_LDFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=9.0 -fembed-bitcode -arch arm64" \
go build -ldflags="-s -w" -tags=ios
ldid -Sdnscrypt-proxy.plist dnscrypt-proxy
```

5. 执行build.sh编译,得到dnscrypt-proxy,将文件example-dnscrypt-proxy.toml改为dnscrypt-proxy.toml,一起传入iPhone做测试

```bash
/usr/bin/dnscrypt-proxy -config /etc/dnscrypt-proxy/dnscrypt-proxy.toml -logfile /tmp/dnscrypt-proxy.log
```

可以看到运行正常:
```
dnscrypt-proxy 2.1.4
Network connectivity detected
Now listening to 127.0.0.1:53
Now listening to 127.0.0.1:53
Now listening to [::1]:53
Now listening to [::1]:53
Source loaded
Source loaded
Firefox workaround initialized
OK (DoH) - rtt: 316ms
OK (DNSCrypt) - rtt: 181ms
OK (DNSCrypt) - rtt: 183ms
OK (DNSCrypt) - rtt: 183ms - additional certificate
OK (DoH) - rtt: 309ms
OK (DNSCrypt) - rtt: 242ms
OK (DNSCrypt) - rtt: 214ms
OK (DoH) - rtt: 406ms
OK (DNSCrypt) - rtt: 259ms
OK (DNSCrypt) - rtt: 263ms
TIMEOUT
OK (DoH) - rtt: 1095ms
TIMEOUT
OK (DNSCrypt) - rtt: 253ms
OK (DNSCrypt) - rtt: 268ms
OK (DoH) - rtt: 394ms
OK (DoH) - rtt: 189ms
OK (DNSCrypt) - rtt: 311ms
OK (DNSCrypt) - rtt: 246ms
OK (DNSCrypt) - rtt: 289ms
OK (DoH) - rtt: 302ms
OK (DNSCrypt) - rtt: 248ms
OK (DNSCrypt) - rtt: 248ms - additional certificate
OK (DNSCrypt) - rtt: 261ms
OK (DNSCrypt) - rtt: 186ms
OK (DNSCrypt) - rtt: 215ms
OK (DNSCrypt) - rtt: 236ms
OK (DNSCrypt) - rtt: 224ms
OK (DNSCrypt) - rtt: 204ms
OK (DNSCrypt) - rtt: 314ms
OK (DNSCrypt) - rtt: 184ms
uses a non-standard provider name ('2.dnscrypt.unfiltered.ns1.adguard.com.' doesn't start with '2.dnscrypt-cert.')
OK (DNSCrypt) - rtt: 183ms
OK (DNSCrypt) - rtt: 185ms
OK (DoH) - rtt: 303ms
OK (DoH) - rtt: 302ms
OK (DNSCrypt) - rtt: 339ms
OK (DNSCrypt) - rtt: 241ms
OK (DNSCrypt) - rtt: 241ms - additional certificate
OK (DNSCrypt) - rtt: 235ms
OK (DNSCrypt) - rtt: 259ms
OK (DNSCrypt) - rtt: 203ms
OK (DoH) - rtt: 310ms
OK (DoH) - rtt: 270ms
OK (DoH) - rtt: 1083ms
TIMEOUT
OK (DNSCrypt) - rtt: 268ms
OK (DoH) - rtt: 301ms
OK (DNSCrypt) - rtt: 245ms
OK (DNSCrypt) - rtt: 245ms - additional certificate
System DNS configuration not usable yet, exceptionally resolving using bootstrap resolvers over tcp
OK (DoH) - rtt: 166ms
OK (DoH) - rtt: 249ms
OK (DNSCrypt) - rtt: 236ms
OK (DoH) - rtt: 267ms
OK (DoH) - rtt: 245ms
OK (DNSCrypt) - rtt: 284ms
OK (DNSCrypt) - rtt: 284ms - additional certificate
TIMEOUT
OK (DoH) - rtt: 295ms
OK (DNSCrypt) - rtt: 284ms
OK (DNSCrypt) - rtt: 116ms
OK (DoH) - rtt: 305ms
OK (DNSCrypt) - rtt: 92ms
TIMEOUT
OK (DoH) - rtt: 408ms
OK (DNSCrypt) - rtt: 246ms
OK (DNSCrypt) - rtt: 224ms
Sorted latencies:
-    92ms starrydns
-   116ms dct-ru1
-   166ms nextdns
-   181ms openinternet
-   183ms quad9-dnscrypt-ip4-nofilter-pri
-   183ms adguard-dns-unfiltered
-   184ms saldns01-conoha-ipv4
-   185ms faelix-ch-ipv4
-   186ms dnswarden-uncensor-dc-swiss
-   189ms cloudflare
-   203ms deffer-dns.au
-   204ms saldns03-conoha-ipv4
-   214ms scaleway-fr
-   215ms techsaviours.org-dnscrypt
-   224ms altername
-   224ms sth-dnscrypt-se
-   235ms dct-de1
-   236ms serbica
-   236ms faelix-uk-ipv4
-   241ms quad9-dnscrypt-ip4-nofilter-ecs-pri
-   242ms dnscrypt.ca-1
-   245ms plan9dns-fl
-   245ms plan9dns-nj-doh
-   246ms ams-dnscrypt-nl
-   246ms v.dnscrypt.uk-ipv4
-   248ms plan9dns-nj
-   249ms dnscrypt.ca-2-doh
-   253ms dct-nl1
-   259ms dct-at1
-   259ms ffmuc.net
-   261ms dnscrypt.uk-ipv4
-   263ms meganerd
-   267ms libredns
-   268ms scaleway-ams
-   268ms pryv8boi
-   270ms sth-doh-se
-   284ms plan9dns-mx
-   284ms dnscrypt.ca-2
-   289ms wevpn-singapore
-   295ms uncensoreddns-dk-ipv4
-   301ms plan9dns-mx-doh
-   302ms njalla-doh
-   302ms bortzmeyer
-   303ms dns.digitale-gesellschaft.ch
-   305ms dnscrypt.ca-1-doh
-   309ms meganerd-doh-ipv4
-   310ms uncensoreddns-ipv4
-   311ms wevpn-useast
-   314ms dnscrypt.pl
-   316ms dns.sb
-   339ms sby-limotelu
-   394ms dns.digitale-gesellschaft.ch-2
-   406ms dns.digitalsize.net
-   408ms doh-crypto-sx
-1083ms doh.appliedprivacy.net
-1095ms ams-doh-nl
Server with the lowest initial latency: starrydns (rtt: 92ms)
dnscrypt-proxy is ready - live servers: 56
```

此时使用nslookup测试dns服务器:

```bash
nslookup www.baidu.com 127.0.0.1
```

得到正确结果:
```
iPhone:~ root# nslookup www.baidu.com 127.0.0.1
Server:                127.0.0.1
Address:        127.0.0.1#53

Non-authoritative answer:
www.baidu.com        canonical name = www.a.shifen.com.
www.a.shifen.com        canonical name = www.wshifen.com.
Name:        www.wshifen.com
Address: 45.113.192.101
Name:        www.wshifen.com
Address: 45.113.192.102
```

下面就是打包为deb了,由于过程较为繁琐,笔者又比较懒就不写了。
实际使用时,需要在“系统设置-Wifi”里面设置DNS为127.0.0.1且删掉其他dns。即可生效,从此远离dns污染




aguai2008 发表于 2023-4-19 23:53:17

非常不错
页: [1]
查看完整版本: 【Golang】编译iOS版dnscrypt-proxy