text: #ifndef ethernet_h_
  #define ethernet_h_
  
  // All symbols exposed to Arduino sketches are contained in this header file
  
  #define MAX_SOCK_NUM 4
  
  #include <inttypes.h>
  #include "Arduino.h"
  #include "Print.h"
  #include "IPAddress.h"
  #include "Client.h"
  #include "Server.h"
  #include "Udp.h"
  
  class EthernetUDP;
  class EthernetClient;
  class EthernetServer;
  class DhcpClass;
  
  class EthernetClass {
  private:
  	static IPAddress _dnsServerAddress;
  	static DhcpClass* _dhcp;
  public:
  	// 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
  	static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
  	static int maintain();
  
  	// Manaul configuration
  	static void begin(uint8_t *mac, IPAddress ip);
  	static void begin(uint8_t *mac, IPAddress ip, IPAddress dns);
  	static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway);
  	static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet);
  	static void init(uint8_t sspin = 10);
  	static IPAddress localIP();
  	static IPAddress subnetMask();
  	static IPAddress gatewayIP();
  	static IPAddress dnsServerIP() { return _dnsServerAddress; }
  
  	friend class EthernetClient;
  	friend class EthernetServer;
  	friend class EthernetUDP;
  private:
  	// Opens a socket(TCP or UDP or IP_RAW mode)
  	static uint8_t socketBegin(uint8_t protocol, uint16_t port);
  	static uint8_t socketStatus(uint8_t s);
  	// Close socket
  	static void socketClose(uint8_t s);
  	// Establish TCP connection (Active connection)
  	static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);
  	// disconnect the connection
  	static void socketDisconnect(uint8_t s);
  	// Establish TCP connection (Passive connection)
  	static uint8_t socketListen(uint8_t s);
  	// Send data (TCP)
  	static uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len);
  	// Receive data (TCP)
  	static int socketRecv(uint8_t s, uint8_t * buf, int16_t len);
  	static uint16_t socketRecvAvailable(uint8_t s);
  	static uint8_t socketPeek(uint8_t s);
  	// 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
  	static int socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port);
  	// copy 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
  	static uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len);
  	// 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
  	static int socketSendUDP(uint8_t s);
  	// Initialize the "random" source port number
  	static void socketPortRand(uint16_t n);
  };
  
  extern EthernetClass Ethernet;
  
  
  #define UDP_TX_PACKET_MAX_SIZE 24
  
  class EthernetUDP : public UDP {
  private:
  	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
  
  protected:
  	uint8_t sockindex;
  	uint16_t _remaining; // remaining bytes of incoming packet yet to be processed
  
  public:
  	EthernetUDP() : sockindex(MAX_SOCK_NUM) {}  // Constructor
  	virtual uint8_t begin(uint16_t);      // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
  	virtual uint8_t beginMulticast(IPAddress, uint16_t);  // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
  	virtual 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
  	virtual 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
  	virtual 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
  	virtual int endPacket();
  	// Write a single byte into the packet
  	virtual size_t write(uint8_t);
  	// Write size bytes from buffer into the packet
  	virtual size_t write(const uint8_t *buffer, size_t size);
  
  	using Print::write;
  
  	// Start processing the next available incoming packet
  	// Returns the size of the packet in bytes, or 0 if no packets are available
  	virtual 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);
  	// 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
  
  	// Return the IP address of the host who sent the current incoming packet
  	virtual IPAddress remoteIP() { return _remoteIP; };
  	// Return the port of the host who sent the current incoming packet
  	virtual uint16_t remotePort() { return _remotePort; };
  };
  
  
  
  
  class EthernetClient : public Client {
  public:
  	EthernetClient() : sockindex(MAX_SOCK_NUM) { };
  	EthernetClient(uint8_t s) : sockindex(s) { };
  
  	uint8_t status();
  	virtual int connect(IPAddress ip, uint16_t port);
  	virtual int connect(const char *host, uint16_t port);
  	virtual size_t write(uint8_t);
  	virtual size_t write(const uint8_t *buf, size_t size);
  	virtual int available();
  	virtual int read();
  	virtual int read(uint8_t *buf, size_t size);
  	virtual int peek();
  	virtual void flush();
  	virtual void stop();
  	virtual uint8_t connected();
  	virtual operator bool() { return sockindex < MAX_SOCK_NUM; }
  	virtual bool operator==(const bool value) { return bool() == value; }
  	virtual bool operator!=(const bool value) { return bool() != value; }
  	virtual bool operator==(const EthernetClient&);
  	virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); };
  	uint8_t getSocketNumber() const { return sockindex; }
  
  	friend class EthernetServer;
  
  	using Print::write;
  
  private:
  	uint8_t sockindex;
  };
  
  
  class EthernetServer : public Server {
  private:
  	uint16_t _port;
  	uint8_t sockindex;
  	void accept();
  public:
  	EthernetServer(uint16_t port) : _port(port), sockindex(MAX_SOCK_NUM) { }
  	EthernetClient available();
  	virtual void begin();
  	virtual size_t write(uint8_t);
  	virtual size_t write(const uint8_t *buf, size_t size);
  	using Print::write;
  
  	// TODO: make private when socket allocation moves to EthernetClass
  	static uint16_t server_port[MAX_SOCK_NUM];
  };
  
  
  class DhcpClass {
  private:
  	uint32_t _dhcpInitialTransactionId;
  	uint32_t _dhcpTransactionId;
  	uint8_t  _dhcpMacAddr[6];
  #ifdef __arm__
  	uint8_t  _dhcpLocalIp[4] __attribute__((aligned(4)));
  	uint8_t  _dhcpSubnetMask[4] __attribute__((aligned(4)));
  	uint8_t  _dhcpGatewayIp[4] __attribute__((aligned(4)));
  	uint8_t  _dhcpDhcpServerIp[4] __attribute__((aligned(4)));
  	uint8_t  _dhcpDnsServerIp[4] __attribute__((aligned(4)));
  #else
  	uint8_t  _dhcpLocalIp[4];
  	uint8_t  _dhcpSubnetMask[4];
  	uint8_t  _dhcpGatewayIp[4];
  	uint8_t  _dhcpDhcpServerIp[4];
  	uint8_t  _dhcpDnsServerIp[4];
  #endif
  	uint32_t _dhcpLeaseTime;
  	uint32_t _dhcpT1, _dhcpT2;
  	uint32_t _renewInSec;
  	uint32_t _rebindInSec;
  	unsigned long _timeout;
  	unsigned long _responseTimeout;
  	unsigned long _lastCheckLeaseMillis;
  	uint8_t _dhcp_state;
  	EthernetUDP _dhcpUdpSocket;
  
  	int request_DHCP_lease();
  	void reset_DHCP_lease();
  	void presend_DHCP();
  	void send_DHCP_MESSAGE(uint8_t, uint16_t);
  	void printByte(char *, uint8_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);
  	int checkLease();
  };
  
  
  
  
  
  #endif
  