classifier.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "net/http/httputil"
  9. "regexp"
  10. "strings"
  11. )
  12. //Zregexp is the regular expression used by ZARDOZ to process headers
  13. var Zregexp string
  14. func init() {
  15. var expressions = []string{
  16. `([ ]([A-Za-z0-9-_]{4,}\.)+\w+)`, // domain name
  17. `([ ]/[A-Za-z0-9-_/.]{4,}[ ])`, // URI path (also partial)
  18. `[[:alpha:]]{4,32}`, // alpha digit token
  19. `([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})`, // IP address
  20. `([0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12})`, // UUID
  21. }
  22. Zregexp = strings.Join(expressions, "|")
  23. }
  24. func passAndLearn(resp *http.Response) error {
  25. ProxyFlow.response = resp
  26. ProxyFlow.seniority++
  27. req := ProxyFlow.request
  28. switch {
  29. case isAuth(resp):
  30. log.Println("401: We don't want to store credentials")
  31. case IsError(resp):
  32. buf := bytes.NewBufferString(BlockMessage)
  33. resp.Body = ioutil.NopCloser(buf)
  34. resp.Status = "403 Forbidden"
  35. resp.StatusCode = 403
  36. resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())}
  37. resp.Header.Set("Content-Encoding", "none")
  38. log.Println("Filing inside bad class")
  39. feedRequest(req, "BAD")
  40. ControPlane.StatsTokens <- "DOWNGRADE"
  41. case isSuccess(resp):
  42. log.Println("Filing inside Good Class: ", resp.StatusCode)
  43. feedRequest(req, "GOOD")
  44. }
  45. return nil
  46. }
  47. func blockAndlearn(resp *http.Response) error {
  48. ProxyFlow.response = resp
  49. ProxyFlow.seniority++
  50. req := ProxyFlow.request
  51. buf := bytes.NewBufferString(BlockMessage)
  52. resp.Body = ioutil.NopCloser(buf)
  53. resp.Status = "403 Forbidden"
  54. resp.StatusCode = 403
  55. resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())}
  56. resp.Header.Set("Content-Encoding", "none")
  57. switch {
  58. case isAuth(resp):
  59. log.Println("401: We don't want to store credentials")
  60. case IsError(resp):
  61. log.Println("Filing inside bad class")
  62. feedRequest(req, "BAD")
  63. case isSuccess(resp):
  64. log.Println("Filing inside Good Class: ", resp.StatusCode)
  65. ControPlane.StatsTokens <- "UPGRADED"
  66. feedRequest(req, "GOOD")
  67. }
  68. return nil
  69. }
  70. func sanitizeHeaders(s string) string {
  71. re, rerr := regexp.Compile(Zregexp)
  72. if rerr != nil {
  73. log.Println("Error Compiling regular expression: ", Zregexp)
  74. }
  75. matched := re.FindAllString(s, -1)
  76. uMatched := Unique(matched)
  77. tmpSt := strings.ToLower(strings.Join(uMatched, " "))
  78. log.Println("Matched: " + tmpSt)
  79. return tmpSt
  80. }
  81. func feedRequest(req *http.Request, class string) {
  82. feed := formatRequest(req)
  83. if class == "BAD" {
  84. log.Println("Feeding BAD token: ", feed)
  85. ControPlane.BadTokens <- sanitizeHeaders(feed)
  86. }
  87. if class == "GOOD" {
  88. log.Println("Feeding GOOD Token:", feed)
  89. ControPlane.GoodTokens <- sanitizeHeaders(feed)
  90. }
  91. }
  92. func formatRequest(req *http.Request) string {
  93. ingestBody := req.ContentLength < 2048 && req.ContentLength > 1
  94. log.Println("Ingesting the body: ", ingestBody)
  95. requestDump, err := httputil.DumpRequest(req, ingestBody)
  96. if err != nil {
  97. fmt.Println(err)
  98. }
  99. return fmt.Sprintf("%s\n", requestDump)
  100. }
  101. //Unique returns unique elements in the string
  102. func Unique(slice []string) []string {
  103. // create a map with all the values as key
  104. uniqMap := make(map[string]struct{})
  105. for _, v := range slice {
  106. uniqMap[v] = struct{}{}
  107. }
  108. // turn the map keys into a slice
  109. uniqSlice := make([]string, 0, len(uniqMap))
  110. for v := range uniqMap {
  111. uniqSlice = append(uniqSlice, v)
  112. }
  113. return uniqSlice
  114. }
  115. func isSuccess(resp *http.Response) bool {
  116. return resp.StatusCode <= 299
  117. }
  118. func isAuth(resp *http.Response) bool {
  119. return resp.StatusCode == 401
  120. }
  121. func IsError(resp *http.Response) bool {
  122. return resp.StatusCode >= 400
  123. }