Statistics
| Branch: | Tag: | Revision:

root / lib / netutils.py @ 11e90588

History | View | Annotate | Download (19.1 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 069a4bcf Guido Trotter
# Workaround a bug in some linux distributions that don't define SO_PEERCRED
56 069a4bcf Guido Trotter
try:
57 a3e964cf Klaus Aehlig
  # pylint: disable=E1101
58 069a4bcf Guido Trotter
  _SO_PEERCRED = IN.SO_PEERCRED
59 069a4bcf Guido Trotter
except AttributeError:
60 069a4bcf Guido Trotter
  _SO_PEERCRED = 17
61 069a4bcf Guido Trotter
62 37531236 Andrea Spadaccini
# Regexes used to find IP addresses in the output of ip.
63 37531236 Andrea Spadaccini
_IP_RE_TEXT = r"[.:a-z0-9]+"      # separate for testing purposes
64 37531236 Andrea Spadaccini
_IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
65 37531236 Andrea Spadaccini
                           re.IGNORECASE)
66 37531236 Andrea Spadaccini
67 37531236 Andrea Spadaccini
# Dict used to convert from a string representing an IP family to an IP
68 37531236 Andrea Spadaccini
# version
69 e687ec01 Michael Hanselmann
_NAME_TO_IP_VER = {
70 37531236 Andrea Spadaccini
  "inet": constants.IP4_VERSION,
71 37531236 Andrea Spadaccini
  "inet6": constants.IP6_VERSION,
72 37531236 Andrea Spadaccini
  }
73 37531236 Andrea Spadaccini
74 37531236 Andrea Spadaccini
75 37531236 Andrea Spadaccini
def _GetIpAddressesFromIpOutput(ip_output):
76 37531236 Andrea Spadaccini
  """Parses the output of the ip command and retrieves the IP addresses and
77 37531236 Andrea Spadaccini
  version.
78 37531236 Andrea Spadaccini

79 37531236 Andrea Spadaccini
  @param ip_output: string containing the output of the ip command;
80 37531236 Andrea Spadaccini
  @rtype: dict; (int, list)
81 37531236 Andrea Spadaccini
  @return: a dict having as keys the IP versions and as values the
82 37531236 Andrea Spadaccini
           corresponding list of addresses found in the IP output.
83 37531236 Andrea Spadaccini

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

98 a744b676 Manuel Franceschini
  @param sock: Unix socket
99 a744b676 Manuel Franceschini
  @rtype: tuple; (number, number, number)
100 a744b676 Manuel Franceschini
  @return: The PID, UID and GID of the connected foreign process.
101 a744b676 Manuel Franceschini

102 a744b676 Manuel Franceschini
  """
103 069a4bcf Guido Trotter
  peercred = sock.getsockopt(socket.SOL_SOCKET, _SO_PEERCRED,
104 a744b676 Manuel Franceschini
                             _STRUCT_UCRED_SIZE)
105 a744b676 Manuel Franceschini
  return struct.unpack(_STRUCT_UCRED, peercred)
106 a744b676 Manuel Franceschini
107 a744b676 Manuel Franceschini
108 37531236 Andrea Spadaccini
def IsValidInterface(ifname):
109 37531236 Andrea Spadaccini
  """Validate an interface name.
110 37531236 Andrea Spadaccini

111 37531236 Andrea Spadaccini
  @type ifname: string
112 37531236 Andrea Spadaccini
  @param ifname: Name of the network interface
113 37531236 Andrea Spadaccini
  @return: boolean indicating whether the interface name is valid or not.
114 37531236 Andrea Spadaccini

115 37531236 Andrea Spadaccini
  """
116 37531236 Andrea Spadaccini
  return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
117 37531236 Andrea Spadaccini
118 37531236 Andrea Spadaccini
119 37531236 Andrea Spadaccini
def GetInterfaceIpAddresses(ifname):
120 37531236 Andrea Spadaccini
  """Returns the IP addresses associated to the interface.
121 37531236 Andrea Spadaccini

122 37531236 Andrea Spadaccini
  @type ifname: string
123 37531236 Andrea Spadaccini
  @param ifname: Name of the network interface
124 37531236 Andrea Spadaccini
  @return: A dict having for keys the IP version (either
125 37531236 Andrea Spadaccini
           L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
126 37531236 Andrea Spadaccini
           values the lists of IP addresses of the respective version
127 37531236 Andrea Spadaccini
           associated to the interface
128 37531236 Andrea Spadaccini

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

144 b705c7a6 Manuel Franceschini
  @type name: str
145 b705c7a6 Manuel Franceschini
  @param name: hostname or None
146 b705c7a6 Manuel Franceschini
  @type family: int
147 b705c7a6 Manuel Franceschini
  @param family: AF_INET | AF_INET6 | None
148 b705c7a6 Manuel Franceschini
  @rtype: L{Hostname}
149 b705c7a6 Manuel Franceschini
  @return: Hostname object
150 17f7fd27 Manuel Franceschini
  @raise errors.OpPrereqError: in case of errors in resolving
151 b705c7a6 Manuel Franceschini

152 b705c7a6 Manuel Franceschini
  """
153 a744b676 Manuel Franceschini
  try:
154 b705c7a6 Manuel Franceschini
    return Hostname(name=name, family=family)
155 a744b676 Manuel Franceschini
  except errors.ResolverError, err:
156 a744b676 Manuel Franceschini
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
157 a744b676 Manuel Franceschini
                               (err[0], err[2]), errors.ECODE_RESOLVER)
158 a744b676 Manuel Franceschini
159 a744b676 Manuel Franceschini
160 b705c7a6 Manuel Franceschini
class Hostname:
161 b705c7a6 Manuel Franceschini
  """Class implementing resolver and hostname functionality.
162 a744b676 Manuel Franceschini

163 a744b676 Manuel Franceschini
  """
164 e7b3ad26 Manuel Franceschini
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
165 e7b3ad26 Manuel Franceschini
166 b705c7a6 Manuel Franceschini
  def __init__(self, name=None, family=None):
167 a744b676 Manuel Franceschini
    """Initialize the host name object.
168 a744b676 Manuel Franceschini

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

171 b705c7a6 Manuel Franceschini
    @type family: int
172 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
173 b705c7a6 Manuel Franceschini
    @type name: str
174 b705c7a6 Manuel Franceschini
    @param name: hostname or None
175 a744b676 Manuel Franceschini

176 a744b676 Manuel Franceschini
    """
177 5f30ea3f Michael Hanselmann
    self.name = self.GetFqdn(name)
178 b705c7a6 Manuel Franceschini
    self.ip = self.GetIP(self.name, family=family)
179 a744b676 Manuel Franceschini
180 f3044516 Manuel Franceschini
  @classmethod
181 f3044516 Manuel Franceschini
  def GetSysName(cls):
182 f3044516 Manuel Franceschini
    """Legacy method the get the current system's name.
183 f3044516 Manuel Franceschini

184 f3044516 Manuel Franceschini
    """
185 f3044516 Manuel Franceschini
    return cls.GetFqdn()
186 f3044516 Manuel Franceschini
187 5f30ea3f Michael Hanselmann
  @classmethod
188 5f30ea3f Michael Hanselmann
  def GetFqdn(cls, hostname=None):
189 f3044516 Manuel Franceschini
    """Return fqdn.
190 f3044516 Manuel Franceschini

191 f3044516 Manuel Franceschini
    If hostname is None the system's fqdn is returned.
192 a744b676 Manuel Franceschini

193 f3044516 Manuel Franceschini
    @type hostname: str
194 f3044516 Manuel Franceschini
    @param hostname: name to be fqdn'ed
195 f3044516 Manuel Franceschini
    @rtype: str
196 f3044516 Manuel Franceschini
    @return: fqdn of given name, if it exists, unmodified name otherwise
197 a744b676 Manuel Franceschini

198 a744b676 Manuel Franceschini
    """
199 f3044516 Manuel Franceschini
    if hostname is None:
200 e91b297c Michael Hanselmann
      virtfqdn = vcluster.GetVirtualHostname()
201 e91b297c Michael Hanselmann
      if virtfqdn:
202 5f30ea3f Michael Hanselmann
        result = virtfqdn
203 e91b297c Michael Hanselmann
      else:
204 5f30ea3f Michael Hanselmann
        result = socket.getfqdn()
205 f3044516 Manuel Franceschini
    else:
206 5f30ea3f Michael Hanselmann
      result = socket.getfqdn(hostname)
207 5f30ea3f Michael Hanselmann
208 5f30ea3f Michael Hanselmann
    return cls.GetNormalizedName(result)
209 a744b676 Manuel Franceschini
210 a744b676 Manuel Franceschini
  @staticmethod
211 b705c7a6 Manuel Franceschini
  def GetIP(hostname, family=None):
212 b705c7a6 Manuel Franceschini
    """Return IP address of given hostname.
213 b705c7a6 Manuel Franceschini

214 b705c7a6 Manuel Franceschini
    Supports both IPv4 and IPv6.
215 a744b676 Manuel Franceschini

216 a744b676 Manuel Franceschini
    @type hostname: str
217 a744b676 Manuel Franceschini
    @param hostname: hostname to look up
218 b705c7a6 Manuel Franceschini
    @type family: int
219 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
220 b705c7a6 Manuel Franceschini
    @rtype: str
221 b705c7a6 Manuel Franceschini
    @return: IP address
222 a744b676 Manuel Franceschini
    @raise errors.ResolverError: in case of errors in resolving
223 a744b676 Manuel Franceschini

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

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

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

273 a744b676 Manuel Franceschini
  Check if the given IP is reachable by doing attempting a TCP connect
274 a744b676 Manuel Franceschini
  to it.
275 a744b676 Manuel Franceschini

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

290 a744b676 Manuel Franceschini
  """
291 25ba209e Michael Hanselmann
  logging.debug("Attempting to reach TCP port %s on target %s with a timeout"
292 25ba209e Michael Hanselmann
                " of %s seconds", port, target, timeout)
293 25ba209e Michael Hanselmann
294 a744b676 Manuel Franceschini
  try:
295 8b312c1d Manuel Franceschini
    family = IPAddress.GetAddressFamily(target)
296 25ba209e Michael Hanselmann
  except errors.IPAddressError, err:
297 25ba209e Michael Hanselmann
    raise errors.ProgrammerError("Family of IP address given in parameter"
298 25ba209e Michael Hanselmann
                                 " 'target' can't be determined: %s" % err)
299 a744b676 Manuel Franceschini
300 a744b676 Manuel Franceschini
  sock = socket.socket(family, socket.SOCK_STREAM)
301 a744b676 Manuel Franceschini
  success = False
302 a744b676 Manuel Franceschini
303 a744b676 Manuel Franceschini
  if source is not None:
304 a744b676 Manuel Franceschini
    try:
305 a744b676 Manuel Franceschini
      sock.bind((source, 0))
306 8ad0da1e Iustin Pop
    except socket.error, err:
307 8ad0da1e Iustin Pop
      if err[0] == errno.EADDRNOTAVAIL:
308 a744b676 Manuel Franceschini
        success = False
309 a744b676 Manuel Franceschini
310 a744b676 Manuel Franceschini
  sock.settimeout(timeout)
311 a744b676 Manuel Franceschini
312 a744b676 Manuel Franceschini
  try:
313 a744b676 Manuel Franceschini
    sock.connect((target, port))
314 a744b676 Manuel Franceschini
    sock.close()
315 a744b676 Manuel Franceschini
    success = True
316 a744b676 Manuel Franceschini
  except socket.timeout:
317 a744b676 Manuel Franceschini
    success = False
318 8ad0da1e Iustin Pop
  except socket.error, err:
319 8ad0da1e Iustin Pop
    success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
320 a744b676 Manuel Franceschini
321 a744b676 Manuel Franceschini
  return success
322 a744b676 Manuel Franceschini
323 a744b676 Manuel Franceschini
324 a744b676 Manuel Franceschini
def GetDaemonPort(daemon_name):
325 a744b676 Manuel Franceschini
  """Get the daemon port for this cluster.
326 a744b676 Manuel Franceschini

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

331 a744b676 Manuel Franceschini
  @type daemon_name: string
332 a744b676 Manuel Franceschini
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
333 a744b676 Manuel Franceschini
  @rtype: int
334 a744b676 Manuel Franceschini

335 a744b676 Manuel Franceschini
  """
336 a744b676 Manuel Franceschini
  if daemon_name not in constants.DAEMONS_PORTS:
337 a744b676 Manuel Franceschini
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
338 a744b676 Manuel Franceschini
339 a744b676 Manuel Franceschini
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
340 a744b676 Manuel Franceschini
  try:
341 a744b676 Manuel Franceschini
    port = socket.getservbyname(daemon_name, proto)
342 a744b676 Manuel Franceschini
  except socket.error:
343 a744b676 Manuel Franceschini
    port = default_port
344 a744b676 Manuel Franceschini
345 a744b676 Manuel Franceschini
  return port
346 8b312c1d Manuel Franceschini
347 8b312c1d Manuel Franceschini
348 8b312c1d Manuel Franceschini
class IPAddress(object):
349 8b312c1d Manuel Franceschini
  """Class that represents an IP address.
350 8b312c1d Manuel Franceschini

351 8b312c1d Manuel Franceschini
  """
352 8b312c1d Manuel Franceschini
  iplen = 0
353 8b312c1d Manuel Franceschini
  family = None
354 8b312c1d Manuel Franceschini
  loopback_cidr = None
355 8b312c1d Manuel Franceschini
356 8b312c1d Manuel Franceschini
  @staticmethod
357 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
358 8b312c1d Manuel Franceschini
    """Abstract method to please pylint.
359 8b312c1d Manuel Franceschini

360 8b312c1d Manuel Franceschini
    """
361 8b312c1d Manuel Franceschini
    raise NotImplementedError
362 8b312c1d Manuel Franceschini
363 8b312c1d Manuel Franceschini
  @classmethod
364 8b312c1d Manuel Franceschini
  def IsValid(cls, address):
365 8b312c1d Manuel Franceschini
    """Validate a IP address.
366 8b312c1d Manuel Franceschini

367 8b312c1d Manuel Franceschini
    @type address: str
368 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
369 8b312c1d Manuel Franceschini
    @rtype: bool
370 8b312c1d Manuel Franceschini
    @return: True if valid, False otherwise
371 8b312c1d Manuel Franceschini

372 8b312c1d Manuel Franceschini
    """
373 8b312c1d Manuel Franceschini
    if cls.family is None:
374 8b312c1d Manuel Franceschini
      try:
375 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
376 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
377 8b312c1d Manuel Franceschini
        return False
378 8b312c1d Manuel Franceschini
    else:
379 8b312c1d Manuel Franceschini
      family = cls.family
380 8b312c1d Manuel Franceschini
381 8b312c1d Manuel Franceschini
    try:
382 8b312c1d Manuel Franceschini
      socket.inet_pton(family, address)
383 8b312c1d Manuel Franceschini
      return True
384 8b312c1d Manuel Franceschini
    except socket.error:
385 8b312c1d Manuel Franceschini
      return False
386 8b312c1d Manuel Franceschini
387 8b312c1d Manuel Franceschini
  @classmethod
388 7df2c4f0 Andrea Spadaccini
  def ValidateNetmask(cls, netmask):
389 7df2c4f0 Andrea Spadaccini
    """Validate a netmask suffix in CIDR notation.
390 7df2c4f0 Andrea Spadaccini

391 7df2c4f0 Andrea Spadaccini
    @type netmask: int
392 7df2c4f0 Andrea Spadaccini
    @param netmask: netmask suffix to validate
393 7df2c4f0 Andrea Spadaccini
    @rtype: bool
394 7df2c4f0 Andrea Spadaccini
    @return: True if valid, False otherwise
395 7df2c4f0 Andrea Spadaccini

396 7df2c4f0 Andrea Spadaccini
    """
397 7df2c4f0 Andrea Spadaccini
    assert (isinstance(netmask, (int, long)))
398 7df2c4f0 Andrea Spadaccini
399 7df2c4f0 Andrea Spadaccini
    return 0 < netmask <= cls.iplen
400 7df2c4f0 Andrea Spadaccini
401 7df2c4f0 Andrea Spadaccini
  @classmethod
402 8b312c1d Manuel Franceschini
  def Own(cls, address):
403 8b312c1d Manuel Franceschini
    """Check if the current host has the the given IP address.
404 8b312c1d Manuel Franceschini

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

408 8b312c1d Manuel Franceschini
    @type address: str
409 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
410 8b312c1d Manuel Franceschini
    @rtype: bool
411 8b312c1d Manuel Franceschini
    @return: True if we own the address, False otherwise
412 8b312c1d Manuel Franceschini

413 8b312c1d Manuel Franceschini
    """
414 8b312c1d Manuel Franceschini
    if cls.family is None:
415 8b312c1d Manuel Franceschini
      try:
416 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
417 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
418 8b312c1d Manuel Franceschini
        return False
419 8b312c1d Manuel Franceschini
    else:
420 8b312c1d Manuel Franceschini
      family = cls.family
421 8b312c1d Manuel Franceschini
422 8b312c1d Manuel Franceschini
    s = socket.socket(family, socket.SOCK_DGRAM)
423 8b312c1d Manuel Franceschini
    success = False
424 8b312c1d Manuel Franceschini
    try:
425 8b312c1d Manuel Franceschini
      try:
426 8b312c1d Manuel Franceschini
        s.bind((address, 0))
427 8b312c1d Manuel Franceschini
        success = True
428 8b312c1d Manuel Franceschini
      except socket.error:
429 8b312c1d Manuel Franceschini
        success = False
430 8b312c1d Manuel Franceschini
    finally:
431 8b312c1d Manuel Franceschini
      s.close()
432 8b312c1d Manuel Franceschini
    return success
433 8b312c1d Manuel Franceschini
434 8b312c1d Manuel Franceschini
  @classmethod
435 8b312c1d Manuel Franceschini
  def InNetwork(cls, cidr, address):
436 8b312c1d Manuel Franceschini
    """Determine whether an address is within a network.
437 8b312c1d Manuel Franceschini

438 8b312c1d Manuel Franceschini
    @type cidr: string
439 8b312c1d Manuel Franceschini
    @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
440 8b312c1d Manuel Franceschini
    @type address: str
441 8b312c1d Manuel Franceschini
    @param address: IP address
442 8b312c1d Manuel Franceschini
    @rtype: bool
443 8b312c1d Manuel Franceschini
    @return: True if address is in cidr, False otherwise
444 8b312c1d Manuel Franceschini

445 8b312c1d Manuel Franceschini
    """
446 8b312c1d Manuel Franceschini
    address_int = cls._GetIPIntFromString(address)
447 8b312c1d Manuel Franceschini
    subnet = cidr.split("/")
448 8b312c1d Manuel Franceschini
    assert len(subnet) == 2
449 8b312c1d Manuel Franceschini
    try:
450 8b312c1d Manuel Franceschini
      prefix = int(subnet[1])
451 8b312c1d Manuel Franceschini
    except ValueError:
452 8b312c1d Manuel Franceschini
      return False
453 8b312c1d Manuel Franceschini
454 8b312c1d Manuel Franceschini
    assert 0 <= prefix <= cls.iplen
455 8b312c1d Manuel Franceschini
    target_int = cls._GetIPIntFromString(subnet[0])
456 8b312c1d Manuel Franceschini
    # Convert prefix netmask to integer value of netmask
457 e687ec01 Michael Hanselmann
    netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
458 8b312c1d Manuel Franceschini
    # Calculate hostmask
459 e687ec01 Michael Hanselmann
    hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
460 8b312c1d Manuel Franceschini
    # Calculate network address by and'ing netmask
461 8b312c1d Manuel Franceschini
    network_int = target_int & netmask_int
462 8b312c1d Manuel Franceschini
    # Calculate broadcast address by or'ing hostmask
463 8b312c1d Manuel Franceschini
    broadcast_int = target_int | hostmask_int
464 8b312c1d Manuel Franceschini
465 8b312c1d Manuel Franceschini
    return network_int <= address_int <= broadcast_int
466 8b312c1d Manuel Franceschini
467 8b312c1d Manuel Franceschini
  @staticmethod
468 8b312c1d Manuel Franceschini
  def GetAddressFamily(address):
469 8b312c1d Manuel Franceschini
    """Get the address family of the given address.
470 8b312c1d Manuel Franceschini

471 8b312c1d Manuel Franceschini
    @type address: str
472 8b312c1d Manuel Franceschini
    @param address: ip address whose family will be returned
473 8b312c1d Manuel Franceschini
    @rtype: int
474 37531236 Andrea Spadaccini
    @return: C{socket.AF_INET} or C{socket.AF_INET6}
475 8b312c1d Manuel Franceschini
    @raise errors.GenericError: for invalid addresses
476 8b312c1d Manuel Franceschini

477 8b312c1d Manuel Franceschini
    """
478 8b312c1d Manuel Franceschini
    try:
479 8b312c1d Manuel Franceschini
      return IP4Address(address).family
480 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
481 8b312c1d Manuel Franceschini
      pass
482 8b312c1d Manuel Franceschini
483 8b312c1d Manuel Franceschini
    try:
484 8b312c1d Manuel Franceschini
      return IP6Address(address).family
485 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
486 8b312c1d Manuel Franceschini
      pass
487 8b312c1d Manuel Franceschini
488 8b312c1d Manuel Franceschini
    raise errors.IPAddressError("Invalid address '%s'" % address)
489 8b312c1d Manuel Franceschini
490 37531236 Andrea Spadaccini
  @staticmethod
491 37531236 Andrea Spadaccini
  def GetVersionFromAddressFamily(family):
492 37531236 Andrea Spadaccini
    """Convert an IP address family to the corresponding IP version.
493 37531236 Andrea Spadaccini

494 37531236 Andrea Spadaccini
    @type family: int
495 37531236 Andrea Spadaccini
    @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
496 37531236 Andrea Spadaccini
    @return: an int containing the IP version, one of L{constants.IP4_VERSION}
497 37531236 Andrea Spadaccini
             or L{constants.IP6_VERSION}
498 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown families
499 37531236 Andrea Spadaccini

500 37531236 Andrea Spadaccini
    """
501 37531236 Andrea Spadaccini
    if family == socket.AF_INET:
502 37531236 Andrea Spadaccini
      return constants.IP4_VERSION
503 37531236 Andrea Spadaccini
    elif family == socket.AF_INET6:
504 37531236 Andrea Spadaccini
      return constants.IP6_VERSION
505 37531236 Andrea Spadaccini
506 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP address family" % family)
507 37531236 Andrea Spadaccini
508 37531236 Andrea Spadaccini
  @staticmethod
509 37531236 Andrea Spadaccini
  def GetAddressFamilyFromVersion(version):
510 37531236 Andrea Spadaccini
    """Convert an IP version to the corresponding IP address family.
511 37531236 Andrea Spadaccini

512 37531236 Andrea Spadaccini
    @type version: int
513 37531236 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
514 37531236 Andrea Spadaccini
                    L{constants.IP6_VERSION}
515 37531236 Andrea Spadaccini
    @return: an int containing the IP address family, one of C{socket.AF_INET}
516 37531236 Andrea Spadaccini
             or C{socket.AF_INET6}
517 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown IP versions
518 37531236 Andrea Spadaccini

519 37531236 Andrea Spadaccini
    """
520 37531236 Andrea Spadaccini
    if version == constants.IP4_VERSION:
521 37531236 Andrea Spadaccini
      return socket.AF_INET
522 37531236 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
523 37531236 Andrea Spadaccini
      return socket.AF_INET6
524 37531236 Andrea Spadaccini
525 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
526 37531236 Andrea Spadaccini
527 7df2c4f0 Andrea Spadaccini
  @staticmethod
528 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpVersion(version):
529 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP version.
530 7df2c4f0 Andrea Spadaccini

531 7df2c4f0 Andrea Spadaccini
    @type version: int
532 7df2c4f0 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
533 7df2c4f0 Andrea Spadaccini
                    L{constants.IP6_VERSION}
534 7df2c4f0 Andrea Spadaccini
    @return: a subclass of L{netutils.IPAddress}
535 7df2c4f0 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknowo IP versions
536 7df2c4f0 Andrea Spadaccini

537 7df2c4f0 Andrea Spadaccini
    """
538 7df2c4f0 Andrea Spadaccini
    if version == constants.IP4_VERSION:
539 7df2c4f0 Andrea Spadaccini
      return IP4Address
540 7df2c4f0 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
541 7df2c4f0 Andrea Spadaccini
      return IP6Address
542 7df2c4f0 Andrea Spadaccini
543 7df2c4f0 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
544 7df2c4f0 Andrea Spadaccini
545 7df2c4f0 Andrea Spadaccini
  @staticmethod
546 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpFamily(family):
547 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP family.
548 7df2c4f0 Andrea Spadaccini

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

553 7df2c4f0 Andrea Spadaccini
    """
554 7df2c4f0 Andrea Spadaccini
    return IPAddress.GetClassFromIpVersion(
555 7df2c4f0 Andrea Spadaccini
              IPAddress.GetVersionFromAddressFamily(family))
556 7df2c4f0 Andrea Spadaccini
557 8b312c1d Manuel Franceschini
  @classmethod
558 8b312c1d Manuel Franceschini
  def IsLoopback(cls, address):
559 8b312c1d Manuel Franceschini
    """Determine whether it is a loopback address.
560 8b312c1d Manuel Franceschini

561 8b312c1d Manuel Franceschini
    @type address: str
562 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
563 8b312c1d Manuel Franceschini
    @rtype: bool
564 8b312c1d Manuel Franceschini
    @return: True if loopback, False otherwise
565 8b312c1d Manuel Franceschini

566 8b312c1d Manuel Franceschini
    """
567 8b312c1d Manuel Franceschini
    try:
568 8b312c1d Manuel Franceschini
      return cls.InNetwork(cls.loopback_cidr, address)
569 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
570 8b312c1d Manuel Franceschini
      return False
571 8b312c1d Manuel Franceschini
572 8b312c1d Manuel Franceschini
573 8b312c1d Manuel Franceschini
class IP4Address(IPAddress):
574 8b312c1d Manuel Franceschini
  """IPv4 address class.
575 8b312c1d Manuel Franceschini

576 8b312c1d Manuel Franceschini
  """
577 8b312c1d Manuel Franceschini
  iplen = 32
578 8b312c1d Manuel Franceschini
  family = socket.AF_INET
579 8b312c1d Manuel Franceschini
  loopback_cidr = "127.0.0.0/8"
580 8b312c1d Manuel Franceschini
581 8b312c1d Manuel Franceschini
  def __init__(self, address):
582 8b312c1d Manuel Franceschini
    """Constructor for IPv4 address.
583 8b312c1d Manuel Franceschini

584 8b312c1d Manuel Franceschini
    @type address: str
585 8b312c1d Manuel Franceschini
    @param address: IP address
586 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
587 8b312c1d Manuel Franceschini

588 8b312c1d Manuel Franceschini
    """
589 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
590 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
591 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv4 Address %s invalid" % address)
592 8b312c1d Manuel Franceschini
593 8b312c1d Manuel Franceschini
    self.address = address
594 8b312c1d Manuel Franceschini
595 8b312c1d Manuel Franceschini
  @staticmethod
596 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
597 8b312c1d Manuel Franceschini
    """Get integer value of IPv4 address.
598 8b312c1d Manuel Franceschini

599 8b312c1d Manuel Franceschini
    @type address: str
600 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
601 8b312c1d Manuel Franceschini
    @rtype: int
602 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
603 8b312c1d Manuel Franceschini

604 8b312c1d Manuel Franceschini
    """
605 8b312c1d Manuel Franceschini
    address_int = 0
606 8b312c1d Manuel Franceschini
    parts = address.split(".")
607 8b312c1d Manuel Franceschini
    assert len(parts) == 4
608 8b312c1d Manuel Franceschini
    for part in parts:
609 8b312c1d Manuel Franceschini
      address_int = (address_int << 8) | int(part)
610 8b312c1d Manuel Franceschini
611 8b312c1d Manuel Franceschini
    return address_int
612 8b312c1d Manuel Franceschini
613 8b312c1d Manuel Franceschini
614 8b312c1d Manuel Franceschini
class IP6Address(IPAddress):
615 8b312c1d Manuel Franceschini
  """IPv6 address class.
616 8b312c1d Manuel Franceschini

617 8b312c1d Manuel Franceschini
  """
618 8b312c1d Manuel Franceschini
  iplen = 128
619 8b312c1d Manuel Franceschini
  family = socket.AF_INET6
620 8b312c1d Manuel Franceschini
  loopback_cidr = "::1/128"
621 8b312c1d Manuel Franceschini
622 8b312c1d Manuel Franceschini
  def __init__(self, address):
623 8b312c1d Manuel Franceschini
    """Constructor for IPv6 address.
624 8b312c1d Manuel Franceschini

625 8b312c1d Manuel Franceschini
    @type address: str
626 8b312c1d Manuel Franceschini
    @param address: IP address
627 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
628 8b312c1d Manuel Franceschini

629 8b312c1d Manuel Franceschini
    """
630 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
631 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
632 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
633 8b312c1d Manuel Franceschini
    self.address = address
634 8b312c1d Manuel Franceschini
635 8b312c1d Manuel Franceschini
  @staticmethod
636 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
637 8b312c1d Manuel Franceschini
    """Get integer value of IPv6 address.
638 8b312c1d Manuel Franceschini

639 8b312c1d Manuel Franceschini
    @type address: str
640 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
641 8b312c1d Manuel Franceschini
    @rtype: int
642 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
643 8b312c1d Manuel Franceschini

644 8b312c1d Manuel Franceschini
    """
645 8b312c1d Manuel Franceschini
    doublecolons = address.count("::")
646 8b312c1d Manuel Franceschini
    assert not doublecolons > 1
647 8b312c1d Manuel Franceschini
    if doublecolons == 1:
648 8b312c1d Manuel Franceschini
      # We have a shorthand address, expand it
649 8b312c1d Manuel Franceschini
      parts = []
650 8b312c1d Manuel Franceschini
      twoparts = address.split("::")
651 d0c8c01d Iustin Pop
      sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
652 d0c8c01d Iustin Pop
      parts = twoparts[0].split(":")
653 17385bd2 Andrea Spadaccini
      parts.extend(["0"] * (8 - sep))
654 d0c8c01d Iustin Pop
      parts += twoparts[1].split(":")
655 8b312c1d Manuel Franceschini
    else:
656 8b312c1d Manuel Franceschini
      parts = address.split(":")
657 8b312c1d Manuel Franceschini
658 8b312c1d Manuel Franceschini
    address_int = 0
659 8b312c1d Manuel Franceschini
    for part in parts:
660 d0c8c01d Iustin Pop
      address_int = (address_int << 16) + int(part or "0", 16)
661 8b312c1d Manuel Franceschini
662 8b312c1d Manuel Franceschini
    return address_int
663 1a8337f2 Manuel Franceschini
664 7845b8c8 Manuel Franceschini
665 981732fb Manuel Franceschini
def FormatAddress(address, family=None):
666 1a8337f2 Manuel Franceschini
  """Format a socket address
667 1a8337f2 Manuel Franceschini

668 1a8337f2 Manuel Franceschini
  @type address: family specific (usually tuple)
669 1a8337f2 Manuel Franceschini
  @param address: address, as reported by this class
670 981732fb Manuel Franceschini
  @type family: integer
671 981732fb Manuel Franceschini
  @param family: socket family (one of socket.AF_*) or None
672 1a8337f2 Manuel Franceschini

673 1a8337f2 Manuel Franceschini
  """
674 981732fb Manuel Franceschini
  if family is None:
675 981732fb Manuel Franceschini
    try:
676 981732fb Manuel Franceschini
      family = IPAddress.GetAddressFamily(address[0])
677 981732fb Manuel Franceschini
    except errors.IPAddressError:
678 981732fb Manuel Franceschini
      raise errors.ParameterError(address)
679 981732fb Manuel Franceschini
680 1a8337f2 Manuel Franceschini
  if family == socket.AF_UNIX and len(address) == 3:
681 1a8337f2 Manuel Franceschini
    return "pid=%s, uid=%s, gid=%s" % address
682 1a8337f2 Manuel Franceschini
683 1a8337f2 Manuel Franceschini
  if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
684 1a8337f2 Manuel Franceschini
    host, port = address
685 1a8337f2 Manuel Franceschini
    if family == socket.AF_INET6:
686 1a8337f2 Manuel Franceschini
      res = "[%s]" % host
687 1a8337f2 Manuel Franceschini
    else:
688 1a8337f2 Manuel Franceschini
      res = host
689 1a8337f2 Manuel Franceschini
690 1a8337f2 Manuel Franceschini
    if port is not None:
691 1a8337f2 Manuel Franceschini
      res += ":%s" % port
692 1a8337f2 Manuel Franceschini
693 1a8337f2 Manuel Franceschini
    return res
694 1a8337f2 Manuel Franceschini
695 1a8337f2 Manuel Franceschini
  raise errors.ParameterError(family, address)