|
|
|
@ -5,6 +5,7 @@ import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"net"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
@ -15,12 +16,14 @@ const ISO8601 = "2006-01-02 15:04:05.999"
|
|
|
|
|
type Storage interface {
|
|
|
|
|
io.Closer
|
|
|
|
|
Open() error
|
|
|
|
|
// AddRecursor() error
|
|
|
|
|
// DeleteRecursors(id) error
|
|
|
|
|
// GetRecursors(net.IP) ([]string,error)
|
|
|
|
|
// AddRule(Rule) error
|
|
|
|
|
// DeleteRule(int) error
|
|
|
|
|
// GetRules() ([]Rule,error)
|
|
|
|
|
AddRecursors(net.IP, int, int, int) error
|
|
|
|
|
DeleteRecursors(int) error
|
|
|
|
|
GetRecursors() ([]RecursorRow, error)
|
|
|
|
|
// UpdateRule(RuleRow) error
|
|
|
|
|
AddRule(RuleRow) error
|
|
|
|
|
GetRule(int) (RuleRow, error)
|
|
|
|
|
GetRules() ([]RuleRow, error)
|
|
|
|
|
DeleteRule(int) error
|
|
|
|
|
Log(QueryLog) error
|
|
|
|
|
GetLog(GetLogInput) ([]QueryLog, error)
|
|
|
|
|
GetLogAggregate(LogAggregateInput) ([]LogAggregateDataPoint, error)
|
|
|
|
@ -31,6 +34,58 @@ type Sqlite struct {
|
|
|
|
|
*sql.DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) GetRecursors() ([]RecursorRow, error) {
|
|
|
|
|
sql := `
|
|
|
|
|
SELECT id, ipAddress, timeoutMs, weight FROM recursors ORDER BY weight ASC;
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
rows, err := ss.Query(sql)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("could not execute select for recursors: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var results []RecursorRow
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var row RecursorRow
|
|
|
|
|
|
|
|
|
|
if err := rows.Scan(&row.ID, &row.IpAddress, &row.TimeoutMs, &row.Weight); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("could not read row: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results = append(results, row)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) DeleteRecursors(id int) error {
|
|
|
|
|
sql := `DELETE FROM recursors WHERE id = ?;`
|
|
|
|
|
if _, err := ss.Exec(sql, id); err != nil {
|
|
|
|
|
return fmt.Errorf("Could not delete recursor: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RecursorRow struct {
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
IpAddress string `json:"ipAddress"`
|
|
|
|
|
TimeoutMs int `json:"timeoutMs"`
|
|
|
|
|
Weight int `json:"weight"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) AddRecursors(ip net.IP, port, timeout, weight int) error {
|
|
|
|
|
sql := `INSERT INTO recursors (ipAddress, timeoutMs, weight) VALUES (?, ?, ?);`
|
|
|
|
|
|
|
|
|
|
if _, err := ss.Exec(sql, fmt.Sprintf("%s:%d", ip.String(), port), timeout, weight); err != nil {
|
|
|
|
|
return fmt.Errorf("could not insert recursor: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type GetLogInput struct {
|
|
|
|
|
Start time.Time
|
|
|
|
|
End time.Time
|
|
|
|
@ -39,6 +94,92 @@ type GetLogInput struct {
|
|
|
|
|
Page int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RuleRow struct {
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
Weight int `json:"weight"`
|
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
|
Created time.Time `json:"created"`
|
|
|
|
|
Rule
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) AddRule(rr RuleRow) error {
|
|
|
|
|
sql := `INSERT INTO rules (name, expression, answerType, answerValue, ttl, weight, enabled, created)
|
|
|
|
|
VALUES (?, ? , ?, ?, ?, ?, 1, ?);`
|
|
|
|
|
|
|
|
|
|
if _, err := ss.Exec(sql, rr.Name, rr.Value, rr.Answer.Type, rr.Answer.Value, rr.TTL, rr.Weight, time.Now().UTC().Format(ISO8601)); err != nil {
|
|
|
|
|
return fmt.Errorf("could not delete rule: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) GetRule(ruleId int) (RuleRow, error) {
|
|
|
|
|
sql := `SELECT id, name, expression, answerType, answerValue, ttl, weight, enabled, created FROM rules WHERE id = ?;`
|
|
|
|
|
|
|
|
|
|
var rr RuleRow
|
|
|
|
|
row := ss.QueryRow(sql, ruleId)
|
|
|
|
|
|
|
|
|
|
var createdTime string
|
|
|
|
|
if err := row.Scan(&rr.ID, &rr.Name, &rr.Value, &rr.Answer.Type, &rr.Answer.Value, &rr.TTL, &rr.Weight, &rr.Enabled, &createdTime); err != nil {
|
|
|
|
|
return rr, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
rr.Created, err = time.Parse(ISO8601, createdTime)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return rr, fmt.Errorf("could not parse time: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rr, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) DeleteRule(ruleId int) error {
|
|
|
|
|
if _, err := ss.Exec(`DELETE FROM rules WHERE id = ?;`, ruleId); err != nil {
|
|
|
|
|
return fmt.Errorf("could not delete rule: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) GetRules() ([]RuleRow, error) {
|
|
|
|
|
sql := `SELECT id, name, expression, answerType, answerValue, ttl, weight, enabled, created FROM rules ORDER BY weight ASC;`
|
|
|
|
|
|
|
|
|
|
rows, err := ss.Query(sql)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var results []RuleRow
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var rule RuleRow
|
|
|
|
|
var createdTime string
|
|
|
|
|
|
|
|
|
|
if err := rows.Scan(
|
|
|
|
|
&rule.ID,
|
|
|
|
|
&rule.Name,
|
|
|
|
|
&rule.Value,
|
|
|
|
|
&rule.Answer.Type,
|
|
|
|
|
&rule.Answer.Value,
|
|
|
|
|
&rule.TTL,
|
|
|
|
|
&rule.Weight,
|
|
|
|
|
&rule.Enabled,
|
|
|
|
|
&createdTime,
|
|
|
|
|
); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("could not read from db: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rule.Created, err = time.Parse(ISO8601, createdTime)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results = append(results, rule)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ss *Sqlite) GetLog(in GetLogInput) ([]QueryLog, error) {
|
|
|
|
|
if in.Limit <= 0 {
|
|
|
|
|
in.Limit = 100
|
|
|
|
@ -67,7 +208,6 @@ func (ss *Sqlite) GetLog(in GetLogInput) ([]QueryLog, error) {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var ql []QueryLog
|
|
|
|
@ -159,16 +299,17 @@ func (ss *Sqlite) GetLogAggregate(la LogAggregateInput) ([]LogAggregateDataPoint
|
|
|
|
|
|
|
|
|
|
sql = fmt.Sprintf(sql, column, timeWindow, column, timeWindow)
|
|
|
|
|
|
|
|
|
|
result, err := ss.Query(sql)
|
|
|
|
|
rows, err := ss.Query(sql)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var results []LogAggregateDataPoint
|
|
|
|
|
for result.Next() {
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var ladp LogAggregateDataPoint
|
|
|
|
|
var timeInterval int64
|
|
|
|
|
if err := result.Scan(
|
|
|
|
|
if err := rows.Scan(
|
|
|
|
|
&ladp.Header,
|
|
|
|
|
&ladp.AverageTotalTime,
|
|
|
|
|
&ladp.Count,
|
|
|
|
@ -244,6 +385,28 @@ func initTable(db *sql.DB) error {
|
|
|
|
|
recurseUpStreamIP TEXT,
|
|
|
|
|
status TEXT NOT NULL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS rules (
|
|
|
|
|
id INTEGER PRIMARY KEY,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
expression TEXT NOT NULL,
|
|
|
|
|
answerType TEXT NOT NULL,
|
|
|
|
|
answerValue TEXT NOT NULL,
|
|
|
|
|
ttl INT NOT NULL,
|
|
|
|
|
weight INT NOT NULL,
|
|
|
|
|
enabled INT NOT NULL,
|
|
|
|
|
created TEXT NOT NULL
|
|
|
|
|
);
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_rules_name ON rules (name);
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_rules_expression ON rules (expression);
|
|
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS recursors (
|
|
|
|
|
id INTEGER PRIMARY KEY,
|
|
|
|
|
ipAddress TEXT NOT NULL,
|
|
|
|
|
timeoutMs INT NOT NULL,
|
|
|
|
|
weight INT NOT NULL
|
|
|
|
|
);
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_recursors_ipAddress ON recursors (ipAddress);
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
if _, err := db.Exec(sql); err != nil {
|
|
|
|
|