/* this function returns a new handle...or -1 if no socket could be allocated */ internal Int32 CreateSocket(ProtocolType protocolType, Int64 timeoutInMachineTicks, bool reservedSocket = false) { switch (protocolType) { case ProtocolType.Tcp: { int handle = reservedSocket ? GetReservedHandle(timeoutInMachineTicks) : GetNextHandle(timeoutInMachineTicks); if (handle != -1) { _sockets[handle] = new TcpSocket(_tcpHandler, handle); return handle; } else { // no handle available //throw Utility.NewSocketException(SocketError.TooManyOpenSockets); return -1; } } case ProtocolType.Udp: { int handle = reservedSocket ? GetReservedHandle(timeoutInMachineTicks) : GetNextHandle(timeoutInMachineTicks); if (handle != -1) { _sockets[handle] = new UdpSocket(this, handle); return handle; } else { // no handle available //throw Utility.NewSocketException(SocketError.TooManyOpenSockets); return -1; } } default: throw new NotSupportedException(); } }
bool RetrieveAckNak(UdpSocket socket, UInt32 transactionID, UInt64 clientHardwareAddress, ref DhcpOffer dhcpOffer, out bool responseIsAck, Int64 timeoutInMachineTicks) { // set default ack/nak response responseIsAck = false; UInt32 assignedIPAddress; DhcpOption[] options; bool success = RetrieveDhcpMessage(socket, new DhcpMessageType[] { DhcpMessageType.DHCPACK, DhcpMessageType.DHCPNAK }, transactionID, clientHardwareAddress, out assignedIPAddress, out options, timeoutInMachineTicks); if (success == false) { return false; /* timeout */ } // analyze return value and update offer values if necessary for (int iOption = 0; iOption < options.Length; iOption++) { switch (options[iOption].Code) { case DhcpOptionCode.DhcpMessageType: { if (options[iOption].Value.Length >= 1) { responseIsAck = ((DhcpMessageType)options[iOption].Value[0] == DhcpMessageType.DHCPACK); } } break; case DhcpOptionCode.SubnetMask: { if (options[iOption].Value.Length >= 4) { dhcpOffer.SubnetMask = ( (UInt32)(options[iOption].Value[0] << 24) + (UInt32)(options[iOption].Value[1] << 16) + (UInt32)(options[iOption].Value[2] << 8) + (UInt32)(options[iOption].Value[3]) ); } } break; case DhcpOptionCode.Router: { /* NOTE: this option may include multiple router addresses; we select the first entry as our default gateway */ if (options[iOption].Value.Length >= 4) { dhcpOffer.GatewayAddress = ( (UInt32)(options[iOption].Value[0] << 24) + (UInt32)(options[iOption].Value[1] << 16) + (UInt32)(options[iOption].Value[2] << 8) + (UInt32)(options[iOption].Value[3]) ); } } break; case DhcpOptionCode.DomainNameServer: { /* NOTE: this option may include multiple DNS servers; we will only select up to the first two servers as our default DNS servers */ System.Collections.ArrayList dnsServerArrayList = new System.Collections.ArrayList(); for (int iDnsServer = 0; iDnsServer < options[iOption].Value.Length; iDnsServer += 4) { if (options[iOption].Value.Length >= iDnsServer + 4) { UInt32 dnsServerAddress = ( (UInt32)(options[iOption].Value[iDnsServer + 0] << 24) + (UInt32)(options[iOption].Value[iDnsServer + 1] << 16) + (UInt32)(options[iOption].Value[iDnsServer + 2] << 8) + (UInt32)(options[iOption].Value[iDnsServer + 3]) ); dnsServerArrayList.Add(dnsServerAddress); } } dhcpOffer.DnsAddresses = (UInt32[])dnsServerArrayList.ToArray(typeof(UInt32)); } break; case DhcpOptionCode.IPAddressLeaseTime: { if (options[iOption].Value.Length >= 4) { dhcpOffer.LeaseExpirationTimeInSeconds = ( (UInt32)(options[iOption].Value[0] << 24) + (UInt32)(options[iOption].Value[1] << 16) + (UInt32)(options[iOption].Value[2] << 8) + (UInt32)(options[iOption].Value[3]) ); } } break; case DhcpOptionCode.ServerIdentifier: { if (options[iOption].Value.Length >= 4) { dhcpOffer.ServerIdentifier = ( (UInt32)(options[iOption].Value[0] << 24) + (UInt32)(options[iOption].Value[1] << 16) + (UInt32)(options[iOption].Value[2] << 8) + (UInt32)(options[iOption].Value[3]) ); } } break; case DhcpOptionCode.RenewalTimeValue: { if (options[iOption].Value.Length >= 4) { dhcpOffer.LeaseRenewalTimeInSeconds = ( (UInt32)(options[iOption].Value[0] << 24) + (UInt32)(options[iOption].Value[1] << 16) + (UInt32)(options[iOption].Value[2] << 8) + (UInt32)(options[iOption].Value[3]) ); } } break; case DhcpOptionCode.RebindingTimeValue: { if (options[iOption].Value.Length >= 4) { dhcpOffer.LeaseRebindingTimeInSeconds = ( (UInt32)(options[iOption].Value[0] << 24) + (UInt32)(options[iOption].Value[1] << 16) + (UInt32)(options[iOption].Value[2] << 8) + (UInt32)(options[iOption].Value[3]) ); } } break; case DhcpOptionCode.ClientIdentifier: /* NOTE: we ignore this since our hardware address matches, although if we ever need to verify it then we can do so here */ break; } } return true; /* success */ }
bool RetrieveDhcpMessage(UdpSocket socket, DhcpMessageType[] messageTypes, UInt32 transactionID, UInt64 clientHardwareAddress, out UInt32 assignedIPAddress, out DhcpOption[] options, Int64 timeoutInMachineTicks) { byte[] dhcpFrameBuffer = new byte[DHCP_FRAME_BUFFER_LENGTH]; while (timeoutInMachineTicks > Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks) { Int32 bytesReceived = socket.Receive(dhcpFrameBuffer, 0, dhcpFrameBuffer.Length, 0, timeoutInMachineTicks); if (bytesReceived == 0) // timeout break; /* parse our DHCP frame */ // validate the operation if ((BootpOperation)dhcpFrameBuffer[0] != BootpOperation.BOOTREPLY) continue; /* filter out this BOOTP/DHCP frame */ /* verify magic cookie {99, 130, 83, 99} is the first 4-byte entry, as per RFC 1497 */ if ((dhcpFrameBuffer[236] != 99) || (dhcpFrameBuffer[237] != 130) || (dhcpFrameBuffer[238] != 83) || (dhcpFrameBuffer[239] != 99)) continue; /* filter out this BOOTP non-DHCP frame */ // verify that the transaction ID matches UInt32 verifyTransactionID = ( (UInt32)(dhcpFrameBuffer[4] << 24) + (UInt32)(dhcpFrameBuffer[5] << 16) + (UInt32)(dhcpFrameBuffer[6] << 8) + (UInt32)(dhcpFrameBuffer[7]) ); if (transactionID != verifyTransactionID) continue; /* filter out this DHCP frame */ // verify the the physical hardware type matches if (dhcpFrameBuffer[1] != (byte)HARDWARE_TYPE_ETHERNET) continue; /* filter out this DHCP frame */ if (dhcpFrameBuffer[2] != (byte)HARDWARE_ADDRESS_SIZE) continue; /* filter out this DHCP frame */ // verify that the physical address matches UInt64 verifyClientHardwareAddress = ( ((UInt64)(dhcpFrameBuffer[28]) << 40) + ((UInt64)(dhcpFrameBuffer[29]) << 32) + ((UInt64)(dhcpFrameBuffer[30]) << 24) + ((UInt64)(dhcpFrameBuffer[31]) << 16) + ((UInt64)(dhcpFrameBuffer[32]) << 8) + (UInt64)(dhcpFrameBuffer[33]) ); if (clientHardwareAddress != verifyClientHardwareAddress) continue; /* filter out this DHCP frame */ // retrieve allocated ip address /* yiaddr (ip address, populated by server */ assignedIPAddress = ( (UInt32)(dhcpFrameBuffer[16] << 24) + (UInt32)(dhcpFrameBuffer[17] << 16) + (UInt32)(dhcpFrameBuffer[18] << 8) + (UInt32)(dhcpFrameBuffer[19]) ); // retrieve options System.Collections.ArrayList optionsArrayList = new System.Collections.ArrayList(); bool optionOverloadReceived = false; DhcpOptionsBlockRange[] optionsBlocks = new DhcpOptionsBlockRange[] { new DhcpOptionsBlockRange(240, DHCP_FRAME_BUFFER_LENGTH - 240 - 1), }; int optionsBlocksIndex = 0; DhcpMessageType verifyMessageType = 0; while (optionsBlocksIndex < optionsBlocks.Length) { Int32 index = optionsBlocks[optionsBlocksIndex].BeginOffset; bool endOpcodeReceived = false; while (!endOpcodeReceived && index <= optionsBlocks[optionsBlocksIndex].EndOffset) { DhcpOptionCode optionCode = (DhcpOptionCode)dhcpFrameBuffer[index]; switch ((DhcpOptionCode)optionCode) { case DhcpOptionCode.Pad: { index++; } break; case DhcpOptionCode.End: { index++; endOpcodeReceived = true; } break; case DhcpOptionCode.OptionOverload: { if (optionsBlocksIndex == 0) { index++; if (dhcpFrameBuffer[index] != 1) break; index++; byte value = dhcpFrameBuffer[index]; index++; int numBlocks = 1 + (((value & 0x01) == 0x01) ? 1 : 0) + (((value & 0x02) == 0x02) ? 1 : 0); optionsBlocks = new DhcpOptionsBlockRange[numBlocks]; int iOptionBlock = 0; optionsBlocks[iOptionBlock++] = new DhcpOptionsBlockRange(240, DHCP_FRAME_BUFFER_LENGTH - 240 - 1); if ((value & 0x01) == 0x01) { /* use file field for extended options */ optionsBlocks[iOptionBlock++] = new DhcpOptionsBlockRange(108, 235); } if ((value & 0x02) == 0x02) { /* use sname field for extended options */ optionsBlocks[iOptionBlock++] = new DhcpOptionsBlockRange(44, 107); } } } break; default: { index++; byte[] value = new byte[Math.Min(dhcpFrameBuffer[index], DHCP_FRAME_BUFFER_LENGTH - index)]; index++; Array.Copy(dhcpFrameBuffer, index, value, 0, value.Length); index += value.Length; // if the option already exists, append to it bool foundOption = false; for (int iExistingOption = 0; iExistingOption < optionsArrayList.Count; iExistingOption++) { if (((DhcpOption)optionsArrayList[iExistingOption]).Code == optionCode) { byte[] newValue = new byte[((DhcpOption)optionsArrayList[iExistingOption]).Value.Length + value.Length]; Array.Copy(((DhcpOption)optionsArrayList[iExistingOption]).Value, 0, newValue, 0, ((DhcpOption)optionsArrayList[iExistingOption]).Value.Length); Array.Copy(value, 0, newValue, ((DhcpOption)optionsArrayList[iExistingOption]).Value.Length, value.Length); optionsArrayList.RemoveAt(iExistingOption); optionsArrayList.Add(new DhcpOption(optionCode, newValue)); foundOption = true; break; } } if (!foundOption) { optionsArrayList.Add(new DhcpOption(optionCode, value)); } if (optionCode == DhcpOptionCode.DhcpMessageType) { verifyMessageType = (DhcpMessageType)value[0]; } } break; } } optionsBlocksIndex++; } options = (DhcpOption[])optionsArrayList.ToArray(typeof(DhcpOption)); // verify that the DHCP message type matches bool messageTypeMatches = false; for (int iMessageType = 0; iMessageType < messageTypes.Length; iMessageType++) { if (messageTypes[iMessageType] == verifyMessageType) { messageTypeMatches = true; break; } } if (messageTypeMatches) return true; /* message matches the messageTypes filter, with a valid frame; return all data to the caller */ } // if we did not receive a message before timeout, return false. // set default return values assignedIPAddress = 0; options = null; return false; }
void SendDhcpMessage(UdpSocket socket, DhcpMessageType messageType, UInt32 dhcpServerIPAddress, UInt32 transactionID, UInt16 secondsElapsed, UInt32 clientIPAddress, UInt64 clientHardwareAddress, DhcpOption[] options, Int64 timeoutInMachineTicks) { if (_isDisposed) return; byte[] dhcpFrameBuffer = new byte[DHCP_FRAME_BUFFER_LENGTH]; // configure DHCP frame /* op (bootp operation) */ dhcpFrameBuffer[0] = (byte)BootpOperation.BOOTREQUEST; /* htype (hardware type) */ dhcpFrameBuffer[1] = (byte)HARDWARE_TYPE_ETHERNET; /* hlen (hardware address length) */ dhcpFrameBuffer[2] = (byte)HARDWARE_ADDRESS_SIZE; /* hops (bootp relay hops; we should always set this to zero) */ dhcpFrameBuffer[3] = 0; /* xid (transaction id; this should be a randomly-generated number) */ dhcpFrameBuffer[4] = (byte)((transactionID >> 24) & 0xFF); dhcpFrameBuffer[5] = (byte)((transactionID >> 16) & 0xFF); dhcpFrameBuffer[6] = (byte)((transactionID >> 8) & 0xFF); dhcpFrameBuffer[7] = (byte)(transactionID & 0xFF); /* secs (seconds elasped since start of DHCP config acquisition process) */ dhcpFrameBuffer[8] = (byte)((secondsElapsed >> 8) & 0xFF); dhcpFrameBuffer[9] = (byte)(secondsElapsed & 0xFF); /* flags (most significant bit is broadcast flags; all others are zeroes) */ /* some DHCP servers do not process the broadcast flag properly, so we allow all broadcast and unicast packets with our hardwareAddress to pass through to the UDP layer instead */ /* see https://support.microsoft.com/en-us/kb/928233 for more details */ dhcpFrameBuffer[10] = 0x00; // 0x80; dhcpFrameBuffer[11] = 0x00; /* ciaddr (client ip address; only filled in if client can respond to ARP requests and is in BOUND, RENEW or REBINDING state) */ dhcpFrameBuffer[12] = (byte)((clientIPAddress >> 24) & 0xFF); dhcpFrameBuffer[13] = (byte)((clientIPAddress >> 16) & 0xFF); dhcpFrameBuffer[14] = (byte)((clientIPAddress >> 8) & 0xFF); dhcpFrameBuffer[15] = (byte)(clientIPAddress & 0xFF); /* yiaddr (ip address, populated by server; this should always be zero in client requests */ dhcpFrameBuffer[16] = 0; dhcpFrameBuffer[17] = 0; dhcpFrameBuffer[18] = 0; dhcpFrameBuffer[19] = 0; /* siaddr (ip address of next address to use in bootp boot process, populated by server; this should always be zero in client requests */ dhcpFrameBuffer[20] = 0; dhcpFrameBuffer[21] = 0; dhcpFrameBuffer[22] = 0; dhcpFrameBuffer[23] = 0; /* giaddr (ip address of relay agent, populated by relay agents; this should always be zero in client requests */ dhcpFrameBuffer[24] = 0; dhcpFrameBuffer[25] = 0; dhcpFrameBuffer[26] = 0; dhcpFrameBuffer[27] = 0; /* chaddr (client hardware address; we fill in the first 6 bytes with our MAC address) */ dhcpFrameBuffer[28] = (byte)((clientHardwareAddress >> 40) & 0xFF); dhcpFrameBuffer[29] = (byte)((clientHardwareAddress >> 32) & 0xFF); dhcpFrameBuffer[30] = (byte)((clientHardwareAddress >> 24) & 0xFF); dhcpFrameBuffer[31] = (byte)((clientHardwareAddress >> 16) & 0xFF); dhcpFrameBuffer[32] = (byte)((clientHardwareAddress >> 8) & 0xFF); dhcpFrameBuffer[33] = (byte)(clientHardwareAddress & 0xFF); Array.Clear(dhcpFrameBuffer, 34, 10); /* sname (null-terminated server hostname, populated by server; always set to zero in client requests */ Array.Clear(dhcpFrameBuffer, 44, 64); /* file (null-terminated boot file name, populaetd by server; always set to zero in client requests */ Array.Clear(dhcpFrameBuffer, 108, 128); /* options; NOTE: we do support overflowing options into the sname and file fields in this implementation of DHCP. */ /* magic cookie {99, 130, 83, 99} is the first 4-byte entry, as per RFC 1497 */ dhcpFrameBuffer[236] = 99; dhcpFrameBuffer[237] = 130; dhcpFrameBuffer[238] = 83; dhcpFrameBuffer[239] = 99; /* now we fill in the options (starting with the DhcpMessageType, then all of the passed-in options, and then the END option */ dhcpFrameBuffer[240] = (byte)DhcpOptionCode.DhcpMessageType; dhcpFrameBuffer[241] = 1; /* Length */ dhcpFrameBuffer[242] = (byte)messageType; int currentOptionPos = 243; if (options != null) { for (int iOption = 0; iOption < options.Length; iOption++) { /* do not include missing/empty options (or pad) */ if (options[iOption].Code != 0) { // if this option will not fit in the options section, stop processing options. if (currentOptionPos + options.Length + 2 /* size of code + length bytes */ + 1 /* size of END option */ > DHCP_FRAME_BUFFER_LENGTH) break; dhcpFrameBuffer[currentOptionPos++] = (byte)options[iOption].Code; dhcpFrameBuffer[currentOptionPos++] = (byte)options[iOption].Value.Length; Array.Copy(options[iOption].Value, 0, dhcpFrameBuffer, currentOptionPos, options[iOption].Value.Length); currentOptionPos += options[iOption].Value.Length; } } } /* finish with "END" option */ dhcpFrameBuffer[currentOptionPos++] = (byte)DhcpOptionCode.End; Array.Clear(dhcpFrameBuffer, currentOptionPos, DHCP_FRAME_BUFFER_LENGTH - currentOptionPos); // expand frame to word boundary, just in case DHCP servers have troubles processing non-aligned frames. Int32 lengthOfFrame = Math.Min(currentOptionPos + (currentOptionPos % 4 != 0 ? (4 - (currentOptionPos % 4)) : 0), DHCP_FRAME_BUFFER_LENGTH); socket.SendTo(dhcpFrameBuffer, 0, lengthOfFrame, 0, timeoutInMachineTicks, dhcpServerIPAddress, DHCP_SERVER_PORT); }
bool RetrieveDnsResponse(UdpSocket socket, UInt16 transactionID, out DnsResponse dnsResponse, Int64 timeoutInMachineTicks) { byte[] dnsFrameBuffer = new byte[DNS_FRAME_BUFFER_LENGTH]; while (timeoutInMachineTicks > Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks) { Int32 bytesReceived = socket.Receive(dnsFrameBuffer, 0, dnsFrameBuffer.Length, 0, timeoutInMachineTicks); if (bytesReceived == 0) // timeout { break; } /* parse our DNS response */ Int32 bufferIndex = 0; // verify that the transaction ID matches UInt16 verifyTransactionID = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); if (transactionID != verifyTransactionID) continue; /* filter out this DHCP frame */ // Flags UInt16 flags = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); DnsResponseCode responseCode = (DnsResponseCode)(flags & 0x0F); // Query Count UInt16 queryCount = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); // Answer Record Count UInt16 answerRecordCount = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); // Authority Record Count UInt16 authorityRecordCount = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); // Additional Information Record Count UInt16 additionalInformationRecordCount = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); /* parse our query records */ string queryName = ""; DnsRecordType queryType = (DnsRecordType)0; for (int iRecord = 0; iRecord < queryCount; iRecord++) { // Query Name bufferIndex += ParseDnsName(dnsFrameBuffer, bufferIndex, out queryName); queryType = (DnsRecordType)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); UInt16 queryClass = (UInt16)( (UInt16)(dnsFrameBuffer[bufferIndex++] << 8) + (UInt16)(dnsFrameBuffer[bufferIndex++]) ); if (queryClass != DNS_RECORD_CLASS_INTERNET) continue; /* filter out the current query */ } /* parse our answer records */ DnsResourceRecord[] answerRecords = new DnsResourceRecord[answerRecordCount]; for (int iRecord = 0; iRecord < answerRecordCount; iRecord++) { // store answer record bufferIndex += ParseResourceRecord(dnsFrameBuffer, bufferIndex, out answerRecords[iRecord]); } /* parse our authority records */ DnsResourceRecord[] authorityRecords = new DnsResourceRecord[authorityRecordCount]; for (int iRecord = 0; iRecord < authorityRecordCount; iRecord++) { // store authority record bufferIndex += ParseResourceRecord(dnsFrameBuffer, bufferIndex, out authorityRecords[iRecord]); } /* parse our authority records */ DnsResourceRecord[] additionalInformationRecords = new DnsResourceRecord[additionalInformationRecordCount]; for (int iRecord = 0; iRecord < additionalInformationRecordCount; iRecord++) { // store authority record bufferIndex += ParseResourceRecord(dnsFrameBuffer, bufferIndex, out additionalInformationRecords[iRecord]); } dnsResponse = new DnsResponse(responseCode, queryType, queryName, answerRecords, authorityRecords, additionalInformationRecords); return true; } // if we did not receive a message before timeout, return false. dnsResponse = null; return false; }