classifier.go 3.8 KB

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