Skip to content

Commit

Permalink
Add special case handling
Browse files Browse the repository at this point in the history
Properly handle cases of:
  - unauthorized
  - rate limit exceeded
  - no new bans

Fixes palner#5
Fixes palner#4

Signed-off-by: Seán C McCord <ulexus@gmail.com>
  • Loading branch information
Ulexus committed Mar 28, 2020
1 parent cec23aa commit db3ab09
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 19 deletions.
Binary file modified clients/go/apiban-iptables-client
Binary file not shown.
19 changes: 7 additions & 12 deletions clients/go/apiban-iptables/apiban-iptables-client.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ func main() {

// if no APIKEY, exit
if len(apiconfig.APIKEY) == 0 {
log.Print("Invalid APIKEY. Exiting.")
runtime.Goexit()
log.Fatalln("Invalid APIKEY. Exiting.")
}

// allow cli of FULL to reset LKID to 100
Expand All @@ -109,14 +108,12 @@ func main() {
ipt, err := iptables.New()
if err != nil {
log.Panic(err)
runtime.Goexit()
}

// Get existing chains from IPTABLES
originaListChain, err := ipt.ListChains("filter")
if err != nil {
log.Panic(err)
runtime.Goexit()
}

// Search for INPUT in IPTABLES
Expand All @@ -142,37 +139,35 @@ func main() {
err = ipt.ClearChain("filter", chain)
if err != nil {
log.Panic(err)
runtime.Goexit()
}

// Add APIBAN chain to INPUT
err = ipt.Insert("filter", "INPUT", 1, "-j", chain)
if err != nil {
log.Panic(err)
runtime.Goexit()
}

// Add APIBAN chain to FORWARD
err = ipt.Insert("filter", "FORWARD", 1, "-j", chain)
if err != nil {
log.Panic(err)
runtime.Goexit()
}
}

// Get list of banned ip's from APIBAN.org
res, err := apiban.Banned(apiconfig.APIKEY, apiconfig.LKID)
if err != nil {
log.Panic(err)
log.Fatalln("failed to get banned list:", err)
}
log.Print("got response")

if len(res.IPs) == 0 {
log.Print("No IP addresses detected. Exiting.")
if res.ID == apiconfig.LKID {
log.Print("Great news... no new bans to add. Exiting...")
os.Exit(0)
}

if res.ID == "none" {
log.Print("Great news... no new bans to add. Exiting...")
if len(res.IPs) == 0 {
log.Print("No IP addresses detected. Exiting.")
os.Exit(0)
}

Expand Down
72 changes: 65 additions & 7 deletions clients/go/apiban/apiban.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
package apiban

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)

Expand Down Expand Up @@ -68,10 +70,6 @@ func Banned(key string, startFrom string) (*Entry, error) {

for {
e, err := queryServer(http.DefaultClient, fmt.Sprintf("%s%s/banned/%s", RootURL, key, out.ID))
if err == ErrBadRequest {
// End of list
return out, nil
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -124,9 +122,9 @@ func queryServer(c *http.Client, u string) (*Entry, error) {
}
defer resp.Body.Close()

// Error code 400 is special for Check errors, indicating the IP address is not blacklisted, so we return a sentinel error here
if resp.StatusCode == 400 {
return nil, ErrBadRequest
// StatusBadRequest (400) has a number of special cases to handle
if resp.StatusCode == http.StatusBadRequest {
return processBadRequest(resp)
}
if resp.StatusCode > 400 && resp.StatusCode < 500 {
return nil, fmt.Errorf("client error (%d) from apiban.org: %s from %q", resp.StatusCode, resp.Status, u)
Expand All @@ -142,5 +140,65 @@ func queryServer(c *http.Client, u string) (*Entry, error) {
if err = json.NewDecoder(resp.Body).Decode(entry); err != nil {
return nil, fmt.Errorf("failed to decode server response: %w", err)
}

return entry, nil
}

func processBadRequest(resp *http.Response) (*Entry, error) {
var buf bytes.Buffer
if _, err := buf.ReadFrom(resp.Body); err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

// Read the bytes buffer into a new bytes.Reader
r := bytes.NewReader(buf.Bytes())

// First, try decoding as a normal entry
e := new(Entry)
if err := json.NewDecoder(r).Decode(e); err == nil {
// Successfully decoded normal entry

switch e.ID {
case "none":
// non-error case
case "unauthorized":
return nil, errors.New("unauthorized")
default:
// unhandled case
return nil, ErrBadRequest
}

if len(e.IPs) > 0 {
switch e.IPs[0] {
case "no new bans":
return e, nil
}
}

// Unhandled case
return nil, ErrBadRequest
}

// Next, try decoding as an errorEntry
if _, err := r.Seek(0, io.SeekStart); err != nil {
return nil, fmt.Errorf("failed to re-seek to beginning of response buffer: %w", err)
}

type errorEntry struct {
AddressCode string `json:"ipaddress"`
IDCode string `json:"ID"`
}

ee := new(errorEntry)
if err := json.NewDecoder(r).Decode(ee); err != nil {
return nil, fmt.Errorf("failed to decode Bad Request response: %w", err)
}

switch ee.AddressCode {
case "rate limit exceeded":
return nil, errors.New("rate limit exceeded")
default:
// unhandled case
return nil, ErrBadRequest
}
}

0 comments on commit db3ab09

Please sign in to comment.