first release

This commit is contained in:
noobly 2019-11-09 10:07:49 +08:00 committed by noobly314
parent 4f7dfab7d9
commit 604f9fb876
15 changed files with 949 additions and 1 deletions

59
.github/workflows/main.yml vendored Normal file
View 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
View 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
View 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
View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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))
}