forked from cyfdecyf/cow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pac.go
154 lines (135 loc) · 3.28 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
152
153
154
package main
import (
"bytes"
"fmt"
"net"
"os"
"strings"
"sync"
"text/template"
"time"
)
var pac struct {
template *template.Template
topLevelDomain string
directList *string // use pointer to guarantee atomic update
updated time.Time
lock sync.Mutex
}
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) {
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 (hostIsIP(host) || directAcc[host] || directAcc[host2domain(host)]) ? direct : httpProxy;
};
`
var err error
pac.template, err = template.New("pac").Parse(pacRawTmpl)
if err != nil {
fmt.Println("Internal error on generating pac file template:", err)
os.Exit(1)
}
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")
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() {
s := strings.Join(siteStat.GetDirectList(), "\",\n\"")
pac.directList = &s
go func() {
for {
time.Sleep(10 * time.Minute)
s = strings.Join(siteStat.GetDirectList(), "\",\n\"")
pac.directList = &s
}
}()
}
func sendPAC(c *clientConn) {
if _, err := c.Write(genPAC(c)); err != nil {
debug.Println("Error sending PAC file")
return
}
}