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:
parent
8e9543970a
commit
28963eb813
6 changed files with 134 additions and 6 deletions
2
build.py
2
build.py
|
@ -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
|
||||
|
|
|
@ -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`)
|
||||
|
|
|
@ -16,7 +16,7 @@ const (
|
|||
DBUpToDate
|
||||
DBModernButAhead
|
||||
|
||||
targetDatabaseVersion = 2
|
||||
targetDatabaseVersion = 3
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -114,6 +114,8 @@ type IPBan struct {
|
|||
BannedForPostID *int
|
||||
CopyPostText template.HTML
|
||||
IP string
|
||||
IPRangeStart string
|
||||
IPRangeEnd string
|
||||
IssuedAt time.Time
|
||||
ipBanBase
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
Loading…
Add table
Add a link
Reference in a new issue