Statistics
| Branch: | Tag: | Revision:

root / lib / netutils.py @ 95e7e85e

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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