[WIP] Fix ban by software version, some other under-hood changes

- Use updated version of upstream go-xmpp + fixes on client-side
- Introduce golangci-lint v2 + fixes
- Move especially noisy debugging stuff to separate config handles to be able to switch them off
- Add a bit debug to ban by software path
This commit is contained in:
eleksir
2025-11-30 14:19:01 +03:00
parent a128b473ff
commit 3d85dbcb58
55 changed files with 4165 additions and 13190 deletions

View File

@@ -5,14 +5,13 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"runtime"
"slices"
"sort"
"strings"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/eleksir/go-xmpp"
"github.com/xmppo/go-xmpp"
)
// ParseEvent парсит ивенты, прилетающие из модуля xmpp (изменения presence, фразы участников, ошибки).
@@ -83,7 +82,9 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
case "error":
// Это ошибка со стороны сервера, видимо, в ответ на наши действия и поскольку такие вещи происходят
// асинхронно, связать их с конкретными нашими действиями довольно сложно.
log.Warn(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Error(str)
}
}
}
@@ -113,66 +114,6 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
switch iqStruct.Xmlns { //nolint:wsl,whitespace
// Запрос номера версии приложения.
case "jabber:iq:version":
log.Infof("Got IQ get request for version from %s", v.From)
if id, err := j.Talk.IqVersionResponse(
v,
j.C.ExeName,
j.C.Version,
(strings.SplitN(runtime.GOOS, "/", 2))[0],
); err != nil {
err := fmt.Errorf(
"unable to send version info to jabber server: id=%s, err=%w",
id,
err,
)
j.GTomb.Kill(err)
return
}
parseError = false
// Нам прислали попингуй.
case "urn:xmpp:ping":
log.Infof("Got IQ get request for pong from %s", v.From)
if id, err := j.Talk.PingResponse(v); err != nil {
err := fmt.Errorf(
"unable to send pong to jabber server: id=%s, err=%w",
id,
err,
)
j.GTomb.Kill(err)
return
}
parseError = false
// У нас запросили время последней активности. В нашем случае это либо время запуска клиента, либо время
// последней фразы в чяти. Xep-0012.
case "jabber:iq:last":
log.Infof("Got IQ get last activity time request from %s", v.From)
if id, err := j.Talk.JabberIqLastResponse(v, j.LastActivity); err != nil {
err := fmt.Errorf(
"unable to send last activity time to jabber server: id=%s, err=%w",
id,
err,
)
j.GTomb.Kill(err)
return
}
parseError = false
// Запрос на список поддерживаемых фич - версия, локальное время, итд.
case xmpp.XMPPNS_DISCO_INFO:
log.Infof("Got IQ get disco#info request from %s", v.From)
@@ -233,7 +174,10 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
// Запрашивают что-то, о чём мы не имеем представления.
default:
log.Info("Got an unknown IQ get request, discarding")
log.Info(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
return
}
@@ -266,7 +210,10 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
default:
log.Info("Got bogus IQ get time request, discarding")
log.Info(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
return
}
@@ -303,7 +250,10 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
default:
log.Info("Got unknown IQ get pubsub something request, discarding")
log.Info(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
parseError = true //nolint:wsl
}
@@ -312,35 +262,13 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
}
}
// Попробуем распарсить входящий запрос как urn:xmpp:ping он приходит при MUC Self-Ping (Schrödinger's Chat).
// Предполагается, что такие респонсы должны приходить только для пинга без серверной оптимизации.
// Хотя идейно мы не поддерживаем работу без серверной оптимизации, но на пинг ответим, нам несложно.
if parseError {
var iqStruct IqPing
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
log.Debugf("Got IQ get request (actually, response) for MUC Self-Ping from %s", v.From)
if id, err := j.Talk.PingResponse(v); err != nil {
err := fmt.Errorf(
"unable to send pong to jabber server: id=%s, err=%w",
id,
err,
)
j.GTomb.Kill(err)
return
}
}
parseError = false
}
// Не знаю, как парсить... залоггируем это дело и пойдём дальше.
if parseError {
log.Infof("Does not look like parsable via iqStruct")
log.Info(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
case xmpp.IQTypeResult:
@@ -381,7 +309,10 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
} else {
// Поскольку никакой логики у нас на этот счёт не предусмотрено, то просто пропускаем ответ.
log.Infof("Got unknown pong from %s to %s", v.From, v.To)
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
}
@@ -415,15 +346,24 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
log.Infof("Got ban successful from %s to %s", v.From, v.To)
} else {
log.Infof("Got ban successful from %s to %s, from room we are not in", v.From, v.To)
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
} else {
log.Infof("Got strange ban successful message from %s to %s", v.From, v.To)
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
} else {
log.Infof("Got strange ban successful message from %s to %s", v.From, v.To)
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
// Ответ с результатом адресован нам.
@@ -459,11 +399,17 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
}
log.Info("Got an IQ result. Dunno how deal with it, discarding")
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
default:
log.Info("Got an IQ result. Dunno how deal with it, discarding")
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
// Этот бот не управляется со стороны сервера, поэтому все попытки порулить игнорируем.
@@ -492,8 +438,9 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
return
}
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
// Нам прилетело сообщение об ошибке.
case xmpp.IQTypeError:
// Если сервер не хочет пинговаться и отвечает ошибкой на пинг, то наверно он не умеет в пинги,
@@ -517,7 +464,10 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
}
} else {
log.Error("Iq parsed as ping, but does not belong to xmlns urn:xmpp:ping")
log.Error(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Error(str)
}
}
parseError = false
@@ -556,14 +506,68 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
"looks like message parsed as jabberIqErrorCancelNotAcceptable but we're not in given room",
)
log.Error(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
} else {
log.Error(
"Looks like message parsed as jabberIqErrorCancelNotAcceptable but addressed not to us",
)
log.Error(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
parseError = false
}
}
if parseError {
var IqErrorCancelServiceNotAvailable IqErrorCancelServiceNotAvailable
if err := xml.Unmarshal(v.Query, &IqErrorCancelServiceNotAvailable); err == nil {
if v.To == j.Talk.JID() {
log.Infof("Jid %s disabled response to iq.version(?) request", v.From)
} else {
log.Error(
"Looks like message parsed as jabberIqErrorCancelServiceNotAvailable but addressed not to us",
)
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
parseError = false
}
}
/*
<iq from='slackware-current@conference.jabber.ru/Batman46(new)' to='buny@jabber.ru/Лунная призма'
type='error' id='94e55122-6c15-4e88-a0df-ae9f02e0b550'>
<query xmlns='jabber:iq:version'/>
<error code='404' type='cancel'>
<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Адресата нет в конференции</text>
</error>
</iq>
*/
if parseError {
var IqErrorCancelItemNotFound IqErrorCancelItemNotFound
if err := xml.Unmarshal(v.Query, &IqErrorCancelItemNotFound); err == nil {
if v.To == j.Talk.JID() {
log.Infof("Jid %s disabled response to iq.version(?) request, hello batman46(new)", v.From)
} else {
log.Error(
"Looks like message parsed as jabberIqErrorCancelServiceNotAvailable but addressed not to us",
)
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
parseError = false
@@ -572,12 +576,19 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
if parseError {
log.Error("Unhandled IQ Error message")
log.Error(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
// Нам прилетело что-то неизвестное из семейства IQ stanza.
// Нам прилетело что-то неизвестное из семейства IQ stanza.
default:
log.Info("Got an unknown IQ request. Dunno how deal with it, discarding")
log.Info(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
// Смена статуса участника.
@@ -614,7 +625,10 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
} else {
log.Errorf("Presence notification, Type: %s, From: %s, Show: %s, Status: %s",
v.Type, v.From, v.Show, v.Status)
log.Error(spew.Sdump(v))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Error(str)
}
}
} else {
log.Infof(
@@ -746,13 +760,18 @@ func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
default:
log.Debug("Got unknown reply to disco#info")
log.Debug(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Debug(str)
}
}
}
// Это что-то неизвестное, подампим событие в лог.
default:
log.Info(spew.Sdump(e))
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
log.Info(str)
}
}
}