LCOV - code coverage report
Current view: top level - netwerk/system/linux - nsNotifyAddrListener_Linux.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 96 256 37.5 %
Date: 2017-07-14 16:53:18 Functions: 8 20 40.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim:set et sw=4 ts=4: */
       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/. */
       6             : 
       7             : #include <stdarg.h>
       8             : #include <fcntl.h>
       9             : #include <poll.h>
      10             : #include <errno.h>
      11             : #include <ifaddrs.h>
      12             : #include <net/if.h>
      13             : 
      14             : #include "nsThreadUtils.h"
      15             : #include "nsIObserverService.h"
      16             : #include "nsServiceManagerUtils.h"
      17             : #include "nsNotifyAddrListener_Linux.h"
      18             : #include "nsString.h"
      19             : #include "mozilla/Logging.h"
      20             : 
      21             : #include "mozilla/Base64.h"
      22             : #include "mozilla/FileUtils.h"
      23             : #include "mozilla/Preferences.h"
      24             : #include "mozilla/Services.h"
      25             : #include "mozilla/SHA1.h"
      26             : #include "mozilla/Sprintf.h"
      27             : #include "mozilla/Telemetry.h"
      28             : 
      29             : /* a shorter name that better explains what it does */
      30             : #define EINTR_RETRY(x) MOZ_TEMP_FAILURE_RETRY(x)
      31             : 
      32             : // period during which to absorb subsequent network change events, in
      33             : // milliseconds
      34             : static const unsigned int kNetworkChangeCoalescingPeriod  = 1000;
      35             : 
      36             : using namespace mozilla;
      37             : 
      38             : static LazyLogModule gNotifyAddrLog("nsNotifyAddr");
      39             : #define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args)
      40             : 
      41             : #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
      42             : 
      43          23 : NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
      44             :                   nsINetworkLinkService,
      45             :                   nsIRunnable,
      46             :                   nsIObserver)
      47             : 
      48           1 : nsNotifyAddrListener::nsNotifyAddrListener()
      49             :     : mLinkUp(true)  // assume true by default
      50             :     , mStatusKnown(false)
      51             :     , mAllowChangedEvent(true)
      52           1 :     , mCoalescingActive(false)
      53             : {
      54           1 :     mShutdownPipe[0] = -1;
      55           1 :     mShutdownPipe[1] = -1;
      56           1 : }
      57             : 
      58           0 : nsNotifyAddrListener::~nsNotifyAddrListener()
      59             : {
      60           0 :     MOZ_ASSERT(!mThread, "nsNotifyAddrListener thread shutdown failed");
      61             : 
      62           0 :     if (mShutdownPipe[0] != -1) {
      63           0 :         EINTR_RETRY(close(mShutdownPipe[0]));
      64             :     }
      65           0 :     if (mShutdownPipe[1] != -1) {
      66           0 :         EINTR_RETRY(close(mShutdownPipe[1]));
      67             :     }
      68           0 : }
      69             : 
      70             : NS_IMETHODIMP
      71           1 : nsNotifyAddrListener::GetIsLinkUp(bool *aIsUp)
      72             : {
      73             :     // XXX This function has not yet been implemented for this platform
      74           1 :     *aIsUp = mLinkUp;
      75           1 :     return NS_OK;
      76             : }
      77             : 
      78             : NS_IMETHODIMP
      79           0 : nsNotifyAddrListener::GetLinkStatusKnown(bool *aIsUp)
      80             : {
      81             :     // XXX This function has not yet been implemented for this platform
      82           0 :     *aIsUp = mStatusKnown;
      83           0 :     return NS_OK;
      84             : }
      85             : 
      86             : NS_IMETHODIMP
      87           0 : nsNotifyAddrListener::GetLinkType(uint32_t *aLinkType)
      88             : {
      89           0 :   NS_ENSURE_ARG_POINTER(aLinkType);
      90             : 
      91             :   // XXX This function has not yet been implemented for this platform
      92           0 :   *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
      93           0 :   return NS_OK;
      94             : }
      95             : 
      96             : //
      97             : // Figure out the current "network identification" string.
      98             : //
      99             : // It detects the IP of the default gateway in the routing table, then the MAC
     100             : // address of that IP in the ARP table before it hashes that string (to avoid
     101             : // information leakage).
     102             : //
     103           1 : void nsNotifyAddrListener::calculateNetworkId(void)
     104             : {
     105           1 :     const char *kProcRoute = "/proc/net/route"; /* IPv4 routes */
     106           1 :     const char *kProcArp = "/proc/net/arp";
     107           1 :     bool found = false;
     108             : 
     109           1 :     FILE *froute = fopen(kProcRoute, "r");
     110           1 :     if (froute) {
     111             :         char buffer[512];
     112           1 :         uint32_t gw = 0;
     113           1 :         char *l = fgets(buffer, sizeof(buffer), froute);
     114           1 :         if (l) {
     115             :             /* skip the title line  */
     116           1 :             while (l) {
     117             :                 char interf[32];
     118             :                 uint32_t dest;
     119             :                 uint32_t gateway;
     120           1 :                 l = fgets(buffer, sizeof(buffer), froute);
     121           1 :                 if (l) {
     122           1 :                     buffer[511]=0; /* as a precaution */
     123             :                     int val = sscanf(buffer, "%31s %x %x",
     124           1 :                                      interf, &dest, &gateway);
     125           1 :                     if ((3 == val) && !dest) {
     126           1 :                         gw = gateway;
     127           2 :                         break;
     128             :                     }
     129             :                 }
     130             :             }
     131             :         }
     132           1 :         fclose(froute);
     133             : 
     134           1 :         if (gw) {
     135             :             /* create a string to search for in the arp table */
     136             :             char searchfor[16];
     137           3 :             SprintfLiteral(searchfor, "%d.%d.%d.%d",
     138             :                            gw & 0xff,
     139           1 :                            (gw >> 8) & 0xff,
     140           1 :                            (gw >> 16) & 0xff,
     141           1 :                            gw >> 24);
     142             : 
     143           1 :             FILE *farp = fopen(kProcArp, "r");
     144           1 :             if (farp) {
     145           1 :                 l = fgets(buffer, sizeof(buffer), farp);
     146           1 :                 while (l) {
     147             :                     /* skip the title line  */
     148           1 :                     l = fgets(buffer, sizeof(buffer), farp);
     149           1 :                     if (l) {
     150           1 :                         buffer[511]=0; /* as a precaution */
     151             :                         int p[4];
     152             :                         char type[16];
     153             :                         char flags[16];
     154             :                         char hw[32];
     155           1 :                         if (7 == sscanf(buffer, "%u.%u.%u.%u %15s %15s %31s",
     156             :                                         &p[0], &p[1], &p[2], &p[3],
     157             :                                         type, flags, hw)) {
     158           2 :                             uint32_t searchip = p[0] | (p[1] << 8) |
     159           2 :                                 (p[2] << 16) | (p[3] << 24);
     160           1 :                             if (gw == searchip) {
     161           1 :                                 LOG(("networkid: MAC %s\n", hw));
     162           2 :                                 nsAutoCString mac(hw);
     163             :                                 // This 'addition' could potentially be a
     164             :                                 // fixed number from the profile or something.
     165           2 :                                 nsAutoCString addition("local-rubbish");
     166           2 :                                 nsAutoCString output;
     167           1 :                                 SHA1Sum sha1;
     168           2 :                                 nsCString combined(mac + addition);
     169           1 :                                 sha1.update(combined.get(), combined.Length());
     170             :                                 uint8_t digest[SHA1Sum::kHashSize];
     171           1 :                                 sha1.finish(digest);
     172             :                                 nsCString newString(reinterpret_cast<char*>(digest),
     173           2 :                                                     SHA1Sum::kHashSize);
     174           1 :                                 nsresult rv = Base64Encode(newString, output);
     175           1 :                                 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     176           1 :                                 LOG(("networkid: id %s\n", output.get()));
     177           1 :                                 if (mNetworkId != output) {
     178             :                                     // new id
     179           1 :                                     Telemetry::Accumulate(Telemetry::NETWORK_ID, 1);
     180           1 :                                     mNetworkId = output;
     181             :                                 }
     182             :                                 else {
     183             :                                     // same id
     184           0 :                                     Telemetry::Accumulate(Telemetry::NETWORK_ID, 2);
     185             :                                 }
     186           1 :                                 found = true;
     187           1 :                                 break;
     188             :                             }
     189             :                         }
     190             :                     }
     191             :                 }
     192           1 :                 fclose(farp);
     193             :             } /* if (farp) */
     194             :         } /* if (gw) */
     195             :     } /* if (froute) */
     196           1 :     if (!found) {
     197             :         // no id
     198           0 :         Telemetry::Accumulate(Telemetry::NETWORK_ID, 0);
     199             :     }
     200           1 : }
     201             : 
     202             : //
     203             : // Check if there's a network interface available to do networking on.
     204             : //
     205           0 : void nsNotifyAddrListener::checkLink(void)
     206             : {
     207             :     struct ifaddrs *list;
     208             :     struct ifaddrs *ifa;
     209           0 :     bool link = false;
     210           0 :     bool prevLinkUp = mLinkUp;
     211             : 
     212           0 :     if (getifaddrs(&list))
     213           0 :         return;
     214             : 
     215             :     // Walk through the linked list, maintaining head pointer so we can free
     216             :     // list later
     217             : 
     218           0 :     for (ifa = list; ifa != nullptr; ifa = ifa->ifa_next) {
     219             :         int family;
     220           0 :         if (ifa->ifa_addr == nullptr)
     221           0 :             continue;
     222             : 
     223           0 :         family = ifa->ifa_addr->sa_family;
     224             : 
     225           0 :         if ((family == AF_INET || family == AF_INET6) &&
     226           0 :             (ifa->ifa_flags & IFF_RUNNING) &&
     227           0 :             !(ifa->ifa_flags & IFF_LOOPBACK)) {
     228             :             // An interface that is UP and not loopback
     229           0 :             link = true;
     230           0 :             break;
     231             :         }
     232             :     }
     233           0 :     mLinkUp = link;
     234           0 :     freeifaddrs(list);
     235             : 
     236           0 :     if (prevLinkUp != mLinkUp) {
     237             :         // UP/DOWN status changed, send appropriate UP/DOWN event
     238           0 :         SendEvent(mLinkUp ?
     239           0 :                   NS_NETWORK_LINK_DATA_UP : NS_NETWORK_LINK_DATA_DOWN);
     240             :     }
     241             : }
     242             : 
     243           0 : void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket)
     244             : {
     245             :     struct  nlmsghdr *nlh;
     246             : 
     247             :     // The buffer size below, (4095) was chosen partly based on testing and
     248             :     // partly on existing sample source code using this size. It needs to be
     249             :     // large enough to hold the netlink messages from the kernel.
     250             :     char buffer[4095];
     251             :     struct rtattr *attr;
     252             :     int attr_len;
     253             :     const struct ifaddrmsg* newifam;
     254             : 
     255             : 
     256           0 :     ssize_t rc = EINTR_RETRY(recv(aNetlinkSocket, buffer, sizeof(buffer), 0));
     257           0 :     if (rc < 0) {
     258           0 :         return;
     259             :     }
     260           0 :     size_t netlink_bytes = rc;
     261             : 
     262           0 :     nlh = reinterpret_cast<struct nlmsghdr *>(buffer);
     263             : 
     264           0 :     bool networkChange = false;
     265             : 
     266           0 :     for (; NLMSG_OK(nlh, netlink_bytes);
     267           0 :          nlh = NLMSG_NEXT(nlh, netlink_bytes)) {
     268             :         char prefixaddr[INET6_ADDRSTRLEN];
     269             :         char localaddr[INET6_ADDRSTRLEN];
     270           0 :         char* addr = nullptr;
     271           0 :         prefixaddr[0] = localaddr[0] = '\0';
     272             : 
     273           0 :         if (NLMSG_DONE == nlh->nlmsg_type) {
     274           0 :             break;
     275             :         }
     276             : 
     277           0 :         LOG(("nsNotifyAddrListener::OnNetlinkMessage: new/deleted address\n"));
     278           0 :         newifam = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(nlh));
     279             : 
     280           0 :         if ((newifam->ifa_family != AF_INET) &&
     281           0 :             (newifam->ifa_family != AF_INET6)) {
     282           0 :             continue;
     283             :         }
     284             : 
     285           0 :         attr = IFA_RTA (newifam);
     286           0 :         attr_len = IFA_PAYLOAD (nlh);
     287           0 :         for (;attr_len && RTA_OK (attr, attr_len);
     288           0 :              attr = RTA_NEXT (attr, attr_len)) {
     289           0 :             if (attr->rta_type == IFA_ADDRESS) {
     290           0 :                 if (newifam->ifa_family == AF_INET) {
     291           0 :                     struct in_addr* in = (struct in_addr*)RTA_DATA(attr);
     292           0 :                     inet_ntop(AF_INET, in, prefixaddr, INET_ADDRSTRLEN);
     293             :                 } else {
     294           0 :                     struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr);
     295           0 :                     inet_ntop(AF_INET6, in, prefixaddr, INET6_ADDRSTRLEN);
     296             :                 }
     297           0 :             } else if (attr->rta_type == IFA_LOCAL) {
     298           0 :                 if (newifam->ifa_family == AF_INET) {
     299           0 :                     struct in_addr* in = (struct in_addr*)RTA_DATA(attr);
     300           0 :                     inet_ntop(AF_INET, in, localaddr, INET_ADDRSTRLEN);
     301             :                 } else {
     302           0 :                     struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr);
     303           0 :                     inet_ntop(AF_INET6, in, localaddr, INET6_ADDRSTRLEN);
     304             :                 }
     305             :             }
     306             :         }
     307           0 :         if (localaddr[0]) {
     308           0 :             addr = localaddr;
     309           0 :         } else if (prefixaddr[0]) {
     310           0 :             addr = prefixaddr;
     311             :         } else {
     312           0 :             continue;
     313             :         }
     314           0 :         if (nlh->nlmsg_type == RTM_NEWADDR) {
     315           0 :             LOG(("nsNotifyAddrListener::OnNetlinkMessage: a new address "
     316             :                  "- %s.", addr));
     317             :             struct ifaddrmsg* ifam;
     318           0 :             nsCString addrStr;
     319           0 :             addrStr.Assign(addr);
     320           0 :             if (auto entry = mAddressInfo.LookupForAdd(addrStr)) {
     321           0 :                 ifam = entry.Data();
     322           0 :                 LOG(("nsNotifyAddrListener::OnNetlinkMessage: the address "
     323             :                      "already known."));
     324           0 :                 if (memcmp(ifam, newifam, sizeof(struct ifaddrmsg))) {
     325           0 :                     LOG(("nsNotifyAddrListener::OnNetlinkMessage: but "
     326             :                          "the address info has been changed."));
     327           0 :                     networkChange = true;
     328           0 :                     memcpy(ifam, newifam, sizeof(struct ifaddrmsg));
     329             :                 }
     330             :             } else {
     331           0 :                 networkChange = true;
     332           0 :                 ifam = (struct ifaddrmsg*)malloc(sizeof(struct ifaddrmsg));
     333           0 :                 memcpy(ifam, newifam, sizeof(struct ifaddrmsg));
     334           0 :                 entry.OrInsert([ifam] () { return ifam; });
     335             :             }
     336             :         } else {
     337           0 :             LOG(("nsNotifyAddrListener::OnNetlinkMessage: an address "
     338             :                  "has been deleted - %s.", addr));
     339           0 :             networkChange = true;
     340           0 :             nsCString addrStr;
     341           0 :             addrStr.Assign(addr);
     342           0 :             mAddressInfo.Remove(addrStr);
     343             :         }
     344             :     }
     345             : 
     346           0 :     if (networkChange && mAllowChangedEvent) {
     347           0 :         NetworkChanged();
     348             :     }
     349             : 
     350           0 :     if (networkChange) {
     351           0 :         checkLink();
     352             :     }
     353             : }
     354             : 
     355             : NS_IMETHODIMP
     356           1 : nsNotifyAddrListener::Run()
     357             : {
     358           1 :     int netlinkSocket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
     359           1 :     if (netlinkSocket < 0) {
     360           0 :         return NS_ERROR_FAILURE;
     361             :     }
     362             : 
     363             :     struct sockaddr_nl addr;
     364           1 :     memset(&addr, 0, sizeof(addr));   // clear addr
     365             : 
     366           1 :     addr.nl_family = AF_NETLINK;
     367           1 :     addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
     368             : 
     369           1 :     if (bind(netlinkSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
     370             :         // failure!
     371           0 :         EINTR_RETRY(close(netlinkSocket));
     372           0 :         return NS_ERROR_FAILURE;
     373             :     }
     374             : 
     375             :     // switch the socket into non-blocking
     376           1 :     int flags = fcntl(netlinkSocket, F_GETFL, 0);
     377           1 :     (void)fcntl(netlinkSocket, F_SETFL, flags | O_NONBLOCK);
     378             : 
     379             :     struct pollfd fds[2];
     380           1 :     fds[0].fd = mShutdownPipe[0];
     381           1 :     fds[0].events = POLLIN;
     382           1 :     fds[0].revents = 0;
     383             : 
     384           1 :     fds[1].fd = netlinkSocket;
     385           1 :     fds[1].events = POLLIN;
     386           1 :     fds[1].revents = 0;
     387             : 
     388           1 :     calculateNetworkId();
     389             : 
     390           1 :     nsresult rv = NS_OK;
     391           1 :     bool shutdown = false;
     392           1 :     int pollWait = -1;
     393           1 :     while (!shutdown) {
     394           1 :         int rc = EINTR_RETRY(poll(fds, 2, pollWait));
     395             : 
     396           0 :         if (rc > 0) {
     397           0 :             if (fds[0].revents & POLLIN) {
     398             :                 // shutdown, abort the loop!
     399           0 :                 LOG(("thread shutdown received, dying...\n"));
     400           0 :                 shutdown = true;
     401           0 :             } else if (fds[1].revents & POLLIN) {
     402           0 :                 LOG(("netlink message received, handling it...\n"));
     403           0 :                 OnNetlinkMessage(netlinkSocket);
     404             :             }
     405           0 :         } else if (rc < 0) {
     406           0 :             rv = NS_ERROR_FAILURE;
     407           0 :             break;
     408             :         }
     409           0 :         if (mCoalescingActive) {
     410             :             // check if coalescing period should continue
     411           0 :             double period = (TimeStamp::Now() - mChangeTime).ToMilliseconds();
     412           0 :             if (period >= kNetworkChangeCoalescingPeriod) {
     413           0 :                 SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
     414           0 :                 calculateNetworkId();
     415           0 :                 mCoalescingActive = false;
     416           0 :                 pollWait = -1; // restore to default
     417             :             } else {
     418             :                 // wait no longer than to the end of the period
     419           0 :                 pollWait = static_cast<int>
     420           0 :                     (kNetworkChangeCoalescingPeriod - period);
     421             :             }
     422             :         }
     423             :     }
     424             : 
     425           0 :     EINTR_RETRY(close(netlinkSocket));
     426             : 
     427           0 :     return rv;
     428             : }
     429             : 
     430             : NS_IMETHODIMP
     431           0 : nsNotifyAddrListener::Observe(nsISupports *subject,
     432             :                               const char *topic,
     433             :                               const char16_t *data)
     434             : {
     435           0 :     if (!strcmp("xpcom-shutdown-threads", topic)) {
     436           0 :         Shutdown();
     437             :     }
     438             : 
     439           0 :     return NS_OK;
     440             : }
     441             : 
     442             : nsresult
     443           1 : nsNotifyAddrListener::Init(void)
     444             : {
     445             :     nsCOMPtr<nsIObserverService> observerService =
     446           2 :         mozilla::services::GetObserverService();
     447           1 :     if (!observerService)
     448           0 :         return NS_ERROR_FAILURE;
     449             : 
     450           2 :     nsresult rv = observerService->AddObserver(this, "xpcom-shutdown-threads",
     451           2 :                                                false);
     452           1 :     NS_ENSURE_SUCCESS(rv, rv);
     453             : 
     454           1 :     Preferences::AddBoolVarCache(&mAllowChangedEvent,
     455           1 :                                  NETWORK_NOTIFY_CHANGED_PREF, true);
     456             : 
     457           1 :     if (-1 == pipe(mShutdownPipe)) {
     458           0 :         return NS_ERROR_FAILURE;
     459             :     }
     460             : 
     461           1 :     rv = NS_NewNamedThread("Link Monitor", getter_AddRefs(mThread), this);
     462           1 :     NS_ENSURE_SUCCESS(rv, rv);
     463             : 
     464           1 :     return NS_OK;
     465             : }
     466             : 
     467             : nsresult
     468           0 : nsNotifyAddrListener::Shutdown(void)
     469             : {
     470             :     // remove xpcom shutdown observer
     471             :     nsCOMPtr<nsIObserverService> observerService =
     472           0 :         mozilla::services::GetObserverService();
     473           0 :     if (observerService)
     474           0 :         observerService->RemoveObserver(this, "xpcom-shutdown-threads");
     475             : 
     476           0 :     LOG(("write() to signal thread shutdown\n"));
     477             : 
     478             :     // awake the thread to make it terminate
     479           0 :     ssize_t rc = EINTR_RETRY(write(mShutdownPipe[1], "1", 1));
     480           0 :     LOG(("write() returned %d, errno == %d\n", (int)rc, errno));
     481             : 
     482           0 :     nsresult rv = mThread->Shutdown();
     483             : 
     484             :     // Have to break the cycle here, otherwise nsNotifyAddrListener holds
     485             :     // onto the thread and the thread holds onto the nsNotifyAddrListener
     486             :     // via its mRunnable
     487           0 :     mThread = nullptr;
     488             : 
     489           0 :     return rv;
     490             : }
     491             : 
     492             : 
     493             : /*
     494             :  * A network event has been registered. Delay the actual sending of the event
     495             :  * for a while and absorb subsequent events in the mean time in an effort to
     496             :  * squash potentially many triggers into a single event.
     497             :  * Only ever called from the same thread.
     498             :  */
     499             : nsresult
     500           0 : nsNotifyAddrListener::NetworkChanged()
     501             : {
     502           0 :     if (mCoalescingActive) {
     503           0 :         LOG(("NetworkChanged: absorbed an event (coalescing active)\n"));
     504             :     } else {
     505             :         // A fresh trigger!
     506           0 :         mChangeTime = TimeStamp::Now();
     507           0 :         mCoalescingActive = true;
     508           0 :         LOG(("NetworkChanged: coalescing period started\n"));
     509             :     }
     510           0 :     return NS_OK;
     511             : }
     512             : 
     513             : /* Sends the given event.  Assumes aEventID never goes out of scope (static
     514             :  * strings are ideal).
     515             :  */
     516             : nsresult
     517           0 : nsNotifyAddrListener::SendEvent(const char *aEventID)
     518             : {
     519           0 :     if (!aEventID)
     520           0 :         return NS_ERROR_NULL_POINTER;
     521             : 
     522           0 :     LOG(("SendEvent: %s\n", aEventID));
     523           0 :     nsresult rv = NS_OK;
     524           0 :     nsCOMPtr<nsIRunnable> event = new ChangeEvent(this, aEventID);
     525           0 :     if (NS_FAILED(rv = NS_DispatchToMainThread(event)))
     526           0 :         NS_WARNING("Failed to dispatch ChangeEvent");
     527           0 :     return rv;
     528             : }
     529             : 
     530             : NS_IMETHODIMP
     531           0 : nsNotifyAddrListener::ChangeEvent::Run()
     532             : {
     533             :     nsCOMPtr<nsIObserverService> observerService =
     534           0 :         mozilla::services::GetObserverService();
     535           0 :     if (observerService)
     536           0 :         observerService->NotifyObservers(
     537             :                 mService, NS_NETWORK_LINK_TOPIC,
     538           0 :                 NS_ConvertASCIItoUTF16(mEventID).get());
     539           0 :     return NS_OK;
     540             : }

Generated by: LCOV version 1.13