#!/usr/bin/python
from scapy.all import *
import argparse
import re
import binascii
import random
import multiprocessing
import logging
import itertools
import codecs
import ipaddress
import os
import sys
import datetime
from scapy.utils import PcapWriter


sys.setrecursionlimit(30000)
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)#supress Scapy warnings`

runPath = os.path.dirname(os.path.abspath(__file__))
runPathTmp  = runPath + "/.."  
logPath     = runPathTmp + '/front/log'
# DEBUG
isDebug = False


#===============================================================================
# UTIL
#===============================================================================

#-------------------------------------------------------------------------------
def timeNow():
    return datetime.datetime.now().replace(microsecond=0)

def write_file (pPath, pText):
    # Write the text depending using the correct python version
    if sys.version_info < (3, 0):
        file = io.open (pPath , mode='w', encoding='utf-8')
        file.write ( pText.decode('unicode_escape') ) 
        file.close() 
    else:
        file = open (pPath, 'w', encoding='utf-8') 
        file.write (pText) 
        file.close() 

# Empty the last run log file
write_file(logPath + "/pialert_pholus_lastrun.log", "")

def file_print(*args):

    result = ''
    
    file = open(logPath + "/pialert_pholus_lastrun.log", "a")    
    for arg in args:                
        result += str(arg)
    print(result)
    file.write(result + '\n')
    file.close()

# Empty the last run log file
write_file(logPath + "/pialert_pholus_subp_pr.log", "")

# For separate logging of the multiprocess subprocess
def file_print_pr(*args):
    if isDebug == False:
        return

    result = ''
    
    file = open(logPath + "/pialert_pholus_subp_pr.log", "a")    
    for arg in args:                
        result += str(arg)
    print(result)
    file.write(result + '\n')
    file.close()

def sanitize_string(input):
    if isinstance(input, bytes):
        input = input.decode('utf-8')
    value = b_to_str(re.sub('[^a-zA-Z0-9-_\s]', '', str(input)))
    return value

#-------------------------------------------------------------------------------
NoneType = type(None)

def b_to_str(value):
    # if value is of other type than string, convert to string
    if value is None:  
        return str("")
    elif isinstance(value, type(None)):
        return str("")
    elif isinstance(value, NoneType):
        return str("")
    elif isinstance(value, str):
        return str(value+"")
    elif isinstance(value, int):
        b_to_str(str(value))
    elif isinstance(value, bool):
        b_to_str(str(value))
    elif isinstance(value, bytes):        
        b_to_str(value.decode('utf-8'))
    elif isinstance(value, list):
        for one in value:            
            b_to_str(one)
    else:
        return str(value)
   
 
 #-------------------------------------------------------------------------------

######################################
### OBTAIN THE SYSTEM IPV6 ADDRESS ###
######################################
def get_my_ipv6_addr(interface):
    myip=""
    try:
        for ifaces in scapy.arch.linux.in6_getifaddr(): #in6_getifaddr() #return a list of IPs - ifaces, etc
            if ifaces[2]==interface:
                if not myip:
                    myip=ifaces[0]
                elif myip[0:6] == "fe80::":
                    myip=ifaces[0]
        return myip
    except:
        file_print("The interface",interface,"does not exist. Please, try again.")
        exit(0)

######################################
### OBTAIN THE SYSTEM IPV4 ADDRESS ###
######################################
def get_my_ipv4_addr(interface):
    myip=""
    try:
        myip=scapy.arch.get_if_addr(interface)
        return myip
    except:
        file_print("The interface",interface,"does not exist. Please, try again.")
        exit(0)

##########################
#####  SNIFFER CLASS #####
##########################
class Sniffer():
    def __init__ (self,filter,interface,sniffer_timeout,queue,dns,show_ttl,dos_ttl, conflict, ttl,d4, d6, target_mac, auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flooding_interval, v4, v6):
        file_print_pr("[DEBUG] sniffer_args: ", filter," ",interface," ",sniffer_timeout," ",queue," ",dns," ",show_ttl," ",dos_ttl," ", conflict," ", ttl," ",d4," ", d6," ", target_mac," ", auto_fake_responses," ",source_IPv6," ", source_IPv4," ", target_mac1," ", target_mac2," ",source_mac," ",hlimit," ",workstation," ",printer," ",googlecast," ",airtv," ",flood," ",flooding_timeout," ",flooding_interval," ", v4," ", v6)
        self.filter = filter
        self.interface = interface
        file_print_pr("[DEBUG] sniffer_timeout: ", sniffer_timeout)
        self.sniffer_timeout=sniffer_timeout
        self.queue=queue
        self.dns=dns
        self.show_ttl=show_ttl
        self.dos_ttl=dos_ttl
        self.conflict=conflict
        self.ttl=ttl
        self.d4=d4
        self.d6=d6
        self.target_mac=target_mac
        self.auto_fake_responses=auto_fake_responses
        self.source_IPv6=source_IPv6
        self.source_IPv4=source_IPv4
        self.target_mac1=target_mac1
        self.target_mac2=target_mac2
        self.source_mac=source_mac
        self.hlimit=hlimit
        self.workstation=workstation
        self.printer=printer
        self.airtv=airtv
        self.googlecast=googlecast
        self.flood=flood
        self.flooding_interval=flooding_interval
        self.flooding_timeout=flooding_timeout
        self.v4=v4
        self.v6=v6
        sniff(filter=self.filter, iface=self.interface, prn=self.handler, store=0, timeout=self.sniffer_timeout)
    def handler(self,packets):
        ext_handler(packets,self.queue,self.dns,self.show_ttl,1,self.dos_ttl,self.conflict, self.ttl,self.interface,self.d4,self.d6,self.target_mac,self.auto_fake_responses,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,self.workstation,self.printer,self.googlecast,self.airtv,self.flood,self.flooding_timeout,self.flooding_interval,self.v4,self.v6)

##################################
#####  OFFLINE SNIFFER CLASS #####
##################################
class Sniffer_Offline():
    def __init__ (self,interface,queue,show_ttl,d4, d6, target_mac,auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit):
        file_print_pr("[DEBUG] Timestamp 1: ", timeNow())
        self.interface = interface
        self.queue=queue
        self.show_ttl=show_ttl
        self.d4=d4
        self.d6=d6
        self.target_mac=target_mac
        self.auto_fake_responses=auto_fake_responses
        self.source_IPv6=source_IPv6
        self.source_IPv4=source_IPv4
        self.target_mac1=target_mac1
        self.target_mac2=target_mac2
        self.source_mac=source_mac
        self.hlimit=hlimit
        sniff(filter="udp and (port 5353 or port 53)", offline=self.interface, prn=self.handler, timeout=1)
    def handler(self,packets):
        ext_handler(packets,self.queue,False,self.show_ttl,1,False,False,4500,self.interface,self.d4,self.d6,self.target_mac,False,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,False,False,False,False,False,10.0,0.1,True,False)

########################################################################
### THE HANDLER THAT THE TWO SNIFFERS CALL - THIS MAKES THE MAIN JOB ###
########################################################################
def ext_handler(packets,queue,unidns,show_ttl,print_res,dos_ttl,conflict,ttl,interface,d4,d6,target_mac,auto_fake_responses,source_IPv6,source_IPv4,target_mac1,target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flodding_interval,v4,v6):

    file_print_pr("[DEBUG] Timestamp 2: ", timeNow())
    file_print_pr("[DEBUG] 3 ")

    dns_type = {12: "PTR", 28: "AAAA", 13: "HINFO",33: "SRV", 1: "A", 255: "* (ANY)", 16: "TXT", 15: "MX", 6: "SOA", 256: "URI", 5: "CNAME",39: "DNAME"}
    Ether_src=packets.getlayer(Ether).src
    IP_src=None
    if packets.haslayer(IPv6):
        IP_src=packets.getlayer(IPv6).src
    elif packets.haslayer(IP):
        IP_src=packets.getlayer(IP).src
    else:
        file_print_pr("[DEBUG] Timestamp 4: ", timeNow())        
        
    res0= Ether_src + " | " + IP_src.ljust(27)
    if packets.haslayer(DNS):
        file_print_pr("[DEBUG] Timestamp 5: ", timeNow()) 
        dns=packets.getlayer(DNS)
        if (conflict or dos_ttl) and dns.ancount>0:
            DNSBlocks = [ ]
            DNSBlocks.append(dns.an)
            if conflict:
                new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=0,qd=dns[DNS].qd)
                if target_mac:
                    new_packet=Ether(src=source_mac,dst=target_mac)
                else:
                    new_packet=Ether(src=source_mac,dst=packets[Ether].dst)
                if packets.haslayer(IPv6):
                    if d6:
                        new_packet=new_packet/IPv6(src=source_IPv6,dst=d6,hlim=packets[IPv6].hlim)
                    else:
                        new_packet=new_packet/IPv6(src=source_IPv6,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim)
                else:
                    if d4:
                        new_packet=new_packet/IP(src=source_IPv4,dst=d4,ttl=packets[IP].ttl)
                    else:
                        new_packet=new_packet/IP(src=source_IPv4,dst=packets[IP].dst,ttl=packets[IP].ttl)
                for p in DNSBlocks:
                    if isinstance(p,DNSRR):
                        new_DNS_packet=new_DNS_packet/p
            elif dos_ttl:
                new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=dns[DNS].arcount,qd=dns[DNS].qd)
                DNSBlocks.append(dns.ar)
                if target_mac:
                    new_packet=Ether(src=source_mac,dst=target_mac)
                else:
                    new_packet=Ether(src=packets[Ether].src,dst=packets[Ether].dst)
                if packets.haslayer(IPv6):
                    if d6:
                        new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=d6,hlim=packets[IPv6].hlim)
                    else:
                        new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim)
                else:
                    if d4:
                        new_packet=new_packet/IP(src=packets[IP].src,dst=d4,ttl=packets[IP].ttl)
                    else:
                        new_packet=new_packet/IP(src=packets[IP].src,dst=packets[IP].dst,ttl=packets[IP].ttl)
                for p in DNSBlocks:
                    if isinstance(p,DNSRR):
                        new_p=DNSRR()
                        new_p.ttl=0
                        new_p.rrname=p.rrname
                        new_p.type=p.type
                        new_p.rclass=p.rclass
                        new_p.rdlen=p.rdlen
                        new_p.rdata=p.rdata
                        new_DNS_packet=new_DNS_packet/new_p            
            if unidns:
                new_packet=new_packet/UDP(dport=53)/new_DNS_packet
            else:
                new_packet=new_packet/UDP(dport=5353,sport=5353)/new_DNS_packet
            for x in range(0,2):#Send each packet twice
                file_print_pr("[DEBUG] Timestamp 6: ", timeNow()) 
                sendp(new_packet,iface=interface)
                file_print_pr("[DEBUG] Timestamp 7: ", timeNow()) 
            
        elif auto_fake_responses or (not (dos_ttl or conflict)):
            ## IF THIS IS A QUERY ##
            file_print_pr("[DEBUG] Timestamp 8: ", timeNow()) 
            if dns.opcode==0:
                res0 = res0 + ""
                if dns.qdcount>0:
                    DNSBlocks = [ ]
                    DNSBlocks.append(dns.qd)
                    for block in DNSBlocks:
                        while isinstance(block,DNSQR):
                            dnsqr=block.getlayer(DNSQR)
                            ### IF WE NEED TO AUTO RESPOND WITH A FAKE | DOS RESPONSE ###
                            if auto_fake_responses:
                                myttl=int(ttl)
                                if isinstance(dnsqr.qname,bytes):
                                    qname=dnsqr.qname.decode("utf-8")
                                else:
                                    qname=dnsqr.qname
                                if isinstance(dnsqr.name,bytes):
                                    name=dnsqr.name.decode("utf-8")
                                else:
                                    name=dnsqr.name
                                if (("in-addr.arpa" in qname) or ("ip6.arpa" in name)) and workstation:
                                    if unidns:
                                        dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR")
                                    else:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR")
                                elif ("_workstation._tcp." in qname) and workstation:
                                    #qname=dnsqr.qname
                                    if qname.endswith('.'):
                                        qname=qname[:-1]
                                    if unidns:
                                        dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)
                                    else:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)
                                elif (("_pdl-datastream._tcp." in qname) or ("_ipp._tcp." in qname)) and printer:
                                    #qname=dnsqr.qname
                                    if qname.endswith('.'):
                                        qname=qname[:-1]
                                    #make the SRV additional record
                                    port=9100
                                    port='{0:016b}'.format(port)
                                    port="{0:0>4X}".format(int(port, 2))
                                    weight=0
                                    weight='{0:016b}'.format(weight)
                                    weight="{0:0>4X}".format(int(weight, 2))
                                    priority=0
                                    priority='{0:016b}'.format(priority)
                                    priority="{0:0>4X}".format(int(priority, 2))
                                    data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
                                    #for explanation, check http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended
                                    sublabels = "HP10000.local".split(".") + [""]
                                    label_format = ""
                                    for s in sublabels:
                                        label_format = '%s%dp' % (label_format, len(s) + 1)
                                    label_data = struct.pack(label_format, *sublabels)
                                    srv_rrname=data+label_data
                                    txt_record=""
                                    rdata=['txtvers=1','qtotal=1','pdl=application/vnd.hp-PCL','ty=MyOfficejet100000','product=(Trexa gureue)','priority=0','adminur=http://'+source_IPv4]
                                    #file_print(type(rdata))
                                    for r in rdata:
                                        length=hex(len(r))[2:]
                                        #check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/
                                        if len(r) > 255:
                                            s=struct.Struct('I')# CHECK IT
                                        else:
                                            s=struct.Struct('B')
                                        value=(len(r),)
                                        packed_data=s.pack(*value)
                                        txt_record=txt_record+packed_data+r
                                    pkt= DNSRR(rrname="MyOfficejet10000._pdl-datasream._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
                                    pkt.rdlen-=1
                                    mylength=len(pkt.rrname)+12
                                    dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
                                    pkt=dnsrr_packet
                                    if unidns:
                                        dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt
                                    else:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt
                                elif ("_googlecast._tcp." in qname) and googlecast:
                                    #qname=dnsqr.qname
                                    if qname.endswith('.'):
                                        qname=qname[:-1]
                                    #make the SRV additional record
                                    port=8009
                                    port='{0:016b}'.format(port)
                                    port="{0:0>4X}".format(int(port, 2))
                                    weight=0
                                    weight='{0:016b}'.format(weight)
                                    weight="{0:0>4X}".format(int(weight, 2))
                                    priority=0
                                    priority='{0:016b}'.format(priority)
                                    priority="{0:0>4X}".format(int(priority, 2))
                                    data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
                                    sublabels = "32799abf-18ea-631d-62ae-390515bb47c5.local".split(".") + [""]
                                    label_format = ""
                                    for s in sublabels:
                                        label_format = '%s%dp' % (label_format, len(s) + 1)
                                    label_data = struct.pack(label_format, *sublabels)
                                    srv_rrname=data+label_data
                                    txt_record=""
                                    rdata=['id=32799abf-18ea-631d-62ae-390515bb47c5','rm=','ve=05','md=Chromecast Ultra','ic=/setup/icon.png','fn=LivingRoom','ca=4101','st=0','bs=FA8FCA7EO948','rs=0']
                                    for r in rdata:
                                        length=hex(len(r))[2:]
                                        if len(r) > 255:
                                            s=struct.Struct('I')# CHECK IT
                                        else:
                                            s=struct.Struct('B')
                                        value=(len(r),)
                                        packed_data=s.pack(*value)
                                        txt_record=txt_record+packed_data+r
                                    pkt= DNSRR(rrname="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
                                    pkt.rdlen-=1
                                    mylength=len(pkt.rrname)+12
                                    dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
                                    pkt=dnsrr_packet
                                    if unidns:
                                        dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)
                                    else:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)
                                elif ("_airplay." in qname) and airtv:
                                    #qname=dnsqr.qname
                                    if qname.endswith('.'):
                                        qname=qname[:-1]

                                    txt_record=""
                                    rdata=['model=J33AP']
                                    for r in rdata:
                                        length=hex(len(r))[2:]
                                        if len(r) > 255:
                                            s=struct.Struct('I')# CHECK IT
                                        else:
                                            s=struct.Struct('B')
                                        value=(len(r),)
                                        packed_data=s.pack(*value)
                                        txt_record=txt_record+packed_data+r
                                    pkt= DNSRR(rrname="Apple TV-mitsos._device-info._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
                                    pkt.rdlen-=1
                                    mylength=len(pkt.rrname)+12
                                    dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
                                    pkt=dnsrr_packet

                                    txt_record=""
                                    rdata=['deviceid=9C:20:7B:AD:6B:E4','features=0x5A7FFFF7,0xE','flags=0x44','model=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','pi=a62a851e-d024-439f-9ee0-01ef1b23d6ca','srcvers=220.68','vv=2']
                                    for r in rdata:
                                        length=hex(len(r))[2:]
                                        if len(r) > 255:
                                            s=struct.Struct('I')# CHECK IT
                                        else:
                                            s=struct.Struct('B')
                                        value=(len(r),)
                                        packed_data=s.pack(*value)
                                        txt_record=txt_record+packed_data+r
                                    pkt2= DNSRR(rrname="Apple TV-mitsos._airplay._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
                                    pkt2.rdlen-=1
                                    mylength=len(pkt2.rrname)+12
                                    dnsrr_packet=str(pkt2)[0:mylength]+str(pkt2)[mylength+1::]
                                    pkt2=dnsrr_packet

                                    txt_record=""
                                    rdata=['cn=0,1,2,3','da=true','et=0,3,5','ft=0x5A7FFFF7,0xE','md=0,1,2','am=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','sf=0x44','tp=UDP','vn=65537','vs=220.68','vv=2']
                                    for r in rdata:
                                        length=hex(len(r))[2:]
                                        if len(r) > 255:
                                            s=struct.Struct('I')# CHECK IT
                                        else:
                                            s=struct.Struct('B')
                                        value=(len(r),)
                                        packed_data=s.pack(*value)
                                        txt_record=txt_record+packed_data+r
                                    pkt3= DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
                                    pkt3.rdlen-=1
                                    mylength=len(pkt3.rrname)+12
                                    dnsrr_packet=str(pkt3)[0:mylength]+str(pkt3)[mylength+1::]
                                    pkt3=dnsrr_packet

                                    #make the SRV additional record
                                    port=7000
                                    port='{0:016b}'.format(port)
                                    port="{0:0>4X}".format(int(port, 2))
                                    weight=0
                                    weight='{0:016b}'.format(weight)
                                    weight="{0:0>4X}".format(int(weight, 2))
                                    priority=0
                                    priority='{0:016b}'.format(priority)
                                    priority="{0:0>4X}".format(int(priority, 2))
                                    data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
                                    sublabels = "Apple-TV.local".split(".") + [""]
                                    label_format = ""
                                    for s in sublabels:
                                        label_format = '%s%dp' % (label_format, len(s) + 1)
                                    label_data = struct.pack(label_format, *sublabels)
                                    srv_rrname=data+label_data

                                    if unidns:
                                        dns_packet=UDP(dport=53)
                                    else:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3,arcount=6)/DNSRR(rrname=qname,ttl=myttl,rdata="Apple TV-mitsos._device-info."+qname,type="PTR")/pkt/DNSRR(rrname="_raop._tcp.local",ttl=myttl,rdata="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="PTR")/pkt2/pkt3/DNSRR(rrname="Apple TV-mitsos."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv6,type="AAAA",rclass=32769)
                                else:
                                    if isinstance(dnsqr.qname,bytes):
                                        qname=dnsqr.qname.decode("utf-8")
                                    else:
                                        qname=dnsqr.qname
                                    if qname.endswith('.'):
                                        qname=qname[:-1]
                                    #file_print("Query Name = ",qname," Type=",dnsqr.qtype)
                                    if unidns:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A")
                                    else:
                                        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A")
                                file_print_pr("[DEBUG] Timestamp 9: ", timeNow())      
                                send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,False,10.0,0.1)#CHANGE DEFAULT VALUES
                                file_print_pr("[DEBUG] Timestamp 10: ", timeNow()) 
                            ### END "IF WE NEED TO AUTO RESPOND WITH A FAKE RESPONSE
                            ### NEXT LINES ARE ONLY USED TO PRINT RESULTS ###
                            if dnsqr.qclass==32769:
                                res = res0 + " | Question            | "+dnsqr.qname.decode("utf-8") + " " + dns_type[dnsqr.qtype] +" QU Class:IN"
                            elif dnsqr.qclass==1:
                                res = res0 + " | Question            | "+dnsqr.qname.decode("utf-8") + " "+ dns_type[dnsqr.qtype] + " QM Class:IN"
                            elif dnsqr.qclass==255:
                                res = res0 + " | Question            | "+dnsqr.qname.decode("utf-8") + " "+ dns_type[dnsqr.qtype] + " QM Class:ANY"
                            else:
                                file_print("DNSQR:")
                                file_print("-----")
                                file_print(dnsqr.show())
                                file_print("DEBUGGING IS NEEDED")
                                exit(0)
                            if print_res==1:
                                file_print(res)
                            queue.put(res)
                            block = block.payload
                if dns.arcount>0:
                    DNSBlocks = [ ]
                    DNSBlocks.append(dns.ar)
                    for block in DNSBlocks:
                        if block.haslayer(DNSRROPT):
                            while isinstance(block,DNSRROPT):#Somewhat equivalent: while not isinstance(an, NoPayload):
                                dnsrropt=block.getlayer(DNSRROPT)
                                #print "DNS OPT Resource Record"
                                if dnsrropt.rrname == ".":
                                    rrname = "<Root>"
                                else:
                                    rrname = dnsrropt.rrname
                                if dnsrropt.type==41:
                                    ARtype="OPT"
                                else:
                                    ARtype=str(dnsrropt.type)
                                res = res0 + " | Additional_Record   | " + rrname.decode("utf-8")  + " " + ARtype 
                                file_print_pr("[DEBUG] Timestamp 11: ", timeNow()) 
                                if dnsrropt.haslayer(EDNS0TLV):
                                    edns0tlv=dnsrropt.getlayer(EDNS0TLV)
                                    if edns0tlv.optcode==4:
                                        optcode="Reserved"
                                    else:
                                        optcode=str(edns0tlv.optcode)
                                    res = res + " EDNS0TLV: " + optcode + " " + codecs.encode(edns0tlv.optdata, 'hex_codec').decode("utf-8")
                                if print_res==1:
                                    file_print(res)
                                queue.put(res)
                                block = block.payload
                        elif block.haslayer(DNSRR):
                            while isinstance(block,DNSRR):#Somewhat equivalent: while not isinstance(an, NoPayload):
                                dnsrr=block.getlayer(DNSRR)
                                if dnsrr.rclass==32769:

                                    str_res0   = str(b_to_str(res0)) + ""
                                    str_rrname = str(b_to_str(dnsrr.rrname)) + ""
                                    str_type   = str(b_to_str(dns_type[dnsrr.type])) + ""
                                    str_rdata  = str(b_to_str(dnsrr.rdata)) + ""

                                    res = str_res0       + " | DNS Resource Record | " + str_rrname + " " + str_type + " QU Class:IN " + str_rdata
                                elif dnsrr.rclass==1:

                                    str_res0   = str(b_to_str(res0)) + ""
                                    str_rrname = str(b_to_str(dnsrr.rrname)) + ""
                                    str_type   = str(b_to_str(dns_type[dnsrr.type])) + ""
                                    str_rdata  = str(b_to_str(dnsrr.rdata)) + ""

                                    res = str_res0 + " | DNS Resource Record | " + str_rrname + " " + str_type + " QM Class:IN " + str_rdata
                                elif dnsrr.qclass==255:

                                    str_res0   = str(b_to_str(res0)) + ""
                                    str_qname  = str(b_to_str(dnsrr.qname)) + ""
                                    str_qtype  = str(b_to_str(dns_type[dnsrr.qtype])) + ""

                                    res = str_res0 + " | Question            | " + str_qname + " " + str_qtype + " QM Class:ANY"
                                else:
                                    file_print("DNSRR:")
                                    file_print("-----")
                                    file_print(dnsrr.show())
                                    file_print("DEBUGGING IS NEEDED HERE")
                                    exit(0)
                                if dnsrr.type==33:#SRV Record

                                    str_res0   = str(b_to_str(res0)) + ""
                                    str_rrname = str(b_to_str(dnsrr.rrname)) + ""
                                    str_type   = str(b_to_str(dns_type[dnsrr.type])) + ""                                    
                                    str_rclass = str(b_to_str(dnsrr.rclass)) + ""

                                    priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX")
                                    weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX")
                                    port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX")

                                    res = str_res0 + " | Additional_Record   | "+ str_rrname + " " + str_type+" " + str_rclass + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::]
                                else:
                                    rdata=dnsrr.rdata
                                    if isinstance(rdata,bytes):
                                        rdata = rdata.decode("utf-8")
                                    if "._tcp." not in rdata and "._udp." not in rdata:
                                        if rdata == "_dhnap.":
                                            rdata=rdata+"_tcp."

                                    str_res0   = str(b_to_str(res0)) + ""
                                    str_rrname = str(b_to_str(dnsrr.rrname)) + ""
                                    str_type   = str(b_to_str(dns_type[dnsrr.type])) + ""
                                    str_rdata  = str(b_to_str(dnsrr.rdata)) + ""
                                    str_rclass = str(b_to_str(dnsrr.rclass)) + ""
                                    file_print_pr("[DEBUG] Timestamp 12: ", timeNow()) 
                                    res = str_res0 + " | Additional_Record   | "+str_rrname + " " + str_type+" " + str_rclass + ' "' +str_rdata+'"'
                                if show_ttl:
                                    res = res + " TTL:"+str(dnsrr.ttl)
                                if print_res==1:
                                    file_print(res)
                                file_print_pr("[DEBUG] Timestamp 13: ", timeNow()) 
                                queue.put(res)
                                block = block.payload
                                file_print_pr("[DEBUG] Timestamp 14: ", timeNow()) 

                if dns.ancount>0:
                    DNSBlocks = [ ]
                    DNSBlocks.append(dns.an)
                    for block in DNSBlocks:
                        file_print_pr("[DEBUG] Timestamp 15: ", timeNow()) 
                        while isinstance(block,DNSRR):
                            dnsrr=block.getlayer(DNSRR)
                            if dnsrr.rclass==1:
                                rclass="Class:IN"
                                file_print_pr("[DEBUG] Timestamp 16: ", timeNow()) 
                            else:
                                rclass="Class:"+str(dnsrr.rclass)
                            rdata=dnsrr.rdata
                            if isinstance(rdata,bytes):
                                rdata = rdata.decode("utf-8")
                            if dnsrr.type==33:#SRV Record
                                priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX")
                                weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX")
                                port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX")
                                res = res0 + " | Answer              | "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::]
                            else:
                                if "._tcp." not in rdata and "._udp." not in rdata:
                                    if rdata  == "_dhnap.":
                                        rdata=dnsrr.rdata+"_tcp."
                                if isinstance(rdata,list):
                                    rdata = b" ".join(rdata).decode("utf-8")
                                res = res0 + " | Answer              | "+dnsrr.rrname.decode("utf-8") + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +rdata+'"'
                                file_print_pr("[DEBUG] Timestamp 17: ", timeNow()) 
                                file_print_pr("[DEBUG] Timestamp 18: ", timeNow()) 
                            if show_ttl:
                                res = res + " TTL:"+str(dnsrr.ttl)
                            if print_res==1:
                                file_print(res)
                            file_print_pr("[DEBUG] Timestamp 19: ", timeNow()) 
                            queue.put(res)
                            block = block.payload
                            file_print_pr("[DEBUG] Timestamp 20: ", timeNow()) 
                if dns.nscount>0:  #MAYBE HERE
                    file_print_pr("[DEBUG] Timestamp 21: ", timeNow()) 
                    DNSBlocks = [ ]
                    DNSBlocks.append(dns.ns)
                    file_print_pr("[DEBUG] Timestamp 22: ", timeNow())                     
                    for block in DNSBlocks:
                        file_print_pr("[DEBUG] Timestamp 23: ", timeNow()) 
                        while isinstance(block,DNSRR):
                            dnsrr=block.getlayer(DNSRR)
                            if dnsrr.rclass==1:
                                rclass="Class:IN"
                                file_print_pr("[DEBUG] Timestamp 24: ", timeNow()) 
                            else:
                                rclass="Class:"+str(dnsrr.rclass) 

                            str_res0   = str(b_to_str(res0)) + ""
                            str_rrname = str(b_to_str(dnsrr.rrname)) + ""
                            str_type   = str(b_to_str(dns_type[dnsrr.type])) + ""
                            str_rdata  = str(b_to_str(dnsrr.rdata)) + ""
                            str_rclass = str(b_to_str(rclass)) + ""

                            res = str_res0 + " Auth_NS: "+str_rrname + " " +str_type+" " + str_rclass + ' "' +str_rdata+'"'
                            if show_ttl:
                                res = res + " TTL:"+str(dnsrr.ttl)
                            if print_res==1:
                                file_print(res)
                            file_print_pr("[DEBUG] Timestamp 25: ", timeNow())                             
                            queue.put(res)
                            block = block.payload
                            file_print_pr("[DEBUG] Timestamp 26: ", timeNow())                 
                else:
                    file_print_pr("[DEBUG] Timestamp 27: ", timeNow()) 
            else:
                file_print_pr("[DEBUG] Timestamp 28: ", timeNow()) 
        else:
            file_print_pr("[DEBUG] Timestamp 29: ", timeNow()) 
    else:
        file_print_pr("[DEBUG] Timestamp 30: ", timeNow()) 

    file_print_pr("[DEBUG] Timestamp 31: ", timeNow()) 
    file_print_pr("[DEBUG] Timestamp 32: ", timeNow(), "  ", res0)  
    file_print_pr("[DEBUG] Timestamp 33: ", timeNow(), "  ", res)  
        

########################################
########### REQUEST FUNCTION ###########
########################################
def requests(interface,v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,d4,d6,hlimit,unidns,domain,query,types_of_queries,add_domain,query_class,flood,flooding_interval,flooding_timeout):
    if add_domain:
        file_print("Sending mdns requests")
    domain_list = domain.split(",")
    query_list = query.split(",")
    if add_domain:
        the_query=query_list[0]+"."+domain_list[0]
    else:
        types_of_queries="ALL"#implies that first a generic scan has beem performed
        the_query=query_list[0]
    dns_query=DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class))
    for j in range(1,len(query_list)):
        if add_domain:
            the_query=query_list[j]+"."+domain_list[0]
        else:
            the_query=query_list[j]
        dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class))
    if add_domain:
        for i in range(1,len(domain_list)):
            for j in query_list:
                the_query=j+"."+domain_list[i]
                dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class))
    if unidns:
        dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query)
    else:
        dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query)
    send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,flood,flooding_timeout,flooding_interval)

########################################
######## SEND PACKETS FUNCTION #########
########################################
def send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,dst_ipv4,source_IPv6,dst_ipv6,interface,hlimit,payload,flood,flooding_timeout,flooding_interval):
    if v4 or not v6:
        packet=IP(src=source_IPv4,dst=dst_ipv4,ttl=hlimit,proto="udp")/payload
        if len(packet)>1500:
            frags=fragment(packet)
            packets=[]
            for frag in frags:
                pkt1=Ether(src=source_mac,dst=target_mac1)/frag
                packets.append(pkt1)
            if flood:
                counter=0.0
                file_print("Stop flooding after ",flooding_timeout," sec.")
                while(counter<float(flooding_timeout)):
                    for packt in packets:
                        sendp(pakt,iface=interface)
                    counter+=float(flooding_interval)
                    time.sleep(float(flooding_interval))
            else:
                for pakt in packets:
                    sendp(pakt,iface=interface)
        else:
            pkt1=Ether(src=source_mac,dst=target_mac1)/packet
            if flood:
                counter=0.0
                file_print("Stop flooding after ",flooding_timeout," sec.")
                while(counter<float(flooding_timeout)):
                    sendp(pkt1,iface=interface)
                    counter+=float(flooding_interval)
                    time.sleep(float(flooding_interval))
            else:
                sendp(pkt1,iface=interface)
    if v6:
        packet=IPv6(src=source_IPv6,dst=dst_ipv6,hlim=hlimit)/payload
        if len(packet)>1500:
            frags2=fragment6(IPv6(src=source_IPv6,dst=dst_ipv6,hlim=hlimit)/IPv6ExtHdrFragment()/payload,1480)
            packets=[]
            for frag2 in frags2:
                pkt2=Ether(src=source_mac,dst=target_mac2)/frag2
                packets.append(pkt2)
            if flood:
                counter=0.0
                file_print("Stop flooding after ",flooding_timeout," sec.")
                while(counter<float(flooding_timeout)):
                    for packt in packets:
                        sendp(pakt,iface=interface)
                    counter+=float(flooding_interval)
                    time.sleep(float(flooding_interval))
            else:
                for packt in packets:
                    sendp(pkt2,iface=interface)
        else:
            pkt2=Ether(src=source_mac,dst=target_mac2)/packet
            if flood:
                counter=0.0
                file_print("Stop flooding after ",flooding_timeout," sec.")
                while(counter<float(flooding_timeout)):
                    sendp(pkt2,iface=interface)
                    counter+=float(flooding_interval)
                    time.sleep(float(flooding_interval))
            else:
                pkt2=Ether(src=source_mac,dst=target_mac2)/packet
                sendp(pkt2,iface=interface)

##########################################################
### Create an IPv6 link local address from MAC address ###
##########################################################
def mac2ipv6(mac):
    ###Code from https://stackoverflow.com/questions/37140846/how-to-convert-ipv6-link-local-address-to-mac-address-in-python###
    # only accept MACs separated by a colon
    parts = mac.split(":")

    # modify parts to match IPv6 value
    parts.insert(3, "ff")
    parts.insert(4, "fe")
    parts[0] = "%x" % (int(parts[0], 16) ^ 2)

    # format output
    ipv6Parts = []
    for i in range(0, len(parts), 2):
        ipv6Parts.append("".join(parts[i:i+2]))
    #ipv6 = "fe80::%s/64" % (":".join(ipv6Parts))
    ipv6 = "fe80::%s" % (":".join(ipv6Parts))
    return ipv6


########################################################
########################################################
def main():
    parser = argparse.ArgumentParser(description='Pholus: An mdns testing tool')
    parser.add_argument('interface',  action="store", help="the network interface to use, or the file (if -rpcap switch is used).")
    parser.add_argument('-rpcap','--readpcap', action="store_true", dest="rpcap", default=False, help="Read packets from a pcap file")
    parser.add_argument('-dns', '--DNS',  action="store_true", dest="dns", default=False, help="Use unicast DNS request; remember to define target address")
    parser.add_argument('-4', '--v4',  action="store_true", dest="v4", default=False, help="Send mdns request/response using IPv4.")
    parser.add_argument('-6', '--v6',  action="store_true", dest="v6", default=False, help="Send mdns request/response using IPv6.")
    parser.add_argument('-hl', '--hlimit',  action="store", dest="hlimit", default=255, type=int, help="The TTL (for IPv4) or Hop Limit (for IPv6).")
    parser.add_argument('-rq', '--request',  action="store_true", dest="request", default=False, help="Send mdns request.")
    parser.add_argument('-rp', '--response',  action="store_true", dest="response", default=False, help="Send unsolicired mdns response.")
    parser.add_argument('-ll', '--link_local',  action="store_true", dest="link_local", default=False, help="As IPv6 source address, use link local address instead of global address")
    parser.add_argument('-s6', '--source_addr_v6',  action="store", dest="source6", default=False, help="The IPv6 address of the sender (if you want to spoof it).")
    parser.add_argument('-s4', '--source_addr_v4',  action="store", dest="source4", default=False, help="The IPv4 address of the sender (if you want to spoof it).")
    parser.add_argument('-d6', '--dest_addr_v6', action="store", dest="d6", default="ff02::fb", help="The IPv6 destination address of the mdns packets")
    parser.add_argument('-d4', '--dest_addr_v4', action="store", dest="d4", default="224.0.0.251", help="The IPv4 destination address of the mdns packets")
    parser.add_argument('-qtype', '--gtype', action="store", dest="qtype", default="ALL", help="qtype of mDNS query (e.g. PTR, etc.)")
    parser.add_argument('-query', '--query', action="store", dest="query", default="_services._dns-sd._udp", help="Query/ies of the mDNS packet; it can be a comma-separated list")
    parser.add_argument('-qclass', '--query_class', action="store", dest="q_class", default=1, help="The class of the query (e.g. QU vs QM)")
    parser.add_argument('-qu', '--qu',  action="store_true", dest="qu", default=False, help="Send a Uniqast (QU) question in Queries")
    parser.add_argument('-dns_response', '--dns_response', action="store", dest="dns_response", default="", help="Responses of the mDNS packet; it can be a comma-separated list")
    parser.add_argument('-sttl', '--show_ttl',  action="store_true", dest="show_ttl", default=False, help="Display the DNS TTL information of the received packets.")
    parser.add_argument('-rm', '--random-mac',  action="store_true", dest="random_mac", default=False, help="Randomise the source MAC address.")
    parser.add_argument('-tm', '--target_mac',  action="store", dest="target_mac", default=False, help="The mac address of the target (optional).")
    parser.add_argument('-sm', '--source_mac',  action="store", dest="source_mac", default=False, help="The mac address of the source, e.g. for spoofing purposes (optional).")
    parser.add_argument('-stimeout','--sniffer_timeout', action="store", dest="sniffer_timeout", default=5, help="The timeout (in seconds) when the integrated sniffer (IF used) will exit automatically.")
    parser.add_argument('-domain','--domain', action="store", dest="domain", default="local", help="The domain to query; default:local")
    parser.add_argument('-rdns_scanning', '--reverse_dns_scanning',  action="store", dest="rdns_scanning", default=False, help="The IPv4 address subnet that we want to scan, e.g. 192.168.169.0/24")
    parser.add_argument('-sscan', '--service_scanning',  action="store_true", dest="service_scan", default=False, help="A service scan, initiated using _services._dns-sd._udp.local")
    parser.add_argument('-ttl','--define_ttl', action="store", dest="ttl", default=4500, help="The TTL to be used in the DNS messages.")
    parser.add_argument('-dos_ttl', '--DoS_zeroizing_ttl',  action="store_true", dest="dos_ttl", default=False, help="DoS hosts by zeroizing the TTL value of the DNS packets.")
    parser.add_argument('-conflict', '--send_conflicting_questions',  action="store_true", dest="conflict", default=False, help="Send conflicting questions with the received ones.")
    parser.add_argument('-afre', '--auto_fake_responses',  action="store_true", dest="auto_fake_responses", default=False, help="Send automatically fake resposnes")
    parser.add_argument('-printer', '--printer',  action="store_true", dest="printer", default=False, help="fake responses for printer")
    parser.add_argument('-airplay', '--airplay',  action="store_true", dest="airtv", default=False, help="fake responses for AirPlay TV")
    parser.add_argument('-workstation', '--workstation',  action="store_true", dest="workstation", default=False, help="fake responses for workstation")
    parser.add_argument('-googlecast', '--googlecast',  action="store_true", dest="googlecast", default=False, help="fake responses for googlecast")
    parser.add_argument('-fl','--flood', action="store_true", dest="flood", default=0, help="flood the targets")
    parser.add_argument('-flooding-interval','--interval-of-flooding', action="store", dest="flooding_interval", default=1, help="the interval between packets when flooding the targets")
    parser.add_argument('-ftimeout','--flooding_timeout', action="store", dest="flooding_timeout", default=200, help="The time (in seconds) to flood your target.")
    values = parser.parse_args()

    if values.rpcap:
        file_print("Read packets from a pcap file")
        if not os.path.isfile(values.interface):
            file_print('[-] ' + values.interface + ' does not exist')
            exit(0)
        elif not os.access(values.interface, os.R_OK):
            file_print('[-] ' + values.interface + ' access is denied')
            exit(0)
        file_print("Press Ctrl-C to exit and print the results")
        q = multiprocessing.Queue()
        file_print_pr("[DEBUG] Timestamp 34: ", timeNow()) 
        pr = multiprocessing.Process(target=Sniffer_Offline, args=(values.interface,q,values.show_ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,values.source6,values.source4,values.target_mac,values.target_mac,values.source_mac,values.hlimit))
        pr.start()
        pr.join(15)
        if pr.is_alive():                    
            file_print("SUB-PROCESS KILLED TIMESTAMP: ", timeNow()) 
        file_print_pr("[DEBUG] Timestamp 35: ", timeNow()) 
        results=[]
        while not q.empty():
            results.append(q.get())
        myset=set(results)
        results=list(myset)
        results.sort()
        file_print("\n*********************************************RESULTS*********************************************")
        for r in results:
            file_print(r)
        exit(0)
    else:
        #####################################LETS DO SOME CHECKS FIRST TO SEE IF WE CAN WORK####################
        if os.geteuid() != 0:
            file_print("You must be root to run this script.")
            exit(1)
        conf.verb=0
        #########################################DEFINE SOURCE ADDRESSES########################################
        source_mac=None
        if values.random_mac:
            source_mac =  ':'.join(map(lambda x: "%02x" % x, [ 0x00, 0x16, 0x3E, random.randint(0x00, 0x7F), random.randint(0x00, 0xFF), random.randint(0x00, 0xFF) ]))
        elif values.source_mac:
            source_mac=values.source_mac
        else:
            try:
                source_mac=get_if_hwaddr(values.interface)
            except:
                file_print("interface ",values.interface," does not exist")
                exit(0)

        if values.qu:
            q_class=32769
        else:
            q_class=int(values.q_class)
        if not values.source6:
            source_IPv6=None
            source_IPv6=get_my_ipv6_addr(values.interface)#Selects global IPv6 address, if available
            if not source_IPv6 or values.random_mac or values.link_local: #If a random MAC is used, construct the corresponding IPv6 Link Local Address
                source_IPv6 = mac2ipv6(source_mac)
        else:
            source_IPv6=values.source6
        if not values.source4:
            source_IPv4=get_my_ipv4_addr(values.interface)
        else:
            source_IPv4=values.source4
        file_print("source MAC address:",source_mac,"source IPv4 Address:",source_IPv4,"source IPv6 address:",source_IPv6)
        #########################################################################################################
        if values.target_mac:
            target_mac1=values.target_mac
            target_mac2=values.target_mac
        else:
            target_mac2="33:33:00:00:00:02"
            target_mac1="01:00:5e:00:00:fb"
        ############################################Sniffing requirements########################################
        q = multiprocessing.Queue()
        if values.dos_ttl or values.auto_fake_responses:
            if values.auto_fake_responses:
                file_print("Send fake responses to requests" )
                if values.target_mac:
                    myfilter = "not ether src " + source_mac + " and not ether dst " + values.target_mac +" and udp and port 5353"
                else:
                    myfilter = "not ether src " + source_mac + " and udp and port 5353"
            elif values.target_mac:
                file_print("Performing implicit DoS by sending automated spoofed DNS Answers with TTL=0" )
                myfilter = "not ether dst " + values.target_mac + " and udp and port 5353"
            else:
                file_print("Performing implicit DoS by sending automated spoofed DNS Answers with TTL=0" )
                myfilter = "udp and port 5353"
            file_print("Sniffer filter is: ",myfilter)
            file_print("I will sniff for ",values.sniffer_timeout," seconds, unless interrupted by Ctrl-C")
            file_print("Press Ctrl-C to exit")
            try:
                file_print_pr("[DEBUG] Timestamp 36: ", timeNow()) 
                Sniffer(myfilter, values.interface, float(values.sniffer_timeout),q,values.dns,values.show_ttl, values.dos_ttl, values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)
                file_print_pr("[DEBUG] Timestamp 37: ", timeNow()) 
            except KeyboardInterrupt:
                file_print("Exiting on user's request 1")
                exit(0)
            exit(0)
        myfilter = "not ether src " + source_mac + " and udp and port 5353"
        file_print("Sniffer filter is: ",myfilter)
        file_print("I will sniff for ",values.sniffer_timeout," seconds, unless interrupted by Ctrl-C")
        pr = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q,values.dns,values.show_ttl, values.dos_ttl, values.conflict, values.ttl,values.d4,values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2, source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
        pr.daemon = True
        file_print_pr("[DEBUG] Timestamp 38: ", timeNow()) 
        pr.start()
        file_print_pr("[DEBUG] Timestamp 39: ", timeNow()) 
        file_print("------------------------------------------------------------------------")
        time.sleep(1)#to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
        file_print_pr("[DEBUG] Timestamp 40: ", timeNow()) 
        ##########################################################################################################
        if values.request:
            file_print_pr("[DEBUG] Timestamp 41: ", timeNow()) 
            requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
            file_print_pr("[DEBUG] Timestamp 42: ", timeNow()) 
        elif values.response:
            #qr=1=>Response, aa=1=>Server is an authority for the domain, rd=0=> Do not query recursively
            file_print_pr("[DEBUG] Timestamp 43: ", timeNow()) 
            if values.dns:
                dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0)
            else:
                dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0)
            file_print_pr("[DEBUG] Timestamp 44: ", timeNow()) 
            responses = values.dns_response.split(",")
            no_of_answers=0
            no_of_additional_records=0
            dnsar=None
            for r in responses:
                values_of_responses = r.split("/")
                dnsrr=DNSRR()
                port=0
                weight=0
                priority=0
                no_of_answers+=1
                this_is_a_dns_ar=False
                rdata=[]
                for dns_values in values_of_responses:
                    #print dns_values
                    value = dns_values.split("==")
                    if value[0]=="Type":
                        dnsrr.type=value[1]
                    elif value[0]=="Name":
                        dnsrr.rrname=value[1]
                    elif value[0]=="Target":
                        rdata.append(value[1])
                    elif value[0]=="TTL":
                        dnsrr.ttl=int(value[1])
                    elif value[0]=="Flush":
                        if value[1]=="True":
                            dnsrr.rclass=32769
                    elif value[0]=="Priority":
                        priority=int(value[1])
                    elif value[0]=="Weight":
                        weight=int(value[1])
                    elif value[0]=="Port":
                        port=int(value[1])
                    elif value[0]=="AR":
                        if value[1]=="True":
                            no_of_additional_records+=1
                            this_is_a_dns_ar=True
                if dnsrr.type==33:
                    port='{0:016b}'.format(port)
                    port="{0:0>4X}".format(int(port, 2))
                    weight='{0:016b}'.format(weight)
                    weight="{0:0>4X}".format(int(weight, 2))
                    priority='{0:016b}'.format(priority)
                    priority="{0:0>4X}".format(int(priority, 2))
                    data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
                    #http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended
                    sublabels = rdata[0].split(".") + [""]
                    label_format = ""
                    for s in sublabels:
                        label_format = '%s%dp' % (label_format, len(s) + 1)
                    label_data = struct.pack(label_format, *sublabels)
                    dnsrr.rdata=data+label_data
                elif dnsrr.type==16:
                    txt_record=""
                    for r in rdata:
                        length=hex(len(r))[2:]
                        #check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/
                        if len(r) > 255:
                            s=struct.Struct('I')# CHECK IT
                        else:
                            s=struct.Struct('B')
                        value=(len(r),)
                        packed_data=s.pack(*value)
                        txt_record=txt_record+packed_data+r
                    dnsrr.rdata=txt_record
                    ##################
                    pkt=dnsrr
                    pkt.rdlen-=1
                    mylength=len(pkt.rrname)+12
                    dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
                    ##################
                    dnsrr=dnsrr_packet
                else:
                    dnsrr.rdata=rdata[0]
                if not this_is_a_dns_ar:
                    dns_packet=dns_packet/dnsrr
                else:
                    if not dnsar:
                        dnsar=dnsrr
                    else:
                        dnsar=dnsar/dnsrr
            if dnsar:
                dns_packet=dns_packet/dnsar
            dns_packet[DNS].ancount=no_of_answers-no_of_additional_records
            dns_packet[DNS].arcount=no_of_additional_records
            send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval)
        elif values.rdns_scanning:
            file_print_pr("[DEBUG] Timestamp 45: ", timeNow()) 
            dns_query=None
            ipn = ipaddress.ip_network(values.rdns_scanning)
            for ip in ipn.hosts():
                the_query = ip.reverse_pointer
                if not dns_query:
                    dns_query=DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class)
                else:
                    dns_query=dns_query/DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class)
            if values.dns:
                dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query)
            else:
                dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query)
            send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval)
            file_print_pr("[DEBUG] Timestamp 46: ", timeNow()) 
        elif values.service_scan:
            file_print_pr("[DEBUG] Timestamp 47: ", timeNow()) 
            requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
            file_print_pr("[DEBUG] Timestamp 48: ", timeNow()) 
        ############################################################################################
        ############################################################################################
        if pr:
            file_print_pr("[DEBUG] Timestamp 49: ", timeNow()) 
            try:
                file_print_pr("[DEBUG] Timestamp 50: ", timeNow())  #HERE STUCK
                pr.join(20)
                if pr.is_alive():                    
                    file_print("SUB-PROCESS KILLED TIMESTAMP: ", timeNow()) 
                    file_print_pr("[DEBUG] Timestamp 51: ", timeNow()) 
                file_print_pr("[DEBUG] Timestamp 52: ", timeNow()) 
            except KeyboardInterrupt:
                file_print("Exiting on user's request 2")
                exit(0)

            file_print_pr("[DEBUG] Timestamp 53: ", timeNow()) 

            #### AFTER EXITING, PRINT THE RESULTS ####
            results=[]
            while not q.empty():
                results.append(q.get())
            if values.rdns_scanning:
                targets=[]
                q2 = multiprocessing.Queue()
                file_print_pr("[DEBUG] Timestamp 54: ", timeNow()) 
                pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))                
                pr2.daemon = True
                pr2.start()
                time.sleep(1)       #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
                file_print_pr("[DEBUG] Timestamp 55: ", timeNow()) 
                for r in results:
                    r2=r.split(" ")
                    service=r2[7].strip('"')
                    if service.endswith('local.'):
                        service=service[:-6]
                    if service.endswith('.'):
                        service=service[:-1]
                    if (r2[1],service) not in targets:
                        targets.append((r2[1],service))
                        requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
                if pr2:
                    file_print_pr("[DEBUG] Timestamp 56: ", timeNow()) 
                    try:
                        file_print_pr("[DEBUG] Timestamp 57: ", timeNow()) 
                        pr2.join(15)
                        if pr.is_alive():                    
                            file_print("SUB-PROCESS KILLED TIMESTAMP: ", timeNow())                             
                        file_print_pr("[DEBUG] Timestamp 58: ", timeNow()) 
                    except KeyboardInterrupt:
                        file_print("Exiting on user's request 3")
                    while not q2.empty():
                        results.append(q2.get())
            elif values.service_scan:
                targets=[]
                q2 = multiprocessing.Queue()
                file_print_pr("[DEBUG] Timestamp 59: ", timeNow()) 
                pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
                pr2.daemon = True
                pr2.start()
                time.sleep(1)       #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
                file_print_pr("[DEBUG] Timestamp 60: ", timeNow()) 
                for r in results:
                    r2=r.split(" ")
                    service=r2[7].strip('"')[:-1]
                    if (r2[1],service) not in targets:
                        file_print((r2[1],service))
                        targets.append((r2[1],service))
                        requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
                if pr2:
                    try:
                        file_print_pr("[DEBUG] Timestamp 61: ", timeNow()) 
                        pr2.join(15)
                        if pr.is_alive():                    
                            file_print("SUB-PROCESS KILLED TIMESTAMP: ", timeNow())                         
                        file_print_pr("[DEBUG] Timestamp 62: ", timeNow()) 
                    except KeyboardInterrupt:
                        file_print("Exiting on user's request 4")
                    while not q2.empty():
                        results.append(q2.get())
                    targets2=[]
                    q3 = multiprocessing.Queue()
                    file_print_pr("[DEBUG] Timestamp 63: ", timeNow()) 
                    pr3 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q3,values.dns,values.show_ttl, values.dos_ttl, values.conflict,values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
                    pr3.daemon = True
                    pr3.start()
                    time.sleep(1)   #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
                    file_print_pr("[DEBUG] Timestamp 64: ", timeNow()) 
                    for r in results:
                        r2=r.split(" ")
                        service=r2[4]
                        if service.endswith('local.'):
                            service=service[:-6]
                        if service.endswith('.'):
                            service=service[:-1]
                        if (r2[1],service) not in targets and (r2[1],service) not in targets2 and "_services._dns-sd._udp" not in service:
                            targets2.append((r2[1],service))
                            requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
                if pr3:
                    try:
                        file_print_pr("[DEBUG] Timestamp 65: ", timeNow()) 
                        pr3.join(15)
                        if pr.is_alive():                    
                            file_print("SUB-PROCESS KILLED TIMESTAMP: ", timeNow()) 
                        file_print_pr("[DEBUG] Timestamp 66: ", timeNow()) 
                    except KeyboardInterrupt:
                        file_print("Exiting on user's request 5")
                while not q3.empty():
                    results.append(q3.get())
            file_print("\n*********************************************RESULTS*********************************************")
            for r in results:
                file_print(r)

if __name__ == '__main__':
    main()
