diff options
| -rw-r--r-- | client/client.go | 9 | ||||
| -rw-r--r-- | cmd/client/main.go | 1 | ||||
| -rw-r--r-- | core/data.go | 33 | ||||
| -rw-r--r-- | core/errors.go | 13 | ||||
| -rw-r--r-- | core/payload.go | 34 | ||||
| -rw-r--r-- | docs/rfc.md | 69 | ||||
| -rw-r--r-- | docs/rfc.txt | 114 | ||||
| -rw-r--r-- | server/message.go | 37 | ||||
| -rw-r--r-- | server/remote.go | 112 | ||||
| -rw-r--r-- | server/server.go | 139 | ||||
| -rw-r--r-- | server/user.go | 68 |
11 files changed, 432 insertions, 197 deletions
diff --git a/client/client.go b/client/client.go index 7fb9cc9..0fe967f 100644 --- a/client/client.go +++ b/client/client.go @@ -1,6 +1,8 @@ package client import ( + "errors" + "io" "net" "go.rctt.net/solec/core" @@ -37,12 +39,12 @@ func (c *Client) Connect() error { } defer c.conn.Close() - hs := core.Handshake{0, 1} + hs := core.Handshake{0, 1, core.ConnTypeUser} if err := core.Send(c.conn, hs); err != nil { return err } - auth := core.Auth{c.uname, c.pass} + auth := core.UserAuth{c.uname, c.pass} if err := core.Send(c.conn, auth); err != nil { return err } @@ -66,6 +68,9 @@ func (c *Client) read() { payload, err := core.Read(c.conn) if err != nil { c.h.HandleError(err) + if errors.Is(err, io.EOF) { + break + } } c.handlePayload(payload) diff --git a/cmd/client/main.go b/cmd/client/main.go index d3bffd7..635e7ca 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -38,7 +38,6 @@ func main() { log.Println("connecting to " + serverAddr + " as " + user) c = client.NewClient(&Handler{}, serverAddr, user, "valid") - go prompt.Read() if err := c.Connect(); err != nil { diff --git a/core/data.go b/core/data.go index a96b56f..dd2777f 100644 --- a/core/data.go +++ b/core/data.go @@ -16,13 +16,14 @@ type Wrapper interface { type PayloadType uint8 const ( - PayloadUnknown PayloadType = 0x00 - PayloadSuccess = 0x01 - PayloadError = 0x02 - PayloadHandshake = 0x03 - PayloadAuth = 0x04 - PayloadMessage = 0x05 - PayloadTest = 0xFF + PayloadUnknown PayloadType = 0x00 + PayloadSuccess = 0x01 + PayloadError = 0x02 + PayloadHandshake = 0x03 + PayloadUserAuth = 0x04 + PayloadMessage = 0x05 + PayloadServerAuth = 0x06 + PayloadTest = 0xFF ) type ErrorType uint8 @@ -33,6 +34,14 @@ const ( ErrorNotFound = 0x02 ) +type ConnType uint8 + +const ( + ConnTypeUnknown ConnType = 0x00 + ConnTypeUser = 0x01 + ConnTypeServer = 0x02 +) + type Frame struct { Length uint16 Type PayloadType @@ -99,7 +108,7 @@ func Decode(buf io.Reader) (any, error) { err := binary.Read(buf, binary.BigEndian, &pTypeByte) if err != nil { - return nil, fmt.Errorf("cannot read payload type: %v", err) + return nil, fmt.Errorf("cannot read payload type: %w", err) } pType := PayloadType(pTypeByte) @@ -107,7 +116,7 @@ func Decode(buf io.Reader) (any, error) { var pLen uint16 err = binary.Read(buf, binary.BigEndian, &pLen) if err != nil { - return nil, fmt.Errorf("cannot read payload length: %v", err) + return nil, fmt.Errorf("cannot read payload length: %w", err) } switch pType { @@ -117,8 +126,10 @@ func Decode(buf io.Reader) (any, error) { return DecodeError(buf) case PayloadHandshake: return DecodeHandshake(buf) - case PayloadAuth: - return DecodeAuth(buf) + case PayloadUserAuth: + return DecodeUserAuth(buf) + case PayloadServerAuth: + return DecodeServerAuth(buf) case PayloadMessage: return DecodeMessage(buf) case PayloadTest: diff --git a/core/errors.go b/core/errors.go index a50fbf7..ed6702d 100644 --- a/core/errors.go +++ b/core/errors.go @@ -3,10 +3,11 @@ package core import "errors" var ( - ErrUnexpectedPayloadType = errors.New("unexpected payload type") - ErrAuthInvalidUser = errors.New("invalid user") - ErrAuthInvalidPassword = errors.New("invalid password") - ErrInvalidAddress = errors.New("invalid address") - ErrNotSupported = errors.New("not supported") - ErrDisconnected = errors.New("disconnected") + ErrUnexpectedPayloadType = errors.New("unexpected payload type") + ErrAuthInvalidUser = errors.New("invalid user") + ErrAuthInvalidPassword = errors.New("invalid password") + ErrAuthReverseLookupFailed = errors.New("declared name does not match with DNS name") + ErrInvalidAddress = errors.New("invalid address") + ErrNotSupported = errors.New("not supported") + ErrDisconnected = errors.New("disconnected") ) diff --git a/core/payload.go b/core/payload.go index b8e8b9f..e943647 100644 --- a/core/payload.go +++ b/core/payload.go @@ -36,11 +36,12 @@ func DecodeError(buf io.Reader) (Error, error) { type Handshake struct { Major, Minor uint8 + ConnType ConnType } func (h Handshake) Wrap() (PayloadType, []any) { return PayloadHandshake, []any{ - h.Major, h.Minor, + h.Major, h.Minor, h.ConnType, } } @@ -57,22 +58,27 @@ func DecodeHandshake(buf io.Reader) (Handshake, error) { return h, err } + err = decodeNumeric(buf, &h.ConnType) + if err != nil { + return h, err + } + return h, nil } -type Auth struct { +type UserAuth struct { Name string Pass string } -func (a Auth) Wrap() (PayloadType, []any) { - return PayloadAuth, []any{ +func (a UserAuth) Wrap() (PayloadType, []any) { + return PayloadUserAuth, []any{ a.Name, a.Pass, } } -func DecodeAuth(buf io.Reader) (Auth, error) { - var a Auth +func DecodeUserAuth(buf io.Reader) (UserAuth, error) { + var a UserAuth err := decodeString(buf, &a.Name) if err != nil { return a, err @@ -86,6 +92,22 @@ func DecodeAuth(buf io.Reader) (Auth, error) { return a, nil } +type ServerAuth struct { + Name string +} + +func (a ServerAuth) Wrap() (PayloadType, []any) { + return PayloadServerAuth, []any{ + a.Name, + } +} + +func DecodeServerAuth(buf io.Reader) (ServerAuth, error) { + var a ServerAuth + err := decodeString(buf, &a.Name) + return a, err +} + type Message struct { Source string Target string diff --git a/docs/rfc.md b/docs/rfc.md index 8c5818e..8296cfa 100644 --- a/docs/rfc.md +++ b/docs/rfc.md @@ -153,15 +153,16 @@ Payload type attributes describes following characteristics: * C - Client: can be send only by a client * E - Empty: signals an event but does not carry any data -| Type | Name | Attributes | -|------|-----------|------------| -| 0x00 | | R | -| 0x01 | Success | SCE | -| 0x02 | Error | S | -| 0x03 | Handshake | SC | -| 0x04 | Auth | C | -| 0x05 | Message | SC | -| 0xFF | Test | R | +| Type | Name | Attributes | +|------|------------|------------| +| 0x00 | | R | +| 0x01 | Success | SCE | +| 0x02 | Error | S | +| 0x03 | Handshake | SC | +| 0x04 | UserAuth | C | +| 0x05 | Message | SC | +| 0x06 | ServerAuth | S | +| 0xFF | Test | R | ### Success @@ -177,8 +178,9 @@ Payload is always empty for this type. | Type | Description | |------|-----------------------------------------------------------| -| 0x01 | Auth failed. Invalid username or password. | +| 0x01 | Client auth failed. Invalid username or password. | | 0x02 | Not found. User or channel cannot access user or channel. | +| 0x03 | Server auth failed. Unknown name. | ### Handshake @@ -200,13 +202,19 @@ different auth method will be used. | 0x01 | User -> Server | | 0x02 | Server -> Server | -### Auth +### UserAuth | Type | Name | |--------|----------| | string | username | | string | password | +### ServerAuth + +| Type | Name | +|--------|----------| +| string | name | + ### Message | Type | Name | @@ -238,7 +246,7 @@ Some operations require multiple rounds of communication. In this case payloads are send in a sequence. Payload that is not part of this specific operation (for example incoming message) cannot interrupt this process. -### Connection initialisation +### Client-Server connection initialisation ~~~ ascii-art +--------+ +--------+ @@ -254,7 +262,7 @@ specific operation (for example incoming message) cannot interrupt this process. | +- If [ver_major] does not match server | | protocol version close the connection | | - | Send [Auth] | + | Send [UserAuth] | +--------------------------->| | | | Send [Error 0x01] | @@ -263,4 +271,37 @@ specific operation (for example incoming message) cannot interrupt this process. | | | Send [Sucesss] | |<---------------------------+ -~~~
\ No newline at end of file +~~~ + +### Server-Server connection initialisation + +~~~ ascii-art ++--------+ +--------+ +| Server | | Server | ++----+---+ +----+---+ + | | + | Initialise TCP connection | + +--------------------------->| + | | + | Send [Handshake] | + +--------------------------->| + | | + | +- If [ver_major] does not match server + | | protocol version close the connection + | | + | Send [ServerAuth] | + +--------------------------->| + | | + | Send [Error 0x03] | + |<---------------------------+- If [name] is not present in known public + | | keys list. + | | + | Send [Sucesss] | + |<---------------------------+ +~~~ + +## Server to server operation + +Exchanging messages between SOLEC servers is a core concept behind the project. +Sending message to user residing on a different server require estabilishing a +connection between both servers. diff --git a/docs/rfc.txt b/docs/rfc.txt index f9e8bca..ffd2f67 100644 --- a/docs/rfc.txt +++ b/docs/rfc.txt @@ -1,7 +1,7 @@ SOLEC Working Group bt, Ed. Internet-Draft RCTT.net -Intended status: Experimental 14 April 2026 -Expires: 16 October 2026 +Intended status: Experimental 18 April 2026 +Expires: 20 October 2026 System of Lightweight Electronic Communication @@ -29,11 +29,14 @@ Table of Contents 2.4.1. Success 2.4.2. Error 2.4.3. Handshake - 2.4.4. Auth - 2.4.5. Message - 2.4.6. Test + 2.4.4. UserAuth + 2.4.5. ServerAuth + 2.4.6. Message + 2.4.7. Test 2.5. Sequential operations - 2.5.1. Connection initialisation + 2.5.1. Client-Server connection initialisation + 2.5.2. Server-Server connection initialisation + 2.6. Server 1. Introduction @@ -153,25 +156,27 @@ Table of Contents * C - Client: can be send only by a client * E - Empty: signals an event but does not carry any data - +======+===========+============+ - | Type | Name | Attributes | - +======+===========+============+ - | 0x00 | | R | - +------+-----------+------------+ - | 0x01 | Success | SCE | - +------+-----------+------------+ - | 0x02 | Error | S | - +------+-----------+------------+ - | 0x03 | Handshake | SC | - +------+-----------+------------+ - | 0x04 | Auth | C | - +------+-----------+------------+ - | 0x05 | Message | SC | - +------+-----------+------------+ - | 0xFF | Test | R | - +------+-----------+------------+ - - Table 1 + +======+============+============+ + | Type | Name | Attributes | + +======+============+============+ + | 0x00 | | R | + +------+------------+------------+ + | 0x01 | Success | SCE | + +------+------------+------------+ + | 0x02 | Error | S | + +------+------------+------------+ + | 0x03 | Handshake | SC | + +------+------------+------------+ + | 0x04 | UserAuth | C | + +------+------------+------------+ + | 0x05 | Message | SC | + +------+------------+------------+ + | 0x06 | ServerAuth | S | + +------+------------+------------+ + | 0xFF | Test | R | + +------+------------+------------+ + + Table 1 2.4.1. Success @@ -192,10 +197,12 @@ Table of Contents +======+============================================================+ | Type | Description | +======+============================================================+ - | 0x01 | Auth failed. Invalid username or password. | + | 0x01 | Client auth failed. Invalid username or password. | +------+------------------------------------------------------------+ - | 0x02 | Not found. User or channel cannot access | - | | user or channel. | + | 0x02 | Not found. User or channel cannot access user or | + | | channel. | + +------+------------------------------------------------------------+ + | 0x03 | Server auth failed. Unknown name. | +------+------------------------------------------------------------+ Table 3 @@ -231,7 +238,7 @@ Table of Contents Table 5 -2.4.4. Auth +2.4.4. UserAuth +========+==========+ | Type | Name | @@ -243,7 +250,17 @@ Table of Contents Table 6 -2.4.5. Message +2.4.5. ServerAuth + + +========+======+ + | Type | Name | + +========+======+ + | string | name | + +--------+------+ + + Table 7 + +2.4.6. Message +===========+=================+ | Type | Name | @@ -257,9 +274,9 @@ Table of Contents | string | message_content | +-----------+-----------------+ - Table 7 + Table 8 -2.4.6. Test +2.4.7. Test Test payload is used for encoder and decoders testing. Clients and servers should ignore this kind of payload. @@ -284,7 +301,7 @@ Table of Contents | uint64 | num4 | +-----------+-------+ - Table 8 + Table 9 2.5. Sequential operations @@ -293,7 +310,7 @@ Table of Contents this specific operation (for example incoming message) cannot interrupt this process. -2.5.1. Connection initialisation +2.5.1. Client-Server connection initialisation +--------+ +--------+ | Client | | Server | @@ -308,7 +325,7 @@ Table of Contents | +- If [ver_major] does not match server | | protocol version close the connection | | - | Send [Auth] | + | Send [UserAuth] | +--------------------------->| | | | Send [Error 0x01] | @@ -317,3 +334,30 @@ Table of Contents | | | Send [Sucesss] | |<---------------------------+ + +2.5.2. Server-Server connection initialisation + + +--------+ +--------+ + | Server | | Server | + +----+---+ +----+---+ + | | + | Initialise TCP connection | + +--------------------------->| + | | + | Send [Handshake] | + +--------------------------->| + | | + | +- If [ver_major] does not match server + | | protocol version close the connection + | | + | Send [ServerAuth] | + +--------------------------->| + | | + | Send [Error 0x03] | + |<---------------------------+- If [name] is not present in known public + | | keys list. + | | + | Send [Sucesss] | + |<---------------------------+ + +2.6. Server diff --git a/server/message.go b/server/message.go index c1384f7..2487abd 100644 --- a/server/message.go +++ b/server/message.go @@ -1,6 +1,8 @@ package server import ( + "errors" + "fmt" "log" "time" @@ -27,3 +29,38 @@ func (s *Server) SendBroadcast(msg string) { } } } + +func (s *Server) handleMessage(msg core.Message) error { + log.Println("message:", msg.Source, "->", msg.Target, msg.Content) + + channel, host, err := core.ReadAddr(msg.Target) + if err != nil { + return err + } + + if host == s.name { + return s.handleLocalMessage(channel, msg) + } + + return s.handleOutboundMessage(channel, host, msg) +} + +func (s *Server) handleLocalMessage(channel string, msg core.Message) error { + s.usersMu.RLock() + user, ok := s.users[channel] + if !ok { + return errors.New("target not found") + } + s.usersMu.RUnlock() + + return user.Send(msg) +} + +func (s *Server) handleOutboundMessage(channel, host string, msg core.Message) error { + remote, err := s.getRemote(host) + if err != nil { + return fmt.Errorf("cannot access remote server: %w", err) + } + + return core.Send(remote.Conn, msg) +} diff --git a/server/remote.go b/server/remote.go new file mode 100644 index 0000000..2449511 --- /dev/null +++ b/server/remote.go @@ -0,0 +1,112 @@ +package server + +import ( + "log" + "net" + + "go.rctt.net/solec/core" +) + +type RemoteServer struct { + Name string + Conn net.Conn +} + +func NewRemoteServer(name string, conn net.Conn) RemoteServer { + return RemoteServer{name, conn} +} + +func (s *Server) handleServerConn(conn net.Conn) { + defer conn.Close() + + name, err := s.performServerAuth(conn) + if err != nil { + log.Println("server auth error:", err) + return + } + + s.serversMu.RLock() + if _, ok := s.servers[name]; ok { + log.Println("server already connected") + return + } + s.serversMu.RUnlock() + + rs := NewRemoteServer(name, conn) + s.serversMu.Lock() + s.servers[name] = rs + s.serversMu.Unlock() + log.Println("connection from server:", name) + + defer func() { + s.serversMu.Lock() + log.Println("server disconnected: ", rs.Name) + delete(s.servers, rs.Name) + s.serversMu.Unlock() + }() + + if err := s.readInput(conn); err != nil { + log.Println(err) + } +} + +func (s *Server) performServerAuth(conn net.Conn) (string, error) { + payload, err := core.Decode(conn) + if err != nil { + return "", err + } + auth, ok := payload.(core.ServerAuth) + if !ok { + return "", core.ErrUnexpectedPayloadType + } + + if err := core.Send(conn, core.Success{}); err != nil { + return "", err + } + + return auth.Name, nil +} + +func (s *Server) getRemote(name string) (RemoteServer, error) { + s.serversMu.RLock() + remote, ok := s.servers[name] + s.serversMu.RUnlock() + + if ok { + return remote, nil + } + + conn, err := s.initRemoteConn(name) + if err != nil { + return RemoteServer{}, err + } + + rs := NewRemoteServer(name, conn) + s.serversMu.Lock() + s.servers[name] = rs + s.serversMu.Unlock() + log.Println("connected to server:", name) + + return rs, nil +} + +func (s *Server) initRemoteConn(name string) (net.Conn, error) { + conn, err := net.Dial("tcp", name+":9999") + if err != nil { + return conn, err + } + + hs := core.Handshake{0, 1, core.ConnTypeServer} + if err := core.Send(conn, hs); err != nil { + conn.Close() + return conn, err + } + + auth := core.ServerAuth{Name: s.name} + if err := core.Send(conn, auth); err != nil { + conn.Close() + return conn, err + } + + return conn, nil +} diff --git a/server/server.go b/server/server.go index 712f654..7573968 100644 --- a/server/server.go +++ b/server/server.go @@ -13,7 +13,9 @@ type Server struct { listenAddr string name string users map[string]User - mu sync.Mutex + servers map[string]RemoteServer + usersMu sync.RWMutex + serversMu sync.RWMutex } func NewServer(listenAddr string, name string) *Server { @@ -21,6 +23,7 @@ func NewServer(listenAddr string, name string) *Server { listenAddr: listenAddr, name: name, users: make(map[string]User), + servers: make(map[string]RemoteServer), } } @@ -45,158 +48,64 @@ func (s *Server) Start() error { func (s *Server) handleConn(conn net.Conn) { defer conn.Close() - if err := s.performHandshake(conn); err != nil { - log.Println("handshake error:", err) - return - } - - user, err := s.performAuth(conn) + cType, err := s.performHandshake(conn) if err != nil { - log.Println("auth error:", err) + log.Println("handshake error:", err) return } - defer func() { - s.mu.Lock() - log.Println("client disconnected: ", user.Name) - delete(s.users[user.Name].Conns, conn) - if len(s.users[user.Name].Conns) == 0 { - log.Println("all connections closed for user:", user.Name) - delete(s.users, user.Name) - } - s.mu.Unlock() - }() - - if err := s.readInput(conn, user); err != nil { - log.Println(err) + switch cType { + case core.ConnTypeUnknown: + log.Println("invalid connection type") + case core.ConnTypeUser: + s.handleUserConn(conn) + case core.ConnTypeServer: + s.handleServerConn(conn) } } -func (s *Server) performHandshake(conn net.Conn) error { - serverHs := core.Handshake{0, 1} +func (s *Server) performHandshake(conn net.Conn) (core.ConnType, error) { + serverHs := core.Handshake{0, 2, core.ConnTypeServer} if err := core.Send(conn, serverHs); err != nil { - return err + return core.ConnTypeUnknown, err } clientPayload, err := core.Decode(conn) if err != nil { - return err + return core.ConnTypeUnknown, err } clientHs, ok := clientPayload.(core.Handshake) if !ok { - return core.ErrUnexpectedPayloadType + return core.ConnTypeUnknown, core.ErrUnexpectedPayloadType } if serverHs.Major != clientHs.Major { - return errors.New("server and client are using different protocol version") - } - - return nil -} - -func (s *Server) performAuth(conn net.Conn) (User, error) { - clientPayload, err := core.Decode(conn) - if err != nil { - return User{}, err - } - - clientAuth, ok := clientPayload.(core.Auth) - if !ok { - return User{}, core.ErrUnexpectedPayloadType - } - - // For testing --- - if clientAuth.Pass != "valid" { - if err := core.Send(conn, core.Error{core.ErrorAuthFailed}); err != nil { - log.Println("cannot send auth error:", err) - } - - return User{}, core.ErrAuthInvalidPassword - } - // --- - - if err := core.Send(conn, core.Success{}); err != nil { - return User{}, err - } - - user, ok := s.users[clientAuth.Name] - if !ok { - log.Println("initial connection from user:", clientAuth.Name) - user = NewUser(conn, clientAuth) - s.users[clientAuth.Name] = user - return user, nil + return clientHs.ConnType, errors.New("server and client are using different protocol version") } - log.Println("next connection from user:", user.Name) - user.Conns[conn] = struct{}{} - return user, nil + return clientHs.ConnType, nil } -func (s *Server) readInput(conn net.Conn, user User) error { +func (s *Server) readInput(conn net.Conn) error { for { payload, err := core.Decode(conn) if err != nil { return err } - if err := s.handlePayload(conn, user, payload); err != nil { + if err := s.handlePayload(payload); err != nil { log.Print("handler error: ", err) } } } -func (s *Server) handlePayload(conn net.Conn, user User, payload any) error { +func (s *Server) handlePayload(payload any) error { switch v := payload.(type) { case core.Message: - return s.handleMessage(conn, user, v) + return s.handleMessage(v) default: return core.ErrUnexpectedPayloadType } } - -func (s *Server) handleMessage(conn net.Conn, user User, msg core.Message) error { - log.Println("message:", user.Name, "->", msg.Target, msg.Content) - - channel, host, err := core.ReadAddr(msg.Target) - if err != nil { - return err - } - - if host == s.name { - return s.handleLocalMessage(channel, msg) - } - - return s.handleOutboundMessage(channel, host, msg) -} - -func (s *Server) handleLocalMessage(channel string, msg core.Message) error { - user, ok := s.users[channel] - if !ok { - return errors.New("target not found") - } - - return user.Send(msg) -} - -func (s *Server) handleOutboundMessage(channel, host string, msg core.Message) error { - conn, err := net.Dial("tcp", host+":9999") - if err != nil { - return err - } - defer conn.Close() - - hs := core.Handshake{0, 1} - if err := core.Send(conn, hs); err != nil { - return err - } - - // TODO, servers should not use this type of auth - auth := core.Auth{"server", "valid"} - if err := core.Send(conn, auth); err != nil { - return err - } - - return core.Send(conn, msg) -} diff --git a/server/user.go b/server/user.go index f69d126..5d2731c 100644 --- a/server/user.go +++ b/server/user.go @@ -1,6 +1,7 @@ package server import ( + "log" "net" "go.rctt.net/solec/core" @@ -11,9 +12,9 @@ type User struct { Conns map[net.Conn]struct{} } -func NewUser(conn net.Conn, auth core.Auth) User { +func NewUser(conn net.Conn, name string) User { u := User{ - Name: auth.Name, + Name: name, Conns: make(map[net.Conn]struct{}), } @@ -31,12 +32,65 @@ func (u *User) Send(payload core.Wrapper) error { return nil } -func (u *User) Auth(pass string) error { - // TODO: Implement auth +func (s *Server) handleUserConn(conn net.Conn) { + name, err := s.performUserAuth(conn) + if err != nil { + log.Println("user auth error:", err) + return + } - if pass != "valid" { - return core.ErrAuthInvalidPassword + s.usersMu.Lock() + user, ok := s.users[name] + if ok { + log.Println("next connection from user:", user.Name) + user.Conns[conn] = struct{}{} + } else { + log.Println("initial connection from user:", name) + user = NewUser(conn, name) + s.users[name] = user } + s.usersMu.Unlock() - return nil + defer func() { + s.usersMu.Lock() + log.Println("client disconnected: ", user.Name) + delete(s.users[user.Name].Conns, conn) + if len(s.users[user.Name].Conns) == 0 { + log.Println("all connections closed for user:", user.Name) + delete(s.users, user.Name) + } + s.usersMu.Unlock() + }() + + if err := s.readInput(conn); err != nil { + log.Println(err) + } +} + +func (s *Server) performUserAuth(conn net.Conn) (string, error) { + clientPayload, err := core.Decode(conn) + if err != nil { + return "", err + } + + clientAuth, ok := clientPayload.(core.UserAuth) + if !ok { + return "", core.ErrUnexpectedPayloadType + } + + // For testing --- + if clientAuth.Pass != "valid" { + if err := core.Send(conn, core.Error{core.ErrorAuthFailed}); err != nil { + log.Println("cannot send auth error:", err) + } + + return "", core.ErrAuthInvalidPassword + } + // --- + + if err := core.Send(conn, core.Success{}); err != nil { + return "", err + } + + return clientAuth.Name, nil } |
