feature: documentation, category enum and cached check-block
This commit is contained in:
parent
d456ceac98
commit
4f1fe4840e
1 changed files with 115 additions and 46 deletions
|
@ -27,6 +27,45 @@ const (
|
|||
defaultUserAgent = "go-abuseipdb/" + Version
|
||||
)
|
||||
|
||||
const (
|
||||
endpointCheck = "check"
|
||||
endpointReports = "reports"
|
||||
endpointBlacklist = "blacklist"
|
||||
endpointReport = "report"
|
||||
endpointCheckBlock = "check-block"
|
||||
endpointBulkReport = "bulk-report"
|
||||
endpointClearAddress = "clear-address"
|
||||
)
|
||||
|
||||
// ReportCategory is documented at https://www.abuseipdb.com/categories
|
||||
type ReportCategory int
|
||||
|
||||
const (
|
||||
DnsCompromise ReportCategory = iota + 1
|
||||
DnsPoisoning
|
||||
FraudOrders
|
||||
DDoSAttack
|
||||
FtpBruteForce
|
||||
PingOfDeath
|
||||
Phishing
|
||||
FraudVoIp
|
||||
OpenProxy
|
||||
WebSpam
|
||||
EmailSpam
|
||||
BlogSpam
|
||||
VpnIp
|
||||
PortScan
|
||||
Hacking
|
||||
SqlInjection
|
||||
Spoofing
|
||||
BruteForce
|
||||
BadWebBot
|
||||
ExploitedHost
|
||||
WebAppAttack
|
||||
Ssh
|
||||
IotTargeted
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
BaseURL *url.URL
|
||||
|
@ -51,6 +90,7 @@ func NewClient(client *http.Client) *Client {
|
|||
return c
|
||||
}
|
||||
|
||||
// AddApiKey pass api key from https://www.abuseipdb.com/account/api
|
||||
func (c *Client) AddApiKey(key string) {
|
||||
c.APIKey = key
|
||||
}
|
||||
|
@ -126,6 +166,9 @@ func (c *Client) do(ctx context.Context, req *http.Request, v any) error {
|
|||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := handleError(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
|
@ -172,14 +215,13 @@ type CheckResult struct {
|
|||
type CheckResultReport struct {
|
||||
ReportedAt time.Time `json:"reportedAt"`
|
||||
Comment string `json:"comment"`
|
||||
Categories []int `json:"categories"`
|
||||
Categories []ReportCategory `json:"categories"`
|
||||
ReporterId int `json:"reporterId"`
|
||||
ReporterCountryCode string `json:"reporterCountryCode"`
|
||||
ReporterCountryName string `json:"reporterCountryName"`
|
||||
}
|
||||
|
||||
var checkEndpoint = "check"
|
||||
|
||||
// Check an IP for abuse and additional information
|
||||
func (c *Client) Check(ctx context.Context, ip net.IP, opts *CheckOptions) (*CheckResult, error) {
|
||||
ipAddress := html.EscapeString(ip.String())
|
||||
parameters := map[string]string{
|
||||
|
@ -195,7 +237,7 @@ func (c *Client) Check(ctx context.Context, ip net.IP, opts *CheckOptions) (*Che
|
|||
}
|
||||
}
|
||||
|
||||
req, err := c.newRequest(http.MethodGet, checkEndpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodGet, endpointCheck, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -208,13 +250,15 @@ func (c *Client) Check(ctx context.Context, ip net.IP, opts *CheckOptions) (*Che
|
|||
return &result, nil
|
||||
}
|
||||
|
||||
// CheckCached uses the client cache and the ip as a key
|
||||
// CheckCached same as Check but uses the client cache and the ip as a key (cache has to be added to client for this to work)
|
||||
func (c *Client) CheckCached(ctx context.Context, ip net.IP, opts *CheckOptions) (*CheckResult, error) {
|
||||
key := fmt.Sprintf("%s:%s", checkEndpoint, ip.String())
|
||||
if c.cache == nil {
|
||||
return nil, errors.New("cache is missing")
|
||||
}
|
||||
key := fmt.Sprintf("%s:%s", endpointCheck, ip.String())
|
||||
if r, err := c.cache.Get(key); err == nil {
|
||||
result := CheckResult{}
|
||||
if jsonErr := json.NewDecoder(bytes.NewReader(r)).Decode(&result); jsonErr == nil {
|
||||
fmt.Println("return cached")
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
|
@ -253,14 +297,14 @@ type ReportsResult struct {
|
|||
type ReportResultReport struct {
|
||||
ReportedAt time.Time `json:"reportedAt"`
|
||||
Comment string `json:"comment"`
|
||||
Categories []int `json:"categories"`
|
||||
Categories []ReportCategory `json:"categories"`
|
||||
ReporterId int `json:"reporterId"`
|
||||
ReporterCountryCode string `json:"reporterCountryCode"`
|
||||
ReporterCountryName string `json:"reporterCountryName"`
|
||||
}
|
||||
|
||||
// Reports returns reports for a certain IP
|
||||
func (c *Client) Reports(ctx context.Context, ip net.IP, opts *ReportsOptions) (*ReportsResult, error) {
|
||||
var endpoint = "reports"
|
||||
ipAddress := html.EscapeString(ip.String())
|
||||
parameters := map[string]string{
|
||||
"ipAddress": ipAddress,
|
||||
|
@ -278,7 +322,7 @@ func (c *Client) Reports(ctx context.Context, ip net.IP, opts *ReportsOptions) (
|
|||
}
|
||||
}
|
||||
|
||||
req, err := c.newRequest(http.MethodGet, endpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodGet, endpointReports, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -332,12 +376,11 @@ func handleBlacklistOptions(opts *BlacklistOptions) map[string]string {
|
|||
return parameters
|
||||
}
|
||||
|
||||
// Blacklist will generate a blacklist based on the Options
|
||||
func (c *Client) Blacklist(ctx context.Context, opts *BlacklistOptions) (*BlacklistResult, error) {
|
||||
var endpoint = "blacklist"
|
||||
|
||||
parameters := handleBlacklistOptions(opts)
|
||||
|
||||
req, err := c.newRequest(http.MethodGet, endpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodGet, endpointBlacklist, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -350,12 +393,11 @@ func (c *Client) Blacklist(ctx context.Context, opts *BlacklistOptions) (*Blackl
|
|||
return &result, nil
|
||||
}
|
||||
|
||||
// BlacklistPlain same as Blacklist but returns a newline separated string
|
||||
func (c *Client) BlacklistPlain(ctx context.Context, opts *BlacklistOptions) (io.Reader, error) {
|
||||
var endpoint = "blacklist"
|
||||
|
||||
parameters := handleBlacklistOptions(opts)
|
||||
|
||||
req, err := c.newRequest(http.MethodGet, endpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodGet, endpointBlacklist, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -374,8 +416,9 @@ func (c *Client) BlacklistPlain(ctx context.Context, opts *BlacklistOptions) (io
|
|||
return &result, nil
|
||||
}
|
||||
|
||||
// ReportOptions at least Categories is required
|
||||
type ReportOptions struct {
|
||||
Categories []int `json:"categories"`
|
||||
Categories []ReportCategory `json:"categories"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Time *time.Time `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
@ -387,9 +430,8 @@ type ReportResult struct {
|
|||
} `json:"data"`
|
||||
}
|
||||
|
||||
// Report sends a report for an IP with additional Information
|
||||
func (c *Client) Report(ctx context.Context, ip net.IP, opts *ReportOptions) (*ReportResult, error) {
|
||||
var endpoint = "report"
|
||||
|
||||
ipAddress := html.EscapeString(ip.String())
|
||||
parameters := map[string]string{
|
||||
"ip": ipAddress,
|
||||
|
@ -399,7 +441,7 @@ func (c *Client) Report(ctx context.Context, ip net.IP, opts *ReportOptions) (*R
|
|||
if opts.Categories != nil {
|
||||
categories := make([]string, len(opts.Categories))
|
||||
for i, category := range opts.Categories {
|
||||
categories[i] = strconv.Itoa(category)
|
||||
categories[i] = strconv.Itoa(int(category))
|
||||
}
|
||||
parameters["categories"] = strings.Join(categories, ",")
|
||||
}
|
||||
|
@ -411,7 +453,7 @@ func (c *Client) Report(ctx context.Context, ip net.IP, opts *ReportOptions) (*R
|
|||
}
|
||||
}
|
||||
|
||||
req, err := c.newRequest(http.MethodPost, endpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodPost, endpointReport, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -445,13 +487,15 @@ type CheckBlockResult struct {
|
|||
} `json:"data"`
|
||||
}
|
||||
|
||||
// CheckBlock test an IPNet of up to /16 and returns all addresses with reports, it is recommended to check /24 networks
|
||||
func (c *Client) CheckBlock(ctx context.Context, ipnNet *net.IPNet, opts *CheckBlockOptions) (*CheckBlockResult, error) {
|
||||
var endpoint = "check-block"
|
||||
|
||||
network := html.EscapeString(ipnNet.String())
|
||||
parameters := map[string]string{
|
||||
"network": network,
|
||||
}
|
||||
if cidr, _ := ipnNet.Mask.Size(); cidr < 16 {
|
||||
return nil, fmt.Errorf("CIDR must be less than or equal to 16")
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
if opts.MaxAgeInDays > 0 && opts.MaxAgeInDays <= 365 {
|
||||
|
@ -459,7 +503,7 @@ func (c *Client) CheckBlock(ctx context.Context, ipnNet *net.IPNet, opts *CheckB
|
|||
}
|
||||
}
|
||||
|
||||
req, err := c.newRequest(http.MethodGet, endpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodGet, endpointCheckBlock, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -472,9 +516,35 @@ func (c *Client) CheckBlock(ctx context.Context, ipnNet *net.IPNet, opts *CheckB
|
|||
return &result, nil
|
||||
}
|
||||
|
||||
// CheckBlockCached same as CheckBlock but uses the client cache and the ipnet as a key (cache has to be added to client for this to work)
|
||||
func (c *Client) CheckBlockCached(ctx context.Context, ipNet *net.IPNet, opts *CheckBlockOptions) (*CheckBlockResult, error) {
|
||||
if c.cache == nil {
|
||||
return nil, errors.New("cache is missing")
|
||||
}
|
||||
key := fmt.Sprintf("%s:%s", endpointCheck, ipNet.String())
|
||||
if r, err := c.cache.Get(key); err == nil {
|
||||
result := CheckBlockResult{}
|
||||
if jsonErr := json.NewDecoder(bytes.NewReader(r)).Decode(&result); jsonErr == nil {
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
if result, err := c.CheckBlock(ctx, ipNet, opts); err == nil {
|
||||
resultEncoded := bytes.Buffer{}
|
||||
if jsonErr := json.NewEncoder(&resultEncoded).Encode(result); jsonErr != nil {
|
||||
return nil, jsonErr
|
||||
}
|
||||
if re := resultEncoded.Bytes(); re != nil {
|
||||
c.cache.Set(key, re)
|
||||
}
|
||||
return result, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
type BulkReportData struct {
|
||||
IpAddress string `csv:"IP"`
|
||||
Categories []int `csv:"Categories"`
|
||||
Categories []ReportCategory `csv:"Categories"`
|
||||
ReportDate time.Time `csv:"ReportDate"`
|
||||
Comment string `csv:"Comment"`
|
||||
}
|
||||
|
@ -510,7 +580,7 @@ func (b *BulkReportDatas) toSlice() [][]string {
|
|||
for _, data := range *b {
|
||||
categories := make([]string, len(data.Categories))
|
||||
for i, category := range data.Categories {
|
||||
categories[i] = strconv.Itoa(category)
|
||||
categories[i] = strconv.Itoa(int(category))
|
||||
}
|
||||
slice = append(slice, []string{
|
||||
data.IpAddress,
|
||||
|
@ -578,9 +648,8 @@ type BulkReportResult struct {
|
|||
} `json:"data"`
|
||||
}
|
||||
|
||||
// BulkReport takes a list of reports the success for ich reported entry
|
||||
func (c *Client) BulkReport(ctx context.Context, data *BulkReportDatas) (*BulkReportResult, error) {
|
||||
var endpoint = "bulk-report"
|
||||
|
||||
if data == nil || len(*data) == 0 {
|
||||
return nil, errors.New("bulk report: no data")
|
||||
}
|
||||
|
@ -593,7 +662,7 @@ func (c *Client) BulkReport(ctx context.Context, data *BulkReportDatas) (*BulkRe
|
|||
}
|
||||
|
||||
requestBody, contentType := toMultipartCsv(csvData)
|
||||
req, err := c.newRequest(http.MethodPost, endpoint, nil, requestBody)
|
||||
req, err := c.newRequest(http.MethodPost, endpointBulkReport, nil, requestBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -613,14 +682,14 @@ type ClearAddressData struct {
|
|||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ClearAddress allow one to delete all reports made from your account for a certain address
|
||||
func (c *Client) ClearAddress(ctx context.Context, ip net.IP) (*ClearAddressData, error) {
|
||||
var endpoint = "clear-address"
|
||||
ipAddress := html.EscapeString(ip.String())
|
||||
parameters := map[string]string{
|
||||
"ipAddress": ipAddress,
|
||||
}
|
||||
|
||||
req, err := c.newRequest(http.MethodDelete, endpoint, parameters, nil)
|
||||
req, err := c.newRequest(http.MethodDelete, endpointClearAddress, parameters, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue