diff --git a/libraries/Ethernet/Dhcp.cpp b/libraries/Ethernet/Dhcp.cpp index 56d5b69516e..7b49037163e 100755 --- a/libraries/Ethernet/Dhcp.cpp +++ b/libraries/Ethernet/Dhcp.cpp @@ -9,123 +9,153 @@ #include "Arduino.h" #include "util.h" + int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) { - _dhcpLeaseTime=0; - _dhcpT1=0; - _dhcpT2=0; - _lastCheck=0; - _timeout = timeout; - _responseTimeout = responseTimeout; - - // zero out _dhcpMacAddr - memset(_dhcpMacAddr, 0, 6); - reset_DHCP_lease(); - - memcpy((void*)_dhcpMacAddr, (void*)mac, 6); - _dhcp_state = STATE_DHCP_START; - return request_DHCP_lease(); + initialize(mac, timeout, responseTimeout); + int result = 0; + while (result == 0) { + result = successful(); + } + return _dhcp_state; +} + +void DhcpClass::initialize(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) { + _dhcpLeaseTime=0; + _dhcpT1=0; + _dhcpT2=0; + _lastCheck=0; + _timeout = timeout; + _responseTimeout = responseTimeout; + + // zero out _dhcpMacAddr + memset(_dhcpMacAddr, 0, 6); + reset_DHCP_lease(); + + memcpy((void*)_dhcpMacAddr, (void*)mac, 6); + _dhcp_state = STATE_DHCP_START; + init_new_DHCP_request(); } void DhcpClass::reset_DHCP_lease(){ - // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp - memset(_dhcpLocalIp, 0, 20); + // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp + memset(_dhcpLocalIp, 0, 20); +} + +// 0 = not finished, 1 = finished and successful, 2 = finished but failed +int DhcpClass::successful() { + int result = step_DHCP_lease(); + if ((millis() - _startTime) > _timeout) { + result = 2; + _dhcp_state = STATE_DHCP_TIMEOUT; + } + if (result > 0) { + _dhcpUdpSocket.stop(); + _dhcpTransactionId++; + } + return result; +} + +void DhcpClass::init_new_DHCP_request(){ + // Pick an initial transaction ID + _dhcpTransactionId = random(1UL, 2000UL); + _dhcpInitialTransactionId = _dhcpTransactionId; + + if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) + { + _dhcp_state = STATE_DHCP_CONNECTION_FAILED; + } + _startTime = millis(); } //return:0 on error, 1 if request is sent and response is received int DhcpClass::request_DHCP_lease(){ - - uint8_t messageType = 0; - - - - // Pick an initial transaction ID - _dhcpTransactionId = random(1UL, 2000UL); - _dhcpInitialTransactionId = _dhcpTransactionId; - - _dhcpUdpSocket.stop(); - 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_REREQUEST){ - _dhcpTransactionId++; - send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - startTime)/1000)); - _dhcp_state = STATE_DHCP_REQUEST; - } - 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; - //use default lease time if we didn't get it - if(_dhcpLeaseTime == 0){ - _dhcpLeaseTime = DEFAULT_LEASE; - } - //calculate T1 & T2 if we didn't get it - if(_dhcpT1 == 0){ - //T1 should be 50% of _dhcpLeaseTime - _dhcpT1 = _dhcpLeaseTime >> 1; - } - if(_dhcpT2 == 0){ - //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime - _dhcpT2 = _dhcpT1 << 1; - } - _renewInSec = _dhcpT1; - _rebindInSec = _dhcpT2; - } - 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; + init_new_DHCP_request(); + + presend_DHCP(); + + int result = 0; + do { + result = step_DHCP_lease(); + } while(result == 0 && ((millis() - _startTime) > _timeout)); + + _dhcpUdpSocket.stop(); + _dhcpTransactionId++; + + return result == 1; +} + +int DhcpClass::step_DHCP_lease(){ + int result = 0; + uint8_t messageType = 0; + + if (_dhcp_state == STATE_DHCP_CONNECTION_FAILED) { + return 2; + } + if (_dhcp_state == STATE_DHCP_LEASED) { + return 1; + } + + if(_dhcp_state == STATE_DHCP_START) + { + _dhcpTransactionId++; + + send_DHCP_MESSAGE(DHCP_DISCOVER, ((millis() - _startTime) / 1000)); + _startResponseTime = millis(); + _dhcp_state = STATE_DHCP_DISCOVER; + } + else if(_dhcp_state == STATE_DHCP_REREQUEST){ + _dhcpTransactionId++; + send_DHCP_MESSAGE(DHCP_REQUEST, ((millis() - _startTime)/1000)); + _startResponseTime = millis(); + _dhcp_state = STATE_DHCP_REQUEST; + } + else if(_dhcp_state == STATE_DHCP_DISCOVER) + { + uint32_t respId; + messageType = tryParseDHCPResponse(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 = tryParseDHCPResponse(respId); + if(messageType == DHCP_ACK) + { + _dhcp_state = STATE_DHCP_LEASED; + result = 1; + //use default lease time if we didn't get it + if(_dhcpLeaseTime == 0){ + _dhcpLeaseTime = DEFAULT_LEASE; + } + //calculate T1 & T2 if we didn't get it + if(_dhcpT1 == 0){ + //T1 should be 50% of _dhcpLeaseTime + _dhcpT1 = _dhcpLeaseTime >> 1; + } + if(_dhcpT2 == 0){ + //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime + _dhcpT2 = _dhcpT1 << 1; + } + _renewInSec = _dhcpT1; + _rebindInSec = _dhcpT2; + } + else if(messageType == DHCP_NAK) + _dhcp_state = STATE_DHCP_START; + } + + if(messageType == 255 && ((millis() - _startResponseTime) > _responseTimeout)) + { + _dhcp_state = STATE_DHCP_START; // response timeout, restart DHCP + } + return result; } void DhcpClass::presend_DHCP() @@ -134,347 +164,341 @@ 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) + 6; // length of hostname + last 3 bytes of mac address - strcpy((char*)&(buffer[18]), HOST_NAME); - - printByte((char*)&(buffer[24]), _dhcpMacAddr[3]); - printByte((char*)&(buffer[26]), _dhcpMacAddr[4]); - printByte((char*)&(buffer[28]), _dhcpMacAddr[5]); - - //put data in W5100 transmit buffer - _dhcpUdpSocket.write(buffer, 30); - - 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 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) + 6; // length of hostname + last 3 bytes of mac address + strcpy((char*)&(buffer[18]), HOST_NAME); + + printByte((char*)&(buffer[24]), _dhcpMacAddr[3]); + printByte((char*)&(buffer[26]), _dhcpMacAddr[4]); + printByte((char*)&(buffer[28]), _dhcpMacAddr[5]); + + //put data in W5100 transmit buffer + _dhcpUdpSocket.write(buffer, 30); + + 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 - (int)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); - for (int i = 0; i < opt_len-4; i++) - { - _dhcpUdpSocket.read(); - } - break; - - case dns : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); - for (int i = 0; i < opt_len-4; i++) - { - _dhcpUdpSocket.read(); - } - 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 dhcpT1value : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); - _dhcpT1 = ntohl(_dhcpT1); - break; - - case dhcpT2value : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); - _dhcpT2 = ntohl(_dhcpT2); - break; - - case dhcpIPaddrLeaseTime : - opt_len = _dhcpUdpSocket.read(); - _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); - _dhcpLeaseTime = ntohl(_dhcpLeaseTime); - _renewInSec = _dhcpLeaseTime; - break; - - 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; +uint8_t DhcpClass::tryParseDHCPResponse(uint32_t& transactionId) { + + uint8_t type = 0; + uint8_t opt_len = 0; + + if (_dhcpUdpSocket.parsePacket() <= 0) { + return 255; + } + // 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 - (int)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); + for (int i = 0; i < opt_len-4; i++) + { + _dhcpUdpSocket.read(); + } + break; + + case dns : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); + for (int i = 0; i < opt_len-4; i++) + { + _dhcpUdpSocket.read(); + } + 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 dhcpT1value : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpT1, sizeof(_dhcpT1)); + _dhcpT1 = ntohl(_dhcpT1); + break; + + case dhcpT2value : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpT2, sizeof(_dhcpT2)); + _dhcpT2 = ntohl(_dhcpT2); + break; + + case dhcpIPaddrLeaseTime : + opt_len = _dhcpUdpSocket.read(); + _dhcpUdpSocket.read((uint8_t*)&_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); + _dhcpLeaseTime = ntohl(_dhcpLeaseTime); + _renewInSec = _dhcpLeaseTime; + break; + + 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; } + /* - returns: - 0/DHCP_CHECK_NONE: nothing happened - 1/DHCP_CHECK_RENEW_FAIL: renew failed - 2/DHCP_CHECK_RENEW_OK: renew success - 3/DHCP_CHECK_REBIND_FAIL: rebind fail - 4/DHCP_CHECK_REBIND_OK: rebind success +returns: +0/DHCP_CHECK_NONE: nothing happened +1/DHCP_CHECK_RENEW_STARTED: a new renew was initiated, call successful() +2/DHCP_CHECK_REBIND_STARTED: a new rebind was initiated, call successful() */ int DhcpClass::checkLease(){ - //this uses a signed / unsigned trick to deal with millis overflow - unsigned long now = millis(); - signed long snow = (long)now; - int rc=DHCP_CHECK_NONE; - if (_lastCheck != 0){ - signed long factor; - //calc how many ms past the timeout we are - factor = snow - (long)_secTimeout; - //if on or passed the timeout, reduce the counters - if ( factor >= 0 ){ - //next timeout should be now plus 1000 ms minus parts of second in factor - _secTimeout = snow + 1000 - factor % 1000; - //how many seconds late are we, minimum 1 - factor = factor / 1000 +1; - - //reduce the counters by that mouch - //if we can assume that the cycle time (factor) is fairly constant - //and if the remainder is less than cycle time * 2 - //do it early instead of late - if(_renewInSec < factor*2 ) - _renewInSec = 0; - else - _renewInSec -= factor; - - if(_rebindInSec < factor*2 ) - _rebindInSec = 0; - else - _rebindInSec -= factor; - } - - //if we have a lease but should renew, do it - if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){ - _dhcp_state = STATE_DHCP_REREQUEST; - rc = 1 + request_DHCP_lease(); - } - - //if we have a lease or is renewing but should bind, do it - if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){ - //this should basically restart completely - _dhcp_state = STATE_DHCP_START; - reset_DHCP_lease(); - rc = 3 + request_DHCP_lease(); - } - } - else{ - _secTimeout = snow + 1000; - } - - _lastCheck = now; - return rc; + //this uses a signed / unsigned trick to deal with millis overflow + unsigned long now = millis(); + signed long snow = (long)now; + int rc=DHCP_CHECK_NONE; + if (_lastCheck != 0){ + signed long factor; + //calc how many ms past the timeout we are + factor = snow - (long)_secTimeout; + //if on or passed the timeout, reduce the counters + if ( factor >= 0 ){ + //next timeout should be now plus 1000 ms minus parts of second in factor + _secTimeout = snow + 1000 - factor % 1000; + //how many seconds late are we, minimum 1 + factor = factor / 1000 +1; + + //reduce the counters by that mouch + //if we can assume that the cycle time (factor) is fairly constant + //and if the remainder is less than cycle time * 2 + //do it early instead of late + if(_renewInSec < factor*2 ) + _renewInSec = 0; + else + _renewInSec -= factor; + + if(_rebindInSec < factor*2 ) + _rebindInSec = 0; + else + _rebindInSec -= factor; + } + + //if we have a lease but should renew, do it + if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <=0){ + _dhcp_state = STATE_DHCP_REREQUEST; + init_new_DHCP_request(); + rc = 1; + } + + //if we have a lease or is renewing but should bind, do it + if( (_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <=0){ + //this should basically restart completely + _dhcp_state = STATE_DHCP_START; + reset_DHCP_lease(); + init_new_DHCP_request(); + rc = 2; + } + } + else{ + _secTimeout = snow + 1000; + } + + _lastCheck = now; + return rc; } IPAddress DhcpClass::getLocalIp() { - return IPAddress(_dhcpLocalIp); + return IPAddress(_dhcpLocalIp); } IPAddress DhcpClass::getSubnetMask() { - return IPAddress(_dhcpSubnetMask); + return IPAddress(_dhcpSubnetMask); } IPAddress DhcpClass::getGatewayIp() { - return IPAddress(_dhcpGatewayIp); + return IPAddress(_dhcpGatewayIp); } IPAddress DhcpClass::getDhcpServerIp() { - return IPAddress(_dhcpDhcpServerIp); + return IPAddress(_dhcpDhcpServerIp); } IPAddress DhcpClass::getDnsServerIp() { - return IPAddress(_dhcpDnsServerIp); + return IPAddress(_dhcpDnsServerIp); } void DhcpClass::printByte(char * buf, uint8_t n ) { - char *str = &buf[1]; - buf[0]='0'; - do { - unsigned long m = n; - n /= 16; - char c = m - 16 * n; - *str-- = c < 10 ? c + '0' : c + 'A' - 10; - } while(n); + char *str = &buf[1]; + buf[0]='0'; + do { + unsigned long m = n; + n /= 16; + char c = m - 16 * n; + *str-- = c < 10 ? c + '0' : c + 'A' - 10; + } while(n); } diff --git a/libraries/Ethernet/Dhcp.h b/libraries/Ethernet/Dhcp.h index 4a47936f0f2..5f333a275f0 100755 --- a/libraries/Ethernet/Dhcp.h +++ b/libraries/Ethernet/Dhcp.h @@ -13,6 +13,8 @@ #define STATE_DHCP_LEASED 3 #define STATE_DHCP_REREQUEST 4 #define STATE_DHCP_RELEASE 5 +#define STATE_DHCP_CONNECTION_FAILED 6 +#define STATE_DHCP_TIMEOUT 7 #define DHCP_FLAGSBROADCAST 0x8000 @@ -52,6 +54,8 @@ #define DHCP_CHECK_RENEW_OK (2) #define DHCP_CHECK_REBIND_FAIL (3) #define DHCP_CHECK_REBIND_OK (4) +#define DHCP_CHECK_RENEW_STARTED (1) +#define DHCP_CHECK_REBIND_STARTED (2) enum { @@ -138,41 +142,54 @@ typedef struct _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]; - uint32_t _dhcpLeaseTime; - uint32_t _dhcpT1, _dhcpT2; - signed long _renewInSec; - signed long _rebindInSec; - signed long _lastCheck; - unsigned long _timeout; - unsigned long _responseTimeout; - unsigned long _secTimeout; - 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); + 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]; + uint32_t _dhcpLeaseTime; + uint32_t _dhcpT1, _dhcpT2; + signed long _renewInSec; + signed long _rebindInSec; + signed long _lastCheck; + unsigned long _timeout; + unsigned long _responseTimeout; + unsigned long _secTimeout; + uint8_t _dhcp_state; + EthernetUDP _dhcpUdpSocket; + uint32_t _startTime; + uint32_t _startResponseTime; + + void init_new_DHCP_request(); + int request_DHCP_lease(); + int step_DHCP_lease(); + void reset_DHCP_lease(); + void presend_DHCP(); + void send_DHCP_MESSAGE(uint8_t, uint16_t); + void printByte(char *, uint8_t); + + uint8_t tryParseDHCPResponse(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(); + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + + // non blocking variant, call successful afterwards + void initialize(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) ; + + // 0 = not finished, 1 = finished and successful, 2 = finished but failed + int successful(); + + // 0 = nothing needed, 1 = renew started, 2 = rebind started, if >0 call successful to wait for + // finishing the rebind/release + int checkLease(); }; #endif diff --git a/libraries/Ethernet/Dns.cpp b/libraries/Ethernet/Dns.cpp index b3c1a9dc148..b6141549ff9 100644 --- a/libraries/Ethernet/Dns.cpp +++ b/libraries/Ethernet/Dns.cpp @@ -50,374 +50,400 @@ void DNSClient::begin(const IPAddress& aDNSServer) { - iDNSServer = aDNSServer; - iRequestId = 0; + 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; - } + // 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 DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) { + int result = startHostRequest(aHostname, aResult); + if (result > 0) + return result; + // Now wait for a response + int ret = 0; + while(ret == 0) + { + ret = getHostRequestResult(aResult); + if (ret == 0) { + delay(50); + } + } + return ret; +} +int DNSClient::startHostRequest(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; + 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) + { + _startResolving = millis(); + _resolving = 1; + return 0; + } + } + } + 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; + // 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; } +int DNSClient::getHostRequestResult(IPAddress& aAddress) { + if (!_resolving) { + return 2; + } + uint16_t ret = ProcessResponse(aAddress); + if (ret == SUCCESS) { + _resolving = 0; + iUdp.stop(); + return 1; + } + else if (ret == 255) { + if ((millis() - _startResolving) > 15000UL) { + _resolving = 0; + iUdp.stop(); + return 2; + } + } + else { + _resolving = 0; + iUdp.stop(); + return 2; + } + return 0; +} -uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) +uint16_t DNSClient::ProcessResponse( 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) != (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; + // Wait for a response packet + if(iUdp.parsePacket() <= 0) + return 255; + + // 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; } diff --git a/libraries/Ethernet/Dns.h b/libraries/Ethernet/Dns.h index 6bcb98ab9eb..9eb91236d7c 100644 --- a/libraries/Ethernet/Dns.h +++ b/libraries/Ethernet/Dns.h @@ -10,32 +10,55 @@ 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); + // 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); + + /** non blocking version of retrieving an IP for a hostname + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address, + in case @result == 1, else nothing will be assigned + @result + 1 if we already have an IP address, + 0 if we have started the dns lookup, start calling getHostRequestResult(), + else: an error code. + + */ + int startHostRequest(const char* aHostname, IPAddress& aResult); + /** get the result of the hostname lookup + @param aResult IPAddress structure to store the returned IP address, + in case @result == 1, else nothing will be assigned + @result + 1 if we have an IP address, + 0 if we do not have a response yet + else: an error occured + */ + int getHostRequestResult(IPAddress& aResult); protected: - uint16_t BuildRequest(const char* aName); - uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); + uint16_t BuildRequest(const char* aName); + uint16_t ProcessResponse(IPAddress& aAddress); - IPAddress iDNSServer; - uint16_t iRequestId; - EthernetUDP iUdp; + IPAddress iDNSServer; + uint16_t iRequestId; + EthernetUDP iUdp; + uint8_t _resolving; + uint32_t _startResolving; }; #endif diff --git a/libraries/Ethernet/Ethernet.cpp b/libraries/Ethernet/Ethernet.cpp index c31a85f0936..9b5620c4af8 100644 --- a/libraries/Ethernet/Ethernet.cpp +++ b/libraries/Ethernet/Ethernet.cpp @@ -4,119 +4,161 @@ // XXX: don't make assumptions about the value of MAX_SOCK_NUM. uint8_t EthernetClass::_state[MAX_SOCK_NUM] = { - 0, 0, 0, 0 }; + 0, 0, 0, 0 }; uint16_t EthernetClass::_server_port[MAX_SOCK_NUM] = { - 0, 0, 0, 0 }; + 0, 0, 0, 0 }; + +int EthernetClass::initialized() { + if (_dhcp == NULL || _initialized) { + return 1; + } + int result = _dhcp->successful(); + if (result == 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(); + _initialized = 1; + _releasing = 0; + } + if (result == 2) { + _initialized = 1; + _releasing = 0; + } + return result; +} + -int EthernetClass::begin(uint8_t *mac_address) +void EthernetClass::initialize(uint8_t *mac_address) { - static DhcpClass s_dhcp; - _dhcp = &s_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; + static DhcpClass s_dhcp; + _dhcp = &s_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 + _dhcp->beginWithDHCP(mac_address); + _initialized = 0; +} + +int EthernetClass::begin(uint8_t *mac_address) { + initialize(mac_address); + int result = 0; + while (result == 0) { + result = initialized(); + } + return result == 1; } void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) { - // Assume the DNS server will be the machine on the same network as the local IP - // but with last octet being '1' - IPAddress dns_server = local_ip; - dns_server[3] = 1; - begin(mac_address, local_ip, dns_server); + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(mac_address, local_ip, dns_server); } void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server) { - // 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_address, local_ip, dns_server, gateway); + // 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_address, local_ip, dns_server, gateway); } void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway) { - IPAddress subnet(255, 255, 255, 0); - begin(mac_address, local_ip, dns_server, gateway, subnet); + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, dns_server, gateway, subnet); } void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) { - W5100.init(); - W5100.setMACAddress(mac); - W5100.setIPAddress(local_ip._address); - W5100.setGatewayIp(gateway._address); - W5100.setSubnetMask(subnet._address); - _dnsServerAddress = dns_server; + W5100.init(); + W5100.setMACAddress(mac); + W5100.setIPAddress(local_ip._address); + W5100.setGatewayIp(gateway._address); + W5100.setSubnetMask(subnet._address); + _dnsServerAddress = dns_server; + _initialized = 1; + _releasing = 0; +} + +int EthernetClass::maintain() { + int result = maintainNeeded(); + if (result == DHCP_CHECK_NONE) { + return result; + } + if (result == DHCP_CHECK_REBIND_STARTED) { + result = 3; + } + int res = maintainFinished(); + while (res == 0) { + delay(10); + res = maintainFinished(); + } + return result + (res == 1 ? 1 : 0); +} + +int EthernetClass::maintainFinished() { + int result = _dhcp->successful(); + if (result == 1) { + _releasing = 0; + W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); + W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); + W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); + _dnsServerAddress = _dhcp->getDnsServerIp(); + } + return result; } -int EthernetClass::maintain(){ - int rc = DHCP_CHECK_NONE; - if(_dhcp != NULL){ - //we have a pointer to dhcp, use it - rc = _dhcp->checkLease(); - switch ( rc ){ - case DHCP_CHECK_NONE: - //nothing done - break; - case DHCP_CHECK_RENEW_OK: - case DHCP_CHECK_REBIND_OK: - //we might have got a new IP. - W5100.setIPAddress(_dhcp->getLocalIp().raw_address()); - W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address()); - W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address()); - _dnsServerAddress = _dhcp->getDnsServerIp(); - break; - default: - //this is actually a error, it will retry though - break; - } - } - return rc; +int EthernetClass::maintainNeeded(){ + int rc = DHCP_CHECK_NONE; + if(_initialized && _dhcp != NULL){ + if (_releasing) { + return 1; + } + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + if (rc != DHCP_CHECK_NONE) { + _releasing = 1; + return rc; + } + } + return rc; } IPAddress EthernetClass::localIP() { - IPAddress ret; - W5100.getIPAddress(ret.raw_address()); - return ret; + IPAddress ret; + W5100.getIPAddress(ret.raw_address()); + return ret; } IPAddress EthernetClass::subnetMask() { - IPAddress ret; - W5100.getSubnetMask(ret.raw_address()); - return ret; + IPAddress ret; + W5100.getSubnetMask(ret.raw_address()); + return ret; } IPAddress EthernetClass::gatewayIP() { - IPAddress ret; - W5100.getGatewayIp(ret.raw_address()); - return ret; + IPAddress ret; + W5100.getGatewayIp(ret.raw_address()); + return ret; } IPAddress EthernetClass::dnsServerIP() { - return _dnsServerAddress; + return _dnsServerAddress; } EthernetClass Ethernet; diff --git a/libraries/Ethernet/Ethernet.h b/libraries/Ethernet/Ethernet.h index 2a07ff35f7f..06816b3f117 100644 --- a/libraries/Ethernet/Ethernet.h +++ b/libraries/Ethernet/Ethernet.h @@ -12,28 +12,45 @@ class EthernetClass { private: - IPAddress _dnsServerAddress; - DhcpClass* _dhcp; + IPAddress _dnsServerAddress; + DhcpClass* _dhcp; + uint8_t _initialized; + uint8_t _releasing; public: - static uint8_t _state[MAX_SOCK_NUM]; - static uint16_t _server_port[MAX_SOCK_NUM]; - // 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 dns_server); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); - void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); - int maintain(); - - IPAddress localIP(); - IPAddress subnetMask(); - IPAddress gatewayIP(); - IPAddress dnsServerIP(); - - friend class EthernetClient; - friend class EthernetServer; + static uint8_t _state[MAX_SOCK_NUM]; + static uint16_t _server_port[MAX_SOCK_NUM]; + // 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 dns_server); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + + // non blocking initialization of the Ethershield through DHCP, call + // initialized afterwards + void initialize(uint8_t *mac_address); + // 0 = not initialized yet, 1 = successful initialization, 2 = DHCP failed + int initialized(); + + int maintain(); + + // non blocking check for maintain the DHCP lease. + // DHCP_CHECK_NONE = no maintain needed + // DHCP_CHECK_RENEW_STARTED = maintain started, call maintainFinished() + // DHCP_CHECK_REBIND_STARTED = maintain started, call maintainFinished() + int maintainNeeded(); + // 0 = not finished, 1 = finished and successful, 2 = finished but failed + int maintainFinished(); + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + + friend class EthernetClient; + friend class EthernetServer; }; extern EthernetClass Ethernet; diff --git a/libraries/Ethernet/EthernetClient.cpp b/libraries/Ethernet/EthernetClient.cpp index 9885efb7850..4693ec44035 100644 --- a/libraries/Ethernet/EthernetClient.cpp +++ b/libraries/Ethernet/EthernetClient.cpp @@ -2,7 +2,7 @@ #include "socket.h" extern "C" { - #include "string.h" +#include "string.h" } #include "Arduino.h" @@ -20,146 +20,217 @@ EthernetClient::EthernetClient() : _sock(MAX_SOCK_NUM) { EthernetClient::EthernetClient(uint8_t sock) : _sock(sock) { } +int EthernetClient::initializeConnection(const char *host, uint16_t port) { + if (_dns) { + return 0; // init already busy + } + IPAddress ip; + _dns = new DNSClient(); + _dns->begin(Ethernet.dnsServerIP()); + int res = _dns->startHostRequest(host, ip); + if (res == 0) { + _dnsresolved = 0; + _port = port; + return 1; + } + else if (res == 1) { + delete _dns; + _dns = NULL; + return initializeConnection(ip, port); + } + else { + delete _dns; + _dns = NULL; + return 0; + } +} int EthernetClient::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; - } + if (!initializeConnection(host, port)) { + return 0; + } + int result = 0; + while (result == 0) { + result = connectionInitialized(); + if (result == 0) { + delay(1); + } + } + return result == 1; +} + +int EthernetClient::initializeConnection(IPAddress ip, uint16_t port) { + _dnsresolved = 1; + if (_sock != MAX_SOCK_NUM) { + return 0; + } + + for (int i = 0; i < MAX_SOCK_NUM; i++) { + uint8_t s = W5100.readSnSR(i); + if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT || s == SnSR::CLOSE_WAIT) { + _sock = i; + break; + } + } + if (_sock == MAX_SOCK_NUM) + return 0; + + _srcport++; + if (_srcport == 0) _srcport = 1024; + socket(_sock, SnMR::TCP, _srcport, 0); + + if (!::connect(_sock, rawIPAddress(ip), port)) { + _sock = MAX_SOCK_NUM; + return 0; + } + _established = 0; + return 1; } int EthernetClient::connect(IPAddress ip, uint16_t port) { - if (_sock != MAX_SOCK_NUM) - return 0; - - for (int i = 0; i < MAX_SOCK_NUM; i++) { - uint8_t s = W5100.readSnSR(i); - if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT || s == SnSR::CLOSE_WAIT) { - _sock = i; - break; - } - } - - if (_sock == MAX_SOCK_NUM) - return 0; - - _srcport++; - if (_srcport == 0) _srcport = 1024; - socket(_sock, SnMR::TCP, _srcport, 0); - - if (!::connect(_sock, rawIPAddress(ip), port)) { - _sock = MAX_SOCK_NUM; - return 0; - } - - while (status() != SnSR::ESTABLISHED) { - delay(1); - if (status() == SnSR::CLOSED) { - _sock = MAX_SOCK_NUM; - return 0; - } - } - - return 1; + if (!initializeConnection(ip, port)) { + return 0; + } + int result = 0; + while (result == 0) { + result = connectionInitialized(); + if (result == 0) { + delay(1); + } + } + return result == 1; +} + +uint8_t EthernetClient::connectionInitialized() { + if (!_dnsresolved) { + IPAddress ip; + int result = _dns->getHostRequestResult(ip); + if (result == 1) { + _dnsresolved = 1; + delete _dns; + _dns = NULL; + return initializeConnection(ip, _port) == 1 ? 0 : 2; + } + else if (result > 1) { + delete _dns; + _dns = NULL; + _sock = MAX_SOCK_NUM; + return 2; + } + return 0; + } + else { + if (_established) { + return _established; + } + if (_sock == MAX_SOCK_NUM) { + return 2; + } + if (status() != SnSR::ESTABLISHED) { + _established = 0; + if (status() == SnSR::CLOSED) { + _sock = MAX_SOCK_NUM; + return 2; + } + } + else { + _established = 1; + } + return _established; + } } size_t EthernetClient::write(uint8_t b) { - return write(&b, 1); + return write(&b, 1); } size_t EthernetClient::write(const uint8_t *buf, size_t size) { - if (_sock == MAX_SOCK_NUM) { - setWriteError(); - return 0; - } - if (!send(_sock, buf, size)) { - setWriteError(); - return 0; - } - return size; + if (_sock == MAX_SOCK_NUM) { + setWriteError(); + return 0; + } + if (!send(_sock, buf, size)) { + setWriteError(); + return 0; + } + return size; } int EthernetClient::available() { - if (_sock != MAX_SOCK_NUM) - return W5100.getRXReceivedSize(_sock); - return 0; + if (_sock != MAX_SOCK_NUM) + return W5100.getRXReceivedSize(_sock); + return 0; } int EthernetClient::read() { - uint8_t b; - if ( recv(_sock, &b, 1) > 0 ) - { - // recv worked - return b; - } - else - { - // No data available - return -1; - } + uint8_t b; + if ( recv(_sock, &b, 1) > 0 ) + { + // recv worked + return b; + } + else + { + // No data available + return -1; + } } int EthernetClient::read(uint8_t *buf, size_t size) { - return recv(_sock, buf, size); + return recv(_sock, buf, size); } int EthernetClient::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; + 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 EthernetClient::flush() { - while (available()) - read(); + while (available()) + read(); } void EthernetClient::stop() { - if (_sock == MAX_SOCK_NUM) - return; + if (_sock == MAX_SOCK_NUM) + return; - // attempt to close the connection gracefully (send a FIN to other side) - disconnect(_sock); - unsigned long start = millis(); + // attempt to close the connection gracefully (send a FIN to other side) + disconnect(_sock); + unsigned long start = millis(); - // wait a second for the connection to close - while (status() != SnSR::CLOSED && millis() - start < 1000) - delay(1); + // wait a second for the connection to close + while (status() != SnSR::CLOSED && millis() - start < 1000) + delay(1); - // if it hasn't closed, close it forcefully - if (status() != SnSR::CLOSED) - close(_sock); + // if it hasn't closed, close it forcefully + if (status() != SnSR::CLOSED) + close(_sock); - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; } uint8_t EthernetClient::connected() { - if (_sock == MAX_SOCK_NUM) return 0; - - uint8_t s = status(); - return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT || - (s == SnSR::CLOSE_WAIT && !available())); + if (_sock == MAX_SOCK_NUM) return 0; + if (_dnsresolved == 0) return 0; + if (_established == 0) return 0; + + uint8_t s = status(); + return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT || + (s == SnSR::CLOSE_WAIT && !available())); } uint8_t EthernetClient::status() { - if (_sock == MAX_SOCK_NUM) return SnSR::CLOSED; - return W5100.readSnSR(_sock); + if (_sock == MAX_SOCK_NUM) return SnSR::CLOSED; + return W5100.readSnSR(_sock); } // the next function allows us to use the client returned by // EthernetServer::available() as the condition in an if-statement. EthernetClient::operator bool() { - return _sock != MAX_SOCK_NUM; + return _sock != MAX_SOCK_NUM; } diff --git a/libraries/Ethernet/EthernetClient.h b/libraries/Ethernet/EthernetClient.h index 44740fea7d0..a307053c24c 100644 --- a/libraries/Ethernet/EthernetClient.h +++ b/libraries/Ethernet/EthernetClient.h @@ -3,35 +3,51 @@ #include "Arduino.h" #include "Print.h" #include "Client.h" +#include "Dns.h" #include "IPAddress.h" class EthernetClient : public Client { public: - EthernetClient(); - EthernetClient(uint8_t sock); - - 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(); - - friend class EthernetServer; - - using Print::write; + EthernetClient(); + EthernetClient(uint8_t sock); + + 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(); + + + // non blocking variant of the connect method, + // 0 = failed, 1 = successful initialization, call connectionInitialized() + virtual int initializeConnection(IPAddress ip, uint16_t port); + // non blocking variant of the connect & DNS method, call connectionInitialized() + // 0 = failed, 1 = successful initialization, call connectionInitialized() + virtual int initializeConnection(const char *host, uint16_t port); + // 0 = still working, 1 = successful connection, 2 = connection failed + virtual uint8_t connectionInitialized(); + + friend class EthernetServer; + + using Print::write; private: - static uint16_t _srcport; - uint8_t _sock; + static uint16_t _srcport; + uint8_t _sock; + uint8_t _established; + uint8_t _dnsresolved; + uint16_t _port; + + DNSClient* _dns; }; #endif diff --git a/libraries/Ethernet/EthernetServer.cpp b/libraries/Ethernet/EthernetServer.cpp index 0308b92619f..37e46fa6aec 100644 --- a/libraries/Ethernet/EthernetServer.cpp +++ b/libraries/Ethernet/EthernetServer.cpp @@ -10,82 +10,82 @@ extern "C" { EthernetServer::EthernetServer(uint16_t port) { - _port = port; + _port = port; } void EthernetServer::begin() { - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - if (client.status() == SnSR::CLOSED) { - socket(sock, SnMR::TCP, _port, 0); - listen(sock); - EthernetClass::_server_port[sock] = _port; - break; - } - } + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { + EthernetClient client(sock); + if (client.status() == SnSR::CLOSED) { + socket(sock, SnMR::TCP, _port, 0); + listen(sock); + EthernetClass::_server_port[sock] = _port; + break; + } + } } void EthernetServer::accept() { - int listening = 0; - - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - - if (EthernetClass::_server_port[sock] == _port) { - if (client.status() == SnSR::LISTEN) { - listening = 1; - } - else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) { - client.stop(); - } - } - } - - if (!listening) { - begin(); - } + int listening = 0; + + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { + EthernetClient client(sock); + + if (EthernetClass::_server_port[sock] == _port) { + if (client.status() == SnSR::LISTEN) { + listening = 1; + } + else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) { + client.stop(); + } + } + } + + if (!listening) { + begin(); + } } EthernetClient EthernetServer::available() { - accept(); - - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - if (EthernetClass::_server_port[sock] == _port && - (client.status() == SnSR::ESTABLISHED || - client.status() == SnSR::CLOSE_WAIT)) { - if (client.available()) { - // XXX: don't always pick the lowest numbered socket. - return client; - } - } - } - - return EthernetClient(MAX_SOCK_NUM); + accept(); + + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { + EthernetClient client(sock); + if (EthernetClass::_server_port[sock] == _port && + (client.status() == SnSR::ESTABLISHED || + client.status() == SnSR::CLOSE_WAIT)) { + if (client.available()) { + // XXX: don't always pick the lowest numbered socket. + return client; + } + } + } + + return EthernetClient(MAX_SOCK_NUM); } size_t EthernetServer::write(uint8_t b) { - return write(&b, 1); + return write(&b, 1); } size_t EthernetServer::write(const uint8_t *buffer, size_t size) { - size_t n = 0; - - accept(); - - for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { - EthernetClient client(sock); - - if (EthernetClass::_server_port[sock] == _port && - client.status() == SnSR::ESTABLISHED) { - n += client.write(buffer, size); - } - } - - return n; + size_t n = 0; + + accept(); + + for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { + EthernetClient client(sock); + + if (EthernetClass::_server_port[sock] == _port && + client.status() == SnSR::ESTABLISHED) { + n += client.write(buffer, size); + } + } + + return n; } diff --git a/libraries/Ethernet/EthernetServer.h b/libraries/Ethernet/EthernetServer.h index 86ccafe9690..0afb67d5845 100644 --- a/libraries/Ethernet/EthernetServer.h +++ b/libraries/Ethernet/EthernetServer.h @@ -8,15 +8,15 @@ class EthernetClient; class EthernetServer : public Server { private: - uint16_t _port; - void accept(); + uint16_t _port; + void accept(); public: - EthernetServer(uint16_t); - 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; + EthernetServer(uint16_t); + 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; }; #endif diff --git a/libraries/Ethernet/EthernetUdp.cpp b/libraries/Ethernet/EthernetUdp.cpp index 37600529f8b..214e6805604 100644 --- a/libraries/Ethernet/EthernetUdp.cpp +++ b/libraries/Ethernet/EthernetUdp.cpp @@ -37,182 +37,182 @@ EthernetUDP::EthernetUDP() : _sock(MAX_SOCK_NUM) {} /* Start EthernetUDP socket, listening at local port PORT */ uint8_t EthernetUDP::begin(uint16_t port) { - if (_sock != MAX_SOCK_NUM) - return 0; + if (_sock != MAX_SOCK_NUM) + return 0; - for (int i = 0; i < MAX_SOCK_NUM; i++) { - uint8_t s = W5100.readSnSR(i); - if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) { - _sock = i; - break; - } - } + for (int i = 0; i < MAX_SOCK_NUM; i++) { + uint8_t s = W5100.readSnSR(i); + if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) { + _sock = i; + break; + } + } - if (_sock == MAX_SOCK_NUM) - return 0; + if (_sock == MAX_SOCK_NUM) + return 0; - _port = port; - _remaining = 0; - socket(_sock, SnMR::UDP, _port, 0); + _port = port; + _remaining = 0; + socket(_sock, SnMR::UDP, _port, 0); - return 1; + return 1; } /* return number of bytes available in the current packet, will return zero if parsePacket hasn't been called yet */ int EthernetUDP::available() { - return _remaining; + return _remaining; } /* Release any resources being used by this EthernetUDP instance */ void EthernetUDP::stop() { - if (_sock == MAX_SOCK_NUM) - return; + if (_sock == MAX_SOCK_NUM) + return; - close(_sock); + close(_sock); - EthernetClass::_server_port[_sock] = 0; - _sock = MAX_SOCK_NUM; + EthernetClass::_server_port[_sock] = 0; + _sock = MAX_SOCK_NUM; } int EthernetUDP::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; - } + // 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 EthernetUDP::beginPacket(IPAddress ip, uint16_t port) { - _offset = 0; - return startUDP(_sock, rawIPAddress(ip), port); + _offset = 0; + return startUDP(_sock, rawIPAddress(ip), port); } int EthernetUDP::endPacket() { - return sendUDP(_sock); + return sendUDP(_sock); } size_t EthernetUDP::write(uint8_t byte) { - return write(&byte, 1); + return write(&byte, 1); } size_t EthernetUDP::write(const uint8_t *buffer, size_t size) { - uint16_t bytes_written = bufferData(_sock, _offset, buffer, size); - _offset += bytes_written; - return bytes_written; + uint16_t bytes_written = bufferData(_sock, _offset, buffer, size); + _offset += bytes_written; + return bytes_written; } int EthernetUDP::parsePacket() { - // discard any remaining bytes in the last packet - flush(); - - if (W5100.getRXReceivedSize(_sock) > 0) - { - //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]; - _remaining = tmpBuf[6]; - _remaining = (_remaining << 8) + tmpBuf[7]; - - // When we get here, any remaining bytes are the data - ret = _remaining; - } - return ret; - } - // There aren't any packets available - return 0; + // discard any remaining bytes in the last packet + flush(); + + if (W5100.getRXReceivedSize(_sock) > 0) + { + //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]; + _remaining = tmpBuf[6]; + _remaining = (_remaining << 8) + tmpBuf[7]; + + // When we get here, any remaining bytes are the data + ret = _remaining; + } + return ret; + } + // There aren't any packets available + return 0; } int EthernetUDP::read() { - uint8_t byte; + uint8_t byte; - if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0)) - { - // We read things without any problems - _remaining--; - return byte; - } + if ((_remaining > 0) && (recv(_sock, &byte, 1) > 0)) + { + // We read things without any problems + _remaining--; + return byte; + } - // If we get here, there's no data available - return -1; + // If we get here, there's no data available + return -1; } int EthernetUDP::read(unsigned char* buffer, size_t len) { - if (_remaining > 0) - { + if (_remaining > 0) + { - int got; + int got; - if (_remaining <= len) - { - // data should fit in the buffer - got = recv(_sock, buffer, _remaining); - } - else - { - // too much data for the buffer, - // grab as much as will fit - got = recv(_sock, buffer, len); - } + if (_remaining <= len) + { + // data should fit in the buffer + got = recv(_sock, buffer, _remaining); + } + else + { + // too much data for the buffer, + // grab as much as will fit + got = recv(_sock, buffer, len); + } - if (got > 0) - { - _remaining -= got; - return got; - } + if (got > 0) + { + _remaining -= got; + return got; + } - } + } - // If we get here, there's no data available or recv failed - return -1; + // If we get here, there's no data available or recv failed + return -1; } int EthernetUDP::peek() { - uint8_t b; - // Unlike recv, peek doesn't check to see if there's any data available, so we must. - // If the user hasn't called parsePacket yet then return nothing otherwise they - // may get the UDP header - if (!_remaining) - return -1; - ::peek(_sock, &b); - return b; + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must. + // If the user hasn't called parsePacket yet then return nothing otherwise they + // may get the UDP header + if (!_remaining) + return -1; + ::peek(_sock, &b); + return b; } void EthernetUDP::flush() { - // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? - // should only occur if recv fails after telling us the data is there, lets - // hope the w5100 always behaves :) - - while (_remaining) - { - read(); - } + // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? + // should only occur if recv fails after telling us the data is there, lets + // hope the w5100 always behaves :) + + while (_remaining) + { + read(); + } } diff --git a/libraries/Ethernet/EthernetUdp.h b/libraries/Ethernet/EthernetUdp.h index 8a6b7ab5a8b..9d69412ef41 100644 --- a/libraries/Ethernet/EthernetUdp.h +++ b/libraries/Ethernet/EthernetUdp.h @@ -43,57 +43,57 @@ class EthernetUDP : public UDP { 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 - uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + 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 + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed public: - EthernetUDP(); // 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 void stop(); // Finish with the UDP socket + EthernetUDP(); // 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 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; + // Sending UDP packets - // 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 + // 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); - // 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; }; + 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; }; }; #endif