mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
bug 347307 - make pac myIPAddress() more accurate r=biesi
This commit is contained in:
parent
35346a740a
commit
0d87d4df42
@ -5,7 +5,6 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ProxyAutoConfig.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
@ -14,6 +13,8 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "prnetdb.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -238,8 +239,14 @@ static const char *sPacUtils =
|
||||
"}\n"
|
||||
"";
|
||||
|
||||
// sRunning is defined for the helper functions only while the
|
||||
// Javascript engine is running and the PAC object cannot be deleted
|
||||
// or reset.
|
||||
static ProxyAutoConfig *sRunning = nullptr;
|
||||
|
||||
// The PACResolver is used for dnsResolve()
|
||||
class PACResolver MOZ_FINAL : public nsIDNSListener
|
||||
, public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -249,21 +256,37 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// nsIDNSListener
|
||||
NS_IMETHODIMP OnLookupComplete(nsICancelable *request,
|
||||
nsIDNSRecord *record,
|
||||
nsresult status)
|
||||
{
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
mRequest = nullptr;
|
||||
mStatus = status;
|
||||
mResponse = record;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsITimerCallback
|
||||
NS_IMETHODIMP Notify(nsITimer *timer)
|
||||
{
|
||||
if (mRequest)
|
||||
mRequest->Cancel(NS_ERROR_NET_TIMEOUT);
|
||||
mTimer = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult mStatus;
|
||||
nsCOMPtr<nsICancelable> mRequest;
|
||||
nsCOMPtr<nsIDNSRecord> mResponse;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(PACResolver, nsIDNSListener)
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(PACResolver, nsIDNSListener, nsITimerCallback)
|
||||
|
||||
static
|
||||
void PACLogToConsole(nsString &aMessage)
|
||||
@ -288,16 +311,45 @@ PACErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
|
||||
PACLogToConsole(formattedMessage);
|
||||
}
|
||||
|
||||
// timeout of 0 means the normal necko timeout strategy, otherwise the dns request
|
||||
// will be canceled after aTimeout milliseconds
|
||||
static
|
||||
JSBool PACResolve(const nsCString &aHostName, nsCString &aDottedDecimal)
|
||||
JSBool PACResolve(const nsCString &aHostName, PRNetAddr *aNetAddr,
|
||||
unsigned int aTimeout)
|
||||
{
|
||||
if (!sRunning) {
|
||||
NS_WARNING("PACResolve without a running ProxyAutoConfig object");
|
||||
return false;
|
||||
}
|
||||
|
||||
return sRunning->ResolveAddress(aHostName, aNetAddr, aTimeout);
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyAutoConfig::ResolveAddress(const nsCString &aHostName,
|
||||
PRNetAddr *aNetAddr,
|
||||
unsigned int aTimeout)
|
||||
{
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
||||
nsCOMPtr<PACResolver> helper = new PACResolver();
|
||||
if (!dns || NS_FAILED(dns->AsyncResolve(aHostName, 0, helper,
|
||||
NS_GetCurrentThread(),
|
||||
getter_AddRefs(helper->mRequest))))
|
||||
if (!dns)
|
||||
return false;
|
||||
|
||||
nsRefPtr<PACResolver> helper = new PACResolver();
|
||||
|
||||
if (NS_FAILED(dns->AsyncResolve(aHostName, 0, helper,
|
||||
NS_GetCurrentThread(),
|
||||
getter_AddRefs(helper->mRequest))))
|
||||
return false;
|
||||
|
||||
if (aTimeout && helper->mRequest) {
|
||||
if (!mTimer)
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (mTimer) {
|
||||
mTimer->InitWithCallback(helper, aTimeout, nsITimer::TYPE_ONE_SHOT);
|
||||
helper->mTimer = mTimer;
|
||||
}
|
||||
}
|
||||
|
||||
// Spin the event loop of the pac thread until lookup is complete.
|
||||
// nsPACman is responsible for keeping a queue and only allowing
|
||||
// one PAC execution at a time even when it is called re-entrantly.
|
||||
@ -305,11 +357,28 @@ JSBool PACResolve(const nsCString &aHostName, nsCString &aDottedDecimal)
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread());
|
||||
|
||||
if (NS_FAILED(helper->mStatus) ||
|
||||
NS_FAILED(helper->mResponse->GetNextAddrAsString(aDottedDecimal)))
|
||||
NS_FAILED(helper->mResponse->GetNextAddr(0, aNetAddr)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool PACResolveToString(const nsCString &aHostName,
|
||||
nsCString &aDottedDecimal,
|
||||
unsigned int aTimeout)
|
||||
{
|
||||
PRNetAddr netAddr;
|
||||
if (!PACResolve(aHostName, &netAddr, aTimeout))
|
||||
return false;
|
||||
|
||||
char dottedDecimal[128];
|
||||
if (PR_NetAddrToString(&netAddr, dottedDecimal, sizeof(dottedDecimal)) != PR_SUCCESS)
|
||||
return false;
|
||||
|
||||
aDottedDecimal.Assign(dottedDecimal);
|
||||
return true;
|
||||
}
|
||||
|
||||
// dnsResolve(host) javascript implementation
|
||||
static
|
||||
JSBool PACDnsResolve(JSContext *cx, unsigned int argc, jsval *vp)
|
||||
@ -324,11 +393,11 @@ JSBool PACDnsResolve(JSContext *cx, unsigned int argc, jsval *vp)
|
||||
return false;
|
||||
|
||||
nsDependentJSString hostName;
|
||||
nsCString dottedDecimal;
|
||||
nsAutoCString dottedDecimal;
|
||||
|
||||
if (!hostName.init(cx, arg1))
|
||||
return false;
|
||||
if (!PACResolve(NS_ConvertUTF16toUTF8(hostName), dottedDecimal))
|
||||
if (!PACResolveToString(NS_ConvertUTF16toUTF8(hostName), dottedDecimal, 0))
|
||||
return false;
|
||||
|
||||
JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get());
|
||||
@ -345,21 +414,12 @@ JSBool PACMyIpAddress(JSContext *cx, unsigned int argc, jsval *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCString hostName;
|
||||
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
||||
if (!dns || NS_FAILED(dns->GetMyHostName(hostName))) {
|
||||
hostName.AssignLiteral("127.0.0.1");
|
||||
if (!sRunning) {
|
||||
NS_WARNING("PAC myIPAddress without a running ProxyAutoConfig object");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsCString dottedDecimal;
|
||||
if (!PACResolve(hostName, dottedDecimal)) {
|
||||
dottedDecimal.AssignLiteral("127.0.0.1");
|
||||
}
|
||||
|
||||
JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get());
|
||||
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(dottedDecimalString));
|
||||
return true;
|
||||
return sRunning->MyIPAddress(vp);
|
||||
}
|
||||
|
||||
// proxyAlert(msg) javascript implementation
|
||||
@ -498,7 +558,7 @@ ProxyAutoConfig::Init(const nsCString &aPACURI,
|
||||
mPACScript = sPacUtils;
|
||||
mPACScript.Append(aPACScript);
|
||||
|
||||
if (!mRunning)
|
||||
if (!sRunning)
|
||||
return SetupJS();
|
||||
|
||||
mJSNeedsSetup = true;
|
||||
@ -509,7 +569,7 @@ nsresult
|
||||
ProxyAutoConfig::SetupJS()
|
||||
{
|
||||
mJSNeedsSetup = false;
|
||||
NS_ABORT_IF_FALSE(!mRunning, "JIT is running");
|
||||
NS_ABORT_IF_FALSE(!sRunning, "JIT is running");
|
||||
|
||||
delete mJSRuntime;
|
||||
mJSRuntime = nullptr;
|
||||
@ -560,9 +620,10 @@ ProxyAutoConfig::GetProxyForURI(const nsCString &aTestURI,
|
||||
JSContext *cx = mJSRuntime->Context();
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
// the mRunning flag keeps a new PAC file from being installed
|
||||
// the sRunning flag keeps a new PAC file from being installed
|
||||
// while the event loop is spinning on a DNS function. Don't early return.
|
||||
mRunning = true;
|
||||
sRunning = this;
|
||||
mRunningHost = aTestHost;
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
js::RootedString uriString(cx, JS_NewStringCopyZ(cx, aTestURI.get()));
|
||||
@ -585,7 +646,9 @@ ProxyAutoConfig::GetProxyForURI(const nsCString &aTestURI,
|
||||
}
|
||||
}
|
||||
}
|
||||
mRunning = false;
|
||||
|
||||
mRunningHost.Truncate();
|
||||
sRunning = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -611,7 +674,7 @@ ProxyAutoConfig::Shutdown()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread for shutdown");
|
||||
|
||||
if (mRunning || mShutdown)
|
||||
if (sRunning || mShutdown)
|
||||
return;
|
||||
|
||||
mShutdown = true;
|
||||
@ -619,5 +682,109 @@ ProxyAutoConfig::Shutdown()
|
||||
mJSRuntime = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyAutoConfig::SrcAddress(const PRNetAddr *remoteAddress, nsCString &localAddress)
|
||||
{
|
||||
PRFileDesc *fd;
|
||||
fd = PR_OpenUDPSocket(remoteAddress->raw.family);
|
||||
if (!fd)
|
||||
return false;
|
||||
|
||||
if (PR_Connect(fd, remoteAddress, 0) != PR_SUCCESS) {
|
||||
PR_Close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
PRNetAddr localName;
|
||||
if (PR_GetSockName(fd, &localName) != PR_SUCCESS) {
|
||||
PR_Close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
PR_Close(fd);
|
||||
|
||||
char dottedDecimal[128];
|
||||
if (PR_NetAddrToString(&localName, dottedDecimal, sizeof(dottedDecimal)) != PR_SUCCESS)
|
||||
return false;
|
||||
|
||||
localAddress.Assign(dottedDecimal);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// hostName is run through a dns lookup and then a udp socket is connected
|
||||
// to the result. If that all works, the local IP address of the socket is
|
||||
// returned to the javascript caller and true is returned from this function.
|
||||
// otherwise false is returned.
|
||||
bool
|
||||
ProxyAutoConfig::MyIPAddressTryHost(const nsCString &hostName,
|
||||
unsigned int timeout,
|
||||
jsval *vp)
|
||||
{
|
||||
PRNetAddr remoteAddress;
|
||||
nsAutoCString localDottedDecimal;
|
||||
JSContext *cx = mJSRuntime->Context();
|
||||
|
||||
if (PACResolve(hostName, &remoteAddress, timeout) &&
|
||||
SrcAddress(&remoteAddress, localDottedDecimal)) {
|
||||
JSString *dottedDecimalString =
|
||||
JS_NewStringCopyZ(cx, localDottedDecimal.get());
|
||||
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(dottedDecimalString));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyAutoConfig::MyIPAddress(jsval *vp)
|
||||
{
|
||||
nsAutoCString remoteDottedDecimal;
|
||||
nsAutoCString localDottedDecimal;
|
||||
JSContext *cx = mJSRuntime->Context();
|
||||
|
||||
// first, lookup the local address of a socket connected
|
||||
// to the host of uri being resolved by the pac file. This is
|
||||
// v6 safe.. but is the last step like that
|
||||
if (MyIPAddressTryHost(mRunningHost, kTimeout, vp))
|
||||
return true;
|
||||
|
||||
// next, look for a route to a public internet address that doesn't need DNS.
|
||||
// This is the google anycast dns address, but it doesn't matter if it
|
||||
// remains operable (as we don't contact it) as long as the address stays
|
||||
// in commonly routed IP address space.
|
||||
remoteDottedDecimal.AssignLiteral("8.8.8.8");
|
||||
if (MyIPAddressTryHost(remoteDottedDecimal, 0, vp))
|
||||
return true;
|
||||
|
||||
// next, use the old algorithm based on the local hostname
|
||||
nsAutoCString hostName;
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
||||
if (dns && NS_SUCCEEDED(dns->GetMyHostName(hostName)) &&
|
||||
PACResolveToString(hostName, localDottedDecimal, kTimeout)) {
|
||||
JSString *dottedDecimalString =
|
||||
JS_NewStringCopyZ(cx, localDottedDecimal.get());
|
||||
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(dottedDecimalString));
|
||||
return true;
|
||||
}
|
||||
|
||||
// next try a couple RFC 1918 variants.. maybe there is a
|
||||
// local route
|
||||
remoteDottedDecimal.AssignLiteral("192.168.0.1");
|
||||
if (MyIPAddressTryHost(remoteDottedDecimal, 0, vp))
|
||||
return true;
|
||||
|
||||
// more RFC 1918
|
||||
remoteDottedDecimal.AssignLiteral("10.0.0.1");
|
||||
if (MyIPAddressTryHost(remoteDottedDecimal, 0, vp))
|
||||
return true;
|
||||
|
||||
// who knows? let's fallback to localhost
|
||||
localDottedDecimal.AssignLiteral("127.0.0.1");
|
||||
JSString *dottedDecimalString =
|
||||
JS_NewStringCopyZ(cx, localDottedDecimal.get());
|
||||
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(dottedDecimalString));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::net
|
||||
|
@ -8,6 +8,10 @@
|
||||
#define ProxyAutoConfig_h__
|
||||
|
||||
#include "nsString.h"
|
||||
#include "jsapi.h"
|
||||
#include "prio.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
@ -21,7 +25,6 @@ class ProxyAutoConfig {
|
||||
public:
|
||||
ProxyAutoConfig()
|
||||
: mJSRuntime(nullptr)
|
||||
, mRunning(false)
|
||||
, mJSNeedsSetup(false)
|
||||
, mShutdown(false)
|
||||
{
|
||||
@ -33,6 +36,9 @@ public:
|
||||
const nsCString &aPACScript);
|
||||
void Shutdown();
|
||||
void GC();
|
||||
bool MyIPAddress(jsval *vp);
|
||||
bool ResolveAddress(const nsCString &aHostName,
|
||||
PRNetAddr *aNetAddr, unsigned int aTimeout);
|
||||
|
||||
/**
|
||||
* Get the proxy string for the specified URI. The proxy string is
|
||||
@ -73,15 +79,22 @@ public:
|
||||
nsACString &result);
|
||||
|
||||
private:
|
||||
const static unsigned int kTimeout = 1000; // ms to allow for myipaddress dns queries
|
||||
|
||||
// used to compile the PAC file and setup the execution context
|
||||
nsresult SetupJS();
|
||||
|
||||
bool SrcAddress(const PRNetAddr *remoteAddress, nsCString &localAddress);
|
||||
bool MyIPAddressTryHost(const nsCString &hostName, unsigned int timeout,
|
||||
jsval *vp);
|
||||
|
||||
JSRuntimeWrapper *mJSRuntime;
|
||||
bool mRunning;
|
||||
bool mJSNeedsSetup;
|
||||
bool mShutdown;
|
||||
nsCString mPACScript;
|
||||
nsCString mPACURI;
|
||||
nsCString mRunningHost;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
|
||||
}} // namespace mozilla::net
|
||||
|
@ -14,6 +14,7 @@
|
||||
// run_pac_test();
|
||||
// run_pac_cancel_test();
|
||||
// run_proxy_host_filters_test();
|
||||
// run_myipaddress_test();
|
||||
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
@ -557,6 +558,45 @@ function host_filters_4()
|
||||
prefs.setCharPref("network.proxy.no_proxies_on", "");
|
||||
do_check_eq(prefs.getCharPref("network.proxy.no_proxies_on"), "");
|
||||
|
||||
run_myipaddress_test();
|
||||
}
|
||||
|
||||
function run_myipaddress_test()
|
||||
{
|
||||
// This test makes sure myIpAddress() comes up with some valid
|
||||
// IP address other than localhost. The DUT must be configured with
|
||||
// an Internet route for this to work - though no Internet traffic
|
||||
// should be created.
|
||||
|
||||
var pac = 'data:text/plain,' +
|
||||
'function FindProxyForURL(url, host) {' +
|
||||
' return "PROXY " + myIpAddress() + ":1234";' +
|
||||
'}';
|
||||
|
||||
// no traffic to this IP is ever sent, it is just a public IP that
|
||||
// does not require DNS to determine a route.
|
||||
var uri = ios.newURI("http://192.0.43.10/", null, null);
|
||||
|
||||
prefs.setIntPref("network.proxy.type", 2);
|
||||
prefs.setCharPref("network.proxy.autoconfig_url", pac);
|
||||
|
||||
var cb = new resolveCallback();
|
||||
cb.nextFunction = myipaddress_callback;
|
||||
var req = pps.asyncResolve(uri, 0, cb);
|
||||
}
|
||||
|
||||
function myipaddress_callback(pi)
|
||||
{
|
||||
do_check_neq(pi, null);
|
||||
do_check_eq(pi.type, "http");
|
||||
do_check_eq(pi.port, 1234);
|
||||
|
||||
// make sure we didn't return localhost
|
||||
do_check_neq(pi.host, null);
|
||||
do_check_neq(pi.host, "127.0.0.1");
|
||||
do_check_neq(pi.host, "::1");
|
||||
|
||||
prefs.setIntPref("network.proxy.type", 0);
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user