pacparser  1.3.7
About: pacparser is a library to parse Proxy Auto-Config (PAC) files (incl. a pactester).
  Fossies Dox: pacparser-1.3.7.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

pacparser.c
Go to the documentation of this file.
1 // Copyright (C) 2007 Manu Garg.
2 // Author: Manu Garg <manugarg@gmail.com>
3 //
4 // pacparser is a library that provides methods to parse proxy auto-config
5 // (PAC) files. Please read README file included with this package for more
6 // information about this library.
7 //
8 // pacparser is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 3 of the License, or (at your option) any later version.
12 
13 // pacparser is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 
22 #include <errno.h>
23 #include <jsapi.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 
28 #ifdef XP_UNIX
29 #include <unistd.h>
30 #include <sys/socket.h> // for AF_INET
31 #include <netdb.h>
32 #endif
33 
34 #ifdef _WIN32
35 #ifdef __MINGW32__
36 // MinGW enables definition of getaddrinfo et al only if WINVER >= 0x0501.
37 #define WINVER 0x0501
38 #endif
39 #include <winsock2.h>
40 #include <ws2tcpip.h>
41 #endif
42 
43 #include "pac_utils.h"
44 #include "pacparser.h"
45 
46 #define MAX_IP_RESULTS 10
47 
48 static char *myip = NULL;
49 
50 // Default error printer function.
51 static int // Number of characters printed, negative value in case of output error.
52 _default_error_printer(const char *fmt, va_list argp)
53 {
54  return vfprintf(stderr, fmt, argp);
55 }
56 
57 // File level variable to hold error printer function pointer.
59 
60 // Set error printer to a user defined function.
61 void
63 {
64  error_printer_func = func;
65 }
66 
67 static int print_error(const char *fmt, ...)
68 {
69  int ret;
70  va_list args;
71  va_start(args, fmt);
72  ret = (*error_printer_func)(fmt, args);
73  va_end(args);
74  return ret;
75 }
76 
77 static int
78 _debug(void) {
79  if(getenv("PACPARSER_DEBUG")) return 1;
80  return 0;
81 }
82 
83 // Utility function to read a file into string.
84 static char * // File content in string or NULL if failed.
85 read_file_into_str(const char *filename)
86 {
87  char *str;
88  int file_size;
89  FILE *fptr;
90  int records_read;
91  if (!(fptr = fopen(filename, "r"))) goto error1;
92  if ((fseek(fptr, 0L, SEEK_END) != 0)) goto error2;
93  if (!(file_size=ftell(fptr))) goto error2;
94  if ((fseek(fptr, 0L, SEEK_SET) != 0)) goto error2;
95  if (!(str = (char*) malloc(file_size+1))) goto error2;
96  if (!(records_read=fread(str, 1, file_size, fptr))) {
97  free(str);
98  goto error2;
99  }
100  str[records_read] = '\0';
101  fclose(fptr);
102  return str;
103 error2:
104  fclose(fptr);
105 error1:
106  return NULL;
107 }
108 
109 static void
110 print_jserror(JSContext *cx, const char *message, JSErrorReport *report)
111 {
112  print_error("JSERROR: %s:%d:\n %s\n",
113  (report->filename ? report->filename : "NULL"), report->lineno,
114  message);
115 }
116 
117 // DNS Resolve function; used by other routines.
118 // This function is used by dnsResolve, dnsResolveEx, myIpAddress,
119 // myIpAddressEx.
120 static int
121 resolve_host(const char *hostname, char *ipaddr_list, int max_results,
122  int req_ai_family)
123 {
124  struct addrinfo hints;
125  struct addrinfo *result;
126  struct addrinfo *ai;
127  char ipaddr[INET6_ADDRSTRLEN];
128  int error;
129 
130  // Truncate ipaddr_list to an empty string.
131  ipaddr_list[0] = '\0';
132 
133 #ifdef _WIN32
134  // On windows, we need to initialize the winsock dll first.
135  WSADATA WsaData;
136  WSAStartup(MAKEWORD(2,0), &WsaData);
137 #endif
138 
139  memset(&hints, 0, sizeof(struct addrinfo));
140 
141  hints.ai_family = req_ai_family;
142  hints.ai_socktype = SOCK_STREAM;
143 
144  error = getaddrinfo(hostname, NULL, &hints, &result);
145  if (error) return error;
146  int i = 0;
147  for(ai = result; ai != NULL && i < max_results; ai = ai->ai_next, i++) {
148  getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr, sizeof(ipaddr), NULL, 0,
149  NI_NUMERICHOST);
150  if (ipaddr_list[0] == '\0') sprintf(ipaddr_list, "%s", ipaddr);
151  else sprintf(ipaddr_list, "%s;%s", ipaddr_list, ipaddr);
152  }
153  freeaddrinfo(result);
154 #ifdef _WIN32
155  WSACleanup();
156 #endif
157  return 0;
158 }
159 
160 // dnsResolve in JS context; not available in core JavaScript.
161 // returns javascript null if not able to resolve.
162 static JSBool // JS_TRUE or JS_FALSE
163 dns_resolve(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
164 {
165  char* name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
166  char* out;
167  char ipaddr[INET6_ADDRSTRLEN] = "";
168 
169  // Return null on failure.
170  if(resolve_host(name, ipaddr, 1, AF_INET)) {
171  *rval = JSVAL_NULL;
172  return JS_TRUE;
173  }
174 
175  out = JS_malloc(cx, strlen(ipaddr) + 1);
176  strcpy(out, ipaddr);
177  JSString *str = JS_NewString(cx, out, strlen(out));
178  *rval = STRING_TO_JSVAL(str);
179  return JS_TRUE;
180 }
181 
182 // dnsResolveEx in JS context; not available in core JavaScript.
183 // returns javascript null if not able to resolve.
184 static JSBool // JS_TRUE or JS_FALSE
185 dns_resolve_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
186  jsval *rval)
187 {
188  char* name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
189  char* out;
190  char ipaddr[INET6_ADDRSTRLEN * MAX_IP_RESULTS + MAX_IP_RESULTS] = "";
191 
192  out = JS_malloc(cx, strlen(ipaddr) + 1);
193  // Return "" on failure.
194  if(resolve_host(name, ipaddr, MAX_IP_RESULTS, AF_UNSPEC)) {
195  strcpy(out, "");
196  }
197  strcpy(out, ipaddr);
198  JSString *str = JS_NewString(cx, out, strlen(out));
199  *rval = STRING_TO_JSVAL(str);
200  return JS_TRUE;
201 }
202 
203 // myIpAddress in JS context; not available in core JavaScript.
204 // returns 127.0.0.1 if not able to determine local ip.
205 static JSBool // JS_TRUE or JS_FALSE
206 my_ip(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
207 {
208  char ipaddr[INET6_ADDRSTRLEN];
209  char* out;
210 
211  if (myip) // If my (client's) IP address is already set.
212  strcpy(ipaddr, myip);
213  else {
214  char name[256];
215  gethostname(name, sizeof(name));
216  if (resolve_host(name, ipaddr, 1, AF_INET)) {
217  strcpy(ipaddr, "127.0.0.1");
218  }
219  }
220 
221  out = JS_malloc(cx, strlen(ipaddr) + 1);
222  strcpy(out, ipaddr);
223  JSString *str = JS_NewString(cx, out, strlen(out));
224  *rval = STRING_TO_JSVAL(str);
225  return JS_TRUE;
226 }
227 
228 // myIpAddressEx in JS context; not available in core JavaScript.
229 // returns 127.0.0.1 if not able to determine local ip.
230 static JSBool // JS_TRUE or JS_FALSE
231 my_ip_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
232 {
233  char ipaddr[INET6_ADDRSTRLEN * MAX_IP_RESULTS + MAX_IP_RESULTS];
234  char* out;
235 
236  if (myip) // If my (client's) IP address is already set.
237  strcpy(ipaddr, myip);
238  else {
239  char name[256];
240  gethostname(name, sizeof(name));
241  if (resolve_host(name, ipaddr, MAX_IP_RESULTS, AF_UNSPEC)) {
242  strcpy(ipaddr, "");
243  }
244  }
245 
246  out = JS_malloc(cx, strlen(ipaddr) + 1);
247  strcpy(out, ipaddr);
248  JSString *str = JS_NewString(cx, out, strlen(out));
249  *rval = STRING_TO_JSVAL(str);
250  return JS_TRUE;
251 }
252 
253 // Define some JS context related variables.
254 static JSRuntime *rt = NULL;
255 static JSContext *cx = NULL;
256 static JSObject *global = NULL;
257 static JSClass global_class = {
258  "global",0,
259  JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
260  JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
261 };
262 
263 // Set my (client's) IP address to a custom value.
264 void
265 pacparser_setmyip(const char *ip)
266 {
267  myip = malloc(strlen(ip) +1); // Allocate space just to be sure.
268  strcpy(myip, ip);
269 }
270 
271 // Decprecated: This function doesn't do anything.
272 //
273 // This function doesn't do anything. Microsoft exntensions are now enabled by
274 // default.
275 void
277 {
278  return;
279 }
280 
281 // Initialize PAC parser.
282 //
283 // - Initializes JavaScript engine,
284 // - Exports dns_functions (defined above) to JavaScript context.
285 // - Sets error reporting function to print_jserror,
286 // - Evaluates JavaScript code in pacUtils variable defined in pac_utils.h.
287 int // 0 (=Failure) or 1 (=Success)
289 {
290  jsval rval;
291  char *error_prefix = "pacparser.c: pacparser_init:";
292  // Initialize JS engine
293  if (!(rt = JS_NewRuntime(8L * 1024L * 1024L)) ||
294  !(cx = JS_NewContext(rt, 8192)) ||
295  !(global = JS_NewObject(cx, &global_class, NULL, NULL)) ||
296  !JS_InitStandardClasses(cx, global)) {
297  print_error("%s %s\n", error_prefix, "Could not initialize JavaScript "
298  "runtime.");
299  return 0;
300  }
301  JS_SetErrorReporter(cx, print_jserror);
302  // Export our functions to Javascript engine
303  if (!JS_DefineFunction(cx, global, "dnsResolve", dns_resolve, 1, 0)) {
304  print_error("%s %s\n", error_prefix,
305  "Could not define dnsResolve in JS context.");
306  return 0;
307  }
308  if (!JS_DefineFunction(cx, global, "myIpAddress", my_ip, 0, 0)) {
309  print_error("%s %s\n", error_prefix,
310  "Could not define myIpAddress in JS context.");
311  return 0;
312  }
313  if (!JS_DefineFunction(cx, global, "dnsResolveEx", dns_resolve_ex, 1, 0)) {
314  print_error("%s %s\n", error_prefix,
315  "Could not define dnsResolveEx in JS context.");
316  return 0;
317  }
318  if (!JS_DefineFunction(cx, global, "myIpAddressEx", my_ip_ex, 0, 0)) {
319  print_error("%s %s\n", error_prefix,
320  "Could not define myIpAddressEx in JS context.");
321  return 0;
322  }
323  // Evaluate pacUtils. Utility functions required to parse pac files.
324  if (!JS_EvaluateScript(cx, // JS engine context
325  global, // global object
326  pacUtils, // this is defined in pac_utils.h
327  strlen(pacUtils),
328  NULL, // filename (NULL in this case)
329  1, // line number, used for reporting.
330  &rval)) {
331  print_error("%s %s\n", error_prefix,
332  "Could not evaluate pacUtils defined in pac_utils.h.");
333  return 0;
334  }
335  if (_debug()) print_error("DEBUG: Pacparser Initalized.\n");
336  return 1;
337 }
338 
339 // Parses the given PAC script string.
340 //
341 // Evaulates the given PAC script string in the JavaScript context created
342 // by pacparser_init.
343 int // 0 (=Failure) or 1 (=Success)
344 pacparser_parse_pac_string(const char *script)
345 {
346  jsval rval;
347  char *error_prefix = "pacparser.c: pacparser_parse_pac_string:";
348  if (cx == NULL || global == NULL) {
349  print_error("%s %s\n", error_prefix, "Pac parser is not initialized.");
350  return 0;
351  }
352  if (!JS_EvaluateScript(cx,
353  global,
354  script, // Script read from pacfile
355  strlen(script),
356  "PAC script",
357  1,
358  &rval)) { // If script evaluation failed
359  print_error("%s %s\n", error_prefix, "Failed to evaluate the pac script.");
360  if (_debug()) print_error("DEBUG: Failed to parse the PAC script:\n%s\n",
361  script);
362  return 0;
363  }
364  if (_debug()) print_error("DEBUG: Parsed the PAC script.\n");
365  return 1;
366 }
367 
368 // Parses the given PAC file.
369 //
370 // reads the given PAC file and evaluates it in the JavaScript context created
371 // by pacparser_init.
372 int // 0 (=Failure) or 1 (=Success)
373 pacparser_parse_pac_file(const char *pacfile)
374 {
375  char *script = NULL;
376 
377  if ((script = read_file_into_str(pacfile)) == NULL) {
378  print_error("pacparser.c: pacparser_parse_pac: %s: %s: %s\n",
379  "Could not read the pacfile: ", pacfile, strerror(errno));
380  return 0;
381  }
382 
383  int result = pacparser_parse_pac_string(script);
384  if (script != NULL) free(script);
385 
386  if (_debug()) {
387  if(result) print_error("DEBUG: Parsed the PAC file: %s\n", pacfile);
388  else print_error("DEBUG: Could not parse the PAC file: %s\n", pacfile);
389  }
390 
391  return result;
392 }
393 
394 // Parses PAC file (same as pacparser_parse_pac_file)
395 //
396 // (Deprecated) Use pacparser_parse_pac_file instead.
397 int // 0 (=Failure) or 1 (=Success)
398 pacparser_parse_pac(const char *pacfile)
399 {
400  return pacparser_parse_pac_file(pacfile);
401 }
402 
403 // Finds proxy for the given URL and Host.
404 //
405 // If JavaScript engine is intialized and findProxyForURL function is defined,
406 // it evaluates code findProxyForURL(url,host) in JavaScript context and
407 // returns the result.
408 char * // Proxy string or NULL if failed.
409 pacparser_find_proxy(const char *url, const char *host)
410 {
411  char *error_prefix = "pacparser.c: pacparser_find_proxy:";
412  if (_debug()) print_error("DEBUG: Finding proxy for URL: %s and Host:"
413  " %s\n", url, host);
414  jsval rval;
415  char *script;
416  if (url == NULL || (strcmp(url, "") == 0)) {
417  print_error("%s %s\n", error_prefix, "URL not defined");
418  return NULL;
419  }
420  if (host == NULL || (strcmp(host,"") == 0)) {
421  print_error("%s %s\n", error_prefix, "Host not defined");
422  return NULL;
423  }
424  if (cx == NULL || global == NULL) {
425  print_error("%s %s\n", error_prefix, "Pac parser is not initialized.");
426  return NULL;
427  }
428  // Test if findProxyForURL is defined.
429  script = "typeof(findProxyForURL);";
430  if (_debug()) print_error("DEBUG: Executing JavaScript: %s\n", script);
431  JS_EvaluateScript(cx, global, script, strlen(script), NULL, 1, &rval);
432  if (strcmp("function", JS_GetStringBytes(JS_ValueToString(cx, rval))) != 0) {
433  print_error("%s %s\n", error_prefix,
434  "Javascript function findProxyForURL not defined.");
435  return NULL;
436  }
437 
438  // URL-encode "'" as we use single quotes to stick the URL into a temporary script.
439  char *sanitized_url = str_replace(url, "'", "%27");
440  // Hostname shouldn't have single quotes in them
441  if (strchr(host, '\'')) {
442  print_error("%s %s\n", error_prefix,
443  "Invalid hostname: hostname can't have single quotes.");
444  return NULL;
445  }
446 
447  script = (char*) malloc(32 + strlen(url) + strlen(host));
448  script[0] = '\0';
449  strcat(script, "findProxyForURL('");
450  strcat(script, sanitized_url);
451  strcat(script, "', '");
452  strcat(script, host);
453  strcat(script, "')");
454  if (_debug()) print_error("DEBUG: Executing JavaScript: %s\n", script);
455  if (!JS_EvaluateScript(cx, global, script, strlen(script), NULL, 1, &rval)) {
456  print_error("%s %s\n", error_prefix, "Problem in executing findProxyForURL.");
457  free(sanitized_url);
458  free(script);
459  return NULL;
460  }
461  free(sanitized_url);
462  free(script);
463  return JS_GetStringBytes(JS_ValueToString(cx, rval));
464 }
465 
466 // Destroys JavaSctipt Engine.
467 void
469 {
470  // Reinitliaze config variables.
471  myip = NULL;
472  if (cx) {
473  JS_DestroyContext(cx);
474  cx = NULL;
475  }
476  if (rt) {
477  JS_DestroyRuntime(rt);
478  rt = NULL;
479  }
480  if (!cx && !rt) JS_ShutDown();
481  global = NULL;
482  if (_debug()) print_error("DEBUG: Pacparser destroyed.\n");
483 }
484 
485 // Finds proxy for the given PAC file, url and host.
486 //
487 // This function is a wrapper around functions pacparser_init,
488 // pacparser_parse_pac, pacparser_find_proxy and pacparser_cleanup. If you just
489 // want to find out proxy a given set of pac file, url and host, this is the
490 // function to call.
491 char * // Proxy string or NULL if failed.
492 pacparser_just_find_proxy(const char *pacfile,
493  const char *url,
494  const char *host)
495 {
496  char *proxy;
497  char *out;
498  int initialized_here = 0;
499  char *error_prefix = "pacparser.c: pacparser_just_find_proxy:";
500  if (!global) {
501  if (!pacparser_init()) {
502  print_error("%s %s\n", error_prefix, "Could not initialize pacparser");
503  return NULL;
504  }
505  initialized_here = 1;
506  }
507  if (!pacparser_parse_pac(pacfile)) {
508  print_error("%s %s %s\n", error_prefix, "Could not parse pacfile",
509  pacfile);
510  if (initialized_here) pacparser_cleanup();
511  return NULL;
512  }
513  if (!(out = pacparser_find_proxy(url, host))) {
514  print_error("%s %s %s\n", error_prefix,
515  "Could not determine proxy for url", url);
516  if (initialized_here) pacparser_cleanup();
517  return NULL;
518  }
519  proxy = (char*) malloc(strlen(out) + 1);
520  strcpy(proxy, out);
521  if (initialized_here) pacparser_cleanup();
522  return proxy;
523 }
524 
525 #define QUOTEME_(x) #x
526 #define QUOTEME(x) QUOTEME_(x)
527 
528 char* pacparser_version(void) {
529 #ifndef VERSION
530  print_error("WARNING: VERSION not defined.");
531  return "";
532 #endif
533  return QUOTEME(VERSION);
534 }
cx
static JSContext * cx
Definition: pacparser.c:255
pacparser_error_printer
int(* pacparser_error_printer)(const char *fmt, va_list argp)
Type definition for pacparser_error_printer.
Definition: pacparser.h:114
pacUtils
static const char * pacUtils
Definition: pac_utils.h:29
pacparser_parse_pac_file
int pacparser_parse_pac_file(const char *pacfile)
Parses the given PAC file.
Definition: pacparser.c:373
my_ip_ex
static JSBool my_ip_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
Definition: pacparser.c:231
pacparser_version
char * pacparser_version(void)
Returns pacparser version.
Definition: pacparser.c:528
pacparser_enable_microsoft_extensions
void pacparser_enable_microsoft_extensions()
(Deprecated) Enable Microsoft IPv6 PAC extensions.
Definition: pacparser.c:276
str_replace
char * str_replace(const char *orig, char *rep, char *with)
Definition: pac_utils.h:338
MAX_IP_RESULTS
#define MAX_IP_RESULTS
Definition: pacparser.c:46
global_class
static JSClass global_class
Definition: pacparser.c:257
my_ip
static JSBool my_ip(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
Definition: pacparser.c:206
pacparser_parse_pac
int pacparser_parse_pac(const char *pacfile)
Parses the gievn pac file.
Definition: pacparser.c:398
_default_error_printer
static int _default_error_printer(const char *fmt, va_list argp)
Definition: pacparser.c:52
error_printer_func
static pacparser_error_printer error_printer_func
Definition: pacparser.c:58
pacparser_setmyip
void pacparser_setmyip(const char *ip)
Sets my IP address.
Definition: pacparser.c:265
pacparser_set_error_printer
void pacparser_set_error_printer(pacparser_error_printer func)
Sets error printing function.
Definition: pacparser.c:62
pacparser.h
dns_resolve
static JSBool dns_resolve(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
Definition: pacparser.c:163
pacparser_init
int pacparser_init()
Initializes pac parser.
Definition: pacparser.c:288
rt
static JSRuntime * rt
Definition: pacparser.c:254
dns_resolve_ex
static JSBool dns_resolve_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
Definition: pacparser.c:185
read_file_into_str
static char * read_file_into_str(const char *filename)
Definition: pacparser.c:85
pacparser_cleanup
void pacparser_cleanup()
Destroys JavaSctipt context.
Definition: pacparser.c:468
print_error
static int print_error(const char *fmt,...)
Definition: pacparser.c:67
pacparser_find_proxy
char * pacparser_find_proxy(const char *url, const char *host)
Finds proxy for the given URL and Host.
Definition: pacparser.c:409
resolve_host
static int resolve_host(const char *hostname, char *ipaddr_list, int max_results, int req_ai_family)
Definition: pacparser.c:121
myip
static char * myip
Definition: pacparser.c:48
pacparser_parse_pac_string
int pacparser_parse_pac_string(const char *script)
Parses the given PAC script string.
Definition: pacparser.c:344
pacparser_just_find_proxy
char * pacparser_just_find_proxy(const char *pacfile, const char *url, const char *host)
Finds proxy for the given PAC file, URL and Host.
Definition: pacparser.c:492
global
static JSObject * global
Definition: pacparser.c:256
pac_utils.h
_debug
static int _debug(void)
Definition: pacparser.c:78
QUOTEME
#define QUOTEME(x)
Definition: pacparser.c:526
print_jserror
static void print_jserror(JSContext *cx, const char *message, JSErrorReport *report)
Definition: pacparser.c:110