mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
1728 lines
46 KiB
C
1728 lines
46 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/*
|
|
* mkautocf.c: Proxy auto-config parser and evaluator
|
|
* Ari Luotonen
|
|
*
|
|
* Updated and Documented by Judson Valeski 11/19/1997
|
|
*/
|
|
#include "mkutils.h" /* LF */
|
|
#include "mktcp.h"
|
|
#include "netutils.h"
|
|
#include "prsystem.h"
|
|
#include "mkpadpac.h"
|
|
#include "net_xp_file.h"
|
|
#include <time.h>
|
|
#include "mkautocf.h"
|
|
#include "xp_mem.h" /* PR_NEWZAP() */
|
|
#ifndef XP_MAC
|
|
#include <sys/types.h>
|
|
#endif
|
|
#include "libi18n.h"
|
|
#include "mkgeturl.h"
|
|
#include "mkstream.h"
|
|
#include "mkgeturl.h"
|
|
#include "mkformat.h"
|
|
#include "mime.h"
|
|
#include "cvactive.h"
|
|
#include "gui.h"
|
|
#include "msgcom.h"
|
|
#include "prefapi.h"
|
|
#define WANT_ENUM_STRING_IDS
|
|
#include "allxpstr.h"
|
|
#undef WANT_ENUM_STRING_IDS
|
|
#include "xp_regexp.h"
|
|
|
|
#include "prnetdb.h"
|
|
|
|
#include "net.h"
|
|
#include "libmocha.h"
|
|
#include "jsapi.h"
|
|
#include "jscompat.h"
|
|
#include "jspubtd.h"
|
|
#include "jsautocf.h"
|
|
|
|
/* for XP_GetString() */
|
|
#include "xpgetstr.h"
|
|
#ifdef NOT
|
|
extern int XP_BAD_KEYWORD_IN_PROXY_AUTOCFG;
|
|
extern int XP_RETRY_AGAIN_PROXY;
|
|
extern int XP_RETRY_AGAIN_SOCKS;
|
|
extern int XP_RETRY_AGAIN_PROXY_OR_SOCKS;
|
|
extern int XP_PROXY_UNAVAILABLE_TRY_AGAIN;
|
|
extern int XP_ALL_PROXIES_DOWN_TRY_AGAIN;
|
|
extern int XP_ALL_SOCKS_DOWN;
|
|
extern int XP_ALL_DOWN_MIX;
|
|
extern int XP_OVERRIDE_PROXY;
|
|
extern int XP_OVERRIDE_SOCKS;
|
|
extern int XP_OVERRIDE_MIX;
|
|
extern int XP_STILL_OVERRIDE_PROXY;
|
|
extern int XP_STILL_OVERRIDE_SOCKS;
|
|
extern int XP_STILL_OVERRIDE_MIX;
|
|
extern int XP_NO_CONFIG_RECIEVED;
|
|
extern int XP_NO_CONFIG_RECEIVED_NO_FAILOVER;
|
|
extern int XP_EMPTY_CONFIG_USE_PREV;
|
|
extern int XP_BAD_CONFIG_USE_PREV;
|
|
extern int XP_BAD_CONFIG_IGNORED;
|
|
extern int XP_BAD_TYPE_USE_PREV;
|
|
extern int XP_BAD_TYPE_CONFIG_IGNORED;
|
|
extern int XP_CONF_LOAD_FAILED_IGNORED;
|
|
extern int XP_CONF_LOAD_FAILED_NO_FAILOVER;
|
|
extern int XP_CONF_LOAD_FAILED_USE_PREV;
|
|
extern int XP_EVEN_SAVED_IS_BAD;
|
|
extern int XP_CONFIG_LOAD_ABORTED;
|
|
extern int XP_CONFIG_BLAST_WARNING;
|
|
extern int XP_RECEIVING_PROXY_AUTOCFG;
|
|
extern int XP_AUTOADMIN_MISSING;
|
|
#endif
|
|
|
|
#define PACF_MIN_RETRY_AFTER 300
|
|
#define PACF_MIN_RETRY_ASK 25
|
|
#define PACF_AUTO_RETRY_AFTER 1800
|
|
#define PACF_DIRECT_INTERVAL 1200
|
|
#define PACF_RETRY_OK_AFTER 30
|
|
|
|
/* Types of retrievals: retrieve directly, thru a proxy, or using socks. */
|
|
typedef enum _PACF_Type {
|
|
PACF_TYPE_INVALID = 0,
|
|
PACF_TYPE_DIRECT,
|
|
PACF_TYPE_PROXY,
|
|
PACF_TYPE_SOCKS
|
|
} PACF_Type;
|
|
|
|
/* Info about the proxy/socks server hostname/IP address and port.
|
|
* These nodes are kept in a list so we don't have to parse the pac file
|
|
* every time to gather proxy/socks info. */
|
|
typedef struct _PACF_Node {
|
|
PACF_Type type; /* direct | proxy | socks */
|
|
char * addr;
|
|
u_long socks_addr;
|
|
short socks_port;
|
|
int retries;
|
|
time_t retrying;
|
|
time_t last_try;
|
|
time_t down_since;
|
|
} PACF_Node;
|
|
|
|
/* Private object for the proxy config loader stream. */
|
|
typedef struct _PACF_Object {
|
|
MWContext * context;
|
|
int flag;
|
|
} PACF_Object;
|
|
|
|
/* A struct for holding queued state.
|
|
*
|
|
* The state is saved when NET_GetURL() is called for the first time,
|
|
* and the proxy autoconfig retrieve has to be started (and finished)
|
|
* before the first actual document can be loaded.
|
|
*
|
|
* A pre-exit function for the proxy autoconfig URL restarts the
|
|
* original URL retrieve by calling NET_GetURL() again with the
|
|
* same parameters. */
|
|
typedef struct _PACF_QueuedState {
|
|
URL_Struct * URL_s;
|
|
FO_Present_Types output_format;
|
|
MWContext * window_id;
|
|
Net_GetUrlExitFunc* exit_routine;
|
|
} PACF_QueuedState;
|
|
|
|
typedef enum {
|
|
PACF_SECONDARY_URL,
|
|
PACF_BINDINGS
|
|
} pc_slot;
|
|
|
|
/* Declared in mkgeturl.c. NET_GetURL uses these variables to determine
|
|
* whether or not the pac file has been loaded. */
|
|
extern XP_Bool NET_FindProxyInJSC(void);
|
|
extern XP_Bool NET_GlobalAcLoaded;
|
|
extern PRBool NET_ProxyAcLoaded;
|
|
|
|
/* Private proxy auto-config variables */
|
|
PRIVATE Bool pacf_do_failover = TRUE;
|
|
PRIVATE Bool pacf_loading = FALSE;
|
|
PRIVATE Bool pacf_ok = FALSE;
|
|
PRIVATE char * pacf_url = NULL;
|
|
PRIVATE char * pacf_src_buf = NULL;
|
|
PRIVATE int pacf_src_len = 0;
|
|
PRIVATE XP_List*pacf_all_nodes = NULL;
|
|
PRIVATE time_t pacf_direct_until = (time_t)0;
|
|
PRIVATE int pacf_direct_cnt = 0;
|
|
PRIVATE PACF_QueuedState *queued_state = NULL;
|
|
PRIVATE XP_List*pacf_bad_keywords = NULL;
|
|
|
|
PRIVATE Bool pacf_find_proxy_undefined = FALSE;
|
|
|
|
/* Javascript stuff. A javascript context does the interpretation and
|
|
* compilation of the pac file. */
|
|
PRIVATE JSPropertySpec pc_props[] = {
|
|
{"secondary", PACF_SECONDARY_URL},
|
|
{"bindings", PACF_BINDINGS},
|
|
{0}
|
|
};
|
|
|
|
PRIVATE JSClass pc_class = {
|
|
"ProxyConfig", 0,
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
};
|
|
|
|
/* PRIVATE JSPropertySpec no_props[1] = { { NULL } }; */
|
|
PRIVATE JSObject *proxyConfig = NULL;
|
|
PRIVATE JSObject *globalConfig = NULL;
|
|
PRIVATE JSContext *configContext = NULL;
|
|
|
|
/* The javascript methods */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_weekdayRange(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dateRange(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_timeRange(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_isPlainHostName(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dnsDomainLevels(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dnsDomainIs(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_localHostOrDomainIs(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_isResolvable(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_dnsResolve(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_isInNet(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_myIpAddress(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
MODULE_PRIVATE JSBool PR_CALLBACK proxy_regExpMatch(JSContext *mc, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval);
|
|
|
|
PRIVATE JSFunctionSpec pc_methods[] = {
|
|
{ "weekdayRange", proxy_weekdayRange, 3},
|
|
{ "dateRange", proxy_dateRange, 7},
|
|
{ "timeRange", proxy_timeRange, 7},
|
|
{ "isPlainHostName", proxy_isPlainHostName, 1},
|
|
{ "dnsDomainLevels", proxy_dnsDomainLevels, 1},
|
|
{ "dnsDomainIs", proxy_dnsDomainIs, 2},
|
|
{ "localHostOrDomainIs", proxy_localHostOrDomainIs, 2},
|
|
{ "isResolvable", proxy_isResolvable, 1},
|
|
{ "dnsResolve", proxy_dnsResolve, 1},
|
|
{ "isInNet", proxy_isInNet, 3},
|
|
{ "myIpAddress", proxy_myIpAddress, 0},
|
|
{ "regExpMatch", proxy_regExpMatch, 2},
|
|
{ "shExpMatch", proxy_regExpMatch, 2},
|
|
{ NULL, NULL, 0}
|
|
};
|
|
|
|
PRIVATE char *mkMethodString( int method ) {
|
|
char *mstr= NULL;
|
|
|
|
switch ( method ) {
|
|
case URL_GET_METHOD:
|
|
mstr = PL_strdup("GET");
|
|
break;
|
|
case URL_POST_METHOD:
|
|
mstr = PL_strdup("POST");
|
|
break;
|
|
case URL_HEAD_METHOD:
|
|
mstr = PL_strdup("HEAD");
|
|
break;
|
|
case URL_PUT_METHOD:
|
|
mstr = PL_strdup("PUT");
|
|
break;
|
|
case URL_DELETE_METHOD:
|
|
mstr = PL_strdup("DELETE");
|
|
break;
|
|
case URL_MKDIR_METHOD :
|
|
mstr = PL_strdup("MKDIR");
|
|
break;
|
|
case URL_MOVE_METHOD:
|
|
mstr = PL_strdup("MOVE");
|
|
break;
|
|
case URL_INDEX_METHOD:
|
|
mstr = PL_strdup("INDEX");
|
|
break;
|
|
case URL_GETPROPERTIES_METHOD:
|
|
mstr = PL_strdup("GETPROPERTIES");
|
|
break;
|
|
}
|
|
return mstr;
|
|
}
|
|
|
|
PRIVATE char *msg2(char *fmt, char *prm) {
|
|
char *msg = (char *)PR_Malloc(PL_strlen(fmt) + 100 +
|
|
(prm ? PL_strlen(prm) : 0));
|
|
if (msg)
|
|
sprintf(msg, fmt, prm ? prm : "-");
|
|
|
|
return msg;
|
|
}
|
|
|
|
PRIVATE Bool confirm2(MWContext *context, char *fmt, char *prm) {
|
|
Bool rv = TRUE;
|
|
char *msg = msg2(fmt, prm);
|
|
if (msg) {
|
|
rv = FE_Confirm(context, msg);
|
|
PR_Free(msg);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
PRIVATE void alert2(MWContext *context, char *fmt, char *prm) {
|
|
char *msg = msg2(fmt, prm);
|
|
if (msg) {
|
|
FE_Alert(context, msg);
|
|
PR_Free(msg);
|
|
}
|
|
}
|
|
|
|
/* Returns a node representing a proxy/SOCKS server address or
|
|
* direct access, and guarantees to return the same node if one
|
|
* was already created for that address. */
|
|
PRIVATE PACF_Node *lookup_create_node(PACF_Type type, char *addr) {
|
|
if (!pacf_all_nodes)
|
|
pacf_all_nodes = XP_ListNew();
|
|
|
|
/* Truncate at 64 characters -- gethostbyname() is evil */
|
|
if (addr && PL_strlen(addr) > 64)
|
|
addr[64] = '\0';
|
|
|
|
if (!type || (!addr && (type == PACF_TYPE_PROXY ||
|
|
type == PACF_TYPE_SOCKS)))
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
XP_List *cur = pacf_all_nodes;
|
|
PACF_Node *node;
|
|
|
|
while ((node = (PACF_Node *)XP_ListNextObject(cur)) != NULL) {
|
|
if (node->type == type &&
|
|
((!node->addr && !addr) ||
|
|
( node->addr && addr && !strcmp(node->addr, addr))))
|
|
{
|
|
return node;
|
|
}
|
|
}
|
|
|
|
node = PR_NEWZAP(PACF_Node);
|
|
if (node) {
|
|
node->type = type;
|
|
node->addr = PL_strdup(addr);
|
|
|
|
XP_ListAddObject(pacf_all_nodes, node);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
}
|
|
|
|
/* Called by netlib when it notices that a proxy is down. */
|
|
PRIVATE PACF_Node * pacf_proxy_is_down(MWContext *context,
|
|
char *proxy_addr,
|
|
u_long socks_addr,
|
|
short socks_port) {
|
|
XP_List *cur = pacf_all_nodes;
|
|
PACF_Node *node;
|
|
|
|
while ((node = (PACF_Node *)XP_ListNextObject(cur)) != NULL) {
|
|
if ((proxy_addr && node->addr && !PL_strcmp(node->addr, proxy_addr)) ||
|
|
(socks_addr &&
|
|
node->socks_addr == socks_addr &&
|
|
node->socks_port == socks_port))
|
|
{
|
|
time_t now = time(NULL);
|
|
|
|
if (!node->down_since) {
|
|
|
|
node->down_since = now;
|
|
}
|
|
node->last_try = now;
|
|
node->retrying = (time_t)0;
|
|
return node;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PRIVATE Bool fill_return_values(PACF_Type type,
|
|
PACF_Node * node,
|
|
char ** ret_proxy_addr,
|
|
u_long * ret_socks_addr,
|
|
short * ret_socks_port) {
|
|
*ret_proxy_addr = NULL;
|
|
*ret_socks_addr = 0;
|
|
*ret_socks_port = 0;
|
|
|
|
if(!node)
|
|
return FALSE;
|
|
|
|
if (type == PACF_TYPE_PROXY) {
|
|
*ret_proxy_addr = node->addr;
|
|
}
|
|
else if (!node->socks_addr) {
|
|
char *p, *host = NULL;
|
|
|
|
StrAllocCopy(host, node->addr);
|
|
p = PL_strchr(host, ':');
|
|
if (p) {
|
|
*p++ = '\0';
|
|
node->socks_port = (short)atoi(p);
|
|
}
|
|
else {
|
|
node->socks_port = 1080;
|
|
}
|
|
|
|
if (isdigit(*host)) {
|
|
/*
|
|
* Some systems return a number instead of a struct
|
|
*/
|
|
node->socks_addr = inet_addr(host);
|
|
}
|
|
else {
|
|
PRStatus rv;
|
|
PRHostEnt *hp;
|
|
PRHostEnt hpbuf;
|
|
char dbbuf[PR_NETDB_BUF_SIZE];
|
|
|
|
NET_InGetHostByName++; /* global semaphore */
|
|
rv = PR_GetHostByName(host, dbbuf, sizeof(dbbuf), &hpbuf);
|
|
hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
|
|
NET_InGetHostByName--; /* global semaphore */
|
|
|
|
if (!hp)
|
|
return FALSE; /* Fail? */
|
|
|
|
memcpy(&node->socks_addr, hp->h_addr, hp->h_length);
|
|
}
|
|
}
|
|
|
|
*ret_socks_addr = node->socks_addr;
|
|
*ret_socks_port = node->socks_port;
|
|
return TRUE;
|
|
}
|
|
|
|
/* NET_SetNoProxyFailover
|
|
* Sets a flag that indicates that proxy failover should not
|
|
* be done. This is used by the Enterprise Kit code, where
|
|
* the customer might configure the client to use specific
|
|
* proxies. In these cases, failover can cause different
|
|
* proxies, or no proxies to be used. */
|
|
PUBLIC void
|
|
NET_SetNoProxyFailover(void) {
|
|
pacf_do_failover = FALSE;
|
|
}
|
|
|
|
PUBLIC PRBool
|
|
NET_LoadingPac(void) {
|
|
return pacf_loading;
|
|
}
|
|
|
|
#ifdef MOZILLA_CLIENT
|
|
/*
|
|
* NET_GetNoProxyFailover
|
|
*/
|
|
MODULE_PRIVATE Bool
|
|
NET_GetNoProxyFailover(void) {
|
|
return ( pacf_do_failover == FALSE );
|
|
}
|
|
#endif /* MOZILLA_CLIENT */
|
|
|
|
/* Called by netlib to get a single string of "host:port" format,
|
|
* given the string of possibilities returned by the Mocha script.
|
|
*
|
|
* This function will return an address to a proxy that is (to its
|
|
* knowledge) up and running. Netlib can later inform this module
|
|
* using the function pacf_proxy_is_down() that a proxy is down
|
|
* and should not be called for a few minutes.
|
|
*
|
|
* Returns FALSE if everything has failed, and an error should be
|
|
* displayed to the user.
|
|
*
|
|
* Returns TRUE if there is hope.
|
|
* If *ret is NULL, a direct connection should be attempted.
|
|
* If *ret is not null, it is the proxy address to use. */
|
|
MODULE_PRIVATE Bool
|
|
pacf_get_proxy_addr(MWContext *context, char *list, char **ret_proxy_addr,
|
|
u_long *ret_socks_addr, short *ret_socks_port) {
|
|
Bool rv = FALSE;
|
|
char *my_copy, *cur, *p, *addr;
|
|
PACF_Type type = PACF_TYPE_INVALID;
|
|
PACF_Node *node;
|
|
PACF_Node *sm_ptr = NULL;
|
|
PACF_Node *went_down = NULL;
|
|
time_t now = time(NULL);
|
|
time_t smallest = (time_t)0;
|
|
int proxy_cnt = 0;
|
|
int socks_cnt = 0;
|
|
int retry_now = 0;
|
|
|
|
if (!list || !*list || !ret_proxy_addr || !ret_socks_addr || !ret_socks_port)
|
|
return FALSE;
|
|
|
|
if (*ret_proxy_addr || *ret_socks_addr) {
|
|
/*
|
|
* We get called here because this proxy/SOCKS server was down.
|
|
*/
|
|
went_down = pacf_proxy_is_down(context, *ret_proxy_addr,
|
|
*ret_socks_addr, *ret_socks_port);
|
|
}
|
|
|
|
*ret_proxy_addr = NULL;
|
|
*ret_socks_addr = 0;
|
|
*ret_socks_port = 0;
|
|
|
|
cur = my_copy = PL_strdup(list);
|
|
|
|
TRACEMSG(("Getting proxy addr from config list: %s", list));
|
|
|
|
do {
|
|
p = PL_strchr(cur, ';');
|
|
if (p) {
|
|
do {
|
|
*p++ = '\0';
|
|
} while (*p && NET_IS_SPACE(*p));
|
|
}
|
|
|
|
for (addr=cur; *addr && !NET_IS_SPACE(*addr); addr++)
|
|
;
|
|
if (*addr) {
|
|
do {
|
|
*addr++ = '\0';
|
|
} while (*addr && NET_IS_SPACE(*addr));
|
|
}
|
|
|
|
type = ((!PL_strcasecmp(cur, "DIRECT")) ? PACF_TYPE_DIRECT :
|
|
(!PL_strcasecmp(cur, "PROXY")) ? PACF_TYPE_PROXY :
|
|
(!PL_strcasecmp(cur, "SOCKS")) ? PACF_TYPE_SOCKS :
|
|
PACF_TYPE_INVALID );
|
|
|
|
if (type == PACF_TYPE_DIRECT) /* don't use a proxy */
|
|
{
|
|
rv = TRUE;
|
|
break; /* done */
|
|
}
|
|
else if (type == PACF_TYPE_PROXY || /* use a proxy or... */
|
|
type == PACF_TYPE_SOCKS ) /* use SOCKS */
|
|
{
|
|
if (type == PACF_TYPE_PROXY)
|
|
proxy_cnt++;
|
|
else
|
|
socks_cnt++;
|
|
|
|
node = lookup_create_node(type, addr);
|
|
if (*addr && node) {
|
|
if (node->down_since && node->retrying &&
|
|
now - node->retrying > PACF_RETRY_OK_AFTER)
|
|
{
|
|
node->down_since = node->retrying = (time_t)0;
|
|
}
|
|
|
|
if (!node->down_since)
|
|
{
|
|
if (fill_return_values(type, node,
|
|
ret_proxy_addr,
|
|
ret_socks_addr,
|
|
ret_socks_port))
|
|
{
|
|
rv = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else if (now - node->last_try >
|
|
(node->retries + 1) * PACF_AUTO_RETRY_AFTER)
|
|
{
|
|
node->retries++;
|
|
if (fill_return_values(type, node,
|
|
ret_proxy_addr,
|
|
ret_socks_addr,
|
|
ret_socks_port))
|
|
{
|
|
node->retrying = now;
|
|
rv = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else if (node != went_down &&
|
|
(!smallest || smallest > node->last_try))
|
|
{
|
|
smallest = node->last_try;
|
|
sm_ptr = node;
|
|
}
|
|
}
|
|
}
|
|
else /* invalid entry */
|
|
{
|
|
char *key = NULL;
|
|
|
|
if (!pacf_bad_keywords)
|
|
{
|
|
pacf_bad_keywords = XP_ListNew();
|
|
}
|
|
else
|
|
{
|
|
XP_List *ptr = pacf_bad_keywords;
|
|
|
|
while ((key = (char *)XP_ListNextObject(ptr)) != NULL) {
|
|
if (!strcmp(key, cur))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!key && XP_ListCount(pacf_bad_keywords) < 3)
|
|
{
|
|
key = PL_strdup(cur);
|
|
XP_ListAddObject(pacf_bad_keywords, key);
|
|
alert2(context, XP_GetString(XP_BAD_KEYWORD_IN_PROXY_AUTOCFG), cur);
|
|
}
|
|
}
|
|
|
|
cur = p;
|
|
|
|
} while (cur && *cur);
|
|
|
|
PR_Free(my_copy);
|
|
|
|
if (rv)
|
|
return TRUE;
|
|
|
|
/* If we didn't return just above here, and we're using a pad pac file,
|
|
* something went wrong with it so disable it for this session and return
|
|
* true so we go direct. */
|
|
if(NET_UsingPadPac()) {
|
|
net_UsePadPac(FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
if (pacf_do_failover && pacf_direct_until) {
|
|
if (now < pacf_direct_until)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else if (!FE_Confirm(context,
|
|
!socks_cnt ? XP_GetString(XP_RETRY_AGAIN_PROXY) :
|
|
!proxy_cnt ? XP_GetString(XP_RETRY_AGAIN_SOCKS) : XP_GetString(XP_RETRY_AGAIN_PROXY_OR_SOCKS)))
|
|
{
|
|
pacf_direct_until = now + (++pacf_direct_cnt) * PACF_DIRECT_INTERVAL;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
pacf_direct_until = (time_t)0;
|
|
retry_now = 1;
|
|
}
|
|
}
|
|
|
|
if (smallest &&
|
|
(retry_now ||
|
|
now - smallest > PACF_MIN_RETRY_AFTER ||
|
|
(sm_ptr && !sm_ptr->retrying &&
|
|
now - smallest > PACF_MIN_RETRY_ASK &&
|
|
confirm2(context,
|
|
(!socks_cnt && proxy_cnt==1 ? XP_GetString(XP_PROXY_UNAVAILABLE_TRY_AGAIN) :
|
|
!socks_cnt && proxy_cnt >1 ? XP_GetString(XP_ALL_PROXIES_DOWN_TRY_AGAIN) :
|
|
!proxy_cnt ? XP_GetString(XP_ALL_SOCKS_DOWN) :
|
|
XP_GetString(XP_ALL_DOWN_MIX)),
|
|
sm_ptr->addr))) &&
|
|
fill_return_values(type, sm_ptr,
|
|
ret_proxy_addr, ret_socks_addr, ret_socks_port))
|
|
{
|
|
sm_ptr->retrying = now;
|
|
sm_ptr->retries++;
|
|
return TRUE;
|
|
}
|
|
else if (pacf_do_failover && FE_Confirm(context,
|
|
!pacf_direct_cnt ?
|
|
(!socks_cnt ? XP_GetString(XP_OVERRIDE_PROXY) :
|
|
!proxy_cnt ? XP_GetString(XP_OVERRIDE_SOCKS) : XP_GetString(XP_OVERRIDE_MIX)) :
|
|
(!socks_cnt ? XP_GetString(XP_STILL_OVERRIDE_PROXY) :
|
|
!proxy_cnt ? XP_GetString(XP_STILL_OVERRIDE_SOCKS) : XP_GetString(XP_STILL_OVERRIDE_SOCKS))))
|
|
{
|
|
pacf_direct_until = now + (++pacf_direct_cnt) * PACF_DIRECT_INTERVAL;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Saves out the proxy autoconfig file to disk, in case the server
|
|
* is down the next time.
|
|
*
|
|
* Returns 0 on success, -1 on failure. */
|
|
PRIVATE int pacf_save_config(void) {
|
|
XP_File fp;
|
|
int32 len = 0;
|
|
|
|
if (!pacf_do_failover || !pacf_src_buf || !*pacf_src_buf || pacf_src_len <= 0)
|
|
return -1;
|
|
|
|
if(!(fp = NET_XP_FileOpen("", xpProxyConfig, XP_FILE_WRITE)))
|
|
return -1;
|
|
|
|
len = NET_XP_FileWrite(pacf_src_buf, pacf_src_len, fp);
|
|
NET_XP_FileClose(fp);
|
|
if (len != pacf_src_len)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Reads the proxy autoconfig file from disk.
|
|
* This is called if the config server is not responding.
|
|
*
|
|
* returns 0 on success -1 on failure. */
|
|
PRIVATE int pacf_read_config(void) {
|
|
XP_StatStruct st;
|
|
XP_File fp;
|
|
|
|
if (NET_XP_Stat("", &st, xpProxyConfig) == -1)
|
|
return -1;
|
|
|
|
if (!(fp = NET_XP_FileOpen("", xpProxyConfig, XP_FILE_READ)))
|
|
return -1;
|
|
|
|
pacf_src_len = st.st_size;
|
|
pacf_src_buf = (char *)PR_Malloc(pacf_src_len + 1);
|
|
if (!pacf_src_buf) {
|
|
NET_XP_FileClose(fp);
|
|
pacf_src_len = 0;
|
|
return -1;
|
|
}
|
|
|
|
if ((pacf_src_len = NET_XP_FileRead(pacf_src_buf, pacf_src_len, fp)) > 0)
|
|
{
|
|
pacf_src_buf[pacf_src_len] = '\0';
|
|
}
|
|
else
|
|
{
|
|
PR_Free(pacf_src_buf);
|
|
pacf_src_buf = NULL;
|
|
pacf_src_len = 0;
|
|
}
|
|
|
|
NET_XP_FileClose(fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Private stream object methods for receiving the proxy autoconfig file. */
|
|
PRIVATE int pacf_write(NET_StreamClass *stream, CONST char *buf, int32 len) {
|
|
PACF_Object *obj;
|
|
obj=stream->data_object;
|
|
if (len > 0) {
|
|
if (!pacf_src_buf)
|
|
pacf_src_buf = (char*)PR_Malloc(len + 1);
|
|
else
|
|
pacf_src_buf = (char*)PR_Realloc(pacf_src_buf,
|
|
pacf_src_len + len + 1);
|
|
|
|
if (!pacf_src_buf) { /* Out of memory */
|
|
pacf_src_len = 0;
|
|
return MK_DATA_LOADED;
|
|
}
|
|
|
|
memcpy(pacf_src_buf + pacf_src_len, buf, len);
|
|
pacf_src_len += len;
|
|
pacf_src_buf[pacf_src_len] = '\0';
|
|
}
|
|
return MK_DATA_LOADED;
|
|
}
|
|
|
|
PRIVATE unsigned int pacf_write_ready(NET_StreamClass *stream) {
|
|
return MAX_WRITE_READY;
|
|
}
|
|
|
|
PUBLIC XP_Bool
|
|
NET_InitPacfContext(void)
|
|
{
|
|
XP_Bool first_time = TRUE;
|
|
|
|
if ( first_time == FALSE ) return TRUE;
|
|
|
|
if ( (PREF_OK != PREF_GetConfigContext(&configContext))
|
|
||(PREF_OK != PREF_GetGlobalConfigObject(&globalConfig)) )
|
|
return FALSE;
|
|
|
|
JS_BeginRequest(configContext);
|
|
|
|
proxyConfig = JS_DefineObject(configContext, globalConfig,
|
|
"ProxyConfig",
|
|
&pc_class,
|
|
NULL,
|
|
JSPROP_ENUMERATE);
|
|
if (proxyConfig) {
|
|
if (!JS_DefineProperties(configContext,
|
|
proxyConfig,
|
|
pc_props)) {
|
|
JS_EndRequest(configContext);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!JS_DefineFunctions(configContext,
|
|
proxyConfig,
|
|
pc_methods)) {
|
|
JS_EndRequest(configContext);
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
JS_DefineObject(configContext, proxyConfig,
|
|
"bindings",
|
|
&pc_class,
|
|
NULL,
|
|
0);
|
|
|
|
JS_EndRequest(configContext);
|
|
|
|
first_time = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PRIVATE void pacf_complete(NET_StreamClass *stream) {
|
|
PACF_Object *obj=stream->data_object;
|
|
jsval result;
|
|
XP_StatStruct st;
|
|
JSBool ok;
|
|
|
|
retry:
|
|
if (!obj->flag || obj->flag == 2) {
|
|
pacf_loading = FALSE;
|
|
if ( !NET_InitPacfContext() ) return;
|
|
}
|
|
|
|
if (!pacf_src_buf) {
|
|
if ( pacf_do_failover == FALSE && !NET_UsingPadPac() ) {
|
|
/* Don't failover to using no proxies */
|
|
FE_Alert(obj->context, XP_GetString(XP_NO_CONFIG_RECEIVED_NO_FAILOVER));
|
|
}
|
|
else if (obj->flag != 2 && (NET_XP_Stat("", &st, xpProxyConfig) == -1) && !NET_UsingPadPac())
|
|
{
|
|
FE_Alert(obj->context, XP_GetString(XP_NO_CONFIG_RECIEVED));
|
|
}
|
|
else
|
|
{
|
|
if(NET_UsingPadPac()) {
|
|
pacf_read_config();
|
|
obj->flag = 1;
|
|
goto retry;
|
|
} else if (obj->flag == 2 ||
|
|
confirm2(obj->context, XP_GetString(XP_EMPTY_CONFIG_USE_PREV), pacf_url)) {
|
|
pacf_read_config();
|
|
obj->flag = 1;
|
|
goto retry;
|
|
}
|
|
}
|
|
pacf_ok = FALSE;
|
|
goto out;
|
|
}
|
|
|
|
JS_BeginRequest(configContext);
|
|
ok = JS_EvaluateScript(configContext, proxyConfig,
|
|
pacf_src_buf, pacf_src_len, pacf_url, 0,
|
|
&result);
|
|
JS_EndRequest(configContext);
|
|
|
|
if (!ok) {
|
|
/* Something went wrong with the js evaluation. If we're using a
|
|
* proxy auto-discovery pac file, don't use it again. */
|
|
if(NET_UsingPadPac()) {
|
|
net_UsePadPac(FALSE);
|
|
goto out;
|
|
}
|
|
if (obj->flag) {
|
|
FE_Alert(obj->context, XP_GetString(XP_EVEN_SAVED_IS_BAD));
|
|
} else if (NET_XP_Stat("", &st, xpProxyConfig) == -1) {
|
|
alert2(obj->context, XP_GetString(XP_BAD_CONFIG_IGNORED), pacf_url);
|
|
} else if (confirm2(obj->context, XP_GetString(XP_BAD_CONFIG_USE_PREV), pacf_url)) {
|
|
PR_Free(pacf_src_buf);
|
|
pacf_src_buf = NULL;
|
|
pacf_src_len = 0;
|
|
pacf_read_config();
|
|
obj->flag = 1; /* Uh (this is a horrid hack) */
|
|
goto retry;
|
|
}
|
|
pacf_ok = FALSE;
|
|
} else {
|
|
pacf_ok = TRUE;
|
|
if (!obj->flag) {
|
|
pacf_save_config();
|
|
}
|
|
}
|
|
|
|
PR_Free(obj);
|
|
|
|
out:
|
|
;
|
|
}
|
|
|
|
PRIVATE void pacf_abort(NET_StreamClass *stream, int status) {
|
|
PACF_Object *obj=stream->data_object;
|
|
pacf_loading = FALSE;
|
|
FE_Alert(obj->context, XP_GetString(XP_GLOBAL_CONFIG_LOAD_ABORTED));
|
|
PR_Free(obj);
|
|
}
|
|
|
|
|
|
/* A stream constructor function for application/x-ns-proxy-autoconfig.
|
|
* This is used by cvmime.c; it's registered as the stream converter for
|
|
* pac files. */
|
|
MODULE_PRIVATE NET_StreamClass *
|
|
NET_ProxyAutoConfig(int fmt, void *data_obj, URL_Struct *URL_s,
|
|
MWContext *w) {
|
|
PACF_Object *obj;
|
|
NET_StreamClass *stream;
|
|
|
|
if (!pacf_loading) {
|
|
/*
|
|
* The Navigator didn't start this config retrieve
|
|
* intentionally. Discarding the config.
|
|
*/
|
|
if(!URL_s)
|
|
return NULL;
|
|
alert2(w, XP_GetString(XP_CONFIG_BLAST_WARNING), URL_s->address);
|
|
return NULL;
|
|
}
|
|
else {
|
|
NET_Progress(w, XP_GetString( XP_RECEIVING_PROXY_AUTOCFG ) );
|
|
}
|
|
|
|
if (pacf_src_buf) {
|
|
PR_Free(pacf_src_buf);
|
|
pacf_src_buf = NULL;
|
|
pacf_src_len = 0;
|
|
}
|
|
|
|
if (!(stream = PR_NEWZAP(NET_StreamClass)))
|
|
return NULL;
|
|
|
|
if (!(obj = PR_NEWZAP(PACF_Object))) {
|
|
PR_Free(stream);
|
|
return NULL;
|
|
}
|
|
|
|
obj->context = w;
|
|
|
|
stream->data_object = obj;
|
|
stream->name = "ProxyAutoConfigLoader";
|
|
stream->complete = (MKStreamCompleteFunc) pacf_complete;
|
|
stream->abort = (MKStreamAbortFunc) pacf_abort;
|
|
stream->put_block = (MKStreamWriteFunc) pacf_write;
|
|
stream->is_write_ready = (MKStreamWriteReadyFunc)pacf_write_ready;
|
|
stream->window_id = w;
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
|
|
/* calls NET_GetURL() to get the url that had been queued up behind the
|
|
* actual pac file url. */
|
|
static void pacf_restart_queued(URL_Struct *URL_s, int status,
|
|
MWContext *window_id) {
|
|
XP_StatStruct st;
|
|
|
|
/* We want to fail silently if we're loading a pad pac and fail,
|
|
* so fall through to bottom and load the queued url. */
|
|
if (pacf_loading && !(NET_UsingPadPac() || foundPADPAC) ) {
|
|
|
|
if ( pacf_do_failover == FALSE ) {
|
|
/* Don't failover to using no proxies */
|
|
FE_Alert(window_id, XP_GetString(XP_CONF_LOAD_FAILED_NO_FAILOVER));
|
|
}
|
|
else if (NET_XP_Stat("", &st, xpProxyConfig) == -1)
|
|
{
|
|
if (status < 0)
|
|
FE_Alert(window_id, XP_GetString(XP_CONF_LOAD_FAILED_IGNORED));
|
|
else {
|
|
alert2(window_id, XP_GetString(XP_BAD_TYPE_CONFIG_IGNORED), pacf_url);
|
|
}
|
|
}
|
|
else if (status == MK_INTERRUPTED)
|
|
{
|
|
/* silently fail and retry later */
|
|
NET_ProxyAcLoaded = FALSE;
|
|
NET_GlobalAcLoaded = FALSE;
|
|
}
|
|
else if (status < 0
|
|
? FE_Confirm(window_id, XP_GetString(XP_CONF_LOAD_FAILED_USE_PREV))
|
|
: confirm2(window_id, XP_GetString(XP_BAD_TYPE_USE_PREV), pacf_url))
|
|
{
|
|
PACF_Object *obj = PR_NEWZAP(PACF_Object);
|
|
NET_StreamClass stream;
|
|
stream.data_object=obj;
|
|
|
|
if (obj) {
|
|
obj->context = window_id;
|
|
obj->flag = 2;
|
|
pacf_complete(&stream);
|
|
}
|
|
}
|
|
|
|
pacf_loading = FALSE;
|
|
} else {
|
|
/* Call this only if everything succeeded -- otherwise
|
|
the netlib has already freed the URL_s, and there isn't
|
|
a clean fix, so for now we'll just forget the URL load
|
|
if proxy config load went foul.
|
|
*/
|
|
if(queued_state) {
|
|
NET_GetURL(queued_state->URL_s,
|
|
queued_state->output_format,
|
|
queued_state->window_id,
|
|
queued_state->exit_routine);
|
|
}
|
|
}
|
|
PR_FREEIF(queued_state);
|
|
queued_state = NULL;
|
|
}
|
|
|
|
|
|
|
|
/* Called by mkgeturl.c to originally retrieve, and re-retrieve
|
|
* the proxy autoconfig file.
|
|
*
|
|
* autoconfig_url is the URL pointing to the autoconfig.
|
|
*
|
|
* The rest of the parameters are what was passed to NET_GetURL(),
|
|
* and when the autoconfig load finishes NET_GetURL() will be called
|
|
* with those exact same parameters.
|
|
*
|
|
* This is because the proxy config is loaded when NET_GetURL() is
|
|
* called for the very first time, and the actual request must be put
|
|
* on hold when the proxy config is being loaded.
|
|
*
|
|
* When called from the explicit proxy config RE-load function
|
|
* NET_ReloadProxyConfig, the four last parameters are all zero,
|
|
* and no request gets restarted. */
|
|
MODULE_PRIVATE int NET_LoadProxyConfig(char *autoconf_url,
|
|
URL_Struct *URL_s,
|
|
FO_Present_Types output_format,
|
|
MWContext *window_id,
|
|
Net_GetUrlExitFunc *exit_routine) {
|
|
URL_Struct *my_url_s = NULL;
|
|
|
|
if (!autoconf_url)
|
|
return -1;
|
|
|
|
if (!PL_strcmp(autoconf_url,"BAD-NOAUTOADMNLIB")) {
|
|
FE_Alert(window_id, XP_GetString( XP_AUTOADMIN_MISSING ));
|
|
return -1;
|
|
}
|
|
|
|
StrAllocCopy(pacf_url, autoconf_url);
|
|
my_url_s = NET_CreateURLStruct(autoconf_url, NET_SUPER_RELOAD);
|
|
|
|
|
|
if (exit_routine) {
|
|
|
|
queued_state = PR_NEWZAP(PACF_QueuedState);
|
|
if(!queued_state)
|
|
return -1;
|
|
queued_state->URL_s = URL_s;
|
|
queued_state->output_format = output_format;
|
|
queued_state->window_id = window_id;
|
|
queued_state->exit_routine = exit_routine;
|
|
|
|
if(!my_url_s)
|
|
return -1;
|
|
|
|
my_url_s->pre_exit_fn = pacf_restart_queued;
|
|
}
|
|
|
|
/* Alert the proxy autoconfig module that config is coming */
|
|
pacf_loading = TRUE;
|
|
pacf_find_proxy_undefined = FALSE;
|
|
|
|
return NET_GetURL(my_url_s, FO_PRESENT, window_id, NULL);
|
|
}
|
|
|
|
/* Returns a pointer to a NULL-terminted buffer which contains
|
|
* the text of the proxy autoconfig file. */
|
|
PUBLIC char * NET_GetProxyConfigSource(void) {
|
|
|
|
return pacf_src_buf;
|
|
|
|
}
|
|
|
|
|
|
/* Given a URL, returns the proxy that should be used.
|
|
* The proxy is determined by the proxy autoconfig file,
|
|
* which is a JavaScript routine. */
|
|
MODULE_PRIVATE char *pacf_find_proxies_for_url(MWContext *context,
|
|
URL_Struct *URL_s ) {
|
|
jsval rv;
|
|
char *buf = NULL;
|
|
char *host = NULL;
|
|
char *p, *q, *r;
|
|
int i, len = 0;
|
|
char *safe_url = NULL;
|
|
char *orig_url = NULL;
|
|
char *method = NULL;
|
|
char *bad_url = NULL;
|
|
char *result = NULL;
|
|
JSBool ok;
|
|
|
|
if(!URL_s)
|
|
return NULL;
|
|
|
|
orig_url=URL_s->address;
|
|
method=mkMethodString(URL_s->method);
|
|
|
|
/* If proxy failover is not allowed, and we weren't
|
|
* able to autoload the proxy, return a string that
|
|
* pacf_get_proxy_addr will always fail with. */
|
|
if ( !pacf_do_failover && !pacf_loading && !pacf_ok && !NET_FindProxyInJSC()) {
|
|
return "";
|
|
}
|
|
|
|
if ( !NET_FindProxyInJSC() && (!orig_url || !pacf_ok || pacf_loading || pacf_find_proxy_undefined))
|
|
return NULL;
|
|
|
|
if (!(bad_url = PL_strdup(orig_url)))
|
|
goto out;
|
|
|
|
len = NET_UnEscapeCnt(bad_url);
|
|
|
|
if (!(safe_url = PR_Malloc(2 * len + 1))) /* worst case */
|
|
goto out;
|
|
|
|
p = bad_url;
|
|
q = safe_url;
|
|
for(i=0; i<len; i++, p++) {
|
|
switch (*p) {
|
|
case '\n':
|
|
*q++ = '\\';
|
|
*q++ = 'n';
|
|
break;
|
|
case '\r':
|
|
*q++ = '\\';
|
|
*q++ = 'r';
|
|
break;
|
|
case '\0':
|
|
*q++ = '\\';
|
|
*q++ = '0';
|
|
break;
|
|
case '"':
|
|
*q++ = '\\';
|
|
*q++ = '"';
|
|
break;
|
|
case '\\':
|
|
*q++ = '\\';
|
|
*q++ = '\\';
|
|
break;
|
|
default:
|
|
*q++ = *p;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
|
|
len = (int)(q - safe_url);
|
|
|
|
if (!(host = PR_Malloc(len + 1)))
|
|
goto out;
|
|
|
|
if (!(buf = PR_Malloc(len*2 + 50)))
|
|
goto out;
|
|
|
|
host[0] = '\0';
|
|
|
|
p = PL_strstr(safe_url, "://");
|
|
if (p) {
|
|
p += 3;
|
|
q = PL_strchr(p, '/');
|
|
if (q)
|
|
*q = '\0';
|
|
r = PL_strchr(p, '@');
|
|
if (r)
|
|
p = r + 1;
|
|
PL_strcpy(host, p);
|
|
if (q)
|
|
*q = '/';
|
|
p = PL_strchr(host, ':');
|
|
if (p)
|
|
*p = '\0';
|
|
}
|
|
|
|
if ( NET_FindProxyInJSC() ) {
|
|
XP_SPRINTF(buf, "ProxyConfig.FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host,
|
|
method ? method : "" );
|
|
} else {
|
|
XP_SPRINTF(buf, "FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host,
|
|
method ? method : "" );
|
|
}
|
|
|
|
JS_BeginRequest(configContext);
|
|
if (!JS_AddRoot(configContext, &rv)) {
|
|
JS_EndRequest(configContext);
|
|
goto out;
|
|
}
|
|
|
|
if ( NET_FindProxyInJSC() ) {
|
|
ok = JS_EvaluateScript(configContext, globalConfig,
|
|
buf, strlen(buf), 0, 0, &rv);
|
|
} else {
|
|
ok = JS_EvaluateScript(configContext, proxyConfig,
|
|
buf, strlen(buf), 0, 0, &rv);
|
|
}
|
|
|
|
if (ok) {
|
|
if (JSVAL_IS_STRING(rv)) {
|
|
const char *name =
|
|
JS_GetStringBytes(JSVAL_TO_STRING(rv));
|
|
if (*name)
|
|
result = PL_strdup(name);
|
|
}
|
|
}
|
|
|
|
JS_RemoveRoot(configContext, &rv);
|
|
JS_EndRequest(configContext);
|
|
out:
|
|
FREEIF(method);
|
|
FREEIF(buf);
|
|
FREEIF(host);
|
|
FREEIF(safe_url);
|
|
FREEIF(bad_url);
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
/* Utility functions to be called from Javascript (aka Mocha).
|
|
* These are the actual implementations of the javascript fucntions that
|
|
* are in the javascript pac file.
|
|
*
|
|
* Get the number of DNS domain levels (number of dots):
|
|
*
|
|
* dnsDomainLevels(host)
|
|
*
|
|
*
|
|
* Host/URL based conditions:
|
|
*
|
|
* isPlainHostName(host)
|
|
* dnsDomainIs(host, name)
|
|
* localHostOrDomainIs(host, name)
|
|
* isResolvable(host)
|
|
* regExpMatch(host or URL, regexp)
|
|
*
|
|
*
|
|
* Date/time based conditions (limits inclusive):
|
|
*
|
|
* weekdayRange(wkday)
|
|
* weekdayRange(wkday, wkday)
|
|
*
|
|
* dateRange(day)
|
|
* dateRange(day, day)
|
|
* dateRange(mon)
|
|
* dateRange(mon, mon)
|
|
* dateRange(year)
|
|
* dateRange(year, year)
|
|
* dateRange(day, mon, day, mon)
|
|
* dateRange(mon, year, mon, year)
|
|
* dateRange(day, mon, year, day, mon, year)
|
|
*
|
|
* timeRange(hour)
|
|
* timeRange(hour, hour)
|
|
* timeRange(hour, min, hour, min)
|
|
* timeRange(hour, min, sec, hour, min, sec)
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
/* Returns true if the hostname doesn't contain a dot
|
|
* (is a plain hostname, not an FQDN).
|
|
*
|
|
* Just a string operation, doesn't consult DNS. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_isPlainHostName(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
|
|
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
|
|
if (h && !PL_strchr(h, '.')) {
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
*rval = JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Returns the number of domain levels in the hostname
|
|
* (the number of dots, actually).
|
|
*
|
|
* Just a string operation, doesn't consult DNS. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_dnsDomainLevels(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
int i = 0;
|
|
|
|
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
|
|
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
|
|
if (h) {
|
|
while (*h) {
|
|
if (*h == '.')
|
|
i++;
|
|
h++;
|
|
}
|
|
}
|
|
}
|
|
|
|
*rval = INT_TO_JSVAL(i);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Checks if the hostname contains the given domain. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_dnsDomainIs(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 2 && JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
|
|
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
const char *p = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
|
|
int len1, len2;
|
|
|
|
if (h && p && (len1 = PL_strlen(h)) >= (len2 = PL_strlen(p)) &&
|
|
(!PL_strcasecmp(h + len1 - len2, p))) {
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
*rval = JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Returns true if the hostname matches exactly the given
|
|
* pattern hostname, or if the hostname is just a local hostname
|
|
* and it matches the hostname in the pattern FQDN hostname. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_localHostOrDomainIs(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 2 && JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
|
|
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
const char *p = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
|
|
char *hp, *pp;
|
|
|
|
if (h && p) {
|
|
hp = PL_strchr(h, '.');
|
|
pp = PL_strchr(p, '.');
|
|
|
|
if (hp || !pp) {
|
|
if (!PL_strcasecmp(h,p)) {
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
else if (!PL_strncasecmp(h, p, pp - p)) {
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
*rval = JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Attempts to resolve the DNS name, and returns TRUE if resolvable. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_isResolvable(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
|
|
const char *h = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
PRHostEnt *hp = NULL;
|
|
PRStatus rv;
|
|
PRHostEnt hpbuf;
|
|
char dbbuf[PR_NETDB_BUF_SIZE];
|
|
|
|
if (h) {
|
|
char *safe = PL_strdup(h);
|
|
if (PL_strlen(safe) > 64)
|
|
safe[64] = '\0';
|
|
rv = PR_GetHostByName(safe, dbbuf, sizeof(dbbuf), &hpbuf);
|
|
hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
|
|
PR_Free(safe);
|
|
}
|
|
|
|
if (hp) {
|
|
TRACEMSG(("~~~~~~~~~~~ isResolvable(%s) returns TRUE\n", h));
|
|
TRACEMSG(("~~~~~~~~~~~ hp = %p, hp->h_name = %s\n", hp, hp->h_name ? hp->h_name : "(null)"));
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
TRACEMSG(("~~~~~~~~~~~ isResolvable(%s) returns FALSE\n", argv[0]));
|
|
*rval = JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Resolves a DNS name, and returns the IP address string.
|
|
*
|
|
* Maintains a private cache for the last resolved address (so this
|
|
* function can be called multiple times with the same host argument
|
|
* without doing actual DNS queries every time). */
|
|
PRIVATE char *proxy_dns_resolve(const char *host) {
|
|
static char *cache_host = NULL;
|
|
static char *cache_ip = NULL;
|
|
PRStatus rv;
|
|
PRHostEnt *hp = NULL;
|
|
PRHostEnt hpbuf;
|
|
char dbbuf[PR_NETDB_BUF_SIZE];
|
|
|
|
if (host) {
|
|
const char *p;
|
|
char *safe = NULL;
|
|
XP_Bool is_numeric_ip = TRUE;
|
|
|
|
for(p=host; *p; p++) {
|
|
if (!NET_IS_DIGIT(*p) && *p != '.') {
|
|
is_numeric_ip = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (is_numeric_ip) {
|
|
return PL_strdup(host);
|
|
}
|
|
|
|
if (cache_host && cache_ip && !PL_strcmp(cache_host, host)) {
|
|
return PL_strdup(cache_ip);
|
|
}
|
|
safe = PL_strdup(host);
|
|
if (safe) {
|
|
if (PL_strlen(safe) > 64)
|
|
safe[64] = '\0';
|
|
|
|
rv = PR_GetHostByName(safe, dbbuf, sizeof(dbbuf), &hpbuf);
|
|
hp = (rv == PR_SUCCESS ? &hpbuf : NULL);
|
|
|
|
PR_Free(safe);
|
|
}
|
|
if (hp) {
|
|
char *ip = NULL;
|
|
struct in_addr in;
|
|
|
|
memcpy(&in.s_addr, hp->h_addr, hp->h_length);
|
|
|
|
ip = inet_ntoa(in);
|
|
if (ip) {
|
|
StrAllocCopy(cache_host, host);
|
|
StrAllocCopy(cache_ip, ip);
|
|
|
|
return PL_strdup(ip);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_dnsResolve(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 1 && JSVAL_IS_STRING(argv[0])) {
|
|
const char *host = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
char *ip = proxy_dns_resolve(host);
|
|
|
|
if (ip) {
|
|
JSString * str = JS_NewString(mc, ip, strlen(ip));
|
|
if (!str) {
|
|
PR_Free(ip);
|
|
return JS_FALSE;
|
|
}
|
|
*rval = STRING_TO_JSVAL(str);
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
*rval = JSVAL_NULL;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Returns the IP address of the host machine as a string. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_myIpAddress(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
static XP_Bool initialized = FALSE;
|
|
static char *my_address = NULL;
|
|
|
|
if (!initialized) {
|
|
char name[100];
|
|
|
|
initialized = TRUE;
|
|
if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
|
|
my_address = proxy_dns_resolve(name);
|
|
}
|
|
}
|
|
|
|
TRACEMSG(("~~~~~~~~~~~~~~~~~~ myIpAddress() returns %s\n", my_address ? my_address : "(null)"));
|
|
|
|
if (my_address) {
|
|
JSString *str;
|
|
str = JS_NewStringCopyZ(mc, my_address);
|
|
if (!str)
|
|
return JS_FALSE;
|
|
*rval = STRING_TO_JSVAL(str);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
*rval = JSVAL_NULL;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Determines if the host IP address belongs to the given network.
|
|
* Uses SOCKS style address pattern and mask:
|
|
*
|
|
* isInNet(hostname, "111.222.33.0", "255.255.255.0"); */
|
|
PRIVATE unsigned long convert_addr(const char *ip) {
|
|
char *p, *q, *buf = NULL;
|
|
int i;
|
|
unsigned char b[4];
|
|
unsigned long addr = 0L;
|
|
|
|
p = buf = PL_strdup(ip);
|
|
if (ip && p) {
|
|
for(i=0; p && i<4; i++) {
|
|
q = PL_strchr(p, '.');
|
|
if (q) {
|
|
*q = '\0';
|
|
}
|
|
b[i] = XP_ATOI(p) & 0xff;
|
|
if (q) {
|
|
p = q+1;
|
|
}
|
|
}
|
|
addr = (((unsigned long)b[0] << 24) |
|
|
((unsigned long)b[1] << 16) |
|
|
((unsigned long)b[2] << 8) |
|
|
((unsigned long)b[3]));
|
|
|
|
PR_Free(buf);
|
|
}
|
|
|
|
return htonl(addr);
|
|
}
|
|
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_isInNet(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 3 &&
|
|
JSVAL_IS_STRING(argv[0]) &&
|
|
JSVAL_IS_STRING(argv[1]) &&
|
|
JSVAL_IS_STRING(argv[2]))
|
|
{
|
|
const char *ipstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
char *ip = proxy_dns_resolve(ipstr);
|
|
const char *patstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
|
|
const char *maskstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[2]));
|
|
|
|
if (ip) {
|
|
unsigned long host = convert_addr(ip);
|
|
unsigned long pat = convert_addr(patstr);
|
|
unsigned long mask = convert_addr(maskstr);
|
|
|
|
PR_Free(ip);
|
|
|
|
if ((mask & host) == (mask & pat)) {
|
|
TRACEMSG(("~~~~~~~~~~~~ isInNet(%s(%s), %s, %s) returns TRUE\n",
|
|
ipstr, ip, patstr, maskstr));
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACEMSG(("~~~~~~~~~~~~ isInNet() returns FALSE\n"));
|
|
|
|
*rval = JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* Does a regular expression match between the host/URL and the pattern.
|
|
*
|
|
* Returns true on match. */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_regExpMatch(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
if (argc >= 2 && JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
|
|
const char *url = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
|
const char *pat = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));
|
|
|
|
if (url && pat && XP_RegExpValid((char *) pat) && /* XXX */
|
|
!XP_RegExpMatch((char *) url, (char *) pat, TRUE)) {/* XXX */
|
|
*rval = JSVAL_TRUE;
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
*rval = JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
PRIVATE
|
|
struct tm * get_struct_tm(JSContext *mc,
|
|
unsigned int *argc, jsval *argv) {
|
|
time_t now = time(NULL);
|
|
|
|
if (*argc > 0 && JSVAL_IS_STRING(argv[*argc-1])) {
|
|
const char *laststr = JS_GetStringBytes(JSVAL_TO_STRING(argv[*argc-1]));
|
|
if (!PL_strcasecmp(laststr, "GMT")) {
|
|
(*argc)--;
|
|
return gmtime(&now);
|
|
}
|
|
}
|
|
return localtime(&now);
|
|
}
|
|
|
|
char *weekdays = "SUNMONTUEWEDTHUFRISAT";
|
|
char *monnames = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
|
|
|
|
PRIVATE int get_no(const char *nam, char *arr) {
|
|
char *p = PL_strcasestr(arr, nam);
|
|
return p ? (((int)(p - arr)) / 3) : -1;
|
|
}
|
|
|
|
PRIVATE int get_em(JSContext *mc, int argc, jsval *argv,
|
|
int *d, int *m, int *y) {
|
|
int i=0;
|
|
|
|
*m = *d = *y = -1;
|
|
|
|
for (i=0; i<argc; i++) {
|
|
if (JSVAL_IS_STRING(argv[i])) {
|
|
*m = get_no(JS_GetStringBytes(JSVAL_TO_STRING(argv[i])), monnames);
|
|
}
|
|
else if (JSVAL_IS_NUMBER(argv[i])) {
|
|
if (JSVAL_TO_INT(argv[i]) > 1900)
|
|
*y = JSVAL_TO_INT(argv[i]) - 1900;
|
|
else
|
|
*d = JSVAL_TO_INT(argv[i]);
|
|
}
|
|
else {
|
|
assert(0);
|
|
}
|
|
}
|
|
return ((*d > -1 ? 1 : 0) +
|
|
(*m > -1 ? 1 : 0) +
|
|
(*y > -1 ? 1 : 0));
|
|
}
|
|
|
|
PRIVATE int are_different(JSContext *mc, jsval *argv) {
|
|
int d1, d2, m1, m2, y1, y2;
|
|
|
|
get_em(mc, 1, argv, &d1, &m1, &y1);
|
|
get_em(mc, 1, &argv[1], &d2, &m2, &y2);
|
|
|
|
return ((d1 == -1 || d2 == -1) &&
|
|
(m1 == -1 || m2 == -1) &&
|
|
(y1 == -1 || y2 == -1));
|
|
}
|
|
|
|
PRIVATE long get_rel(int sel_d, int sel_m, int sel_y, int d, int m, int y) {
|
|
return ((sel_d > -1 ? d : 0) +
|
|
(sel_m > -1 ? m : 0) * 31 +
|
|
(sel_y > -1 ? y : 0) * 372);
|
|
}
|
|
|
|
PRIVATE int cmp_properly(int sel_y, long rel_lo, long rel, long rel_hi) {
|
|
if (sel_y || rel_lo < rel_hi)
|
|
return rel_lo <= rel && rel <= rel_hi;
|
|
else
|
|
return rel_lo <= rel || rel <= rel_hi;
|
|
}
|
|
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_dateRange(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
int d1, d2, m1, m2, y1, y2;
|
|
struct tm *tms = get_struct_tm(mc, &argc, argv);
|
|
|
|
d1 = d2 = m1 = m2 = y1 = y2 = -1;
|
|
|
|
if (argc == 1 || argc == 3 || (argc==2 && are_different(mc, argv)))
|
|
{
|
|
*rval = ((get_em(mc, argc, argv, &d1, &m1, &y1) &&
|
|
(d1 == -1 || d1 == tms->tm_mday) &&
|
|
(m1 == -1 || m1 == tms->tm_mon) &&
|
|
(y1 == -1 || y1 == tms->tm_year))
|
|
? JSVAL_TRUE : JSVAL_FALSE);
|
|
}
|
|
else
|
|
{
|
|
*rval = (((get_em(mc, argc/2, argv, &d1, &m1, &y1) ==
|
|
get_em(mc, argc/2, &argv[argc/2], &d2, &m2, &y2)) &&
|
|
cmp_properly(y1,
|
|
get_rel(d1, m1, y1, d1, m1, y1),
|
|
get_rel(d1, m1, y1, tms->tm_mday, tms->tm_mon, tms->tm_year),
|
|
get_rel(d1, m1, y1, d2, m2, y2)))
|
|
? JSVAL_TRUE : JSVAL_FALSE);
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_weekdayRange(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
struct tm *tms = get_struct_tm(mc, &argc, argv);
|
|
int i=0, j=0;
|
|
|
|
if (argc >= 1)
|
|
i = get_no(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), weekdays);
|
|
if (argc >= 2)
|
|
j = get_no(JS_GetStringBytes(JSVAL_TO_STRING(argv[1])), weekdays);
|
|
|
|
*rval = (((argc == 1 && i == tms->tm_wday) ||
|
|
(argc == 2 && ((i <= j && (i <= tms->tm_wday && j >= tms->tm_wday)) ||
|
|
(i > j && (i <= tms->tm_wday || j >= tms->tm_wday)))))
|
|
? JSVAL_TRUE : JSVAL_FALSE);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/* timeRange(<hr>)
|
|
* timeRange(<hr>, <hr>)
|
|
* timeRange(<hr>, <min>, <hr>, <min>)
|
|
* timeRange(<hr>, <min>, <sec>, <hr>, <min>, <sec>) */
|
|
MODULE_PRIVATE JSBool PR_CALLBACK
|
|
proxy_timeRange(JSContext *mc, JSObject *obj, unsigned int argc,
|
|
jsval *argv, jsval *rval) {
|
|
int32 secondsA=0, secondsB=0, secondsC=0;
|
|
struct tm *tms = get_struct_tm(mc, &argc, argv);
|
|
|
|
if(argc == 1) {
|
|
secondsA = JSVAL_TO_INT(argv[0])*60*60;
|
|
secondsB = tms->tm_hour*60*60;
|
|
*rval = (secondsA == secondsB) ? JSVAL_TRUE : JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
if(argc == 2) {
|
|
secondsA = JSVAL_TO_INT(argv[0])*60*60;
|
|
secondsB = tms->tm_hour*60*60;
|
|
secondsC = JSVAL_TO_INT(argv[1])*60*60;
|
|
}
|
|
if(argc == 4) {
|
|
secondsA = JSVAL_TO_INT(argv[0])*60*60 + JSVAL_TO_INT(argv[1])*60;
|
|
secondsB = tms->tm_hour*60*60 + tms->tm_min*60;
|
|
secondsC = JSVAL_TO_INT(argv[2])*60*60 + JSVAL_TO_INT(argv[3])*60;
|
|
}
|
|
if(argc == 6) {
|
|
secondsA = JSVAL_TO_INT(argv[0])*60*60 + JSVAL_TO_INT(argv[1])*60 + JSVAL_TO_INT(argv[2]);
|
|
secondsB = tms->tm_hour*60*60 + tms->tm_min*60 + tms->tm_sec;
|
|
secondsC = JSVAL_TO_INT(argv[3])*60*60 + JSVAL_TO_INT(argv[4])*60 + JSVAL_TO_INT(argv[5]);
|
|
}
|
|
*rval = (secondsA <= secondsB && secondsB < secondsC) ? JSVAL_TRUE : JSVAL_FALSE;
|
|
return JS_TRUE;
|
|
}
|
|
|