Skip to content

Commit

Permalink
add REST API and refactor directory structure
Browse files Browse the repository at this point in the history
  • Loading branch information
Jozef Reisinger committed Nov 10, 2021
1 parent 9888383 commit 261a2c4
Show file tree
Hide file tree
Showing 22 changed files with 186 additions and 105 deletions.
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
test:
go test -cover
go test -cover ./...

install: test
go install cmd/checkip.go
go install

run: install
checkip 91.228.166.47
checkip 209.141.33.65
checkip 218.92.0.158
checkip -j 218.92.0.158 | jq -r '.[] | select(.Type=="Sec" or .Type=="InfoSec") | "\(.IsMalicious)\t\(.Name)"' | sort

test-api:
curl -s 'localhost:8000/api/v1/91.228.166.47' | jq -r '.ProbabilityMalicious'
curl -s 'localhost:8000/api/v1/209.141.33.65' | jq -r '.ProbabilityMalicious'
curl -s 'localhost:8000/api/v1/218.92.0.158' | jq -r '.ProbabilityMalicious'

PLATFORMS := linux/amd64 darwin/amd64 linux/arm windows/amd64

Expand Down
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# checkip

`checkip` is a CLI tool and library that checks an IP address using various
public services. It provides generic and security information in a simple and
quick way.
`checkip` checks an IP address using various public services. It provides
generic and security information in a simple and quick way.

<img src="checkip.png" width="800">
It can be run as a CLI tool or as JSON API.

The CLI tool can also print all data in JSON format so you can pick what you
want to see, e.g.:
<img src="checkip.png" width="800">

```
checkip -j 218.92.0.158 | \
jq -r '.[] | select(.Type=="Sec" or .Type=="InfoSec") | "\(.IsMalicious)\t\(.Name)"' | sort
checkip -s &
curl -s localhost:8000/api/v1/218.92.0.158 | \
jq -r '.Results[] | select(.Type=="Sec" or .Type=="InfoSec") | "\(.IsMalicious)\t\(.Name)"' | sort
false abuseipdb.com
false cinsscore.com
false threatcrowd.org
Expand All @@ -23,7 +22,7 @@ true otx.alienvault.com

## Installation and configuration

To install the CLI tool
To install the CLI/API tool

```
git clone git@github.com:jreisinger/checkip.git
Expand Down Expand Up @@ -54,7 +53,7 @@ a new way for checking IP addresses just implement the
[InfoChecker](https://pkg.go.dev/github.com/jreisinger/checkip#InfoChecker),
[SecChecker](https://pkg.go.dev/github.com/jreisinger/checkip#SecChecker) or
[InfoSecChecker](https://pkg.go.dev/github.com/jreisinger/checkip#InfoSecChecker)
interface and add it to `cmd/checkip.go`. Then
interface and add it to `main.go`. Then

```
make run # see the picture above
Expand Down
31 changes: 31 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package api

import (
"fmt"
"net"
"net/http"
"strings"

checkip "github.com/jreisinger/checkip/pkg"
)

type Checkers []checkip.Checker

func (c Checkers) Handler(w http.ResponseWriter, r *http.Request) {
ip := strings.Split(r.URL.Path, "/")[3]
netip := net.ParseIP(ip)
if netip == nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "wrong IP address: %q", ip)
return
}
results := checkip.Run(c, netip)
b, err := checkip.JSON(results)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "marshalling JSON: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, "%s", b)
}
52 changes: 0 additions & 52 deletions cmd/checkip.go

This file was deleted.

52 changes: 52 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Checkip quickly finds information about an IP address from a CLI.
package cmd

import (
"fmt"
"log"
"sort"

checkip "github.com/jreisinger/checkip/pkg"
"github.com/logrusorgru/aurora"
)

type byName []checkip.Result

func (x byName) Len() int { return len(x) }
func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }

// Print prints condensed results to stdout.
func Print(results []checkip.Result) error {
sort.Sort(byName(results))

var malicious, totalSec float64
for _, r := range results {
if r.Err != nil {
log.Print(r.ErrMsg)
}
if r.Type == "Info" || r.Type == "InfoSec" {
fmt.Printf("%-15s %s\n", r.Name, r.Info)
}
if r.Type == "Sec" || r.Type == "InfoSec" {
totalSec++
if r.IsMalicious {
malicious++
}
}
}
probabilityMalicious := malicious / totalSec

var msg string
switch {
case probabilityMalicious <= 0.15:
msg = fmt.Sprint(aurora.Green("Malicious"))
case probabilityMalicious <= 0.50:
msg = fmt.Sprint(aurora.Yellow("Malicious"))
default:
msg = fmt.Sprint(aurora.Red("Malicious"))
}

_, err := fmt.Printf("%s\t%.0f%% (%d/%d)\n", msg, probabilityMalicious*100, int(malicious), int(totalSec))
return err
}
56 changes: 56 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"flag"
"log"
"net"
"net/http"
"os"

"github.com/jreisinger/checkip/api"
"github.com/jreisinger/checkip/cmd"
checkip "github.com/jreisinger/checkip/pkg"
)

var s = flag.Bool("s", false, "serve JSON API")

func main() {
flag.Parse()

checkers := []checkip.Checker{
&checkip.AS{},
&checkip.AbuseIPDB{},
&checkip.Blocklist{},
&checkip.CINSArmy{},
&checkip.DNS{},
&checkip.Geo{},
&checkip.IPsum{},
&checkip.OTX{},
&checkip.Shodan{},
&checkip.ThreatCrowd{},
&checkip.VirusTotal{},
}

if *s {
c := api.Checkers(checkers)
http.HandleFunc("/api/v1/", c.Handler)
log.Fatal(http.ListenAndServe(":8000", nil))
} else {

log.SetFlags(0)
log.SetPrefix(os.Args[0] + ": ")

if len(flag.Args()) != 1 {
log.Fatal("missing IP address")
}

ipaddr := net.ParseIP(flag.Arg(0))
if ipaddr == nil {
log.Fatalf("wrong IP address: %s\n", flag.Arg(0))
}

results := checkip.Run(checkers, ipaddr)

cmd.Print(results)
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
71 changes: 31 additions & 40 deletions checkip.go → pkg/checkip.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ package checkip
import (
"encoding/json"
"fmt"
"log"
"net"
"os"
"regexp"
"sort"
"sync"

"github.com/logrusorgru/aurora"
)

// Checker runs a check of an IP address. String() returns checker's name.
Expand Down Expand Up @@ -84,53 +79,49 @@ func Run(checkers []Checker, ipaddr net.IP) []Result {
return res
}

type byName []Result
// JSON marshals results into JSON format.
func JSON(results []Result) ([]byte, error) {
type Overview struct {
Infos []struct {
Name string
Info string
}
ProbabilityMalicious float64
}

func (x byName) Len() int { return len(x) }
func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
var o Overview

// Print prints condensed results to stdout.
func Print(results []Result) error {
sort.Sort(byName(results))
for _, d := range results {
i := struct {
Name string
Info string
}{d.Name, d.Info}
o.Infos = append(o.Infos, i)
}
o.ProbabilityMalicious = probabilityMalicious(results)

j := struct {
Overview
Results []Result
}{
Overview: o,
Results: results,
}

return json.Marshal(&j)
}

func probabilityMalicious(results []Result) float64 {
var malicious, totalSec float64
for _, r := range results {
if r.Err != nil {
log.Print(r.ErrMsg)
}
if r.Type == "Info" || r.Type == "InfoSec" {
fmt.Printf("%-15s %s\n", r.Name, r.Info)
}
if r.Type == "Sec" || r.Type == "InfoSec" {
totalSec++
if r.IsMalicious {
malicious++
}
}
}
probabilityMalicious := malicious / totalSec

var msg string
switch {
case probabilityMalicious <= 0.15:
msg = fmt.Sprint(aurora.Green("Malicious"))
case probabilityMalicious <= 0.50:
msg = fmt.Sprint(aurora.Yellow("Malicious"))
default:
msg = fmt.Sprint(aurora.Red("Malicious"))
}

_, err := fmt.Printf("%s\t%.0f%% (%d/%d)\n", msg, probabilityMalicious*100, int(malicious), int(totalSec))
return err
}

// PrintJSON prints all data from results in JSON format to stdout.
func PrintJSON(results []Result) error {
sort.Sort(byName(results))

enc := json.NewEncoder(os.Stdout)
return enc.Encode(&results)
return malicious / totalSec
}

func redactSecrets(s string) string {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 261a2c4

Please sign in to comment.