# Copyright 2009 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Utility functions related to IP Addresses & Hostnames."""

# TODO(tstromberg): Investigate replacement with ipaddr library

__author__ = 'tstromberg@google.com (Thomas Stromberg)'

import math
import re
import util
import os.path
import socket
import sys
import traceback
import zlib

# TODO(tstromberg): Find a way to combine the following two regexps.

# Used to decide whether or not to benchmark a name
INTERNAL_RE = re.compile('^0|\.pro[md]z*\.|\.corp|\.bor|\.hot$|internal|dmz|\._[ut][dc]p\.|intra|\.\w$|\.\w{5,}$', re.IGNORECASE)

# Used to decide if a hostname should be censored later.
PRIVATE_RE = re.compile('^\w+dc\.|^\w+ds\.|^\w+sv\.|^\w+nt\.|\.corp|internal|intranet|\.local', re.IGNORECASE)

# ^.*[\w-]+\.[\w-]+\.[\w-]+\.[a-zA-Z]+\.$|^[\w-]+\.[\w-]{3,}\.[a-zA-Z]+\.$
FQDN_RE = re.compile('^.*\..*\..*\..*\.$|^.*\.[\w-]*\.\w{3,4}\.$|^[\w-]+\.[\w-]{4,}\.\w+\.')

IP_RE = re.compile('^[0-9.]+$')


def ExtractIPsFromString(ip_string):
  """Return a tuple of ip addressed held in a string."""

  ips = []
  # IPV6 If this regexp is too loose, see Regexp-IPv6 in CPAN for inspiration.
  ips.extend(re.findall('[\dabcdef:]+:[\dabcdef:]+', ip_string, re.IGNORECASE))
  ips.extend(re.findall('\d+\.\d+\.\d+\.+\d+', ip_string))
  return ips

def ExtractIPTuplesFromString(ip_string):
  ip_tuples = []
  for ip in ExtractIPsFromString(ip_string):
      ip_tuples.append((ip,ip))
  return ip_tuples

def IsPrivateHostname(hostname):
  """Basic matching to determine if the hostname is likely to be 'internal'."""
  if PRIVATE_RE.search(hostname):
    return True
  else:
    return False

def IsLoopbackIP(ip):
  """Boolean check to see if an IP is private or not.

  Returns: Number of bits that should be preserved.
  """
  if ip.startswith('127.') or ip == '::1':
    return True
  else:
    return False

def IsPrivateIP(ip):
  """Boolean check to see if an IP is private or not.

  Returns: Number of bits that should be preserved.
  """
  if re.match('^10\.', ip):
    return 1
  elif re.match('^192\.168', ip):
    return 2
  elif re.match('^172\.(1[6-9]|2[0-9]|3[0-1])\.', ip):
    return 1
  else:
    return None

def MaskIPBits(ip, use_bits):
  """Mask an IP, but still keep a meaningful checksum."""
  ip_parts = ip.split('.')
  checksum = zlib.crc32(''.join(ip_parts[use_bits:]))
  masked_ip = '.'.join(ip_parts[0:use_bits])
  return masked_ip + ".x-" + str(checksum)[-4:]

def MaskPrivateHost(ip, hostname, name):
  """Mask unnamed private IP's."""

  # If we have a name not listed as SYS-x.x.x.x, then we're clear.
  if name and ip not in name:
    return (ip, hostname, name)

  use_bits = IsPrivateIP(ip)
  if use_bits:
    ip = MaskIPBits(ip, use_bits)
    hostname = 'internal.ip'
  elif IsPrivateHostname(hostname):
    ip = MaskIPBits(ip, 2)
    hostname = 'internal.name'

  if 'SYS-' in name:
    name = "SYS-%s" % ip
  else:
    name = ''
  return (ip, hostname, name)

Split off IP/hostname munging to addr_util