mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
init
This commit is contained in:
133
back/internal/tools/ipinfo/tool.go
Normal file
133
back/internal/tools/ipinfo/tool.go
Normal file
@@ -0,0 +1,133 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user