netwerk/base/src/nsIOService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set ts=4 sw=4 cindent et: */
     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 "mozilla/DebugOnly.h"
     9 #include "nsIOService.h"
    10 #include "nsIProtocolHandler.h"
    11 #include "nsIFileProtocolHandler.h"
    12 #include "nscore.h"
    13 #include "nsIURI.h"
    14 #include "prprf.h"
    15 #include "nsIErrorService.h"
    16 #include "netCore.h"
    17 #include "nsIObserverService.h"
    18 #include "nsIPrefService.h"
    19 #include "nsXPCOM.h"
    20 #include "nsIProxiedProtocolHandler.h"
    21 #include "nsIProxyInfo.h"
    22 #include "nsEscape.h"
    23 #include "nsNetCID.h"
    24 #include "nsCRT.h"
    25 #include "nsSimpleNestedURI.h"
    26 #include "nsNetUtil.h"
    27 #include "nsTArray.h"
    28 #include "nsIConsoleService.h"
    29 #include "nsIUploadChannel2.h"
    30 #include "nsXULAppAPI.h"
    31 #include "nsIProtocolProxyCallback.h"
    32 #include "nsICancelable.h"
    33 #include "nsINetworkLinkService.h"
    34 #include "nsPISocketTransportService.h"
    35 #include "nsAsyncRedirectVerifyHelper.h"
    36 #include "nsURLHelper.h"
    37 #include "nsPIDNSService.h"
    38 #include "nsIProtocolProxyService2.h"
    39 #include "MainThreadUtils.h"
    41 #if defined(XP_WIN)
    42 #include "nsNativeConnectionHelper.h"
    43 #endif
    45 using namespace mozilla;
    47 #define PORT_PREF_PREFIX           "network.security.ports."
    48 #define PORT_PREF(x)               PORT_PREF_PREFIX x
    49 #define AUTODIAL_PREF              "network.autodial-helper.enabled"
    50 #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status"
    52 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
    53 // "network.segment.count" and "network.segment.size" would be better names,
    54 // but the old names are still used to preserve backward compatibility.
    55 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
    56 #define NECKO_BUFFER_CACHE_SIZE_PREF  "network.buffer.cache.size"
    58 #define MAX_RECURSION_COUNT 50
    60 nsIOService* gIOService = nullptr;
    61 static bool gHasWarnedUploadChannel2;
    63 // A general port blacklist.  Connections to these ports will not be allowed unless 
    64 // the protocol overrides.
    65 //
    66 // TODO: I am sure that there are more ports to be added.  
    67 //       This cut is based on the classic mozilla codebase
    69 int16_t gBadPortList[] = { 
    70   1,    // tcpmux          
    71   7,    // echo     
    72   9,    // discard          
    73   11,   // systat   
    74   13,   // daytime          
    75   15,   // netstat  
    76   17,   // qotd             
    77   19,   // chargen  
    78   20,   // ftp-data         
    79   21,   // ftp-cntl 
    80   22,   // ssh              
    81   23,   // telnet   
    82   25,   // smtp     
    83   37,   // time     
    84   42,   // name     
    85   43,   // nicname  
    86   53,   // domain  
    87   77,   // priv-rjs 
    88   79,   // finger   
    89   87,   // ttylink  
    90   95,   // supdup   
    91   101,  // hostriame
    92   102,  // iso-tsap 
    93   103,  // gppitnp  
    94   104,  // acr-nema 
    95   109,  // pop2     
    96   110,  // pop3     
    97   111,  // sunrpc   
    98   113,  // auth     
    99   115,  // sftp     
   100   117,  // uucp-path
   101   119,  // nntp     
   102   123,  // NTP
   103   135,  // loc-srv / epmap         
   104   139,  // netbios
   105   143,  // imap2  
   106   179,  // BGP
   107   389,  // ldap        
   108   465,  // smtp+ssl
   109   512,  // print / exec          
   110   513,  // login         
   111   514,  // shell         
   112   515,  // printer         
   113   526,  // tempo         
   114   530,  // courier        
   115   531,  // Chat         
   116   532,  // netnews        
   117   540,  // uucp       
   118   556,  // remotefs    
   119   563,  // nntp+ssl
   120   587,  //
   121   601,  //       
   122   636,  // ldap+ssl
   123   993,  // imap+ssl
   124   995,  // pop3+ssl
   125   2049, // nfs
   126   4045, // lockd
   127   6000, // x11        
   128   0,    // This MUST be zero so that we can populating the array
   129 };
   131 static const char kProfileChangeNetTeardownTopic[] = "profile-change-net-teardown";
   132 static const char kProfileChangeNetRestoreTopic[] = "profile-change-net-restore";
   133 static const char kProfileDoChange[] = "profile-do-change";
   135 // Necko buffer defaults
   136 uint32_t   nsIOService::gDefaultSegmentSize = 4096;
   137 uint32_t   nsIOService::gDefaultSegmentCount = 24;
   139 ////////////////////////////////////////////////////////////////////////////////
   141 nsIOService::nsIOService()
   142     : mOffline(true)
   143     , mOfflineForProfileChange(false)
   144     , mManageOfflineStatus(false)
   145     , mSettingOffline(false)
   146     , mSetOfflineValue(false)
   147     , mShutdown(false)
   148     , mNetworkLinkServiceInitialized(false)
   149     , mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
   150     , mAutoDialEnabled(false)
   151 {
   152 }
   154 nsresult
   155 nsIOService::Init()
   156 {
   157     nsresult rv;
   159     // We need to get references to the DNS service so that we can shut it
   160     // down later. If we wait until the nsIOService is being shut down,
   161     // GetService will fail at that point.
   163     mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
   164     if (NS_FAILED(rv)) {
   165         NS_WARNING("failed to get DNS service");
   166         return rv;
   167     }
   169     // XXX hack until xpidl supports error info directly (bug 13423)
   170     nsCOMPtr<nsIErrorService> errorService = do_GetService(NS_ERRORSERVICE_CONTRACTID);
   171     if (errorService) {
   172         errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK, NECKO_MSGS_URL);
   173     }
   174     else
   175         NS_WARNING("failed to get error service");
   177     // setup our bad port list stuff
   178     for(int i=0; gBadPortList[i]; i++)
   179         mRestrictedPortList.AppendElement(gBadPortList[i]);
   181     // Further modifications to the port list come from prefs
   182     nsCOMPtr<nsIPrefBranch> prefBranch;
   183     GetPrefBranch(getter_AddRefs(prefBranch));
   184     if (prefBranch) {
   185         prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
   186         prefBranch->AddObserver(AUTODIAL_PREF, this, true);
   187         prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
   188         prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
   189         prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
   190         PrefsChanged(prefBranch);
   191     }
   193     // Register for profile change notifications
   194     nsCOMPtr<nsIObserverService> observerService =
   195         mozilla::services::GetObserverService();
   196     if (observerService) {
   197         observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
   198         observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
   199         observerService->AddObserver(this, kProfileDoChange, true);
   200         observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
   201         observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
   202     }
   203     else
   204         NS_WARNING("failed to get observer service");
   206     gIOService = this;
   208     InitializeNetworkLinkService();
   210     return NS_OK;
   211 }
   214 nsIOService::~nsIOService()
   215 {
   216     gIOService = nullptr;
   217 }
   219 nsresult
   220 nsIOService::InitializeSocketTransportService()
   221 {
   222     nsresult rv = NS_OK;
   224     if (!mSocketTransportService) {
   225         mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   226         if (NS_FAILED(rv)) {
   227             NS_WARNING("failed to get socket transport service");
   228         }
   229     }
   231     if (mSocketTransportService) {
   232         rv = mSocketTransportService->Init();
   233         NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
   234         mSocketTransportService->SetAutodialEnabled(mAutoDialEnabled);
   235         mSocketTransportService->SetOffline(false);
   236     }
   238     return rv;
   239 }
   241 nsresult
   242 nsIOService::InitializeNetworkLinkService()
   243 {
   244     nsresult rv = NS_OK;
   246     if (mNetworkLinkServiceInitialized)
   247         return rv;
   249     if (!NS_IsMainThread()) {
   250         NS_WARNING("Network link service should be created on main thread"); 
   251         return NS_ERROR_FAILURE; 
   252     }
   254     // go into managed mode if we can, and chrome process
   255     if (XRE_GetProcessType() == GeckoProcessType_Default)
   256     {
   257         mNetworkLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
   258     }
   260     if (mNetworkLinkService) {
   261         mNetworkLinkServiceInitialized = true;
   262     }
   263     else {
   264         // We can't really determine if the machine has a usable network connection,
   265         // so let's cross our fingers!
   266         mManageOfflineStatus = false;
   267     }
   270     if (mManageOfflineStatus)
   271         TrackNetworkLinkStatusForOffline();
   272     else
   273         SetOffline(false);
   275     return rv;
   276 }
   278 nsIOService*
   279 nsIOService::GetInstance() {
   280     if (!gIOService) {
   281         gIOService = new nsIOService();
   282         if (!gIOService)
   283             return nullptr;
   284         NS_ADDREF(gIOService);
   286         nsresult rv = gIOService->Init();
   287         if (NS_FAILED(rv)) {
   288             NS_RELEASE(gIOService);
   289             return nullptr;
   290         }
   291         return gIOService;
   292     }
   293     NS_ADDREF(gIOService);
   294     return gIOService;
   295 }
   297 NS_IMPL_ISUPPORTS(nsIOService,
   298                   nsIIOService,
   299                   nsIIOService2,
   300                   nsINetUtil,
   301                   nsISpeculativeConnect,
   302                   nsIObserver,
   303                   nsISupportsWeakReference)
   305 ////////////////////////////////////////////////////////////////////////////////
   307 nsresult
   308 nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
   309                                     uint32_t flags,
   310                                     nsAsyncRedirectVerifyHelper *helper)
   311 {
   312     nsCOMPtr<nsIChannelEventSink> sink =
   313         do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID);
   314     if (sink) {
   315         nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan,
   316                                                         newChan, flags);
   317         if (NS_FAILED(rv))
   318             return rv;
   319     }
   321     // Finally, our category
   322     nsCOMArray<nsIChannelEventSink> entries;
   323     mChannelEventSinks.GetEntries(entries);
   324     int32_t len = entries.Count();
   325     for (int32_t i = 0; i < len; ++i) {
   326         nsresult rv = helper->DelegateOnChannelRedirect(entries[i], oldChan,
   327                                                         newChan, flags);
   328         if (NS_FAILED(rv))
   329             return rv;
   330     }
   331     return NS_OK;
   332 }
   334 nsresult
   335 nsIOService::CacheProtocolHandler(const char *scheme, nsIProtocolHandler *handler)
   336 {
   337     for (unsigned int i=0; i<NS_N(gScheme); i++)
   338     {
   339         if (!nsCRT::strcasecmp(scheme, gScheme[i]))
   340         {
   341             nsresult rv;
   342             NS_ASSERTION(!mWeakHandler[i], "Protocol handler already cached");
   343             // Make sure the handler supports weak references.
   344             nsCOMPtr<nsISupportsWeakReference> factoryPtr = do_QueryInterface(handler, &rv);
   345             if (!factoryPtr)
   346             {
   347                 // Don't cache handlers that don't support weak reference as
   348                 // there is real danger of a circular reference.
   349 #ifdef DEBUG_dp
   350                 printf("DEBUG: %s protcol handler doesn't support weak ref. Not cached.\n", scheme);
   351 #endif /* DEBUG_dp */
   352                 return NS_ERROR_FAILURE;
   353             }
   354             mWeakHandler[i] = do_GetWeakReference(handler);
   355             return NS_OK;
   356         }
   357     }
   358     return NS_ERROR_FAILURE;
   359 }
   361 nsresult
   362 nsIOService::GetCachedProtocolHandler(const char *scheme, nsIProtocolHandler **result, uint32_t start, uint32_t end)
   363 {
   364     uint32_t len = end - start - 1;
   365     for (unsigned int i=0; i<NS_N(gScheme); i++)
   366     {
   367         if (!mWeakHandler[i])
   368             continue;
   370         // handle unterminated strings
   371         // start is inclusive, end is exclusive, len = end - start - 1
   372         if (end ? (!nsCRT::strncasecmp(scheme + start, gScheme[i], len)
   373                    && gScheme[i][len] == '\0')
   374                 : (!nsCRT::strcasecmp(scheme, gScheme[i])))
   375         {
   376             return CallQueryReferent(mWeakHandler[i].get(), result);
   377         }
   378     }
   379     return NS_ERROR_FAILURE;
   380 }
   382 NS_IMETHODIMP
   383 nsIOService::GetProtocolHandler(const char* scheme, nsIProtocolHandler* *result)
   384 {
   385     nsresult rv;
   387     NS_ENSURE_ARG_POINTER(scheme);
   388     // XXX we may want to speed this up by introducing our own protocol 
   389     // scheme -> protocol handler mapping, avoiding the string manipulation
   390     // and service manager stuff
   392     rv = GetCachedProtocolHandler(scheme, result);
   393     if (NS_SUCCEEDED(rv))
   394         return rv;
   396     bool externalProtocol = false;
   397     nsCOMPtr<nsIPrefBranch> prefBranch;
   398     GetPrefBranch(getter_AddRefs(prefBranch));
   399     if (prefBranch) {
   400         nsAutoCString externalProtocolPref("network.protocol-handler.external.");
   401         externalProtocolPref += scheme;
   402         rv = prefBranch->GetBoolPref(externalProtocolPref.get(), &externalProtocol);
   403         if (NS_FAILED(rv)) {
   404             externalProtocol = false;
   405         }
   406     }
   408     if (!externalProtocol) {
   409         nsAutoCString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
   410         contractID += scheme;
   411         ToLowerCase(contractID);
   413         rv = CallGetService(contractID.get(), result);
   414         if (NS_SUCCEEDED(rv)) {
   415             CacheProtocolHandler(scheme, *result);
   416             return rv;
   417         }
   419 #ifdef MOZ_X11
   420         // check to see whether GVFS can handle this URI scheme.  if it can
   421         // create a nsIURI for the "scheme:", then we assume it has support for
   422         // the requested protocol.  otherwise, we failover to using the default
   423         // protocol handler.
   425         rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gio",
   426                             result);
   427         if (NS_SUCCEEDED(rv)) {
   428             nsAutoCString spec(scheme);
   429             spec.Append(':');
   431             nsIURI *uri;
   432             rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
   433             if (NS_SUCCEEDED(rv)) {
   434                 NS_RELEASE(uri);
   435                 return rv;
   436             }
   438             NS_RELEASE(*result);
   439         }
   441         // check to see whether GnomeVFS can handle this URI scheme.  if it can
   442         // create a nsIURI for the "scheme:", then we assume it has support for
   443         // the requested protocol.  otherwise, we failover to using the default
   444         // protocol handler.
   446         // XXX should this be generalized into something that searches a
   447         // category?  (see bug 234714)
   449         rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"moz-gnomevfs",
   450                             result);
   451         if (NS_SUCCEEDED(rv)) {
   452             nsAutoCString spec(scheme);
   453             spec.Append(':');
   455             nsIURI *uri;
   456             rv = (*result)->NewURI(spec, nullptr, nullptr, &uri);
   457             if (NS_SUCCEEDED(rv)) {
   458                 NS_RELEASE(uri);
   459                 return rv;
   460             }
   462             NS_RELEASE(*result);
   463         }
   464 #endif
   465     }
   467     // Okay we don't have a protocol handler to handle this url type, so use
   468     // the default protocol handler.  This will cause urls to get dispatched
   469     // out to the OS ('cause we can't do anything with them) when we try to
   470     // read from a channel created by the default protocol handler.
   472     rv = CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default",
   473                         result);
   474     if (NS_FAILED(rv))
   475         return NS_ERROR_UNKNOWN_PROTOCOL;
   477     return rv;
   478 }
   480 NS_IMETHODIMP
   481 nsIOService::ExtractScheme(const nsACString &inURI, nsACString &scheme)
   482 {
   483     return net_ExtractURLScheme(inURI, nullptr, nullptr, &scheme);
   484 }
   486 NS_IMETHODIMP 
   487 nsIOService::GetProtocolFlags(const char* scheme, uint32_t *flags)
   488 {
   489     nsCOMPtr<nsIProtocolHandler> handler;
   490     nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
   491     if (NS_FAILED(rv)) return rv;
   493     rv = handler->GetProtocolFlags(flags);
   494     return rv;
   495 }
   497 class AutoIncrement
   498 {
   499     public:
   500         AutoIncrement(uint32_t *var) : mVar(var)
   501         {
   502             ++*var;
   503         }
   504         ~AutoIncrement()
   505         {
   506             --*mVar;
   507         }
   508     private:
   509         uint32_t *mVar;
   510 };
   512 nsresult
   513 nsIOService::NewURI(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIURI **result)
   514 {
   515     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
   517     static uint32_t recursionCount = 0;
   518     if (recursionCount >= MAX_RECURSION_COUNT)
   519         return NS_ERROR_MALFORMED_URI;
   520     AutoIncrement inc(&recursionCount);
   522     nsAutoCString scheme;
   523     nsresult rv = ExtractScheme(aSpec, scheme);
   524     if (NS_FAILED(rv)) {
   525         // then aSpec is relative
   526         if (!aBaseURI)
   527             return NS_ERROR_MALFORMED_URI;
   529         rv = aBaseURI->GetScheme(scheme);
   530         if (NS_FAILED(rv)) return rv;
   531     }
   533     // now get the handler for this scheme
   534     nsCOMPtr<nsIProtocolHandler> handler;
   535     rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
   536     if (NS_FAILED(rv)) return rv;
   538     return handler->NewURI(aSpec, aCharset, aBaseURI, result);
   539 }
   542 NS_IMETHODIMP 
   543 nsIOService::NewFileURI(nsIFile *file, nsIURI **result)
   544 {
   545     nsresult rv;
   546     NS_ENSURE_ARG_POINTER(file);
   548     nsCOMPtr<nsIProtocolHandler> handler;
   550     rv = GetProtocolHandler("file", getter_AddRefs(handler));
   551     if (NS_FAILED(rv)) return rv;
   553     nsCOMPtr<nsIFileProtocolHandler> fileHandler( do_QueryInterface(handler, &rv) );
   554     if (NS_FAILED(rv)) return rv;
   556     return fileHandler->NewFileURI(file, result);
   557 }
   559 NS_IMETHODIMP
   560 nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result)
   561 {
   562     return NewChannelFromURIWithProxyFlags(aURI, nullptr, 0, result);
   563 }
   565 NS_IMETHODIMP
   566 nsIOService::NewChannelFromURIWithProxyFlags(nsIURI *aURI,
   567                                              nsIURI *aProxyURI,
   568                                              uint32_t aProxyFlags,
   569                                              nsIChannel **result)
   570 {
   571     nsresult rv;
   572     NS_ENSURE_ARG_POINTER(aURI);
   574     nsAutoCString scheme;
   575     rv = aURI->GetScheme(scheme);
   576     if (NS_FAILED(rv))
   577         return rv;
   579     nsCOMPtr<nsIProtocolHandler> handler;
   580     rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
   581     if (NS_FAILED(rv))
   582         return rv;
   584     uint32_t protoFlags;
   585     rv = handler->GetProtocolFlags(&protoFlags);
   586     if (NS_FAILED(rv))
   587         return rv;
   589     nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler);
   590     if (pph)
   591         rv = pph->NewProxiedChannel(aURI, nullptr, aProxyFlags, aProxyURI, result);
   592     else
   593         rv = handler->NewChannel(aURI, result);
   594     NS_ENSURE_SUCCESS(rv, rv);
   596     // Some extensions override the http protocol handler and provide their own
   597     // implementation. The channels returned from that implementation doesn't
   598     // seem to always implement the nsIUploadChannel2 interface, presumably
   599     // because it's a new interface.
   600     // Eventually we should remove this and simply require that http channels
   601     // implement the new interface.
   602     // See bug 529041
   603     if (!gHasWarnedUploadChannel2 && scheme.EqualsLiteral("http")) {
   604         nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(*result);
   605         if (!uploadChannel2) {
   606             nsCOMPtr<nsIConsoleService> consoleService =
   607                 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   608             if (consoleService) {
   609                 consoleService->LogStringMessage(NS_LITERAL_STRING(
   610                     "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
   611                                                                    ).get());
   612             }
   613             gHasWarnedUploadChannel2 = true;
   614         }
   615     }
   617     return NS_OK;
   618 }
   620 NS_IMETHODIMP
   621 nsIOService::NewChannel(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIChannel **result)
   622 {
   623     nsresult rv;
   624     nsCOMPtr<nsIURI> uri;
   625     rv = NewURI(aSpec, aCharset, aBaseURI, getter_AddRefs(uri));
   626     if (NS_FAILED(rv)) return rv;
   628     return NewChannelFromURI(uri, result);
   629 }
   631 bool
   632 nsIOService::IsLinkUp()
   633 {
   634     InitializeNetworkLinkService();
   636     if (!mNetworkLinkService) {
   637         // We cannot decide, assume the link is up
   638         return true;
   639     }
   641     bool isLinkUp;
   642     nsresult rv;
   643     rv = mNetworkLinkService->GetIsLinkUp(&isLinkUp);
   644     if (NS_FAILED(rv)) {
   645         return true;
   646     }
   648     return isLinkUp;
   649 }
   651 NS_IMETHODIMP
   652 nsIOService::GetOffline(bool *offline)
   653 {
   654     *offline = mOffline;
   655     return NS_OK;
   656 }
   658 NS_IMETHODIMP
   659 nsIOService::SetOffline(bool offline)
   660 {
   661     // When someone wants to go online (!offline) after we got XPCOM shutdown
   662     // throw ERROR_NOT_AVAILABLE to prevent return to online state.
   663     if ((mShutdown || mOfflineForProfileChange) && !offline)
   664         return NS_ERROR_NOT_AVAILABLE;
   666     // SetOffline() may re-enter while it's shutting down services.
   667     // If that happens, save the most recent value and it will be
   668     // processed when the first SetOffline() call is done bringing
   669     // down the service.
   670     mSetOfflineValue = offline;
   671     if (mSettingOffline) {
   672         return NS_OK;
   673     }
   675     mSettingOffline = true;
   677     nsCOMPtr<nsIObserverService> observerService =
   678         mozilla::services::GetObserverService();
   680     NS_ASSERTION(observerService, "The observer service should not be null");
   682     if (XRE_GetProcessType() == GeckoProcessType_Default) {
   683         if (observerService) {
   684             (void)observerService->NotifyObservers(nullptr,
   685                 NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, offline ? 
   686                 MOZ_UTF16("true") :
   687                 MOZ_UTF16("false"));
   688         }
   689     }
   691     nsIIOService *subject = static_cast<nsIIOService *>(this);
   692     while (mSetOfflineValue != mOffline) {
   693         offline = mSetOfflineValue;
   695         if (offline && !mOffline) {
   696             NS_NAMED_LITERAL_STRING(offlineString, NS_IOSERVICE_OFFLINE);
   697             mOffline = true; // indicate we're trying to shutdown
   699             // don't care if notifications fail
   700             if (observerService)
   701                 observerService->NotifyObservers(subject,
   702                                                  NS_IOSERVICE_GOING_OFFLINE_TOPIC,
   703                                                  offlineString.get());
   705             if (mDNSService)
   706                 mDNSService->SetOffline(true);
   708             if (mSocketTransportService)
   709                 mSocketTransportService->SetOffline(true);
   711             if (observerService)
   712                 observerService->NotifyObservers(subject,
   713                                                  NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
   714                                                  offlineString.get());
   715         }
   716         else if (!offline && mOffline) {
   717             // go online
   718             if (mDNSService) {
   719                 mDNSService->SetOffline(false);
   720                 DebugOnly<nsresult> rv = mDNSService->Init();
   721                 NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
   722             }
   723             InitializeSocketTransportService();
   724             mOffline = false;    // indicate success only AFTER we've
   725                                     // brought up the services
   727             // trigger a PAC reload when we come back online
   728             if (mProxyService)
   729                 mProxyService->ReloadPAC();
   731             // don't care if notification fails
   732             if (observerService)
   733                 observerService->NotifyObservers(subject,
   734                                                  NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
   735                                                  NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get());
   736         }
   737     }
   739     // Don't notify here, as the above notifications (if used) suffice.
   740     if ((mShutdown || mOfflineForProfileChange) && mOffline) {
   741         // be sure to try and shutdown both (even if the first fails)...
   742         // shutdown dns service first, because it has callbacks for socket transport
   743         if (mDNSService) {
   744             DebugOnly<nsresult> rv = mDNSService->Shutdown();
   745             NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service shutdown failed");
   746         }
   747         if (mSocketTransportService) {
   748             DebugOnly<nsresult> rv = mSocketTransportService->Shutdown();
   749             NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed");
   750         }
   751     }
   753     mSettingOffline = false;
   755     return NS_OK;
   756 }
   759 NS_IMETHODIMP
   760 nsIOService::AllowPort(int32_t inPort, const char *scheme, bool *_retval)
   761 {
   762     int16_t port = inPort;
   763     if (port == -1) {
   764         *_retval = true;
   765         return NS_OK;
   766     }
   768     if (port == 0) {
   769         *_retval = false;
   770         return NS_OK;
   771     }
   773     // first check to see if the port is in our blacklist:
   774     int32_t badPortListCnt = mRestrictedPortList.Length();
   775     for (int i=0; i<badPortListCnt; i++)
   776     {
   777         if (port == mRestrictedPortList[i])
   778         {
   779             *_retval = false;
   781             // check to see if the protocol wants to override
   782             if (!scheme)
   783                 return NS_OK;
   785             nsCOMPtr<nsIProtocolHandler> handler;
   786             nsresult rv = GetProtocolHandler(scheme, getter_AddRefs(handler));
   787             if (NS_FAILED(rv)) return rv;
   789             // let the protocol handler decide
   790             return handler->AllowPort(port, scheme, _retval);
   791         }
   792     }
   794     *_retval = true;
   795     return NS_OK;
   796 }
   798 ////////////////////////////////////////////////////////////////////////////////
   800 void
   801 nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
   802 {
   803     if (!prefs) return;
   805     // Look for extra ports to block
   806     if (!pref || strcmp(pref, PORT_PREF("banned")) == 0)
   807         ParsePortList(prefs, PORT_PREF("banned"), false);
   809     // ...as well as previous blocks to remove.
   810     if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0)
   811         ParsePortList(prefs, PORT_PREF("banned.override"), true);
   813     if (!pref || strcmp(pref, AUTODIAL_PREF) == 0) {
   814         bool enableAutodial = false;
   815         nsresult rv = prefs->GetBoolPref(AUTODIAL_PREF, &enableAutodial);
   816         // If pref not found, default to disabled.
   817         mAutoDialEnabled = enableAutodial;
   818         if (NS_SUCCEEDED(rv)) {
   819             if (mSocketTransportService)
   820                 mSocketTransportService->SetAutodialEnabled(enableAutodial);
   821         }
   822     }
   824     if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
   825         bool manage;
   826         if (mNetworkLinkServiceInitialized &&
   827             NS_SUCCEEDED(prefs->GetBoolPref(MANAGE_OFFLINE_STATUS_PREF,
   828                                             &manage)))
   829             SetManageOfflineStatus(manage);
   830     }
   832     if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
   833         int32_t count;
   834         if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_COUNT_PREF,
   835                                            &count)))
   836             /* check for bogus values and default if we find such a value */
   837             if (count > 0)
   838                 gDefaultSegmentCount = count;
   839     }
   841     if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
   842         int32_t size;
   843         if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_SIZE_PREF,
   844                                            &size)))
   845             /* check for bogus values and default if we find such a value
   846              * the upper limit here is arbitrary. having a 1mb segment size
   847              * is pretty crazy.  if you remove this, consider adding some
   848              * integer rollover test.
   849              */
   850             if (size > 0 && size < 1024*1024)
   851                 gDefaultSegmentSize = size;
   852         NS_WARN_IF_FALSE( (!(size & (size - 1))) , "network segment size is not a power of 2!");
   853     }
   854 }
   856 void
   857 nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove)
   858 {
   859     nsXPIDLCString portList;
   861     // Get a pref string and chop it up into a list of ports.
   862     prefBranch->GetCharPref(pref, getter_Copies(portList));
   863     if (portList) {
   864         nsTArray<nsCString> portListArray;
   865         ParseString(portList, ',', portListArray);
   866         uint32_t index;
   867         for (index=0; index < portListArray.Length(); index++) {
   868             portListArray[index].StripWhitespace();
   869             int32_t portBegin, portEnd;
   871             if (PR_sscanf(portListArray[index].get(), "%d-%d", &portBegin, &portEnd) == 2) {
   872                if ((portBegin < 65536) && (portEnd < 65536)) {
   873                    int32_t curPort;
   874                    if (remove) {
   875                         for (curPort=portBegin; curPort <= portEnd; curPort++)
   876                             mRestrictedPortList.RemoveElement(curPort);
   877                    } else {
   878                         for (curPort=portBegin; curPort <= portEnd; curPort++)
   879                             mRestrictedPortList.AppendElement(curPort);
   880                    }
   881                }
   882             } else {
   883                nsresult aErrorCode;
   884                int32_t port = portListArray[index].ToInteger(&aErrorCode);
   885                if (NS_SUCCEEDED(aErrorCode) && port < 65536) {
   886                    if (remove)
   887                        mRestrictedPortList.RemoveElement(port);
   888                    else
   889                        mRestrictedPortList.AppendElement(port);
   890                }
   891             }
   893         }
   894     }
   895 }
   897 void
   898 nsIOService::GetPrefBranch(nsIPrefBranch **result)
   899 {
   900     *result = nullptr;
   901     CallGetService(NS_PREFSERVICE_CONTRACTID, result);
   902 }
   904 // nsIObserver interface
   905 NS_IMETHODIMP
   906 nsIOService::Observe(nsISupports *subject,
   907                      const char *topic,
   908                      const char16_t *data)
   909 {
   910     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   911         nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
   912         if (prefBranch)
   913             PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
   914     }
   915     else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
   916         if (!mOffline) {
   917             mOfflineForProfileChange = true;
   918             SetOffline(true);
   919         }
   920     }
   921     else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
   922         if (mOfflineForProfileChange) {
   923             mOfflineForProfileChange = false;
   924             if (!mManageOfflineStatus ||
   925                 NS_FAILED(TrackNetworkLinkStatusForOffline())) {
   926                 SetOffline(false);
   927             }
   928         } 
   929     } 
   930     else if (!strcmp(topic, kProfileDoChange)) { 
   931         if (data && NS_LITERAL_STRING("startup").Equals(data)) {
   932             // Lazy initialization of network link service (see bug 620472)
   933             InitializeNetworkLinkService();
   934             // Set up the initilization flag regardless the actuall result.
   935             // If we fail here, we will fail always on.
   936             mNetworkLinkServiceInitialized = true;
   937             // And now reflect the preference setting
   938             nsCOMPtr<nsIPrefBranch> prefBranch;
   939             GetPrefBranch(getter_AddRefs(prefBranch));
   940             PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
   941         }
   942     }
   943     else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   944         // Remember we passed XPCOM shutdown notification to prevent any
   945         // changes of the offline status from now. We must not allow going
   946         // online after this point.
   947         mShutdown = true;
   949         SetOffline(true);
   951         // Break circular reference.
   952         mProxyService = nullptr;
   953     }
   954     else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
   955         if (!mOfflineForProfileChange && mManageOfflineStatus) {
   956             TrackNetworkLinkStatusForOffline();
   957         }
   958     }
   960     return NS_OK;
   961 }
   963 // nsINetUtil interface
   964 NS_IMETHODIMP
   965 nsIOService::ParseContentType(const nsACString &aTypeHeader,
   966                               nsACString &aCharset,
   967                               bool *aHadCharset,
   968                               nsACString &aContentType)
   969 {
   970     net_ParseContentType(aTypeHeader, aContentType, aCharset, aHadCharset);
   971     return NS_OK;
   972 }
   974 NS_IMETHODIMP
   975 nsIOService::ProtocolHasFlags(nsIURI   *uri,
   976                               uint32_t  flags,
   977                               bool     *result)
   978 {
   979     NS_ENSURE_ARG(uri);
   981     *result = false;
   982     nsAutoCString scheme;
   983     nsresult rv = uri->GetScheme(scheme);
   984     NS_ENSURE_SUCCESS(rv, rv);
   986     uint32_t protocolFlags;
   987     rv = GetProtocolFlags(scheme.get(), &protocolFlags);
   989     if (NS_SUCCEEDED(rv)) {
   990         *result = (protocolFlags & flags) == flags;
   991     }
   993     return rv;
   994 }
   996 NS_IMETHODIMP
   997 nsIOService::URIChainHasFlags(nsIURI   *uri,
   998                               uint32_t  flags,
   999                               bool     *result)
  1001     nsresult rv = ProtocolHasFlags(uri, flags, result);
  1002     NS_ENSURE_SUCCESS(rv, rv);
  1004     if (*result) {
  1005         return rv;
  1008     // Dig deeper into the chain.  Note that this is not a do/while loop to
  1009     // avoid the extra addref/release on |uri| in the common (non-nested) case.
  1010     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
  1011     while (nestedURI) {
  1012         nsCOMPtr<nsIURI> innerURI;
  1013         rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
  1014         NS_ENSURE_SUCCESS(rv, rv);
  1016         rv = ProtocolHasFlags(innerURI, flags, result);
  1018         if (*result) {
  1019             return rv;
  1022         nestedURI = do_QueryInterface(innerURI);
  1025     return rv;
  1028 NS_IMETHODIMP
  1029 nsIOService::ToImmutableURI(nsIURI* uri, nsIURI** result)
  1031     if (!uri) {
  1032         *result = nullptr;
  1033         return NS_OK;
  1036     nsresult rv = NS_EnsureSafeToReturn(uri, result);
  1037     NS_ENSURE_SUCCESS(rv, rv);
  1039     NS_TryToSetImmutable(*result);
  1040     return NS_OK;
  1043 NS_IMETHODIMP
  1044 nsIOService::NewSimpleNestedURI(nsIURI* aURI, nsIURI** aResult)
  1046     NS_ENSURE_ARG(aURI);
  1048     nsCOMPtr<nsIURI> safeURI;
  1049     nsresult rv = NS_EnsureSafeToReturn(aURI, getter_AddRefs(safeURI));
  1050     NS_ENSURE_SUCCESS(rv, rv);
  1052     NS_IF_ADDREF(*aResult = new nsSimpleNestedURI(safeURI));
  1053     return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
  1056 NS_IMETHODIMP
  1057 nsIOService::SetManageOfflineStatus(bool aManage) {
  1058     nsresult rv = NS_OK;
  1060     // SetManageOfflineStatus must throw when we fail to go from non-managed
  1061     // to managed.  Usually because there is no link monitoring service 
  1062     // available.  Failure to do this switch is detected by a failure of 
  1063     // TrackNetworkLinkStatusForOffline().  When there is no network link 
  1064     // available during call to InitializeNetworkLinkService(), application is
  1065     // put to offline mode.  And when we change mMangeOfflineStatus to false 
  1066     // on the next line we get stuck on being offline even though the link 
  1067     // becomes later available.
  1068     bool wasManaged = mManageOfflineStatus;
  1069     mManageOfflineStatus = aManage;
  1071     InitializeNetworkLinkService();
  1073     if (mManageOfflineStatus && !wasManaged) {
  1074         rv = TrackNetworkLinkStatusForOffline();
  1075         if (NS_FAILED(rv))
  1076             mManageOfflineStatus = false;
  1078     return rv;
  1081 NS_IMETHODIMP
  1082 nsIOService::GetManageOfflineStatus(bool* aManage) {
  1083     *aManage = mManageOfflineStatus;
  1084     return NS_OK;
  1087 nsresult
  1088 nsIOService::TrackNetworkLinkStatusForOffline()
  1090     NS_ASSERTION(mManageOfflineStatus,
  1091                  "Don't call this unless we're managing the offline status");
  1092     if (!mNetworkLinkService)
  1093         return NS_ERROR_FAILURE;
  1095     if (mShutdown)
  1096         return NS_ERROR_NOT_AVAILABLE;
  1098     // check to make sure this won't collide with Autodial
  1099     if (mSocketTransportService) {
  1100         bool autodialEnabled = false;
  1101         mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
  1102         // If autodialing-on-link-down is enabled, check if the OS auto dial 
  1103         // option is set to always autodial. If so, then we are 
  1104         // always up for the purposes of offline management.
  1105         if (autodialEnabled) {
  1106 #if defined(XP_WIN)
  1107             // On Windows, we should first check with the OS
  1108             // to see if autodial is enabled.  If it is
  1109             // enabled then we are allowed to manage the
  1110             // offline state.
  1111             if(nsNativeConnectionHelper::IsAutodialEnabled()) 
  1112                 return SetOffline(false);
  1113 #else
  1114             return SetOffline(false);
  1115 #endif
  1119     bool isUp;
  1120     nsresult rv = mNetworkLinkService->GetIsLinkUp(&isUp);
  1121     NS_ENSURE_SUCCESS(rv, rv);
  1122     return SetOffline(!isUp);
  1125 NS_IMETHODIMP
  1126 nsIOService::EscapeString(const nsACString& aString,
  1127                           uint32_t aEscapeType,
  1128                           nsACString& aResult)
  1130   NS_ENSURE_ARG_MAX(aEscapeType, 4);
  1132   nsAutoCString stringCopy(aString);
  1133   nsCString result;
  1135   if (!NS_Escape(stringCopy, result, (nsEscapeMask) aEscapeType))
  1136     return NS_ERROR_OUT_OF_MEMORY;
  1138   aResult.Assign(result);
  1140   return NS_OK;
  1143 NS_IMETHODIMP 
  1144 nsIOService::EscapeURL(const nsACString &aStr, 
  1145                        uint32_t aFlags, nsACString &aResult)
  1147   aResult.Truncate();
  1148   NS_EscapeURL(aStr.BeginReading(), aStr.Length(), 
  1149                aFlags | esc_AlwaysCopy, aResult);
  1150   return NS_OK;
  1153 NS_IMETHODIMP 
  1154 nsIOService::UnescapeString(const nsACString &aStr, 
  1155                             uint32_t aFlags, nsACString &aResult)
  1157   aResult.Truncate();
  1158   NS_UnescapeURL(aStr.BeginReading(), aStr.Length(), 
  1159                  aFlags | esc_AlwaysCopy, aResult);
  1160   return NS_OK;
  1163 NS_IMETHODIMP
  1164 nsIOService::ExtractCharsetFromContentType(const nsACString &aTypeHeader,
  1165                                            nsACString &aCharset,
  1166                                            int32_t *aCharsetStart,
  1167                                            int32_t *aCharsetEnd,
  1168                                            bool *aHadCharset)
  1170     nsAutoCString ignored;
  1171     net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset,
  1172                          aCharsetStart, aCharsetEnd);
  1173     if (*aHadCharset && *aCharsetStart == *aCharsetEnd) {
  1174         *aHadCharset = false;
  1176     return NS_OK;
  1179 // nsISpeculativeConnect
  1180 class IOServiceProxyCallback MOZ_FINAL : public nsIProtocolProxyCallback
  1182 public:
  1183     NS_DECL_ISUPPORTS
  1184     NS_DECL_NSIPROTOCOLPROXYCALLBACK
  1186     IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks,
  1187                            nsIOService *aIOService)
  1188         : mCallbacks(aCallbacks)
  1189         , mIOService(aIOService)
  1190     { }
  1192 private:
  1193     nsRefPtr<nsIInterfaceRequestor> mCallbacks;
  1194     nsRefPtr<nsIOService>           mIOService;
  1195 };
  1197 NS_IMPL_ISUPPORTS(IOServiceProxyCallback, nsIProtocolProxyCallback)
  1199 NS_IMETHODIMP
  1200 IOServiceProxyCallback::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
  1201                                          nsIProxyInfo *pi, nsresult status)
  1203     // Checking proxy status for speculative connect
  1204     nsAutoCString type;
  1205     if (NS_SUCCEEDED(status) && pi &&
  1206         NS_SUCCEEDED(pi->GetType(type)) &&
  1207         !type.EqualsLiteral("direct")) {
  1208         // proxies dont do speculative connect
  1209         return NS_OK;
  1212     nsCOMPtr<nsIURI> uri;
  1213     nsresult rv = channel->GetURI(getter_AddRefs(uri));
  1214     if (NS_FAILED(rv))
  1215         return NS_OK;
  1217     nsAutoCString scheme;
  1218     rv = uri->GetScheme(scheme);
  1219     if (NS_FAILED(rv))
  1220         return NS_OK;
  1222     nsCOMPtr<nsIProtocolHandler> handler;
  1223     rv = mIOService->GetProtocolHandler(scheme.get(),
  1224                                         getter_AddRefs(handler));
  1225     if (NS_FAILED(rv))
  1226         return NS_OK;
  1228     nsCOMPtr<nsISpeculativeConnect> speculativeHandler =
  1229         do_QueryInterface(handler);
  1230     if (!speculativeHandler)
  1231         return NS_OK;
  1233     speculativeHandler->SpeculativeConnect(uri,
  1234                                            mCallbacks);
  1235     return NS_OK;
  1238 NS_IMETHODIMP
  1239 nsIOService::SpeculativeConnect(nsIURI *aURI,
  1240                                 nsIInterfaceRequestor *aCallbacks)
  1242     // Check for proxy information. If there is a proxy configured then a
  1243     // speculative connect should not be performed because the potential
  1244     // reward is slim with tcp peers closely located to the browser.
  1245     nsresult rv;
  1246     nsCOMPtr<nsIProtocolProxyService> pps =
  1247             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
  1248     if (NS_FAILED(rv))
  1249         return rv;
  1251     nsCOMPtr<nsIChannel> channel;
  1252     rv = NewChannelFromURI(aURI, getter_AddRefs(channel));
  1253     if (NS_FAILED(rv))
  1254         return rv;
  1256     nsCOMPtr<nsICancelable> cancelable;
  1257     nsRefPtr<IOServiceProxyCallback> callback =
  1258         new IOServiceProxyCallback(aCallbacks, this);
  1259     return pps->AsyncResolve(channel, 0, callback, getter_AddRefs(cancelable));

mercurial