-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpacparser.go
214 lines (198 loc) · 5.65 KB
/
pacparser.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package pacparser
// go-pacparser - golang bindings for pacparser library
import (
"errors"
"net"
"os"
"strings"
"unsafe"
)
// #cgo LDFLAGS: -lpacparser
// #include <stdarg.h>
// #include <stdio.h>
// #include <strings.h>
// #include <pacparser.h>
// #include <stdlib.h>
// static char lastError[2048] = "";
// static int bufferPosition = 0;
//
// int bufferErrors(const char *fmt, va_list argp) {
// bufferPosition += vsnprintf(lastError+bufferPosition,
// sizeof(lastError)-bufferPosition, fmt, argp);
// return bufferPosition;
// }
//
// char *getLastError() {
// return (char *)lastError;
// }
//
// void resetLastError() {
// bufferPosition = 0;
// lastError[0] = '\0';
// }
//
import "C"
// MaxConcurrency maximum pending requests
const MaxConcurrency = 100
// ParserInstance is the core package instance that stores information
// for all functions contained within the package
type ParserInstance struct {
pac string // pac file body
myip string // returned by myIpAddress() javascript function
err error // last instance error
}
// Unexported common response struct
type parserResponse struct {
status bool // translated error from pacparser
proxy string // response from FindProxyForURL
err error // last request error
}
// Unexported parse request struct
type parsePacRequest struct {
inst *ParserInstance
resp chan *parserResponse
}
// Unexported findProxy request struct
type findProxyRequest struct {
inst *ParserInstance
url string // url argument to FindProxyForURL
host string // host argument to FindProxyForURL
resp chan *parserResponse
}
// internal variables
var parsePacChannel chan *parsePacRequest
var findProxyChannel chan *findProxyRequest
var myIpDefault string
// Process upstream error responses
func getLastError() error {
var lines []string // error lines
// pull and trim upstream error string
str := strings.TrimSpace(C.GoString(C.getLastError()))
// check string
if str == "" {
return nil
}
// reset upstream error buffer
C.resetLastError()
// split upstream message on newline
for _, l := range strings.Split(str, "\n") {
var split []string // temp split holder
// remove library prefixes
if strings.HasPrefix(strings.TrimSpace(l), "pacparser.c: ") {
split = strings.SplitN(l, ": ", 3)
if len(split) == 3 {
l = split[2]
}
}
// append lines and standardize output
lines = append(lines, strings.TrimSuffix(strings.TrimSpace(l), "."))
}
// check length - remove last line
if len(lines) > 1 {
lines = lines[:len(lines)-1]
}
// rejoin and return as error
return errors.New(strings.Join(lines, " "))
}
// Handler to ensure only one active request to the underlying library
func parseHandler() {
// event loop
for {
select {
// handle parse requests
case req := <-parsePacChannel:
// initialize pacparser library context
C.pacparser_init()
// deprecated function in newer library versions
// and simply returns without taking any action
C.pacparser_enable_microsoft_extensions()
// build response
resp := new(parserResponse)
// parse pac contents and set error
// upstream function returns 1 on success and 0 on failure
cstrPac := C.CString(req.inst.pac)
resp.status = (int(C.pacparser_parse_pac_string(cstrPac)) != 0)
C.free(unsafe.Pointer(cstrPac))
// set error
resp.err = getLastError()
// cleanup library context
C.pacparser_cleanup()
// send response
req.resp <- resp
// handle find requests
case req := <-findProxyChannel:
// initialize pacparser library context
C.pacparser_init()
// deprecated function in newer library versions
// and simply returns without taking any action
C.pacparser_enable_microsoft_extensions()
// build response
resp := new(parserResponse)
// parse pac contents to ensure we are using the right body
// upstream function returns 1 on success and 0 on failure
cstrPac := C.CString(req.inst.pac)
resp.status = (int(C.pacparser_parse_pac_string(cstrPac)) != 0)
C.free(unsafe.Pointer(cstrPac))
// set error
resp.err = getLastError()
// check response
if resp.status && resp.err == nil {
// set ip
cstrIP := C.CString(req.inst.myip)
C.pacparser_setmyip(cstrIP)
// find proxy
cstrUrl := C.CString(req.url)
cstrHost := C.CString(req.host)
resp.proxy = C.GoString(C.pacparser_find_proxy(cstrUrl, cstrHost))
C.free(unsafe.Pointer(cstrIP))
C.free(unsafe.Pointer(cstrUrl))
C.free(unsafe.Pointer(cstrHost))
// set error
resp.err = getLastError()
// workaround for sometimes empty values
if resp.proxy == "" {
resp.proxy = "undefined"
}
// handle undefined proxy returns
if resp.proxy == "undefined" {
resp.status = false
// set error if empty
if resp.err == nil {
resp.err = errors.New("Invalid proxy returned")
}
}
}
// cleanup library context
C.pacparser_cleanup()
// send response
req.resp <- resp
}
}
}
// Initialize base parser library and start handler
func init() {
// set error handler
C.pacparser_set_error_printer(C.pacparser_error_printer(C.bufferErrors))
// build channels
parsePacChannel = make(chan *parsePacRequest, 100)
findProxyChannel = make(chan *findProxyRequest, 100)
// set default ip
myIpDefault = "127.0.0.1"
// attempt to find local hostname
if host, err := os.Hostname(); err == nil {
// attempt to resolve returned host
if addrs, err := net.LookupIP(host); err == nil {
// loop over resolved addresses
for _, addr := range addrs {
if !addr.IsLoopback() {
// set default ip address
myIpDefault = addr.String()
// break after first valid address
break
}
}
}
}
// spawn handler
go parseHandler()
}