github.com/crowdsecurity/crowdsec@v1.6.1/pkg/cticlient/client.go (about) 1 package cticlient 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "net/http" 9 "strings" 10 11 log "github.com/sirupsen/logrus" 12 ) 13 14 const ( 15 CTIBaseUrl = "https://cti.api.crowdsec.net/v2" 16 smokeEndpoint = "/smoke" 17 fireEndpoint = "/fire" 18 ) 19 20 var ( 21 ErrUnauthorized = errors.New("unauthorized") 22 ErrLimit = errors.New("request quota exceeded, please reduce your request rate") 23 ErrNotFound = errors.New("ip not found") 24 ErrDisabled = errors.New("cti is disabled") 25 ErrUnknown = errors.New("unknown error") 26 ) 27 28 type CrowdsecCTIClient struct { 29 httpClient *http.Client 30 apiKey string 31 Logger *log.Entry 32 } 33 34 func (c *CrowdsecCTIClient) doRequest(method string, endpoint string, params map[string]string) ([]byte, error) { 35 url := CTIBaseUrl + endpoint 36 if len(params) > 0 { 37 url += "?" 38 for k, v := range params { 39 url += fmt.Sprintf("%s=%s&", k, v) 40 } 41 } 42 req, err := http.NewRequest(method, url, nil) 43 if err != nil { 44 return nil, err 45 } 46 req.Header.Set("x-api-key", c.apiKey) 47 resp, err := c.httpClient.Do(req) 48 if err != nil { 49 return nil, err 50 } 51 defer resp.Body.Close() 52 if resp.StatusCode != http.StatusOK { 53 if resp.StatusCode == http.StatusForbidden { 54 return nil, ErrUnauthorized 55 } 56 if resp.StatusCode == http.StatusTooManyRequests { 57 return nil, ErrLimit 58 } 59 if resp.StatusCode == http.StatusNotFound { 60 return nil, ErrNotFound 61 } 62 return nil, fmt.Errorf("unexpected http code : %s", resp.Status) 63 } 64 respBody, err := io.ReadAll(resp.Body) 65 if err != nil { 66 return nil, err 67 } 68 return respBody, nil 69 } 70 71 func (c *CrowdsecCTIClient) GetIPInfo(ip string) (*SmokeItem, error) { 72 body, err := c.doRequest(http.MethodGet, smokeEndpoint+"/"+ip, nil) 73 if err != nil { 74 if errors.Is(err, ErrNotFound) { 75 return &SmokeItem{}, nil 76 } 77 return nil, err 78 } 79 item := SmokeItem{} 80 err = json.Unmarshal(body, &item) 81 if err != nil { 82 return nil, err 83 } 84 return &item, nil 85 } 86 87 func (c *CrowdsecCTIClient) SearchIPs(ips []string) (*SearchIPResponse, error) { 88 params := make(map[string]string) 89 params["ips"] = strings.Join(ips, ",") 90 body, err := c.doRequest(http.MethodGet, smokeEndpoint, params) 91 if err != nil { 92 return nil, err 93 } 94 searchIPResponse := SearchIPResponse{} 95 err = json.Unmarshal(body, &searchIPResponse) 96 if err != nil { 97 return nil, err 98 } 99 return &searchIPResponse, nil 100 } 101 102 func (c *CrowdsecCTIClient) Fire(params FireParams) (*FireResponse, error) { 103 paramsMap := make(map[string]string) 104 if params.Page != nil { 105 paramsMap["page"] = fmt.Sprintf("%d", *params.Page) 106 } 107 if params.Since != nil { 108 paramsMap["since"] = *params.Since 109 } 110 if params.Limit != nil { 111 paramsMap["limit"] = fmt.Sprintf("%d", *params.Limit) 112 } 113 114 body, err := c.doRequest(http.MethodGet, fireEndpoint, paramsMap) 115 if err != nil { 116 return nil, err 117 } 118 fireResponse := FireResponse{} 119 err = json.Unmarshal(body, &fireResponse) 120 if err != nil { 121 return nil, err 122 } 123 return &fireResponse, nil 124 } 125 126 func NewCrowdsecCTIClient(options ...func(*CrowdsecCTIClient)) *CrowdsecCTIClient { 127 client := &CrowdsecCTIClient{} 128 for _, option := range options { 129 option(client) 130 } 131 if client.httpClient == nil { 132 client.httpClient = &http.Client{} 133 } 134 // we cannot return with a ni logger, so we set a default one 135 if client.Logger == nil { 136 client.Logger = log.NewEntry(log.New()) 137 } 138 return client 139 } 140 141 func WithLogger(logger *log.Entry) func(*CrowdsecCTIClient) { 142 return func(c *CrowdsecCTIClient) { 143 c.Logger = logger 144 } 145 } 146 147 func WithHTTPClient(httpClient *http.Client) func(*CrowdsecCTIClient) { 148 return func(c *CrowdsecCTIClient) { 149 c.httpClient = httpClient 150 } 151 } 152 153 func WithAPIKey(apiKey string) func(*CrowdsecCTIClient) { 154 return func(c *CrowdsecCTIClient) { 155 c.apiKey = apiKey 156 } 157 }