diff --git a/.gitignore b/.gitignore index 86050ae..d36329c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,12 @@ libtailscale.so libtailscale.a libtailscale.h libtailscale.tar* + +/tstestcontrol/libtstestcontrol.a +/tstestcontrol/libtstestcontrol.h + +/swift/build + /ruby/tmp/ /ruby/pkg/ /ruby/doc/ @@ -9,6 +15,8 @@ libtailscale.tar* /ruby/ext/libtailscale/go.mod /ruby/ext/libtailscale/go.sum /ruby/LICENSE + /sourcepkg/libtailscale /sourcepkg/libtailscale.tar* -/vendor/ \ No newline at end of file + +/vendor/ diff --git a/go.mod b/go.mod index 8a9036b..0e80699 100644 --- a/go.mod +++ b/go.mod @@ -1,86 +1,99 @@ module github.com/tailscale/libtailscale -go 1.21 +go 1.23.1 -require tailscale.com v1.48.0 +require tailscale.com v1.76.6 require ( - filippo.io/edwards25519 v1.0.0 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/akutz/memconn v0.1.0 // indirect - github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect + github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.18.22 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.21 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect - github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 // indirect - github.com/aws/smithy-go v1.13.5 // indirect - github.com/coreos/go-iptables v0.6.0 // indirect - github.com/creack/pty v1.1.18 // indirect - github.com/dblohm7/wingoes v0.0.0-20230803162905-5c6286bb8c6e // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect + github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect + github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect + github.com/aws/smithy-go v1.19.0 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/coder/websocket v1.8.12 // indirect + github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect + github.com/creack/pty v1.1.23 // indirect + github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect + github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect + github.com/djherbis/times v1.6.0 // indirect + github.com/fxamacker/cbor/v2 v2.6.0 // indirect + github.com/gaissmai/bart v0.11.1 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c // indirect - github.com/hdevalence/ed25519consensus v0.1.0 // indirect - github.com/illarion/gonotify v1.0.1 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/csrf v1.7.2 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/illarion/gonotify/v2 v2.0.3 // indirect + github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect + github.com/jellydator/ttlcache/v3 v3.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect - github.com/jsimonetti/rtnetlink v1.3.2 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/jsimonetti/rtnetlink v1.4.0 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/kr/fs v0.1.0 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/sdnotify v1.0.0 // indirect - github.com/mdlayher/socket v0.4.1 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/mdlayher/socket v0.5.0 // indirect + github.com/miekg/dns v1.1.58 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect - github.com/pierrec/lz4/v4 v4.1.17 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pkg/sftp v1.13.5 // indirect - github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect - github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pkg/sftp v1.13.6 // indirect + github.com/prometheus-community/pro-bing v0.4.0 // indirect + github.com/safchain/ethtool v0.3.0 // indirect + github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect + github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect + github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 // indirect github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect - github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect - github.com/tailscale/wireguard-go v0.0.0-20230710185534-bb2c8f22eccf // indirect + github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect + github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect + github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 // indirect + github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 // indirect + github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 // indirect + github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc // indirect + github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e // indirect github.com/tcnksm/go-httpstat v0.2.0 // indirect - github.com/u-root/u-root v0.11.0 // indirect - github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect - github.com/vishvananda/netlink v1.2.1-beta.2 // indirect + github.com/u-root/u-root v0.12.0 // indirect + github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect github.com/vishvananda/netns v0.0.4 // indirect github.com/x448/float16 v0.8.4 // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect - go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect - golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53 // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/term v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect + golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.23.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect - gvisor.dev/gvisor v0.0.0-20230504175454-7b0a1988a28f // indirect - honnef.co/go/tools v0.4.3 // indirect - inet.af/peercred v0.0.0-20210906144145-0893ea02156a // indirect - inet.af/wf v0.0.0-20221017222439-36129f591884 // indirect - nhooyr.io/websocket v1.8.7 // indirect + gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 // indirect + honnef.co/go/tools v0.5.1 // indirect ) diff --git a/go.sum b/go.sum index 5e9e10e..e702a3d 100644 --- a/go.sum +++ b/go.sum @@ -1,104 +1,107 @@ -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc= filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.22 h1:7vkUEmjjv+giht4wIROqLs+49VWmiQMMHSduxmoNKLU= -github.com/aws/aws-sdk-go-v2/config v1.18.22/go.mod h1:mN7Li1wxaPxSSy4Xkr6stFuinJGf3VZW3ZSNvO0q6sI= -github.com/aws/aws-sdk-go-v2/credentials v1.13.21 h1:VRiXnPEaaPeGeoFcXvMZOB5K/yfIXOYE3q97Kgb0zbU= -github.com/aws/aws-sdk-go-v2/credentials v1.13.21/go.mod h1:90Dk1lJoMyspa/EDUrldTxsPns0wn6+KpRKpdAWc0uA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= -github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3 h1:TQZH0Djie8VVgTBDOQ02M4zVHJFrNzLMsYMbNfRitVM= -github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3/go.mod h1:p6MaesK9061w6NTiFmZpUzEkKUY5blKlwD2zYyErxKA= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 h1:6UbNM/KJhMBfOI5+lpVcJ/8OA7cBSz0O6OX37SRKlSw= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.10/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ= -github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE= -github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= +github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw= +github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= +github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7 h1:a8HvP/+ew3tKwSXqL3BCSjiuicr+XTU2eFYeogV9GJE= +github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7/go.mod h1:Q7XIWsMo0JcMpI/6TGD6XXcXcV1DbTj6e9BKNntIMIM= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= +github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= +github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bramvdbogaerde/go-scp v1.4.0 h1:jKMwpwCbcX1KyvDbm/PDJuXcMuNVlLGi0Q0reuzjyKY= +github.com/bramvdbogaerde/go-scp v1.4.0/go.mod h1:on2aH5AxaFb2G0N5Vsdy6B0Ml7k9HuHSwfo1y0QzAbQ= +github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= +github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= +github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= +github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= +github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0= +github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= +github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dblohm7/wingoes v0.0.0-20230803162905-5c6286bb8c6e h1:tTRuQNnXKO6Ffu62nk9bnnPx/m+IyNMdFFfzsETyRO8= -github.com/dblohm7/wingoes v0.0.0-20230803162905-5c6286bb8c6e/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs= -github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= -github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk= +github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= +github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= +github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= +github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI= +github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= +github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc= +github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c h1:06RMfw+TMMHtRuUOroMeatRCCgSMWXCJQeABvHU69YQ= -github.com/google/nftables v0.1.1-0.20230115205135-9aa6fdf5a28c/go.mod h1:BVIYo3cdnT4qSylnYqcd5YtmXhr51cJPGtnLBe/uLBU= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= -github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= -github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwsoio= -github.com/illarion/gonotify v1.0.1/go.mod h1:zt5pmDofZpU1f8aqlK0+95eQhoEAn/d4G4B/FjVW4jE= -github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E= -github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= +github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= +github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/hugelgupf/vmtest v0.0.0-20240102225328-693afabdd27f h1:ov45/OzrJG8EKbGjn7jJZQJTN7Z1t73sFYNIRd64YlI= +github.com/hugelgupf/vmtest v0.0.0-20240102225328-693afabdd27f/go.mod h1:JoDrYMZpDPYo6uH9/f6Peqms3zNNWT2XiGgioMOIGuI= +github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A= +github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE= +github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= +github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +github.com/jellydator/ttlcache/v3 v3.1.0 h1:0gPFG0IHHP6xyUyXq+JaD8fwkDCqgqwohXNJBcYE71g= +github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -106,14 +109,10 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= -github.com/jsimonetti/rtnetlink v1.3.2 h1:dcn0uWkfxycEEyNy0IGfx3GrhQ38LH7odjxAghimsVI= -github.com/jsimonetti/rtnetlink v1.3.2/go.mod h1:BBu4jZCpTjP6Gk0/wfrO8qcqymnN3g0hoFqObRmUo6U= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= +github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= @@ -122,149 +121,169 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI= +github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= -github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go= -github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= +github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4= +github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= +github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d h1:K3j02b5j2Iw1xoggN9B2DIEkhWGheqFOeDkdJdBrJI8= -github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d/go.mod h1:2P+hpOwd53e7JMX/L4f3VXkv1G+33ES6IWZSrkIeWNs= -github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e h1:JyeJF/HuSwvxWtsR1c0oKX1lzaSH5Wh4aX+MgiStaGQ= -github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e/go.mod h1:DjoeCULdP6vTJ/xY+nzzR9LaUHprkbZEpNidX0aqEEk= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU= +github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= +github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ= +github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4= +github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= +github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= +github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4 h1:rXZGgEa+k2vJM8xT0PoSKfVXwFGPQ3z3CJfmnHJkZZw= +github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ= github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= -github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk= -github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= -github.com/tailscale/wireguard-go v0.0.0-20230710185534-bb2c8f22eccf h1:bHQHwIHId353jAF2Lm0cGDjJpse/PYS0I0DTtihL9Ls= -github.com/tailscale/wireguard-go v0.0.0-20230710185534-bb2c8f22eccf/go.mod h1:QRIcq2+DbdIC5sKh/gcAZhuqu6WT6L6G8/ALPN5wqYw= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw= +github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU= +github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= +github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 h1:Gz0rz40FvFVLTBk/K8UNAenb36EbDSnh+q7Z9ldcC8w= +github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU= +github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:tdUdyPqJ0C97SJfjB9tW6EylTtreyee9C44de+UBG0g= +github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= +github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M= +github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y= +github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc h1:cezaQN9pvKVaw56Ma5qr/G646uKIYP0yQf+OyWN/okc= +github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= +github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek= +github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg= +github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= +github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= -github.com/u-root/gobusybox/src v0.0.0-20221229083637-46b2883a7f90 h1:zTk5683I9K62wtZ6eUa6vu6IWwVHXPnoKK5n2unAwv0= -github.com/u-root/gobusybox/src v0.0.0-20221229083637-46b2883a7f90/go.mod h1:lYt+LVfZBBwDZ3+PHk4k/c/TnKOkjJXiJO73E32Mmpc= -github.com/u-root/u-root v0.11.0 h1:6gCZLOeRyevw7gbTwMj3fKxnr9+yHFlgF3N7udUVNO8= -github.com/u-root/u-root v0.11.0/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY= -github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg= -github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= -github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/u-root/gobusybox/src v0.0.0-20231228173702-b69f654846aa h1:unMPGGK/CRzfg923allsikmvk2l7beBeFPUNC4RVX/8= +github.com/u-root/gobusybox/src v0.0.0-20231228173702-b69f654846aa/go.mod h1:Zj4Tt22fJVn/nz/y6Ergm1SahR9dio1Zm/D2/S0TmXM= +github.com/u-root/u-root v0.12.0 h1:K0AuBFriwr0w/PGS3HawiAw89e3+MU7ks80GpghAsNs= +github.com/u-root/u-root v0.12.0/go.mod h1:FYjTOh4IkIZHhjsd17lb8nYW6udgXdJhG1c0r6u0arI= +github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw= +github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= -go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 h1:X66ZEoMN2SuaoI/dfZVYobB6E5zjZyyHUMWlCA7MgGE= -go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= -golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53 h1:w/MOPdQ1IoYoDou3L55ZbTx2Nhn7JAhX1BBZor8qChU= -golang.org/x/exp/typeparams v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= +golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20230504175454-7b0a1988a28f h1:8GE2MRjGiFmfpon8dekPI08jEuNMQzSffVHgdupcO4E= -gvisor.dev/gvisor v0.0.0-20230504175454-7b0a1988a28f/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q= -honnef.co/go/tools v0.4.3 h1:o/n5/K5gXqk8Gozvs2cnL0F2S1/g1vcGCAx2vETjITw= -honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= +gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 h1:TU8z2Lh3Bbq77w0t1eG8yRlLcNHzZu3x6mhoH2Mk0c8= +gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= +honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= +honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg= -inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU= -inet.af/wf v0.0.0-20221017222439-36129f591884 h1:zg9snq3Cpy50lWuVqDYM7AIRVTtU50y5WXETMFohW/Q= -inet.af/wf v0.0.0-20221017222439-36129f591884/go.mod h1:bSAQ38BYbY68uwpasXOTZo22dKGy9SNvI6PZFeKomZE= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= -software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= -tailscale.com v1.48.0 h1:jpc6Fu/dBddptXw1VJ9Euny8+xB00YV91dSwcfuoxw4= -tailscale.com v1.48.0/go.mod h1:RWW4emjviEEAIqr6P6bbZZGXr19BdAdtwtUVfW9SBvU= +software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= +software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= +tailscale.com v1.76.6 h1:qxRVe/ljIVWixIiCLOHrakbsoXcw/dKaKCZt25tJ7gc= +tailscale.com v1.76.6/go.mod h1:myCwmhYBvMCF/5OgBYuIW42zscuEo30bAml7wABVZLk= diff --git a/swift/Makefile b/swift/Makefile new file mode 100644 index 0000000..375ceb2 --- /dev/null +++ b/swift/Makefile @@ -0,0 +1,20 @@ +# Copyright (c) Tailscale Inc & AUTHORS +# SPDX-License-Identifier: BSD-3-Clause + +## Prettifies xcode output for xcode tests using xcpretty, if it is installed +XCPRETTIFIER := xcpretty +ifeq (, $(shell which $(XCPRETTIFIER))) + XCPRETTIFIER := cat +endif + +OUTPUT_DIR=build + +build: + mkdir -p $(OUTPUT_DIR) + xcodebuild build -scheme TailscaleKit -derivedDataPath $(OUTPUT_DIR) -configuration Release -destination 'generic/platform=macOS,arch=arm64' -destination 'generic/platform=iOS' | $(XCPRETTIFIER) + +test: + xcodebuild test -scheme TailscaleKitXCTests -derivedDataPath $(OUTPUT_DIR) -configuration Debug | $(XCPRETTIFIER) + +clean: + rm -rf $(OUTPUT_DIR) \ No newline at end of file diff --git a/swift/README.md b/swift/README.md new file mode 100644 index 0000000..1ca7199 --- /dev/null +++ b/swift/README.md @@ -0,0 +1,91 @@ +# TailscaleKit + +The TailscaleKit Swift package provides an embedded network interface that can be +used to listen for and dial connections to other [Tailscale](https://tailscale.com) nodes in addition +to an extension to URLSession which allows you to make URL requests to nodes on you Tailnet directly. + +The interfaces are similar in design to NWConnection, but are Swift 6 compliant and +designed to be used in modern async/await style code. + +## Build and Install + +Build Requirements: + - XCode 16.1 or newer + +Building Tailscale.framework: + +From /swift +``` +$ make build +``` + +Will build TailscaleKit.framework into /swift/build/Build/Products. + +Separate frameworks will be built for macOS and iOS. All dependencies (libtailscale.a) +are built automatically. Swift 6 is supported. + +Alternatively, you may build from xCode using the Tailscale scheme. + +Non-apple builds are not supported (yet). We do use URLSession and Combine though +it is possible to purge both. + +## Tests + +From /swift +``` +$ make test +``` + + +## Usage + +Nodes need to be authorized in order to function. Set an auth key via +the config.authKey parameter, or watch the log stream and respond to the printed +authorization URL. + +Here's a working example using an auth key: + +```Swift + +// Configures a Tailscale node and starts it up. The node here (and the key we would use to +// authenticate it) are marked as 'ephemeral' - meaning that the node will be disposed of as +// soon as it goes offline. +func start() -> TailscaleNode { + let dataDir = getDocumentDirectoryPath().absoluteString + "tailscale" + let authKey = "tsnet-auth-put-your-auth-key-key-here" + let config = Configuration(hostName: "TSNet-Test", + path: dataDir, + authKey: authKey, + controlURL: Configuration.defaultControlURL, + ephemeral: true) + + // The logger is configurable. The default will just print. + let node = try TailscaleNode(config: config, logger: DefaultLogger()) + + // Bring the node up + try await node.up() + return node +} + +// Do a URL request via the loopback proxy +// Where url is a node on your tailnet such as https://server.fiesty-pangolin.ts.net/thing +func fetchURL(_ url: URL, tailscale: TailscaleNode) async throws -> Data { + // You can cache this. It will not change once the node is up. + let sessionConfig = try await URLSessionConfiguration.tailscaleSession(tailscale) + let session = URLSession(configuration: sessionConfig) + + // Make the request + let req = URLRequest(url: url) + let (data, _) = try await session.data(for: req) + return data +} +``` + +See the [TailscaleKitTests](./Tests/TailscaleKitTests/TailscaleKitTests.swift) for more examples. + +## Contributing + +Pull requests are welcome on GitHub at https://github.com/tailscale/libtailscale + +Please file any issues about this code or the hosted service on +[the issue tracker](https://github.com/tailscale/tailscale/issues). diff --git a/swift/TailscaleKit.xcodeproj/project.pbxproj b/swift/TailscaleKit.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a132530 --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/project.pbxproj @@ -0,0 +1,858 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXAggregateTarget section */ + C2BED0552CCF3031004A2544 /* libtstestcontrol */ = { + isa = PBXAggregateTarget; + buildConfigurationList = C2BED0572CCF3031004A2544 /* Build configuration list for PBXAggregateTarget "libtstestcontrol" */; + buildPhases = ( + C2BED0562CCF3031004A2544 /* ShellScript */, + ); + dependencies = ( + ); + name = libtstestcontrol; + packageProductDependencies = ( + ); + productName = libtailscale; + }; + C2EE3B622CCBE88400CF5BE0 /* libtailscale */ = { + isa = PBXAggregateTarget; + buildConfigurationList = C2EE3B632CCBE88400CF5BE0 /* Build configuration list for PBXAggregateTarget "libtailscale" */; + buildPhases = ( + C2EE3B662CCBE88E00CF5BE0 /* ShellScript */, + ); + dependencies = ( + ); + name = libtailscale; + packageProductDependencies = ( + ); + productName = libtailscale; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + C2BED05D2CCFC68D004A2544 /* libtstestcontrol.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */; }; + C2E1C30B2CC9EF1A00ADC565 /* libtailscale.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */; }; + C2E1C3142CCA8B7C00ADC565 /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C286408D2CCA8CB600CD5EBC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C2E1C2D12CC9B5A400ADC565 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C28640612CCA8C9D00CD5EBC; + remoteInfo = TailscaleTestHost; + }; + C2BED05A2CCF308D004A2544 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C2E1C2D12CC9B5A400ADC565 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C2BED0552CCF3031004A2544; + remoteInfo = tsnetintegration; + }; + C2E1C3152CCA8B7C00ADC565 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C2E1C2D12CC9B5A400ADC565 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C2E1C2D92CC9B5A400ADC565; + remoteInfo = Tailscale; + }; + C2EE3B692CCBED1E00CF5BE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C2E1C2D12CC9B5A400ADC565 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C2EE3B622CCBE88400CF5BE0; + remoteInfo = libtailscale; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TailscaleKitTestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtstestcontrol.a; path = ../tstestconrol/libtstestcontrol.a; sourceTree = ""; }; + C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TailscaleKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtailscale.a; path = ../libtailscale.a; sourceTree = ""; }; + C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TailscaleKitXCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C2E3E87F2D2718D0004992A2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + C2E3E8802D2718D6004992A2 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + publicHeaders = ( + TailscaleKit.h, + ); + target = C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */; + }; + C2E3E87E2D2711BF004992A2 /* Exceptions for "TailscaleKitTestHost" folder in "TailscaleKitTestHost" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ + C2EE3B702CCC179300CF5BE0 /* Exceptions for "TailscaleKitXCTests" folder in "Compile Sources" phase from "TailscaleKitXCTests" target */ = { + isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + buildPhase = C2E1C30C2CCA8B7C00ADC565 /* Sources */; + membershipExceptions = ( + "Test-Bridging-Header.h", + ); + }; +/* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + C28640632CCA8C9D00CD5EBC /* TailscaleKitTestHost */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + C2E3E87E2D2711BF004992A2 /* Exceptions for "TailscaleKitTestHost" folder in "TailscaleKitTestHost" target */, + ); + path = TailscaleKitTestHost; + sourceTree = ""; + }; + C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + C2E1C2EC2CC9B5A400ADC565 /* Exceptions for "TailscaleKit" folder in "TailscaleKit" target */, + ); + path = TailscaleKit; + sourceTree = ""; + }; + C2E1C3112CCA8B7C00ADC565 /* TailscaleKitXCTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + C2EE3B702CCC179300CF5BE0 /* Exceptions for "TailscaleKitXCTests" folder in "Compile Sources" phase from "TailscaleKitXCTests" target */, + ); + path = TailscaleKitXCTests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + C286405F2CCA8C9D00CD5EBC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2E1C2D72CC9B5A400ADC565 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2E1C30B2CC9EF1A00ADC565 /* libtailscale.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2E1C30D2CCA8B7C00ADC565 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2BED05D2CCFC68D004A2544 /* libtstestcontrol.a in Frameworks */, + C2E1C3142CCA8B7C00ADC565 /* TailscaleKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C2E1C2D02CC9B5A400ADC565 = { + isa = PBXGroup; + children = ( + C2E3E8802D2718D6004992A2 /* Makefile */, + C2E3E87F2D2718D0004992A2 /* README.md */, + C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */, + C2E1C3112CCA8B7C00ADC565 /* TailscaleKitXCTests */, + C28640632CCA8C9D00CD5EBC /* TailscaleKitTestHost */, + C2E1C2FB2CC9B9E300ADC565 /* Frameworks */, + C2E1C2DB2CC9B5A400ADC565 /* Products */, + ); + sourceTree = ""; + }; + C2E1C2DB2CC9B5A400ADC565 /* Products */ = { + isa = PBXGroup; + children = ( + C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */, + C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */, + C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */, + ); + name = Products; + sourceTree = ""; + }; + C2E1C2FB2CC9B9E300ADC565 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C2BED05C2CCFC68D004A2544 /* libtstestcontrol.a */, + C2E1C2FC2CC9B9E300ADC565 /* libtailscale.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + C2E1C2D52CC9B5A400ADC565 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */ = { + isa = PBXNativeTarget; + buildConfigurationList = C28640842CCA8C9E00CD5EBC /* Build configuration list for PBXNativeTarget "TailscaleKitTestHost" */; + buildPhases = ( + C286405E2CCA8C9D00CD5EBC /* Sources */, + C286405F2CCA8C9D00CD5EBC /* Frameworks */, + C28640602CCA8C9D00CD5EBC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + C28640632CCA8C9D00CD5EBC /* TailscaleKitTestHost */, + ); + name = TailscaleKitTestHost; + packageProductDependencies = ( + ); + productName = TailscaleTestHost; + productReference = C28640622CCA8C9D00CD5EBC /* TailscaleKitTestHost.app */; + productType = "com.apple.product-type.application"; + }; + C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2E1C2ED2CC9B5A400ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKit" */; + buildPhases = ( + C2E1C2D52CC9B5A400ADC565 /* Headers */, + C2E1C2D62CC9B5A400ADC565 /* Sources */, + C2E1C2D72CC9B5A400ADC565 /* Frameworks */, + C2E1C2D82CC9B5A400ADC565 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C2EE3B6A2CCBED1E00CF5BE0 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + C2E1C2DC2CC9B5A400ADC565 /* TailscaleKit */, + ); + name = TailscaleKit; + packageProductDependencies = ( + ); + productName = Tailscale; + productReference = C2E1C2DA2CC9B5A400ADC565 /* TailscaleKit.framework */; + productType = "com.apple.product-type.framework"; + }; + C2E1C30F2CCA8B7C00ADC565 /* TailscaleKitXCTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C2E1C3172CCA8B7C00ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKitXCTests" */; + buildPhases = ( + C2E1C30C2CCA8B7C00ADC565 /* Sources */, + C2E1C30D2CCA8B7C00ADC565 /* Frameworks */, + C2E1C30E2CCA8B7C00ADC565 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C2BED05B2CCF308D004A2544 /* PBXTargetDependency */, + C2E1C3162CCA8B7C00ADC565 /* PBXTargetDependency */, + C286408E2CCA8CB600CD5EBC /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + C2E1C3112CCA8B7C00ADC565 /* TailscaleKitXCTests */, + ); + name = TailscaleKitXCTests; + packageProductDependencies = ( + ); + productName = TailscaleXCTests; + productReference = C2E1C3102CCA8B7C00ADC565 /* TailscaleKitXCTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C2E1C2D12CC9B5A400ADC565 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1610; + LastUpgradeCheck = 1610; + TargetAttributes = { + C28640612CCA8C9D00CD5EBC = { + CreatedOnToolsVersion = 16.1; + }; + C2E1C2D92CC9B5A400ADC565 = { + CreatedOnToolsVersion = 16.1; + }; + C2E1C30F2CCA8B7C00ADC565 = { + CreatedOnToolsVersion = 16.1; + TestTargetID = C28640612CCA8C9D00CD5EBC; + }; + C2EE3B622CCBE88400CF5BE0 = { + CreatedOnToolsVersion = 16.1; + }; + }; + }; + buildConfigurationList = C2E1C2D42CC9B5A400ADC565 /* Build configuration list for PBXProject "TailscaleKit" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C2E1C2D02CC9B5A400ADC565; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = C2E1C2DB2CC9B5A400ADC565 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */, + C2E1C30F2CCA8B7C00ADC565 /* TailscaleKitXCTests */, + C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */, + C2EE3B622CCBE88400CF5BE0 /* libtailscale */, + C2BED0552CCF3031004A2544 /* libtstestcontrol */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C28640602CCA8C9D00CD5EBC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2E1C2D82CC9B5A400ADC565 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2E1C30E2CCA8B7C00ADC565 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + C2BED0562CCF3031004A2544 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + "$(SRCROOT)/../tstestcontrol/libtstestcontrol.a", + "$(SRCROOT)/../tstestcontrol/libtstestcontrol.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\npushd .\ncd $(SCROOT)/../tstestcontrol\nmake all\npopd\n"; + }; + C2EE3B662CCBE88E00CF5BE0 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + "$(SRCROOT)/../libtailscale.a", + "$(SRCROOT)/../libtailscale.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\npushd .\ncd $(SCROOT)/..\nmake libtailscale\npopd\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C286405E2CCA8C9D00CD5EBC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2E1C2D62CC9B5A400ADC565 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C2E1C30C2CCA8B7C00ADC565 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C286408E2CCA8CB600CD5EBC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C28640612CCA8C9D00CD5EBC /* TailscaleKitTestHost */; + targetProxy = C286408D2CCA8CB600CD5EBC /* PBXContainerItemProxy */; + }; + C2BED05B2CCF308D004A2544 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C2BED0552CCF3031004A2544 /* libtstestcontrol */; + targetProxy = C2BED05A2CCF308D004A2544 /* PBXContainerItemProxy */; + }; + C2E1C3162CCA8B7C00ADC565 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C2E1C2D92CC9B5A400ADC565 /* TailscaleKit */; + targetProxy = C2E1C3152CCA8B7C00ADC565 /* PBXContainerItemProxy */; + }; + C2EE3B6A2CCBED1E00CF5BE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C2EE3B622CCBE88400CF5BE0 /* libtailscale */; + targetProxy = C2EE3B692CCBED1E00CF5BE0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + C28640852CCA8C9E00CD5EBC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = TailscaleKitTestHost/TailscaleKitTestHost.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = W5364U7YZB; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = TailscaleKitTestHost/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.TailscaleTestHost; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + C28640862CCA8C9E00CD5EBC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = TailscaleKitTestHost/TailscaleKitTestHost.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = W5364U7YZB; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = TailscaleKitTestHost/Info.plist; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.TailscaleTestHost; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + C2BED0582CCF3031004A2544 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = W5364U7YZB; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + C2BED0592CCF3031004A2544 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = W5364U7YZB; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + C2E1C2EE2CC9B5A400ADC565 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = W5364U7YZB; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/.."; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/..", + "$(SRCROOT)", + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(SRCROOT)/.."; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.Tailscale; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 6.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Debug; + }; + C2E1C2EF2CC9B5A400ADC565 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = W5364U7YZB; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/.."; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/..", + "$(SRCROOT)", + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 18.1; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(SRCROOT)/.."; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.Tailscale; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 6.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.1; + }; + name = Release; + }; + C2E1C2F02CC9B5A400ADC565 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + C2E1C2F12CC9B5A400ADC565 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + C2E1C3182CCA8B7C00ADC565 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5364U7YZB; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/../tstestcontrol"; + LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../tstestcontrol"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.TailscaleXCTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/TailscaleKitXCTests/Test-Bridging-Header.h"; + SWIFT_VERSION = 6.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TailscaleKitTestHost.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/TailscaleKitTestHost"; + }; + name = Debug; + }; + C2E1C3192CCA8B7C00ADC565 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = W5364U7YZB; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/../tstestcontrol"; + LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../tstestcontrol"; + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.tailscale.TailscaleXCTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/TailscaleKitXCTests/Test-Bridging-Header.h"; + SWIFT_VERSION = 6.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TailscaleKitTestHost.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/TailscaleKitTestHost"; + }; + name = Release; + }; + C2EE3B642CCBE88400CF5BE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = W5364U7YZB; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + C2EE3B652CCBE88400CF5BE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = W5364U7YZB; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C28640842CCA8C9E00CD5EBC /* Build configuration list for PBXNativeTarget "TailscaleKitTestHost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C28640852CCA8C9E00CD5EBC /* Debug */, + C28640862CCA8C9E00CD5EBC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C2BED0572CCF3031004A2544 /* Build configuration list for PBXAggregateTarget "libtstestcontrol" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2BED0582CCF3031004A2544 /* Debug */, + C2BED0592CCF3031004A2544 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C2E1C2D42CC9B5A400ADC565 /* Build configuration list for PBXProject "TailscaleKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2E1C2F02CC9B5A400ADC565 /* Debug */, + C2E1C2F12CC9B5A400ADC565 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C2E1C2ED2CC9B5A400ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2E1C2EE2CC9B5A400ADC565 /* Debug */, + C2E1C2EF2CC9B5A400ADC565 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C2E1C3172CCA8B7C00ADC565 /* Build configuration list for PBXNativeTarget "TailscaleKitXCTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2E1C3182CCA8B7C00ADC565 /* Debug */, + C2E1C3192CCA8B7C00ADC565 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C2EE3B632CCBE88400CF5BE0 /* Build configuration list for PBXAggregateTarget "libtailscale" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C2EE3B642CCBE88400CF5BE0 /* Debug */, + C2EE3B652CCBE88400CF5BE0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C2E1C2D12CC9B5A400ADC565 /* Project object */; +} diff --git a/swift/TailscaleKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/swift/TailscaleKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit.xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit.xcscheme new file mode 100644 index 0000000..1c775c4 --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKit.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKitTestHost.xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKitTestHost.xcscheme new file mode 100644 index 0000000..350934f --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKitTestHost.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKitXCTests.xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKitXCTests.xcscheme new file mode 100644 index 0000000..d85e8c1 --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/TailscaleKitXCTests.xcscheme @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtstestcontrol.xcscheme b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtstestcontrol.xcscheme new file mode 100644 index 0000000..02a4f8f --- /dev/null +++ b/swift/TailscaleKit.xcodeproj/xcshareddata/xcschemes/libtstestcontrol.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/swift/TailscaleKit/IncomingConnection.swift b/swift/TailscaleKit/IncomingConnection.swift new file mode 100644 index 0000000..9a13ce6 --- /dev/null +++ b/swift/TailscaleKit/IncomingConnection.swift @@ -0,0 +1,103 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import Combine + +/// IncomingConnection is use to read incoming message from an inbound +/// connection. IncomingConnections are not instantiated directly, +/// they are returned by Listener.accept +public actor IncomingConnection { + private let logger: LogSink? + private var conn: TailscaleConnection = 0 + private let reader: SocketReader + + public let remoteAddress: String? + + @Published public var state: ConnectionState = .idle + + init(conn: TailscaleConnection, remoteAddress: String?, logger: LogSink? = nil) async { + self.logger = logger + self.conn = conn + self.state = .connected + self.remoteAddress = remoteAddress + reader = SocketReader(conn: conn) + } + + deinit { + if conn != 0 { + unistd.close(conn) + } + } + + public func close() { + if conn != 0 { + unistd.close(conn) + conn = 0 + } + state = .closed + } + + /// Returns up to size bytes from the connection. Blocks until + /// data is available + public func receive(maximumLength: Int = 4096, timeout: Int32) async throws -> Data { + guard state == .connected else { + throw TailscaleError.connectionClosed + } + + return try await reader.read(timeout: timeout, len: maximumLength) + } + + /// Reads a complete message from the connection + public func receiveMessage( timeout: Int32) async throws -> Data { + guard state == .connected else { + throw TailscaleError.connectionClosed + } + + return try await reader.readAll(timeout: timeout) + } +} + +/// Serializes read operations from an IncomingConnection +private actor SocketReader { + // We'll read in 2048 byte chunks which should be sufficient to hold the payload + // of a single packet + private static let maxBufferSize = 2048 + private let conn: TailscaleConnection + private var buffer = [UInt8](repeating:0, count: maxBufferSize) + + init(conn: TailscaleConnection) { + self.conn = conn + } + + func read(timeout: Int32, len: Int) throws -> Data { + var p: pollfd = .init(fd: conn, events: Int16(POLLIN), revents: 0) + let res = poll(&p, 1, timeout) + guard res > 0 else { + throw TailscaleError.readFailed + } + + let bytesToRead = min(len, Self.maxBufferSize) + var bytesRead = 0 + buffer.withUnsafeMutableBufferPointer { ptr in + bytesRead = unistd.read(conn, ptr.baseAddress, bytesToRead) + } + + if bytesRead < 0 { + throw TailscaleError.readFailed + } + return Data(buffer[0.. Data { + var data: Data = .init() + while true { + let read = try read(timeout: timeout, len: Self.maxBufferSize) + data.append(read) + if read.count < Self.maxBufferSize { + break + } + } + return data + } +} + diff --git a/swift/TailscaleKit/Listener.swift b/swift/TailscaleKit/Listener.swift new file mode 100644 index 0000000..c1d8d2f --- /dev/null +++ b/swift/TailscaleKit/Listener.swift @@ -0,0 +1,120 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import Combine + +/// A Listener is used to await incoming connections from another +/// Tailnet node. +public actor Listener { + private var tailscale: TailscaleHandle + private var listener: TailscaleListener = 0 + private var proto: NetProtocol + private var address: String + + private let logger: LogSink? + + @Published public var state: ListenterState = .idle + + /// Initializes and readies a new listener + /// + /// @param tailscale A handle to a Tailscale server + /// @param proto The ip protocol to listen for + /// @param address The adderss (ip:port or port) to listen on + /// @param logger An optional LogSink + init(tailscale: TailscaleHandle, + proto: NetProtocol, + address: String, + logger: LogSink? = nil) async throws { + self.logger = logger + self.tailscale = tailscale + self.address = address + self.proto = proto + + let res = tailscale_listen(tailscale, proto.rawValue, address, &listener) + + guard res == 0 else { + state = .failed + let msg = tailscale.getErrorMessage() + let err = TailscaleError.fromPosixErrCode(res, msg) + logger?.log("Listener failed to initialize: \(msg) (\(err.localizedDescription))") + throw err + } + state = .listening + } + + deinit { + if listener != 0 { + unistd.close(listener) + } + } + + /// Closes the listener. It cannot be restarted + /// Listeners will be closed automatically on deallocation + public func close() { + if listener != 0 { + unistd.close(listener) + listener = 0 + } + state = .closed + } + + /// Blocks and awaits a new incoming connection + /// + /// @See tailscale_accept in Tailscale.h + /// @See tailscale_getremoteaddr in Tailscale.h + /// + /// @param timeout The timeout for the underlying poll(2) in seconds. This has a maximum + /// value of Int32.max ms and supports millisecond precision per poll(2) + /// @throws TailscaleError on failure or timeout + /// @returns An incoming connection from which you can receive() Data + func accept(timeout: TimeInterval = 60) async throws -> IncomingConnection { + if timeout * 1000 > Double(Int32.max) || timeout < 0 { + throw TailscaleError.invalidTimeout + } + + logger?.log("Listening for \(proto.rawValue) on \(address)") + + var p: pollfd = .init(fd: listener, events: Int16(POLLIN), revents: 0) + let ret = poll(&p, 1, Int32(timeout * 1000)) + guard ret > 0 else { + close() + throw TailscaleError.fromPosixErrCode(errno, "Poll failed") + } + + logger?.log("Accepting \(proto.rawValue) connection via \(address)") + guard listener != 0 else { + close() + throw TailscaleError.listenerClosed + } + + var connfd: Int32 = 0 + let res = tailscale_accept(listener, &connfd) + guard res == 0 else { + close() + let msg = tailscale.getErrorMessage() + throw TailscaleError.fromPosixErrCode(res, msg) + } + + /// We extract the remove address here for utility so you know + /// who's calling, so you can dial back. + var remoteAddress: String? + var buffer = [Int8](repeating:0, count: 64) + buffer.withUnsafeMutableBufferPointer { buf in + let err = tailscale_getremoteaddr(listener, connfd, buf.baseAddress, 64) + if err == 0 { + remoteAddress = String(cString: buf.baseAddress!) + } else { + let msg = tailscale.getErrorMessage() + let err = TailscaleError.fromPosixErrCode(err, msg) + logger?.log("Failed to get remote address: \(msg) \(err.localizedDescription)") + // Do not throw here. Lack of a remote address is not fatal + // The caller can directly invoke server.addrs() if required. + } + } + + logger?.log("Accepted \(proto.rawValue) fd:\(connfd) from:\(remoteAddress ?? "unknown")") + return await IncomingConnection(conn: connfd, + remoteAddress: remoteAddress, + logger: logger) + } +} diff --git a/swift/TailscaleKit/LogSink.swift b/swift/TailscaleKit/LogSink.swift new file mode 100644 index 0000000..30931b4 --- /dev/null +++ b/swift/TailscaleKit/LogSink.swift @@ -0,0 +1,31 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +/// A generic interface for sinking log messages from the Swift wrapper +/// and go +public protocol LogSink: Sendable { + /// An optional file handle. The go backend will write all internal logs + /// to this. STDOUT_FILENO or a handle to a writable file. + var logFileHandle: Int32? { get } + + /// Called for swfit interal logs. + func log(_ message: String) +} + +/// Dumps all internal logs to NSLog and go logs to stdout +struct DefaultLogger: LogSink { + var logFileHandle: Int32? = STDOUT_FILENO + + func log(_ message: String) { + NSLog(message) + } +} + +/// Discards all logs +struct BlackholeLogger: LogSink { + var logFileHandle: Int32? + + func log(_ message: String) { + // Go back to the Shadow! + } +} diff --git a/swift/TailscaleKit/OutgoingConnection.swift b/swift/TailscaleKit/OutgoingConnection.swift new file mode 100644 index 0000000..cca0f61 --- /dev/null +++ b/swift/TailscaleKit/OutgoingConnection.swift @@ -0,0 +1,110 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import Combine + +/// ConnectionState indicates the state of individual TSConnection instances +public enum ConnectionState { + case idle ///< Reads and writes are not possible. Connections will transition to connected automatically + case connected ///< Connected and ready to read/write + case closed ///< Closed and ready to be disposed of. Closed connections cannot be reconnected. + case failed ///< The attempt to dial the connection failed +} + + +/// ListnerState indicates the state of individual TSListener instances +public enum ListenterState { + case idle ///< Waiting. + case listening ///< Listening + case closed ///< Closed and ready to be disposed of. + case failed ///< The attempt to start the listener failed +} + +typealias TailscaleHandle = Int32 +typealias TailscaleConnection = Int32 +typealias TailscaleListener = Int32 + +/// Outgoing connections are used to send data to other endpoints +/// on the tailnet. +/// +/// For HTTP(s), consider using URLSession.tailscaleSession +public actor OutgoingConnection { + private var tailscale: TailscaleHandle + private var proto: NetProtocol + private var address: String + private var conn: TailscaleConnection = 0 + + private let logger: LogSink + + /// The state of the connection. Listen for transitions to determine + /// if the connection may be used for send/receive operations. + public var state: ConnectionState = .idle + + /// Creates a new outgoing connection + /// + /// @param tailscale The tailscale Server to use + /// @param address The remote address and port + /// @param proto The ip protocol + /// @param logger + /// + /// @throws TailscaleError on failure + init(tailscale: TailscaleHandle, + to address: String, + proto: NetProtocol, + logger: LogSink) async throws { + + self.logger = logger + self.proto = proto + self.address = address + self.tailscale = tailscale + } + + /// Connects the outgoing connection to the remote. On success, the + /// connection state will be .connected. + /// + /// @See tailscale_dial in Tailscale.h + /// + /// @throws TailscaleError on failure + func connect() async throws { + let res = tailscale_dial(tailscale, proto.rawValue, address, &conn) + + guard res == 0 else { + self.state = .failed + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + + self.state = .connected + } + + deinit { + if conn != 0 { + unistd.close(conn) + } + } + + /// Closes the outgoing connection. Further sends are not possible. + /// Connections will be closed on deallocation. Sets the connection + /// state to .closed + public func close() { + if conn != 0 { + unistd.close(conn) + conn = 0 + } + state = .closed + } + + /// Sends the given data to the connection + /// + /// @throws TailscaleError on failure + public func send(_ data: Data) throws { + guard state == .connected else { + throw TailscaleError.connectionClosed + } + + let bytesWritten = unistd.write(conn, data.withUnsafeBytes { $0.baseAddress! }, data.count) + + if bytesWritten != data.count { + throw TailscaleError.shortWrite + } + } +} diff --git a/swift/TailscaleKit/TailscaleError.swift b/swift/TailscaleKit/TailscaleError.swift new file mode 100644 index 0000000..9cfe7eb --- /dev/null +++ b/swift/TailscaleKit/TailscaleError.swift @@ -0,0 +1,50 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + + +public enum TailscaleError: Error { + case badInterfaceHandle ///< The tailscale handle is bad. + case listenerClosed ///< The listener was closed and cannot accept new connections + case invalidTimeout ///< The provided listener timeout is invalid + case connectionClosed ///< The underlying connection is closed + case readFailed ///< Read failed + case shortWrite ///< Some data was not written to the connection + case invalidProxyAddress ///< Some data was not written to the connection + case invalidControlURL ///< The provided control URL is invalid + + case cannotFetchIps(_ details: String? = nil) ///< The IPs for the Tailscale server could not be read + case posixError(_ err: POSIXError, _ details: String? = nil) ///< A posix error was thrown with the given err code and details + case unknownPosixError(_ err: Int32, _ details: String? = nil) ///< An unknown posix error occurred + case internalError(_ details: String? = nil) ///< A generic internal error occurred + + /// Create a Tailscale error from an underlying posix error code + static func fromPosixErrCode(_ code: Int32, _ details: String? = nil) -> TailscaleError { + if code == -1 { + return .internalError(details) + } + if let code = POSIXErrorCode(rawValue: code){ + return .posixError( POSIXError(code)) + } + return unknownPosixError(code, details) + } +} + + +extension TailscaleHandle { + static let kMaxErrorMessageLength: Int = 256 + + /// Returns the last error message in the Tailscale server as a string. + /// Handles messages up to kMaxErrorMessageLength bytes only. + internal func getErrorMessage() -> String { + let buf = UnsafeMutablePointer.allocate(capacity: Self.kMaxErrorMessageLength) + defer { + buf.deallocate() + + } + let res = tailscale_errmsg(self, buf, 256) + if res != 0 { + return "Error fetch failure: \(res)" + } + return String(cString: buf) + } +} diff --git a/swift/TailscaleKit/TailscaleKit.docc/Tailscale.md b/swift/TailscaleKit/TailscaleKit.docc/Tailscale.md new file mode 100644 index 0000000..236b59e --- /dev/null +++ b/swift/TailscaleKit/TailscaleKit.docc/Tailscale.md @@ -0,0 +1,3 @@ +# ``TailscaleKit`` + +Swift 6 wrapper for libtailscale diff --git a/swift/TailscaleKit/TailscaleKit.h b/swift/TailscaleKit/TailscaleKit.h new file mode 100644 index 0000000..c157d66 --- /dev/null +++ b/swift/TailscaleKit/TailscaleKit.h @@ -0,0 +1,201 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +#import + +//! Project version number for Tailscale. +FOUNDATION_EXPORT double TailscaleKitVersionNumber; + +//! Project version string for Tailscale. +FOUNDATION_EXPORT const unsigned char TailscaleKitVersionString[]; + + +// TODO: Is there away to avoid the header duplication here? +// WARNING: Adding/changing the libtailscale functions must be replicated here +#include + +#ifndef TAILSCALEKIT_H +#define TAILSCALEKIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Use this library to compile Tailscale into your program and get +// an entirely userspace IP address on a tailnet. +// +// From here you can listen for other programs on your tailnet dialing +// you, or connect directly to other services. +// + +// tailscale is a handle onto a Tailscale server. +typedef int tailscale; + +// tailscale_new creates a tailscale server object. +// +// No network connection is initialized until tailscale_start is called. +extern tailscale tailscale_new(); + +// tailscale_start connects the server to the tailnet. +// +// Calling this function is optional as it will be called by the first use +// of tailscale_listen or tailscale_dial on a server. +// +// See also: tailscale_up. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_start(tailscale sd); + +// tailscale_up connects the server to the tailnet and waits for it to be usable. +// +// To cancel an in-progress call to tailscale_up, use tailscale_close. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_up(tailscale sd); + +// tailscale_close shuts down the server. +// +// Returns: +// 0 - success +// EBADF - sd is not a valid tailscale +// -1 - other error, details printed to the tsnet logger +extern int tailscale_close(tailscale sd); + +// The following set tailscale configuration options. +// +// Configure these options before any explicit or implicit call to tailscale_start. +// +// For details of each value see the godoc for the fields of tsnet.Server. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_set_dir(tailscale sd, const char* dir); +extern int tailscale_set_hostname(tailscale sd, const char* hostname); +extern int tailscale_set_authkey(tailscale sd, const char* authkey); +extern int tailscale_set_control_url(tailscale sd, const char* control_url); +extern int tailscale_set_ephemeral(tailscale sd, int ephemeral); + +// tailscale_set_logfd instructs the tailscale instance to write logs to fd. +// +// An fd value of -1 means discard all logging. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_set_logfd(tailscale sd, int fd); + +// A tailscale_conn is a connection to an address on the tailnet. +// +// It is a pipe(2) on which you can use read(2), write(2), and close(2). +// For extra control over the connection, see the tailscale_conn_* functions. +typedef int tailscale_conn; + +// Returns the IP addresses of the the Tailscale server as +// a comma separated list. +// +// The provided buffer must be of sufficient size to hold the concatenated +// IPs as strings. This is typically , but maybe empty, or +// contain any number of ips. The caller is responsible for parsing +// the output. You may assume the output is a list of well-formed IPs. +// +// Returns: +// 0 - Success +// EBADF - sd is not a valid tailscale, or l or conn are not valid listeneras or connections +// ERANGE - insufficient storage for buf +extern int tailscale_getips(tailscale sd, char* buf, size_t buflen); + +// tailscale_dial connects to the address on the tailnet. +// +// The newly allocated connection is written to conn_out. +// +// network is a NUL-terminated string of the form "tcp", "udp", etc. +// addr is a NUL-terminated string of an IP address or domain name. +// +// It will start the server if it has not been started yet. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_dial(tailscale sd, const char* network, const char* addr, tailscale_conn* conn_out); + +// A tailscale_listener is a socket on the tailnet listening for connections. +// +// It is much like allocating a system socket(2) and calling listen(2). +// Accept connections with tailscale_accept and close the listener with close. +// +// Under the hood, a tailscale_listener is one half of a socketpair itself, +// used to move the connection fd from Go to C. This means you can use epoll +// or its equivalent on a tailscale_listener to know if there is a connection +// read to accept. +typedef int tailscale_listener; + +// tailscale_listen listens for a connection on the tailnet. +// +// It is the spiritual equivalent to listen(2). +// The newly allocated listener is written to listener_out. +// +// network is a NUL-terminated string of the form "tcp", "udp", etc. +// addr is a NUL-terminated string of an IP address or domain name. +// +// It will start the server if it has not been started yet. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_listen(tailscale sd, const char* network, const char* addr, tailscale_listener* listener_out); + +// Returns the remote address for an incoming connection for a particular listener. The address (eitehr ip4 or ip6) +// will ge written to buf on on success. +// Returns: +// 0 - Success +// EBADF - sd is not a valid tailscale, or l or conn are not valid listeneras or connections +// ERANGE - insufficient storage for buf +extern int tailscale_getremoteaddr(tailscale_listener l, tailscale_conn conn, char* buf, size_t buflen); + + +// tailscale_accept accepts a connection on a tailscale_listener. +// +// It is the spiritual equivalent to accept(2). +// +// The newly allocated connection is written to conn_out. +// +// Returns: +// 0 - success +// EBADF - listener is not a valid tailscale +// -1 - call tailscale_errmsg for details +extern int tailscale_accept(tailscale_listener listener, tailscale_conn* conn_out); + +// tailscale_loopback starts a loopback address server. +// +// The server has multiple functions. +// +// It can be used as a SOCKS5 proxy onto the tailnet. +// Authentication is required with the username "tsnet" and +// the value of proxy_cred used as the password. +// +// The HTTP server also serves out the "LocalAPI" on /localapi. +// As the LocalAPI is powerful, access to endpoints requires BOTH passing a +// "Sec-Tailscale: localapi" HTTP header and passing local_api_cred as +// the basic auth password. +// +// The pointers proxy_cred_out and local_api_cred_out must be non-NIL +// and point to arrays that can hold 33 bytes. The first 32 bytes are +// the credential and the final byte is a NUL terminator. +// +// If tailscale_loopback returns, then addr_our, proxy_cred_out, +// and local_api_cred_out are all NUL-terminated. +// +// Returns zero on success or -1 on error, call tailscale_errmsg for details. +extern int tailscale_loopback(tailscale sd, char* addr_out, size_t addrlen, char* proxy_cred_out, char* local_api_cred_out); + +// tailscale_errmsg writes the details of the last error to buf. +// +// After returning, buf is always NUL-terminated. +// +// Returns: +// 0 - success +// EBADF - sd is not a valid tailscale +// ERANGE - insufficient storage for buf +extern int tailscale_errmsg(tailscale sd, char* buf, size_t buflen); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/swift/TailscaleKit/TailscaleNode.swift b/swift/TailscaleKit/TailscaleNode.swift new file mode 100644 index 0000000..7523ba5 --- /dev/null +++ b/swift/TailscaleKit/TailscaleNode.swift @@ -0,0 +1,247 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +/// Configuration for a tailscale application node +public struct Configuration: Sendable { + let hostName: String ///< The hostname of the node/application instance + let path: String + let authKey: String? ///< An auth key. Leave empty to use web auth + let controlURL: String ///< URL for Tailscale control + let ephemeral: Bool + + static let defaultControlURL = "https://controlplane.tailscale.com" +} + +/// The layer 3 protocol to use +public enum NetProtocol: String { + case tcp = "tcp" + case udp = "udp" +} + +public typealias IPAddresses = (ip4: String?, ip6: String?) + +/// TSInterface creates and manages a single userspace Tailscale application +/// node. You may instantiate several "nodes" in a single application. Each +/// will get a unique IP address on the Tailnet. +/// +/// The provided wrapper abstract away the C code and allow the writing of proper, +/// compiler checked thread-safe Swift 6. +public actor TailscaleNode { + + /// Handle to the underlying Tailscale server. Use this when instantiating + /// new IncomingConnections or OutgoingConnections + let tailscale: TailscaleHandle? + + private let logger: LogSink? + + /// Instantiate a new TailscaleNode with the given configuration and + /// and optional LogSink. If no LogSink is provided, logs will be + /// discarded. + /// + /// @See tailscale_set_* in Tailscale.h + /// @See tailscale_start in Tailscale.h + /// + /// @throws TailscaleError on failure + public init(config: Configuration, logger: LogSink?) throws { + self.logger = logger ?? BlackholeLogger() + + tailscale = tailscale_new() + + guard let tailscale else { + throw TailscaleError.badInterfaceHandle + } + + logger?.log("Tailscale starting: \(tailscale)") + + if let fd = logger?.logFileHandle { + tailscale_set_logfd(tailscale, fd) + } + + if let authKey = config.authKey { + tailscale_set_authkey(tailscale, authKey) + } + tailscale_set_hostname(tailscale, config.hostName) + tailscale_set_dir(tailscale, config.path) + tailscale_set_control_url(tailscale, config.controlURL) + tailscale_set_ephemeral(tailscale, config.ephemeral ? 1 : 0) + + let res = tailscale_start(tailscale) + + guard res == 0 else { + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + + logger?.log("Tailscale started... \(tailscale)") + } + + deinit { + if let tailscale { + tailscale_close(tailscale) + } + } + + /// Closes/stops the Tailscale server + /// + /// @See tailscale_close in Tailscale.h + /// + /// @Throws TailscaleError on failure + public func close() async throws { + guard let tailscale else { + throw TailscaleError.badInterfaceHandle + } + + logger?.log("Closing Tailscale: \(tailscale)") + let res = tailscale_close(tailscale) + + guard res == 0 else { + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + logger?.log("Closed Tailscale:\(tailscale)") + } + + /// Brings up the Tailscale server + /// + /// @See tailscale_up in Tailscale.h + /// + /// @throws TailscaleError on failure + public func up() async throws { + guard let tailscale else { + throw TailscaleError.badInterfaceHandle + } + + logger?.log("Bringing Tailscale up :\(tailscale)") + let res = tailscale_up(tailscale) + + guard res == 0 else { + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + logger?.log("Brought Tailscale up:\(tailscale)") + } + + /// Tears down the Tailscale server. + /// + /// @See tailscale_down in Tailscale.h + /// + /// @throws TailscaleError on failure + public func down() throws { + guard let tailscale else { + throw TailscaleError.badInterfaceHandle + } + logger?.log("Taking Tailscale down :\(tailscale)") + + let res = tailscale_up(tailscale) + + guard res == 0 else { + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + logger?.log("Took Tailscale down:\(tailscale)") + } + + /// Returns the addresses on the Tailscale server + /// + /// @See tailscale_getips in Tailscale.h + /// + /// @returns An ipV4 and ipV5 address tuple + /// @throws TailscaleError on failure + public func addrs() async throws -> IPAddresses { + guard let tailscale else { + throw TailscaleError.badInterfaceHandle + } + + let buf = UnsafeMutablePointer.allocate(capacity: 128) + defer { + buf.deallocate() + } + let res = tailscale_getips(tailscale, buf, 128) + + guard res == 0 else { + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + + let ipList = String(cString: buf) + return ipList.toIPPair() + } + + public struct LoopbackConfig: Sendable { + let address: String + let proxyCredential: String + let localAPIKey: String + } + + private var loopbackConfig: LoopbackConfig? + + /// Starts and returns the address and credentials of a SOCKS5 proxy which can also + /// be used to query the localAPI + public func loopback() throws -> LoopbackConfig { + guard let tailscale else { + throw TailscaleError.badInterfaceHandle + } + + if let loopbackConfig = loopbackConfig { + return loopbackConfig + } + + let addrBuf = UnsafeMutablePointer.allocate(capacity: 64) + let proxyCredBuf = UnsafeMutablePointer.allocate(capacity: 33) + let apiCredBuf = UnsafeMutablePointer.allocate(capacity: 33) + defer { + addrBuf.deallocate() + proxyCredBuf.deallocate() + apiCredBuf.deallocate() + } + + let res = tailscale_loopback(tailscale, addrBuf, 64, proxyCredBuf, apiCredBuf) + guard res == 0 else { + throw TailscaleError.fromPosixErrCode(res, tailscale.getErrorMessage()) + } + + loopbackConfig = LoopbackConfig(address: String(cString: addrBuf), + proxyCredential: String(cString: proxyCredBuf), + localAPIKey: String(cString: apiCredBuf)) + return loopbackConfig! + + } +} + +// MARK: - IP String list to IPAddresses tuple + +enum IPAddrType { + case v4 + case v6 + case none +} + +extension String { + // tailscale.go sends us the tailnetIPs as a comma separated list. This will + // turn them into an IPAddresses tuple + func toIPPair() -> IPAddresses { + let ips = self.split(separator: ",").map { String($0) } + var result: IPAddresses = (nil, nil) + for ip in ips { + let type = ip.tsNetIPAddrType() + switch type { + case .v4: + result.ip4 = ip + case .v6: + result.ip6 = ip + case .none: + break + } + } + return result + } + + + // This can be naive since the backend is only vending well + // formed IPs to us. + func tsNetIPAddrType() -> IPAddrType { + if self.contains(".") { + return .v4 + } else if self.contains(":") { + return .v6 + } + return .none + } +} + + diff --git a/swift/TailscaleKit/URLSession+Tailscale.swift b/swift/TailscaleKit/URLSession+Tailscale.swift new file mode 100644 index 0000000..825b61f --- /dev/null +++ b/swift/TailscaleKit/URLSession+Tailscale.swift @@ -0,0 +1,37 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +extension URLSessionConfiguration { + + /// Adds the a connectionProxyDictionary to a URLSessionConfiguration to + /// proxy all requests through the given TailscaleNode. + /// + /// This can also be use to make requests to LocalAPI + func proxyVia(_ node: TailscaleNode) async throws { + let proxyConfig = try await node.loopback() + + // The address is always v4 and it's always : + let parts = proxyConfig.address.split(separator: ":") + let addr = parts.first + let port = parts.last + guard parts.count == 2, let addr, let port else { + throw TailscaleError.invalidProxyAddress + } + + self.connectionProxyDictionary = [ + kCFProxyTypeKey: kCFProxyTypeSOCKS, + kCFProxyUsernameKey: "tsnet", + kCFProxyPasswordKey: proxyConfig.proxyCredential, + kCFNetworkProxiesSOCKSEnable: true, + kCFNetworkProxiesSOCKSProxy: addr, + kCFNetworkProxiesSOCKSPort: port + ] + } + + static func tailscaleSession(_ node: TailscaleNode) async throws -> URLSessionConfiguration { + let config = URLSessionConfiguration.default + try await config.proxyVia(node) + return config + } + +} diff --git a/swift/TailscaleKitTestHost/ContentView.swift b/swift/TailscaleKitTestHost/ContentView.swift new file mode 100644 index 0000000..6788c27 --- /dev/null +++ b/swift/TailscaleKitTestHost/ContentView.swift @@ -0,0 +1,15 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import SwiftUI + +/// We need a proper host process to run the unit tests +struct ContentView: View { + var body: some View { + VStack { + Text("Tailscale Test Host") + } + .padding() + } +} + diff --git a/swift/TailscaleKitTestHost/Info.plist b/swift/TailscaleKitTestHost/Info.plist new file mode 100644 index 0000000..6a6654d --- /dev/null +++ b/swift/TailscaleKitTestHost/Info.plist @@ -0,0 +1,11 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/swift/TailscaleKitTestHost/TailscaleKitTestHost.entitlements b/swift/TailscaleKitTestHost/TailscaleKitTestHost.entitlements new file mode 100644 index 0000000..40b639e --- /dev/null +++ b/swift/TailscaleKitTestHost/TailscaleKitTestHost.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/swift/TailscaleKitTestHost/TailscaleKitTestHostApp.swift b/swift/TailscaleKitTestHost/TailscaleKitTestHostApp.swift new file mode 100644 index 0000000..93f581b --- /dev/null +++ b/swift/TailscaleKitTestHost/TailscaleKitTestHostApp.swift @@ -0,0 +1,13 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import SwiftUI + +@main +struct TailscaleKitTestHostApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/swift/TailscaleKitXCTests/TailscaleKitTests.swift b/swift/TailscaleKitXCTests/TailscaleKitTests.swift new file mode 100644 index 0000000..bb3b7e1 --- /dev/null +++ b/swift/TailscaleKitXCTests/TailscaleKitTests.swift @@ -0,0 +1,199 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +import XCTest +@testable import TailscaleKit + +final class TailscaleKitTests: XCTestCase { + var controlURL: String = "" + + override func setUp() async throws { + if controlURL == "" { + var buf = [CChar](repeating:0, count: 1024) + let res = buf.withUnsafeMutableBufferPointer { ptr in + return run_control(ptr.baseAddress!, 1024) + } + controlURL = String(validatingCString: buf) ?? "" + guard !controlURL.isEmpty else { + throw TailscaleError.invalidControlURL + } + if res == 0 { + print("Started control with url \(controlURL)") + } + } + } + + override func tearDown() async throws { + stop_control() + } + + func testV4() async throws { + try await runConnectionTests(for: .v4) + } + + func testV6() async throws { + try await runConnectionTests(for: .v6) + } + + func runConnectionTests(for netType: IPAddrType) async throws { + let logger = BlackholeLogger() + + let want = "Hello Tailscale".data(using: .utf8)! + + do { + let ts1 = try TailscaleNode(config: mockConfig(), logger: logger) + try await ts1.up() + + let ts2 = try TailscaleNode(config: mockConfig(), logger: logger) + try await ts2.up() + + let ts1_addr = try await ts1.addrs() + let ts2_addr = try await ts2.addrs() + + print("ts1 addresses are \(ts1_addr)") + print("ts2_adddreses are \(ts2_addr)") + + let msgReceived = expectation(description: "ex") + let lisetnerUp = expectation(description: "lisetnerUp") + + var listenerAddr: String? + var writerAddr: String? + + switch netType { + case .v4: + listenerAddr = ts1_addr.ip4 + writerAddr = ts2_addr.ip4 + case .v6: + // barnstar: Validity of listener IPs is loadbearing. accept fails + // in the C code if you listen on an invalid addr. + listenerAddr = if let a = ts1_addr.ip6 { "[\(a)]"} else { nil } + writerAddr = if let a = ts2_addr.ip6 { "[\(a)]"} else { nil } + case .none: + XCTFail("Invalid IP Type") + } + + guard let ts1Handle = await ts1.tailscale, + let ts2Handle = await ts2.tailscale, + let listenerAddr else { + XCTFail() + return + } + + // Run a listener in a separate task, wait for the inbound + // connection and read the data + Task { + let listener = try await Listener(tailscale: ts1Handle, + proto: .tcp, + address: ":8081", + logger: logger) + lisetnerUp.fulfill() + let inbound = try await listener.accept() + await listener.close() + + // We can trust the backend here but this is slightly flaky since remoteAddress can be + // nil for legitimate reasons. + // let inboundIP = await inbound.remoteAddress + // XCTAssertEqual(inboundIP, writerAddr) + + let got = try await inbound.receiveMessage(timeout: 2) + print("got \(got)") + XCTAssert(got == want) + + msgReceived.fulfill() + } + + //Make sure somebody is listening + await fulfillment(of: [lisetnerUp], timeout: 5.0) + + let outgoing = try await OutgoingConnection(tailscale: ts2Handle, + to: "\(listenerAddr):8081", + proto: .tcp, + logger: logger) + try await outgoing.connect() + + print("sending \(want)") + try await outgoing.send(want) + + await fulfillment(of: [msgReceived], timeout: 5.0) + + print("closing conn") + await outgoing.close() + + try await ts1.down() + try await ts2.down() + } catch { + XCTFail("Init Failed: \(error)") + } + } + + /// The hostCount here is load bearing. Each mock host must have a unique + /// path and hostname. + var hostCount = 0 + func mockConfig() -> Configuration { + let temp = getDocumentDirectoryPath().absoluteString + "tailscale\(hostCount)" + hostCount += 1 + return Configuration( + hostName: "testHost-\(hostCount)", + path: temp, + authKey: nil, + controlURL: controlURL, + ephemeral: false) + } + + + func testProxy() async throws { + let config = mockConfig() + let logger = BlackholeLogger() + + do { + let ts1 = try TailscaleNode(config: config, logger: logger) + try await ts1.up() + + let sessionConfig = try await URLSessionConfiguration.tailscaleSession(ts1) + let session = URLSession(configuration: sessionConfig) + + let url = URL(string: "https://tailscale.com")! + let req = URLRequest(url: url) + let (data, _) = try await session.data(for: req) + + print("Got proxied data \(data.count)") + XCTAssert(data.count > 0) + } + } + + + func exampleProxiedTailnetRequest() async throws { + let logger = DefaultLogger() + + do { + let temp = getDocumentDirectoryPath().absoluteString + "tailscale\(hostCount)" + let authKey = "put-you-auth-key-key-here" + let config = Configuration(hostName: "TSNet-Test", + path: temp, + authKey: authKey, + controlURL: Configuration.defaultControlURL, + ephemeral: true) + + let ts1 = try TailscaleNode(config: config, logger: logger) + try await ts1.up() + + let sessionConfig = try await URLSessionConfiguration.tailscaleSession(ts1) + let session = URLSession(configuration: sessionConfig) + + // Replace this with the IP or fqdn of a service running on your tailnet + let url = URL(string: "https://myservice.my-tailnet.ts.net")! + let req = URLRequest(url: url) + let (data, _) = try await session.data(for: req) + + print("Got proxied data \(data.count)") + XCTAssert(data.count > 0) + } + } +} + + +func getDocumentDirectoryPath() -> URL { + let arrayPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + let docDirectoryPath = arrayPaths[0] + return docDirectoryPath +} diff --git a/swift/TailscaleKitXCTests/Test-Bridging-Header.h b/swift/TailscaleKitXCTests/Test-Bridging-Header.h new file mode 100644 index 0000000..4fe4d9e --- /dev/null +++ b/swift/TailscaleKitXCTests/Test-Bridging-Header.h @@ -0,0 +1,15 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +#include + +// External definitions for libtstestcontrol + +// TODO: Is there away to avoid the header duplication here? +// WARNING: Adding/changing the libtstestcontrol functions must be replicated here + +// Runs a new control. Returns the URL in the buffer +extern int run_control(char* buf, size_t buflen); + +// Stops the running control +extern void stop_control(); diff --git a/tailscale.c b/tailscale.c index 7a39f50..0502fe7 100644 --- a/tailscale.c +++ b/tailscale.c @@ -19,6 +19,8 @@ extern int TsnetSetAuthKey(int sd, char* str); extern int TsnetSetControlURL(int sd, char* str); extern int TsnetSetEphemeral(int sd, int ephemeral); extern int TsnetSetLogFD(int sd, int fd); +extern int TsnetGetIps(int sd, char *buf, size_t buflen); +extern int TsnetGetRemoteAddr(int listener, int conn, char *buf, size_t buflen); extern int TsnetListen(int sd, char* net, char* addr, int* listenerOut); extern int TsnetLoopback(int sd, char* addrOut, size_t addrLen, char* proxyOut, char* localOut); @@ -70,6 +72,14 @@ int tailscale_accept(tailscale_listener ld, tailscale_conn* conn_out) { return 0; } +int tailscale_getremoteaddr(tailscale_listener l, tailscale_conn conn, char* buf, size_t buflen) { + return TsnetGetRemoteAddr(l, conn, buf, buflen); +} + +int tailscale_getips(tailscale sd, char* buf, size_t buflen) { + return TsnetGetIps(sd, buf, buflen); +} + int tailscale_set_dir(tailscale sd, const char* dir) { return TsnetSetDir(sd, (char*)dir); } diff --git a/tailscale.go b/tailscale.go index 7087ed0..68dca70 100644 --- a/tailscale.go +++ b/tailscale.go @@ -13,6 +13,8 @@ import ( "io" "net" "os" + "regexp" + "strings" "sync" "syscall" "unsafe" @@ -57,6 +59,8 @@ type listener struct { s *server ln net.Listener fd int // go side fd of socketpair sent to C + mu sync.Mutex + m map[C.int]net.Addr //maps fds to remote addresses for lookup } // conns tracks all the pipe(2)s allocated via tsnet_dial. @@ -141,6 +145,36 @@ func TsnetClose(sd C.int) C.int { return 0 } +//export TsnetGetIps +func TsnetGetIps(sd C.int, buf *C.char, buflen C.size_t) C.int { + if buf == nil { + panic("errmsg passed nil buf") + } else if buflen == 0 { + panic("errmsg passed buflen of 0") + } + + servers.mu.Lock() + s := servers.m[sd] + servers.mu.Unlock() + + out := unsafe.Slice((*byte)(unsafe.Pointer(buf)), buflen) + + if s == nil { + out[0] = '\x00' + return C.EBADF + } + + ip4, ip6 := s.s.TailscaleIPs() + joined := strings.Join([]string{ip4.String(), ip6.String()}, ",") + n := copy(out, joined) + if len(out) < len(joined)-1 { + out[len(out)-1] = '\x00' // always NUL-terminate + return C.ERANGE + } + out[n] = '\x00' + return 0 +} + //export TsnetErrmsg func TsnetErrmsg(sd C.int, buf *C.char, buflen C.size_t) C.int { if buf == nil { @@ -195,7 +229,8 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { if listeners.m == nil { listeners.m = map[C.int]*listener{} } - listeners.m[fdC] = &listener{s: s, ln: ln, fd: sp} + listener := &listener{s: s, ln: ln, fd: sp, m: map[C.int]net.Addr{}} + listeners.m[fdC] = listener listeners.mu.Unlock() cleanup := func() { @@ -246,6 +281,12 @@ func TsnetListen(sd C.int, network, addr *C.char, listenerOut *C.int) C.int { netConn.Close() // fallthrough to close connFd, then continue Accept()ing } + + // map the connection to the remote address + listener.mu.Lock() + listener.m[connFd] = netConn.RemoteAddr() + listener.mu.Unlock() + syscall.Close(int(connFd)) // now owned by recvmsg } }() @@ -309,6 +350,49 @@ func newConn(s *server, netConn net.Conn, connOut *C.int) error { return nil } +//export TsnetGetRemoteAddr +func TsnetGetRemoteAddr(listener C.int, conn C.int, buf *C.char, buflen C.size_t) C.int { + if buf == nil { + panic("errmsg passed nil buf") + } else if buflen == 0 { + panic("errmsg passed buflen of 0") + } + out := unsafe.Slice((*byte)(unsafe.Pointer(buf)), buflen) + + listeners.mu.Lock() + defer listeners.mu.Unlock() + l := listeners.m[listener] + if l == nil { + out[0] = '\x00' + return C.EBADF + } + + l.mu.Lock() + defer l.mu.Unlock() + addr, ok := l.m[conn] + if !ok { + out[0] = '\x00' + return C.EBADF + } + + ip := extractIP(addr.String()) + + n := copy(out, ip) + if len(out) < len(ip)-1 { + out[len(out)-1] = '\x00' // always NUL-terminate + return C.ERANGE + } + out[n] = '\x00' + return 0 +} + +// Strips the port from connection IPs +func extractIP(ipWithPort string) string { + re := regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|\[([0-9a-fA-F:]+)\]`) + match := re.FindString(ipWithPort) + return match +} + //export TsnetDial func TsnetDial(sd C.int, network, addr *C.char, connOut *C.int) C.int { s, err := getServer(sd) @@ -392,6 +476,7 @@ func TsnetSetLogFD(sd, fd C.int) C.int { f := os.NewFile(uintptr(fd), "logfd") s.s.Logf = func(format string, args ...any) { fmt.Fprintf(f, format, args...) + fmt.Fprintf(f, "\n") } return 0 } diff --git a/tailscale.h b/tailscale.h index 8265698..df08d7e 100644 --- a/tailscale.h +++ b/tailscale.h @@ -67,6 +67,7 @@ extern int tailscale_set_hostname(tailscale sd, const char* hostname); extern int tailscale_set_authkey(tailscale sd, const char* authkey); extern int tailscale_set_control_url(tailscale sd, const char* control_url); extern int tailscale_set_ephemeral(tailscale sd, int ephemeral); + // tailscale_set_logfd instructs the tailscale instance to write logs to fd. // // An fd value of -1 means discard all logging. @@ -80,6 +81,20 @@ extern int tailscale_set_logfd(tailscale sd, int fd); // For extra control over the connection, see the tailscale_conn_* functions. typedef int tailscale_conn; +// Returns the IP addresses of the the Tailscale server as +// a comma separated list. +// +// The provided buffer must be of sufficient size to hold the concatenated +// IPs as strings. This is typically , but maybe empty, or +// contain any number of ips. The caller is responsible for parsing +// the output. You may assume the output is a list of well-formed IPs. +// +// Returns: +// 0 - Success +// EBADF - sd is not a valid tailscale, or l or conn are not valid listeneras or connections +// ERANGE - insufficient storage for buf +extern int tailscale_getips(tailscale sd, char* buf, size_t buflen); + // tailscale_dial connects to the address on the tailnet. // // The newly allocated connection is written to conn_out. @@ -116,6 +131,15 @@ typedef int tailscale_listener; // Returns zero on success or -1 on error, call tailscale_errmsg for details. extern int tailscale_listen(tailscale sd, const char* network, const char* addr, tailscale_listener* listener_out); +// Returns the remote address for an incoming connection for a particular listener. The address (eitehr ip4 or ip6) +// will ge written to buf on on success. +// Returns: +// 0 - Success +// EBADF - sd is not a valid tailscale, or l or conn are not valid listeneras or connections +// ERANGE - insufficient storage for buf +extern int tailscale_getremoteaddr(tailscale_listener l, tailscale_conn conn, char* buf, size_t buflen); + + // tailscale_accept accepts a connection on a tailscale_listener. // // It is the spiritual equivalent to accept(2). diff --git a/tailscale_test.go b/tailscale_test.go index efa35d7..e79fed9 100644 --- a/tailscale_test.go +++ b/tailscale_test.go @@ -58,3 +58,22 @@ func TestConn(t *testing.T) { t.Errorf("want no remaining tsnet_listener objects, got %d", remLns) } } + +func TestExtractIP(t *testing.T) { + ipv4 := "1.23.33.4:12343" + ipv6 := "[1::2234::34fc::44]:56576" + + got4 := extractIP(ipv4) + got6 := extractIP(ipv6) + + want4 := "1.23.33.4" + want6 := "[1::2234::34fc::44]" + + if got4 != want4 { + t.Errorf("ipv4 port stripping failed") + } + + if got6 != want6 { + t.Errorf("ipv6 port stripping failed %s != %s", got6, want6) + } +} diff --git a/tstestcontrol/Makefile b/tstestcontrol/Makefile new file mode 100644 index 0000000..d0acb98 --- /dev/null +++ b/tstestcontrol/Makefile @@ -0,0 +1,10 @@ +# Copyright (c) Tailscale Inc & AUTHORS +# SPDX-License-Identifier: BSD-3-Clause + +all: + go build -buildmode=c-archive -o libtstestcontrol.a + +clean: + rm libtstestcontrol.a + rm libtstestcontrol.h + diff --git a/tstestcontrol/tstestcontrol.c b/tstestcontrol/tstestcontrol.c new file mode 100644 index 0000000..8d4451e --- /dev/null +++ b/tstestcontrol/tstestcontrol.c @@ -0,0 +1,23 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +#include "tstestcontrol.h" +#include + +// Functions exported by go +extern long long RunControl(char* buf, size_t buflen); +extern void StopControl(); + +// run_control starts an ephemeral control test server on localhost. +// buf must be a char* of sufficient size to hold the resulting URL +// stop_control must be called when you are finished with the instance +// +// returns -1 on failure, 0 on success +int run_control(char* buf, size_t buflen) { + return RunControl(buf, buflen); +} + +// stop_control() stops the e +void stop_control() { + StopControl(); +} \ No newline at end of file diff --git a/tstestcontrol/tstestcontrol.go b/tstestcontrol/tstestcontrol.go new file mode 100644 index 0000000..4da2d9a --- /dev/null +++ b/tstestcontrol/tstestcontrol.go @@ -0,0 +1,212 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// A Go c-archive of tsnet integration/control test utilities +// This mirrors athe functionality in tstest without the depenency +// on go tests so it can be bundled as a static library and used to drive +// integration tests on other platforms +package main + +import "C" + +//#include "errno.h" +import ( + "context" + "crypto/tls" + "errors" + "net" + "net/http" + "net/http/httptest" + "sync" + "unsafe" + + "tailscale.com/net/netaddr" + "tailscale.com/net/netns" + "tailscale.com/net/stun" + "tailscale.com/tstest/integration/testcontrol" + "tailscale.com/types/logger" + + "tailscale.com/derp" + "tailscale.com/derp/derphttp" + "tailscale.com/tailcfg" + "tailscale.com/types/key" + "tailscale.com/types/nettype" +) + +func main() {} + +//export StopControl +func StopControl() { + netns.SetEnabled(true) + if control != nil { + control.HTTPTestServer.Close() + } + cleanup() + control = nil +} + +var control *testcontrol.Server +var cleanup func() + +// TODO(barnstar): by purging this of the go testing dependency, we lost some logging and things fail silently. +// that needs to be plumbed back in. + +//export RunControl +func RunControl(buf *C.char, buflen C.size_t) C.int { + if control != nil { + return -1 + } + + if buf == nil { + return -1 + } else if buflen == 0 { + return -1 + } + out := unsafe.Slice((*byte)(unsafe.Pointer(buf)), buflen) + + // Corp#4520: don't use netns for tests. + netns.SetEnabled(false) + + derpLogf := logger.Discard + derpMap, c, err := runDERPAndSTUN(derpLogf, "127.0.0.1") + if err != nil { + out[0] = '\x00' + return -1 + } + + control := &testcontrol.Server{ + DERPMap: derpMap, + } + control.HTTPTestServer = httptest.NewUnstartedServer(control) + control.HTTPTestServer.Start() + controlURL := control.HTTPTestServer.URL + cleanup = c + + n := copy(out, controlURL) + out[n] = '\x00' + + return 0 +} + +// RunDERPAndSTUN runs a local DERP and STUN server for tests, returning the derpMap +// that clients should use. This creates resources that must be cleaned up with the +// returned cleanup function. +func runDERPAndSTUN(logf logger.Logf, ipAddress string) (derpMap *tailcfg.DERPMap, cleanup func(), err error) { + d := derp.NewServer(key.NewNode(), logf) + + ln, err := net.Listen("tcp", net.JoinHostPort(ipAddress, "0")) + if err != nil { + return nil, nil, err + } + + httpsrv := httptest.NewUnstartedServer(derphttp.Handler(d)) + httpsrv.Listener.Close() + httpsrv.Listener = ln + httpsrv.Config.ErrorLog = logger.StdLogger(logf) + httpsrv.Config.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) + httpsrv.StartTLS() + + stunAddr, stunCleanup, err := serveWithPacketListener(nettype.Std{}) + if err != nil { + return nil, nil, err + } + + m := &tailcfg.DERPMap{ + Regions: map[int]*tailcfg.DERPRegion{ + 1: { + RegionID: 1, + RegionCode: "test", + Nodes: []*tailcfg.DERPNode{ + { + Name: "t1", + RegionID: 1, + HostName: ipAddress, + IPv4: ipAddress, + IPv6: "none", + STUNPort: stunAddr.Port, + DERPPort: httpsrv.Listener.Addr().(*net.TCPAddr).Port, + InsecureForTests: true, + STUNTestIP: ipAddress, + }, + }, + }, + }, + } + + logf("DERP httpsrv listener: %v", httpsrv.Listener.Addr()) + + cleanupfn := func() { + httpsrv.CloseClientConnections() + httpsrv.Close() + d.Close() + stunCleanup() + ln.Close() + } + + return m, cleanupfn, nil +} + +type stunStats struct { + mu sync.Mutex + // +checklocks:mu + readIPv4 int + // +checklocks:mu + readIPv6 int +} + +func serveWithPacketListener(ln nettype.PacketListener) (addr *net.UDPAddr, cleanupFn func(), err error) { + // TODO(crawshaw): use stats to test re-STUN logic + var stats stunStats + + pc, err := ln.ListenPacket(context.Background(), "udp4", ":0") + if err != nil { + return nil, nil, err + } + addr = pc.LocalAddr().(*net.UDPAddr) + if len(addr.IP) == 0 || addr.IP.IsUnspecified() { + addr.IP = net.ParseIP("127.0.0.1") + } + doneCh := make(chan struct{}) + go runSTUN(pc.(nettype.PacketConn), &stats, doneCh) + return addr, func() { + pc.Close() + <-doneCh + }, nil +} + +func runSTUN(pc nettype.PacketConn, stats *stunStats, done chan<- struct{}) { + defer close(done) + + var buf [64 << 10]byte + for { + n, src, err := pc.ReadFromUDPAddrPort(buf[:]) + if err != nil { + if errors.Is(err, net.ErrClosed) { + return + } + continue + } + src = netaddr.Unmap(src) + pkt := buf[:n] + if !stun.Is(pkt) { + continue + } + txid, err := stun.ParseBindingRequest(pkt) + if err != nil { + continue + } + + stats.mu.Lock() + if src.Addr().Is4() { + stats.readIPv4++ + } else { + stats.readIPv6++ + } + stats.mu.Unlock() + + res := stun.Response(txid, src) + if _, err := pc.WriteToUDPAddrPort(res, src); err != nil { + // TODO(barnstar): inject logging from C + } + } +} diff --git a/tstestcontrol/tstestcontrol.h b/tstestcontrol/tstestcontrol.h new file mode 100644 index 0000000..3be87a0 --- /dev/null +++ b/tstestcontrol/tstestcontrol.h @@ -0,0 +1,26 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#ifndef TAILSCALE_H +#define TAILSCALE_H + +#ifdef __cplusplus +extern "C" { +#endif + +// External definitions for libtstestcontrol.h + +// Runs a new control. Returns the URL in the buffer +// returns 0 on success, an error code on failure +extern int run_control(char* buf, size_t buflen); + +// Stops the running control +extern void stop_control(); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file