Skip to content

Commit 0c13c77

Browse files
committed
Add routes info
1 parent 86d1dc1 commit 0c13c77

File tree

9 files changed

+171
-29
lines changed

9 files changed

+171
-29
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ Options:
9898
-h, --help display help information
9999
-p, --provider *set types of api provider (space separated) --provider='ipdata ipinfo minfraud'
100100
-i, --ip input ip address --ip='8.8.8.8'
101+
--route set if you need route data from IRR --route
101102
--debug set if you need verbose logs --debug
102103
```
103104
@@ -111,9 +112,9 @@ $ export FRAUD_CHECK_IPINFOIO_TOKEN=yyy
111112
# check ip address
112113
$ ./go-ip-fraud-check single -p 'ipdata ipinfo' -i 8.8.8.8
113114

114-
2021/10/25 00:54:26 [INFO] [INFO] Use ipdata.co
115-
2021/10/25 00:54:26 [INFO] [INFO] Use ipinfo.io
116-
{"list":[{"service_name":"ipdata.co","ip":"8.8.8.8","hostname":"","isp":"Google LLC","organization":"","asn":15169,"risk_score":0,"is_anonymous":false,"is_anonymous_vpn":false,"is_hosting":false,"is_proxy":false,"is_tor":false,"is_bot":false,"is_bogon":false,"has_other_threat":false,"country":"US","city":"","region":"","latitude":0,"longitude":0,"error":""},{"service_name":"ipinfo.io","ip":"8.8.8.8","hostname":"dns.google","isp":"","organization":"Google LLC","asn":15169,"risk_score":0,"is_anonymous":false,"is_anonymous_vpn":false,"is_hosting":false,"is_proxy":false,"is_tor":false,"is_bot":false,"is_bogon":false,"has_other_threat":false,"country":"US","city":"Mountain View","region":"California","latitude":37.4056,"longitude":-122.0775,"error":""}]}
115+
2021/10/25 00:54:26 [INFO] Use ipdata.co
116+
2021/10/25 00:54:26 [INFO] Use ipinfo.io
117+
{"list":[{"service_name":"ipdata.co","ip":"8.8.8.8","hostname":"","isp":"Google LLC","organization":"","asn":15169,"risk_score":0,"is_anonymous":false,"is_anonymous_vpn":false,"is_hosting":false,"is_proxy":false,"is_tor":false,"is_bot":false,"is_bogon":false,"has_other_threat":false,"country":"US","city":"","region":"","latitude":0,"longitude":0,"error":""},{"service_name":"ipinfo.io","ip":"8.8.8.8","hostname":"dns.google","isp":"","organization":"Google LLC","asn":15169,"risk_score":0,"is_anonymous":false,"is_anonymous_vpn":false,"is_hosting":false,"is_proxy":false,"is_tor":false,"is_bot":false,"is_bogon":false,"has_other_threat":false,"country":"US","city":"Mountain View","region":"California","latitude":37.4056,"longitude":-122.0775,"error":""}],"as_prefix":["66.249.80.0/20","74.125.57.240/29","216.239.44.0/24", ...]}
117118
```
118119
119120
### list command
@@ -122,6 +123,7 @@ $ ./go-ip-fraud-check single -p 'ipdata ipinfo' -i 8.8.8.8
122123
123124
```bash
124125
./go-ip-fraud-check list -h
126+
125127
Exec api call of ip address fraud check providers from csv list file
126128

127129
Options:
@@ -130,6 +132,7 @@ Options:
130132
-p, --provider *set types of api provider (space separated) --provider='ipdata ipinfo minfraud'
131133
-i, --input *input csv/tsv file path --input='./input.csv'
132134
-o, --output *output tsv file path --output='./output.tsv'
135+
--route set if you need route data from IRR (this might be slow) --route
133136
--debug set if you use HTTP debug feature --debug
134137
```
135138
@@ -150,8 +153,8 @@ ip_address
150153

151154
# check risk from the CSV file
152155
$ ./go-ip-fraud-check list -p 'ipdata ipinfo' -i ./input.csv -o ./output.tsv
153-
2021/10/25 00:58:29 [INFO] [INFO] Use ipdata.co
154-
2021/10/25 00:58:29 [INFO] [INFO] Use ipinfo.io
156+
2021/10/25 00:58:29 [INFO] Use ipdata.co
157+
2021/10/25 00:58:29 [INFO] Use ipinfo.io
155158
2021/10/25 00:58:30 [INFO] exec #: [2]
156159
2021/10/25 00:58:29 [INFO] exec #: [0]
157160
2021/10/25 00:58:31 [INFO] exec #: [1]
@@ -185,6 +188,7 @@ func main() {
185188
// you can set auth values to config directly, otherwise used from environment variables.
186189
IPdatacoAPIKey: "<your ipdata.co API key>",
187190
IPinfoioToken: "<your ipinfo.io API token>",
191+
UseRoute: true,
188192
Debug: false,
189193
}
190194

cmd/command_list.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var outputHeader = []string{
3333
"is_bot",
3434
"is_bogon",
3535
"has_other_threat",
36+
// "as_routes", append by code
3637
}
3738

3839
// parameters of 'list' command.
@@ -41,6 +42,7 @@ type listT struct {
4142
Provider string `cli:"*p,provider" usage:"set types of api provider (space separated) --provider='ipdata ipinfo minfraud'"`
4243
InputCSV string `cli:"*i,input" usage:"input csv/tsv file path --input='./input.csv'"`
4344
Output string `cli:"*o,output" usage:"output tsv file path --output='./output.tsv'"`
45+
UseRoute bool `cli:"route" usage:"set if you need route data from IRR (this might be slow) --route"`
4446
Debug bool `cli:"debug" usage:"set if you use HTTP debug feature --debug"`
4547
}
4648

@@ -67,6 +69,7 @@ type ListRunner struct {
6769
Provider string
6870
InputCSV string
6971
Output string
72+
UseRoute bool
7073
Debug bool
7174
}
7275

@@ -75,6 +78,7 @@ func newListRunner(p listT) ListRunner {
7578
Provider: p.Provider,
7679
InputCSV: p.InputCSV,
7780
Output: p.Output,
81+
UseRoute: p.UseRoute,
7882
Debug: p.Debug,
7983
}
8084
}
@@ -95,7 +99,8 @@ func (r *ListRunner) Run() error {
9599
return err
96100
}
97101

98-
maxReq := make(chan struct{}, 2)
102+
maxReqNum := 3
103+
maxReq := make(chan struct{}, maxReqNum)
99104

100105
providerList, err := getProvidersFromString(r.Provider)
101106
if err != nil {
@@ -104,12 +109,16 @@ func (r *ListRunner) Run() error {
104109

105110
logger := &log.StdLogger{}
106111
svc, err := ipfraudcheck.New(ipfraudcheck.Config{
107-
Debug: r.Debug,
108-
Logger: logger,
112+
UseRoute: r.UseRoute,
113+
Debug: r.Debug,
114+
Logger: logger,
109115
}, providerList)
110116
if err != nil {
111117
panic(err)
112118
}
119+
if r.UseRoute {
120+
outputHeader = append(outputHeader, "as_routes")
121+
}
113122

114123
providerSize := len(providerList)
115124
result := make([]string, len(lines)*providerSize)
@@ -151,6 +160,10 @@ func (r *ListRunner) execAPI(svc *ipfraudcheck.Client, param map[string]string)
151160
for i, r := range resp.List {
152161
row := make([]string, 0, len(outputHeader))
153162
for _, v := range outputHeader {
163+
if v == "as_routes" {
164+
row = append(row, strings.Join(resp.ASPrefix, " "))
165+
continue
166+
}
154167
row = append(row, getValue(param, r, v))
155168
}
156169
rows[i] = row

cmd/command_single.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type singleT struct {
1616
cli.Helper
1717
Provider string `cli:"*p,provider" usage:"set types of api provider (space separated) --provider='ipdata ipinfo minfraud'"`
1818
IPAddress string `cli:"i,ip" usage:"input ip address --ip='8.8.8.8'"`
19+
UseRoute bool `cli:"route" usage:"set if you need route data from IRR --route"`
1920
Debug bool `cli:"debug" usage:"set if you need verbose logs --debug"`
2021
}
2122

@@ -45,13 +46,15 @@ type SingleRunner struct {
4546
// parameters
4647
Provider string
4748
IPAddress string
49+
UseRoute bool
4850
Debug bool
4951
}
5052

5153
func newSingleRunner(p singleT) SingleRunner {
5254
return SingleRunner{
5355
Provider: p.Provider,
5456
IPAddress: p.IPAddress,
57+
UseRoute: p.UseRoute,
5558
Debug: p.Debug,
5659
}
5760
}
@@ -63,8 +66,9 @@ func (r *SingleRunner) Run() error {
6366
}
6467

6568
svc, err := ipfraudcheck.New(ipfraudcheck.Config{
66-
Debug: r.Debug,
67-
Logger: &log.StdLogger{},
69+
UseRoute: r.UseRoute,
70+
Debug: r.Debug,
71+
Logger: &log.StdLogger{},
6872
}, providerList)
6973
if err != nil {
7074
panic(err)

example/example_ipdataco.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ func main() {
1515
flag.StringVar(&ipaddr, "ipaddr", "", "set target ip address")
1616
flag.Parse()
1717

18-
svc, err := ipfraudcheck.New(ipfraudcheck.Config{}, []provider.Provider{
18+
svc, err := ipfraudcheck.New(ipfraudcheck.Config{
19+
UseRoute: true,
20+
}, []provider.Provider{
1921
&ipdataco.IPdatacoProvider{},
2022
})
2123
if err != nil {

example/example_ipinfoio.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ func main() {
1515
flag.StringVar(&ipaddr, "ipaddr", "", "set target ip address")
1616
flag.Parse()
1717

18-
svc, err := ipfraudcheck.New(ipfraudcheck.Config{}, []provider.Provider{
18+
svc, err := ipfraudcheck.New(ipfraudcheck.Config{
19+
UseRoute: true,
20+
}, []provider.Provider{
1921
&ipinfoio.IPinfoioProvider{},
2022
})
2123
if err != nil {

example/example_minfraud.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ func main() {
1515
flag.StringVar(&ipaddr, "ipaddr", "", "set target ip address")
1616
flag.Parse()
1717

18-
svc, err := ipfraudcheck.New(ipfraudcheck.Config{}, []provider.Provider{
18+
svc, err := ipfraudcheck.New(ipfraudcheck.Config{
19+
UseRoute: true,
20+
}, []provider.Provider{
1921
&minfraud.MinFraudProvider{},
2022
})
2123
if err != nil {

ipfraudcheck/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ type Config struct {
2424
MinFraudLicenseKey string
2525

2626
// common option
27-
Debug bool
28-
Timeout time.Duration
29-
Logger log.Logger
27+
UseRoute bool
28+
Debug bool
29+
Timeout time.Duration
30+
Logger log.Logger
3031
}
3132

3233
func (c Config) GetLogger() log.Logger {

ipfraudcheck/ipfraudcheck.go

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
type Client struct {
1111
logger log.Logger
1212
providers []provider.Provider
13+
useRoutes bool
1314
}
1415

1516
func New(conf Config, providers []provider.Provider) (*Client, error) {
@@ -29,15 +30,29 @@ func New(conf Config, providers []provider.Provider) (*Client, error) {
2930
logger := conf.GetLogger()
3031

3132
for _, e := range enabledProviders {
32-
logger.Infof("[INFO] Use %s\n", e.String())
33+
logger.Infof("Use %s\n", e.String())
3334
}
3435

3536
return &Client{
3637
logger: logger,
3738
providers: enabledProviders,
39+
useRoutes: conf.UseRoute,
3840
}, nil
3941
}
4042

43+
func (c Client) RawCheckIP(ipaddr string) ([]interface{}, error) {
44+
list := make([]interface{}, len(c.providers))
45+
for i, p := range c.providers {
46+
resp, err := p.RawCheckIP(ipaddr)
47+
if err != nil {
48+
return nil, err
49+
}
50+
list[i] = resp
51+
}
52+
53+
return list, nil
54+
}
55+
4156
func (c Client) CheckIP(ipaddr string) (Response, error) {
4257
list := make([]provider.FraudCheckResponse, len(c.providers))
4358
for i, p := range c.providers {
@@ -48,24 +63,33 @@ func (c Client) CheckIP(ipaddr string) (Response, error) {
4863
list[i] = resp
4964
}
5065

51-
return Response{
66+
resp := Response{
5267
List: list,
53-
}, nil
68+
}
69+
if c.useRoutes {
70+
asn, ok := resp.FindASN()
71+
if ok {
72+
cli := NewWhoisClient()
73+
routes, err := cli.GetRoutes(asn)
74+
if err != nil {
75+
return resp, err
76+
}
77+
resp.ASPrefix = routes
78+
}
79+
}
80+
return resp, nil
5481
}
5582

5683
type Response struct {
57-
List []provider.FraudCheckResponse `json:"list"`
84+
List []provider.FraudCheckResponse `json:"list"`
85+
ASPrefix []string `json:"as_prefix"`
5886
}
5987

60-
func (c Client) RawCheckIP(ipaddr string) ([]interface{}, error) {
61-
list := make([]interface{}, len(c.providers))
62-
for i, p := range c.providers {
63-
resp, err := p.RawCheckIP(ipaddr)
64-
if err != nil {
65-
return nil, err
88+
func (r Response) FindASN() (int64, bool) {
89+
for _, resp := range r.List {
90+
if resp.ASNumber != 0 {
91+
return resp.ASNumber, true
6692
}
67-
list[i] = resp
6893
}
69-
70-
return list, nil
94+
return 0, false
7195
}

ipfraudcheck/whois_routes.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package ipfraudcheck
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net"
7+
"strconv"
8+
"strings"
9+
"time"
10+
)
11+
12+
const (
13+
defaultWhoisServer = "whois.radb.net"
14+
defaultWhoisPort = 43
15+
defaultWhoisTimeout = 30 * time.Second
16+
)
17+
18+
type WhoisClient struct {
19+
Server string
20+
Port int
21+
Timeout time.Duration
22+
}
23+
24+
func NewWhoisClient() WhoisClient {
25+
return WhoisClient{
26+
Server: defaultWhoisServer,
27+
Port: defaultWhoisPort,
28+
Timeout: defaultWhoisTimeout,
29+
}
30+
}
31+
32+
func (c WhoisClient) GetRoutes(asn int64) ([]string, error) {
33+
byt, err := whois(c.Server, c.Port, c.Timeout, asn)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
lines := strings.Split(string(byt), "\n")
39+
routes := make([]string, 0, len(lines)/8)
40+
for _, line := range lines {
41+
if !strings.HasPrefix(line, "route") {
42+
continue
43+
}
44+
route := strings.TrimSpace(
45+
strings.TrimPrefix(
46+
strings.TrimPrefix(line, "route6:"),
47+
"route:"))
48+
routes = append(routes, route)
49+
}
50+
return routes, nil
51+
}
52+
53+
// codes are base on: https://github.com/likexian/whois
54+
func whois(server string, port int, timeout time.Duration, asn int64) ([]byte, error) {
55+
dialer := &net.Dialer{
56+
Timeout: timeout,
57+
}
58+
59+
now := time.Now()
60+
conn, err := dialer.Dial("tcp", net.JoinHostPort(server, strconv.Itoa(port)))
61+
if err != nil {
62+
return nil, fmt.Errorf("whois: connect to whois server failed: %w", err)
63+
}
64+
defer conn.Close()
65+
66+
// send query
67+
_ = conn.SetWriteDeadline(time.Now().Add(dialer.Timeout - time.Since(now)))
68+
query := getWhoisQuery(server, asn)
69+
_, err = conn.Write([]byte(query + "\r\n"))
70+
if err != nil {
71+
return nil, fmt.Errorf("whois: send to whois server failed: %w", err)
72+
}
73+
74+
// get response
75+
_ = conn.SetReadDeadline(time.Now().Add(dialer.Timeout - time.Since(now)))
76+
buf, err := ioutil.ReadAll(conn)
77+
if err != nil {
78+
return nil, fmt.Errorf("whois: read from whois server failed: %w", err)
79+
}
80+
81+
return buf, nil
82+
}
83+
84+
func getWhoisQuery(server string, asn int64) string {
85+
switch server {
86+
case "whois.radb.net":
87+
return fmt.Sprintf("-i origin AS%d", asn)
88+
}
89+
return fmt.Sprintf("AS%d", asn)
90+
}

0 commit comments

Comments
 (0)