diff --git a/.cross_compile.sh b/.cross_compile.sh index 63a152d..bb55a74 100644 --- a/.cross_compile.sh +++ b/.cross_compile.sh @@ -26,15 +26,15 @@ for pl in ${PLATFORMS}; do echo "build => ${TARGET}" if [ "${DEBUG_MODE}" == "debug" ]; then go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \ - -ldflags "-X 'github.com/xgadget-lab/nexttrace/config.Version=${BUILD_VERSION}' \ - -X 'github.com/xgadget-lab/nexttrace/config.BuildDate=${BUILD_DATE}' \ - -X 'github.com/xgadget-lab/nexttrace/config.CommitID=${COMMIT_SHA1}'\ + -ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\ -w -s" else go build -trimpath -o ${TARGET} \ - -ldflags "-X 'github.com/xgadget-lab/nexttrace/config.Version=${BUILD_VERSION}' \ - -X 'github.com/xgadget-lab/nexttrace/config.BuildDate=${BUILD_DATE}' \ - -X 'github.com/xgadget-lab/nexttrace/config.CommitID=${COMMIT_SHA1}'\ + -ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\ -w -s" fi done @@ -46,15 +46,15 @@ done echo "build => ${TARGET}" if [ "${DEBUG_MODE}" == "debug" ]; then go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \ - -ldflags "-X 'github.com/xgadget-lab/nexttrace/config.Version=${BUILD_VERSION}' \ - -X 'github.com/xgadget-lab/nexttrace/config.BuildDate=${BUILD_DATE}' \ - -X 'github.com/xgadget-lab/nexttrace/config.CommitID=${COMMIT_SHA1}'\ + -ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\ -w -s" else go build -trimpath -o ${TARGET} \ - -ldflags "-X 'github.com/xgadget-lab/nexttrace/config.Version=${BUILD_VERSION}' \ - -X 'github.com/xgadget-lab/nexttrace/config.BuildDate=${BUILD_DATE}' \ - -X 'github.com/xgadget-lab/nexttrace/config.CommitID=${COMMIT_SHA1}'\ + -ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \ + -X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\ -w -s" fi diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c282d88..c5e67d4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -38,7 +38,3 @@ copyright: [v2fly](https://github.com/v2fly) ## 请附上出错时软件输出的错误信息 - -## 是否查询过本仓库wiki有没有类似错误 - - diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 6cd4fd2..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,15 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "gomod" - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d454f27..bf6da9e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,152 +1,38 @@ -name: Build & Release - on: - workflow_dispatch: push: - branches: - - main - - backup - tags: - - "v*" - paths: - - "**/*.go" - - "go.mod" - - "go.sum" - - ".github/workflows/*.yml" pull_request: - types: [opened, synchronize, reopened] - paths: - - "**/*.go" - - "go.mod" - - "go.sum" - - ".github/workflows/*.yml" + +name: Test & Build Release jobs: - build: + Test: runs-on: ubuntu-latest - strategy: - matrix: - # Include amd64 on all platforms. - goos: [windows, freebsd, openbsd, linux, dragonfly, darwin] - goarch: [amd64, 386] - exclude: - # Exclude i386 on darwin and dragonfly. - - goarch: 386 - goos: dragonfly - - goarch: 386 - goos: darwin - include: - # BEIGIN MacOS ARM64 - - goos: darwin - goarch: arm64 - # END macOS ARM64 - # BEGIN Linux ARM 5 6 7 - - goos: linux - goarch: arm - goarm: 7 - - goos: linux - goarch: arm - goarm: 6 - - goos: linux - goarch: arm - goarm: 5 - # END Linux ARM 5 6 7 - # BEGIN Android ARM 8 - - goos: android - goarch: arm64 - # END Android ARM 8 - # Windows ARM - - goos: windows - goarch: arm64 - - goos: windows - goarch: arm - goarm: 7 - # BEGIN Other architectures - # BEGIN riscv64 & ARM64 - - goos: linux - goarch: arm64 - - goos: linux - goarch: riscv64 - # END riscv64 & ARM64 - # BEGIN MIPS - - goos: linux - goarch: mips64 - - goos: linux - goarch: mips64le - - goos: linux - goarch: mipsle - - goos: linux - goarch: mips - # END MIPS - # BEGIN PPC - - goos: linux - goarch: ppc64 - - goos: linux - goarch: ppc64le - # END PPC - # BEGIN FreeBSD ARM - - goos: freebsd - goarch: arm64 - - goos: freebsd - goarch: arm - goarm: 7 - # END FreeBSD ARM - # BEGIN S390X - - goos: linux - goarch: s390x - # END S390X - # END Other architectures - # BEGIN OPENBSD ARM - - goos: openbsd - goarch: arm64 - - goos: openbsd - goarch: arm - goarm: 7 - env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - GOARM: ${{ matrix.goarm }} - CGO_ENABLED: 0 steps: - - name: Checkout codebase - uses: actions/checkout@v3 - - name: Show workflow information - run: | - if [ ! -z $GOARM ]; then - export GOARM=v$GOARM - fi - export _NAME="nexttrace_${GOOS}_${GOARCH}${GOARM}" - if [ "$GOOS" == "windows" ]; then - export _NAME="$_NAME.exe" - fi - echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, RELEASE_NAME: $_NAME" - echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV - echo "BUILD_VERSION=$(git describe --tags --always)" >> $GITHUB_ENV - echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV - echo "COMMIT_SHA1=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - name: Set up Go - uses: actions/setup-go@v4 + - uses: actions/checkout@v3 + + - uses: actions/setup-go@v2 with: - go-version: '1.20' - - name: Get project dependencies - run: go mod download - - name: Build - run: | - go build -trimpath -o dist/${ASSET_NAME} \ - -ldflags "-X 'github.com/xgadget-lab/nexttrace/config.Version=${BUILD_VERSION}' \ - -X 'github.com/xgadget-lab/nexttrace/config.BuildDate=${BUILD_DATE}' \ - -X 'github.com/xgadget-lab/nexttrace/config.CommitID=${COMMIT_SHA1}'\ - -w -s" - - name: Upload files to Artifacts - uses: actions/upload-artifact@v3 + go-version: "1.18" + + - name: Test + run: sudo go test -v -coverprofile='coverage.out' -covermode=count ./... + + Build: + needs: test + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-go@v2 with: - name: ${{ env.ASSET_NAME }} - path: | - dist/${{ env.ASSET_NAME }} + go-version: "1.18" + + - run: bash .cross_compile.sh + - name: Release - if: startsWith(github.ref, 'refs/tags/v') uses: softprops/action-gh-release@v1 with: # 将下述可执行文件 release 上去 - draft: true # Release草稿 + draft: false # Release草稿 files: | dist/* env: @@ -156,7 +42,6 @@ jobs: needs: build # The type of runner that the job will run on runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') # Steps represent a sequence of tasks that will be executed as part of the job steps: diff --git a/.github/workflows/publishNewFormula.yml b/.github/workflows/publishNewFormula.yml index 8c64550..972457b 100644 --- a/.github/workflows/publishNewFormula.yml +++ b/.github/workflows/publishNewFormula.yml @@ -21,7 +21,7 @@ jobs: git config --global user.name "${{ secrets.git_name }}" - name: Clone repo run: | - git clone https://github.com/xgadget-lab/homebrew-nexttrace.git + git clone https://github.com/sjlleo/homebrew-nexttrace.git - name: Exec scipt run: | cd homebrew-nexttrace @@ -38,7 +38,7 @@ jobs: run: | cd homebrew-nexttrace git commit -am 'Publish a new version with Formula' || true - git remote set-url origin https://${{ secrets.gt_token }}@github.com/xgadget-lab/homebrew-nexttrace.git + git remote set-url origin https://${{ secrets.gt_token }}@github.com/sjlleo/homebrew-nexttrace.git git push # env: # SSH_AUTH_SOCK: /tmp/ssh_agent.sock diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 3fcd527..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Test - -on: - push: - branches: - - main - - backup - paths: - - "**/*.go" - - "go.mod" - - "go.sum" - - ".github/workflows/*.yml" - pull_request: - types: [opened, synchronize, reopened] - paths: - - "**/*.go" - - "go.mod" - - "go.sum" - - ".github/workflows/*.yml" - workflow_dispatch: - -jobs: - test: - permissions: - contents: read - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [windows-latest, ubuntu-latest, macos-latest] - steps: - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.20' - check-latest: true - - name: Checkout codebase - uses: actions/checkout@v3 - - name: Test with unix - if: ${{ matrix.os != 'windows-latest' }} - run: sudo go test -v -coverprofile='coverage.out' -covermode=count ./... - - name: Test with windows - if: ${{ matrix.os == 'windows-latest' }} - run: go test -v -coverprofile='coverage.out' -covermode=count ./... diff --git a/README.md b/README.md index 550a0dd..852de14 100644 --- a/README.md +++ b/README.md @@ -8,101 +8,27 @@ An open source visual routing tool that pursues light weight, developed using Golang. -

- - Github Actions - - - - - - - -

- -NextTrace is part of the [OwO Network](https://github.com/OwO-Network) project. The project is a joint initiative of Leo and Vincent. - -If you like this project, [Donate us](https://afdian.net/a/sjlleo/plan) to help us to provide long-lasting API cost expenses. - -The project is currently undergoing a major refactoring. During this period, this repository will no longer accept new feature requests or bug reports. If you have any issues, please go to the ISSUE section of [tsosunchia/nexttrace-core](https://github.com/tsosunchia/nexttrace-core) to provide feedback. Thank you for your support! - ## How To Use Document Language: English | [简体中文](README_zh_CN.md) -### Automated Install +### Automated Installation -* Linux - * One-click installation script - * Download from GitHub +```bash +# Linux one-click install script +bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh) - ```shell - bash -c "$(curl -Ls https://github.com/sjlleo/nexttrace/raw/main/nt_install.sh)" - ``` - * GHPROXY mirror (For use in Mainland China) +# macOS brew install command +brew tap xgadget-lab/nexttrace && brew install nexttrace - ```shell - bash -c "$(curl -Ls https://ghproxy.com/https://github.com/sjlleo/nexttrace/raw/main/nt_install.sh)" - ``` - * Arch Linux AUR installation command - * Build from source +# GHProxy Mirror (For China Mainland User) +bash -c "$(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)" +``` - ```shell - yay -S nexttrace - ``` - * Directly download bin package (only supports amd64) - - ```shell - yay -S nexttrace-bin - ``` - * The two types of AUR builds are maintained by huyz and ouuan, respectively - * Linuxbrew's installation command - - Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64) - -* macOS - * macOS Homebrew's installation command - * Homebrew-core version - - ```shell - brew install nexttrace - ``` - * This repository's ACTIONS automatically built version (updates faster) - - ```shell - brew tap xgadget-lab/nexttrace && brew install xgadget-lab/nexttrace/nexttrace - ``` - * The homebrew-core build is maintained by chenrui333, please note that this version's updates may lag behind the repository Action automatically version - -* Windows - * Windows Scoop installation command - * Scoop-extras version - - ```powershell - scoop bucket add extras && scoop install extras/nexttrace - ``` - - * Scoop-extra is maintained by soenggam - - Please note, the repositories for all of the above installation methods are maintained by open source enthusiasts. Availability and timely updates are not guaranteed. If you encounter problems, please contact the repository maintainer to solve them, or use the binary packages provided by the official build of this project. - -### Manual Install -* Download the precompiled executable - - For users not covered by the above methods, please go directly to [Release](https://github.com/sjlleo/nexttrace/releases/latest) to download the compiled binary executable. - - * `Release` provides compiled binary executables for many systems and different architectures. If none are available, you can compile it yourself. - * Some essential dependencies of this project are not fully implemented on `Windows` by `Golang`, so currently, `NextTrace` is in an experimental support phase on the `Windows` platform. - -* Install from source - - After installing Go >= 1.20 yourself, you can use the following command to install - - ```shell - go install github.com/xgadget-lab/nexttrace@latest - ``` - After installation, the executable is in the `$GOPATH/bin` directory. If you have not set `GOPATH`, it is in the `$HOME/go/bin` directory. +Windows users please go to [Release Page](https://github.com/sjlleo/nexttrace/releases/latest) directly and download exe file. +- `Release` provides compiled executables for many systems and architectures, if not, you can compile it yourself. +- Some of the necessary dependencies of this project are not fully implemented in `Golang` on `Windows`, so currently `NextTrace` is experimental on `Windows` platform. ### Get Started @@ -114,17 +40,9 @@ nexttrace 1.0.0.1 # URL nexttrace http://example.com:8080/index.html?q=1 -# Form printing +# Form printing (output all hops at one time, wait 20-40 seconds) nexttrace --table 1.0.0.1 -# An Output Easy to Parse -nexttrace --raw 1.0.0.1 -nexttrace --json 1.0.0.1 - -# IPv4/IPv6 Resolve Only -nexttrace --ipv4 g.co -nexttrace --ipv6 g.co - # IPv6 ICMP Trace nexttrace 2606:4700:4700::1111 @@ -190,9 +108,6 @@ nexttrace --first 5 --max-hops 10 www.decix.net # Turn off the IP reverse parsing function nexttrace --no-rdns www.bbix.net -# Set the payload size to 1024 bytes -nexttrace --psize 1024 example.com - # Feature: print Route-Path diagram # Route-Path diagram example: # AS6453 Tata Communication「Singapore『Singapore』」 @@ -205,25 +120,15 @@ nexttrace --psize 1024 example.com nexttrace --route-path www.time.com.my ``` -`NextTrace` supports users to select their own IP API (currently supports: `LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`) +`NextTrace` supports users to select their own IP API (currently supports: `LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`) ```bash -# You can specify the IP database by yourself [IP-API.com here], if not specified, LeoMoeAPI will be used -nexttrace --data-provider ip-api.com -## Note There are frequency limits for free queries of the ipinfo and IPInsight APIs. You can purchase services from these providers to remove the limits -## If necessary, you can clone this project, add the token provided by ipinfo or IPInsight and compile it yourself -## Note For the offline database IPInfoLocal, please download it manually and rename it to ipinfoLocal.mmdb. (You can download it from here: https://ipinfo.io/signup?ref=free-database-downloads) -## For the offline database Ip2region, you can download it manually and rename it to ip2region.db, or let NextTrace download it automatically +# You can specify the IP database by yourself [IP.SB here], if not specified, LeoMoeAPI will be used +nexttrace --data-provider IP.SB +## Note that the ipinfo API needs users to purchase services from ipinfo. If necessary, you can clone this project, add the token provided by ipinfo and compile it yourself ## Fill the token to: ipgeo/tokens.go ## Please be aware: Due to the serious abuse of IP.SB, you will often be not able to query IP data from this source -## IP-API.com has a stricter restiction on API calls, if you can't query IP data from this source, please try again in a few minutes - -# The Pure-FTPd IP database defaults to using http://127.0.0.1:2060 as the query interface. To customize it, please use environment variables -export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060 -## You can use https://github.com/freshcn/qqwry to build your own Pure-FTPd IP database service - -# You can also specify the default IP database by setting an environment variable -export NEXTTRACE_DATAPROVIDER=ipinfo +## IPAPI.com has a stricter restiction on API calls, if you can't query IP data from this source, please try again in a few minutes. ``` `NextTrace` supports mixed parameters and shortened parameters @@ -246,36 +151,27 @@ NextTrace BackEnd is now open-source. https://github.com/sjlleo/nexttrace-backend -NextTrace `LeoMoeAPI` now utilizes the Proof of Work (POW) mechanism to prevent abuse, where NextTrace introduces the powclient library as a client-side component. Both the POW CLIENT and SERVER are open source, and everyone is welcome to use them. (Please direct any POW module-related questions to the corresponding repositories) - -- [GitHub - tsosunchia/powclient: Proof of Work CLIENT for NextTrace](https://github.com/tsosunchia/powclient) -- [GitHub - tsosunchia/powserver: Proof of Work SERVER for NextTrace](https://github.com/tsosunchia/powserver) - All NextTrace IP geolocation `API DEMO` can refer to [here](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/) ### For full usage list, please refer to the usage menu ```shell -Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp] - [-F|--fast-trace] [-p|--port ] [-q|--queries - ] [--parallel-requests ] [-m|--max-hops - ] [-d|--data-provider - (Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)] - [-n|--no-rdns] [-a|--always-rdns] [-P|--route-path] - [-r|--report] [--dn42] [-o|--output] [-t|--table] [--raw] - [-j|--json] [-c|--classic] [-f|--first ] [-M|--map] - [-v|--version] [-s|--source ""] [-D|--dev ""] - [-R|--route] [-z|--send-time ] [-i|--ttl-time - ] [--timeout ] [--psize ] - [_positionalArg_nexttrace_31 ""] [--dot-server - (dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language - (en|cn)] +usage: nexttrace [-h|--help] [-T|--tcp] [-U|--udp] [-F|--fast-trace] [-p|--port + ] [-q|--queries ] [--parallel-requests + ] [-m|--max-hops ] [-d|--data-provider + (IP.SB|IPInfo|IPInsight|IPAPI.com)] [-n|--no-rdns] + [-a|--always-rdns] [-P|--route-path] [-r|--report] + [-o|--output] [-t|--table] [-c|--classic] [-f|--first + ] [-M|--map] [-v|--version] [-s|--source ""] + [-D|--dev ""] [-R|--route] [-z|--send-time ] + [-i|--ttl-time ] [-g|--language (en|cn)] [IP Address + or Domain] + + An open source visual route tracking CLI tool Arguments: -h --help Print help information - -4 --ipv4 Use IPv4 only - -6 --ipv6 Use IPv6 only -T --tcp Use TCP SYN for tracerouting (default port is 80) -U --udp Use UDP SYN for tracerouting (default port @@ -296,10 +192,9 @@ Arguments: 18 -m --max-hops Set the max number of hops (max TTL to be reached). Default: 30 - -d --data-provider Choose IP Geograph Data Provider [IP.SB, - IPInfo, IPInsight, IP-API.com, Ip2region, - IPInfoLocal, CHUNZHEN, disable-geoip]. - Default: LeoMoeAPI + -d --data-provider Choose IP Geograph Data Provider + [LeoMoeAPI,IP.SB, IPInfo, IPInsight, + IPAPI.com]. Default: LeoMoeAPI -n --no-rdns Do not resolve IP addresses to their domain names -a --always-rdns Always resolve IP addresses to their @@ -307,38 +202,26 @@ Arguments: -P --route-path Print traceroute hop path by ASN and location -r --report output using report mode - --dn42 DN42 Mode -o --output Write trace result to file (RealTimePrinter ONLY) -t --table Output trace results as table - --raw An Output Easy to Parse - -j --json Output trace results as JSON -c --classic Classic Output trace results like BestTrace -f --first Start from the first_ttl hop (instead from 1). Default: 1 - -M --map Disable Print Trace Map + -M --map Disable Print Trace Map Function -v --version Print version info and exit -s --source Use source src_addr for outgoing packets -D --dev Use the following Network Devices as the source address in outgoing packets -R --route Show Routing Table [Provided By BGP.Tools] - -z --send-time Set how many [milliseconds] between - sending each packet.. Useful when some - routers use rate-limit for ICMP messages. - Default: 100 - -i --ttl-time Set how many [milliseconds] between - sending packets groups by TTL. Useful when - some routers use rate-limit for ICMP - messages. Default: 500 - --timeout The number of [milliseconds] to keep probe - sockets open before giving up on the - connection.. Default: 1000 - --psize Set the packet size (payload size). - Default: 52 - --_positionalArg_nexttrace_31 IP Address or domain name - --dot-server Use DoT Server for DNS Parse [dnssb, - aliyun, dnspod, google, cloudflare] + -z --send-time Set the time interval for sending every + packet. Useful when some routers use + rate-limit for ICMP messages. Default: 100 + -i --ttl-time Set the time interval for sending packets + groups by TTL. Useful when some routers + use rate-limit for ICMP messages. Default: + 500 -g --language Choose the language for displaying [en, cn]. Default: cn ``` @@ -355,21 +238,7 @@ Arguments: Please Notice that `NextTrace Enhanced` is currently not supported in English. -[https://github.com/OwO-Network/nexttrace-enhanced](https://github.com/OwO-Network/nexttrace-enhanced) - -## OpenTrace - -`OpenTrace` is the cross-platform `GUI` version of `NextTrace` developed by @Archeb, bringing a familiar but more powerful user experience. - -This software is still in the early stages of development and may have many flaws and errors. We value your feedback. - -[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace) - -## NEXTTRACE WEB API - -`NextTraceWebApi` is a web-based server implementation of `NextTrace` in the `MTR` style, offering various deployment options including `Docker`. - -[https://github.com/tsosunchia/nexttracewebapi](https://github.com/tsosunchia/nexttracewebapi) +https://github.com/OwO-Network/nexttrace-enhanced ## LeoMoeAPI Credit @@ -379,7 +248,7 @@ The LeoMoeAPI data is subject to copyright restrictions from multiple data sourc 1. We would like to credit samleong123 for providing nodes in Malaysia, TOHUNET Looking Glass for global nodes, and Ping.sx from Misaka, where more than 80% of reliable calibration data comes from ping/mtr reports. -2. At the same time, we would like to credit isyekong for their contribution to rDNS-based calibration ideas and data. LeoMoeAPI is accelerating the development of rDNS resolution function, and has already achieved automated geolocation resolution for some backbone networks, but there are some misjudgments. We hope that NextTrace will become a One-Man ISP-friendly traceroute tool in the future, and we are working on improving the calibration of these ASN micro-backbones as much as possible. +2. At the same time, we would like to credit isyekong for their contribution on rDNS-based calibration ideas and data. LeoMoeAPI is accelerating the development of rDNS resolution function, and has already achieved automated geolocation resolution for some backbone networks, but there are some misjudgments. We hope that NextTrace will become a One-Man ISP-friendly traceroute tool in the future, and we are working on improving the calibration of these ASN micro-backbones as much as possible. 3. In terms of development, I would like to credit missuo and zhshch for their help with Go cross-compilation, design concepts and TCP/UDP Traceroute refactoring, and tsosunchia for their support on TraceMap. @@ -396,7 +265,7 @@ We hope you can give us as much feedback as possible on IP geolocation errors (s ## Credits -[BGP.TOOLS](https://bgp.tools) provided some data support for this project. And we would like to express our sincere gratitude. +BGP.TOOLS provided some data support for this project and we would like to express our sincere gratitude. [Vincent Young](https://github.com/missuo) (i@yyt.moe) @@ -406,19 +275,12 @@ We hope you can give us as much feedback as possible on IP geolocation errors (s [waiting4new](https://github.com/waiting4new) -[FFEE_CO](https://github.com/fkx4-p) +[FFEE_CO](https://github.com/fkx4-p) ### Others Although other third-party APIs are integrated in this project, please refer to the official website of the third-party APIs for specific TOS and AUP. If you encounter IP data errors, please contact them directly to correct them. -For feedback related to corrections about IP information, we currently have two channels available: ->- [IP 错误报告汇总帖](https://github.com/sjlleo/nexttrace/issues/41) in the GITHUB ISSUES section of this project (Recommended) ->- This project's dedicated correction email: `correction@moeqing.com` (Please note that this email is only for correcting IP-related information. For other feedback, please submit an ISSUE) - -How to obtain the freshly baked binary executable of the latest commit? -> Please go to the most recent [Build & Release](https://github.com/sjlleo/nexttrace/actions/workflows/build.yml) workflow in GitHub Actions. - ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=sjlleo/nexttrace&type=Date)](https://star-history.com/#sjlleo/nexttrace&Date) diff --git a/README_zh_CN.md b/README_zh_CN.md index 9688906..b3ce6b5 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -23,9 +23,6 @@

-如果您喜欢这个项目,可以通过[爱发电支持](https://afdian.net/a/sjlleo/plan)我们项目的持续发展,您的捐助将用于服务器和 API 开支,非常感谢! - -本项目近期在进行整体重构,期间此仓库不再接受新功能需求、问题BUG反馈,如有问题请前往 [tsosunchia/nexttrace-core](https://github.com/tsosunchia/nexttrace-core) 的ISSUE区反馈,感谢您的支持! ## How To Use @@ -35,77 +32,21 @@ ### Automated Install -* Linux - * 一键安装脚本 - * Github下载 +```bash +# Linux 一键安装脚本 +bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh) - ```shell - bash -c "$(curl -Ls https://github.com/sjlleo/nexttrace/raw/main/nt_install.sh)" - ``` - * GHPROXY镜像(中国大陆使用) +# GHPROXY 镜像(国内使用) +bash <(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh) - ```shell - bash -c "$(curl -Ls https://ghproxy.com/https://github.com/sjlleo/nexttrace/raw/main/nt_install.sh)" - ``` - * Arch Linux AUR 安装命令 - * 由源码构建 +# macOS brew 安装命令 +brew tap xgadget-lab/nexttrace && brew install nexttrace +``` - ```shell - yay -S nexttrace - ``` - * 直接下载bin包(仅支持amd64) - - ```shell - yay -S nexttrace-bin` - ``` - * AUR 的2种构建分别由 huyz 和 ouuan 维护 - * Linuxbrew 安装命令 - - 同macOS Homebrew安装方法(homebrew-core版仅支持amd64) - -* macOS - * macOS Homebrew 安装命令 - * homebrew-core版 - - ```shell - brew install nexttrace - ``` - * 本仓库ACTIONS自动构建版(更新更快) - - ```shell - brew tap xgadget-lab/nexttrace && brew install xgadget-lab/nexttrace/nexttrace - ``` - * homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本 - -* Windows - * Windows Scoop 安装命令 - * scoop-extras版 - - ```powershell - scoop bucket add extras && scoop install extras/nexttrace - ``` - - * scoop-extra 由 soenggam 维护 - - 请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。 - -### Manual Install -* 下载预编译的可执行程序 - - 对于以上方法没有涵盖的用户,请直接前往 [Release](https://github.com/sjlleo/nexttrace/releases/latest) 下载编译后的二进制可执行文件。 - - * `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。 - * 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。 - -* 从源码安装 - - 您可在自行安装Go >= 1.20后,使用以下命令安装 - - ```shell - go install github.com/xgadget-lab/nexttrace@latest - ``` - 安装后可执行文件在`$GOPATH/bin`目录下,如果您没有设置`GOPATH`,则在`$HOME/go/bin`目录下。 +Windows 用户请直接前往 [Release](https://github.com/sjlleo/nexttrace/releases/latest) 下载编译后的二进制 exe 文件。 +- `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。 +- 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。 ### Get Started @@ -120,14 +61,6 @@ nexttrace http://example.com:8080/index.html?q=1 # 表格打印,使用 --table / -t 参数,将实时显示结果 nexttrace --table 1.0.0.1 -# 一个方便供机器读取转化的模式 -nexttrace --raw 1.0.0.1 -nexttrace --json 1.0.0.1 - -# 只进行IPv4/IPv6解析 -nexttrace --ipv4 g.co -nexttrace --ipv6 g.co - # IPv6 ICMP Trace nexttrace 2606:4700:4700::1111 @@ -196,9 +129,6 @@ nexttrace --first 5 --max-hops 10 www.decix.net # 关闭IP反向解析功能 nexttrace --no-rdns www.bbix.net -# 设置载荷大小为1024字节 -nexttrace --psize 1024 example.com - # 特色功能:打印Route-Path图 # Route-Path图示例: # AS6453 塔塔通信「Singapore『Singapore』」 @@ -211,61 +141,45 @@ nexttrace --psize 1024 example.com nexttrace --route-path www.time.com.my ``` -`NextTrace`支持用户自主选择 IP 数据库(目前支持:`LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`) +`NextTrace`支持用户自主选择 IP 数据库(目前支持:`LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`) ```bash -# 可以自行指定IP数据库[此处为IP-API.com],不指定则默认为LeoMoeAPI -nexttrace --data-provider ip-api.com -## 特别的: 其中 ipinfo 和 IPInsight API 对于免费版查询有频率限制,可从这些服务商自行购买服务以解除限制,如有需要可以 clone 本项目添加其提供的 token 自行编译 +# 可以自行指定IP数据库[此处为IP.SB],不指定则默认为LeoMoeAPI +nexttrace --data-provider IP.SB +## 特别的:其中 ipinfo API 需要从 ipinfo 自行购买服务,如有需要可以 clone 本项目添加其提供的 token 自行编译 ## TOKEN填写路径:ipgeo/tokens.go -## 特别的: 对于离线库 IPInfoLocal,请自行下载并命名为 ipinfoLocal.mmdb (可以从这里下载:https://ipinfo.io/signup?ref=free-database-downloads) -## 对于离线库 Ip2region 可NextTrace自动下载,也可自行下载并命名为 ip2region.db + ## 另外:由于IP.SB被滥用比较严重,会经常出现无法查询的问题,请知悉。 -## IP-API.com限制调用较为严格,如有查询不到的情况,请几分钟后再试。 - -# 纯真IP数据库默认使用 http://127.0.0.1:2060 作为查询接口,如需自定义请使用环境变量 -export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060 -## 可使用 https://github.com/freshcn/qqwry 自行搭建纯真IP数据库服务 - -# 也可以通过设置环境变量来指定默认IP数据库 -export NEXTTRACE_DATAPROVIDER=ipinfo +## IPAPI.com限制调用较为严格,如有查询不到的情况,请几分钟后再试。 ``` `NextTrace`支持使用混合参数和简略参数 ```bash Example: -nexttrace --data-provider ip-api.com --max-hops 20 --tcp --port 443 --queries 5 --no-rdns 1.1.1.1 +nexttrace --data-provider IPAPI.com --max-hops 20 --tcp --port 443 --queries 5 --no-rdns 1.1.1.1 nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:4860::8888 Equivalent to: -nexttrace -d ip-api.com -m 20 -T -p 443 -q 5 -n 1.1.1.1 +nexttrace -d IPAPI.com -m 20 -T -p 443 -q 5 -n 1.1.1.1 nexttrace -T -q 2 --parallel-requests 1 -t -R 2001:4860:4860::8888 ``` ### 全部用法详见 Usage 菜单 ```shell -Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp] - [-F|--fast-trace] [-p|--port ] [-q|--queries - ] [--parallel-requests ] [-m|--max-hops - ] [-d|--data-provider - (Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)] - [-n|--no-rdns] [-a|--always-rdns] [-P|--route-path] - [-r|--report] [--dn42] [-o|--output] [-t|--table] [--raw] - [-j|--json] [-c|--classic] [-f|--first ] [-M|--map] - [-v|--version] [-s|--source ""] [-D|--dev ""] - [-R|--route] [-z|--send-time ] [-i|--ttl-time - ] [--timeout ] [--psize ] - [_positionalArg_nexttrace_31 ""] [--dot-server - (dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language - (en|cn)] - +Usage: nexttrace [-h|--help] [-T|--tcp] [-U|--udp] [-F|--fast-trace] [-p|--port + ] [-q|--queries ] [--parallel-requests + ] [-m|--max-hops ] [-d|--data-provider + (IP.SB|IPInfo|IPInsight|IPAPI.com)] [-n|--no-rdns] + [-r|--route-path] [-o|--output] [-t|--table] [-c|--classic] + [-f|--first ] [-M|--map] [-v|--version] [-s|--source + ""] [-D|--dev ""] [-R|--route] [-z|--send-time + ] [-i|--ttl-time ] + [IP Address or Domain name] Arguments: -h --help Print help information - -4 --ipv4 Use IPv4 only - -6 --ipv6 Use IPv6 only -T --tcp Use TCP SYN for tracerouting (default port is 80) -U --udp Use UDP SYN for tracerouting (default port @@ -286,49 +200,33 @@ Arguments: 18 -m --max-hops Set the max number of hops (max TTL to be reached). Default: 30 - -d --data-provider Choose IP Geograph Data Provider [IP.SB, - IPInfo, IPInsight, IP-API.com, Ip2region, - IPInfoLocal, CHUNZHEN, disable-geoip]. - Default: LeoMoeAPI - -n --no-rdns Do not resolve IP addresses to their + -d --data-provider Choose IP Geograph Data Provider + [LeoMoeAPI,IP.SB, IPInfo, IPInsight, + IPAPI.com]. Default: LeoMoeAPI + -n --no-rdns Do not resolve IP addresses to their domain names - -a --always-rdns Always resolve IP addresses to their - domain names - -P --route-path Print traceroute hop path by ASN and + -r --route-path Print traceroute hop path by ASN and location - -r --report output using report mode - --dn42 DN42 Mode -o --output Write trace result to file (RealTimePrinter ONLY) -t --table Output trace results as table - --raw An Output Easy to Parse - -j --json Output trace results as JSON -c --classic Classic Output trace results like BestTrace -f --first Start from the first_ttl hop (instead from 1). Default: 1 - -M --map Disable Print Trace Map + -M --map Disable Print Trace Map Function -v --version Print version info and exit -s --source Use source src_addr for outgoing packets -D --dev Use the following Network Devices as the source address in outgoing packets -R --route Show Routing Table [Provided By BGP.Tools] - -z --send-time Set how many [milliseconds] between - sending each packet.. Useful when some - routers use rate-limit for ICMP messages. - Default: 100 - -i --ttl-time Set how many [milliseconds] between - sending packets groups by TTL. Useful when - some routers use rate-limit for ICMP - messages. Default: 500 - --timeout The number of [milliseconds] to keep probe - sockets open before giving up on the - connection.. Default: 1000 - --psize Set the packet size (payload size). - Default: 52 - --_positionalArg_nexttrace_31 IP Address or domain name - --dot-server Use DoT Server for DNS Parse [dnssb, - aliyun, dnspod, google, cloudflare] + -z --send-time Set the time interval for sending every + packet. Useful when some routers use + rate-limit for ICMP messages.. Default: 0 + -i --ttl-time Set the time interval for sending packets + groups by TTL. Useful when some routers + use rate-limit for ICMP messages.. + Default: 500 -g --language Choose the language for displaying [en, cn]. Default: cn ``` @@ -349,31 +247,13 @@ NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://g [GitHub - sjlleo/nexttrace-backend: NextTrace BackEnd](https://github.com/sjlleo/nexttrace-backend) -NextTrace `LeoMoeAPI`现已使用Proof of Work(POW)机制来防止滥用,其中NextTrace作为客户端引入了powclient库,POW CLIENT/SERVER均已开源,欢迎大家使用。(POW模块相关问题请发到对应的仓库) -- [GitHub - tsosunchia/powclient: Proof of Work CLIENT for NextTrace](https://github.com/tsosunchia/powclient) -- [GitHub - tsosunchia/powserver: Proof of Work SERVER for NextTrace](https://github.com/tsosunchia/powserver) - ## NextTrace Enhanced -[https://github.com/OwO-Network/nexttrace-enhanced](https://github.com/OwO-Network/nexttrace-enhanced) - -## OpenTrace - -`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。 - -该软件仍然处于早期开发阶段,可能存在许多缺陷和错误,需要您宝贵的使用反馈。 - -[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace) - -## NEXTTRACE WEB API - -`NextTraceWebApi`是一个`MTR`风格的`NextTrace`网页版服务端实现,提供了包括`Docker`在内多种部署方式。 - -[https://github.com/tsosunchia/nexttracewebapi](https://github.com/tsosunchia/nexttracewebapi) +https://github.com/OwO-Network/nexttrace-enhanced ## Credits -[BGP.TOOLS](https://bgp.tools) 提供了本项目的一些数据支持,在此表示由衷地感谢。 +BGP.TOOLS 提供了本项目的一些数据支持,在此表示由衷地感谢。 [Vincent Young](https://github.com/missuo) (i@yyt.moe) @@ -389,21 +269,10 @@ NextTrace `LeoMoeAPI`现已使用Proof of Work(POW)机制来防止滥用,其 其他第三方 API 尽管集成在本项目内,但是具体的 TOS 以及 AUP,请详见第三方 API 官网。如遇到 IP 数据错误,也请直接联系他们纠错。 -如何获取最新commit的新鲜出炉的二进制可执行文件? ->请前往GitHub Actions中最新一次 [Build & Release](https://github.com/sjlleo/nexttrace/actions/workflows/build.yml) workflow. - ## IP 数据以及精准度说明 -对于IP相关信息的纠错反馈,我们目前开放了两个渠道: ->- 本项目的GITHUB ISSUES区中的[IP 错误报告汇总帖](https://github.com/sjlleo/nexttrace/issues/41) ->- 本项目的纠错专用邮箱: `correction@moeqing.com` (请注意此邮箱仅供IP相关信息纠错专用,其他反馈请发送ISSUE) - NextTrace 有多个数据源可以选择,目前默认使用的 LeoMoeAPI 为我们项目维护的数据源。 -该项目由 OwO Network 的 [Missuo](https://github.com/missuo) && [Leo](https://github.com/sjlleo) 发起,由 [Zhshch](https://github.com/zhshch2002/) 完成最早期架构的编写和指导,后由 Leo 完成了大部分开发工作,现主要交由 [tsosunchia](https://github.com/tsosunchia) 以及 MoeQing Network 完成后续的二开和维护工作。 - -LeoMoeAPI 是 [Leo](https://github.com/sjlleo) 的作品,归属于 Leo Network,由 [Leo](https://github.com/sjlleo) 完成整套后端 API 编写,该接口未经允许不可用于任何第三方用途。 - LeoMoeAPI 早期数据主要来自 IPInsight、IPInfo,随着项目发展,越来越多的志愿者参与进了这个项目。目前 LeoMoeAPI 有近一半的数据是社区提供的,而另外一半主要来自于包含 IPInfo、IPData、BigDataCloud、IPGeoLocation 在内的多个第三方数据。 LeoMoeAPI 的骨干网数据有近 70% 是社区自发反馈又或者是项目组成员校准的,这给本项目的路由跟踪基础功能带来了一定的保证,但是全球骨干网的体量庞大,我们并无能力如 IPIP 等商业公司拥有海量监测节点,这使得 LeoMoeAPI 的数据精准度无法和形如 BestTrace(IPIP)相提并论。 diff --git a/cmd/cmd.go b/cmd/cmd.go deleted file mode 100644 index 58384b1..0000000 --- a/cmd/cmd.go +++ /dev/null @@ -1,369 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - "log" - "net" - "os" - "os/signal" - "runtime" - "strings" - "time" - - "github.com/akamensky/argparse" - "github.com/syndtr/gocapability/capability" - "github.com/xgadget-lab/nexttrace/config" - fastTrace "github.com/xgadget-lab/nexttrace/fast_trace" - "github.com/xgadget-lab/nexttrace/ipgeo" - "github.com/xgadget-lab/nexttrace/printer" - "github.com/xgadget-lab/nexttrace/reporter" - "github.com/xgadget-lab/nexttrace/trace" - "github.com/xgadget-lab/nexttrace/tracelog" - "github.com/xgadget-lab/nexttrace/tracemap" - "github.com/xgadget-lab/nexttrace/util" - "github.com/xgadget-lab/nexttrace/wshandle" -) - -func Excute() { - parser := argparse.NewParser("nexttrace", "An open source visual route tracking CLI tool") - // Create string flag - ipv4Only := parser.Flag("4", "ipv4", &argparse.Options{Help: "Use IPv4 only"}) - ipv6Only := parser.Flag("6", "ipv6", &argparse.Options{Help: "Use IPv6 only"}) - tcp := parser.Flag("T", "tcp", &argparse.Options{Help: "Use TCP SYN for tracerouting (default port is 80)"}) - udp := parser.Flag("U", "udp", &argparse.Options{Help: "Use UDP SYN for tracerouting (default port is 53)"}) - fast_trace := parser.Flag("F", "fast-trace", &argparse.Options{Help: "One-Key Fast Trace to China ISPs"}) - port := parser.Int("p", "port", &argparse.Options{Help: "Set the destination port to use. It is either initial udp port value for \"default\"" + - "method (incremented by each probe, default is 33434), or initial seq for \"icmp\" (incremented as well, default from 1), or some constant" + - "destination port for other methods (with default of 80 for \"tcp\", 53 for \"udp\", etc.)"}) - numMeasurements := parser.Int("q", "queries", &argparse.Options{Default: 3, Help: "Set the number of probes per each hop"}) - parallelRequests := parser.Int("", "parallel-requests", &argparse.Options{Default: 18, Help: "Set ParallelRequests number. It should be 1 when there is a multi-routing"}) - maxHops := parser.Int("m", "max-hops", &argparse.Options{Default: 30, Help: "Set the max number of hops (max TTL to be reached)"}) - dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI", - Help: "Choose IP Geograph Data Provider [IP.SB, IPInfo, IPInsight, IP-API.com, Ip2region, IPInfoLocal, CHUNZHEN, disable-geoip]"}) - noRdns := parser.Flag("n", "no-rdns", &argparse.Options{Help: "Do not resolve IP addresses to their domain names"}) - alwaysRdns := parser.Flag("a", "always-rdns", &argparse.Options{Help: "Always resolve IP addresses to their domain names"}) - routePath := parser.Flag("P", "route-path", &argparse.Options{Help: "Print traceroute hop path by ASN and location"}) - report := parser.Flag("r", "report", &argparse.Options{Help: "output using report mode"}) - dn42 := parser.Flag("", "dn42", &argparse.Options{Help: "DN42 Mode"}) - output := parser.Flag("o", "output", &argparse.Options{Help: "Write trace result to file (RealTimePrinter ONLY)"}) - tablePrint := parser.Flag("t", "table", &argparse.Options{Help: "Output trace results as table"}) - rawPrint := parser.Flag("", "raw", &argparse.Options{Help: "An Output Easy to Parse"}) - jsonPrint := parser.Flag("j", "json", &argparse.Options{Help: "Output trace results as JSON"}) - classicPrint := parser.Flag("c", "classic", &argparse.Options{Help: "Classic Output trace results like BestTrace"}) - beginHop := parser.Int("f", "first", &argparse.Options{Default: 1, Help: "Start from the first_ttl hop (instead from 1)"}) - disableMaptrace := parser.Flag("M", "map", &argparse.Options{Help: "Disable Print Trace Map"}) - ver := parser.Flag("v", "version", &argparse.Options{Help: "Print version info and exit"}) - srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"}) - srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"}) - router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"}) - packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 100, Help: "Set how many [milliseconds] between sending each packet.. Useful when some routers use rate-limit for ICMP messages"}) - ttlInterval := parser.Int("i", "ttl-time", &argparse.Options{Default: 500, Help: "Set how many [milliseconds] between sending packets groups by TTL. Useful when some routers use rate-limit for ICMP messages"}) - timeout := parser.Int("", "timeout", &argparse.Options{Default: 1000, Help: "The number of [milliseconds] to keep probe sockets open before giving up on the connection."}) - packetSize := parser.Int("", "psize", &argparse.Options{Default: 52, Help: "Set the packet size (payload size)"}) - str := parser.StringPositional(&argparse.Options{Help: "IP Address or domain name"}) - dot := parser.Selector("", "dot-server", []string{"dnssb", "aliyun", "dnspod", "google", "cloudflare"}, &argparse.Options{ - Help: "Use DoT Server for DNS Parse [dnssb, aliyun, dnspod, google, cloudflare]"}) - lang := parser.Selector("g", "language", []string{"en", "cn"}, &argparse.Options{Default: "cn", - Help: "Choose the language for displaying [en, cn]"}) - - err := parser.Parse(os.Args) - if err != nil { - // In case of error print error and print usage - // This can also be done by passing -h or --help flags - fmt.Print(parser.Usage(err)) - return - } - if !*jsonPrint { - printer.Version() - } - if *ver { - printer.CopyRight() - os.Exit(0) - } - - domain := *str - - if *port == 0 { - *port = 80 - } - - if *fast_trace { - var paramsFastTrace = fastTrace.ParamsFastTrace{ - SrcDev: *srcDev, - SrcAddr: *srcAddr, - BeginHop: *beginHop, - MaxHops: *maxHops, - RDns: !*noRdns, - AlwaysWaitRDNS: *alwaysRdns, - Lang: *lang, - PktSize: *packetSize, - Timeout: time.Duration(*timeout) * time.Millisecond, - } - - fastTrace.FastTest(*tcp, *output, paramsFastTrace) - if *output { - fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中") - } - - os.Exit(0) - } - - // DOMAIN处理开始 - if domain == "" { - fmt.Print(parser.Usage(err)) - return - } - - if strings.Contains(domain, "/") { - domain = strings.Split(domain, "/")[2] - } - - if strings.Contains(domain, "]") { - domain = strings.Split(strings.Split(domain, "]")[0], "[")[1] - } else if strings.Contains(domain, ":") { - if strings.Count(domain, ":") == 1 { - domain = strings.Split(domain, ":")[0] - } - } - // DOMAIN处理结束 - - capabilities_check() - // return - - var ip net.IP - - if runtime.GOOS == "windows" && (*tcp || *udp) { - fmt.Println("NextTrace 基于 Windows 的路由跟踪还在早期开发阶段,目前还存在诸多问题,TCP/UDP SYN 包请求可能不能正常运行") - } - - if *dn42 { - // 初始化配置 - config.InitConfig() - *dataOrigin = "DN42" - *disableMaptrace = true - } - - /** - * 此处若使用goroutine同时运行ws的建立与nslookup, - * 会导致第一跳的IP信息无法获取,原因不明。 - */ - //var wg sync.WaitGroup - //wg.Add(2) - // - //go func() { - // defer wg.Done() - if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" { - val, ok := os.LookupEnv("NEXTTRACE_DATAPROVIDER") - if ok { - *dataOrigin = val - } else { - w := wshandle.New() - w.Interrupt = make(chan os.Signal, 1) - signal.Notify(w.Interrupt, os.Interrupt) - defer func() { - w.Conn.Close() - }() - } - } - //}() - // - //go func() { - // defer wg.Done() - if *udp { - if *ipv6Only { - fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.") - os.Exit(0) - } - ip = util.DomainLookUp(domain, "4", *dot, *jsonPrint) - } else { - if *ipv6Only { - ip = util.DomainLookUp(domain, "6", *dot, *jsonPrint) - } else if *ipv4Only { - ip = util.DomainLookUp(domain, "4", *dot, *jsonPrint) - } else { - ip = util.DomainLookUp(domain, "all", *dot, *jsonPrint) - } - } - //}() - // - //wg.Wait() - - if *srcDev != "" { - dev, _ := net.InterfaceByName(*srcDev) - - if addrs, err := dev.Addrs(); err == nil { - for _, addr := range addrs { - if (addr.(*net.IPNet).IP.To4() == nil) == (ip.To4() == nil) { - *srcAddr = addr.(*net.IPNet).IP.String() - } - } - } - } - - if !*jsonPrint { - printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize) - } - - var m trace.Method = "" - - switch { - case *tcp: - m = trace.TCPTrace - case *udp: - m = trace.UDPTrace - default: - m = trace.ICMPTrace - } - - if !*tcp && *port == 80 { - *port = 53 - } - - var conf = trace.Config{ - DN42: *dn42, - SrcAddr: *srcAddr, - BeginHop: *beginHop, - DestIP: ip, - DestPort: *port, - MaxHops: *maxHops, - PacketInterval: *packetInterval, - TTLInterval: *ttlInterval, - NumMeasurements: *numMeasurements, - ParallelRequests: *parallelRequests, - Lang: *lang, - RDns: !*noRdns, - AlwaysWaitRDNS: *alwaysRdns, - IPGeoSource: ipgeo.GetSource(*dataOrigin), - Timeout: time.Duration(*timeout) * time.Millisecond, - PktSize: *packetSize, - } - - if !*tablePrint { - if *classicPrint { - conf.RealtimePrinter = printer.ClassicPrinter - } else if *rawPrint { - conf.RealtimePrinter = printer.EasyPrinter - } else { - if *output { - conf.RealtimePrinter = tracelog.RealtimePrinter - } else if *router { - conf.RealtimePrinter = printer.RealtimePrinterWithRouter - fmt.Println("路由表数据源由 BGP.Tools 提供,在此特表感谢") - } else { - conf.RealtimePrinter = printer.RealtimePrinter - } - } - } else { - if !*report { - conf.AsyncPrinter = printer.TracerouteTablePrinter - } - } - - if *jsonPrint { - conf.RealtimePrinter = nil - conf.AsyncPrinter = nil - } - - if util.Uninterrupted != "" && *rawPrint { - for { - _, err := trace.Traceroute(m, conf) - if err != nil { - fmt.Println(err) - } - } - } - - res, err := trace.Traceroute(m, conf) - - if err != nil { - log.Fatalln(err) - } - - if *tablePrint { - printer.TracerouteTablePrinter(res) - } - - if *routePath { - r := reporter.New(res, ip.String()) - r.Print() - } - - r, err := json.Marshal(res) - if err != nil { - fmt.Println(err) - return - } - if !*disableMaptrace && strings.ToUpper(*dataOrigin) == "LEOMOEAPI" { - url, err := tracemap.GetMapUrl(string(r)) - if err != nil { - log.Fatalln(err) - } - res.TraceMapUrl = url - if !*jsonPrint { - tracemap.PrintMapUrl(url) - } - } - r, err = json.Marshal(res) - if err != nil { - fmt.Println(err) - return - } - if *jsonPrint { - fmt.Println(string(r)) - } -} - -func capabilities_check() { - - // Windows 判断放在前面,防止遇到一些奇奇怪怪的问题 - if runtime.GOOS == "windows" { - // Running on Windows, skip checking capabilities - return - } - - uid := os.Getuid() - if uid == 0 { - // Running as root, skip checking capabilities - return - } - - /*** - * 检查当前进程是否有两个关键的权限 - ==== 看不到我 ==== - * 没办法啦 - * 自己之前承诺的坑补全篇 - * 被迫填坑系列 qwq - ==== 看不到我 ==== - ***/ - - // NewPid 已经被废弃了,这里改用 NewPid2 方法 - caps, err := capability.NewPid2(0) - if err != nil { - // 判断是否为macOS - if runtime.GOOS == "darwin" { - // macOS下报错有问题 - } else { - fmt.Println(err) - } - return - } - - // load 获取全部的 caps 信息 - err = caps.Load() - if err != nil { - fmt.Println(err) - return - } - - // 判断一下权限有木有 - if caps.Get(capability.EFFECTIVE, capability.CAP_NET_RAW) && caps.Get(capability.EFFECTIVE, capability.CAP_NET_ADMIN) { - // 有权限啦 - return - } else { - // 没权限啦 - fmt.Println("您正在以普通用户权限运行 NextTrace,但 NextTrace 未被赋予监听网络套接字的ICMP消息包、修改IP头信息(TTL)等路由跟踪所需的权限") - fmt.Println("请使用管理员用户执行 `sudo setcap cap_net_raw,cap_net_admin+eip ${your_nexttrace_path}/nexttrace` 命令,赋予相关权限后再运行~") - fmt.Println("什么?为什么 ping 普通用户执行不要 root 权限?因为这些工具在管理员安装时就已经被赋予了一些必要的权限,具体请使用 `getcap /usr/bin/ping` 查看") - } -} diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go deleted file mode 100644 index 93fd8d7..0000000 --- a/cmd/cmd_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package cmd - -import "testing" - -func TestCMD(t *testing.T) { - Excute() -} diff --git a/config/basic.go b/config/basic.go deleted file mode 100644 index 94c5fd1..0000000 --- a/config/basic.go +++ /dev/null @@ -1,5 +0,0 @@ -package config - -var Version = "v0.0.0.alpha" -var BuildDate = "" -var CommitID = "" diff --git a/config/viper.go b/config/viper.go index 3801e0b..8f31cce 100644 --- a/config/viper.go +++ b/config/viper.go @@ -27,14 +27,8 @@ func InitConfig() { err := viper.ReadInConfig() // Find and read the config file if err != nil { // Handle errors reading the config file fmt.Println("未能找到配置文件,我们将在您的运行目录为您创建 nt_config.yaml 默认配置") - err := viper.SafeWriteConfigAs("./nt_config.yaml") - if err != nil { - return - } + viper.SafeWriteConfigAs("./nt_config.yaml") } - err = viper.ReadInConfig() - if err != nil { - return - } + viper.ReadInConfig() } diff --git a/fast_trace/basic.go b/fast_trace/basic.go deleted file mode 100644 index e74d8fc..0000000 --- a/fast_trace/basic.go +++ /dev/null @@ -1,186 +0,0 @@ -package fastTrace - -type AllLocationCollection struct { - Beijing BackBoneCollection - Shanghai BackBoneCollection - Guangzhou BackBoneCollection - Hangzhou BackBoneCollection - Hefei BackBoneCollection - Changsha BackBoneCollection -} - -type BackBoneCollection struct { - Location string - CT163 ISPCollection - CTCN2 ISPCollection - CU169 ISPCollection - CU9929 ISPCollection - CM ISPCollection - CMIN2 ISPCollection - EDU ISPCollection - CST ISPCollection -} - -type ISPCollection struct { - ISPName string - IP string - IPv6 string -} - -const ( - CT163 string = "电信 163 AS4134" - CTCN2 string = "电信 CN2 AS4809" - CU169 string = "联通 169 AS4837" - CU9929 string = "联通 A网 AS9929" - CM string = "移动 骨干网 AS9808" - CMIN2 string = "移动 CMIN2 AS58807" - EDU string = "教育网 CERNET AS4538" -) - -var TestIPsCollection = AllLocationCollection{ - Beijing: Beijing, - Shanghai: Shanghai, - Guangzhou: Guangzhou, - Hangzhou: Hangzhou, - Hefei: Hefei, -} - -var Beijing = BackBoneCollection{ - Location: "北京", - CT163: ISPCollection{ - ISPName: CT163, - IP: "ipv4.pek-4134.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.pek-4134.nexttrace-io-fasttrace-endpoint.win.", - }, - - CU169: ISPCollection{ - ISPName: CU169, - IP: "ipv4.pek-4837.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.pek-4837.nexttrace-io-fasttrace-endpoint.win.", - }, - - CU9929: ISPCollection{ - ISPName: CU9929, - IP: "ipv4.pek-9929.nexttrace-io-fasttrace-endpoint.win.", - }, - - CM: ISPCollection{ - ISPName: CM, - IP: "ipv4.pek-9808.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.pek-9808.nexttrace-io-fasttrace-endpoint.win.", - }, - - CMIN2: ISPCollection{ - ISPName: CMIN2, - IP: "ipv4.pek-58807.nexttrace-io-fasttrace-endpoint.win.", - }, - - EDU: ISPCollection{ - ISPName: EDU, - IP: "ipv4.pek-4538.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.pek-4538.nexttrace-io-fasttrace-endpoint.win.", - }, -} - -var Shanghai = BackBoneCollection{ - Location: "上海", - CT163: ISPCollection{ - ISPName: CT163, - IP: "ipv4.sha-4134.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.sha-4134.nexttrace-io-fasttrace-endpoint.win.", - }, - - CTCN2: ISPCollection{ - ISPName: CTCN2, - IP: "ipv4.sha-4809.nexttrace-io-fasttrace-endpoint.win.", - }, - - CU169: ISPCollection{ - ISPName: CU169, - IP: "ipv4.sha-4837.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.sha-4837.nexttrace-io-fasttrace-endpoint.win.", - }, - - CU9929: ISPCollection{ - ISPName: CU9929, - IP: "ipv4.sha-9929.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.sha-9929.nexttrace-io-fasttrace-endpoint.win.", - }, - - CM: ISPCollection{ - ISPName: CM, - IP: "ipv4.sha-9808.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.sha-9808.nexttrace-io-fasttrace-endpoint.win.", - }, - - CMIN2: ISPCollection{ - ISPName: CMIN2, - IP: "ipv4.sha-58807.nexttrace-io-fasttrace-endpoint.win.", - }, - - EDU: ISPCollection{ - ISPName: EDU, - IP: "ipv4.sha-4538.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.sha-4538.nexttrace-io-fasttrace-endpoint.win.", - }, -} - -var Guangzhou = BackBoneCollection{ - Location: "广州", - CT163: ISPCollection{ - ISPName: CT163, - IP: "ipv4.can-4134.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.can-4134.nexttrace-io-fasttrace-endpoint.win.", - }, - - CU169: ISPCollection{ - ISPName: CU169, - IP: "ipv4.can-4837.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.can-4837.nexttrace-io-fasttrace-endpoint.win.", - }, - - CM: ISPCollection{ - ISPName: CM, - IP: "ipv4.can-9808.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.can-9808.nexttrace-io-fasttrace-endpoint.win.", - }, -} - -var Hangzhou = BackBoneCollection{ - Location: "杭州", - CT163: ISPCollection{ - ISPName: CT163, - IP: "ipv4.hgh-4134.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.hgh-4134.nexttrace-io-fasttrace-endpoint.win.", - }, - CU169: ISPCollection{ - ISPName: CU169, - IP: "ipv4.hgh-4837.nexttrace-io-fasttrace-endpoint.win.", - }, - CM: ISPCollection{ - ISPName: CM, - IP: "ipv4.hgh-9808.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.hgh-9808.nexttrace-io-fasttrace-endpoint.win.", - }, - // 浙江大学 教育网 - EDU: ISPCollection{ - ISPName: EDU, - IP: "ipv4.hgh-4538.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.hgh-4538.nexttrace-io-fasttrace-endpoint.win.", - }, -} - -var Hefei = BackBoneCollection{ - Location: "合肥", - // 中国科学技术大学 教育网 - EDU: ISPCollection{ - ISPName: EDU, - IP: "ipv4.hef-4538.nexttrace-io-fasttrace-endpoint.win.", - IPv6: "ipv6.hef-4538.nexttrace-io-fasttrace-endpoint.win.", - }, - // 中国科学技术大学 科技网 - CST: ISPCollection{ - ISPName: "中国科学技术大学 科技网 AS7497", - IP: "ipv4.hef-7497.nexttrace-io-fasttrace-endpoint.win.", - }, -} diff --git a/fast_trace/fast_trace ipv6.go b/fast_trace/fast_trace ipv6.go deleted file mode 100644 index fb08fcf..0000000 --- a/fast_trace/fast_trace ipv6.go +++ /dev/null @@ -1,150 +0,0 @@ -package fastTrace - -import ( - "fmt" - "github.com/xgadget-lab/nexttrace/ipgeo" - "github.com/xgadget-lab/nexttrace/printer" - "github.com/xgadget-lab/nexttrace/trace" - "github.com/xgadget-lab/nexttrace/tracelog" - "github.com/xgadget-lab/nexttrace/util" - "github.com/xgadget-lab/nexttrace/wshandle" - "log" - "os" - "os/signal" -) - -var pFastTracer ParamsFastTrace - -func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) { - fp, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm) - if err != nil { - return - } - defer func(fp *os.File) { - err := fp.Close() - if err != nil { - log.Fatal(err) - } - }(fp) - - log.SetOutput(fp) - log.SetFlags(0) - fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX) - log.Printf("『%s %s 』\n", location, ispCollection.ISPName) - fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, pFastTracer.MaxHops, pFastTracer.PktSize) - log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, pFastTracer.MaxHops, pFastTracer.PktSize) - ip := util.DomainLookUp(ispCollection.IPv6, "6", "", true) - var conf = trace.Config{ - BeginHop: pFastTracer.BeginHop, - DestIP: ip, - DestPort: 80, - MaxHops: pFastTracer.MaxHops, - NumMeasurements: 3, - ParallelRequests: 18, - RDns: pFastTracer.RDns, - AlwaysWaitRDNS: pFastTracer.AlwaysWaitRDNS, - PacketInterval: 100, - TTLInterval: 500, - IPGeoSource: ipgeo.GetSource("LeoMoeAPI"), - Timeout: pFastTracer.Timeout, - PktSize: pFastTracer.PktSize, - Lang: pFastTracer.Lang, - } - - if oe { - conf.RealtimePrinter = tracelog.RealtimePrinter - } else { - conf.RealtimePrinter = printer.RealtimePrinter - } - - _, err = trace.Traceroute(f.TracerouteMethod, conf) - - if err != nil { - log.Fatal(err) - } - - println() -} - -func (f *FastTracer) testAll_v6() { - f.testCT_v6() - println() - f.testCU_v6() - println() - f.testCM_v6() - println() - f.testEDU_v6() -} - -func (f *FastTracer) testCT_v6() { - f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163) - f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163) - f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163) - f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163) -} - -func (f *FastTracer) testCU_v6() { - f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169) - f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169) - f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929) - f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169) -} - -func (f *FastTracer) testCM_v6() { - f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM) - f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM) - f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM) - f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM) -} - -func (f *FastTracer) testEDU_v6() { - f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU) - f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU) - f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU) -} - -func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) { - var c string - - oe = outEnable - pFastTracer = paramsFastTrace - - fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网") - fmt.Print("请选择选项:") - _, err := fmt.Scanln(&c) - if err != nil { - c = "1" - } - - ft := FastTracer{} - - // 建立 WebSocket 连接 - w := wshandle.New() - w.Interrupt = make(chan os.Signal, 1) - signal.Notify(w.Interrupt, os.Interrupt) - defer func() { - w.Conn.Close() - }() - - if !tm { - ft.TracerouteMethod = trace.ICMPTrace - fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数") - } else { - ft.TracerouteMethod = trace.TCPTrace - } - - switch c { - case "1": - ft.testAll_v6() - case "2": - ft.testCT_v6() - case "3": - ft.testCU_v6() - case "4": - ft.testCM_v6() - case "5": - ft.testEDU_v6() - default: - ft.testAll_v6() - } -} diff --git a/fast_trace/fast_trace.go b/fast_trace/fast_trace.go deleted file mode 100644 index 729185c..0000000 --- a/fast_trace/fast_trace.go +++ /dev/null @@ -1,199 +0,0 @@ -package fastTrace - -import ( - "fmt" - "github.com/xgadget-lab/nexttrace/ipgeo" - "github.com/xgadget-lab/nexttrace/printer" - "github.com/xgadget-lab/nexttrace/trace" - "github.com/xgadget-lab/nexttrace/tracelog" - "github.com/xgadget-lab/nexttrace/util" - "github.com/xgadget-lab/nexttrace/wshandle" - "log" - "net" - "os" - "os/signal" - "time" -) - -type FastTracer struct { - TracerouteMethod trace.Method - ParamsFastTrace ParamsFastTrace -} - -type ParamsFastTrace struct { - SrcDev string - SrcAddr string - BeginHop int - MaxHops int - RDns bool - AlwaysWaitRDNS bool - Lang string - PktSize int - Timeout time.Duration -} - -var oe = false - -func (f *FastTracer) tracert(location string, ispCollection ISPCollection) { - fp, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm) - if err != nil { - return - } - defer func(fp *os.File) { - err := fp.Close() - if err != nil { - log.Fatal(err) - } - }(fp) - - log.SetOutput(fp) - log.SetFlags(0) - fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX) - log.Printf("『%s %s 』\n", location, ispCollection.ISPName) - fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize) - log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize) - ip := util.DomainLookUp(ispCollection.IP, "4", "", true) - var conf = trace.Config{ - BeginHop: f.ParamsFastTrace.BeginHop, - DestIP: ip, - DestPort: 80, - MaxHops: f.ParamsFastTrace.MaxHops, - NumMeasurements: 3, - ParallelRequests: 18, - RDns: f.ParamsFastTrace.RDns, - AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS, - PacketInterval: 100, - TTLInterval: 500, - IPGeoSource: ipgeo.GetSource("LeoMoeAPI"), - Timeout: f.ParamsFastTrace.Timeout, - SrcAddr: f.ParamsFastTrace.SrcAddr, - PktSize: f.ParamsFastTrace.PktSize, - Lang: f.ParamsFastTrace.Lang, - } - - if oe { - conf.RealtimePrinter = tracelog.RealtimePrinter - } else { - conf.RealtimePrinter = printer.RealtimePrinter - } - - _, err = trace.Traceroute(f.TracerouteMethod, conf) - - if err != nil { - log.Fatal(err) - } - println() -} - -func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) { - var c string - pFastTrace := paramsFastTrace - oe = outEnable - fmt.Println("Hi,欢迎使用 Fast Trace 功能,请注意 Fast Trace 功能只适合新手使用\n因为国内网络复杂,我们设置的测试目标有限,建议普通用户自测以获得更加精准的路由情况") - fmt.Println("请您选择要测试的 IP 类型\n1. IPv4\n2. IPv6") - fmt.Print("请选择选项:") - _, err := fmt.Scanln(&c) - if err != nil { - c = "1" - } - if c == "2" { - FastTestv6(tm, outEnable, paramsFastTrace) - return - } - - if pFastTrace.SrcDev != "" { - dev, _ := net.InterfaceByName(pFastTrace.SrcDev) - if addrs, err := dev.Addrs(); err == nil { - for _, addr := range addrs { - if addr.(*net.IPNet).IP.To4() != nil { - pFastTrace.SrcAddr = addr.(*net.IPNet).IP.String() - } - } - } - } - - fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网") - fmt.Print("请选择选项:") - _, err = fmt.Scanln(&c) - if err != nil { - c = "1" - } - - ft := FastTracer{ - ParamsFastTrace: pFastTrace, - } - - // 建立 WebSocket 连接 - w := wshandle.New() - w.Interrupt = make(chan os.Signal, 1) - signal.Notify(w.Interrupt, os.Interrupt) - defer func() { - w.Conn.Close() - }() - - if !tm { - ft.TracerouteMethod = trace.ICMPTrace - fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数") - } else { - ft.TracerouteMethod = trace.TCPTrace - } - - switch c { - case "1": - ft.testAll() - case "2": - ft.testCT() - case "3": - ft.testCU() - case "4": - ft.testCM() - case "5": - ft.testEDU() - default: - ft.testAll() - } -} - -func (f *FastTracer) testAll() { - f.testCT() - println() - f.testCU() - println() - f.testCM() - println() - f.testEDU() -} - -func (f *FastTracer) testCT() { - f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CTCN2) - f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163) - f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163) -} - -func (f *FastTracer) testCU() { - f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169) - f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU9929) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929) - f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169) - f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169) -} - -func (f *FastTracer) testCM() { - f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM) - f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CMIN2) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CMIN2) - f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM) - f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM) -} - -func (f *FastTracer) testEDU() { - f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU) - f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU) - f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU) - // 科技网暂时算在EDU里面,等拿到了足够多的数据再分离出去,单独用于测试 - f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.CST) -} diff --git a/fast_trace/fast_trace_test.go b/fast_trace/fast_trace_test.go deleted file mode 100644 index bb55b11..0000000 --- a/fast_trace/fast_trace_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package fastTrace - -import ( - "fmt" - "os" - "os/signal" - "testing" - - "github.com/xgadget-lab/nexttrace/trace" - "github.com/xgadget-lab/nexttrace/wshandle" -) - -func TestTrace(t *testing.T) { - pFastTrace := ParamsFastTrace{ - SrcDev: "", - SrcAddr: "", - BeginHop: 1, - MaxHops: 30, - RDns: false, - AlwaysWaitRDNS: false, - Lang: "", - PktSize: 52, - } - ft := FastTracer{ParamsFastTrace: pFastTrace} - // 建立 WebSocket 连接 - w := wshandle.New() - w.Interrupt = make(chan os.Signal, 1) - signal.Notify(w.Interrupt, os.Interrupt) - defer func() { - w.Conn.Close() - }() - fmt.Println("TCP v4") - ft.TracerouteMethod = trace.TCPTrace - //ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU) - //fmt.Println("TCP v6") - //ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU) - //fmt.Println("ICMP v4") - //ft.TracerouteMethod = trace.ICMPTrace - //ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU) - //fmt.Println("ICMP v6") - //ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU) -} diff --git a/go.mod b/go.mod index 71a32a1..b37be71 100644 --- a/go.mod +++ b/go.mod @@ -5,42 +5,37 @@ go 1.20 require ( github.com/akamensky/argparse v1.4.0 github.com/google/gopacket v1.1.19 - github.com/oschwald/maxminddb-golang v1.10.0 - github.com/spf13/viper v1.16.0 - github.com/stretchr/testify v1.8.4 + github.com/spf13/viper v1.15.0 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 - github.com/tsosunchia/powclient v0.1.3 - golang.org/x/net v0.10.0 - golang.org/x/sync v0.2.0 + golang.org/x/net v0.7.0 + golang.org/x/sync v0.1.0 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/text v0.7.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) require ( - github.com/fatih/color v1.15.0 + github.com/fatih/color v1.14.1 github.com/gorilla/websocket v1.5.0 github.com/lionsoul2014/ip2region v2.11.1+incompatible github.com/rodaine/table v1.1.0 github.com/tidwall/gjson v1.14.4 github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.5.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6df12e2..2d2cece 100644 --- a/go.sum +++ b/go.sum @@ -57,9 +57,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -136,7 +136,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -147,16 +147,14 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= -github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -165,17 +163,17 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4= github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -186,9 +184,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= @@ -200,8 +197,6 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tsosunchia/powclient v0.1.3 h1:L29HQdyZZ0Vcrn0ZzNIyrTpLOnycm3oHF46sYJ+vkhQ= -github.com/tsosunchia/powclient v0.1.3/go.mod h1:Pm4MP3QqN74SfNskPpFIEyT+NQrcABGoXkkeRwjlMEE= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -218,7 +213,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -283,9 +278,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -305,8 +299,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -339,14 +333,12 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -354,10 +346,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/ipgeo/chunzhen.go b/ipgeo/chunzhen.go deleted file mode 100644 index 2e4a711..0000000 --- a/ipgeo/chunzhen.go +++ /dev/null @@ -1,91 +0,0 @@ -package ipgeo - -import ( - "encoding/json" - "github.com/xgadget-lab/nexttrace/util" - "io" - "log" - "net/http" - "strings" - "time" -) - -func Chunzhen(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) { - url := util.GetenvDefault("NEXTTRACE_CHUNZHENURL", "http://127.0.0.1:2060") + "?ip=" + ip - client := &http.Client{ - // 2 秒超时 - Timeout: timeout, - } - req, _ := http.NewRequest("GET", url, nil) - content, err := client.Do(req) - if err != nil { - log.Println("纯真 请求超时(2s),请切换其他API使用") - return &IPGeoData{}, err - } - body, _ := io.ReadAll(content.Body) - - var data map[string]interface{} - err = json.Unmarshal(body, &data) - if err != nil { - return &IPGeoData{}, err - } - city := data[ip].(map[string]interface{})["area"].(string) - region := data[ip].(map[string]interface{})["country"].(string) - var asn string - if data[ip].(map[string]interface{})["asn"] != nil { - asn = data[ip].(map[string]interface{})["asn"].(string) - } - // 判断是否前两个字为香港或台湾 - var country string - provinces := []string{ - "北京", - "天津", - "河北", - "山西", - "内蒙古", - "辽宁", - "吉林", - "黑龙江", - "上海", - "江苏", - "浙江", - "安徽", - "福建", - "江西", - "山东", - "河南", - "湖北", - "湖南", - "广东", - "广西", - "海南", - "重庆", - "四川", - "贵州", - "云南", - "西藏", - "陕西", - "甘肃", - "青海", - "宁夏", - "新疆", - "台湾", - "香港", - "澳门", - } - for _, province := range provinces { - if strings.Contains(region, province) { - country = "中国" - city = region + city - break - } - } - if country == "" { - country = region - } - return &IPGeoData{ - Asnumber: asn, - Country: country, - City: city, - }, nil -} diff --git a/ipgeo/dn42.go b/ipgeo/dn42.go deleted file mode 100644 index 6c0355e..0000000 --- a/ipgeo/dn42.go +++ /dev/null @@ -1,63 +0,0 @@ -package ipgeo - -import ( - "strings" - "time" - - "github.com/xgadget-lab/nexttrace/dn42" -) - -func LtdCodeToCountryOrAreaName(Code string) string { - countryName := []string{"United States", "Afghanistan", "Åland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", " Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada ", "Cape Verde", "Cayman Islands", "Central Africa", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo (Brazzaville)", "DRC", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", " Denmark", "Djibouti", "Dominica", "Dominica", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", " French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Vatican", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "British Isles of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "North Korea", "South Korea", " Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "FYROM", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", " Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia (Federated States of)", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana", "Norway", "Oman", "Pakistan", "Palau", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "St. Helena", "St. Kitts and Nevis", "St. Lucia", "St. Pierre and Miquelon", "St. Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen Islands", "Swaziland", "Sweden ", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", " United Kingdom", "U.S. Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "British Virgin Islands", "U.S. Virgin Islands", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"} - countryCode := []string{"us", "af", "ax", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "au", "at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", "bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi", "kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cn", "cx", "cc", "co", "km", "cg", "cd", "ck", "cr", "ci", "hr", "cu", "cy", "cz", "dk", "dj", "dm", "do", "ec", "eg", "sv", "gq", "er", "ee", "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg", "gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in", "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", "kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", "mn", "me", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", "ro", "ru", "rw", "sh", "kn", "lc", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gs", "es", "lk", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", "tz", "th", "tl", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "gb", "um", "uy", "uz", "vu", "ve", "vn", "vg", "vi", "wf", "eh", "ye", "zm", "zw"} - Code = strings.ToLower(Code) - for i, v := range countryCode { - if strings.Contains(Code, v) { - return countryName[i] - } - } - return Code -} - -func DN42(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) { - data := &IPGeoData{} - // 先解析传入过来的数据 - ipTmp := strings.Split(ip, ",") - if len(ipTmp) > 1 { - ip = ipTmp[0] - } - // 先查找 GeoFeed - if geo, find := dn42.GetGeoFeed(ip); find { - data.Country = geo.LtdCode - data.City = geo.City - data.Asnumber = geo.ASN - data.Whois = geo.IPWhois - } - // 如果没找到,查找 PTR - if len(ipTmp) > 1 { - // 存在 PTR 记录 - if res, err := dn42.FindPtrRecord(ipTmp[1]); err == nil && res.LtdCode != "" { - data.Country = res.LtdCode - data.Prov = res.Region - data.City = res.City - } - } - - data.Country = LtdCodeToCountryOrAreaName(data.Country) - - switch data.Country { - case "Hong Kong": - data.Country = "China" - data.Prov = "Hong Kong" - case "Taiwan": - data.Country = "China" - data.Prov = "Taiwan" - case "Macao": - data.Country = "China" - data.Prov = "Macao" - case "": - data.Country = "Unknown" - } - - return data, nil -} diff --git a/ipgeo/dn42_test.go b/ipgeo/dn42_test.go deleted file mode 100644 index 0ebc61d..0000000 --- a/ipgeo/dn42_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package ipgeo - -// func TestDN42(t *testing.T) { -// DN42("") -// } diff --git a/ipgeo/ip2region.go b/ipgeo/ip2region.go deleted file mode 100644 index 2e335b7..0000000 --- a/ipgeo/ip2region.go +++ /dev/null @@ -1,78 +0,0 @@ -package ipgeo - -import ( - "errors" - "fmt" - "io" - "net/http" - "os" - "time" - - "github.com/lionsoul2014/ip2region/v1.0/binding/golang/ip2region" -) - -const ( - ipDataBasePath = "./ip2region.db" - defaultDownURL = "1" - originURL = "https://ghproxy.com/?q=https://github.com/bqf9979/ip2region/blob/master/data/ip2region.db?raw=true" -) - -func downloadDataBase() error { - fmt.Println("Downloading DataBase...") - resp, err := http.Get(originURL) - if err != nil { - return err - } - defer resp.Body.Close() - - // Create the file - out, err := os.Create(ipDataBasePath) - if err != nil { - return err - } - defer out.Close() - - // Write the body to file - _, err = io.Copy(out, resp.Body) - return err -} - -func IP2Region(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) { - if _, err := os.Stat(ipDataBasePath); os.IsNotExist(err) { - if err = downloadDataBase(); err != nil { - panic("Download Failed!") - } - } - region, err := ip2region.New(ipDataBasePath) - if err != nil { - panic("Cannot find ip2region.db") - } - defer region.Close() - info, searchErr := region.MemorySearch(ip) - if searchErr != nil { - return &IPGeoData{}, errors.New("no results") - } - - if info.Country == "0" { - info.Country = "" - } - - if info.Province == "0" { - info.Province = "" - } - - if info.City == "0" { - info.City = "" - } - - if info.ISP == "0" { - info.ISP = "" - } - - return &IPGeoData{ - Owner: info.ISP, - Country: info.Country, - Prov: info.Province, - City: info.City, - }, nil -} diff --git a/ipgeo/ipapicom.go b/ipgeo/ipapicom.go deleted file mode 100644 index 131e9bf..0000000 --- a/ipgeo/ipapicom.go +++ /dev/null @@ -1,43 +0,0 @@ -package ipgeo - -import ( - "errors" - "io" - "log" - "net/http" - "regexp" - "time" - - "github.com/tidwall/gjson" -) - -func IPApiCom(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) { - url := "http://ip-api.com/json/" + ip + "?fields=status,message,country,regionName,city,isp,as" - client := &http.Client{ - // 2 秒超时 - Timeout: timeout, - } - req, _ := http.NewRequest("GET", url, nil) - req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0") - content, err := client.Do(req) - if err != nil { - log.Println("ip-api.com 请求超时(2s),请切换其他API使用") - return nil, err - } - body, _ := io.ReadAll(content.Body) - res := gjson.ParseBytes(body) - - if res.Get("status").String() != "success" { - return &IPGeoData{}, errors.New("超过API阈值") - } - - re := regexp.MustCompile("[0-9]+") - - return &IPGeoData{ - Asnumber: re.FindString(res.Get("as").String()), - Country: res.Get("country").String(), - City: res.Get("city").String(), - Prov: res.Get("regionName").String(), - Owner: res.Get("isp").String(), - }, nil -} diff --git a/ipgeo/ipfilter.go b/ipgeo/ipfilter.go deleted file mode 100644 index b7aa5b1..0000000 --- a/ipgeo/ipfilter.go +++ /dev/null @@ -1,86 +0,0 @@ -package ipgeo - -import ( - "net" -) - -func cidrRangeContains(cidrRange string, checkIP string) bool { - _, ipNet, err := net.ParseCIDR(cidrRange) - if err != nil { - return false - } - secondIP := net.ParseIP(checkIP) - return ipNet.Contains(secondIP) -} - -// Filter 被选到的返回 geodata, true 否则返回 nil, false -func Filter(ip string) (*IPGeoData, bool) { - //geodata := &IPGeoData{} - asn := "" - whois := "" - isFiltered := false - switch { - //rfc1918 - case net.ParseIP(ip).IsPrivate(): - asn = "" - whois = "RFC1918" - isFiltered = true - //IANA Reserved Address Space - case cidrRangeContains("100.64.0.0/10", ip): - asn = "" - whois = "RFC6598" - isFiltered = true - case cidrRangeContains("198.18.0.0/15", ip): - asn = "" - whois = "RFC2544" - isFiltered = true - case cidrRangeContains("198.51.100.0/24", ip): - fallthrough - case cidrRangeContains("203.0.113.0/24", ip): - asn = "" - whois = "RFC5737" - isFiltered = true - case cidrRangeContains("240.0.0.0/4", ip): - asn = "" - whois = "RFC1112" - isFiltered = true - //Defense Information System Network - case cidrRangeContains("6.0.0.0/8", ip): - fallthrough - case cidrRangeContains("7.0.0.0/8", ip): - fallthrough - case cidrRangeContains("11.0.0.0/8", ip): - fallthrough - case cidrRangeContains("21.0.0.0/8", ip): - fallthrough - case cidrRangeContains("22.0.0.0/8", ip): - fallthrough - case cidrRangeContains("26.0.0.0/8", ip): - fallthrough - case cidrRangeContains("28.0.0.0/8", ip): - fallthrough - case cidrRangeContains("29.0.0.0/8", ip): - fallthrough - case cidrRangeContains("30.0.0.0/8", ip): - fallthrough - case cidrRangeContains("33.0.0.0/8", ip): - fallthrough - case cidrRangeContains("55.0.0.0/8", ip): - fallthrough - case cidrRangeContains("214.0.0.0/8", ip): - fallthrough - case cidrRangeContains("215.0.0.0/8", ip): - asn = "" - whois = "DOD" - isFiltered = true - default: - } - if !isFiltered { - return nil, false - } else { - return &IPGeoData{ - Asnumber: asn, - Whois: whois, - }, true - } -} diff --git a/ipgeo/ipgeo.go b/ipgeo/ipgeo.go deleted file mode 100644 index ec6923e..0000000 --- a/ipgeo/ipgeo.go +++ /dev/null @@ -1,62 +0,0 @@ -package ipgeo - -import ( - "strings" - "time" -) - -type IPGeoData struct { - IP string `json:"ip"` - Asnumber string `json:"asnumber"` - Country string `json:"country"` - CountryEn string `json:"country_en"` - Prov string `json:"prov"` - ProvEn string `json:"prov_en"` - City string `json:"city"` - CityEn string `json:"city_en"` - District string `json:"district"` - Owner string `json:"owner"` - Isp string `json:"isp"` - Domain string `json:"domain"` - Whois string `json:"whois"` - Lat float64 `json:"lat"` - Lng float64 `json:"lng"` - Prefix string `json:"prefix"` - Router map[string][]string `json:"router"` - Source string `json:"source"` -} - -type Source = func(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error) - -func GetSource(s string) Source { - switch strings.ToUpper(s) { - case "DN42": - return DN42 - case "LEOMOEAPI": - return LeoIP - case "IP.SB": - return IPSB - case "IPINSIGHT": - return IPInSight - case "IPAPI.COM": - return IPApiCom - case "IP-API.COM": - return IPApiCom - case "IPINFO": - return IPInfo - case "IP2REGION": - return IP2Region - case "IPINFOLOCAL": - return IPInfoLocal - case "CHUNZHEN": - return Chunzhen - case "DISABLE-GEOIP": - return disableGeoIP - default: - return LeoIP - } -} - -func disableGeoIP(string, time.Duration, string, bool) (*IPGeoData, error) { - return &IPGeoData{}, nil -} diff --git a/ipgeo/ipgeo_test.go b/ipgeo/ipgeo_test.go deleted file mode 100644 index c496c88..0000000 --- a/ipgeo/ipgeo_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package ipgeo - -import ( - "errors" - "fmt" - "log" - "os" - "strconv" - "testing" -) - -// import ( -// "testing" - -// "github.com/stretchr/testify/assert" -// ) - -func TestXxx(t *testing.T) { - const ID_FIXED_HEADER = "10" - var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位 - var ttl = fmt.Sprintf("%06b", 95) //取TTL的后6位 - fmt.Println(os.Getpid()&0x7f, 95) - - var parity int - id := ID_FIXED_HEADER + processID + ttl - for _, c := range id { - if c == '1' { - parity++ - } - } - if parity%2 == 0 { - id += "1" - } else { - id += "0" - } - process_id, ttl_r, _ := reverseID(id) - log.Println(process_id, ttl_r) -} - -func reverseID(id string) (int64, int64, error) { - ttl, _ := strconv.ParseInt(id[9:15], 2, 32) - //process ID - processID, _ := strconv.ParseInt(id[2:9], 2, 32) - - parity := 0 - for i := 0; i < len(id)-1; i++ { - if id[i] == '1' { - parity++ - } - } - - if parity%2 == 1 { - if id[len(id)-1] == '0' { - fmt.Println("Parity check passed.") - } else { - fmt.Println("Parity check failed.") - return 0, 0, errors.New("err") - } - } else { - if id[len(id)-1] == '1' { - fmt.Println("Parity check passed.") - } else { - fmt.Println("Parity check failed.") - return 0, 0, errors.New("err") - } - } - return processID, ttl, nil -} - -// func TestLeoIP(t *testing.T) { -// // res, err := LeoIP("1.1.1.1") -// // assert.Nil(t, err) -// // assert.NotNil(t, res) -// // assert.NotEmpty(t, res.Asnumber) -// // assert.NotEmpty(t, res.Isp) -// } - -// func TestIPSB(t *testing.T) { -// // Not available -// //res, err := IPSB("1.1.1.1") -// //assert.Nil(t, err) -// //assert.NotNil(t, res) -// //assert.NotEmpty(t, res.Asnumber) -// //assert.NotEmpty(t, res.Isp) -// } - -// func TestIPInfo(t *testing.T) { -// res, err := IPInfo("1.1.1.1") -// assert.Nil(t, err) -// assert.NotNil(t, res) -// // assert.NotEmpty(t, res.Country) -// assert.NotEmpty(t, res.City) -// assert.NotEmpty(t, res.Prov) -// } - -// func TestIPInSight(t *testing.T) { -// // res, err := IPInSight("1.1.1.1") -// // assert.Nil(t, err) -// // assert.NotNil(t, res) -// // assert.NotEmpty(t, res.Country) -// // assert.NotEmpty(t, res.Prov) -// // 这个库有时候不提供城市信息,返回值为"" -// //assert.NotEmpty(t, res.City) -// } - -// func TestIPApiCom(t *testing.T) { -// res, err := IPApiCom("1.1.1.1") -// assert.Nil(t, err) -// assert.NotNil(t, res) -// assert.NotEmpty(t, res.Country) -// assert.NotEmpty(t, res.City) -// assert.NotEmpty(t, res.Prov) -// } diff --git a/ipgeo/ipinfo.go b/ipgeo/ipinfo.go deleted file mode 100644 index 0c745a6..0000000 --- a/ipgeo/ipinfo.go +++ /dev/null @@ -1,310 +0,0 @@ -package ipgeo - -import ( - "io" - "net/http" - "strings" - "time" - - "github.com/tidwall/gjson" -) - -func IPInfo(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) { - url := "https://ipinfo.io/" + ip + "?token=" + token.ipinfo - client := &http.Client{ - // 2 秒超时 - Timeout: timeout, - } - resp, err := client.Get(url) - //resp, err := http.Get("https://ipinfo.io/" + ip + "?token=" + token.ipinfo) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - res := gjson.ParseBytes(body) - - var country string - country = res.Get("country").String() - if res.Get("country").String() == "HK" || res.Get("country").String() == "TW" { - country = "CN" - } - // ISO-3166 转换 - var countryMap = map[string]string{ - "AF": "Afghanistan", - "AX": "Åland Islands", - "AL": "Albania", - "DZ": "Algeria", - "AS": "American Samoa", - "AD": "Andorra", - "AO": "Angola", - "AI": "Anguilla", - "AQ": "Antarctica", - "AG": "Antigua and Barbuda", - "AR": "Argentina", - "AM": "Armenia", - "AW": "Aruba", - "AU": "Australia", - "AT": "Austria", - "AZ": "Azerbaijan", - "BH": "Bahrain", - "BS": "Bahamas", - "BD": "Bangladesh", - "BB": "Barbados", - "BY": "Belarus", - "BE": "Belgium", - "BZ": "Belize", - "BJ": "Benin", - "BM": "Bermuda", - "BT": "Bhutan", - "BO": "Bolivia", - "BQ": "Bonaire", - "BA": "Bosnia and Herzegovina", - "BW": "Botswana", - "BV": "Bouvet Island", - "BR": "Brazil", - "IO": "British Indian Ocean Territory", - "BN": "Brunei Darussalam", - "BG": "Bulgaria", - "BF": "Burkina Faso", - "BI": "Burundi", - "KH": "Cambodia", - "CM": "Cameroon", - "CA": "Canada", - "CV": "Cape Verde", - "KY": "Cayman Islands", - "CF": "Central African Republic", - "TD": "Chad", - "CL": "Chile", - "CN": "China", - "CX": "Christmas Island", - "CC": "Cocos (Keeling) Islands", - "CO": "Colombia", - "KM": "Comoros", - "CG": "Congo", - "CD": "Congo", - "CK": "Cook Islands", - "CR": "Costa Rica", - "CI": "Côte d'Ivoire", - "HR": "Croatia", - "CU": "Cuba", - "CW": "Curaçao", - "CY": "Cyprus", - "CZ": "Czech Republic", - "DK": "Denmark", - "DJ": "Djibouti", - "DM": "Dominica", - "DO": "Dominican Republic", - "EC": "Ecuador", - "EG": "Egypt", - "SV": "El Salvador", - "GQ": "Equatorial Guinea", - "ER": "Eritrea", - "EE": "Estonia", - "ET": "Ethiopia", - "FK": "Falkland Islands (Malvinas)", - "FO": "Faroe Islands", - "FJ": "Fiji", - "FI": "Finland", - "FR": "France", - "GF": "French Guiana", - "PF": "French Polynesia", - "TF": "French Southern Territories", - "GA": "Gabon", - "GM": "Gambia", - "GE": "Georgia", - "DE": "Germany", - "GH": "Ghana", - "GI": "Gibraltar", - "GR": "Greece", - "GL": "Greenland", - "GD": "Grenada", - "GP": "Guadeloupe", - "GU": "Guam", - "GT": "Guatemala", - "GG": "Guernsey", - "GN": "Guinea", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HT": "Haiti", - "HM": "Heard Island and McDonald Islands", - "VA": "Holy See (Vatican City State)", - "HN": "Honduras", - "HK": "Hong Kong", - "HU": "Hungary", - "IS": "Iceland", - "IN": "India", - "ID": "Indonesia", - "IR": "Iran", - "IQ": "Iraq", - "IE": "Ireland", - "IM": "Isle of Man", - "IL": "Israel", - "IT": "Italy", - "JM": "Jamaica", - "JP": "Japan", - "JE": "Jersey", - "JO": "Jordan", - "KZ": "Kazakhstan", - "KE": "Kenya", - "KI": "Kiribati", - "KP": "Korea", - "KR": "Korea", - "KW": "Kuwait", - "KG": "Kyrgyzstan", - "LA": "Lao People's Democratic Republic", - "LV": "Latvia", - "LB": "Lebanon", - "LS": "Lesotho", - "LR": "Liberia", - "LY": "Libya", - "LI": "Liechtenstein", - "LT": "Lithuania", - "LU": "Luxembourg", - "MO": "Macao", - "MK": "Macedonia", - "MG": "Madagascar", - "MW": "Malawi", - "MY": "Malaysia", - "MV": "Maldives", - "ML": "Mali", - "MT": "Malta", - "MH": "Marshall Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MU": "Mauritius", - "YT": "Mayotte", - "MX": "Mexico", - "FM": "Micronesia", - "MD": "Moldova", - "MC": "Monaco", - "MN": "Mongolia", - "ME": "Montenegro", - "MS": "Montserrat", - "MA": "Morocco", - "MZ": "Mozambique", - "MM": "Myanmar", - "NA": "Namibia", - "NR": "Nauru", - "NP": "Nepal", - "NL": "Netherlands", - "NC": "New Caledonia", - "NZ": "New Zealand", - "NI": "Nicaragua", - "NE": "Niger", - "NG": "Nigeria", - "NU": "Niue", - "NF": "Norfolk Island", - "MP": "Northern Mariana Islands", - "NO": "Norway", - "OM": "Oman", - "PK": "Pakistan", - "PW": "Palau", - "PS": "Palestine", - "PA": "Panama", - "PG": "Papua New Guinea", - "PY": "Paraguay", - "PE": "Peru", - "PH": "Philippines", - "PN": "Pitcairn", - "PL": "Poland", - "PT": "Portugal", - "PR": "Puerto Rico", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RU": "Russian Federation", - "RW": "Rwanda", - "BL": "Saint Barthélemy", - "SH": "Saint Helena", - "KN": "Saint Kitts and Nevis", - "LC": "Saint Lucia", - "MF": "Saint Martin (French part)", - "PM": "Saint Pierre and Miquelon", - "VC": "Saint Vincent and the Grenadines", - "WS": "Samoa", - "SM": "San Marino", - "ST": "Sao Tome and Principe", - "SA": "Saudi Arabia", - "SN": "Senegal", - "RS": "Serbia", - "SC": "Seychelles", - "SL": "Sierra Leone", - "SG": "Singapore", - "SX": "Sint Maarten (Dutch part)", - "SK": "Slovakia", - "SI": "Slovenia", - "SB": "Solomon Islands", - "SO": "Somalia", - "ZA": "South Africa", - "GS": "South Georgia and the South Sandwich Islands", - "SS": "South Sudan", - "ES": "Spain", - "LK": "Sri Lanka", - "SD": "Sudan", - "SR": "Suriname", - "SJ": "Svalbard and Jan Mayen", - "SZ": "Swaziland", - "SE": "Sweden", - "CH": "Switzerland", - "SY": "Syrian Arab Republic", - "TW": "Taiwan", - "TJ": "Tajikistan", - "TZ": "Tanzania", - "TH": "Thailand", - "TL": "Timor-Leste", - "TG": "Togo", - "TK": "Tokelau", - "TO": "Tonga", - "TT": "Trinidad and Tobago", - "TN": "Tunisia", - "TR": "Turkey", - "TM": "Turkmenistan", - "TC": "Turks and Caicos Islands", - "TV": "Tuvalu", - "UG": "Uganda", - "UA": "Ukraine", - "AE": "United Arab Emirates", - "GB": "United Kingdom", - "US": "United States of America", - "UM": "United States Minor Outlying Islands", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VU": "Vanuatu", - "VE": "Venezuela", - "VN": "Viet Nam", - "VG": "Virgin Islands", - "VI": "Virgin Islands", - "WF": "Wallis and Futuna", - "EH": "Western Sahara", - "YE": "Yemen", - "ZM": "Zambia", - "ZW": "Zimbabwe", - } - country = countryMap[country] - i := strings.Index(res.Get("org").String(), " ") - var owner string - if i == -1 { - owner = "" - } else { - owner = res.Get("org").String()[i:] - } - - var asnumber = "" - // 有时候不返回asn或其本身没有asn - if strings.HasPrefix(res.Get("org").String(), "AS") { - asnumber = strings.Fields(strings.TrimPrefix(res.Get("org").String(), "AS"))[0] - } - - return &IPGeoData{ - Asnumber: asnumber, - Country: country, - City: res.Get("city").String(), - Prov: res.Get("region").String(), - Owner: owner, - }, nil -} diff --git a/ipgeo/ipinfoLocal.go b/ipgeo/ipinfoLocal.go deleted file mode 100644 index b897017..0000000 --- a/ipgeo/ipinfoLocal.go +++ /dev/null @@ -1,53 +0,0 @@ -package ipgeo - -import ( - "errors" - "github.com/oschwald/maxminddb-golang" - "net" - "os" - "strings" - "time" -) - -const ( - ipinfoDataBasePath = "./ipinfoLocal.mmdb" -) - -func IPInfoLocal(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) { - if _, err := os.Stat(ipinfoDataBasePath); os.IsNotExist(err) { - panic("Cannot find ipinfoLocal.mmdb") - } - region, err := maxminddb.Open(ipinfoDataBasePath) - if err != nil { - panic("Cannot find ipinfoLocal.mmdb") - } - defer func(region *maxminddb.Reader) { - err := region.Close() - if err != nil { - panic(err) - } - }(region) - var record interface{} - searchErr := region.Lookup(net.ParseIP(ip), &record) - if searchErr != nil { - return &IPGeoData{}, errors.New("no results") - } - recordMap := record.(map[string]interface{}) - country_name := recordMap["country_name"].(string) - prov := "" - if recordMap["country"].(string) == "HK" { - country_name = "China" - prov = "Hong Kong" - } - if recordMap["country"].(string) == "TW" { - country_name = "China" - prov = "Taiwan" - } - return &IPGeoData{ - Asnumber: strings.TrimPrefix(recordMap["asn"].(string), "AS"), - Country: country_name, - City: "", - Prov: prov, - Owner: recordMap["as_name"].(string), - }, nil -} diff --git a/ipgeo/ipinsight.go b/ipgeo/ipinsight.go deleted file mode 100644 index 0e7cfda..0000000 --- a/ipgeo/ipinsight.go +++ /dev/null @@ -1,32 +0,0 @@ -package ipgeo - -import ( - "io" - "net/http" - "time" - - "github.com/tidwall/gjson" -) - -func IPInSight(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) { - client := &http.Client{ - // 2 秒超时 - Timeout: timeout, - } - resp, err := client.Get("https://api.ipinsight.io/ip/" + ip + "?token=" + token.ipinsight) - if err != nil { - return nil, err - } - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - res := gjson.ParseBytes(body) - - return &IPGeoData{ - Country: res.Get("country_name").String(), - City: res.Get("city_name").String(), - Prov: res.Get("region_name").String(), - }, nil -} diff --git a/ipgeo/ipsb.go b/ipgeo/ipsb.go deleted file mode 100644 index 1d9fbad..0000000 --- a/ipgeo/ipsb.go +++ /dev/null @@ -1,42 +0,0 @@ -package ipgeo - -import ( - "io" - "log" - "net/http" - "os" - "time" - - "github.com/tidwall/gjson" -) - -func IPSB(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) { - url := "https://api.ip.sb/geoip/" + ip - client := &http.Client{ - // 2 秒超时 - Timeout: timeout, - } - req, _ := http.NewRequest("GET", url, nil) - // 设置 UA,ip.sb 默认禁止 go-client User-Agent 的 api 请求 - req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0") - content, err := client.Do(req) - if err != nil { - log.Println("api.ip.sb 请求超时(2s),请切换其他API使用") - return nil, err - } - body, _ := io.ReadAll(content.Body) - res := gjson.ParseBytes(body) - - if res.Get("country").String() == "" { - // 什么都拿不到,证明被Cloudflare风控了 - os.Exit(1) - } - - return &IPGeoData{ - Asnumber: res.Get("asn").String(), - Country: res.Get("country").String(), - City: res.Get("city").String(), - Prov: res.Get("region").String(), - Owner: res.Get("isp").String(), - }, nil -} diff --git a/ipgeo/leo.go b/ipgeo/leo.go deleted file mode 100644 index 6d62649..0000000 --- a/ipgeo/leo.go +++ /dev/null @@ -1,114 +0,0 @@ -package ipgeo - -import ( - "encoding/json" - "errors" - "strconv" - "sync" - "time" - - "github.com/tidwall/gjson" - "github.com/xgadget-lab/nexttrace/wshandle" -) - -/*** - * 原理介绍 By Leo - * WebSocket 一共开启了一个发送和一个接收协程,在 New 了一个连接的实例对象后,不给予关闭,持续化连接 - * 当有新的IP请求时,一直在等待IP数据的发送协程接收到从 leo.go 的 sendIPRequest 函数发来的IP数据,向服务端发送数据 - * 由于实际使用时有大量并发,但是 ws 在同一时刻每次有且只能处理一次发送一条数据,所以必须给 ws 连接上互斥锁,保证每次只有一个协程访问 - * 运作模型可以理解为一个 Node 一直在等待数据,当获得一个新的任务后,转交给下一个协程,不再关注这个 Node 的下一步处理过程,并且回到空闲状态继续等待新的任务 -***/ - -// IPPool IP 查询池 map - ip - ip channel -type IPPool struct { - pool map[string]chan IPGeoData - poolMux sync.Mutex -} - -var IPPools = IPPool{ - pool: make(map[string]chan IPGeoData), -} - -func sendIPRequest(ip string) { - wsConn := wshandle.GetWsConn() - wsConn.MsgSendCh <- ip -} - -func receiveParse() { - // 获得连接实例 - wsConn := wshandle.GetWsConn() - // 防止多协程抢夺一个ws连接,导致死锁,当一个协程获得ws的控制权后上锁 - wsConn.ConnMux.Lock() - // 函数退出时解锁,给其他协程使用 - defer wsConn.ConnMux.Unlock() - for { - // 接收到了一条IP信息 - data := <-wsConn.MsgReceiveCh - - // json解析 -> data - res := gjson.Parse(data) - // 根据返回的IP信息,发送给对应等待回复的IP通道上 - var domain = res.Get("domain").String() - - if res.Get("domain").String() == "" { - domain = res.Get("owner").String() - } - - m := make(map[string][]string) - err := json.Unmarshal([]byte(res.Get("router").String()), &m) - if err != nil { - // 此处是正常的,因为有些IP没有路由信息 - } - - lat, _ := strconv.ParseFloat(res.Get("lat").String(), 32) - lng, _ := strconv.ParseFloat(res.Get("lng").String(), 32) - - IPPools.pool[gjson.Parse(data).Get("ip").String()] <- IPGeoData{ - Asnumber: res.Get("asnumber").String(), - Country: res.Get("country").String(), - CountryEn: res.Get("country_en").String(), - Prov: res.Get("prov").String(), - ProvEn: res.Get("prov_en").String(), - City: res.Get("city").String(), - CityEn: res.Get("city_en").String(), - District: res.Get("district").String(), - Owner: domain, - Lat: lat, - Lng: lng, - Isp: res.Get("isp").String(), - Whois: res.Get("whois").String(), - Prefix: res.Get("prefix").String(), - Router: m, - } - } -} - -func LeoIP(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error) { - // TODO: 根据lang的值请求中文/英文API - // TODO: 根据maptrace的值决定是否请求经纬度信息 - if timeout < 5*time.Second { - timeout = 5 * time.Second - } - - // 缓存中没有找到IP信息,需要请求API获取 - IPPools.poolMux.Lock() - // 如果之前已经被别的协程初始化过了就不用初始化了 - if IPPools.pool[ip] == nil { - IPPools.pool[ip] = make(chan IPGeoData) - } - IPPools.poolMux.Unlock() - // 发送请求 - sendIPRequest(ip) - // 同步开启监听 - go receiveParse() - - // 拥塞,等待数据返回 - select { - case res := <-IPPools.pool[ip]: - return &res, nil - // 5秒后依旧没有接收到返回的IP数据,不再等待,超时异常处理 - case <-time.After(timeout): - // 这里不可以返回一个 nil,否则在访问对象内部的键值的时候会报空指针的 Fatal Error - return &IPGeoData{}, errors.New("TimeOut") - } -} diff --git a/ipgeo/tokens.go b/ipgeo/tokens.go deleted file mode 100644 index 78a5bf9..0000000 --- a/ipgeo/tokens.go +++ /dev/null @@ -1,15 +0,0 @@ -package ipgeo - -import "github.com/xgadget-lab/nexttrace/util" - -type tokenData struct { - ipinsight string - ipinfo string - ipleo string -} - -var token = tokenData{ - ipinsight: util.GetenvDefault("NEXTTRACE_IPINSIGHT_TOKEN", ""), - ipinfo: util.GetenvDefault("NEXTTRACE_IPINFO_TOKEN", ""), - ipleo: "NextTraceDemo", -} diff --git a/main.go b/main.go deleted file mode 100644 index e9f9195..0000000 --- a/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "github.com/xgadget-lab/nexttrace/cmd" -) - -func main() { - cmd.Excute() -} diff --git a/nt_config.yaml b/nt_config.yaml deleted file mode 100644 index e884f40..0000000 --- a/nt_config.yaml +++ /dev/null @@ -1,2 +0,0 @@ -geofeedpath: ./geofeed.csv -ptrpath: ./ptr.csv diff --git a/nt_install.sh b/nt_install.sh index 6d8f941..1b1ab3a 100644 --- a/nt_install.sh +++ b/nt_install.sh @@ -93,7 +93,7 @@ checkWgetPackage() { downloadBinrayFile() { echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息" # 简单说明一下,Github提供了一个API,可以获取最新发行版本的二进制文件下载地址(对应的是browser_download_url),根据刚刚测得的osDistribution、archParam,获取对应的下载地址 - latestURL=$(curl -sL https://api.github.com/repos/sjlleo/nexttrace-core/releases/latest | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}') + latestURL=$(curl -s https://api.github.com/repos/sjlleo/nexttrace/releases/latest | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}') if [ "$countryCode" == "CN" ]; then echo -e "${Info} 检测到国内环境,正在使用镜像下载" diff --git a/pow/pow.go b/pow/pow.go deleted file mode 100644 index aab7bd5..0000000 --- a/pow/pow.go +++ /dev/null @@ -1,42 +0,0 @@ -package pow - -import ( - "fmt" - "github.com/tsosunchia/powclient" - "github.com/xgadget-lab/nexttrace/util" - "net/url" - "os" -) - -const ( - baseURL = "/v3/challenge" -) - -func GetToken(fastIp string, host string, port string) (string, error) { - getTokenParams := powclient.NewGetTokenParams() - u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: baseURL} - getTokenParams.BaseUrl = u.String() - getTokenParams.SNI = host - getTokenParams.Host = host - getTokenParams.UserAgent = util.UserAgent - proxyUrl := util.GetProxy() - if proxyUrl != nil { - getTokenParams.Proxy = proxyUrl - } - var err error - // 尝试三次RetToken,如果都失败了,异常退出 - for i := 0; i < 3; i++ { - token, err := powclient.RetToken(getTokenParams) - if err != nil { - continue - } - //fmt.Println("GetToken success", token, getTokenParams.UserAgent) - return token, nil - } - if err != nil { - fmt.Println(err) - } - fmt.Println("RetToken failed 3 times, exit") - os.Exit(1) - return "", nil -} diff --git a/pow/pow_test.go b/pow/pow_test.go deleted file mode 100644 index 72b63c9..0000000 --- a/pow/pow_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package pow - -import ( - "fmt" - "github.com/stretchr/testify/assert" - "testing" - "time" -) - -func TestGetToken(t *testing.T) { - // 计时开始 - start := time.Now() - token, err := GetToken("103.120.18.35", "api.leo.moe", "443") - // 计时结束 - end := time.Now() - fmt.Println("耗时:", end.Sub(start)) - fmt.Println("token:", token) - assert.NoError(t, err, "GetToken() returned an error") -} diff --git a/printer/basic.go b/printer/basic.go deleted file mode 100644 index 93c0c64..0000000 --- a/printer/basic.go +++ /dev/null @@ -1,101 +0,0 @@ -package printer - -import ( - "fmt" - "github.com/xgadget-lab/nexttrace/config" - "github.com/xgadget-lab/nexttrace/trace" - "net" - - "github.com/fatih/color" -) - -var version = config.Version -var buildDate = config.BuildDate -var commitID = config.CommitID - -func Version() { - fmt.Fprintf(color.Output, "%s %s %s %s\n", - color.New(color.FgWhite, color.Bold).Sprintf("%s", "NextTrace"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", version), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", buildDate), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", commitID), - ) -} - -func CopyRight() { - fmt.Fprintf(color.Output, "\n%s\n%s\n%s %s\n\n%s\n%s %s\n%s %s\n%s %s\n\n%s\n%s\n%s %s\n\n", - color.New(color.FgCyan, color.Bold).Sprintf("%s", "NextTrace CopyRight"), - color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Project Creator"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "Leo"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@leo.moe"), - color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Project Maintainer"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "Tso"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "Vincent"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@vincent.moe"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "Leo"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@leo.moe"), - color.New(color.FgCyan, color.Bold).Sprintf("%s", "Special Acknowledgement List"), - color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Major Contributor"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "zhshch"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "zhshch@athorx.com"), - ) - - MoeQingOrgCopyRight() - PluginCopyRight() -} - -func MoeQingOrgCopyRight() { - fmt.Fprintf(color.Output, "%s\n%s %s\n%s %s\n\n", - color.New(color.FgHiYellow, color.Bold).Sprintf("%s", "MoeQing Network"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "YekongTAT"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "yekongtat@gmail.com"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "Haima"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "haima@peers.cloud"), - ) -} - -func PluginCopyRight() { - fmt.Fprintf(color.Output, "%s\n%s %s\n\n", - color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Map Plugin Author"), - color.New(color.FgWhite, color.Bold).Sprintf("%s", "Tso"), - color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"), - ) -} - -func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string, maxHops int, packetSize int) { - fmt.Println("IP Geo Data Provider: " + dataOrigin) - - if ip.String() == domain { - fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", ip.String(), maxHops, packetSize) - } else { - fmt.Printf("traceroute to %s (%s), %d hops max, %d bytes packets\n", ip.String(), domain, maxHops, packetSize) - } -} - -func applyLangSetting(h *trace.Hop) { - if len(h.Geo.Country) <= 1 { - h.Geo.Country = "局域网" - h.Geo.CountryEn = "LAN Address" - } - - if h.Lang == "en" { - if h.Geo.Country == "Anycast" { - - } else if h.Geo.Prov == "骨干网" { - h.Geo.Prov = "BackBone" - } else if h.Geo.ProvEn == "" { - h.Geo.Country = h.Geo.CountryEn - } else { - if h.Geo.CityEn == "" { - h.Geo.Country = h.Geo.ProvEn - h.Geo.Prov = h.Geo.CountryEn - h.Geo.City = "" - } else { - h.Geo.Country = h.Geo.CityEn - h.Geo.Prov = h.Geo.ProvEn - h.Geo.City = h.Geo.CountryEn - } - } - } -} diff --git a/printer/classic_printer.go b/printer/classic_printer.go deleted file mode 100644 index 1f206a9..0000000 --- a/printer/classic_printer.go +++ /dev/null @@ -1,104 +0,0 @@ -package printer - -import ( - "fmt" - "strings" - - "github.com/xgadget-lab/nexttrace/trace" -) - -type HopInfo int - -const ( - General HopInfo = 0 - IXP HopInfo = 1 - Peer HopInfo = 2 - PoP HopInfo = 3 - Aboard HopInfo = 4 -) - -func findLatestAvailableHop(res *trace.Result, ttl int, probesIndex int) int { - for ttl > 0 { - // 查找上一个跃点是不是有效结果 - ttl-- - // 判断此TTL跃点是否有效并判断地理位置结构体是否已经初始化 - if len(res.Hops[ttl]) != 0 && res.Hops[ttl][probesIndex].Success && res.Hops[ttl][probesIndex].Geo != nil { - // TTL虽有效,但地理位置API没有能够正确返回数据,依旧不能视为有效数据 - if res.Hops[ttl][probesIndex].Geo.Country == "" { - // 跳过继续寻找上一个有效跃点 - continue - } - return ttl - } - } - // 没找到 - return -1 -} - -func unifyName(name string) string { - if name == "China" || name == "CN" { - return "中国" - } else if name == "Hong kong" || name == "香港" || name == "Central and Western" { - return "中国香港" - } else if name == "Taiwan" || name == "台湾" { - return "中国台湾" - } else { - return name - } -} - -func chinaISPPeer(hostname string) bool { - var keyWords = []string{"china", "ct", "cu", "cm", "cnc", "4134", "4837", "4809", "9929"} - for _, k := range keyWords { - if strings.Contains(strings.ToLower(hostname), k) { - return true - } - } - return false -} - -func chinaMainland(h trace.Hop) bool { - if unifyName(h.Geo.Country) == "中国" && unifyName(h.Geo.Prov) != "中国香港" && unifyName(h.Geo.Prov) != "中国台湾" { - return true - } else { - return false - } -} - -func makeHopsType(res *trace.Result, ttl int) map[int]HopInfo { - // 创建一个字典,存放所有当前TTL的跃点类型集合 - hopProbesMap := make(map[int]HopInfo) - for i := range res.Hops[ttl] { - // 判断是否res.Hops[ttl][i]是一个有效的跃点并且地理位置信息已经初始化 - if res.Hops[ttl][i].Success && res.Hops[ttl][i].Geo != nil { - if availableTTL := findLatestAvailableHop(res, ttl, i); availableTTL != -1 { - switch { - case strings.Contains(res.Hops[ttl][i].Geo.District, "IXP") || strings.Contains(strings.ToLower(res.Hops[ttl][i].Hostname), "ix"): - hopProbesMap[i] = IXP - case strings.Contains(res.Hops[ttl][i].Geo.District, "Peer") || chinaISPPeer(res.Hops[ttl][i].Hostname): - hopProbesMap[i] = Peer - case strings.Contains(res.Hops[ttl][i].Geo.District, "PoP"): - hopProbesMap[i] = PoP - // 2个有效跃点必须都为有效数据,如果当前跳没有地理位置信息或者为局域网,不能视为有效节点 - case res.Hops[availableTTL][i].Geo.Country != "LAN Address" && res.Hops[ttl][i].Geo.Country != "LAN Address" && res.Hops[ttl][i].Geo.Country != "" && - // 一个跃点在中国大陆,另外一个跃点在其他地区,则可以推断出数据包跨境 - chinaMainland(res.Hops[availableTTL][i]) != chinaMainland(res.Hops[ttl][i]): - // TODO: 将先后2跳跃点信息汇报给API,以完善相关数据 - hopProbesMap[i] = Aboard - } - } else { - hopProbesMap[i] = General - } - } - } - - return hopProbesMap -} - -func ClassicPrinter(res *trace.Result, ttl int) { - fmt.Print(ttl + 1) - hopsTypeMap := makeHopsType(res, ttl) - for i := range res.Hops[ttl] { - HopPrinter(res.Hops[ttl][i], hopsTypeMap[i]) - } -} diff --git a/printer/easy.go b/printer/easy.go deleted file mode 100644 index dc3ace4..0000000 --- a/printer/easy.go +++ /dev/null @@ -1,32 +0,0 @@ -package printer - -import ( - "fmt" - - "github.com/xgadget-lab/nexttrace/trace" -) - -func EasyPrinter(res *trace.Result, ttl int) { - for i := range res.Hops[ttl] { - if res.Hops[ttl][i].Address == nil { - fmt.Printf("%d|*||||||\n", ttl+1) - continue - } - applyLangSetting(&res.Hops[ttl][i]) // 应用语言设置 - fmt.Printf( - "%d|%s|%s|%.2f|%s|%s|%s|%s|%s|%s|%.4f|%.4f\n", - ttl+1, - res.Hops[ttl][i].Address.String(), - res.Hops[ttl][i].Hostname, - float64(res.Hops[ttl][i].RTT.Microseconds())/1000, - res.Hops[ttl][i].Geo.Asnumber, - res.Hops[ttl][i].Geo.Country, - res.Hops[ttl][i].Geo.Prov, - res.Hops[ttl][i].Geo.City, - res.Hops[ttl][i].Geo.District, - res.Hops[ttl][i].Geo.Owner, - res.Hops[ttl][i].Geo.Lat, - res.Hops[ttl][i].Geo.Lng, - ) - } -} diff --git a/printer/printer.go b/printer/printer.go deleted file mode 100644 index 48d3ddd..0000000 --- a/printer/printer.go +++ /dev/null @@ -1,113 +0,0 @@ -package printer - -import ( - "fmt" - "strings" - - "github.com/xgadget-lab/nexttrace/trace" - - "github.com/xgadget-lab/nexttrace/ipgeo" -) - -// var dataOrigin string - -// func TraceroutePrinter(res *trace.Result) { -// for i, hop := range res.Hops { -// fmt.Print(i + 1) -// for _, h := range hop { -// HopPrinter(h) -// } -// } -// } - -//此文件目前仅供classic_printer使用 - -const ( - RED_PREFIX = "\033[1;31m" - GREEN_PREFIX = "\033[1;32m" - YELLOW_PREFIX = "\033[1;33m" - BLUE_PREFIX = "\033[1;34m" - CYAN_PREFIX = "\033[1;36m" - RESET_PREFIX = "\033[0m" -) - -func HopPrinter(h trace.Hop, info HopInfo) { - if h.Address == nil { - fmt.Println("\t*") - } else { - applyLangSetting(&h) // 应用语言设置 - txt := "\t" - - if h.Hostname == "" { - txt += fmt.Sprint(h.Address, " ", fmt.Sprintf("%.2f", h.RTT.Seconds()*1000), "ms") - } else { - txt += fmt.Sprint(h.Hostname, " (", h.Address, ") ", fmt.Sprintf("%.2f", h.RTT.Seconds()*1000), "ms") - } - - if h.Geo != nil { - txt += " " + formatIpGeoData(h.Address.String(), h.Geo) - } - switch info { - case IXP: - fmt.Print(CYAN_PREFIX) - case PoP: - fmt.Print(CYAN_PREFIX) - case Peer: - fmt.Print(YELLOW_PREFIX) - case Aboard: - fmt.Print(GREEN_PREFIX) - } - - fmt.Println(txt) - - if info != General { - fmt.Print(RESET_PREFIX) - } - } -} - -func formatIpGeoData(ip string, data *ipgeo.IPGeoData) string { - var res = make([]string, 0, 10) - - if data.Asnumber == "" { - res = append(res, "*") - } else { - res = append(res, "AS"+data.Asnumber) - } - - // TODO: 判断阿里云和腾讯云内网,数据不足,有待进一步完善 - // TODO: 移动IDC判断到Hop.fetchIPData函数,减少API调用 - //if strings.HasPrefix(ip, "9.") { - // res = append(res, "LAN Address") - //} else if strings.HasPrefix(ip, "11.") { - // res = append(res, "LAN Address") - //} else if data.Country == "" { - // res = append(res, "LAN Address") - if false { - } else { - // 有些IP的归属信息为空,这个时候将ISP的信息填入 - if data.Owner == "" { - data.Owner = data.Isp - } - if data.Prov == "" && data.City == "" { - // anyCast或是骨干网数据不应该有国家信息 - data.Owner = data.Owner + ", " + data.Owner - } else { - // 非骨干网正常填入IP的国家信息数据 - res = append(res, data.Country) - } - - if data.Prov != "" { - res = append(res, data.Prov) - } - if data.City != "" { - res = append(res, data.City) - } - - if data.Owner != "" { - res = append(res, data.Owner) - } - } - - return strings.Join(res, ", ") -} diff --git a/printer/printer_test.go b/printer/printer_test.go deleted file mode 100644 index dfa7bc5..0000000 --- a/printer/printer_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package printer - -// func TestPrintTraceRouteNav(t *testing.T) { -// PrintTraceRouteNav(util.DomainLookUp("1.1.1.1", false), "1.1.1.1", "dataOrigin") -// } - -// var testGeo = &ipgeo.IPGeoData{ -// Asnumber: "TestAsnumber", -// Country: "TestCountry", -// Prov: "TestProv", -// City: "TestCity", -// District: "TestDistrict", -// Owner: "TestOwner", -// Isp: "TestIsp", -// } - -// var testResult = &trace.Result{ -// Hops: [][]trace.Hop{ -// { -// { -// Success: true, -// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")}, -// Hostname: "test", -// TTL: 0, -// RTT: 10 * time.Millisecond, -// Error: nil, -// Geo: testGeo, -// }, -// { -// Success: true, -// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")}, -// Hostname: "test", -// TTL: 0, -// RTT: 10 * time.Millisecond, -// Error: nil, -// Geo: testGeo, -// }, -// }, -// { -// { -// Success: false, -// Address: nil, -// Hostname: "", -// TTL: 0, -// RTT: 0, -// Error: errors.New("test error"), -// Geo: nil, -// }, -// { -// Success: true, -// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")}, -// Hostname: "test", -// TTL: 0, -// RTT: 10 * time.Millisecond, -// Error: nil, -// Geo: nil, -// }, -// }, -// { -// { -// Success: true, -// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")}, -// Hostname: "test", -// TTL: 0, -// RTT: 0, -// Error: nil, -// Geo: &ipgeo.IPGeoData{}, -// }, -// { -// Success: true, -// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")}, -// Hostname: "", -// TTL: 0, -// RTT: 10 * time.Millisecond, -// Error: nil, -// Geo: testGeo, -// }, -// }, -// }, -// } - -// // func TestTraceroutePrinter(t *testing.T) { -// // TraceroutePrinter(testResult) -// // } - -// func TestTracerouteTablePrinter(t *testing.T) { -// TracerouteTablePrinter(testResult) -// } - -// func TestRealtimePrinter(t *testing.T) { -// RealtimePrinter(testResult, 0) -// // RealtimePrinter(testResult, 1) -// // RealtimePrinter(testResult, 2) -// } diff --git a/printer/realtime_printer.go b/printer/realtime_printer.go deleted file mode 100644 index e3e9791..0000000 --- a/printer/realtime_printer.go +++ /dev/null @@ -1,166 +0,0 @@ -package printer - -import ( - "fmt" - "net" - "strconv" - "strings" - - "github.com/fatih/color" - "github.com/xgadget-lab/nexttrace/trace" -) - -func RealtimePrinter(res *trace.Result, ttl int) { - fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1)) - - // 去重 - var latestIP string - tmpMap := make(map[string][]string) - for i, v := range res.Hops[ttl] { - if v.Address == nil && latestIP != "" { - tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*")) - continue - } else if v.Address == nil { - continue - } - - if _, exist := tmpMap[v.Address.String()]; !exist { - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i)) - // 首次进入 - if latestIP == "" { - for j := 0; j < i; j++ { - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*")) - } - } - latestIP = v.Address.String() - } - - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000)) - } - - if latestIP == "" { - fmt.Fprintf(color.Output, "%s\n", - color.New(color.FgWhite, color.Bold).Sprintf("*"), - ) - return - } - - var blockDisplay = false - for ip, v := range tmpMap { - if blockDisplay { - fmt.Printf("%4s", "") - } - if net.ParseIP(ip).To4() == nil { - fmt.Fprintf(color.Output, "%s", - color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip), - ) - } else { - fmt.Fprintf(color.Output, "%s", - color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip), - ) - } - - i, _ := strconv.Atoi(v[0]) - if res.Hops[ttl][i].Geo.Asnumber != "" { - /*** CMIN2, CUG, CN2, CUII, CTG 改为壕金色高亮 - /* 小孩子不懂事加着玩的 - /* 此处的高亮不代表任何线路质量 - /* 仅代表走了这部分的ASN - /* 如果使用这些ASN的IP同样会被高亮 - ***/ - switch { - case res.Hops[ttl][i].Geo.Asnumber == "58807": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "10099": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "4809": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "9929": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "23764": - fallthrough - case res.Hops[ttl][i].Geo.Whois == "CMIN2-NET": - fallthrough - case strings.HasPrefix(res.Hops[ttl][i].Address.String(), "59.43."): - fmt.Fprintf(color.Output, " %s", color.New(color.FgHiYellow, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber)) - default: - fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber)) - } - - } else { - fmt.Printf(" %-8s", "*") - } - - if net.ParseIP(ip).To4() != nil { - whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-") - if len(whoisFormat) > 1 { - whoisFormat[0] = strings.Join(whoisFormat[:2], "-") - } - - if whoisFormat[0] != "" { - whoisFormat[0] = "[" + whoisFormat[0] + "]" - } - - // CMIN2, CUII, CN2, CUG 改为壕金色高亮 - switch { - case res.Hops[ttl][i].Geo.Asnumber == "58807": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "10099": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "4809": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "9929": - fallthrough - case res.Hops[ttl][i].Geo.Asnumber == "23764": - fallthrough - case whoisFormat[0] == "[CNC-BACKBONE]": - fallthrough - case whoisFormat[0] == "[CUG-BACKBONE]": - fallthrough - case whoisFormat[0] == "[CMIN2-NET]": - fallthrough - case strings.HasPrefix(res.Hops[ttl][i].Address.String(), "59.43."): - fmt.Fprintf(color.Output, " %s", color.New(color.FgHiYellow, color.Bold).Sprintf("%-16s", whoisFormat[0])) - default: - fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0])) - } - } - - applyLangSetting(&res.Hops[ttl][i]) // 应用语言设置 - - if net.ParseIP(ip).To4() != nil { - - fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ", - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District), - fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner), - color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][i].Hostname), - ) - } else { - fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ", - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District), - fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner), - color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][i].Hostname), - ) - } - - for j := 1; j < len(v); j++ { - if len(v) == 2 || j == 1 { - fmt.Fprintf(color.Output, "%s", - color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]), - ) - } else { - fmt.Fprintf(color.Output, " / %s", - color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]), - ) - } - } - fmt.Println() - blockDisplay = true - } -} diff --git a/printer/realtime_printer_router.go b/printer/realtime_printer_router.go deleted file mode 100644 index 18007bb..0000000 --- a/printer/realtime_printer_router.go +++ /dev/null @@ -1,152 +0,0 @@ -package printer - -import ( - "fmt" - "net" - "strconv" - "strings" - - "github.com/fatih/color" - "github.com/xgadget-lab/nexttrace/trace" -) - -func RealtimePrinterWithRouter(res *trace.Result, ttl int) { - fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1)) - - // 去重 - var latestIP string - tmpMap := make(map[string][]string) - for i, v := range res.Hops[ttl] { - if v.Address == nil && latestIP != "" { - tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*")) - continue - } else if v.Address == nil { - continue - } - - if _, exist := tmpMap[v.Address.String()]; !exist { - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i)) - // 首次进入 - if latestIP == "" { - for j := 0; j < i; j++ { - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*")) - } - } - latestIP = v.Address.String() - } - - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000)) - } - - if latestIP == "" { - fmt.Fprintf(color.Output, "%s\n", - color.New(color.FgWhite, color.Bold).Sprintf("*"), - ) - return - } - - var blockDisplay = false - for ip, v := range tmpMap { - if blockDisplay { - fmt.Printf("%4s", "") - } - if net.ParseIP(ip).To4() == nil { - fmt.Fprintf(color.Output, "%s", - color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip), - ) - } else { - fmt.Fprintf(color.Output, "%s", - color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip), - ) - } - - i, _ := strconv.Atoi(v[0]) - - if res.Hops[ttl][i].Geo.Asnumber != "" { - fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber)) - } else { - fmt.Printf(" %-8s", "*") - } - - if net.ParseIP(ip).To4() != nil { - whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-") - if len(whoisFormat) > 1 { - whoisFormat[0] = strings.Join(whoisFormat[:2], "-") - } - - if whoisFormat[0] != "" { - whoisFormat[0] = "[" + whoisFormat[0] + "]" - } - fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0])) - } - - if res.Hops[ttl][i].Geo.Country == "" { - res.Hops[ttl][i].Geo.Country = "LAN Address" - } - - if net.ParseIP(ip).To4() != nil { - - fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ", - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District), - fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner), - color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][i].Hostname), - ) - } else { - fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ", - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City), - color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District), - fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner), - color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][i].Hostname), - ) - } - - for j := 1; j < len(v); j++ { - if len(v) == 2 || j == 1 { - fmt.Fprintf(color.Output, "%s", - color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]), - ) - } else { - fmt.Fprintf(color.Output, " / %s", - color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]), - ) - } - } - i = 0 - fmt.Println() - if res.Hops[ttl][i].Geo != nil && !blockDisplay { - fmt.Fprintf(color.Output, "%s %s %s %s %s\n", - color.New(color.FgWhite, color.Bold).Sprintf("-"), - color.New(color.FgHiYellow, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prefix), - color.New(color.FgWhite, color.Bold).Sprintf("路由表"), - color.New(color.FgHiCyan, color.Bold).Sprintf("Beta"), - color.New(color.FgWhite, color.Bold).Sprintf("-"), - ) - GetRouter(&res.Hops[ttl][i].Geo.Router, "AS"+res.Hops[ttl][i].Geo.Asnumber) - } - blockDisplay = true - } -} - -func GetRouter(r *map[string][]string, node string) { - routeMap := *r - for _, v := range routeMap[node] { - if len(routeMap[v]) != 0 { - fmt.Fprintf(color.Output, " %s %s %s\n", - color.New(color.FgWhite, color.Bold).Sprintf("%s", routeMap[v][0]), - color.New(color.FgWhite, color.Bold).Sprintf("%s", v), - color.New(color.FgHiBlue, color.Bold).Sprintf("%s", node), - ) - } else { - fmt.Fprintf(color.Output, " %s %s\n", - color.New(color.FgWhite, color.Bold).Sprintf("%s", v), - color.New(color.FgHiBlue, color.Bold).Sprintf("%s", node), - ) - } - - } -} diff --git a/printer/tableprinter.go b/printer/tableprinter.go deleted file mode 100644 index 4d6ad97..0000000 --- a/printer/tableprinter.go +++ /dev/null @@ -1,130 +0,0 @@ -package printer - -import ( - "fmt" - "strings" - - "github.com/xgadget-lab/nexttrace/ipgeo" - - "github.com/xgadget-lab/nexttrace/trace" - - "github.com/fatih/color" - "github.com/rodaine/table" -) - -type rowData struct { - Hop string - IP string - Latency string - Asnumber string - Country string - Prov string - City string - District string - Owner string -} - -func TracerouteTablePrinter(res *trace.Result) { - // 初始化表格 - tbl := New() - for _, hop := range res.Hops { - for k, h := range hop { - data := tableDataGenerator(h) - if k > 0 { - data.Hop = "" - } - if data.Country == "" && data.Prov == "" && data.City == "" { - tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, "", data.Owner) - } else { - if data.City != "" { - tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.City+", "+data.Prov+", "+data.Country, data.Owner) - } else if data.Prov != "" { - tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Prov+", "+data.Country, data.Owner) - } else { - tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country, data.Owner) - } - - } - } - } - fmt.Print("\033[H\033[2J") - // 打印表格 - tbl.Print() -} - -func New() table.Table { - // 初始化表格 - headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() - columnFmt := color.New(color.FgYellow).SprintfFunc() - - tbl := table.New("Hop", "IP", "Lantency", "ASN", "Location", "Owner") - tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) - return tbl -} - -func tableDataGenerator(h trace.Hop) *rowData { - if h.Address == nil { - return &rowData{ - Hop: fmt.Sprint(h.TTL), - IP: "*", - } - } else { - lantency := fmt.Sprintf("%.2fms", h.RTT.Seconds()*1000) - IP := h.Address.String() - - if strings.HasPrefix(IP, "9.") { - return &rowData{ - Hop: fmt.Sprint(h.TTL), - IP: IP, - Latency: lantency, - Country: "LAN Address", - Prov: "", - Owner: "", - } - } else if strings.HasPrefix(IP, "11.") { - return &rowData{ - Hop: fmt.Sprint(h.TTL), - IP: IP, - Latency: lantency, - Country: "LAN Address", - Prov: "", - Owner: "", - } - } - - if h.Hostname != "" { - IP = fmt.Sprint(h.Hostname, " (", IP, ") ") - } - - if h.Geo == nil { - h.Geo = &ipgeo.IPGeoData{} - } - - r := &rowData{ - Hop: fmt.Sprint(h.TTL), - IP: IP, - Latency: lantency, - Asnumber: h.Geo.Asnumber, - Country: h.Geo.CountryEn, - Prov: h.Geo.ProvEn, - City: h.Geo.CityEn, - District: h.Geo.District, - Owner: h.Geo.Owner, - } - - if h.Geo == nil { - return r - } - - if h.Geo.Owner == "" { - h.Geo.Owner = h.Geo.Isp - } - r.Asnumber = h.Geo.Asnumber - r.Country = h.Geo.CountryEn - r.Prov = h.Geo.ProvEn - r.City = h.Geo.CityEn - r.District = h.Geo.District - r.Owner = h.Geo.Owner - return r - } -} diff --git a/reporter/reporter.go b/reporter/reporter.go deleted file mode 100644 index 2ea348b..0000000 --- a/reporter/reporter.go +++ /dev/null @@ -1,183 +0,0 @@ -package reporter - -import ( - "fmt" - "net" - "strings" - "sync" - - "github.com/xgadget-lab/nexttrace/ipgeo" - "github.com/xgadget-lab/nexttrace/trace" -) - -type Reporter interface { - Print() -} - -func New(rs *trace.Result, ip string) Reporter { - experimentTag() - r := reporter{ - routeResult: rs, - targetIP: ip, - } - return &r -} - -type reporter struct { - targetTTL uint16 - targetIP string - routeReport map[uint16][]routeReportNode - routeReportLock sync.Mutex - routeResult *trace.Result - wg sync.WaitGroup -} - -type routeReportNode struct { - asn string - isp string - geo []string - ix bool -} - -func experimentTag() { - fmt.Println("Route-Path 功能实验室") -} - -func (r *reporter) generateRouteReportNode(ip string, ipGeoData ipgeo.IPGeoData, ttl uint16) { - - var success = true - - defer r.wg.Done() - - rpn := routeReportNode{} - ptr, err := net.LookupAddr(ip) - - if err == nil { - if strings.Contains(strings.ToLower(ptr[0]), "ix") { - rpn.ix = true - } else { - rpn.ix = false - } - } - // TODO: 这种写法不好,后面再重构一下 - // 判断反向解析的域名中又或者是IP地理位置数据库中,是否出现了 IX - if strings.Contains(strings.ToLower(ipGeoData.Isp), "exchange") || strings.Contains(strings.ToLower(ipGeoData.Isp), "ix") || strings.Contains(strings.ToLower(ipGeoData.Owner), "exchange") || strings.Contains(strings.ToLower(ipGeoData.Owner), "ix") { - rpn.ix = true - } - - // TODO: 正则判断POP并且提取带宽大小等信息 - - // CN2 需要特殊处理,因为他们很多没有ASN - // 但是目前这种写法是不规范的,属于凭空标记4809的IP - // TODO: 用更好的方式显示 CN2 骨干网的路由 Path - if strings.HasPrefix(ip, "59.43") { - rpn.asn = "4809" - } else { - rpn.asn = ipGeoData.Asnumber - } - - // 无论最后一跳是否为存在地理位置信息(AnyCast),都应该给予显示 - if (ipGeoData.Country == "" || ipGeoData.Country == "LAN Address" || ipGeoData.Country == "-") && ip != r.targetIP { - success = false - } else { - if ipGeoData.City == "" { - rpn.geo = []string{ipGeoData.Country, ipGeoData.Prov} - } else { - rpn.geo = []string{ipGeoData.Country, ipGeoData.City} - } - } - if ipGeoData.Asnumber == "" { - rpn.asn = "*" - } - - if ipGeoData.Isp == "" { - rpn.isp = ipGeoData.Owner - } else { - rpn.isp = ipGeoData.Isp - } - - // 有效记录 - if success { - // 锁住资源,防止同时写panic - r.routeReportLock.Lock() - // 添加到MAP中 - r.routeReport[ttl] = append(r.routeReport[ttl], rpn) - // 写入完成,解锁释放资源给其他协程 - r.routeReportLock.Unlock() - } -} - -func (r *reporter) InitialBaseData() Reporter { - reportNodes := map[uint16][]routeReportNode{} - - r.routeReport = reportNodes - r.targetTTL = uint16(len(r.routeResult.Hops)) - - for i := uint16(0); i < r.targetTTL; i++ { - traceHop := r.routeResult.Hops[i][0] - if traceHop.Success { - currentIP := traceHop.Address.String() - r.wg.Add(1) - go r.generateRouteReportNode(currentIP, *traceHop.Geo, i) - } - } - - // 等待所有的子协程运行完毕 - r.wg.Wait() - return r -} - -func (r *reporter) Print() { - var beforeActiveTTL uint16 = 0 - r.InitialBaseData() - // 尝试首个有效 TTL - for i := uint16(0); i < r.targetTTL; i++ { - if len(r.routeReport[i]) != 0 { - beforeActiveTTL = i - // 找到以后便不再循环 - break - } - } - - for i := beforeActiveTTL; i < r.targetTTL; i++ { - // 计算该TTL内的数据长度,如果为0,则代表没有有效数据 - if len(r.routeReport[i]) == 0 { - // 跳过改跃点的数据整理 - continue - } - nodeReport := r.routeReport[i][0] - - if i == beforeActiveTTL { - fmt.Printf("AS%s %s「%s『%s", nodeReport.asn, nodeReport.isp, nodeReport.geo[0], nodeReport.geo[1]) - } else { - nodeReportBefore := r.routeReport[beforeActiveTTL][0] - // ASN 相同,同个 ISP 内部的数据传递 - if nodeReportBefore.asn == nodeReport.asn { - // Same ASN but Coutry or City Changed - if nodeReportBefore.geo[0] != nodeReport.geo[0] { - fmt.Printf("』→ %s『%s", nodeReport.geo[0], nodeReport.geo[1]) - } else { - if nodeReportBefore.geo[1] != nodeReport.geo[1] { - fmt.Printf(" → %s", nodeReport.geo[1]) - } - } - } else { - // ASN 不同,跨 ISP 的数据传递,这里可能会出现 POP、IP Transit、Peer、Exchange - fmt.Printf("』」") - if int(i) != len(r.routeReport)+1 { - // 部分 Shell 客户端可能无法很好的展示这个特殊字符 - // TODO: 寻找其他替代字符 - fmt.Printf("\n ╭╯\n ╰") - } - if nodeReport.ix { - fmt.Printf("AS%s \033[42;37mIXP\033[0m %s「%s『%s", nodeReport.asn, nodeReport.isp, nodeReport.geo[0], nodeReport.geo[1]) - } else { - fmt.Printf("AS%s %s「%s『%s", nodeReport.asn, nodeReport.isp, nodeReport.geo[0], nodeReport.geo[1]) - } - } - } - // 标记为最新的一个有效跃点 - beforeActiveTTL = i - } - fmt.Println("』」") -} diff --git a/reporter/reporter_test.go b/reporter/reporter_test.go deleted file mode 100644 index c95acdc..0000000 --- a/reporter/reporter_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package reporter - -import ( - "net" - "testing" - "time" - - "github.com/xgadget-lab/nexttrace/ipgeo" - "github.com/xgadget-lab/nexttrace/trace" -) - -var testResult = &trace.Result{ - Hops: [][]trace.Hop{ - { - { - Success: true, - Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")}, - Hostname: "test", - TTL: 0, - RTT: 10 * time.Millisecond, - Error: nil, - Geo: &ipgeo.IPGeoData{ - Asnumber: "4808", - Country: "中国", - Prov: "北京市", - City: "北京市", - District: "北京市", - Owner: "", - Isp: "中国联通", - }, - }, - }, - { - { - Success: true, - Address: &net.IPAddr{IP: net.ParseIP("114.249.16.1")}, - Hostname: "test", - TTL: 0, - RTT: 10 * time.Millisecond, - Error: nil, - Geo: &ipgeo.IPGeoData{ - Asnumber: "4808", - Country: "中国", - Prov: "北京市", - City: "北京市", - District: "北京市", - Owner: "", - Isp: "中国联通", - }, - }, - }, - { - { - Success: true, - Address: &net.IPAddr{IP: net.ParseIP("219.158.5.150")}, - Hostname: "test", - TTL: 0, - RTT: 10 * time.Millisecond, - Error: nil, - Geo: &ipgeo.IPGeoData{ - Asnumber: "4837", - Country: "中国", - Prov: "", - City: "", - District: "", - Owner: "", - Isp: "中国联通", - }, - }, - }, - { - { - Success: true, - Address: &net.IPAddr{IP: net.ParseIP("62.115.125.160")}, - Hostname: "test", - TTL: 0, - RTT: 10 * time.Millisecond, - Error: nil, - Geo: &ipgeo.IPGeoData{ - Asnumber: "1299", - Country: "Sweden", - Prov: "Stockholm County", - City: "Stockholm", - District: "", - Owner: "", - Isp: "Telia Company AB", - }, - }, - }, - { - { - Success: true, - Address: &net.IPAddr{IP: net.ParseIP("213.226.68.73")}, - Hostname: "test", - TTL: 0, - RTT: 10 * time.Millisecond, - Error: nil, - Geo: &ipgeo.IPGeoData{ - Asnumber: "56630", - Country: "Germany", - Prov: "Hesse, Frankfurt", - City: "", - District: "", - Owner: "", - Isp: "Melbikomas UAB", - }, - }, - }, - }, -} - -func TestPrint(t *testing.T) { - r := New(testResult, "213.226.68.73") - r.Print() -} diff --git a/trace/icmp_ipv4.go b/trace/icmp_ipv4.go index 4cd9235..6bb5f56 100644 --- a/trace/icmp_ipv4.go +++ b/trace/icmp_ipv4.go @@ -1,7 +1,6 @@ package trace import ( - "bytes" "encoding/binary" "errors" "fmt" @@ -12,7 +11,6 @@ import ( "sync" "time" - "github.com/xgadget-lab/nexttrace/trace/internal" "golang.org/x/net/context" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" @@ -28,30 +26,6 @@ type ICMPTracer struct { icmpListen net.PacketConn final int finalLock sync.Mutex - fetchLock sync.Mutex -} - -func (t *ICMPTracer) PrintFunc() { - defer t.wg.Done() - var ttl = t.Config.BeginHop - 1 - for { - if t.AsyncPrinter != nil { - t.AsyncPrinter(&t.res) - } - if len(t.res.Hops)-1 > ttl { - if len(t.res.Hops[ttl]) == t.NumMeasurements { - if t.RealtimePrinter != nil { - t.RealtimePrinter(&t.res, ttl) - } - ttl++ - - if ttl == t.final-1 || ttl >= t.MaxHops-1 { - return - } - } - } - <-time.After(200 * time.Millisecond) - } } func (t *ICMPTracer) Execute() (*Result, error) { @@ -65,7 +39,7 @@ func (t *ICMPTracer) Execute() (*Result, error) { var err error - t.icmpListen, err = internal.ListenICMP("ip4:1", t.SrcAddr) + t.icmpListen, err = net.ListenPacket("ip4:1", t.SrcAddr) if err != nil { return &t.res, err } @@ -78,7 +52,6 @@ func (t *ICMPTracer) Execute() (*Result, error) { go t.listenICMP() t.wg.Add(1) - go t.PrintFunc() for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ { t.inflightRequestRWLock.Lock() t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements) @@ -97,22 +70,15 @@ func (t *ICMPTracer) Execute() (*Result, error) { t.wg.Wait() t.res.reduce(t.final) if t.final != -1 { - if t.RealtimePrinter != nil { - t.RealtimePrinter(&t.res, t.final-1) - } } else { for i := 0; i < t.NumMeasurements; i++ { t.res.add(Hop{ - Success: false, Address: nil, TTL: 30, RTT: 0, Error: ErrHopLimitTimeout, }) } - if t.RealtimePrinter != nil { - t.RealtimePrinter(&t.res, t.MaxHops-1) - } } return &t.res, nil } @@ -178,7 +144,6 @@ func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data defer t.inflightRequestRWLock.RUnlock() if _, ok := t.inflightRequest[ttl]; ok { t.inflightRequest[ttl] <- Hop{ - Success: true, Address: msg.Peer, } } @@ -255,9 +220,8 @@ func (t *ICMPTracer) send(ttl int) error { icmpHeader := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ - ID: id, - //Data: []byte("HELLO-R-U-THERE"), - Data: bytes.Repeat([]byte{1}, t.Config.PktSize), + ID: id, + Data: []byte("HELLO-R-U-THERE"), Seq: ttl, }, } @@ -302,10 +266,6 @@ func (t *ICMPTracer) send(ttl int) error { h.TTL = ttl h.RTT = rtt - t.fetchLock.Lock() - defer t.fetchLock.Unlock() - h.fetchIPData(t.Config) - t.res.add(h) case <-time.After(t.Timeout): if t.final != -1 && ttl > t.final { @@ -313,7 +273,6 @@ func (t *ICMPTracer) send(ttl int) error { } t.res.add(Hop{ - Success: false, Address: nil, TTL: ttl, RTT: 0, diff --git a/trace/icmp_ipv6.go b/trace/icmp_ipv6.go index 69bcf90..81041ed 100644 --- a/trace/icmp_ipv6.go +++ b/trace/icmp_ipv6.go @@ -1,7 +1,6 @@ package trace import ( - "bytes" "encoding/binary" "log" "net" @@ -10,7 +9,6 @@ import ( "sync" "time" - "github.com/xgadget-lab/nexttrace/trace/internal" "golang.org/x/net/context" "golang.org/x/net/icmp" "golang.org/x/net/ipv6" @@ -27,32 +25,6 @@ type ICMPTracerv6 struct { icmpListen net.PacketConn final int finalLock sync.Mutex - fetchLock sync.Mutex -} - -func (t *ICMPTracerv6) PrintFunc() { - // defer t.wg.Done() - var ttl = t.Config.BeginHop - 1 - for { - if t.AsyncPrinter != nil { - t.AsyncPrinter(&t.res) - } - - // 接收的时候检查一下是不是 3 跳都齐了 - if len(t.res.Hops)-1 > ttl { - if len(t.res.Hops[ttl]) == t.NumMeasurements { - if t.RealtimePrinter != nil { - t.RealtimePrinter(&t.res, ttl) - } - ttl++ - if ttl == t.final { - return - } - } - } - - <-time.After(200 * time.Millisecond) - } } func (t *ICMPTracerv6) Execute() (*Result, error) { @@ -66,7 +38,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) { var err error - t.icmpListen, err = internal.ListenICMP("ip6:58", t.SrcAddr) + t.icmpListen, err = net.ListenPacket("ip6:58", t.SrcAddr) if err != nil { return &t.res, err } @@ -79,7 +51,6 @@ func (t *ICMPTracerv6) Execute() (*Result, error) { t.final = -1 go t.listenICMP() - go t.PrintFunc() for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ { t.inflightRequestRWLock.Lock() t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements) @@ -114,23 +85,16 @@ func (t *ICMPTracerv6) Execute() (*Result, error) { // } t.wg.Wait() t.res.reduce(t.final) - if t.final != -1 { - if t.RealtimePrinter != nil { - t.RealtimePrinter(&t.res, t.final-1) - } - } else { + if t.final == -1 { + for i := 0; i < t.NumMeasurements; i++ { t.res.add(Hop{ - Success: false, Address: nil, TTL: 30, RTT: 0, Error: ErrHopLimitTimeout, }) } - if t.RealtimePrinter != nil { - t.RealtimePrinter(&t.res, t.MaxHops-1) - } } return &t.res, nil @@ -236,7 +200,6 @@ func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, dat defer t.inflightRequestRWLock.RUnlock() if _, ok := t.inflightRequest[ttl]; ok { t.inflightRequest[ttl] <- Hop{ - Success: true, Address: msg.Peer, } } @@ -252,9 +215,8 @@ func (t *ICMPTracerv6) send(ttl int) error { icmpHeader := icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ - ID: id, - //Data: []byte("HELLO-R-U-THERE"), - Data: bytes.Repeat([]byte{1}, t.Config.PktSize), + ID: id, + Data: []byte("HELLO-R-U-THERE"), Seq: ttl, }, } @@ -302,10 +264,6 @@ func (t *ICMPTracerv6) send(ttl int) error { h.TTL = ttl h.RTT = rtt - t.fetchLock.Lock() - defer t.fetchLock.Unlock() - h.fetchIPData(t.Config) - t.res.add(h) case <-time.After(t.Timeout): @@ -314,7 +272,6 @@ func (t *ICMPTracerv6) send(ttl int) error { } t.res.add(Hop{ - Success: false, Address: nil, TTL: ttl, RTT: 0, diff --git a/trace/internal/icmp_darwin.go b/trace/internal/icmp_darwin.go deleted file mode 100644 index dd2fbe6..0000000 --- a/trace/internal/icmp_darwin.go +++ /dev/null @@ -1,49 +0,0 @@ -//go:build darwin - -package internal - -import ( - "context" - "errors" - "net" - "os" - "syscall" - "unsafe" -) - -//go:linkname internetSocket net.internetSocket -func internetSocket(ctx context.Context, net string, laddr, raddr interface{}, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error) - -//go:linkname newIPConn net.newIPConn -func newIPConn(fd unsafe.Pointer) *net.IPConn - -var ( - errUnknownNetwork = errors.New("unknown network type") - - networkMap = map[string]string{ - "ip4:icmp": "udp4", - "ip4:1": "udp4", - "ip6:icmp": "udp6", - "ip6:58": "udp6", - } -) - -func ListenICMP(network string, laddr string) (net.PacketConn, error) { - if os.Getuid() == 0 { // root - return net.ListenPacket(network, laddr) - } else { - if nw, ok := networkMap[network]; ok { - proto := syscall.IPPROTO_ICMP - if nw == "udp6" { - proto = syscall.IPPROTO_ICMPV6 - } - isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen", nil) - if err != nil { - panic(err) - } - return newIPConn(isock), nil - } else { - return nil, errUnknownNetwork - } - } -} diff --git a/trace/internal/icmp_general.go b/trace/internal/icmp_general.go deleted file mode 100644 index 89c1ce5..0000000 --- a/trace/internal/icmp_general.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !darwin - -package internal - -import "net" - -func ListenICMP(network string, laddr string) (net.PacketConn, error) { - return net.ListenPacket(network, laddr) -} diff --git a/trace/tcp_ipv4.go b/trace/tcp_ipv4.go index bea55b3..296b9ff 100644 --- a/trace/tcp_ipv4.go +++ b/trace/tcp_ipv4.go @@ -31,8 +31,7 @@ type TCPTracer struct { final int finalLock sync.Mutex - sem *semaphore.Weighted - fetchLock sync.Mutex + sem *semaphore.Weighted } func (t *TCPTracer) Execute() (*Result, error) { @@ -80,30 +79,11 @@ func (t *TCPTracer) Execute() (*Result, error) { for i := 0; i < t.NumMeasurements; i++ { t.wg.Add(1) go t.send(ttl) - <-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval)) - } - if t.RealtimePrinter != nil { - // 对于实时模式,应该按照TTL进行并发请求 - t.wg.Wait() - t.RealtimePrinter(&t.res, ttl-1) - } - <-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval)) + } + time.Sleep(1 * time.Millisecond) } - go func() { - if t.AsyncPrinter != nil { - for { - t.AsyncPrinter(&t.res) - time.Sleep(200 * time.Millisecond) - } - } - }() - - // 如果是表格模式,则一次性并发请求 - if t.RealtimePrinter == nil { - t.wg.Wait() - } t.res.reduce(t.final) return &t.res, nil @@ -169,7 +149,6 @@ func (t *TCPTracer) listenTCP() { if ch, ok := t.inflightRequest[int(tcp.Ack-1)]; ok { // 最后一跳 ch <- Hop{ - Success: true, Address: msg.Peer, } } @@ -192,7 +171,6 @@ func (t *TCPTracer) handleICMPMessage(msg ReceivedMessage, data []byte) { return } ch <- Hop{ - Success: true, Address: msg.Peer, } @@ -234,11 +212,6 @@ func (t *TCPTracer) send(ttl int) error { ComputeChecksums: true, FixLengths: true, } - - desiredPayloadSize := t.Config.PktSize - payload := make([]byte, desiredPayloadSize) - copy(buf.Bytes(), payload) - if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil { return err } @@ -291,10 +264,6 @@ func (t *TCPTracer) send(ttl int) error { h.TTL = ttl h.RTT = rtt - t.fetchLock.Lock() - defer t.fetchLock.Unlock() - h.fetchIPData(t.Config) - t.res.add(h) case <-time.After(t.Timeout): @@ -303,7 +272,6 @@ func (t *TCPTracer) send(ttl int) error { } t.res.add(Hop{ - Success: false, Address: nil, TTL: ttl, RTT: 0, diff --git a/trace/tcp_ipv6.go b/trace/tcp_ipv6.go index 3574a35..1e229f9 100644 --- a/trace/tcp_ipv6.go +++ b/trace/tcp_ipv6.go @@ -31,8 +31,7 @@ type TCPTracerv6 struct { final int finalLock sync.Mutex - sem *semaphore.Weighted - fetchLock sync.Mutex + sem *semaphore.Weighted } func (t *TCPTracerv6) Execute() (*Result, error) { @@ -64,7 +63,7 @@ func (t *TCPTracerv6) Execute() (*Result, error) { t.sem = semaphore.NewWeighted(int64(t.ParallelRequests)) - for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ { + for ttl := 1; ttl <= t.MaxHops; ttl++ { // 如果到达最终跳,则退出 if t.final != -1 && ttl > t.final { break @@ -72,29 +71,11 @@ func (t *TCPTracerv6) Execute() (*Result, error) { for i := 0; i < t.NumMeasurements; i++ { t.wg.Add(1) go t.send(ttl) - <-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval)) } - if t.RealtimePrinter != nil { - // 对于实时模式,应该按照TTL进行并发请求 - t.wg.Wait() - t.RealtimePrinter(&t.res, ttl-1) - } - <-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval)) + time.Sleep(1 * time.Millisecond) + } - go func() { - if t.AsyncPrinter != nil { - for { - t.AsyncPrinter(&t.res) - time.Sleep(200 * time.Millisecond) - } - } - - }() - - if t.RealtimePrinter == nil { - t.wg.Wait() - } t.res.reduce(t.final) return &t.res, nil @@ -161,7 +142,6 @@ func (t *TCPTracerv6) listenTCP() { if ch, ok := t.inflightRequest[int(tcp.Ack-1)]; ok { // 最后一跳 ch <- Hop{ - Success: true, Address: msg.Peer, } } @@ -181,7 +161,6 @@ func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) { } // log.Println("发送数据", sequenceNumber) ch <- Hop{ - Success: true, Address: msg.Peer, } // log.Println("发送成功") @@ -224,11 +203,6 @@ func (t *TCPTracerv6) send(ttl int) error { ComputeChecksums: true, FixLengths: true, } - - desiredPayloadSize := t.Config.PktSize - payload := make([]byte, desiredPayloadSize) - copy(buf.Bytes(), payload) - if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil { return err } @@ -274,10 +248,6 @@ func (t *TCPTracerv6) send(ttl int) error { h.TTL = ttl h.RTT = rtt - t.fetchLock.Lock() - defer t.fetchLock.Unlock() - h.fetchIPData(t.Config) - t.res.add(h) case <-time.After(t.Timeout): @@ -286,7 +256,6 @@ func (t *TCPTracerv6) send(ttl int) error { } t.res.add(Hop{ - Success: false, Address: nil, TTL: ttl, RTT: 0, diff --git a/trace/temp_printer.go b/trace/temp_printer.go deleted file mode 100644 index 752b350..0000000 --- a/trace/temp_printer.go +++ /dev/null @@ -1,76 +0,0 @@ -package trace - -import ( - "fmt" - "strings" - - "github.com/xgadget-lab/nexttrace/ipgeo" -) - -func HopPrinter(h Hop) { - if h.Address == nil { - fmt.Println("\t*") - } else { - txt := "\t" - - if h.Hostname == "" { - txt += fmt.Sprint(h.Address, " ", fmt.Sprintf("%.2f", h.RTT.Seconds()*1000), "ms") - } else { - txt += fmt.Sprint(h.Hostname, " (", h.Address, ") ", fmt.Sprintf("%.2f", h.RTT.Seconds()*1000), "ms") - } - - if h.Geo != nil { - txt += " " + formatIpGeoData(h.Address.String(), h.Geo) - } - - fmt.Println(txt) - } -} - -func formatIpGeoData(ip string, data *ipgeo.IPGeoData) string { - var res = make([]string, 0, 10) - - if data.Asnumber == "" { - res = append(res, "*") - } else { - res = append(res, "AS"+data.Asnumber) - } - - // TODO: 判断阿里云和腾讯云内网,数据不足,有待进一步完善 - // TODO: 移动IDC判断到Hop.fetchIPData函数,减少API调用 - if strings.HasPrefix(ip, "9.") { - res = append(res, "LAN Address", "") - } else if strings.HasPrefix(ip, "11.") { - res = append(res, "LAN Address", "") - } else if data.Country == "" { - res = append(res, "LAN Address") - } else { - // 有些IP的归属信息为空,这个时候将ISP的信息填入 - if data.Owner == "" { - data.Owner = data.Isp - } - if data.District != "" { - data.City = data.City + ", " + data.District - } - if data.Prov == "" && data.City == "" { - // anyCast或是骨干网数据不应该有国家信息 - data.Owner = data.Owner + ", " + data.Owner - } else { - // 非骨干网正常填入IP的国家信息数据 - res = append(res, data.Country) - } - - if data.Prov != "" { - res = append(res, data.Prov) - } - if data.City != "" { - res = append(res, data.City) - } - - if data.Owner != "" { - res = append(res, data.Owner) - } - } - - return strings.Join(res, ", ") -} diff --git a/trace/trace.go b/trace/trace.go index 13303ea..de96015 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -5,16 +5,12 @@ import ( "net" "sync" "time" - - "github.com/xgadget-lab/nexttrace/ipgeo" - "github.com/xgadget-lab/nexttrace/util" ) var ( ErrInvalidMethod = errors.New("invalid method") ErrTracerouteExecuted = errors.New("traceroute already executed") ErrHopLimitTimeout = errors.New("hop timeout") - geoCache = sync.Map{} ) type Config struct { @@ -27,17 +23,8 @@ type Config struct { DestIP net.IP DestPort int Quic bool - IPGeoSource ipgeo.Source - RDns bool - AlwaysWaitRDNS bool PacketInterval int TTLInterval int - Lang string - DN42 bool - RealtimePrinter func(res *Result, ttl int) - AsyncPrinter func(res *Result) - PktSize int - Maptrace bool } type Method string @@ -93,9 +80,8 @@ func Traceroute(method Method, config Config) (*Result, error) { } type Result struct { - Hops [][]Hop - lock sync.Mutex - TraceMapUrl string + Hops [][]Hop + lock sync.Mutex } func (s *Result) add(hop Hop) { @@ -116,112 +102,9 @@ func (s *Result) reduce(final int) { } type Hop struct { - Success bool Address net.Addr Hostname string TTL int RTT time.Duration Error error - Geo *ipgeo.IPGeoData - Lang string -} - -func (h *Hop) fetchIPData(c Config) (err error) { - - // DN42 - if c.DN42 { - var ip string - // 首先查找 PTR 记录 - r, err := util.LookupAddr(h.Address.String()) - if err == nil && len(r) > 0 { - h.Hostname = r[0][:len(r[0])-1] - ip = h.Address.String() + "," + h.Hostname - } - h.Geo, err = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace) - return nil - } - - // Debug Area - // c.AlwaysWaitRDNS = true - - // Initialize a rDNS Channel - rDNSChan := make(chan []string) - fetchDoneChan := make(chan bool) - - if c.RDns && h.Hostname == "" { - // Create a rDNS Query go-routine - go func() { - r, err := util.LookupAddr(h.Address.String()) - if err != nil { - // No PTR Record - rDNSChan <- nil - } else { - // One PTR Record is found - rDNSChan <- r - } - }() - } - - // Create Data Fetcher go-routine - go func() { - // Start to fetch IP Geolocation data - if c.IPGeoSource != nil && h.Geo == nil { - res := false - h.Lang = c.Lang - h.Geo, res = ipgeo.Filter(h.Address.String()) - if !res { - timeout := c.Timeout - if c.Timeout < 2*time.Second { - timeout = 2 * time.Second - } - //h.Geo, err = c.IPGeoSource(h.Address.String(), timeout, c.Lang, c.Maptrace) - if cacheVal, ok := geoCache.Load(h.Address.String()); ok { - // 如果缓存中已有结果,直接使用 - h.Geo = cacheVal.(*ipgeo.IPGeoData) - } else { - // 如果缓存中无结果,进行查询并将结果存入缓存 - h.Geo, err = c.IPGeoSource(h.Address.String(), timeout, c.Lang, c.Maptrace) - if err == nil { - geoCache.Store(h.Address.String(), h.Geo) - } - } - } - } - // Fetch Done - fetchDoneChan <- true - }() - - // Select Close Flag - var selectClose bool - if !c.AlwaysWaitRDNS { - select { - case <-fetchDoneChan: - // When fetch done signal recieved, stop waiting PTR record - case ptr := <-rDNSChan: - // process result - if err == nil && len(ptr) > 0 { - h.Hostname = ptr[0][:len(ptr[0])-1] - } - selectClose = true - } - } else { - select { - case ptr := <-rDNSChan: - // process result - if err == nil && len(ptr) > 0 { - h.Hostname = ptr[0] - } - // 1 second timeout - case <-time.After(time.Second * 1): - } - selectClose = true - } - - // When Select Close, fetchDoneChan Reciever will also be closed - if selectClose { - // New a reciever to prevent channel congestion - <-fetchDoneChan - } - - return } diff --git a/trace/udp.go b/trace/udp_ipv4.go similarity index 85% rename from trace/udp.go rename to trace/udp_ipv4.go index a8f7b1a..eb5f918 100644 --- a/trace/udp.go +++ b/trace/udp_ipv4.go @@ -28,8 +28,7 @@ type UDPTracer struct { final int finalLock sync.Mutex - sem *semaphore.Weighted - fetchLock sync.Mutex + sem *semaphore.Weighted } func (t *UDPTracer) Execute() (*Result, error) { @@ -61,26 +60,9 @@ func (t *UDPTracer) Execute() (*Result, error) { for i := 0; i < t.NumMeasurements; i++ { t.wg.Add(1) go t.send(ttl) - <-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval)) + } - if t.RealtimePrinter != nil { - // 对于实时模式,应该按照TTL进行并发请求 - t.wg.Wait() - t.RealtimePrinter(&t.res, ttl-1) - } - <-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval)) - } - go func() { - if t.AsyncPrinter != nil { - for { - t.AsyncPrinter(&t.res) - time.Sleep(200 * time.Millisecond) - } - } - }() - // 如果是表格模式,则一次性并发请求 - if t.AsyncPrinter != nil { - t.wg.Wait() + time.Sleep(1 * time.Millisecond) } t.res.reduce(t.final) @@ -129,7 +111,6 @@ func (t *UDPTracer) handleICMPMessage(msg ReceivedMessage, data []byte) { return } ch <- Hop{ - Success: true, Address: msg.Peer, } } @@ -189,12 +170,7 @@ func (t *UDPTracer) send(ttl int) error { ComputeChecksums: true, FixLengths: true, } - - desiredPayloadSize := t.Config.PktSize - payload := make([]byte, desiredPayloadSize) - copy(buf.Bytes(), payload) - - if err := gopacket.SerializeLayers(buf, opts, udpHeader); err != nil { + if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload("HAJSFJHKAJSHFKJHAJKFHKASHKFHHKAFKHFAHSJK")); err != nil { return err } @@ -231,7 +207,6 @@ func (t *UDPTracer) send(ttl int) error { return } hopCh <- Hop{ - Success: true, Address: peer, } }() @@ -262,10 +237,6 @@ func (t *UDPTracer) send(ttl int) error { h.TTL = ttl h.RTT = rtt - t.fetchLock.Lock() - defer t.fetchLock.Unlock() - h.fetchIPData(t.Config) - t.res.add(h) case <-time.After(t.Timeout): @@ -274,7 +245,6 @@ func (t *UDPTracer) send(ttl int) error { } t.res.add(Hop{ - Success: false, Address: nil, TTL: ttl, RTT: 0, diff --git a/tracelog/log.go b/tracelog/log.go deleted file mode 100644 index 2008be3..0000000 --- a/tracelog/log.go +++ /dev/null @@ -1,116 +0,0 @@ -package tracelog - -import ( - "fmt" - "io" - "log" - "net" - "os" - "strconv" - "strings" - - "github.com/xgadget-lab/nexttrace/trace" -) - -func RealtimePrinter(res *trace.Result, ttl int) { - f, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm) - if err != nil { - return - } - defer func(f *os.File) { - err := f.Close() - if err != nil { - log.Fatal(err) - } - }(f) - - multiWriter := io.MultiWriter(os.Stdout, f) - log.SetOutput(multiWriter) - log.SetFlags(0) - var res_str string - res_str += fmt.Sprintf("%-2d ", ttl+1) - - // 去重 - var latestIP string - tmpMap := make(map[string][]string) - for i, v := range res.Hops[ttl] { - if v.Address == nil && latestIP != "" { - tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*")) - continue - } else if v.Address == nil { - continue - } - - if _, exist := tmpMap[v.Address.String()]; !exist { - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i)) - // 首次进入 - if latestIP == "" { - for j := 0; j < i; j++ { - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*")) - } - } - latestIP = v.Address.String() - } - - tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000)) - } - - if latestIP == "" { - res_str += fmt.Sprintf("%s\n", "*") - log.Print(res_str) - return - } - - var blockDisplay = false - for ip, v := range tmpMap { - if blockDisplay { - res_str += fmt.Sprintf("%4s", "") - } - if net.ParseIP(ip).To4() == nil { - res_str += fmt.Sprintf("%-25s ", ip) - } else { - res_str += fmt.Sprintf("%-15s ", ip) - } - - i, _ := strconv.Atoi(v[0]) - - if res.Hops[ttl][i].Geo.Asnumber != "" { - res_str += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber) - } else { - res_str += fmt.Sprintf(" %-8s", "*") - } - - if net.ParseIP(ip).To4() != nil { - whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-") - if len(whoisFormat) > 1 { - whoisFormat[0] = strings.Join(whoisFormat[:2], "-") - } - - if whoisFormat[0] != "" { - whoisFormat[0] = "[" + whoisFormat[0] + "]" - } - res_str += fmt.Sprintf("%-16s", whoisFormat[0]) - } - - if res.Hops[ttl][i].Geo.Country == "" { - res.Hops[ttl][i].Geo.Country = "LAN Address" - } - - if net.ParseIP(ip).To4() != nil { - - res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-39s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname) - } else { - res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-35s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname) - } - - for j := 1; j < len(v); j++ { - if len(v) == 2 || j == 1 { - res_str += v[j] - } else { - res_str += fmt.Sprintf("/ %s", v[j]) - } - } - log.Print(res_str) - blockDisplay = true - } -} diff --git a/tracemap/tracemap.go b/tracemap/tracemap.go index ad33ddd..e00a195 100644 --- a/tracemap/tracemap.go +++ b/tracemap/tracemap.go @@ -1,74 +1,20 @@ package tracemap import ( - "crypto/tls" - "errors" "fmt" - "github.com/fatih/color" - "github.com/xgadget-lab/nexttrace/util" "io" - "net" "net/http" - "net/url" "strings" - "time" + + "github.com/fatih/color" ) -func GetMapUrl(r string) (string, error) { - host, port := util.GetHostAndPort() - fastIp := "api.leo.moe" - // 如果 host 是一个 IP 使用默认域名 - if valid := net.ParseIP(host); valid != nil { - host = "api.leo.moe" - } else { - // 默认配置完成,开始寻找最优 IP - fastIp = util.GetFastIP(host, port, false) - } - u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: "/tracemap/api"} - tracemapUrl := u.String() - - client := &http.Client{ - Timeout: 5 * time.Second, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - ServerName: host, - }, - }, - } - proxyUrl := util.GetProxy() - if proxyUrl != nil { - client.Transport.(*http.Transport).Proxy = http.ProxyURL(proxyUrl) - } - req, err := http.NewRequest("POST", tracemapUrl, strings.NewReader(r)) - if err != nil { - return "", errors.New("an issue occurred while connecting to the tracemap API") - } - req.Header.Add("User-Agent", util.UserAgent) - req.Host = host - req.Header.Add("Content-Type", "application/json") - resp, err := client.Do(req) - if err != nil { - return "", errors.New("an issue occurred while connecting to the tracemap API") - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - return - } - }(resp.Body) - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", errors.New("an issue occurred while connecting to the tracemap API") - } - return string(body), nil -} - -func PrintMapUrl(r string) { - _, err := fmt.Fprintf(color.Output, "%s %s\n", +func GetMapUrl(r string) { + url := "https://api.leo.moe/tracemap/api" + resp, _ := http.Post(url, "application/json", strings.NewReader(string(r))) + body, _ := io.ReadAll(resp.Body) + fmt.Fprintf(color.Output, "%s %s\n", color.New(color.FgWhite, color.Bold).Sprintf("%s", "MapTrace URL:"), - color.New(color.FgBlue, color.Bold).Sprintf("%s", string(r)), + color.New(color.FgBlue, color.Bold).Sprintf("%s", string(body)), ) - if err != nil { - return - } } diff --git a/util/dns_test.go b/util/dns_test.go index 568d599..9730b34 100644 --- a/util/dns_test.go +++ b/util/dns_test.go @@ -8,13 +8,6 @@ import ( func TestDNS(t *testing.T) { resolver := DNSSB() - ips, _ := resolver.LookupHost(context.Background(), "www.bing.com") - fmt.Println(ips) -} - -func TestDomainLookUp(t *testing.T) { - ips := DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "all", "", false) - fmt.Println(ips) - ips = DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "4", "", false) + ips, _ := resolver.LookupHost(context.Background(), "www.google.com") fmt.Println(ips) } diff --git a/util/util.go b/util/util.go index 27afdce..1173859 100644 --- a/util/util.go +++ b/util/util.go @@ -3,42 +3,14 @@ package util import ( "context" "fmt" - "github.com/xgadget-lab/nexttrace/config" "log" "net" - "net/url" "os" - "runtime" - "strings" - "sync" "github.com/fatih/color" ) -var Uninterrupted = GetenvDefault("NEXTTRACE_UNINTERRUPTED", "") -var EnvToken = GetenvDefault("NEXTTRACE_TOKEN", "") -var UserAgent = fmt.Sprintf("NextTrace %s/%s/%s", config.Version, runtime.GOOS, runtime.GOARCH) -var RdnsCache sync.Map - -func LookupAddr(addr string) ([]string, error) { - // 如果在缓存中找到,直接返回 - if hostname, ok := RdnsCache.Load(addr); ok { - //fmt.Println("hit RdnsCache for", addr, hostname) - return []string{hostname.(string)}, nil - } - // 如果缓存中未找到,进行 DNS 查询 - names, err := net.LookupAddr(addr) - if err != nil { - return nil, err - } - // 将查询结果存入缓存 - if len(names) > 0 { - RdnsCache.Store(addr, names[0]) - } - return names, nil -} - -// LocalIPPort get the local ip and port based on our destination ip +// get the local ip and port based on our destination ip func LocalIPPort(dstip net.IP) (net.IP, int) { serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345") if err != nil { @@ -73,8 +45,7 @@ func LocalIPPortv6(dstip net.IP) (net.IP, int) { return nil, -1 } -func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) net.IP { - // ipVersion: 4, 6, all +func DomainLookUp(host string, ipv4Only bool, dotServer string) net.IP { var ( r *net.Resolver ips []net.IP @@ -94,8 +65,8 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput default: r = newUDPResolver() } - ipsStr, err := r.LookupHost(context.Background(), host) - for _, v := range ipsStr { + ips_str, err := r.LookupHost(context.Background(), host) + for _, v := range ips_str { ips = append(ips, net.ParseIP(v)) } if err != nil { @@ -103,29 +74,16 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput os.Exit(1) } - //var ipv6Flag = false - //TODO: 此处代码暂无意义 - //if ipv6Flag { - // fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.") - // if len(ips) == 0 { - // os.Exit(0) - // } - //} + var ipv6Flag = false - // Filter by IPv4/IPv6 - if ipVersion != "all" { - var filteredIPs []net.IP - for _, ip := range ips { - if ipVersion == "4" && ip.To4() != nil { - filteredIPs = append(filteredIPs, ip) - } else if ipVersion == "6" && strings.Contains(ip.String(), ":") { - filteredIPs = append(filteredIPs, ip) - } + if ipv6Flag { + fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.") + if len(ips) == 0 { + os.Exit(0) } - ips = filteredIPs } - if (len(ips) == 1) || (disableOutput) { + if len(ips) == 1 { return ips[0] } else { fmt.Println("Please Choose the IP You Want To TraceRoute") @@ -137,10 +95,7 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput } var index int fmt.Printf("Your Option: ") - _, err := fmt.Scanln(&index) - if err != nil { - index = 0 - } + fmt.Scanln(&index) if index >= len(ips) || index < 0 { fmt.Println("Your Option is invalid") os.Exit(3) @@ -156,43 +111,3 @@ func GetenvDefault(key, defVal string) string { } return defVal } - -func GetHostAndPort() (host string, port string) { - var hostP = GetenvDefault("NEXTTRACE_HOSTPORT", "api.leo.moe") - // 解析域名 - hostArr := strings.Split(hostP, ":") - // 判断是否有指定端口 - if len(hostArr) > 1 { - // 判断是否为 IPv6 - if strings.HasPrefix(hostP, "[") { - tmp := strings.Split(hostP, "]") - host = tmp[0] - host = host[1:] - if port = tmp[1]; port != "" { - port = port[1:] - } - } else { - host, port = hostArr[0], hostArr[1] - } - } else { - host = hostP - } - if port == "" { - // 默认端口 - port = "443" - } - return -} - -func GetProxy() *url.URL { - proxyURLStr := GetenvDefault("NEXTTRACE_PROXY", "") - if proxyURLStr == "" { - return nil - } - proxyURL, err := url.Parse(proxyURLStr) - if err != nil { - log.Println("Failed to parse proxy URL:", err) - return nil - } - return proxyURL -} diff --git a/wshandle/client.go b/wshandle/client.go index c02734b..b8bade1 100644 --- a/wshandle/client.go +++ b/wshandle/client.go @@ -2,18 +2,18 @@ package wshandle import ( "crypto/tls" - "github.com/xgadget-lab/nexttrace/pow" - "github.com/xgadget-lab/nexttrace/util" "log" "net" "net/http" "net/url" "os" "os/signal" + "strings" "sync" "time" "github.com/gorilla/websocket" + "github.com/xgadget-lab/nexttrace/util" ) type WsConn struct { @@ -29,10 +29,8 @@ type WsConn struct { } var wsconn *WsConn -var host, port, fastIp string -var envToken = util.EnvToken -var cacheToken string -var cacheTokenFailedTimes int +var hostP = util.GetenvDefault("NEXTTRACE_HOSTPORT", "api.leo.moe") +var host, port, fast_ip string func (c *WsConn) keepAlive() { go func() { @@ -118,40 +116,15 @@ func (c *WsConn) messageSendHandler() { } func (c *WsConn) recreateWsConn() { - // 尝试重新连线 - u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"} + u := url.URL{Scheme: "wss", Host: fast_ip + ":" + port, Path: "/v2/ipGeoWs"} // log.Printf("connecting to %s", u.String()) - jwtToken, ua := envToken, []string{"Privileged Client"} - err := error(nil) - if envToken == "" { - // 无环境变量 token - if cacheToken == "" { - // 无cacheToken, 重新获取 token - jwtToken, err = pow.GetToken(fastIp, host, port) - if err != nil { - log.Println(err) - os.Exit(1) - } - } else { - // 使用 cacheToken - jwtToken = cacheToken - } - ua = []string{util.UserAgent} - } - cacheToken = jwtToken requestHeader := http.Header{ - "Host": []string{host}, - "User-Agent": ua, - "Authorization": []string{"Bearer " + jwtToken}, + "Host": []string{host}, } dialer := websocket.DefaultDialer dialer.TLSClientConfig = &tls.Config{ ServerName: host, } - proxyUrl := util.GetProxy() - if proxyUrl != nil { - dialer.Proxy = http.ProxyURL(proxyUrl) - } ws, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader) c.Conn = ws if err != nil { @@ -159,11 +132,6 @@ func (c *WsConn) recreateWsConn() { // <-time.After(time.Second * 1) c.Connected = false c.Connecting = false - if cacheTokenFailedTimes > 3 { - cacheToken = "" - } - cacheTokenFailedTimes += 1 - //fmt.Println("重连失败", cacheTokenFailedTimes, "次") return } else { c.Connected = true @@ -175,40 +143,47 @@ func (c *WsConn) recreateWsConn() { } func createWsConn() *WsConn { - //fmt.Println("正在连接 WS") // 设置终端中断通道 interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) - host, port = util.GetHostAndPort() + // 解析域名 + hostArr := strings.Split(hostP, ":") + // 判断是否有指定端口 + if len(hostArr) > 1 { + // 判断是否为 IPv6 + if strings.HasPrefix(hostP, "[") { + tmp := strings.Split(hostP, "]") + host = tmp[0] + host = host[1:] + if port = tmp[1]; port != "" { + port = port[1:] + } + } else { + host, port = hostArr[0], hostArr[1] + } + } else { + host = hostP + } + if port == "" { + // 默认端口 + port = "443" + } + // 默认配置完成,开始寻找最优 IP + fast_ip = GetFastIP(host, port) + // 如果 host 是一个 IP 使用默认域名 if valid := net.ParseIP(host); valid != nil { host = "api.leo.moe" - } else { - // 默认配置完成,开始寻找最优 IP - fastIp = util.GetFastIP(host, port, true) } - jwtToken, ua := envToken, []string{"Privileged Client"} - err := error(nil) - if envToken == "" { - jwtToken, err = pow.GetToken(fastIp, host, port) - if err != nil { - log.Println(err) - os.Exit(1) - } - ua = []string{util.UserAgent} - } - cacheToken = jwtToken - cacheTokenFailedTimes = 0 + // 判断是否是一个 IP requestHeader := http.Header{ - "Host": []string{host}, - "User-Agent": ua, - "Authorization": []string{"Bearer " + jwtToken}, + "Host": []string{host}, } dialer := websocket.DefaultDialer dialer.TLSClientConfig = &tls.Config{ ServerName: host, } - u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"} + u := url.URL{Scheme: "wss", Host: fast_ip + ":" + port, Path: "/v2/ipGeoWs"} // log.Printf("connecting to %s", u.String()) c, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader) diff --git a/util/latency.go b/wshandle/latency.go similarity index 68% rename from util/latency.go rename to wshandle/latency.go index 21fd061..aae0af9 100644 --- a/util/latency.go +++ b/wshandle/latency.go @@ -1,4 +1,4 @@ -package util +package wshandle import ( "fmt" @@ -14,16 +14,13 @@ var ( result string results = make(chan string) ) -var FastIpCache = "" -func GetFastIP(domain string, port string, enableOutput bool) string { - if FastIpCache != "" { - return FastIpCache - } +func GetFastIP(domain string, port string) string { ips, err := net.LookupIP(domain) if err != nil { log.Fatal("DNS resolution failed, please check your system DNS Settings") + return "" } for _, ip := range ips { @@ -33,6 +30,7 @@ func GetFastIP(domain string, port string, enableOutput bool) string { select { case result = <-results: case <-time.After(1 * time.Second): + } if result == "" { log.Fatal("IP connection has been timeout, please check your network") @@ -40,15 +38,13 @@ func GetFastIP(domain string, port string, enableOutput bool) string { res := strings.Split(result, "-") if len(ips) > 1 { - if enableOutput { - _, _ = fmt.Fprintf(color.Output, "%s prefered API IP - %s - %s\n", - color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"), - color.New(color.FgGreen, color.Bold).Sprintf("%s", res[0]), - color.New(color.FgCyan, color.Bold).Sprintf("%sms", res[1]), - ) - } + _, _ = fmt.Fprintf(color.Output, "%s prefered API IP - %s - %s\n", + color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"), + color.New(color.FgGreen, color.Bold).Sprintf("%s", res[0]), + color.New(color.FgCyan, color.Bold).Sprintf("%sms", res[1]), + ) } - FastIpCache = res[0] + return res[0] } diff --git a/util/latency_test.go b/wshandle/latency_test.go similarity index 53% rename from util/latency_test.go rename to wshandle/latency_test.go index 4d5f85a..ba5ab82 100644 --- a/util/latency_test.go +++ b/wshandle/latency_test.go @@ -1,9 +1,9 @@ -package util +package wshandle import ( "testing" ) func TestGetFastIP(t *testing.T) { - GetFastIP("api.leo.moe", "443", true) + GetFastIP("api.leo.moe", "443") }