summaryrefslogtreecommitdiff
path: root/internal/server
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2025-07-06 00:51:33 +0300
committerKyren223 <Kyren223@proton.me>2025-07-06 00:51:33 +0300
commite7344492595b26fd7ae21b945d1bdfe2ef115f10 (patch)
treed15598991805ef530be5332cf43f6ea629f7adaf /internal/server
parent8f8736d09efce46147ff37d4840fb424aa6cb7e4 (diff)
Added terms of service (incomplete!!!!) to the server, and added
authentication separation between API calls, actually authentication (nonce, and verification) is not implemented yet
Diffstat (limited to 'internal/server')
-rw-r--r--internal/server/ctxkeys/ctxkeys.go16
-rw-r--r--internal/server/server.go97
-rw-r--r--internal/server/session/session.go35
3 files changed, 117 insertions, 31 deletions
diff --git a/internal/server/ctxkeys/ctxkeys.go b/internal/server/ctxkeys/ctxkeys.go
index 1fb0fa6..827e6ea 100644
--- a/internal/server/ctxkeys/ctxkeys.go
+++ b/internal/server/ctxkeys/ctxkeys.go
@@ -14,15 +14,23 @@ const (
IpAddr
Evicted
EvictedBy
+ Request
+ RequestType
KeyMax
)
var keyNames = map[key]string{
- UserID: "user_id",
- IpAddr: "ip_addr",
- Evicted: "evicted",
- EvictedBy: "evicted_by",
+ UserID: "user_id",
+ IpAddr: "ip_addr",
+ Evicted: "evicted",
+ EvictedBy: "evicted_by",
+ Request: "request",
+ RequestType: "request_type",
+}
+
+func (k key) String() string {
+ return keyNames[k]
}
func init() {
diff --git a/internal/server/server.go b/internal/server/server.go
index 32c1205..cdb9313 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -83,11 +83,15 @@ func (s *server) AddSession(session *session.Session, userId snowflake.ID, pubKe
if sess, ok := s.sessions[session.ID()]; ok {
EvictSession(sess) // last connection wins
slog.Info("closed due to new connection from another location",
- ctxkeys.IpAddr, sess.Addr(), ctxkeys.UserID, sess.ID(),
- ctxkeys.EvictedBy, session.Addr())
+ ctxkeys.IpAddr.String(), sess.Addr(),
+ ctxkeys.UserID.String(), sess.ID(),
+ ctxkeys.EvictedBy.String(), session.Addr(),
+ )
slog.Info("this session evicted another session",
- ctxkeys.IpAddr, session.Addr(), ctxkeys.UserID, session.ID(),
- ctxkeys.Evicted, sess.Addr())
+ ctxkeys.IpAddr.String(), session.Addr(),
+ ctxkeys.UserID.String(), session.ID(),
+ ctxkeys.Evicted.String(), sess.Addr(),
+ )
}
s.sessions[session.ID()] = session
@@ -98,8 +102,7 @@ func EvictSession(sess *session.Session) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
payload := &packet.Error{
- Error: "new connection from another location, closing this one",
- PktType: packet.PacketError,
+ Error: "new connection from another location, closing this one",
}
pkt := packet.NewPacket(packet.NewMsgPackEncoder(payload))
sess.Write(ctx, pkt)
@@ -206,6 +209,7 @@ func (server *server) handleConnection(conn net.Conn) {
// Writer
go func() {
defer close(done)
+ defer conn.Close() // To unblock reader
writeQueue := sess.Read()
for packet := range writeQueue {
@@ -242,6 +246,10 @@ func (server *server) handleConnection(conn net.Conn) {
}
}()
+ // NOTE: IMPROTANT LEGAL STUFF
+ // Sending this first thing, before client sends us any data
+ sendTosInfo(ctx, sess)
+
// Reader
buffer := make([]byte, 512)
for {
@@ -328,11 +336,60 @@ func processPacket(ctx context.Context, sess *session.Session, pkt packet.Packet
}
func processRequest(ctx context.Context, sess *session.Session, request packet.Payload) packet.Payload {
- log.Println(sess.Addr(), "processing", request.Type(), "request:", request)
+ slog.InfoContext(ctx, "processing request",
+ ctxkeys.RequestType.String(),
+ request.Type(), ctxkeys.Request.String(), request,
+ )
+
+ if !sess.IsTosAccepted() {
+ if acceptTos, ok := request.(*packet.AcceptTos); ok && acceptTos.IAgreeToTheTermsOfServiceAndPrivacyPolicy {
+ sess.ReceivedTosAcceptance()
+ slog.InfoContext(ctx, "terms of service accepted, continuing...")
+ return &api.ErrSuccess
+ }
+
+ slog.InfoContext(ctx, "refused terms of service, refusing service...")
+ sess.Close() // Refuse to receive any more requests
+ return &packet.Error{Error: "Terms of Service not accepted, refusing service"}
+ }
+
+ assert.Assert(sess.IsTosAccepted(), "justified paranoia") // Just in case
// TODO: add a way to measure the time each request/response took and log it
// Potentially even separate time for code vs DB operations
+
var response packet.Payload
+
+ if sess.IsAuthenticated() {
+ // Authentication only requests, others will be handled without auth even if authenticated
+ authCtx := ctxkeys.WithValue(ctx, ctxkeys.UserID, sess.ID())
+ response = processAuthenticatedRequests(authCtx, sess, request)
+ }
+
+ if response != nil {
+ return response
+ }
+
+ switch request := request.(type) {
+ // TODO:
+ // case *packet.GetNonce:
+ // response = timeout(5*time.Millisecond, api.SetUserData, ctx, sess, request)
+
+ // TODO:
+ // case *packet.Authenticate:
+ // response = timeout(5*time.Millisecond, api.SetUserData, ctx, sess, request)
+
+ default:
+ _ = request // FIXME: remove
+ response = &packet.Error{Error: "use of disallowed packet type for request"}
+ }
+
+ return response
+}
+
+func processAuthenticatedRequests(ctx context.Context, sess *session.Session, request packet.Payload) packet.Payload {
+ var response packet.Payload
+
switch request := request.(type) {
case *packet.SetUserData:
@@ -383,13 +440,7 @@ func processRequest(ctx context.Context, sess *session.Session, request packet.P
response = timeout(10*time.Millisecond, api.GetUsers, ctx, sess, request)
default:
- response = &packet.Error{Error: "use of disallowed packet type for request"}
- }
-
- // TODO: Isn't this weird? shouldn't it always be packet.ErrorPacket type?
- // rather than copying the pkt type of the request?
- if response, ok := response.(*packet.Error); ok {
- response.PktType = request.Type()
+ response = nil
}
return response
@@ -422,7 +473,23 @@ func timeout[T packet.Payload](
}
}
-func (server *server) sendInitialPackets(ctx context.Context, sess *session.Session) bool {
+func sendTosInfo(ctx context.Context, sess *session.Session) bool {
+ // FIXME: ===== REPLACE WITH ACTUAL TOS =====
+ // TODO: Get TOS and privacy policy
+ tos := "WIP Terms of Service"
+ privacy := "WIP Privacy Policy"
+ date := "2025-07-03"
+
+ payload := &packet.TosInfo{
+ Tos: tos,
+ PrivacyPolicy: privacy,
+ Date: date,
+ }
+ pkt := packet.NewPacket(packet.NewMsgPackEncoder(payload))
+ return sess.Write(ctx, pkt)
+}
+
+func (server *server) sendInitialAuthPackets(ctx context.Context, sess *session.Session) bool {
payload := api.GetUserData(ctx, sess, &packet.GetUserData{})
if payload == &api.ErrInternalError {
return false
diff --git a/internal/server/session/session.go b/internal/server/session/session.go
index ffe4e13..013ff71 100644
--- a/internal/server/session/session.go
+++ b/internal/server/session/session.go
@@ -36,8 +36,9 @@ type Session struct {
issuedTime time.Time
challenge []byte
- pubKey ed25519.PublicKey
- id snowflake.ID
+ isTosAccepted bool
+ pubKey ed25519.PublicKey
+ id snowflake.ID
mu sync.Mutex
}
@@ -50,16 +51,18 @@ func NewSession(
assert.NotNil(addr, "tcp address should be valid")
assert.NotNil(manager, "session manager should be valid")
session := &Session{
- manager: manager,
- addr: addr,
- cancel: cancel,
- writeQueue: make(chan packet.Packet, WriteQueueSize),
- writerWg: writerWg,
- issuedTime: time.Time{},
- challenge: make([]byte, 32),
- pubKey: ed25519.PublicKey{},
- id: snowflake.InvalidID,
- mu: sync.Mutex{},
+ manager: manager,
+ addr: addr,
+ cancel: cancel,
+ writeQueue: make(chan packet.Packet, WriteQueueSize),
+ writerWg: writerWg,
+ writeMu: sync.RWMutex{},
+ issuedTime: time.Time{},
+ challenge: make([]byte, 32),
+ pubKey: ed25519.PublicKey{},
+ id: snowflake.InvalidID,
+ mu: sync.Mutex{},
+ isTosAccepted: false,
}
return session
}
@@ -69,6 +72,14 @@ func (s *Session) Addr() *net.TCPAddr {
return s.addr
}
+func (s *Session) IsTosAccepted() bool {
+ return s.isTosAccepted
+}
+
+func (s *Session) ReceivedTosAcceptance() {
+ s.isTosAccepted = true
+}
+
func (s *Session) IsAuthenticated() bool {
return s.id != snowflake.InvalidID
}