forked from cyfdecyf/cow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpac.go
140 lines (124 loc) · 3.21 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
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"text/template"
)
var pac struct {
template *template.Template
topLevelDomain string
proxyServerAddr string
}
func init() {
const pacRawTmpl = `var direct = 'DIRECT';
var httpProxy = '{{.ProxyAddr}}';
var directList = [
"localhost",
"0.1"{{.DirectDomains}}
];
var directAcc = {};
for (var i = 0; i < directList.length; i += 1) {
directAcc[directList[i]] = true;
}
var topLevel = {
{{.TopLevel}}
};
function host2domain(host) {
var lastDot = host.lastIndexOf(".");
if (lastDot === -1)
return host;
// 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[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")
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
}
func initProxyServerAddr() {
listen, port := splitHostPort(config.listenAddr)
if listen == "0.0.0.0" {
addrs, err := hostIP()
if err != nil {
errl.Println("Either change listen address to specific IP, or correct your host network settings.")
os.Exit(1)
}
for _, ip := range addrs {
pac.proxyServerAddr += fmt.Sprintf("PROXY %s:%s; ", ip, port)
}
pac.proxyServerAddr += "DIRECT"
info.Printf("proxy listen address is %s, PAC will have proxy address: %s\n",
config.listenAddr, pac.proxyServerAddr)
} else {
pac.proxyServerAddr = fmt.Sprintf("PROXY %s; DIRECT", config.listenAddr)
}
}
// 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")
var pacDirect = []byte("function FindProxyForURL(url, host) { return 'DIRECT'; };")
func sendPAC(w io.Writer) {
// domains in PAC file needs double quote
ds1 := strings.Join(alwaysDirectDs.toSlice(), "\",\n\"")
ds2 := strings.Join(directDs.toSlice(), "\",\n\"")
var ds string
if ds1 == "" {
ds = ds2
} else if ds2 == "" {
ds = ds1
} else {
ds = ds1 + "\",\n\"" + ds2
}
if ds == "" {
// Empty direct domain list
w.Write(pacHeader)
w.Write(pacDirect)
return
}
data := struct {
ProxyAddr string
DirectDomains string
TopLevel string
}{
pac.proxyServerAddr,
",\n\"" + ds + "\"",
pac.topLevelDomain,
}
if _, err := w.Write(pacHeader); err != nil {
debug.Println("Error writing pac header")
return
}
// debug.Println("direct:", data.DirectDomains)
buf := new(bytes.Buffer)
if err := pac.template.Execute(buf, data); err != nil {
errl.Println("Error generating pac file:", err)
}
if _, err := w.Write(buf.Bytes()); err != nil {
debug.Println("Error writing pac content:", err)
}
}