dline: Should be working now

This commit is contained in:
Daniel Oaks 2016-11-04 21:14:52 +10:00
parent 8c797d0f76
commit 011419e755
4 changed files with 242 additions and 17 deletions

View File

@ -206,7 +206,7 @@ var Commands = map[string]Command{
minParams: 1, minParams: 1,
}, },
"UNDLINE": { "UNDLINE": {
handler: undlineHandler, handler: unDLineHandler,
minParams: 1, minParams: 1,
oper: true, oper: true,
}, },

View File

@ -3,15 +3,34 @@
package irc package irc
import "net" import (
import "time" "errors"
"fmt"
"net"
"time"
"strings"
"encoding/json"
"github.com/DanielOaks/girc-go/ircmsg"
"github.com/tidwall/buntdb"
)
const (
keyDlineEntry = "bans.dline %s"
)
var (
errNoExistingBan = errors.New("Ban does not exist")
)
// IPRestrictTime contains the expiration info about the given IP. // IPRestrictTime contains the expiration info about the given IP.
type IPRestrictTime struct { type IPRestrictTime struct {
// Duration is how long this block lasts for.
Duration time.Duration
// Expires is when this block expires. // Expires is when this block expires.
Expires time.Time Expires time.Time
// Length is how long this block lasts for.
Length time.Duration
} }
// IsExpired returns true if the time has expired. // IsExpired returns true if the time has expired.
@ -24,7 +43,7 @@ type IPBanInfo struct {
// Reason is the ban reason. // Reason is the ban reason.
Reason string Reason string
// OperReason is an oper ban reason. // OperReason is an oper ban reason.
OperReason string OperReason string `json:"oper_reason"`
// Time holds details about the duration, if it exists. // Time holds details about the duration, if it exists.
Time *IPRestrictTime Time *IPRestrictTime
} }
@ -55,22 +74,21 @@ type DLineManager struct {
// NewDLineManager returns a new DLineManager. // NewDLineManager returns a new DLineManager.
func NewDLineManager() *DLineManager { func NewDLineManager() *DLineManager {
dm := DLineManager{ var dm DLineManager
addresses: make(map[string]*dLineAddr), dm.addresses = make(map[string]*dLineAddr)
networks: make(map[string]*dLineNet), dm.networks = make(map[string]*dLineNet)
}
return &dm return &dm
} }
// AddNetwork adds a network to the blocked list. // AddNetwork adds a network to the blocked list.
func (dm *DLineManager) AddNetwork(network net.IPNet, length *IPRestrictTime) { func (dm *DLineManager) AddNetwork(network net.IPNet, length *IPRestrictTime, reason string, operReason string) {
netString := network.String() netString := network.String()
dln := dLineNet{ dln := dLineNet{
Network: network, Network: network,
Info: IPBanInfo{ Info: IPBanInfo{
Time: length, Time: length,
Reason: "", Reason: reason,
OperReason: "", OperReason: operReason,
}, },
} }
dm.networks[netString] = &dln dm.networks[netString] = &dln
@ -83,14 +101,14 @@ func (dm *DLineManager) RemoveNetwork(network net.IPNet) {
} }
// AddIP adds an IP address to the blocked list. // AddIP adds an IP address to the blocked list.
func (dm *DLineManager) AddIP(addr net.IP, length *IPRestrictTime) { func (dm *DLineManager) AddIP(addr net.IP, length *IPRestrictTime, reason string, operReason string) {
addrString := addr.String() addrString := addr.String()
dla := dLineAddr{ dla := dLineAddr{
Address: addr, Address: addr,
Info: IPBanInfo{ Info: IPBanInfo{
Time: length, Time: length,
Reason: "", Reason: reason,
OperReason: "", OperReason: operReason,
}, },
} }
dm.addresses[addrString] = &dla dm.addresses[addrString] = &dla
@ -149,3 +167,209 @@ func (dm *DLineManager) CheckIP(addr net.IP) (isBanned bool, info *IPBanInfo) {
// no matches! // no matches!
return false, nil return false, nil
} }
// DLINE [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]
func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// check oper permissions
if !client.class.Capabilities["oper:local_ban"] {
client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, "Insufficient oper privs")
return false
}
currentArg := 0
// duration
duration, err := time.ParseDuration(msg.Params[currentArg])
durationIsUsed := err == nil
if durationIsUsed {
currentArg++
}
// get host
if len(msg.Params) < currentArg+1 {
client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters")
return false
}
hostString := msg.Params[currentArg]
currentArg++
// check host
var hostAddr net.IP
var hostNet *net.IPNet
_, hostNet, err = net.ParseCIDR(hostString)
if err != nil {
hostAddr = net.ParseIP(hostString)
}
if hostAddr == nil && hostNet == nil {
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Could not parse IP address or CIDR network")
return false
}
if hostNet == nil {
hostString = hostAddr.String()
} else {
hostString = hostNet.String()
}
// check remote
if len(msg.Params) > currentArg && msg.Params[currentArg] == "ON" {
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Remote servers not yet supported")
return false
}
// get comment(s)
reason := "No reason given"
operReason := "No reason given"
if len(msg.Params) > currentArg {
tempReason := strings.TrimSpace(msg.Params[currentArg])
if len(tempReason) > 0 && tempReason != "|" {
tempReasons := strings.SplitN(tempReason, "|", 2)
if tempReasons[0] != "" {
reason = tempReasons[0]
}
if len(tempReasons) > 1 && tempReasons[1] != "" {
operReason = tempReasons[1]
} else {
operReason = reason
}
}
}
// assemble ban info
var banTime *IPRestrictTime
if durationIsUsed {
banTime = &IPRestrictTime{
Duration: duration,
Expires: time.Now().Add(duration),
}
}
info := IPBanInfo{
Reason: reason,
OperReason: operReason,
Time: banTime,
}
// save in datastore
err = server.store.Update(func(tx *buntdb.Tx) error {
dlineKey := fmt.Sprintf(keyDlineEntry, hostString)
// assemble json from ban info
b, err := json.Marshal(info)
if err != nil {
return err
}
tx.Set(dlineKey, string(b), nil)
return nil
})
if hostNet == nil {
server.dlines.AddIP(hostAddr, banTime, reason, operReason)
} else {
server.dlines.AddNetwork(*hostNet, banTime, reason, operReason)
}
if durationIsUsed {
client.Notice(fmt.Sprintf("Added temporary (%s) D-Line for %s", duration.String(), hostString))
} else {
client.Notice(fmt.Sprintf("Added D-Line for %s", hostString))
}
return false
}
func unDLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// check oper permissions
if !client.class.Capabilities["oper:local_unban"] {
client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, "Insufficient oper privs")
return false
}
// get host
hostString := msg.Params[0]
// check host
var hostAddr net.IP
var hostNet *net.IPNet
_, hostNet, err := net.ParseCIDR(hostString)
if err != nil {
hostAddr = net.ParseIP(hostString)
}
if hostAddr == nil && hostNet == nil {
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Could not parse IP address or CIDR network")
return false
}
if hostNet == nil {
hostString = hostAddr.String()
} else {
hostString = hostNet.String()
}
// save in datastore
err = server.store.Update(func(tx *buntdb.Tx) error {
dlineKey := fmt.Sprintf(keyDlineEntry, hostString)
// check if it exists or not
val, err := tx.Get(dlineKey)
if val == "" {
return errNoExistingBan
} else if err != nil {
return err
}
tx.Delete(hostString)
return nil
})
if err != nil {
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, fmt.Sprintf("Could not remove ban [%s]", err.Error()))
}
if hostNet == nil {
server.dlines.RemoveIP(hostAddr)
} else {
server.dlines.RemoveNetwork(*hostNet)
}
client.Notice(fmt.Sprintf("Removed D-Line for %s", hostString))
return false
}
func (s *Server) loadDLines() {
s.dlines = NewDLineManager()
// load from datastore
s.store.View(func(tx *buntdb.Tx) error {
//TODO(dan): We could make this safer
tx.AscendKeys("bans.dline *", func(key, value string) bool {
// load addr/net
var hostAddr net.IP
var hostNet *net.IPNet
_, hostNet, err := net.ParseCIDR(key)
if err != nil {
hostAddr = net.ParseIP(key)
}
// load ban info
var info IPBanInfo
json.Unmarshal([]byte(value), &info)
// add to the server
if hostNet == nil {
s.dlines.AddIP(hostAddr, info.Time, info.Reason, info.OperReason)
} else {
s.dlines.AddNetwork(*hostNet, info.Time, info.Reason, info.OperReason)
}
return true // true to continue I guess?
})
return nil
})
}

View File

@ -99,7 +99,7 @@ Prints debug information about the IRCd. <option> can be one of:
}, },
"dline": { "dline": {
oper: true, oper: true,
text: `DLINE [duration] <ip>/<net> [reason [| oper reason]] text: `DLINE [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]
Bans an IP address or network from connecting to the server. If the duration is Bans an IP address or network from connecting to the server. If the duration is
given then only for that long. The reason is shown to the user themselves, but given then only for that long. The reason is shown to the user themselves, but

View File

@ -154,6 +154,7 @@ const (
RPL_HELPSTART = "704" RPL_HELPSTART = "704"
RPL_HELPTXT = "705" RPL_HELPTXT = "705"
RPL_ENDOFHELP = "706" RPL_ENDOFHELP = "706"
ERR_NOPRIVS = "723"
RPL_MONONLINE = "730" RPL_MONONLINE = "730"
RPL_MONOFFLINE = "731" RPL_MONOFFLINE = "731"
RPL_MONLIST = "732" RPL_MONLIST = "732"