def _generate_request(self, offer_packet: DhcpPacket) -> DhcpPacket: """Generates a DHCP REQUEST packet. """ packet = self._generate_base_packet() packet.AddLine("dhcp_message_type: DHCP_REQUEST") self._add_option_list(packet) self._set_lease_time(packet) for opt in ["server_identifier"]: packet.SetOption(opt, offer_packet.GetOption(opt)) packet.SetOption("request_ip_address", offer_packet.GetOption("yiaddr")) return packet
def ap_func( self ): global selfmac def sendlan( o ): o.dst = gwmac o.src = selfmac print "发送出去" print `o` print "发送结束" #lancap.send( o ) #self.send_s.sendto(str(o),0,(gwip,53) ) print "结果:", wincap.sendto( self.send_s, str(o), (gwip,53) ) print "开始AP线程." lancap = getcap( lanid ) apcap = getcap( apid ) for a, b in apcap: d = dpkt.ethernet.Ethernet( b ) #d.dst = gwmac if isinstance ( d.data, dpkt.ip6.IP6): continue print `d` ip = d.data if isinstance( d.data, dpkt.arp.ARP ): print "ARP包" #sendlan( d ) continue if ip.dst == "\xff" * 6: # 广播包,直接转 print "广播包" d.src = selfmac lancap.send( d ) if isinstance( d.data.data, dpkt.udp.UDP ): if not selfmac: selfmac = d.dst udp = ip.data print "UDP 包",tip(ip.src),ip.data.sport,"==>", tip(ip.dst),udp.dport if udp.dport == 67: print "DHCP" pak = DhcpPacket() pak.DecodePacket( udp.data ) print pak.str() if pak.GetOption("dhcp_message_type") continue p = self.up.handleOut( ip.src, ip.data.sport, ip.dst, ip.data.dport ) d.data.src = selfip if( ip.data.dport == 53 ): d.data.dst = dnsip d.data.sum = 0 d.data.id = 0 # sendlan( d ) def lan_func(self ): lancap = getcap( lanid ) apcap = getcap( apid ) lancap.setfilter( )
def handle_socket(self) -> None: """Retrieves the next, waiting DHCP packet, parses it and calls the handler of the associated request. """ try: data, source_address = self._socket.recvfrom(2048) if len(data) == 0: self._log.warning("unexpectedly received EOF!") return packet = DhcpPacket() packet.source_address = source_address packet.DecodePacket(data) if (not packet.IsDhcpPacket()) or ( not packet.IsOption("dhcp_message_type") ): self._log.debug("Ignoring invalid packet") return dhcp_type = packet.GetOption("dhcp_message_type")[0] if dhcp_type not in self._DHCP_TYPE_HANDLERS: self._log.debug("Ignoring packet of unexpected DHCP type %d", dhcp_type) return xid = int.from_bytes(packet.GetOption('xid'), "big") if xid not in self._requests: self._log.debug("Ignoring answer with xid %r", xid) return request = self._requests[xid] clb_name = self._DHCP_TYPE_HANDLERS[dhcp_type] if not hasattr(request, clb_name): self._log.error("request has no callback '%s'", clb_name) return clb = getattr(request, clb_name) clb(packet) except Exception: self._log.exception('handling DHCP packet failed')
def _retrieve_server_ip(self, packet: DhcpPacket) -> None: """In case we're sending the requests to more than one DHCP server, attempt to determine which DHCP server answered, so that we can restrict our future requests to only one server. """ if len(self._server_ips) > 1: self._log.debug("Attempting to find server ip") try: self._server_ips = [IPv4Address(packet.GetOption('server_identifier'))] except Exception: self._log.exception("invalid server ip response") else: # We were able to determine a single DHCP server with which we # will communicate from now on. self._log.debug("Found server ip %s", self._server_ips[0])
def handle_dhcp_ack(self, packet: DhcpPacket) -> None: """Called by the requestor as soon as a DHCP ACK packet is received for our XID. In case the packet matches what we currently expect, the packet is parsed and the success handler called. The request instance (self) is removed from the requestor and will therefore be destroyed soon. """ if self._state != self.AR_REQUEST: return self._log.debug("Received ACK") if not self._valid_source_address(packet): return if self._timeout_obj: self._timeout_mgr.del_timeout_object(self._timeout_obj) self._requestor.del_request(self) result = {} # type: Dict[str, Any] result['domain'] = packet.GetOption('domain_name').decode("ascii") translate_ips = { 'yiaddr': 'ip_address', 'subnet_mask': 'subnet_mask', 'router': 'gateway', } for opt_name in translate_ips: if not packet.IsOption(opt_name): continue val = packet.GetOption(opt_name) if len(val) == 4: result[translate_ips[opt_name]] = str(IPv4Address(val)) dns = [] # type: List[str] result['dns'] = dns dns_list = packet.GetOption('domain_name_server') while len(dns_list) >= 4: dns.append(str(IPv4Address(dns_list[:4]))) dns_list = dns_list[4:] if packet.IsOption('classless_static_route'): static_routes = parse_classless_static_routes( list(packet.GetOption('classless_static_route')) ) if static_routes is not None: if 'gateway' in result: # We MUST ignore a regular default route if static routes # are sent. del result['gateway'] # Find and filter out default route (if any). And set it as # the new gateway parameter. result['static_routes'] = [] for network, netmask, gateway in static_routes: if network == '0.0.0.0' and netmask == '0.0.0.0': result['gateway'] = gateway else: result['static_routes'].append((network, netmask, gateway)) del static_routes # Calculate lease timeouts (with RFC T1/T2 if not found in packet) lease_delta = int.from_bytes(packet.GetOption('ip_address_lease_time'), "big") result['lease_timeout'] = self._start_time + lease_delta if packet.IsOption('renewal_time_value'): renewal_delta = int.from_bytes( packet.GetOption('renewal_time_value'), "big" ) else: renewal_delta = int(lease_delta * 0.5) + random.randint(-5, 5) result['renewal_timeout'] = self._start_time + renewal_delta if packet.IsOption('rebinding_time_value'): rebinding_delta = int.from_bytes( packet.GetOption('rebinding_time_value'), "big" ) else: rebinding_delta = int(lease_delta * 0.875) + random.randint(-5, 5) result['rebinding_timeout'] = self._start_time + rebinding_delta self._success_handler(result)
def ack(packet, defaultnbi, msgtype): """ The ack function constructs either a BSDP[LIST] or BSDP[SELECT] ACK DhcpPacket(), determined by the given msgtype, 'list' or 'select'. It calls the previously defined getSysIdEntitlement() and parseOptions() functions for either msgtype. """ bsdpack = DhcpPacket() try: # Get the requesting client's clientsysid and MAC address from the # BSDP options clientsysid = \ str(strlist(packet.GetOption('vendor_class_identifier'))).split('/')[2] clientmacaddr = chaddr_to_mac(packet.GetOption('chaddr')) # Decode and parse the BSDP options from vendor_encapsulated_options bsdpoptions = \ parseOptions(packet.GetOption('vendor_encapsulated_options')) # Figure out the NBIs this clientsysid is entitled to enablednbis = getSysIdEntitlement(nbiimages, clientsysid, clientmacaddr, msgtype) # The Startup Disk preference panel in OS X uses a randomized reply port # instead of the standard port 68. We check for the existence of that # option in the bsdpoptions dict and if found set replyport to it. if 'reply_port' in bsdpoptions: replyport = int( str( format(bsdpoptions['reply_port'][0], 'x') + format(bsdpoptions['reply_port'][1], 'x')), 16) else: replyport = 68 # Get the client's IP address, a standard DHCP option clientip = ipv4(packet.GetOption('ciaddr')) if str(clientip) == '0.0.0.0': clientip = ipv4(packet.GetOption('request_ip_address')) logging.debug( "Did not get a valid clientip, using request_ip_address %s instead" % (str(clientip), )) except: logging.debug("Unexpected error: ack() common %s" % sys.exc_info()[1]) raise #print 'Configuring common BSDP packet options' # We construct the rest of our common BSDP reply parameters according to # Apple's spec. The only noteworthy parameter here is sname, a zero-padded # 64 byte string list containing the BSDP server's hostname. bsdpack.SetOption("op", [2]) bsdpack.SetOption("htype", packet.GetOption('htype')) bsdpack.SetOption("hlen", packet.GetOption('hlen')) bsdpack.SetOption("xid", packet.GetOption('xid')) bsdpack.SetOption("ciaddr", packet.GetOption('ciaddr')) bsdpack.SetOption("siaddr", serverip) bsdpack.SetOption("yiaddr", [0, 0, 0, 0]) bsdpack.SetOption("sname", strlist(serverhostname.ljust(64, '\x00')).list()) bsdpack.SetOption("chaddr", packet.GetOption('chaddr')) bsdpack.SetOption("dhcp_message_type", [5]) bsdpack.SetOption("server_identifier", serverip) bsdpack.SetOption("vendor_class_identifier", strlist('AAPLBSDPC').list()) # Process BSDP[LIST] requests if msgtype == 'list': #print 'Creating LIST packet' try: nameslength = 0 n = 2 # First calculate the total length of the names of all combined # NBIs, a required parameter that is part of the BSDP # vendor_encapsulated_options. for i in enablednbis: nameslength += i['length'] # Next calculate the total length of all enabled NBIs totallength = len(enablednbis) * 5 + nameslength # The bsdpimagelist var is inserted into vendor_encapsulated_options # and comprises of the option code (9), total length of options, # the IDs and names of all NBIs and the 4 byte string list that # contains the default NBI ID. Promise, all of this is part of # the BSDP spec, go look it up. bsdpimagelist = [9, totallength] bsdpimagelist += imagenameslist defaultnbi = '%04X' % defaultnbi # Encode the default NBI option (7) its standard length (4) and the # 16 bit string list representation of defaultnbi defaultnbi = [7,4,129,0] + \ [int(defaultnbi[i:i+n], 16) for i in range(0, len(defaultnbi), n)] if int(defaultnbi[-1:][0]) == 0: hasnulldefault = True else: hasnulldefault = False # To prevent sending a default image ID of 0 (zero) to the client # after the initial INFORM[LIST] request we test for 0 and if # so, skip inserting the defaultnbi BSDP option. Since it is # optional anyway we won't confuse the client. compiledlistpacket = strlist([1, 1, 1, 4, 2, 128, 128]).list() if not hasnulldefault: compiledlistpacket += strlist(defaultnbi).list() compiledlistpacket += strlist(bsdpimagelist).list() # And finally, once we have all the image list encoding taken care # of, we plug them into the vendor_encapsulated_options DHCP # option after the option header: # - [1,1,1] = BSDP message type (1), length (1), value (1 = list) # - [4,2,255,255] = Server priority message type 4, length 2, # value 0xffff (65535 - Highest) # - defaultnbi (option 7) - Optional, not sent if '0' # - List of all available Image IDs (option 9) bsdpack.SetOption("vendor_encapsulated_options", compiledlistpacket) # Some debugging to stdout logging.debug('-=========================================-') logging.debug("Return ACK[LIST] to " + str(clientip) + ' on ' + str(replyport)) if hasnulldefault is False: logging.debug("Default boot image ID: " + str(defaultnbi[2:])) except: logging.debug("Unexpected error ack() list: %s" % sys.exc_info()[1]) raise # Process BSDP[SELECT] requests elif msgtype == 'select': #print 'Creating SELECT packet' # Get the value of selected_boot_image as sent by the client and convert # the value for later use. try: imageid = int( '%02X' % bsdpoptions['selected_boot_image'][2] + '%02X' % bsdpoptions['selected_boot_image'][3], 16) except: logging.debug("Unexpected error ack() select: imageid %s" % sys.exc_info()[1]) raise # Initialize variables for the booter file (kernel) and the dmg path booterfile = '' rootpath = '' selectedimage = '' if nbiurl.hostname[0].isalpha(): basedmgpath = getBaseDmgPath(nbiurl) # Iterate over enablednbis and retrieve the kernel and boot DMG for each try: for nbidict in enablednbis: if nbidict['id'] == imageid: booterfile = nbidict['booter'] rootpath = basedmgpath + nbidict['dmg'] # logging.debug('-->> Using boot image URI: ' + str(rootpath)) selectedimage = bsdpoptions['selected_boot_image'] # logging.debug('ACK[SELECT] image ID: ' + str(selectedimage)) except: logging.debug("Unexpected error ack() selectedimage: %s" % sys.exc_info()[1]) raise # Generate the rest of the BSDP[SELECT] ACK packet by encoding the # name of the kernel (file), the TFTP path and the vendor encapsulated # options: # - [1,1,2] = BSDP message type (1), length (1), value (2 = select) # - [8,4] = BSDP selected_image (8), length (4), encoded image ID try: bsdpack.SetOption("file", strlist(booterfile.ljust(128, '\x00')).list()) bsdpack.SetOption("root_path", strlist(rootpath).list()) bsdpack.SetOption("vendor_encapsulated_options", strlist([1, 1, 2, 8, 4] + selectedimage).list()) except: logging.debug("Unexpected error ack() select SetOption: %s" % sys.exc_info()[1]) raise try: # Some debugging to stdout logging.debug('-=========================================-') logging.debug("Return ACK[SELECT] to " + str(clientip) + ' on ' + str(replyport)) logging.debug( "--> TFTP path: %s\n-->Boot image URI: %s" % (str(strlist(bsdpack.GetOption("file"))), str(rootpath))) except: logging.debug("Unexpected error ack() select print debug: %s" % sys.exc_info()[1]) raise # Return the finished packet, client IP and reply port back to the caller return bsdpack, clientip, replyport