Statistics
| Branch: | Tag: | Revision:

root / lib / netutils.py @ 91c17910

History | View | Annotate | Download (18.6 kB)

1 a744b676 Manuel Franceschini
#
2 a744b676 Manuel Franceschini
#
3 a744b676 Manuel Franceschini
4 8ad0da1e Iustin Pop
# Copyright (C) 2010, 2011, 2012 Google Inc.
5 a744b676 Manuel Franceschini
#
6 a744b676 Manuel Franceschini
# This program is free software; you can redistribute it and/or modify
7 a744b676 Manuel Franceschini
# it under the terms of the GNU General Public License as published by
8 a744b676 Manuel Franceschini
# the Free Software Foundation; either version 2 of the License, or
9 a744b676 Manuel Franceschini
# (at your option) any later version.
10 a744b676 Manuel Franceschini
#
11 a744b676 Manuel Franceschini
# This program is distributed in the hope that it will be useful, but
12 a744b676 Manuel Franceschini
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a744b676 Manuel Franceschini
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a744b676 Manuel Franceschini
# General Public License for more details.
15 a744b676 Manuel Franceschini
#
16 a744b676 Manuel Franceschini
# You should have received a copy of the GNU General Public License
17 a744b676 Manuel Franceschini
# along with this program; if not, write to the Free Software
18 a744b676 Manuel Franceschini
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a744b676 Manuel Franceschini
# 02110-1301, USA.
20 a744b676 Manuel Franceschini
21 a744b676 Manuel Franceschini
22 a744b676 Manuel Franceschini
"""Ganeti network utility module.
23 a744b676 Manuel Franceschini

24 a744b676 Manuel Franceschini
This module holds functions that can be used in both daemons (all) and
25 a744b676 Manuel Franceschini
the command line scripts.
26 a744b676 Manuel Franceschini

27 a744b676 Manuel Franceschini
"""
28 a744b676 Manuel Franceschini
29 a744b676 Manuel Franceschini
30 a744b676 Manuel Franceschini
import errno
31 37531236 Andrea Spadaccini
import os
32 a744b676 Manuel Franceschini
import re
33 a744b676 Manuel Franceschini
import socket
34 a744b676 Manuel Franceschini
import struct
35 a744b676 Manuel Franceschini
import IN
36 37531236 Andrea Spadaccini
import logging
37 a744b676 Manuel Franceschini
38 a744b676 Manuel Franceschini
from ganeti import constants
39 a744b676 Manuel Franceschini
from ganeti import errors
40 37531236 Andrea Spadaccini
from ganeti import utils
41 e91b297c Michael Hanselmann
from ganeti import vcluster
42 a744b676 Manuel Franceschini
43 a744b676 Manuel Franceschini
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
44 a744b676 Manuel Franceschini
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
45 a744b676 Manuel Franceschini
#
46 a744b676 Manuel Franceschini
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
47 a744b676 Manuel Franceschini
# pid_t to "int".
48 a744b676 Manuel Franceschini
#
49 a744b676 Manuel Franceschini
# IEEE Std 1003.1-2008:
50 a744b676 Manuel Franceschini
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
51 a744b676 Manuel Franceschini
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
52 a744b676 Manuel Franceschini
_STRUCT_UCRED = "iII"
53 a744b676 Manuel Franceschini
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
54 a744b676 Manuel Franceschini
55 37531236 Andrea Spadaccini
# Regexes used to find IP addresses in the output of ip.
56 37531236 Andrea Spadaccini
_IP_RE_TEXT = r"[.:a-z0-9]+"      # separate for testing purposes
57 37531236 Andrea Spadaccini
_IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
58 37531236 Andrea Spadaccini
                           re.IGNORECASE)
59 37531236 Andrea Spadaccini
60 37531236 Andrea Spadaccini
# Dict used to convert from a string representing an IP family to an IP
61 37531236 Andrea Spadaccini
# version
62 e687ec01 Michael Hanselmann
_NAME_TO_IP_VER = {
63 37531236 Andrea Spadaccini
  "inet": constants.IP4_VERSION,
64 37531236 Andrea Spadaccini
  "inet6": constants.IP6_VERSION,
65 37531236 Andrea Spadaccini
  }
66 37531236 Andrea Spadaccini
67 37531236 Andrea Spadaccini
68 37531236 Andrea Spadaccini
def _GetIpAddressesFromIpOutput(ip_output):
69 37531236 Andrea Spadaccini
  """Parses the output of the ip command and retrieves the IP addresses and
70 37531236 Andrea Spadaccini
  version.
71 37531236 Andrea Spadaccini

72 37531236 Andrea Spadaccini
  @param ip_output: string containing the output of the ip command;
73 37531236 Andrea Spadaccini
  @rtype: dict; (int, list)
74 37531236 Andrea Spadaccini
  @return: a dict having as keys the IP versions and as values the
75 37531236 Andrea Spadaccini
           corresponding list of addresses found in the IP output.
76 37531236 Andrea Spadaccini

77 37531236 Andrea Spadaccini
  """
78 37531236 Andrea Spadaccini
  addr = dict((i, []) for i in _NAME_TO_IP_VER.values())
79 37531236 Andrea Spadaccini
80 37531236 Andrea Spadaccini
  for row in ip_output.splitlines():
81 37531236 Andrea Spadaccini
    match = _IP_FAMILY_RE.search(row)
82 37531236 Andrea Spadaccini
    if match and IPAddress.IsValid(match.group("ip")):
83 37531236 Andrea Spadaccini
      addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip"))
84 37531236 Andrea Spadaccini
85 37531236 Andrea Spadaccini
  return addr
86 37531236 Andrea Spadaccini
87 a744b676 Manuel Franceschini
88 a744b676 Manuel Franceschini
def GetSocketCredentials(sock):
89 a744b676 Manuel Franceschini
  """Returns the credentials of the foreign process connected to a socket.
90 a744b676 Manuel Franceschini

91 a744b676 Manuel Franceschini
  @param sock: Unix socket
92 a744b676 Manuel Franceschini
  @rtype: tuple; (number, number, number)
93 a744b676 Manuel Franceschini
  @return: The PID, UID and GID of the connected foreign process.
94 a744b676 Manuel Franceschini

95 a744b676 Manuel Franceschini
  """
96 a744b676 Manuel Franceschini
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
97 a744b676 Manuel Franceschini
                             _STRUCT_UCRED_SIZE)
98 a744b676 Manuel Franceschini
  return struct.unpack(_STRUCT_UCRED, peercred)
99 a744b676 Manuel Franceschini
100 a744b676 Manuel Franceschini
101 37531236 Andrea Spadaccini
def IsValidInterface(ifname):
102 37531236 Andrea Spadaccini
  """Validate an interface name.
103 37531236 Andrea Spadaccini

104 37531236 Andrea Spadaccini
  @type ifname: string
105 37531236 Andrea Spadaccini
  @param ifname: Name of the network interface
106 37531236 Andrea Spadaccini
  @return: boolean indicating whether the interface name is valid or not.
107 37531236 Andrea Spadaccini

108 37531236 Andrea Spadaccini
  """
109 37531236 Andrea Spadaccini
  return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
110 37531236 Andrea Spadaccini
111 37531236 Andrea Spadaccini
112 37531236 Andrea Spadaccini
def GetInterfaceIpAddresses(ifname):
113 37531236 Andrea Spadaccini
  """Returns the IP addresses associated to the interface.
114 37531236 Andrea Spadaccini

115 37531236 Andrea Spadaccini
  @type ifname: string
116 37531236 Andrea Spadaccini
  @param ifname: Name of the network interface
117 37531236 Andrea Spadaccini
  @return: A dict having for keys the IP version (either
118 37531236 Andrea Spadaccini
           L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
119 37531236 Andrea Spadaccini
           values the lists of IP addresses of the respective version
120 37531236 Andrea Spadaccini
           associated to the interface
121 37531236 Andrea Spadaccini

122 37531236 Andrea Spadaccini
  """
123 37531236 Andrea Spadaccini
  result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show",
124 37531236 Andrea Spadaccini
                         ifname])
125 37531236 Andrea Spadaccini
126 37531236 Andrea Spadaccini
  if result.failed:
127 37531236 Andrea Spadaccini
    logging.error("Error running the ip command while getting the IP"
128 37531236 Andrea Spadaccini
                  " addresses of %s", ifname)
129 37531236 Andrea Spadaccini
    return None
130 37531236 Andrea Spadaccini
131 37531236 Andrea Spadaccini
  return _GetIpAddressesFromIpOutput(result.output)
132 37531236 Andrea Spadaccini
133 37531236 Andrea Spadaccini
134 b705c7a6 Manuel Franceschini
def GetHostname(name=None, family=None):
135 b705c7a6 Manuel Franceschini
  """Returns a Hostname object.
136 a744b676 Manuel Franceschini

137 b705c7a6 Manuel Franceschini
  @type name: str
138 b705c7a6 Manuel Franceschini
  @param name: hostname or None
139 b705c7a6 Manuel Franceschini
  @type family: int
140 b705c7a6 Manuel Franceschini
  @param family: AF_INET | AF_INET6 | None
141 b705c7a6 Manuel Franceschini
  @rtype: L{Hostname}
142 b705c7a6 Manuel Franceschini
  @return: Hostname object
143 17f7fd27 Manuel Franceschini
  @raise errors.OpPrereqError: in case of errors in resolving
144 b705c7a6 Manuel Franceschini

145 b705c7a6 Manuel Franceschini
  """
146 a744b676 Manuel Franceschini
  try:
147 b705c7a6 Manuel Franceschini
    return Hostname(name=name, family=family)
148 a744b676 Manuel Franceschini
  except errors.ResolverError, err:
149 a744b676 Manuel Franceschini
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
150 a744b676 Manuel Franceschini
                               (err[0], err[2]), errors.ECODE_RESOLVER)
151 a744b676 Manuel Franceschini
152 a744b676 Manuel Franceschini
153 b705c7a6 Manuel Franceschini
class Hostname:
154 b705c7a6 Manuel Franceschini
  """Class implementing resolver and hostname functionality.
155 a744b676 Manuel Franceschini

156 a744b676 Manuel Franceschini
  """
157 e7b3ad26 Manuel Franceschini
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
158 e7b3ad26 Manuel Franceschini
159 b705c7a6 Manuel Franceschini
  def __init__(self, name=None, family=None):
160 a744b676 Manuel Franceschini
    """Initialize the host name object.
161 a744b676 Manuel Franceschini

162 b705c7a6 Manuel Franceschini
    If the name argument is None, it will use this system's name.
163 b705c7a6 Manuel Franceschini

164 b705c7a6 Manuel Franceschini
    @type family: int
165 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
166 b705c7a6 Manuel Franceschini
    @type name: str
167 b705c7a6 Manuel Franceschini
    @param name: hostname or None
168 a744b676 Manuel Franceschini

169 a744b676 Manuel Franceschini
    """
170 5f30ea3f Michael Hanselmann
    self.name = self.GetFqdn(name)
171 b705c7a6 Manuel Franceschini
    self.ip = self.GetIP(self.name, family=family)
172 a744b676 Manuel Franceschini
173 f3044516 Manuel Franceschini
  @classmethod
174 f3044516 Manuel Franceschini
  def GetSysName(cls):
175 f3044516 Manuel Franceschini
    """Legacy method the get the current system's name.
176 f3044516 Manuel Franceschini

177 f3044516 Manuel Franceschini
    """
178 f3044516 Manuel Franceschini
    return cls.GetFqdn()
179 f3044516 Manuel Franceschini
180 5f30ea3f Michael Hanselmann
  @classmethod
181 5f30ea3f Michael Hanselmann
  def GetFqdn(cls, hostname=None):
182 f3044516 Manuel Franceschini
    """Return fqdn.
183 f3044516 Manuel Franceschini

184 f3044516 Manuel Franceschini
    If hostname is None the system's fqdn is returned.
185 a744b676 Manuel Franceschini

186 f3044516 Manuel Franceschini
    @type hostname: str
187 f3044516 Manuel Franceschini
    @param hostname: name to be fqdn'ed
188 f3044516 Manuel Franceschini
    @rtype: str
189 f3044516 Manuel Franceschini
    @return: fqdn of given name, if it exists, unmodified name otherwise
190 a744b676 Manuel Franceschini

191 a744b676 Manuel Franceschini
    """
192 f3044516 Manuel Franceschini
    if hostname is None:
193 e91b297c Michael Hanselmann
      virtfqdn = vcluster.GetVirtualHostname()
194 e91b297c Michael Hanselmann
      if virtfqdn:
195 5f30ea3f Michael Hanselmann
        result = virtfqdn
196 e91b297c Michael Hanselmann
      else:
197 5f30ea3f Michael Hanselmann
        result = socket.getfqdn()
198 f3044516 Manuel Franceschini
    else:
199 5f30ea3f Michael Hanselmann
      result = socket.getfqdn(hostname)
200 5f30ea3f Michael Hanselmann
201 5f30ea3f Michael Hanselmann
    return cls.GetNormalizedName(result)
202 a744b676 Manuel Franceschini
203 a744b676 Manuel Franceschini
  @staticmethod
204 b705c7a6 Manuel Franceschini
  def GetIP(hostname, family=None):
205 b705c7a6 Manuel Franceschini
    """Return IP address of given hostname.
206 b705c7a6 Manuel Franceschini

207 b705c7a6 Manuel Franceschini
    Supports both IPv4 and IPv6.
208 a744b676 Manuel Franceschini

209 a744b676 Manuel Franceschini
    @type hostname: str
210 a744b676 Manuel Franceschini
    @param hostname: hostname to look up
211 b705c7a6 Manuel Franceschini
    @type family: int
212 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
213 b705c7a6 Manuel Franceschini
    @rtype: str
214 b705c7a6 Manuel Franceschini
    @return: IP address
215 a744b676 Manuel Franceschini
    @raise errors.ResolverError: in case of errors in resolving
216 a744b676 Manuel Franceschini

217 a744b676 Manuel Franceschini
    """
218 a744b676 Manuel Franceschini
    try:
219 b705c7a6 Manuel Franceschini
      if family in (socket.AF_INET, socket.AF_INET6):
220 b705c7a6 Manuel Franceschini
        result = socket.getaddrinfo(hostname, None, family)
221 b705c7a6 Manuel Franceschini
      else:
222 b43dcc5a Manuel Franceschini
        result = socket.getaddrinfo(hostname, None)
223 a744b676 Manuel Franceschini
    except (socket.gaierror, socket.herror, socket.error), err:
224 a744b676 Manuel Franceschini
      # hostname not found in DNS, or other socket exception in the
225 a744b676 Manuel Franceschini
      # (code, description format)
226 a744b676 Manuel Franceschini
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
227 a744b676 Manuel Franceschini
228 b705c7a6 Manuel Franceschini
    # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
229 b705c7a6 Manuel Franceschini
    # canonname, sockaddr). We return the first tuple's first address in
230 b705c7a6 Manuel Franceschini
    # sockaddr
231 e7b3ad26 Manuel Franceschini
    try:
232 e7b3ad26 Manuel Franceschini
      return result[0][4][0]
233 e7b3ad26 Manuel Franceschini
    except IndexError, err:
234 39d1744a Iustin Pop
      # we don't have here an actual error code, it's just that the
235 39d1744a Iustin Pop
      # data type returned by getaddrinfo is not what we expected;
236 39d1744a Iustin Pop
      # let's keep the same format in the exception arguments with a
237 39d1744a Iustin Pop
      # dummy error code
238 39d1744a Iustin Pop
      raise errors.ResolverError(hostname, 0,
239 39d1744a Iustin Pop
                                 "Unknown error in getaddrinfo(): %s" % err)
240 a744b676 Manuel Franceschini
241 e7b3ad26 Manuel Franceschini
  @classmethod
242 e7b3ad26 Manuel Franceschini
  def GetNormalizedName(cls, hostname):
243 a744b676 Manuel Franceschini
    """Validate and normalize the given hostname.
244 a744b676 Manuel Franceschini

245 a744b676 Manuel Franceschini
    @attention: the validation is a bit more relaxed than the standards
246 a744b676 Manuel Franceschini
        require; most importantly, we allow underscores in names
247 a744b676 Manuel Franceschini
    @raise errors.OpPrereqError: when the name is not valid
248 a744b676 Manuel Franceschini

249 a744b676 Manuel Franceschini
    """
250 a744b676 Manuel Franceschini
    hostname = hostname.lower()
251 e7b3ad26 Manuel Franceschini
    if (not cls._VALID_NAME_RE.match(hostname) or
252 a744b676 Manuel Franceschini
        # double-dots, meaning empty label
253 a744b676 Manuel Franceschini
        ".." in hostname or
254 a744b676 Manuel Franceschini
        # empty initial label
255 a744b676 Manuel Franceschini
        hostname.startswith(".")):
256 a744b676 Manuel Franceschini
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
257 a744b676 Manuel Franceschini
                                 errors.ECODE_INVAL)
258 a744b676 Manuel Franceschini
    if hostname.endswith("."):
259 a744b676 Manuel Franceschini
      hostname = hostname.rstrip(".")
260 a744b676 Manuel Franceschini
    return hostname
261 a744b676 Manuel Franceschini
262 a744b676 Manuel Franceschini
263 a744b676 Manuel Franceschini
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
264 a744b676 Manuel Franceschini
  """Simple ping implementation using TCP connect(2).
265 a744b676 Manuel Franceschini

266 a744b676 Manuel Franceschini
  Check if the given IP is reachable by doing attempting a TCP connect
267 a744b676 Manuel Franceschini
  to it.
268 a744b676 Manuel Franceschini

269 a744b676 Manuel Franceschini
  @type target: str
270 77b02a99 René Nussbaumer
  @param target: the IP to ping
271 a744b676 Manuel Franceschini
  @type port: int
272 a744b676 Manuel Franceschini
  @param port: the port to connect to
273 a744b676 Manuel Franceschini
  @type timeout: int
274 a744b676 Manuel Franceschini
  @param timeout: the timeout on the connection attempt
275 a744b676 Manuel Franceschini
  @type live_port_needed: boolean
276 a744b676 Manuel Franceschini
  @param live_port_needed: whether a closed port will cause the
277 a744b676 Manuel Franceschini
      function to return failure, as if there was a timeout
278 a744b676 Manuel Franceschini
  @type source: str or None
279 a744b676 Manuel Franceschini
  @param source: if specified, will cause the connect to be made
280 a744b676 Manuel Franceschini
      from this specific source address; failures to bind other
281 a744b676 Manuel Franceschini
      than C{EADDRNOTAVAIL} will be ignored
282 a744b676 Manuel Franceschini

283 a744b676 Manuel Franceschini
  """
284 a744b676 Manuel Franceschini
  try:
285 8b312c1d Manuel Franceschini
    family = IPAddress.GetAddressFamily(target)
286 a744b676 Manuel Franceschini
  except errors.GenericError:
287 a744b676 Manuel Franceschini
    return False
288 a744b676 Manuel Franceschini
289 a744b676 Manuel Franceschini
  sock = socket.socket(family, socket.SOCK_STREAM)
290 a744b676 Manuel Franceschini
  success = False
291 a744b676 Manuel Franceschini
292 a744b676 Manuel Franceschini
  if source is not None:
293 a744b676 Manuel Franceschini
    try:
294 a744b676 Manuel Franceschini
      sock.bind((source, 0))
295 8ad0da1e Iustin Pop
    except socket.error, err:
296 8ad0da1e Iustin Pop
      if err[0] == errno.EADDRNOTAVAIL:
297 a744b676 Manuel Franceschini
        success = False
298 a744b676 Manuel Franceschini
299 a744b676 Manuel Franceschini
  sock.settimeout(timeout)
300 a744b676 Manuel Franceschini
301 a744b676 Manuel Franceschini
  try:
302 a744b676 Manuel Franceschini
    sock.connect((target, port))
303 a744b676 Manuel Franceschini
    sock.close()
304 a744b676 Manuel Franceschini
    success = True
305 a744b676 Manuel Franceschini
  except socket.timeout:
306 a744b676 Manuel Franceschini
    success = False
307 8ad0da1e Iustin Pop
  except socket.error, err:
308 8ad0da1e Iustin Pop
    success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
309 a744b676 Manuel Franceschini
310 a744b676 Manuel Franceschini
  return success
311 a744b676 Manuel Franceschini
312 a744b676 Manuel Franceschini
313 a744b676 Manuel Franceschini
def GetDaemonPort(daemon_name):
314 a744b676 Manuel Franceschini
  """Get the daemon port for this cluster.
315 a744b676 Manuel Franceschini

316 a744b676 Manuel Franceschini
  Note that this routine does not read a ganeti-specific file, but
317 a744b676 Manuel Franceschini
  instead uses C{socket.getservbyname} to allow pre-customization of
318 a744b676 Manuel Franceschini
  this parameter outside of Ganeti.
319 a744b676 Manuel Franceschini

320 a744b676 Manuel Franceschini
  @type daemon_name: string
321 a744b676 Manuel Franceschini
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
322 a744b676 Manuel Franceschini
  @rtype: int
323 a744b676 Manuel Franceschini

324 a744b676 Manuel Franceschini
  """
325 a744b676 Manuel Franceschini
  if daemon_name not in constants.DAEMONS_PORTS:
326 a744b676 Manuel Franceschini
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
327 a744b676 Manuel Franceschini
328 a744b676 Manuel Franceschini
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
329 a744b676 Manuel Franceschini
  try:
330 a744b676 Manuel Franceschini
    port = socket.getservbyname(daemon_name, proto)
331 a744b676 Manuel Franceschini
  except socket.error:
332 a744b676 Manuel Franceschini
    port = default_port
333 a744b676 Manuel Franceschini
334 a744b676 Manuel Franceschini
  return port
335 8b312c1d Manuel Franceschini
336 8b312c1d Manuel Franceschini
337 8b312c1d Manuel Franceschini
class IPAddress(object):
338 8b312c1d Manuel Franceschini
  """Class that represents an IP address.
339 8b312c1d Manuel Franceschini

340 8b312c1d Manuel Franceschini
  """
341 8b312c1d Manuel Franceschini
  iplen = 0
342 8b312c1d Manuel Franceschini
  family = None
343 8b312c1d Manuel Franceschini
  loopback_cidr = None
344 8b312c1d Manuel Franceschini
345 8b312c1d Manuel Franceschini
  @staticmethod
346 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
347 8b312c1d Manuel Franceschini
    """Abstract method to please pylint.
348 8b312c1d Manuel Franceschini

349 8b312c1d Manuel Franceschini
    """
350 8b312c1d Manuel Franceschini
    raise NotImplementedError
351 8b312c1d Manuel Franceschini
352 8b312c1d Manuel Franceschini
  @classmethod
353 8b312c1d Manuel Franceschini
  def IsValid(cls, address):
354 8b312c1d Manuel Franceschini
    """Validate a IP address.
355 8b312c1d Manuel Franceschini

356 8b312c1d Manuel Franceschini
    @type address: str
357 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
358 8b312c1d Manuel Franceschini
    @rtype: bool
359 8b312c1d Manuel Franceschini
    @return: True if valid, False otherwise
360 8b312c1d Manuel Franceschini

361 8b312c1d Manuel Franceschini
    """
362 8b312c1d Manuel Franceschini
    if cls.family is None:
363 8b312c1d Manuel Franceschini
      try:
364 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
365 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
366 8b312c1d Manuel Franceschini
        return False
367 8b312c1d Manuel Franceschini
    else:
368 8b312c1d Manuel Franceschini
      family = cls.family
369 8b312c1d Manuel Franceschini
370 8b312c1d Manuel Franceschini
    try:
371 8b312c1d Manuel Franceschini
      socket.inet_pton(family, address)
372 8b312c1d Manuel Franceschini
      return True
373 8b312c1d Manuel Franceschini
    except socket.error:
374 8b312c1d Manuel Franceschini
      return False
375 8b312c1d Manuel Franceschini
376 8b312c1d Manuel Franceschini
  @classmethod
377 7df2c4f0 Andrea Spadaccini
  def ValidateNetmask(cls, netmask):
378 7df2c4f0 Andrea Spadaccini
    """Validate a netmask suffix in CIDR notation.
379 7df2c4f0 Andrea Spadaccini

380 7df2c4f0 Andrea Spadaccini
    @type netmask: int
381 7df2c4f0 Andrea Spadaccini
    @param netmask: netmask suffix to validate
382 7df2c4f0 Andrea Spadaccini
    @rtype: bool
383 7df2c4f0 Andrea Spadaccini
    @return: True if valid, False otherwise
384 7df2c4f0 Andrea Spadaccini

385 7df2c4f0 Andrea Spadaccini
    """
386 7df2c4f0 Andrea Spadaccini
    assert (isinstance(netmask, (int, long)))
387 7df2c4f0 Andrea Spadaccini
388 7df2c4f0 Andrea Spadaccini
    return 0 < netmask <= cls.iplen
389 7df2c4f0 Andrea Spadaccini
390 7df2c4f0 Andrea Spadaccini
  @classmethod
391 8b312c1d Manuel Franceschini
  def Own(cls, address):
392 8b312c1d Manuel Franceschini
    """Check if the current host has the the given IP address.
393 8b312c1d Manuel Franceschini

394 8b312c1d Manuel Franceschini
    This is done by trying to bind the given address. We return True if we
395 8b312c1d Manuel Franceschini
    succeed or false if a socket.error is raised.
396 8b312c1d Manuel Franceschini

397 8b312c1d Manuel Franceschini
    @type address: str
398 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
399 8b312c1d Manuel Franceschini
    @rtype: bool
400 8b312c1d Manuel Franceschini
    @return: True if we own the address, False otherwise
401 8b312c1d Manuel Franceschini

402 8b312c1d Manuel Franceschini
    """
403 8b312c1d Manuel Franceschini
    if cls.family is None:
404 8b312c1d Manuel Franceschini
      try:
405 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
406 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
407 8b312c1d Manuel Franceschini
        return False
408 8b312c1d Manuel Franceschini
    else:
409 8b312c1d Manuel Franceschini
      family = cls.family
410 8b312c1d Manuel Franceschini
411 8b312c1d Manuel Franceschini
    s = socket.socket(family, socket.SOCK_DGRAM)
412 8b312c1d Manuel Franceschini
    success = False
413 8b312c1d Manuel Franceschini
    try:
414 8b312c1d Manuel Franceschini
      try:
415 8b312c1d Manuel Franceschini
        s.bind((address, 0))
416 8b312c1d Manuel Franceschini
        success = True
417 8b312c1d Manuel Franceschini
      except socket.error:
418 8b312c1d Manuel Franceschini
        success = False
419 8b312c1d Manuel Franceschini
    finally:
420 8b312c1d Manuel Franceschini
      s.close()
421 8b312c1d Manuel Franceschini
    return success
422 8b312c1d Manuel Franceschini
423 8b312c1d Manuel Franceschini
  @classmethod
424 8b312c1d Manuel Franceschini
  def InNetwork(cls, cidr, address):
425 8b312c1d Manuel Franceschini
    """Determine whether an address is within a network.
426 8b312c1d Manuel Franceschini

427 8b312c1d Manuel Franceschini
    @type cidr: string
428 8b312c1d Manuel Franceschini
    @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
429 8b312c1d Manuel Franceschini
    @type address: str
430 8b312c1d Manuel Franceschini
    @param address: IP address
431 8b312c1d Manuel Franceschini
    @rtype: bool
432 8b312c1d Manuel Franceschini
    @return: True if address is in cidr, False otherwise
433 8b312c1d Manuel Franceschini

434 8b312c1d Manuel Franceschini
    """
435 8b312c1d Manuel Franceschini
    address_int = cls._GetIPIntFromString(address)
436 8b312c1d Manuel Franceschini
    subnet = cidr.split("/")
437 8b312c1d Manuel Franceschini
    assert len(subnet) == 2
438 8b312c1d Manuel Franceschini
    try:
439 8b312c1d Manuel Franceschini
      prefix = int(subnet[1])
440 8b312c1d Manuel Franceschini
    except ValueError:
441 8b312c1d Manuel Franceschini
      return False
442 8b312c1d Manuel Franceschini
443 8b312c1d Manuel Franceschini
    assert 0 <= prefix <= cls.iplen
444 8b312c1d Manuel Franceschini
    target_int = cls._GetIPIntFromString(subnet[0])
445 8b312c1d Manuel Franceschini
    # Convert prefix netmask to integer value of netmask
446 e687ec01 Michael Hanselmann
    netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
447 8b312c1d Manuel Franceschini
    # Calculate hostmask
448 e687ec01 Michael Hanselmann
    hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
449 8b312c1d Manuel Franceschini
    # Calculate network address by and'ing netmask
450 8b312c1d Manuel Franceschini
    network_int = target_int & netmask_int
451 8b312c1d Manuel Franceschini
    # Calculate broadcast address by or'ing hostmask
452 8b312c1d Manuel Franceschini
    broadcast_int = target_int | hostmask_int
453 8b312c1d Manuel Franceschini
454 8b312c1d Manuel Franceschini
    return network_int <= address_int <= broadcast_int
455 8b312c1d Manuel Franceschini
456 8b312c1d Manuel Franceschini
  @staticmethod
457 8b312c1d Manuel Franceschini
  def GetAddressFamily(address):
458 8b312c1d Manuel Franceschini
    """Get the address family of the given address.
459 8b312c1d Manuel Franceschini

460 8b312c1d Manuel Franceschini
    @type address: str
461 8b312c1d Manuel Franceschini
    @param address: ip address whose family will be returned
462 8b312c1d Manuel Franceschini
    @rtype: int
463 37531236 Andrea Spadaccini
    @return: C{socket.AF_INET} or C{socket.AF_INET6}
464 8b312c1d Manuel Franceschini
    @raise errors.GenericError: for invalid addresses
465 8b312c1d Manuel Franceschini

466 8b312c1d Manuel Franceschini
    """
467 8b312c1d Manuel Franceschini
    try:
468 8b312c1d Manuel Franceschini
      return IP4Address(address).family
469 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
470 8b312c1d Manuel Franceschini
      pass
471 8b312c1d Manuel Franceschini
472 8b312c1d Manuel Franceschini
    try:
473 8b312c1d Manuel Franceschini
      return IP6Address(address).family
474 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
475 8b312c1d Manuel Franceschini
      pass
476 8b312c1d Manuel Franceschini
477 8b312c1d Manuel Franceschini
    raise errors.IPAddressError("Invalid address '%s'" % address)
478 8b312c1d Manuel Franceschini
479 37531236 Andrea Spadaccini
  @staticmethod
480 37531236 Andrea Spadaccini
  def GetVersionFromAddressFamily(family):
481 37531236 Andrea Spadaccini
    """Convert an IP address family to the corresponding IP version.
482 37531236 Andrea Spadaccini

483 37531236 Andrea Spadaccini
    @type family: int
484 37531236 Andrea Spadaccini
    @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
485 37531236 Andrea Spadaccini
    @return: an int containing the IP version, one of L{constants.IP4_VERSION}
486 37531236 Andrea Spadaccini
             or L{constants.IP6_VERSION}
487 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown families
488 37531236 Andrea Spadaccini

489 37531236 Andrea Spadaccini
    """
490 37531236 Andrea Spadaccini
    if family == socket.AF_INET:
491 37531236 Andrea Spadaccini
      return constants.IP4_VERSION
492 37531236 Andrea Spadaccini
    elif family == socket.AF_INET6:
493 37531236 Andrea Spadaccini
      return constants.IP6_VERSION
494 37531236 Andrea Spadaccini
495 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP address family" % family)
496 37531236 Andrea Spadaccini
497 37531236 Andrea Spadaccini
  @staticmethod
498 37531236 Andrea Spadaccini
  def GetAddressFamilyFromVersion(version):
499 37531236 Andrea Spadaccini
    """Convert an IP version to the corresponding IP address family.
500 37531236 Andrea Spadaccini

501 37531236 Andrea Spadaccini
    @type version: int
502 37531236 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
503 37531236 Andrea Spadaccini
                    L{constants.IP6_VERSION}
504 37531236 Andrea Spadaccini
    @return: an int containing the IP address family, one of C{socket.AF_INET}
505 37531236 Andrea Spadaccini
             or C{socket.AF_INET6}
506 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown IP versions
507 37531236 Andrea Spadaccini

508 37531236 Andrea Spadaccini
    """
509 37531236 Andrea Spadaccini
    if version == constants.IP4_VERSION:
510 37531236 Andrea Spadaccini
      return socket.AF_INET
511 37531236 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
512 37531236 Andrea Spadaccini
      return socket.AF_INET6
513 37531236 Andrea Spadaccini
514 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
515 37531236 Andrea Spadaccini
516 7df2c4f0 Andrea Spadaccini
  @staticmethod
517 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpVersion(version):
518 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP version.
519 7df2c4f0 Andrea Spadaccini

520 7df2c4f0 Andrea Spadaccini
    @type version: int
521 7df2c4f0 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
522 7df2c4f0 Andrea Spadaccini
                    L{constants.IP6_VERSION}
523 7df2c4f0 Andrea Spadaccini
    @return: a subclass of L{netutils.IPAddress}
524 7df2c4f0 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknowo IP versions
525 7df2c4f0 Andrea Spadaccini

526 7df2c4f0 Andrea Spadaccini
    """
527 7df2c4f0 Andrea Spadaccini
    if version == constants.IP4_VERSION:
528 7df2c4f0 Andrea Spadaccini
      return IP4Address
529 7df2c4f0 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
530 7df2c4f0 Andrea Spadaccini
      return IP6Address
531 7df2c4f0 Andrea Spadaccini
532 7df2c4f0 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
533 7df2c4f0 Andrea Spadaccini
534 7df2c4f0 Andrea Spadaccini
  @staticmethod
535 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpFamily(family):
536 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP family.
537 7df2c4f0 Andrea Spadaccini

538 7df2c4f0 Andrea Spadaccini
    @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
539 7df2c4f0 Andrea Spadaccini
    @return: a subclass of L{netutils.IPAddress}
540 7df2c4f0 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknowo IP versions
541 7df2c4f0 Andrea Spadaccini

542 7df2c4f0 Andrea Spadaccini
    """
543 7df2c4f0 Andrea Spadaccini
    return IPAddress.GetClassFromIpVersion(
544 7df2c4f0 Andrea Spadaccini
              IPAddress.GetVersionFromAddressFamily(family))
545 7df2c4f0 Andrea Spadaccini
546 8b312c1d Manuel Franceschini
  @classmethod
547 8b312c1d Manuel Franceschini
  def IsLoopback(cls, address):
548 8b312c1d Manuel Franceschini
    """Determine whether it is a loopback address.
549 8b312c1d Manuel Franceschini

550 8b312c1d Manuel Franceschini
    @type address: str
551 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
552 8b312c1d Manuel Franceschini
    @rtype: bool
553 8b312c1d Manuel Franceschini
    @return: True if loopback, False otherwise
554 8b312c1d Manuel Franceschini

555 8b312c1d Manuel Franceschini
    """
556 8b312c1d Manuel Franceschini
    try:
557 8b312c1d Manuel Franceschini
      return cls.InNetwork(cls.loopback_cidr, address)
558 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
559 8b312c1d Manuel Franceschini
      return False
560 8b312c1d Manuel Franceschini
561 8b312c1d Manuel Franceschini
562 8b312c1d Manuel Franceschini
class IP4Address(IPAddress):
563 8b312c1d Manuel Franceschini
  """IPv4 address class.
564 8b312c1d Manuel Franceschini

565 8b312c1d Manuel Franceschini
  """
566 8b312c1d Manuel Franceschini
  iplen = 32
567 8b312c1d Manuel Franceschini
  family = socket.AF_INET
568 8b312c1d Manuel Franceschini
  loopback_cidr = "127.0.0.0/8"
569 8b312c1d Manuel Franceschini
570 8b312c1d Manuel Franceschini
  def __init__(self, address):
571 8b312c1d Manuel Franceschini
    """Constructor for IPv4 address.
572 8b312c1d Manuel Franceschini

573 8b312c1d Manuel Franceschini
    @type address: str
574 8b312c1d Manuel Franceschini
    @param address: IP address
575 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
576 8b312c1d Manuel Franceschini

577 8b312c1d Manuel Franceschini
    """
578 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
579 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
580 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv4 Address %s invalid" % address)
581 8b312c1d Manuel Franceschini
582 8b312c1d Manuel Franceschini
    self.address = address
583 8b312c1d Manuel Franceschini
584 8b312c1d Manuel Franceschini
  @staticmethod
585 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
586 8b312c1d Manuel Franceschini
    """Get integer value of IPv4 address.
587 8b312c1d Manuel Franceschini

588 8b312c1d Manuel Franceschini
    @type address: str
589 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
590 8b312c1d Manuel Franceschini
    @rtype: int
591 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
592 8b312c1d Manuel Franceschini

593 8b312c1d Manuel Franceschini
    """
594 8b312c1d Manuel Franceschini
    address_int = 0
595 8b312c1d Manuel Franceschini
    parts = address.split(".")
596 8b312c1d Manuel Franceschini
    assert len(parts) == 4
597 8b312c1d Manuel Franceschini
    for part in parts:
598 8b312c1d Manuel Franceschini
      address_int = (address_int << 8) | int(part)
599 8b312c1d Manuel Franceschini
600 8b312c1d Manuel Franceschini
    return address_int
601 8b312c1d Manuel Franceschini
602 8b312c1d Manuel Franceschini
603 8b312c1d Manuel Franceschini
class IP6Address(IPAddress):
604 8b312c1d Manuel Franceschini
  """IPv6 address class.
605 8b312c1d Manuel Franceschini

606 8b312c1d Manuel Franceschini
  """
607 8b312c1d Manuel Franceschini
  iplen = 128
608 8b312c1d Manuel Franceschini
  family = socket.AF_INET6
609 8b312c1d Manuel Franceschini
  loopback_cidr = "::1/128"
610 8b312c1d Manuel Franceschini
611 8b312c1d Manuel Franceschini
  def __init__(self, address):
612 8b312c1d Manuel Franceschini
    """Constructor for IPv6 address.
613 8b312c1d Manuel Franceschini

614 8b312c1d Manuel Franceschini
    @type address: str
615 8b312c1d Manuel Franceschini
    @param address: IP address
616 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
617 8b312c1d Manuel Franceschini

618 8b312c1d Manuel Franceschini
    """
619 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
620 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
621 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
622 8b312c1d Manuel Franceschini
    self.address = address
623 8b312c1d Manuel Franceschini
624 8b312c1d Manuel Franceschini
  @staticmethod
625 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
626 8b312c1d Manuel Franceschini
    """Get integer value of IPv6 address.
627 8b312c1d Manuel Franceschini

628 8b312c1d Manuel Franceschini
    @type address: str
629 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
630 8b312c1d Manuel Franceschini
    @rtype: int
631 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
632 8b312c1d Manuel Franceschini

633 8b312c1d Manuel Franceschini
    """
634 8b312c1d Manuel Franceschini
    doublecolons = address.count("::")
635 8b312c1d Manuel Franceschini
    assert not doublecolons > 1
636 8b312c1d Manuel Franceschini
    if doublecolons == 1:
637 8b312c1d Manuel Franceschini
      # We have a shorthand address, expand it
638 8b312c1d Manuel Franceschini
      parts = []
639 8b312c1d Manuel Franceschini
      twoparts = address.split("::")
640 d0c8c01d Iustin Pop
      sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
641 d0c8c01d Iustin Pop
      parts = twoparts[0].split(":")
642 17385bd2 Andrea Spadaccini
      parts.extend(["0"] * (8 - sep))
643 d0c8c01d Iustin Pop
      parts += twoparts[1].split(":")
644 8b312c1d Manuel Franceschini
    else:
645 8b312c1d Manuel Franceschini
      parts = address.split(":")
646 8b312c1d Manuel Franceschini
647 8b312c1d Manuel Franceschini
    address_int = 0
648 8b312c1d Manuel Franceschini
    for part in parts:
649 d0c8c01d Iustin Pop
      address_int = (address_int << 16) + int(part or "0", 16)
650 8b312c1d Manuel Franceschini
651 8b312c1d Manuel Franceschini
    return address_int
652 1a8337f2 Manuel Franceschini
653 7845b8c8 Manuel Franceschini
654 981732fb Manuel Franceschini
def FormatAddress(address, family=None):
655 1a8337f2 Manuel Franceschini
  """Format a socket address
656 1a8337f2 Manuel Franceschini

657 1a8337f2 Manuel Franceschini
  @type address: family specific (usually tuple)
658 1a8337f2 Manuel Franceschini
  @param address: address, as reported by this class
659 981732fb Manuel Franceschini
  @type family: integer
660 981732fb Manuel Franceschini
  @param family: socket family (one of socket.AF_*) or None
661 1a8337f2 Manuel Franceschini

662 1a8337f2 Manuel Franceschini
  """
663 981732fb Manuel Franceschini
  if family is None:
664 981732fb Manuel Franceschini
    try:
665 981732fb Manuel Franceschini
      family = IPAddress.GetAddressFamily(address[0])
666 981732fb Manuel Franceschini
    except errors.IPAddressError:
667 981732fb Manuel Franceschini
      raise errors.ParameterError(address)
668 981732fb Manuel Franceschini
669 1a8337f2 Manuel Franceschini
  if family == socket.AF_UNIX and len(address) == 3:
670 1a8337f2 Manuel Franceschini
    return "pid=%s, uid=%s, gid=%s" % address
671 1a8337f2 Manuel Franceschini
672 1a8337f2 Manuel Franceschini
  if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
673 1a8337f2 Manuel Franceschini
    host, port = address
674 1a8337f2 Manuel Franceschini
    if family == socket.AF_INET6:
675 1a8337f2 Manuel Franceschini
      res = "[%s]" % host
676 1a8337f2 Manuel Franceschini
    else:
677 1a8337f2 Manuel Franceschini
      res = host
678 1a8337f2 Manuel Franceschini
679 1a8337f2 Manuel Franceschini
    if port is not None:
680 1a8337f2 Manuel Franceschini
      res += ":%s" % port
681 1a8337f2 Manuel Franceschini
682 1a8337f2 Manuel Franceschini
    return res
683 1a8337f2 Manuel Franceschini
684 1a8337f2 Manuel Franceschini
  raise errors.ParameterError(family, address)