2026-04-04 10:29:02 +00:00
|
|
|
package log
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2026-04-04 16:10:33 +01:00
|
|
|
"dappco.re/go/proxy"
|
2026-04-04 10:29:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// NewAccessLog creates an append-only access log.
|
2026-04-05 00:04:02 +00:00
|
|
|
//
|
|
|
|
|
// al := log.NewAccessLog("/var/log/proxy-access.log")
|
|
|
|
|
// defer al.Close()
|
2026-04-04 10:29:02 +00:00
|
|
|
func NewAccessLog(path string) *AccessLog {
|
|
|
|
|
return &AccessLog{path: path}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:45:11 +00:00
|
|
|
// Close releases the underlying file handle if the log has been opened.
|
|
|
|
|
//
|
|
|
|
|
// al := log.NewAccessLog("/var/log/proxy-access.log")
|
|
|
|
|
// defer al.Close()
|
|
|
|
|
func (l *AccessLog) Close() {
|
|
|
|
|
if l == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
defer l.mu.Unlock()
|
2026-04-05 00:04:02 +00:00
|
|
|
if l.file != nil {
|
|
|
|
|
_ = l.file.Close()
|
|
|
|
|
l.file = nil
|
2026-04-04 23:45:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 02:34:53 +00:00
|
|
|
// OnLogin writes a connect line such as:
|
|
|
|
|
//
|
|
|
|
|
// al.OnLogin(proxy.Event{Miner: &proxy.Miner{}})
|
|
|
|
|
// // 2026-04-04T12:00:00Z CONNECT 10.0.0.1 WALLET XMRig/6.21.0
|
2026-04-04 10:29:02 +00:00
|
|
|
func (l *AccessLog) OnLogin(e proxy.Event) {
|
|
|
|
|
if l == nil || e.Miner == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-04-04 22:29:00 +00:00
|
|
|
l.writeConnectLine(e.Miner.IP(), e.Miner.User(), e.Miner.Agent())
|
2026-04-04 10:29:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 02:34:53 +00:00
|
|
|
// OnClose writes a close line such as:
|
|
|
|
|
//
|
|
|
|
|
// al.OnClose(proxy.Event{Miner: &proxy.Miner{}})
|
|
|
|
|
// // 2026-04-04T12:00:00Z CLOSE 10.0.0.1 WALLET rx=512 tx=4096
|
2026-04-04 10:29:02 +00:00
|
|
|
func (l *AccessLog) OnClose(e proxy.Event) {
|
|
|
|
|
if l == nil || e.Miner == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-04-04 22:29:00 +00:00
|
|
|
l.writeCloseLine(e.Miner.IP(), e.Miner.User(), e.Miner.RX(), e.Miner.TX())
|
2026-04-04 10:29:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewShareLog creates an append-only share log.
|
2026-04-05 00:04:02 +00:00
|
|
|
//
|
|
|
|
|
// sl := log.NewShareLog("/var/log/proxy-shares.log")
|
|
|
|
|
// defer sl.Close()
|
2026-04-04 10:29:02 +00:00
|
|
|
func NewShareLog(path string) *ShareLog {
|
|
|
|
|
return &ShareLog{path: path}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 23:45:11 +00:00
|
|
|
// Close releases the underlying file handle if the log has been opened.
|
|
|
|
|
//
|
|
|
|
|
// sl := log.NewShareLog("/var/log/proxy-shares.log")
|
|
|
|
|
// defer sl.Close()
|
|
|
|
|
func (l *ShareLog) Close() {
|
|
|
|
|
if l == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
l.mu.Lock()
|
|
|
|
|
defer l.mu.Unlock()
|
2026-04-05 00:04:02 +00:00
|
|
|
if l.file != nil {
|
|
|
|
|
_ = l.file.Close()
|
|
|
|
|
l.file = nil
|
2026-04-04 23:45:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 02:34:53 +00:00
|
|
|
// OnAccept writes an accept line such as:
|
|
|
|
|
//
|
|
|
|
|
// sl.OnAccept(proxy.Event{Miner: &proxy.Miner{}, Diff: 100000, Latency: 82})
|
|
|
|
|
// // 2026-04-04T12:00:00Z ACCEPT WALLET diff=100000 latency=82ms
|
2026-04-04 10:29:02 +00:00
|
|
|
func (l *ShareLog) OnAccept(e proxy.Event) {
|
|
|
|
|
if l == nil || e.Miner == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
l.writeAcceptLine(e.Miner.User(), e.Diff, uint64(e.Latency))
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 02:34:53 +00:00
|
|
|
// OnReject writes a reject line such as:
|
|
|
|
|
//
|
|
|
|
|
// sl.OnReject(proxy.Event{Miner: &proxy.Miner{}, Error: "Invalid nonce"})
|
|
|
|
|
// // 2026-04-04T12:00:00Z REJECT WALLET reason="Invalid nonce"
|
2026-04-04 10:29:02 +00:00
|
|
|
func (l *ShareLog) OnReject(e proxy.Event) {
|
|
|
|
|
if l == nil || e.Miner == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
l.writeRejectLine(e.Miner.User(), e.Error)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 22:29:00 +00:00
|
|
|
func (accessLog *AccessLog) writeConnectLine(ip, user, agent string) {
|
2026-04-04 22:09:58 +00:00
|
|
|
accessLog.mu.Lock()
|
|
|
|
|
defer accessLog.mu.Unlock()
|
|
|
|
|
if err := accessLog.ensureFile(); err != nil {
|
2026-04-04 10:29:02 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var builder strings.Builder
|
|
|
|
|
builder.WriteString(time.Now().UTC().Format(time.RFC3339))
|
|
|
|
|
builder.WriteByte(' ')
|
2026-04-04 22:29:00 +00:00
|
|
|
builder.WriteString("CONNECT")
|
2026-04-04 10:29:02 +00:00
|
|
|
builder.WriteString(" ")
|
|
|
|
|
builder.WriteString(ip)
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
builder.WriteString(user)
|
2026-04-04 21:52:13 +00:00
|
|
|
builder.WriteString(" ")
|
|
|
|
|
builder.WriteString(agent)
|
2026-04-04 22:29:00 +00:00
|
|
|
builder.WriteByte('\n')
|
2026-04-05 00:04:02 +00:00
|
|
|
_, _ = accessLog.file.WriteString(builder.String())
|
2026-04-04 22:29:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (accessLog *AccessLog) writeCloseLine(ip, user string, rx, tx uint64) {
|
|
|
|
|
accessLog.mu.Lock()
|
|
|
|
|
defer accessLog.mu.Unlock()
|
|
|
|
|
if err := accessLog.ensureFile(); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var builder strings.Builder
|
|
|
|
|
builder.WriteString(time.Now().UTC().Format(time.RFC3339))
|
|
|
|
|
builder.WriteByte(' ')
|
|
|
|
|
builder.WriteString("CLOSE")
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
builder.WriteString(ip)
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
builder.WriteString(user)
|
2026-04-04 21:52:13 +00:00
|
|
|
builder.WriteString(" rx=")
|
|
|
|
|
builder.WriteString(strconv.FormatUint(rx, 10))
|
|
|
|
|
builder.WriteString(" tx=")
|
|
|
|
|
builder.WriteString(strconv.FormatUint(tx, 10))
|
2026-04-04 10:29:02 +00:00
|
|
|
builder.WriteByte('\n')
|
2026-04-05 00:04:02 +00:00
|
|
|
_, _ = accessLog.file.WriteString(builder.String())
|
2026-04-04 10:29:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 22:09:58 +00:00
|
|
|
func (shareLog *ShareLog) writeAcceptLine(user string, diff uint64, latency uint64) {
|
|
|
|
|
shareLog.mu.Lock()
|
|
|
|
|
defer shareLog.mu.Unlock()
|
|
|
|
|
if err := shareLog.ensureFile(); err != nil {
|
2026-04-04 10:29:02 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var builder strings.Builder
|
|
|
|
|
builder.WriteString(time.Now().UTC().Format(time.RFC3339))
|
|
|
|
|
builder.WriteString(" ACCEPT")
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
builder.WriteString(user)
|
|
|
|
|
builder.WriteString(" diff=")
|
|
|
|
|
builder.WriteString(strconv.FormatUint(diff, 10))
|
|
|
|
|
builder.WriteString(" latency=")
|
|
|
|
|
builder.WriteString(strconv.FormatUint(latency, 10))
|
|
|
|
|
builder.WriteString("ms")
|
|
|
|
|
builder.WriteByte('\n')
|
2026-04-05 00:04:02 +00:00
|
|
|
_, _ = shareLog.file.WriteString(builder.String())
|
2026-04-04 10:29:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 22:09:58 +00:00
|
|
|
func (shareLog *ShareLog) writeRejectLine(user, reason string) {
|
|
|
|
|
shareLog.mu.Lock()
|
|
|
|
|
defer shareLog.mu.Unlock()
|
|
|
|
|
if err := shareLog.ensureFile(); err != nil {
|
2026-04-04 10:29:02 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var builder strings.Builder
|
|
|
|
|
builder.WriteString(time.Now().UTC().Format(time.RFC3339))
|
|
|
|
|
builder.WriteString(" REJECT ")
|
|
|
|
|
builder.WriteString(user)
|
|
|
|
|
builder.WriteString(" reason=\"")
|
|
|
|
|
builder.WriteString(reason)
|
|
|
|
|
builder.WriteString("\"\n")
|
2026-04-05 00:04:02 +00:00
|
|
|
_, _ = shareLog.file.WriteString(builder.String())
|
2026-04-04 10:29:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 22:09:58 +00:00
|
|
|
func (accessLog *AccessLog) ensureFile() error {
|
2026-04-05 00:04:02 +00:00
|
|
|
if accessLog.file != nil {
|
2026-04-04 10:29:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
2026-04-04 22:09:58 +00:00
|
|
|
f, err := os.OpenFile(accessLog.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
2026-04-04 10:29:02 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-04-05 00:04:02 +00:00
|
|
|
accessLog.file = f
|
2026-04-04 10:29:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 22:09:58 +00:00
|
|
|
func (shareLog *ShareLog) ensureFile() error {
|
2026-04-05 00:04:02 +00:00
|
|
|
if shareLog.file != nil {
|
2026-04-04 10:29:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
2026-04-04 22:09:58 +00:00
|
|
|
f, err := os.OpenFile(shareLog.path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
2026-04-04 10:29:02 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-04-05 00:04:02 +00:00
|
|
|
shareLog.file = f
|
2026-04-04 10:29:02 +00:00
|
|
|
return nil
|
|
|
|
|
}
|