mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-08-07 15:24:46 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7260f3e487 | |||
| a42b66e8bd | |||
| 28d556b8d4 | |||
| 33701f50ec | |||
| abfeebf67d | |||
| 3b293048f4 | |||
| ac3638f6b1 | |||
| f7ff41f1dc | |||
| f5c8ff77e9 | |||
| 87e06100df | |||
| 1ed9e0fceb | |||
| b9d6aaebbe | |||
| a99780ed9d | |||
| 92765b4261 | |||
| 8b88b4a403 | |||
| 2ce6720a6e | |||
| 772ccc9607 | |||
| 7724ff8176 | |||
| 2a3d486126 | |||
| 932e383051 | |||
| dbe3516204 | |||
| 6fc9f1b405 | |||
| 370c17e48c | |||
| 92da73aa9d | |||
| 0c65daaa7d | |||
| b46acd7f63 | |||
| e3a9aa4296 | |||
| 3886021ba5 | |||
| 4d3ebaf984 | |||
| 9131e842a5 | |||
| 4f9cfa6542 | |||
| a1a8645294 | |||
| b95b289a8c | |||
| 425554077f | |||
| 7b2f286210 | |||
| 0f893fab47 | |||
| 12c615e2e6 | |||
| 6e5ccd7c85 | |||
| 3ec500bdbb | |||
| 8f75feac94 | |||
| 448a7798f7 |
@@ -0,0 +1,8 @@
|
||||
images:
|
||||
- name: cloudflared
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
registries:
|
||||
- name: docker.io/cloudflare
|
||||
user: env:DOCKER_USER
|
||||
password: env:DOCKER_PASSWORD
|
||||
@@ -9,6 +9,7 @@ guide/public
|
||||
\#*\#
|
||||
cscope.*
|
||||
cloudflared
|
||||
cloudflared.pkg
|
||||
cloudflared.exe
|
||||
!cmd/cloudflared/
|
||||
.DS_Store
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# uninstall first in case this is an upgrade
|
||||
/usr/local/bin/cloudflared service uninstall
|
||||
|
||||
# install the new service using launchctl
|
||||
/usr/local/bin/cloudflared service install
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
/usr/local/bin/cloudflared service uninstall
|
||||
rm /usr/local/bin/cloudflared
|
||||
pkgutil --forget com.cloudflare.cloudflared
|
||||
Vendored
-6
@@ -2,12 +2,6 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if ! git describe --tags --exact-match 2>/dev/null ; then
|
||||
echo "Skipping public release for an untagged commit."
|
||||
echo "##teamcity[buildStatus status='SUCCESS' text='Skipped due to lack of tag']"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" != "Darwin" ]] ; then
|
||||
echo "This should be run on macOS"
|
||||
exit 1
|
||||
|
||||
Vendored
+1
-4
@@ -64,7 +64,4 @@ git config user.name "cloudflare-warp-bot"
|
||||
git config user.email "warp-bot@cloudflare.com"
|
||||
git commit -m "Release Argo Tunnel $VERSION"
|
||||
|
||||
#git push -v origin master
|
||||
# don't push to master, push to some testing branch
|
||||
git checkout -b deploy-test
|
||||
git push -v origin deploy-test
|
||||
git push -v origin master
|
||||
|
||||
@@ -4,7 +4,8 @@ VERSION_FLAGS := -ldflags='-X "main.Version=$(VERSION)" -X "main.BuildTime=$(DAT
|
||||
|
||||
IMPORT_PATH := github.com/cloudflare/cloudflared
|
||||
PACKAGE_DIR := $(CURDIR)/packaging
|
||||
INSTALL_BINDIR := usr/local/bin
|
||||
INSTALL_BINDIR := /usr/bin/
|
||||
MAN_DIR := /usr/share/man/man1/
|
||||
|
||||
EQUINOX_FLAGS = --version="$(VERSION)" \
|
||||
--platforms="$(EQUINOX_BUILD_PLATFORMS)" \
|
||||
@@ -17,7 +18,9 @@ ifeq ($(EQUINOX_IS_DRAFT), true)
|
||||
endif
|
||||
|
||||
LOCAL_ARCH ?= $(shell uname -m)
|
||||
ifeq ($(LOCAL_ARCH),x86_64)
|
||||
ifneq ($(GOARCH),)
|
||||
TARGET_ARCH ?= $(GOARCH)
|
||||
else ifeq ($(LOCAL_ARCH),x86_64)
|
||||
TARGET_ARCH ?= amd64
|
||||
else ifeq ($(shell echo $(LOCAL_ARCH) | head -c 5),armv8)
|
||||
TARGET_ARCH ?= arm64
|
||||
@@ -40,6 +43,20 @@ else
|
||||
$(error This system's OS $(LOCAL_OS) isn't supported)
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_OS), windows)
|
||||
EXECUTABLE_PATH=./cloudflared.exe
|
||||
else
|
||||
EXECUTABLE_PATH=./cloudflared
|
||||
endif
|
||||
|
||||
ifeq ($(FLAVOR), centos-7)
|
||||
TARGET_PUBLIC_REPO ?= el7
|
||||
else ifeq ($(FLAVOR), centos-6)
|
||||
TARGET_PUBLIC_REPO ?= el6
|
||||
else
|
||||
TARGET_PUBLIC_REPO ?= $(FLAVOR)
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: cloudflared test
|
||||
|
||||
@@ -63,12 +80,42 @@ test: vet
|
||||
test-ssh-server:
|
||||
docker-compose -f ssh_server_tests/docker-compose.yml up
|
||||
|
||||
.PHONY: cloudflared-deb
|
||||
cloudflared-deb: cloudflared
|
||||
define publish_package
|
||||
for HOST in $(CF_PKG_HOSTS); do \
|
||||
ssh-keyscan -t rsa $$HOST >> ~/.ssh/known_hosts; \
|
||||
scp -4 cloudflared_$(VERSION)_amd64.deb cfsync@$$HOST:/state/cf-pkg/staging/$(1)/$(TARGET_PUBLIC_REPO)/cloudflared/; \
|
||||
done
|
||||
endef
|
||||
|
||||
.PHONY: publish-deb
|
||||
publish-deb: cloudflared-deb
|
||||
$(call publish_package,apt)
|
||||
|
||||
.PHONY: publish-rpm
|
||||
publish-rpm: cloudflared-rpm
|
||||
$(call publish_package,yum)
|
||||
|
||||
define build_package
|
||||
mkdir -p $(PACKAGE_DIR)
|
||||
cp cloudflared $(PACKAGE_DIR)/cloudflared
|
||||
fakeroot fpm -C $(PACKAGE_DIR) -s dir -t deb --deb-compression bzip2 \
|
||||
-a $(TARGET_ARCH) -v $(VERSION) -n cloudflared cloudflared=/usr/local/bin/
|
||||
cat cloudflared_man_template | sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' > $(PACKAGE_DIR)/cloudflared.1
|
||||
fakeroot fpm -C $(PACKAGE_DIR) -s dir -t $(1) --$(1)-compression bzip2 \
|
||||
--description 'Cloudflare Argo tunnel daemon' \
|
||||
--vendor 'Cloudflare' \
|
||||
--license 'Cloudflare Service Agreement' \
|
||||
--url 'https://github.com/cloudflare/cloudflared' \
|
||||
-m 'Cloudflare <support@cloudflare.com>' \
|
||||
-a $(TARGET_ARCH) -v $(VERSION) -n cloudflared --after-install postinst.sh --after-remove postrm.sh \
|
||||
cloudflared=$(INSTALL_BINDIR) cloudflared.1=$(MAN_DIR)
|
||||
endef
|
||||
|
||||
.PHONY: cloudflared-deb
|
||||
cloudflared-deb: cloudflared
|
||||
$(call build_package,deb)
|
||||
|
||||
.PHONY: cloudflared-rpm
|
||||
cloudflared-rpm: cloudflared
|
||||
$(call build_package,rpm)
|
||||
|
||||
.PHONY: cloudflared-darwin-amd64.tgz
|
||||
cloudflared-darwin-amd64.tgz: cloudflared
|
||||
@@ -88,6 +135,10 @@ homebrew-release: homebrew-upload
|
||||
release: bin/equinox
|
||||
bin/equinox release $(EQUINOX_FLAGS) -- $(VERSION_FLAGS) $(IMPORT_PATH)/cmd/cloudflared
|
||||
|
||||
.PHONY: github-release
|
||||
github-release: cloudflared
|
||||
python3 github_release.py --path $(EXECUTABLE_PATH) --release-version $(VERSION)
|
||||
|
||||
bin/equinox:
|
||||
mkdir -p bin
|
||||
curl -s https://bin.equinox.io/c/75JtLRTsJ3n/release-tool-beta-$(EQUINOX_PLATFORM).tgz | tar xz -C bin/
|
||||
|
||||
@@ -1,3 +1,49 @@
|
||||
2020.7.2
|
||||
- 2020-07-02 AUTH-2644: Change install location and add man page
|
||||
- 2020-07-02 TUN-3131: Allow user to specify tunnel credentials path, and remove it in tunnel delete command
|
||||
- 2020-07-03 TUN-3008: Implement cloudflared tunnel cleanup command
|
||||
- 2020-07-02 TUN-3150: cloudflared tunnel list's table should use intelligent column width
|
||||
- 2020-07-07 TUN-3169: Move on to the next address when edge returns duplicate connection. There's no point in trying to connect to the same address since it will be hashed to the same metal. Improve logging of errors from serve tunnel loop, hide useless context cancelled error.
|
||||
- 2020-07-08 beautify package meta information generated by fpm (#218)
|
||||
- 2020-07-06 AUTH-2871: fix rpm builds
|
||||
- 2020-07-08 AUTH-2858: Set file to disable autoupdate
|
||||
- 2020-07-09 AUTH-2872: Adds centos-6 build
|
||||
|
||||
2020.7.1
|
||||
- 2020-07-02 DEVTOOLS-7321: Push GitHub homebrew updates to master
|
||||
- 2020-07-06 TUN-3161: Upgrade golang.org/x/ deps
|
||||
- 2020-06-30 AUTH-2854: Create cloudflared RPMs
|
||||
- 2020-06-26 AUTH-2850 log config file path
|
||||
|
||||
2020.7.0
|
||||
- 2020-06-30 TUN-3140: Add timestamps to terminal log entries
|
||||
- 2020-06-30 AUTH-2860: Fix builds
|
||||
- 2020-06-25 TUN-3007: Implement named tunnel connection registration and unregistration.
|
||||
|
||||
2020.6.6
|
||||
- 2020-06-23 AUTH-2685: Adds script to create release
|
||||
- 2020-06-25 AUTH-2652: Update cloudflare repo
|
||||
- 2020-06-26 AUTH-2718: Add target for publishing deb to pkg.cloudflare repo
|
||||
- 2020-06-26 AUTH-2849 all log output to stderr
|
||||
- 2020-06-17 TUN-3106: Pass NamedTunnel config to StartServer
|
||||
- 2020-06-18 TUN-3107: UnregisterConnection shouldn't wrap nil error as RPC error
|
||||
- 2020-06-17 AUTH-2652: Adds .docker-images to push images to docker hub
|
||||
- 2020-06-18 AUTH-2712 mac package build script and better config file handling when started as a service
|
||||
|
||||
2020.6.5
|
||||
- 2020-06-16 DEVTOOLS-7321: Don't skip macOS builds based on tag
|
||||
- 2020-06-16 fix for a flaky test
|
||||
- 2020-06-16 AUTH-2815 flag check was wrong. stupid oversight
|
||||
- 2020-06-16 TUN-3101: Tunnel list command should only show non-deleted, by default
|
||||
- 2020-06-16 TUN-3066: Command line action for tunnel run
|
||||
- 2020-06-16 TUN-3100 make updater report the right text
|
||||
|
||||
2020.6.4
|
||||
- 2020-06-11 TUN-3085: Pass connection authentication information using TunnelAuth struct
|
||||
- 2020-06-15 TUN-3084: Generate and store tunnel_secret value during tunnel creation
|
||||
- 2020-06-16 AUTH-2815 add the log file to support the config.yaml file
|
||||
- 2020-06-02 TUN-3015: Add a new cap'n'proto RPC interface for connection registration as well as matching client and server implementations. The old interface extends the new one for backward compatibility.
|
||||
|
||||
2020.6.3
|
||||
- 2020-06-15 DEVTOOLS-7321: Add openssh-client pkg for missing ssh-keyscan
|
||||
- 2020-06-15 AUTH-2813 adds back a single file support a cloudflared log file
|
||||
|
||||
+131
@@ -22,6 +22,18 @@ stretch: &stretch
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make cloudflared-deb
|
||||
publish-deb:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- build-essential
|
||||
- fakeroot
|
||||
- rubygem-fpm
|
||||
- openssh-client
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make publish-deb
|
||||
release-linux-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
@@ -31,6 +43,18 @@ stretch: &stretch
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make release
|
||||
github-release-linux-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- build-essential
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
post-cache:
|
||||
- pip3 install pygithub
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make github-release
|
||||
release-linux-armv6:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
@@ -42,6 +66,20 @@ stretch: &stretch
|
||||
- export GOARCH=arm
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- make release
|
||||
github-release-linux-armv6:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- crossbuild-essential-armhf
|
||||
- gcc-arm-linux-gnueabihf
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
post-cache:
|
||||
- pip3 install pygithub
|
||||
- export GOOS=linux
|
||||
- export GOARCH=arm
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- make github-release
|
||||
release-linux-386:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
@@ -51,6 +89,18 @@ stretch: &stretch
|
||||
- export GOOS=linux
|
||||
- export GOARCH=386
|
||||
- make release
|
||||
github-release-linux-386:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- gcc-multilib
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
post-cache:
|
||||
- pip3 install pygithub
|
||||
- export GOOS=linux
|
||||
- export GOARCH=386
|
||||
- make github-release
|
||||
release-windows-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
@@ -61,6 +111,19 @@ stretch: &stretch
|
||||
- export GOARCH=amd64
|
||||
- export CC=x86_64-w64-mingw32-gcc
|
||||
- make release
|
||||
github-release-windows-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- gcc-mingw-w64
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
post-cache:
|
||||
- pip3 install pygithub
|
||||
- export GOOS=windows
|
||||
- export GOARCH=amd64
|
||||
- export CC=x86_64-w64-mingw32-gcc
|
||||
- make github-release
|
||||
release-windows-386:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
@@ -71,6 +134,19 @@ stretch: &stretch
|
||||
- export GOARCH=386
|
||||
- export CC=i686-w64-mingw32-gcc-win32
|
||||
- make release
|
||||
github-release-windows-386:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- gcc-mingw-w64
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
post-cache:
|
||||
- pip3 install pygithub
|
||||
- export GOOS=windows
|
||||
- export GOARCH=386
|
||||
- export CC=i686-w64-mingw32-gcc-win32
|
||||
- make github-release
|
||||
test:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
@@ -94,6 +170,61 @@ jessie: *stretch
|
||||
|
||||
buster: *stretch
|
||||
|
||||
centos-6:
|
||||
build-rpm:
|
||||
build_dir: *build_dir
|
||||
builddeps: &el6_builddeps
|
||||
- https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
|
||||
post-cache:
|
||||
- source /usr/local/rvm/scripts/rvm
|
||||
- rvm use 1.9
|
||||
- sudo yum install -y fakeroot
|
||||
- wget https://golang.org/dl/go1.12.9.linux-amd64.tar.gz -P /tmp/
|
||||
- sudo tar -C /usr/local -xzf /tmp/go1.12.9.linux-amd64.tar.gz
|
||||
- export PATH=$PATH:/usr/local/go/bin
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make cloudflared-rpm
|
||||
publish-rpm:
|
||||
build_dir: *build_dir
|
||||
builddeps: *el6_builddeps
|
||||
post-cache:
|
||||
- source /usr/local/rvm/scripts/rvm
|
||||
- rvm use 1.9
|
||||
- sudo yum install -y fakeroot
|
||||
- wget https://golang.org/dl/go1.12.9.linux-amd64.tar.gz -P /tmp/
|
||||
- sudo tar -C /usr/local -xzf /tmp/go1.12.9.linux-amd64.tar.gz
|
||||
- export PATH=$PATH:/usr/local/go/bin
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make publish-rpm
|
||||
|
||||
|
||||
centos-7:
|
||||
publish-rpm:
|
||||
build_dir: *build_dir
|
||||
builddeps: &el7_builddeps
|
||||
- https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
|
||||
post-cache:
|
||||
- sudo yum install -y fakeroot
|
||||
- wget https://golang.org/dl/go1.12.9.linux-amd64.tar.gz -P /tmp/
|
||||
- sudo tar -C /usr/local -xzf /tmp/go1.12.9.linux-amd64.tar.gz
|
||||
- export PATH=$PATH:/usr/local/go/bin
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make publish-rpm
|
||||
build-rpm:
|
||||
build_dir: *build_dir
|
||||
builddeps: *el7_builddeps
|
||||
post-cache:
|
||||
- sudo yum install -y fakeroot
|
||||
- wget https://golang.org/dl/go1.12.9.linux-amd64.tar.gz -P /tmp/
|
||||
- sudo tar -C /usr/local -xzf /tmp/go1.12.9.linux-amd64.tar.gz
|
||||
- export PATH=$PATH:/usr/local/go/bin
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- make cloudflared-rpm
|
||||
|
||||
# cfsetup compose
|
||||
default-stack: test_dbconnect
|
||||
test_dbconnect:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.\" Manpage for cloudflared.
|
||||
.TH man 1 ${DATE} "${VERSION}" "cloudflared man page"
|
||||
.SH NAME
|
||||
cloudflared \- creates a connection to the cloudflare edge network
|
||||
.SH DESCRIPTION
|
||||
cloudflared creates a persistent connection between a local service and the Cloudflare network. Once the daemon is running and the Tunnel has been configured, the local service can be locked down to only allow connections from Cloudflare.
|
||||
@@ -106,7 +106,7 @@ func ssh(c *cli.Context) error {
|
||||
wsConn := carrier.NewWSConnection(logger, false)
|
||||
|
||||
if c.NArg() > 0 || c.IsSet(sshURLFlag) {
|
||||
localForwarder, err := config.ValidateUrl(c)
|
||||
localForwarder, err := config.ValidateUrl(c, true)
|
||||
if err != nil {
|
||||
logger.Errorf("Error validating origin URL: %s", err)
|
||||
return errors.Wrap(err, "error validating origin URL")
|
||||
|
||||
@@ -4,25 +4,65 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/cloudflare/cloudflared/validation"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"gopkg.in/urfave/cli.v2/altsrc"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/validation"
|
||||
)
|
||||
|
||||
var (
|
||||
// File names from which we attempt to read configuration.
|
||||
// DefaultConfigFiles is the file names from which we attempt to read configuration.
|
||||
DefaultConfigFiles = []string{"config.yml", "config.yaml"}
|
||||
|
||||
// DefaultUnixConfigLocation is the primary location to find a config file
|
||||
DefaultUnixConfigLocation = "/usr/local/etc/cloudflared"
|
||||
|
||||
// DefaultUnixLogLocation is the primary location to find log files
|
||||
DefaultUnixLogLocation = "/var/log/cloudflared"
|
||||
|
||||
// Launchd doesn't set root env variables, so there is default
|
||||
// Windows default config dir was ~/cloudflare-warp in documentation; let's keep it compatible
|
||||
DefaultConfigDirs = []string{"~/.cloudflared", "~/.cloudflare-warp", "~/cloudflare-warp", "/usr/local/etc/cloudflared", "/etc/cloudflared"}
|
||||
DefaultConfigDirs = []string{"~/.cloudflared", "~/.cloudflare-warp", "~/cloudflare-warp", "/etc/cloudflared", DefaultUnixConfigLocation}
|
||||
)
|
||||
|
||||
const DefaultCredentialFile = "cert.pem"
|
||||
|
||||
// DefaultConfigDirectory returns the default directory of the config file
|
||||
func DefaultConfigDirectory() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
path := os.Getenv("CFDPATH")
|
||||
if path == "" {
|
||||
path = filepath.Join(os.Getenv("ProgramFiles(x86)"), "cloudflared")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) { //doesn't exist, so return an empty failure string
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
return DefaultUnixConfigLocation
|
||||
}
|
||||
|
||||
// DefaultLogDirectory returns the default directory for log files
|
||||
func DefaultLogDirectory() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return DefaultConfigDirectory()
|
||||
}
|
||||
return DefaultUnixLogLocation
|
||||
}
|
||||
|
||||
// DefaultConfigPath returns the default location of a config file
|
||||
func DefaultConfigPath() string {
|
||||
dir := DefaultConfigDirectory()
|
||||
if dir == "" {
|
||||
return DefaultConfigFiles[0]
|
||||
}
|
||||
return filepath.Join(dir, DefaultConfigFiles[0])
|
||||
}
|
||||
|
||||
// FileExists checks to see if a file exist at the provided path.
|
||||
func FileExists(path string) (bool, error) {
|
||||
f, err := os.Open(path)
|
||||
@@ -64,10 +104,43 @@ func FindDefaultConfigPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// FindOrCreateConfigPath returns the first path that contains a config file
|
||||
// or creates one in the primary default path if it doesn't exist
|
||||
func FindOrCreateConfigPath() string {
|
||||
path := FindDefaultConfigPath()
|
||||
|
||||
if path == "" {
|
||||
// create the default directory if it doesn't exist
|
||||
path = DefaultConfigPath()
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// write a new config file out
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
logDir := DefaultLogDirectory()
|
||||
os.MkdirAll(logDir, os.ModePerm) //try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs
|
||||
|
||||
c := Root{
|
||||
LogDirectory: logDir,
|
||||
}
|
||||
if err := yaml.NewEncoder(file).Encode(&c); err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// FindLogSettings gets the log directory and level from the config file
|
||||
func FindLogSettings() (string, string) {
|
||||
configPath := FindDefaultConfigPath()
|
||||
defaultDirectory := filepath.Dir(configPath)
|
||||
configPath := FindOrCreateConfigPath()
|
||||
defaultDirectory := DefaultLogDirectory()
|
||||
defaultLevel := "info"
|
||||
|
||||
file, err := os.Open(configPath)
|
||||
@@ -104,9 +177,9 @@ func ValidateUnixSocket(c *cli.Context) (string, error) {
|
||||
|
||||
// ValidateUrl will validate url flag correctness. It can be either from --url or argument
|
||||
// Notice ValidateUnixSocket, it will enforce --unix-socket is not used with --url or argument
|
||||
func ValidateUrl(c *cli.Context) (string, error) {
|
||||
func ValidateUrl(c *cli.Context, allowFromArgs bool) (string, error) {
|
||||
var url = c.String("url")
|
||||
if c.NArg() > 0 {
|
||||
if allowFromArgs && c.NArg() > 0 {
|
||||
if c.IsSet("url") {
|
||||
return "", errors.New("Specified origin urls using both --url and argument. Decide which one you want, I can only support one.")
|
||||
}
|
||||
|
||||
@@ -49,9 +49,6 @@ func TestConfigChanged(t *testing.T) {
|
||||
os.Remove(filePath)
|
||||
}()
|
||||
c := &Root{
|
||||
OrgKey: "abcd",
|
||||
ConfigType: "mytype",
|
||||
CheckinInterval: 1,
|
||||
Forwarders: []Forwarder{
|
||||
{
|
||||
URL: "test.daltoniam.com",
|
||||
|
||||
@@ -34,14 +34,11 @@ type DNSResolver struct {
|
||||
|
||||
// Root is the base options to configure the service
|
||||
type Root struct {
|
||||
OrgKey string `json:"org_key" yaml:"orgKey"`
|
||||
ConfigType string `json:"type"`
|
||||
LogDirectory string `json:"log_directory" yaml:"logDirectory,omitempty"`
|
||||
LogLevel string `json:"log_level" yaml:"logLevel"`
|
||||
CheckinInterval int `json:"checkin_interval" yaml:"checkinInterval"`
|
||||
Forwarders []Forwarder `json:"forwarders,omitempty"`
|
||||
Tunnels []Tunnel `json:"tunnels,omitempty"`
|
||||
Resolver DNSResolver `json:"resolver,omitempty"`
|
||||
LogDirectory string `json:"log_directory" yaml:"logDirectory,omitempty"`
|
||||
LogLevel string `json:"log_level" yaml:"logLevel,omitempty"`
|
||||
Forwarders []Forwarder `json:"forwarders,omitempty" yaml:"forwarders,omitempty"`
|
||||
Tunnels []Tunnel `json:"tunnels,omitempty" yaml:"tunnels,omitempty"`
|
||||
Resolver DNSResolver `json:"resolver,omitempty" yaml:"resolver,omitempty"`
|
||||
}
|
||||
|
||||
// Hash returns the computed values to see if the forwarder values change
|
||||
|
||||
@@ -129,7 +129,7 @@ func action(version string, shutdownC, graceShutdownC chan struct{}) cli.ActionF
|
||||
tags := make(map[string]string)
|
||||
tags["hostname"] = c.String("hostname")
|
||||
raven.SetTagsContext(tags)
|
||||
raven.CapturePanic(func() { err = tunnel.StartServer(c, version, shutdownC, graceShutdownC) }, nil)
|
||||
raven.CapturePanic(func() { err = tunnel.StartServer(c, version, shutdownC, graceShutdownC, nil) }, nil)
|
||||
exitCode := 0
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
@@ -182,12 +182,13 @@ func handleServiceMode(shutdownC chan struct{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
configPath := config.FindDefaultConfigPath()
|
||||
configPath := config.FindOrCreateConfigPath()
|
||||
configManager, err := config.NewFileManager(f, configPath, logger)
|
||||
if err != nil {
|
||||
logger.Errorf("Cannot setup config file for monitoring: %s", err)
|
||||
return err
|
||||
}
|
||||
logger.Infof("monitoring config file at: %s", configPath)
|
||||
|
||||
serviceCallback := func(t string, name string, err error) {
|
||||
if err != nil {
|
||||
|
||||
@@ -172,6 +172,8 @@ func Commands() []*cli.Command {
|
||||
subcommands = append(subcommands, buildCreateCommand())
|
||||
subcommands = append(subcommands, buildListCommand())
|
||||
subcommands = append(subcommands, buildDeleteCommand())
|
||||
subcommands = append(subcommands, buildRunCommand())
|
||||
subcommands = append(subcommands, buildCleanupCommand())
|
||||
|
||||
cmds = append(cmds, &cli.Command{
|
||||
Name: "tunnel",
|
||||
@@ -206,7 +208,7 @@ func Commands() []*cli.Command {
|
||||
}
|
||||
|
||||
func tunnel(c *cli.Context) error {
|
||||
return StartServer(c, version, shutdownC, graceShutdownC)
|
||||
return StartServer(c, version, shutdownC, graceShutdownC, nil)
|
||||
}
|
||||
|
||||
func Init(v string, s, g chan struct{}) {
|
||||
@@ -215,7 +217,12 @@ func Init(v string, s, g chan struct{}) {
|
||||
|
||||
func createLogger(c *cli.Context, isTransport bool) (logger.Service, error) {
|
||||
loggerOpts := []logger.Option{}
|
||||
logPath := c.String(logDirectoryFlag)
|
||||
|
||||
logPath := c.String("logfile")
|
||||
if logPath == "" {
|
||||
logPath = c.String(logDirectoryFlag)
|
||||
}
|
||||
|
||||
if logPath != "" {
|
||||
loggerOpts = append(loggerOpts, logger.DefaultFile(logPath))
|
||||
}
|
||||
@@ -232,7 +239,7 @@ func createLogger(c *cli.Context, isTransport bool) (logger.Service, error) {
|
||||
return logger.New(loggerOpts...)
|
||||
}
|
||||
|
||||
func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan struct{}) error {
|
||||
func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan struct{}, namedTunnel *origin.NamedTunnelConfig) error {
|
||||
logger, err := createLogger(c, false)
|
||||
if err != nil {
|
||||
return cliutil.PrintLoggerSetupError("error setting up logger", err)
|
||||
@@ -353,7 +360,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
|
||||
defer wg.Done()
|
||||
hello.StartHelloWorldServer(logger, helloListener, shutdownC)
|
||||
}()
|
||||
c.Set("url", "https://"+helloListener.Addr().String())
|
||||
forceSetFlag(c, "url", "https://"+helloListener.Addr().String())
|
||||
}
|
||||
|
||||
if c.IsSet(sshServerFlag) {
|
||||
@@ -403,7 +410,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
|
||||
close(shutdownC)
|
||||
}
|
||||
}()
|
||||
c.Set("url", "ssh://"+localServerAddress)
|
||||
forceSetFlag(c, "url", "ssh://"+localServerAddress)
|
||||
}
|
||||
|
||||
url := c.String("url")
|
||||
@@ -447,7 +454,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
|
||||
}
|
||||
errC <- websocket.StartProxyServer(logger, listener, staticHost, shutdownC, streamHandler)
|
||||
}()
|
||||
c.Set("url", "http://"+listener.Addr().String())
|
||||
forceSetFlag(c, "url", "http://"+listener.Addr().String())
|
||||
}
|
||||
|
||||
transportLogger, err := createLogger(c, true)
|
||||
@@ -455,7 +462,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
|
||||
return errors.Wrap(err, "error setting up transport logger")
|
||||
}
|
||||
|
||||
tunnelConfig, err := prepareTunnelConfig(c, buildInfo, version, logger, transportLogger)
|
||||
tunnelConfig, err := prepareTunnelConfig(c, buildInfo, version, logger, transportLogger, namedTunnel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -475,6 +482,15 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
|
||||
return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"), logger)
|
||||
}
|
||||
|
||||
// forceSetFlag attempts to set the given flag value in the closest context that has it defined
|
||||
func forceSetFlag(c *cli.Context, name, value string) {
|
||||
for _, ctx := range c.Lineage() {
|
||||
if err := ctx.Set(name, value); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Before(c *cli.Context) error {
|
||||
logger, err := createLogger(c, false)
|
||||
if err != nil {
|
||||
@@ -833,12 +849,17 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
|
||||
Hidden: shouldHide,
|
||||
}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{
|
||||
Name: logDirectoryFlag,
|
||||
Aliases: []string{"logfile"}, // This flag used to be called logfile when it was a single file
|
||||
Usage: "Save application log to this directory for reporting issues.",
|
||||
Name: "logfile",
|
||||
Usage: "Save application log to this file for reporting issues.",
|
||||
EnvVars: []string{"TUNNEL_LOGFILE"},
|
||||
Hidden: shouldHide,
|
||||
}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{
|
||||
Name: logDirectoryFlag,
|
||||
Usage: "Save application log to this directory for reporting issues.",
|
||||
EnvVars: []string{"TUNNEL_LOGDIRECTORY"},
|
||||
Hidden: shouldHide,
|
||||
}),
|
||||
altsrc.NewIntFlag(&cli.IntFlag{
|
||||
Name: "ha-connections",
|
||||
Value: 4,
|
||||
@@ -958,13 +979,6 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
|
||||
EnvVars: []string{"TUNNEL_USE_RECONNECT_TOKEN"},
|
||||
Hidden: true,
|
||||
}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: "use-quick-reconnects",
|
||||
Usage: "Test reestablishing connections with the new 'connection digest' flow.",
|
||||
Value: true,
|
||||
EnvVars: []string{"TUNNEL_USE_QUICK_RECONNECTS"},
|
||||
Hidden: true,
|
||||
}),
|
||||
altsrc.NewDurationFlag(&cli.DurationFlag{
|
||||
Name: "dial-edge-timeout",
|
||||
Usage: "Maximum wait time to set up a connection with the edge",
|
||||
@@ -1082,8 +1096,8 @@ func stdinControl(reconnectCh chan origin.ReconnectSignal, logger logger.Service
|
||||
logger.Infof("Unknown command: %s", command)
|
||||
fallthrough
|
||||
case "help":
|
||||
logger.Info(`Supported command:
|
||||
reconnect [delay]
|
||||
logger.Info(`Supported command:
|
||||
reconnect [delay]
|
||||
- restarts one randomly chosen connection with optional delay before reconnect`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,10 @@ func prepareTunnelConfig(
|
||||
version string,
|
||||
logger logger.Service,
|
||||
transportLogger logger.Service,
|
||||
namedTunnel *origin.NamedTunnelConfig,
|
||||
) (*origin.TunnelConfig, error) {
|
||||
compatibilityMode := namedTunnel == nil
|
||||
|
||||
hostname, err := validation.ValidateHostname(c.String("hostname"))
|
||||
if err != nil {
|
||||
logger.Errorf("Invalid hostname: %s", err)
|
||||
@@ -181,7 +184,7 @@ func prepareTunnelConfig(
|
||||
|
||||
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID})
|
||||
|
||||
originURL, err := config.ValidateUrl(c)
|
||||
originURL, err := config.ValidateUrl(c, compatibilityMode)
|
||||
if err != nil {
|
||||
logger.Errorf("Error validating origin URL: %s", err)
|
||||
return nil, errors.Wrap(err, "Error validating origin URL")
|
||||
@@ -254,38 +257,52 @@ func prepareTunnelConfig(
|
||||
return nil, errors.Wrap(err, "unable to create TLS config to connect with edge")
|
||||
}
|
||||
|
||||
if namedTunnel != nil {
|
||||
clientUUID, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't generate clientUUID")
|
||||
}
|
||||
namedTunnel.Client = tunnelpogs.ClientInfo{
|
||||
ClientID: clientUUID[:],
|
||||
Features: []string{origin.FeatureSerializedHeaders},
|
||||
Version: version,
|
||||
Arch: fmt.Sprintf("%s_%s", buildInfo.GoOS, buildInfo.GoArch),
|
||||
}
|
||||
}
|
||||
|
||||
return &origin.TunnelConfig{
|
||||
BuildInfo: buildInfo,
|
||||
ClientID: clientID,
|
||||
ClientTlsConfig: httpTransport.TLSClientConfig,
|
||||
CompressionQuality: c.Uint64("compression-quality"),
|
||||
EdgeAddrs: c.StringSlice("edge"),
|
||||
GracePeriod: c.Duration("grace-period"),
|
||||
HAConnections: c.Int("ha-connections"),
|
||||
HTTPTransport: httpTransport,
|
||||
HeartbeatInterval: c.Duration("heartbeat-interval"),
|
||||
Hostname: hostname,
|
||||
HTTPHostHeader: c.String("http-host-header"),
|
||||
IncidentLookup: origin.NewIncidentLookup(),
|
||||
IsAutoupdated: c.Bool("is-autoupdated"),
|
||||
IsFreeTunnel: isFreeTunnel,
|
||||
LBPool: c.String("lb-pool"),
|
||||
Logger: logger,
|
||||
TransportLogger: transportLogger,
|
||||
MaxHeartbeats: c.Uint64("heartbeat-count"),
|
||||
Metrics: tunnelMetrics,
|
||||
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
|
||||
NoChunkedEncoding: c.Bool("no-chunked-encoding"),
|
||||
OriginCert: originCert,
|
||||
OriginUrl: originURL,
|
||||
ReportedVersion: version,
|
||||
Retries: c.Uint("retries"),
|
||||
RunFromTerminal: isRunningFromTerminal(),
|
||||
Tags: tags,
|
||||
TlsConfig: toEdgeTLSConfig,
|
||||
UseDeclarativeTunnel: c.Bool("use-declarative-tunnels"),
|
||||
UseReconnectToken: c.Bool("use-reconnect-token"),
|
||||
UseQuickReconnects: c.Bool("use-quick-reconnects"),
|
||||
BuildInfo: buildInfo,
|
||||
ClientID: clientID,
|
||||
ClientTlsConfig: httpTransport.TLSClientConfig,
|
||||
CompressionQuality: c.Uint64("compression-quality"),
|
||||
EdgeAddrs: c.StringSlice("edge"),
|
||||
GracePeriod: c.Duration("grace-period"),
|
||||
HAConnections: c.Int("ha-connections"),
|
||||
HTTPTransport: httpTransport,
|
||||
HeartbeatInterval: c.Duration("heartbeat-interval"),
|
||||
Hostname: hostname,
|
||||
HTTPHostHeader: c.String("http-host-header"),
|
||||
IncidentLookup: origin.NewIncidentLookup(),
|
||||
IsAutoupdated: c.Bool("is-autoupdated"),
|
||||
IsFreeTunnel: isFreeTunnel,
|
||||
LBPool: c.String("lb-pool"),
|
||||
Logger: logger,
|
||||
TransportLogger: transportLogger,
|
||||
MaxHeartbeats: c.Uint64("heartbeat-count"),
|
||||
Metrics: tunnelMetrics,
|
||||
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
|
||||
NoChunkedEncoding: c.Bool("no-chunked-encoding"),
|
||||
OriginCert: originCert,
|
||||
OriginUrl: originURL,
|
||||
ReportedVersion: version,
|
||||
Retries: c.Uint("retries"),
|
||||
RunFromTerminal: isRunningFromTerminal(),
|
||||
Tags: tags,
|
||||
TlsConfig: toEdgeTLSConfig,
|
||||
NamedTunnel: namedTunnel,
|
||||
ReplaceExisting: c.Bool("force"),
|
||||
// turn off use of reconnect token and auth refresh when using named tunnels
|
||||
UseReconnectToken: compatibilityMode && c.Bool("use-reconnect-token"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,60 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/certutil"
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
"github.com/cloudflare/cloudflared/origin"
|
||||
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
"github.com/cloudflare/cloudflared/tunnelstore"
|
||||
)
|
||||
|
||||
const (
|
||||
credFileFlagAlias = "cred-file"
|
||||
)
|
||||
|
||||
var (
|
||||
showDeletedFlag = &cli.BoolFlag{
|
||||
Name: "show-deleted",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Include deleted tunnels in the list",
|
||||
}
|
||||
outputFormatFlag = &cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "Render output using given `FORMAT`. Valid options are 'json' or 'yaml'",
|
||||
}
|
||||
forceFlag = &cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "By default, if a tunnel is currently being run from a cloudflared, you can't " +
|
||||
"simultaneously rerun it again from a second cloudflared. The --force flag lets you " +
|
||||
"overwrite the previous tunnel. If you want to use a single hostname with multiple " +
|
||||
"tunnels, you can do so with Cloudflare's Load Balancer product.",
|
||||
}
|
||||
credentialsFileFlag = &cli.StringFlag{
|
||||
Name: "credentials-file",
|
||||
Aliases: []string{credFileFlagAlias},
|
||||
Usage: "File path of tunnel credentials",
|
||||
}
|
||||
)
|
||||
|
||||
const hideSubcommands = true
|
||||
@@ -39,6 +70,13 @@ func buildCreateCommand() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
// generateTunnelSecret as an array of 32 bytes using secure random number generator
|
||||
func generateTunnelSecret() ([]byte, error) {
|
||||
randomBytes := make([]byte, 32)
|
||||
_, err := rand.Read(randomBytes)
|
||||
return randomBytes, err
|
||||
}
|
||||
|
||||
func createTunnel(c *cli.Context) error {
|
||||
if c.NArg() != 1 {
|
||||
return cliutil.UsageError(`"cloudflared tunnel create" requires exactly 1 argument, the name of tunnel to create.`)
|
||||
@@ -50,16 +88,36 @@ func createTunnel(c *cli.Context) error {
|
||||
return errors.Wrap(err, "error setting up logger")
|
||||
}
|
||||
|
||||
client, err := newTunnelstoreClient(c, logger)
|
||||
tunnelSecret, err := generateTunnelSecret()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tunnel, err := client.CreateTunnel(name)
|
||||
cert, originCertPath, err := getOriginCertFromContext(c, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := newTunnelstoreClient(c, cert, logger)
|
||||
|
||||
tunnel, err := client.CreateTunnel(name, tunnelSecret)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error creating a new tunnel")
|
||||
}
|
||||
|
||||
if writeFileErr := writeTunnelCredentials(tunnel.ID, cert.AccountID, originCertPath, tunnelSecret, logger); err != nil {
|
||||
var errorLines []string
|
||||
errorLines = append(errorLines, fmt.Sprintf("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write to the tunnel credentials file at %v.json.", tunnel.Name, tunnel.ID, tunnel.ID))
|
||||
errorLines = append(errorLines, fmt.Sprintf("The file-writing error is: %v", writeFileErr))
|
||||
if deleteErr := client.DeleteTunnel(tunnel.ID); deleteErr != nil {
|
||||
errorLines = append(errorLines, fmt.Sprintf("Cloudflared tried to delete the tunnel for you, but encountered an error. You should use `cloudflared tunnel delete %v` to delete the tunnel yourself, because the tunnel can't be run without the tunnelfile.", tunnel.ID))
|
||||
errorLines = append(errorLines, fmt.Sprintf("The delete tunnel error is: %v", deleteErr))
|
||||
} else {
|
||||
errorLines = append(errorLines, fmt.Sprintf("The tunnel was deleted, because the tunnel can't be run without the tunnelfile"))
|
||||
}
|
||||
errorMsg := strings.Join(errorLines, "\n")
|
||||
return errors.New(errorMsg)
|
||||
}
|
||||
|
||||
if outputFormat := c.String(outputFormatFlag.Name); outputFormat != "" {
|
||||
return renderOutput(outputFormat, &tunnel)
|
||||
}
|
||||
@@ -68,6 +126,83 @@ func createTunnel(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func tunnelFilePath(tunnelID, directory string) (string, error) {
|
||||
fileName := fmt.Sprintf("%v.json", tunnelID)
|
||||
filePath := filepath.Clean(fmt.Sprintf("%s/%s", directory, fileName))
|
||||
return homedir.Expand(filePath)
|
||||
}
|
||||
|
||||
func writeTunnelCredentials(tunnelID, accountID, originCertPath string, tunnelSecret []byte, logger logger.Service) error {
|
||||
originCertDir := filepath.Dir(originCertPath)
|
||||
filePath, err := tunnelFilePath(tunnelID, originCertDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := json.Marshal(pogs.TunnelAuth{
|
||||
AccountTag: accountID,
|
||||
TunnelSecret: tunnelSecret,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to marshal tunnel credentials to JSON")
|
||||
}
|
||||
logger.Infof("Writing tunnel credentials to %v. cloudflared chose this file based on where your origin certificate was found.", filePath)
|
||||
logger.Infof("Keep this file secret. To revoke these credentials, delete the tunnel.")
|
||||
return ioutil.WriteFile(filePath, body, 400)
|
||||
}
|
||||
|
||||
func readTunnelCredentials(c *cli.Context, tunnelID string, logger logger.Service) (*pogs.TunnelAuth, error) {
|
||||
filePath, err := tunnelCredentialsPath(c, tunnelID, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "couldn't read tunnel credentials from %v", filePath)
|
||||
}
|
||||
|
||||
var auth pogs.TunnelAuth
|
||||
if err = json.Unmarshal(body, &auth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &auth, nil
|
||||
}
|
||||
|
||||
func tunnelCredentialsPath(c *cli.Context, tunnelID string, logger logger.Service) (string, error) {
|
||||
if filePath := c.String("credentials-file"); filePath != "" {
|
||||
if validFilePath(filePath) {
|
||||
return filePath, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to look for tunnel credentials in the origin cert directory
|
||||
if originCertPath, err := findOriginCert(c, logger); err == nil {
|
||||
originCertDir := filepath.Dir(originCertPath)
|
||||
if filePath, err := tunnelFilePath(tunnelID, originCertDir); err == nil {
|
||||
if validFilePath(filePath) {
|
||||
return filePath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort look under default config directories
|
||||
for _, configDir := range config.DefaultConfigDirs {
|
||||
if filePath, err := tunnelFilePath(tunnelID, configDir); err == nil {
|
||||
if validFilePath(filePath) {
|
||||
return filePath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Tunnel credentials file not found")
|
||||
}
|
||||
|
||||
func validFilePath(path string) bool {
|
||||
fileStat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !fileStat.IsDir()
|
||||
}
|
||||
|
||||
func buildListCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "list",
|
||||
@@ -75,7 +210,7 @@ func buildListCommand() *cli.Command {
|
||||
Usage: "List existing tunnels",
|
||||
ArgsUsage: " ",
|
||||
Hidden: hideSubcommands,
|
||||
Flags: []cli.Flag{outputFormatFlag},
|
||||
Flags: []cli.Flag{outputFormatFlag, showDeletedFlag},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,25 +220,34 @@ func listTunnels(c *cli.Context) error {
|
||||
return errors.Wrap(err, "error setting up logger")
|
||||
}
|
||||
|
||||
client, err := newTunnelstoreClient(c, logger)
|
||||
cert, _, err := getOriginCertFromContext(c, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := newTunnelstoreClient(c, cert, logger)
|
||||
|
||||
tunnels, err := client.ListTunnels()
|
||||
allTunnels, err := client.ListTunnels()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error listing tunnels")
|
||||
}
|
||||
|
||||
var tunnels []tunnelstore.Tunnel
|
||||
if c.Bool("show-deleted") {
|
||||
tunnels = allTunnels
|
||||
} else {
|
||||
for _, tunnel := range allTunnels {
|
||||
if tunnel.DeletedAt.IsZero() {
|
||||
tunnels = append(tunnels, tunnel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if outputFormat := c.String(outputFormatFlag.Name); outputFormat != "" {
|
||||
return renderOutput(outputFormat, tunnels)
|
||||
}
|
||||
|
||||
if len(tunnels) > 0 {
|
||||
const listFormat = "%-40s%-30s%-30s%s\n"
|
||||
fmt.Printf(listFormat, "ID", "NAME", "CREATED", "CONNECTIONS")
|
||||
for _, t := range tunnels {
|
||||
fmt.Printf(listFormat, t.ID, t.Name, t.CreatedAt.Format(time.RFC3339), fmtConnections(t.Connections))
|
||||
}
|
||||
fmtAndPrintTunnelList(tunnels)
|
||||
} else {
|
||||
fmt.Println("You have no tunnels, use 'cloudflared tunnel create' to define a new tunnel")
|
||||
}
|
||||
@@ -111,6 +255,30 @@ func listTunnels(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fmtAndPrintTunnelList(tunnels []tunnelstore.Tunnel) {
|
||||
const (
|
||||
minWidth = 0
|
||||
tabWidth = 8
|
||||
padding = 1
|
||||
padChar = ' '
|
||||
flags = 0
|
||||
)
|
||||
|
||||
writer := tabwriter.NewWriter(os.Stdout, minWidth, tabWidth, padding, padChar, flags)
|
||||
|
||||
// Print column headers with tabbed columns
|
||||
fmt.Fprintln(writer, "ID\tNAME\tCREATED\tCONNECTIONS\t")
|
||||
|
||||
// Loop through tunnels, create formatted string for each, and print using tabwriter
|
||||
for _, t := range tunnels {
|
||||
formattedStr := fmt.Sprintf("%s\t%s\t%s\t%s\t", t.ID, t.Name, t.CreatedAt.Format(time.RFC3339), fmtConnections(t.Connections))
|
||||
fmt.Fprintln(writer, formattedStr)
|
||||
}
|
||||
|
||||
// Write data buffered in tabwriter to output
|
||||
writer.Flush()
|
||||
}
|
||||
|
||||
func fmtConnections(connections []tunnelstore.Connection) string {
|
||||
|
||||
// Count connections per colo
|
||||
@@ -141,6 +309,7 @@ func buildDeleteCommand() *cli.Command {
|
||||
Usage: "Delete existing tunnel with given ID",
|
||||
ArgsUsage: "TUNNEL-ID",
|
||||
Hidden: hideSubcommands,
|
||||
Flags: []cli.Flag{credentialsFileFlag},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,15 +324,26 @@ func deleteTunnel(c *cli.Context) error {
|
||||
return errors.Wrap(err, "error setting up logger")
|
||||
}
|
||||
|
||||
client, err := newTunnelstoreClient(c, logger)
|
||||
cert, _, err := getOriginCertFromContext(c, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := newTunnelstoreClient(c, cert, logger)
|
||||
|
||||
if err := client.DeleteTunnel(id); err != nil {
|
||||
return errors.Wrapf(err, "Error deleting tunnel %s", id)
|
||||
}
|
||||
|
||||
tunnelCredentialsPath, err := tunnelCredentialsPath(c, id, logger)
|
||||
if err != nil {
|
||||
logger.Infof("Cannot locate tunnel credentials to delete, error: %v. Please delete the file manually", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = os.Remove(tunnelCredentialsPath); err != nil {
|
||||
logger.Infof("Cannot delete tunnel credentials, error: %v. Please delete the file manually", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -180,27 +360,99 @@ func renderOutput(format string, v interface{}) error {
|
||||
}
|
||||
}
|
||||
|
||||
func newTunnelstoreClient(c *cli.Context, logger logger.Service) (tunnelstore.Client, error) {
|
||||
originCertPath, err := findOriginCert(c, logger)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error locating origin cert")
|
||||
}
|
||||
func newTunnelstoreClient(c *cli.Context, cert *certutil.OriginCert, logger logger.Service) tunnelstore.Client {
|
||||
client := tunnelstore.NewRESTClient(c.String("api-url"), cert.AccountID, cert.ServiceKey, logger)
|
||||
return client
|
||||
}
|
||||
|
||||
func getOriginCertFromContext(c *cli.Context, logger logger.Service) (cert *certutil.OriginCert, originCertPath string, err error) {
|
||||
originCertPath, err = findOriginCert(c, logger)
|
||||
if err != nil {
|
||||
return nil, "", errors.Wrap(err, "Error locating origin cert")
|
||||
}
|
||||
blocks, err := readOriginCert(originCertPath, logger)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Can't read origin cert from %s", originCertPath)
|
||||
return nil, "", errors.Wrapf(err, "Can't read origin cert from %s", originCertPath)
|
||||
}
|
||||
|
||||
cert, err := certutil.DecodeOriginCert(blocks)
|
||||
cert, err = certutil.DecodeOriginCert(blocks)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error decoding origin cert")
|
||||
return nil, "", errors.Wrap(err, "Error decoding origin cert")
|
||||
}
|
||||
|
||||
if cert.AccountID == "" {
|
||||
return nil, errors.Errorf(`Origin certificate needs to be refreshed before creating new tunnels.\nDelete %s and run "cloudflared login" to obtain a new cert.`, originCertPath)
|
||||
return nil, "", errors.Errorf(`Origin certificate needs to be refreshed before creating new tunnels.\nDelete %s and run "cloudflared login" to obtain a new cert.`, originCertPath)
|
||||
}
|
||||
return cert, originCertPath, nil
|
||||
}
|
||||
|
||||
func buildRunCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "run",
|
||||
Action: cliutil.ErrorHandler(runTunnel),
|
||||
Usage: "Proxy a local web server by running the given tunnel",
|
||||
ArgsUsage: "TUNNEL-ID",
|
||||
Hidden: hideSubcommands,
|
||||
Flags: []cli.Flag{forceFlag, credentialsFileFlag},
|
||||
}
|
||||
}
|
||||
|
||||
func runTunnel(c *cli.Context) error {
|
||||
if c.NArg() != 1 {
|
||||
return cliutil.UsageError(`"cloudflared tunnel run" requires exactly 1 argument, the ID of the tunnel to run.`)
|
||||
}
|
||||
id := c.Args().First()
|
||||
tunnelID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error parsing tunnel ID")
|
||||
}
|
||||
|
||||
client := tunnelstore.NewRESTClient(c.String("api-url"), cert.AccountID, cert.ServiceKey, logger)
|
||||
logger, err := logger.New()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error setting up logger")
|
||||
}
|
||||
|
||||
return client, nil
|
||||
credentials, err := readTunnelCredentials(c, id, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Debugf("Read credentials for %v", credentials.AccountTag)
|
||||
return StartServer(c, version, shutdownC, graceShutdownC, &origin.NamedTunnelConfig{Auth: *credentials, ID: tunnelID})
|
||||
}
|
||||
|
||||
func buildCleanupCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "cleanup",
|
||||
Action: cliutil.ErrorHandler(cleanupConnections),
|
||||
Usage: "Cleanup connections for the tunnel with given IDs",
|
||||
ArgsUsage: "TUNNEL-IDS",
|
||||
Hidden: hideSubcommands,
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupConnections(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return cliutil.UsageError(`"cloudflared tunnel cleanup" requires at least 1 argument, the IDs of the tunnels to cleanup connections.`)
|
||||
}
|
||||
|
||||
logger, err := logger.New()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error setting up logger")
|
||||
}
|
||||
|
||||
cert, _, err := getOriginCertFromContext(c, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := newTunnelstoreClient(c, cert, logger)
|
||||
|
||||
for i := 0; i < c.NArg(); i++ {
|
||||
id := c.Args().Get(i)
|
||||
logger.Infof("Cleanup connection for tunnel %s", id)
|
||||
if err := client.CleanupConnections(id); err != nil {
|
||||
logger.Errorf("Error cleaning up connections for tunnel %s, error :%v", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudflare/cloudflared/tunnelstore"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_fmtConnections(t *testing.T) {
|
||||
@@ -69,3 +73,13 @@ func Test_fmtConnections(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnelfilePath(t *testing.T) {
|
||||
originCertDir := filepath.Dir("~/.cloudflared/cert.pem")
|
||||
actual, err := tunnelFilePath("tunnel", originCertDir)
|
||||
assert.NoError(t, err)
|
||||
homeDir, err := homedir.Dir()
|
||||
assert.NoError(t, err)
|
||||
expected := fmt.Sprintf("%s/.cloudflared/tunnel.json", homeDir)
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
"github.com/equinox-io/equinox"
|
||||
"github.com/facebookgo/grace/gracenet"
|
||||
@@ -17,10 +19,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultCheckUpdateFreq = time.Hour * 24
|
||||
appID = "app_idCzgxYerVD"
|
||||
noUpdateInShellMessage = "cloudflared will not automatically update when run from the shell. To enable auto-updates, run cloudflared as a service: https://developers.cloudflare.com/argo-tunnel/reference/service/"
|
||||
noUpdateOnWindowsMessage = "cloudflared will not automatically update on Windows systems."
|
||||
DefaultCheckUpdateFreq = time.Hour * 24
|
||||
appID = "app_idCzgxYerVD"
|
||||
noUpdateInShellMessage = "cloudflared will not automatically update when run from the shell. To enable auto-updates, run cloudflared as a service: https://developers.cloudflare.com/argo-tunnel/reference/service/"
|
||||
noUpdateOnWindowsMessage = "cloudflared will not automatically update on Windows systems."
|
||||
noUpdateManagedPackageMessage = "cloudflared will not automatically update if installed by a package manager."
|
||||
isManagedInstallFile = ".installedFromPackageManager"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -67,7 +71,7 @@ type UpdateOutcome struct {
|
||||
}
|
||||
|
||||
func (uo *UpdateOutcome) noUpdate() bool {
|
||||
return uo.Error != nil && uo.Updated == false
|
||||
return uo.Error == nil && uo.Updated == false
|
||||
}
|
||||
|
||||
func checkForUpdateAndApply() UpdateOutcome {
|
||||
@@ -98,6 +102,11 @@ func Update(_ *cli.Context) error {
|
||||
return errors.Wrap(err, "error setting up logger")
|
||||
}
|
||||
|
||||
if wasInstalledFromPackageManager() {
|
||||
logger.Error("cloudflared was installed by a package manager. Please update using the same method.")
|
||||
return nil
|
||||
}
|
||||
|
||||
updateOutcome := loggedUpdate(logger)
|
||||
if updateOutcome.Error != nil {
|
||||
return &statusErr{updateOutcome.Error}
|
||||
@@ -217,6 +226,11 @@ func SupportAutoUpdate(logger logger.Service) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if wasInstalledFromPackageManager() {
|
||||
logger.Info(noUpdateManagedPackageMessage)
|
||||
return false
|
||||
}
|
||||
|
||||
if isRunningFromTerminal() {
|
||||
logger.Info(noUpdateInShellMessage)
|
||||
return false
|
||||
@@ -224,6 +238,11 @@ func SupportAutoUpdate(logger logger.Service) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func wasInstalledFromPackageManager() bool {
|
||||
ok, _ := config.FileExists(filepath.Join(config.DefaultUnixConfigLocation, isManagedInstallFile))
|
||||
return ok
|
||||
}
|
||||
|
||||
func isRunningFromTerminal() bool {
|
||||
return terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/python3
|
||||
"""
|
||||
Creates Github Releases and uploads assets
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from github import Github, GithubException, UnknownObjectException
|
||||
|
||||
FORMAT = "%(levelname)s - %(asctime)s: %(message)s"
|
||||
logging.basicConfig(format=FORMAT)
|
||||
|
||||
CLOUDFLARED_REPO = os.environ.get("GITHUB_REPO", "cloudflare/cloudflared")
|
||||
GITHUB_CONFLICT_CODE = "already_exists"
|
||||
|
||||
|
||||
def assert_tag_exists(repo, version):
|
||||
""" Raise exception if repo does not contain a tag matching version """
|
||||
tags = repo.get_tags()
|
||||
if not tags or tags[0].name != version:
|
||||
raise Exception("Tag {} not found".format(version))
|
||||
|
||||
|
||||
def get_or_create_release(repo, version, dry_run=False):
|
||||
"""
|
||||
Get a Github Release matching the version tag or create a new one.
|
||||
If a conflict occurs on creation, attempt to fetch the Release on last time
|
||||
"""
|
||||
try:
|
||||
release = repo.get_release(version)
|
||||
logging.info("Release %s found", version)
|
||||
return release
|
||||
except UnknownObjectException:
|
||||
logging.info("Release %s not found", version)
|
||||
|
||||
# We dont want to create a new release tag if one doesnt already exist
|
||||
assert_tag_exists(repo, version)
|
||||
|
||||
if dry_run:
|
||||
logging.info("Skipping Release creation because of dry-run")
|
||||
return
|
||||
|
||||
try:
|
||||
logging.info("Creating release %s", version)
|
||||
return repo.create_git_release(version, version, "")
|
||||
except GithubException as e:
|
||||
errors = e.data.get("errors", [])
|
||||
if e.status == 422 and any(
|
||||
[err.get("code") == GITHUB_CONFLICT_CODE for err in errors]
|
||||
):
|
||||
logging.warning(
|
||||
"Conflict: Release was likely just made by a different build: %s",
|
||||
e.data,
|
||||
)
|
||||
return repo.get_release(version)
|
||||
raise e
|
||||
|
||||
|
||||
def parse_args():
|
||||
""" Parse and validate args """
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Creates Github Releases and uploads assets."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-key", default=os.environ.get("API_KEY"), help="Github API key"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--release-version",
|
||||
metavar="version",
|
||||
default=os.environ.get("VERSION"),
|
||||
help="Release version",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--path", default=os.environ.get("ASSET_PATH"), help="Asset path"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name", default=os.environ.get("ASSET_NAME"), help="Asset Name"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run", action="store_true", help="Do not create release or upload asset"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
is_valid = True
|
||||
if not args.release_version:
|
||||
logging.error("Missing release version")
|
||||
is_valid = False
|
||||
|
||||
if not args.path:
|
||||
logging.error("Missing asset path")
|
||||
is_valid = False
|
||||
|
||||
if not args.name:
|
||||
logging.error("Missing asset name")
|
||||
is_valid = False
|
||||
|
||||
if not args.api_key:
|
||||
logging.error("Missing API key")
|
||||
is_valid = False
|
||||
|
||||
if is_valid:
|
||||
return args
|
||||
|
||||
parser.print_usage()
|
||||
exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
""" Attempts to upload Asset to Github Release. Creates Release if it doesnt exist """
|
||||
try:
|
||||
args = parse_args()
|
||||
client = Github(args.api_key)
|
||||
repo = client.get_repo(CLOUDFLARED_REPO)
|
||||
release = get_or_create_release(repo, args.release_version, args.dry_run)
|
||||
|
||||
if args.dry_run:
|
||||
logging.info("Skipping asset upload because of dry-run")
|
||||
return
|
||||
|
||||
release.upload_asset(args.path, name=args.name)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
exit(1)
|
||||
|
||||
|
||||
main()
|
||||
@@ -54,15 +54,14 @@ require (
|
||||
github.com/prometheus/common v0.7.0 // indirect
|
||||
github.com/prometheus/procfs v0.0.5 // indirect
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tinylib/msgp v1.1.0 // indirect
|
||||
github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 // indirect
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
||||
golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
|
||||
google.golang.org/appengine v1.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd // indirect
|
||||
google.golang.org/grpc v1.24.0 // indirect
|
||||
|
||||
@@ -206,6 +206,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -224,6 +226,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
|
||||
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
@@ -245,6 +249,9 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9Y
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 h1:nq114VpM8lsSlP+lyUbANecYHYiFcSNFtqcBlxRV+gA=
|
||||
golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
|
||||
+4
-13
@@ -114,21 +114,12 @@ func New(opts ...Option) (Service, error) {
|
||||
}
|
||||
|
||||
if !config.terminalOutputDisabled {
|
||||
terminalFormatter := NewTerminalFormatter(time.RFC3339)
|
||||
|
||||
if len(config.supportedTerminalLevels) == 0 {
|
||||
l.Add(os.Stdout, NewTerminalFormatter(""), InfoLevel)
|
||||
l.Add(os.Stderr, NewTerminalFormatter(""), ErrorLevel, FatalLevel)
|
||||
l.Add(os.Stderr, terminalFormatter, InfoLevel, ErrorLevel, FatalLevel)
|
||||
} else {
|
||||
errLevels := []Level{}
|
||||
outLevels := []Level{}
|
||||
for _, level := range config.supportedTerminalLevels {
|
||||
if level == ErrorLevel || level == FatalLevel {
|
||||
errLevels = append(errLevels, level)
|
||||
} else {
|
||||
outLevels = append(outLevels, level)
|
||||
}
|
||||
}
|
||||
l.Add(os.Stdout, NewTerminalFormatter(""), outLevels...)
|
||||
l.Add(os.Stderr, NewTerminalFormatter(""), errLevels...)
|
||||
l.Add(os.Stderr, terminalFormatter, config.supportedTerminalLevels...)
|
||||
}
|
||||
}
|
||||
return l, nil
|
||||
|
||||
+6
-5
@@ -76,17 +76,18 @@ func NewTerminalFormatter(format string) Formatter {
|
||||
// Timestamp returns the log level with a matching color to the log type
|
||||
func (f *TerminalFormatter) Timestamp(l Level, d time.Time) string {
|
||||
t := ""
|
||||
dateStr := "[" + d.Format(f.format) + "] "
|
||||
switch l {
|
||||
case InfoLevel:
|
||||
t = f.output("[INFO] ", skittles.Cyan)
|
||||
t = f.output("INFO", skittles.Cyan)
|
||||
case ErrorLevel:
|
||||
t = f.output("[ERROR] ", skittles.Red)
|
||||
t = f.output("ERROR", skittles.Red)
|
||||
case DebugLevel:
|
||||
t = f.output("[DEBUG] ", skittles.Yellow)
|
||||
t = f.output("DEBUG", skittles.Yellow)
|
||||
case FatalLevel:
|
||||
t = f.output("[FATAL] ", skittles.Red)
|
||||
t = f.output("FATAL", skittles.Red)
|
||||
}
|
||||
return t
|
||||
return t + dateStr
|
||||
}
|
||||
|
||||
// Content just writes the log line straight to the sources
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
TARGET_DIRECTORY=".build"
|
||||
BINARY_NAME="cloudflared"
|
||||
VERSION=$(git describe --tags --always --dirty="-dev")
|
||||
PRODUCT="cloudflared"
|
||||
|
||||
|
||||
echo "building cloudflared"
|
||||
make cloudflared
|
||||
|
||||
echo "creating build directory"
|
||||
mkdir ${TARGET_DIRECTORY}
|
||||
mkdir ${TARGET_DIRECTORY}/contents
|
||||
cp -r .mac_resources/scripts ${TARGET_DIRECTORY}/scripts
|
||||
|
||||
echo "move cloudflared into the build directory"
|
||||
mv $BINARY_NAME {$TARGET_DIRECTORY}/contents/${PRODUCT}
|
||||
|
||||
echo "build the installer package"
|
||||
pkgbuild --identifier com.cloudflare.${PRODUCT} \
|
||||
--version ${VERSION} \
|
||||
--scripts ${TARGET_DIRECTORY}/scripts \
|
||||
--root ${TARGET_DIRECTORY}/contents \
|
||||
--install-location /usr/local/bin \
|
||||
${PRODUCT}.pkg
|
||||
# TODO: our iOS/Mac account doesn't have this installer certificate type.
|
||||
# need to find how we can get it --sign "Developer ID Installer: Cloudflare" \
|
||||
|
||||
echo "cleaning up the build directory"
|
||||
rm -rf $TARGET_DIRECTORY
|
||||
@@ -80,7 +80,7 @@ type tunnelError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func NewSupervisor(config *TunnelConfig, u uuid.UUID) (*Supervisor, error) {
|
||||
func NewSupervisor(config *TunnelConfig, cloudflaredUUID uuid.UUID) (*Supervisor, error) {
|
||||
var (
|
||||
edgeIPs *edgediscovery.Edge
|
||||
err error
|
||||
@@ -93,8 +93,9 @@ func NewSupervisor(config *TunnelConfig, u uuid.UUID) (*Supervisor, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Supervisor{
|
||||
cloudflaredUUID: u,
|
||||
cloudflaredUUID: cloudflaredUUID,
|
||||
config: config,
|
||||
edgeIPs: edgeIPs,
|
||||
tunnelErrors: make(chan tunnelError),
|
||||
@@ -226,17 +227,17 @@ func (s *Supervisor) startFirstTunnel(ctx context.Context, connectedSignal *sign
|
||||
addr *net.TCPAddr
|
||||
err error
|
||||
)
|
||||
const thisConnID = 0
|
||||
const firstConnIndex = 0
|
||||
defer func() {
|
||||
s.tunnelErrors <- tunnelError{index: thisConnID, addr: addr, err: err}
|
||||
s.tunnelErrors <- tunnelError{index: firstConnIndex, addr: addr, err: err}
|
||||
}()
|
||||
|
||||
addr, err = s.edgeIPs.GetAddr(thisConnID)
|
||||
addr, err = s.edgeIPs.GetAddr(firstConnIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = ServeTunnelLoop(ctx, s, s.config, addr, thisConnID, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
|
||||
err = ServeTunnelLoop(ctx, s, s.config, addr, firstConnIndex, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
|
||||
// If the first tunnel disconnects, keep restarting it.
|
||||
edgeErrors := 0
|
||||
for s.unusedIPs() {
|
||||
@@ -254,12 +255,12 @@ func (s *Supervisor) startFirstTunnel(ctx context.Context, connectedSignal *sign
|
||||
return
|
||||
}
|
||||
if edgeErrors >= 2 {
|
||||
addr, err = s.edgeIPs.GetDifferentAddr(thisConnID)
|
||||
addr, err = s.edgeIPs.GetDifferentAddr(firstConnIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = ServeTunnelLoop(ctx, s, s.config, addr, thisConnID, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
|
||||
err = ServeTunnelLoop(ctx, s, s.config, addr, firstConnIndex, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+175
-84
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
"github.com/cloudflare/cloudflared/signal"
|
||||
"github.com/cloudflare/cloudflared/tunnelrpc"
|
||||
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
"github.com/cloudflare/cloudflared/validation"
|
||||
"github.com/cloudflare/cloudflared/websocket"
|
||||
@@ -47,47 +48,46 @@ type registerRPCName string
|
||||
const (
|
||||
register registerRPCName = "register"
|
||||
reconnect registerRPCName = "reconnect"
|
||||
unknown registerRPCName = "unknown"
|
||||
)
|
||||
|
||||
type TunnelConfig struct {
|
||||
BuildInfo *buildinfo.BuildInfo
|
||||
ClientID string
|
||||
ClientTlsConfig *tls.Config
|
||||
CloseConnOnce *sync.Once // Used to close connectedSignal no more than once
|
||||
CompressionQuality uint64
|
||||
EdgeAddrs []string
|
||||
GracePeriod time.Duration
|
||||
HAConnections int
|
||||
HTTPTransport http.RoundTripper
|
||||
HeartbeatInterval time.Duration
|
||||
Hostname string
|
||||
HTTPHostHeader string
|
||||
IncidentLookup IncidentLookup
|
||||
IsAutoupdated bool
|
||||
IsFreeTunnel bool
|
||||
LBPool string
|
||||
Logger logger.Service
|
||||
TransportLogger logger.Service
|
||||
MaxHeartbeats uint64
|
||||
Metrics *TunnelMetrics
|
||||
MetricsUpdateFreq time.Duration
|
||||
NoChunkedEncoding bool
|
||||
OriginCert []byte
|
||||
ReportedVersion string
|
||||
Retries uint
|
||||
RunFromTerminal bool
|
||||
Tags []tunnelpogs.Tag
|
||||
TlsConfig *tls.Config
|
||||
UseDeclarativeTunnel bool
|
||||
WSGI bool
|
||||
BuildInfo *buildinfo.BuildInfo
|
||||
ClientID string
|
||||
ClientTlsConfig *tls.Config
|
||||
CloseConnOnce *sync.Once // Used to close connectedSignal no more than once
|
||||
CompressionQuality uint64
|
||||
EdgeAddrs []string
|
||||
GracePeriod time.Duration
|
||||
HAConnections int
|
||||
HTTPTransport http.RoundTripper
|
||||
HeartbeatInterval time.Duration
|
||||
Hostname string
|
||||
HTTPHostHeader string
|
||||
IncidentLookup IncidentLookup
|
||||
IsAutoupdated bool
|
||||
IsFreeTunnel bool
|
||||
LBPool string
|
||||
Logger logger.Service
|
||||
TransportLogger logger.Service
|
||||
MaxHeartbeats uint64
|
||||
Metrics *TunnelMetrics
|
||||
MetricsUpdateFreq time.Duration
|
||||
NoChunkedEncoding bool
|
||||
OriginCert []byte
|
||||
ReportedVersion string
|
||||
Retries uint
|
||||
RunFromTerminal bool
|
||||
Tags []tunnelpogs.Tag
|
||||
TlsConfig *tls.Config
|
||||
WSGI bool
|
||||
// OriginUrl may not be used if a user specifies a unix socket.
|
||||
OriginUrl string
|
||||
|
||||
// feature-flag to use new edge reconnect tokens
|
||||
UseReconnectToken bool
|
||||
// feature-flag for using ConnectionDigest
|
||||
UseQuickReconnects bool
|
||||
|
||||
NamedTunnel *NamedTunnelConfig
|
||||
ReplaceExisting bool
|
||||
}
|
||||
|
||||
// ReconnectTunnelCredentialManager is invoked by functions in this file to
|
||||
@@ -102,8 +102,10 @@ type ReconnectTunnelCredentialManager interface {
|
||||
|
||||
type dupConnRegisterTunnelError struct{}
|
||||
|
||||
var errDuplicationConnection = &dupConnRegisterTunnelError{}
|
||||
|
||||
func (e dupConnRegisterTunnelError) Error() string {
|
||||
return "already connected to this server"
|
||||
return "already connected to this server, trying another address"
|
||||
}
|
||||
|
||||
type muxerShutdownError struct{}
|
||||
@@ -170,12 +172,31 @@ func (c *TunnelConfig) RegistrationOptions(connectionID uint8, OriginLocalIP str
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TunnelConfig) SupportedFeatures() []string {
|
||||
basic := []string{FeatureSerializedHeaders}
|
||||
if c.UseQuickReconnects {
|
||||
basic = append(basic, FeatureQuickReconnects)
|
||||
func (c *TunnelConfig) ConnectionOptions(originLocalAddr string) *tunnelpogs.ConnectionOptions {
|
||||
// attempt to parse out origin IP, but don't fail since it's informational field
|
||||
host, _, _ := net.SplitHostPort(originLocalAddr)
|
||||
originIP := net.ParseIP(host)
|
||||
|
||||
return &tunnelpogs.ConnectionOptions{
|
||||
Client: c.NamedTunnel.Client,
|
||||
OriginLocalIP: originIP,
|
||||
ReplaceExisting: c.ReplaceExisting,
|
||||
CompressionQuality: uint8(c.CompressionQuality),
|
||||
}
|
||||
return basic
|
||||
}
|
||||
|
||||
func (c *TunnelConfig) SupportedFeatures() []string {
|
||||
features := []string{FeatureSerializedHeaders}
|
||||
if c.NamedTunnel == nil {
|
||||
features = append(features, FeatureQuickReconnects)
|
||||
}
|
||||
return features
|
||||
}
|
||||
|
||||
type NamedTunnelConfig struct {
|
||||
Auth pogs.TunnelAuth
|
||||
ID uuid.UUID
|
||||
Client pogs.ClientInfo
|
||||
}
|
||||
|
||||
func StartTunnelDaemon(ctx context.Context, config *TunnelConfig, connectedSignal *signal.Signal, cloudflaredID uuid.UUID, reconnectCh chan ReconnectSignal) error {
|
||||
@@ -190,9 +211,9 @@ func ServeTunnelLoop(ctx context.Context,
|
||||
credentialManager ReconnectTunnelCredentialManager,
|
||||
config *TunnelConfig,
|
||||
addr *net.TCPAddr,
|
||||
connectionID uint8,
|
||||
connectionIndex uint8,
|
||||
connectedSignal *signal.Signal,
|
||||
u uuid.UUID,
|
||||
cloudflaredUUID uuid.UUID,
|
||||
bufferPool *buffer.Pool,
|
||||
reconnectCh chan ReconnectSignal,
|
||||
) error {
|
||||
@@ -213,16 +234,16 @@ func ServeTunnelLoop(ctx context.Context,
|
||||
credentialManager,
|
||||
config,
|
||||
config.Logger,
|
||||
addr, connectionID,
|
||||
addr, connectionIndex,
|
||||
connectedFuse,
|
||||
&backoff,
|
||||
u,
|
||||
cloudflaredUUID,
|
||||
bufferPool,
|
||||
reconnectCh,
|
||||
)
|
||||
if recoverable {
|
||||
if duration, ok := backoff.GetBackoffDuration(ctx); ok {
|
||||
config.Logger.Infof("Retrying in %s seconds: connectionID: %d", duration, connectionID)
|
||||
config.Logger.Infof("Retrying connection %d in %s seconds", connectionIndex, duration)
|
||||
backoff.Backoff(ctx)
|
||||
continue
|
||||
}
|
||||
@@ -237,10 +258,10 @@ func ServeTunnel(
|
||||
config *TunnelConfig,
|
||||
logger logger.Service,
|
||||
addr *net.TCPAddr,
|
||||
connectionID uint8,
|
||||
connectionIndex uint8,
|
||||
connectedFuse *h2mux.BooleanFuse,
|
||||
backoff *BackoffHandler,
|
||||
u uuid.UUID,
|
||||
cloudflaredUUID uuid.UUID,
|
||||
bufferPool *buffer.Pool,
|
||||
reconnectCh chan ReconnectSignal,
|
||||
) (err error, recoverable bool) {
|
||||
@@ -256,22 +277,18 @@ func ServeTunnel(
|
||||
}
|
||||
}()
|
||||
|
||||
connectionTag := uint8ToString(connectionID)
|
||||
|
||||
// additional tags to send other than hostname which is set in cloudflared main package
|
||||
tags := make(map[string]string)
|
||||
tags["ha"] = connectionTag
|
||||
connectionTag := uint8ToString(connectionIndex)
|
||||
|
||||
// Returns error from parsing the origin URL or handshake errors
|
||||
handler, originLocalIP, err := NewTunnelHandler(ctx, config, addr, connectionID, bufferPool)
|
||||
handler, originLocalAddr, err := NewTunnelHandler(ctx, config, addr, connectionIndex, bufferPool)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case connection.DialError:
|
||||
logger.Errorf("Unable to dial edge: %s connectionID: %d", err, connectionID)
|
||||
logger.Errorf("Connection %d unable to dial edge: %s", connectionIndex, err)
|
||||
case h2mux.MuxerHandshakeError:
|
||||
logger.Errorf("Handshake failed with edge server: %s connectionID: %d", err, connectionID)
|
||||
logger.Errorf("Connection %d handshake with edge server failed: %s", connectionIndex, err)
|
||||
default:
|
||||
logger.Errorf("Tunnel creation failure: %s connectionID: %d", err, connectionID)
|
||||
logger.Errorf("Connection %d failed: %s", connectionIndex, err)
|
||||
return err, false
|
||||
}
|
||||
return err, true
|
||||
@@ -287,20 +304,21 @@ func ServeTunnel(
|
||||
}
|
||||
}()
|
||||
|
||||
if config.NamedTunnel != nil {
|
||||
return RegisterConnection(ctx, handler.muxer, config, connectionIndex, originLocalAddr)
|
||||
}
|
||||
|
||||
if config.UseReconnectToken && connectedFuse.Value() {
|
||||
token, tokenErr := credentialManager.ReconnectToken()
|
||||
eventDigest, eventDigestErr := credentialManager.EventDigest()
|
||||
// if we have both credentials, we can reconnect
|
||||
if tokenErr == nil && eventDigestErr == nil {
|
||||
var connDigest []byte
|
||||
|
||||
// check if we can use Quick Reconnects
|
||||
if config.UseQuickReconnects {
|
||||
if digest, connDigestErr := credentialManager.ConnDigest(connectionID); connDigestErr == nil {
|
||||
connDigest = digest
|
||||
}
|
||||
if digest, connDigestErr := credentialManager.ConnDigest(connectionIndex); connDigestErr == nil {
|
||||
connDigest = digest
|
||||
}
|
||||
return ReconnectTunnel(serveCtx, token, eventDigest, connDigest, handler.muxer, config, logger, connectionID, originLocalIP, u, credentialManager)
|
||||
|
||||
return ReconnectTunnel(serveCtx, token, eventDigest, connDigest, handler.muxer, config, logger, connectionIndex, originLocalAddr, cloudflaredUUID, credentialManager)
|
||||
}
|
||||
// log errors and proceed to RegisterTunnel
|
||||
if tokenErr != nil {
|
||||
@@ -310,7 +328,7 @@ func ServeTunnel(
|
||||
logger.Errorf("Couldn't get event digest: %s", eventDigestErr)
|
||||
}
|
||||
}
|
||||
return RegisterTunnel(serveCtx, credentialManager, handler.muxer, config, logger, connectionID, originLocalIP, u)
|
||||
return RegisterTunnel(serveCtx, credentialManager, handler.muxer, config, logger, connectionIndex, originLocalAddr, cloudflaredUUID)
|
||||
})
|
||||
|
||||
errGroup.Go(func() error {
|
||||
@@ -319,12 +337,15 @@ func ServeTunnel(
|
||||
select {
|
||||
case <-serveCtx.Done():
|
||||
// UnregisterTunnel blocks until the RPC call returns
|
||||
var err error
|
||||
if connectedFuse.Value() {
|
||||
err = UnregisterTunnel(handler.muxer, config.GracePeriod, config.TransportLogger)
|
||||
if config.NamedTunnel != nil {
|
||||
_ = UnregisterConnection(ctx, handler.muxer, config)
|
||||
} else {
|
||||
_ = UnregisterTunnel(handler.muxer, config.GracePeriod, config.TransportLogger)
|
||||
}
|
||||
}
|
||||
handler.muxer.Shutdown()
|
||||
return err
|
||||
return nil
|
||||
case <-updateMetricsTickC:
|
||||
handler.UpdateMetrics(connectionTag)
|
||||
}
|
||||
@@ -355,31 +376,33 @@ func ServeTunnel(
|
||||
|
||||
err = errGroup.Wait()
|
||||
if err != nil {
|
||||
_ = newClientRegisterTunnelError(err, config.Metrics.regFail, unknown)
|
||||
|
||||
switch castedErr := err.(type) {
|
||||
case dupConnRegisterTunnelError:
|
||||
logger.Info("Already connected to this server, selecting a different one")
|
||||
return err, true
|
||||
case serverRegisterTunnelError:
|
||||
logger.Errorf("Register tunnel error from server side: %s", castedErr.cause)
|
||||
switch err := err.(type) {
|
||||
case *dupConnRegisterTunnelError:
|
||||
// don't retry this connection anymore, let supervisor pick new a address
|
||||
return err, false
|
||||
case *serverRegisterTunnelError:
|
||||
logger.Errorf("Register tunnel error from server side: %s", err.cause)
|
||||
// Don't send registration error return from server to Sentry. They are
|
||||
// logged on server side
|
||||
if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 {
|
||||
logger.Error(activeIncidentsMsg(incidents))
|
||||
}
|
||||
return castedErr.cause, !castedErr.permanent
|
||||
case clientRegisterTunnelError:
|
||||
logger.Errorf("Register tunnel error on client side: %s", castedErr.cause)
|
||||
return err.cause, !err.permanent
|
||||
case *clientRegisterTunnelError:
|
||||
logger.Errorf("Register tunnel error on client side: %s", err.cause)
|
||||
return err, true
|
||||
case muxerShutdownError:
|
||||
case *muxerShutdownError:
|
||||
logger.Info("Muxer shutdown")
|
||||
return err, true
|
||||
case *ReconnectSignal:
|
||||
logger.Infof("Restarting due to reconnect signal in %d seconds", castedErr.Delay)
|
||||
castedErr.DelayBeforeReconnect()
|
||||
logger.Infof("Restarting connection %d due to reconnect signal in %d seconds", connectionIndex, err.Delay)
|
||||
err.DelayBeforeReconnect()
|
||||
return err, true
|
||||
default:
|
||||
if err == context.Canceled {
|
||||
logger.Debugf("Serve tunnel error: %s", err)
|
||||
return err, false
|
||||
}
|
||||
logger.Errorf("Serve tunnel error: %s", err)
|
||||
return err, true
|
||||
}
|
||||
@@ -387,6 +410,74 @@ func ServeTunnel(
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func RegisterConnection(
|
||||
ctx context.Context,
|
||||
muxer *h2mux.Muxer,
|
||||
config *TunnelConfig,
|
||||
connectionIndex uint8,
|
||||
originLocalAddr string,
|
||||
) error {
|
||||
const registerConnection = "registerConnection"
|
||||
|
||||
config.TransportLogger.Debug("initiating RPC stream for RegisterConnection")
|
||||
rpc, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger, openStreamTimeout)
|
||||
if err != nil {
|
||||
// RPC stream open error
|
||||
return newClientRegisterTunnelError(err, config.Metrics.rpcFail, registerConnection)
|
||||
}
|
||||
defer rpc.Close()
|
||||
|
||||
conn, err := rpc.RegisterConnection(
|
||||
ctx,
|
||||
config.NamedTunnel.Auth,
|
||||
config.NamedTunnel.ID,
|
||||
connectionIndex,
|
||||
config.ConnectionOptions(originLocalAddr),
|
||||
)
|
||||
if err != nil {
|
||||
if err.Error() == DuplicateConnectionError {
|
||||
config.Metrics.regFail.WithLabelValues("dup_edge_conn", registerConnection).Inc()
|
||||
return errDuplicationConnection
|
||||
}
|
||||
config.Metrics.regFail.WithLabelValues("server_error", registerConnection).Inc()
|
||||
return serverRegistrationErrorFromRPC(err)
|
||||
}
|
||||
|
||||
config.Metrics.regSuccess.WithLabelValues(registerConnection).Inc()
|
||||
config.Logger.Infof("Connection %d registered with %s using ID %s", connectionIndex, conn.Location, conn.UUID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func serverRegistrationErrorFromRPC(err error) *serverRegisterTunnelError {
|
||||
if retryable, ok := err.(*tunnelpogs.RetryableError); ok {
|
||||
return &serverRegisterTunnelError{
|
||||
cause: retryable.Unwrap(),
|
||||
permanent: false,
|
||||
}
|
||||
}
|
||||
return &serverRegisterTunnelError{
|
||||
cause: err,
|
||||
permanent: true,
|
||||
}
|
||||
}
|
||||
|
||||
func UnregisterConnection(
|
||||
ctx context.Context,
|
||||
muxer *h2mux.Muxer,
|
||||
config *TunnelConfig,
|
||||
) error {
|
||||
config.TransportLogger.Debug("initiating RPC stream for UnregisterConnection")
|
||||
rpc, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger, openStreamTimeout)
|
||||
if err != nil {
|
||||
// RPC stream open error
|
||||
return newClientRegisterTunnelError(err, config.Metrics.rpcFail, register)
|
||||
}
|
||||
defer rpc.Close()
|
||||
|
||||
return rpc.UnregisterConnection(ctx)
|
||||
}
|
||||
|
||||
func RegisterTunnel(
|
||||
ctx context.Context,
|
||||
credentialManager ReconnectTunnelCredentialManager,
|
||||
@@ -431,7 +522,7 @@ func ReconnectTunnel(
|
||||
config *TunnelConfig,
|
||||
logger logger.Service,
|
||||
connectionID uint8,
|
||||
originLocalIP string,
|
||||
originLocalAddr string,
|
||||
uuid uuid.UUID,
|
||||
credentialManager ReconnectTunnelCredentialManager,
|
||||
) error {
|
||||
@@ -453,7 +544,7 @@ func ReconnectTunnel(
|
||||
eventDigest,
|
||||
connDigest,
|
||||
config.Hostname,
|
||||
config.RegistrationOptions(connectionID, originLocalIP, uuid),
|
||||
config.RegistrationOptions(connectionID, originLocalAddr, uuid),
|
||||
)
|
||||
if registrationErr := registration.DeserializeError(); registrationErr != nil {
|
||||
// ReconnectTunnel RPC failure
|
||||
@@ -502,11 +593,11 @@ func processRegistrationSuccess(
|
||||
func processRegisterTunnelError(err tunnelpogs.TunnelRegistrationError, metrics *TunnelMetrics, name registerRPCName) error {
|
||||
if err.Error() == DuplicateConnectionError {
|
||||
metrics.regFail.WithLabelValues("dup_edge_conn", string(name)).Inc()
|
||||
return dupConnRegisterTunnelError{}
|
||||
return errDuplicationConnection
|
||||
}
|
||||
metrics.regFail.WithLabelValues("server_error", string(name)).Inc()
|
||||
return serverRegisterTunnelError{
|
||||
cause: fmt.Errorf("Server error: %s", err.Error()),
|
||||
cause: err,
|
||||
permanent: err.IsPermanent(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
ln -s /usr/bin/cloudflared /usr/local/bin/cloudflared
|
||||
mkdir -p /usr/local/etc/cloudflared/
|
||||
touch /usr/local/etc/cloudflared/.installedFromPackageManager || true
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
rm /usr/local/bin/cloudflared
|
||||
rm /usr/local/etc/cloudflared/.installedFromPackageManager || true
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/proxy"
|
||||
@@ -58,7 +59,6 @@ func startTestServer(t *testing.T, httpHandler func(w http.ResponseWriter, r *ht
|
||||
|
||||
// start the servers
|
||||
go http.ListenAndServe(":8085", mux)
|
||||
|
||||
}
|
||||
|
||||
func respondWithJSON(w http.ResponseWriter, v interface{}, status int) {
|
||||
@@ -78,6 +78,7 @@ func OkJSONResponseHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func TestSocksConnection(t *testing.T) {
|
||||
startTestServer(t, OkJSONResponseHandler)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
b := sendSocksRequest(t)
|
||||
assert.True(t, len(b) > 0, "no data returned!")
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ func TestSessionLogWrite(t *testing.T) {
|
||||
testStr := "hi"
|
||||
logger := createSessionLogger(t)
|
||||
defer func() {
|
||||
logger.Close()
|
||||
os.Remove(sessionLogFileName)
|
||||
}()
|
||||
|
||||
logger.Write([]byte(testStr))
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
logger.Close()
|
||||
|
||||
f, err := os.Open(sessionLogFileName)
|
||||
if err != nil {
|
||||
t.Fatal("couldn't read the log file!", err)
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
package pogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"zombiezen.com/go/capnproto2/pogs"
|
||||
"zombiezen.com/go/capnproto2/server"
|
||||
|
||||
"github.com/cloudflare/cloudflared/tunnelrpc"
|
||||
)
|
||||
|
||||
type RegistrationServer interface {
|
||||
RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error)
|
||||
UnregisterConnection(ctx context.Context)
|
||||
}
|
||||
|
||||
type ClientInfo struct {
|
||||
ClientID []byte `capnp:"clientId"` // must be a slice for capnp compatibility
|
||||
Features []string
|
||||
Version string
|
||||
Arch string
|
||||
}
|
||||
|
||||
type ConnectionOptions struct {
|
||||
Client ClientInfo
|
||||
OriginLocalIP net.IP `capnp:"originLocalIp"`
|
||||
ReplaceExisting bool
|
||||
CompressionQuality uint8
|
||||
}
|
||||
|
||||
type TunnelAuth struct {
|
||||
AccountTag string
|
||||
TunnelSecret []byte
|
||||
}
|
||||
|
||||
func (p *ConnectionOptions) MarshalCapnproto(s tunnelrpc.ConnectionOptions) error {
|
||||
return pogs.Insert(tunnelrpc.ConnectionOptions_TypeID, s.Struct, p)
|
||||
}
|
||||
|
||||
func (p *ConnectionOptions) UnmarshalCapnproto(s tunnelrpc.ConnectionOptions) error {
|
||||
return pogs.Extract(p, tunnelrpc.ConnectionOptions_TypeID, s.Struct)
|
||||
}
|
||||
|
||||
func (a *TunnelAuth) MarshalCapnproto(s tunnelrpc.TunnelAuth) error {
|
||||
return pogs.Insert(tunnelrpc.TunnelAuth_TypeID, s.Struct, a)
|
||||
}
|
||||
|
||||
func (a *TunnelAuth) UnmarshalCapnproto(s tunnelrpc.TunnelAuth) error {
|
||||
return pogs.Extract(a, tunnelrpc.TunnelAuth_TypeID, s.Struct)
|
||||
}
|
||||
|
||||
type ConnectionDetails struct {
|
||||
UUID uuid.UUID
|
||||
Location string
|
||||
}
|
||||
|
||||
func (details *ConnectionDetails) MarshalCapnproto(s tunnelrpc.ConnectionDetails) error {
|
||||
if err := s.SetUuid(details.UUID[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.SetLocationName(details.Location); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (details *ConnectionDetails) UnmarshalCapnproto(s tunnelrpc.ConnectionDetails) error {
|
||||
uuidBytes, err := s.Uuid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
details.UUID, err = uuid.FromBytes(uuidBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
details.Location, err = s.LocationName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func MarshalError(s tunnelrpc.ConnectionError, err error) error {
|
||||
if err := s.SetCause(err.Error()); err != nil {
|
||||
return err
|
||||
}
|
||||
if retryableErr, ok := err.(*RetryableError); ok {
|
||||
s.SetShouldRetry(true)
|
||||
s.SetRetryAfter(int64(retryableErr.Delay))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i TunnelServer_PogsImpl) RegisterConnection(p tunnelrpc.RegistrationServer_registerConnection) error {
|
||||
server.Ack(p.Options)
|
||||
|
||||
auth, err := p.Params.Auth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pogsAuth TunnelAuth
|
||||
err = pogsAuth.UnmarshalCapnproto(auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uuidBytes, err := p.Params.TunnelId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tunnelID, err := uuid.FromBytes(uuidBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connIndex := p.Params.ConnIndex()
|
||||
options, err := p.Params.Options()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pogsOptions ConnectionOptions
|
||||
err = pogsOptions.UnmarshalCapnproto(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
connDetails, callError := i.impl.RegisterConnection(p.Ctx, pogsAuth, tunnelID, connIndex, &pogsOptions)
|
||||
|
||||
resp, err := p.Results.NewResult()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if callError != nil {
|
||||
if connError, err := resp.Result().NewError(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return MarshalError(connError, callError)
|
||||
}
|
||||
}
|
||||
|
||||
if details, err := resp.Result().NewConnectionDetails(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return connDetails.MarshalCapnproto(details)
|
||||
}
|
||||
}
|
||||
|
||||
func (i TunnelServer_PogsImpl) UnregisterConnection(p tunnelrpc.RegistrationServer_unregisterConnection) error {
|
||||
server.Ack(p.Options)
|
||||
|
||||
i.impl.UnregisterConnection(p.Ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c TunnelServer_PogsClient) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
|
||||
client := tunnelrpc.TunnelServer{Client: c.Client}
|
||||
promise := client.RegisterConnection(ctx, func(p tunnelrpc.RegistrationServer_registerConnection_Params) error {
|
||||
tunnelAuth, err := p.NewAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = auth.MarshalCapnproto(tunnelAuth); err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.SetAuth(tunnelAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.SetTunnelId(tunnelID[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.SetConnIndex(connIndex)
|
||||
connectionOptions, err := p.NewOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = options.MarshalCapnproto(connectionOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
response, err := promise.Result().Struct()
|
||||
if err != nil {
|
||||
return nil, wrapRPCError(err)
|
||||
}
|
||||
result := response.Result()
|
||||
switch result.Which() {
|
||||
case tunnelrpc.ConnectionResponse_result_Which_error:
|
||||
resultError, err := result.Error()
|
||||
if err != nil {
|
||||
return nil, wrapRPCError(err)
|
||||
}
|
||||
cause, err := resultError.Cause()
|
||||
if err != nil {
|
||||
return nil, wrapRPCError(err)
|
||||
}
|
||||
err = errors.New(cause)
|
||||
if resultError.ShouldRetry() {
|
||||
err = RetryErrorAfter(err, time.Duration(resultError.RetryAfter()))
|
||||
}
|
||||
return nil, err
|
||||
|
||||
case tunnelrpc.ConnectionResponse_result_Which_connectionDetails:
|
||||
connDetails, err := result.ConnectionDetails()
|
||||
if err != nil {
|
||||
return nil, wrapRPCError(err)
|
||||
}
|
||||
details := new(ConnectionDetails)
|
||||
if err = details.UnmarshalCapnproto(connDetails); err != nil {
|
||||
return nil, wrapRPCError(err)
|
||||
}
|
||||
return details, nil
|
||||
}
|
||||
|
||||
return nil, newRPCError("unknown result which %d", result.Which())
|
||||
}
|
||||
|
||||
func (c TunnelServer_PogsClient) UnregisterConnection(ctx context.Context) error {
|
||||
client := tunnelrpc.TunnelServer{Client: c.Client}
|
||||
promise := client.UnregisterConnection(ctx, func(p tunnelrpc.RegistrationServer_unregisterConnection_Params) error {
|
||||
return nil
|
||||
})
|
||||
_, err := promise.Struct()
|
||||
if err != nil {
|
||||
return wrapRPCError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package pogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
capnp "zombiezen.com/go/capnproto2"
|
||||
"zombiezen.com/go/capnproto2/rpc"
|
||||
|
||||
"github.com/cloudflare/cloudflared/tunnelrpc"
|
||||
)
|
||||
|
||||
const testAccountTag = "abc123"
|
||||
|
||||
func TestMarshalConnectionOptions(t *testing.T) {
|
||||
clientID := uuid.New()
|
||||
orig := ConnectionOptions{
|
||||
Client: ClientInfo{
|
||||
ClientID: clientID[:],
|
||||
Features: []string{"a", "b"},
|
||||
Version: "1.2.3",
|
||||
Arch: "macos",
|
||||
},
|
||||
OriginLocalIP: []byte{10, 2, 3, 4},
|
||||
ReplaceExisting: false,
|
||||
CompressionQuality: 1,
|
||||
}
|
||||
|
||||
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
|
||||
require.NoError(t, err)
|
||||
capnpOpts, err := tunnelrpc.NewConnectionOptions(seg)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = orig.MarshalCapnproto(capnpOpts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var pogsOpts ConnectionOptions
|
||||
err = pogsOpts.UnmarshalCapnproto(capnpOpts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, orig, pogsOpts)
|
||||
}
|
||||
|
||||
func TestConnectionRegistrationRPC(t *testing.T) {
|
||||
p1, p2 := net.Pipe()
|
||||
|
||||
t1, t2 := rpc.StreamTransport(p1), rpc.StreamTransport(p2)
|
||||
|
||||
// Server-side
|
||||
testImpl := testConnectionRegistrationServer{}
|
||||
srv := TunnelServer_ServerToClient(&testImpl)
|
||||
serverConn := rpc.NewConn(t1, rpc.MainInterface(srv.Client))
|
||||
defer serverConn.Wait()
|
||||
|
||||
ctx := context.Background()
|
||||
clientConn := rpc.NewConn(t2)
|
||||
defer clientConn.Close()
|
||||
client := TunnelServer_PogsClient{
|
||||
Client: clientConn.Bootstrap(ctx),
|
||||
Conn: clientConn,
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
clientID := uuid.New()
|
||||
options := &ConnectionOptions{
|
||||
Client: ClientInfo{
|
||||
ClientID: clientID[:],
|
||||
Features: []string{"foo"},
|
||||
Version: "1.2.3",
|
||||
Arch: "macos",
|
||||
},
|
||||
OriginLocalIP: net.IP{10, 20, 30, 40},
|
||||
ReplaceExisting: true,
|
||||
CompressionQuality: 0,
|
||||
}
|
||||
|
||||
expectedDetails := ConnectionDetails{
|
||||
UUID: uuid.New(),
|
||||
Location: "TEST",
|
||||
}
|
||||
testImpl.details = &expectedDetails
|
||||
testImpl.err = nil
|
||||
|
||||
auth := TunnelAuth{
|
||||
AccountTag: testAccountTag,
|
||||
TunnelSecret: []byte{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
// success
|
||||
tunnelID := uuid.New()
|
||||
details, err := client.RegisterConnection(ctx, auth, tunnelID, 2, options)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedDetails, *details)
|
||||
|
||||
// regular error
|
||||
testImpl.details = nil
|
||||
testImpl.err = errors.New("internal")
|
||||
|
||||
_, err = client.RegisterConnection(ctx, auth, tunnelID, 2, options)
|
||||
assert.EqualError(t, err, "internal")
|
||||
|
||||
// retriable error
|
||||
testImpl.details = nil
|
||||
const delay = 27 * time.Second
|
||||
testImpl.err = RetryErrorAfter(errors.New("retryable"), delay)
|
||||
|
||||
_, err = client.RegisterConnection(ctx, auth, tunnelID, 2, options)
|
||||
assert.EqualError(t, err, "retryable")
|
||||
|
||||
re, ok := err.(*RetryableError)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, delay, re.Delay)
|
||||
}
|
||||
|
||||
type testConnectionRegistrationServer struct {
|
||||
mockTunnelServerBase
|
||||
|
||||
details *ConnectionDetails
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *testConnectionRegistrationServer) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
|
||||
if auth.AccountTag != testAccountTag {
|
||||
panic("bad account tag: " + auth.AccountTag)
|
||||
}
|
||||
if t.err != nil {
|
||||
return nil, t.err
|
||||
}
|
||||
if t.details != nil {
|
||||
return t.details, nil
|
||||
}
|
||||
|
||||
panic("either details or err mush be set")
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package pogs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RetryableError struct {
|
||||
err error
|
||||
Delay time.Duration
|
||||
}
|
||||
|
||||
func (re *RetryableError) Error() string {
|
||||
return re.err.Error()
|
||||
}
|
||||
|
||||
// RetryErrorAfter wraps err to indicate that client should retry after delay
|
||||
func RetryErrorAfter(err error, delay time.Duration) *RetryableError {
|
||||
return &RetryableError{
|
||||
err: err,
|
||||
Delay: delay,
|
||||
}
|
||||
}
|
||||
|
||||
func (re *RetryableError) Unwrap() error {
|
||||
return re.err
|
||||
}
|
||||
|
||||
// RPCError is used to indicate errors returned by the RPC subsystem rather
|
||||
// than failure of a remote operation
|
||||
type RPCError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (re *RPCError) Error() string {
|
||||
return re.err.Error()
|
||||
}
|
||||
|
||||
func wrapRPCError(err error) *RPCError {
|
||||
if err != nil {
|
||||
return &RPCError{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newRPCError(format string, args ...interface{}) *RPCError {
|
||||
return &RPCError{
|
||||
fmt.Errorf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (re *RPCError) Unwrap() error {
|
||||
return re.err
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package pogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// mockTunnelServerBase provides a placeholder implementation
|
||||
// for TunnelServer interface that can be used to build
|
||||
// mocks for specific unit tests without having to implement every method
|
||||
type mockTunnelServerBase struct{}
|
||||
|
||||
func (mockTunnelServerBase) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
|
||||
panic("unexpected call to RegisterConnection")
|
||||
}
|
||||
|
||||
func (mockTunnelServerBase) UnregisterConnection(ctx context.Context) {
|
||||
panic("unexpected call to UnregisterConnection")
|
||||
}
|
||||
|
||||
func (mockTunnelServerBase) RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) *TunnelRegistration {
|
||||
panic("unexpected call to RegisterTunnel")
|
||||
}
|
||||
|
||||
func (mockTunnelServerBase) GetServerInfo(ctx context.Context) (*ServerInfo, error) {
|
||||
panic("unexpected call to GetServerInfo")
|
||||
}
|
||||
|
||||
func (mockTunnelServerBase) UnregisterTunnel(ctx context.Context, gracePeriodNanoSec int64) error {
|
||||
panic("unexpected call to UnregisterTunnel")
|
||||
}
|
||||
|
||||
func (mockTunnelServerBase) Authenticate(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) (*AuthenticateResponse, error) {
|
||||
panic("unexpected call to Authenticate")
|
||||
}
|
||||
|
||||
func (mockTunnelServerBase) ReconnectTunnel(ctx context.Context, jwt, eventDigest, connDigest []byte, hostname string, options *RegistrationOptions) (*TunnelRegistration, error) {
|
||||
panic("unexpected call to ReconnectTunnel")
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ func UnmarshalServerInfo(s tunnelrpc.ServerInfo) (*ServerInfo, error) {
|
||||
}
|
||||
|
||||
type TunnelServer interface {
|
||||
RegistrationServer
|
||||
RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) *TunnelRegistration
|
||||
GetServerInfo(ctx context.Context) (*ServerInfo, error)
|
||||
UnregisterTunnel(ctx context.Context, gracePeriodNanoSec int64) error
|
||||
|
||||
@@ -78,7 +78,60 @@ struct AuthenticateResponse {
|
||||
hoursUntilRefresh @3 :UInt8;
|
||||
}
|
||||
|
||||
interface TunnelServer {
|
||||
struct ClientInfo {
|
||||
# The tunnel client's unique identifier, used to verify a reconnection.
|
||||
clientId @0 :Data;
|
||||
# Set of features this cloudflared knows it supports
|
||||
features @1 :List(Text);
|
||||
# Information about the running binary.
|
||||
version @2 :Text;
|
||||
# Client OS and CPU info
|
||||
arch @3 :Text;
|
||||
}
|
||||
|
||||
struct ConnectionOptions {
|
||||
# client details
|
||||
client @0 :ClientInfo;
|
||||
# origin LAN IP
|
||||
originLocalIp @1 :Data;
|
||||
# What to do if connection already exists
|
||||
replaceExisting @2 :Bool;
|
||||
# cross stream compression setting, 0 - off, 3 - high
|
||||
compressionQuality @3 :UInt8;
|
||||
}
|
||||
|
||||
struct ConnectionResponse {
|
||||
result :union {
|
||||
error @0 :ConnectionError;
|
||||
connectionDetails @1 :ConnectionDetails;
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionError {
|
||||
cause @0 :Text;
|
||||
# How long should this connection wait to retry in ns
|
||||
retryAfter @1 :Int64;
|
||||
shouldRetry @2 :Bool;
|
||||
}
|
||||
|
||||
struct ConnectionDetails {
|
||||
# identifier of this connection
|
||||
uuid @0 :Data;
|
||||
# airport code of the colo where this connection landed
|
||||
locationName @1 :Text;
|
||||
}
|
||||
|
||||
struct TunnelAuth {
|
||||
accountTag @0 :Text;
|
||||
tunnelSecret @1 :Data;
|
||||
}
|
||||
|
||||
interface RegistrationServer {
|
||||
registerConnection @0 (auth :TunnelAuth, tunnelId :Data, connIndex :UInt8, options :ConnectionOptions) -> (result :ConnectionResponse);
|
||||
unregisterConnection @1 () -> ();
|
||||
}
|
||||
|
||||
interface TunnelServer extends (RegistrationServer) {
|
||||
registerTunnel @0 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration);
|
||||
getServerInfo @1 () -> (result :ServerInfo);
|
||||
unregisterTunnel @2 (gracePeriodNanoSec :Int64) -> ();
|
||||
|
||||
+1366
-126
File diff suppressed because it is too large
Load Diff
+29
-15
@@ -30,6 +30,7 @@ type Tunnel struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
DeletedAt time.Time `json:"deleted_at"`
|
||||
Connections []Connection `json:"connections"`
|
||||
}
|
||||
|
||||
@@ -39,10 +40,11 @@ type Connection struct {
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
CreateTunnel(name string) (*Tunnel, error)
|
||||
GetTunnel(id string) (*Tunnel, error)
|
||||
DeleteTunnel(id string) error
|
||||
CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, error)
|
||||
GetTunnel(tunnelID string) (*Tunnel, error)
|
||||
DeleteTunnel(tunnelID string) error
|
||||
ListTunnels() ([]Tunnel, error)
|
||||
CleanupConnections(tunnelID string) error
|
||||
}
|
||||
|
||||
type RESTClient struct {
|
||||
@@ -74,15 +76,17 @@ func NewRESTClient(baseURL string, accountTag string, authToken string, logger l
|
||||
}
|
||||
|
||||
type newTunnel struct {
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name"`
|
||||
TunnelSecret []byte `json:"tunnel_secret"`
|
||||
}
|
||||
|
||||
func (r *RESTClient) CreateTunnel(name string) (*Tunnel, error) {
|
||||
func (r *RESTClient) CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, error) {
|
||||
if name == "" {
|
||||
return nil, errors.New("tunnel name required")
|
||||
}
|
||||
body, err := json.Marshal(&newTunnel{
|
||||
Name: name,
|
||||
Name: name,
|
||||
TunnelSecret: tunnelSecret,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to serialize new tunnel request")
|
||||
@@ -101,11 +105,11 @@ func (r *RESTClient) CreateTunnel(name string) (*Tunnel, error) {
|
||||
return nil, ErrTunnelNameConflict
|
||||
}
|
||||
|
||||
return nil, statusCodeToError("create", resp)
|
||||
return nil, statusCodeToError("create tunnel", resp)
|
||||
}
|
||||
|
||||
func (r *RESTClient) GetTunnel(id string) (*Tunnel, error) {
|
||||
resp, err := r.sendRequest("GET", id, nil)
|
||||
func (r *RESTClient) GetTunnel(tunnelID string) (*Tunnel, error) {
|
||||
resp, err := r.sendRequest("GET", tunnelID, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "REST request failed")
|
||||
}
|
||||
@@ -115,17 +119,17 @@ func (r *RESTClient) GetTunnel(id string) (*Tunnel, error) {
|
||||
return unmarshalTunnel(resp.Body)
|
||||
}
|
||||
|
||||
return nil, statusCodeToError("read", resp)
|
||||
return nil, statusCodeToError("get tunnel", resp)
|
||||
}
|
||||
|
||||
func (r *RESTClient) DeleteTunnel(id string) error {
|
||||
resp, err := r.sendRequest("DELETE", id, nil)
|
||||
func (r *RESTClient) DeleteTunnel(tunnelID string) error {
|
||||
resp, err := r.sendRequest("DELETE", tunnelID, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "REST request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return statusCodeToError("delete", resp)
|
||||
return statusCodeToError("delete tunnel", resp)
|
||||
}
|
||||
|
||||
func (r *RESTClient) ListTunnels() ([]Tunnel, error) {
|
||||
@@ -143,7 +147,17 @@ func (r *RESTClient) ListTunnels() ([]Tunnel, error) {
|
||||
return tunnels, nil
|
||||
}
|
||||
|
||||
return nil, statusCodeToError("list", resp)
|
||||
return nil, statusCodeToError("list tunnels", resp)
|
||||
}
|
||||
|
||||
func (r *RESTClient) CleanupConnections(tunnelID string) error {
|
||||
resp, err := r.sendRequest("DELETE", fmt.Sprintf("%s/connections", tunnelID), nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "REST request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return statusCodeToError("cleanup connections", resp)
|
||||
}
|
||||
|
||||
func (r *RESTClient) resolve(target string) string {
|
||||
@@ -186,6 +200,6 @@ func statusCodeToError(op string, resp *http.Response) error {
|
||||
case http.StatusNotFound:
|
||||
return ErrNotFound
|
||||
}
|
||||
return errors.Errorf("API call to %s tunnel failed with status %d: %s", op,
|
||||
return errors.Errorf("API call to %s failed with status %d: %s", op,
|
||||
resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
+12
-10
@@ -36,7 +36,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
|
||||
// during the authentication phase the client first attempts the "none" method
|
||||
// then any untried methods suggested by the server.
|
||||
tried := make(map[string]bool)
|
||||
var tried []string
|
||||
var lastMethods []string
|
||||
|
||||
sessionID := c.transport.getSessionID()
|
||||
@@ -49,7 +49,9 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
// success
|
||||
return nil
|
||||
} else if ok == authFailure {
|
||||
tried[auth.method()] = true
|
||||
if m := auth.method(); !contains(tried, m) {
|
||||
tried = append(tried, m)
|
||||
}
|
||||
}
|
||||
if methods == nil {
|
||||
methods = lastMethods
|
||||
@@ -61,7 +63,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
findNext:
|
||||
for _, a := range config.Auth {
|
||||
candidateMethod := a.method()
|
||||
if tried[candidateMethod] {
|
||||
if contains(tried, candidateMethod) {
|
||||
continue
|
||||
}
|
||||
for _, meth := range methods {
|
||||
@@ -72,16 +74,16 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried))
|
||||
return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
|
||||
}
|
||||
|
||||
func keys(m map[string]bool) []string {
|
||||
s := make([]string, 0, len(m))
|
||||
|
||||
for key := range m {
|
||||
s = append(s, key)
|
||||
func contains(list []string, e string) bool {
|
||||
for _, s := range list {
|
||||
if s == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return s
|
||||
return false
|
||||
}
|
||||
|
||||
// An AuthMethod represents an instance of an RFC 4252 authentication method.
|
||||
|
||||
+2
-6
@@ -107,6 +107,7 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
||||
|
||||
// dialCall is an in-flight Transport dial call to a host.
|
||||
type dialCall struct {
|
||||
_ incomparable
|
||||
p *clientConnPool
|
||||
done chan struct{} // closed when done
|
||||
res *ClientConn // valid after done is closed
|
||||
@@ -180,6 +181,7 @@ func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn)
|
||||
}
|
||||
|
||||
type addConnCall struct {
|
||||
_ incomparable
|
||||
p *clientConnPool
|
||||
done chan struct{} // closed when done
|
||||
err error
|
||||
@@ -200,12 +202,6 @@ func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
||||
close(c.done)
|
||||
}
|
||||
|
||||
func (p *clientConnPool) addConn(key string, cc *ClientConn) {
|
||||
p.mu.Lock()
|
||||
p.addConnLocked(key, cc)
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// p.mu must be held
|
||||
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
||||
for _, v := range p.conns[key] {
|
||||
|
||||
+2
@@ -8,6 +8,8 @@ package http2
|
||||
|
||||
// flow is the flow control window's size.
|
||||
type flow struct {
|
||||
_ incomparable
|
||||
|
||||
// n is the number of DATA bytes we're allowed to send.
|
||||
// A flow is kept both on a conn and a per-stream.
|
||||
n int32
|
||||
|
||||
+7
@@ -105,7 +105,14 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
||||
// makes that struct also non-comparable, and generally doesn't add
|
||||
// any size (as long as it's first).
|
||||
type incomparable [0]func()
|
||||
|
||||
type node struct {
|
||||
_ incomparable
|
||||
|
||||
// children is non-nil for internal nodes
|
||||
children *[256]*node
|
||||
|
||||
|
||||
+7
-6
@@ -19,7 +19,6 @@ package http2 // import "golang.org/x/net/http2"
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -173,11 +172,6 @@ func (s SettingID) String() string {
|
||||
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
||||
errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
||||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
//
|
||||
@@ -247,6 +241,7 @@ func (cw closeWaiter) Wait() {
|
||||
// Its buffered writer is lazily allocated as needed, to minimize
|
||||
// idle memory usage with many connections.
|
||||
type bufferedWriter struct {
|
||||
_ incomparable
|
||||
w io.Writer // immutable
|
||||
bw *bufio.Writer // non-nil when data is buffered
|
||||
}
|
||||
@@ -319,6 +314,7 @@ func bodyAllowedForStatus(status int) bool {
|
||||
}
|
||||
|
||||
type httpError struct {
|
||||
_ incomparable
|
||||
msg string
|
||||
timeout bool
|
||||
}
|
||||
@@ -382,3 +378,8 @@ func (s *sorter) SortStrings(ss []string) {
|
||||
func validPseudoPath(v string) bool {
|
||||
return (len(v) > 0 && v[0] == '/') || v == "*"
|
||||
}
|
||||
|
||||
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
||||
// makes that struct also non-comparable, and generally doesn't add
|
||||
// any size (as long as it's first).
|
||||
type incomparable [0]func()
|
||||
|
||||
+10
-11
@@ -252,7 +252,7 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
||||
}
|
||||
}
|
||||
if !haveRequired {
|
||||
return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher.")
|
||||
return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 or TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,13 +581,10 @@ type stream struct {
|
||||
cancelCtx func()
|
||||
|
||||
// owned by serverConn's serve loop:
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
declBodyBytes int64 // or -1 if undeclared
|
||||
flow flow // limits writing from Handler to client
|
||||
inflow flow // what the client is allowed to POST/etc to us
|
||||
parent *stream // or nil
|
||||
numTrailerValues int64
|
||||
weight uint8
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
declBodyBytes int64 // or -1 if undeclared
|
||||
flow flow // limits writing from Handler to client
|
||||
inflow flow // what the client is allowed to POST/etc to us
|
||||
state streamState
|
||||
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
|
||||
gotTrailerHeader bool // HEADER frame for trailers was seen
|
||||
@@ -764,6 +761,7 @@ func (sc *serverConn) readFrames() {
|
||||
|
||||
// frameWriteResult is the message passed from writeFrameAsync to the serve goroutine.
|
||||
type frameWriteResult struct {
|
||||
_ incomparable
|
||||
wr FrameWriteRequest // what was written (or attempted)
|
||||
err error // result of the writeFrame call
|
||||
}
|
||||
@@ -774,7 +772,7 @@ type frameWriteResult struct {
|
||||
// serverConn.
|
||||
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest) {
|
||||
err := wr.write.writeFrame(sc)
|
||||
sc.wroteFrameCh <- frameWriteResult{wr, err}
|
||||
sc.wroteFrameCh <- frameWriteResult{wr: wr, err: err}
|
||||
}
|
||||
|
||||
func (sc *serverConn) closeAllStreamsOnConnClose() {
|
||||
@@ -1164,7 +1162,7 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
|
||||
if wr.write.staysWithinBuffer(sc.bw.Available()) {
|
||||
sc.writingFrameAsync = false
|
||||
err := wr.write.writeFrame(sc)
|
||||
sc.wroteFrame(frameWriteResult{wr, err})
|
||||
sc.wroteFrame(frameWriteResult{wr: wr, err: err})
|
||||
} else {
|
||||
sc.writingFrameAsync = true
|
||||
go sc.writeFrameAsync(wr)
|
||||
@@ -2060,7 +2058,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
||||
var trailer http.Header
|
||||
for _, v := range rp.header["Trailer"] {
|
||||
for _, key := range strings.Split(v, ",") {
|
||||
key = http.CanonicalHeaderKey(strings.TrimSpace(key))
|
||||
key = http.CanonicalHeaderKey(textproto.TrimString(key))
|
||||
switch key {
|
||||
case "Transfer-Encoding", "Trailer", "Content-Length":
|
||||
// Bogus. (copy of http1 rules)
|
||||
@@ -2278,6 +2276,7 @@ func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) {
|
||||
// requestBody is the Handler's Request.Body type.
|
||||
// Read and Close may be called concurrently.
|
||||
type requestBody struct {
|
||||
_ incomparable
|
||||
stream *stream
|
||||
conn *serverConn
|
||||
closed bool // for use by Close only
|
||||
|
||||
+97
-21
@@ -93,7 +93,7 @@ type Transport struct {
|
||||
// send in the initial settings frame. It is how many bytes
|
||||
// of response headers are allowed. Unlike the http2 spec, zero here
|
||||
// means to use a default limit (currently 10MB). If you actually
|
||||
// want to advertise an ulimited value to the peer, Transport
|
||||
// want to advertise an unlimited value to the peer, Transport
|
||||
// interprets the highest possible value here (0xffffffff or 1<<32-1)
|
||||
// to mean no limit.
|
||||
MaxHeaderListSize uint32
|
||||
@@ -108,6 +108,19 @@ type Transport struct {
|
||||
// waiting for their turn.
|
||||
StrictMaxConcurrentStreams bool
|
||||
|
||||
// ReadIdleTimeout is the timeout after which a health check using ping
|
||||
// frame will be carried out if no frame is received on the connection.
|
||||
// Note that a ping response will is considered a received frame, so if
|
||||
// there is no other traffic on the connection, the health check will
|
||||
// be performed every ReadIdleTimeout interval.
|
||||
// If zero, no health check is performed.
|
||||
ReadIdleTimeout time.Duration
|
||||
|
||||
// PingTimeout is the timeout after which the connection will be closed
|
||||
// if a response to Ping is not received.
|
||||
// Defaults to 15s.
|
||||
PingTimeout time.Duration
|
||||
|
||||
// t1, if non-nil, is the standard library Transport using
|
||||
// this transport. Its settings are used (but not its
|
||||
// RoundTrip method, etc).
|
||||
@@ -131,6 +144,14 @@ func (t *Transport) disableCompression() bool {
|
||||
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
|
||||
}
|
||||
|
||||
func (t *Transport) pingTimeout() time.Duration {
|
||||
if t.PingTimeout == 0 {
|
||||
return 15 * time.Second
|
||||
}
|
||||
return t.PingTimeout
|
||||
|
||||
}
|
||||
|
||||
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||
func ConfigureTransport(t1 *http.Transport) error {
|
||||
@@ -227,6 +248,7 @@ type ClientConn struct {
|
||||
br *bufio.Reader
|
||||
fr *Framer
|
||||
lastActive time.Time
|
||||
lastIdle time.Time // time last idle
|
||||
// Settings from peer: (also guarded by mu)
|
||||
maxFrameSize uint32
|
||||
maxConcurrentStreams uint32
|
||||
@@ -674,6 +696,20 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
func (cc *ClientConn) healthCheck() {
|
||||
pingTimeout := cc.t.pingTimeout()
|
||||
// We don't need to periodically ping in the health check, because the readLoop of ClientConn will
|
||||
// trigger the healthCheck again if there is no frame received.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
||||
defer cancel()
|
||||
err := cc.Ping(ctx)
|
||||
if err != nil {
|
||||
cc.closeForLostPing()
|
||||
cc.t.connPool().MarkDead(cc)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
@@ -736,7 +772,8 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
||||
}
|
||||
|
||||
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay &&
|
||||
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32
|
||||
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 &&
|
||||
!cc.tooIdleLocked()
|
||||
st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest
|
||||
return
|
||||
}
|
||||
@@ -746,6 +783,16 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||
return st.canTakeNewRequest
|
||||
}
|
||||
|
||||
// tooIdleLocked reports whether this connection has been been sitting idle
|
||||
// for too much wall time.
|
||||
func (cc *ClientConn) tooIdleLocked() bool {
|
||||
// The Round(0) strips the monontonic clock reading so the
|
||||
// times are compared based on their wall time. We don't want
|
||||
// to reuse a connection that's been sitting idle during
|
||||
// VM/laptop suspend if monotonic time was also frozen.
|
||||
return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout
|
||||
}
|
||||
|
||||
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
||||
// only be called when we're idle, but because we're coming from a new
|
||||
// goroutine, there could be a new request coming in at the same time,
|
||||
@@ -834,14 +881,12 @@ func (cc *ClientConn) sendGoAway() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
//
|
||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
// closes the client connection immediately. In-flight requests are interrupted.
|
||||
// err is sent to streams.
|
||||
func (cc *ClientConn) closeForError(err error) error {
|
||||
cc.mu.Lock()
|
||||
defer cc.cond.Broadcast()
|
||||
defer cc.mu.Unlock()
|
||||
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
for id, cs := range cc.streams {
|
||||
select {
|
||||
case cs.resc <- resAndError{err: err}:
|
||||
@@ -854,6 +899,20 @@ func (cc *ClientConn) Close() error {
|
||||
return cc.tconn.Close()
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
//
|
||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
return cc.closeForError(err)
|
||||
}
|
||||
|
||||
// closes the client connection immediately. In-flight requests are interrupted.
|
||||
func (cc *ClientConn) closeForLostPing() error {
|
||||
err := errors.New("http2: client connection lost")
|
||||
return cc.closeForError(err)
|
||||
}
|
||||
|
||||
const maxAllocFrameSize = 512 << 10
|
||||
|
||||
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
|
||||
@@ -904,7 +963,7 @@ func commaSeparatedTrailers(req *http.Request) (string, error) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
switch k {
|
||||
case "Transfer-Encoding", "Trailer", "Content-Length":
|
||||
return "", &badStringError{"invalid Trailer key", k}
|
||||
return "", fmt.Errorf("invalid Trailer key %q", k)
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
@@ -1150,6 +1209,7 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
||||
}
|
||||
return errClientConnUnusable
|
||||
}
|
||||
cc.lastIdle = time.Time{}
|
||||
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
|
||||
if waitingForConn != nil {
|
||||
close(waitingForConn)
|
||||
@@ -1381,13 +1441,6 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
|
||||
}
|
||||
}
|
||||
|
||||
type badStringError struct {
|
||||
what string
|
||||
str string
|
||||
}
|
||||
|
||||
func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
||||
|
||||
// requires cc.mu be held.
|
||||
func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) {
|
||||
cc.hbuf.Reset()
|
||||
@@ -1603,6 +1656,7 @@ func (cc *ClientConn) writeHeader(name, value string) {
|
||||
}
|
||||
|
||||
type resAndError struct {
|
||||
_ incomparable
|
||||
res *http.Response
|
||||
err error
|
||||
}
|
||||
@@ -1638,6 +1692,7 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
||||
delete(cc.streams, id)
|
||||
if len(cc.streams) == 0 && cc.idleTimer != nil {
|
||||
cc.idleTimer.Reset(cc.idleTimeout)
|
||||
cc.lastIdle = time.Now()
|
||||
}
|
||||
close(cs.done)
|
||||
// Wake up checkResetOrDone via clientStream.awaitFlowControl and
|
||||
@@ -1649,6 +1704,7 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
||||
|
||||
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
|
||||
type clientConnReadLoop struct {
|
||||
_ incomparable
|
||||
cc *ClientConn
|
||||
closeWhenIdle bool
|
||||
}
|
||||
@@ -1728,8 +1784,17 @@ func (rl *clientConnReadLoop) run() error {
|
||||
rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse
|
||||
gotReply := false // ever saw a HEADERS reply
|
||||
gotSettings := false
|
||||
readIdleTimeout := cc.t.ReadIdleTimeout
|
||||
var t *time.Timer
|
||||
if readIdleTimeout != 0 {
|
||||
t = time.AfterFunc(readIdleTimeout, cc.healthCheck)
|
||||
defer t.Stop()
|
||||
}
|
||||
for {
|
||||
f, err := cc.fr.ReadFrame()
|
||||
if t != nil {
|
||||
t.Reset(readIdleTimeout)
|
||||
}
|
||||
if err != nil {
|
||||
cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err)
|
||||
}
|
||||
@@ -1878,7 +1943,9 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
|
||||
}
|
||||
|
||||
header := make(http.Header)
|
||||
regularFields := f.RegularFields()
|
||||
strs := make([]string, len(regularFields))
|
||||
header := make(http.Header, len(regularFields))
|
||||
res := &http.Response{
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
@@ -1886,7 +1953,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
StatusCode: statusCode,
|
||||
Status: status + " " + http.StatusText(statusCode),
|
||||
}
|
||||
for _, hf := range f.RegularFields() {
|
||||
for _, hf := range regularFields {
|
||||
key := http.CanonicalHeaderKey(hf.Name)
|
||||
if key == "Trailer" {
|
||||
t := res.Trailer
|
||||
@@ -1898,7 +1965,18 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
t[http.CanonicalHeaderKey(v)] = nil
|
||||
})
|
||||
} else {
|
||||
header[key] = append(header[key], hf.Value)
|
||||
vv := header[key]
|
||||
if vv == nil && len(strs) > 0 {
|
||||
// More than likely this will be a single-element key.
|
||||
// Most headers aren't multi-valued.
|
||||
// Set the capacity on strs[0] to 1, so any future append
|
||||
// won't extend the slice into the other strings.
|
||||
vv, strs = strs[:1:1], strs[1:]
|
||||
vv[0] = hf.Value
|
||||
header[key] = vv
|
||||
} else {
|
||||
header[key] = append(vv, hf.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2184,8 +2262,6 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errInvalidTrailers = errors.New("http2: invalid trailers")
|
||||
|
||||
func (rl *clientConnReadLoop) endStream(cs *clientStream) {
|
||||
// TODO: check that any declared content-length matches, like
|
||||
// server.go's (*stream).endStream method.
|
||||
@@ -2416,7 +2492,6 @@ func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error)
|
||||
var (
|
||||
errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
|
||||
errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit")
|
||||
errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers")
|
||||
)
|
||||
|
||||
func (cc *ClientConn) logf(format string, args ...interface{}) {
|
||||
@@ -2455,6 +2530,7 @@ func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
|
||||
// gzipReader wraps a response body so it can lazily
|
||||
// call gzip.NewReader on the first call to Read
|
||||
type gzipReader struct {
|
||||
_ incomparable
|
||||
body io.ReadCloser // underlying Response.Body
|
||||
zr *gzip.Reader // lazily-initialized gzip reader
|
||||
zerr error // sticky error
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// +build go1.13
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package idna
|
||||
|
||||
|
||||
+4733
File diff suppressed because it is too large
Load Diff
+12
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !race
|
||||
|
||||
package socket
|
||||
|
||||
func (m *Message) raceRead() {
|
||||
}
|
||||
func (m *Message) raceWrite() {
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build race
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This package reads and writes the Message buffers using a
|
||||
// direct system call, which the race detector can't see.
|
||||
// These functions tell the race detector what is going on during the syscall.
|
||||
|
||||
func (m *Message) raceRead() {
|
||||
for _, b := range m.Buffers {
|
||||
if len(b) > 0 {
|
||||
runtime.RaceReadRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
if b := m.OOB; len(b) > 0 {
|
||||
runtime.RaceReadRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
func (m *Message) raceWrite() {
|
||||
for _, b := range m.Buffers {
|
||||
if len(b) > 0 {
|
||||
runtime.RaceWriteRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
if b := m.OOB; len(b) > 0 {
|
||||
runtime.RaceWriteRange(unsafe.Pointer(&b[0]), len(b))
|
||||
}
|
||||
}
|
||||
+6
@@ -13,6 +13,9 @@ import (
|
||||
)
|
||||
|
||||
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||
for i := range ms {
|
||||
ms[i].raceWrite()
|
||||
}
|
||||
hs := make(mmsghdrs, len(ms))
|
||||
var parseFn func([]byte, string) (net.Addr, error)
|
||||
if c.network != "tcp" {
|
||||
@@ -43,6 +46,9 @@ func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
|
||||
}
|
||||
|
||||
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
|
||||
for i := range ms {
|
||||
ms[i].raceRead()
|
||||
}
|
||||
hs := make(mmsghdrs, len(ms))
|
||||
var marshalFn func(net.Addr) []byte
|
||||
if c.network != "tcp" {
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||
m.raceWrite()
|
||||
var h msghdr
|
||||
vs := make([]iovec, len(m.Buffers))
|
||||
var sa []byte
|
||||
@@ -48,6 +49,7 @@ func (c *Conn) recvMsg(m *Message, flags int) error {
|
||||
}
|
||||
|
||||
func (c *Conn) sendMsg(m *Message, flags int) error {
|
||||
m.raceRead()
|
||||
var h msghdr
|
||||
vs := make([]iovec, len(m.Buffers))
|
||||
var sa []byte
|
||||
|
||||
+26
-1
@@ -4,4 +4,29 @@
|
||||
|
||||
package socket
|
||||
|
||||
func probeProtocolStack() int { return 4 }
|
||||
import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// See version list in https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/param.h
|
||||
var (
|
||||
osreldateOnce sync.Once
|
||||
osreldate uint32
|
||||
)
|
||||
|
||||
// First __DragonFly_version after September 2019 ABI changes
|
||||
// http://lists.dragonflybsd.org/pipermail/users/2019-September/358280.html
|
||||
const _dragonflyABIChangeVersion = 500705
|
||||
|
||||
func probeProtocolStack() int {
|
||||
osreldateOnce.Do(func() { osreldate, _ = syscall.SysctlUint32("kern.osreldate") })
|
||||
var p uintptr
|
||||
if int(unsafe.Sizeof(p)) == 8 && osreldate >= _dragonflyABIChangeVersion {
|
||||
return int(unsafe.Sizeof(p))
|
||||
}
|
||||
// 64-bit Dragonfly before the September 2019 ABI changes still requires
|
||||
// 32-bit aligned access to network subsystem.
|
||||
return 4
|
||||
}
|
||||
|
||||
+3
-3
@@ -403,9 +403,9 @@ func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, resu
|
||||
|
||||
// Where should scanning start?
|
||||
if dstStart.After(srcStart) {
|
||||
advance := dstStart.Sub(srcStart) / srcInterval
|
||||
srcIndex += int(advance)
|
||||
srcStart = srcStart.Add(advance * srcInterval)
|
||||
advance := int(dstStart.Sub(srcStart) / srcInterval)
|
||||
srcIndex += advance
|
||||
srcStart = srcStart.Add(time.Duration(advance) * srcInterval)
|
||||
}
|
||||
|
||||
// The i'th value is computed as show below.
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@ func marshalInterface(b []byte, cm *ControlMessage) []byte {
|
||||
}
|
||||
|
||||
func parseInterface(cm *ControlMessage, b []byte) {
|
||||
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&b[0]))
|
||||
var sadl syscall.SockaddrDatalink
|
||||
copy((*[unsafe.Sizeof(sadl)]byte)(unsafe.Pointer(&sadl))[:], b)
|
||||
cm.IfIndex = int(sadl.Index)
|
||||
}
|
||||
|
||||
+2
-3
@@ -14,9 +14,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = 4 // protocol version
|
||||
HeaderLen = 20 // header length without extension headers
|
||||
maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
|
||||
Version = 4 // protocol version
|
||||
HeaderLen = 20 // header length without extension headers
|
||||
)
|
||||
|
||||
type HeaderFlags int
|
||||
|
||||
+7
-10
@@ -13,16 +13,13 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidConn = errors.New("invalid connection")
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errMissingHeader = errors.New("missing header")
|
||||
errNilHeader = errors.New("nil header")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errExtHeaderTooShort = errors.New("extension header too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNoSuchInterface = errors.New("no such interface")
|
||||
errNoSuchMulticastInterface = errors.New("no such multicast interface")
|
||||
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
errInvalidConn = errors.New("invalid connection")
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errNilHeader = errors.New("nil header")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errExtHeaderTooShort = errors.New("extension header too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
|
||||
// See https://www.freebsd.org/doc/en/books/porters-handbook/versions.html.
|
||||
freebsdVersion uint32
|
||||
|
||||
+3
@@ -7,12 +7,15 @@
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/internal/socket"
|
||||
)
|
||||
|
||||
var errNoSuchInterface = errors.New("no such interface")
|
||||
|
||||
func (so *sockOpt) setIPMreq(c *socket.Conn, ifi *net.Interface, grp net.IP) error {
|
||||
mreq := ipMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
|
||||
if err := setIPMreqInterface(&mreq, ifi); err != nil {
|
||||
|
||||
+4
-3
@@ -11,13 +11,14 @@ import (
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error {
|
||||
prog := sockFProg{
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(f)),
|
||||
Filter: (*sockFilter)(unsafe.Pointer(&f[0])),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&f[0])),
|
||||
}
|
||||
b := (*[sizeofSockFprog]byte)(unsafe.Pointer(&prog))[:sizeofSockFprog]
|
||||
b := (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&prog))[:unix.SizeofSockFprog]
|
||||
return so.Set(c, b)
|
||||
}
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -35,7 +36,7 @@ var (
|
||||
ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: sysSOL_SOCKET, Name: sysSO_ATTACH_FILTER, Len: sizeofSockFprog}},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: unix.SOL_SOCKET, Name: unix.SO_ATTACH_FILTER, Len: unix.SizeofSockFprog}},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs defs_freebsd.go
|
||||
|
||||
package ipv4
|
||||
|
||||
const (
|
||||
sysIP_OPTIONS = 0x1
|
||||
sysIP_HDRINCL = 0x2
|
||||
sysIP_TOS = 0x3
|
||||
sysIP_TTL = 0x4
|
||||
sysIP_RECVOPTS = 0x5
|
||||
sysIP_RECVRETOPTS = 0x6
|
||||
sysIP_RECVDSTADDR = 0x7
|
||||
sysIP_SENDSRCADDR = 0x7
|
||||
sysIP_RETOPTS = 0x8
|
||||
sysIP_RECVIF = 0x14
|
||||
sysIP_ONESBCAST = 0x17
|
||||
sysIP_BINDANY = 0x18
|
||||
sysIP_RECVTTL = 0x41
|
||||
sysIP_MINTTL = 0x42
|
||||
sysIP_DONTFRAG = 0x43
|
||||
sysIP_RECVTOS = 0x44
|
||||
|
||||
sysIP_MULTICAST_IF = 0x9
|
||||
sysIP_MULTICAST_TTL = 0xa
|
||||
sysIP_MULTICAST_LOOP = 0xb
|
||||
sysIP_ADD_MEMBERSHIP = 0xc
|
||||
sysIP_DROP_MEMBERSHIP = 0xd
|
||||
sysIP_MULTICAST_VIF = 0xe
|
||||
sysIP_ADD_SOURCE_MEMBERSHIP = 0x46
|
||||
sysIP_DROP_SOURCE_MEMBERSHIP = 0x47
|
||||
sysIP_BLOCK_SOURCE = 0x48
|
||||
sysIP_UNBLOCK_SOURCE = 0x49
|
||||
sysMCAST_JOIN_GROUP = 0x50
|
||||
sysMCAST_LEAVE_GROUP = 0x51
|
||||
sysMCAST_JOIN_SOURCE_GROUP = 0x52
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = 0x53
|
||||
sysMCAST_BLOCK_SOURCE = 0x54
|
||||
sysMCAST_UNBLOCK_SOURCE = 0x55
|
||||
|
||||
sizeofSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
|
||||
sizeofIPMreq = 0x8
|
||||
sizeofIPMreqn = 0xc
|
||||
sizeofIPMreqSource = 0xc
|
||||
sizeofGroupReq = 0x88
|
||||
sizeofGroupSourceReq = 0x108
|
||||
)
|
||||
|
||||
type sockaddrStorage struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
X__ss_pad1 [6]uint8
|
||||
X__ss_align int64
|
||||
X__ss_pad2 [112]uint8
|
||||
}
|
||||
|
||||
type sockaddrInet struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
Port uint16
|
||||
Addr [4]byte /* in_addr */
|
||||
Zero [8]uint8
|
||||
}
|
||||
|
||||
type ipMreq struct {
|
||||
Multiaddr [4]byte /* in_addr */
|
||||
Interface [4]byte /* in_addr */
|
||||
}
|
||||
|
||||
type ipMreqn struct {
|
||||
Multiaddr [4]byte /* in_addr */
|
||||
Address [4]byte /* in_addr */
|
||||
Ifindex int32
|
||||
}
|
||||
|
||||
type ipMreqSource struct {
|
||||
Multiaddr [4]byte /* in_addr */
|
||||
Sourceaddr [4]byte /* in_addr */
|
||||
Interface [4]byte /* in_addr */
|
||||
}
|
||||
|
||||
type groupReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
}
|
||||
|
||||
type groupSourceReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
Source sockaddrStorage
|
||||
}
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -133,16 +128,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -133,16 +128,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -133,16 +128,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -133,16 +128,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -133,16 +128,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-17
@@ -57,9 +57,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -72,8 +69,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -137,15 +132,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -55,9 +55,6 @@ const (
|
||||
sysSO_EE_ORIGIN_TXSTATUS = 0x4
|
||||
sysSO_EE_ORIGIN_TIMESTAMPING = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet = 0x10
|
||||
sizeofInetPktinfo = 0xc
|
||||
@@ -70,8 +67,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPFilter = 0x4
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -135,16 +130,3 @@ type groupSourceReq struct {
|
||||
type icmpFilter struct {
|
||||
Data uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-1
@@ -15,7 +15,6 @@ var (
|
||||
errMissingAddress = errors.New("missing address")
|
||||
errHeaderTooShort = errors.New("header too short")
|
||||
errInvalidConnType = errors.New("invalid conn type")
|
||||
errNoSuchInterface = errors.New("no such interface")
|
||||
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||
)
|
||||
|
||||
|
||||
+4
-3
@@ -11,13 +11,14 @@ import (
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error {
|
||||
prog := sockFProg{
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(f)),
|
||||
Filter: (*sockFilter)(unsafe.Pointer(&f[0])),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&f[0])),
|
||||
}
|
||||
b := (*[sizeofSockFprog]byte)(unsafe.Pointer(&prog))[:sizeofSockFprog]
|
||||
b := (*[unix.SizeofSockFprog]byte)(unsafe.Pointer(&prog))[:unix.SizeofSockFprog]
|
||||
return so.Set(c, b)
|
||||
}
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/internal/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -40,7 +41,7 @@ var (
|
||||
ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: sysSOL_SOCKET, Name: sysSO_ATTACH_FILTER, Len: sizeofSockFprog}},
|
||||
ssoAttachFilter: {Option: socket.Option{Level: unix.SOL_SOCKET, Name: unix.SO_ATTACH_FILTER, Len: unix.SizeofSockFprog}},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs defs_freebsd.go
|
||||
|
||||
package ipv6
|
||||
|
||||
const (
|
||||
sysIPV6_UNICAST_HOPS = 0x4
|
||||
sysIPV6_MULTICAST_IF = 0x9
|
||||
sysIPV6_MULTICAST_HOPS = 0xa
|
||||
sysIPV6_MULTICAST_LOOP = 0xb
|
||||
sysIPV6_JOIN_GROUP = 0xc
|
||||
sysIPV6_LEAVE_GROUP = 0xd
|
||||
sysIPV6_PORTRANGE = 0xe
|
||||
sysICMP6_FILTER = 0x12
|
||||
|
||||
sysIPV6_CHECKSUM = 0x1a
|
||||
sysIPV6_V6ONLY = 0x1b
|
||||
|
||||
sysIPV6_IPSEC_POLICY = 0x1c
|
||||
|
||||
sysIPV6_RTHDRDSTOPTS = 0x23
|
||||
|
||||
sysIPV6_RECVPKTINFO = 0x24
|
||||
sysIPV6_RECVHOPLIMIT = 0x25
|
||||
sysIPV6_RECVRTHDR = 0x26
|
||||
sysIPV6_RECVHOPOPTS = 0x27
|
||||
sysIPV6_RECVDSTOPTS = 0x28
|
||||
|
||||
sysIPV6_USE_MIN_MTU = 0x2a
|
||||
sysIPV6_RECVPATHMTU = 0x2b
|
||||
|
||||
sysIPV6_PATHMTU = 0x2c
|
||||
|
||||
sysIPV6_PKTINFO = 0x2e
|
||||
sysIPV6_HOPLIMIT = 0x2f
|
||||
sysIPV6_NEXTHOP = 0x30
|
||||
sysIPV6_HOPOPTS = 0x31
|
||||
sysIPV6_DSTOPTS = 0x32
|
||||
sysIPV6_RTHDR = 0x33
|
||||
|
||||
sysIPV6_RECVTCLASS = 0x39
|
||||
|
||||
sysIPV6_AUTOFLOWLABEL = 0x3b
|
||||
|
||||
sysIPV6_TCLASS = 0x3d
|
||||
sysIPV6_DONTFRAG = 0x3e
|
||||
|
||||
sysIPV6_PREFER_TEMPADDR = 0x3f
|
||||
|
||||
sysIPV6_BINDANY = 0x40
|
||||
|
||||
sysIPV6_MSFILTER = 0x4a
|
||||
|
||||
sysMCAST_JOIN_GROUP = 0x50
|
||||
sysMCAST_LEAVE_GROUP = 0x51
|
||||
sysMCAST_JOIN_SOURCE_GROUP = 0x52
|
||||
sysMCAST_LEAVE_SOURCE_GROUP = 0x53
|
||||
sysMCAST_BLOCK_SOURCE = 0x54
|
||||
sysMCAST_UNBLOCK_SOURCE = 0x55
|
||||
|
||||
sysIPV6_PORTRANGE_DEFAULT = 0x0
|
||||
sysIPV6_PORTRANGE_HIGH = 0x1
|
||||
sysIPV6_PORTRANGE_LOW = 0x2
|
||||
|
||||
sizeofSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
sizeofIPv6Mtuinfo = 0x20
|
||||
|
||||
sizeofIPv6Mreq = 0x14
|
||||
sizeofGroupReq = 0x88
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
)
|
||||
|
||||
type sockaddrStorage struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
X__ss_pad1 [6]uint8
|
||||
X__ss_align int64
|
||||
X__ss_pad2 [112]uint8
|
||||
}
|
||||
|
||||
type sockaddrInet6 struct {
|
||||
Len uint8
|
||||
Family uint8
|
||||
Port uint16
|
||||
Flowinfo uint32
|
||||
Addr [16]byte /* in6_addr */
|
||||
Scope_id uint32
|
||||
}
|
||||
|
||||
type inet6Pktinfo struct {
|
||||
Addr [16]byte /* in6_addr */
|
||||
Ifindex uint32
|
||||
}
|
||||
|
||||
type ipv6Mtuinfo struct {
|
||||
Addr sockaddrInet6
|
||||
Mtu uint32
|
||||
}
|
||||
|
||||
type ipv6Mreq struct {
|
||||
Multiaddr [16]byte /* in6_addr */
|
||||
Interface uint32
|
||||
}
|
||||
|
||||
type groupReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
}
|
||||
|
||||
type groupSourceReq struct {
|
||||
Interface uint32
|
||||
Group sockaddrStorage
|
||||
Source sockaddrStorage
|
||||
}
|
||||
|
||||
type icmpv6Filter struct {
|
||||
Filt [8]uint32
|
||||
}
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -155,16 +150,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -155,16 +150,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -155,16 +150,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -155,16 +150,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x104
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x8
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -155,16 +150,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [2]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-17
@@ -86,9 +86,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -100,8 +97,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -159,15 +154,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
-18
@@ -84,9 +84,6 @@ const (
|
||||
sysICMPV6_FILTER_BLOCKOTHERS = 0x3
|
||||
sysICMPV6_FILTER_PASSONLY = 0x4
|
||||
|
||||
sysSOL_SOCKET = 0x1
|
||||
sysSO_ATTACH_FILTER = 0x1a
|
||||
|
||||
sizeofKernelSockaddrStorage = 0x80
|
||||
sizeofSockaddrInet6 = 0x1c
|
||||
sizeofInet6Pktinfo = 0x14
|
||||
@@ -98,8 +95,6 @@ const (
|
||||
sizeofGroupSourceReq = 0x108
|
||||
|
||||
sizeofICMPv6Filter = 0x20
|
||||
|
||||
sizeofSockFprog = 0x10
|
||||
)
|
||||
|
||||
type kernelSockaddrStorage struct {
|
||||
@@ -157,16 +152,3 @@ type groupSourceReq struct {
|
||||
type icmpv6Filter struct {
|
||||
Data [8]uint32
|
||||
}
|
||||
|
||||
type sockFProg struct {
|
||||
Len uint16
|
||||
Pad_cgo_0 [6]byte
|
||||
Filter *sockFilter
|
||||
}
|
||||
|
||||
type sockFilter struct {
|
||||
Code uint16
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
K uint32
|
||||
}
|
||||
|
||||
+8
-3
@@ -39,20 +39,25 @@ func (bigEndian) Uint64(b []byte) uint64 {
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
}
|
||||
|
||||
// hostByteOrder returns binary.LittleEndian on little-endian machines and
|
||||
// binary.BigEndian on big-endian machines.
|
||||
// hostByteOrder returns littleEndian on little-endian machines and
|
||||
// bigEndian on big-endian machines.
|
||||
func hostByteOrder() byteOrder {
|
||||
switch runtime.GOARCH {
|
||||
case "386", "amd64", "amd64p32",
|
||||
"alpha",
|
||||
"arm", "arm64",
|
||||
"mipsle", "mips64le", "mips64p32le",
|
||||
"nios2",
|
||||
"ppc64le",
|
||||
"riscv", "riscv64":
|
||||
"riscv", "riscv64",
|
||||
"sh":
|
||||
return littleEndian{}
|
||||
case "armbe", "arm64be",
|
||||
"m68k",
|
||||
"mips", "mips64", "mips64p32",
|
||||
"ppc", "ppc64",
|
||||
"s390", "s390x",
|
||||
"shbe",
|
||||
"sparc", "sparc64":
|
||||
return bigEndian{}
|
||||
}
|
||||
|
||||
+9
@@ -114,6 +114,15 @@ var ARM struct {
|
||||
_ CacheLinePad
|
||||
}
|
||||
|
||||
// MIPS64X contains the supported CPU features of the current mips64/mips64le
|
||||
// platforms. If the current platform is not mips64/mips64le or the current
|
||||
// operating system is not Linux then all feature flags are false.
|
||||
var MIPS64X struct {
|
||||
_ CacheLinePad
|
||||
HasMSA bool // MIPS SIMD architecture
|
||||
_ CacheLinePad
|
||||
}
|
||||
|
||||
// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms.
|
||||
// If the current platform is not ppc64/ppc64le then all feature flags are false.
|
||||
//
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix,ppc64
|
||||
// +build aix
|
||||
|
||||
package cpu
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
import "runtime"
|
||||
|
||||
const cacheLineSize = 64
|
||||
|
||||
func init() {
|
||||
switch runtime.GOOS {
|
||||
case "android", "darwin", "netbsd":
|
||||
// Android and iOS don't seem to allow reading these registers.
|
||||
//
|
||||
// NetBSD:
|
||||
// ID_AA64ISAR0_EL1 is a privileged register and cannot be read from EL0.
|
||||
// It can be read via sysctl(3). Example for future implementers:
|
||||
// https://nxr.netbsd.org/xref/src/usr.sbin/cpuctl/arch/aarch64.c
|
||||
//
|
||||
// Fake the minimal features expected by
|
||||
// TestARM64minimalFeatures.
|
||||
ARM64.HasASIMD = true
|
||||
ARM64.HasFP = true
|
||||
case "linux":
|
||||
doinit()
|
||||
default:
|
||||
readARM64Registers()
|
||||
}
|
||||
}
|
||||
|
||||
func readARM64Registers() {
|
||||
Initialized = true
|
||||
|
||||
// ID_AA64ISAR0_EL1
|
||||
isar0 := getisar0()
|
||||
|
||||
switch extractBits(isar0, 4, 7) {
|
||||
case 1:
|
||||
ARM64.HasAES = true
|
||||
case 2:
|
||||
ARM64.HasAES = true
|
||||
ARM64.HasPMULL = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 8, 11) {
|
||||
case 1:
|
||||
ARM64.HasSHA1 = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 12, 15) {
|
||||
case 1:
|
||||
ARM64.HasSHA2 = true
|
||||
case 2:
|
||||
ARM64.HasSHA2 = true
|
||||
ARM64.HasSHA512 = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 16, 19) {
|
||||
case 1:
|
||||
ARM64.HasCRC32 = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 20, 23) {
|
||||
case 2:
|
||||
ARM64.HasATOMICS = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 28, 31) {
|
||||
case 1:
|
||||
ARM64.HasASIMDRDM = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 32, 35) {
|
||||
case 1:
|
||||
ARM64.HasSHA3 = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 36, 39) {
|
||||
case 1:
|
||||
ARM64.HasSM3 = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 40, 43) {
|
||||
case 1:
|
||||
ARM64.HasSM4 = true
|
||||
}
|
||||
|
||||
switch extractBits(isar0, 44, 47) {
|
||||
case 1:
|
||||
ARM64.HasASIMDDP = true
|
||||
}
|
||||
|
||||
// ID_AA64ISAR1_EL1
|
||||
isar1 := getisar1()
|
||||
|
||||
switch extractBits(isar1, 0, 3) {
|
||||
case 1:
|
||||
ARM64.HasDCPOP = true
|
||||
}
|
||||
|
||||
switch extractBits(isar1, 12, 15) {
|
||||
case 1:
|
||||
ARM64.HasJSCVT = true
|
||||
}
|
||||
|
||||
switch extractBits(isar1, 16, 19) {
|
||||
case 1:
|
||||
ARM64.HasFCMA = true
|
||||
}
|
||||
|
||||
switch extractBits(isar1, 20, 23) {
|
||||
case 1:
|
||||
ARM64.HasLRCPC = true
|
||||
}
|
||||
|
||||
// ID_AA64PFR0_EL1
|
||||
pfr0 := getpfr0()
|
||||
|
||||
switch extractBits(pfr0, 16, 19) {
|
||||
case 0:
|
||||
ARM64.HasFP = true
|
||||
case 1:
|
||||
ARM64.HasFP = true
|
||||
ARM64.HasFPHP = true
|
||||
}
|
||||
|
||||
switch extractBits(pfr0, 20, 23) {
|
||||
case 0:
|
||||
ARM64.HasASIMD = true
|
||||
case 1:
|
||||
ARM64.HasASIMD = true
|
||||
ARM64.HasASIMDHP = true
|
||||
}
|
||||
|
||||
switch extractBits(pfr0, 32, 35) {
|
||||
case 1:
|
||||
ARM64.HasSVE = true
|
||||
}
|
||||
}
|
||||
|
||||
func extractBits(data uint64, start, end uint) uint {
|
||||
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func getisar0() uint64
|
||||
TEXT ·getisar0(SB),NOSPLIT,$0-8
|
||||
// get Instruction Set Attributes 0 into x0
|
||||
// mrs x0, ID_AA64ISAR0_EL1 = d5380600
|
||||
WORD $0xd5380600
|
||||
MOVD R0, ret+0(FP)
|
||||
RET
|
||||
|
||||
// func getisar1() uint64
|
||||
TEXT ·getisar1(SB),NOSPLIT,$0-8
|
||||
// get Instruction Set Attributes 1 into x0
|
||||
// mrs x0, ID_AA64ISAR1_EL1 = d5380620
|
||||
WORD $0xd5380620
|
||||
MOVD R0, ret+0(FP)
|
||||
RET
|
||||
|
||||
// func getpfr0() uint64
|
||||
TEXT ·getpfr0(SB),NOSPLIT,$0-8
|
||||
// get Processor Feature Register 0 into x0
|
||||
// mrs x0, ID_AA64PFR0_EL1 = d5380400
|
||||
WORD $0xd5380400
|
||||
MOVD R0, ret+0(FP)
|
||||
RET
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo
|
||||
|
||||
package cpu
|
||||
|
||||
func getisar0() uint64
|
||||
func getisar1() uint64
|
||||
func getpfr0() uint64
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gccgo
|
||||
|
||||
package cpu
|
||||
|
||||
func getisar0() uint64 { return 0 }
|
||||
func getisar1() uint64 { return 0 }
|
||||
func getpfr0() uint64 { return 0 }
|
||||
Generated
Vendored
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user