1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
package server
import (
"context"
"encoding/hex"
"fmt"
"log/slog"
"net"
"sync"
"go.neonxp.ru/qchat/internal/chat"
"go.neonxp.ru/qchat/internal/config"
"golang.org/x/crypto/ssh"
)
type Server struct {
cfg *config.Config
chat *chat.Chat
}
func New(cfg *config.Config, ch *chat.Chat) *Server {
return &Server{
cfg: cfg,
chat: ch,
}
}
func (s *Server) Run(ctx context.Context) error {
config := &ssh.ServerConfig{
PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
return &ssh.Permissions{
ExtraData: map[any]any{
"identify": hex.EncodeToString(pubKey.Marshal()),
},
}, nil
},
}
private, err := ssh.ParsePrivateKey([]byte(s.cfg.Server.PrivateKey))
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
config.AddHostKey(private)
listener, err := net.Listen("tcp", s.cfg.Server.Addr)
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
go func() {
<-ctx.Done()
_ = listener.Close()
}()
wg := sync.WaitGroup{}
defer wg.Wait()
slog.InfoContext(ctx, "started server at", slog.String("addr", s.cfg.Server.Addr))
for {
nConn, err := listener.Accept()
if err != nil {
slog.ErrorContext(ctx, "failed to accept incoming connection", slog.Any("err", err))
continue
}
wg.Go(func() {
if err := s.serveConn(ctx, nConn, config); err != nil {
slog.ErrorContext(ctx, "connection error", slog.Any("err", err))
}
})
}
}
|