Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more
Library for ENC28J60 Ethernet modules.

Ported to mbed from Norbert Truchsess's UIPEthernet library for Arduino. Thank you Norbert!
- Full support for persistent (streaming) TCP/IP and UDP connections Client and Server each, ARP, ICMP, DHCP and DNS.
- Works with both Mbed OS 2 and Mbed OS 5.
Usage:
- Import the library into your project.
- Add
#include "UipEthernet.h"tomain.cpp - Create one instance of the UipEthernet class initialized with the MAC address you'd like to use and SPI pins of the connected Mbed board.
Example programs:
Import programWebSwitch_ENC28J60
HTTP Server serving a simple webpage which enables to remotely turn a digital output on/off. Compile, download, run and type 'IP_address/secret/' (don't forget the last '/') into your web browser and hit ENTER.
Import programHTTPServer_Echo_ENC28J60
A simple HTTP server echoing received requests. Ethernet connection is over an ENC28J60 board. Usage: Type the server's IP address into you web browser and hit <ENTER>.
Import programTcpServer_ENC28J60
Simple TCP/IP Server using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programTcpClient_ENC28J60
Simple TCP/IP Client using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programUdpServer_ENC28J60
Simple UDP Server using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programUdpClient_ENC28J60
Simple UDP Client using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programMQTT_Hello_ENC28J60
MQTT Client example program. Ethernet connection is via an ENC28J60 module.
Diff: Dns.cpp
- Revision:
- 0:5350a66d5279
- Child:
- 2:049ce85163c5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Dns.cpp Mon Sep 15 11:12:30 2014 +0000
@@ -0,0 +1,454 @@
+// mbed DNS client for Enc28J60-based Ethernet shield
+// (c) Copyright 2009-2010 MCQN Ltd.
+// Released under Apache License, version 2.0
+#include "Udp.h"
+#include "util.h"
+
+#include "Dns.h"
+#include <string.h>
+//#include <stdlib.h>
+
+#include "mbed.h"
+#include "uip_clock.h"
+
+#define SOCKET_NONE 255
+// Various flags and header field values for a DNS message
+
+#define UDP_HEADER_SIZE 8
+#define DNS_HEADER_SIZE 12
+#define TTL_SIZE 4
+#define QUERY_FLAG (0)
+#define RESPONSE_FLAG (1 << 15)
+#define QUERY_RESPONSE_MASK (1 << 15)
+#define OPCODE_STANDARD_QUERY (0)
+#define OPCODE_INVERSE_QUERY (1 << 11)
+#define OPCODE_STATUS_REQUEST (2 << 11)
+#define OPCODE_MASK (15 << 11)
+#define AUTHORITATIVE_FLAG (1 << 10)
+#define TRUNCATION_FLAG (1 << 9)
+#define RECURSION_DESIRED_FLAG (1 << 8)
+#define RECURSION_AVAILABLE_FLAG (1 << 7)
+#define RESP_NO_ERROR (0)
+#define RESP_FORMAT_ERROR (1)
+#define RESP_SERVER_FAILURE (2)
+#define RESP_NAME_ERROR (3)
+#define RESP_NOT_IMPLEMENTED (4)
+#define RESP_REFUSED (5)
+#define RESP_MASK (15)
+#define TYPE_A (0x0001)
+#define CLASS_IN (0x0001)
+#define LABEL_COMPRESSION_MASK (0xC0)
+// Port number that DNS servers listen on
+
+#define DNS_PORT 53
+
+// Possible return codes from ProcessResponse
+
+#define SUCCESS 1
+#define TIMED_OUT - 1
+#define INVALID_SERVER - 2
+#define TRUNCATED - 3
+#define INVALID_RESPONSE - 4
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void DNSClient::begin(const IPAddress& aDNSServer) {
+ iDNSServer = aDNSServer;
+ iRequestId = 0;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int DNSClient::inet_aton(const char* aIPAddrString, IPAddress& aResult) {
+
+ // See if we've been given a valid IP address
+ const char* p = aIPAddrString;
+ while(*p && ((*p == '.') || (*p >= '0') || (*p <= '9'))) {
+ p++;
+ }
+
+ if(*p == '\0') {
+
+ // It's looking promising, we haven't found any invalid characters
+ p = aIPAddrString;
+
+ int segment = 0;
+ int segmentValue = 0;
+ while(*p && (segment < 4)) {
+ if(*p == '.') {
+
+ // We've reached the end of a segment
+ if(segmentValue > 255) {
+
+ // You can't have IP address segments that don't fit in a byte
+ return 0;
+ }
+ else {
+ aResult[segment] = (uint8_t) segmentValue;
+ segment++;
+ segmentValue = 0;
+ }
+ }
+ else {
+
+ // Next digit
+ segmentValue = (segmentValue * 10) + (*p - '0');
+ }
+
+ p++;
+ }
+
+ // We've reached the end of address, but there'll still be the last
+ // segment to deal with
+ if((segmentValue > 255) || (segment > 3)) {
+
+ // You can't have IP address segments that don't fit in a byte,
+ // or more than four segments
+ return 0;
+ }
+ else {
+ aResult[segment] = (uint8_t) segmentValue;
+ return 1;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) {
+ int ret = 0;
+
+ // See if it's a numeric IP address
+
+ if(inet_aton(aHostname, aResult)) {
+
+ // It is, our work here is done
+ return 1;
+ }
+
+ // Check we've got a valid DNS server to use
+ if(iDNSServer == INADDR_NONE) {
+ return INVALID_SERVER;
+ }
+
+ // Find a socket to use
+ if(iUdp.begin(1024 + (clock_time() & 0xF)) == 1) {
+
+ // Try up to three times
+ int retries = 0;
+ // while ((retries < 3) && (ret <= 0))
+ {
+ // Send DNS request
+ ret = iUdp.beginPacket(iDNSServer, DNS_PORT);
+ if(ret != 0) {
+
+ // Now output the request data
+ ret = BuildRequest(aHostname);
+ if(ret != 0) {
+
+ // And finally send the request
+ ret = iUdp.endPacket();
+ if(ret != 0) {
+
+ // Now wait for a response
+ int wait_retries = 0;
+ ret = TIMED_OUT;
+ while((wait_retries < 3) && (ret == TIMED_OUT)) {
+ ret = ProcessResponse(5000, aResult);
+ wait_retries++;
+ }
+ }
+ }
+ }
+
+ retries++;
+ }
+
+ // We're done with the socket now
+ iUdp.stop();
+ }
+
+ return ret;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t DNSClient::BuildRequest(const char* aName) {
+
+ // Build header
+
+ // 1 1 1 1 1 1
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | ID |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | QDCOUNT |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | ANCOUNT |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | NSCOUNT |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | ARCOUNT |
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // As we only support one request at a time at present, we can simplify
+ // some of this header
+ iRequestId = clock_time(); // generate a random ID
+ uint16_t twoByteBuffer;
+
+ // FIXME We should also check that there's enough space available to write to, rather
+
+ // FIXME than assume there's enough space (as the code does at present)
+ iUdp.write((uint8_t*) &iRequestId, sizeof(iRequestId));
+
+ twoByteBuffer = htons(QUERY_FLAG | OPCODE_STANDARD_QUERY | RECURSION_DESIRED_FLAG);
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ twoByteBuffer = htons(1); // One question record
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ twoByteBuffer = 0; // Zero answer records
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ // and zero additional records
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ // Build question
+ const char* start = aName;
+ const char* end = start;
+ uint8_t len;
+ // Run through the name being requested
+
+ while(*end) {
+
+ // Find out how long this section of the name is
+ end = start;
+ while(*end && (*end != '.')) {
+ end++;
+ }
+
+ if(end - start > 0) {
+
+ // Write out the size of this section
+ len = end - start;
+ iUdp.write(&len, sizeof(len));
+
+ // And then write out the section
+ iUdp.write((uint8_t*)start, end - start);
+ }
+
+ start = end + 1;
+ }
+
+ // We've got to the end of the question name, so
+ // terminate it with a zero-length section
+ len = 0;
+ iUdp.write(&len, sizeof(len));
+
+ // Finally the type and class of question
+ twoByteBuffer = htons(TYPE_A);
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ twoByteBuffer = htons(CLASS_IN); // Internet class of question
+ iUdp.write((uint8_t*) &twoByteBuffer, sizeof(twoByteBuffer));
+
+ // Success! Everything buffered okay
+ return 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) {
+ uint32_t startTime = clock_time();
+
+ // Wait for a response packet
+
+ while(iUdp.parsePacket() <= 0) {
+ if((clock_time() - startTime) > aTimeout)
+ return TIMED_OUT;
+ wait(0.050);
+ }
+
+ // We've had a reply!
+ // Read the UDP header
+ uint8_t header[DNS_HEADER_SIZE]; // Enough space to reuse for the DNS header
+
+ // Check that it's a response from the right server and the right port
+ if((iDNSServer != iUdp.remoteIP()) || (iUdp.remotePort() != DNS_PORT)) {
+
+ // It's not from who we expected
+ return INVALID_SERVER;
+ }
+
+ // Read through the rest of the response
+ if(iUdp.available() < DNS_HEADER_SIZE) {
+ return TRUNCATED;
+ }
+
+ iUdp.read(header, DNS_HEADER_SIZE);
+
+ uint16_t header_flags = htons(*((uint16_t*) &header[2]));
+ // Check that it's a response to this request
+
+ if
+ (
+ (iRequestId != (*((uint16_t*) &header[0])))
+ || ((header_flags & QUERY_RESPONSE_MASK) != (uint16_t) RESPONSE_FLAG)
+ ) {
+
+ // Mark the entire packet as read
+ iUdp.flush();
+ return INVALID_RESPONSE;
+ }
+
+ // Check for any errors in the response (or in our request)
+ // although we don't do anything to get round these
+ if((header_flags & TRUNCATION_FLAG) || (header_flags & RESP_MASK)) {
+
+ // Mark the entire packet as read
+ iUdp.flush();
+ return -5; //INVALID_RESPONSE;
+ }
+
+ // And make sure we've got (at least) one answer
+ uint16_t answerCount = htons(*((uint16_t*) &header[6]));
+ if(answerCount == 0) {
+
+ // Mark the entire packet as read
+ iUdp.flush();
+ return -6; //INVALID_RESPONSE;
+ }
+
+ // Skip over any questions
+ for(uint16_t i = 0; i < htons(*((uint16_t*) &header[4])); i++) {
+
+ // Skip over the name
+ uint8_t len;
+ do
+ {
+ iUdp.read(&len, sizeof(len));
+ if(len > 0) {
+
+ // Don't need to actually read the data out for the string, just
+ // advance ptr to beyond it
+ while(len--) {
+ iUdp.read(); // we don't care about the returned byte
+ }
+ }
+ } while(len != 0);
+
+ // Now jump over the type and class
+ for(int i = 0; i < 4; i++) {
+ iUdp.read(); // we don't care about the returned byte
+ }
+ }
+
+ // Now we're up to the bit we're interested in, the answer
+ // There might be more than one answer (although we'll just use the first
+ // type A answer) and some authority and additional resource records but
+ // we're going to ignore all of them.
+ for(uint16_t i = 0; i < answerCount; i++) {
+
+ // Skip the name
+ uint8_t len;
+ do
+ {
+ iUdp.read(&len, sizeof(len));
+ if((len & LABEL_COMPRESSION_MASK) == 0) {
+
+ // It's just a normal label
+ if(len > 0) {
+
+ // And it's got a length
+ // Don't need to actually read the data out for the string,
+ // just advance ptr to beyond it
+ while(len--) {
+ iUdp.read(); // we don't care about the returned byte
+ }
+ }
+ }
+ else {
+
+ // This is a pointer to a somewhere else in the message for the
+ // rest of the name. We don't care about the name, and RFC1035
+ // says that a name is either a sequence of labels ended with a
+ // 0 length octet or a pointer or a sequence of labels ending in
+ // a pointer. Either way, when we get here we're at the end of
+ // the name
+ // Skip over the pointer
+ iUdp.read(); // we don't care about the returned byte
+
+ // And set len so that we drop out of the name loop
+ len = 0;
+ }
+ } while(len != 0);
+
+ // Check the type and class
+ uint16_t answerType;
+ uint16_t answerClass;
+ iUdp.read((uint8_t*) &answerType, sizeof(answerType));
+ iUdp.read((uint8_t*) &answerClass, sizeof(answerClass));
+
+ // Ignore the Time-To-Live as we don't do any caching
+ for(int i = 0; i < TTL_SIZE; i++) {
+ iUdp.read(); // we don't care about the returned byte
+ }
+
+ // And read out the length of this answer
+ // Don't need header_flags anymore, so we can reuse it here
+ iUdp.read((uint8_t*) &header_flags, sizeof(header_flags));
+
+ if((htons(answerType) == TYPE_A) && (htons(answerClass) == CLASS_IN)) {
+ if(htons(header_flags) != 4) {
+
+ // It's a weird size
+ // Mark the entire packet as read
+ iUdp.flush();
+ return -9; //INVALID_RESPONSE;
+ }
+
+ iUdp.read(aAddress.raw_address(), 4);
+ return SUCCESS;
+ }
+ else {
+
+ // This isn't an answer type we're after, move onto the next one
+ for(uint16_t i = 0; i < htons(header_flags); i++) {
+ iUdp.read(); // we don't care about the returned byte
+ }
+ }
+ }
+
+ // Mark the entire packet as read
+ iUdp.flush();
+
+ // If we get here then we haven't found an answer
+ return -10; //INVALID_RESPONSE;
+}
+
