build: Add conditional compilation

Two conditionals are introduced in this commit:
- if <ifaddrs.h> is not available, NetworkInterface helper will always return
  an empty set of interfaces
- If dropping/elevating effective user/group is not supported, an error
  will be thrown if used (e.g., if general.user or general.group is
  configured)

Both conditionals are necessary on Android platform.

Change-Id: Ib360e03514af97ed2d68032fbcbe279a8dc84682
diff --git a/.jenkins.d/20-tests.sh b/.jenkins.d/20-tests.sh
index 6385fc9..a02a94c 100755
--- a/.jenkins.d/20-tests.sh
+++ b/.jenkins.d/20-tests.sh
@@ -25,6 +25,7 @@
 # Run unit tests
 # Core
 ./build/unit-tests-core -l test_suite
+sudo ./build/unit-tests-core -t CorePrivilegeHelper -l test_suite
 
 # Daemon
 ./build/unit-tests-daemon -l test_suite
diff --git a/core/network-interface.cpp b/core/network-interface.cpp
index 1aad63e..c6d23a3 100644
--- a/core/network-interface.cpp
+++ b/core/network-interface.cpp
@@ -31,9 +31,11 @@
 #include <type_traits>
 #include <unordered_map>
 
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>     // for getifaddrs()
+
 #include <arpa/inet.h>   // for inet_ntop()
 #include <netinet/in.h>  // for struct sockaddr_in{,6}
-#include <ifaddrs.h>     // for getifaddrs()
 
 #if defined(__linux__)
 #include <net/if_arp.h>        // for ARPHRD_* constants
@@ -43,6 +45,9 @@
 #include <net/if_types.h>      // for IFT_* constants
 #endif
 
+#endif // HAVE_IFADDRS_H
+
+
 NFD_LOG_INIT("NetworkInterfaceInfo");
 
 namespace nfd {
@@ -73,6 +78,7 @@
   }
 #endif
 
+#ifdef HAVE_IFADDRS_H
   using namespace boost::asio::ip;
   using std::strerror;
 
@@ -170,6 +176,9 @@
   }
 
   return v;
+#else
+  return {};
+#endif // HAVE_IFADDRS_H
 }
 
 } // namespace nfd
diff --git a/core/privilege-helper.cpp b/core/privilege-helper.cpp
index 6f87f43..1296331 100644
--- a/core/privilege-helper.cpp
+++ b/core/privilege-helper.cpp
@@ -32,15 +32,18 @@
 
 NFD_LOG_INIT("PrivilegeHelper");
 
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
 uid_t PrivilegeHelper::s_normalUid = ::geteuid();
 gid_t PrivilegeHelper::s_normalGid = ::getegid();
 
 uid_t PrivilegeHelper::s_privilegedUid = ::geteuid();
 gid_t PrivilegeHelper::s_privilegedGid = ::getegid();
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
 
 void
 PrivilegeHelper::initialize(const std::string& userName, const std::string& groupName)
 {
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
   static const size_t MAX_GROUP_BUFFER_SIZE = 16384; // 16kB
   static const size_t MAX_PASSWD_BUFFER_SIZE = 16384;
 
@@ -126,11 +129,15 @@
 
       s_normalUid = passwd.pw_uid;
     }
+#else
+  throw Error("Dropping and raising privileges is not supported on this platform");
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
 }
 
 void
 PrivilegeHelper::drop()
 {
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
   NFD_LOG_TRACE("dropping to effective gid=" << s_normalGid);
   if (::setegid(s_normalGid) != 0)
     {
@@ -150,11 +157,15 @@
     }
 
   NFD_LOG_INFO("dropped to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+#else
+  throw Error("Dropping privileges is not supported on this platform");
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
 }
 
 void
 PrivilegeHelper::raise()
 {
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
   NFD_LOG_TRACE("elevating to effective uid=" << s_privilegedUid);
   if (::seteuid(s_privilegedUid) != 0)
     {
@@ -173,6 +184,9 @@
       throw PrivilegeHelper::Error(error.str());
     }
   NFD_LOG_INFO("elevated to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+#else
+  throw Error("Elevating privileges is not supported on this platform");
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
 }
 
 void
diff --git a/core/privilege-helper.hpp b/core/privilege-helper.hpp
index 92bd53d..db22177 100644
--- a/core/privilege-helper.hpp
+++ b/core/privilege-helper.hpp
@@ -65,18 +65,19 @@
   static void
   runElevated(function<void()> f);
 
-private:
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
 
   static void
   raise();
 
 private:
-
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
   static uid_t s_normalUid;
   static gid_t s_normalGid;
 
   static uid_t s_privilegedUid;
   static gid_t s_privilegedGid;
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
 };
 
 } // namespace nfd
diff --git a/tests/core/privilege-helper.cpp b/tests/core/privilege-helper.cpp
new file mode 100644
index 0000000..bd5f989
--- /dev/null
+++ b/tests/core/privilege-helper.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/privilege-helper.hpp"
+
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(CorePrivilegeHelper, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(DropRaise)
+{
+  if (::geteuid() != 0) {
+    BOOST_TEST_MESSAGE("This test case needs to be run as super user, skipping");
+    return;
+  }
+
+  // The following assumes that nobody/nogroup is present on the test system
+  BOOST_CHECK_NO_THROW(PrivilegeHelper::initialize("nobody", "nogroup"));
+  BOOST_CHECK_EQUAL(::geteuid(), 0);
+
+  BOOST_CHECK_NO_THROW(PrivilegeHelper::drop());
+  BOOST_CHECK_NE(::geteuid(), 0);
+
+  // separate runElevated case to improve log reporting (otherwise output is unreadable)
+  BOOST_CHECK_NO_THROW(PrivilegeHelper::runElevated([]{}));
+  PrivilegeHelper::runElevated([] {
+      BOOST_CHECK_EQUAL(::geteuid(), 0);
+    });
+  BOOST_CHECK_NE(::geteuid(), 0);
+
+  BOOST_CHECK_NO_THROW(PrivilegeHelper::raise());
+  BOOST_CHECK_EQUAL(::geteuid(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
+
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
diff --git a/wscript b/wscript
index 65c249a..34de382 100644
--- a/wscript
+++ b/wscript
@@ -73,6 +73,15 @@
     conf.checkDependency(name='librt', lib='rt', mandatory=False)
     conf.checkDependency(name='libresolv', lib='resolv', mandatory=False)
 
+    conf.check_cxx(header_name='ifaddrs.h', mandatory=False)
+    try:
+        for function in ['setegid', 'seteuid', 'sysconf', 'getgrnam_r', 'getpwnam_r']:
+            conf.check_cxx(function_name=function,
+                           header_name=['unistd.h', 'pwd.h', 'grp.h'], mandatory=True)
+        conf.define('HAVE_PRIVILEGE_DROP_AND_ELEVATE', 1)
+    except:
+        Logs.warn('Dropping privileges is not supported on this platform')
+
     boost_libs = 'system chrono program_options random'
     if conf.options.with_tests:
         conf.env['WITH_TESTS'] = 1