Statistics
| Branch: | Tag: | Revision:

root / lib / netutils.py @ 7142485a

History | View | Annotate | Download (18.2 kB)

1 a744b676 Manuel Franceschini
#
2 a744b676 Manuel Franceschini
#
3 a744b676 Manuel Franceschini
4 a744b676 Manuel Franceschini
# Copyright (C) 2010 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 a744b676 Manuel Franceschini
42 a744b676 Manuel Franceschini
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
43 a744b676 Manuel Franceschini
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
44 a744b676 Manuel Franceschini
#
45 a744b676 Manuel Franceschini
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
46 a744b676 Manuel Franceschini
# pid_t to "int".
47 a744b676 Manuel Franceschini
#
48 a744b676 Manuel Franceschini
# IEEE Std 1003.1-2008:
49 a744b676 Manuel Franceschini
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
50 a744b676 Manuel Franceschini
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
51 a744b676 Manuel Franceschini
_STRUCT_UCRED = "iII"
52 a744b676 Manuel Franceschini
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
53 a744b676 Manuel Franceschini
54 37531236 Andrea Spadaccini
# Regexes used to find IP addresses in the output of ip.
55 37531236 Andrea Spadaccini
_IP_RE_TEXT = r"[.:a-z0-9]+"      # separate for testing purposes
56 37531236 Andrea Spadaccini
_IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
57 37531236 Andrea Spadaccini
                           re.IGNORECASE)
58 37531236 Andrea Spadaccini
59 37531236 Andrea Spadaccini
# Dict used to convert from a string representing an IP family to an IP
60 37531236 Andrea Spadaccini
# version
61 e687ec01 Michael Hanselmann
_NAME_TO_IP_VER = {
62 37531236 Andrea Spadaccini
  "inet": constants.IP4_VERSION,
63 37531236 Andrea Spadaccini
  "inet6": constants.IP6_VERSION,
64 37531236 Andrea Spadaccini
  }
65 37531236 Andrea Spadaccini
66 37531236 Andrea Spadaccini
67 37531236 Andrea Spadaccini
def _GetIpAddressesFromIpOutput(ip_output):
68 37531236 Andrea Spadaccini
  """Parses the output of the ip command and retrieves the IP addresses and
69 37531236 Andrea Spadaccini
  version.
70 37531236 Andrea Spadaccini

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

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

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

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

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

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

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

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

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

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

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

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

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

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

176 f3044516 Manuel Franceschini
    """
177 f3044516 Manuel Franceschini
    return cls.GetFqdn()
178 f3044516 Manuel Franceschini
179 a744b676 Manuel Franceschini
  @staticmethod
180 f3044516 Manuel Franceschini
  def GetFqdn(hostname=None):
181 f3044516 Manuel Franceschini
    """Return fqdn.
182 f3044516 Manuel Franceschini

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

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

190 a744b676 Manuel Franceschini
    """
191 f3044516 Manuel Franceschini
    if hostname is None:
192 f3044516 Manuel Franceschini
      return socket.getfqdn()
193 f3044516 Manuel Franceschini
    else:
194 f3044516 Manuel Franceschini
      return socket.getfqdn(hostname)
195 a744b676 Manuel Franceschini
196 a744b676 Manuel Franceschini
  @staticmethod
197 b705c7a6 Manuel Franceschini
  def GetIP(hostname, family=None):
198 b705c7a6 Manuel Franceschini
    """Return IP address of given hostname.
199 b705c7a6 Manuel Franceschini

200 b705c7a6 Manuel Franceschini
    Supports both IPv4 and IPv6.
201 a744b676 Manuel Franceschini

202 a744b676 Manuel Franceschini
    @type hostname: str
203 a744b676 Manuel Franceschini
    @param hostname: hostname to look up
204 b705c7a6 Manuel Franceschini
    @type family: int
205 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
206 b705c7a6 Manuel Franceschini
    @rtype: str
207 b705c7a6 Manuel Franceschini
    @return: IP address
208 a744b676 Manuel Franceschini
    @raise errors.ResolverError: in case of errors in resolving
209 a744b676 Manuel Franceschini

210 a744b676 Manuel Franceschini
    """
211 a744b676 Manuel Franceschini
    try:
212 b705c7a6 Manuel Franceschini
      if family in (socket.AF_INET, socket.AF_INET6):
213 b705c7a6 Manuel Franceschini
        result = socket.getaddrinfo(hostname, None, family)
214 b705c7a6 Manuel Franceschini
      else:
215 b43dcc5a Manuel Franceschini
        result = socket.getaddrinfo(hostname, None)
216 a744b676 Manuel Franceschini
    except (socket.gaierror, socket.herror, socket.error), err:
217 a744b676 Manuel Franceschini
      # hostname not found in DNS, or other socket exception in the
218 a744b676 Manuel Franceschini
      # (code, description format)
219 a744b676 Manuel Franceschini
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
220 a744b676 Manuel Franceschini
221 b705c7a6 Manuel Franceschini
    # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
222 b705c7a6 Manuel Franceschini
    # canonname, sockaddr). We return the first tuple's first address in
223 b705c7a6 Manuel Franceschini
    # sockaddr
224 e7b3ad26 Manuel Franceschini
    try:
225 e7b3ad26 Manuel Franceschini
      return result[0][4][0]
226 e7b3ad26 Manuel Franceschini
    except IndexError, err:
227 e7b3ad26 Manuel Franceschini
      raise errors.ResolverError("Unknown error in getaddrinfo(): %s" % err)
228 a744b676 Manuel Franceschini
229 e7b3ad26 Manuel Franceschini
  @classmethod
230 e7b3ad26 Manuel Franceschini
  def GetNormalizedName(cls, hostname):
231 a744b676 Manuel Franceschini
    """Validate and normalize the given hostname.
232 a744b676 Manuel Franceschini

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

237 a744b676 Manuel Franceschini
    """
238 a744b676 Manuel Franceschini
    hostname = hostname.lower()
239 e7b3ad26 Manuel Franceschini
    if (not cls._VALID_NAME_RE.match(hostname) or
240 a744b676 Manuel Franceschini
        # double-dots, meaning empty label
241 a744b676 Manuel Franceschini
        ".." in hostname or
242 a744b676 Manuel Franceschini
        # empty initial label
243 a744b676 Manuel Franceschini
        hostname.startswith(".")):
244 a744b676 Manuel Franceschini
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
245 a744b676 Manuel Franceschini
                                 errors.ECODE_INVAL)
246 a744b676 Manuel Franceschini
    if hostname.endswith("."):
247 a744b676 Manuel Franceschini
      hostname = hostname.rstrip(".")
248 a744b676 Manuel Franceschini
    return hostname
249 a744b676 Manuel Franceschini
250 a744b676 Manuel Franceschini
251 a744b676 Manuel Franceschini
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
252 a744b676 Manuel Franceschini
  """Simple ping implementation using TCP connect(2).
253 a744b676 Manuel Franceschini

254 a744b676 Manuel Franceschini
  Check if the given IP is reachable by doing attempting a TCP connect
255 a744b676 Manuel Franceschini
  to it.
256 a744b676 Manuel Franceschini

257 a744b676 Manuel Franceschini
  @type target: str
258 77b02a99 Renรฉ Nussbaumer
  @param target: the IP to ping
259 a744b676 Manuel Franceschini
  @type port: int
260 a744b676 Manuel Franceschini
  @param port: the port to connect to
261 a744b676 Manuel Franceschini
  @type timeout: int
262 a744b676 Manuel Franceschini
  @param timeout: the timeout on the connection attempt
263 a744b676 Manuel Franceschini
  @type live_port_needed: boolean
264 a744b676 Manuel Franceschini
  @param live_port_needed: whether a closed port will cause the
265 a744b676 Manuel Franceschini
      function to return failure, as if there was a timeout
266 a744b676 Manuel Franceschini
  @type source: str or None
267 a744b676 Manuel Franceschini
  @param source: if specified, will cause the connect to be made
268 a744b676 Manuel Franceschini
      from this specific source address; failures to bind other
269 a744b676 Manuel Franceschini
      than C{EADDRNOTAVAIL} will be ignored
270 a744b676 Manuel Franceschini

271 a744b676 Manuel Franceschini
  """
272 a744b676 Manuel Franceschini
  try:
273 8b312c1d Manuel Franceschini
    family = IPAddress.GetAddressFamily(target)
274 a744b676 Manuel Franceschini
  except errors.GenericError:
275 a744b676 Manuel Franceschini
    return False
276 a744b676 Manuel Franceschini
277 a744b676 Manuel Franceschini
  sock = socket.socket(family, socket.SOCK_STREAM)
278 a744b676 Manuel Franceschini
  success = False
279 a744b676 Manuel Franceschini
280 a744b676 Manuel Franceschini
  if source is not None:
281 a744b676 Manuel Franceschini
    try:
282 a744b676 Manuel Franceschini
      sock.bind((source, 0))
283 a744b676 Manuel Franceschini
    except socket.error, (errcode, _):
284 a744b676 Manuel Franceschini
      if errcode == errno.EADDRNOTAVAIL:
285 a744b676 Manuel Franceschini
        success = False
286 a744b676 Manuel Franceschini
287 a744b676 Manuel Franceschini
  sock.settimeout(timeout)
288 a744b676 Manuel Franceschini
289 a744b676 Manuel Franceschini
  try:
290 a744b676 Manuel Franceschini
    sock.connect((target, port))
291 a744b676 Manuel Franceschini
    sock.close()
292 a744b676 Manuel Franceschini
    success = True
293 a744b676 Manuel Franceschini
  except socket.timeout:
294 a744b676 Manuel Franceschini
    success = False
295 a744b676 Manuel Franceschini
  except socket.error, (errcode, _):
296 a744b676 Manuel Franceschini
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
297 a744b676 Manuel Franceschini
298 a744b676 Manuel Franceschini
  return success
299 a744b676 Manuel Franceschini
300 a744b676 Manuel Franceschini
301 a744b676 Manuel Franceschini
def GetDaemonPort(daemon_name):
302 a744b676 Manuel Franceschini
  """Get the daemon port for this cluster.
303 a744b676 Manuel Franceschini

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

308 a744b676 Manuel Franceschini
  @type daemon_name: string
309 a744b676 Manuel Franceschini
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
310 a744b676 Manuel Franceschini
  @rtype: int
311 a744b676 Manuel Franceschini

312 a744b676 Manuel Franceschini
  """
313 a744b676 Manuel Franceschini
  if daemon_name not in constants.DAEMONS_PORTS:
314 a744b676 Manuel Franceschini
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
315 a744b676 Manuel Franceschini
316 a744b676 Manuel Franceschini
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
317 a744b676 Manuel Franceschini
  try:
318 a744b676 Manuel Franceschini
    port = socket.getservbyname(daemon_name, proto)
319 a744b676 Manuel Franceschini
  except socket.error:
320 a744b676 Manuel Franceschini
    port = default_port
321 a744b676 Manuel Franceschini
322 a744b676 Manuel Franceschini
  return port
323 8b312c1d Manuel Franceschini
324 8b312c1d Manuel Franceschini
325 8b312c1d Manuel Franceschini
class IPAddress(object):
326 8b312c1d Manuel Franceschini
  """Class that represents an IP address.
327 8b312c1d Manuel Franceschini

328 8b312c1d Manuel Franceschini
  """
329 8b312c1d Manuel Franceschini
  iplen = 0
330 8b312c1d Manuel Franceschini
  family = None
331 8b312c1d Manuel Franceschini
  loopback_cidr = None
332 8b312c1d Manuel Franceschini
333 8b312c1d Manuel Franceschini
  @staticmethod
334 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
335 8b312c1d Manuel Franceschini
    """Abstract method to please pylint.
336 8b312c1d Manuel Franceschini

337 8b312c1d Manuel Franceschini
    """
338 8b312c1d Manuel Franceschini
    raise NotImplementedError
339 8b312c1d Manuel Franceschini
340 8b312c1d Manuel Franceschini
  @classmethod
341 8b312c1d Manuel Franceschini
  def IsValid(cls, address):
342 8b312c1d Manuel Franceschini
    """Validate a IP address.
343 8b312c1d Manuel Franceschini

344 8b312c1d Manuel Franceschini
    @type address: str
345 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
346 8b312c1d Manuel Franceschini
    @rtype: bool
347 8b312c1d Manuel Franceschini
    @return: True if valid, False otherwise
348 8b312c1d Manuel Franceschini

349 8b312c1d Manuel Franceschini
    """
350 8b312c1d Manuel Franceschini
    if cls.family is None:
351 8b312c1d Manuel Franceschini
      try:
352 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
353 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
354 8b312c1d Manuel Franceschini
        return False
355 8b312c1d Manuel Franceschini
    else:
356 8b312c1d Manuel Franceschini
      family = cls.family
357 8b312c1d Manuel Franceschini
358 8b312c1d Manuel Franceschini
    try:
359 8b312c1d Manuel Franceschini
      socket.inet_pton(family, address)
360 8b312c1d Manuel Franceschini
      return True
361 8b312c1d Manuel Franceschini
    except socket.error:
362 8b312c1d Manuel Franceschini
      return False
363 8b312c1d Manuel Franceschini
364 8b312c1d Manuel Franceschini
  @classmethod
365 7df2c4f0 Andrea Spadaccini
  def ValidateNetmask(cls, netmask):
366 7df2c4f0 Andrea Spadaccini
    """Validate a netmask suffix in CIDR notation.
367 7df2c4f0 Andrea Spadaccini

368 7df2c4f0 Andrea Spadaccini
    @type netmask: int
369 7df2c4f0 Andrea Spadaccini
    @param netmask: netmask suffix to validate
370 7df2c4f0 Andrea Spadaccini
    @rtype: bool
371 7df2c4f0 Andrea Spadaccini
    @return: True if valid, False otherwise
372 7df2c4f0 Andrea Spadaccini

373 7df2c4f0 Andrea Spadaccini
    """
374 7df2c4f0 Andrea Spadaccini
    assert (isinstance(netmask, (int, long)))
375 7df2c4f0 Andrea Spadaccini
376 7df2c4f0 Andrea Spadaccini
    return 0 < netmask <= cls.iplen
377 7df2c4f0 Andrea Spadaccini
378 7df2c4f0 Andrea Spadaccini
  @classmethod
379 8b312c1d Manuel Franceschini
  def Own(cls, address):
380 8b312c1d Manuel Franceschini
    """Check if the current host has the the given IP address.
381 8b312c1d Manuel Franceschini

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

385 8b312c1d Manuel Franceschini
    @type address: str
386 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
387 8b312c1d Manuel Franceschini
    @rtype: bool
388 8b312c1d Manuel Franceschini
    @return: True if we own the address, False otherwise
389 8b312c1d Manuel Franceschini

390 8b312c1d Manuel Franceschini
    """
391 8b312c1d Manuel Franceschini
    if cls.family is None:
392 8b312c1d Manuel Franceschini
      try:
393 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
394 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
395 8b312c1d Manuel Franceschini
        return False
396 8b312c1d Manuel Franceschini
    else:
397 8b312c1d Manuel Franceschini
      family = cls.family
398 8b312c1d Manuel Franceschini
399 8b312c1d Manuel Franceschini
    s = socket.socket(family, socket.SOCK_DGRAM)
400 8b312c1d Manuel Franceschini
    success = False
401 8b312c1d Manuel Franceschini
    try:
402 8b312c1d Manuel Franceschini
      try:
403 8b312c1d Manuel Franceschini
        s.bind((address, 0))
404 8b312c1d Manuel Franceschini
        success = True
405 8b312c1d Manuel Franceschini
      except socket.error:
406 8b312c1d Manuel Franceschini
        success = False
407 8b312c1d Manuel Franceschini
    finally:
408 8b312c1d Manuel Franceschini
      s.close()
409 8b312c1d Manuel Franceschini
    return success
410 8b312c1d Manuel Franceschini
411 8b312c1d Manuel Franceschini
  @classmethod
412 8b312c1d Manuel Franceschini
  def InNetwork(cls, cidr, address):
413 8b312c1d Manuel Franceschini
    """Determine whether an address is within a network.
414 8b312c1d Manuel Franceschini

415 8b312c1d Manuel Franceschini
    @type cidr: string
416 8b312c1d Manuel Franceschini
    @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
417 8b312c1d Manuel Franceschini
    @type address: str
418 8b312c1d Manuel Franceschini
    @param address: IP address
419 8b312c1d Manuel Franceschini
    @rtype: bool
420 8b312c1d Manuel Franceschini
    @return: True if address is in cidr, False otherwise
421 8b312c1d Manuel Franceschini

422 8b312c1d Manuel Franceschini
    """
423 8b312c1d Manuel Franceschini
    address_int = cls._GetIPIntFromString(address)
424 8b312c1d Manuel Franceschini
    subnet = cidr.split("/")
425 8b312c1d Manuel Franceschini
    assert len(subnet) == 2
426 8b312c1d Manuel Franceschini
    try:
427 8b312c1d Manuel Franceschini
      prefix = int(subnet[1])
428 8b312c1d Manuel Franceschini
    except ValueError:
429 8b312c1d Manuel Franceschini
      return False
430 8b312c1d Manuel Franceschini
431 8b312c1d Manuel Franceschini
    assert 0 <= prefix <= cls.iplen
432 8b312c1d Manuel Franceschini
    target_int = cls._GetIPIntFromString(subnet[0])
433 8b312c1d Manuel Franceschini
    # Convert prefix netmask to integer value of netmask
434 e687ec01 Michael Hanselmann
    netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
435 8b312c1d Manuel Franceschini
    # Calculate hostmask
436 e687ec01 Michael Hanselmann
    hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
437 8b312c1d Manuel Franceschini
    # Calculate network address by and'ing netmask
438 8b312c1d Manuel Franceschini
    network_int = target_int & netmask_int
439 8b312c1d Manuel Franceschini
    # Calculate broadcast address by or'ing hostmask
440 8b312c1d Manuel Franceschini
    broadcast_int = target_int | hostmask_int
441 8b312c1d Manuel Franceschini
442 8b312c1d Manuel Franceschini
    return network_int <= address_int <= broadcast_int
443 8b312c1d Manuel Franceschini
444 8b312c1d Manuel Franceschini
  @staticmethod
445 8b312c1d Manuel Franceschini
  def GetAddressFamily(address):
446 8b312c1d Manuel Franceschini
    """Get the address family of the given address.
447 8b312c1d Manuel Franceschini

448 8b312c1d Manuel Franceschini
    @type address: str
449 8b312c1d Manuel Franceschini
    @param address: ip address whose family will be returned
450 8b312c1d Manuel Franceschini
    @rtype: int
451 37531236 Andrea Spadaccini
    @return: C{socket.AF_INET} or C{socket.AF_INET6}
452 8b312c1d Manuel Franceschini
    @raise errors.GenericError: for invalid addresses
453 8b312c1d Manuel Franceschini

454 8b312c1d Manuel Franceschini
    """
455 8b312c1d Manuel Franceschini
    try:
456 8b312c1d Manuel Franceschini
      return IP4Address(address).family
457 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
458 8b312c1d Manuel Franceschini
      pass
459 8b312c1d Manuel Franceschini
460 8b312c1d Manuel Franceschini
    try:
461 8b312c1d Manuel Franceschini
      return IP6Address(address).family
462 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
463 8b312c1d Manuel Franceschini
      pass
464 8b312c1d Manuel Franceschini
465 8b312c1d Manuel Franceschini
    raise errors.IPAddressError("Invalid address '%s'" % address)
466 8b312c1d Manuel Franceschini
467 37531236 Andrea Spadaccini
  @staticmethod
468 37531236 Andrea Spadaccini
  def GetVersionFromAddressFamily(family):
469 37531236 Andrea Spadaccini
    """Convert an IP address family to the corresponding IP version.
470 37531236 Andrea Spadaccini

471 37531236 Andrea Spadaccini
    @type family: int
472 37531236 Andrea Spadaccini
    @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
473 37531236 Andrea Spadaccini
    @return: an int containing the IP version, one of L{constants.IP4_VERSION}
474 37531236 Andrea Spadaccini
             or L{constants.IP6_VERSION}
475 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown families
476 37531236 Andrea Spadaccini

477 37531236 Andrea Spadaccini
    """
478 37531236 Andrea Spadaccini
    if family == socket.AF_INET:
479 37531236 Andrea Spadaccini
      return constants.IP4_VERSION
480 37531236 Andrea Spadaccini
    elif family == socket.AF_INET6:
481 37531236 Andrea Spadaccini
      return constants.IP6_VERSION
482 37531236 Andrea Spadaccini
483 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP address family" % family)
484 37531236 Andrea Spadaccini
485 37531236 Andrea Spadaccini
  @staticmethod
486 37531236 Andrea Spadaccini
  def GetAddressFamilyFromVersion(version):
487 37531236 Andrea Spadaccini
    """Convert an IP version to the corresponding IP address family.
488 37531236 Andrea Spadaccini

489 37531236 Andrea Spadaccini
    @type version: int
490 37531236 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
491 37531236 Andrea Spadaccini
                    L{constants.IP6_VERSION}
492 37531236 Andrea Spadaccini
    @return: an int containing the IP address family, one of C{socket.AF_INET}
493 37531236 Andrea Spadaccini
             or C{socket.AF_INET6}
494 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown IP versions
495 37531236 Andrea Spadaccini

496 37531236 Andrea Spadaccini
    """
497 37531236 Andrea Spadaccini
    if version == constants.IP4_VERSION:
498 37531236 Andrea Spadaccini
      return socket.AF_INET
499 37531236 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
500 37531236 Andrea Spadaccini
      return socket.AF_INET6
501 37531236 Andrea Spadaccini
502 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
503 37531236 Andrea Spadaccini
504 7df2c4f0 Andrea Spadaccini
  @staticmethod
505 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpVersion(version):
506 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP version.
507 7df2c4f0 Andrea Spadaccini

508 7df2c4f0 Andrea Spadaccini
    @type version: int
509 7df2c4f0 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
510 7df2c4f0 Andrea Spadaccini
                    L{constants.IP6_VERSION}
511 7df2c4f0 Andrea Spadaccini
    @return: a subclass of L{netutils.IPAddress}
512 7df2c4f0 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknowo IP versions
513 7df2c4f0 Andrea Spadaccini

514 7df2c4f0 Andrea Spadaccini
    """
515 7df2c4f0 Andrea Spadaccini
    if version == constants.IP4_VERSION:
516 7df2c4f0 Andrea Spadaccini
      return IP4Address
517 7df2c4f0 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
518 7df2c4f0 Andrea Spadaccini
      return IP6Address
519 7df2c4f0 Andrea Spadaccini
520 7df2c4f0 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
521 7df2c4f0 Andrea Spadaccini
522 7df2c4f0 Andrea Spadaccini
  @staticmethod
523 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpFamily(family):
524 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP family.
525 7df2c4f0 Andrea Spadaccini

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

530 7df2c4f0 Andrea Spadaccini
    """
531 7df2c4f0 Andrea Spadaccini
    return IPAddress.GetClassFromIpVersion(
532 7df2c4f0 Andrea Spadaccini
              IPAddress.GetVersionFromAddressFamily(family))
533 7df2c4f0 Andrea Spadaccini
534 8b312c1d Manuel Franceschini
  @classmethod
535 8b312c1d Manuel Franceschini
  def IsLoopback(cls, address):
536 8b312c1d Manuel Franceschini
    """Determine whether it is a loopback address.
537 8b312c1d Manuel Franceschini

538 8b312c1d Manuel Franceschini
    @type address: str
539 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
540 8b312c1d Manuel Franceschini
    @rtype: bool
541 8b312c1d Manuel Franceschini
    @return: True if loopback, False otherwise
542 8b312c1d Manuel Franceschini

543 8b312c1d Manuel Franceschini
    """
544 8b312c1d Manuel Franceschini
    try:
545 8b312c1d Manuel Franceschini
      return cls.InNetwork(cls.loopback_cidr, address)
546 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
547 8b312c1d Manuel Franceschini
      return False
548 8b312c1d Manuel Franceschini
549 8b312c1d Manuel Franceschini
550 8b312c1d Manuel Franceschini
class IP4Address(IPAddress):
551 8b312c1d Manuel Franceschini
  """IPv4 address class.
552 8b312c1d Manuel Franceschini

553 8b312c1d Manuel Franceschini
  """
554 8b312c1d Manuel Franceschini
  iplen = 32
555 8b312c1d Manuel Franceschini
  family = socket.AF_INET
556 8b312c1d Manuel Franceschini
  loopback_cidr = "127.0.0.0/8"
557 8b312c1d Manuel Franceschini
558 8b312c1d Manuel Franceschini
  def __init__(self, address):
559 8b312c1d Manuel Franceschini
    """Constructor for IPv4 address.
560 8b312c1d Manuel Franceschini

561 8b312c1d Manuel Franceschini
    @type address: str
562 8b312c1d Manuel Franceschini
    @param address: IP address
563 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
564 8b312c1d Manuel Franceschini

565 8b312c1d Manuel Franceschini
    """
566 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
567 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
568 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv4 Address %s invalid" % address)
569 8b312c1d Manuel Franceschini
570 8b312c1d Manuel Franceschini
    self.address = address
571 8b312c1d Manuel Franceschini
572 8b312c1d Manuel Franceschini
  @staticmethod
573 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
574 8b312c1d Manuel Franceschini
    """Get integer value of IPv4 address.
575 8b312c1d Manuel Franceschini

576 8b312c1d Manuel Franceschini
    @type address: str
577 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
578 8b312c1d Manuel Franceschini
    @rtype: int
579 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
580 8b312c1d Manuel Franceschini

581 8b312c1d Manuel Franceschini
    """
582 8b312c1d Manuel Franceschini
    address_int = 0
583 8b312c1d Manuel Franceschini
    parts = address.split(".")
584 8b312c1d Manuel Franceschini
    assert len(parts) == 4
585 8b312c1d Manuel Franceschini
    for part in parts:
586 8b312c1d Manuel Franceschini
      address_int = (address_int << 8) | int(part)
587 8b312c1d Manuel Franceschini
588 8b312c1d Manuel Franceschini
    return address_int
589 8b312c1d Manuel Franceschini
590 8b312c1d Manuel Franceschini
591 8b312c1d Manuel Franceschini
class IP6Address(IPAddress):
592 8b312c1d Manuel Franceschini
  """IPv6 address class.
593 8b312c1d Manuel Franceschini

594 8b312c1d Manuel Franceschini
  """
595 8b312c1d Manuel Franceschini
  iplen = 128
596 8b312c1d Manuel Franceschini
  family = socket.AF_INET6
597 8b312c1d Manuel Franceschini
  loopback_cidr = "::1/128"
598 8b312c1d Manuel Franceschini
599 8b312c1d Manuel Franceschini
  def __init__(self, address):
600 8b312c1d Manuel Franceschini
    """Constructor for IPv6 address.
601 8b312c1d Manuel Franceschini

602 8b312c1d Manuel Franceschini
    @type address: str
603 8b312c1d Manuel Franceschini
    @param address: IP address
604 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
605 8b312c1d Manuel Franceschini

606 8b312c1d Manuel Franceschini
    """
607 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
608 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
609 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
610 8b312c1d Manuel Franceschini
    self.address = address
611 8b312c1d Manuel Franceschini
612 8b312c1d Manuel Franceschini
  @staticmethod
613 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
614 8b312c1d Manuel Franceschini
    """Get integer value of IPv6 address.
615 8b312c1d Manuel Franceschini

616 8b312c1d Manuel Franceschini
    @type address: str
617 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
618 8b312c1d Manuel Franceschini
    @rtype: int
619 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
620 8b312c1d Manuel Franceschini

621 8b312c1d Manuel Franceschini
    """
622 8b312c1d Manuel Franceschini
    doublecolons = address.count("::")
623 8b312c1d Manuel Franceschini
    assert not doublecolons > 1
624 8b312c1d Manuel Franceschini
    if doublecolons == 1:
625 8b312c1d Manuel Franceschini
      # We have a shorthand address, expand it
626 8b312c1d Manuel Franceschini
      parts = []
627 8b312c1d Manuel Franceschini
      twoparts = address.split("::")
628 d0c8c01d Iustin Pop
      sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
629 d0c8c01d Iustin Pop
      parts = twoparts[0].split(":")
630 17385bd2 Andrea Spadaccini
      parts.extend(["0"] * (8 - sep))
631 d0c8c01d Iustin Pop
      parts += twoparts[1].split(":")
632 8b312c1d Manuel Franceschini
    else:
633 8b312c1d Manuel Franceschini
      parts = address.split(":")
634 8b312c1d Manuel Franceschini
635 8b312c1d Manuel Franceschini
    address_int = 0
636 8b312c1d Manuel Franceschini
    for part in parts:
637 d0c8c01d Iustin Pop
      address_int = (address_int << 16) + int(part or "0", 16)
638 8b312c1d Manuel Franceschini
639 8b312c1d Manuel Franceschini
    return address_int
640 1a8337f2 Manuel Franceschini
641 7845b8c8 Manuel Franceschini
642 981732fb Manuel Franceschini
def FormatAddress(address, family=None):
643 1a8337f2 Manuel Franceschini
  """Format a socket address
644 1a8337f2 Manuel Franceschini

645 1a8337f2 Manuel Franceschini
  @type address: family specific (usually tuple)
646 1a8337f2 Manuel Franceschini
  @param address: address, as reported by this class
647 981732fb Manuel Franceschini
  @type family: integer
648 981732fb Manuel Franceschini
  @param family: socket family (one of socket.AF_*) or None
649 1a8337f2 Manuel Franceschini

650 1a8337f2 Manuel Franceschini
  """
651 981732fb Manuel Franceschini
  if family is None:
652 981732fb Manuel Franceschini
    try:
653 981732fb Manuel Franceschini
      family = IPAddress.GetAddressFamily(address[0])
654 981732fb Manuel Franceschini
    except errors.IPAddressError:
655 981732fb Manuel Franceschini
      raise errors.ParameterError(address)
656 981732fb Manuel Franceschini
657 1a8337f2 Manuel Franceschini
  if family == socket.AF_UNIX and len(address) == 3:
658 1a8337f2 Manuel Franceschini
    return "pid=%s, uid=%s, gid=%s" % address
659 1a8337f2 Manuel Franceschini
660 1a8337f2 Manuel Franceschini
  if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
661 1a8337f2 Manuel Franceschini
    host, port = address
662 1a8337f2 Manuel Franceschini
    if family == socket.AF_INET6:
663 1a8337f2 Manuel Franceschini
      res = "[%s]" % host
664 1a8337f2 Manuel Franceschini
    else:
665 1a8337f2 Manuel Franceschini
      res = host
666 1a8337f2 Manuel Franceschini
667 1a8337f2 Manuel Franceschini
    if port is not None:
668 1a8337f2 Manuel Franceschini
      res += ":%s" % port
669 1a8337f2 Manuel Franceschini
670 1a8337f2 Manuel Franceschini
    return res
671 1a8337f2 Manuel Franceschini
672 1a8337f2 Manuel Franceschini
  raise errors.ParameterError(family, address)