From 654d0306cf1fe5dbd748867ec51f2a724c2d3faa Mon Sep 17 00:00:00 2001 From: Martin/Geno Date: Sun, 11 Feb 2018 19:35:32 +0100 Subject: [PATCH] improve client --- client/auth.go | 60 +++++++++++++++++++--------------- client/client.go | 38 ++++++++-------------- client/comm.go | 10 ------ daemon/tester/bot.go | 27 ++++++++++------ daemon/tester/tester.go | 5 ++- messages/message.go | 35 ++++++++++++++++++++ messages/presence.go | 53 +++++------------------------- messages/sasl.go | 54 ++++++++++++++++++++++++++++--- messages/utils.go | 66 ++++++++++++++++++++++++++++++++++++++ server/extension/iq.go | 2 +- server/state/connect.go | 2 +- server/toclient/connect.go | 6 ++-- server/toserver/connect.go | 2 +- server/utils/main.go | 18 ----------- 14 files changed, 232 insertions(+), 146 deletions(-) create mode 100644 messages/message.go create mode 100644 messages/utils.go diff --git a/client/auth.go b/client/auth.go index f2bc9fb..3c38474 100644 --- a/client/auth.go +++ b/client/auth.go @@ -19,25 +19,27 @@ func (client *Client) auth(password string) error { } //auth: mechanism := "" + challenge := &messages.SASLChallenge{} + response := &messages.SASLResponse{} for _, m := range f.Mechanisms.Mechanism { - if m == "PLAIN" { - mechanism = m - // Plain authentication: send base64-encoded \x00 user \x00 password. - raw := "\x00" + client.JID.Local + "\x00" + password - enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw))) - base64.StdEncoding.Encode(enc, []byte(raw)) - fmt.Fprintf(client.conn, "%s\n", messages.NSSASL, enc) - break + if m == "SCRAM-SHA-1" { + /* + mechanism = m + TODO + break + */ } + if m == "DIGEST-MD5" { mechanism = m // Digest-MD5 authentication - fmt.Fprintf(client.conn, "\n", messages.NSSASL) - var ch string - if err := client.ReadElement(&ch); err != nil { + client.Out.Encode(&messages.SASLAuth{ + Mechanism: m, + }) + if err := client.ReadElement(challenge); err != nil { return err } - b, err := base64.StdEncoding.DecodeString(string(ch)) + b, err := base64.StdEncoding.DecodeString(challenge.Body) if err != nil { return err } @@ -62,29 +64,37 @@ func (client *Client) auth(password string) error { message := "username=\"" + client.JID.Local + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr + "\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset - fmt.Fprintf(client.conn, "%s\n", messages.NSSASL, base64.StdEncoding.EncodeToString([]byte(message))) + response.Body = base64.StdEncoding.EncodeToString([]byte(message)) + client.Out.Encode(response) + break + } + if m == "PLAIN" { + mechanism = m + // Plain authentication: send base64-encoded \x00 user \x00 password. + raw := "\x00" + client.JID.Local + "\x00" + password + enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw))) + base64.StdEncoding.Encode(enc, []byte(raw)) + client.Out.Encode(&messages.SASLAuth{ + Mechanism: "PLAIN", + Body: string(enc), + }) - err = client.ReadElement(&ch) - if err != nil { - return err - } - _, err = base64.StdEncoding.DecodeString(ch) - if err != nil { - return err - } - fmt.Fprintf(client.conn, "\n", messages.NSSASL) break } } if mechanism == "" { - return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism) + return fmt.Errorf("PLAIN authentication is not an option: %s", f.Mechanisms.Mechanism) } element, err := client.Read() if err != nil { return err } - if element.Name.Local != "success" { - return errors.New("auth failed: " + element.Name.Local) + fail := messages.SASLFailure{} + if err := client.In.DecodeElement(&fail, element); err == nil { + return errors.New(messages.XMLChildrenString(fail) + " : " + fail.Body) + } + if err := client.In.DecodeElement(&messages.SASLSuccess{}, element); err != nil { + return errors.New("auth failed - with unexpected answer") } return nil } diff --git a/client/client.go b/client/client.go index 95895b7..b731595 100644 --- a/client/client.go +++ b/client/client.go @@ -11,7 +11,6 @@ import ( "dev.sum7.eu/genofire/yaja/messages" "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" ) // Client holds XMPP connection opitons @@ -24,7 +23,7 @@ type Client struct { } func NewClient(jid *model.JID, password string) (*Client, error) { - return NewClientProtocolDuration(jid, password, "tcp", -1) + return NewClientProtocolDuration(jid, password, "tcp", 0) } func NewClientProtocolDuration(jid *model.JID, password string, proto string, timeout time.Duration) (*Client, error) { @@ -43,12 +42,7 @@ func NewClientProtocolDuration(jid *model.JID, password string, proto string, ti if len(a) == 1 { addr += ":5222" } - var conn net.Conn - if timeout >= 0 { - conn, err = net.DialTimeout(proto, addr, timeout) - } else { - conn, err = net.Dial(proto, addr) - } + conn, err := net.DialTimeout(proto, addr, timeout) if err != nil { return nil, err } @@ -124,12 +118,11 @@ func (client *Client) connect(password string) error { if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil { return err } - err := client.auth(password) - if err != nil { + if err := client.auth(password); err != nil { return err } - _, err = client.startStream() - if err != nil { + + if _, err := client.startStream(); err != nil { return err } // bind to resource @@ -137,23 +130,22 @@ func (client *Client) connect(password string) error { if client.JID.Resource != "" { bind.Resource = client.JID.Resource } - client.Out.Encode(&messages.IQClient{ + if err := client.Out.Encode(&messages.IQClient{ Type: messages.IQTypeSet, - To: model.NewJID(client.JID.Domain), From: client.JID, - ID: utils.CreateCookieString(), + 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.Type == messages.ErrorClientTypeCancel && iq.Error.ServiceUnavailable != nil { - //TODO binding service unavailable - } else { - return errors.New(fmt.Sprintf("recv error on iq>bind: %s[%s]: %s -> %v", iq.Error.Code, iq.Error.Type, iq.Error.Text, iq.Error.Other)) + 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(" result missing ") @@ -162,10 +154,8 @@ func (client *Client) connect(password string) error { client.JID.Domain = iq.Bind.JID.Domain client.JID.Resource = iq.Bind.JID.Resource } else { - return errors.New(fmt.Sprintf("%v", iq.Other)) + return errors.New(messages.XMLChildrenString(iq.Other)) } // set status - err = client.Send(&messages.PresenceClient{Show: messages.ShowTypeXA, Status: "online"}) - - return err + return client.Send(&messages.PresenceClient{Show: messages.PresenceShowXA, Status: "online"}) } diff --git a/client/comm.go b/client/comm.go index 0f3c80f..63b6154 100644 --- a/client/comm.go +++ b/client/comm.go @@ -5,7 +5,6 @@ import ( "log" "dev.sum7.eu/genofire/yaja/messages" - "dev.sum7.eu/genofire/yaja/server/utils" ) func (client *Client) Read() (*xml.StartElement, error) { @@ -51,25 +50,16 @@ func (client *Client) Send(p interface{}) error { msg, ok := p.(*messages.MessageClient) if ok { msg.From = client.JID - if msg.ID == "" { - msg.ID = utils.CreateCookieString() - } return client.Out.Encode(msg) } iq, ok := p.(*messages.IQClient) if ok { iq.From = client.JID - if iq.ID == "" { - iq.ID = utils.CreateCookieString() - } return client.Out.Encode(iq) } pc, ok := p.(*messages.PresenceClient) if ok { pc.From = client.JID - if pc.ID == "" { - pc.ID = utils.CreateCookieString() - } return client.Out.Encode(pc) } return client.Out.Encode(p) diff --git a/daemon/tester/bot.go b/daemon/tester/bot.go index b13c39c..aa1aa53 100644 --- a/daemon/tester/bot.go +++ b/daemon/tester/bot.go @@ -6,7 +6,6 @@ import ( log "github.com/sirupsen/logrus" "dev.sum7.eu/genofire/yaja/messages" - "dev.sum7.eu/genofire/yaja/server/utils" ) func (t *Tester) StartBot(status *Status) { @@ -24,7 +23,7 @@ func (t *Tester) StartBot(status *Status) { errMSG := &messages.StreamError{} err = status.client.In.DecodeElement(errMSG, element) if err == nil { - logCTX.Errorf("recv stream error: %s: %v", errMSG.Text, errMSG.Any) + logCTX.Errorf("recv stream error: %s: %s", errMSG.Text, messages.XMLChildrenString(errMSG.Any)) status.client.Close() status.Login = false return @@ -40,7 +39,7 @@ func (t *Tester) StartBot(status *Status) { iq.From = status.client.JID status.client.Out.Encode(iq) } else { - logCTX.Warnf("unsupport iq recv: %v", iq) + logCTX.Warnf("recv iq unsupport: %s", messages.XMLChildrenString(iq)) } continue } @@ -51,7 +50,7 @@ func (t *Tester) StartBot(status *Status) { sender := pres.From logPres := logCTX.WithField("from", sender.Full()) if pres.Type == messages.PresenceTypeSubscribe { - logPres.Debugf("recv subscribe") + logPres.Debugf("recv presence subscribe") pres.Type = messages.PresenceTypeSubscribed pres.To = sender pres.From = nil @@ -59,17 +58,19 @@ func (t *Tester) StartBot(status *Status) { logPres.Debugf("accept new subscribe") pres.Type = messages.PresenceTypeSubscribe - pres.ID = utils.CreateCookieString() + pres.ID = "" status.client.Out.Encode(pres) logPres.Info("request also subscribe") } else if pres.Type == messages.PresenceTypeSubscribed { - logPres.Info("recv accepted subscribe") + logPres.Info("recv presence accepted subscribe") } else if pres.Type == messages.PresenceTypeUnsubscribe { - logPres.Info("recv remove subscribe") + logPres.Info("recv presence remove subscribe") } else if pres.Type == messages.PresenceTypeUnsubscribed { - logPres.Info("recv removed subscribe") + logPres.Info("recv presence removed subscribe") + } else if pres.Type == messages.PresenceTypeUnavailable { + logPres.Debug("recv presence unavailable") } else { - logCTX.Warnf("unsupported presence recv: %v", pres) + logCTX.Warnf("recv presence unsupported: %s -> %s", pres.Type, messages.XMLChildrenString(pres)) } continue } @@ -82,7 +83,13 @@ func (t *Tester) StartBot(status *Status) { } logCTX = logCTX.WithField("from", msg.From.Full()).WithField("msg-recv", msg.Body) if msg.Error != nil { - logCTX.Debugf("recv msg with error %s[%s]: %s -> %v -> %v", msg.Error.Code, msg.Error.Type, msg.Error.Text, msg.Error.StanzaErrorGroup, msg.Error.Other) + if msg.Error.Type == "auth" { + logCTX.Warnf("recv msg with error not auth") + status.Login = false + status.client.Close() + return + } + logCTX.Debugf("recv msg with error %s[%s]: %s -> %s -> %s", msg.Error.Code, msg.Error.Type, msg.Error.Text, messages.XMLChildrenString(msg.Error.StanzaErrorGroup), messages.XMLChildrenString(msg.Error.Other)) continue } diff --git a/daemon/tester/tester.go b/daemon/tester/tester.go index ca59f1d..cec0c4b 100644 --- a/daemon/tester/tester.go +++ b/daemon/tester/tester.go @@ -9,7 +9,6 @@ import ( "dev.sum7.eu/genofire/yaja/client" "dev.sum7.eu/genofire/yaja/messages" "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" ) type Tester struct { @@ -149,12 +148,12 @@ func (t *Tester) CheckStatus() { logCTXTo.Debug("could not recv msg") } } - msg = utils.CreateCookieString() + msg = messages.CreateCookieString() logCTXTo = logCTXTo.WithField("msg-send", msg) own.client.Send(&messages.MessageClient{ Body: "checkmsg " + msg, - Type: messages.ChatTypeChat, + Type: messages.MessageTypeChat, To: s.JID, }) own.MessageForConnection[s.JID.Bare()] = msg diff --git a/messages/message.go b/messages/message.go new file mode 100644 index 0000000..2fc27ac --- /dev/null +++ b/messages/message.go @@ -0,0 +1,35 @@ +package messages + +import ( + "encoding/xml" + + "dev.sum7.eu/genofire/yaja/model" +) + +type MessageType string + +const ( + MessageTypeChat MessageType = "chat" + MessageTypeGroupchat MessageType = "groupchat" + MessageTypeError MessageType = "error" + MessageTypeHeadline MessageType = "headline" + MessageTypeNormal MessageType = "normal" +) + +// MessageClient element +type MessageClient struct { + XMLName xml.Name `xml:"jabber:client message"` + From *model.JID `xml:"from,attr,omitempty"` + ID string `xml:"id,attr,omitempty"` + To *model.JID `xml:"to,attr,omitempty"` + Type MessageType `xml:"type,attr,omitempty"` + Lang string `xml:"lang,attr,omitempty"` + Subject string `xml:"subject"` + Body string `xml:"body"` + Thread string `xml:"thread"` + // Any hasn't matched element + Other []XMLElement `xml:",any"` + + Delay *Delay `xml:"delay"` + Error *ErrorClient +} diff --git a/messages/presence.go b/messages/presence.go index c443efb..f9e324a 100644 --- a/messages/presence.go +++ b/messages/presence.go @@ -6,15 +6,6 @@ import ( "dev.sum7.eu/genofire/yaja/model" ) -type XMLElement struct { - XMLName xml.Name - InnerXML string `xml:",innerxml"` -} - -type Delay struct { - Stamp string `xml:"stamp,attr"` -} - type PresenceType string const ( @@ -27,13 +18,13 @@ const ( PresenceTypeError PresenceType = "error" ) -type ShowType string +type PresenceShow string const ( - ShowTypeAway ShowType = "away" - ShowTypeChat ShowType = "chat" - ShowTypeDND ShowType = "dnd" - ShowTypeXA ShowType = "xa" + PresenceShowAway PresenceShow = "away" + PresenceShowChat PresenceShow = "chat" + PresenceShowDND PresenceShow = "dnd" + PresenceShowXA PresenceShow = "xa" ) // PresenceClient element @@ -45,39 +36,11 @@ type PresenceClient struct { Type PresenceType `xml:"type,attr,omitempty"` Lang string `xml:"lang,attr,omitempty"` - Show ShowType `xml:"show,omitempty"` // away, chat, dnd, xa - Status string `xml:"status,omitempty"` // sb []clientText - Priority string `xml:"priority,omitempty"` + Show PresenceShow `xml:"show,omitempty"` // away, chat, dnd, xa + Status string `xml:"status,omitempty"` // sb []clientText + Priority string `xml:"priority,omitempty"` // Caps *ClientCaps `xml:"c"` Delay *Delay `xml:"delay"` Error *ErrorClient } - -type ChatType string - -const ( - ChatTypeChat ChatType = "chat" - ChatTypeGroupchat ChatType = "groupchat" - ChatTypeError ChatType = "error" - ChatTypeHeadline ChatType = "headline" - ChatTypeNormal ChatType = "normal" -) - -// MessageClient element -type MessageClient struct { - XMLName xml.Name `xml:"jabber:client message"` - From *model.JID `xml:"from,attr,omitempty"` - ID string `xml:"id,attr,omitempty"` - To *model.JID `xml:"to,attr,omitempty"` - Type ChatType `xml:"type,attr,omitempty"` - Lang string `xml:"lang,attr,omitempty"` - Subject string `xml:"subject"` - Body string `xml:"body"` - Thread string `xml:"thread"` - // Any hasn't matched element - Other []XMLElement `xml:",any"` - - Delay *Delay `xml:"delay"` - Error *ErrorClient -} diff --git a/messages/sasl.go b/messages/sasl.go index 6e0fc1d..04edc7f 100644 --- a/messages/sasl.go +++ b/messages/sasl.go @@ -1,6 +1,14 @@ package messages -import "encoding/xml" +import ( + "encoding/xml" +) + +// RFC 3920 C.4 SASL name space +type SASLMechanisms struct { + XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"` + Mechanism []string `xml:"mechanism"` +} // SASLAuth element type SASLAuth struct { @@ -9,8 +17,44 @@ type SASLAuth struct { Body string `xml:",chardata"` } -// RFC 3920 C.4 SASL name space -type SASLMechanisms struct { - XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"` - Mechanism []string `xml:"mechanism"` +// SASLChallenge element +type SASLChallenge struct { + XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl challenge"` + Body string `xml:",chardata"` +} + +// SASLResponse element +type SASLResponse struct { + XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl response"` + Body string `xml:",chardata"` +} + +// SASLSuccess element +type SASLSuccess struct { + XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl success"` + Body string `xml:",chardata"` +} + +// SASLAbout element +type SASLAbout struct { + XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl abort"` +} + +// SASLFailure element +type SASLFailure struct { + XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"` + + Aborted *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl aborted"` + AccountDisabled *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl account-disabled"` + CredentialsExpired *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl credentials-expired"` + EncryptionRequired *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl encryption-required"` + IncorrectEncoding *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl incorrect-encoding"` + InvalidAuthzid *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl invalid-authzid"` + InvalidMechanism *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl invalid-mechanism"` + MalformedRequest *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl malformed-request"` + MechanismTooWeak *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism-too-weak"` + NotAuthorized *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl not-authorized"` + TemporaryAuthFailure *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl temporary-auth-failure"` + + Body string `xml:",chardata"` } diff --git a/messages/utils.go b/messages/utils.go new file mode 100644 index 0000000..7cf8d0b --- /dev/null +++ b/messages/utils.go @@ -0,0 +1,66 @@ +package messages + +import ( + "crypto/rand" + "encoding/binary" + "encoding/xml" + "fmt" + "reflect" +) + +type Delay struct { + Stamp string `xml:"stamp,attr"` +} + +type XMLElement struct { + XMLName xml.Name + InnerXML string `xml:",innerxml"` +} + +func XMLChildrenString(o interface{}) (result string) { + first := true + val := reflect.ValueOf(o) + if val.Kind() == reflect.Interface && !val.IsNil() { + elm := val.Elem() + if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { + val = elm + } + } + if val.Kind() != reflect.Struct { + return + } + // struct + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + if valueField.Kind() == reflect.Interface && !valueField.IsNil() { + elm := valueField.Elem() + if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr { + valueField = elm + } + } + + if xmlElement, ok := valueField.Interface().(*xml.Name); ok && xmlElement != nil { + if first { + first = false + } else { + result += ", " + } + result += xmlElement.Local + } + } + return +} + +// Cookie is used to give a unique identifier to each request. +type Cookie uint64 + +func CreateCookie() Cookie { + var buf [8]byte + if _, err := rand.Reader.Read(buf[:]); err != nil { + panic("Failed to read random bytes: " + err.Error()) + } + return Cookie(binary.LittleEndian.Uint64(buf[:])) +} +func CreateCookieString() string { + return fmt.Sprintf("%x", CreateCookie()) +} diff --git a/server/extension/iq.go b/server/extension/iq.go index eb9b4c8..fd1f767 100644 --- a/server/extension/iq.go +++ b/server/extension/iq.go @@ -51,7 +51,7 @@ func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client) // not extensions found if count != 1 { - log.Debugf("%s - %s: %v", msg.XMLName.Space, msg.Type, msg.Other) + log.Debugf("%s - %s: %s", msg.XMLName.Space, msg.Type, messages.XMLChildrenString(msg.Other)) } return true diff --git a/server/state/connect.go b/server/state/connect.go index 643553a..a75681f 100644 --- a/server/state/connect.go +++ b/server/state/connect.go @@ -44,7 +44,7 @@ func (state *Start) Process() State { fmt.Fprintf(state.Client.Conn, ` `, - utils.CreateCookie(), messages.NSClient, messages.NSStream) + messages.CreateCookie(), messages.NSClient, messages.NSStream) fmt.Fprintf(state.Client.Conn, ` diff --git a/server/toclient/connect.go b/server/toclient/connect.go index fed6e1f..7f9d0ca 100644 --- a/server/toclient/connect.go +++ b/server/toclient/connect.go @@ -73,7 +73,7 @@ func (state *TLSStream) Process() state.State { PLAIN `, - utils.CreateCookie(), messages.NSClient, messages.NSStream, + messages.CreateCookie(), messages.NSClient, messages.NSStream, messages.NSSASL, messages.NSFeaturesIQRegister) } else { fmt.Fprintf(state.Client.Conn, ` @@ -83,7 +83,7 @@ func (state *TLSStream) Process() state.State { PLAIN `, - utils.CreateCookie(), messages.NSClient, messages.NSStream, + messages.CreateCookie(), messages.NSClient, messages.NSStream, messages.NSSASL) } @@ -174,7 +174,7 @@ func (state *AuthedStart) Process() state.State { `, - messages.NSStream, state.Client.JID.Domain, utils.CreateCookie(), messages.NSClient, + messages.NSStream, state.Client.JID.Domain, messages.CreateCookie(), messages.NSClient, messages.NSBind) return state.Next diff --git a/server/toserver/connect.go b/server/toserver/connect.go index bb5a0ae..64ad62f 100644 --- a/server/toserver/connect.go +++ b/server/toserver/connect.go @@ -97,7 +97,7 @@ func (state *TLSStream) Process() state.State { `, - utils.CreateCookie(), messages.NSClient, messages.NSStream, + messages.CreateCookie(), messages.NSClient, messages.NSStream, messages.NSSASL) return state.Next diff --git a/server/utils/main.go b/server/utils/main.go index ecb094a..c602c1f 100644 --- a/server/utils/main.go +++ b/server/utils/main.go @@ -1,25 +1,7 @@ package utils import ( - "crypto/rand" - "encoding/binary" - "fmt" - "dev.sum7.eu/genofire/yaja/model" ) -// Cookie is used to give a unique identifier to each request. -type Cookie uint64 - -func CreateCookie() Cookie { - var buf [8]byte - if _, err := rand.Reader.Read(buf[:]); err != nil { - panic("Failed to read random bytes: " + err.Error()) - } - return Cookie(binary.LittleEndian.Uint64(buf[:])) -} -func CreateCookieString() string { - return fmt.Sprintf("%x", CreateCookie()) -} - type DomainRegisterAllowed func(*model.JID) bool