diff --git a/lib/bootstrap.py b/lib/bootstrap.py
index 8c4663f8253ae4e2ec9110ca1eed6af01f006688..0ae435c6d5ac28612a07e58556a8c0da985b0131 100644
--- a/lib/bootstrap.py
+++ b/lib/bootstrap.py
@@ -222,6 +222,8 @@ def _WaitForNodeDaemon(node_name):
 
   """
   def _CheckNodeDaemon():
+    # Pylint bug <http://www.logilab.org/ticket/35642>
+    # pylint: disable=E1101
     result = rpc.BootstrapRunner().call_version([node_name])[node_name]
     if result.fail_msg:
       raise utils.RetryAgain()
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 3b5ff61e05b7c34cdaf6379373a581c39228938e..7e8e3b5992494ff426b0a6be0e1d2dd45a335377 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -5909,9 +5909,7 @@ class LUNodeSetParams(LogicalUnit):
 
     if old_role == self._ROLE_OFFLINE and new_role != old_role:
       # Trying to transition out of offline status
-      # TODO: Use standard RPC runner, but make sure it works when the node is
-      # still marked offline
-      result = rpc.BootstrapRunner().call_version([node.name])[node.name]
+      result = self.rpc.call_version([node.name])[node.name]
       if result.fail_msg:
         raise errors.OpPrereqError("Node %s is being de-offlined but fails"
                                    " to report its version: %s" %
diff --git a/lib/rpc.py b/lib/rpc.py
index 5a9b167ca4d5a24d4c3f3704210169453279e1ec..3584ffc49eb89774e0296b901427a34dd2d3f4d0 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -239,11 +239,13 @@ class RpcResult(object):
     raise ec(*args) # pylint: disable=W0142
 
 
-def _SsconfResolver(node_list, _,
+def _SsconfResolver(ssconf_ips, node_list, _,
                     ssc=ssconf.SimpleStore,
                     nslookup_fn=netutils.Hostname.GetIP):
   """Return addresses for given node names.
 
+  @type ssconf_ips: bool
+  @param ssconf_ips: Use the ssconf IPs
   @type node_list: list
   @param node_list: List of node names
   @type ssc: class
@@ -255,9 +257,13 @@ def _SsconfResolver(node_list, _,
 
   """
   ss = ssc()
-  iplist = ss.GetNodePrimaryIPList()
   family = ss.GetPrimaryIPFamily()
-  ipmap = dict(entry.split() for entry in iplist)
+
+  if ssconf_ips:
+    iplist = ss.GetNodePrimaryIPList()
+    ipmap = dict(entry.split() for entry in iplist)
+  else:
+    ipmap = {}
 
   result = []
   for node in node_list:
@@ -584,6 +590,7 @@ _ENCODERS = {
 class RpcRunner(_RpcClientBase,
                 _generated_rpc.RpcClientDefault,
                 _generated_rpc.RpcClientBootstrap,
+                _generated_rpc.RpcClientDnsOnly,
                 _generated_rpc.RpcClientConfig):
   """RPC runner class.
 
@@ -624,6 +631,7 @@ class RpcRunner(_RpcClientBase,
                             _req_process_fn=_req_process_fn)
     _generated_rpc.RpcClientConfig.__init__(self)
     _generated_rpc.RpcClientBootstrap.__init__(self)
+    _generated_rpc.RpcClientDnsOnly.__init__(self)
     _generated_rpc.RpcClientDefault.__init__(self)
 
   def _InstDict(self, instance, hvp=None, bep=None, osp=None):
@@ -684,7 +692,7 @@ class JobQueueRunner(_RpcClientBase, _generated_rpc.RpcClientJobQueue):
 
     """
     if address_list is None:
-      resolver = _SsconfResolver
+      resolver = compat.partial(_SsconfResolver, True)
     else:
       # Caller provided an address list
       resolver = _StaticResolver(address_list)
@@ -694,7 +702,9 @@ class JobQueueRunner(_RpcClientBase, _generated_rpc.RpcClientJobQueue):
     _generated_rpc.RpcClientJobQueue.__init__(self)
 
 
-class BootstrapRunner(_RpcClientBase, _generated_rpc.RpcClientBootstrap):
+class BootstrapRunner(_RpcClientBase,
+                      _generated_rpc.RpcClientBootstrap,
+                      _generated_rpc.RpcClientDnsOnly):
   """RPC wrappers for bootstrapping.
 
   """
@@ -702,8 +712,27 @@ class BootstrapRunner(_RpcClientBase, _generated_rpc.RpcClientBootstrap):
     """Initializes this class.
 
     """
-    _RpcClientBase.__init__(self, _SsconfResolver, _ENCODERS.get)
+    # Pylint doesn't recognize multiple inheritance properly, see
+    # <http://www.logilab.org/ticket/36586> and
+    # <http://www.logilab.org/ticket/35642>
+    # pylint: disable=W0233
+    _RpcClientBase.__init__(self, compat.partial(_SsconfResolver, True),
+                            _ENCODERS.get)
     _generated_rpc.RpcClientBootstrap.__init__(self)
+    _generated_rpc.RpcClientDnsOnly.__init__(self)
+
+
+class DnsOnlyRunner(_RpcClientBase, _generated_rpc.RpcClientDnsOnly):
+  """RPC wrappers for calls using only DNS.
+
+  """
+  def __init__(self):
+    """Initialize this class.
+
+    """
+    _RpcClientBase.__init__(self, compat.partial(_SsconfResolver, False),
+                            _ENCODERS.get)
+    _generated_rpc.RpcClientDnsOnly.__init__(self)
 
 
 class ConfigRunner(_RpcClientBase, _generated_rpc.RpcClientConfig):
@@ -721,7 +750,7 @@ class ConfigRunner(_RpcClientBase, _generated_rpc.RpcClientConfig):
       lock_monitor_cb = None
 
     if address_list is None:
-      resolver = _SsconfResolver
+      resolver = compat.partial(_SsconfResolver, True)
     else:
       # Caller provided an address list
       resolver = _StaticResolver(address_list)
diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py
index 44bdc4c06999ff549c0aaf45e360f8ac00bf36b4..669f79129dd53b63d2d6a9ebda5a809759dec7e5 100644
--- a/lib/rpc_defs.py
+++ b/lib/rpc_defs.py
@@ -543,7 +543,10 @@ CALLS = {
      "Requests a node to clean the cluster information it has"),
     ("master_info", MULTI, None, TMO_URGENT, [], None, None,
      "Query master info"),
-    ("version", MULTI, None, TMO_URGENT, [], None, None, "Query node version"),
+    ]),
+  "RpcClientDnsOnly": _Prepare([
+    ("version", MULTI, ACCEPT_OFFLINE_NODE, TMO_URGENT, [], None, None,
+     "Query node version"),
     ]),
   "RpcClientConfig": _Prepare([
     ("upload_file", MULTI, None, TMO_NORMAL, [
diff --git a/test/ganeti.rpc_unittest.py b/test/ganeti.rpc_unittest.py
index 1eb03e85d44525c13eed281e41688fe0de231aee..0fe47a371db4ec42127f3fe7b37204b566c35553 100755
--- a/test/ganeti.rpc_unittest.py
+++ b/test/ganeti.rpc_unittest.py
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2010, 2011 Google Inc.
+# Copyright (C) 2010, 2011, 2012 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -61,6 +61,13 @@ def GetFakeSimpleStoreClass(fn):
   return FakeSimpleStore
 
 
+def _RaiseNotImplemented():
+  """Simple wrapper to raise NotImplementedError.
+
+  """
+  raise NotImplementedError
+
+
 class TestRpcProcessor(unittest.TestCase):
   def _FakeAddressLookup(self, map):
     return lambda node_list: [map.get(node) for node in node_list]
@@ -331,7 +338,7 @@ class TestSsconfResolver(unittest.TestCase):
     node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
     node_addr_list = [" ".join(t) for t in zip(node_list, addr_list)]
     ssc = GetFakeSimpleStoreClass(lambda _: node_addr_list)
-    result = rpc._SsconfResolver(node_list, NotImplemented,
+    result = rpc._SsconfResolver(True, node_list, NotImplemented,
                                  ssc=ssc, nslookup_fn=NotImplemented)
     self.assertEqual(result, zip(node_list, addr_list))
 
@@ -341,7 +348,17 @@ class TestSsconfResolver(unittest.TestCase):
     ssc = GetFakeSimpleStoreClass(lambda _: [])
     node_addr_map = dict(zip(node_list, addr_list))
     nslookup_fn = lambda name, family=None: node_addr_map.get(name)
-    result = rpc._SsconfResolver(node_list, NotImplemented,
+    result = rpc._SsconfResolver(True, node_list, NotImplemented,
+                                 ssc=ssc, nslookup_fn=nslookup_fn)
+    self.assertEqual(result, zip(node_list, addr_list))
+
+  def testDisabledSsconfIp(self):
+    addr_list = ["192.0.2.%d" % n for n in range(0, 255, 13)]
+    node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
+    ssc = GetFakeSimpleStoreClass(_RaiseNotImplemented)
+    node_addr_map = dict(zip(node_list, addr_list))
+    nslookup_fn = lambda name, family=None: node_addr_map.get(name)
+    result = rpc._SsconfResolver(False, node_list, NotImplemented,
                                  ssc=ssc, nslookup_fn=nslookup_fn)
     self.assertEqual(result, zip(node_list, addr_list))
 
@@ -353,7 +370,7 @@ class TestSsconfResolver(unittest.TestCase):
     ssc = GetFakeSimpleStoreClass(lambda _: node_addr_list)
     node_addr_map = dict(zip(node_list[:n], addr_list[:n]))
     nslookup_fn = lambda name, family=None: node_addr_map.get(name)
-    result = rpc._SsconfResolver(node_list, NotImplemented,
+    result = rpc._SsconfResolver(True, node_list, NotImplemented,
                                  ssc=ssc, nslookup_fn=nslookup_fn)
     self.assertEqual(result, zip(node_list, addr_list))
 
@@ -362,7 +379,7 @@ class TestSsconfResolver(unittest.TestCase):
     node_list = ["node%d.example.com" % n for n in range(0, 255, 11)]
     node_addr_list = [" ".join(t) for t in zip(node_list, addr_list)]
     ssc = GetFakeSimpleStoreClass(lambda _: node_addr_list)
-    result = rpc._SsconfResolver(node_list, NotImplemented,
+    result = rpc._SsconfResolver(True, node_list, NotImplemented,
                                  ssc=ssc, nslookup_fn=NotImplemented)
     self.assertEqual(result, zip(node_list, addr_list))