mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-11 16:37:25 +02:00
134 lines
3.8 KiB
Go
134 lines
3.8 KiB
Go
package ipinfo
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/anotherhadi/iknowyou/internal/tools"
|
|
)
|
|
|
|
const (
|
|
name = "ipinfo"
|
|
description = "IP geolocation via ipinfo.io — returns city, region, country, coordinates, ASN/org, timezone, and hostname."
|
|
link = "https://ipinfo.io"
|
|
icon = ""
|
|
)
|
|
|
|
type Config struct {
|
|
Token string `yaml:"token" iky:"desc=ipinfo.io API token (optional — free tier allows 50k req/month without one);required=false"`
|
|
}
|
|
|
|
type Runner struct {
|
|
cfg Config
|
|
}
|
|
|
|
func New() tools.ToolRunner {
|
|
cfg := Config{}
|
|
tools.ApplyDefaults(&cfg)
|
|
return &Runner{cfg: cfg}
|
|
}
|
|
|
|
func (r *Runner) Name() string { return name }
|
|
func (r *Runner) Description() string { return description }
|
|
func (r *Runner) Link() string { return link }
|
|
func (r *Runner) Icon() string { return icon }
|
|
|
|
func (r *Runner) InputTypes() []tools.InputType {
|
|
return []tools.InputType{tools.InputTypeIP}
|
|
}
|
|
|
|
func (r *Runner) ConfigPtr() interface{} { return &r.cfg }
|
|
|
|
func (r *Runner) ConfigFields() []tools.ConfigField {
|
|
return tools.ReflectConfigFields(r.cfg)
|
|
}
|
|
|
|
type ipinfoResponse struct {
|
|
IP string `json:"ip"`
|
|
Hostname string `json:"hostname"`
|
|
City string `json:"city"`
|
|
Region string `json:"region"`
|
|
Country string `json:"country"`
|
|
Loc string `json:"loc"`
|
|
Org string `json:"org"`
|
|
Postal string `json:"postal"`
|
|
Timezone string `json:"timezone"`
|
|
Bogon bool `json:"bogon"`
|
|
}
|
|
|
|
func (r *Runner) Run(ctx context.Context, target string, _ tools.InputType, out chan<- tools.Event) error {
|
|
defer close(out)
|
|
|
|
url := fmt.Sprintf("https://ipinfo.io/%s/json", target)
|
|
if r.cfg.Token != "" {
|
|
url += "?token=" + r.cfg.Token
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: err.Error()}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 0}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
if ctx.Err() != nil {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: "scan cancelled"}
|
|
} else {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: err.Error()}
|
|
}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 0}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var info ipinfoResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: "failed to parse response"}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 0}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|
|
|
|
if info.Bogon {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeOutput, Payload: fmt.Sprintf("IP: %s\nType: Bogon/Private address", info.IP)}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: 1}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|
|
|
|
var sb strings.Builder
|
|
field := func(label, value string) {
|
|
if value != "" {
|
|
sb.WriteString(fmt.Sprintf("%-12s %s\n", label+":", value))
|
|
}
|
|
}
|
|
|
|
field("IP", info.IP)
|
|
field("Hostname", info.Hostname)
|
|
field("City", info.City)
|
|
field("Region", info.Region)
|
|
field("Country", info.Country)
|
|
field("Coordinates", info.Loc)
|
|
field("Postal", info.Postal)
|
|
field("Timezone", info.Timezone)
|
|
field("Org/ASN", info.Org)
|
|
|
|
result := strings.TrimSpace(sb.String())
|
|
count := 0
|
|
if result != "" {
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeOutput, Payload: result}
|
|
count = 1
|
|
}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: count}
|
|
out <- tools.Event{Tool: name, Type: tools.EventTypeDone}
|
|
return nil
|
|
}
|