aboutsummaryrefslogtreecommitdiff
path: root/hotline/server.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2025-11-22 19:42:32 -0800
committerJeff Halter <868228+jhalter@users.noreply.github.com>2025-11-22 19:43:12 -0800
commit357baa94bf9b1d4b623f8a9c04b9d591e9f60406 (patch)
tree2bd41be1c6974d91cb47b2781e318abc62903e80 /hotline/server.go
parent8686ff94c313671deeec7623230cbac93b66e0eb (diff)
Add optional TLS support for encrypted client connections
- Add TLSConfig and TLSPort fields to Server struct - Add WithTLS option function for configuration - Add ServeWithTLS and ServeFileTransfersWithTLS methods - Update ListenAndServe to start TLS listeners when configured - Add -tls-cert, -tls-key, -tls-port command-line flags - Fix data race in rateLimiters map access with mutex - Add TLS documentation with certificate generation instructions
Diffstat (limited to 'hotline/server.go')
-rw-r--r--hotline/server.go47
1 files changed, 46 insertions, 1 deletions
diff --git a/hotline/server.go b/hotline/server.go
index 0395d7b..2c25e93 100644
--- a/hotline/server.go
+++ b/hotline/server.go
@@ -5,6 +5,7 @@ import (
"bytes"
"context"
"crypto/rand"
+ "crypto/tls"
"encoding/binary"
"errors"
"fmt"
@@ -40,7 +41,8 @@ type Server struct {
NetInterface string
Port int
- rateLimiters map[string]*rate.Limiter
+ rateLimiters map[string]*rate.Limiter
+ rateLimitersMu sync.Mutex
handlers map[TranType]HandlerFunc
@@ -71,6 +73,9 @@ type Server struct {
// TrackerRegistrar handles tracker registration (injectable for testing)
TrackerRegistrar TrackerRegistrar
+
+ TLSConfig *tls.Config
+ TLSPort int
}
type Option = func(s *Server)
@@ -108,6 +113,14 @@ func WithTrackerRegistrar(registrar TrackerRegistrar) func(s *Server) {
}
}
+// WithTLS optionally enables TLS support on the specified port.
+func WithTLS(tlsConfig *tls.Config, port int) func(s *Server) {
+ return func(s *Server) {
+ s.TLSConfig = tlsConfig
+ s.TLSPort = port
+ }
+}
+
type ServerConfig struct {
}
@@ -168,6 +181,28 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
log.Fatal(s.ServeFileTransfers(ctx, ln))
}()
+ if s.TLSConfig != nil {
+ wg.Add(1)
+ go func() {
+ ln, err := net.Listen("tcp", fmt.Sprintf("%s:%v", s.NetInterface, s.TLSPort))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ log.Fatal(s.ServeWithTLS(ctx, ln))
+ }()
+
+ wg.Add(1)
+ go func() {
+ ln, err := net.Listen("tcp", fmt.Sprintf("%s:%v", s.NetInterface, s.TLSPort+1))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ log.Fatal(s.ServeFileTransfersWithTLS(ctx, ln))
+ }()
+ }
+
wg.Wait()
return nil
@@ -195,6 +230,14 @@ func (s *Server) ServeFileTransfers(ctx context.Context, ln net.Listener) error
}
}
+func (s *Server) ServeWithTLS(ctx context.Context, ln net.Listener) error {
+ return s.Serve(ctx, tls.NewListener(ln, s.TLSConfig))
+}
+
+func (s *Server) ServeFileTransfersWithTLS(ctx context.Context, ln net.Listener) error {
+ return s.ServeFileTransfers(ctx, tls.NewListener(ln, s.TLSConfig))
+}
+
func (s *Server) sendTransaction(t Transaction) error {
client := s.ClientMgr.Get(t.ClientID)
@@ -249,11 +292,13 @@ func (s *Server) Serve(ctx context.Context, ln net.Listener) error {
defer func() { _ = conn.Close() }()
// Check if we have an existing rate limit for the IP and create one if we do not.
+ s.rateLimitersMu.Lock()
rl, ok := s.rateLimiters[ipAddr]
if !ok {
rl = rate.NewLimiter(perIPRateLimit, 1)
s.rateLimiters[ipAddr] = rl
}
+ s.rateLimitersMu.Unlock()
// Check if the rate limit is exceeded and close the connection if so.
if !rl.Allow() {