feat(sol): UDP + HTTP listener
This commit is contained in:
91
server/api.go
Normal file
91
server/api.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"wol-sol-agent/auth"
|
||||
"wol-sol-agent/system"
|
||||
|
||||
"git.secnex.io/secnex/masterlog"
|
||||
)
|
||||
|
||||
// APIServer handles HTTP API requests
|
||||
type APIServer struct {
|
||||
port int
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
|
||||
// NewAPIServer creates a new API server
|
||||
func NewAPIServer(port int, authenticator *auth.Authenticator) *APIServer {
|
||||
return &APIServer{
|
||||
port: port,
|
||||
authenticator: authenticator,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the API server
|
||||
func (s *APIServer) Start(done chan bool) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/shutdown", s.handleShutdown)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", s.port),
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
masterlog.Info("API server listening on port", map[string]interface{}{
|
||||
"port": s.port,
|
||||
})
|
||||
|
||||
// Start server in goroutine
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
masterlog.Error("API server error", map[string]interface{}{
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for shutdown signal
|
||||
<-done
|
||||
server.Close()
|
||||
}
|
||||
|
||||
func (s *APIServer) handleShutdown(w http.ResponseWriter, r *http.Request) {
|
||||
// Check authentication
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="SOL Agent"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
masterlog.Error("Unauthorized API request", map[string]interface{}{
|
||||
"address": r.RemoteAddr,
|
||||
"error": "no auth",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Verify credentials
|
||||
if !s.authenticator.Verify(username, password) {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="SOL Agent"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
masterlog.Error("Unauthorized API request", map[string]interface{}{
|
||||
"address": r.RemoteAddr,
|
||||
"error": "invalid credentials",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
masterlog.Info("Authenticated shutdown request", map[string]interface{}{
|
||||
"address": r.RemoteAddr,
|
||||
})
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Shutting down system...\n")
|
||||
|
||||
// Shutdown in a goroutine to allow response to be sent
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
system.Shutdown()
|
||||
}()
|
||||
}
|
||||
107
server/udp.go
Normal file
107
server/udp.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"wol-sol-agent/auth"
|
||||
"wol-sol-agent/magicpacket"
|
||||
"wol-sol-agent/system"
|
||||
|
||||
"git.secnex.io/secnex/masterlog"
|
||||
)
|
||||
|
||||
// UDPServer handles UDP connections for Magic Packets
|
||||
type UDPServer struct {
|
||||
port int
|
||||
authenticator *auth.Authenticator
|
||||
}
|
||||
|
||||
// NewUDPServer creates a new UDP server
|
||||
func NewUDPServer(port int, authenticator *auth.Authenticator) *UDPServer {
|
||||
return &UDPServer{
|
||||
port: port,
|
||||
authenticator: authenticator,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the UDP server
|
||||
func (s *UDPServer) Start(done chan bool) {
|
||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", s.port))
|
||||
if err != nil {
|
||||
masterlog.Error("Failed to resolve UDP address", map[string]interface{}{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
masterlog.Error("Failed to listen on UDP port", map[string]interface{}{
|
||||
"error": err,
|
||||
"port": s.port,
|
||||
})
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
masterlog.Info("UDP server listening on port", map[string]interface{}{
|
||||
"port": s.port,
|
||||
})
|
||||
|
||||
buffer := make([]byte, 2048)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
n, clientAddr, err := conn.ReadFromUDP(buffer)
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
continue
|
||||
}
|
||||
masterlog.Error("Error reading UDP packet", map[string]interface{}{
|
||||
"error": err,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
masterlog.Info("Received UDP packet", map[string]interface{}{
|
||||
"bytes": n,
|
||||
"address": clientAddr,
|
||||
})
|
||||
|
||||
// Check authentication and magic packet
|
||||
if s.isValidAuthenticatedMagicPacket(buffer[:n]) {
|
||||
masterlog.Info("Authenticated Magic Packet detected! Shutting down system...", map[string]interface{}{
|
||||
"address": clientAddr,
|
||||
})
|
||||
system.Shutdown()
|
||||
return
|
||||
}
|
||||
masterlog.Error("Invalid or unauthenticated packet", map[string]interface{}{
|
||||
"address": clientAddr,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isValidAuthenticatedMagicPacket checks if the packet contains valid authentication
|
||||
// followed by a valid Magic Packet
|
||||
func (s *UDPServer) isValidAuthenticatedMagicPacket(data []byte) bool {
|
||||
username, password, magicPacketData, ok := magicpacket.ExtractAuthFromPacket(data)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Verify authentication
|
||||
if !s.authenticator.Verify(username, password) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if it's a valid magic packet
|
||||
return magicpacket.IsMagicPacket(magicPacketData)
|
||||
}
|
||||
Reference in New Issue
Block a user