add client logging + improve logging
This commit is contained in:
parent
654d0306cf
commit
3c13b83657
|
@ -22,6 +22,7 @@ func (client *Client) auth(password string) error {
|
||||||
challenge := &messages.SASLChallenge{}
|
challenge := &messages.SASLChallenge{}
|
||||||
response := &messages.SASLResponse{}
|
response := &messages.SASLResponse{}
|
||||||
for _, m := range f.Mechanisms.Mechanism {
|
for _, m := range f.Mechanisms.Mechanism {
|
||||||
|
client.Logging.Debugf("try auth with '%s'", m)
|
||||||
if m == "SCRAM-SHA-1" {
|
if m == "SCRAM-SHA-1" {
|
||||||
/*
|
/*
|
||||||
mechanism = m
|
mechanism = m
|
||||||
|
@ -33,10 +34,10 @@ func (client *Client) auth(password string) error {
|
||||||
if m == "DIGEST-MD5" {
|
if m == "DIGEST-MD5" {
|
||||||
mechanism = m
|
mechanism = m
|
||||||
// Digest-MD5 authentication
|
// Digest-MD5 authentication
|
||||||
client.Out.Encode(&messages.SASLAuth{
|
client.Send(&messages.SASLAuth{
|
||||||
Mechanism: m,
|
Mechanism: m,
|
||||||
})
|
})
|
||||||
if err := client.ReadElement(challenge); err != nil {
|
if err := client.ReadDecode(challenge); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b, err := base64.StdEncoding.DecodeString(challenge.Body)
|
b, err := base64.StdEncoding.DecodeString(challenge.Body)
|
||||||
|
@ -65,7 +66,7 @@ func (client *Client) auth(password string) error {
|
||||||
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
|
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
|
||||||
|
|
||||||
response.Body = base64.StdEncoding.EncodeToString([]byte(message))
|
response.Body = base64.StdEncoding.EncodeToString([]byte(message))
|
||||||
client.Out.Encode(response)
|
client.Send(response)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if m == "PLAIN" {
|
if m == "PLAIN" {
|
||||||
|
@ -74,7 +75,7 @@ func (client *Client) auth(password string) error {
|
||||||
raw := "\x00" + client.JID.Local + "\x00" + password
|
raw := "\x00" + client.JID.Local + "\x00" + password
|
||||||
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
|
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
|
||||||
base64.StdEncoding.Encode(enc, []byte(raw))
|
base64.StdEncoding.Encode(enc, []byte(raw))
|
||||||
client.Out.Encode(&messages.SASLAuth{
|
client.Send(&messages.SASLAuth{
|
||||||
Mechanism: "PLAIN",
|
Mechanism: "PLAIN",
|
||||||
Body: string(enc),
|
Body: string(enc),
|
||||||
})
|
})
|
||||||
|
@ -85,15 +86,17 @@ func (client *Client) auth(password string) error {
|
||||||
if mechanism == "" {
|
if mechanism == "" {
|
||||||
return fmt.Errorf("PLAIN authentication is not an option: %s", f.Mechanisms.Mechanism)
|
return fmt.Errorf("PLAIN authentication is not an option: %s", f.Mechanisms.Mechanism)
|
||||||
}
|
}
|
||||||
|
client.Logging.Info("used auth with '%s'", mechanism)
|
||||||
|
|
||||||
element, err := client.Read()
|
element, err := client.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fail := messages.SASLFailure{}
|
fail := messages.SASLFailure{}
|
||||||
if err := client.In.DecodeElement(&fail, element); err == nil {
|
if err := client.Decode(&fail, element); err == nil {
|
||||||
return errors.New(messages.XMLChildrenString(fail) + " : " + fail.Body)
|
return errors.New(messages.XMLChildrenString(fail) + " : " + fail.Body)
|
||||||
}
|
}
|
||||||
if err := client.In.DecodeElement(&messages.SASLSuccess{}, element); err != nil {
|
if err := client.Decode(&messages.SASLSuccess{}, element); err != nil {
|
||||||
return errors.New("auth failed - with unexpected answer")
|
return errors.New("auth failed - with unexpected answer")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
137
client/client.go
137
client/client.go
|
@ -3,32 +3,41 @@ package client
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/model"
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client holds XMPP connection opitons
|
// Client holds XMPP connection opitons
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
Protocol string // tcp tcp4 tcp6 are supported
|
||||||
|
Timeout time.Duration
|
||||||
conn net.Conn // connection to server
|
conn net.Conn // connection to server
|
||||||
Out *xml.Encoder
|
out *xml.Encoder
|
||||||
In *xml.Decoder
|
in *xml.Decoder
|
||||||
|
|
||||||
|
Logging *log.Logger
|
||||||
|
|
||||||
JID *model.JID
|
JID *model.JID
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(jid *model.JID, password string) (*Client, error) {
|
func NewClient(jid *model.JID, password string) (*Client, error) {
|
||||||
return NewClientProtocolDuration(jid, password, "tcp", 0)
|
client := &Client{
|
||||||
}
|
Protocol: "tcp",
|
||||||
|
JID: jid,
|
||||||
|
Logging: log.New(),
|
||||||
|
}
|
||||||
|
return client, client.Connect(password)
|
||||||
|
|
||||||
func NewClientProtocolDuration(jid *model.JID, password string, proto string, timeout time.Duration) (*Client, error) {
|
}
|
||||||
_, srvEntries, err := net.LookupSRV("xmpp-client", "tcp", jid.Domain)
|
func (client *Client) Connect(password string) error {
|
||||||
addr := jid.Domain + ":5222"
|
_, srvEntries, err := net.LookupSRV("xmpp-client", "tcp", client.JID.Domain)
|
||||||
|
addr := client.JID.Domain + ":5222"
|
||||||
if err == nil && len(srvEntries) > 0 {
|
if err == nil && len(srvEntries) > 0 {
|
||||||
bestSrv := srvEntries[0]
|
bestSrv := srvEntries[0]
|
||||||
for _, srv := range srvEntries {
|
for _, srv := range srvEntries {
|
||||||
|
@ -42,23 +51,20 @@ func NewClientProtocolDuration(jid *model.JID, password string, proto string, ti
|
||||||
if len(a) == 1 {
|
if len(a) == 1 {
|
||||||
addr += ":5222"
|
addr += ":5222"
|
||||||
}
|
}
|
||||||
conn, err := net.DialTimeout(proto, addr, timeout)
|
if client.Protocol == "" {
|
||||||
if err != nil {
|
client.Protocol = "tcp"
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
client := &Client{
|
conn, err := net.DialTimeout(client.Protocol, addr, client.Timeout)
|
||||||
conn: conn,
|
client.setConnection(conn)
|
||||||
In: xml.NewDecoder(conn),
|
if err != nil {
|
||||||
Out: xml.NewEncoder(conn),
|
return err
|
||||||
|
|
||||||
JID: jid,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = client.connect(password); err != nil {
|
if err = client.connect(password); err != nil {
|
||||||
client.Close()
|
client.Close()
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
return client, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the XMPP connection
|
// Close closes the XMPP connection
|
||||||
|
@ -68,94 +74,3 @@ func (c *Client) Close() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) startStream() (*messages.StreamFeatures, error) {
|
|
||||||
// XMPP-Connection
|
|
||||||
_, err := fmt.Fprintf(client.conn, "<?xml version='1.0'?>\n"+
|
|
||||||
"<stream:stream to='%s' xmlns='%s'\n"+
|
|
||||||
" xmlns:stream='%s' version='1.0'>\n",
|
|
||||||
model.XMLEscape(client.JID.Domain), messages.NSClient, messages.NSStream)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
element, err := client.Read()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if element.Name.Space != messages.NSStream || element.Name.Local != "stream" {
|
|
||||||
return nil, errors.New("is not stream")
|
|
||||||
}
|
|
||||||
f := &messages.StreamFeatures{}
|
|
||||||
if err := client.ReadElement(f); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) connect(password string) error {
|
|
||||||
if _, err := client.startStream(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := client.Out.Encode(&messages.TLSStartTLS{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var p messages.TLSProceed
|
|
||||||
if err := client.ReadElement(&p); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Change tcp to tls
|
|
||||||
tlsconn := tls.Client(client.conn, &tls.Config{
|
|
||||||
ServerName: client.JID.Domain,
|
|
||||||
})
|
|
||||||
client.conn = tlsconn
|
|
||||||
client.In = xml.NewDecoder(client.conn)
|
|
||||||
client.Out = xml.NewEncoder(client.conn)
|
|
||||||
|
|
||||||
if err := tlsconn.Handshake(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := client.auth(password); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := client.startStream(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// bind to resource
|
|
||||||
bind := &messages.Bind{}
|
|
||||||
if client.JID.Resource != "" {
|
|
||||||
bind.Resource = client.JID.Resource
|
|
||||||
}
|
|
||||||
if err := client.Out.Encode(&messages.IQClient{
|
|
||||||
Type: messages.IQTypeSet,
|
|
||||||
From: client.JID,
|
|
||||||
To: model.NewJID(client.JID.Domain),
|
|
||||||
Bind: bind,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var iq messages.IQClient
|
|
||||||
if err := client.ReadElement(&iq); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if iq.Error != nil {
|
|
||||||
if iq.Error.ServiceUnavailable == nil {
|
|
||||||
return errors.New(fmt.Sprintf("recv error on iq>bind: %s[%s]: %s -> %s -> %s", iq.Error.Code, iq.Error.Type, iq.Error.Text, messages.XMLChildrenString(iq.Error.StanzaErrorGroup), messages.XMLChildrenString(iq.Error.Other)))
|
|
||||||
}
|
|
||||||
} else if iq.Bind == nil {
|
|
||||||
return errors.New("<iq> result missing <bind>")
|
|
||||||
} else if iq.Bind.JID != nil {
|
|
||||||
client.JID.Local = iq.Bind.JID.Local
|
|
||||||
client.JID.Domain = iq.Bind.JID.Domain
|
|
||||||
client.JID.Resource = iq.Bind.JID.Resource
|
|
||||||
} else {
|
|
||||||
return errors.New(messages.XMLChildrenString(iq.Other))
|
|
||||||
}
|
|
||||||
// set status
|
|
||||||
return client.Send(&messages.PresenceClient{Show: messages.PresenceShowXA, Status: "online"})
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,26 +2,34 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"log"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (client *Client) Read() (*xml.StartElement, error) {
|
func (client *Client) Read() (*xml.StartElement, error) {
|
||||||
for {
|
for {
|
||||||
nextToken, err := client.In.Token()
|
nextToken, err := client.in.Token()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch nextToken.(type) {
|
switch nextToken.(type) {
|
||||||
case xml.StartElement:
|
case xml.StartElement:
|
||||||
element := nextToken.(xml.StartElement)
|
element := nextToken.(xml.StartElement)
|
||||||
|
client.Logging.Debug("recv xml: ", messages.XMLStartElementToString(&element))
|
||||||
return &element, nil
|
return &element, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (client *Client) Decode(p interface{}, element *xml.StartElement) error {
|
||||||
func (client *Client) ReadElement(p interface{}) error {
|
err := client.in.DecodeElement(p, element)
|
||||||
|
if err != nil {
|
||||||
|
client.Logging.Debugf("decode failed xml: %s to: %v", messages.XMLStartElementToString(element), p)
|
||||||
|
} else {
|
||||||
|
client.Logging.Debugf("decode xml: %s to: %v with children %s", messages.XMLStartElementToString(element), p, messages.XMLChildrenString(p))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (client *Client) ReadDecode(p interface{}) error {
|
||||||
element, err := client.Read()
|
element, err := client.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -31,36 +39,45 @@ func (client *Client) ReadElement(p interface{}) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
iq = &messages.IQClient{}
|
iq = &messages.IQClient{}
|
||||||
}
|
}
|
||||||
err = client.In.DecodeElement(iq, element)
|
err = client.Decode(iq, element)
|
||||||
if err == nil && iq.Ping != nil {
|
if err == nil && iq.Ping != nil {
|
||||||
log.Println("answer ping")
|
client.Logging.Info("ReadElement: auto answer ping")
|
||||||
iq.Type = messages.IQTypeResult
|
iq.Type = messages.IQTypeResult
|
||||||
iq.To = iq.From
|
iq.To = iq.From
|
||||||
iq.From = client.JID
|
iq.From = client.JID
|
||||||
client.Out.Encode(iq)
|
client.Send(iq)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return client.In.DecodeElement(p, element)
|
return client.Decode(p, element)
|
||||||
|
}
|
||||||
|
func (client *Client) encode(p interface{}) error {
|
||||||
|
err := client.out.Encode(p)
|
||||||
|
if err != nil {
|
||||||
|
client.Logging.Debugf("encode failed %v", p)
|
||||||
|
} else {
|
||||||
|
client.Logging.Debugf("encode %v with children %s", p, messages.XMLChildrenString(p))
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Send(p interface{}) error {
|
func (client *Client) Send(p interface{}) error {
|
||||||
msg, ok := p.(*messages.MessageClient)
|
msg, ok := p.(*messages.MessageClient)
|
||||||
if ok {
|
if ok {
|
||||||
msg.From = client.JID
|
msg.From = client.JID
|
||||||
return client.Out.Encode(msg)
|
return client.encode(msg)
|
||||||
}
|
}
|
||||||
iq, ok := p.(*messages.IQClient)
|
iq, ok := p.(*messages.IQClient)
|
||||||
if ok {
|
if ok {
|
||||||
iq.From = client.JID
|
iq.From = client.JID
|
||||||
return client.Out.Encode(iq)
|
return client.encode(iq)
|
||||||
}
|
}
|
||||||
pc, ok := p.(*messages.PresenceClient)
|
pc, ok := p.(*messages.PresenceClient)
|
||||||
if ok {
|
if ok {
|
||||||
pc.From = client.JID
|
pc.From = client.JID
|
||||||
return client.Out.Encode(pc)
|
return client.encode(pc)
|
||||||
}
|
}
|
||||||
return client.Out.Encode(p)
|
return client.encode(p)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (client *Client) setConnection(conn net.Conn) {
|
||||||
|
client.conn = conn
|
||||||
|
client.in = xml.NewDecoder(client.conn)
|
||||||
|
client.out = xml.NewEncoder(client.conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) startStream() (*messages.StreamFeatures, error) {
|
||||||
|
// XMPP-Connection
|
||||||
|
_, err := fmt.Fprintf(client.conn, "<?xml version='1.0'?>\n"+
|
||||||
|
"<stream:stream to='%s' xmlns='%s'\n"+
|
||||||
|
" xmlns:stream='%s' version='1.0'>\n",
|
||||||
|
model.XMLEscape(client.JID.Domain), messages.NSClient, messages.NSStream)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
element, err := client.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if element.Name.Space != messages.NSStream || element.Name.Local != "stream" {
|
||||||
|
return nil, errors.New("is not stream")
|
||||||
|
}
|
||||||
|
f := &messages.StreamFeatures{}
|
||||||
|
if err := client.ReadDecode(f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
debug := "stream start >"
|
||||||
|
if f.StartTLS != nil {
|
||||||
|
debug += " tls"
|
||||||
|
}
|
||||||
|
debug += " mechanism("
|
||||||
|
for _, m := range f.Mechanisms.Mechanism {
|
||||||
|
debug += m
|
||||||
|
}
|
||||||
|
debug += ")"
|
||||||
|
if f.Bind != nil {
|
||||||
|
debug += " bind"
|
||||||
|
}
|
||||||
|
client.Logging.Info(debug)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) connect(password string) error {
|
||||||
|
if _, err := client.startStream(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := client.Send(&messages.TLSStartTLS{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var p messages.TLSProceed
|
||||||
|
if err := client.ReadDecode(&p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Change tcp to tls
|
||||||
|
tlsconn := tls.Client(client.conn, &tls.Config{
|
||||||
|
ServerName: client.JID.Domain,
|
||||||
|
})
|
||||||
|
client.setConnection(tlsconn)
|
||||||
|
|
||||||
|
if err := tlsconn.Handshake(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := client.auth(password); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := client.startStream(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// bind to resource
|
||||||
|
bind := &messages.Bind{}
|
||||||
|
if client.JID.Resource != "" {
|
||||||
|
bind.Resource = client.JID.Resource
|
||||||
|
}
|
||||||
|
if err := client.Send(&messages.IQClient{
|
||||||
|
Type: messages.IQTypeSet,
|
||||||
|
To: model.NewJID(client.JID.Domain),
|
||||||
|
Bind: bind,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var iq messages.IQClient
|
||||||
|
if err := client.ReadDecode(&iq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if iq.Error != nil {
|
||||||
|
if iq.Error.ServiceUnavailable == nil {
|
||||||
|
return errors.New(fmt.Sprintf("recv error on iq>bind: %s[%s]: %s -> %s -> %s", iq.Error.Code, iq.Error.Type, iq.Error.Text, messages.XMLChildrenString(iq.Error.StanzaErrorGroup), messages.XMLChildrenString(iq.Error.Other)))
|
||||||
|
} else {
|
||||||
|
client.Logging.Warn("connected without setting resource with bind after auth: service-unavailable")
|
||||||
|
}
|
||||||
|
} else if iq.Bind == nil {
|
||||||
|
return errors.New("<iq> result missing <bind>")
|
||||||
|
} else if iq.Bind.JID != nil {
|
||||||
|
client.JID.Local = iq.Bind.JID.Local
|
||||||
|
client.JID.Domain = iq.Bind.JID.Domain
|
||||||
|
client.JID.Resource = iq.Bind.JID.Resource
|
||||||
|
client.Logging.Info("set resource by server bind")
|
||||||
|
} else {
|
||||||
|
return errors.New(messages.XMLChildrenString(iq.Other))
|
||||||
|
}
|
||||||
|
// set status
|
||||||
|
return client.Send(&messages.PresenceClient{Show: messages.PresenceShowXA, Status: "online"})
|
||||||
|
}
|
|
@ -38,8 +38,18 @@ var TesterCMD = &cobra.Command{
|
||||||
if err := file.ReadJSON(configTester.AccountsPath, testerInstance); err != nil {
|
if err := file.ReadJSON(configTester.AccountsPath, testerInstance); err != nil {
|
||||||
log.Warn("unable to load state file:", err)
|
log.Warn("unable to load state file:", err)
|
||||||
}
|
}
|
||||||
|
testerInstance.Admins = configTester.Admins
|
||||||
|
testerInstance.LoggingBots = configTester.LoggingBots
|
||||||
|
clientLogger := log.New()
|
||||||
|
clientLogger.SetLevel(configTester.LoggingClients)
|
||||||
|
testerInstance.LoggingClients = clientLogger
|
||||||
|
|
||||||
mainClient, err := client.NewClientProtocolDuration(configTester.Client.JID, configTester.Client.Password, "tcp", configTester.Timeout.Duration)
|
mainClient := &client.Client{
|
||||||
|
JID: configTester.Client.JID,
|
||||||
|
Timeout: configTester.Timeout.Duration,
|
||||||
|
Logging: clientLogger,
|
||||||
|
}
|
||||||
|
err := mainClient.Connect(configTester.Client.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("unable to connect with main jabber client: ", err)
|
log.Fatal("unable to connect with main jabber client: ", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *Tester) StartBot(status *Status) {
|
func (t *Tester) StartBot(status *Status) {
|
||||||
|
logger := log.New()
|
||||||
|
logger.SetLevel(t.LoggingBots)
|
||||||
|
logCTX := logger.WithFields(log.Fields{
|
||||||
|
"type": "bot",
|
||||||
|
"jid": status.client.JID.Full(),
|
||||||
|
})
|
||||||
for {
|
for {
|
||||||
logCTX := log.WithField("jid", status.client.JID.Full())
|
|
||||||
|
|
||||||
element, err := status.client.Read()
|
element, err := status.client.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -21,7 +26,7 @@ func (t *Tester) StartBot(status *Status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
errMSG := &messages.StreamError{}
|
errMSG := &messages.StreamError{}
|
||||||
err = status.client.In.DecodeElement(errMSG, element)
|
err = status.client.Decode(errMSG, element)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logCTX.Errorf("recv stream error: %s: %s", errMSG.Text, messages.XMLChildrenString(errMSG.Any))
|
logCTX.Errorf("recv stream error: %s: %s", errMSG.Text, messages.XMLChildrenString(errMSG.Any))
|
||||||
status.client.Close()
|
status.client.Close()
|
||||||
|
@ -30,14 +35,14 @@ func (t *Tester) StartBot(status *Status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
iq := &messages.IQClient{}
|
iq := &messages.IQClient{}
|
||||||
err = status.client.In.DecodeElement(iq, element)
|
err = status.client.Decode(iq, element)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if iq.Ping != nil {
|
if iq.Ping != nil {
|
||||||
logCTX.Debug("answer ping")
|
logCTX.Debug("answer ping")
|
||||||
iq.Type = messages.IQTypeResult
|
iq.Type = messages.IQTypeResult
|
||||||
iq.To = iq.From
|
iq.To = iq.From
|
||||||
iq.From = status.client.JID
|
iq.From = status.client.JID
|
||||||
status.client.Out.Encode(iq)
|
status.client.Send(iq)
|
||||||
} else {
|
} else {
|
||||||
logCTX.Warnf("recv iq unsupport: %s", messages.XMLChildrenString(iq))
|
logCTX.Warnf("recv iq unsupport: %s", messages.XMLChildrenString(iq))
|
||||||
}
|
}
|
||||||
|
@ -45,7 +50,7 @@ func (t *Tester) StartBot(status *Status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pres := &messages.PresenceClient{}
|
pres := &messages.PresenceClient{}
|
||||||
err = status.client.In.DecodeElement(pres, element)
|
err = status.client.Decode(pres, element)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sender := pres.From
|
sender := pres.From
|
||||||
logPres := logCTX.WithField("from", sender.Full())
|
logPres := logCTX.WithField("from", sender.Full())
|
||||||
|
@ -54,12 +59,12 @@ func (t *Tester) StartBot(status *Status) {
|
||||||
pres.Type = messages.PresenceTypeSubscribed
|
pres.Type = messages.PresenceTypeSubscribed
|
||||||
pres.To = sender
|
pres.To = sender
|
||||||
pres.From = nil
|
pres.From = nil
|
||||||
status.client.Out.Encode(pres)
|
status.client.Send(pres)
|
||||||
logPres.Debugf("accept new subscribe")
|
logPres.Debugf("accept new subscribe")
|
||||||
|
|
||||||
pres.Type = messages.PresenceTypeSubscribe
|
pres.Type = messages.PresenceTypeSubscribe
|
||||||
pres.ID = ""
|
pres.ID = ""
|
||||||
status.client.Out.Encode(pres)
|
status.client.Send(pres)
|
||||||
logPres.Info("request also subscribe")
|
logPres.Info("request also subscribe")
|
||||||
} else if pres.Type == messages.PresenceTypeSubscribed {
|
} else if pres.Type == messages.PresenceTypeSubscribed {
|
||||||
logPres.Info("recv presence accepted subscribe")
|
logPres.Info("recv presence accepted subscribe")
|
||||||
|
@ -76,7 +81,7 @@ func (t *Tester) StartBot(status *Status) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := &messages.MessageClient{}
|
msg := &messages.MessageClient{}
|
||||||
err = status.client.In.DecodeElement(msg, element)
|
err = status.client.Decode(msg, element)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logCTX.Warnf("unsupport xml recv: %s <-> %v", err, element)
|
logCTX.Warnf("unsupport xml recv: %s <-> %v", err, element)
|
||||||
continue
|
continue
|
||||||
|
@ -100,6 +105,30 @@ func (t *Tester) StartBot(status *Status) {
|
||||||
case "ping":
|
case "ping":
|
||||||
status.client.Send(messages.MessageClient{Type: msg.Type, To: msg.From, Body: "pong"})
|
status.client.Send(messages.MessageClient{Type: msg.Type, To: msg.From, Body: "pong"})
|
||||||
|
|
||||||
|
case "disconnect":
|
||||||
|
first := true
|
||||||
|
allAdmins := ""
|
||||||
|
isAdmin := false
|
||||||
|
for _, jid := range t.Admins {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
allAdmins += ", "
|
||||||
|
}
|
||||||
|
allAdmins += jid.Bare()
|
||||||
|
if jid.Bare() == msg.From.Bare() {
|
||||||
|
isAdmin = true
|
||||||
|
status.client.Send(messages.MessageClient{Type: msg.Type, To: jid, Body: "last message, disconnect requested by " + msg.From.Bare()})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isAdmin {
|
||||||
|
status.Login = false
|
||||||
|
status.client.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.client.Send(messages.MessageClient{Type: msg.Type, To: msg.From, Body: "not allowed, ask " + allAdmins})
|
||||||
|
|
||||||
case "checkmsg":
|
case "checkmsg":
|
||||||
if len(msgText) == 2 {
|
if len(msgText) == 2 {
|
||||||
t.UpdateConnectionStatus(msg.From, status.client.JID, msgText[1])
|
t.UpdateConnectionStatus(msg.From, status.client.JID, msgText[1])
|
||||||
|
|
|
@ -11,6 +11,8 @@ type Config struct {
|
||||||
AccountsPath string `toml:"accounts_path"`
|
AccountsPath string `toml:"accounts_path"`
|
||||||
OutputPath string `toml:"output_path"`
|
OutputPath string `toml:"output_path"`
|
||||||
Logging log.Level `toml:"logging"`
|
Logging log.Level `toml:"logging"`
|
||||||
|
LoggingClients log.Level `toml:"logging_clients"`
|
||||||
|
LoggingBots log.Level `toml:"logging_bots"`
|
||||||
Timeout duration.Duration `toml:"timeout"`
|
Timeout duration.Duration `toml:"timeout"`
|
||||||
Interval duration.Duration `toml:"interval"`
|
Interval duration.Duration `toml:"interval"`
|
||||||
Admins []*model.JID `toml:"admins"`
|
Admins []*model.JID `toml:"admins"`
|
||||||
|
|
|
@ -38,16 +38,26 @@ func (s *Status) Update(timeout time.Duration) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bareJID := model.NewJID(s.account.JID.Bare())
|
c := &client.Client{
|
||||||
if client, err := client.NewClientProtocolDuration(bareJID, s.account.Password, "tcp4", timeout/2); err == nil {
|
JID: model.NewJID(s.account.JID.Bare()),
|
||||||
|
Protocol: "tcp4",
|
||||||
|
Logging: s.client.Logging,
|
||||||
|
Timeout: timeout / 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Connect(s.account.Password); err == nil {
|
||||||
s.IPv4 = true
|
s.IPv4 = true
|
||||||
client.Close()
|
c.Close()
|
||||||
} else {
|
} else {
|
||||||
s.IPv4 = false
|
s.IPv4 = false
|
||||||
}
|
}
|
||||||
if client, err := client.NewClientProtocolDuration(bareJID, s.account.Password, "tcp6", timeout/2); err == nil {
|
|
||||||
|
c.JID = model.NewJID(s.account.JID.Bare())
|
||||||
|
c.Protocol = "tcp6"
|
||||||
|
|
||||||
|
if err := c.Connect(s.account.Password); err == nil {
|
||||||
s.IPv6 = true
|
s.IPv6 = true
|
||||||
client.Close()
|
c.Close()
|
||||||
} else {
|
} else {
|
||||||
s.IPv6 = false
|
s.IPv6 = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ type Tester struct {
|
||||||
Accounts map[string]*Account `json:"accounts"`
|
Accounts map[string]*Account `json:"accounts"`
|
||||||
Status map[string]*Status `json:"-"`
|
Status map[string]*Status `json:"-"`
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
|
LoggingClients *log.Logger `json:"-"`
|
||||||
|
LoggingBots log.Level `json:"-"`
|
||||||
|
Admins []*model.JID `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTester() *Tester {
|
func NewTester() *Tester {
|
||||||
|
@ -68,7 +71,12 @@ func (t *Tester) Connect(acc *Account) {
|
||||||
logCTX.Warn("is already loggedin")
|
logCTX.Warn("is already loggedin")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c, err := client.NewClientProtocolDuration(acc.JID, acc.Password, "tcp", t.Timeout)
|
c := &client.Client{
|
||||||
|
Timeout: t.Timeout,
|
||||||
|
JID: acc.JID,
|
||||||
|
Logging: t.LoggingClients,
|
||||||
|
}
|
||||||
|
err := c.Connect(acc.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logCTX.Warnf("could not connect client: %s", err)
|
logCTX.Warnf("could not connect client: %s", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -143,7 +151,7 @@ func (t *Tester) CheckStatus() {
|
||||||
logCTXTo = logCTXTo.WithField("msg-old", msg)
|
logCTXTo = logCTXTo.WithField("msg-old", msg)
|
||||||
own.Connections[jid] = false
|
own.Connections[jid] = false
|
||||||
if ok, exists := own.Connections[jid]; !exists || ok {
|
if ok, exists := own.Connections[jid]; !exists || ok {
|
||||||
logCTXTo.Warn("could not recv msg")
|
logCTXTo.Info("could not recv msg")
|
||||||
} else {
|
} else {
|
||||||
logCTXTo.Debug("could not recv msg")
|
logCTXTo.Debug("could not recv msg")
|
||||||
}
|
}
|
||||||
|
@ -157,7 +165,7 @@ func (t *Tester) CheckStatus() {
|
||||||
To: s.JID,
|
To: s.JID,
|
||||||
})
|
})
|
||||||
own.MessageForConnection[s.JID.Bare()] = msg
|
own.MessageForConnection[s.JID.Bare()] = msg
|
||||||
logCTXTo.Info("test send")
|
logCTXTo.Debug("test send")
|
||||||
send++
|
send++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ type StreamFeatures struct {
|
||||||
XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
|
XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
|
||||||
StartTLS *TLSStartTLS
|
StartTLS *TLSStartTLS
|
||||||
Mechanisms SASLMechanisms
|
Mechanisms SASLMechanisms
|
||||||
Bind Bind
|
Bind *Bind
|
||||||
Session bool
|
Session bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,18 @@ type XMLElement struct {
|
||||||
InnerXML string `xml:",innerxml"`
|
InnerXML string `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func XMLStartElementToString(element *xml.StartElement) string {
|
||||||
|
if element == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
debug := fmt.Sprintf("<%s xmlns=\"%s\"", element.Name.Local, element.Name.Space)
|
||||||
|
for _, attr := range element.Attr {
|
||||||
|
debug = fmt.Sprintf("%s %s=\"%s\"", debug, attr.Name, attr.Value)
|
||||||
|
}
|
||||||
|
debug += ">"
|
||||||
|
return debug
|
||||||
|
}
|
||||||
|
|
||||||
func XMLChildrenString(o interface{}) (result string) {
|
func XMLChildrenString(o interface{}) (result string) {
|
||||||
first := true
|
first := true
|
||||||
val := reflect.ValueOf(o)
|
val := reflect.ValueOf(o)
|
||||||
|
|
Reference in New Issue