1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-17 10:56:24 -07:00

Add IP Range parsing, start working on adding range bans

This commit is contained in:
Eggbertx 2023-12-28 00:36:24 -08:00
parent 8e9543970a
commit 28963eb813
6 changed files with 134 additions and 6 deletions

View file

@ -39,7 +39,7 @@ release_files = (
)
GOCHAN_VERSION = "3.9.0"
DATABASE_VERSION = "2" # stored in DBNAME.DBPREFIXdatabase_version
DATABASE_VERSION = "3" # stored in DBNAME.DBPREFIXdatabase_version
PATH_NOTHING = -1
PATH_UNKNOWN = 0

View file

@ -65,7 +65,9 @@ func (dbu *GCDatabaseUpdater) MigrateDB() (bool, error) {
if err != nil {
return false, err
}
defer tx.Rollback()
defer func() {
tx.Rollback()
}()
switch criticalConfig.DBtype {
case "mysql":
@ -108,7 +110,9 @@ func (dbu *GCDatabaseUpdater) MigrateDB() (bool, error) {
if err != nil {
return false, err
}
defer rows.Close()
defer func() {
rows.Close()
}()
for rows.Next() {
var tableName string
err = rows.Scan(&tableName)
@ -120,6 +124,49 @@ func (dbu *GCDatabaseUpdater) MigrateDB() (bool, error) {
return false, err
}
}
if err = rows.Close(); err != nil {
return false, err
}
query = `SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'DBPREFIXip_ban'
AND COLUMN_NAME = 'ip'`
if err = dbu.db.QueryRowTxSQL(tx, query, nil, []any{&numColumns}); err != nil {
return false, err
}
if numColumns > 0 {
// add range_start and range_end columns
query = `ALTER TABLE DBPREFIXip_ban
ADD COLUMN IF NOT EXISTS range_start VARBINARY(16) NOT NULL
ADD COLUMN IF NOT EXISTS range_end VARBINARY(16) NOT NULL`
if _, err = gcsql.ExecTxSQL(tx, query); err != nil {
return false, err
}
// convert string to IP range
if rows, err = dbu.db.QuerySQL(`SELECT id, ip FROM DBPREFIXip_ban`); err != nil {
return false, err
}
var rangeStart string
var rangeEnd string
for rows.Next() {
var id int
var ipOrCIDR string
if err = rows.Scan(&id, &ipOrCIDR); err != nil {
return false, err
}
if rangeStart, rangeEnd, err = gcutil.ParseIPRange(ipOrCIDR); err != nil {
return false, err
}
query = `UPDATE DBPREFIXip_ban SET range_start = INET6_ATON(?), range_end = ? WHERE id = ?`
if _, err = gcsql.ExecTxSQL(tx, query, rangeStart, rangeEnd, id); err != nil {
return false, err
}
query = `ALTER TABLE DBPREFIXip_ban DROP COLUMN ip`
if _, err = gcsql.ExecTxSQL(tx, query); err != nil {
return false, err
}
}
}
err = nil
case "postgres":
_, err = gcsql.ExecSQL(`ALTER TABLE DBPREFIXwordfilters DROP CONSTRAINT IF EXISTS board_id_fk`)

View file

@ -16,7 +16,7 @@ const (
DBUpToDate
DBModernButAhead
targetDatabaseVersion = 2
targetDatabaseVersion = 3
)
var (

View file

@ -114,6 +114,8 @@ type IPBan struct {
BannedForPostID *int
CopyPostText template.HTML
IP string
IPRangeStart string
IPRangeEnd string
IssuedAt time.Time
ipBanBase
}

View file

@ -10,6 +10,7 @@ import (
"math/rand"
"net"
"net/http"
"net/netip"
"os"
"path"
"path/filepath"
@ -157,6 +158,83 @@ func MarshalJSON(data interface{}, indent bool) (string, error) {
return string(jsonBytes), err
}
// ParseIPRange takes a single IP address or an IP range of the form "networkIP/netmaskbits" and
// gives the starting IP and ending IP in the subnet
//
// More info: https://en.wikipedia.org/wiki/Subnet
func ParseIPRange(ipOrCIDR string) (string, string, error) {
var ipStart netip.Addr
if strings.ContainsRune(ipOrCIDR, '/') {
var ipEnd netip.Addr
// CIDR range
prefix, err := netip.ParsePrefix(ipOrCIDR)
if err != nil {
return "", "", err
}
ipStart = prefix.Addr()
ipEnd = prefix.Addr()
var tmp netip.Addr
for {
tmp = ipEnd.Next()
if !prefix.Contains(tmp) {
break
}
ipEnd = tmp
}
return ipStart.String(), ipEnd.String(), nil
}
// single IP
var err error
if ipStart, err = netip.ParseAddr(ipOrCIDR); err != nil {
return "", "", err
}
return ipStart.String(), ipStart.String(), nil
}
// GetIPRangeString returns an IP address if start == end, or the subnet of all IP
// addresses between start and end
func GetIPRangeString(start string, end string) (string, error) {
if start == end {
return start, nil
}
startIP := net.ParseIP(start)
endIP := net.ParseIP(end)
if startIP == nil {
return "", fmt.Errorf("invalid IP address %s", start)
}
if endIP == nil {
return "", fmt.Errorf("invalid IP address %s", end)
}
if len(startIP) != len(endIP) {
return "", errors.New("ip addresses must both be IPv4 or IPv6")
}
if startIP.To4() != nil {
startIP = startIP.To4()
endIP = endIP.To4()
}
bits := 0
var ipn net.IPNet
for b := range startIP {
if startIP[b] == endIP[b] {
bits += 8
continue
}
for i := 7; i >= 0; i-- {
if startIP[b]&(1<<i) == endIP[b]&(1<<i) {
bits++
continue
}
ipn = net.IPNet{IP: startIP, Mask: net.CIDRMask(bits, len(startIP)*8)}
return ipn.String(), nil
}
}
ipn = net.IPNet{IP: startIP, Mask: net.CIDRMask(bits, len(startIP)*8)}
return ipn.String(), nil
}
// ParseName takes a name string from a request object and returns the name and tripcode parts
func ParseName(name string) (string, string) {
var namePart string

View file

@ -146,7 +146,8 @@ CREATE TABLE DBPREFIXip_ban(
copy_post_text TEXT NOT NULL,
is_thread_ban BOOL NOT NULL,
is_active BOOL NOT NULL,
ip VARCHAR(45) NOT NULL,
range_start VARBINARY(16) NOT NULL,
range_end VARBINARY(16) NOT NULL,
issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
appeal_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
@ -268,4 +269,4 @@ CREATE TABLE DBPREFIXwordfilters(
);
INSERT INTO DBPREFIXdatabase_version(component, version)
VALUES('gochan', 2);
VALUES('gochan', 3);