forked from cyfdecyf/cow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pac.go
151 lines (132 loc) · 3.27 KB
/
pac.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main
import (
"bytes"
"fmt"
"net"
"strings"
"text/template"
"time"
)
var pac struct {
template *template.Template
topLevelDomain string
directList string
}
func init() {
const pacRawTmpl = `var direct = 'DIRECT';
var httpProxy = 'PROXY {{.ProxyAddr}}; DIRECT';
var directList = [
"",
"{{.DirectDomains}}"
];
var directAcc = {};
for (var i = 0; i < directList.length; i += 1) {
directAcc[directList[i]] = true;
}
var topLevel = {
{{.TopLevel}}
};
// only handles IPv4 address now
function hostIsIP(host) {
var parts = host.split('.');
if (parts.length != 4) {
return false;
}
for (var i = 3; i >= 0; i--) {
if (parts[i].length === 0 || parts[i].length > 3) {
return false;
}
var n = Number(parts[i]);
if (isNaN(n) || n < 0 || n > 255) {
return false;
}
}
return true;
}
function host2Domain(host) {
if (hostIsIP(host)) {
return ""; // IP address has no domain
}
var lastDot = host.lastIndexOf('.');
if (lastDot === -1) {
return ""; // simple host name has no domain
}
// Find the second last dot
dot2ndLast = host.lastIndexOf(".", lastDot-1);
if (dot2ndLast === -1)
return host;
var part = host.substring(dot2ndLast+1, lastDot);
if (topLevel[part]) {
var dot3rdLast = host.lastIndexOf(".", dot2ndLast-1);
if (dot3rdLast === -1) {
return host;
}
return host.substring(dot3rdLast+1);
}
return host.substring(dot2ndLast+1);
}
function FindProxyForURL(url, host) {
return (directAcc[host] || directAcc[host2Domain(host)]) ? direct : httpProxy;
}
`
var err error
pac.template, err = template.New("pac").Parse(pacRawTmpl)
if err != nil {
Fatal("Internal error on generating pac file template:", err)
}
var buf bytes.Buffer
for k, _ := range topLevelDomain {
buf.WriteString(fmt.Sprintf("\t\"%s\": true,\n", k))
}
pac.topLevelDomain = buf.String()[:buf.Len()-2] // remove the final comma
}
// No need for content-length as we are closing connection
var pacHeader = []byte("HTTP/1.1 200 OK\r\nServer: cow-proxy\r\n" +
"Content-Type: application/x-ns-proxy-autoconfig\r\nConnection: close\r\n\r\n")
// Different client will have different proxy URL, so generate it upon each request.
func genPAC(c *clientConn) []byte {
buf := new(bytes.Buffer)
proxyAddr := c.proxy.addrInPAC
if proxyAddr == "" {
host, _ := splitHostPort(c.LocalAddr().String())
proxyAddr = net.JoinHostPort(host, c.proxy.port)
}
if pac.directList == "" {
// Empty direct domain list
buf.Write(pacHeader)
pacproxy := fmt.Sprintf("function FindProxyForURL(url, host) { return 'PROXY %s; DIRECT'; };",
proxyAddr)
buf.Write([]byte(pacproxy))
return buf.Bytes()
}
data := struct {
ProxyAddr string
DirectDomains string
TopLevel string
}{
proxyAddr,
pac.directList,
pac.topLevelDomain,
}
buf.Write(pacHeader)
if err := pac.template.Execute(buf, data); err != nil {
errl.Println("Error generating pac file:", err)
panic("Error generating pac file")
}
return buf.Bytes()
}
func initPAC() {
pac.directList = strings.Join(siteStat.GetDirectList(), "\",\n\"")
go func() {
for {
time.Sleep(10 * time.Minute)
pac.directList = strings.Join(siteStat.GetDirectList(), "\",\n\"")
}
}()
}
func sendPAC(c *clientConn) {
if _, err := c.Write(genPAC(c)); err != nil {
debug.Println("Error sending PAC file")
return
}
}