#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""
Manipulation d'IPv4

Copyright (C) Frédéric Pauget
Licence : GPLv2
"""

import re, netaddr
from config import NETs_regexp, prefix

# Pour accélérer QuadToDec...
__QuadToDecDone = {}

def QuadToDec(ip) :
    """
    Retourne la représentation décimale d'une ip
    ip est de la forme xxx.xxx.xxx.xxx
    """
    if ip in __QuadToDecDone:
        return __QuadToDecDone[ip]
    test = ip.split('.')
    if len(test)!=4 : raise ValueError('IP %r Invalide' % (ip,))
    ip_dec = 0
    for z in range(0,4) :
        n = int(test[z])
        if n<0 or n>255 : raise ValueError('IP Invalide')
        ip_dec += n * ( 256**(3-z) )

    __QuadToDecDone[ip] = ip_dec
    return ip_dec

# Pour accélérer DecToQuad
__DecToQuadDone = {}

def DecToQuad(ip_dec) :
    """
    Retourne la représentation habituelle d'une ip (xxx.xxx.xxx.xxx)
    ip_dec est l'IP en base 10
    """
    if ip_dec in __DecToQuadDone:
        return __DecToQuadDone[ip_dec]
    try :
        result = "%d.%d.%d.%d" % ( \
       ip_dec/(256**3) ,
      (ip_dec%(256**3)) / (256**2) ,
    ( (ip_dec%(256**3)) % (256**2) ) / 256 ,
    ( (ip_dec%(256**3)) % (256**2) ) % 256 )
        __DecToQuadDone[ip_dec] = result
        return result
    except :
        raise ValueError('IP Invalide')

__paramDone = {}

def param(net, raw=False) :
    """
    net est un résau fourni sous la forme xxx.xxx.xxx.xxx/yy
    si donnée valide retourne un dictionnaire :
        { 'network' : xxx.xxx.xxx.xxx ,
          'netmask' : yyy.yyy.yyy.yyy ,
          'broadcast' : zzz.zzz.zzz.zzz }
    sinon retourne {}

    Si raw = False, alors, on ne convertit pas les résultats sous forme pointée.
    Ils restent sous forme d'un entier.
    """
    if raw and net in __paramDone:
        return __paramDone[net]
    reseau = {}
    ip, mask = net.split('/')

    try :
        mask = int(mask)
        dec_ip = QuadToDec(ip)
        if dec_ip == -1 : raise
    except :
        return {}

    # Calcul du netmask
    non_dec_netmask = netmask(mask, dec=False)
    dec_netmask = netmask(mask)

    reseau = { 'netmask' : dec_netmask,
               'network' : dec_ip & dec_netmask,
               'broadcast' : dec_ip | non_dec_netmask }
    if not raw:
        for i in reseau.keys():
            reseau[i] = DecToQuad(reseau[i])
    else:
        __paramDone[net] = reseau
    return reseau

def AddrInNet(ip,net) :
    """
    ip est de la forme xxx.xxx.xxx.xxx
    net est de la forme xxx.xxx.xxx.xxx/yy
    net peut être une liste de chaînes ci-dessus
    Retourne True si l'ip est dans un des réseaux.
    Note : retourne False si l'IP est une adresse de réseau ou broadcast
    """
    if not ip or ip == '<automatique>':
        return False
    if type(net)==str : net = [ net ]
    r = False
    ip = QuadToDec(ip)
    for ne in net :
        n = param(ne, raw=True)
        if ip == n['broadcast'] or ip == n['network'] :
            return False
        r = r or n['netmask'] & ip == n['network']

    return r

def AddrInNets(ip,nets) :
    """ Vérifie si l'ip est au moins dans un des réseaux
    de la liste nets (voir AddrInNet) """
    for net in nets :
        if AddrInNet(ip,net) :
            return net
    return ''

def NetInNet(net1, net2) :
    """
    net1 est de la forme xxx.xxx.xxx.xxx/yy
    net2 est de la forme xxx.xxx.xxx.xxx/yy
    Retourne True si net1 est un sous-réseaux de net2
    """
    n1 = param(net1, raw=True)
    n2 = param(net2, raw=True)
    s1 = net1.split('/')[1]
    s2 = net1.split('/')[1]
    return s1<=s2 and (n1['network'] == n2['network'] or AddrInNet(DecToQuad(n1['network']), net2))

def NetInNets(net1, nets):
    """ Vérifie si le premier paramètre est un sous-réseau des réseaux de la liste du second paramètre"""
    for net in nets:
        if NetInNet(net1, net) :
            return net
    return ''

def is_crans(ip):
    """ Vérifie que l'ip est dans le réseau CRANS
    """
    # Pour titanic
    ip = netaddr.IPAddress(ip)
    if str(ip) in [ '138.231.136.14', '2a01:240:fe3d:4:a873:65ff:fe63:6f75']:
        return False
    if re.match(NETs_regexp['all'], str(ip)) or ip in netaddr.IPNetwork(prefix['subnet'][0]):
        return True
    return False

def netmask(mask, dec=True) :
    """ On génère le sous réseau /bits
    """
    non_dec_netmask = (1L<<(32-mask)) - 1
    dec_netmask = (1L<<32) - 1 - non_dec_netmask
    if dec :
        return dec_netmask
    else :
        return non_dec_netmask

def IpSubnet(ip, mask) :
    dec_mask = netmask(mask)
    dec_ip = QuadToDec(ip)
    start_ip = DecToQuad(dec_ip & dec_mask)
    subnet = start_ip + "/%s" % mask
    return subnet

def NetSubnets(net, subnet_mask) :
    """ On construit une liste des sous réseaux /subnet_mask
    compris dans le sous réseau /mask comprenant l'ip ip
    """
    subnets = []
    # On récupère une ip du réseau indiqué
    ip = net.split('/')[0]
    # On récupère la valeur du masque
    mask = int(net.split('/')[1])
    # On transforme les valeurs d'entrées en valeurs décimales
    # On définit la valeur du sous réseau initial
    dec_netmask = netmask(mask)
    dec_ip = QuadToDec(ip)
    dec_subnet_netmask = netmask(subnet_mask)
    # On calcule la première ip du sous réseau indiqué
    start_ip = dec_ip & dec_netmask
    seq_ip = start_ip
    # On fait une itération sur toutes les ip du sous réseau
    while (seq_ip & dec_netmask) == start_ip:
        # On récupère le sous réseau de la taille demandée
        subnet = DecToQuad(seq_ip & dec_subnet_netmask) + "/%s" % subnet_mask
        if not subnets.count(subnet) :
            subnets.append(subnet)
        seq_ip += 1
    return subnets

if __name__ == '__main__' :
    import sys
    print param(sys.argv[1])
