├── .gitignore ├── rule.lua ├── display_block.go ├── restrict.go ├── request_utils.go ├── LICENSE ├── connection_utils.go ├── main.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled object files, static and dynamic libs 2 | reqstrict 3 | -------------------------------------------------------------------------------- /rule.lua: -------------------------------------------------------------------------------- 1 | function filter (request) 2 | if string.find(request,"google") then 3 | return false 4 | else 5 | return true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /display_block.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const displayBlockedMessage = ` 4 | 5 | Proxy prevented this page from loading 6 | 10 | 11 | 12 |

Proxy prevented this page from loading...

13 |
14 | ` 15 | -------------------------------------------------------------------------------- /restrict.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func filter(configFile string, request string) string { 8 | L := lua.NewState() 9 | defer L.Close() 10 | if err := L.DoFile(configFile); err != nil { 11 | panic(err) 12 | } 13 | 14 | if err := L.CallByParam(lua.P{ 15 | Fn: L.GetGlobal("filter"), 16 | NRet: 1, 17 | Protect: true, 18 | }, lua.LString(request)); err != nil { 19 | panic(err) 20 | } 21 | ret := L.Get(-1) 22 | L.Pop(1) 23 | return ret.String() 24 | } 25 | -------------------------------------------------------------------------------- /request_utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net" 7 | "strings" 8 | ) 9 | 10 | func getHostName(request string) string { 11 | hostname := strings.Split((strings.Split(request, "\r\n")[1]), ": ")[1] 12 | return hostname 13 | } 14 | 15 | func modifyRequest(request string) string { 16 | modifiedRequest := strings.Replace(request, "HTTP/1.1", "HTTP/1.0", -1) 17 | return strings.Replace(modifiedRequest, "Connection: keep-alive", "", -1) 18 | } 19 | 20 | func readRequest(connection net.Conn) (string, string) { 21 | buf := make([]byte, 0) 22 | temp := make([]byte, 256) 23 | for { 24 | n, err := connection.Read(temp) 25 | if err != nil { 26 | if err != io.EOF { 27 | log.Println("Read error:", err) 28 | } 29 | break 30 | } 31 | buf = append(buf, temp[:n]...) 32 | if strings.HasSuffix(string(buf[:]), "\r\n\r\n") { 33 | break 34 | } 35 | } 36 | if len(buf) != 0 { 37 | request := string(buf[:]) 38 | return modifyRequest(request), getHostName(request) 39 | } 40 | return "", "" 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 vinay8494 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /connection_utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | ) 7 | 8 | func copyData(from net.Conn, to net.Conn) { 9 | var bytes []byte = make([]byte, 256) 10 | for { 11 | read, err := from.Read(bytes) 12 | if err != nil { 13 | break 14 | } 15 | _, err = to.Write(bytes[:read]) 16 | if err != nil { 17 | break 18 | } 19 | } 20 | } 21 | 22 | func matchConnections(waiting chan net.Conn, spaces chan bool) { 23 | for connection := range waiting { 24 | <-spaces 25 | go func(connection net.Conn) { 26 | handleConnection(connection) 27 | spaces <- true 28 | }(connection) 29 | } 30 | } 31 | 32 | func handleConnection(connection net.Conn) { 33 | defer connection.Close() 34 | 35 | request, hostname := readRequest(connection) 36 | 37 | if filter(scriptFile, request) == "true" { 38 | remote, err := net.Dial("tcp", hostname+":80") 39 | if err != nil { 40 | return 41 | } 42 | defer remote.Close() 43 | 44 | _, err = remote.Write([]byte(request)) 45 | if err != nil { 46 | log.Println(err) 47 | return 48 | } 49 | copyData(remote, connection) 50 | } else { 51 | connection.Write([]byte(displayBlockedMessage)) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net" 7 | "strconv" 8 | ) 9 | 10 | var port int 11 | 12 | func init() { 13 | const ( 14 | defaultPort = 8000 15 | portUsage = "The proxy server's port" 16 | ) 17 | flag.IntVar(&port, "p", defaultPort, portUsage) 18 | } 19 | 20 | var bindAddress string 21 | 22 | func init() { 23 | const ( 24 | defaultBindAddress = "0.0.0.0" 25 | addressUsage = "The proxy server's bind address" 26 | ) 27 | flag.StringVar(&bindAddress, "b", defaultBindAddress, addressUsage) 28 | } 29 | 30 | var maxConnections int 31 | 32 | func init() { 33 | const ( 34 | defaultMaxConn = 20 35 | maxConnUsage = "Maximum connections to proxy" 36 | ) 37 | flag.IntVar(&maxConnections, "m", defaultMaxConn, maxConnUsage) 38 | } 39 | 40 | var scriptFile string 41 | 42 | func init() { 43 | const ( 44 | defaultScriptFile = "rule.lua" 45 | scriptFileUsage = "The path of the lua script file" 46 | ) 47 | flag.StringVar(&scriptFile, "f", defaultScriptFile, scriptFileUsage) 48 | } 49 | 50 | func main() { 51 | flag.Parse() 52 | 53 | host := bindAddress + ":" + strconv.Itoa(port) 54 | server, err := net.Listen("tcp", host) 55 | if err != nil { 56 | log.Fatal("Error occured while listening:", err) 57 | } 58 | log.Println("Proxying on", host) 59 | 60 | waiting := make(chan net.Conn) 61 | spaces := make(chan bool, maxConnections) 62 | 63 | for i := 0; i < maxConnections; i++ { 64 | spaces <- true 65 | } 66 | 67 | go matchConnections(waiting, spaces) 68 | 69 | for { 70 | connection, err := server.Accept() 71 | if err != nil { 72 | log.Println(err) 73 | } else { 74 | waiting <- connection 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reqstrict 2 | 3 | Reqstrict is rule based forward proxy server. It restricts only HTTP (and not HTTPs) requests based on certain rules as configured by the user. 4 | 5 | ## Motivation 6 | Reqstrict is created to ease down the process of configuring proxy server. The user is expected to modify script in order to manage the proxy server. 7 | 8 | ## Installation 9 | ``` 10 | $ git clone https://github.com/gophergala2016/reqstrict.git 11 | $ cd reqstrict 12 | $ go get github.com/yuin/gopher-lua 13 | $ go build 14 | $ ./reqstrict 15 | ``` 16 | 17 | ## Usage 18 | In order to use reqstrict as your proxy server, you first need to configure proxy settings of your browser. You can do it by following steps for [Chrome](https://support.google.com/chrome/answer/96815?hl=en) and [Firefox](http://www.wikihow.com/Enter-Proxy-Settings-in-Firefox) 19 | 20 | ``` 21 | ./reqstrict -b [bindaddress] -p [port] -f [scriptfile] -m [maxconnection] 22 | -b string 23 | The proxy server's bind address (default "0.0.0.0") 24 | -f string 25 | The path of the lua script file (default "rule.lua") 26 | -m int 27 | Maximum connections to proxy server (default 20) 28 | -p int 29 | The proxy server's port (default 8000) 30 | 31 | ``` 32 | You can configure rules in rule.lua 33 | 34 | for example, if you want to block http://www.google.com, then simply put function as : 35 | ``` 36 | function filter (request) 37 | if string.find(request,"google") then 38 | return false 39 | else 40 | return true 41 | end 42 | end 43 | ``` 44 | Similarly you can block requests coming from certain User-Agents also. 45 | 46 | Note : Do not change the function name or return type (true or false). 47 | 48 | ## License 49 | Code released under [the MIT license](https://github.com/gophergala2016/reqstrict/blob/master/LICENSE). 50 | --------------------------------------------------------------------------------