-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1c60b62
Showing
10 changed files
with
1,073 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# These are supported funding model platforms | ||
|
||
github: palner |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# ---> macOS | ||
.DS_Store | ||
.AppleDouble | ||
.LSOverride | ||
# Icon must end with two \r | ||
Icon | ||
# Thumbnails | ||
._* | ||
# Files that might appear in the root of a volume | ||
.DocumentRevisions-V100 | ||
.fseventsd | ||
.Spotlight-V100 | ||
.TemporaryItems | ||
.Trashes | ||
.VolumeIcon.icns | ||
# Directories potentially created on remote AFP share | ||
.AppleDB | ||
.AppleDesktop | ||
Network Trash Folder | ||
Temporary Items | ||
.apdisk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"default": true, | ||
"MD003": { "style": "atx_closed" }, | ||
"MD007": { "indent": 4 }, | ||
"MD013": false, | ||
"MD040": false, | ||
"no-hard-tabs": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
golang | ||
redis | ||
IPTABLES | ||
iptables | ||
opn | ||
htables | ||
autoexpire | ||
rtimer | ||
ipaddresses | ||
ipban | ||
Voi | ||
rtc | ||
ipset | ||
opnsense | ||
ipsets | ||
Nsense | ||
homeserver | ||
apiban |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# APIBAN-tools | ||
|
||
apiban-tools is brought to you by [apiban.org](https://apiban.org) and is an open source set of tools to help automate apiban based actions, such as `check`, `get`, etc. | ||
|
||
For more information, please visit apiban.org. | ||
|
||
**APIBAN is made possible by the generosity of our [sponsors](https://apiban.org/doc.html#sponsors).** | ||
|
||
## Getting Help | ||
|
||
Help is provided by LOD (<https://www.lod.com>) and an APIBAN room ([#apiban:matrix.lod.com](https://matrix.to/#/#apiban:matrix.lod.com)) is available on the LOD Matrix server. The software is provided under the GPLv2 license. | ||
|
||
## Warranty | ||
|
||
ABIBAN data is provided in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
/* | ||
* Copyright (C) 2020-2021 Fred Posner (palner.com) | ||
* | ||
* This file is part of APIBAN.org. | ||
* | ||
* apiban is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version | ||
* | ||
* apiban is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
* | ||
*/ | ||
|
||
package golib | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
var ( | ||
// RootURL is the base URI of the APIBAN.org API server | ||
RootURL = "https://apiban.org/api/" | ||
) | ||
|
||
// ErrBadRequest indicates a 400 response was received; | ||
// | ||
// NOTE: this is used by the server to indicate both that an IP address is not | ||
// blocked (when calling Check) and that the list is complete (when calling | ||
// Banned) | ||
var ErrBadRequest = errors.New("bad request") | ||
|
||
// Entry describes a set of blocked IP addresses from APIBAN.org | ||
type Entry struct { | ||
|
||
// ID is the timestamp of the next Entry | ||
ID string `json:"ID"` | ||
|
||
// IPs is the list of blocked IP addresses in this entry | ||
IPs []string `json:"ipaddress"` | ||
} | ||
|
||
type ApiGetJson struct { | ||
SetType string `json:"set"` | ||
ID string `json:"id"` | ||
} | ||
|
||
type ApiGetCheck struct { | ||
SetType string `json:"set"` | ||
IP string `json:"ipaddress"` | ||
} | ||
|
||
// Banned returns a set of banned addresses, optionally limited to the | ||
// specified startFrom ID. If no startFrom is supplied, the entire current list will | ||
// be pulled. | ||
func Banned(key string, startFrom string, set string) (*Entry, error) { | ||
if key == "" { | ||
return nil, errors.New("API Key is required") | ||
} | ||
|
||
if key == "MY API KEY" { | ||
return nil, errors.New("API Key is required") | ||
} | ||
|
||
if startFrom == "" { | ||
startFrom = "100" // NOTE: arbitrary ID copied from reference source | ||
} | ||
|
||
if set == "" { | ||
set = "sip" | ||
} | ||
|
||
p := &ApiGetJson{ | ||
ID: startFrom, | ||
SetType: set, | ||
} | ||
|
||
p_json, err := json.Marshal(p) | ||
if err != nil { | ||
return nil, errors.New("error making json payload") | ||
} | ||
|
||
out := &Entry{ | ||
ID: startFrom, | ||
} | ||
|
||
url := RootURL + "get" | ||
for { | ||
e, err := queryServerv2(key, p_json, url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if e.ID == "none" { | ||
// List complete | ||
return out, nil | ||
} | ||
if e.ID == "" { | ||
return nil, errors.New("empty ID received") | ||
} | ||
|
||
// Set the next ID | ||
out.ID = e.ID | ||
|
||
// Aggregate the received IPs | ||
out.IPs = append(out.IPs, e.IPs...) | ||
} | ||
} | ||
|
||
// Check queries APIBAN.org to see if the provided IP address is blocked. | ||
func Check(key string, ip string, set string) (bool, error) { | ||
if key == "" { | ||
return false, errors.New("API Key is required") | ||
} | ||
if ip == "" { | ||
return false, errors.New("IP address is required") | ||
} | ||
|
||
if set == "" { | ||
set = "sip" | ||
} | ||
|
||
p := &ApiGetCheck{ | ||
IP: ip, | ||
SetType: set, | ||
} | ||
|
||
p_json, err := json.Marshal(p) | ||
if err != nil { | ||
return false, errors.New("error making json payload") | ||
} | ||
|
||
url := RootURL + "check" | ||
entry, err := queryServerv2(key, p_json, url) | ||
if err == ErrBadRequest { | ||
// Not blocked | ||
return false, nil | ||
} | ||
if err != nil { | ||
return false, err | ||
} | ||
if entry == nil { | ||
return false, errors.New("empty entry received") | ||
} else if len(entry.IPs) == 1 { | ||
if entry.IPs[0] == "not blocked" { | ||
// Not blocked | ||
return false, nil | ||
} | ||
} | ||
|
||
// IP address is blocked | ||
return true, nil | ||
} | ||
|
||
func queryServerv2(key string, p_json []byte, url string) (*Entry, error) { | ||
sendbody := strings.NewReader(string(p_json)) | ||
bearer := "Bearer " + key | ||
req, err := http.NewRequest("POST", url, sendbody) | ||
req.Header.Add("Authorization", bearer) | ||
if err != nil { | ||
// handle err | ||
return nil, err | ||
} | ||
|
||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return nil, fmt.Errorf("query error: %s", err.Error()) | ||
} | ||
defer resp.Body.Close() | ||
|
||
switch { | ||
case resp.StatusCode == http.StatusBadRequest || | ||
resp.StatusCode == http.StatusNotFound || | ||
resp.StatusCode == http.StatusForbidden: | ||
return processBadRequest(resp) | ||
case resp.StatusCode == http.StatusOK: | ||
break | ||
case resp.StatusCode > 400 && resp.StatusCode < 500: | ||
return nil, fmt.Errorf("client error (%d) from apiban.org: %s from %q", resp.StatusCode, resp.Status, url) | ||
case resp.StatusCode >= 500: | ||
return nil, fmt.Errorf("server error (%d) from apiban.org: %s from %q", resp.StatusCode, resp.Status, url) | ||
case resp.StatusCode > 299: | ||
return nil, fmt.Errorf("unhandled error (%d) from apiban.org: %s from %q", resp.StatusCode, resp.Status, url) | ||
} | ||
|
||
entry := new(Entry) | ||
if err = json.NewDecoder(resp.Body).Decode(entry); err != nil { | ||
return nil, fmt.Errorf("failed to decode server response: %s", err.Error()) | ||
} | ||
|
||
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: %s", err.Error()) | ||
} | ||
|
||
switch ee.AddressCode { | ||
case "rate limit exceeded": | ||
return nil, errors.New("rate limit exceeded") | ||
default: | ||
// unhandled case | ||
return nil, ErrBadRequest | ||
} | ||
} |
Oops, something went wrong.