1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ProxyAutoConfig.h"
8 #include "nsICancelable.h"
9 #include "nsIDNSListener.h"
10 #include "nsIDNSRecord.h"
11 #include "nsIDNSService.h"
13 #include "nsThreadUtils.h"
14 #include "nsIConsoleService.h"
15 #include "nsIURLParser.h"
16 #include "nsJSUtils.h"
17 #include "jsfriendapi.h"
18 #include "js/CallAndConstruct.h" // JS_CallFunctionName
19 #include "js/CompilationAndEvaluation.h" // JS::Compile
20 #include "js/ContextOptions.h"
21 #include "js/Initialization.h"
22 #include "js/PropertyAndElement.h" // JS_DefineFunctions, JS_GetProperty
23 #include "js/PropertySpec.h"
24 #include "js/SourceText.h" // JS::Source{Ownership,Text}
25 #include "js/Utility.h"
26 #include "js/Warnings.h" // JS::SetWarningReporter
29 #include "mozilla/Atomics.h"
30 #include "mozilla/SpinEventLoopUntil.h"
31 #include "mozilla/ipc/Endpoint.h"
32 #include "mozilla/net/DNS.h"
33 #include "mozilla/net/SocketProcessChild.h"
34 #include "mozilla/net/SocketProcessParent.h"
35 #include "mozilla/net/ProxyAutoConfigChild.h"
36 #include "mozilla/net/ProxyAutoConfigParent.h"
37 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
38 #include "nsServiceManagerUtils.h"
41 #if defined(XP_MACOSX)
42 # include "nsMacUtilsImpl.h"
45 #include "XPCSelfHostedShmem.h"
50 // These are some global helper symbols the PAC format requires that we provide
51 // that are initialized as part of the global javascript context used for PAC
52 // evaluations. Additionally dnsResolve(host) and myIpAddress() are supplied in
53 // the same context but are implemented as c++ helpers. alert(msg) is similarly
56 // Per ProxyAutoConfig::Init, this data must be ASCII.
58 static const char sAsciiPacUtils
[] =
59 #include "ascii_pac_utils.inc"
62 // sRunning is defined for the helper functions only while the
63 // Javascript engine is running and the PAC object cannot be deleted
65 static Atomic
<uint32_t, Relaxed
>& RunningIndex() {
66 static Atomic
<uint32_t, Relaxed
> sRunningIndex(0xdeadbeef);
69 static ProxyAutoConfig
* GetRunning() {
70 MOZ_ASSERT(RunningIndex() != 0xdeadbeef);
71 return static_cast<ProxyAutoConfig
*>(PR_GetThreadPrivate(RunningIndex()));
74 static void SetRunning(ProxyAutoConfig
* arg
) {
75 MOZ_ASSERT(RunningIndex() != 0xdeadbeef);
76 MOZ_DIAGNOSTIC_ASSERT_IF(!arg
, GetRunning() != nullptr);
77 MOZ_DIAGNOSTIC_ASSERT_IF(arg
, GetRunning() == nullptr);
78 PR_SetThreadPrivate(RunningIndex(), arg
);
81 // The PACResolver is used for dnsResolve()
82 class PACResolver final
: public nsIDNSListener
,
83 public nsITimerCallback
,
86 NS_DECL_THREADSAFE_ISUPPORTS
88 explicit PACResolver(nsIEventTarget
* aTarget
)
89 : mStatus(NS_ERROR_FAILURE
),
90 mMainThreadEventTarget(aTarget
),
91 mMutex("PACResolver::Mutex") {}
94 NS_IMETHOD
OnLookupComplete(nsICancelable
* request
, nsIDNSRecord
* record
,
95 nsresult status
) override
{
96 nsCOMPtr
<nsITimer
> timer
;
98 MutexAutoLock
lock(mMutex
);
113 NS_IMETHOD
Notify(nsITimer
* timer
) override
{
114 nsCOMPtr
<nsICancelable
> request
;
116 MutexAutoLock
lock(mMutex
);
117 request
.swap(mRequest
);
121 request
->Cancel(NS_ERROR_NET_TIMEOUT
);
127 NS_IMETHOD
GetName(nsACString
& aName
) override
{
128 aName
.AssignLiteral("PACResolver");
133 nsCOMPtr
<nsICancelable
> mRequest
;
134 nsCOMPtr
<nsIDNSRecord
> mResponse
;
135 nsCOMPtr
<nsITimer
> mTimer
;
136 nsCOMPtr
<nsIEventTarget
> mMainThreadEventTarget
;
137 Mutex mMutex MOZ_UNANNOTATED
;
140 ~PACResolver() = default;
142 NS_IMPL_ISUPPORTS(PACResolver
, nsIDNSListener
, nsITimerCallback
, nsINamed
)
144 static void PACLogToConsole(nsString
& aMessage
) {
145 if (XRE_IsSocketProcess()) {
146 auto task
= [message(aMessage
)]() {
147 SocketProcessChild
* child
= SocketProcessChild::GetSingleton();
149 Unused
<< child
->SendOnConsoleMessage(message
);
152 if (NS_IsMainThread()) {
155 NS_DispatchToMainThread(NS_NewRunnableFunction("PACLogToConsole", task
));
160 nsCOMPtr
<nsIConsoleService
> consoleService
=
161 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
162 if (!consoleService
) return;
164 consoleService
->LogStringMessage(aMessage
.get());
167 // Javascript errors and warnings are logged to the main error console
168 static void PACLogErrorOrWarning(const nsAString
& aKind
,
169 JSErrorReport
* aReport
) {
170 nsString
formattedMessage(u
"PAC Execution "_ns
);
171 formattedMessage
+= aKind
;
172 formattedMessage
+= u
": "_ns
;
173 if (aReport
->message()) {
174 formattedMessage
.Append(NS_ConvertUTF8toUTF16(aReport
->message().c_str()));
176 formattedMessage
+= u
" ["_ns
;
177 formattedMessage
.Append(aReport
->linebuf(), aReport
->linebufLength());
178 formattedMessage
+= u
"]"_ns
;
179 PACLogToConsole(formattedMessage
);
182 static void PACWarningReporter(JSContext
* aCx
, JSErrorReport
* aReport
) {
184 MOZ_ASSERT(aReport
->isWarning());
186 PACLogErrorOrWarning(u
"Warning"_ns
, aReport
);
189 class MOZ_STACK_CLASS AutoPACErrorReporter
{
193 explicit AutoPACErrorReporter(JSContext
* aCx
) : mCx(aCx
) {}
194 ~AutoPACErrorReporter() {
195 if (!JS_IsExceptionPending(mCx
)) {
198 JS::ExceptionStack
exnStack(mCx
);
199 if (!JS::StealPendingExceptionStack(mCx
, &exnStack
)) {
203 JS::ErrorReportBuilder
report(mCx
);
204 if (!report
.init(mCx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
205 JS_ClearPendingException(mCx
);
209 PACLogErrorOrWarning(u
"Error"_ns
, report
.report());
213 // timeout of 0 means the normal necko timeout strategy, otherwise the dns
214 // request will be canceled after aTimeout milliseconds
215 static bool PACResolve(const nsACString
& aHostName
, NetAddr
* aNetAddr
,
216 unsigned int aTimeout
) {
218 NS_WARNING("PACResolve without a running ProxyAutoConfig object");
222 return GetRunning()->ResolveAddress(aHostName
, aNetAddr
, aTimeout
);
225 ProxyAutoConfig::ProxyAutoConfig()
228 MOZ_COUNT_CTOR(ProxyAutoConfig
);
231 bool ProxyAutoConfig::ResolveAddress(const nsACString
& aHostName
,
232 NetAddr
* aNetAddr
, unsigned int aTimeout
) {
233 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
234 if (!dns
) return false;
236 RefPtr
<PACResolver
> helper
= new PACResolver(mMainThreadEventTarget
);
237 OriginAttributes attrs
;
239 // When the PAC script attempts to resolve a domain, we must make sure we
240 // don't use TRR, otherwise the TRR channel might also attempt to resolve
241 // a name and we'll have a deadlock.
242 nsIDNSService::DNSFlags flags
=
243 nsIDNSService::RESOLVE_PRIORITY_MEDIUM
|
244 nsIDNSService::GetFlagsFromTRRMode(nsIRequest::TRR_DISABLED_MODE
);
246 if (NS_FAILED(dns
->AsyncResolveNative(
247 aHostName
, nsIDNSService::RESOLVE_TYPE_DEFAULT
, flags
, nullptr,
248 helper
, GetCurrentSerialEventTarget(), attrs
,
249 getter_AddRefs(helper
->mRequest
)))) {
253 if (aTimeout
&& helper
->mRequest
) {
254 if (!mTimer
) mTimer
= NS_NewTimer();
256 mTimer
->SetTarget(mMainThreadEventTarget
);
257 mTimer
->InitWithCallback(helper
, aTimeout
, nsITimer::TYPE_ONE_SHOT
);
258 helper
->mTimer
= mTimer
;
262 // Spin the event loop of the pac thread until lookup is complete.
263 // nsPACman is responsible for keeping a queue and only allowing
264 // one PAC execution at a time even when it is called re-entrantly.
265 SpinEventLoopUntil("ProxyAutoConfig::ResolveAddress"_ns
, [&, helper
, this]() {
266 if (!helper
->mRequest
) {
269 if (this->mShutdown
) {
270 NS_WARNING("mShutdown set with PAC request not cancelled");
271 MOZ_ASSERT(NS_FAILED(helper
->mStatus
));
277 if (NS_FAILED(helper
->mStatus
)) {
281 nsCOMPtr
<nsIDNSAddrRecord
> rec
= do_QueryInterface(helper
->mResponse
);
282 return !(!rec
|| NS_FAILED(rec
->GetNextAddr(0, aNetAddr
)));
285 static bool PACResolveToString(const nsACString
& aHostName
,
286 nsCString
& aDottedDecimal
,
287 unsigned int aTimeout
) {
289 if (!PACResolve(aHostName
, &netAddr
, aTimeout
)) return false;
291 char dottedDecimal
[128];
292 if (!netAddr
.ToStringBuffer(dottedDecimal
, sizeof(dottedDecimal
))) {
296 aDottedDecimal
.Assign(dottedDecimal
);
300 // dnsResolve(host) javascript implementation
301 static bool PACDnsResolve(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
302 JS::CallArgs args
= CallArgsFromVp(argc
, vp
);
304 if (NS_IsMainThread()) {
305 NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?");
309 if (!args
.requireAtLeast(cx
, "dnsResolve", 1)) return false;
311 // Previously we didn't check the type of the argument, so just converted it
312 // to string. A badly written PAC file oculd pass null or undefined here
313 // which could lead to odd results if there are any hosts called "null"
314 // on the network. See bug 1724345 comment 6.
315 if (!args
[0].isString()) {
316 args
.rval().setNull();
320 JS::Rooted
<JSString
*> arg1(cx
);
321 arg1
= args
[0].toString();
323 nsAutoJSString hostName
;
324 nsAutoCString dottedDecimal
;
326 if (!hostName
.init(cx
, arg1
)) return false;
327 if (PACResolveToString(NS_ConvertUTF16toUTF8(hostName
), dottedDecimal
, 0)) {
328 JSString
* dottedDecimalString
= JS_NewStringCopyZ(cx
, dottedDecimal
.get());
329 if (!dottedDecimalString
) {
333 args
.rval().setString(dottedDecimalString
);
335 args
.rval().setNull();
341 // myIpAddress() javascript implementation
342 static bool PACMyIpAddress(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
343 JS::CallArgs args
= JS::CallArgsFromVp(argc
, vp
);
345 if (NS_IsMainThread()) {
346 NS_WARNING("DNS Resolution From PAC on Main Thread. How did that happen?");
351 NS_WARNING("PAC myIPAddress without a running ProxyAutoConfig object");
355 return GetRunning()->MyIPAddress(args
);
358 // proxyAlert(msg) javascript implementation
359 static bool PACProxyAlert(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
360 JS::CallArgs args
= CallArgsFromVp(argc
, vp
);
362 if (!args
.requireAtLeast(cx
, "alert", 1)) return false;
364 JS::Rooted
<JSString
*> arg1(cx
, JS::ToString(cx
, args
[0]));
365 if (!arg1
) return false;
367 nsAutoJSString message
;
368 if (!message
.init(cx
, arg1
)) return false;
370 nsAutoString alertMessage
;
371 alertMessage
.AssignLiteral(u
"PAC-alert: ");
372 alertMessage
.Append(message
);
373 PACLogToConsole(alertMessage
);
375 args
.rval().setUndefined(); /* return undefined */
379 static const JSFunctionSpec PACGlobalFunctions
[] = {
380 JS_FN("dnsResolve", PACDnsResolve
, 1, 0),
382 // a global "var pacUseMultihomedDNS = true;" will change behavior
383 // of myIpAddress to actively use DNS
384 JS_FN("myIpAddress", PACMyIpAddress
, 0, 0),
385 JS_FN("alert", PACProxyAlert
, 1, 0), JS_FS_END
};
387 // JSContextWrapper is a c++ object that manages the context for the JS engine
388 // used on the PAC thread. It is initialized and destroyed on the PAC thread.
389 class JSContextWrapper
{
391 static JSContextWrapper
* Create(uint32_t aExtraHeapSize
) {
392 JSContext
* cx
= JS_NewContext(JS::DefaultHeapMaxBytes
+ aExtraHeapSize
);
393 if (NS_WARN_IF(!cx
)) return nullptr;
395 JS::ContextOptionsRef(cx
).setDisableIon().setDisableEvalSecurityChecks();
397 JSContextWrapper
* entry
= new JSContextWrapper(cx
);
398 if (NS_FAILED(entry
->Init())) {
406 JSContext
* Context() const { return mContext
; }
408 JSObject
* Global() const { return mGlobal
; }
410 ~JSContextWrapper() {
413 MOZ_COUNT_DTOR(JSContextWrapper
);
416 JS_DestroyContext(mContext
);
420 void SetOK() { mOK
= true; }
422 bool IsOK() { return mOK
; }
426 JS::PersistentRooted
<JSObject
*> mGlobal
;
429 static const JSClass sGlobalClass
;
431 explicit JSContextWrapper(JSContext
* cx
)
432 : mContext(cx
), mGlobal(cx
, nullptr), mOK(false) {
433 MOZ_COUNT_CTOR(JSContextWrapper
);
438 * Not setting this will cause JS_CHECK_RECURSION to report false
441 JS_SetNativeStackQuota(mContext
, 128 * sizeof(size_t) * 1024);
443 JS::SetWarningReporter(mContext
, PACWarningReporter
);
445 // When available, set the self-hosted shared memory to be read, so that
446 // we can decode the self-hosted content instead of parsing it.
448 auto& shm
= xpc::SelfHostedShmem::GetSingleton();
449 JS::SelfHostedCache selfHostedContent
= shm
.Content();
451 if (!JS::InitSelfHostedCode(mContext
, selfHostedContent
)) {
452 return NS_ERROR_OUT_OF_MEMORY
;
456 JS::RealmOptions options
;
457 options
.creationOptions().setNewCompartmentInSystemZone();
459 .setClampAndJitterTime(false)
460 .setReduceTimerPrecisionCallerType(
461 RTPCallerTypeToToken(RTPCallerType::Normal
));
462 mGlobal
= JS_NewGlobalObject(mContext
, &sGlobalClass
, nullptr,
463 JS::DontFireOnNewGlobalHook
, options
);
465 JS_ClearPendingException(mContext
);
466 return NS_ERROR_OUT_OF_MEMORY
;
468 JS::Rooted
<JSObject
*> global(mContext
, mGlobal
);
470 JSAutoRealm
ar(mContext
, global
);
471 AutoPACErrorReporter
aper(mContext
);
472 if (!JS_DefineFunctions(mContext
, global
, PACGlobalFunctions
)) {
473 return NS_ERROR_FAILURE
;
476 JS_FireOnNewGlobalObject(mContext
, global
);
482 const JSClass
JSContextWrapper::sGlobalClass
= {"PACResolutionThreadGlobal",
483 JSCLASS_GLOBAL_FLAGS
,
484 &JS::DefaultGlobalClassOps
};
486 void ProxyAutoConfig::SetThreadLocalIndex(uint32_t index
) {
487 RunningIndex() = index
;
490 nsresult
ProxyAutoConfig::ConfigurePAC(const nsACString
& aPACURI
,
491 const nsACString
& aPACScriptData
,
493 uint32_t aExtraHeapSize
,
494 nsISerialEventTarget
* aEventTarget
) {
495 mShutdown
= false; // Shutdown needs to be called prior to destruction
499 // The full PAC script data is the concatenation of 1) the various functions
500 // exposed to PAC scripts in |sAsciiPacUtils| and 2) the user-provided PAC
501 // script data. Historically this was single-byte Latin-1 text (usually just
502 // ASCII, but bug 296163 has a real-world Latin-1 example). We now support
503 // UTF-8 if the full data validates as UTF-8, before falling back to Latin-1.
504 // (Technically this is a breaking change: intentional Latin-1 scripts that
505 // happen to be valid UTF-8 may have different behavior. We assume such cases
506 // are vanishingly rare.)
508 // Supporting both UTF-8 and Latin-1 requires that the functions exposed to
509 // PAC scripts be both UTF-8- and Latin-1-compatible: that is, they must be
511 mConcatenatedPACData
= sAsciiPacUtils
;
512 if (!mConcatenatedPACData
.Append(aPACScriptData
, mozilla::fallible
)) {
513 return NS_ERROR_OUT_OF_MEMORY
;
516 mIncludePath
= aIncludePath
;
517 mExtraHeapSize
= aExtraHeapSize
;
518 mMainThreadEventTarget
= aEventTarget
;
520 if (!GetRunning()) return SetupJS();
522 mJSNeedsSetup
= true;
526 nsresult
ProxyAutoConfig::SetupJS() {
527 mJSNeedsSetup
= false;
528 MOZ_DIAGNOSTIC_ASSERT(!GetRunning(), "JIT is running");
530 return NS_ERROR_ALREADY_INITIALIZED
;
533 #if defined(XP_MACOSX)
534 nsMacUtilsImpl::EnableTCSMIfAvailable();
538 mJSContext
= nullptr;
540 if (mConcatenatedPACData
.IsEmpty()) return NS_ERROR_FAILURE
;
542 NS_GetCurrentThread()->SetCanInvokeJS(true);
544 mJSContext
= JSContextWrapper::Create(mExtraHeapSize
);
545 if (!mJSContext
) return NS_ERROR_FAILURE
;
547 JSContext
* cx
= mJSContext
->Context();
548 JSAutoRealm
ar(cx
, mJSContext
->Global());
549 AutoPACErrorReporter
aper(cx
);
551 // check if this is a data: uri so that we don't spam the js console with
552 // huge meaningless strings. this is not on the main thread, so it can't
553 // use nsIURI scheme methods
555 nsDependentCSubstring(mPACURI
, 0, 5).LowerCaseEqualsASCII("data:", 5);
559 JS::Rooted
<JSObject
*> global(cx
, mJSContext
->Global());
561 auto CompilePACScript
= [this](JSContext
* cx
) -> JSScript
* {
562 JS::CompileOptions
options(cx
);
563 options
.setSkipFilenameValidation(true);
564 options
.setFileAndLine(this->mPACURI
.get(), 1);
566 // Per ProxyAutoConfig::Init, compile as UTF-8 if the full data is UTF-8,
567 // and otherwise inflate Latin-1 to UTF-16 and compile that.
568 const char* scriptData
= this->mConcatenatedPACData
.get();
569 size_t scriptLength
= this->mConcatenatedPACData
.Length();
570 if (mozilla::IsUtf8(mozilla::Span(scriptData
, scriptLength
))) {
571 JS::SourceText
<Utf8Unit
> srcBuf
;
572 if (!srcBuf
.init(cx
, scriptData
, scriptLength
,
573 JS::SourceOwnership::Borrowed
)) {
577 return JS::Compile(cx
, options
, srcBuf
);
580 // nsReadableUtils.h says that "ASCII" is a misnomer "for legacy reasons",
581 // and this handles not just ASCII but Latin-1 too.
582 NS_ConvertASCIItoUTF16
inflated(this->mConcatenatedPACData
);
584 JS::SourceText
<char16_t
> source
;
585 if (!source
.init(cx
, inflated
.get(), inflated
.Length(),
586 JS::SourceOwnership::Borrowed
)) {
590 return JS::Compile(cx
, options
, source
);
593 JS::Rooted
<JSScript
*> script(cx
, CompilePACScript(cx
));
594 if (!script
|| !JS_ExecuteScript(cx
, script
)) {
595 nsString
alertMessage(u
"PAC file failed to install from "_ns
);
597 alertMessage
+= u
"data: URI"_ns
;
599 alertMessage
+= NS_ConvertUTF8toUTF16(mPACURI
);
601 PACLogToConsole(alertMessage
);
603 return NS_ERROR_FAILURE
;
608 nsString
alertMessage(u
"PAC file installed from "_ns
);
610 alertMessage
+= u
"data: URI"_ns
;
612 alertMessage
+= NS_ConvertUTF8toUTF16(mPACURI
);
614 PACLogToConsole(alertMessage
);
616 // we don't need these now
617 mConcatenatedPACData
.Truncate();
623 void ProxyAutoConfig::GetProxyForURIWithCallback(
624 const nsACString
& aTestURI
, const nsACString
& aTestHost
,
625 std::function
<void(nsresult aStatus
, const nsACString
& aResult
)>&&
627 nsAutoCString result
;
628 nsresult status
= GetProxyForURI(aTestURI
, aTestHost
, result
);
629 aCallback(status
, result
);
632 nsresult
ProxyAutoConfig::GetProxyForURI(const nsACString
& aTestURI
,
633 const nsACString
& aTestHost
,
634 nsACString
& result
) {
635 if (mJSNeedsSetup
) SetupJS();
637 if (!mJSContext
|| !mJSContext
->IsOK()) return NS_ERROR_NOT_AVAILABLE
;
639 JSContext
* cx
= mJSContext
->Context();
640 JSAutoRealm
ar(cx
, mJSContext
->Global());
641 AutoPACErrorReporter
aper(cx
);
643 // the sRunning flag keeps a new PAC file from being installed
644 // while the event loop is spinning on a DNS function. Don't early return.
646 mRunningHost
= aTestHost
;
648 nsresult rv
= NS_ERROR_FAILURE
;
649 nsCString
clensedURI(aTestURI
);
652 nsCOMPtr
<nsIURLParser
> urlParser
=
653 do_GetService(NS_STDURLPARSER_CONTRACTID
);
658 uint32_t authorityPos
;
659 int32_t authorityLen
;
661 rv
= urlParser
->ParseURL(aTestURI
.BeginReading(), aTestURI
.Length(),
662 &schemePos
, &schemeLen
, &authorityPos
,
663 &authorityLen
, &pathPos
, &pathLen
);
665 if (NS_SUCCEEDED(rv
)) {
667 // cut off the path but leave the initial slash
670 aTestURI
.Left(clensedURI
, aTestURI
.Length() - pathLen
);
674 JS::Rooted
<JSString
*> uriString(
676 JS_NewStringCopyN(cx
, clensedURI
.BeginReading(), clensedURI
.Length()));
677 JS::Rooted
<JSString
*> hostString(
678 cx
, JS_NewStringCopyN(cx
, aTestHost
.BeginReading(), aTestHost
.Length()));
680 if (uriString
&& hostString
) {
681 JS::RootedValueArray
<2> args(cx
);
682 args
[0].setString(uriString
);
683 args
[1].setString(hostString
);
685 JS::Rooted
<JS::Value
> rval(cx
);
686 JS::Rooted
<JSObject
*> global(cx
, mJSContext
->Global());
687 bool ok
= JS_CallFunctionName(cx
, global
, "FindProxyForURL", args
, &rval
);
689 if (ok
&& rval
.isString()) {
690 nsAutoJSString pacString
;
691 if (pacString
.init(cx
, rval
.toString())) {
692 CopyUTF16toUTF8(pacString
, result
);
698 mRunningHost
.Truncate();
703 void ProxyAutoConfig::GC() {
704 if (!mJSContext
|| !mJSContext
->IsOK()) return;
706 JSAutoRealm
ar(mJSContext
->Context(), mJSContext
->Global());
707 JS_MaybeGC(mJSContext
->Context());
710 ProxyAutoConfig::~ProxyAutoConfig() {
711 MOZ_COUNT_DTOR(ProxyAutoConfig
);
712 MOZ_ASSERT(mShutdown
, "Shutdown must be called before dtor.");
713 NS_ASSERTION(!mJSContext
,
714 "~ProxyAutoConfig leaking JS context that "
715 "should have been deleted on pac thread");
718 void ProxyAutoConfig::Shutdown() {
719 MOZ_ASSERT(!NS_IsMainThread(), "wrong thread for shutdown");
721 if (NS_WARN_IF(GetRunning()) || mShutdown
) {
727 mJSContext
= nullptr;
730 bool ProxyAutoConfig::SrcAddress(const NetAddr
* remoteAddress
,
731 nsCString
& localAddress
) {
733 fd
= PR_OpenUDPSocket(remoteAddress
->raw
.family
);
734 if (!fd
) return false;
736 PRNetAddr prRemoteAddress
;
737 NetAddrToPRNetAddr(remoteAddress
, &prRemoteAddress
);
738 if (PR_Connect(fd
, &prRemoteAddress
, 0) != PR_SUCCESS
) {
744 if (PR_GetSockName(fd
, &localName
) != PR_SUCCESS
) {
751 char dottedDecimal
[128];
752 if (PR_NetAddrToString(&localName
, dottedDecimal
, sizeof(dottedDecimal
)) !=
757 localAddress
.Assign(dottedDecimal
);
762 // hostName is run through a dns lookup and then a udp socket is connected
763 // to the result. If that all works, the local IP address of the socket is
764 // returned to the javascript caller and |*aResult| is set to true. Otherwise
765 // |*aResult| is set to false.
766 bool ProxyAutoConfig::MyIPAddressTryHost(const nsACString
& hostName
,
767 unsigned int timeout
,
768 const JS::CallArgs
& aArgs
,
772 NetAddr remoteAddress
;
773 nsAutoCString localDottedDecimal
;
774 JSContext
* cx
= mJSContext
->Context();
776 if (PACResolve(hostName
, &remoteAddress
, timeout
) &&
777 SrcAddress(&remoteAddress
, localDottedDecimal
)) {
778 JSString
* dottedDecimalString
=
779 JS_NewStringCopyZ(cx
, localDottedDecimal
.get());
780 if (!dottedDecimalString
) {
785 aArgs
.rval().setString(dottedDecimalString
);
790 bool ProxyAutoConfig::MyIPAddress(const JS::CallArgs
& aArgs
) {
791 nsAutoCString remoteDottedDecimal
;
792 nsAutoCString localDottedDecimal
;
793 JSContext
* cx
= mJSContext
->Context();
794 JS::Rooted
<JS::Value
> v(cx
);
795 JS::Rooted
<JSObject
*> global(cx
, mJSContext
->Global());
797 bool useMultihomedDNS
=
798 JS_GetProperty(cx
, global
, "pacUseMultihomedDNS", &v
) &&
799 !v
.isUndefined() && ToBoolean(v
);
801 // first, lookup the local address of a socket connected
802 // to the host of uri being resolved by the pac file. This is
803 // v6 safe.. but is the last step like that
804 bool rvalAssigned
= false;
805 if (useMultihomedDNS
) {
806 if (!MyIPAddressTryHost(mRunningHost
, kTimeout
, aArgs
, &rvalAssigned
) ||
811 // we can still do the fancy multi homing thing if the host is a literal
812 if (HostIsIPLiteral(mRunningHost
) &&
813 (!MyIPAddressTryHost(mRunningHost
, kTimeout
, aArgs
, &rvalAssigned
) ||
819 // next, look for a route to a public internet address that doesn't need DNS.
820 // This is the google anycast dns address, but it doesn't matter if it
821 // remains operable (as we don't contact it) as long as the address stays
822 // in commonly routed IP address space.
823 remoteDottedDecimal
.AssignLiteral("8.8.8.8");
824 if (!MyIPAddressTryHost(remoteDottedDecimal
, 0, aArgs
, &rvalAssigned
) ||
829 // finally, use the old algorithm based on the local hostname
830 nsAutoCString hostName
;
831 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
832 // without multihomedDNS use such a short timeout that we are basically
833 // just looking at the cache for raw dotted decimals
834 uint32_t timeout
= useMultihomedDNS
? kTimeout
: 1;
835 if (dns
&& NS_SUCCEEDED(dns
->GetMyHostName(hostName
)) &&
836 PACResolveToString(hostName
, localDottedDecimal
, timeout
)) {
837 JSString
* dottedDecimalString
=
838 JS_NewStringCopyZ(cx
, localDottedDecimal
.get());
839 if (!dottedDecimalString
) {
843 aArgs
.rval().setString(dottedDecimalString
);
847 // next try a couple RFC 1918 variants.. maybe there is a
849 remoteDottedDecimal
.AssignLiteral("192.168.0.1");
850 if (!MyIPAddressTryHost(remoteDottedDecimal
, 0, aArgs
, &rvalAssigned
) ||
856 remoteDottedDecimal
.AssignLiteral("10.0.0.1");
857 if (!MyIPAddressTryHost(remoteDottedDecimal
, 0, aArgs
, &rvalAssigned
) ||
862 // who knows? let's fallback to localhost
863 localDottedDecimal
.AssignLiteral("127.0.0.1");
864 JSString
* dottedDecimalString
=
865 JS_NewStringCopyZ(cx
, localDottedDecimal
.get());
866 if (!dottedDecimalString
) {
870 aArgs
.rval().setString(dottedDecimalString
);
874 RemoteProxyAutoConfig::RemoteProxyAutoConfig() = default;
876 RemoteProxyAutoConfig::~RemoteProxyAutoConfig() = default;
878 nsresult
RemoteProxyAutoConfig::Init(nsIThread
* aPACThread
) {
879 MOZ_ASSERT(NS_IsMainThread());
881 RefPtr
<SocketProcessParent
> socketProcessParent
=
882 SocketProcessParent::GetSingleton();
883 if (!socketProcessParent
) {
884 return NS_ERROR_NOT_AVAILABLE
;
887 ipc::Endpoint
<PProxyAutoConfigParent
> parent
;
888 ipc::Endpoint
<PProxyAutoConfigChild
> child
;
889 nsresult rv
= PProxyAutoConfig::CreateEndpoints(
890 ipc::EndpointProcInfo::Current(),
891 socketProcessParent
->OtherEndpointProcInfo(), &parent
, &child
);
896 Unused
<< socketProcessParent
->SendInitProxyAutoConfigChild(std::move(child
));
897 mProxyAutoConfigParent
= new ProxyAutoConfigParent();
898 return aPACThread
->Dispatch(
899 NS_NewRunnableFunction("ProxyAutoConfigParent::ProxyAutoConfigParent",
900 [proxyAutoConfigParent(mProxyAutoConfigParent
),
901 endpoint
{std::move(parent
)}]() mutable {
902 proxyAutoConfigParent
->Init(std::move(endpoint
));
906 nsresult
RemoteProxyAutoConfig::ConfigurePAC(const nsACString
& aPACURI
,
907 const nsACString
& aPACScriptData
,
909 uint32_t aExtraHeapSize
,
910 nsISerialEventTarget
*) {
911 Unused
<< mProxyAutoConfigParent
->SendConfigurePAC(
912 aPACURI
, aPACScriptData
, aIncludePath
, aExtraHeapSize
);
916 void RemoteProxyAutoConfig::Shutdown() { mProxyAutoConfigParent
->Close(); }
918 void RemoteProxyAutoConfig::GC() {
919 // Do nothing. GC would be performed when there is not pending query in socket
923 void RemoteProxyAutoConfig::GetProxyForURIWithCallback(
924 const nsACString
& aTestURI
, const nsACString
& aTestHost
,
925 std::function
<void(nsresult aStatus
, const nsACString
& aResult
)>&&
927 if (!mProxyAutoConfigParent
->CanSend()) {
931 mProxyAutoConfigParent
->SendGetProxyForURI(
933 [aCallback
](std::tuple
<nsresult
, nsCString
>&& aResult
) {
934 auto [status
, result
] = aResult
;
935 aCallback(status
, result
);
937 [aCallback
](mozilla::ipc::ResponseRejectReason
&& aReason
) {
938 aCallback(NS_ERROR_FAILURE
, ""_ns
);
943 } // namespace mozilla