Zardoz is a small WFA which tries to learn from the server and client the rules of what to block.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

147 lines
3.3 KiB

package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
)
//Zexpressions is the set of regexp being used by zardoz
var Zexpressions = []string{
`[[:alpha:]]{4,32}`, // alpha digit token
`[ ]([A-Za-z0-9-_]{4,}\.)+\w+`, // domain name
`[ ]/[A-Za-z0-9-_/.]{4,}[ ]`, // URI path (also partial)
`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}`, // IP address
`[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
}
func passAndLearn(resp *http.Response) error {
ProxyFlow.response = resp
ProxyFlow.seniority++
req := ProxyFlow.request
switch {
case isAuth(resp):
log.Println("401: We don't want to store credentials")
case isError(resp):
buf := bytes.NewBufferString(BlockMessage)
resp.Body = ioutil.NopCloser(buf)
resp.Status = "403 Forbidden"
resp.StatusCode = 403
resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())}
resp.Header.Set("Content-Encoding", "none")
resp.Header.Set("Cache-Control", "no-cache, no-store")
log.Println("Filing inside bad class")
feedRequest(req, "BAD")
ControPlane.StatsTokens <- "DOWNGRADE"
case isSuccess(resp):
log.Println("Filing inside Good Class: ", resp.StatusCode)
feedRequest(req, "GOOD")
}
return nil
}
func blockAndlearn(resp *http.Response) error {
ProxyFlow.response = resp
ProxyFlow.seniority++
req := ProxyFlow.request
buf := bytes.NewBufferString(BlockMessage)
resp.Body = ioutil.NopCloser(buf)
resp.Status = "403 Forbidden"
resp.StatusCode = 403
resp.Header["Content-Length"] = []string{fmt.Sprint(buf.Len())}
resp.Header.Set("Content-Encoding", "none")
resp.Header.Set("Cache-Control", "no-cache, no-store")
switch {
case isAuth(resp):
log.Println("401: We don't want to store credentials")
case isError(resp):
log.Println("Filing inside bad class")
feedRequest(req, "BAD")
case isSuccess(resp):
log.Println("Filing inside Good Class: ", resp.StatusCode)
ControPlane.StatsTokens <- "UPGRADED"
feedRequest(req, "GOOD")
}
return nil
}
func feedRequest(req *http.Request, class string) {
feed := SourceIP(req)
// feed := formatRequest(req)
if class == "BAD" {
log.Println("Feeding BAD token: ", feed)
ControPlane.BadTokens <- feed
}
if class == "GOOD" {
log.Println("Feeding GOOD Token:", feed)
ControPlane.GoodTokens <- feed
}
}
//Unique returns unique elements in the string
func Unique(slice []string) []string {
// create a map with all the values as key
uniqMap := make(map[string]struct{})
for _, v := range slice {
uniqMap[v] = struct{}{}
}
// turn the map keys into a slice
uniqSlice := make([]string, 0, len(uniqMap))
for v := range uniqMap {
uniqSlice = append(uniqSlice, v)
}
return uniqSlice
}
func isSuccess(resp *http.Response) bool {
return resp.StatusCode <= 299
}
func isAuth(resp *http.Response) bool {
return resp.StatusCode == 401
}
func isError(resp *http.Response) bool {
return resp.StatusCode >= 400 && resp.StatusCode != 401
}
//SourceIP returns the source IP of a http call
func SourceIP(req *http.Request) string {
var feed string
if feed = req.Header.Get("X-Forwarded-For"); feed != "" {
log.Println("Got X-Forwarded-For: " + feed)
} else {
feed, _, _ = net.SplitHostPort(req.RemoteAddr)
log.Println("NO X-Forwarded-For, using: "+feed+" from ", req.RemoteAddr)
}
return feed
}