first release
This commit is contained in:
parent
4f7dfab7d9
commit
604f9fb876
59
.github/workflows/main.yml
vendored
Normal file
59
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
publish_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# checkout code
|
||||
- uses: actions/checkout@master
|
||||
# create release
|
||||
- uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: |
|
||||
Changes in this Release
|
||||
- First Change
|
||||
- Second Change
|
||||
draft: false
|
||||
prerelease: false
|
||||
# build binaries
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.13.5'
|
||||
- name: build
|
||||
run: |
|
||||
mkdir pingme-linux-amd64
|
||||
mkdir pingme-darwin-amd64
|
||||
env VERSION=$(echo ${GITHUB_REF:10}) GIN_MODE=release GOOS=linux GOARCH=amd64 bash -c 'go build -ldflags "-X main.VersionString=$VERSION" -o pingme-linux-amd64/pingme *.go'
|
||||
env VERSION=$(echo ${GITHUB_REF:10}) GIN_MODE=release GOOS=darwin GOARCH=amd64 bash -c 'go build -ldflags "-X main.VersionString=$VERSION" -o pingme-darwin-amd64/pingme *.go'
|
||||
tar -C $PWD -cvzf pingme-linux-amd64.tar.gz pingme-linux-amd64
|
||||
tar -C $PWD -cvzf pingme-darwin-amd64.tar.gz pingme-darwin-amd64
|
||||
# upload binaries linux
|
||||
- uses: actions/upload-release-asset@v1.0.1
|
||||
id: upload-release-asset-linux
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./pingme-linux-amd64.tar.gz
|
||||
asset_name: pingme-linux-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
# upload binaries darwin
|
||||
- uses: actions/upload-release-asset@v1.0.1
|
||||
id: upload-release-asset-darwin
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./pingme-darwin-amd64.tar.gz
|
||||
asset_name: pingme-darwin-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Binary Output
|
||||
pingme
|
||||
pingme-cli
|
||||
|
||||
# Script
|
||||
*.sh
|
||||
|
||||
# Log
|
||||
*.log
|
||||
*.dat
|
||||
*.txt
|
||||
|
||||
# Database
|
||||
*.db
|
||||
|
||||
# Config
|
||||
config.json
|
20
LICENSE.md
Normal file
20
LICENSE.md
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2019-2020 Noobly
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
109
README.md
109
README.md
@ -1 +1,108 @@
|
||||
# pingme
|
||||
# Introduction
|
||||
|
||||
Pingme is ping probe command line tool, supporting ICMP, TCP and HTTP protocols.
|
||||
|
||||
You can also use it to query IP information from third-party api provider (currently we use [https://ip-api.com](https://ip-api.com)).
|
||||
|
||||
# Features
|
||||
|
||||
- Support ICMP/TCP/HTTP protocols
|
||||
- Query basic IP information
|
||||
|
||||
# Installation
|
||||
|
||||
1. Download latest [release](https://github.com/noobly314/pingme/releases/latest) (recommend)
|
||||
|
||||
2. Use go get
|
||||
|
||||
```
|
||||
go get -u github.com/noobly314/pingme
|
||||
```
|
||||
|
||||
3. Build on your own
|
||||
|
||||
```
|
||||
git clone https://github.com/noobly314/pingme.git
|
||||
cd pingme
|
||||
go build
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
```
|
||||
-h string
|
||||
HTTP Ping
|
||||
-i string
|
||||
ICMP Ping
|
||||
-m string
|
||||
MTR Trace
|
||||
-q string
|
||||
Query ip information
|
||||
-t string
|
||||
TCP Ping
|
||||
-v Version
|
||||
```
|
||||
|
||||
# Examples
|
||||
|
||||
```
|
||||
$ pingme -h https://www.google.com
|
||||
Proxy : false
|
||||
Scheme : https
|
||||
Host : www.google.com
|
||||
DNS Lookup: 2.05 ms
|
||||
TCP : 2.41 ms
|
||||
TLS : 68.92 ms
|
||||
Process : 29.28 ms
|
||||
Transfer : 0.19 ms
|
||||
Total : 103.06 ms
|
||||
```
|
||||
|
||||
```
|
||||
$ pingme -i www.google.com
|
||||
ICMP OPEN 74.125.200.147 2.2 ms
|
||||
```
|
||||
|
||||
```
|
||||
$ pingme -t www.google.com:443
|
||||
TCP OPEN www.google.com:443
|
||||
```
|
||||
|
||||
```
|
||||
$ pingme -q www.google.com
|
||||
IP : 172.217.194.103
|
||||
City : Queenstown Estate
|
||||
Country: Singapore
|
||||
ISP : Google LLC
|
||||
AS : AS15169 Google LLC
|
||||
```
|
||||
|
||||
# Note
|
||||
|
||||
Root permission is required when running ICMP ping, since it needs to open raw socket.
|
||||
|
||||
You can either use sudo command, or set setuid bit for pingme.
|
||||
|
||||
```
|
||||
// Use sudo for one-time ping
|
||||
$ sudo pingme -i google.com
|
||||
|
||||
// Set setuid bit
|
||||
$ sudo chown root:root pingme
|
||||
$ sudo chmod u+s pingme
|
||||
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
See the [LICENSE](https://github.com/noobly314/pingme/blob/master/LICENSE.md) file for license rights and limitations (MIT).
|
||||
|
||||
# Acknowledgements
|
||||
|
||||
[https://ip-api.com](https://ip-api.com)
|
||||
|
||||
[lmas/icmp_ping.go](https://gist.github.com/lmas/c13d1c9de3b2224f9c26435eb56e6ef3)
|
||||
|
||||
[sparrc/go-ping](https://github.com/sparrc/go-ping)
|
||||
|
||||
[davecheney/httpstat](https://github.com/davecheney/httpstat)
|
||||
|
49
flag.go
Normal file
49
flag.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Version bool
|
||||
var PingDst string
|
||||
var TCPingDst string
|
||||
var HTTPingDst string
|
||||
var MtrDst string
|
||||
var Query string
|
||||
|
||||
var CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
var Usage = func() {
|
||||
fmt.Fprintf(CommandLine.Output(), "Usage:\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func init_flag() {
|
||||
flag.BoolVar(&Version, "v", false, "Version")
|
||||
flag.StringVar(&PingDst, "i", "", "ICMP Ping")
|
||||
flag.StringVar(&TCPingDst, "t", "", "TCP Ping")
|
||||
flag.StringVar(&HTTPingDst, "h", "", "HTTP Ping")
|
||||
flag.StringVar(&MtrDst, "m", "", "MTR Trace")
|
||||
flag.StringVar(&Query, "q", "", "Query ip information")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func hasFlag() bool {
|
||||
found := false
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
found = true
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func isFlagPassed(name string) bool {
|
||||
found := false
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
if f.Name == name {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
return found
|
||||
}
|
17
go.mod
Normal file
17
go.mod
Normal file
@ -0,0 +1,17 @@
|
||||
module github.com/noobly314/pingme
|
||||
|
||||
require (
|
||||
github.com/aeden/traceroute v0.0.0-20181124220833-147686d9cb0f
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/gin-contrib/cors v1.3.0
|
||||
github.com/gin-gonic/gin v1.5.0
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.10 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
go.etcd.io/bbolt v1.3.3
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
|
||||
)
|
||||
|
||||
go 1.13
|
96
go.sum
Normal file
96
go.sum
Normal file
@ -0,0 +1,96 @@
|
||||
github.com/aeden/traceroute v0.0.0-20181124220833-147686d9cb0f h1:LbPChgmEVYTHVg0zmAydDO3YNRgkt31svsI7LJ0crjs=
|
||||
github.com/aeden/traceroute v0.0.0-20181124220833-147686d9cb0f/go.mod h1:WwE/rUGG8pQ7L4JiBoNPCTZGQGWnciVaM+pXYfQR9ps=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gin-contrib/cors v1.3.0 h1:PolezCc89peu+NgkIWt9OB01Kbzt6IP0J/JvkG6xxlg=
|
||||
github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/noobly314/pingme v0.0.0-20191212162823-642d9c7d6afb h1:51sLIkuBVRY1BYlv1lxfG1MfugQ+x+7p68l8EMtiiTU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c h1:gqEdF4VwBu3lTKGHS9rXE9x1/pEaSwCXRLOZRF6qtlw=
|
||||
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c/go.mod h1:eMyUVp6f/5jnzM+3zahzl7q6UXLbgSc3MKg/+ow9QW0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
105
httping/httping.go
Normal file
105
httping/httping.go
Normal file
@ -0,0 +1,105 @@
|
||||
package httping
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpproxy"
|
||||
)
|
||||
|
||||
type Stats struct {
|
||||
Proxy bool
|
||||
Scheme string
|
||||
DNS int64
|
||||
TCP int64
|
||||
TLS int64
|
||||
Process int64
|
||||
Transfer int64
|
||||
Total int64
|
||||
}
|
||||
|
||||
func New(address string) (Stats, error) {
|
||||
var err error
|
||||
var stats Stats
|
||||
var t0, t1, t2, t3, t4, t5, t6, t7 int64
|
||||
|
||||
req, _ := http.NewRequest("GET", address, nil)
|
||||
trace := &httptrace.ClientTrace{
|
||||
DNSStart: func(info httptrace.DNSStartInfo) {
|
||||
t0 = time.Now().UnixNano()
|
||||
},
|
||||
DNSDone: func(info httptrace.DNSDoneInfo) {
|
||||
t1 = time.Now().UnixNano()
|
||||
if info.Err != nil {
|
||||
err = info.Err
|
||||
log.Fatal(info.Err)
|
||||
}
|
||||
},
|
||||
ConnectStart: func(net, addr string) {
|
||||
},
|
||||
ConnectDone: func(net, addr string, err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("unable to connect to host %v: %v", addr, err)
|
||||
}
|
||||
t2 = time.Now().UnixNano()
|
||||
},
|
||||
GotConn: func(info httptrace.GotConnInfo) {
|
||||
t3 = time.Now().UnixNano()
|
||||
},
|
||||
GotFirstResponseByte: func() {
|
||||
t4 = time.Now().UnixNano()
|
||||
},
|
||||
TLSHandshakeStart: func() {
|
||||
t5 = time.Now().UnixNano()
|
||||
},
|
||||
TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
|
||||
t6 = time.Now().UnixNano()
|
||||
},
|
||||
}
|
||||
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||
c := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
_, err = c.Do(req)
|
||||
if err != nil {
|
||||
match, _ := regexp.MatchString("Client.Timeout exceeded", err.Error())
|
||||
if match {
|
||||
log.Fatal("Connection timeout")
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
t7 = time.Now().UnixNano()
|
||||
|
||||
if strings.HasPrefix(address, "http://") {
|
||||
stats.Scheme = "http"
|
||||
} else if strings.HasPrefix(address, "https://") {
|
||||
stats.Scheme = "https"
|
||||
}
|
||||
|
||||
if t0 == 0 {
|
||||
t0 = t2
|
||||
t1 = t2
|
||||
}
|
||||
|
||||
stats.DNS = t1 - t0
|
||||
stats.TCP = t2 - t1
|
||||
stats.Process = t4 - t3
|
||||
stats.Transfer = t7 - t4
|
||||
stats.TLS = t6 - t5
|
||||
stats.Total = t7 - t0
|
||||
|
||||
// Detect proxies
|
||||
pc := httpproxy.FromEnvironment()
|
||||
if pc.HTTPProxy != "" {
|
||||
stats.Proxy = true
|
||||
}
|
||||
|
||||
return stats, err
|
||||
}
|
102
log.go
Normal file
102
log.go
Normal file
@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/noobly314/pingme/httping"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
var (
|
||||
cyan = color.New(color.FgCyan).SprintfFunc()
|
||||
blue = color.New(color.FgBlue).SprintfFunc()
|
||||
green = color.New(color.FgGreen).SprintfFunc()
|
||||
yellow = color.New(color.FgYellow).SprintfFunc()
|
||||
red = color.New(color.FgRed).SprintfFunc()
|
||||
)
|
||||
|
||||
func init_log() {
|
||||
formatter := &logrus.TextFormatter{
|
||||
DisableTimestamp: true,
|
||||
}
|
||||
log.SetFormatter(formatter)
|
||||
log.Out = os.Stdout
|
||||
}
|
||||
|
||||
func logHttping(stats httping.Stats, err error, address string) {
|
||||
if err == nil {
|
||||
fmt.Printf("%s: %s\n", cyan("%-10s", "Proxy"), strconv.FormatBool(stats.Proxy))
|
||||
fmt.Printf("%s: %s\n", cyan("%-10s", "Scheme"), stats.Scheme)
|
||||
fmt.Printf("%s: %s\n", cyan("%-10s", "Host"), parseInput(address))
|
||||
fmt.Printf("%s: %.2f ms\n", cyan("%-10s", "DNS Lookup"), float64(stats.DNS)/1e6)
|
||||
fmt.Printf("%s: %.2f ms\n", cyan("%-10s", "TCP"), float64(stats.TCP)/1e6)
|
||||
if stats.Scheme == "https" {
|
||||
fmt.Printf("%s: %.2f ms\n", cyan("%-10s", "TLS"), float64(stats.TLS)/1e6)
|
||||
}
|
||||
fmt.Printf("%s: %.2f ms\n", cyan("%-10s", "Process"), float64(stats.Process)/1e6)
|
||||
fmt.Printf("%s: %.2f ms\n", cyan("%-10s", "Transfer"), float64(stats.Transfer)/1e6)
|
||||
fmt.Printf("%s: %.2f ms\n", cyan("%-10s", "Total"), float64(stats.Total)/1e6)
|
||||
}
|
||||
}
|
||||
|
||||
func logTcping(code int, address string) {
|
||||
if code == 0 {
|
||||
fmt.Printf("%s%s%s\n", cyan("%-7s", "TCP"), green("%-10s", "OPEN"), address)
|
||||
} else if code == 1 {
|
||||
fmt.Printf("%s%s%s\n", cyan("%-7s", "TCP"), yellow("%-10s", "CLOSED"), address)
|
||||
} else if code == 2 {
|
||||
fmt.Printf("%s%s%s\n", cyan("%-7s", "TCP"), red("%-10s", "ERROR"), address)
|
||||
}
|
||||
}
|
||||
|
||||
func logPing(dst *net.IPAddr, dur time.Duration, err error) {
|
||||
if err != nil {
|
||||
match, _ := regexp.MatchString("operation not permitted", err.Error())
|
||||
if match {
|
||||
fmt.Printf("%s%s%s\n", cyan("%-7s", "ICMP"), red("%-10s", "ERROR"), red("No privileges"))
|
||||
} else {
|
||||
fmt.Printf("%s%s%s\n", cyan("%-7s", "ICMP"), red("%-10s", "ERROR"), dst.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s%s%s %s ms\n", cyan("%-7s", "ICMP"), green("%-10s", "OPEN"), dst.String(), fmt.Sprintf("%.1f", float64(dur.Microseconds())/1000))
|
||||
}
|
||||
|
||||
func logMtr(hops []string, address string) {
|
||||
for _, h := range hops {
|
||||
fmt.Printf("%s%s\n", cyan("%-7s", "MTR"), h)
|
||||
}
|
||||
}
|
||||
|
||||
func logQuery(info IPInfo) {
|
||||
v := reflect.ValueOf(info)
|
||||
names := make([]string, v.NumField())
|
||||
values := make([]string, v.NumField())
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
names[i] = v.Type().Field(i).Name
|
||||
values[i] = v.Field(i).Interface().(string)
|
||||
}
|
||||
l := getMaxNameLength(names)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fmt.Printf("%s: %s\n", cyan("%-*s", l, names[i]), values[i])
|
||||
}
|
||||
}
|
||||
|
||||
func getMaxNameLength(names []string) int {
|
||||
var length int
|
||||
for _, val := range names {
|
||||
if len(val) > length {
|
||||
length = len(val)
|
||||
}
|
||||
}
|
||||
return length
|
||||
}
|
82
main.go
Normal file
82
main.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/noobly314/pingme/httping"
|
||||
"github.com/noobly314/pingme/mtr"
|
||||
"github.com/noobly314/pingme/ping"
|
||||
"github.com/noobly314/pingme/tcping"
|
||||
)
|
||||
|
||||
var (
|
||||
VersionString string
|
||||
)
|
||||
|
||||
func init() {
|
||||
init_log()
|
||||
init_flag()
|
||||
}
|
||||
|
||||
func main() {
|
||||
if !hasFlag() {
|
||||
switch len(flag.Args()) {
|
||||
case 0:
|
||||
flag.PrintDefaults()
|
||||
case 1:
|
||||
addr := flag.Args()[0]
|
||||
|
||||
// Query
|
||||
address := parseInput(addr)
|
||||
info := queryInfo(address)
|
||||
logQuery(info)
|
||||
|
||||
// HTTP Ping
|
||||
if strings.HasPrefix(addr, "http://") || strings.HasPrefix(addr, "https://") {
|
||||
fmt.Println()
|
||||
stats, err := httping.New(addr)
|
||||
logHttping(stats, err, addr)
|
||||
}
|
||||
case 2:
|
||||
addr := flag.Args()[0]
|
||||
port := flag.Args()[1]
|
||||
ip := lookupIP(addr)
|
||||
address := net.JoinHostPort(ip, port)
|
||||
c := tcping.New(address)
|
||||
logTcping(c, address)
|
||||
default:
|
||||
log.Warn("Too many arguments.")
|
||||
}
|
||||
} else {
|
||||
if isFlagPassed("v") {
|
||||
// Version
|
||||
fmt.Println(VersionString)
|
||||
} else if isFlagPassed("i") {
|
||||
// ICMP Ping
|
||||
dst, dur, err := ping.New(PingDst)
|
||||
logPing(dst, dur, err)
|
||||
} else if isFlagPassed("t") {
|
||||
// TCP Ping
|
||||
c := tcping.New(TCPingDst)
|
||||
logTcping(c, TCPingDst)
|
||||
} else if isFlagPassed("h") {
|
||||
// HTTP Ping
|
||||
stats, err := httping.New(HTTPingDst)
|
||||
logHttping(stats, err, HTTPingDst)
|
||||
} else if isFlagPassed("m") {
|
||||
// MTR
|
||||
hops, err := mtr.New(MtrDst)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
logMtr(hops, MtrDst)
|
||||
} else if isFlagPassed("q") {
|
||||
address := parseInput(Query)
|
||||
info := queryInfo(address)
|
||||
logQuery(info)
|
||||
}
|
||||
}
|
||||
}
|
48
mtr/mtr.go
Normal file
48
mtr/mtr.go
Normal file
@ -0,0 +1,48 @@
|
||||
package mtr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MtrLine struct {
|
||||
Name string
|
||||
Pos int
|
||||
IP string
|
||||
}
|
||||
|
||||
func New(s string) ([]string, error) {
|
||||
_, err := exec.Command("mtr", "-v", s).Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
fmt.Println("Waiting for MTR results...")
|
||||
out, err := exec.Command("mtr", "--raw", s).Output()
|
||||
hops := parseOutput(out)
|
||||
return hops, nil
|
||||
}
|
||||
|
||||
func parseOutput(b []byte) []string {
|
||||
var hops []string
|
||||
var matrix []MtrLine
|
||||
|
||||
raw := strings.Split(string(b), "\n")
|
||||
for _, rec := range raw {
|
||||
if len(rec) > 0 && rec[0] == 104 {
|
||||
tuple := strings.Split(rec, " ")
|
||||
pos, _ := strconv.Atoi(tuple[1])
|
||||
matrix = append(matrix, MtrLine{Name: tuple[0], Pos: pos, IP: tuple[2]})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(matrix, func(i, j int) bool { return matrix[i].Pos < matrix[j].Pos })
|
||||
|
||||
for _, tuple := range matrix {
|
||||
hops = append(hops, tuple.IP)
|
||||
}
|
||||
|
||||
return hops
|
||||
}
|
120
ping/ping.go
Normal file
120
ping/ping.go
Normal file
@ -0,0 +1,120 @@
|
||||
package ping
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
type IPType struct {
|
||||
Type string
|
||||
ListenAddr string
|
||||
Network string
|
||||
ICMPNetwork string
|
||||
ProtocolNumber int
|
||||
RequestMessageType icmp.Type
|
||||
ReplyMessageType icmp.Type
|
||||
}
|
||||
|
||||
var (
|
||||
IPType4 = IPType{
|
||||
Type: "4",
|
||||
ListenAddr: "0.0.0.0",
|
||||
Network: "ip4",
|
||||
ICMPNetwork: "ip4:icmp",
|
||||
ProtocolNumber: 1,
|
||||
RequestMessageType: ipv4.ICMPTypeEcho,
|
||||
ReplyMessageType: ipv4.ICMPTypeEchoReply,
|
||||
}
|
||||
IPType6 = IPType{
|
||||
Type: "6",
|
||||
ListenAddr: "::",
|
||||
Network: "ip6",
|
||||
ICMPNetwork: "ip6:ipv6-icmp",
|
||||
ProtocolNumber: 58,
|
||||
RequestMessageType: ipv6.ICMPTypeEchoRequest,
|
||||
ReplyMessageType: ipv6.ICMPTypeEchoReply,
|
||||
}
|
||||
)
|
||||
|
||||
func New(address string) (*net.IPAddr, time.Duration, error) {
|
||||
// Check ip type
|
||||
// Resolve address
|
||||
var err error
|
||||
var dst *net.IPAddr
|
||||
var ipType IPType
|
||||
dst, err = net.ResolveIPAddr("ip4", address)
|
||||
if err != nil {
|
||||
dst, err = net.ResolveIPAddr("ip6", address)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
} else {
|
||||
ipType = IPType6
|
||||
}
|
||||
} else {
|
||||
ipType = IPType4
|
||||
}
|
||||
|
||||
// Start listening for icmp replies
|
||||
c, err := icmp.ListenPacket(ipType.ICMPNetwork, ipType.ListenAddr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// Make a new ICMP message
|
||||
m := icmp.Message{
|
||||
Type: ipType.RequestMessageType,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff, Seq: 1,
|
||||
Data: []byte(""),
|
||||
},
|
||||
}
|
||||
b, err := m.Marshal(nil)
|
||||
if err != nil {
|
||||
return dst, 0, err
|
||||
}
|
||||
|
||||
// Send it
|
||||
start := time.Now()
|
||||
n, err := c.WriteTo(b, dst)
|
||||
if err != nil {
|
||||
return dst, 0, err
|
||||
} else if n != len(b) {
|
||||
return dst, 0, fmt.Errorf("got %v; want %v", n, len(b))
|
||||
}
|
||||
|
||||
// Wait for a reply
|
||||
reply := make([]byte, 1500)
|
||||
err = c.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
if err != nil {
|
||||
return dst, 0, err
|
||||
}
|
||||
n, peer, err := c.ReadFrom(reply)
|
||||
if err != nil {
|
||||
return dst, 0, err
|
||||
}
|
||||
duration := time.Since(start)
|
||||
|
||||
// Pack it up boys, we're done here
|
||||
rm, err := icmp.ParseMessage(ipType.ProtocolNumber, reply[:n])
|
||||
if err != nil {
|
||||
return dst, 0, err
|
||||
}
|
||||
|
||||
//return dst, duration, nil
|
||||
switch rm.Type {
|
||||
case ipType.ReplyMessageType:
|
||||
return dst, duration, nil
|
||||
default:
|
||||
return dst, 0, fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
|
||||
}
|
||||
|
||||
return dst, 0, err
|
||||
}
|
47
query.go
Normal file
47
query.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
API string = "http://ip-api.com/json/"
|
||||
)
|
||||
|
||||
type IPInfo struct {
|
||||
IP string `json:"query"`
|
||||
City string `json:"city"`
|
||||
Country string `json:"country"`
|
||||
ISP string `json:"isp"`
|
||||
AS string `json:"as"`
|
||||
}
|
||||
|
||||
func queryInfo(address string) IPInfo {
|
||||
var info IPInfo
|
||||
|
||||
res, err := http.Get(API + address)
|
||||
if err != nil {
|
||||
match, _ := regexp.MatchString("connection reset by peer", err.Error())
|
||||
if match {
|
||||
log.Fatal("Oops, your connection was reset by magic power. You may need to set env http_proxy.")
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = json.Unmarshal(body, &info)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
//printInfo(info)
|
||||
return info
|
||||
}
|
29
tcping/tcping.go
Normal file
29
tcping/tcping.go
Normal file
@ -0,0 +1,29 @@
|
||||
package tcping
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
func New(address string) int {
|
||||
d := net.Dialer{Timeout: 3 * time.Second}
|
||||
_, err := d.Dial("tcp", address)
|
||||
if err != nil {
|
||||
match, _ := regexp.MatchString("refused", err.Error())
|
||||
if match {
|
||||
// Closed
|
||||
return 1
|
||||
}
|
||||
match, _ = regexp.MatchString("timeout", err.Error())
|
||||
if match {
|
||||
// Timeout
|
||||
return 2
|
||||
}
|
||||
} else {
|
||||
// Open
|
||||
return 0
|
||||
}
|
||||
// Default
|
||||
return 2
|
||||
}
|
50
util.go
Normal file
50
util.go
Normal file
@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func lookupIP(s string) string {
|
||||
// is ip
|
||||
if isValidIP(s) {
|
||||
return s
|
||||
}
|
||||
// is hostname
|
||||
hostname := parseInput(s)
|
||||
ips, err := net.LookupIP(hostname)
|
||||
if err != nil {
|
||||
fmt.Println(hostname)
|
||||
fmt.Println(err)
|
||||
log.Fatal("IP address not found.")
|
||||
}
|
||||
return ips[0].String()
|
||||
}
|
||||
|
||||
func parseInput(s string) string {
|
||||
re := regexp.MustCompile(`^https?\:\/\/([^\/:?#]+)(?:[\/:?#]|$)`)
|
||||
results := re.FindStringSubmatch(s)
|
||||
if len(results) == 0 {
|
||||
return s
|
||||
}
|
||||
if len(results) == 2 {
|
||||
return results[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isValidIP(s string) bool {
|
||||
re := regexp.MustCompile(`((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))`)
|
||||
return re.Match([]byte(s))
|
||||
}
|
||||
|
||||
func isPrivateIPv4(s string) bool {
|
||||
re := regexp.MustCompile(`(^192\.168\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])$)|(^172\.([1][6-9]|[2][0-9]|[3][0-1])\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])$)|(^10\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])\.([0-9]|[0-9][0-9]|[0-2][0-5][0-5])$)`)
|
||||
return re.Match([]byte(s))
|
||||
}
|
||||
|
||||
func isPrivateIPv6(s string) bool {
|
||||
re := regexp.MustCompile(`(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`)
|
||||
return re.Match([]byte(s))
|
||||
}
|
Loading…
Reference in New Issue
Block a user