Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a54d4d6d64 | |||
| 9fb0df401e | |||
| 223acbf427 | |||
| 1fd5cebabb | |||
| 32dbc95581 | |||
| a7d010334c | |||
| ec14e26e83 | |||
| b6670cc921 | |||
| 9c58c2af2e | |||
| 711bed47f7 | |||
| 6efd033bb9 | |||
| cee1c30c63 | |||
| cc439eb7d4 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
bin/*
|
||||
|
||||
bin/
|
||||
lookupip
|
||||
|
||||
8
.vscode/tasks.json
vendored
8
.vscode/tasks.json
vendored
@@ -14,7 +14,13 @@
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
"clear": true
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Go Test All",
|
||||
"type": "shell",
|
||||
"command": "go test ./...",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
57
main.go
57
main.go
@@ -2,54 +2,25 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ryanehamil/lookupip/src/ipapi"
|
||||
"github.com/ryanehamil/lookupip/src/utils"
|
||||
)
|
||||
|
||||
var verbose bool = false
|
||||
var detail bool
|
||||
var ip string
|
||||
var properties []string
|
||||
|
||||
func main() {
|
||||
parseFlags()
|
||||
|
||||
if verbose {
|
||||
fmt.Println("Verbose mode on")
|
||||
}
|
||||
data, err := ipapi.Lookup(ip, properties)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
result := ipapi.GetProperties(data, properties, detail)
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func parseFlags() {
|
||||
_ip := flag.String("ip", "", "IP address to lookup")
|
||||
_props := flag.String("p", "Country", "Properties to retrieve")
|
||||
flag.BoolVar(&detail, "d", false, "Show Detail")
|
||||
_verbose := flag.Bool("v", false, "Verbose output")
|
||||
ip := flag.String("ip", "", "IP address to lookup")
|
||||
properties := flag.String("p", "", "Properties to retrieve")
|
||||
detail := flag.Bool("d", false, "Show Detail")
|
||||
flag.Parse()
|
||||
_loose := flag.Args()
|
||||
|
||||
if *_verbose {
|
||||
verbose = true
|
||||
}
|
||||
if *_ip == "" {
|
||||
if len(_loose) == 0 {
|
||||
} else {
|
||||
ip = _loose[0]
|
||||
}
|
||||
} else {
|
||||
ip = *_ip
|
||||
}
|
||||
if _props != nil {
|
||||
properties = strings.Split(*_props, ",")
|
||||
}
|
||||
// Use the IP-API to lookup the IP address
|
||||
data, err := ipapi.Lookup(*ip)
|
||||
utils.HandleError(err)
|
||||
|
||||
// Format the data to a string
|
||||
result := ipapi.GetProperties(data, *properties, *detail)
|
||||
|
||||
// Print result with PrintOut
|
||||
utils.PrintOut(result)
|
||||
utils.Exit(0)
|
||||
}
|
||||
|
||||
38
main_test.go
Normal file
38
main_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ryanehamil/lookupip/src/ipapi"
|
||||
"github.com/ryanehamil/lookupip/src/utils"
|
||||
)
|
||||
|
||||
// TestBuildURL
|
||||
// Tests that the buildURL function returns a correct URL
|
||||
func TestLookup(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
want string
|
||||
explain string
|
||||
}{
|
||||
{"", "Any IPV4", "Lookup my own IP"},
|
||||
{"8.8.8.8", "United States", "Lookup Google's DNS"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
ip := test.input
|
||||
// Use the IP-API to lookup anything
|
||||
data, _ := ipapi.Lookup(ip)
|
||||
|
||||
got := ipapi.GetProperties(data, "", false)
|
||||
|
||||
if test.want == "Any IPV4" {
|
||||
if !utils.CheckValidIP(data.Query) {
|
||||
t.Errorf("%q. error-buildURL(%q) = %v, want %v", test.explain, test.input, got, test.want)
|
||||
}
|
||||
} else if got != test.want {
|
||||
t.Errorf("%q. lookup(%q) = %v, want %v", test.explain, test.input, got, test.want)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@ package ipapi
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ryanehamil/lookupip/src/utils"
|
||||
)
|
||||
@@ -36,51 +38,70 @@ type IPAPI struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Build URL to query IP-API
|
||||
// https://ip-api.com/docs/api:json
|
||||
func buildURL(ip string) string {
|
||||
return "http://ip-api.com/json/" + ip
|
||||
// Add the .String() method to IPAPI
|
||||
func (i *IPAPI) String() string {
|
||||
return i.Query
|
||||
}
|
||||
|
||||
func Lookup(ip string, properties []string) (*IPAPI, error) {
|
||||
var data *IPAPI
|
||||
// Build URL to query IP-API
|
||||
// https://ip-api.com/docs/api:json
|
||||
func buildURL(ip string) (string, error) {
|
||||
url := "http://ip-api.com/json"
|
||||
if ip != "" {
|
||||
ip = strings.TrimSpace(ip)
|
||||
if !utils.CheckValidIP(ip) {
|
||||
var err = errors.New("invalid IP address, failed CheckValidIP")
|
||||
return url, err
|
||||
}
|
||||
url += fmt.Sprintf("/%s", ip)
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
|
||||
if !utils.CheckValidIP(ip) {
|
||||
return data, errors.New("please enter a valid IP address")
|
||||
// Get IP-API data about IP
|
||||
//
|
||||
// https://ip-api.com/docs/api:json
|
||||
func Lookup(ip string) (data IPAPI, err error) {
|
||||
|
||||
url, error := buildURL(ip)
|
||||
if error != nil {
|
||||
return data, error
|
||||
}
|
||||
|
||||
url := buildURL(ip)
|
||||
resp, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
// DebugOut(Error, err.Error())
|
||||
return data, err
|
||||
if error != nil {
|
||||
return data, error
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&data)
|
||||
if err != nil {
|
||||
// DebugOut(Error, err.Error())
|
||||
return data, err
|
||||
if error != nil {
|
||||
return data, error
|
||||
}
|
||||
|
||||
if data.Status == "fail" {
|
||||
// DebugOut(Error, data.Message)
|
||||
return data, errors.New(data.Message)
|
||||
utils.PrintOut("IP-API returned an error: " + data.Message)
|
||||
utils.Exit(1)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func GetProperties(data *IPAPI, properties []string, detail bool) string {
|
||||
var result string
|
||||
var output string = ""
|
||||
func GetProperties(data IPAPI, properties_string string, detail bool) (output string) {
|
||||
if properties_string == "" {
|
||||
properties_string = "Country"
|
||||
}
|
||||
result := ""
|
||||
properties := strings.Split(properties_string, ",")
|
||||
|
||||
for _, property := range properties {
|
||||
datafield := reflect.Indirect(reflect.ValueOf(data)).FieldByName(property).String()
|
||||
if datafield != "" {
|
||||
result = datafield
|
||||
} else {
|
||||
result = "Not found"
|
||||
result = ""
|
||||
}
|
||||
|
||||
if detail {
|
||||
output += property + ": " + result + "\n"
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,8 @@ package ipapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ryanehamil/lookupip/src/utils"
|
||||
)
|
||||
|
||||
// TestBuildURL
|
||||
@@ -12,13 +14,20 @@ func TestBuildURL(t *testing.T) {
|
||||
want string
|
||||
explain string
|
||||
}{
|
||||
{"127.0.0.1", "http://ip-api.com/json/127.0.0.1", "Valid Return"},
|
||||
{"127.0.0.1", "http://ip-api.com/json/127.0.0.1", "Valid IP"},
|
||||
{" 127.0.0.1 ", "http://ip-api.com/json/127.0.0.1", "Dirty IP"},
|
||||
{"127.foo.bar.1", "invalid IP address, failed CheckValidIP", "Unsantized input"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := buildURL(test.ip)
|
||||
if got != test.want {
|
||||
t.Errorf("CheckValidIP(%q) = %v, want %v", test.ip, got, test.want)
|
||||
got, err := buildURL(test.ip)
|
||||
|
||||
if err != nil {
|
||||
if err.Error() != test.want {
|
||||
t.Errorf("%q. error-buildURL(%q) = %v, want %v", test.explain, test.ip, got, test.want)
|
||||
}
|
||||
} else if got != test.want {
|
||||
t.Errorf("%q. buildURL(%q) = %v, want %v", test.explain, test.ip, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,17 +40,48 @@ func TestLookup(t *testing.T) {
|
||||
want string
|
||||
explain string
|
||||
}{
|
||||
{"8.8.8.8", "United States", "Valid Return"},
|
||||
{"8.8.8.8", "United States", "Google Public DNS"},
|
||||
{"199.211.133.90", "United States", "Naval Oceanography"},
|
||||
{"116.202.3.251", "Germany", "JAM Software Germany"},
|
||||
{"", "Any IP", "Get my IP"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got, err := Lookup(test.ip, []string{"country"})
|
||||
if err != nil {
|
||||
t.Errorf("CheckValidIP(%q) = %v, want %v", test.ip, err, test.want)
|
||||
}
|
||||
if test.want != got.Country {
|
||||
t.Errorf("CheckValidIP(%q) = %v, want %v", test.ip, got, test.want)
|
||||
data, err := Lookup(test.ip)
|
||||
got := data.Country
|
||||
if err != nil && err.Error() != test.want {
|
||||
t.Errorf("%q. error-buildURL(%q) = %v, want %v", test.explain, test.ip, got, test.want)
|
||||
} else if test.want == "Any IP" {
|
||||
if !utils.CheckValidIP(data.Query) {
|
||||
t.Errorf("%q. error-buildURL(%q) = %v, want %v", test.explain, test.ip, got, test.want)
|
||||
}
|
||||
} else if got != test.want {
|
||||
t.Errorf("%q. buildURL(%q) = %v, want %v", test.explain, test.ip, got, test.want)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProperties(t *testing.T) {
|
||||
var tests = []struct {
|
||||
properties string
|
||||
want string
|
||||
explain string
|
||||
}{
|
||||
{"", "United States", "No properties requested"},
|
||||
{"Country", "United States", "Properties: Country"},
|
||||
{"Country,ISP", "United States,Google LLC", "Properties: Country,ISP"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
// This test relies on the lookup function
|
||||
ip := "8.8.8.8"
|
||||
// Use the IP-API to lookup anything
|
||||
data, _ := Lookup(ip)
|
||||
|
||||
got := GetProperties(data, test.properties, false)
|
||||
if got != test.want {
|
||||
t.Errorf("%q. GetProperties(%q) = %v, want %v", test.explain, test.properties, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,42 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Verbosity int
|
||||
|
||||
const (
|
||||
Info Verbosity = iota
|
||||
Warning
|
||||
Error
|
||||
Debug
|
||||
)
|
||||
// For printing to the console
|
||||
//
|
||||
// Currently using fmt.Println
|
||||
func PrintOut(msg ...interface{}) {
|
||||
fmt.Println(msg...)
|
||||
}
|
||||
|
||||
// Check if a string is a valid ipv4 address
|
||||
//
|
||||
// Currently using regexp.MustCompile and regexp.MatchString to return boolean
|
||||
func CheckValidIP(ip string) bool {
|
||||
re := regexp.MustCompile(`^(([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])$`)
|
||||
return re.MatchString(ip)
|
||||
}
|
||||
|
||||
// Error handler for the program
|
||||
//
|
||||
// Checks if error !nil and prints the error to the console
|
||||
func HandleError(err error) {
|
||||
if err != nil {
|
||||
PrintOut(err)
|
||||
Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple Exit function to handle exit codes
|
||||
//
|
||||
// Might need to be changed to a more robust one
|
||||
func Exit(code int) {
|
||||
if code != 0 {
|
||||
panic("exit")
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ func TestCheckValidIP(t *testing.T) {
|
||||
explain string
|
||||
}{
|
||||
{"127.0.0.1", true, "Valid IP"},
|
||||
{"127.O.O.1", false, "Invalid IP"},
|
||||
{"127.X.X.1", false, "Invalid IP"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := CheckValidIP(test.ip)
|
||||
if got != test.want {
|
||||
t.Errorf("CheckValidIP(%q) = %v, want %v", test.ip, got, test.want)
|
||||
t.Errorf("%q. CheckValidIP(%q) = %v", test.explain, test.ip, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user