events_stream.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. package events_stream
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strconv"
  7. "sync"
  8. "time"
  9. "github.com/bettercap/bettercap/session"
  10. "github.com/evilsocket/islazy/fs"
  11. "github.com/evilsocket/islazy/str"
  12. "github.com/evilsocket/islazy/tui"
  13. )
  14. type rotation struct {
  15. sync.Mutex
  16. Enabled bool
  17. Compress bool
  18. Format string
  19. How string
  20. Period float64
  21. }
  22. type EventsStream struct {
  23. session.SessionModule
  24. timeFormat string
  25. outputName string
  26. output io.Writer
  27. rotation rotation
  28. triggerList *TriggerList
  29. waitFor string
  30. waitChan chan *session.Event
  31. eventListener <-chan session.Event
  32. quit chan bool
  33. dumpHttpReqs bool
  34. dumpHttpResp bool
  35. dumpFormatHex bool
  36. }
  37. func NewEventsStream(s *session.Session) *EventsStream {
  38. mod := &EventsStream{
  39. SessionModule: session.NewSessionModule("events.stream", s),
  40. output: os.Stdout,
  41. timeFormat: "15:04:05",
  42. quit: make(chan bool),
  43. waitChan: make(chan *session.Event),
  44. waitFor: "",
  45. triggerList: NewTriggerList(),
  46. }
  47. mod.State.Store("ignoring", &mod.Session.EventsIgnoreList)
  48. mod.AddHandler(session.NewModuleHandler("events.stream on", "",
  49. "Start events stream.",
  50. func(args []string) error {
  51. return mod.Start()
  52. }))
  53. mod.AddHandler(session.NewModuleHandler("events.stream off", "",
  54. "Stop events stream.",
  55. func(args []string) error {
  56. return mod.Stop()
  57. }))
  58. mod.AddHandler(session.NewModuleHandler("events.show LIMIT?", "events.show(\\s\\d+)?",
  59. "Show events stream.",
  60. func(args []string) error {
  61. limit := -1
  62. if len(args) == 1 {
  63. arg := str.Trim(args[0])
  64. limit, _ = strconv.Atoi(arg)
  65. }
  66. return mod.Show(limit)
  67. }))
  68. on := session.NewModuleHandler("events.on TAG COMMANDS", `events\.on ([^\s]+) (.+)`,
  69. "Run COMMANDS when an event with the specified TAG is triggered.",
  70. func(args []string) error {
  71. return mod.addTrigger(args[0], args[1])
  72. })
  73. on.Complete("events.on", s.EventsCompleter)
  74. mod.AddHandler(on)
  75. mod.AddHandler(session.NewModuleHandler("events.triggers", "",
  76. "Show the list of event triggers created by the events.on command.",
  77. func(args []string) error {
  78. return mod.showTriggers()
  79. }))
  80. onClear := session.NewModuleHandler("events.trigger.delete TRIGGER_ID", `events\.trigger\.delete ([^\s]+)`,
  81. "Remove an event trigger given its TRIGGER_ID (use events.triggers to see the list of triggers).",
  82. func(args []string) error {
  83. return mod.clearTrigger(args[0])
  84. })
  85. onClear.Complete("events.trigger.delete", mod.triggerList.Completer)
  86. mod.AddHandler(onClear)
  87. mod.AddHandler(session.NewModuleHandler("events.triggers.clear", "",
  88. "Remove all event triggers (use events.triggers to see the list of triggers).",
  89. func(args []string) error {
  90. return mod.clearTrigger("")
  91. }))
  92. mod.AddHandler(session.NewModuleHandler("events.waitfor TAG TIMEOUT?", `events.waitfor ([^\s]+)([\s\d]*)`,
  93. "Wait for an event with the given tag either forever or for a timeout in seconds.",
  94. func(args []string) error {
  95. tag := args[0]
  96. timeout := 0
  97. if len(args) == 2 {
  98. t := str.Trim(args[1])
  99. if t != "" {
  100. n, err := strconv.Atoi(t)
  101. if err != nil {
  102. return err
  103. }
  104. timeout = n
  105. }
  106. }
  107. return mod.startWaitingFor(tag, timeout)
  108. }))
  109. ignore := session.NewModuleHandler("events.ignore FILTER", "events.ignore ([^\\s]+)",
  110. "Events with an identifier matching this filter will not be shown (use multiple times to add more filters).",
  111. func(args []string) error {
  112. return mod.Session.EventsIgnoreList.Add(args[0])
  113. })
  114. ignore.Complete("events.ignore", s.EventsCompleter)
  115. mod.AddHandler(ignore)
  116. include := session.NewModuleHandler("events.include FILTER", "events.include ([^\\s]+)",
  117. "Used to remove filters passed with the events.ignore command.",
  118. func(args []string) error {
  119. return mod.Session.EventsIgnoreList.Remove(args[0])
  120. })
  121. include.Complete("events.include", s.EventsCompleter)
  122. mod.AddHandler(include)
  123. mod.AddHandler(session.NewModuleHandler("events.filters", "",
  124. "Print the list of filters used to ignore events.",
  125. func(args []string) error {
  126. if mod.Session.EventsIgnoreList.Empty() {
  127. mod.Printf("Ignore filters list is empty.\n")
  128. } else {
  129. mod.Session.EventsIgnoreList.RLock()
  130. defer mod.Session.EventsIgnoreList.RUnlock()
  131. for _, filter := range mod.Session.EventsIgnoreList.Filters() {
  132. mod.Printf(" '%s'\n", string(filter))
  133. }
  134. }
  135. return nil
  136. }))
  137. mod.AddHandler(session.NewModuleHandler("events.filters.clear", "",
  138. "Clear the list of filters passed with the events.ignore command.",
  139. func(args []string) error {
  140. mod.Session.EventsIgnoreList.Clear()
  141. return nil
  142. }))
  143. mod.AddHandler(session.NewModuleHandler("events.clear", "",
  144. "Clear events stream.",
  145. func(args []string) error {
  146. mod.Session.Events.Clear()
  147. return nil
  148. }))
  149. mod.AddParam(session.NewStringParameter("events.stream.output",
  150. "",
  151. "",
  152. "If not empty, events will be written to this file instead of the standard output."))
  153. mod.AddParam(session.NewStringParameter("events.stream.time.format",
  154. mod.timeFormat,
  155. "",
  156. "Date and time format to use for events reporting."))
  157. mod.AddParam(session.NewBoolParameter("events.stream.output.rotate",
  158. "true",
  159. "If true will enable log rotation."))
  160. mod.AddParam(session.NewBoolParameter("events.stream.output.rotate.compress",
  161. "true",
  162. "If true will enable log rotation compression."))
  163. mod.AddParam(session.NewStringParameter("events.stream.output.rotate.how",
  164. "size",
  165. "(size|time)",
  166. "Rotate by 'size' or 'time'."))
  167. mod.AddParam(session.NewStringParameter("events.stream.output.rotate.format",
  168. "2006-01-02 15:04:05",
  169. "",
  170. "Datetime format to use for log rotation file names."))
  171. mod.AddParam(session.NewDecimalParameter("events.stream.output.rotate.when",
  172. "10",
  173. "File size (in MB) or time duration (in seconds) for log rotation."))
  174. mod.AddParam(session.NewBoolParameter("events.stream.http.request.dump",
  175. "false",
  176. "If true all HTTP requests will be dumped."))
  177. mod.AddParam(session.NewBoolParameter("events.stream.http.response.dump",
  178. "false",
  179. "If true all HTTP responses will be dumped."))
  180. mod.AddParam(session.NewBoolParameter("events.stream.http.format.hex",
  181. "true",
  182. "If true dumped HTTP bodies will be in hexadecimal format."))
  183. return mod
  184. }
  185. func (mod *EventsStream) Name() string {
  186. return "events.stream"
  187. }
  188. func (mod *EventsStream) Description() string {
  189. return "Print events as a continuous stream."
  190. }
  191. func (mod *EventsStream) Author() string {
  192. return "Simone Margaritelli <evilsocket@gmail.com>"
  193. }
  194. func (mod *EventsStream) Configure() (err error) {
  195. var output string
  196. if err, output = mod.StringParam("events.stream.output"); err == nil {
  197. if output == "" {
  198. mod.output = os.Stdout
  199. } else if mod.outputName, err = fs.Expand(output); err == nil {
  200. mod.output, err = os.OpenFile(mod.outputName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  201. if err != nil {
  202. return err
  203. }
  204. }
  205. }
  206. if err, mod.rotation.Enabled = mod.BoolParam("events.stream.output.rotate"); err != nil {
  207. return err
  208. } else if err, mod.timeFormat = mod.StringParam("events.stream.time.format"); err != nil {
  209. return err
  210. } else if err, mod.rotation.Compress = mod.BoolParam("events.stream.output.rotate.compress"); err != nil {
  211. return err
  212. } else if err, mod.rotation.Format = mod.StringParam("events.stream.output.rotate.format"); err != nil {
  213. return err
  214. } else if err, mod.rotation.How = mod.StringParam("events.stream.output.rotate.how"); err != nil {
  215. return err
  216. } else if err, mod.rotation.Period = mod.DecParam("events.stream.output.rotate.when"); err != nil {
  217. return err
  218. }
  219. if err, mod.dumpHttpReqs = mod.BoolParam("events.stream.http.request.dump"); err != nil {
  220. return err
  221. } else if err, mod.dumpHttpResp = mod.BoolParam("events.stream.http.response.dump"); err != nil {
  222. return err
  223. } else if err, mod.dumpFormatHex = mod.BoolParam("events.stream.http.format.hex"); err != nil {
  224. return err
  225. }
  226. return err
  227. }
  228. func (mod *EventsStream) Start() error {
  229. if err := mod.Configure(); err != nil {
  230. return err
  231. }
  232. return mod.SetRunning(true, func() {
  233. mod.eventListener = mod.Session.Events.Listen()
  234. defer mod.Session.Events.Unlisten(mod.eventListener)
  235. for {
  236. var e session.Event
  237. select {
  238. case e = <-mod.eventListener:
  239. if e.Tag == mod.waitFor {
  240. mod.waitFor = ""
  241. mod.waitChan <- &e
  242. }
  243. if !mod.Session.EventsIgnoreList.Ignored(e) {
  244. mod.View(e, true)
  245. }
  246. // this could generate sys.log events and lock the whole
  247. // events.stream, make it async
  248. go mod.dispatchTriggers(e)
  249. case <-mod.quit:
  250. return
  251. }
  252. }
  253. })
  254. }
  255. func (mod *EventsStream) Show(limit int) error {
  256. events := mod.Session.Events.Sorted()
  257. num := len(events)
  258. selected := []session.Event{}
  259. for i := range events {
  260. e := events[num-1-i]
  261. if !mod.Session.EventsIgnoreList.Ignored(e) {
  262. selected = append(selected, e)
  263. if len(selected) == limit {
  264. break
  265. }
  266. }
  267. }
  268. if numSelected := len(selected); numSelected > 0 {
  269. mod.Printf("\n")
  270. for i := range selected {
  271. mod.View(selected[numSelected-1-i], false)
  272. }
  273. mod.Session.Refresh()
  274. }
  275. return nil
  276. }
  277. func (mod *EventsStream) startWaitingFor(tag string, timeout int) error {
  278. if timeout == 0 {
  279. mod.Info("waiting for event %s ...", tui.Green(tag))
  280. } else {
  281. mod.Info("waiting for event %s for %d seconds ...", tui.Green(tag), timeout)
  282. go func() {
  283. time.Sleep(time.Duration(timeout) * time.Second)
  284. mod.waitFor = ""
  285. mod.waitChan <- nil
  286. }()
  287. }
  288. mod.waitFor = tag
  289. event := <-mod.waitChan
  290. if event == nil {
  291. return fmt.Errorf("'events.waitFor %s %d' timed out.", tag, timeout)
  292. } else {
  293. mod.Debug("got event: %v", event)
  294. }
  295. return nil
  296. }
  297. func (mod *EventsStream) Stop() error {
  298. return mod.SetRunning(false, func() {
  299. mod.quit <- true
  300. if mod.output != os.Stdout {
  301. if fp, ok := mod.output.(*os.File); ok {
  302. fp.Close()
  303. }
  304. }
  305. })
  306. }