mirror of
https://github.com/taigrr/godns
synced 2025-01-18 04:03:25 -08:00
Compare commits
No commits in common. "master" and "V1.6.1" have entirely different histories.
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
@ -1,12 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
custom: https://www.paypal.me/timothyye
|
|
||||||
20
.github/workflows/go.yml
vendored
20
.github/workflows/go.yml
vendored
@ -1,20 +0,0 @@
|
|||||||
name: Go
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Set up Go 1.15.7
|
|
||||||
uses: actions/setup-go@v1
|
|
||||||
with:
|
|
||||||
go-version: 1.15.7
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: cd cmd/godns && go build -v .
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -28,9 +28,7 @@ config.json
|
|||||||
*.log
|
*.log
|
||||||
*.swp
|
*.swp
|
||||||
*.gz
|
*.gz
|
||||||
godns
|
cmd/godns/godns
|
||||||
godns.exe
|
|
||||||
config.json
|
|
||||||
cmd/godns/config.json
|
cmd/godns/config.json
|
||||||
|
|
||||||
/.idea
|
/.idea
|
||||||
|
|||||||
17
.travis.yml
Normal file
17
.travis.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -v
|
||||||
|
- go get -v github.com/bitly/go-simplejson
|
||||||
|
- go get -v github.com/fatih/color
|
||||||
|
- go get -u github.com/alecthomas/gometalinter
|
||||||
|
script:
|
||||||
|
- gometalinter --install
|
||||||
|
- gometalinter --config=./gometalinter.json --vendor ./...
|
||||||
|
- cp ./config_sample.json ./config.json
|
||||||
|
- go test -v ./...
|
||||||
@ -1,76 +0,0 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to making participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces
|
|
||||||
when an individual is representing the project or its community. Examples of
|
|
||||||
representing a project or community include using an official project e-mail
|
|
||||||
address, posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event. Representation of a project may be
|
|
||||||
further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at i@xiaozhou.net. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
|
||||||
https://www.contributor-covenant.org/faq
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
FROM golang:alpine AS builder
|
|
||||||
RUN mkdir /godns
|
|
||||||
ADD . /godns/
|
|
||||||
WORKDIR /godns
|
|
||||||
RUN CGO_ENABLED=0 go build -o godns cmd/godns/godns.go
|
|
||||||
|
|
||||||
FROM gcr.io/distroless/base
|
|
||||||
COPY --from=builder /godns/godns /godns
|
|
||||||
ENTRYPOINT ["/godns"]
|
|
||||||
81
Gopkg.lock
generated
Normal file
81
Gopkg.lock
generated
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:b520b55fc1146c5b0eea03b07233f7a3d4a9be985c037c91ea6b82ecb81bd521"
|
||||||
|
name = "github.com/bitly/go-simplejson"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "aabad6e819789e569bd6aabf444c935aa9ba1e44"
|
||||||
|
version = "v0.5.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
|
||||||
|
name = "github.com/fatih/color"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
|
||||||
|
version = "v1.7.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
||||||
|
name = "github.com/mattn/go-colorable"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||||
|
version = "v0.0.9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
||||||
|
name = "github.com/mattn/go-isatty"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||||
|
version = "v0.0.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:8c520a50d95df847d7732e971616b443afb0d95c3be465e3952bfde515d4e47a"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"internal/socks",
|
||||||
|
"proxy",
|
||||||
|
]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "faa378e6dbaed88bd8100f8bcf09939375c6e8fa"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:4dcdb3f7c22f5f048de7c7ad5b72c4547d63bfb4923da15538d0051d553b2ef8"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "11551d06cbcc94edc80a0facaccbda56473c19c1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "v3"
|
||||||
|
digest = "1:7388652e2215a3f45d341d58766ed58317971030eb1cbd75f005f96ace8e9196"
|
||||||
|
name = "gopkg.in/alexcesaro/quotedprintable.v3"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "2caba252f4dc53eaf6b553000885530023f54623"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:eebd52aee67d9f1e0af1859a584e14b1817581dd5f90d4585418c27b6a93c966"
|
||||||
|
name = "gopkg.in/gomail.v2"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "UT"
|
||||||
|
revision = "41f3572897373c5538c50a2402db15db079fa4fd"
|
||||||
|
version = "2.0.0"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
input-imports = [
|
||||||
|
"github.com/bitly/go-simplejson",
|
||||||
|
"github.com/fatih/color",
|
||||||
|
"golang.org/x/net/proxy",
|
||||||
|
"gopkg.in/gomail.v2",
|
||||||
|
]
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
||||||
46
Gopkg.toml
Normal file
46
Gopkg.toml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/bitly/go-simplejson"
|
||||||
|
version = "0.5.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/fatih/color"
|
||||||
|
version = "1.7.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "gopkg.in/gomail.v2"
|
||||||
|
version = "2.0.0"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
44
Makefile
44
Makefile
@ -1,44 +0,0 @@
|
|||||||
# Binary name
|
|
||||||
BINARY=godns
|
|
||||||
# Builds the project
|
|
||||||
build:
|
|
||||||
GO111MODULE=on go build -ldflags "-X main.Version=${VERSION}" -o ${BINARY} cmd/godns/godns.go
|
|
||||||
# Installs our project: copies binaries
|
|
||||||
install:
|
|
||||||
GO111MODULE=on go install
|
|
||||||
image:
|
|
||||||
# Build docker image
|
|
||||||
go clean
|
|
||||||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t timothyye/godns:${VERSION} . --push
|
|
||||||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t timothyye/godns:latest . --push
|
|
||||||
release:
|
|
||||||
# Clean
|
|
||||||
go clean
|
|
||||||
rm -rf *.gz
|
|
||||||
# Build for mac
|
|
||||||
GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}" cmd/godns/godns.go
|
|
||||||
tar czvf ${BINARY}-mac64-${VERSION}.tar.gz ./${BINARY}
|
|
||||||
# Build for linux
|
|
||||||
go clean
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}" cmd/godns/godns.go
|
|
||||||
tar czvf ${BINARY}-linux64-${VERSION}.tar.gz ./${BINARY}
|
|
||||||
# Build for arm
|
|
||||||
go clean
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}" cmd/godns/godns.go
|
|
||||||
tar czvf ${BINARY}-arm64-${VERSION}.tar.gz ./${BINARY}
|
|
||||||
go clean
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}" cmd/godns/godns.go
|
|
||||||
tar czvf ${BINARY}-arm-${VERSION}.tar.gz ./${BINARY}
|
|
||||||
# Build for win
|
|
||||||
go clean
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}" cmd/godns/godns.go
|
|
||||||
tar czvf ${BINARY}-win64-${VERSION}.tar.gz ./${BINARY}.exe
|
|
||||||
make image
|
|
||||||
# Cleans our projects: deletes binaries
|
|
||||||
clean:
|
|
||||||
go clean
|
|
||||||
rm -rf ./godns
|
|
||||||
rm -rf ./godns.exe
|
|
||||||
rm -rf *.gz
|
|
||||||
|
|
||||||
.PHONY: clean build
|
|
||||||
593
README.md
593
README.md
@ -7,8 +7,10 @@
|
|||||||
╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝
|
╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝
|
||||||
```
|
```
|
||||||
|
|
||||||
[![Apache licensed][9]][10] [![Docker][3]][4] [![Go Report Card][11]][12] [![Cover.Run][15]][16] [![GoDoc][13]][14]
|
[![MIT licensed][9]][10] [![Build Status][1]][2] [![Docker][3]][4] [![Go Report Card][11]][12] [![Cover.Run][15]][16] [![GoDoc][13]][14]
|
||||||
|
|
||||||
|
[1]: https://travis-ci.org/TimothyYe/godns.svg?branch=master
|
||||||
|
[2]: https://travis-ci.org/TimothyYe/godns
|
||||||
[3]: https://images.microbadger.com/badges/image/timothyye/godns.svg
|
[3]: https://images.microbadger.com/badges/image/timothyye/godns.svg
|
||||||
[4]: https://microbadger.com/images/timothyye/godns
|
[4]: https://microbadger.com/images/timothyye/godns
|
||||||
[9]: https://img.shields.io/badge/license-Apache-blue.svg
|
[9]: https://img.shields.io/badge/license-Apache-blue.svg
|
||||||
@ -20,152 +22,86 @@
|
|||||||
[15]: https://img.shields.io/badge/cover.run-88.2%25-green.svg
|
[15]: https://img.shields.io/badge/cover.run-88.2%25-green.svg
|
||||||
[16]: https://cover.run/go/github.com/timothyye/godns
|
[16]: https://cover.run/go/github.com/timothyye/godns
|
||||||
|
|
||||||
[GoDNS](https://github.com/TimothyYe/godns) is a dynamic DNS (DDNS) client tool. It is a rewrite in [Go](https://golang.org) of my early [DynDNS](https://github.com/TimothyYe/DynDNS) open source project.
|
GoDNS is a dynamic DNS (DDNS) client tool, it is based on my early open source project: [DynDNS](https://github.com/TimothyYe/DynDNS).
|
||||||
|
|
||||||
Currently supports updating A records for subdomains. Doesn't support updating of root domains.
|
Now I rewrite [DynDNS](https://github.com/TimothyYe/DynDNS) by Golang and call it [GoDNS](https://github.com/TimothyYe/godns).
|
||||||
|
|
||||||
---
|
## Supported DNS Provider
|
||||||
- [Supported DNS Providers](#supported-dns-providers)
|
* Cloudflare ([https://cloudflare.com](https://cloudflare.com))
|
||||||
- [Supported Platforms](#supported-platforms)
|
* DNSPod ([https://www.dnspod.cn/](https://www.dnspod.cn/))
|
||||||
- [Pre-conditions](#pre-conditions)
|
* HE.net (Hurricane Electric) ([https://dns.he.net/](https://dns.he.net/))
|
||||||
- [Installation](#installation)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [Configuration](#configuration)
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Configuration properties](#configuration-properties)
|
|
||||||
- [Configuration examples](#configuration-examples)
|
|
||||||
- [Cloudflare](#cloudflare)
|
|
||||||
- [DNSPod](#dnspod)
|
|
||||||
- [Dreamhost](#dreamhost)
|
|
||||||
- [Google Domains](#google-domains)
|
|
||||||
- [AliDNS](#alidns)
|
|
||||||
- [DuckDNS](#duckdns)
|
|
||||||
- [No-IP](#no-ip)
|
|
||||||
- [HE.net](#henet)
|
|
||||||
- [Notifications](#notifications)
|
|
||||||
- [Email](#email)
|
|
||||||
- [Telegram](#telegram)
|
|
||||||
- [Slack](#slack)
|
|
||||||
- [Miscellaneous topics](#miscellaneous-topics)
|
|
||||||
- [IPv6 support](#ipv6-support)
|
|
||||||
- [Network interface IP address](#network-interface-ip-address)
|
|
||||||
- [SOCKS5 proxy support](#socks5-proxy-support)
|
|
||||||
- [Running GoDNS](#running-godns)
|
|
||||||
- [As a manual daemon](#as-a-manual-daemon)
|
|
||||||
- [As a managed daemon (with upstart)](#as-a-managed-daemon-with-upstart)
|
|
||||||
- [As a managed daemon (with systemd)](#as-a-managed-daemon-with-systemd)
|
|
||||||
- [As a Docker container](#as-a-docker-container)
|
|
||||||
- [As a Windows service](#as-a-windows-service)
|
|
||||||
- [Special Thanks](#special-thanks)
|
|
||||||
|
|
||||||
---
|
|
||||||
## Supported DNS Providers
|
|
||||||
|
|
||||||
| Provider | IPv4 support | IPv6 support | Root Domain | Subdomains |
|
|
||||||
| ------------------------------------- | :----------------: | :----------------: | :----------------: | :----------------: |
|
|
||||||
| [Cloudflare][cloudflare] | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
|
||||||
| [Google Domains][google.domains] | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
|
||||||
| [DNSPod][dnspod] | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
|
||||||
| [HE.net (Hurricane Electric)][he.net] | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
|
||||||
| [AliDNS][alidns] | :white_check_mark: | :x: | :x: | :white_check_mark: |
|
|
||||||
| [DuckDNS][duckdns] | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
|
||||||
| [Dreamhost][dreamhost] | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
|
||||||
| [No-IP][no-ip] | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
|
||||||
|
|
||||||
[cloudflare]: https://cloudflare.com
|
|
||||||
[google.domains]: https://domains.google
|
|
||||||
[dnspod]: https://www.dnspod.cn
|
|
||||||
[he.net]: https://dns.he.net
|
|
||||||
[alidns]: https://help.aliyun.com/product/29697.html
|
|
||||||
[duckdns]: https://www.duckdns.org
|
|
||||||
[dreamhost]: https://www.dreamhost.com
|
|
||||||
[no-ip]: https://www.noip.com
|
|
||||||
|
|
||||||
Tip: You can follow this [issue](https://github.com/TimothyYe/godns/issues/76) to view the current status of DDNS for root domains.
|
|
||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
|
|
||||||
* Linux
|
* Linux
|
||||||
* MacOS
|
* MacOS
|
||||||
* ARM Linux (Raspberry Pi, etc.)
|
* ARM Linux (Raspberry Pi, etc...)
|
||||||
* Windows
|
* Windows
|
||||||
* MIPS32 platform
|
* MIPS32 platform
|
||||||
|
|
||||||
To compile binaries for MIPS (mips or mipsle), run:
|
## MIPS32 platform
|
||||||
|
|
||||||
```bash
|
To compile binaries for MIPS (mips or mipsle):
|
||||||
GOOS=linux GOARCH=mips/mipsle GOMIPS=softfloat go build -a
|
|
||||||
```
|
|
||||||
|
|
||||||
The binary can run on routers as well.
|
```
|
||||||
|
GOOS=linux GOARCH=mips/mipsle GOMIPS=softfloat go build -a
|
||||||
## Pre-conditions
|
|
||||||
|
|
||||||
To use GoDNS, it is assumed:
|
|
||||||
* You registered (now own) a domain
|
|
||||||
* Domain was delegated to a supported [DNS provider](#supported-dns-providers) (i.e. it has nameserver `NS` records pointing at a supported provider)
|
|
||||||
|
|
||||||
Alternatively, you can sign in to [DuckDNS](https://www.duckdns.org) (with a social account) and get a subdomain on the duckdns.org domain for free.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Build GoDNS by running (from the root of the repository):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd cmd/godns # go to the GoDNS directory
|
|
||||||
go get -v # get dependencies
|
|
||||||
go build # build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also download a compiled binary from the [releases](https://github.com/TimothyYe/godns/releases).
|
And the binary can run well on routers.
|
||||||
## Usage
|
|
||||||
|
|
||||||
Print usage/help by running:
|
## Pre-condition
|
||||||
|
|
||||||
|
* Register and own a domain.
|
||||||
|
|
||||||
|
* Domain's nameserver points to [DNSPod](https://www.dnspod.cn/) or [HE.net](https://dns.he.net/) or [Cloudflare](https://www.cloudflare.com/).
|
||||||
|
|
||||||
|
## Get it
|
||||||
|
|
||||||
|
### Build it from source code
|
||||||
|
|
||||||
|
* Get source code from Github:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/timothyye/godns.git
|
||||||
|
```
|
||||||
|
* Go into the godns directory, get related library and then build it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd cmd/godns
|
||||||
|
go get -v
|
||||||
|
go build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download from releases
|
||||||
|
|
||||||
|
Download compiled binaries from [releases](https://github.com/TimothyYe/godns/releases)
|
||||||
|
|
||||||
|
## Get help
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ./godns -h
|
$ ./godns -h
|
||||||
Usage of ./godns:
|
Usage of ./godns:
|
||||||
-c string
|
-c string
|
||||||
Specify a config file (default "./config.json")
|
Specify a config file (default "./config.json")
|
||||||
|
-d Run it as docker mode
|
||||||
-h Show help
|
-h Show help
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Config it
|
||||||
|
|
||||||
### Overview
|
* Get [config_sample.json](https://github.com/timothyye/godns/blob/master/config_sample.json) from Github.
|
||||||
|
* Rename it to **config.json**.
|
||||||
|
* Configure your provider, domain/sub-domain info, username and password, etc.
|
||||||
|
* Configure the SMTP options if you want, a mail notification will sent to your mailbox once the IP is changed.
|
||||||
|
* Save it in the same directory of GoDNS, or use -c=your_conf_path command.
|
||||||
|
|
||||||
* Make a copy of [config_sample.json](./config_sample.json) and name it `config.json`
|
### Config example for Cloudflare
|
||||||
* Configure your provider, domain/subdomain info, credentials, etc.
|
|
||||||
* Configure a notification medium (e.g. SMTP to receive emails) to get notified when your IP address changes
|
|
||||||
* Place the file in the same directory of GoDNS or use the `-c=path/to/your/file.json` option
|
|
||||||
|
|
||||||
### Configuration properties
|
For Cloudflare, you need to provide email & Global API Key as password, and config all the domains & subdomains.
|
||||||
|
|
||||||
* `provider` — One of the [supported provider to use](#supported-dns-providers): `Cloudflare`, `Google`, `DNSPod`, `AliDNS`, `HE`, `DuckDNS` or `Dreamhost`.
|
|
||||||
* `email` — Email or account name of the DNS provider.
|
|
||||||
* `password` — Password of the DNS provider.
|
|
||||||
* `login_token` — API token of the DNS provider.
|
|
||||||
* `domains` — Domains list, with your sub domains.
|
|
||||||
* `ip_url` — A URL for fetching one's public IPv4 address.
|
|
||||||
* `ipv6_url` — A URL for fetching one's public IPv6 address.
|
|
||||||
* `ip_type` — Switch deciding if IPv4 or IPv6 should be used (when [supported](#supported-dns-providers)). Available values: `IPv4` or `IPv6`.
|
|
||||||
* `interval` — How often (in seconds) the public IP should be updated.
|
|
||||||
* `socks5_proxy` — Socks5 proxy server.
|
|
||||||
* `resolver` — Address of a public DNS server to use. For instance to use [Google's public DNS](https://developers.google.com/speed/public-dns/docs/using), you can set `8.8.8.8` when using GoDNS in IPv4 mode or `2001:4860:4860::8888` in IPv6 mode.
|
|
||||||
|
|
||||||
### Configuration examples
|
|
||||||
|
|
||||||
#### Cloudflare
|
|
||||||
|
|
||||||
For Cloudflare, you need to provide the email & Global API Key as password (or to use the API token) and config all the domains & subdomains.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Using email & Global API Key</summary>
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"provider": "Cloudflare",
|
"provider": "Cloudflare",
|
||||||
"email": "you@example.com",
|
"email": "you@example.com"
|
||||||
"password": "Global API Key",
|
"password": "Global API Key",
|
||||||
"domains": [{
|
"domains": [{
|
||||||
"domain_name": "example.com",
|
"domain_name": "example.com",
|
||||||
@ -175,47 +111,19 @@ For Cloudflare, you need to provide the email & Global API Key as password (or t
|
|||||||
"sub_domains": ["www","test"]
|
"sub_domains": ["www","test"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resolver": "8.8.8.8",
|
"ip_url": "https://ifconfig.co/ip",
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
"socks5_proxy": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
### Config example for DNSPod
|
||||||
<summary>Using the API Token</summary>
|
|
||||||
|
|
||||||
```json
|
For DNSPod, you need to provide email & password, and config all the domains & subdomains.
|
||||||
{
|
|
||||||
"provider": "Cloudflare",
|
|
||||||
"login_token": "API Token",
|
|
||||||
"domains": [{
|
|
||||||
"domain_name": "example.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
},{
|
|
||||||
"domain_name": "example2.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### DNSPod
|
|
||||||
|
|
||||||
For DNSPod, you need to provide your API Token(you can create it [here](https://www.dnspod.cn/console/user/security)), and config all the domains & subdomains.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"provider": "DNSPod",
|
"provider": "DNSPod",
|
||||||
|
"password": "",
|
||||||
"login_token": "your_id,your_token",
|
"login_token": "your_id,your_token",
|
||||||
"domains": [{
|
"domains": [{
|
||||||
"domain_name": "example.com",
|
"domain_name": "example.com",
|
||||||
@ -225,161 +133,14 @@ For DNSPod, you need to provide your API Token(you can create it [here](https://
|
|||||||
"sub_domains": ["www","test"]
|
"sub_domains": ["www","test"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resolver": "8.8.8.8",
|
"ip_url": "http://members.3322.org/dyndns/getip",
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"ip_type": "IPV4",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
"socks5_proxy": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</details>
|
### Config example for HE.net
|
||||||
|
|
||||||
#### Dreamhost
|
|
||||||
|
|
||||||
For Dreamhost, you need to provide your API Token(you can create it [here](https://panel.dreamhost.com/?tree=home.api)), and config all the domains & subdomains.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"provider": "Dreamhost",
|
|
||||||
"login_token": "your_api_key",
|
|
||||||
"domains": [{
|
|
||||||
"domain_name": "example.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
},{
|
|
||||||
"domain_name": "example2.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"ip_type": "IPV4",
|
|
||||||
"interval": 300,
|
|
||||||
"resolver": "ns1.dreamhost.com",
|
|
||||||
"socks5_proxy": ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### Google Domains
|
|
||||||
|
|
||||||
For Google Domains, you need to provide email & password, and config all the domains & subdomains.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"provider": "Google",
|
|
||||||
"email": "Your_Username",
|
|
||||||
"password": "Your_Password",
|
|
||||||
"domains": [{
|
|
||||||
"domain_name": "example.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
},{
|
|
||||||
"domain_name": "example2.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### AliDNS
|
|
||||||
|
|
||||||
For AliDNS, you need to provide `AccessKeyID` & `AccessKeySecret` as `email` & `password`, and config all the domains & subdomains.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"provider": "AliDNS",
|
|
||||||
"email": "AccessKeyID",
|
|
||||||
"password": "AccessKeySecret",
|
|
||||||
"login_token": "",
|
|
||||||
"domains": [{
|
|
||||||
"domain_name": "example.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
},{
|
|
||||||
"domain_name": "example2.com",
|
|
||||||
"sub_domains": ["www","test"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### DuckDNS
|
|
||||||
|
|
||||||
For DuckDNS, only need to provide the `token`, config 1 default domain & subdomains.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"provider": "DuckDNS",
|
|
||||||
"password": "",
|
|
||||||
"login_token": "3aaaaaaaa-f411-4198-a5dc-8381cac61b87",
|
|
||||||
"domains": [
|
|
||||||
{
|
|
||||||
"domain_name": "www.duckdns.org",
|
|
||||||
"sub_domains": [
|
|
||||||
"myname"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### No-IP
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"provider": "NoIP",
|
|
||||||
"email": "mail@example.com",
|
|
||||||
"password": "YourPassword",
|
|
||||||
"domains": [
|
|
||||||
{
|
|
||||||
"domain_name": "ddns.net",
|
|
||||||
"sub_domains": ["timothyye6"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ip_type": "IPv4",
|
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### HE.net
|
|
||||||
|
|
||||||
For HE, email is not needed, just fill DDNS key to password, and config all the domains & subdomains.
|
For HE, email is not needed, just fill DDNS key to password, and config all the domains & subdomains.
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example</summary>
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"provider": "HE",
|
"provider": "HE",
|
||||||
@ -393,227 +154,111 @@ For HE, email is not needed, just fill DDNS key to password, and config all the
|
|||||||
"sub_domains": ["www","test"]
|
"sub_domains": ["www","test"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resolver": "8.8.8.8",
|
"ip_url": "http://members.3322.org/dyndns/getip",
|
||||||
"ip_url": "https://myip.biturl.top",
|
|
||||||
"interval": 300,
|
|
||||||
"socks5_proxy": ""
|
"socks5_proxy": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
### HE.net DDNS configuration
|
||||||
<summary>Provider configuration</summary>
|
|
||||||
|
|
||||||
Add a new "A record" and make sure that "Enable entry for dynamic dns" is checked:
|
Add a new "A record", make sure that "Enable entry for dynamic dns" is checked:
|
||||||
|
|
||||||
<img src="./snapshots/he1.png" width="640" />
|
<img src="https://github.com/TimothyYe/godns/blob/master/snapshots/he1.png?raw=true" width="640" />
|
||||||
|
|
||||||
Fill in your own DDNS key or generate a random DDNS key for this new created "A record":
|
Fill your own DDNS key or generate a random DDNS key for this new created "A record":
|
||||||
|
|
||||||
<img src="./snapshots/he2.png" width="640" />
|
<img src="https://github.com/TimothyYe/godns/blob/master/snapshots/he2.png?raw=true" width="640" />
|
||||||
|
|
||||||
Remember the DDNS key and set it in the `password` property in the configuration file.
|
Remember the DDNS key and fill it as password to the config.json.
|
||||||
|
|
||||||
__NOTICE__: If you have multiple domains or subdomains, make sure their DDNS key are the same.
|
__NOTICE__: If you have multiple domains or subdomains, make sure their DDNS key are the same.
|
||||||
</details>
|
|
||||||
|
|
||||||
### Notifications
|
### Get an IP address from the interface
|
||||||
|
|
||||||
GoDNS can send a notification each time the IP changes.
|
For some reasons if you want to get an IP directly from the interface, say `eth0` for Linux or `Local Area Connection` for Windows, update config file like this:
|
||||||
|
```json
|
||||||
|
"ip_url": "",
|
||||||
|
"ip_interface": "eth0",
|
||||||
|
```
|
||||||
|
|
||||||
#### Email
|
If you set both `ip_url` and `ip_interface`, it first tries to get an IP address online, and if not succeed, gets
|
||||||
|
an IP address from the interface as a fallback.
|
||||||
|
|
||||||
Emails are sent over [SMTP](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol). Update your configuration with the following snippet:
|
Note that IPv6 address will be ignored currently.
|
||||||
|
|
||||||
|
### Email notification support
|
||||||
|
|
||||||
|
Update config file and provide your SMTP options, a notification mail will be sent to your mailbox once the IP is changed and updated.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"notify": {
|
"notify": {
|
||||||
"mail": {
|
"enabled": true,
|
||||||
"enabled": true,
|
"smtp_server": "smtp.example.com",
|
||||||
"smtp_server": "smtp.example.com",
|
"smtp_username": "user",
|
||||||
"smtp_username": "user",
|
"smtp_password": "password",
|
||||||
"smtp_password": "password",
|
"smtp_port": 25,
|
||||||
"smtp_port": 25,
|
"send_to": "my_mail@example.com"
|
||||||
"send_to": "my_mail@example.com"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Each time the IP changes, you will receive an email like that:
|
Notification mail example:
|
||||||
|
|
||||||
<img src="https://github.com/TimothyYe/godns/blob/master/snapshots/mail.png?raw=true" />
|
<img src="https://github.com/TimothyYe/godns/blob/master/snapshots/mail.png?raw=true" />
|
||||||
|
|
||||||
#### Telegram
|
### SOCKS5 proxy support
|
||||||
|
|
||||||
To receive a [Telegram](https://telegram.org/) message each time the IP changes, update your configuration with the following snippet:
|
You can also use SOCKS5 proxy, just fill SOCKS5 address to the ```socks5_proxy``` item:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"notify": {
|
|
||||||
"telegram": {
|
|
||||||
"enabled": true,
|
|
||||||
"bot_api_key": "11111:aaaa-bbbb",
|
|
||||||
"chat_id": "-123456",
|
|
||||||
"message_template": "Domain *{{ .Domain }}* is updated to %0A{{ .CurrentIP }}",
|
|
||||||
"use_proxy": false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `message_template` property supports [markdown](https://www.markdownguide.org). New lines needs to be escaped with `%0A`.
|
|
||||||
|
|
||||||
#### Slack
|
|
||||||
|
|
||||||
To receive a [Slack](https://slack.com) message each time the IP changes, update your configuration with the following snippet:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"notify": {
|
|
||||||
"slack": {
|
|
||||||
"enabled": true,
|
|
||||||
"bot_api_token": "xoxb-xxx",
|
|
||||||
"channel": "your_channel",
|
|
||||||
"message_template": "Domain *{{ .Domain }}* is updated to \n{{ .CurrentIP }}",
|
|
||||||
"use_proxy": false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `message_template` property supports [markdown](https://www.markdownguide.org). New lines needs to be escaped with `\n`.
|
|
||||||
|
|
||||||
### Miscellaneous topics
|
|
||||||
|
|
||||||
#### IPv6 support
|
|
||||||
|
|
||||||
Most of the [providers](#supported-dns-providers) support IPv6.
|
|
||||||
|
|
||||||
To enable the `IPv6` support of GoDNS, there are two solutions to choose from:
|
|
||||||
|
|
||||||
1. Use an online service to lookup the external IPv6
|
|
||||||
|
|
||||||
For that:
|
|
||||||
|
|
||||||
- Set the `ip_type` as `IPv6`, and make sure the `ipv6_url` is configured
|
|
||||||
- Create an `AAAA` record instead of an `A` record in your DNS provider
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Configuration example</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"domains": [
|
|
||||||
{
|
|
||||||
"domain_name": "example.com",
|
|
||||||
"sub_domains": [
|
|
||||||
"ipv6"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"resolver": "2001:4860:4860::8888",
|
|
||||||
"ipv6_url": "https://api-ipv6.ip.sb/ip",
|
|
||||||
"ip_type": "IPv6"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
2. Let GoDNS find the IPv6 of the network interface of the machine it is running on (more on that [later](#network-interface-ip-address)).
|
|
||||||
|
|
||||||
For this to happen, just leave `ip_url` and `ipv6_url` empty.
|
|
||||||
|
|
||||||
Note that the network interface must be configured with an IPv6 for this to work.
|
|
||||||
|
|
||||||
#### Network interface IP address
|
|
||||||
|
|
||||||
For some reasons if you want to get the IP address associated to a network interface (instead of performing an online lookup), you can specify it in the configuration file this way:
|
|
||||||
|
|
||||||
```json
|
|
||||||
...
|
|
||||||
"ip_url": "",
|
|
||||||
"ip_interface": "interface-name",
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
With `interface-name` replaced by the name of the network interface, e.g. `eth0` on Linux or `Local Area Connection` on Windows.
|
|
||||||
|
|
||||||
Note: If `ip_url` is also specified, it will be used to perform an online lookup first and the network interface IP will be used as a fallback in case of failure.
|
|
||||||
|
|
||||||
#### SOCKS5 proxy support
|
|
||||||
|
|
||||||
You can make all remote calls go through a [SOCKS5 proxy](https://en.wikipedia.org/wiki/SOCKS#SOCKS5) by specifying it in the configuration file this way:
|
|
||||||
|
|
||||||
```json
|
|
||||||
...
|
|
||||||
"socks5_proxy": "127.0.0.1:7070"
|
"socks5_proxy": "127.0.0.1:7070"
|
||||||
"use_proxy": true
|
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running GoDNS
|
Now all the queries will go through the specified SOCKS5 proxy.
|
||||||
|
|
||||||
There are few ways to run GoDNS.
|
## Run it as a daemon manually
|
||||||
|
|
||||||
### As a manual daemon
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nohup ./godns &
|
nohup ./godns &
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: when the program stops, it will not be restarted.
|
## Run it as a daemon, manage it via Upstart
|
||||||
|
|
||||||
### As a managed daemon (with upstart)
|
* Install `upstart` first
|
||||||
|
* Copy `./upstart/godns.conf` to `/etc/init`
|
||||||
1. Install `upstart` first (if not available already)
|
* Start it as a system service:
|
||||||
2. Copy `./upstart/godns.conf` to `/etc/init` (and tweak it to your needs)
|
|
||||||
3. Start the service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo start godns
|
|
||||||
```
|
|
||||||
|
|
||||||
### As a managed daemon (with systemd)
|
|
||||||
|
|
||||||
1. Install `systemd` first (it not available already)
|
|
||||||
2. Copy `./systemd/godns.service` to `/lib/systemd/system` (and tweak it to your needs)
|
|
||||||
3. Start the service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable godns
|
|
||||||
sudo systemctl start godns
|
|
||||||
```
|
|
||||||
|
|
||||||
### As a Docker container
|
|
||||||
|
|
||||||
With `/path/to/config.json` your local configuration file, run:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run \
|
sudo start godns
|
||||||
-d --name godns --restart=always \
|
|
||||||
-v /path/to/config.json:/config.json \
|
|
||||||
timothyye/godns:latest
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### As a Windows service
|
## Run it as a daemon, manage it via Systemd
|
||||||
|
|
||||||
1. Download the latest version of [NSSM](https://nssm.cc/download)
|
* Modify `./systemd/godns.service` and config it.
|
||||||
|
* Copy `./systemd/godns.service` to `/lib/systemd/system`
|
||||||
|
* Start it as a systemd service:
|
||||||
|
|
||||||
2. In an administrative prompt, from the folder where NSSM was downloaded, e.g. `C:\Downloads\nssm\` **win64**, run:
|
```bash
|
||||||
|
sudo systemctl enable godns
|
||||||
```
|
sudo systemctl start godns
|
||||||
nssm install YOURSERVICENAME
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Follow the interface to configure the service. In the "Application" tab just indicate where the `godns.exe` file is. Optionally you can also define a description on the "Details" tab and define a log file on the "I/O" tab. Finish by clicking on the "Install service" button.
|
|
||||||
|
|
||||||
4. The service will now start along Windows.
|
|
||||||
|
|
||||||
Note: you can uninstall the service by running:
|
|
||||||
|
|
||||||
```
|
|
||||||
nssm remove YOURSERVICENAME
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Special Thanks
|
## Run it with docker
|
||||||
|
|
||||||
<img src="https://i.imgur.com/xhe5RLZ.jpg" width="80px" align="right" />
|
Now godns supports to run in docker.
|
||||||
|
|
||||||
Thanks JetBrains for sponsoring this project with [free open source license](https://www.jetbrains.com/community/opensource/).
|
* Get [config_sample.json](https://github.com/timothyye/godns/blob/master/config_sample.json) from Github.
|
||||||
|
* Rename it to **config.json**.
|
||||||
|
* Run GoDNS with docker:
|
||||||
|
|
||||||
> I like GoLand, it is an amazing and productive tool.
|
```bash
|
||||||
|
docker run -d --name godns --restart=always \
|
||||||
|
-v /path/to/config.json:/usr/local/godns/config.json timothyye/godns:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run it as a Windows service
|
||||||
|
|
||||||
|
* Get [birkett/srvany-ng](https://github.com/birkett/srvany-ng/releases) from Github.
|
||||||
|
* Uncompress and place the executable `srvany-ng.exe` to the same directory where `godns.exe` in.
|
||||||
|
* Create a service and add registry keys according to the guide [here](https://github.com/birkett/srvany-ng).
|
||||||
|
|
||||||
|
## Enjoy it!
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
theme: jekyll-theme-cayman
|
|
||||||
9
cmd/godns/Dockerfile
Normal file
9
cmd/godns/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
FROM timothyye/alpine:3.6-glibc
|
||||||
|
MAINTAINER Timothy
|
||||||
|
RUN apk add --update ca-certificates
|
||||||
|
RUN mkdir -p /usr/local/godns
|
||||||
|
COPY godns /usr/local/godns
|
||||||
|
RUN chmod +x /usr/local/godns/godns
|
||||||
|
RUN rm -rf /var/cache/apk/*
|
||||||
|
WORKDIR /usr/local/godns
|
||||||
|
ENTRYPOINT ["./godns", "-c", "/usr/local/godns/config.json"]
|
||||||
37
cmd/godns/Makefile
Normal file
37
cmd/godns/Makefile
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Binary name
|
||||||
|
BINARY=godns
|
||||||
|
# Builds the project
|
||||||
|
build:
|
||||||
|
go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
||||||
|
# Installs our project: copies binaries
|
||||||
|
install:
|
||||||
|
go install
|
||||||
|
release:
|
||||||
|
# Clean
|
||||||
|
go clean
|
||||||
|
rm -rf *.gz
|
||||||
|
# Build for mac
|
||||||
|
go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
||||||
|
tar czvf ${BINARY}-mac64-${VERSION}.tar.gz ./${BINARY}
|
||||||
|
# Build for linux
|
||||||
|
go clean
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
||||||
|
tar czvf ${BINARY}-linux64-${VERSION}.tar.gz ./${BINARY}
|
||||||
|
# Build for arm
|
||||||
|
go clean
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
||||||
|
tar czvf ${BINARY}-arm64-${VERSION}.tar.gz ./${BINARY}
|
||||||
|
go clean
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
||||||
|
tar czvf ${BINARY}-arm-${VERSION}.tar.gz ./${BINARY}
|
||||||
|
# Build for win
|
||||||
|
go clean
|
||||||
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ${BINARY}.exe -ldflags "-X main.Version=${VERSION}"
|
||||||
|
tar czvf ${BINARY}-win64-${VERSION}.tar.gz ./${BINARY}.exe
|
||||||
|
go clean
|
||||||
|
# Cleans our projects: deletes binaries
|
||||||
|
clean:
|
||||||
|
go clean
|
||||||
|
rm -rf *.gz
|
||||||
|
|
||||||
|
.PHONY: clean build
|
||||||
@ -41,7 +41,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init log settings
|
// Init log settings
|
||||||
log.SetPrefix("[GoDNS] ")
|
log.SetPrefix("【GoDNS】")
|
||||||
log.Println("GoDNS started, entering main loop...")
|
log.Println("GoDNS started, entering main loop...")
|
||||||
dnsLoop()
|
dnsLoop()
|
||||||
}
|
}
|
||||||
@ -50,17 +50,17 @@ func dnsLoop() {
|
|||||||
panicChan := make(chan godns.Domain)
|
panicChan := make(chan godns.Domain)
|
||||||
|
|
||||||
log.Println("Creating DNS handler with provider:", configuration.Provider)
|
log.Println("Creating DNS handler with provider:", configuration.Provider)
|
||||||
h := handler.CreateHandler(configuration.Provider)
|
handler := handler.CreateHandler(configuration.Provider)
|
||||||
h.SetConfiguration(&configuration)
|
handler.SetConfiguration(&configuration)
|
||||||
for i := range configuration.Domains {
|
for i := range configuration.Domains {
|
||||||
go h.DomainLoop(&configuration.Domains[i], panicChan)
|
go handler.DomainLoop(&configuration.Domains[i], panicChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
panicCount := 0
|
panicCount := 0
|
||||||
for {
|
for {
|
||||||
failDomain := <-panicChan
|
failDomain := <-panicChan
|
||||||
log.Println("Got panic in goroutine, will start a new one... :", panicCount)
|
log.Println("Got panic in goroutine, will start a new one... :", panicCount)
|
||||||
go h.DomainLoop(&failDomain, panicChan)
|
go handler.DomainLoop(&failDomain, panicChan)
|
||||||
|
|
||||||
panicCount++
|
panicCount++
|
||||||
if panicCount >= godns.PanicMax {
|
if panicCount >= godns.PanicMax {
|
||||||
|
|||||||
@ -18,30 +18,15 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ip_url": "https://myip.biturl.top",
|
"ip_url": "http://members.3322.org/dyndns/getip",
|
||||||
"ipv6_url": "https://api-ipv6.ip.sb/ip",
|
|
||||||
"ip_type": "IPv4",
|
|
||||||
"interval": 300,
|
|
||||||
"resolver": "8.8.8.8",
|
|
||||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",
|
|
||||||
"ip_interface": "eth0",
|
"ip_interface": "eth0",
|
||||||
"socks5_proxy": "",
|
"socks5_proxy": "",
|
||||||
"use_proxy": false,
|
|
||||||
"notify": {
|
"notify": {
|
||||||
"telegram": {
|
"enabled": false,
|
||||||
"enabled": false,
|
"smtp_server": "",
|
||||||
"bot_api_key": "",
|
"smtp_username": "",
|
||||||
"chat_id": "",
|
"smtp_password": "",
|
||||||
"message_template": "",
|
"smtp_port": 25,
|
||||||
"use_proxy": false
|
"send_to": ""
|
||||||
},
|
|
||||||
"mail": {
|
|
||||||
"enabled": false,
|
|
||||||
"smtp_server": "",
|
|
||||||
"smtp_username": "",
|
|
||||||
"smtp_password": "",
|
|
||||||
"smtp_port": 25,
|
|
||||||
"send_to": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
go.mod
18
go.mod
@ -1,18 +0,0 @@
|
|||||||
module github.com/TimothyYe/godns
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/bitly/go-simplejson v0.5.0
|
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
|
||||||
github.com/bogdanovich/dns_resolver v0.0.0-20170211073258-a8e42bc6a5b6
|
|
||||||
github.com/fatih/color v1.7.0
|
|
||||||
github.com/google/uuid v1.1.1
|
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
|
||||||
github.com/miekg/dns v1.1.29
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
|
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
|
||||||
)
|
|
||||||
|
|
||||||
go 1.13
|
|
||||||
46
go.sum
46
go.sum
@ -1,46 +0,0 @@
|
|||||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
|
||||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
|
||||||
github.com/bogdanovich/dns_resolver v0.0.0-20170211073258-a8e42bc6a5b6 h1:oV1V+uwP+sjmdSkvMxsl/l+HE+N8wbL49wCXZPel25M=
|
|
||||||
github.com/bogdanovich/dns_resolver v0.0.0-20170211073258-a8e42bc6a5b6/go.mod h1:txOV61Nn+21z77KUMkNsp8lTHoOFTtqotltQAFenS9I=
|
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
|
||||||
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
|
|
||||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190226215855-775f8194d0f9 h1:N26gncmS+iqc/W/SKhX3ElI5pkt72XYoRLgi5Z70LSc=
|
|
||||||
golang.org/x/sys v0.0.0-20190226215855-775f8194d0f9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
|
||||||
@ -1,166 +0,0 @@
|
|||||||
package alidns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AliDNS token
|
|
||||||
type AliDNS struct {
|
|
||||||
AccessKeyID string
|
|
||||||
AccessKeySecret string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
publicParm = map[string]string{
|
|
||||||
"AccessKeyId": "",
|
|
||||||
"Format": "JSON",
|
|
||||||
"Version": "2015-01-09",
|
|
||||||
"SignatureMethod": "HMAC-SHA1",
|
|
||||||
"Timestamp": "",
|
|
||||||
"SignatureVersion": "1.0",
|
|
||||||
"SignatureNonce": "",
|
|
||||||
}
|
|
||||||
baseURL = "http://alidns.aliyuncs.com/"
|
|
||||||
instance *AliDNS
|
|
||||||
once sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
type domainRecordsResp struct {
|
|
||||||
RequestID string `json:"RequestId"`
|
|
||||||
TotalCount int
|
|
||||||
PageNumber int
|
|
||||||
PageSize int
|
|
||||||
DomainRecords domainRecords
|
|
||||||
}
|
|
||||||
|
|
||||||
type domainRecords struct {
|
|
||||||
Record []DomainRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainRecord struct
|
|
||||||
type DomainRecord struct {
|
|
||||||
DomainName string
|
|
||||||
RecordID string `json:"RecordId"`
|
|
||||||
RR string
|
|
||||||
Type string
|
|
||||||
Value string
|
|
||||||
Line string
|
|
||||||
Priority int
|
|
||||||
TTL int
|
|
||||||
Status string
|
|
||||||
Locked bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHTTPBody(url string) ([]byte, error) {
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
return body, err
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("status %d, Error:%s", resp.StatusCode, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAliDNS function creates instance of AliDNS and return
|
|
||||||
func NewAliDNS(key, secret string) *AliDNS {
|
|
||||||
once.Do(func() {
|
|
||||||
instance = &AliDNS{
|
|
||||||
AccessKeyID: key,
|
|
||||||
AccessKeySecret: secret,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDomainRecords gets all the doamin records according to input subdomain key
|
|
||||||
func (d *AliDNS) GetDomainRecords(domain, rr string) []DomainRecord {
|
|
||||||
resp := &domainRecordsResp{}
|
|
||||||
parms := map[string]string{
|
|
||||||
"Action": "DescribeSubDomainRecords",
|
|
||||||
"SubDomain": fmt.Sprintf("%s.%s", rr, domain),
|
|
||||||
}
|
|
||||||
urlPath := d.genRequestURL(parms)
|
|
||||||
body, err := getHTTPBody(urlPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("GetDomainRecords error.%+v\n", err)
|
|
||||||
} else {
|
|
||||||
if err := json.Unmarshal(body, resp); err != nil {
|
|
||||||
fmt.Printf("GetDomainRecords error. %+v\n", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return resp.DomainRecords.Record
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDomainRecord updates domain record
|
|
||||||
func (d *AliDNS) UpdateDomainRecord(r DomainRecord) error {
|
|
||||||
parms := map[string]string{
|
|
||||||
"Action": "UpdateDomainRecord",
|
|
||||||
"RecordId": r.RecordID,
|
|
||||||
"RR": r.RR,
|
|
||||||
"Type": r.Type,
|
|
||||||
"Value": r.Value,
|
|
||||||
"TTL": strconv.Itoa(r.TTL),
|
|
||||||
"Line": r.Line,
|
|
||||||
}
|
|
||||||
|
|
||||||
urlPath := d.genRequestURL(parms)
|
|
||||||
if urlPath == "" {
|
|
||||||
return errors.New("failed to generate request URL")
|
|
||||||
}
|
|
||||||
_, err := getHTTPBody(urlPath)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("UpdateDomainRecord error.%+v\n", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *AliDNS) genRequestURL(parms map[string]string) string {
|
|
||||||
var pArr []string
|
|
||||||
ps := map[string]string{}
|
|
||||||
for k, v := range publicParm {
|
|
||||||
ps[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range parms {
|
|
||||||
ps[k] = v
|
|
||||||
}
|
|
||||||
now := time.Now().UTC()
|
|
||||||
ps["AccessKeyId"] = d.AccessKeyID
|
|
||||||
ps["SignatureNonce"] = strconv.Itoa(int(now.UnixNano()) + rand.Intn(99999))
|
|
||||||
ps["Timestamp"] = now.Format("2006-01-02T15:04:05Z")
|
|
||||||
|
|
||||||
for k, v := range ps {
|
|
||||||
pArr = append(pArr, fmt.Sprintf("%s=%s", k, v))
|
|
||||||
}
|
|
||||||
sort.Strings(pArr)
|
|
||||||
path := strings.Join(pArr, "&")
|
|
||||||
|
|
||||||
s := "GET&%2F&" + url.QueryEscape(path)
|
|
||||||
s = strings.Replace(s, "%3A", "%253A", -1)
|
|
||||||
s = strings.Replace(s, "%40", "%2540", -1)
|
|
||||||
s = strings.Replace(s, "%2A", "%252A", -1)
|
|
||||||
mac := hmac.New(sha1.New, []byte(d.AccessKeySecret+"&"))
|
|
||||||
|
|
||||||
if _, err := mac.Write([]byte(s)); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
|
||||||
return fmt.Sprintf("%s?%s&Signature=%s", baseURL, path, url.QueryEscape(sign))
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
package alidns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"runtime/debug"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler struct
|
|
||||||
type Handler struct {
|
|
||||||
Configuration *godns.Settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
|
||||||
handler.Configuration = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
|
||||||
panicChan <- *domain
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
looping := false
|
|
||||||
aliDNS := NewAliDNS(handler.Configuration.Email, handler.Configuration.Password)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
|
|
||||||
looping = true
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Failed to get current IP:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Println("currentIP is:", currentIP)
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
|
||||||
hostname := subDomain + "." + domain.DomainName
|
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//check against currently known IP, if no change, skip update
|
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
lastIP = currentIP
|
|
||||||
|
|
||||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
|
||||||
records := aliDNS.GetDomainRecords(domain.DomainName, subDomain)
|
|
||||||
if records == nil || len(records) == 0 {
|
|
||||||
log.Printf("Cannot get subdomain %s from AliDNS.\r\n", subDomain)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
records[0].Value = currentIP
|
|
||||||
if err := aliDNS.UpdateDomainRecord(records[0]); err != nil {
|
|
||||||
log.Printf("Failed to update IP for subdomain:%s\r\n", subDomain)
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
log.Printf("IP updated for subdomain:%s\r\n", subDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send notification
|
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
|
||||||
log.Printf("Failed to send notification")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,22 +1,21 @@
|
|||||||
package cloudflare
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
"github.com/TimothyYe/godns"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler struct definition
|
// CloudflareHandler struct definition
|
||||||
type Handler struct {
|
type CloudflareHandler struct {
|
||||||
Configuration *godns.Settings
|
Configuration *godns.Settings
|
||||||
API string
|
API string
|
||||||
}
|
}
|
||||||
@ -41,7 +40,6 @@ type DNSRecord struct {
|
|||||||
Proxied bool `json:"proxied"`
|
Proxied bool `json:"proxied"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
ZoneID string `json:"zone_id"`
|
ZoneID string `json:"zone_id"`
|
||||||
TTL int32 `json:"ttl"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetIP updates DNSRecord.IP
|
// SetIP updates DNSRecord.IP
|
||||||
@ -62,13 +60,13 @@ type Zone struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
// SetConfiguration pass dns settings and store it to handler instance
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
func (handler *CloudflareHandler) SetConfiguration(conf *godns.Settings) {
|
||||||
handler.Configuration = conf
|
handler.Configuration = conf
|
||||||
handler.API = "https://api.cloudflare.com/client/v4"
|
handler.API = "https://api.cloudflare.com/client/v4"
|
||||||
}
|
}
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
// DomainLoop the main logic loop
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
func (handler *CloudflareHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
||||||
@ -77,15 +75,7 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var lastIP string
|
var lastIP string
|
||||||
looping := false
|
|
||||||
for {
|
for {
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
looping = true
|
|
||||||
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error in GetCurrentIP:", err)
|
log.Println("Error in GetCurrentIP:", err)
|
||||||
@ -96,6 +86,8 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
if currentIP == lastIP {
|
if currentIP == lastIP {
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
log.Printf("IP is the same as cached one. Skip update.\n")
|
||||||
} else {
|
} else {
|
||||||
|
lastIP = currentIP
|
||||||
|
|
||||||
log.Println("Checking IP for domain", domain.DomainName)
|
log.Println("Checking IP for domain", domain.DomainName)
|
||||||
zoneID := handler.getZone(domain.DomainName)
|
zoneID := handler.getZone(domain.DomainName)
|
||||||
if zoneID != "" {
|
if zoneID != "" {
|
||||||
@ -103,18 +95,13 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
|
|
||||||
// update records
|
// update records
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
if !recordTracked(domain, &rec) {
|
if recordTracked(domain, &rec) != true {
|
||||||
log.Println("Skiping record:", rec.Name)
|
log.Println("Skiping record:", rec.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rec.IP != currentIP {
|
if rec.IP != currentIP {
|
||||||
log.Printf("IP mismatch: Current(%+v) vs Cloudflare(%+v)\r\n", currentIP, rec.IP)
|
log.Printf("IP mismatch: Current(%+v) vs Cloudflare(%+v)\r\n", currentIP, rec.IP)
|
||||||
lastIP = handler.updateRecord(rec, currentIP)
|
handler.updateRecord(rec, currentIP)
|
||||||
|
|
||||||
// Send notification
|
|
||||||
if err := godns.SendNotify(handler.Configuration, rec.Name, currentIP); err != nil {
|
|
||||||
log.Println("Failed to send notification")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Record OK: %+v - %+v\r\n", rec.Name, rec.IP)
|
log.Printf("Record OK: %+v - %+v\r\n", rec.Name, rec.IP)
|
||||||
}
|
}
|
||||||
@ -123,16 +110,21 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
log.Println("Failed to find zone for domain:", domain.DomainName)
|
log.Println("Failed to find zone for domain:", domain.DomainName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Interval is 5 minutes
|
||||||
|
log.Printf("Going to sleep, will start next checking in %d minutes...\r\n", godns.INTERVAL)
|
||||||
|
time.Sleep(time.Minute * godns.INTERVAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if record is present in domain conf
|
// Check if record is present in domain conf
|
||||||
func recordTracked(domain *godns.Domain, record *DNSRecord) bool {
|
func recordTracked(domain *godns.Domain, record *DNSRecord) bool {
|
||||||
|
|
||||||
if record.Name == domain.DomainName {
|
if record.Name == domain.DomainName {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
for _, subDomain := range domain.SubDomains {
|
||||||
sd := fmt.Sprintf("%s.%s", subDomain, domain.DomainName)
|
sd := subDomain + "." + domain.DomainName
|
||||||
if record.Name == sd {
|
if record.Name == sd {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -142,31 +134,34 @@ func recordTracked(domain *godns.Domain, record *DNSRecord) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new request with auth in place and optional proxy
|
// Create a new request with auth in place and optional proxy
|
||||||
func (handler *Handler) newRequest(method, url string, body io.Reader) (*http.Request, *http.Client) {
|
func (handler *CloudflareHandler) newRequest(method, url string, body io.Reader) (*http.Request, *http.Client) {
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
client := &http.Client{}
|
||||||
if client == nil {
|
|
||||||
log.Println("cannot create HTTP client")
|
if handler.Configuration.Socks5Proxy != "" {
|
||||||
|
log.Println("use socks5 proxy:" + handler.Configuration.Socks5Proxy)
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", handler.Configuration.Socks5Proxy, nil, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("can't connect to the proxy:", err)
|
||||||
|
} else {
|
||||||
|
httpTransport := &http.Transport{}
|
||||||
|
client.Transport = httpTransport
|
||||||
|
httpTransport.Dial = dialer.Dial
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req, _ := http.NewRequest(method, handler.API+url, body)
|
req, _ := http.NewRequest(method, handler.API+url, body)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("X-Auth-Email", handler.Configuration.Email)
|
||||||
if handler.Configuration.Email != "" && handler.Configuration.Password != "" {
|
req.Header.Set("X-Auth-Key", handler.Configuration.Password)
|
||||||
req.Header.Set("X-Auth-Email", handler.Configuration.Email)
|
|
||||||
req.Header.Set("X-Auth-Key", handler.Configuration.Password)
|
|
||||||
} else if handler.Configuration.LoginToken != "" {
|
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", handler.Configuration.LoginToken))
|
|
||||||
}
|
|
||||||
|
|
||||||
return req, client
|
return req, client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the correct zone via domain name
|
// Find the correct zone via domain name
|
||||||
func (handler *Handler) getZone(domain string) string {
|
func (handler *CloudflareHandler) getZone(domain string) string {
|
||||||
|
|
||||||
var z ZoneResponse
|
var z ZoneResponse
|
||||||
|
|
||||||
req, client := handler.newRequest("GET", fmt.Sprintf("/zones?name=%s", domain), nil)
|
req, client := handler.newRequest("GET", "/zones", nil)
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Request error:", err.Error())
|
log.Println("Request error:", err.Error())
|
||||||
@ -194,20 +189,12 @@ func (handler *Handler) getZone(domain string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get all DNS A records for a zone
|
// Get all DNS A records for a zone
|
||||||
func (handler *Handler) getDNSRecords(zoneID string) []DNSRecord {
|
func (handler *CloudflareHandler) getDNSRecords(zoneID string) []DNSRecord {
|
||||||
|
|
||||||
var empty []DNSRecord
|
var empty []DNSRecord
|
||||||
var r DNSRecordResponse
|
var r DNSRecordResponse
|
||||||
var recordType string
|
|
||||||
|
|
||||||
if handler.Configuration.IPType == "" || strings.ToUpper(handler.Configuration.IPType) == godns.IPV4 {
|
req, client := handler.newRequest("GET", "/zones/"+zoneID+"/dns_records?type=A", nil)
|
||||||
recordType = "A"
|
|
||||||
} else if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
|
|
||||||
recordType = "AAAA"
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Querying records with type:", recordType)
|
|
||||||
req, client := handler.newRequest("GET", fmt.Sprintf("/zones/"+zoneID+"/dns_records?type=%s&page=1&per_page=500", recordType), nil)
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Request error:", err.Error())
|
log.Println("Request error:", err.Error())
|
||||||
@ -231,11 +218,10 @@ func (handler *Handler) getDNSRecords(zoneID string) []DNSRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update DNS A Record with new IP
|
// Update DNS A Record with new IP
|
||||||
func (handler *Handler) updateRecord(record DNSRecord, newIP string) string {
|
func (handler *CloudflareHandler) updateRecord(record DNSRecord, newIP string) {
|
||||||
|
|
||||||
var r DNSRecordUpdateResponse
|
var r DNSRecordUpdateResponse
|
||||||
record.SetIP(newIP)
|
record.SetIP(newIP)
|
||||||
var lastIP string
|
|
||||||
|
|
||||||
j, _ := json.Marshal(record)
|
j, _ := json.Marshal(record)
|
||||||
req, client := handler.newRequest("PUT",
|
req, client := handler.newRequest("PUT",
|
||||||
@ -245,7 +231,7 @@ func (handler *Handler) updateRecord(record DNSRecord, newIP string) string {
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Request error:", err.Error())
|
log.Println("Request error:", err.Error())
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
@ -253,14 +239,12 @@ func (handler *Handler) updateRecord(record DNSRecord, newIP string) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Decoder error: %+v\n", err)
|
log.Printf("Decoder error: %+v\n", err)
|
||||||
log.Printf("Response body: %+v\n", string(body))
|
log.Printf("Response body: %+v\n", string(body))
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
if r.Success != true {
|
if r.Success != true {
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
log.Printf("Response failed: %+v\n", string(body))
|
log.Printf("Response failed: %+v\n", string(body))
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Record updated: %+v - %+v", record.Name, record.IP)
|
log.Printf("Record updated: %+v - %+v", record.Name, record.IP)
|
||||||
lastIP = record.IP
|
|
||||||
}
|
}
|
||||||
return lastIP
|
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package cloudflare
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -160,8 +160,8 @@ func TestRecordTracked(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, rec := range resp.Records {
|
for _, rec := range resp.Records {
|
||||||
if recordTracked(domain, &rec) {
|
if recordTracked(domain, &rec) != true {
|
||||||
t.Logf("Record founded: %+v\r\n", rec.Name)
|
t.Errorf("invalid record skip: %+v\r\n", rec.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +1,7 @@
|
|||||||
package dnspod
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@ -15,20 +14,21 @@ import (
|
|||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
"github.com/TimothyYe/godns"
|
||||||
"github.com/bitly/go-simplejson"
|
"github.com/bitly/go-simplejson"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler struct definition
|
// DNSPodHandler struct definition
|
||||||
type Handler struct {
|
type DNSPodHandler struct {
|
||||||
Configuration *godns.Settings
|
Configuration *godns.Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
// SetConfiguration pass dns settings and store it to handler instance
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
func (handler *DNSPodHandler) SetConfiguration(conf *godns.Settings) {
|
||||||
handler.Configuration = conf
|
handler.Configuration = conf
|
||||||
}
|
}
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
// DomainLoop the main logic loop
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
func (handler *DNSPodHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
||||||
@ -36,16 +36,8 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
looping := false
|
var lastIP string
|
||||||
for {
|
for {
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
|
|
||||||
looping = true
|
|
||||||
|
|
||||||
log.Printf("Checking IP for domain %s \r\n", domain.DomainName)
|
log.Printf("Checking IP for domain %s \r\n", domain.DomainName)
|
||||||
domainID := handler.GetDomain(domain.DomainName)
|
domainID := handler.GetDomain(domain.DomainName)
|
||||||
|
|
||||||
@ -61,46 +53,45 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
}
|
}
|
||||||
log.Println("currentIP is:", currentIP)
|
log.Println("currentIP is:", currentIP)
|
||||||
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
//check against locally cached IP, if no change, skip update
|
||||||
hostname := subDomain + "." + domain.DomainName
|
if currentIP == lastIP {
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
log.Printf("IP is the same as cached one. Skip update.\n")
|
||||||
if err != nil {
|
} else {
|
||||||
log.Println(err)
|
lastIP = currentIP
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//check against currently known IP, if no change, skip update
|
for _, subDomain := range domain.SubDomains {
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
lastIP = currentIP
|
|
||||||
|
|
||||||
subDomainID, ip := handler.GetSubDomain(domainID, subDomain)
|
subDomainID, ip := handler.GetSubDomain(domainID, subDomain)
|
||||||
|
|
||||||
if subDomainID == "" || ip == "" {
|
if subDomainID == "" || ip == "" {
|
||||||
log.Printf("Domain or subdomain not configured yet. domain: %s.%s subDomainID: %s ip: %s\n", subDomain, domain.DomainName, subDomainID, ip)
|
log.Printf("domain: %s.%s subDomainID: %s ip: %s\n", subDomain, domain.DomainName, subDomainID, ip)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue to check the IP of subdomain
|
// Continue to check the IP of sub-domain
|
||||||
if len(ip) > 0 && strings.TrimRight(currentIP, "\n") != strings.TrimRight(ip, "\n") {
|
if len(ip) > 0 && strings.TrimRight(currentIP, "\n") != strings.TrimRight(ip, "\n") {
|
||||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
||||||
handler.UpdateIP(domainID, subDomainID, subDomain, currentIP)
|
handler.UpdateIP(domainID, subDomainID, subDomain, currentIP)
|
||||||
|
|
||||||
// Send notification
|
// Send mail notification if notify is enabled
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
if handler.Configuration.Notify.Enabled {
|
||||||
log.Println("Failed to send notification")
|
log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
|
||||||
|
godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s.%s Current IP is same as domain IP, no need to update...\n", subDomain, domain.DomainName)
|
log.Printf("%s.%s Current IP is same as domain IP, no need to update...\n", subDomain, domain.DomainName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Interval is 5 minutes
|
||||||
|
log.Printf("Going to sleep, will start next checking in %d minutes...\r\n", godns.INTERVAL)
|
||||||
|
time.Sleep(time.Minute * godns.INTERVAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateHeader generates the request header for DNSPod API
|
// GenerateHeader generates the request header for DNSPod API
|
||||||
func (handler *Handler) GenerateHeader(content url.Values) url.Values {
|
func (handler *DNSPodHandler) GenerateHeader(content url.Values) url.Values {
|
||||||
header := url.Values{}
|
header := url.Values{}
|
||||||
if handler.Configuration.LoginToken != "" {
|
if handler.Configuration.LoginToken != "" {
|
||||||
header.Add("login_token", handler.Configuration.LoginToken)
|
header.Add("login_token", handler.Configuration.LoginToken)
|
||||||
@ -120,7 +111,7 @@ func (handler *Handler) GenerateHeader(content url.Values) url.Values {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDomain returns specific domain by name
|
// GetDomain returns specific domain by name
|
||||||
func (handler *Handler) GetDomain(name string) int64 {
|
func (handler *DNSPodHandler) GetDomain(name string) int64 {
|
||||||
|
|
||||||
var ret int64
|
var ret int64
|
||||||
values := url.Values{}
|
values := url.Values{}
|
||||||
@ -169,7 +160,8 @@ func (handler *Handler) GetDomain(name string) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSubDomain returns subdomain by domain id
|
// GetSubDomain returns subdomain by domain id
|
||||||
func (handler *Handler) GetSubDomain(domainID int64, name string) (string, string) {
|
func (handler *DNSPodHandler) GetSubDomain(domainID int64, name string) (string, string) {
|
||||||
|
log.Println("debug:", domainID, name)
|
||||||
var ret, ip string
|
var ret, ip string
|
||||||
value := url.Values{}
|
value := url.Values{}
|
||||||
value.Add("domain_id", strconv.FormatInt(domainID, 10))
|
value.Add("domain_id", strconv.FormatInt(domainID, 10))
|
||||||
@ -177,15 +169,6 @@ func (handler *Handler) GetSubDomain(domainID int64, name string) (string, strin
|
|||||||
value.Add("length", "1")
|
value.Add("length", "1")
|
||||||
value.Add("sub_domain", name)
|
value.Add("sub_domain", name)
|
||||||
|
|
||||||
if handler.Configuration.IPType == "" || strings.ToUpper(handler.Configuration.IPType) == godns.IPV4 {
|
|
||||||
value.Add("record_type", "A")
|
|
||||||
} else if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
|
|
||||||
value.Add("record_type", "AAAA")
|
|
||||||
} else {
|
|
||||||
log.Println("Error: must specify \"ip_type\" in config for DNSPod.")
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := handler.PostData("/Record.List", value)
|
response, err := handler.PostData("/Record.List", value)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -222,21 +205,12 @@ func (handler *Handler) GetSubDomain(domainID int64, name string) (string, strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIP update subdomain with current IP
|
// UpdateIP update subdomain with current IP
|
||||||
func (handler *Handler) UpdateIP(domainID int64, subDomainID string, subDomainName string, ip string) {
|
func (handler *DNSPodHandler) UpdateIP(domainID int64, subDomainID string, subDomainName string, ip string) {
|
||||||
value := url.Values{}
|
value := url.Values{}
|
||||||
value.Add("domain_id", strconv.FormatInt(domainID, 10))
|
value.Add("domain_id", strconv.FormatInt(domainID, 10))
|
||||||
value.Add("record_id", subDomainID)
|
value.Add("record_id", subDomainID)
|
||||||
value.Add("sub_domain", subDomainName)
|
value.Add("sub_domain", subDomainName)
|
||||||
|
value.Add("record_type", "A")
|
||||||
if strings.ToUpper(handler.Configuration.IPType) == godns.IPV4 {
|
|
||||||
value.Add("record_type", "A")
|
|
||||||
} else if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
|
|
||||||
value.Add("record_type", "AAAA")
|
|
||||||
} else {
|
|
||||||
log.Println("Error: must specify \"ip_type\" in config for DNSPod.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
value.Add("record_line", "默认")
|
value.Add("record_line", "默认")
|
||||||
value.Add("value", ip)
|
value.Add("value", ip)
|
||||||
|
|
||||||
@ -257,18 +231,27 @@ func (handler *Handler) UpdateIP(domainID int64, subDomainID string, subDomainNa
|
|||||||
|
|
||||||
if sjson.Get("status").Get("code").MustString() == "1" {
|
if sjson.Get("status").Get("code").MustString() == "1" {
|
||||||
log.Println("New IP updated!")
|
log.Println("New IP updated!")
|
||||||
} else {
|
|
||||||
log.Println("Failed to update IP record:", sjson.Get("status").Get("message").MustString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostData post data and invoke DNSPod API
|
// PostData post data and invoke DNSPod API
|
||||||
func (handler *Handler) PostData(url string, content url.Values) (string, error) {
|
func (handler *DNSPodHandler) PostData(url string, content url.Values) (string, error) {
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
client := &http.Client{}
|
||||||
|
|
||||||
if client == nil {
|
if handler.Configuration.Socks5Proxy != "" {
|
||||||
return "", errors.New("failed to create HTTP client")
|
|
||||||
|
log.Println("use socks5 proxy:" + handler.Configuration.Socks5Proxy)
|
||||||
|
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", handler.Configuration.Socks5Proxy, nil, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("can't connect to the proxy:", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpTransport := &http.Transport{}
|
||||||
|
client.Transport = httpTransport
|
||||||
|
httpTransport.Dial = dialer.Dial
|
||||||
}
|
}
|
||||||
|
|
||||||
values := handler.GenerateHeader(content)
|
values := handler.GenerateHeader(content)
|
||||||
@ -1,138 +0,0 @@
|
|||||||
package dreamhost
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DreamhostURL the API address for dreamhost.com
|
|
||||||
DreamhostURL = "https://api.dreamhost.com"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler struct
|
|
||||||
type Handler struct {
|
|
||||||
Configuration *godns.Settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
|
||||||
handler.Configuration = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
|
||||||
panicChan <- *domain
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
looping := false
|
|
||||||
for {
|
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
looping = true
|
|
||||||
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Println("get_currentIP:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Println("currentIP is:", currentIP)
|
|
||||||
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
|
||||||
hostname := subDomain + "." + domain.DomainName
|
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//check against currently known IP, if no change, skip update
|
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
|
||||||
handler.UpdateIP(hostname, currentIP, lastIP)
|
|
||||||
|
|
||||||
// Send notification
|
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
|
||||||
log.Println("Failed to send notification")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIP update subdomain with current IP
|
|
||||||
func (handler *Handler) UpdateIP(hostname, currentIP, lastIP string) {
|
|
||||||
|
|
||||||
handler.updateDNS(lastIP, currentIP, hostname, "remove")
|
|
||||||
handler.updateDNS(lastIP, currentIP, hostname, "add")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateDNS can add or remove DNS records.
|
|
||||||
func (handler *Handler) updateDNS(dns, ip, hostname, action string) {
|
|
||||||
ipType := "A"
|
|
||||||
if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
|
|
||||||
ipType = "AAAA"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates UUID
|
|
||||||
uid, _ := uuid.NewRandom()
|
|
||||||
values := url.Values{}
|
|
||||||
values.Add("record", hostname)
|
|
||||||
values.Add("key", handler.Configuration.LoginToken)
|
|
||||||
values.Add("type", ipType)
|
|
||||||
values.Add("unique_id", uid.String())
|
|
||||||
switch action {
|
|
||||||
case "remove":
|
|
||||||
// Build URL query (remove)
|
|
||||||
values.Add("cmd", "dns-remove_record")
|
|
||||||
values.Add("value", dns)
|
|
||||||
case "add":
|
|
||||||
// Build URL query (add)
|
|
||||||
values.Add("cmd", "dns-add_record")
|
|
||||||
values.Add("value", ip)
|
|
||||||
default:
|
|
||||||
log.Fatalf("Unknown action %s\n", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
|
||||||
req, _ := http.NewRequest("POST", DreamhostURL, strings.NewReader(values.Encode()))
|
|
||||||
req.SetBasicAuth(handler.Configuration.Email, handler.Configuration.Password)
|
|
||||||
|
|
||||||
if handler.Configuration.UserAgent != "" {
|
|
||||||
req.Header.Add("User-Agent", handler.Configuration.UserAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Request error...")
|
|
||||||
log.Println("Err:", err.Error())
|
|
||||||
} else {
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
log.Println("Update IP success:", string(body))
|
|
||||||
} else {
|
|
||||||
log.Println("Update IP failed:", string(body))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
package duck
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DuckUrl the API address for Duck DNS
|
|
||||||
DuckUrl = "https://www.duckdns.org/update?domains=%s&token=%s&%s"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler struct
|
|
||||||
type Handler struct {
|
|
||||||
Configuration *godns.Settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
|
||||||
handler.Configuration = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
|
||||||
panicChan <- *domain
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
looping := false
|
|
||||||
|
|
||||||
for {
|
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
|
|
||||||
looping = true
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Println("get_currentIP:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("currentIP is:", currentIP)
|
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
|
||||||
var ip string
|
|
||||||
|
|
||||||
if strings.ToUpper(handler.Configuration.IPType) == godns.IPV4 {
|
|
||||||
ip = fmt.Sprintf("ip=%s", currentIP)
|
|
||||||
} else if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
|
|
||||||
ip = fmt.Sprintf("ipv6=%s", currentIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
|
||||||
hostname := subDomain + "." + domain.DomainName
|
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//check against currently known IP, if no change, skip update
|
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
// update IP with HTTP GET request
|
|
||||||
resp, err := client.Get(fmt.Sprintf(DuckUrl, subDomain, handler.Configuration.LoginToken, ip))
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
log.Print("Failed to update sub domain:", subDomain)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil || string(body) != "OK" {
|
|
||||||
log.Println("Failed to update the IP")
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
log.Print("IP updated to:", currentIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send notification
|
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
|
||||||
log.Println("Failed to send notification")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
package google
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// GoogleURL the API address for Google Domains
|
|
||||||
GoogleURL = "https://%s:%s@domains.google.com/nic/update?hostname=%s.%s&myip=%s"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler struct
|
|
||||||
type Handler struct {
|
|
||||||
Configuration *godns.Settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
|
||||||
handler.Configuration = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
|
||||||
panicChan <- *domain
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
looping := false
|
|
||||||
|
|
||||||
for {
|
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
|
|
||||||
looping = true
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("get_currentIP:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Println("currentIP is:", currentIP)
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
|
||||||
hostname := subDomain + "." + domain.DomainName
|
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//check against currently known IP, if no change, skip update
|
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
|
||||||
handler.UpdateIP(domain.DomainName, subDomain, currentIP)
|
|
||||||
|
|
||||||
// Send notification
|
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
|
||||||
log.Println("Failed to send notification")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIP update subdomain with current IP
|
|
||||||
func (handler *Handler) UpdateIP(domain, subDomain, currentIP string) {
|
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
|
||||||
resp, err := client.Get(fmt.Sprintf(GoogleURL,
|
|
||||||
handler.Configuration.Email,
|
|
||||||
handler.Configuration.Password,
|
|
||||||
subDomain,
|
|
||||||
domain,
|
|
||||||
currentIP))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
log.Print("Failed to update sub domain:", subDomain)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Request error...")
|
|
||||||
log.Println("Err:", err.Error())
|
|
||||||
} else {
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
if strings.Contains(string(body), "good") {
|
|
||||||
log.Println("Update IP success:", string(body))
|
|
||||||
} else if strings.Contains(string(body), "nochg") {
|
|
||||||
log.Println("IP not changed:", string(body))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Println("Update IP failed:", string(body))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +1,6 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import "github.com/TimothyYe/godns"
|
||||||
"github.com/TimothyYe/godns"
|
|
||||||
"github.com/TimothyYe/godns/handler/alidns"
|
|
||||||
"github.com/TimothyYe/godns/handler/cloudflare"
|
|
||||||
"github.com/TimothyYe/godns/handler/dnspod"
|
|
||||||
"github.com/TimothyYe/godns/handler/dreamhost"
|
|
||||||
"github.com/TimothyYe/godns/handler/duck"
|
|
||||||
"github.com/TimothyYe/godns/handler/google"
|
|
||||||
"github.com/TimothyYe/godns/handler/he"
|
|
||||||
"github.com/TimothyYe/godns/handler/noip"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IHandler is the interface for all DNS handlers
|
// IHandler is the interface for all DNS handlers
|
||||||
type IHandler interface {
|
type IHandler interface {
|
||||||
@ -18,27 +8,17 @@ type IHandler interface {
|
|||||||
DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain)
|
DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateHandler creates DNS handler by different providers
|
// CreateHandler creates dns handler by different providers
|
||||||
func CreateHandler(provider string) IHandler {
|
func CreateHandler(provider string) IHandler {
|
||||||
var handler IHandler
|
var handler IHandler
|
||||||
|
|
||||||
switch provider {
|
switch provider {
|
||||||
case godns.CLOUDFLARE:
|
case godns.CLOUDFLARE:
|
||||||
handler = IHandler(&cloudflare.Handler{})
|
handler = IHandler(&CloudflareHandler{})
|
||||||
case godns.DNSPOD:
|
case godns.DNSPOD:
|
||||||
handler = IHandler(&dnspod.Handler{})
|
handler = IHandler(&DNSPodHandler{})
|
||||||
case godns.DREAMHOST:
|
|
||||||
handler = IHandler(&dreamhost.Handler{})
|
|
||||||
case godns.HE:
|
case godns.HE:
|
||||||
handler = IHandler(&he.Handler{})
|
handler = IHandler(&HEHandler{})
|
||||||
case godns.ALIDNS:
|
|
||||||
handler = IHandler(&alidns.Handler{})
|
|
||||||
case godns.GOOGLE:
|
|
||||||
handler = IHandler(&google.Handler{})
|
|
||||||
case godns.DUCK:
|
|
||||||
handler = IHandler(&duck.Handler{})
|
|
||||||
case godns.NOIP:
|
|
||||||
handler = IHandler(&noip.Handler{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package he
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -11,6 +11,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
"github.com/TimothyYe/godns"
|
||||||
|
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -18,18 +20,18 @@ var (
|
|||||||
HEUrl = "https://dyn.dns.he.net/nic/update"
|
HEUrl = "https://dyn.dns.he.net/nic/update"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler struct
|
// HEHandler struct
|
||||||
type Handler struct {
|
type HEHandler struct {
|
||||||
Configuration *godns.Settings
|
Configuration *godns.Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
// SetConfiguration pass dns settings and store it to handler instance
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
func (handler *HEHandler) SetConfiguration(conf *godns.Settings) {
|
||||||
handler.Configuration = conf
|
handler.Configuration = conf
|
||||||
}
|
}
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
// DomainLoop the main logic loop
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
func (handler *HEHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
||||||
@ -37,15 +39,8 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
looping := false
|
var lastIP string
|
||||||
for {
|
for {
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
looping = true
|
|
||||||
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -55,40 +50,50 @@ func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.
|
|||||||
log.Println("currentIP is:", currentIP)
|
log.Println("currentIP is:", currentIP)
|
||||||
|
|
||||||
//check against locally cached IP, if no change, skip update
|
//check against locally cached IP, if no change, skip update
|
||||||
|
if currentIP == lastIP {
|
||||||
|
log.Printf("IP is the same as cached one. Skip update.\n")
|
||||||
|
} else {
|
||||||
|
lastIP = currentIP
|
||||||
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
for _, subDomain := range domain.SubDomains {
|
||||||
hostname := subDomain + "." + domain.DomainName
|
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//check against currently known IP, if no change, skip update
|
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName)
|
||||||
handler.UpdateIP(domain.DomainName, subDomain, currentIP)
|
handler.UpdateIP(domain.DomainName, subDomain, currentIP)
|
||||||
|
|
||||||
// Send notification
|
// Send mail notification if notify is enabled
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
if handler.Configuration.Notify.Enabled {
|
||||||
log.Println("Failed to send notification")
|
log.Print("Sending notification to:", handler.Configuration.Notify.SendTo)
|
||||||
|
godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Interval is 5 minutes
|
||||||
|
log.Printf("Going to sleep, will start next checking in %d minutes...\r\n", godns.INTERVAL)
|
||||||
|
time.Sleep(time.Minute * godns.INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIP update subdomain with current IP
|
// UpdateIP update subdomain with current IP
|
||||||
func (handler *Handler) UpdateIP(domain, subDomain, currentIP string) {
|
func (handler *HEHandler) UpdateIP(domain, subDomain, currentIP string) {
|
||||||
values := url.Values{}
|
values := url.Values{}
|
||||||
values.Add("hostname", fmt.Sprintf("%s.%s", subDomain, domain))
|
values.Add("hostname", fmt.Sprintf("%s.%s", subDomain, domain))
|
||||||
values.Add("password", handler.Configuration.Password)
|
values.Add("password", handler.Configuration.Password)
|
||||||
values.Add("myip", currentIP)
|
values.Add("myip", currentIP)
|
||||||
|
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
client := &http.Client{}
|
||||||
|
|
||||||
|
if handler.Configuration.Socks5Proxy != "" {
|
||||||
|
log.Println("use socks5 proxy:" + handler.Configuration.Socks5Proxy)
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", handler.Configuration.Socks5Proxy, nil, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("can't connect to the proxy:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httpTransport := &http.Transport{}
|
||||||
|
client.Transport = httpTransport
|
||||||
|
httpTransport.Dial = dialer.Dial
|
||||||
|
}
|
||||||
|
|
||||||
req, _ := http.NewRequest("POST", HEUrl, strings.NewReader(values.Encode()))
|
req, _ := http.NewRequest("POST", HEUrl, strings.NewReader(values.Encode()))
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
@ -1,114 +0,0 @@
|
|||||||
package noip
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/TimothyYe/godns"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// NoIPUrl the API address for NoIP
|
|
||||||
NoIPUrl = "https://%s:%s@dynupdate.no-ip.com/nic/update?hostname=%s&%s"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler struct
|
|
||||||
type Handler struct {
|
|
||||||
Configuration *godns.Settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfiguration pass dns settings and store it to handler instance
|
|
||||||
func (handler *Handler) SetConfiguration(conf *godns.Settings) {
|
|
||||||
handler.Configuration = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainLoop the main logic loop
|
|
||||||
func (handler *Handler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
|
||||||
panicChan <- *domain
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
looping := false
|
|
||||||
|
|
||||||
for {
|
|
||||||
if looping {
|
|
||||||
// Sleep with interval
|
|
||||||
log.Printf("Going to sleep, will start next checking in %d seconds...\r\n", handler.Configuration.Interval)
|
|
||||||
time.Sleep(time.Second * time.Duration(handler.Configuration.Interval))
|
|
||||||
}
|
|
||||||
|
|
||||||
looping = true
|
|
||||||
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Println("get_currentIP:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("currentIP is:", currentIP)
|
|
||||||
client := godns.GetHttpClient(handler.Configuration, handler.Configuration.UseProxy)
|
|
||||||
|
|
||||||
var ip string
|
|
||||||
if strings.ToUpper(handler.Configuration.IPType) == godns.IPV4 {
|
|
||||||
ip = fmt.Sprintf("myip=%s", currentIP)
|
|
||||||
} else if strings.ToUpper(handler.Configuration.IPType) == godns.IPV6 {
|
|
||||||
ip = fmt.Sprintf("myipv6=%s", currentIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, subDomain := range domain.SubDomains {
|
|
||||||
hostname := subDomain + "." + domain.DomainName
|
|
||||||
lastIP, err := godns.ResolveDNS(hostname, handler.Configuration.Resolver, handler.Configuration.IPType)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//check against currently known IP, if no change, skip update
|
|
||||||
if currentIP == lastIP {
|
|
||||||
log.Printf("IP is the same as cached one. Skip update.\n")
|
|
||||||
} else {
|
|
||||||
req, _ := http.NewRequest("GET", fmt.Sprintf(
|
|
||||||
NoIPUrl,
|
|
||||||
handler.Configuration.Email,
|
|
||||||
handler.Configuration.Password,
|
|
||||||
hostname,
|
|
||||||
ip), nil)
|
|
||||||
|
|
||||||
if handler.Configuration.UserAgent != "" {
|
|
||||||
req.Header.Add("User-Agent", handler.Configuration.UserAgent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update IP with HTTP GET request
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
log.Print("Failed to update sub domain:", subDomain)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil || !strings.Contains(string(body), "good") {
|
|
||||||
log.Println("Failed to update the IP")
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
log.Print("IP updated to:", currentIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send notification
|
|
||||||
if err := godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP); err != nil {
|
|
||||||
log.Println("Failed to send notification")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
// Package resolver is a simple dns resolver
|
|
||||||
// based on miekg/dns
|
|
||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNSResolver represents a dns resolver
|
|
||||||
type DNSResolver struct {
|
|
||||||
Servers []string
|
|
||||||
RetryTimes int
|
|
||||||
r *rand.Rand
|
|
||||||
}
|
|
||||||
|
|
||||||
// New initializes DnsResolver.
|
|
||||||
func New(servers []string) *DNSResolver {
|
|
||||||
for i := range servers {
|
|
||||||
servers[i] = net.JoinHostPort(servers[i], "53")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DNSResolver{servers, len(servers) * 2, rand.New(rand.NewSource(time.Now().UnixNano()))}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFromResolvConf initializes DnsResolver from resolv.conf like file.
|
|
||||||
func NewFromResolvConf(path string) (*DNSResolver, error) {
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
return &DNSResolver{}, errors.New("no such file or directory: " + path)
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := dns.ClientConfigFromFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return &DNSResolver{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var servers []string
|
|
||||||
for _, ipAddress := range config.Servers {
|
|
||||||
servers = append(servers, net.JoinHostPort(ipAddress, "53"))
|
|
||||||
}
|
|
||||||
return &DNSResolver{servers, len(servers) * 2, rand.New(rand.NewSource(time.Now().UnixNano()))}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupHost returns IP addresses of provied host.
|
|
||||||
// In case of timeout retries query RetryTimes times.
|
|
||||||
func (r *DNSResolver) LookupHost(host string, dnsType uint16) ([]net.IP, error) {
|
|
||||||
return r.lookupHost(host, dnsType, r.RetryTimes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DNSResolver) lookupHost(host string, dnsType uint16, triesLeft int) ([]net.IP, error) {
|
|
||||||
m1 := new(dns.Msg)
|
|
||||||
m1.Id = dns.Id()
|
|
||||||
m1.RecursionDesired = true
|
|
||||||
m1.Question = make([]dns.Question, 1)
|
|
||||||
|
|
||||||
switch dnsType {
|
|
||||||
case dns.TypeA:
|
|
||||||
m1.Question[0] = dns.Question{Name: dns.Fqdn(host), Qtype: dns.TypeA, Qclass: dns.ClassINET}
|
|
||||||
case dns.TypeAAAA:
|
|
||||||
m1.Question[0] = dns.Question{Name: dns.Fqdn(host), Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}
|
|
||||||
}
|
|
||||||
|
|
||||||
in, err := dns.Exchange(m1, r.Servers[r.r.Intn(len(r.Servers))])
|
|
||||||
|
|
||||||
var result []net.IP
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if strings.HasSuffix(err.Error(), "i/o timeout") && triesLeft > 0 {
|
|
||||||
triesLeft--
|
|
||||||
return r.lookupHost(host, dnsType, triesLeft)
|
|
||||||
}
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if in != nil && in.Rcode != dns.RcodeSuccess {
|
|
||||||
return result, errors.New(dns.RcodeToString[in.Rcode])
|
|
||||||
}
|
|
||||||
|
|
||||||
if dnsType == dns.TypeA {
|
|
||||||
if len(in.Answer) > 0 {
|
|
||||||
for _, record := range in.Answer {
|
|
||||||
if t, ok := record.(*dns.A); ok {
|
|
||||||
result = append(result, t.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return result, errors.New("empty result")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dnsType == dns.TypeAAAA {
|
|
||||||
if len(in.Answer) > 0 {
|
|
||||||
for _, record := range in.Answer {
|
|
||||||
if t, ok := record.(*dns.AAAA); ok {
|
|
||||||
result = append(result, t.AAAA)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return result, errors.New("Cannot resolve this domain, please make sure the IP type is right")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
package resolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
servers := []string{"8.8.8.8", "8.8.4.4"}
|
|
||||||
expectedServers := []string{"8.8.8.8:53", "8.8.4.4:53"}
|
|
||||||
resolver := New(servers)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(resolver.Servers, expectedServers) {
|
|
||||||
t.Error("resolver.Servers: ", resolver.Servers, "should be equal to", expectedServers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupHost_ValidServer(t *testing.T) {
|
|
||||||
resolver := New([]string{"8.8.8.8", "8.8.4.4"})
|
|
||||||
result, err := resolver.LookupHost("google-public-dns-a.google.com", dns.TypeA)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
t.Error("Should succeed dns lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
if result[0].String() != "8.8.8.8" {
|
|
||||||
t.Error("google-public-dns-a.google.com should be resolved to 8.8.8.8")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLookupHostIPv6_ValidServer(t *testing.T) {
|
|
||||||
resolver := New([]string{"2001:4860:4860::8888", "2001:4860:4860::8844"})
|
|
||||||
result, err := resolver.LookupHost("google-public-dns-a.google.com", dns.TypeAAAA)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
t.Error("Should succeed dns lookup")
|
|
||||||
}
|
|
||||||
|
|
||||||
if result[0].String() != "2001:4860:4860::8888" {
|
|
||||||
t.Error("result should be: 2001:4860:4860::8888")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
settings.go
40
settings.go
@ -12,26 +12,8 @@ type Domain struct {
|
|||||||
SubDomains []string `json:"sub_domains"`
|
SubDomains []string `json:"sub_domains"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify struct for slack notification
|
|
||||||
type SlackNotify struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
BotApiToken string `json:"bot_api_token"`
|
|
||||||
Channel string `json:"channel"`
|
|
||||||
MsgTemplate string `json:"message_template"`
|
|
||||||
UseProxy bool `json:"use_proxy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify struct for telegram notification
|
|
||||||
type TelegramNotify struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
BotApiKey string `json:"bot_api_key"`
|
|
||||||
ChatId string `json:"chat_id"`
|
|
||||||
MsgTemplate string `json:"message_template"`
|
|
||||||
UseProxy bool `json:"use_proxy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify struct for SMTP notification
|
// Notify struct for SMTP notification
|
||||||
type MailNotify struct {
|
type Notify struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
SMTPServer string `json:"smtp_server"`
|
SMTPServer string `json:"smtp_server"`
|
||||||
SMTPUsername string `json:"smtp_username"`
|
SMTPUsername string `json:"smtp_username"`
|
||||||
@ -40,13 +22,6 @@ type MailNotify struct {
|
|||||||
SendTo string `json:"send_to"`
|
SendTo string `json:"send_to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify struct
|
|
||||||
type Notify struct {
|
|
||||||
Telegram TelegramNotify `json:"telegram"`
|
|
||||||
Mail MailNotify `json:"mail"`
|
|
||||||
Slack SlackNotify `json:"slack"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings struct
|
// Settings struct
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
@ -55,16 +30,12 @@ type Settings struct {
|
|||||||
LoginToken string `json:"login_token"`
|
LoginToken string `json:"login_token"`
|
||||||
Domains []Domain `json:"domains"`
|
Domains []Domain `json:"domains"`
|
||||||
IPUrl string `json:"ip_url"`
|
IPUrl string `json:"ip_url"`
|
||||||
IPV6Url string `json:"ipv6_url"`
|
|
||||||
Interval int `json:"interval"`
|
|
||||||
UserAgent string `json:"user_agent,omitempty"`
|
|
||||||
LogPath string `json:"log_path"`
|
LogPath string `json:"log_path"`
|
||||||
Socks5Proxy string `json:"socks5_proxy"`
|
Socks5Proxy string `json:"socks5_proxy"`
|
||||||
Notify Notify `json:"notify"`
|
Notify Notify `json:"notify"`
|
||||||
IPInterface string `json:"ip_interface"`
|
IPInterface string `json:"ip_interface"`
|
||||||
IPType string `json:"ip_type"`
|
//the code is not ready to update AAAA record
|
||||||
Resolver string `json:"resolver"`
|
//IPType string `json:"ip_type"`
|
||||||
UseProxy bool `json:"use_proxy"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadSettings -- Load settings from config file
|
// LoadSettings -- Load settings from config file
|
||||||
@ -82,10 +53,5 @@ func LoadSettings(configPath string, settings *Settings) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.Interval == 0 {
|
|
||||||
// set default interval as 5 minutes if interval is 0
|
|
||||||
settings.Interval = 5 * 60
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ var mailTemplate = `
|
|||||||
<h1 class="size-28" style="Margin-top: 0;Margin-bottom: 0;font-style: normal;font-weight: normal;color: #000;font-size: 24px;line-height: 32px;font-family: avenir,sans-serif;text-align: center;"
|
<h1 class="size-28" style="Margin-top: 0;Margin-bottom: 0;font-style: normal;font-weight: normal;color: #000;font-size: 24px;line-height: 32px;font-family: avenir,sans-serif;text-align: center;"
|
||||||
lang="x-size-28">
|
lang="x-size-28">
|
||||||
<span class="font-avenir">
|
<span class="font-avenir">
|
||||||
<span style="color:#ffffff">Your IP address has been changed to</span>
|
<span style="color:#ffffff">Your IP address is changed to</span>
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<h1 class="size-48" style="Margin-top: 20px;Margin-bottom: 0;font-style: normal;font-weight: normal;color: #000;font-size: 36px;line-height: 43px;font-family: avenir,sans-serif;text-align: center;"
|
<h1 class="size-48" style="Margin-top: 20px;Margin-bottom: 0;font-style: normal;font-weight: normal;color: #000;font-size: 36px;line-height: 43px;font-family: avenir,sans-serif;text-align: center;"
|
||||||
|
|||||||
324
utils.go
324
utils.go
@ -2,20 +2,14 @@ package godns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
dnsResolver "github.com/TimothyYe/godns/resolver"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
"gopkg.in/gomail.v2"
|
"gopkg.in/gomail.v2"
|
||||||
)
|
)
|
||||||
@ -40,26 +34,14 @@ https://github.com/TimothyYe/godns
|
|||||||
const (
|
const (
|
||||||
// PanicMax is the max allowed panic times
|
// PanicMax is the max allowed panic times
|
||||||
PanicMax = 5
|
PanicMax = 5
|
||||||
|
// INTERVAL is minute
|
||||||
|
INTERVAL = 5
|
||||||
// DNSPOD for dnspod.cn
|
// DNSPOD for dnspod.cn
|
||||||
DNSPOD = "DNSPod"
|
DNSPOD = "DNSPod"
|
||||||
// HE for he.net
|
// HE for he.net
|
||||||
HE = "HE"
|
HE = "HE"
|
||||||
// CLOUDFLARE for cloudflare.com
|
// CLOUDFLARE for cloudflare.com
|
||||||
CLOUDFLARE = "Cloudflare"
|
CLOUDFLARE = "Cloudflare"
|
||||||
// ALIDNS for AliDNS
|
|
||||||
ALIDNS = "AliDNS"
|
|
||||||
// GOOGLE for Google Domains
|
|
||||||
GOOGLE = "Google"
|
|
||||||
// DUCK for Duck DNS
|
|
||||||
DUCK = "DuckDNS"
|
|
||||||
// DREAMHOST for Dreamhost
|
|
||||||
DREAMHOST = "Dreamhost"
|
|
||||||
// NOIP for NoIP
|
|
||||||
NOIP = "NoIP"
|
|
||||||
// IPV4 for IPV4 mode
|
|
||||||
IPV4 = "IPV4"
|
|
||||||
// IPV6 for IPV6 mode
|
|
||||||
IPV6 = "IPV6"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//GetIPFromInterface gets IP address from the specific interface
|
//GetIPFromInterface gets IP address from the specific interface
|
||||||
@ -98,19 +80,23 @@ func GetIPFromInterface(configuration *Settings) (string, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if isIPv4(ip.String()) {
|
//the code is not ready for updating an AAAA record
|
||||||
if strings.ToUpper(configuration.IPType) != IPV4 {
|
/*
|
||||||
continue
|
if (isIPv4(ip.String())){
|
||||||
}
|
if (configuration.IPType=="IPv6"){
|
||||||
} else {
|
continue;
|
||||||
if strings.ToUpper(configuration.IPType) != IPV6 {
|
}
|
||||||
continue
|
}else{
|
||||||
}
|
if (configuration.IPType!="IPv6"){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
if !isIPv4(ip.String()) {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip.String() != "" {
|
return ip.String(), nil
|
||||||
return ip.String(), nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return "", errors.New("can't get a vaild address from " + configuration.IPInterface)
|
return "", errors.New("can't get a vaild address from " + configuration.IPInterface)
|
||||||
}
|
}
|
||||||
@ -119,31 +105,11 @@ func isIPv4(ip string) bool {
|
|||||||
return strings.Count(ip, ":") < 2
|
return strings.Count(ip, ":") < 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHttpClient creates the HTTP client and return it
|
|
||||||
func GetHttpClient(configuration *Settings, useProxy bool) *http.Client {
|
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
if useProxy && configuration.Socks5Proxy != "" {
|
|
||||||
log.Println("use socks5 proxy:" + configuration.Socks5Proxy)
|
|
||||||
dialer, err := proxy.SOCKS5("tcp", configuration.Socks5Proxy, nil, proxy.Direct)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("can't connect to the proxy:", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
httpTransport := &http.Transport{}
|
|
||||||
client.Transport = httpTransport
|
|
||||||
httpTransport.Dial = dialer.Dial
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
//GetCurrentIP gets an IP from either internet or specific interface, depending on configuration
|
//GetCurrentIP gets an IP from either internet or specific interface, depending on configuration
|
||||||
func GetCurrentIP(configuration *Settings) (string, error) {
|
func GetCurrentIP(configuration *Settings) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if configuration.IPUrl != "" || configuration.IPV6Url != "" {
|
if configuration.IPUrl != "" {
|
||||||
ip, err := GetIPOnline(configuration)
|
ip, err := GetIPOnline(configuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("get ip online failed. Fallback to get ip from interface if possible.")
|
log.Println("get ip online failed. Fallback to get ip from interface if possible.")
|
||||||
@ -168,15 +134,22 @@ func GetCurrentIP(configuration *Settings) (string, error) {
|
|||||||
func GetIPOnline(configuration *Settings) (string, error) {
|
func GetIPOnline(configuration *Settings) (string, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
var response *http.Response
|
if configuration.Socks5Proxy != "" {
|
||||||
var err error
|
|
||||||
|
|
||||||
if configuration.IPType == "" || strings.ToUpper(configuration.IPType) == IPV4 {
|
log.Println("use socks5 proxy:" + configuration.Socks5Proxy)
|
||||||
response, err = client.Get(configuration.IPUrl)
|
dialer, err := proxy.SOCKS5("tcp", configuration.Socks5Proxy, nil, proxy.Direct)
|
||||||
} else {
|
if err != nil {
|
||||||
response, err = client.Get(configuration.IPV6Url)
|
log.Println("can't connect to the proxy:", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpTransport := &http.Transport{}
|
||||||
|
client.Transport = httpTransport
|
||||||
|
httpTransport.Dial = dialer.Dial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response, err := client.Get(configuration.IPUrl)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Cannot get IP...")
|
log.Println("Cannot get IP...")
|
||||||
return "", err
|
return "", err
|
||||||
@ -190,228 +163,52 @@ func GetIPOnline(configuration *Settings) (string, error) {
|
|||||||
|
|
||||||
// CheckSettings check the format of settings
|
// CheckSettings check the format of settings
|
||||||
func CheckSettings(config *Settings) error {
|
func CheckSettings(config *Settings) error {
|
||||||
switch config.Provider {
|
if config.Provider == DNSPOD {
|
||||||
case DNSPOD:
|
|
||||||
if config.Password == "" && config.LoginToken == "" {
|
if config.Password == "" && config.LoginToken == "" {
|
||||||
return errors.New("password or login token cannot be empty")
|
return errors.New("password or login token cannot be empty")
|
||||||
}
|
}
|
||||||
case HE:
|
} else if config.Provider == HE {
|
||||||
if config.Password == "" {
|
if config.Password == "" {
|
||||||
return errors.New("password cannot be empty")
|
return errors.New("password cannot be empty")
|
||||||
}
|
}
|
||||||
case CLOUDFLARE:
|
} else if config.Provider == CLOUDFLARE {
|
||||||
if config.LoginToken == "" {
|
|
||||||
if config.Email == "" {
|
|
||||||
return errors.New("email cannot be empty")
|
|
||||||
}
|
|
||||||
if config.Password == "" {
|
|
||||||
return errors.New("password cannot be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ALIDNS:
|
|
||||||
if config.Email == "" {
|
if config.Email == "" {
|
||||||
return errors.New("email cannot be empty")
|
return errors.New("email cannot be empty")
|
||||||
}
|
}
|
||||||
if config.Password == "" {
|
if config.Password == "" {
|
||||||
return errors.New("password cannot be empty")
|
return errors.New("password cannot be empty")
|
||||||
}
|
}
|
||||||
case DUCK:
|
} else {
|
||||||
if config.LoginToken == "" {
|
return errors.New("please provide supported DNS provider: DNSPod/HE")
|
||||||
return errors.New("login token cannot be empty")
|
|
||||||
}
|
|
||||||
case GOOGLE:
|
|
||||||
fallthrough
|
|
||||||
case NOIP:
|
|
||||||
if config.Email == "" {
|
|
||||||
return errors.New("email cannot be empty")
|
|
||||||
}
|
|
||||||
if config.Password == "" {
|
|
||||||
return errors.New("password cannot be empty")
|
|
||||||
}
|
|
||||||
case DREAMHOST:
|
|
||||||
if config.LoginToken == "" {
|
|
||||||
return errors.New("login token cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return errors.New("please provide supported DNS provider: DNSPod/HE/AliDNS/Cloudflare/GoogleDomain/DuckDNS/Dreamhost")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTelegramNotify sends notify if IP is changed
|
// SendNotify sends mail notify if IP is changed
|
||||||
func SendTelegramNotify(configuration *Settings, domain, currentIP string) error {
|
func SendNotify(configuration *Settings, domain, currentIP string) error {
|
||||||
if !configuration.Notify.Telegram.Enabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if configuration.Notify.Telegram.BotApiKey == "" {
|
|
||||||
return errors.New("bot api key cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if configuration.Notify.Telegram.ChatId == "" {
|
|
||||||
return errors.New("chat id cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
client := GetHttpClient(configuration, configuration.Notify.Telegram.UseProxy)
|
|
||||||
tpl := configuration.Notify.Telegram.MsgTemplate
|
|
||||||
if tpl == "" {
|
|
||||||
tpl = "_Your IP address is changed to_%0A%0A*{{ .CurrentIP }}*%0A%0ADomain *{{ .Domain }}* is updated"
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := buildTemplate(currentIP, domain, tpl)
|
|
||||||
reqURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&parse_mode=Markdown&text=%s",
|
|
||||||
configuration.Notify.Telegram.BotApiKey,
|
|
||||||
configuration.Notify.Telegram.ChatId,
|
|
||||||
msg)
|
|
||||||
var response *http.Response
|
|
||||||
var err error
|
|
||||||
|
|
||||||
response, err = client.Get(reqURL)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(response.Body)
|
|
||||||
type ResponseParameters struct {
|
|
||||||
MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
|
|
||||||
RetryAfter int `json:"retry_after"` // optional
|
|
||||||
}
|
|
||||||
type APIResponse struct {
|
|
||||||
Ok bool `json:"ok"`
|
|
||||||
Result json.RawMessage `json:"result"`
|
|
||||||
ErrorCode int `json:"error_code"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Parameters *ResponseParameters `json:"parameters"`
|
|
||||||
}
|
|
||||||
var resp APIResponse
|
|
||||||
err = json.Unmarshal(body, &resp)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("error:", err)
|
|
||||||
return errors.New("failed to parse response")
|
|
||||||
}
|
|
||||||
if !resp.Ok {
|
|
||||||
return errors.New(resp.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendMailNotify sends mail notify if IP is changed
|
|
||||||
func SendMailNotify(configuration *Settings, domain, currentIP string) error {
|
|
||||||
if !configuration.Notify.Mail.Enabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Print("Sending notification to:", configuration.Notify.Mail.SendTo)
|
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
|
|
||||||
m.SetHeader("From", configuration.Notify.Mail.SMTPUsername)
|
m.SetHeader("From", configuration.Notify.SMTPUsername)
|
||||||
m.SetHeader("To", configuration.Notify.Mail.SendTo)
|
m.SetHeader("To", configuration.Notify.SendTo)
|
||||||
m.SetHeader("Subject", "GoDNS Notification")
|
m.SetHeader("Subject", "GoDNS Notification")
|
||||||
log.Println("currentIP:", currentIP)
|
log.Println("currentIP:", currentIP)
|
||||||
log.Println("domain:", domain)
|
log.Println("domain:", domain)
|
||||||
m.SetBody("text/html", buildTemplate(currentIP, domain, mailTemplate))
|
m.SetBody("text/html", buildTemplate(currentIP, domain))
|
||||||
|
|
||||||
d := gomail.NewDialer(configuration.Notify.Mail.SMTPServer, configuration.Notify.Mail.SMTPPort, configuration.Notify.Mail.SMTPUsername, configuration.Notify.Mail.SMTPPassword)
|
d := gomail.NewPlainDialer(configuration.Notify.SMTPServer, configuration.Notify.SMTPPort, configuration.Notify.SMTPUsername, configuration.Notify.SMTPPassword)
|
||||||
|
|
||||||
// Send the email config by sendlist .
|
// Send the email config by sendlist .
|
||||||
if err := d.DialAndSend(m); err != nil {
|
if err := d.DialAndSend(m); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendSlack sends slack if IP is changed
|
|
||||||
func SendSlackNotify(configuration *Settings, domain, currentIP string) error {
|
|
||||||
if !configuration.Notify.Slack.Enabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if configuration.Notify.Slack.BotApiToken == "" {
|
|
||||||
return errors.New("bot api token cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if configuration.Notify.Slack.Channel == "" {
|
|
||||||
return errors.New("channel cannot be empty")
|
|
||||||
}
|
|
||||||
client := GetHttpClient(configuration, configuration.Notify.Slack.UseProxy)
|
|
||||||
tpl := configuration.Notify.Slack.MsgTemplate
|
|
||||||
if tpl == "" {
|
|
||||||
tpl = "_Your IP address is changed to_\n\n*{{ .CurrentIP }}*\n\nDomain *{{ .Domain }}* is updated"
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := buildTemplate(currentIP, domain, tpl)
|
|
||||||
|
|
||||||
var response *http.Response
|
|
||||||
var err error
|
|
||||||
|
|
||||||
formData := url.Values{
|
|
||||||
"token": {configuration.Notify.Slack.BotApiToken},
|
|
||||||
"channel": {configuration.Notify.Slack.Channel},
|
|
||||||
"text": {msg},
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err = client.PostForm("https://slack.com/api/chat.postMessage", formData)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(response.Body)
|
|
||||||
type ResponseParameters struct {
|
|
||||||
MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
|
|
||||||
RetryAfter int `json:"retry_after"` // optional
|
|
||||||
}
|
|
||||||
type APIResponse struct {
|
|
||||||
Ok bool `json:"ok"`
|
|
||||||
Result json.RawMessage `json:"result"`
|
|
||||||
ErrorCode int `json:"error_code"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Parameters *ResponseParameters `json:"parameters"`
|
|
||||||
}
|
|
||||||
var resp APIResponse
|
|
||||||
err = json.Unmarshal(body, &resp)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("error:", err)
|
|
||||||
return errors.New("failed to parse response")
|
|
||||||
}
|
|
||||||
if !resp.Ok {
|
|
||||||
return errors.New(resp.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendNotify sends notify if IP is changed
|
|
||||||
func SendNotify(configuration *Settings, domain, currentIP string) error {
|
|
||||||
err := SendTelegramNotify(configuration, domain, currentIP)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Send telegram notification with error:", err.Error())
|
|
||||||
}
|
|
||||||
err = SendMailNotify(configuration, domain, currentIP)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Send email notification with error:", err.Error())
|
log.Println("Send email notification with error:", err.Error())
|
||||||
}
|
return err
|
||||||
err = SendSlackNotify(configuration, domain, currentIP)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Send slack notification with error:", err.Error())
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTemplate(currentIP, domain string, tplsrc string) string {
|
func buildTemplate(currentIP, domain string) string {
|
||||||
t := template.New("notification template")
|
t := template.New("notification template")
|
||||||
if _, err := t.Parse(tplsrc); err != nil {
|
t.Parse(mailTemplate)
|
||||||
log.Println("Failed to parse template")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
CurrentIP string
|
CurrentIP string
|
||||||
@ -429,34 +226,3 @@ func buildTemplate(currentIP, domain string, tplsrc string) string {
|
|||||||
|
|
||||||
return tpl.String()
|
return tpl.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveDNS will query DNS for a given hostname.
|
|
||||||
func ResolveDNS(hostname, resolver, ipType string) (string, error) {
|
|
||||||
var dnsType uint16
|
|
||||||
if ipType == "" || strings.ToUpper(ipType) == IPV4 {
|
|
||||||
dnsType = dns.TypeA
|
|
||||||
} else {
|
|
||||||
dnsType = dns.TypeAAAA
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no DNS server is set in config file, falls back to default resolver.
|
|
||||||
if resolver == "" {
|
|
||||||
dnsAdress, err := net.LookupHost(hostname)
|
|
||||||
if err != nil {
|
|
||||||
return "<nil>", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return dnsAdress[0], nil
|
|
||||||
}
|
|
||||||
res := dnsResolver.New([]string{resolver})
|
|
||||||
// In case of i/o timeout
|
|
||||||
res.RetryTimes = 5
|
|
||||||
|
|
||||||
ip, err := res.LookupHost(hostname, dnsType)
|
|
||||||
if err != nil {
|
|
||||||
return "<nil>", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ip[0].String(), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestGetCurrentIP(t *testing.T) {
|
func TestGetCurrentIP(t *testing.T) {
|
||||||
conf := &Settings{IPUrl: "https://myip.biturl.top"}
|
conf := &Settings{IPUrl: "http://members.3322.org/dyndns/getip"}
|
||||||
ip, _ := GetCurrentIP(conf)
|
ip, _ := GetCurrentIP(conf)
|
||||||
|
|
||||||
if ip == "" {
|
if ip == "" {
|
||||||
@ -13,6 +13,13 @@ func TestGetCurrentIP(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
t.Log("IP is:" + ip)
|
t.Log("IP is:" + ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf = &Settings{Socks5Proxy: "localhost:8899", IPUrl: "http://members.3322.org/dyndns/getip"}
|
||||||
|
ip, err := GetCurrentIP(conf)
|
||||||
|
|
||||||
|
if ip != "" && err == nil {
|
||||||
|
t.Error("should return error")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckSettings(t *testing.T) {
|
func TestCheckSettings(t *testing.T) {
|
||||||
|
|||||||
10
vendor/github.com/bitly/go-simplejson/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/bitly/go-simplejson/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.0.3
|
||||||
|
- 1.1.2
|
||||||
|
- 1.2
|
||||||
|
- tip
|
||||||
|
install:
|
||||||
|
- go get github.com/bmizerany/assert
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
17
vendor/github.com/bitly/go-simplejson/LICENSE
generated
vendored
Normal file
17
vendor/github.com/bitly/go-simplejson/LICENSE
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
13
vendor/github.com/bitly/go-simplejson/README.md
generated
vendored
Normal file
13
vendor/github.com/bitly/go-simplejson/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
### go-simplejson
|
||||||
|
|
||||||
|
a Go package to interact with arbitrary JSON
|
||||||
|
|
||||||
|
[](http://travis-ci.org/bitly/go-simplejson)
|
||||||
|
|
||||||
|
### Importing
|
||||||
|
|
||||||
|
import github.com/bitly/go-simplejson
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
Visit the docs on [gopkgdoc](http://godoc.org/github.com/bitly/go-simplejson)
|
||||||
446
vendor/github.com/bitly/go-simplejson/simplejson.go
generated
vendored
Normal file
446
vendor/github.com/bitly/go-simplejson/simplejson.go
generated
vendored
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
package simplejson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// returns the current implementation version
|
||||||
|
func Version() string {
|
||||||
|
return "0.5.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Json struct {
|
||||||
|
data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJson returns a pointer to a new `Json` object
|
||||||
|
// after unmarshaling `body` bytes
|
||||||
|
func NewJson(body []byte) (*Json, error) {
|
||||||
|
j := new(Json)
|
||||||
|
err := j.UnmarshalJSON(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a pointer to a new, empty `Json` object
|
||||||
|
func New() *Json {
|
||||||
|
return &Json{
|
||||||
|
data: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface returns the underlying data
|
||||||
|
func (j *Json) Interface() interface{} {
|
||||||
|
return j.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns its marshaled data as `[]byte`
|
||||||
|
func (j *Json) Encode() ([]byte, error) {
|
||||||
|
return j.MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodePretty returns its marshaled data as `[]byte` with indentation
|
||||||
|
func (j *Json) EncodePretty() ([]byte, error) {
|
||||||
|
return json.MarshalIndent(&j.data, "", " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the json.Marshaler interface.
|
||||||
|
func (j *Json) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(&j.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set modifies `Json` map by `key` and `value`
|
||||||
|
// Useful for changing single key/value in a `Json` object easily.
|
||||||
|
func (j *Json) Set(key string, val interface{}) {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPath modifies `Json`, recursively checking/creating map keys for the supplied path,
|
||||||
|
// and then finally writing in the value
|
||||||
|
func (j *Json) SetPath(branch []string, val interface{}) {
|
||||||
|
if len(branch) == 0 {
|
||||||
|
j.data = val
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// in order to insert our branch, we need map[string]interface{}
|
||||||
|
if _, ok := (j.data).(map[string]interface{}); !ok {
|
||||||
|
// have to replace with something suitable
|
||||||
|
j.data = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
curr := j.data.(map[string]interface{})
|
||||||
|
|
||||||
|
for i := 0; i < len(branch)-1; i++ {
|
||||||
|
b := branch[i]
|
||||||
|
// key exists?
|
||||||
|
if _, ok := curr[b]; !ok {
|
||||||
|
n := make(map[string]interface{})
|
||||||
|
curr[b] = n
|
||||||
|
curr = n
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the value is the right sort of thing
|
||||||
|
if _, ok := curr[b].(map[string]interface{}); !ok {
|
||||||
|
// have to replace with something suitable
|
||||||
|
n := make(map[string]interface{})
|
||||||
|
curr[b] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
curr = curr[b].(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// add remaining k/v
|
||||||
|
curr[branch[len(branch)-1]] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del modifies `Json` map by deleting `key` if it is present.
|
||||||
|
func (j *Json) Del(key string) {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(m, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a pointer to a new `Json` object
|
||||||
|
// for `key` in its `map` representation
|
||||||
|
//
|
||||||
|
// useful for chaining operations (to traverse a nested JSON):
|
||||||
|
// js.Get("top_level").Get("dict").Get("value").Int()
|
||||||
|
func (j *Json) Get(key string) *Json {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err == nil {
|
||||||
|
if val, ok := m[key]; ok {
|
||||||
|
return &Json{val}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Json{nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPath searches for the item as specified by the branch
|
||||||
|
// without the need to deep dive using Get()'s.
|
||||||
|
//
|
||||||
|
// js.GetPath("top_level", "dict")
|
||||||
|
func (j *Json) GetPath(branch ...string) *Json {
|
||||||
|
jin := j
|
||||||
|
for _, p := range branch {
|
||||||
|
jin = jin.Get(p)
|
||||||
|
}
|
||||||
|
return jin
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndex returns a pointer to a new `Json` object
|
||||||
|
// for `index` in its `array` representation
|
||||||
|
//
|
||||||
|
// this is the analog to Get when accessing elements of
|
||||||
|
// a json array instead of a json object:
|
||||||
|
// js.Get("top_level").Get("array").GetIndex(1).Get("key").Int()
|
||||||
|
func (j *Json) GetIndex(index int) *Json {
|
||||||
|
a, err := j.Array()
|
||||||
|
if err == nil {
|
||||||
|
if len(a) > index {
|
||||||
|
return &Json{a[index]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Json{nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckGet returns a pointer to a new `Json` object and
|
||||||
|
// a `bool` identifying success or failure
|
||||||
|
//
|
||||||
|
// useful for chained operations when success is important:
|
||||||
|
// if data, ok := js.Get("top_level").CheckGet("inner"); ok {
|
||||||
|
// log.Println(data)
|
||||||
|
// }
|
||||||
|
func (j *Json) CheckGet(key string) (*Json, bool) {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err == nil {
|
||||||
|
if val, ok := m[key]; ok {
|
||||||
|
return &Json{val}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map type asserts to `map`
|
||||||
|
func (j *Json) Map() (map[string]interface{}, error) {
|
||||||
|
if m, ok := (j.data).(map[string]interface{}); ok {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("type assertion to map[string]interface{} failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array type asserts to an `array`
|
||||||
|
func (j *Json) Array() ([]interface{}, error) {
|
||||||
|
if a, ok := (j.data).([]interface{}); ok {
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("type assertion to []interface{} failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool type asserts to `bool`
|
||||||
|
func (j *Json) Bool() (bool, error) {
|
||||||
|
if s, ok := (j.data).(bool); ok {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return false, errors.New("type assertion to bool failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// String type asserts to `string`
|
||||||
|
func (j *Json) String() (string, error) {
|
||||||
|
if s, ok := (j.data).(string); ok {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return "", errors.New("type assertion to string failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes type asserts to `[]byte`
|
||||||
|
func (j *Json) Bytes() ([]byte, error) {
|
||||||
|
if s, ok := (j.data).(string); ok {
|
||||||
|
return []byte(s), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("type assertion to []byte failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringArray type asserts to an `array` of `string`
|
||||||
|
func (j *Json) StringArray() ([]string, error) {
|
||||||
|
arr, err := j.Array()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
retArr := make([]string, 0, len(arr))
|
||||||
|
for _, a := range arr {
|
||||||
|
if a == nil {
|
||||||
|
retArr = append(retArr, "")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s, ok := a.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
retArr = append(retArr, s)
|
||||||
|
}
|
||||||
|
return retArr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustArray guarantees the return of a `[]interface{}` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you want to interate over array values in a succinct manner:
|
||||||
|
// for i, v := range js.Get("results").MustArray() {
|
||||||
|
// fmt.Println(i, v)
|
||||||
|
// }
|
||||||
|
func (j *Json) MustArray(args ...[]interface{}) []interface{} {
|
||||||
|
var def []interface{}
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustArray() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := j.Array()
|
||||||
|
if err == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustMap guarantees the return of a `map[string]interface{}` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you want to interate over map values in a succinct manner:
|
||||||
|
// for k, v := range js.Get("dictionary").MustMap() {
|
||||||
|
// fmt.Println(k, v)
|
||||||
|
// }
|
||||||
|
func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} {
|
||||||
|
var def map[string]interface{}
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustMap() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := j.Map()
|
||||||
|
if err == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustString guarantees the return of a `string` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want a `string` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default"))
|
||||||
|
func (j *Json) MustString(args ...string) string {
|
||||||
|
var def string
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustString() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := j.String()
|
||||||
|
if err == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustStringArray guarantees the return of a `[]string` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you want to interate over array values in a succinct manner:
|
||||||
|
// for i, s := range js.Get("results").MustStringArray() {
|
||||||
|
// fmt.Println(i, s)
|
||||||
|
// }
|
||||||
|
func (j *Json) MustStringArray(args ...[]string) []string {
|
||||||
|
var def []string
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustStringArray() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := j.StringArray()
|
||||||
|
if err == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt guarantees the return of an `int` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want an `int` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150))
|
||||||
|
func (j *Json) MustInt(args ...int) int {
|
||||||
|
var def int
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustInt() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := j.Int()
|
||||||
|
if err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFloat64 guarantees the return of a `float64` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want a `float64` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150))
|
||||||
|
func (j *Json) MustFloat64(args ...float64) float64 {
|
||||||
|
var def float64
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustFloat64() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := j.Float64()
|
||||||
|
if err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustBool guarantees the return of a `bool` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want a `bool` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(true))
|
||||||
|
func (j *Json) MustBool(args ...bool) bool {
|
||||||
|
var def bool
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustBool() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := j.Bool()
|
||||||
|
if err == nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt64 guarantees the return of an `int64` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want an `int64` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustInt64(), js.Get("optional_param").MustInt64(5150))
|
||||||
|
func (j *Json) MustInt64(args ...int64) int64 {
|
||||||
|
var def int64
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustInt64() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := j.Int64()
|
||||||
|
if err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUInt64 guarantees the return of an `uint64` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want an `uint64` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustUint64(), js.Get("optional_param").MustUint64(5150))
|
||||||
|
func (j *Json) MustUint64(args ...uint64) uint64 {
|
||||||
|
var def uint64
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustUint64() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := j.Uint64()
|
||||||
|
if err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
75
vendor/github.com/bitly/go-simplejson/simplejson_go10.go
generated
vendored
Normal file
75
vendor/github.com/bitly/go-simplejson/simplejson_go10.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// +build !go1.1
|
||||||
|
|
||||||
|
package simplejson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFromReader returns a *Json by decoding from an io.Reader
|
||||||
|
func NewFromReader(r io.Reader) (*Json, error) {
|
||||||
|
j := new(Json)
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
err := dec.Decode(&j.data)
|
||||||
|
return j, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the json.Unmarshaler interface.
|
||||||
|
func (j *Json) UnmarshalJSON(p []byte) error {
|
||||||
|
return json.Unmarshal(p, &j.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 coerces into a float64
|
||||||
|
func (j *Json) Float64() (float64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case float32, float64:
|
||||||
|
return reflect.ValueOf(j.data).Float(), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return float64(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return float64(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int coerces into an int
|
||||||
|
func (j *Json) Int() (int, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case float32, float64:
|
||||||
|
return int(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return int(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return int(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 coerces into an int64
|
||||||
|
func (j *Json) Int64() (int64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case float32, float64:
|
||||||
|
return int64(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return reflect.ValueOf(j.data).Int(), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return int64(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 coerces into an uint64
|
||||||
|
func (j *Json) Uint64() (uint64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case float32, float64:
|
||||||
|
return uint64(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return uint64(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return reflect.ValueOf(j.data).Uint(), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
89
vendor/github.com/bitly/go-simplejson/simplejson_go11.go
generated
vendored
Normal file
89
vendor/github.com/bitly/go-simplejson/simplejson_go11.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// +build go1.1
|
||||||
|
|
||||||
|
package simplejson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implements the json.Unmarshaler interface.
|
||||||
|
func (j *Json) UnmarshalJSON(p []byte) error {
|
||||||
|
dec := json.NewDecoder(bytes.NewBuffer(p))
|
||||||
|
dec.UseNumber()
|
||||||
|
return dec.Decode(&j.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromReader returns a *Json by decoding from an io.Reader
|
||||||
|
func NewFromReader(r io.Reader) (*Json, error) {
|
||||||
|
j := new(Json)
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
dec.UseNumber()
|
||||||
|
err := dec.Decode(&j.data)
|
||||||
|
return j, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 coerces into a float64
|
||||||
|
func (j *Json) Float64() (float64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
return j.data.(json.Number).Float64()
|
||||||
|
case float32, float64:
|
||||||
|
return reflect.ValueOf(j.data).Float(), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return float64(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return float64(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int coerces into an int
|
||||||
|
func (j *Json) Int() (int, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
i, err := j.data.(json.Number).Int64()
|
||||||
|
return int(i), err
|
||||||
|
case float32, float64:
|
||||||
|
return int(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return int(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return int(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 coerces into an int64
|
||||||
|
func (j *Json) Int64() (int64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
return j.data.(json.Number).Int64()
|
||||||
|
case float32, float64:
|
||||||
|
return int64(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return reflect.ValueOf(j.data).Int(), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return int64(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 coerces into an uint64
|
||||||
|
func (j *Json) Uint64() (uint64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
return strconv.ParseUint(j.data.(json.Number).String(), 10, 64)
|
||||||
|
case float32, float64:
|
||||||
|
return uint64(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return uint64(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return reflect.ValueOf(j.data).Uint(), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
5
vendor/github.com/fatih/color/.travis.yml
generated
vendored
Normal file
5
vendor/github.com/fatih/color/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.8.x
|
||||||
|
- tip
|
||||||
|
|
||||||
27
vendor/github.com/fatih/color/Gopkg.lock
generated
vendored
Normal file
27
vendor/github.com/fatih/color/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/mattn/go-colorable"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||||
|
version = "v0.0.9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/mattn/go-isatty"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||||
|
version = "v0.0.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
||||||
30
vendor/github.com/fatih/color/Gopkg.toml
generated
vendored
Normal file
30
vendor/github.com/fatih/color/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/mattn/go-colorable"
|
||||||
|
version = "0.0.9"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/mattn/go-isatty"
|
||||||
|
version = "0.0.3"
|
||||||
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Fatih Arslan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
179
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
179
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# Color [](https://godoc.org/github.com/fatih/color) [](https://travis-ci.org/fatih/color)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||||
|
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||||
|
has support for Windows too! The API can be used in several ways, pick one that
|
||||||
|
suits you.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/fatih/color
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `vendor` folder is here for stability. Remove the folder if you
|
||||||
|
already have the dependencies in your GOPATH.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Standard colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Print with default helper functions
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// A newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// These are using the default foreground colors
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mix and reuse colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with white background.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use your own output (io.Writer)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use your own io.Writer output
|
||||||
|
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||||
|
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
blue.Fprint(writer, "This will print text in blue.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom print functions (PrintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a custom print function for convenience
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("Warning")
|
||||||
|
red("Error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom fprint functions (FprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
blue := color.New(FgBlue).FprintfFunc()
|
||||||
|
blue(myWriter, "important notice: %s", stars)
|
||||||
|
|
||||||
|
// Mix up with multiple attributes
|
||||||
|
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||||
|
success(myWriter, "Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert into noncolor strings (SprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
yellow := color.New(color.FgYellow).SprintFunc()
|
||||||
|
red := color.New(color.FgRed).SprintFunc()
|
||||||
|
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("This %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
// Use helper functions
|
||||||
|
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||||
|
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||||
|
|
||||||
|
// Windows supported too! Just don't forget to change the output to color.Output
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plug into existing code
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use handy standard colors
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will now be in yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // Don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // Use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will now be bold magenta.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable/Enable color
|
||||||
|
|
||||||
|
There might be a case where you want to explicitly disable/enable color output. the
|
||||||
|
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||||
|
(for example if the output were piped directly to `less`)
|
||||||
|
|
||||||
|
`Color` has support to disable/enable colors both globally and for single color
|
||||||
|
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
|
||||||
|
can easily disable the color output with:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
* Save/Return previous values
|
||||||
|
* Evaluate fmt.Formatter interface
|
||||||
|
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Fatih Arslan](https://github.com/fatih)
|
||||||
|
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||||
|
|
||||||
603
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
603
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
@ -0,0 +1,603 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||||
|
// false or true based on the stdout's file descriptor referring to a terminal
|
||||||
|
// or not. This is a global option and affects all colors. For more control
|
||||||
|
// over each color block use the methods DisableColor() individually.
|
||||||
|
NoColor = os.Getenv("TERM") == "dumb" ||
|
||||||
|
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||||
|
|
||||||
|
// Output defines the standard output of the print functions. By default
|
||||||
|
// os.Stdout is used.
|
||||||
|
Output = colorable.NewColorableStdout()
|
||||||
|
|
||||||
|
// Error defines a color supporting writer for os.Stderr.
|
||||||
|
Error = colorable.NewColorableStderr()
|
||||||
|
|
||||||
|
// colorsCache is used to reduce the count of created Color objects and
|
||||||
|
// allows to reuse already created objects with required Attribute.
|
||||||
|
colorsCache = make(map[Attribute]*Color)
|
||||||
|
colorsCacheMu sync.Mutex // protects colorsCache
|
||||||
|
)
|
||||||
|
|
||||||
|
// Color defines a custom color object which is defined by SGR parameters.
|
||||||
|
type Color struct {
|
||||||
|
params []Attribute
|
||||||
|
noColor *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute defines a single SGR Code
|
||||||
|
type Attribute int
|
||||||
|
|
||||||
|
const escape = "\x1b"
|
||||||
|
|
||||||
|
// Base attributes
|
||||||
|
const (
|
||||||
|
Reset Attribute = iota
|
||||||
|
Bold
|
||||||
|
Faint
|
||||||
|
Italic
|
||||||
|
Underline
|
||||||
|
BlinkSlow
|
||||||
|
BlinkRapid
|
||||||
|
ReverseVideo
|
||||||
|
Concealed
|
||||||
|
CrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack Attribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
FgHiBlack Attribute = iota + 90
|
||||||
|
FgHiRed
|
||||||
|
FgHiGreen
|
||||||
|
FgHiYellow
|
||||||
|
FgHiBlue
|
||||||
|
FgHiMagenta
|
||||||
|
FgHiCyan
|
||||||
|
FgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background text colors
|
||||||
|
const (
|
||||||
|
BgBlack Attribute = iota + 40
|
||||||
|
BgRed
|
||||||
|
BgGreen
|
||||||
|
BgYellow
|
||||||
|
BgBlue
|
||||||
|
BgMagenta
|
||||||
|
BgCyan
|
||||||
|
BgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
BgHiBlack Attribute = iota + 100
|
||||||
|
BgHiRed
|
||||||
|
BgHiGreen
|
||||||
|
BgHiYellow
|
||||||
|
BgHiBlue
|
||||||
|
BgHiMagenta
|
||||||
|
BgHiCyan
|
||||||
|
BgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a newly created color object.
|
||||||
|
func New(value ...Attribute) *Color {
|
||||||
|
c := &Color{params: make([]Attribute, 0)}
|
||||||
|
c.Add(value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the given parameters immediately. It will change the color of
|
||||||
|
// output with the given SGR parameters until color.Unset() is called.
|
||||||
|
func Set(p ...Attribute) *Color {
|
||||||
|
c := New(p...)
|
||||||
|
c.Set()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset resets all escape attributes and clears the output. Usually should
|
||||||
|
// be called after Set().
|
||||||
|
func Unset() {
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the SGR sequence.
|
||||||
|
func (c *Color) Set() *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unset() {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) setWriter(w io.Writer) *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unsetWriter(w io.Writer) {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||||
|
func (c *Color) Add(value ...Attribute) *Color {
|
||||||
|
c.params = append(c.params, value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) prepend(value Attribute) {
|
||||||
|
c.params = append(c.params, 0)
|
||||||
|
copy(c.params[1:], c.params[0:])
|
||||||
|
c.params[0] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint formats using the default formats for its operands and writes to w.
|
||||||
|
// Spaces are added between operands when neither is a string.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
c.setWriter(w)
|
||||||
|
defer c.unsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are added between operands when neither is a
|
||||||
|
// string. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprint(Output, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf formats according to a format specifier and writes to w.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.setWriter(w)
|
||||||
|
defer c.unsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf formats according to a format specifier and writes to standard output.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||||
|
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprintf(Output, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln formats using the default formats for its operands and writes to w.
|
||||||
|
// Spaces are always added between operands and a newline is appended.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
c.setWriter(w)
|
||||||
|
defer c.unsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are always added between operands and a newline is
|
||||||
|
// appended. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprintln(Output, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is just like Print, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprint(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintln(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintln(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprint().
|
||||||
|
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||||
|
return func(w io.Writer, a ...interface{}) {
|
||||||
|
c.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Print().
|
||||||
|
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) {
|
||||||
|
c.Print(a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprintf().
|
||||||
|
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||||
|
return func(w io.Writer, format string, a ...interface{}) {
|
||||||
|
c.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Printf().
|
||||||
|
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||||
|
return func(format string, a ...interface{}) {
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprintln().
|
||||||
|
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||||
|
return func(w io.Writer, a ...interface{}) {
|
||||||
|
c.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Println().
|
||||||
|
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) {
|
||||||
|
c.Println(a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output, example:
|
||||||
|
//
|
||||||
|
// put := New(FgYellow).SprintFunc()
|
||||||
|
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||||
|
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintfFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output.
|
||||||
|
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||||
|
return func(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output.
|
||||||
|
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintln(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||||
|
// an example output might be: "1;36" -> bold cyan
|
||||||
|
func (c *Color) sequence() string {
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(format, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||||
|
// be printed.
|
||||||
|
func (c *Color) wrap(s string) string {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.format() + s + c.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) format() string {
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unformat() string {
|
||||||
|
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableColor disables the color output. Useful to not change any existing
|
||||||
|
// code and still being able to output. Can be used for flags like
|
||||||
|
// "--no-color". To enable back use EnableColor() method.
|
||||||
|
func (c *Color) DisableColor() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableColor enables the color output. Use it in conjunction with
|
||||||
|
// DisableColor(). Otherwise this method has no side effects.
|
||||||
|
func (c *Color) EnableColor() {
|
||||||
|
c.noColor = boolPtr(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) isNoColorSet() bool {
|
||||||
|
// check first if we have user setted action
|
||||||
|
if c.noColor != nil {
|
||||||
|
return *c.noColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not return the global option, which is disabled by default
|
||||||
|
return NoColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns a boolean value indicating whether two colors are equal.
|
||||||
|
func (c *Color) Equals(c2 *Color) bool {
|
||||||
|
if len(c.params) != len(c2.params) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range c.params {
|
||||||
|
if !c2.attrExists(attr) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) attrExists(a Attribute) bool {
|
||||||
|
for _, attr := range c.params {
|
||||||
|
if attr == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolPtr(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCachedColor(p Attribute) *Color {
|
||||||
|
colorsCacheMu.Lock()
|
||||||
|
defer colorsCacheMu.Unlock()
|
||||||
|
|
||||||
|
c, ok := colorsCache[p]
|
||||||
|
if !ok {
|
||||||
|
c = New(p)
|
||||||
|
colorsCache[p] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||||
|
c := getCachedColor(p)
|
||||||
|
|
||||||
|
if !strings.HasSuffix(format, "\n") {
|
||||||
|
format += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a) == 0 {
|
||||||
|
c.Print(format)
|
||||||
|
} else {
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||||
|
c := getCachedColor(p)
|
||||||
|
|
||||||
|
if len(a) == 0 {
|
||||||
|
return c.SprintFunc()(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Black is a convenient helper function to print with black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// Red is a convenient helper function to print with red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// Green is a convenient helper function to print with green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// Yellow is a convenient helper function to print with yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// Blue is a convenient helper function to print with blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// Magenta is a convenient helper function to print with magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||||
|
|
||||||
|
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// White is a convenient helper function to print with white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// BlackString is a convenient helper function to return a string with black
|
||||||
|
// foreground.
|
||||||
|
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// RedString is a convenient helper function to return a string with red
|
||||||
|
// foreground.
|
||||||
|
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// GreenString is a convenient helper function to return a string with green
|
||||||
|
// foreground.
|
||||||
|
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// YellowString is a convenient helper function to return a string with yellow
|
||||||
|
// foreground.
|
||||||
|
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// BlueString is a convenient helper function to return a string with blue
|
||||||
|
// foreground.
|
||||||
|
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// MagentaString is a convenient helper function to return a string with magenta
|
||||||
|
// foreground.
|
||||||
|
func MagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyanString is a convenient helper function to return a string with cyan
|
||||||
|
// foreground.
|
||||||
|
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// WhiteString is a convenient helper function to return a string with white
|
||||||
|
// foreground.
|
||||||
|
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||||
|
|
||||||
|
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||||
|
|
||||||
|
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||||
|
|
||||||
|
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||||
|
|
||||||
|
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||||
|
// foreground.
|
||||||
|
func HiBlackString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiBlack, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||||
|
// foreground.
|
||||||
|
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||||
|
// foreground.
|
||||||
|
func HiGreenString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiGreen, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||||
|
// foreground.
|
||||||
|
func HiYellowString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiYellow, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||||
|
// foreground.
|
||||||
|
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||||
|
// foreground.
|
||||||
|
func HiMagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||||
|
// foreground.
|
||||||
|
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||||
|
// foreground.
|
||||||
|
func HiWhiteString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiWhite, a...)
|
||||||
|
}
|
||||||
133
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
133
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
Package color is an ANSI color package to output colorized or SGR defined
|
||||||
|
output to the standard output. The API can be used in several way, pick one
|
||||||
|
that suits you.
|
||||||
|
|
||||||
|
Use simple and default helper functions with predefined foreground colors:
|
||||||
|
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// a newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// More default foreground colors..
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Yellow("Yellow color too!")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
// Hi-intensity colors
|
||||||
|
color.HiGreen("Bright green color.")
|
||||||
|
color.HiBlack("Bright black means gray..")
|
||||||
|
color.HiWhite("Shiny white color!")
|
||||||
|
|
||||||
|
However there are times where custom color mixes are required. Below are some
|
||||||
|
examples to create custom color objects and use the print functions of each
|
||||||
|
separate color object.
|
||||||
|
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with White background.")
|
||||||
|
|
||||||
|
// Use your own io.Writer output
|
||||||
|
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||||
|
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
blue.Fprint(myWriter, "This will print text in blue.")
|
||||||
|
|
||||||
|
You can create PrintXxx functions to simplify even more:
|
||||||
|
|
||||||
|
// Create a custom print function for convenient
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("warning")
|
||||||
|
red("error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("don't forget this...")
|
||||||
|
|
||||||
|
You can also FprintXxx functions to pass your own io.Writer:
|
||||||
|
|
||||||
|
blue := color.New(FgBlue).FprintfFunc()
|
||||||
|
blue(myWriter, "important notice: %s", stars)
|
||||||
|
|
||||||
|
// Mix up with multiple attributes
|
||||||
|
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||||
|
success(myWriter, don't forget this...")
|
||||||
|
|
||||||
|
|
||||||
|
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
|
||||||
|
yellow := New(FgYellow).SprintFunc()
|
||||||
|
red := New(FgRed).SprintFunc()
|
||||||
|
|
||||||
|
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Windows support is enabled by default. All Print functions work as intended.
|
||||||
|
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||||
|
set the output to color.Output:
|
||||||
|
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Using with existing code is possible. Just use the Set() method to set the
|
||||||
|
standard output to the given parameters. That way a rewrite of an existing
|
||||||
|
code is not required.
|
||||||
|
|
||||||
|
// Use handy standard colors.
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will be now in Yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will be now bold magenta.")
|
||||||
|
|
||||||
|
There might be a case where you want to disable color output (for example to
|
||||||
|
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||||
|
disable colors both globally and for single color definition. For example
|
||||||
|
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||||
|
the color output with:
|
||||||
|
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
*/
|
||||||
|
package color
|
||||||
9
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
|
||||||
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
48
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
Normal file
48
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# go-colorable
|
||||||
|
|
||||||
|
[](http://godoc.org/github.com/mattn/go-colorable)
|
||||||
|
[](https://travis-ci.org/mattn/go-colorable)
|
||||||
|
[](https://coveralls.io/github/mattn/go-colorable?branch=master)
|
||||||
|
[](https://goreportcard.com/report/mattn/go-colorable)
|
||||||
|
|
||||||
|
Colorable writer for windows.
|
||||||
|
|
||||||
|
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
||||||
|
This package is possible to handle escape sequence for ansi color on windows.
|
||||||
|
|
||||||
|
## Too Bad!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## So Good!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||||
|
logrus.SetOutput(colorable.NewColorableStdout())
|
||||||
|
|
||||||
|
logrus.Info("succeeded")
|
||||||
|
logrus.Warn("not correct")
|
||||||
|
logrus.Error("something error")
|
||||||
|
logrus.Fatal("panic")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can compile above code on non-windows OSs.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get github.com/mattn/go-colorable
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
# Author
|
||||||
|
|
||||||
|
Yasuhiro Matsumoto (a.k.a mattn)
|
||||||
29
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
Normal file
29
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence.
|
||||||
|
func NewColorable(file *os.File) io.Writer {
|
||||||
|
if file == nil {
|
||||||
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
|
func NewColorableStdout() io.Writer {
|
||||||
|
return os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
|
func NewColorableStderr() io.Writer {
|
||||||
|
return os.Stderr
|
||||||
|
}
|
||||||
30
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
Normal file
30
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// +build !windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence.
|
||||||
|
func NewColorable(file *os.File) io.Writer {
|
||||||
|
if file == nil {
|
||||||
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
|
func NewColorableStdout() io.Writer {
|
||||||
|
return os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
|
func NewColorableStderr() io.Writer {
|
||||||
|
return os.Stderr
|
||||||
|
}
|
||||||
884
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
Normal file
884
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
Normal file
@ -0,0 +1,884 @@
|
|||||||
|
// +build windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
foregroundBlue = 0x1
|
||||||
|
foregroundGreen = 0x2
|
||||||
|
foregroundRed = 0x4
|
||||||
|
foregroundIntensity = 0x8
|
||||||
|
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
|
||||||
|
backgroundBlue = 0x10
|
||||||
|
backgroundGreen = 0x20
|
||||||
|
backgroundRed = 0x40
|
||||||
|
backgroundIntensity = 0x80
|
||||||
|
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||||
|
)
|
||||||
|
|
||||||
|
type wchar uint16
|
||||||
|
type short int16
|
||||||
|
type dword uint32
|
||||||
|
type word uint16
|
||||||
|
|
||||||
|
type coord struct {
|
||||||
|
x short
|
||||||
|
y short
|
||||||
|
}
|
||||||
|
|
||||||
|
type smallRect struct {
|
||||||
|
left short
|
||||||
|
top short
|
||||||
|
right short
|
||||||
|
bottom short
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleScreenBufferInfo struct {
|
||||||
|
size coord
|
||||||
|
cursorPosition coord
|
||||||
|
attributes word
|
||||||
|
window smallRect
|
||||||
|
maximumWindowSize coord
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleCursorInfo struct {
|
||||||
|
size dword
|
||||||
|
visible int32
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||||
|
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||||
|
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||||
|
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||||
|
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
||||||
|
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||||
|
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||||
|
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Writer provide colorable Writer to the console
|
||||||
|
type Writer struct {
|
||||||
|
out io.Writer
|
||||||
|
handle syscall.Handle
|
||||||
|
oldattr word
|
||||||
|
oldpos coord
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||||
|
func NewColorable(file *os.File) io.Writer {
|
||||||
|
if file == nil {
|
||||||
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if isatty.IsTerminal(file.Fd()) {
|
||||||
|
var csbi consoleScreenBufferInfo
|
||||||
|
handle := syscall.Handle(file.Fd())
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
|
func NewColorableStdout() io.Writer {
|
||||||
|
return NewColorable(os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
|
func NewColorableStderr() io.Writer {
|
||||||
|
return NewColorable(os.Stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var color256 = map[int]int{
|
||||||
|
0: 0x000000,
|
||||||
|
1: 0x800000,
|
||||||
|
2: 0x008000,
|
||||||
|
3: 0x808000,
|
||||||
|
4: 0x000080,
|
||||||
|
5: 0x800080,
|
||||||
|
6: 0x008080,
|
||||||
|
7: 0xc0c0c0,
|
||||||
|
8: 0x808080,
|
||||||
|
9: 0xff0000,
|
||||||
|
10: 0x00ff00,
|
||||||
|
11: 0xffff00,
|
||||||
|
12: 0x0000ff,
|
||||||
|
13: 0xff00ff,
|
||||||
|
14: 0x00ffff,
|
||||||
|
15: 0xffffff,
|
||||||
|
16: 0x000000,
|
||||||
|
17: 0x00005f,
|
||||||
|
18: 0x000087,
|
||||||
|
19: 0x0000af,
|
||||||
|
20: 0x0000d7,
|
||||||
|
21: 0x0000ff,
|
||||||
|
22: 0x005f00,
|
||||||
|
23: 0x005f5f,
|
||||||
|
24: 0x005f87,
|
||||||
|
25: 0x005faf,
|
||||||
|
26: 0x005fd7,
|
||||||
|
27: 0x005fff,
|
||||||
|
28: 0x008700,
|
||||||
|
29: 0x00875f,
|
||||||
|
30: 0x008787,
|
||||||
|
31: 0x0087af,
|
||||||
|
32: 0x0087d7,
|
||||||
|
33: 0x0087ff,
|
||||||
|
34: 0x00af00,
|
||||||
|
35: 0x00af5f,
|
||||||
|
36: 0x00af87,
|
||||||
|
37: 0x00afaf,
|
||||||
|
38: 0x00afd7,
|
||||||
|
39: 0x00afff,
|
||||||
|
40: 0x00d700,
|
||||||
|
41: 0x00d75f,
|
||||||
|
42: 0x00d787,
|
||||||
|
43: 0x00d7af,
|
||||||
|
44: 0x00d7d7,
|
||||||
|
45: 0x00d7ff,
|
||||||
|
46: 0x00ff00,
|
||||||
|
47: 0x00ff5f,
|
||||||
|
48: 0x00ff87,
|
||||||
|
49: 0x00ffaf,
|
||||||
|
50: 0x00ffd7,
|
||||||
|
51: 0x00ffff,
|
||||||
|
52: 0x5f0000,
|
||||||
|
53: 0x5f005f,
|
||||||
|
54: 0x5f0087,
|
||||||
|
55: 0x5f00af,
|
||||||
|
56: 0x5f00d7,
|
||||||
|
57: 0x5f00ff,
|
||||||
|
58: 0x5f5f00,
|
||||||
|
59: 0x5f5f5f,
|
||||||
|
60: 0x5f5f87,
|
||||||
|
61: 0x5f5faf,
|
||||||
|
62: 0x5f5fd7,
|
||||||
|
63: 0x5f5fff,
|
||||||
|
64: 0x5f8700,
|
||||||
|
65: 0x5f875f,
|
||||||
|
66: 0x5f8787,
|
||||||
|
67: 0x5f87af,
|
||||||
|
68: 0x5f87d7,
|
||||||
|
69: 0x5f87ff,
|
||||||
|
70: 0x5faf00,
|
||||||
|
71: 0x5faf5f,
|
||||||
|
72: 0x5faf87,
|
||||||
|
73: 0x5fafaf,
|
||||||
|
74: 0x5fafd7,
|
||||||
|
75: 0x5fafff,
|
||||||
|
76: 0x5fd700,
|
||||||
|
77: 0x5fd75f,
|
||||||
|
78: 0x5fd787,
|
||||||
|
79: 0x5fd7af,
|
||||||
|
80: 0x5fd7d7,
|
||||||
|
81: 0x5fd7ff,
|
||||||
|
82: 0x5fff00,
|
||||||
|
83: 0x5fff5f,
|
||||||
|
84: 0x5fff87,
|
||||||
|
85: 0x5fffaf,
|
||||||
|
86: 0x5fffd7,
|
||||||
|
87: 0x5fffff,
|
||||||
|
88: 0x870000,
|
||||||
|
89: 0x87005f,
|
||||||
|
90: 0x870087,
|
||||||
|
91: 0x8700af,
|
||||||
|
92: 0x8700d7,
|
||||||
|
93: 0x8700ff,
|
||||||
|
94: 0x875f00,
|
||||||
|
95: 0x875f5f,
|
||||||
|
96: 0x875f87,
|
||||||
|
97: 0x875faf,
|
||||||
|
98: 0x875fd7,
|
||||||
|
99: 0x875fff,
|
||||||
|
100: 0x878700,
|
||||||
|
101: 0x87875f,
|
||||||
|
102: 0x878787,
|
||||||
|
103: 0x8787af,
|
||||||
|
104: 0x8787d7,
|
||||||
|
105: 0x8787ff,
|
||||||
|
106: 0x87af00,
|
||||||
|
107: 0x87af5f,
|
||||||
|
108: 0x87af87,
|
||||||
|
109: 0x87afaf,
|
||||||
|
110: 0x87afd7,
|
||||||
|
111: 0x87afff,
|
||||||
|
112: 0x87d700,
|
||||||
|
113: 0x87d75f,
|
||||||
|
114: 0x87d787,
|
||||||
|
115: 0x87d7af,
|
||||||
|
116: 0x87d7d7,
|
||||||
|
117: 0x87d7ff,
|
||||||
|
118: 0x87ff00,
|
||||||
|
119: 0x87ff5f,
|
||||||
|
120: 0x87ff87,
|
||||||
|
121: 0x87ffaf,
|
||||||
|
122: 0x87ffd7,
|
||||||
|
123: 0x87ffff,
|
||||||
|
124: 0xaf0000,
|
||||||
|
125: 0xaf005f,
|
||||||
|
126: 0xaf0087,
|
||||||
|
127: 0xaf00af,
|
||||||
|
128: 0xaf00d7,
|
||||||
|
129: 0xaf00ff,
|
||||||
|
130: 0xaf5f00,
|
||||||
|
131: 0xaf5f5f,
|
||||||
|
132: 0xaf5f87,
|
||||||
|
133: 0xaf5faf,
|
||||||
|
134: 0xaf5fd7,
|
||||||
|
135: 0xaf5fff,
|
||||||
|
136: 0xaf8700,
|
||||||
|
137: 0xaf875f,
|
||||||
|
138: 0xaf8787,
|
||||||
|
139: 0xaf87af,
|
||||||
|
140: 0xaf87d7,
|
||||||
|
141: 0xaf87ff,
|
||||||
|
142: 0xafaf00,
|
||||||
|
143: 0xafaf5f,
|
||||||
|
144: 0xafaf87,
|
||||||
|
145: 0xafafaf,
|
||||||
|
146: 0xafafd7,
|
||||||
|
147: 0xafafff,
|
||||||
|
148: 0xafd700,
|
||||||
|
149: 0xafd75f,
|
||||||
|
150: 0xafd787,
|
||||||
|
151: 0xafd7af,
|
||||||
|
152: 0xafd7d7,
|
||||||
|
153: 0xafd7ff,
|
||||||
|
154: 0xafff00,
|
||||||
|
155: 0xafff5f,
|
||||||
|
156: 0xafff87,
|
||||||
|
157: 0xafffaf,
|
||||||
|
158: 0xafffd7,
|
||||||
|
159: 0xafffff,
|
||||||
|
160: 0xd70000,
|
||||||
|
161: 0xd7005f,
|
||||||
|
162: 0xd70087,
|
||||||
|
163: 0xd700af,
|
||||||
|
164: 0xd700d7,
|
||||||
|
165: 0xd700ff,
|
||||||
|
166: 0xd75f00,
|
||||||
|
167: 0xd75f5f,
|
||||||
|
168: 0xd75f87,
|
||||||
|
169: 0xd75faf,
|
||||||
|
170: 0xd75fd7,
|
||||||
|
171: 0xd75fff,
|
||||||
|
172: 0xd78700,
|
||||||
|
173: 0xd7875f,
|
||||||
|
174: 0xd78787,
|
||||||
|
175: 0xd787af,
|
||||||
|
176: 0xd787d7,
|
||||||
|
177: 0xd787ff,
|
||||||
|
178: 0xd7af00,
|
||||||
|
179: 0xd7af5f,
|
||||||
|
180: 0xd7af87,
|
||||||
|
181: 0xd7afaf,
|
||||||
|
182: 0xd7afd7,
|
||||||
|
183: 0xd7afff,
|
||||||
|
184: 0xd7d700,
|
||||||
|
185: 0xd7d75f,
|
||||||
|
186: 0xd7d787,
|
||||||
|
187: 0xd7d7af,
|
||||||
|
188: 0xd7d7d7,
|
||||||
|
189: 0xd7d7ff,
|
||||||
|
190: 0xd7ff00,
|
||||||
|
191: 0xd7ff5f,
|
||||||
|
192: 0xd7ff87,
|
||||||
|
193: 0xd7ffaf,
|
||||||
|
194: 0xd7ffd7,
|
||||||
|
195: 0xd7ffff,
|
||||||
|
196: 0xff0000,
|
||||||
|
197: 0xff005f,
|
||||||
|
198: 0xff0087,
|
||||||
|
199: 0xff00af,
|
||||||
|
200: 0xff00d7,
|
||||||
|
201: 0xff00ff,
|
||||||
|
202: 0xff5f00,
|
||||||
|
203: 0xff5f5f,
|
||||||
|
204: 0xff5f87,
|
||||||
|
205: 0xff5faf,
|
||||||
|
206: 0xff5fd7,
|
||||||
|
207: 0xff5fff,
|
||||||
|
208: 0xff8700,
|
||||||
|
209: 0xff875f,
|
||||||
|
210: 0xff8787,
|
||||||
|
211: 0xff87af,
|
||||||
|
212: 0xff87d7,
|
||||||
|
213: 0xff87ff,
|
||||||
|
214: 0xffaf00,
|
||||||
|
215: 0xffaf5f,
|
||||||
|
216: 0xffaf87,
|
||||||
|
217: 0xffafaf,
|
||||||
|
218: 0xffafd7,
|
||||||
|
219: 0xffafff,
|
||||||
|
220: 0xffd700,
|
||||||
|
221: 0xffd75f,
|
||||||
|
222: 0xffd787,
|
||||||
|
223: 0xffd7af,
|
||||||
|
224: 0xffd7d7,
|
||||||
|
225: 0xffd7ff,
|
||||||
|
226: 0xffff00,
|
||||||
|
227: 0xffff5f,
|
||||||
|
228: 0xffff87,
|
||||||
|
229: 0xffffaf,
|
||||||
|
230: 0xffffd7,
|
||||||
|
231: 0xffffff,
|
||||||
|
232: 0x080808,
|
||||||
|
233: 0x121212,
|
||||||
|
234: 0x1c1c1c,
|
||||||
|
235: 0x262626,
|
||||||
|
236: 0x303030,
|
||||||
|
237: 0x3a3a3a,
|
||||||
|
238: 0x444444,
|
||||||
|
239: 0x4e4e4e,
|
||||||
|
240: 0x585858,
|
||||||
|
241: 0x626262,
|
||||||
|
242: 0x6c6c6c,
|
||||||
|
243: 0x767676,
|
||||||
|
244: 0x808080,
|
||||||
|
245: 0x8a8a8a,
|
||||||
|
246: 0x949494,
|
||||||
|
247: 0x9e9e9e,
|
||||||
|
248: 0xa8a8a8,
|
||||||
|
249: 0xb2b2b2,
|
||||||
|
250: 0xbcbcbc,
|
||||||
|
251: 0xc6c6c6,
|
||||||
|
252: 0xd0d0d0,
|
||||||
|
253: 0xdadada,
|
||||||
|
254: 0xe4e4e4,
|
||||||
|
255: 0xeeeeee,
|
||||||
|
}
|
||||||
|
|
||||||
|
// `\033]0;TITLESTR\007`
|
||||||
|
func doTitleSequence(er *bytes.Reader) error {
|
||||||
|
var c byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
c, err = er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != '0' && c != '2' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c, err = er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != ';' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
title := make([]byte, 0, 80)
|
||||||
|
for {
|
||||||
|
c, err = er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c == 0x07 || c == '\n' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
title = append(title, c)
|
||||||
|
}
|
||||||
|
if len(title) > 0 {
|
||||||
|
title8, err := syscall.UTF16PtrFromString(string(title))
|
||||||
|
if err == nil {
|
||||||
|
procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write write data on console
|
||||||
|
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
|
var csbi consoleScreenBufferInfo
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
|
||||||
|
er := bytes.NewReader(data)
|
||||||
|
var bw [1]byte
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
c1, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c1 != 0x1b {
|
||||||
|
bw[0] = c1
|
||||||
|
w.out.Write(bw[:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c2, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
if c2 == ']' {
|
||||||
|
if err := doTitleSequence(er); err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c2 != 0x5b {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
var m byte
|
||||||
|
for {
|
||||||
|
c, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||||
|
m = c
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf.Write([]byte(string(c)))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m {
|
||||||
|
case 'A':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.y -= short(n)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'B':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.y += short(n)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'C':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.x += short(n)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'D':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.x -= short(n)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'E':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.x = 0
|
||||||
|
csbi.cursorPosition.y += short(n)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'F':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.x = 0
|
||||||
|
csbi.cursorPosition.y -= short(n)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'G':
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
csbi.cursorPosition.x = short(n - 1)
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'H', 'f':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
token := strings.Split(buf.String(), ";")
|
||||||
|
switch len(token) {
|
||||||
|
case 1:
|
||||||
|
n1, err := strconv.Atoi(token[0])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
csbi.cursorPosition.y = short(n1 - 1)
|
||||||
|
case 2:
|
||||||
|
n1, err := strconv.Atoi(token[0])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n2, err := strconv.Atoi(token[1])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
csbi.cursorPosition.x = short(n2 - 1)
|
||||||
|
csbi.cursorPosition.y = short(n1 - 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
csbi.cursorPosition.y = 0
|
||||||
|
}
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
|
case 'J':
|
||||||
|
n := 0
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var count, written dword
|
||||||
|
var cursor coord
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
switch n {
|
||||||
|
case 0:
|
||||||
|
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||||
|
case 1:
|
||||||
|
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.window.top-csbi.cursorPosition.y)*csbi.size.x)
|
||||||
|
case 2:
|
||||||
|
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||||
|
}
|
||||||
|
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
|
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
|
case 'K':
|
||||||
|
n := 0
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
var cursor coord
|
||||||
|
var count, written dword
|
||||||
|
switch n {
|
||||||
|
case 0:
|
||||||
|
cursor = coord{x: csbi.cursorPosition.x + 1, y: csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x - 1)
|
||||||
|
case 1:
|
||||||
|
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||||
|
case 2:
|
||||||
|
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x)
|
||||||
|
}
|
||||||
|
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
|
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
|
case 'm':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
attr := csbi.attributes
|
||||||
|
cs := buf.String()
|
||||||
|
if cs == "" {
|
||||||
|
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
token := strings.Split(cs, ";")
|
||||||
|
for i := 0; i < len(token); i++ {
|
||||||
|
ns := token[i]
|
||||||
|
if n, err = strconv.Atoi(ns); err == nil {
|
||||||
|
switch {
|
||||||
|
case n == 0 || n == 100:
|
||||||
|
attr = w.oldattr
|
||||||
|
case 1 <= n && n <= 5:
|
||||||
|
attr |= foregroundIntensity
|
||||||
|
case n == 7:
|
||||||
|
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||||
|
case n == 22 || n == 25:
|
||||||
|
attr |= foregroundIntensity
|
||||||
|
case n == 27:
|
||||||
|
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||||
|
case 30 <= n && n <= 37:
|
||||||
|
attr &= backgroundMask
|
||||||
|
if (n-30)&1 != 0 {
|
||||||
|
attr |= foregroundRed
|
||||||
|
}
|
||||||
|
if (n-30)&2 != 0 {
|
||||||
|
attr |= foregroundGreen
|
||||||
|
}
|
||||||
|
if (n-30)&4 != 0 {
|
||||||
|
attr |= foregroundBlue
|
||||||
|
}
|
||||||
|
case n == 38: // set foreground color.
|
||||||
|
if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
|
||||||
|
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||||
|
if n256foreAttr == nil {
|
||||||
|
n256setup()
|
||||||
|
}
|
||||||
|
attr &= backgroundMask
|
||||||
|
attr |= n256foreAttr[n256]
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attr = attr & (w.oldattr & backgroundMask)
|
||||||
|
}
|
||||||
|
case n == 39: // reset foreground color.
|
||||||
|
attr &= backgroundMask
|
||||||
|
attr |= w.oldattr & foregroundMask
|
||||||
|
case 40 <= n && n <= 47:
|
||||||
|
attr &= foregroundMask
|
||||||
|
if (n-40)&1 != 0 {
|
||||||
|
attr |= backgroundRed
|
||||||
|
}
|
||||||
|
if (n-40)&2 != 0 {
|
||||||
|
attr |= backgroundGreen
|
||||||
|
}
|
||||||
|
if (n-40)&4 != 0 {
|
||||||
|
attr |= backgroundBlue
|
||||||
|
}
|
||||||
|
case n == 48: // set background color.
|
||||||
|
if i < len(token)-2 && token[i+1] == "5" {
|
||||||
|
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||||
|
if n256backAttr == nil {
|
||||||
|
n256setup()
|
||||||
|
}
|
||||||
|
attr &= foregroundMask
|
||||||
|
attr |= n256backAttr[n256]
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attr = attr & (w.oldattr & foregroundMask)
|
||||||
|
}
|
||||||
|
case n == 49: // reset foreground color.
|
||||||
|
attr &= foregroundMask
|
||||||
|
attr |= w.oldattr & backgroundMask
|
||||||
|
case 90 <= n && n <= 97:
|
||||||
|
attr = (attr & backgroundMask)
|
||||||
|
attr |= foregroundIntensity
|
||||||
|
if (n-90)&1 != 0 {
|
||||||
|
attr |= foregroundRed
|
||||||
|
}
|
||||||
|
if (n-90)&2 != 0 {
|
||||||
|
attr |= foregroundGreen
|
||||||
|
}
|
||||||
|
if (n-90)&4 != 0 {
|
||||||
|
attr |= foregroundBlue
|
||||||
|
}
|
||||||
|
case 100 <= n && n <= 107:
|
||||||
|
attr = (attr & foregroundMask)
|
||||||
|
attr |= backgroundIntensity
|
||||||
|
if (n-100)&1 != 0 {
|
||||||
|
attr |= backgroundRed
|
||||||
|
}
|
||||||
|
if (n-100)&2 != 0 {
|
||||||
|
attr |= backgroundGreen
|
||||||
|
}
|
||||||
|
if (n-100)&4 != 0 {
|
||||||
|
attr |= backgroundBlue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'h':
|
||||||
|
var ci consoleCursorInfo
|
||||||
|
cs := buf.String()
|
||||||
|
if cs == "5>" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 0
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
} else if cs == "?25" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 1
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
}
|
||||||
|
case 'l':
|
||||||
|
var ci consoleCursorInfo
|
||||||
|
cs := buf.String()
|
||||||
|
if cs == "5>" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 1
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
} else if cs == "?25" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 0
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
}
|
||||||
|
case 's':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
w.oldpos = csbi.cursorPosition
|
||||||
|
case 'u':
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleColor struct {
|
||||||
|
rgb int
|
||||||
|
red bool
|
||||||
|
green bool
|
||||||
|
blue bool
|
||||||
|
intensity bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c consoleColor) foregroundAttr() (attr word) {
|
||||||
|
if c.red {
|
||||||
|
attr |= foregroundRed
|
||||||
|
}
|
||||||
|
if c.green {
|
||||||
|
attr |= foregroundGreen
|
||||||
|
}
|
||||||
|
if c.blue {
|
||||||
|
attr |= foregroundBlue
|
||||||
|
}
|
||||||
|
if c.intensity {
|
||||||
|
attr |= foregroundIntensity
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c consoleColor) backgroundAttr() (attr word) {
|
||||||
|
if c.red {
|
||||||
|
attr |= backgroundRed
|
||||||
|
}
|
||||||
|
if c.green {
|
||||||
|
attr |= backgroundGreen
|
||||||
|
}
|
||||||
|
if c.blue {
|
||||||
|
attr |= backgroundBlue
|
||||||
|
}
|
||||||
|
if c.intensity {
|
||||||
|
attr |= backgroundIntensity
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var color16 = []consoleColor{
|
||||||
|
{0x000000, false, false, false, false},
|
||||||
|
{0x000080, false, false, true, false},
|
||||||
|
{0x008000, false, true, false, false},
|
||||||
|
{0x008080, false, true, true, false},
|
||||||
|
{0x800000, true, false, false, false},
|
||||||
|
{0x800080, true, false, true, false},
|
||||||
|
{0x808000, true, true, false, false},
|
||||||
|
{0xc0c0c0, true, true, true, false},
|
||||||
|
{0x808080, false, false, false, true},
|
||||||
|
{0x0000ff, false, false, true, true},
|
||||||
|
{0x00ff00, false, true, false, true},
|
||||||
|
{0x00ffff, false, true, true, true},
|
||||||
|
{0xff0000, true, false, false, true},
|
||||||
|
{0xff00ff, true, false, true, true},
|
||||||
|
{0xffff00, true, true, false, true},
|
||||||
|
{0xffffff, true, true, true, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
type hsv struct {
|
||||||
|
h, s, v float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a hsv) dist(b hsv) float32 {
|
||||||
|
dh := a.h - b.h
|
||||||
|
switch {
|
||||||
|
case dh > 0.5:
|
||||||
|
dh = 1 - dh
|
||||||
|
case dh < -0.5:
|
||||||
|
dh = -1 - dh
|
||||||
|
}
|
||||||
|
ds := a.s - b.s
|
||||||
|
dv := a.v - b.v
|
||||||
|
return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toHSV(rgb int) hsv {
|
||||||
|
r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
|
||||||
|
float32((rgb&0x00FF00)>>8)/256.0,
|
||||||
|
float32(rgb&0x0000FF)/256.0
|
||||||
|
min, max := minmax3f(r, g, b)
|
||||||
|
h := max - min
|
||||||
|
if h > 0 {
|
||||||
|
if max == r {
|
||||||
|
h = (g - b) / h
|
||||||
|
if h < 0 {
|
||||||
|
h += 6
|
||||||
|
}
|
||||||
|
} else if max == g {
|
||||||
|
h = 2 + (b-r)/h
|
||||||
|
} else {
|
||||||
|
h = 4 + (r-g)/h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h /= 6.0
|
||||||
|
s := max - min
|
||||||
|
if max != 0 {
|
||||||
|
s /= max
|
||||||
|
}
|
||||||
|
v := max
|
||||||
|
return hsv{h: h, s: s, v: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
type hsvTable []hsv
|
||||||
|
|
||||||
|
func toHSVTable(rgbTable []consoleColor) hsvTable {
|
||||||
|
t := make(hsvTable, len(rgbTable))
|
||||||
|
for i, c := range rgbTable {
|
||||||
|
t[i] = toHSV(c.rgb)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t hsvTable) find(rgb int) consoleColor {
|
||||||
|
hsv := toHSV(rgb)
|
||||||
|
n := 7
|
||||||
|
l := float32(5.0)
|
||||||
|
for i, p := range t {
|
||||||
|
d := hsv.dist(p)
|
||||||
|
if d < l {
|
||||||
|
l, n = d, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color16[n]
|
||||||
|
}
|
||||||
|
|
||||||
|
func minmax3f(a, b, c float32) (min, max float32) {
|
||||||
|
if a < b {
|
||||||
|
if b < c {
|
||||||
|
return a, c
|
||||||
|
} else if a < c {
|
||||||
|
return a, b
|
||||||
|
} else {
|
||||||
|
return c, b
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if a < c {
|
||||||
|
return b, c
|
||||||
|
} else if b < c {
|
||||||
|
return b, a
|
||||||
|
} else {
|
||||||
|
return c, a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var n256foreAttr []word
|
||||||
|
var n256backAttr []word
|
||||||
|
|
||||||
|
func n256setup() {
|
||||||
|
n256foreAttr = make([]word, 256)
|
||||||
|
n256backAttr = make([]word, 256)
|
||||||
|
t := toHSVTable(color16)
|
||||||
|
for i, rgb := range color256 {
|
||||||
|
c := t.find(rgb)
|
||||||
|
n256foreAttr[i] = c.foregroundAttr()
|
||||||
|
n256backAttr[i] = c.backgroundAttr()
|
||||||
|
}
|
||||||
|
}
|
||||||
55
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
Normal file
55
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NonColorable hold writer but remove escape sequence.
|
||||||
|
type NonColorable struct {
|
||||||
|
out io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
|
||||||
|
func NewNonColorable(w io.Writer) io.Writer {
|
||||||
|
return &NonColorable{out: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write write data on console
|
||||||
|
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||||
|
er := bytes.NewReader(data)
|
||||||
|
var bw [1]byte
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
c1, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c1 != 0x1b {
|
||||||
|
bw[0] = c1
|
||||||
|
w.out.Write(bw[:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c2, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c2 != 0x5b {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for {
|
||||||
|
c, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf.Write([]byte(string(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
9
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5
|
||||||
9
vendor/github.com/mattn/go-isatty/LICENSE
generated
vendored
Normal file
9
vendor/github.com/mattn/go-isatty/LICENSE
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
|
||||||
|
|
||||||
|
MIT License (Expat)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
50
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
Normal file
50
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# go-isatty
|
||||||
|
|
||||||
|
[](http://godoc.org/github.com/mattn/go-isatty)
|
||||||
|
[](https://travis-ci.org/mattn/go-isatty)
|
||||||
|
[](https://coveralls.io/github/mattn/go-isatty?branch=master)
|
||||||
|
[](https://goreportcard.com/report/mattn/go-isatty)
|
||||||
|
|
||||||
|
isatty for golang
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
|
fmt.Println("Is Terminal")
|
||||||
|
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||||
|
fmt.Println("Is Cygwin/MSYS2 Terminal")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Is Not Terminal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get github.com/mattn/go-isatty
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Yasuhiro Matsumoto (a.k.a mattn)
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
* k-takata: base idea for IsCygwinTerminal
|
||||||
|
|
||||||
|
https://github.com/k-takata/go-iscygpty
|
||||||
2
vendor/github.com/mattn/go-isatty/doc.go
generated
vendored
Normal file
2
vendor/github.com/mattn/go-isatty/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package isatty implements interface to isatty
|
||||||
|
package isatty
|
||||||
15
vendor/github.com/mattn/go-isatty/isatty_appengine.go
generated
vendored
Normal file
15
vendor/github.com/mattn/go-isatty/isatty_appengine.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
// IsTerminal returns true if the file descriptor is terminal which
|
||||||
|
// is always false on on appengine classic which is a sandboxed PaaS.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
18
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
Normal file
18
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TIOCGETA
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios syscall.Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
||||||
18
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
Normal file
18
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// +build linux
|
||||||
|
// +build !appengine,!ppc64,!ppc64le
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios syscall.Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
||||||
19
vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
generated
vendored
Normal file
19
vendor/github.com/mattn/go-isatty/isatty_linux_ppc64x.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// +build linux
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
syscall "golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlReadTermios = syscall.TCGETS
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios syscall.Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
||||||
10
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
Normal file
10
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// +build !windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
16
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
Normal file
16
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// +build solaris
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termio unix.Termio
|
||||||
|
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
94
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
Normal file
94
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// +build windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileNameInfo uintptr = 2
|
||||||
|
fileTypePipe = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||||
|
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procGetFileType = kernel32.NewProc("GetFileType")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Check if GetFileInformationByHandleEx is available.
|
||||||
|
if procGetFileInformationByHandleEx.Find() != nil {
|
||||||
|
procGetFileInformationByHandleEx = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var st uint32
|
||||||
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||||
|
return r != 0 && e == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pipe name is used for cygwin/msys2 pty.
|
||||||
|
// Cygwin/MSYS2 PTY has a name like:
|
||||||
|
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
||||||
|
func isCygwinPipeName(name string) bool {
|
||||||
|
token := strings.Split(name, "-")
|
||||||
|
if len(token) < 5 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[0] != `\msys` && token[0] != `\cygwin` {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(token[2], "pty") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[3] != `from` && token[3] != `to` {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[4] != "master" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
if procGetFileInformationByHandleEx == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cygwin/msys's pty is a pipe.
|
||||||
|
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
|
||||||
|
if ft != fileTypePipe || e != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [2 + syscall.MAX_PATH]uint16
|
||||||
|
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
|
||||||
|
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
|
||||||
|
uintptr(len(buf)*2), 0, 0)
|
||||||
|
if r == 0 || e != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
l := *(*uint32)(unsafe.Pointer(&buf))
|
||||||
|
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
|
||||||
|
}
|
||||||
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
||||||
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||||
27
vendor/golang.org/x/net/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/net/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
22
vendor/golang.org/x/net/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/net/PATENTS
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
||||||
168
vendor/golang.org/x/net/internal/socks/client.go
generated
vendored
Normal file
168
vendor/golang.org/x/net/internal/socks/client.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2018 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 socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
noDeadline = time.Time{}
|
||||||
|
aLongTimeAgo = time.Unix(1, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
|
||||||
|
host, port, err := splitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
|
||||||
|
c.SetDeadline(deadline)
|
||||||
|
defer c.SetDeadline(noDeadline)
|
||||||
|
}
|
||||||
|
if ctx != context.Background() {
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer func() {
|
||||||
|
close(done)
|
||||||
|
if ctxErr == nil {
|
||||||
|
ctxErr = <-errCh
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
c.SetDeadline(aLongTimeAgo)
|
||||||
|
errCh <- ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
errCh <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
|
||||||
|
b = append(b, Version5)
|
||||||
|
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
|
||||||
|
b = append(b, 1, byte(AuthMethodNotRequired))
|
||||||
|
} else {
|
||||||
|
ams := d.AuthMethods
|
||||||
|
if len(ams) > 255 {
|
||||||
|
return nil, errors.New("too many authentication methods")
|
||||||
|
}
|
||||||
|
b = append(b, byte(len(ams)))
|
||||||
|
for _, am := range ams {
|
||||||
|
b = append(b, byte(am))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b[0] != Version5 {
|
||||||
|
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||||
|
}
|
||||||
|
am := AuthMethod(b[1])
|
||||||
|
if am == AuthMethodNoAcceptableMethods {
|
||||||
|
return nil, errors.New("no acceptable authentication methods")
|
||||||
|
}
|
||||||
|
if d.Authenticate != nil {
|
||||||
|
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b = b[:0]
|
||||||
|
b = append(b, Version5, byte(d.cmd), 0)
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
b = append(b, AddrTypeIPv4)
|
||||||
|
b = append(b, ip4...)
|
||||||
|
} else if ip6 := ip.To16(); ip6 != nil {
|
||||||
|
b = append(b, AddrTypeIPv6)
|
||||||
|
b = append(b, ip6...)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unknown address type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(host) > 255 {
|
||||||
|
return nil, errors.New("FQDN too long")
|
||||||
|
}
|
||||||
|
b = append(b, AddrTypeFQDN)
|
||||||
|
b = append(b, byte(len(host)))
|
||||||
|
b = append(b, host...)
|
||||||
|
}
|
||||||
|
b = append(b, byte(port>>8), byte(port))
|
||||||
|
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b[0] != Version5 {
|
||||||
|
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||||
|
}
|
||||||
|
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
|
||||||
|
return nil, errors.New("unknown error " + cmdErr.String())
|
||||||
|
}
|
||||||
|
if b[2] != 0 {
|
||||||
|
return nil, errors.New("non-zero reserved field")
|
||||||
|
}
|
||||||
|
l := 2
|
||||||
|
var a Addr
|
||||||
|
switch b[3] {
|
||||||
|
case AddrTypeIPv4:
|
||||||
|
l += net.IPv4len
|
||||||
|
a.IP = make(net.IP, net.IPv4len)
|
||||||
|
case AddrTypeIPv6:
|
||||||
|
l += net.IPv6len
|
||||||
|
a.IP = make(net.IP, net.IPv6len)
|
||||||
|
case AddrTypeFQDN:
|
||||||
|
if _, err := io.ReadFull(c, b[:1]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l += int(b[0])
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
|
||||||
|
}
|
||||||
|
if cap(b) < l {
|
||||||
|
b = make([]byte, l)
|
||||||
|
} else {
|
||||||
|
b = b[:l]
|
||||||
|
}
|
||||||
|
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if a.IP != nil {
|
||||||
|
copy(a.IP, b)
|
||||||
|
} else {
|
||||||
|
a.Name = string(b[:len(b)-2])
|
||||||
|
}
|
||||||
|
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
|
||||||
|
return &a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitHostPort(address string) (string, int, error) {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
portnum, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
if 1 > portnum || portnum > 0xffff {
|
||||||
|
return "", 0, errors.New("port number out of range " + port)
|
||||||
|
}
|
||||||
|
return host, portnum, nil
|
||||||
|
}
|
||||||
316
vendor/golang.org/x/net/internal/socks/socks.go
generated
vendored
Normal file
316
vendor/golang.org/x/net/internal/socks/socks.go
generated
vendored
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
// Copyright 2018 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 socks provides a SOCKS version 5 client implementation.
|
||||||
|
//
|
||||||
|
// SOCKS protocol version 5 is defined in RFC 1928.
|
||||||
|
// Username/Password authentication for SOCKS version 5 is defined in
|
||||||
|
// RFC 1929.
|
||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Command represents a SOCKS command.
|
||||||
|
type Command int
|
||||||
|
|
||||||
|
func (cmd Command) String() string {
|
||||||
|
switch cmd {
|
||||||
|
case CmdConnect:
|
||||||
|
return "socks connect"
|
||||||
|
case cmdBind:
|
||||||
|
return "socks bind"
|
||||||
|
default:
|
||||||
|
return "socks " + strconv.Itoa(int(cmd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An AuthMethod represents a SOCKS authentication method.
|
||||||
|
type AuthMethod int
|
||||||
|
|
||||||
|
// A Reply represents a SOCKS command reply code.
|
||||||
|
type Reply int
|
||||||
|
|
||||||
|
func (code Reply) String() string {
|
||||||
|
switch code {
|
||||||
|
case StatusSucceeded:
|
||||||
|
return "succeeded"
|
||||||
|
case 0x01:
|
||||||
|
return "general SOCKS server failure"
|
||||||
|
case 0x02:
|
||||||
|
return "connection not allowed by ruleset"
|
||||||
|
case 0x03:
|
||||||
|
return "network unreachable"
|
||||||
|
case 0x04:
|
||||||
|
return "host unreachable"
|
||||||
|
case 0x05:
|
||||||
|
return "connection refused"
|
||||||
|
case 0x06:
|
||||||
|
return "TTL expired"
|
||||||
|
case 0x07:
|
||||||
|
return "command not supported"
|
||||||
|
case 0x08:
|
||||||
|
return "address type not supported"
|
||||||
|
default:
|
||||||
|
return "unknown code: " + strconv.Itoa(int(code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wire protocol constants.
|
||||||
|
const (
|
||||||
|
Version5 = 0x05
|
||||||
|
|
||||||
|
AddrTypeIPv4 = 0x01
|
||||||
|
AddrTypeFQDN = 0x03
|
||||||
|
AddrTypeIPv6 = 0x04
|
||||||
|
|
||||||
|
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
|
||||||
|
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
|
||||||
|
|
||||||
|
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||||
|
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||||
|
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||||
|
|
||||||
|
StatusSucceeded Reply = 0x00
|
||||||
|
)
|
||||||
|
|
||||||
|
// An Addr represents a SOCKS-specific address.
|
||||||
|
// Either Name or IP is used exclusively.
|
||||||
|
type Addr struct {
|
||||||
|
Name string // fully-qualified domain name
|
||||||
|
IP net.IP
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Addr) Network() string { return "socks" }
|
||||||
|
|
||||||
|
func (a *Addr) String() string {
|
||||||
|
if a == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
port := strconv.Itoa(a.Port)
|
||||||
|
if a.IP == nil {
|
||||||
|
return net.JoinHostPort(a.Name, port)
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(a.IP.String(), port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Conn represents a forward proxy connection.
|
||||||
|
type Conn struct {
|
||||||
|
net.Conn
|
||||||
|
|
||||||
|
boundAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundAddr returns the address assigned by the proxy server for
|
||||||
|
// connecting to the command target address from the proxy server.
|
||||||
|
func (c *Conn) BoundAddr() net.Addr {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.boundAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Dialer holds SOCKS-specific options.
|
||||||
|
type Dialer struct {
|
||||||
|
cmd Command // either CmdConnect or cmdBind
|
||||||
|
proxyNetwork string // network between a proxy server and a client
|
||||||
|
proxyAddress string // proxy server address
|
||||||
|
|
||||||
|
// ProxyDial specifies the optional dial function for
|
||||||
|
// establishing the transport connection.
|
||||||
|
ProxyDial func(context.Context, string, string) (net.Conn, error)
|
||||||
|
|
||||||
|
// AuthMethods specifies the list of request authention
|
||||||
|
// methods.
|
||||||
|
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||||
|
AuthMethods []AuthMethod
|
||||||
|
|
||||||
|
// Authenticate specifies the optional authentication
|
||||||
|
// function. It must be non-nil when AuthMethods is not empty.
|
||||||
|
// It must return an error when the authentication is failed.
|
||||||
|
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext connects to the provided address on the provided
|
||||||
|
// network.
|
||||||
|
//
|
||||||
|
// The returned error value may be a net.OpError. When the Op field of
|
||||||
|
// net.OpError contains "socks", the Source field contains a proxy
|
||||||
|
// server address and the Addr field contains a command target
|
||||||
|
// address.
|
||||||
|
//
|
||||||
|
// See func Dial of the net package of standard library for a
|
||||||
|
// description of the network and address parameters.
|
||||||
|
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
if err := d.validateTarget(network, address); err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
if ctx == nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var c net.Conn
|
||||||
|
if d.ProxyDial != nil {
|
||||||
|
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
|
||||||
|
} else {
|
||||||
|
var dd net.Dialer
|
||||||
|
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
a, err := d.connect(ctx, c, address)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
return &Conn{Conn: c, boundAddr: a}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialWithConn initiates a connection from SOCKS server to the target
|
||||||
|
// network and address using the connection c that is already
|
||||||
|
// connected to the SOCKS server.
|
||||||
|
//
|
||||||
|
// It returns the connection's local address assigned by the SOCKS
|
||||||
|
// server.
|
||||||
|
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
|
||||||
|
if err := d.validateTarget(network, address); err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
if ctx == nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||||
|
}
|
||||||
|
a, err := d.connect(ctx, c, address)
|
||||||
|
if err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to the provided address on the provided network.
|
||||||
|
//
|
||||||
|
// Unlike DialContext, it returns a raw transport connection instead
|
||||||
|
// of a forward proxy connection.
|
||||||
|
//
|
||||||
|
// Deprecated: Use DialContext or DialWithConn instead.
|
||||||
|
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||||
|
if err := d.validateTarget(network, address); err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var c net.Conn
|
||||||
|
if d.ProxyDial != nil {
|
||||||
|
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
|
||||||
|
} else {
|
||||||
|
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) validateTarget(network, address string) error {
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp6", "tcp4":
|
||||||
|
default:
|
||||||
|
return errors.New("network not implemented")
|
||||||
|
}
|
||||||
|
switch d.cmd {
|
||||||
|
case CmdConnect, cmdBind:
|
||||||
|
default:
|
||||||
|
return errors.New("command not implemented")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
|
||||||
|
for i, s := range []string{d.proxyAddress, address} {
|
||||||
|
host, port, err := splitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
a := &Addr{Port: port}
|
||||||
|
a.IP = net.ParseIP(host)
|
||||||
|
if a.IP == nil {
|
||||||
|
a.Name = host
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
proxy = a
|
||||||
|
} else {
|
||||||
|
dst = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDialer returns a new Dialer that dials through the provided
|
||||||
|
// proxy server's network and address.
|
||||||
|
func NewDialer(network, address string) *Dialer {
|
||||||
|
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
authUsernamePasswordVersion = 0x01
|
||||||
|
authStatusSucceeded = 0x00
|
||||||
|
)
|
||||||
|
|
||||||
|
// UsernamePassword are the credentials for the username/password
|
||||||
|
// authentication method.
|
||||||
|
type UsernamePassword struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate authenticates a pair of username and password with the
|
||||||
|
// proxy server.
|
||||||
|
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
|
||||||
|
switch auth {
|
||||||
|
case AuthMethodNotRequired:
|
||||||
|
return nil
|
||||||
|
case AuthMethodUsernamePassword:
|
||||||
|
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
|
||||||
|
return errors.New("invalid username/password")
|
||||||
|
}
|
||||||
|
b := []byte{authUsernamePasswordVersion}
|
||||||
|
b = append(b, byte(len(up.Username)))
|
||||||
|
b = append(b, up.Username...)
|
||||||
|
b = append(b, byte(len(up.Password)))
|
||||||
|
b = append(b, up.Password...)
|
||||||
|
// TODO(mikio): handle IO deadlines and cancelation if
|
||||||
|
// necessary
|
||||||
|
if _, err := rw.Write(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(rw, b[:2]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if b[0] != authUsernamePasswordVersion {
|
||||||
|
return errors.New("invalid username/password version")
|
||||||
|
}
|
||||||
|
if b[1] != authStatusSucceeded {
|
||||||
|
return errors.New("username/password authentication failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
|
||||||
|
}
|
||||||
18
vendor/golang.org/x/net/proxy/direct.go
generated
vendored
Normal file
18
vendor/golang.org/x/net/proxy/direct.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2011 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 proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type direct struct{}
|
||||||
|
|
||||||
|
// Direct is a direct proxy: one that makes network connections directly.
|
||||||
|
var Direct = direct{}
|
||||||
|
|
||||||
|
func (direct) Dial(network, addr string) (net.Conn, error) {
|
||||||
|
return net.Dial(network, addr)
|
||||||
|
}
|
||||||
140
vendor/golang.org/x/net/proxy/per_host.go
generated
vendored
Normal file
140
vendor/golang.org/x/net/proxy/per_host.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Copyright 2011 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 proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A PerHost directs connections to a default Dialer unless the host name
|
||||||
|
// requested matches one of a number of exceptions.
|
||||||
|
type PerHost struct {
|
||||||
|
def, bypass Dialer
|
||||||
|
|
||||||
|
bypassNetworks []*net.IPNet
|
||||||
|
bypassIPs []net.IP
|
||||||
|
bypassZones []string
|
||||||
|
bypassHosts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||||
|
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||||
|
// the configured rules.
|
||||||
|
func NewPerHost(defaultDialer, bypass Dialer) *PerHost {
|
||||||
|
return &PerHost{
|
||||||
|
def: defaultDialer,
|
||||||
|
bypass: bypass,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to the address addr on the given network through either
|
||||||
|
// defaultDialer or bypass.
|
||||||
|
func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||||
|
host, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.dialerForRequest(host).Dial(network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PerHost) dialerForRequest(host string) Dialer {
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
for _, net := range p.bypassNetworks {
|
||||||
|
if net.Contains(ip) {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, bypassIP := range p.bypassIPs {
|
||||||
|
if bypassIP.Equal(ip) {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.def
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, zone := range p.bypassZones {
|
||||||
|
if strings.HasSuffix(host, zone) {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
if host == zone[1:] {
|
||||||
|
// For a zone ".example.com", we match "example.com"
|
||||||
|
// too.
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, bypassHost := range p.bypassHosts {
|
||||||
|
if bypassHost == host {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.def
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFromString parses a string that contains comma-separated values
|
||||||
|
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||||
|
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||||
|
// (localhost). A best effort is made to parse the string and errors are
|
||||||
|
// ignored.
|
||||||
|
func (p *PerHost) AddFromString(s string) {
|
||||||
|
hosts := strings.Split(s, ",")
|
||||||
|
for _, host := range hosts {
|
||||||
|
host = strings.TrimSpace(host)
|
||||||
|
if len(host) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(host, "/") {
|
||||||
|
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||||
|
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||||
|
p.AddNetwork(net)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
p.AddIP(ip)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(host, "*.") {
|
||||||
|
p.AddZone(host[1:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.AddHost(host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||||
|
// this will only take effect if a literal IP address is dialed. A connection
|
||||||
|
// to a named host will never match an IP.
|
||||||
|
func (p *PerHost) AddIP(ip net.IP) {
|
||||||
|
p.bypassIPs = append(p.bypassIPs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||||
|
// this will only take effect if a literal IP address is dialed. A connection
|
||||||
|
// to a named host will never match.
|
||||||
|
func (p *PerHost) AddNetwork(net *net.IPNet) {
|
||||||
|
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||||
|
// "example.com" matches "example.com" and all of its subdomains.
|
||||||
|
func (p *PerHost) AddZone(zone string) {
|
||||||
|
if strings.HasSuffix(zone, ".") {
|
||||||
|
zone = zone[:len(zone)-1]
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(zone, ".") {
|
||||||
|
zone = "." + zone
|
||||||
|
}
|
||||||
|
p.bypassZones = append(p.bypassZones, zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHost specifies a host name that will use the bypass proxy.
|
||||||
|
func (p *PerHost) AddHost(host string) {
|
||||||
|
if strings.HasSuffix(host, ".") {
|
||||||
|
host = host[:len(host)-1]
|
||||||
|
}
|
||||||
|
p.bypassHosts = append(p.bypassHosts, host)
|
||||||
|
}
|
||||||
134
vendor/golang.org/x/net/proxy/proxy.go
generated
vendored
Normal file
134
vendor/golang.org/x/net/proxy/proxy.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2011 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 proxy provides support for a variety of protocols to proxy network
|
||||||
|
// data.
|
||||||
|
package proxy // import "golang.org/x/net/proxy"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Dialer is a means to establish a connection.
|
||||||
|
type Dialer interface {
|
||||||
|
// Dial connects to the given address via the proxy.
|
||||||
|
Dial(network, addr string) (c net.Conn, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth contains authentication parameters that specific Dialers may require.
|
||||||
|
type Auth struct {
|
||||||
|
User, Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromEnvironment returns the dialer specified by the proxy related variables in
|
||||||
|
// the environment.
|
||||||
|
func FromEnvironment() Dialer {
|
||||||
|
allProxy := allProxyEnv.Get()
|
||||||
|
if len(allProxy) == 0 {
|
||||||
|
return Direct
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyURL, err := url.Parse(allProxy)
|
||||||
|
if err != nil {
|
||||||
|
return Direct
|
||||||
|
}
|
||||||
|
proxy, err := FromURL(proxyURL, Direct)
|
||||||
|
if err != nil {
|
||||||
|
return Direct
|
||||||
|
}
|
||||||
|
|
||||||
|
noProxy := noProxyEnv.Get()
|
||||||
|
if len(noProxy) == 0 {
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
perHost := NewPerHost(proxy, Direct)
|
||||||
|
perHost.AddFromString(noProxy)
|
||||||
|
return perHost
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||||
|
// from a URL with such a scheme.
|
||||||
|
var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
|
||||||
|
|
||||||
|
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||||
|
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||||
|
// by FromURL.
|
||||||
|
func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
|
||||||
|
if proxySchemes == nil {
|
||||||
|
proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
|
||||||
|
}
|
||||||
|
proxySchemes[scheme] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromURL returns a Dialer given a URL specification and an underlying
|
||||||
|
// Dialer for it to make network requests.
|
||||||
|
func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
|
||||||
|
var auth *Auth
|
||||||
|
if u.User != nil {
|
||||||
|
auth = new(Auth)
|
||||||
|
auth.User = u.User.Username()
|
||||||
|
if p, ok := u.User.Password(); ok {
|
||||||
|
auth.Password = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "socks5":
|
||||||
|
return SOCKS5("tcp", u.Host, auth, forward)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||||
|
// was registered by another package.
|
||||||
|
if proxySchemes != nil {
|
||||||
|
if f, ok := proxySchemes[u.Scheme]; ok {
|
||||||
|
return f(u, forward)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
allProxyEnv = &envOnce{
|
||||||
|
names: []string{"ALL_PROXY", "all_proxy"},
|
||||||
|
}
|
||||||
|
noProxyEnv = &envOnce{
|
||||||
|
names: []string{"NO_PROXY", "no_proxy"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// envOnce looks up an environment variable (optionally by multiple
|
||||||
|
// names) once. It mitigates expensive lookups on some platforms
|
||||||
|
// (e.g. Windows).
|
||||||
|
// (Borrowed from net/http/transport.go)
|
||||||
|
type envOnce struct {
|
||||||
|
names []string
|
||||||
|
once sync.Once
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envOnce) Get() string {
|
||||||
|
e.once.Do(e.init)
|
||||||
|
return e.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envOnce) init() {
|
||||||
|
for _, n := range e.names {
|
||||||
|
e.val = os.Getenv(n)
|
||||||
|
if e.val != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset is used by tests
|
||||||
|
func (e *envOnce) reset() {
|
||||||
|
e.once = sync.Once{}
|
||||||
|
e.val = ""
|
||||||
|
}
|
||||||
36
vendor/golang.org/x/net/proxy/socks5.go
generated
vendored
Normal file
36
vendor/golang.org/x/net/proxy/socks5.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2011 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 proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/socks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||||
|
// address with an optional username and password.
|
||||||
|
// See RFC 1928 and RFC 1929.
|
||||||
|
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||||
|
d := socks.NewDialer(network, address)
|
||||||
|
if forward != nil {
|
||||||
|
d.ProxyDial = func(_ context.Context, network string, address string) (net.Conn, error) {
|
||||||
|
return forward.Dial(network, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if auth != nil {
|
||||||
|
up := socks.UsernamePassword{
|
||||||
|
Username: auth.User,
|
||||||
|
Password: auth.Password,
|
||||||
|
}
|
||||||
|
d.AuthMethods = []socks.AuthMethod{
|
||||||
|
socks.AuthMethodNotRequired,
|
||||||
|
socks.AuthMethodUsernamePassword,
|
||||||
|
}
|
||||||
|
d.Authenticate = up.Authenticate
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
3
vendor/golang.org/x/sys/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/sys/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
||||||
3
vendor/golang.org/x/sys/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/sys/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||||
27
vendor/golang.org/x/sys/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/sys/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
||||||
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
Normal file
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
_obj/
|
||||||
|
unix.test
|
||||||
173
vendor/golang.org/x/sys/unix/README.md
generated
vendored
Normal file
173
vendor/golang.org/x/sys/unix/README.md
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
# Building `sys/unix`
|
||||||
|
|
||||||
|
The sys/unix package provides access to the raw system call interface of the
|
||||||
|
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix
|
||||||
|
|
||||||
|
Porting Go to a new architecture/OS combination or adding syscalls, types, or
|
||||||
|
constants to an existing architecture/OS pair requires some manual effort;
|
||||||
|
however, there are tools that automate much of the process.
|
||||||
|
|
||||||
|
## Build Systems
|
||||||
|
|
||||||
|
There are currently two ways we generate the necessary files. We are currently
|
||||||
|
migrating the build system to use containers so the builds are reproducible.
|
||||||
|
This is being done on an OS-by-OS basis. Please update this documentation as
|
||||||
|
components of the build system change.
|
||||||
|
|
||||||
|
### Old Build System (currently for `GOOS != "Linux" || GOARCH == "sparc64"`)
|
||||||
|
|
||||||
|
The old build system generates the Go files based on the C header files
|
||||||
|
present on your system. This means that files
|
||||||
|
for a given GOOS/GOARCH pair must be generated on a system with that OS and
|
||||||
|
architecture. This also means that the generated code can differ from system
|
||||||
|
to system, based on differences in the header files.
|
||||||
|
|
||||||
|
To avoid this, if you are using the old build system, only generate the Go
|
||||||
|
files on an installation with unmodified header files. It is also important to
|
||||||
|
keep track of which version of the OS the files were generated from (ex.
|
||||||
|
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
|
||||||
|
and have each OS upgrade correspond to a single change.
|
||||||
|
|
||||||
|
To build the files for your current OS and architecture, make sure GOOS and
|
||||||
|
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
||||||
|
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
||||||
|
|
||||||
|
Requirements: bash, perl, go
|
||||||
|
|
||||||
|
### New Build System (currently for `GOOS == "Linux" && GOARCH != "sparc64"`)
|
||||||
|
|
||||||
|
The new build system uses a Docker container to generate the go files directly
|
||||||
|
from source checkouts of the kernel and various system libraries. This means
|
||||||
|
that on any platform that supports Docker, all the files using the new build
|
||||||
|
system can be generated at once, and generated files will not change based on
|
||||||
|
what the person running the scripts has installed on their computer.
|
||||||
|
|
||||||
|
The OS specific files for the new build system are located in the `${GOOS}`
|
||||||
|
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
|
||||||
|
the kernel or system library updates, modify the Dockerfile at
|
||||||
|
`${GOOS}/Dockerfile` to checkout the new release of the source.
|
||||||
|
|
||||||
|
To build all the files under the new build system, you must be on an amd64/Linux
|
||||||
|
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
|
||||||
|
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
||||||
|
system. Running `mkall.sh -n` shows the commands that will be run.
|
||||||
|
|
||||||
|
Requirements: bash, perl, go, docker
|
||||||
|
|
||||||
|
## Component files
|
||||||
|
|
||||||
|
This section describes the various files used in the code generation process.
|
||||||
|
It also contains instructions on how to modify these files to add a new
|
||||||
|
architecture/OS or to add additional syscalls, types, or constants. Note that
|
||||||
|
if you are using the new build system, the scripts cannot be called normally.
|
||||||
|
They must be called from within the docker container.
|
||||||
|
|
||||||
|
### asm files
|
||||||
|
|
||||||
|
The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
|
||||||
|
call dispatch. There are three entry points:
|
||||||
|
```
|
||||||
|
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||||
|
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||||
|
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||||
|
```
|
||||||
|
The first and second are the standard ones; they differ only in how many
|
||||||
|
arguments can be passed to the kernel. The third is for low-level use by the
|
||||||
|
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
|
||||||
|
let it know that a system call is running.
|
||||||
|
|
||||||
|
When porting Go to an new architecture/OS, this file must be implemented for
|
||||||
|
each GOOS/GOARCH pair.
|
||||||
|
|
||||||
|
### mksysnum
|
||||||
|
|
||||||
|
Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl`
|
||||||
|
for the old system). This script takes in a list of header files containing the
|
||||||
|
syscall number declarations and parses them to produce the corresponding list of
|
||||||
|
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
||||||
|
constants.
|
||||||
|
|
||||||
|
Adding new syscall numbers is mostly done by running the build on a sufficiently
|
||||||
|
new installation of the target OS (or updating the source checkouts for the
|
||||||
|
new build system). However, depending on the OS, you make need to update the
|
||||||
|
parsing in mksysnum.
|
||||||
|
|
||||||
|
### mksyscall.pl
|
||||||
|
|
||||||
|
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
||||||
|
hand-written Go files which implement system calls (for unix, the specific OS,
|
||||||
|
or the specific OS/Architecture pair respectively) that need special handling
|
||||||
|
and list `//sys` comments giving prototypes for ones that can be generated.
|
||||||
|
|
||||||
|
The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts
|
||||||
|
them into syscalls. This requires the name of the prototype in the comment to
|
||||||
|
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
||||||
|
prototype can be exported (capitalized) or not.
|
||||||
|
|
||||||
|
Adding a new syscall often just requires adding a new `//sys` function prototype
|
||||||
|
with the desired arguments and a capitalized name so it is exported. However, if
|
||||||
|
you want the interface to the syscall to be different, often one will make an
|
||||||
|
unexported `//sys` prototype, an then write a custom wrapper in
|
||||||
|
`syscall_${GOOS}.go`.
|
||||||
|
|
||||||
|
### types files
|
||||||
|
|
||||||
|
For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
|
||||||
|
`types_${GOOS}.go` on the old system). This file includes standard C headers and
|
||||||
|
creates Go type aliases to the corresponding C types. The file is then fed
|
||||||
|
through godef to get the Go compatible definitions. Finally, the generated code
|
||||||
|
is fed though mkpost.go to format the code correctly and remove any hidden or
|
||||||
|
private identifiers. This cleaned-up code is written to
|
||||||
|
`ztypes_${GOOS}_${GOARCH}.go`.
|
||||||
|
|
||||||
|
The hardest part about preparing this file is figuring out which headers to
|
||||||
|
include and which symbols need to be `#define`d to get the actual data
|
||||||
|
structures that pass through to the kernel system calls. Some C libraries
|
||||||
|
preset alternate versions for binary compatibility and translate them on the
|
||||||
|
way in and out of system calls, but there is almost always a `#define` that can
|
||||||
|
get the real ones.
|
||||||
|
See `types_darwin.go` and `linux/types.go` for examples.
|
||||||
|
|
||||||
|
To add a new type, add in the necessary include statement at the top of the
|
||||||
|
file (if it is not already there) and add in a type alias line. Note that if
|
||||||
|
your type is significantly different on different architectures, you may need
|
||||||
|
some `#if/#elif` macros in your include statements.
|
||||||
|
|
||||||
|
### mkerrors.sh
|
||||||
|
|
||||||
|
This script is used to generate the system's various constants. This doesn't
|
||||||
|
just include the error numbers and error strings, but also the signal numbers
|
||||||
|
an a wide variety of miscellaneous constants. The constants come from the list
|
||||||
|
of include files in the `includes_${uname}` variable. A regex then picks out
|
||||||
|
the desired `#define` statements, and generates the corresponding Go constants.
|
||||||
|
The error numbers and strings are generated from `#include <errno.h>`, and the
|
||||||
|
signal numbers and strings are generated from `#include <signal.h>`. All of
|
||||||
|
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
|
||||||
|
`_errors.c`, which prints out all the constants.
|
||||||
|
|
||||||
|
To add a constant, add the header that includes it to the appropriate variable.
|
||||||
|
Then, edit the regex (if necessary) to match the desired constant. Avoid making
|
||||||
|
the regex too broad to avoid matching unintended constants.
|
||||||
|
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
### `zerror_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A file containing all of the system's generated error numbers, error strings,
|
||||||
|
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
||||||
|
|
||||||
|
### `zsyscall_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
||||||
|
Generated by `mksyscall.pl` (see above).
|
||||||
|
|
||||||
|
### `zsysnum_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A list of numeric constants for all the syscall number of the specific GOOS
|
||||||
|
and GOARCH. Generated by mksysnum (see above).
|
||||||
|
|
||||||
|
### `ztypes_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A file containing Go types for passing into (or returning from) syscalls.
|
||||||
|
Generated by godefs and the types file (see above).
|
||||||
124
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
Normal file
124
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// CPU affinity functions
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||||
|
|
||||||
|
// CPUSet represents a CPU affinity mask.
|
||||||
|
type CPUSet [cpuSetSize]cpuMask
|
||||||
|
|
||||||
|
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||||
|
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||||
|
if e != 0 {
|
||||||
|
return errnoErr(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
||||||
|
// If pid is 0 the calling thread is used.
|
||||||
|
func SchedGetaffinity(pid int, set *CPUSet) error {
|
||||||
|
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
||||||
|
// If pid is 0 the calling thread is used.
|
||||||
|
func SchedSetaffinity(pid int, set *CPUSet) error {
|
||||||
|
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero clears the set s, so that it contains no CPUs.
|
||||||
|
func (s *CPUSet) Zero() {
|
||||||
|
for i := range s {
|
||||||
|
s[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuBitsIndex(cpu int) int {
|
||||||
|
return cpu / _NCPUBITS
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuBitsMask(cpu int) cpuMask {
|
||||||
|
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adds cpu to the set s.
|
||||||
|
func (s *CPUSet) Set(cpu int) {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
s[i] |= cpuBitsMask(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes cpu from the set s.
|
||||||
|
func (s *CPUSet) Clear(cpu int) {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
s[i] &^= cpuBitsMask(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSet reports whether cpu is in the set s.
|
||||||
|
func (s *CPUSet) IsSet(cpu int) bool {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
return s[i]&cpuBitsMask(cpu) != 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the number of CPUs in the set s.
|
||||||
|
func (s *CPUSet) Count() int {
|
||||||
|
c := 0
|
||||||
|
for _, b := range s {
|
||||||
|
c += onesCount64(uint64(b))
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// onesCount64 is a copy of Go 1.9's math/bits.OnesCount64.
|
||||||
|
// Once this package can require Go 1.9, we can delete this
|
||||||
|
// and update the caller to use bits.OnesCount64.
|
||||||
|
func onesCount64(x uint64) int {
|
||||||
|
const m0 = 0x5555555555555555 // 01010101 ...
|
||||||
|
const m1 = 0x3333333333333333 // 00110011 ...
|
||||||
|
const m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ...
|
||||||
|
const m3 = 0x00ff00ff00ff00ff // etc.
|
||||||
|
const m4 = 0x0000ffff0000ffff
|
||||||
|
|
||||||
|
// Implementation: Parallel summing of adjacent bits.
|
||||||
|
// See "Hacker's Delight", Chap. 5: Counting Bits.
|
||||||
|
// The following pattern shows the general approach:
|
||||||
|
//
|
||||||
|
// x = x>>1&(m0&m) + x&(m0&m)
|
||||||
|
// x = x>>2&(m1&m) + x&(m1&m)
|
||||||
|
// x = x>>4&(m2&m) + x&(m2&m)
|
||||||
|
// x = x>>8&(m3&m) + x&(m3&m)
|
||||||
|
// x = x>>16&(m4&m) + x&(m4&m)
|
||||||
|
// x = x>>32&(m5&m) + x&(m5&m)
|
||||||
|
// return int(x)
|
||||||
|
//
|
||||||
|
// Masking (& operations) can be left away when there's no
|
||||||
|
// danger that a field's sum will carry over into the next
|
||||||
|
// field: Since the result cannot be > 64, 8 bits is enough
|
||||||
|
// and we can ignore the masks for the shifts by 8 and up.
|
||||||
|
// Per "Hacker's Delight", the first line can be simplified
|
||||||
|
// more, but it saves at best one instruction, so we leave
|
||||||
|
// it alone for clarity.
|
||||||
|
const m = 1<<64 - 1
|
||||||
|
x = x>>1&(m0&m) + x&(m0&m)
|
||||||
|
x = x>>2&(m1&m) + x&(m1&m)
|
||||||
|
x = (x>>4 + x) & (m2 & m)
|
||||||
|
x += x >> 8
|
||||||
|
x += x >> 16
|
||||||
|
x += x >> 32
|
||||||
|
return int(x) & (1<<7 - 1)
|
||||||
|
}
|
||||||
14
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
Normal file
14
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2018 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 darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
type Signal = syscall.Signal
|
||||||
|
type Errno = syscall.Errno
|
||||||
|
type SysProcAttr = syscall.SysProcAttr
|
||||||
29
vendor/golang.org/x/sys/unix/asm_darwin_386.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_darwin_386.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for 386, Darwin
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_darwin_amd64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_darwin_amd64.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for AMD64, Darwin
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
30
vendor/golang.org/x/sys/unix/asm_darwin_arm.s
generated
vendored
Normal file
30
vendor/golang.org/x/sys/unix/asm_darwin_arm.s
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2015 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
|
||||||
|
// +build arm,darwin
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for ARM, Darwin
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
B syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·RawSyscall6(SB)
|
||||||
30
vendor/golang.org/x/sys/unix/asm_darwin_arm64.s
generated
vendored
Normal file
30
vendor/golang.org/x/sys/unix/asm_darwin_arm64.s
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2015 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
|
||||||
|
// +build arm64,darwin
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for AMD64, Darwin
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
B syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
B syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for AMD64, DragonFly
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_freebsd_386.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_freebsd_386.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for 386, FreeBSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_freebsd_amd64.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for AMD64, FreeBSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_freebsd_arm.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_freebsd_arm.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2012 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for ARM, FreeBSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
B syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·RawSyscall6(SB)
|
||||||
65
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
Normal file
65
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for 386, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
||||||
|
// instead of the glibc-specific "CALL 0x10(GS)".
|
||||||
|
#define INVOKE_SYSCALL INT $0x80
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
CALL runtime·entersyscall(SB)
|
||||||
|
MOVL trap+0(FP), AX // syscall entry
|
||||||
|
MOVL a1+4(FP), BX
|
||||||
|
MOVL a2+8(FP), CX
|
||||||
|
MOVL a3+12(FP), DX
|
||||||
|
MOVL $0, SI
|
||||||
|
MOVL $0, DI
|
||||||
|
INVOKE_SYSCALL
|
||||||
|
MOVL AX, r1+16(FP)
|
||||||
|
MOVL DX, r2+20(FP)
|
||||||
|
CALL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVL trap+0(FP), AX // syscall entry
|
||||||
|
MOVL a1+4(FP), BX
|
||||||
|
MOVL a2+8(FP), CX
|
||||||
|
MOVL a3+12(FP), DX
|
||||||
|
MOVL $0, SI
|
||||||
|
MOVL $0, DI
|
||||||
|
INVOKE_SYSCALL
|
||||||
|
MOVL AX, r1+16(FP)
|
||||||
|
MOVL DX, r2+20(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||||
|
JMP syscall·socketcall(SB)
|
||||||
|
|
||||||
|
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
|
||||||
|
JMP syscall·rawsocketcall(SB)
|
||||||
|
|
||||||
|
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·seek(SB)
|
||||||
57
vendor/golang.org/x/sys/unix/asm_linux_amd64.s
generated
vendored
Normal file
57
vendor/golang.org/x/sys/unix/asm_linux_amd64.s
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for AMD64, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
CALL runtime·entersyscall(SB)
|
||||||
|
MOVQ a1+8(FP), DI
|
||||||
|
MOVQ a2+16(FP), SI
|
||||||
|
MOVQ a3+24(FP), DX
|
||||||
|
MOVQ $0, R10
|
||||||
|
MOVQ $0, R8
|
||||||
|
MOVQ $0, R9
|
||||||
|
MOVQ trap+0(FP), AX // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVQ AX, r1+32(FP)
|
||||||
|
MOVQ DX, r2+40(FP)
|
||||||
|
CALL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVQ a1+8(FP), DI
|
||||||
|
MOVQ a2+16(FP), SI
|
||||||
|
MOVQ a3+24(FP), DX
|
||||||
|
MOVQ $0, R10
|
||||||
|
MOVQ $0, R8
|
||||||
|
MOVQ $0, R9
|
||||||
|
MOVQ trap+0(FP), AX // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVQ AX, r1+32(FP)
|
||||||
|
MOVQ DX, r2+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
||||||
|
JMP syscall·gettimeofday(SB)
|
||||||
56
vendor/golang.org/x/sys/unix/asm_linux_arm.s
generated
vendored
Normal file
56
vendor/golang.org/x/sys/unix/asm_linux_arm.s
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for arm, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVW trap+0(FP), R7
|
||||||
|
MOVW a1+4(FP), R0
|
||||||
|
MOVW a2+8(FP), R1
|
||||||
|
MOVW a3+12(FP), R2
|
||||||
|
MOVW $0, R3
|
||||||
|
MOVW $0, R4
|
||||||
|
MOVW $0, R5
|
||||||
|
SWI $0
|
||||||
|
MOVW R0, r1+16(FP)
|
||||||
|
MOVW $0, R0
|
||||||
|
MOVW R0, r2+20(FP)
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVW trap+0(FP), R7 // syscall entry
|
||||||
|
MOVW a1+4(FP), R0
|
||||||
|
MOVW a2+8(FP), R1
|
||||||
|
MOVW a3+12(FP), R2
|
||||||
|
SWI $0
|
||||||
|
MOVW R0, r1+16(FP)
|
||||||
|
MOVW $0, R0
|
||||||
|
MOVW R0, r2+20(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·seek(SB)
|
||||||
52
vendor/golang.org/x/sys/unix/asm_linux_arm64.s
generated
vendored
Normal file
52
vendor/golang.org/x/sys/unix/asm_linux_arm64.s
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2015 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 linux
|
||||||
|
// +build arm64
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R0
|
||||||
|
MOVD a2+16(FP), R1
|
||||||
|
MOVD a3+24(FP), R2
|
||||||
|
MOVD $0, R3
|
||||||
|
MOVD $0, R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD trap+0(FP), R8 // syscall entry
|
||||||
|
SVC
|
||||||
|
MOVD R0, r1+32(FP) // r1
|
||||||
|
MOVD R1, r2+40(FP) // r2
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
B syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVD a1+8(FP), R0
|
||||||
|
MOVD a2+16(FP), R1
|
||||||
|
MOVD a3+24(FP), R2
|
||||||
|
MOVD $0, R3
|
||||||
|
MOVD $0, R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD trap+0(FP), R8 // syscall entry
|
||||||
|
SVC
|
||||||
|
MOVD R0, r1+32(FP)
|
||||||
|
MOVD R1, r2+40(FP)
|
||||||
|
RET
|
||||||
56
vendor/golang.org/x/sys/unix/asm_linux_mips64x.s
generated
vendored
Normal file
56
vendor/golang.org/x/sys/unix/asm_linux_mips64x.s
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2015 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 linux
|
||||||
|
// +build mips64 mips64le
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for mips64, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
JAL runtime·entersyscall(SB)
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R2, r1+32(FP)
|
||||||
|
MOVV R3, r2+40(FP)
|
||||||
|
JAL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R2, r1+32(FP)
|
||||||
|
MOVV R3, r2+40(FP)
|
||||||
|
RET
|
||||||
54
vendor/golang.org/x/sys/unix/asm_linux_mipsx.s
generated
vendored
Normal file
54
vendor/golang.org/x/sys/unix/asm_linux_mipsx.s
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2016 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 linux
|
||||||
|
// +build mips mipsle
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for mips, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
JAL runtime·entersyscall(SB)
|
||||||
|
MOVW a1+4(FP), R4
|
||||||
|
MOVW a2+8(FP), R5
|
||||||
|
MOVW a3+12(FP), R6
|
||||||
|
MOVW R0, R7
|
||||||
|
MOVW trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVW R2, r1+16(FP) // r1
|
||||||
|
MOVW R3, r2+20(FP) // r2
|
||||||
|
JAL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVW a1+4(FP), R4
|
||||||
|
MOVW a2+8(FP), R5
|
||||||
|
MOVW a3+12(FP), R6
|
||||||
|
MOVW trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVW R2, r1+16(FP)
|
||||||
|
MOVW R3, r2+20(FP)
|
||||||
|
RET
|
||||||
56
vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
generated
vendored
Normal file
56
vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2014 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 linux
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for ppc64, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
BR syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
BR syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R3
|
||||||
|
MOVD a2+16(FP), R4
|
||||||
|
MOVD a3+24(FP), R5
|
||||||
|
MOVD R0, R6
|
||||||
|
MOVD R0, R7
|
||||||
|
MOVD R0, R8
|
||||||
|
MOVD trap+0(FP), R9 // syscall entry
|
||||||
|
SYSCALL R9
|
||||||
|
MOVD R3, r1+32(FP)
|
||||||
|
MOVD R4, r2+40(FP)
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
BR syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
BR syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVD a1+8(FP), R3
|
||||||
|
MOVD a2+16(FP), R4
|
||||||
|
MOVD a3+24(FP), R5
|
||||||
|
MOVD R0, R6
|
||||||
|
MOVD R0, R7
|
||||||
|
MOVD R0, R8
|
||||||
|
MOVD trap+0(FP), R9 // syscall entry
|
||||||
|
SYSCALL R9
|
||||||
|
MOVD R3, r1+32(FP)
|
||||||
|
MOVD R4, r2+40(FP)
|
||||||
|
RET
|
||||||
56
vendor/golang.org/x/sys/unix/asm_linux_s390x.s
generated
vendored
Normal file
56
vendor/golang.org/x/sys/unix/asm_linux_s390x.s
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2016 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 s390x
|
||||||
|
// +build linux
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for s390x, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
BR syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
BR syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R2
|
||||||
|
MOVD a2+16(FP), R3
|
||||||
|
MOVD a3+24(FP), R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD $0, R6
|
||||||
|
MOVD $0, R7
|
||||||
|
MOVD trap+0(FP), R1 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVD R2, r1+32(FP)
|
||||||
|
MOVD R3, r2+40(FP)
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
BR syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
BR syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVD a1+8(FP), R2
|
||||||
|
MOVD a2+16(FP), R3
|
||||||
|
MOVD a3+24(FP), R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD $0, R6
|
||||||
|
MOVD $0, R7
|
||||||
|
MOVD trap+0(FP), R1 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVD R2, r1+32(FP)
|
||||||
|
MOVD R3, r2+40(FP)
|
||||||
|
RET
|
||||||
29
vendor/golang.org/x/sys/unix/asm_netbsd_386.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_netbsd_386.s
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2009 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for 386, NetBSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user