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
|
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 {
|
type Client struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
BaseURL *url.URL
|
BaseURL *url.URL
|
||||||
|
@ -51,6 +90,7 @@ func NewClient(client *http.Client) *Client {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddApiKey pass api key from https://www.abuseipdb.com/account/api
|
||||||
func (c *Client) AddApiKey(key string) {
|
func (c *Client) AddApiKey(key string) {
|
||||||
c.APIKey = key
|
c.APIKey = key
|
||||||
}
|
}
|
||||||
|
@ -126,6 +166,9 @@ func (c *Client) do(ctx context.Context, req *http.Request, v any) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if err := handleError(resp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
|
@ -172,14 +215,13 @@ type CheckResult struct {
|
||||||
type CheckResultReport struct {
|
type CheckResultReport struct {
|
||||||
ReportedAt time.Time `json:"reportedAt"`
|
ReportedAt time.Time `json:"reportedAt"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
Categories []int `json:"categories"`
|
Categories []ReportCategory `json:"categories"`
|
||||||
ReporterId int `json:"reporterId"`
|
ReporterId int `json:"reporterId"`
|
||||||
ReporterCountryCode string `json:"reporterCountryCode"`
|
ReporterCountryCode string `json:"reporterCountryCode"`
|
||||||
ReporterCountryName string `json:"reporterCountryName"`
|
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) {
|
func (c *Client) Check(ctx context.Context, ip net.IP, opts *CheckOptions) (*CheckResult, error) {
|
||||||
ipAddress := html.EscapeString(ip.String())
|
ipAddress := html.EscapeString(ip.String())
|
||||||
parameters := map[string]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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -208,13 +250,15 @@ func (c *Client) Check(ctx context.Context, ip net.IP, opts *CheckOptions) (*Che
|
||||||
return &result, nil
|
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) {
|
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 {
|
if r, err := c.cache.Get(key); err == nil {
|
||||||
result := CheckResult{}
|
result := CheckResult{}
|
||||||
if jsonErr := json.NewDecoder(bytes.NewReader(r)).Decode(&result); jsonErr == nil {
|
if jsonErr := json.NewDecoder(bytes.NewReader(r)).Decode(&result); jsonErr == nil {
|
||||||
fmt.Println("return cached")
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,14 +297,14 @@ type ReportsResult struct {
|
||||||
type ReportResultReport struct {
|
type ReportResultReport struct {
|
||||||
ReportedAt time.Time `json:"reportedAt"`
|
ReportedAt time.Time `json:"reportedAt"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
Categories []int `json:"categories"`
|
Categories []ReportCategory `json:"categories"`
|
||||||
ReporterId int `json:"reporterId"`
|
ReporterId int `json:"reporterId"`
|
||||||
ReporterCountryCode string `json:"reporterCountryCode"`
|
ReporterCountryCode string `json:"reporterCountryCode"`
|
||||||
ReporterCountryName string `json:"reporterCountryName"`
|
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) {
|
func (c *Client) Reports(ctx context.Context, ip net.IP, opts *ReportsOptions) (*ReportsResult, error) {
|
||||||
var endpoint = "reports"
|
|
||||||
ipAddress := html.EscapeString(ip.String())
|
ipAddress := html.EscapeString(ip.String())
|
||||||
parameters := map[string]string{
|
parameters := map[string]string{
|
||||||
"ipAddress": ipAddress,
|
"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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -332,12 +376,11 @@ func handleBlacklistOptions(opts *BlacklistOptions) map[string]string {
|
||||||
return parameters
|
return parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blacklist will generate a blacklist based on the Options
|
||||||
func (c *Client) Blacklist(ctx context.Context, opts *BlacklistOptions) (*BlacklistResult, error) {
|
func (c *Client) Blacklist(ctx context.Context, opts *BlacklistOptions) (*BlacklistResult, error) {
|
||||||
var endpoint = "blacklist"
|
|
||||||
|
|
||||||
parameters := handleBlacklistOptions(opts)
|
parameters := handleBlacklistOptions(opts)
|
||||||
|
|
||||||
req, err := c.newRequest(http.MethodGet, endpoint, parameters, nil)
|
req, err := c.newRequest(http.MethodGet, endpointBlacklist, parameters, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -350,12 +393,11 @@ func (c *Client) Blacklist(ctx context.Context, opts *BlacklistOptions) (*Blackl
|
||||||
return &result, nil
|
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) {
|
func (c *Client) BlacklistPlain(ctx context.Context, opts *BlacklistOptions) (io.Reader, error) {
|
||||||
var endpoint = "blacklist"
|
|
||||||
|
|
||||||
parameters := handleBlacklistOptions(opts)
|
parameters := handleBlacklistOptions(opts)
|
||||||
|
|
||||||
req, err := c.newRequest(http.MethodGet, endpoint, parameters, nil)
|
req, err := c.newRequest(http.MethodGet, endpointBlacklist, parameters, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -374,8 +416,9 @@ func (c *Client) BlacklistPlain(ctx context.Context, opts *BlacklistOptions) (io
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReportOptions at least Categories is required
|
||||||
type ReportOptions struct {
|
type ReportOptions struct {
|
||||||
Categories []int `json:"categories"`
|
Categories []ReportCategory `json:"categories"`
|
||||||
Comment string `json:"comment,omitempty"`
|
Comment string `json:"comment,omitempty"`
|
||||||
Time *time.Time `json:"timestamp,omitempty"`
|
Time *time.Time `json:"timestamp,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -387,9 +430,8 @@ type ReportResult struct {
|
||||||
} `json:"data"`
|
} `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) {
|
func (c *Client) Report(ctx context.Context, ip net.IP, opts *ReportOptions) (*ReportResult, error) {
|
||||||
var endpoint = "report"
|
|
||||||
|
|
||||||
ipAddress := html.EscapeString(ip.String())
|
ipAddress := html.EscapeString(ip.String())
|
||||||
parameters := map[string]string{
|
parameters := map[string]string{
|
||||||
"ip": ipAddress,
|
"ip": ipAddress,
|
||||||
|
@ -399,7 +441,7 @@ func (c *Client) Report(ctx context.Context, ip net.IP, opts *ReportOptions) (*R
|
||||||
if opts.Categories != nil {
|
if opts.Categories != nil {
|
||||||
categories := make([]string, len(opts.Categories))
|
categories := make([]string, len(opts.Categories))
|
||||||
for i, category := range opts.Categories {
|
for i, category := range opts.Categories {
|
||||||
categories[i] = strconv.Itoa(category)
|
categories[i] = strconv.Itoa(int(category))
|
||||||
}
|
}
|
||||||
parameters["categories"] = strings.Join(categories, ",")
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -445,13 +487,15 @@ type CheckBlockResult struct {
|
||||||
} `json:"data"`
|
} `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) {
|
func (c *Client) CheckBlock(ctx context.Context, ipnNet *net.IPNet, opts *CheckBlockOptions) (*CheckBlockResult, error) {
|
||||||
var endpoint = "check-block"
|
|
||||||
|
|
||||||
network := html.EscapeString(ipnNet.String())
|
network := html.EscapeString(ipnNet.String())
|
||||||
parameters := map[string]string{
|
parameters := map[string]string{
|
||||||
"network": network,
|
"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 != nil {
|
||||||
if opts.MaxAgeInDays > 0 && opts.MaxAgeInDays <= 365 {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -472,9 +516,35 @@ func (c *Client) CheckBlock(ctx context.Context, ipnNet *net.IPNet, opts *CheckB
|
||||||
return &result, nil
|
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 {
|
type BulkReportData struct {
|
||||||
IpAddress string `csv:"IP"`
|
IpAddress string `csv:"IP"`
|
||||||
Categories []int `csv:"Categories"`
|
Categories []ReportCategory `csv:"Categories"`
|
||||||
ReportDate time.Time `csv:"ReportDate"`
|
ReportDate time.Time `csv:"ReportDate"`
|
||||||
Comment string `csv:"Comment"`
|
Comment string `csv:"Comment"`
|
||||||
}
|
}
|
||||||
|
@ -510,7 +580,7 @@ func (b *BulkReportDatas) toSlice() [][]string {
|
||||||
for _, data := range *b {
|
for _, data := range *b {
|
||||||
categories := make([]string, len(data.Categories))
|
categories := make([]string, len(data.Categories))
|
||||||
for i, category := range data.Categories {
|
for i, category := range data.Categories {
|
||||||
categories[i] = strconv.Itoa(category)
|
categories[i] = strconv.Itoa(int(category))
|
||||||
}
|
}
|
||||||
slice = append(slice, []string{
|
slice = append(slice, []string{
|
||||||
data.IpAddress,
|
data.IpAddress,
|
||||||
|
@ -578,9 +648,8 @@ type BulkReportResult struct {
|
||||||
} `json:"data"`
|
} `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) {
|
func (c *Client) BulkReport(ctx context.Context, data *BulkReportDatas) (*BulkReportResult, error) {
|
||||||
var endpoint = "bulk-report"
|
|
||||||
|
|
||||||
if data == nil || len(*data) == 0 {
|
if data == nil || len(*data) == 0 {
|
||||||
return nil, errors.New("bulk report: no data")
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -613,14 +682,14 @@ type ClearAddressData struct {
|
||||||
} `json:"data"`
|
} `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) {
|
func (c *Client) ClearAddress(ctx context.Context, ip net.IP) (*ClearAddressData, error) {
|
||||||
var endpoint = "clear-address"
|
|
||||||
ipAddress := html.EscapeString(ip.String())
|
ipAddress := html.EscapeString(ip.String())
|
||||||
parameters := map[string]string{
|
parameters := map[string]string{
|
||||||
"ipAddress": ipAddress,
|
"ipAddress": ipAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := c.newRequest(http.MethodDelete, endpoint, parameters, nil)
|
req, err := c.newRequest(http.MethodDelete, endpointClearAddress, parameters, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue