Implement ban by software version xep-0092
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package jabber
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -54,6 +55,21 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
||||
}
|
||||
}
|
||||
|
||||
id, err := j.QuerySoftwareVersion(v.From)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to query user software and version: jid=%s id=%s, err=%w",
|
||||
v.JID,
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
evilJid := strings.SplitN(v.JID, "/", 2)[0]
|
||||
evilNick := ""
|
||||
evilNicks := strings.SplitN(v.From, "/", 2)
|
||||
@@ -380,4 +396,208 @@ func (j *Jabber) BunyChat(v xmpp.Chat) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// BunySoftwareVersion производит проверку по чёрному списку версий и названий клинтского ПО.
|
||||
func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) error {
|
||||
var err error
|
||||
|
||||
if v.From != "" {
|
||||
// На всякий случай: себя никогда не баним, явным образом
|
||||
if v.From == j.Talk.JID() {
|
||||
return err
|
||||
}
|
||||
|
||||
room := strings.SplitN(v.From, "/", 2)[0]
|
||||
|
||||
var (
|
||||
presenceJSONStrings []string
|
||||
)
|
||||
|
||||
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
||||
presenceJSONStrings = InterfaceToStringSlice(presenceJSONInterface)
|
||||
}
|
||||
|
||||
log.Debugf("Looking up %s in presence db", v.From)
|
||||
|
||||
for _, presenceJSONString := range presenceJSONStrings {
|
||||
var p xmpp.Presence
|
||||
_ = json.Unmarshal([]byte(presenceJSONString), &p)
|
||||
|
||||
if p.Affiliation == "admin" || p.Affiliation == "owner" {
|
||||
log.Infof("Skipping useragent blacklist check for %s because his affiliation is %s", v.From, p.Affiliation)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// На всякий случай.
|
||||
if p.Role == "moderator" {
|
||||
log.Infof("Skipping useragent blacklist check for %s because his role is %s", v.From, p.Role)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if v.From == p.From {
|
||||
log.Errorf("Found jid of %s in presence db: %s", v.From, p.JID)
|
||||
|
||||
// Lookup in global blacklist.
|
||||
for _, cRoom := range j.RoomsConnected {
|
||||
if cRoom == room {
|
||||
for _, bEntry := range j.BlackList.Blacklist {
|
||||
// Обработаем правила глобального чёрного списка
|
||||
if bEntry.RoomName == "" {
|
||||
for _, useragent := range bEntry.UserAgent {
|
||||
switch {
|
||||
case useragent.Name != "" && useragent.Version != "" && useragent.Os != "":
|
||||
if ver.Name == useragent.Name && ver.Version == useragent.Version && ver.Os == useragent.Os {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
case useragent.Name != "" && useragent.Version != "":
|
||||
if ver.Name == useragent.Name && ver.Version == useragent.Version {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
case useragent.Name != "":
|
||||
if ver.Name == useragent.Name {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
case useragent.Version != "":
|
||||
if ver.Version == useragent.Version {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bEntry.RoomName == room {
|
||||
for _, useragent := range bEntry.UserAgent {
|
||||
switch {
|
||||
case useragent.Name != "" && useragent.Version != "" && useragent.Os != "":
|
||||
if ver.Name == useragent.Name && ver.Version == useragent.Version && ver.Os == useragent.Os {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
case useragent.Name != "" && useragent.Version != "":
|
||||
if ver.Name == useragent.Name && ver.Version == useragent.Version {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
case useragent.Name != "":
|
||||
if ver.Name == useragent.Name {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
case useragent.Version != "":
|
||||
if ver.Version == useragent.Version {
|
||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"unable to ban user: id=%s, err=%w",
|
||||
id,
|
||||
err,
|
||||
)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
|
||||
@@ -356,7 +356,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
||||
log.Debugf("Got S2C pong answer from %s to %s", v.From, v.To)
|
||||
j.ServerPingTimestampRx = time.Now().Unix() //nolint:wsl
|
||||
|
||||
// Похоже на понг второй стадии xep-0410 MUC-Ping-а, который у нас не реализован
|
||||
// Похоже на понг второй стадии xep-0410 MUC-Ping-а, который у нас не реализован.
|
||||
case v.To == j.Talk.JID() && string(v.Query) == "<XMLElement></XMLElement>":
|
||||
mucNameMatch := slices.Contains(j.RoomsConnected, v.From)
|
||||
|
||||
@@ -425,6 +425,41 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
||||
log.Debug(spew.Sdump(e))
|
||||
}
|
||||
|
||||
// Ответ с результатом адресован нам.
|
||||
case v.To == j.Talk.JID():
|
||||
var softwareVersion IqResultSoftwareVersion
|
||||
if err := xml.Unmarshal(v.Query, &softwareVersion); err == nil {
|
||||
if softwareVersion.Os == "" {
|
||||
log.Infof(
|
||||
"Recieved software version query result for %s: software=%s version=%s",
|
||||
v.From,
|
||||
softwareVersion.Name,
|
||||
softwareVersion.Version,
|
||||
)
|
||||
} else {
|
||||
log.Infof(
|
||||
"Recieved software version query result for %s: software=%s version=%s os=%s",
|
||||
v.From,
|
||||
softwareVersion.Name,
|
||||
softwareVersion.Version,
|
||||
softwareVersion.Os,
|
||||
)
|
||||
}
|
||||
|
||||
if err = j.BunySoftwareVersion(v, softwareVersion); err != nil {
|
||||
log.Errorf("Unable to query client software version: %s", err)
|
||||
|
||||
j.GTomb.Kill(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Got an IQ result. Dunno how deal with it, discarding")
|
||||
log.Debug(spew.Sdump(e))
|
||||
|
||||
default:
|
||||
log.Info("Got an IQ result. Dunno how deal with it, discarding")
|
||||
log.Debug(spew.Sdump(e))
|
||||
@@ -633,7 +668,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
||||
var p xmpp.Presence
|
||||
_ = json.Unmarshal([]byte(presenceJSONString), &p)
|
||||
|
||||
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для grouchat)
|
||||
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для groupchat)
|
||||
// просто замещаем его.
|
||||
if p.From == v.From {
|
||||
continue
|
||||
|
||||
36
internal/jabber/query_version.go
Normal file
36
internal/jabber/query_version.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package jabber
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/eleksir/go-xmpp"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// QuerySoftwareVersion запрошивает версию и название клиента.
|
||||
func (j *Jabber) QuerySoftwareVersion(jid string) (string, error) {
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
)
|
||||
|
||||
if id, err = j.Talk.RawInformationQuery(
|
||||
j.Talk.JID(),
|
||||
jid,
|
||||
uuid.New().String(),
|
||||
xmpp.IQTypeGet,
|
||||
"jabber:iq:version",
|
||||
"",
|
||||
); err != nil {
|
||||
err = fmt.Errorf(
|
||||
"unable to query software version of jid=%s err=%w",
|
||||
jid,
|
||||
err,
|
||||
)
|
||||
} else {
|
||||
log.Infof("Query software version of %s", jid)
|
||||
}
|
||||
|
||||
return id, err
|
||||
}
|
||||
@@ -79,6 +79,11 @@ type MyBlackList struct {
|
||||
JidRe []string `json:"jid_re,omitempty"`
|
||||
NickRe []string `json:"nick_re,omitempty"`
|
||||
PhraseRe []string `json:"phrase_re,omitempty"`
|
||||
UserAgent []struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Os string `json:"os,omitempty"`
|
||||
} `json:"user_agent,omitempty"`
|
||||
} `json:"blacklist,omitempty"`
|
||||
}
|
||||
|
||||
@@ -197,4 +202,14 @@ type IqErrorCancelNotAcceptable struct {
|
||||
} `xml:"not-acceptable"`
|
||||
}
|
||||
|
||||
// IqResultSoftwareVersion прототип структурки для разбора IQ ответов на запрос о названии и версии клиентского ПО.
|
||||
type IqResultSoftwareVersion struct {
|
||||
XMLName xml.Name `xml:"query"`
|
||||
Text string `xml:",chardata"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
Name string `xml:"name"`
|
||||
Version string `xml:"version"`
|
||||
Os string `xml:"os,omitempty"`
|
||||
}
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
|
||||
Reference in New Issue
Block a user