# coding: UTF-8
import ipaddress
import os
import socket
import struct
import sys

class IP:
    def __init__( self, buff = None ):
        header   = struct.unpack( '<BBHHHBBH4s4s', buff )
        self.ver = header[0] >> 4
        self.ihl = header[0] & 0xF

        self.tos          = header[1]
        self.len          = header[2]
        self.id           = header[3]
        self.offset       = header[4]
        self.ttl          = header[5]
        self.protocol_num = header[6]
        self.sum          = header[7]
        self.src          = header[8]
        self.dst          = header[9]

        # IPアドレスを可読な形で変数に格納
        self.src_address = ipaddress.ip_address( self.src )
        self.dst_address = ipaddress.ip_address( self.dst )

        # プロトコルの低数値を名称にマッピング
        self.protocol_map = { 1: "ICMP", 6: "TCP", 17: "UDP" }
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except Exception as e:
            print( '%s No protocol for %s' % ( e, self.protocol_num ) )
            self.protocol = str( self.protocol_num )

class ICMP:
    def __init__( self, buff ):
        header    = struct.unpack( 'BBHHH', buff )
        self.type = header[0]
        self.code = header[1]
        self.sum  = header[2]
        self.id   = header[3]
        self.seq  = header[4]

def sniff( host ):
    # sniffer.py と同じ処理
    if os.name == 'nt':
        socket_protocol = socket.IPPROTO_IP
    else:
        socket_protocol = socket.IPPROTO_ICMP

    sniffer = socket.socket( socket.AF_INET, socket.SOCK_RAW, socket_protocol )
    sniffer.bind( ( host, 0 ) )

    sniffer.setsockopt( socket.IPPROTO_IP, socket.IP_HDRINCL, 1 )

    if os.name == 'nt':
        sniffer.ioctl( socket.SIO_RCVALL, socket.RCVALL_ON )

    try:
        while True:
            # パケットの読み込み
            raw_buffer = sniffer.recvfrom( 65565 )[0]
            # バッファの最初の20バイトからIP構造体を作成
            ip_header = IP( raw_buffer[0:20] )

            # ICMPであればそれを処理
            if ip_header.protocol == "ICMP":
                print( 'Protocol: %s %s -> %s' % ( ip_header.protocol, ip_header.src_address, ip_header.dst_address ) )
                print( 'Version: %s' % ( ip_header.ver )  )
                print( 'Header Length: %s TTL: %s' % ( ip_header.ihl, ip_header.ttl ) )

                # ICMPの位置を計算
                offset      = ip_header.ihl*4
                buff        = raw_buffer[offset: offset + 8] 
                icmp_header = ICMP( buff )
                print( 'ICMP -> Type: %s Code %s\n' % (icmp_header.type, icmp_header.code ) )

    except KeyboardInterrupt:
        # Windowsの場合はプロミスキャスモードを無効化
        if os.name == 'nt':
            sniffer.ioctl( socket.SIO_RCVALL, socket.RCVALL_OFF )
        sys.exit()

if __name__ == '__main__':
    if len( sys.argv ) == 2:
        host = sys.argv[1]
    else:
        host = '192.168.0.1'
    sniff( host )