├── .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 |
--------------------------------------------------------------------------------