fix: sécuriser shutdown, config par défaut et reconnexion socket
Co-authored-by: aider (openrouter/openai/gpt-5.3-codex) <aider@aider.chat>
This commit is contained in:
@ -103,13 +103,20 @@ func (w *UnixSocketWriter) Write(rec api.LogRecord) error {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
// Connect if not already connected
|
||||
if w.conn == nil {
|
||||
ensureConn := func() error {
|
||||
if w.conn != nil {
|
||||
return nil
|
||||
}
|
||||
conn, err := net.Dial("unix", w.socketPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to socket %s: %w", w.socketPath, err)
|
||||
}
|
||||
w.conn = conn
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ensureConn(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := json.Marshal(rec)
|
||||
@ -120,12 +127,18 @@ func (w *UnixSocketWriter) Write(rec api.LogRecord) error {
|
||||
// Add newline for line-based protocols
|
||||
data = append(data, '\n')
|
||||
|
||||
_, err = w.conn.Write(data)
|
||||
if err != nil {
|
||||
// Connection failed, try to reconnect
|
||||
w.conn.Close()
|
||||
if _, err = w.conn.Write(data); err != nil {
|
||||
_ = w.conn.Close()
|
||||
w.conn = nil
|
||||
return fmt.Errorf("failed to write to socket: %w", err)
|
||||
|
||||
if err2 := ensureConn(); err2 != nil {
|
||||
return fmt.Errorf("failed to write to socket and reconnect failed: %w", err2)
|
||||
}
|
||||
if _, err2 := w.conn.Write(data); err2 != nil {
|
||||
_ = w.conn.Close()
|
||||
w.conn = nil
|
||||
return fmt.Errorf("failed to write to socket after reconnect: %w", err2)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"ja4sentinel/api"
|
||||
)
|
||||
@ -102,7 +107,6 @@ func TestMultiWriter(t *testing.T) {
|
||||
defer fileWriter.Close()
|
||||
|
||||
multiWriter.Add(fileWriter)
|
||||
multiWriter.Add(NewStdoutWriter())
|
||||
|
||||
rec := api.LogRecord{
|
||||
SrcIP: "192.168.1.1",
|
||||
@ -233,3 +237,123 @@ func TestUnixSocketWriter(t *testing.T) {
|
||||
|
||||
writer.Close()
|
||||
}
|
||||
|
||||
type unixTestServer struct {
|
||||
listener net.Listener
|
||||
received chan string
|
||||
mu sync.Mutex
|
||||
conns map[net.Conn]struct{}
|
||||
}
|
||||
|
||||
func newUnixTestServer(path string) (*unixTestServer, error) {
|
||||
_ = os.Remove(path)
|
||||
ln, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &unixTestServer{
|
||||
listener: ln,
|
||||
received: make(chan string, 10),
|
||||
conns: make(map[net.Conn]struct{}),
|
||||
}
|
||||
|
||||
go s.serve()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *unixTestServer) serve() {
|
||||
for {
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.conns[conn] = struct{}{}
|
||||
s.mu.Unlock()
|
||||
|
||||
go func(c net.Conn) {
|
||||
defer func() {
|
||||
s.mu.Lock()
|
||||
delete(s.conns, c)
|
||||
s.mu.Unlock()
|
||||
_ = c.Close()
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(c)
|
||||
for scanner.Scan() {
|
||||
s.received <- scanner.Text()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *unixTestServer) close(path string) {
|
||||
_ = s.listener.Close()
|
||||
|
||||
s.mu.Lock()
|
||||
for c := range s.conns {
|
||||
_ = c.Close()
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
_ = os.Remove(path)
|
||||
}
|
||||
|
||||
func TestUnixSocketWriter_ReconnectAndWrite(t *testing.T) {
|
||||
socketPath := filepath.Join(t.TempDir(), "ja4sentinel.sock")
|
||||
|
||||
server1, err := newUnixTestServer(socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start first unix test server: %v", err)
|
||||
}
|
||||
|
||||
writer, err := NewUnixSocketWriter(socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("NewUnixSocketWriter() error = %v", err)
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
rec1 := api.LogRecord{
|
||||
SrcIP: "192.168.1.1",
|
||||
SrcPort: 11111,
|
||||
DstIP: "10.0.0.1",
|
||||
DstPort: 443,
|
||||
JA4: "first",
|
||||
}
|
||||
if err := writer.Write(rec1); err != nil {
|
||||
t.Fatalf("first Write() error = %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-server1.received:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timeout waiting first message on unix socket")
|
||||
}
|
||||
|
||||
server1.close(socketPath)
|
||||
|
||||
server2, err := newUnixTestServer(socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to restart unix test server: %v", err)
|
||||
}
|
||||
defer server2.close(socketPath)
|
||||
|
||||
rec2 := api.LogRecord{
|
||||
SrcIP: "192.168.1.2",
|
||||
SrcPort: 22222,
|
||||
DstIP: "10.0.0.2",
|
||||
DstPort: 443,
|
||||
JA4: "second",
|
||||
}
|
||||
if err := writer.Write(rec2); err != nil {
|
||||
t.Fatalf("second Write() after reconnect error = %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-server2.received:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timeout waiting second message after reconnect")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user