########################################################################
#
#  Copyright (C) 2009, CZ.NIC, z.s.p.o.
# 
#  This file is part of dnstester.
#
#  dnstester is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  dnstester is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
########################################################################

from ctypes import *

MAX_NUM_ENTRIES = 200
MAXLEN_PHYSADDR = 8

def bytes2ip(ip):
    r = []
    for i in range(4):
        t = (ip >> (8 * i)) & 0xff
        r.append(str(t))
    return '.'.join(r)


class MibIpNetRow(Structure):
    _fields_ = [
            ('index', c_uint),
            ('phys_addr_len', c_uint),
            ('phys_addr', c_byte * MAXLEN_PHYSADDR),
            ('addr', c_uint),
            ('type', c_uint),
        ]

    def byte2hex(self, b):

        digits = (
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f',
            )
        b0 = b & 0x0f
        b1 = (b & 0xf0) >> 4
        return digits[b1] + digits[b0]

    def printable_mac(self):
        a = []
        for i in range(self.phys_addr_len):
            a.append(self.byte2hex(self.phys_addr[i]))
        return ':'.join(a)

    def printable_ip(self):
        return bytes2ip(self.addr)

    def __str__(self):
        return 'iface=%d, phyaddr=%s, type=%d, ip=%s' % (self.index, self.printable_mac(), self.type, self.printable_ip())

class MibIpNetTable(Structure):
    _fields_ = [('num_entries', c_uint), ('table', MibIpNetRow * MAX_NUM_ENTRIES)]


class MibIpForwardRow(Structure):
    _fields_ = [
            ('forward_dest', c_uint),
            ('forward_mask', c_uint),
            ('forward_policy', c_uint),
            ('forward_nexthop', c_uint),
            ('forward_ifindex', c_uint),
            ('forward_type', c_uint),
            ('forward_proto', c_uint),
            ('forward_age', c_uint),
            ('forward_nexthopas', c_uint),
            ('forward_metric1', c_uint),
            ('forward_metric2', c_uint),
            ('forward_metric3', c_uint),
            ('forward_metric4', c_uint),
            ('forward_metric5', c_uint),
        ]

    def printable_nexthop(self):
        return bytes2ip(self.forward_nexthop)


class MibIpForwardTable(Structure):
    _fields_ = [('num_entries', c_uint), ('table', MibIpForwardRow * MAX_NUM_ENTRIES)]


def get_arp(ip):
    
    get_ip_net_table = windll.iphlpapi.GetIpNetTable

    table = MibIpNetTable()
    size = c_uint(sizeof(table))

    r = get_ip_net_table(byref(table), byref(size), 0)

    if r != 0:
        return None, 'returned buffer size: %d' % size.value

    for i in range(table.num_entries):
        rec = table.table[i]
        if rec.printable_ip() == ip:
            return rec.printable_mac(), ''

    return None, 'arp record of default gateway not found'


def detect_gateway():
    get_ip_net_table = windll.iphlpapi.GetIpForwardTable

    table = MibIpForwardTable()
    size = c_uint(sizeof(table))

    r = get_ip_net_table(byref(table), byref(size), 0)

    if r != 0:
        return None, ('returned buffer size: %d' % size.value)

    for i in range(table.num_entries):
        rec = table.table[i]
        if rec.forward_dest == 0:
            return rec.printable_nexthop(), ''

    return None, 'default destination not found'


def get_gateway_ipmac():

    gateway, route_log = detect_gateway()
    log = "Route log:\n" + route_log

    if not gateway:
        return (None, None), log

    mac, arp_log = get_arp(gateway)
    log += "ARP log:\n" + arp_log

    return (gateway, mac), log

