aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-10-11 20:50:20 +0000
committerEmiliano Ciavatta2020-10-11 20:50:20 +0000
commit828aeef9a7333aaabeaf9324a85aac56348b3805 (patch)
treeae86bb8ead2940852e85a48104b4e2567e559d62
parentbc989facca1f3381afed2f7c982da7784fad2327 (diff)
Add SearchController
-rw-r--r--application_context.go4
-rw-r--r--application_router.go71
-rw-r--r--connections_controller.go47
-rw-r--r--search_controller.go44
-rw-r--r--storage.go2
5 files changed, 83 insertions, 85 deletions
diff --git a/application_context.go b/application_context.go
index fc76d00..8abb6f4 100644
--- a/application_context.go
+++ b/application_context.go
@@ -112,9 +112,9 @@ func (sm *ApplicationContext) configure() {
sm.RulesManager = rulesManager
sm.PcapImporter = NewPcapImporter(sm.Storage, *serverNet, sm.RulesManager)
sm.ServicesController = NewServicesController(sm.Storage)
- sm.ConnectionsController = NewConnectionsController(sm.Storage, sm.ServicesController)
- sm.ConnectionStreamsController = NewConnectionStreamsController(sm.Storage)
sm.SearchController = NewSearchController(sm.Storage)
+ sm.ConnectionsController = NewConnectionsController(sm.Storage, sm.SearchController, sm.ServicesController)
+ sm.ConnectionStreamsController = NewConnectionStreamsController(sm.Storage)
sm.StatisticsController = NewStatisticsController(sm.Storage)
sm.IsConfigured = true
}
diff --git a/application_router.go b/application_router.go
index dc9f9d4..656b63e 100644
--- a/application_router.go
+++ b/application_router.go
@@ -22,8 +22,6 @@ import (
"fmt"
"github.com/gin-gonic/contrib/static"
"github.com/gin-gonic/gin"
- "github.com/gin-gonic/gin/binding"
- "github.com/go-playground/validator/v10"
log "github.com/sirupsen/logrus"
"net/http"
"os"
@@ -297,71 +295,42 @@ func CreateApplicationRouter(applicationContext *ApplicationContext,
})
api.GET("/searches", func(c *gin.Context) {
- success(c, applicationContext.SearchController.PerformedSearches())
+ success(c, applicationContext.SearchController.GetPerformedSearches())
})
api.POST("/searches/perform", func(c *gin.Context) {
var options SearchOptions
-
- parentIsZero := func (fl validator.FieldLevel) bool {
- log.Println(fl.FieldName())
- log.Println("noooooo")
- return fl.Parent().IsZero()
- }
- eitherWith := func (fl validator.FieldLevel) bool {
- otherField := fl.Parent().FieldByName(fl.Param())
- log.Println(fl.Param())
- log.Println("bbbbbbbbbb")
- return (fl.Field().IsZero() && !otherField.IsZero()) || (!fl.Field().IsZero() && otherField.IsZero())
- }
- aaa := func (fl validator.FieldLevel) bool {
-
- log.Println("awww")
- return fl.Field().IsZero()
+ if err := c.ShouldBindJSON(&options); err != nil {
+ badRequest(c, err)
+ return
}
- bbb := func (fl validator.FieldLevel) bool {
-
- log.Println("iiiii")
- return true
+ // stupid checks because validator library is a shit
+ var badContentError error
+ if options.TextSearch.isZero() == options.RegexSearch.isZero() {
+ badContentError = errors.New("specify either 'text_search' or 'regex_search'")
}
-
- if validate, ok := binding.Validator.Engine().(*validator.Validate); ok {
- if err := validate.RegisterValidation("parent_is_zero", parentIsZero); err != nil {
- log.WithError(err).Panic("cannot register 'topzero' validator")
+ if !options.TextSearch.isZero() {
+ if (options.TextSearch.Terms == nil) == (options.TextSearch.ExactPhrase == "") {
+ badContentError = errors.New("specify either 'terms' or 'exact_phrase'")
}
- if err := validate.RegisterValidation("either_with", eitherWith); err != nil {
- log.WithError(err).Panic("cannot register 'either_with' validator")
+ if (options.TextSearch.Terms == nil) && (options.TextSearch.ExcludedTerms != nil) {
+ badContentError = errors.New("'excluded_terms' must be specified only with 'terms'")
}
- if err := validate.RegisterValidation("aaa", aaa); err != nil {
- log.WithError(err).Panic("cannot register 'either_with' validator")
- }
- if err := validate.RegisterValidation("bbb", bbb); err != nil {
- log.WithError(err).Panic("cannot register 'either_with' validator")
+ }
+ if !options.RegexSearch.isZero() {
+ if (options.RegexSearch.Pattern == "") == (options.RegexSearch.NotPattern == "") {
+ badContentError = errors.New("specify either 'pattern' or 'not_pattern'")
}
- } else {
- log.Panic("cannot ")
}
-
- if err := c.ShouldBindJSON(&options); err != nil {
- badRequest(c, err)
+ if badContentError != nil {
+ badRequest(c, badContentError)
return
}
- log.Println(options)
-
-
- success(c, "ok")
-
-
-
-
-
-
-
- //success(c, applicationContext.SearchController.PerformSearch(c, options))
+ success(c, applicationContext.SearchController.PerformSearch(c, options))
})
api.GET("/streams/:id", func(c *gin.Context) {
diff --git a/connections_controller.go b/connections_controller.go
index 30a5ee5..a293a80 100644
--- a/connections_controller.go
+++ b/connections_controller.go
@@ -48,33 +48,37 @@ type Connection struct {
}
type ConnectionsFilter struct {
- From string `form:"from" binding:"omitempty,hexadecimal,len=24"`
- To string `form:"to" binding:"omitempty,hexadecimal,len=24"`
- ServicePort uint16 `form:"service_port"`
- ClientAddress string `form:"client_address" binding:"omitempty,ip"`
- ClientPort uint16 `form:"client_port"`
- MinDuration uint `form:"min_duration"`
- MaxDuration uint `form:"max_duration" binding:"omitempty,gtefield=MinDuration"`
- MinBytes uint `form:"min_bytes"`
- MaxBytes uint `form:"max_bytes" binding:"omitempty,gtefield=MinBytes"`
- StartedAfter int64 `form:"started_after" `
- StartedBefore int64 `form:"started_before" binding:"omitempty,gtefield=StartedAfter"`
- ClosedAfter int64 `form:"closed_after" `
- ClosedBefore int64 `form:"closed_before" binding:"omitempty,gtefield=ClosedAfter"`
- Hidden bool `form:"hidden"`
- Marked bool `form:"marked"`
- MatchedRules []string `form:"matched_rules" binding:"dive,hexadecimal,len=24"`
- Limit int64 `form:"limit"`
+ From string `form:"from" binding:"omitempty,hexadecimal,len=24"`
+ To string `form:"to" binding:"omitempty,hexadecimal,len=24"`
+ ServicePort uint16 `form:"service_port"`
+ ClientAddress string `form:"client_address" binding:"omitempty,ip"`
+ ClientPort uint16 `form:"client_port"`
+ MinDuration uint `form:"min_duration"`
+ MaxDuration uint `form:"max_duration" binding:"omitempty,gtefield=MinDuration"`
+ MinBytes uint `form:"min_bytes"`
+ MaxBytes uint `form:"max_bytes" binding:"omitempty,gtefield=MinBytes"`
+ StartedAfter int64 `form:"started_after" `
+ StartedBefore int64 `form:"started_before" binding:"omitempty,gtefield=StartedAfter"`
+ ClosedAfter int64 `form:"closed_after" `
+ ClosedBefore int64 `form:"closed_before" binding:"omitempty,gtefield=ClosedAfter"`
+ Hidden bool `form:"hidden"`
+ Marked bool `form:"marked"`
+ MatchedRules []string `form:"matched_rules" binding:"dive,hexadecimal,len=24"`
+ PerformedSearch string `form:"performed_search" binding:"omitempty,hexadecimal,len=24"`
+ Limit int64 `form:"limit"`
}
type ConnectionsController struct {
storage Storage
+ searchController *SearchController
servicesController *ServicesController
}
-func NewConnectionsController(storage Storage, servicesController *ServicesController) ConnectionsController {
+func NewConnectionsController(storage Storage, searchesController *SearchController,
+ servicesController *ServicesController) ConnectionsController {
return ConnectionsController{
storage: storage,
+ searchController: searchesController,
servicesController: servicesController,
}
}
@@ -144,6 +148,13 @@ func (cc ConnectionsController) GetConnections(c context.Context, filter Connect
query = query.Filter(OrderedDocument{{"matched_rules", UnorderedDocument{"$all": matchedRules}}})
}
+ performedSearchID, _ := RowIDFromHex(filter.PerformedSearch)
+ if !performedSearchID.IsZero() {
+ performedSearch := cc.searchController.GetPerformedSearch(performedSearchID)
+ if !performedSearch.ID.IsZero() {
+ query = query.Filter(OrderedDocument{{"_id", UnorderedDocument{"$in": performedSearch.AffectedConnections}}})
+ }
+ }
if filter.Limit > 0 && filter.Limit <= MaxQueryLimit {
query = query.Limit(filter.Limit)
} else {
diff --git a/search_controller.go b/search_controller.go
index ad47dbc..723cd93 100644
--- a/search_controller.go
+++ b/search_controller.go
@@ -26,14 +26,15 @@ import (
)
const (
- secondsToNano = 1000 * 1000 * 1000
- maxSearchTimeout = 60 * secondsToNano
+ secondsToNano = 1000 * 1000 * 1000
+ maxSearchTimeout = 10 * secondsToNano
+ maxRecentSearches = 200
)
type PerformedSearch struct {
ID RowID `bson:"_id" json:"id"`
SearchOptions SearchOptions `bson:"search_options" json:"search_options"`
- AffectedConnections []RowID `bson:"affected_connections" json:"affected_connections,omitempty"`
+ AffectedConnections []RowID `bson:"affected_connections" json:"-"`
AffectedConnectionsCount int `bson:"affected_connections_count" json:"affected_connections_count"`
StartedAt time.Time `bson:"started_at" json:"started_at"`
FinishedAt time.Time `bson:"finished_at" json:"finished_at"`
@@ -42,21 +43,21 @@ type PerformedSearch struct {
}
type SearchOptions struct {
- TextSearch TextSearch `bson:"text_search" json:"text_search" validate:"either_with=RegexSearch"`
- RegexSearch RegexSearch `bson:"regex_search" json:"regex_search" validate:"either_with=TextSearch"`
+ TextSearch TextSearch `bson:"text_search" json:"text_search"`
+ RegexSearch RegexSearch `bson:"regex_search" json:"regex_search"`
Timeout time.Duration `bson:"timeout" json:"timeout" binding:"max=60"`
}
type TextSearch struct {
- Terms []string `bson:"terms" json:"terms" binding:"parent_is_zero|either_with=ExactPhrase,isdefault|min=1,dive,min=3"`
- ExcludedTerms []string `bson:"excluded_terms" json:"excluded_terms" binding:"required_with=Terms,dive,isdefault|min=1"`
- ExactPhrase string `bson:"exact_phrase" json:"exact_phrase" binding:"isdefault|min=3,parent_is_zero|either_with=Terms"`
+ Terms []string `bson:"terms" json:"terms" binding:"isdefault|min=1,dive,min=3"`
+ ExcludedTerms []string `bson:"excluded_terms" json:"excluded_terms" binding:"isdefault|min=1,dive,min=3"`
+ ExactPhrase string `bson:"exact_phrase" json:"exact_phrase" binding:"isdefault|min=3"`
CaseSensitive bool `bson:"case_sensitive" json:"case_sensitive"`
}
type RegexSearch struct {
- Pattern string `bson:"pattern" json:"pattern" binding:"parent_is_zero|either_with=NotPattern,isdefault|min=3"`
- NotPattern string `bson:"not_pattern" json:"not_pattern" binding:"parent_is_zero|either_with=Pattern,isdefault|min=3"`
+ Pattern string `bson:"pattern" json:"pattern" binding:"isdefault|min=3"`
+ NotPattern string `bson:"not_pattern" json:"not_pattern" binding:"isdefault|min=3"`
CaseInsensitive bool `bson:"case_insensitive" json:"case_insensitive"`
MultiLine bool `bson:"multi_line" json:"multi_line"`
IgnoreWhitespaces bool `bson:"ignore_whitespaces" json:"ignore_whitespaces"`
@@ -71,8 +72,8 @@ type SearchController struct {
func NewSearchController(storage Storage) *SearchController {
var searches []PerformedSearch
- if err := storage.Find(Searches).All(&searches); err != nil {
- // log.WithError(err).Panic("failed to retrieve performed searches")
+ if err := storage.Find(Searches).Limit(maxRecentSearches).All(&searches); err != nil {
+ log.WithError(err).Panic("failed to retrieve performed searches")
}
return &SearchController{
@@ -81,13 +82,27 @@ func NewSearchController(storage Storage) *SearchController {
}
}
-func (sc *SearchController) PerformedSearches() []PerformedSearch {
+func (sc *SearchController) GetPerformedSearches() []PerformedSearch {
sc.mutex.Lock()
defer sc.mutex.Unlock()
return sc.performedSearches
}
+func (sc *SearchController) GetPerformedSearch(id RowID) PerformedSearch {
+ sc.mutex.Lock()
+ defer sc.mutex.Unlock()
+
+ var performedSearch PerformedSearch
+ for _, search := range sc.performedSearches {
+ if search.ID == id {
+ performedSearch = search
+ }
+ }
+
+ return performedSearch
+}
+
func (sc *SearchController) PerformSearch(c context.Context, options SearchOptions) PerformedSearch {
findQuery := sc.storage.Find(ConnectionStreams).Projection(OrderedDocument{{"connection_id", 1}}).Context(c)
timeout := options.Timeout * secondsToNano
@@ -163,6 +178,9 @@ func (sc *SearchController) PerformSearch(c context.Context, options SearchOptio
sc.mutex.Lock()
sc.performedSearches = append([]PerformedSearch{performedSearch}, sc.performedSearches...)
+ if len(sc.performedSearches) > maxRecentSearches {
+ sc.performedSearches = sc.performedSearches[:200]
+ }
sc.mutex.Unlock()
return performedSearch
diff --git a/storage.go b/storage.go
index 304e88c..8505bfe 100644
--- a/storage.go
+++ b/storage.go
@@ -77,7 +77,7 @@ func NewMongoStorage(uri string, port int, database string) (*MongoStorage, erro
ConnectionStreams: db.Collection(ConnectionStreams),
ImportingSessions: db.Collection(ImportingSessions),
Rules: db.Collection(Rules),
- Searches: db.Collection(Services),
+ Searches: db.Collection(Searches),
Settings: db.Collection(Settings),
Services: db.Collection(Services),
Statistics: db.Collection(Statistics),