From 88e858f6e39525cfea264f53e14de9a85010f902 Mon Sep 17 00:00:00 2001 From: amcewen Date: Mon, 10 Jan 2011 14:54:29 +0000 Subject: [PATCH 1/6] Fix for issue 439. UDP API changed to derive from Stream. The old sendPacket and readPacket calls have been removed, and replaced with Stream-derived alternatives which provide more commonality with other communications classes and to allow both buffered and full-packet-at-a-time uses. Also includes the introduction of an IPAddress class to make passing them around easier (and require fewer pointers to be exposed) --- libraries/Ethernet/IPAddress.cpp | 39 +++++ libraries/Ethernet/IPAddress.h | 55 ++++++ libraries/Ethernet/Udp.cpp | 165 +++++++++--------- libraries/Ethernet/Udp.h | 53 ++++-- .../UDPSendReceiveString.pde | 33 ++-- .../examples/UdpNtpClient/UdpNtpClient.pde | 15 +- libraries/Ethernet/keywords.txt | 7 + libraries/Ethernet/utility/socket.cpp | 55 ++++++ libraries/Ethernet/utility/socket.h | 21 +++ libraries/Ethernet/utility/w5100.cpp | 12 +- libraries/Ethernet/utility/w5100.h | 16 +- 11 files changed, 355 insertions(+), 116 deletions(-) create mode 100644 libraries/Ethernet/IPAddress.cpp create mode 100644 libraries/Ethernet/IPAddress.h diff --git a/libraries/Ethernet/IPAddress.cpp b/libraries/Ethernet/IPAddress.cpp new file mode 100644 index 00000000000..389329de0fd --- /dev/null +++ b/libraries/Ethernet/IPAddress.cpp @@ -0,0 +1,39 @@ + +#include +#include + +IPAddress::IPAddress() +{ + memset(_address, 0, sizeof(_address)); +} + +IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +{ + _address[0] = first_octet; + _address[1] = second_octet; + _address[2] = third_octet; + _address[3] = fourth_octet; +} + +IPAddress::IPAddress(uint32_t address) +{ + memcpy(_address, &address, sizeof(_address)); +} + +IPAddress::IPAddress(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); +} + +IPAddress& IPAddress::operator=(const uint8_t *address) +{ + memcpy(_address, address, sizeof(_address)); + return *this; +} + +IPAddress& IPAddress::operator=(uint32_t address) +{ + memcpy(_address, (const uint8_t *)&address, sizeof(_address)); + return *this; +} + diff --git a/libraries/Ethernet/IPAddress.h b/libraries/Ethernet/IPAddress.h new file mode 100644 index 00000000000..586385320aa --- /dev/null +++ b/libraries/Ethernet/IPAddress.h @@ -0,0 +1,55 @@ +/* + * + * MIT License: + * Copyright (c) 2011 Adrian McEwen + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * adrianm@mcqn.com 1/1/2011 + */ + +#ifndef IPAddress_h +#define IPAddress_h + +// A class to make it easier to handle and pass around IP addresses + +class IPAddress { +private: + uint8_t _address[4]; // IPv4 address + +public: + // Constructors + IPAddress(); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint32_t address); + IPAddress(const uint8_t *address); + + // Overloaded cast operator to allow IPAddress objects to be used where a pointer + // to a four-byte uint8_t array is expected + operator uint8_t*() { return _address; }; + + // Overloaded index operator to allow getting and setting individual octets of the address + uint8_t operator[](int index) const { return _address[index]; }; + uint8_t& operator[](int index) { return _address[index]; }; + + // Overloaded copy operators to allow initialisation of IPAddress objects from other types + IPAddress& operator=(const uint8_t *address); + IPAddress& operator=(uint32_t address); +}; + +#endif diff --git a/libraries/Ethernet/Udp.cpp b/libraries/Ethernet/Udp.cpp index 07c454df228..6ad8aca7ebe 100644 --- a/libraries/Ethernet/Udp.cpp +++ b/libraries/Ethernet/Udp.cpp @@ -56,106 +56,111 @@ uint8_t UDP::begin(uint16_t port) { return 1; } -/* Send packet contained in buf of length len to peer at specified ip, and port */ -/* Use this function to transmit binary data that might contain 0x00 bytes*/ -/* This function returns sent data size for success else -1. */ -uint16_t UDP::sendPacket(uint8_t * buf, uint16_t len, uint8_t * ip, uint16_t port){ - return sendto(_sock,(const uint8_t *)buf,len,ip,port); -} - -/* Send zero-terminated string str as packet to peer at specified ip, and port */ -/* This function returns sent data size for success else -1. */ -uint16_t UDP::sendPacket(const char str[], uint8_t * ip, uint16_t port){ - // compute strlen - const char *s; - for(s = str; *s; ++s); - uint16_t len = (s-str); - // send packet - return sendto(_sock,(const uint8_t *)str,len,ip,port); -} /* Is data available in rx buffer? Returns 0 if no, number of available bytes if yes. * returned value includes 8 byte UDP header!*/ int UDP::available() { return W5100.getRXReceivedSize(_sock); } +/* Release any resources being used by this UDP instance */ +void UDP::stop() +{ + if (_sock == MAX_SOCK_NUM) + return; -/* Read a received packet into buffer buf (which is of maximum length len); */ -/* store calling ip and port as well. Call available() to make sure data is ready first. */ -/* NOTE: I don't believe len is ever checked in implementation of recvfrom(),*/ -/* so it's easy to overflow buffer. so we check and truncate. */ -/* returns number of bytes read, or negative number of bytes we would have needed if we truncated */ -int UDP::readPacket(uint8_t * buf, uint16_t bufLen, uint8_t *ip, uint16_t *port) { - int packetLen = available()-8; //skip UDP header; - if(packetLen < 0 ) return 0; // no real data here - if(packetLen > (int)bufLen) { - //packet is too large - truncate - //HACK - hand-parse the UDP packet using TCP recv method - uint8_t tmpBuf[8]; - int i; - //read 8 header bytes and get IP and port from it - recv(_sock,tmpBuf,8); - ip[0] = tmpBuf[0]; - ip[1] = tmpBuf[1]; - ip[2] = tmpBuf[2]; - ip[3] = tmpBuf[3]; - *port = tmpBuf[4]; - *port = (*port << 8) + tmpBuf[5]; - - //now copy first (bufLen) bytes into buf - for(i=0;i<(int)bufLen;i++) { - recv(_sock,tmpBuf,1); - buf[i]=tmpBuf[0]; - } + close(_sock); - //and just read the rest byte by byte and throw it away - while(available()) { - recv(_sock,tmpBuf,1); - } + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; +} - return (-1*packetLen); +int UDP::beginPacket(IPAddress ip, uint16_t port) +{ + _offset = 0; + return startUDP(_sock, ip, port); +} - //ALTERNATIVE: requires stdlib - takes a bunch of space - /*//create new buffer and read everything into it - uint8_t * tmpBuf = (uint8_t *)malloc(packetLen); - recvfrom(_sock,tmpBuf,packetLen,ip,port); - if(!tmpBuf) return 0; //couldn't allocate - // copy first bufLen bytes - for(unsigned int i=0; i 0) + { + _remoteIP = tmpBuf; + _remotePort = tmpBuf[4]; + _remotePort = (_remotePort << 8) + tmpBuf[5]; + // When we get here, any remaining bytes are the data + ret = available(); + } + return ret; } -/* Release any resources being used by this UDP instance */ -void UDP::stop() +int UDP::read() { - if (_sock == MAX_SOCK_NUM) - return; + uint8_t byte; + if (recv(_sock, &byte, 1) > 0) + { + // We read things without any problems + return byte; + } + // If we get here, there's no data available + return -1; +} - close(_sock); +int UDP::read(unsigned char* buffer, size_t len) +{ + /* In the readPacket that copes with truncating packets, the buffer was + filled with this code. Not sure why it loops round reading out a byte + at a time. + int i; + for(i=0;i<(int)bufLen;i++) { + recv(_sock,tmpBuf,1); + buf[i]=tmpBuf[0]; + } + */ + return recv(_sock, buffer, len); +} - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; +int UDP::peek() +{ + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must + if (!available()) + return -1; + ::peek(_sock, &b); + return b; +} + +void UDP::flush() +{ + while (available()) + { + read(); + } } diff --git a/libraries/Ethernet/Udp.h b/libraries/Ethernet/Udp.h index c801ee277cd..3970a9662d6 100644 --- a/libraries/Ethernet/Udp.h +++ b/libraries/Ethernet/Udp.h @@ -37,28 +37,57 @@ #ifndef udp_h #define udp_h +#include +#include + #define UDP_TX_PACKET_MAX_SIZE 24 -class UDP { +class UDP : public Stream { private: uint8_t _sock; // socket ID for Wiz5100 uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + uint16_t _offset; // offset into the packet being sent public: - UDP(); + UDP(); // Constructor uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use - int available(); // has data been received? + void stop(); // Finish with the UDP socket + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + int beginPacket(IPAddress ip, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPacket(); + // Write a single byte into the packet + virtual void write(uint8_t); + // Write a string of characters into the packet + virtual void write(const char *str); + // Write size bytes from buffer into the packet + virtual void write(const uint8_t *buffer, size_t size); - // C-style buffer-oriented functions - uint16_t sendPacket(uint8_t *, uint16_t, uint8_t *, uint16_t); //send a packet to specified peer - uint16_t sendPacket(const char[], uint8_t *, uint16_t); //send a string as a packet to specified peer - int readPacket(uint8_t *, uint16_t); // read a received packet - int readPacket(uint8_t *, uint16_t, uint8_t *, uint16_t *); // read a received packet, also return sender's ip and port - // readPacket that fills a character string buffer - int readPacket(char *, uint16_t, uint8_t *, uint16_t &); + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len); + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet - // Finish with the UDP socket - void stop(); + // Return the IP address of the host who sent the current incoming packet + IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + uint16_t remotePort() { return _remotePort; }; }; #endif diff --git a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde index f27290f5424..e234101397a 100644 --- a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde +++ b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde @@ -22,15 +22,10 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; // local port to listen on -// the next two variables are set when a packet is received -byte remoteIp[4]; // holds received packet's originating IP -unsigned int remotePort; // holds received packet's originating port - // buffers for receiving and sending data char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, char ReplyBuffer[] = "acknowledged"; // a string to send back @@ -48,19 +43,33 @@ void setup() { void loop() { // if there's data available, read a packet - int packetSize = Udp.available(); // note that this includes the UDP header + int packetSize = Udp.parsePacket(); if(packetSize) { - packetSize = packetSize - 8; // subtract the 8 byte header Serial.print("Received packet of size "); Serial.println(packetSize); - - // read the packet into packetBufffer and get the senders IP addr and port number - Udp.readPacket(packetBuffer,UDP_TX_PACKET_MAX_SIZE, remoteIp, remotePort); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBufffer + Udp.read((byte*)packetBuffer,UDP_TX_PACKET_MAX_SIZE); Serial.println("Contents:"); Serial.println(packetBuffer); - Udp.sendPacket( ReplyBuffer, remoteIp, remotePort); + // send a reply, to the IP address and port that sent us the packet we received + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write(ReplyBuffer); + Udp.endPacket(); } delay(10); } diff --git a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde index 22bc754c566..cfb5a2bc1d2 100644 --- a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde +++ b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde @@ -24,14 +24,12 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; // local port to listen for UDP packets -byte timeServer[] = { - 192, 43, 244, 18}; // time.nist.gov NTP server +IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message @@ -55,8 +53,9 @@ void loop() // wait to see if a reply is available delay(1000); - if ( Udp.available() ) { - Udp.readPacket(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer + if ( Udp.parsePacket() ) { + // We've received a packet, read the data from it + Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: @@ -118,7 +117,9 @@ unsigned long sendNTPpacket(byte *address) // all NTP fields have been given values, now // you can send a packet requesting a timestamp: - Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE, address, 123); //NTP requests are to port 123 + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer,NTP_PACKET_SIZE); + Udp.endPacket(); } diff --git a/libraries/Ethernet/keywords.txt b/libraries/Ethernet/keywords.txt index ebc57936394..7fdcedf090e 100644 --- a/libraries/Ethernet/keywords.txt +++ b/libraries/Ethernet/keywords.txt @@ -9,6 +9,7 @@ Ethernet KEYWORD1 Client KEYWORD1 Server KEYWORD1 +IPAddress KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -19,10 +20,16 @@ connect KEYWORD2 write KEYWORD2 available KEYWORD2 read KEYWORD2 +peek KEYWORD2 flush KEYWORD2 stop KEYWORD2 connected KEYWORD2 begin KEYWORD2 +beginPacket KEYWORD2 +endPacket KEYWORD2 +parsePacket KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/libraries/Ethernet/utility/socket.cpp b/libraries/Ethernet/utility/socket.cpp index cad54a5e379..487584511e4 100644 --- a/libraries/Ethernet/utility/socket.cpp +++ b/libraries/Ethernet/utility/socket.cpp @@ -344,3 +344,58 @@ uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len) return ret; } +uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len) +{ + uint16_t ret =0; + if (len > W5100.getTXFreeSize(s)) + { + ret = W5100.getTXFreeSize(s); // check size not to exceed MAX size. + } + else + { + ret = len; + } + W5100.send_data_processing_offset(s, offset, buf, ret); + return ret; +} + +int startUDP(SOCKET s, uint8_t* addr, uint16_t port) +{ + if + ( + ((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) || + ((port == 0x00)) + ) + { + return 0; + } + else + { + W5100.writeSnDIPR(s, addr); + W5100.writeSnDPORT(s, port); + return 1; + } +} + +int sendUDP(SOCKET s) +{ + W5100.execCmdSn(s, Sock_SEND); + + /* +2008.01 bj */ + while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) + { + if (W5100.readSnIR(s) & SnIR::TIMEOUT) + { + /* +2008.01 [bj]: clear interrupt */ + W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT)); + return 0; + } + } + + /* +2008.01 bj */ + W5100.writeSnIR(s, SnIR::SEND_OK); + + /* Sent ok */ + return 1; +} + diff --git a/libraries/Ethernet/utility/socket.h b/libraries/Ethernet/utility/socket.h index 48e2d877b73..37ea93f3d75 100755 --- a/libraries/Ethernet/utility/socket.h +++ b/libraries/Ethernet/utility/socket.h @@ -16,5 +16,26 @@ extern uint16_t recvfrom(SOCKET s, uint8_t * buf, uint16_t len, uint8_t * addr, extern uint16_t igmpsend(SOCKET s, const uint8_t * buf, uint16_t len); +// Functions to allow buffered UDP send (i.e. where the UDP datagram is built up over a +// number of calls before being sent +/* + @brief This function sets up a UDP datagram, the data for which will be provided by one + or more calls to bufferData and then finally sent with sendUDP. + @return 1 if the datagram was successfully set up, or 0 if there was an error +*/ +extern int startUDP(SOCKET s, uint8_t* addr, uint16_t port); +/* + @brief This function copies up to len bytes of data from buf into a UDP datagram to be + sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls. + @return Number of bytes successfully buffered +*/ +uint16_t bufferData(SOCKET s, uint16_t offset, const uint8_t* buf, uint16_t len); +/* + @brief Send a UDP datagram built up from a sequence of startUDP followed by one or more + calls to bufferData. + @return 1 if the datagram was successfully sent, or 0 if there was an error +*/ +int sendUDP(SOCKET s); + #endif /* _SOCKET_H_ */ diff --git a/libraries/Ethernet/utility/w5100.cpp b/libraries/Ethernet/utility/w5100.cpp index aaf3071f7d0..aafabaeec7b 100644 --- a/libraries/Ethernet/utility/w5100.cpp +++ b/libraries/Ethernet/utility/w5100.cpp @@ -65,10 +65,16 @@ uint16_t W5100Class::getRXReceivedSize(SOCKET s) } -void W5100Class::send_data_processing(SOCKET s, uint8_t *data, uint16_t len) +void W5100Class::send_data_processing(SOCKET s, const uint8_t *data, uint16_t len) { - uint16_t ptr = readSnTX_WR(s); + // This is same as having no offset in a call to send_data_processing_offset + send_data_processing_offset(s, 0, data, len); +} +void W5100Class::send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len) +{ + uint16_t ptr = readSnTX_WR(s); + ptr += data_offset; uint16_t offset = ptr & SMASK; uint16_t dstAddr = offset + SBASE[s]; @@ -132,7 +138,7 @@ uint8_t W5100Class::write(uint16_t _addr, uint8_t _data) return 1; } -uint16_t W5100Class::write(uint16_t _addr, uint8_t *_buf, uint16_t _len) +uint16_t W5100Class::write(uint16_t _addr, const uint8_t *_buf, uint16_t _len) { for (int i=0; i<_len; i++) { diff --git a/libraries/Ethernet/utility/w5100.h b/libraries/Ethernet/utility/w5100.h index 118e5d80d43..9872c7ccb09 100755 --- a/libraries/Ethernet/utility/w5100.h +++ b/libraries/Ethernet/utility/w5100.h @@ -146,7 +146,19 @@ class W5100Class { * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer * register. User should read upper byte first and lower byte later to get proper value. */ - void send_data_processing(SOCKET s, uint8_t *data, uint16_t len); + void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); + /** + * @brief A copy of send_data_processing that uses the provided ptr for the + * write offset. Only needed for the "streaming" UDP API, where + * a single UDP packet is built up over a number of calls to + * send_data_processing_ptr, because TX_WR doesn't seem to get updated + * correctly in those scenarios + * @param ptr value to use in place of TX_WR. If 0, then the value is read + * in from TX_WR + * @return New value for ptr, to be used in the next call + */ +// FIXME Update documentation + void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); /** * @brief This function is being called by recv() also. @@ -182,7 +194,7 @@ class W5100Class { // --------------- private: static uint8_t write(uint16_t _addr, uint8_t _data); - static uint16_t write(uint16_t addr, uint8_t *buf, uint16_t len); + static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len); static uint8_t read(uint16_t addr); static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len); From 5caad5bdb4492c23f553ec94bd100ff78e87604f Mon Sep 17 00:00:00 2001 From: amcewen Date: Thu, 13 Jan 2011 17:55:08 +0000 Subject: [PATCH 2/6] Added a method to read data into a char buffer so that character-based (rather than byte-based) operations don't require a cast. As requested by Tom Igoe. Part of the fix to issue 439. --- libraries/Ethernet/Udp.h | 3 +++ .../examples/UDPSendReceiveString/UDPSendReceiveString.pde | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/Ethernet/Udp.h b/libraries/Ethernet/Udp.h index 3970a9662d6..8cd3b065a1b 100644 --- a/libraries/Ethernet/Udp.h +++ b/libraries/Ethernet/Udp.h @@ -80,6 +80,9 @@ class UDP : public Stream { // Read up to len bytes from the current packet and place them into buffer // Returns the number of bytes read, or 0 if none are available virtual int read(unsigned char* buffer, size_t len); + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); }; // Return the next byte from the current packet without moving on to the next byte virtual int peek(); virtual void flush(); // Finish reading the current packet diff --git a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde index e234101397a..081d6911485 100644 --- a/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde +++ b/libraries/Ethernet/examples/UDPSendReceiveString/UDPSendReceiveString.pde @@ -62,7 +62,7 @@ void loop() { Serial.println(Udp.remotePort()); // read the packet into packetBufffer - Udp.read((byte*)packetBuffer,UDP_TX_PACKET_MAX_SIZE); + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); Serial.println("Contents:"); Serial.println(packetBuffer); From a42dc0b455513b9cf3127b9720cc66d0db274b03 Mon Sep 17 00:00:00 2001 From: amcewen Date: Sun, 16 Jan 2011 20:11:50 +0000 Subject: [PATCH 3/6] Fix for issue 62, adding DHCP support. New begin() method added to EthernetClass which takes just a MAC address and gets the rest of its configuration information via DHCP. Examples updated to use the IPAddress class and some have been changed to get their config via DHCP. --- libraries/Ethernet/Client.cpp | 4 +- libraries/Ethernet/Client.h | 6 +- libraries/Ethernet/Dhcp.cpp | 341 ++++++++++++++++++ libraries/Ethernet/Dhcp.h | 158 ++++++++ libraries/Ethernet/Ethernet.cpp | 77 +++- libraries/Ethernet/Ethernet.h | 18 +- libraries/Ethernet/IPAddress.cpp | 5 + libraries/Ethernet/IPAddress.h | 15 +- libraries/Ethernet/Udp.cpp | 2 +- .../BarometricPressureWebServer.pde | 9 +- .../examples/ChatServer/ChatServer.pde | 8 +- .../examples/PachubeClient/PachubeClient.pde | 21 +- .../PachubeClientString.pde | 12 +- .../examples/TelnetClient/TelnetClient.pde | 6 +- .../examples/UdpNtpClient/UdpNtpClient.pde | 19 +- .../Ethernet/examples/WebClient/WebClient.pde | 16 +- .../Ethernet/examples/WebServer/WebServer.pde | 4 +- libraries/Ethernet/util.h | 13 + 18 files changed, 661 insertions(+), 73 deletions(-) create mode 100755 libraries/Ethernet/Dhcp.cpp create mode 100755 libraries/Ethernet/Dhcp.h create mode 100644 libraries/Ethernet/util.h diff --git a/libraries/Ethernet/Client.cpp b/libraries/Ethernet/Client.cpp index 96d5c7eedbd..7ae55d08d5f 100644 --- a/libraries/Ethernet/Client.cpp +++ b/libraries/Ethernet/Client.cpp @@ -16,7 +16,7 @@ uint16_t Client::_srcport = 1024; Client::Client(uint8_t sock) : _sock(sock) { } -Client::Client(uint8_t *ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) { +Client::Client(IPAddress& ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) { } uint8_t Client::connect() { @@ -38,7 +38,7 @@ uint8_t Client::connect() { if (_srcport == 0) _srcport = 1024; socket(_sock, SnMR::TCP, _srcport, 0); - if (!::connect(_sock, _ip, _port)) { + if (!::connect(_sock, _ip.raw_address(), _port)) { _sock = MAX_SOCK_NUM; return 0; } diff --git a/libraries/Ethernet/Client.h b/libraries/Ethernet/Client.h index 8725f158a9c..e92e90e33f1 100644 --- a/libraries/Ethernet/Client.h +++ b/libraries/Ethernet/Client.h @@ -7,8 +7,8 @@ class Client : public Stream { public: Client(); - Client(uint8_t); - Client(uint8_t *, uint16_t); + Client(uint8_t sock); + Client(IPAddress& ip, uint16_t port); uint8_t status(); uint8_t connect(); @@ -31,7 +31,7 @@ class Client : public Stream { private: static uint16_t _srcport; uint8_t _sock; - uint8_t *_ip; + IPAddress _ip; uint16_t _port; }; diff --git a/libraries/Ethernet/Dhcp.cpp b/libraries/Ethernet/Dhcp.cpp new file mode 100755 index 00000000000..8264b435bd1 --- /dev/null +++ b/libraries/Ethernet/Dhcp.cpp @@ -0,0 +1,341 @@ +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#include "w5100.h" + +#include +#include +#include "Dhcp.h" +#include "wiring.h" +#include "util.h" + +int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + uint8_t dhcp_state = STATE_DHCP_START; + uint8_t messageType = 0; + + // zero out _dhcpMacAddr, _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp + memset(_dhcpMacAddr, 0, 26); + + memcpy((void*)_dhcpMacAddr, (void*)mac, 6); + + // Pick an initial transaction ID + _dhcpTransactionId = random(1UL, 2000UL); + _dhcpInitialTransactionId = _dhcpTransactionId; + + if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) + { + // Couldn't get a socket + return 0; + } + + presend_DHCP(); + + int result = 0; + + unsigned long startTime = millis(); + + while(dhcp_state != STATE_DHCP_LEASED) + { + if(dhcp_state == STATE_DHCP_START) + { + _dhcpTransactionId++; + + send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - startTime) / 1000)); + dhcp_state = STATE_DHCP_DISCOVER; + } + else if(dhcp_state == STATE_DHCP_DISCOVER) + { + uint32_t respId; + messageType = parseDHCPResponse(responseTimeout, respId); + if(messageType == DHCP_OFFER) + { + // We'll use the transaction ID that the offer came with, + // rather than the one we were up to + _dhcpTransactionId = respId; + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime) / 1000)); + dhcp_state = STATE_DHCP_REQUEST; + } + } + else if(dhcp_state == STATE_DHCP_REQUEST) + { + uint32_t respId; + messageType = parseDHCPResponse(responseTimeout, respId); + if(messageType == DHCP_ACK) + { + dhcp_state = STATE_DHCP_LEASED; + result = 1; + } + else if(messageType == DHCP_NAK) + dhcp_state = STATE_DHCP_START; + } + + if(messageType == 255) + { + messageType = 0; + dhcp_state = STATE_DHCP_START; + } + + if(result != 1 && ((millis() - startTime) > timeout)) + break; + } + + // We're done with the socket now + _dhcpUdpSocket.stop(); + _dhcpTransactionId++; + + return result; +} + +void DhcpClass::presend_DHCP() +{ +} + +void DhcpClass::send_DHCP_MESSAGE(uint8_t messageType, uint16_t secondsElapsed) +{ + uint8_t buffer[32]; + memset(buffer, 0, 32); + IPAddress dest_addr( 255, 255, 255, 255 ); // Broadcast address + + if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) + { + // FIXME Need to return errors + return; + } + + buffer[0] = DHCP_BOOTREQUEST; // op + buffer[1] = DHCP_HTYPE10MB; // htype + buffer[2] = DHCP_HLENETHERNET; // hlen + buffer[3] = DHCP_HOPS; // hops + + // xid + unsigned long xid = htonl(_dhcpTransactionId); + memcpy(buffer + 4, &(xid), 4); + + // 8, 9 - seconds elapsed + buffer[8] = ((secondsElapsed & 0xff00) >> 8); + buffer[9] = (secondsElapsed & 0x00ff); + + // flags + unsigned short flags = htons(DHCP_FLAGSBROADCAST); + memcpy(buffer + 10, &(flags), 2); + + // ciaddr: already zeroed + // yiaddr: already zeroed + // siaddr: already zeroed + // giaddr: already zeroed + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 28); + + memset(buffer, 0, 32); // clear local buffer + + memcpy(buffer, _dhcpMacAddr, 6); // chaddr + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 16); + + memset(buffer, 0, 32); // clear local buffer + + // leave zeroed out for sname && file + // put in W5100 transmit buffer x 6 (192 bytes) + + for(int i = 0; i < 6; i++) { + _dhcpUdpSocket.write(buffer, 32); + } + + // OPT - Magic Cookie + buffer[0] = (uint8_t)((MAGIC_COOKIE >> 24)& 0xFF); + buffer[1] = (uint8_t)((MAGIC_COOKIE >> 16)& 0xFF); + buffer[2] = (uint8_t)((MAGIC_COOKIE >> 8)& 0xFF); + buffer[3] = (uint8_t)(MAGIC_COOKIE& 0xFF); + + // OPT - message type + buffer[4] = dhcpMessageType; + buffer[5] = 0x01; + buffer[6] = messageType; //DHCP_REQUEST; + + // OPT - client identifier + buffer[7] = dhcpClientIdentifier; + buffer[8] = 0x07; + buffer[9] = 0x01; + memcpy(buffer + 10, _dhcpMacAddr, 6); + + // OPT - host name + buffer[16] = hostName; + buffer[17] = strlen(HOST_NAME) + 3; // length of hostname + last 3 bytes of mac address + strcpy((char*)&(buffer[18]), HOST_NAME); + + buffer[24] = _dhcpMacAddr[3]; + buffer[25] = _dhcpMacAddr[4]; + buffer[26] = _dhcpMacAddr[5]; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 27); + + if(messageType == DHCP_REQUEST) + { + buffer[0] = dhcpRequestedIPaddr; + buffer[1] = 0x04; + buffer[2] = _dhcpLocalIp[0]; + buffer[3] = _dhcpLocalIp[1]; + buffer[4] = _dhcpLocalIp[2]; + buffer[5] = _dhcpLocalIp[3]; + + buffer[6] = dhcpServerIdentifier; + buffer[7] = 0x04; + buffer[8] = _dhcpDhcpServerIp[0]; + buffer[9] = _dhcpDhcpServerIp[1]; + buffer[10] = _dhcpDhcpServerIp[2]; + buffer[11] = _dhcpDhcpServerIp[3]; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 12); + } + + buffer[0] = dhcpParamRequest; + buffer[1] = 0x06; + buffer[2] = subnetMask; + buffer[3] = routersOnSubnet; + buffer[4] = dns; + buffer[5] = domainName; + buffer[6] = dhcpT1value; + buffer[7] = dhcpT2value; + buffer[8] = endOption; + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 9); + + _dhcpUdpSocket.endPacket(); +} + +uint8_t DhcpClass::parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId) +{ + uint8_t type = 0; + uint8_t opt_len = 0; + + unsigned long startTime = millis(); + + while(_dhcpUdpSocket.parsePacket() <= 0) + { + if((millis() - startTime) > responseTimeout) + { + return 255; + } + delay(50); + } + // start reading in the packet + RIP_MSG_FIXED fixedMsg; + _dhcpUdpSocket.read((uint8_t*)&fixedMsg, sizeof(RIP_MSG_FIXED)); + + if(fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) + { + transactionId = ntohl(fixedMsg.xid); + if(memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || (transactionId < _dhcpInitialTransactionId) || (transactionId > _dhcpTransactionId)) + { + // Need to read the rest of the packet here regardless + _dhcpUdpSocket.flush(); + return 0; + } + + memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); + + // Skip to the option part + // Doing this a byte at a time so we don't have to put a big buffer + // on the stack (as we don't have lots of memory lying around) + for (int i =0; i < (240 - sizeof(RIP_MSG_FIXED)); i++) + { + _dhcpUdpSocket.read(); // we don't care about the returned byte + } + + while (_dhcpUdpSocket.available() > 0) + { + switch (_dhcpUdpSocket.read()) + { + case endOption : + break; + + case padOption : + break; + + case dhcpMessageType : + opt_len = _dhcpUdpSocket.read(); + type = _dhcpUdpSocket.read(); + break; + + case subnetMask : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpSubnetMask, 4); + break; + + case routersOnSubnet : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpGatewayIp, 4); + break; + + case dns : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); + break; + + case dhcpServerIdentifier : + opt_len = _dhcpUdpSocket.read(); + if( *((uint32_t*)_dhcpDhcpServerIp) == 0 || + IPAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP() ) + { + _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); + } + else + { + // Skip over the rest of this option + while (opt_len--) + { + _dhcpUdpSocket.read(); + } + } + break; + + case dhcpIPaddrLeaseTime : + default : + opt_len = _dhcpUdpSocket.read(); + // Skip over the rest of this option + while (opt_len--) + { + _dhcpUdpSocket.read(); + } + break; + } + } + } + + // Need to skip to end of the packet regardless here + _dhcpUdpSocket.flush(); + + return type; +} + +IPAddress DhcpClass::getLocalIp() +{ + return IPAddress(_dhcpLocalIp); +} + +IPAddress DhcpClass::getSubnetMask() +{ + return IPAddress(_dhcpSubnetMask); +} + +IPAddress DhcpClass::getGatewayIp() +{ + return IPAddress(_dhcpGatewayIp); +} + +IPAddress DhcpClass::getDhcpServerIp() +{ + return IPAddress(_dhcpDhcpServerIp); +} + +IPAddress DhcpClass::getDnsServerIp() +{ + return IPAddress(_dhcpDnsServerIp); +} + diff --git a/libraries/Ethernet/Dhcp.h b/libraries/Ethernet/Dhcp.h new file mode 100755 index 00000000000..c0034940c27 --- /dev/null +++ b/libraries/Ethernet/Dhcp.h @@ -0,0 +1,158 @@ +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#ifndef Dhcp_h +#define Dhcp_h + +#include "Udp.h" + +/* DHCP state machine. */ +#define STATE_DHCP_START 0 +#define STATE_DHCP_DISCOVER 1 +#define STATE_DHCP_REQUEST 2 +#define STATE_DHCP_LEASED 3 +#define STATE_DHCP_REREQUEST 4 +#define STATE_DHCP_RELEASE 5 + +#define DHCP_FLAGSBROADCAST 0x8000 + +/* UDP port numbers for DHCP */ +#define DHCP_SERVER_PORT 67 /* from server to client */ +#define DHCP_CLIENT_PORT 68 /* from client to server */ + +/* DHCP message OP code */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* DHCP message type */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +#define DHCP_HTYPE10MB 1 +#define DHCP_HTYPE100MB 2 + +#define DHCP_HLENETHERNET 6 +#define DHCP_HOPS 0 +#define DHCP_SECS 0 + +#define MAGIC_COOKIE 0x63825363 +#define MAX_DHCP_OPT 16 + +#define HOST_NAME "WIZnet" + +enum +{ + padOption = 0, + subnetMask = 1, + timerOffset = 2, + routersOnSubnet = 3, + /* timeServer = 4, + nameServer = 5,*/ + dns = 6, + /*logServer = 7, + cookieServer = 8, + lprServer = 9, + impressServer = 10, + resourceLocationServer = 11,*/ + hostName = 12, + /*bootFileSize = 13, + meritDumpFile = 14,*/ + domainName = 15, + /*swapServer = 16, + rootPath = 17, + extentionsPath = 18, + IPforwarding = 19, + nonLocalSourceRouting = 20, + policyFilter = 21, + maxDgramReasmSize = 22, + defaultIPTTL = 23, + pathMTUagingTimeout = 24, + pathMTUplateauTable = 25, + ifMTU = 26, + allSubnetsLocal = 27, + broadcastAddr = 28, + performMaskDiscovery = 29, + maskSupplier = 30, + performRouterDiscovery = 31, + routerSolicitationAddr = 32, + staticRoute = 33, + trailerEncapsulation = 34, + arpCacheTimeout = 35, + ethernetEncapsulation = 36, + tcpDefaultTTL = 37, + tcpKeepaliveInterval = 38, + tcpKeepaliveGarbage = 39, + nisDomainName = 40, + nisServers = 41, + ntpServers = 42, + vendorSpecificInfo = 43, + netBIOSnameServer = 44, + netBIOSdgramDistServer = 45, + netBIOSnodeType = 46, + netBIOSscope = 47, + xFontServer = 48, + xDisplayManager = 49,*/ + dhcpRequestedIPaddr = 50, + dhcpIPaddrLeaseTime = 51, + /*dhcpOptionOverload = 52,*/ + dhcpMessageType = 53, + dhcpServerIdentifier = 54, + dhcpParamRequest = 55, + /*dhcpMsg = 56, + dhcpMaxMsgSize = 57,*/ + dhcpT1value = 58, + dhcpT2value = 59, + /*dhcpClassIdentifier = 60,*/ + dhcpClientIdentifier = 61, + endOption = 255 +}; + +typedef struct _RIP_MSG_FIXED +{ + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint8_t ciaddr[4]; + uint8_t yiaddr[4]; + uint8_t siaddr[4]; + uint8_t giaddr[4]; + uint8_t chaddr[6]; +}RIP_MSG_FIXED; + +class DhcpClass { +private: + uint32_t _dhcpInitialTransactionId; + uint32_t _dhcpTransactionId; + uint8_t _dhcpMacAddr[6]; + uint8_t _dhcpLocalIp[4]; + uint8_t _dhcpSubnetMask[4]; + uint8_t _dhcpGatewayIp[4]; + uint8_t _dhcpDhcpServerIp[4]; + uint8_t _dhcpDnsServerIp[4]; + UDP _dhcpUdpSocket; + + void presend_DHCP(); + void send_DHCP_MESSAGE(uint8_t, uint16_t); + + uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId); +public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); +}; + +#endif diff --git a/libraries/Ethernet/Ethernet.cpp b/libraries/Ethernet/Ethernet.cpp index 91cbacd3966..937f5b4f9a1 100644 --- a/libraries/Ethernet/Ethernet.cpp +++ b/libraries/Ethernet/Ethernet.cpp @@ -1,5 +1,6 @@ #include "w5100.h" #include "Ethernet.h" +#include "Dhcp.h" // XXX: don't make assumptions about the value of MAX_SOCK_NUM. uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { @@ -7,30 +8,78 @@ uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { 0, 0, 0, 0 }; -void EthernetClass::begin(uint8_t *mac, uint8_t *ip) +int EthernetClass::begin(uint8_t *mac_address) { - uint8_t gateway[4]; - gateway[0] = ip[0]; - gateway[1] = ip[1]; - gateway[2] = ip[2]; + DhcpClass dhcp; + + // Initialise the basic info + W5100.init(); + W5100.setMACAddress(mac_address); + W5100.setIPAddress(IPAddress(0,0,0,0).raw_address()); + + // Now try to get our config info from a DHCP server + int ret = dhcp.beginWithDHCP(mac_address); + if(ret == 1) + { + // We've successfully found a DHCP server and got our configuration info, so set things + // accordingly + W5100.setIPAddress(dhcp.getLocalIp().raw_address()); + W5100.setGatewayIp(dhcp.getGatewayIp().raw_address()); + W5100.setSubnetMask(dhcp.getSubnetMask().raw_address()); + _dnsServerAddress = dhcp.getDnsServerIp(); + } + + return ret; +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; gateway[3] = 1; - begin(mac, ip, gateway); + begin(mac_address, local_ip, gateway); } -void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway) +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway) { - uint8_t subnet[] = { - 255, 255, 255, 0 }; - begin(mac, ip, gateway, subnet); + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, gateway, subnet); } -void EthernetClass::begin(uint8_t *mac, uint8_t *ip, uint8_t *gateway, uint8_t *subnet) +void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress gateway, IPAddress subnet) { W5100.init(); W5100.setMACAddress(mac); - W5100.setIPAddress(ip); - W5100.setGatewayIp(gateway); - W5100.setSubnetMask(subnet); + W5100.setIPAddress(local_ip._address); + W5100.setGatewayIp(gateway._address); + W5100.setSubnetMask(subnet._address); +} + +IPAddress EthernetClass::localIP() +{ + IPAddress ret; + W5100.getIPAddress(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::subnetMask() +{ + IPAddress ret; + W5100.getSubnetMask(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::gatewayIP() +{ + IPAddress ret; + W5100.getGatewayIp(ret.raw_address()); + return ret; +} + +IPAddress EthernetClass::dnsServerIP() +{ + return _dnsServerAddress; } EthernetClass Ethernet; diff --git a/libraries/Ethernet/Ethernet.h b/libraries/Ethernet/Ethernet.h index a91f1aa177b..fdf0b7f39b7 100644 --- a/libraries/Ethernet/Ethernet.h +++ b/libraries/Ethernet/Ethernet.h @@ -3,6 +3,7 @@ #include //#include "w5100.h" +#include "IPAddress.h" #include "Client.h" #include "Server.h" @@ -10,12 +11,23 @@ class EthernetClass { private: + IPAddress _dnsServerAddress; public: static uint8_t _state[MAX_SOCK_NUM]; static uint16_t _server_port[MAX_SOCK_NUM]; - void begin(uint8_t *, uint8_t *); - void begin(uint8_t *, uint8_t *, uint8_t *); - void begin(uint8_t *, uint8_t *, uint8_t *, uint8_t *); + // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the + // configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac_address); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress gateway, IPAddress subnet); + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + friend class Client; friend class Server; }; diff --git a/libraries/Ethernet/IPAddress.cpp b/libraries/Ethernet/IPAddress.cpp index 389329de0fd..408d518a0e7 100644 --- a/libraries/Ethernet/IPAddress.cpp +++ b/libraries/Ethernet/IPAddress.cpp @@ -37,3 +37,8 @@ IPAddress& IPAddress::operator=(uint32_t address) return *this; } +bool IPAddress::operator==(const uint8_t* addr) +{ + return memcmp(addr, _address, sizeof(_address)) == 0; +} + diff --git a/libraries/Ethernet/IPAddress.h b/libraries/Ethernet/IPAddress.h index 586385320aa..1d19bbd222b 100644 --- a/libraries/Ethernet/IPAddress.h +++ b/libraries/Ethernet/IPAddress.h @@ -31,6 +31,11 @@ class IPAddress { private: uint8_t _address[4]; // IPv4 address + // Access the raw byte array containing the address. Because this returns a pointer + // to the internal structure rather than a copy of the address this function should only + // be used when you know that the usage of the returned uint8_t* will be transient and not + // stored. + uint8_t* raw_address() { return _address; }; public: // Constructors @@ -41,7 +46,9 @@ class IPAddress { // Overloaded cast operator to allow IPAddress objects to be used where a pointer // to a four-byte uint8_t array is expected - operator uint8_t*() { return _address; }; + operator uint32_t() { return *((uint32_t*)_address); }; + bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); }; + bool operator==(const uint8_t* addr); // Overloaded index operator to allow getting and setting individual octets of the address uint8_t operator[](int index) const { return _address[index]; }; @@ -50,6 +57,12 @@ class IPAddress { // Overloaded copy operators to allow initialisation of IPAddress objects from other types IPAddress& operator=(const uint8_t *address); IPAddress& operator=(uint32_t address); + + friend class EthernetClass; + friend class UDP; + friend class Client; + friend class Server; + friend class DhcpClass; }; #endif diff --git a/libraries/Ethernet/Udp.cpp b/libraries/Ethernet/Udp.cpp index 6ad8aca7ebe..a8c98c3f49b 100644 --- a/libraries/Ethernet/Udp.cpp +++ b/libraries/Ethernet/Udp.cpp @@ -77,7 +77,7 @@ void UDP::stop() int UDP::beginPacket(IPAddress ip, uint16_t port) { _offset = 0; - return startUDP(_sock, ip, port); + return startUDP(_sock, ip.raw_address(), port); } int UDP::endPacket() diff --git a/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde b/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde index a58433fed5b..3f43d96dbc0 100644 --- a/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde +++ b/libraries/Ethernet/examples/BarometricPressureWebServer/BarometricPressureWebServer.pde @@ -31,12 +31,9 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // assign an IP address for the controller: -byte ip[] = { - 192,168,1,20 }; -byte gateway[] = { - 192,168,1,1}; -byte subnet[] = { - 255, 255, 255, 0 }; +IPAddress ip(192,168,1,20); +IPAddress gateway(192,168,1,1); +IPAddress subnet(255, 255, 255, 0); // Initialize the Ethernet server library diff --git a/libraries/Ethernet/examples/ChatServer/ChatServer.pde b/libraries/Ethernet/examples/ChatServer/ChatServer.pde index 50ae01408b8..8267a5dd45c 100644 --- a/libraries/Ethernet/examples/ChatServer/ChatServer.pde +++ b/libraries/Ethernet/examples/ChatServer/ChatServer.pde @@ -24,9 +24,9 @@ // The IP address will be dependent on your local network. // gateway and subnet are optional: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 192,168,1, 177 }; -byte gateway[] = { 192,168,1, 1 }; -byte subnet[] = { 255, 255, 0, 0 }; +IPAddress ip(192,168,1, 177); +IPAddress gateway(192,168,1, 1); +IPAddress subnet(255, 255, 0, 0); // telnet defaults to port 23 Server server(23); @@ -60,4 +60,4 @@ void loop() { // echo the bytes to the server as well: Serial.print(thisChar); } -} \ No newline at end of file +} diff --git a/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde b/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde index fe94541e127..687fa437e65 100644 --- a/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde +++ b/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde @@ -23,20 +23,13 @@ #include // assign a MAC address for the ethernet controller. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield // fill in your address here: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -// assign an IP address for the controller: -byte ip[] = { - 192,169,1,20 }; -byte gateway[] = { - 192,168,1,1}; -byte subnet[] = { - 255, 255, 255, 0 }; // The address of the server you want to connect to (pachube.com): -byte server[] = { - 209,40,205,190 }; +IPAddress server(209,40,205,190); // initialize the library instance: Client client(server, 80); @@ -46,9 +39,15 @@ boolean lastConnected = false; // state of the connection last time through const int postingInterval = 10000; //delay between updates to Pachube.com void setup() { - // start the ethernet connection and serial port: - Ethernet.begin(mac, ip); + // start serial port: Serial.begin(9600); + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } // give the ethernet module time to boot up: delay(1000); } diff --git a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde index 225823ac16f..0db6e219a38 100644 --- a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde +++ b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde @@ -29,16 +29,12 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // assign an IP address for the controller: -byte ip[] = { - 192,169,1,20 }; -byte gateway[] = { - 192,168,1,1}; -byte subnet[] = { - 255, 255, 255, 0 }; +IPAddress ip(192,169,1,20); +IPAddress gateway(192,168,1,1); +IPAddress subnet(255, 255, 255, 0); // The address of the server you want to connect to (pachube.com): -byte server[] = { - 209,40,205,190 }; +IPAddress server(209,40,205,190); // initialize the library instance: Client client(server, 80); diff --git a/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde b/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde index aca1fa9adc5..95756d07692 100644 --- a/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde +++ b/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde @@ -24,12 +24,10 @@ // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { - 192,168,1,177 }; +IPAddress ip(192,168,1,177); // Enter the IP address of the server you're connecting to: -byte server[] = { - 1,1,1,1 }; +IPAddress server(1,1,1,1); // Initialize the Ethernet client library // with the IP address and port of the server diff --git a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde index cfb5a2bc1d2..7c2d3ea9e97 100644 --- a/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde +++ b/libraries/Ethernet/examples/UdpNtpClient/UdpNtpClient.pde @@ -20,13 +20,11 @@ #include #include -// Enter a MAC address and IP address for your controller below. -// The IP address will be dependent on your local network: +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -IPAddress ip(192, 168, 1, 177); - unsigned int localPort = 8888; // local port to listen for UDP packets IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server @@ -40,11 +38,16 @@ UDP Udp; void setup() { + Serial.begin(9600); + // start Ethernet and UDP - Ethernet.begin(mac,ip); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } Udp.begin(localPort); - - Serial.begin(9600); } void loop() @@ -99,7 +102,7 @@ void loop() } // send an NTP request to the time server at the given address -unsigned long sendNTPpacket(byte *address) +unsigned long sendNTPpacket(IPAddress& address) { // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); diff --git a/libraries/Ethernet/examples/WebClient/WebClient.pde b/libraries/Ethernet/examples/WebClient/WebClient.pde index a2d3503115e..74d34e0cecd 100644 --- a/libraries/Ethernet/examples/WebClient/WebClient.pde +++ b/libraries/Ethernet/examples/WebClient/WebClient.pde @@ -15,11 +15,10 @@ #include #include -// Enter a MAC address and IP address for your controller below. -// The IP address will be dependent on your local network: +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 192,168,1,177 }; -byte server[] = { 173,194,33,104 }; // Google +IPAddress server(173,194,33,104); // Google // Initialize the Ethernet client library // with the IP address and port of the server @@ -27,10 +26,15 @@ byte server[] = { 173,194,33,104 }; // Google Client client(server, 80); void setup() { - // start the Ethernet connection: - Ethernet.begin(mac, ip); // start the serial library: Serial.begin(9600); + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } // give the Ethernet shield a second to initialize: delay(1000); Serial.println("connecting..."); diff --git a/libraries/Ethernet/examples/WebServer/WebServer.pde b/libraries/Ethernet/examples/WebServer/WebServer.pde index 77bcb204ed8..c69a56a4040 100644 --- a/libraries/Ethernet/examples/WebServer/WebServer.pde +++ b/libraries/Ethernet/examples/WebServer/WebServer.pde @@ -21,7 +21,7 @@ // Enter a MAC address and IP address for your controller below. // The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; -byte ip[] = { 192,168,1, 177 }; +IPAddress ip(192,168,1, 177); // Initialize the Ethernet server library // with the IP address and port you want to use @@ -79,4 +79,4 @@ void loop() // close the connection: client.stop(); } -} \ No newline at end of file +} diff --git a/libraries/Ethernet/util.h b/libraries/Ethernet/util.h new file mode 100644 index 00000000000..220011b8fd4 --- /dev/null +++ b/libraries/Ethernet/util.h @@ -0,0 +1,13 @@ +#ifndef UTIL_H +#define UTIL_H + +#define htons(x) ( (x)<<8 | ((x)>>8)&0xFF ) +#define ntohs(x) htons(x) + +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#define ntohl(x) htonl(x) + +#endif From a310cb8e4dad410af717e3fe9502fc3f98127892 Mon Sep 17 00:00:00 2001 From: amcewen Date: Tue, 25 Jan 2011 16:29:38 +0000 Subject: [PATCH 4/6] Proposed fix for issue 243, adding DNS to the Ethernet library. Uses a slightly modified version of the agreed API as the host/port parameters have been moved from the Client constructor to the Client::connect methods. This means it's possible for errors to be returned if the DNS lookup fails and also reduces the RAM footprint of the Client class as it no longer needs to store the host/port for later use in Client::connect. --- libraries/Ethernet/Client.cpp | 22 +- libraries/Ethernet/Client.h | 6 +- libraries/Ethernet/Dns.cpp | 423 ++++++++++++++++++ libraries/Ethernet/Dns.h | 41 ++ libraries/Ethernet/IPAddress.h | 4 + .../examples/PachubeClient/PachubeClient.pde | 7 +- .../PachubeClientString.pde | 18 +- .../examples/TelnetClient/TelnetClient.pde | 4 +- .../Ethernet/examples/WebClient/WebClient.pde | 4 +- 9 files changed, 503 insertions(+), 26 deletions(-) create mode 100644 libraries/Ethernet/Dns.cpp create mode 100644 libraries/Ethernet/Dns.h diff --git a/libraries/Ethernet/Client.cpp b/libraries/Ethernet/Client.cpp index 7ae55d08d5f..8a525ca7e7b 100644 --- a/libraries/Ethernet/Client.cpp +++ b/libraries/Ethernet/Client.cpp @@ -10,16 +10,32 @@ extern "C" { #include "Ethernet.h" #include "Client.h" #include "Server.h" +#include "Dns.h" uint16_t Client::_srcport = 1024; +Client::Client() : _sock(MAX_SOCK_NUM) { +} + Client::Client(uint8_t sock) : _sock(sock) { } -Client::Client(IPAddress& ip, uint16_t port) : _ip(ip), _port(port), _sock(MAX_SOCK_NUM) { +int Client::connect(const char* host, uint16_t port) { + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return connect(remote_addr, port); + } else { + return ret; + } } -uint8_t Client::connect() { +int Client::connect(IPAddress ip, uint16_t port) { if (_sock != MAX_SOCK_NUM) return 0; @@ -38,7 +54,7 @@ uint8_t Client::connect() { if (_srcport == 0) _srcport = 1024; socket(_sock, SnMR::TCP, _srcport, 0); - if (!::connect(_sock, _ip.raw_address(), _port)) { + if (!::connect(_sock, ip.raw_address(), port)) { _sock = MAX_SOCK_NUM; return 0; } diff --git a/libraries/Ethernet/Client.h b/libraries/Ethernet/Client.h index e92e90e33f1..23a8f186091 100644 --- a/libraries/Ethernet/Client.h +++ b/libraries/Ethernet/Client.h @@ -8,10 +8,10 @@ class Client : public Stream { public: Client(); Client(uint8_t sock); - Client(IPAddress& ip, uint16_t port); uint8_t status(); - uint8_t connect(); + int connect(IPAddress ip, uint16_t port); + int connect(const char *host, uint16_t port); virtual void write(uint8_t); virtual void write(const char *str); virtual void write(const uint8_t *buf, size_t size); @@ -31,8 +31,6 @@ class Client : public Stream { private: static uint16_t _srcport; uint8_t _sock; - IPAddress _ip; - uint16_t _port; }; #endif diff --git a/libraries/Ethernet/Dns.cpp b/libraries/Ethernet/Dns.cpp new file mode 100644 index 00000000000..3a31e35a4f3 --- /dev/null +++ b/libraries/Ethernet/Dns.cpp @@ -0,0 +1,423 @@ +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#include "w5100.h" +#include "Udp.h" +#include "util.h" + +#include "Dns.h" +#include +//#include +#include "wiring.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 + +void DNSClient::begin(const IPAddress& aDNSServer) +{ + iDNSServer = aDNSServer; + iRequestId = 0; +} + + +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] = (byte)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] = (byte)segmentValue; + return 1; + } + } + else + { + return 0; + } +} + +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+(millis() & 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; +} + +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 = millis(); // 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; +} + + +uint16_t DNSClient::ProcessResponse(int aTimeout, IPAddress& aAddress) +{ + uint32_t startTime = millis(); + + // Wait for a response packet + while(iUdp.parsePacket() <= 0) + { + if((millis() - startTime) > aTimeout) + return TIMED_OUT; + delay(50); + } + + // 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 != 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 (int 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 (int 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 (int 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; +} + diff --git a/libraries/Ethernet/Dns.h b/libraries/Ethernet/Dns.h new file mode 100644 index 00000000000..9582351f243 --- /dev/null +++ b/libraries/Ethernet/Dns.h @@ -0,0 +1,41 @@ +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#ifndef DNSClient_h +#define DNSClient_h + +#include + +class DNSClient +{ +public: + // ctor + void begin(const IPAddress& aDNSServer); + + /** Convert a numeric IP address string into a four-byte IP address. + @param aIPAddrString IP address to convert + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int inet_aton(const char *aIPAddrString, IPAddress& aResult); + + /** Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int getHostByName(const char* aHostname, IPAddress& aResult); + +protected: + uint16_t BuildRequest(const char* aName); + uint16_t ProcessResponse(int aTimeout, IPAddress& aAddress); + + IPAddress iDNSServer; + uint16_t iRequestId; + UDP iUdp; +}; + +#endif diff --git a/libraries/Ethernet/IPAddress.h b/libraries/Ethernet/IPAddress.h index 1d19bbd222b..487e420bd4f 100644 --- a/libraries/Ethernet/IPAddress.h +++ b/libraries/Ethernet/IPAddress.h @@ -63,6 +63,10 @@ class IPAddress { friend class Client; friend class Server; friend class DhcpClass; + friend class DNSClient; }; +const IPAddress INADDR_NONE(0,0,0,0); + + #endif diff --git a/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde b/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde index 687fa437e65..af9bf1f053a 100644 --- a/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde +++ b/libraries/Ethernet/examples/PachubeClient/PachubeClient.pde @@ -28,11 +28,8 @@ byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -// The address of the server you want to connect to (pachube.com): -IPAddress server(209,40,205,190); - // initialize the library instance: -Client client(server, 80); +Client client; long lastConnectionTime = 0; // last time you connected to the server, in milliseconds boolean lastConnected = false; // state of the connection last time through the main loop @@ -85,7 +82,7 @@ void loop() { // this method makes a HTTP connection to the server: void sendData(int thisData) { // if there's a successful connection: - if (client.connect()) { + if (client.connect("www.pachube.com", 80)) { Serial.println("connecting..."); // send the HTTP PUT request. // fill in your feed address here: diff --git a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde index 0db6e219a38..e6dbf09ea1f 100644 --- a/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde +++ b/libraries/Ethernet/examples/PachubeClientString/PachubeClientString.pde @@ -28,16 +28,9 @@ // fill in your address here: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -// assign an IP address for the controller: -IPAddress ip(192,169,1,20); -IPAddress gateway(192,168,1,1); -IPAddress subnet(255, 255, 255, 0); - -// The address of the server you want to connect to (pachube.com): -IPAddress server(209,40,205,190); // initialize the library instance: -Client client(server, 80); +Client client; long lastConnectionTime = 0; // last time you connected to the server, in milliseconds boolean lastConnected = false; // state of the connection last time through the main loop @@ -45,8 +38,13 @@ const int postingInterval = 10000; //delay between updates to Pachube.com void setup() { // start the ethernet connection and serial port: - Ethernet.begin(mac, ip); Serial.begin(9600); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for(;;) + ; + } // give the ethernet module time to boot up: delay(1000); } @@ -92,7 +90,7 @@ void loop() { // this method makes a HTTP connection to the server: void sendData(String thisData) { // if there's a successful connection: - if (client.connect()) { + if (client.connect("www.pachube.com", 80)) { Serial.println("connecting..."); // send the HTTP PUT request. // fill in your feed address here: diff --git a/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde b/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde index 95756d07692..7502d218dd4 100644 --- a/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde +++ b/libraries/Ethernet/examples/TelnetClient/TelnetClient.pde @@ -33,7 +33,7 @@ IPAddress server(1,1,1,1); // with the IP address and port of the server // that you want to connect to (port 23 is default for telnet; // if you're using Processing's ChatServer, use port 10002): -Client client(server, 10002); +Client client; void setup() { // start the Ethernet connection: @@ -45,7 +45,7 @@ void setup() { Serial.println("connecting..."); // if you get a connection, report back via serial: - if (client.connect()) { + if (client.connect(server, 10002)) { Serial.println("connected"); } else { diff --git a/libraries/Ethernet/examples/WebClient/WebClient.pde b/libraries/Ethernet/examples/WebClient/WebClient.pde index 74d34e0cecd..646c3aaddcf 100644 --- a/libraries/Ethernet/examples/WebClient/WebClient.pde +++ b/libraries/Ethernet/examples/WebClient/WebClient.pde @@ -23,7 +23,7 @@ IPAddress server(173,194,33,104); // Google // Initialize the Ethernet client library // with the IP address and port of the server // that you want to connect to (port 80 is default for HTTP): -Client client(server, 80); +Client client; void setup() { // start the serial library: @@ -40,7 +40,7 @@ void setup() { Serial.println("connecting..."); // if you get a connection, report back via serial: - if (client.connect()) { + if (client.connect(server, 80)) { Serial.println("connected"); // Make a HTTP request: client.println("GET /search?q=arduino HTTP/1.0"); From 4000c9199b7f91907519a46d692d660012ade090 Mon Sep 17 00:00:00 2001 From: amcewen Date: Fri, 4 Feb 2011 21:15:42 +0000 Subject: [PATCH 5/6] Added new method to UDP to take a hostname rather than an IP address. Part of issue 243 --- libraries/Ethernet/Udp.cpp | 17 +++++++++++++++++ libraries/Ethernet/Udp.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/libraries/Ethernet/Udp.cpp b/libraries/Ethernet/Udp.cpp index a8c98c3f49b..2c8b1033211 100644 --- a/libraries/Ethernet/Udp.cpp +++ b/libraries/Ethernet/Udp.cpp @@ -30,6 +30,7 @@ #include "socket.h" #include "Ethernet.h" #include "Udp.h" +#include "Dns.h" /* Constructor */ UDP::UDP() : _sock(MAX_SOCK_NUM) {} @@ -74,6 +75,22 @@ void UDP::stop() _sock = MAX_SOCK_NUM; } +int UDP::beginPacket(const char *host, uint16_t port) +{ + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return beginPacket(remote_addr, port); + } else { + return ret; + } +} + int UDP::beginPacket(IPAddress ip, uint16_t port) { _offset = 0; diff --git a/libraries/Ethernet/Udp.h b/libraries/Ethernet/Udp.h index 8cd3b065a1b..99df53f15af 100644 --- a/libraries/Ethernet/Udp.h +++ b/libraries/Ethernet/Udp.h @@ -60,6 +60,9 @@ class UDP : public Stream { // Start building up a packet to send to the remote host specific in ip and port // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port int beginPacket(IPAddress ip, uint16_t port); + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + int beginPacket(const char *host, uint16_t port); // Finish off this packet and send it // Returns 1 if the packet was sent successfully, 0 if there was an error int endPacket(); From 7f18110b803609974a3897749d9d1f67ae614fff Mon Sep 17 00:00:00 2001 From: amcewen Date: Fri, 4 Feb 2011 21:44:51 +0000 Subject: [PATCH 6/6] Fixed bug in parsePacket where it could block indefinitely if called when no packets were available to be read. --- libraries/Ethernet/Udp.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/libraries/Ethernet/Udp.cpp b/libraries/Ethernet/Udp.cpp index 2c8b1033211..aed5d983b32 100644 --- a/libraries/Ethernet/Udp.cpp +++ b/libraries/Ethernet/Udp.cpp @@ -121,20 +121,25 @@ void UDP::write(const uint8_t *buffer, size_t size) int UDP::parsePacket() { - //HACK - hand-parse the UDP packet using TCP recv method - uint8_t tmpBuf[8]; - int ret =0; - //read 8 header bytes and get IP and port from it - ret = recv(_sock,tmpBuf,8); - if (ret > 0) + if (available() > 0) { - _remoteIP = tmpBuf; - _remotePort = tmpBuf[4]; - _remotePort = (_remotePort << 8) + tmpBuf[5]; - // When we get here, any remaining bytes are the data - ret = available(); + //HACK - hand-parse the UDP packet using TCP recv method + uint8_t tmpBuf[8]; + int ret =0; + //read 8 header bytes and get IP and port from it + ret = recv(_sock,tmpBuf,8); + if (ret > 0) + { + _remoteIP = tmpBuf; + _remotePort = tmpBuf[4]; + _remotePort = (_remotePort << 8) + tmpBuf[5]; + // When we get here, any remaining bytes are the data + ret = available(); + } + return ret; } - return ret; + // There aren't any packets available + return 0; } int UDP::read()