Exemplo n.º 1
0
/*****************************************************************************
  Function:
	void TCPIP_SMTP_Flush(void)

  Description:
	Flushes the SMTP socket and forces all data to be sent.

  Precondition:
	TCPIP_SMTP_UsageBegin returned true on a previous call.

  Parameters:
	None

  Returns:
	None
	
  Remarks:
	This function should only be called externally when the SMTP client is
	generating an on-the-fly message.  (That is, TCPIP_SMTP_MailSend was called
	with SMTPClient.Body set to NULL.)
  ***************************************************************************/
void TCPIP_SMTP_Flush(void)
{
	TCPIP_TCP_Flush(MySocket);
}
Exemplo n.º 2
0
static void TCPIP_SMTP_ClientProcess(void)
{
    uint8_t			    i;
    uint16_t			w;
    uint8_t			    vBase64Buffer[4];
    static uint32_t	    SMTPTimer;

    static uint8_t		RXBuffer[4];
    static const uint8_t *ROMStrPtr, *ROMStrPtr2;
    static const uint8_t *RAMStrPtr;
    static uint16_t		wAddressLength;
    TCPIP_DNS_RESULT    dnsRes;
    DNS_RESOLVE_TYPE    dnsType;

    switch(TransportState)
    {
        case TRANSPORT_HOME:
            // TCPIP_SMTP_UsageBegin() is the only function which will kick
            // the state machine into the next state
            break;

        case TRANSPORT_BEGIN:
            // Wait for the user to program all the pointers and then
            // call TCPIP_SMTP_MailSend()
            if(!SMTPFlags.bits.ReadyToStart)
            {
                break;
            }

            SMTPClient.Server = FindEmailServer(&SMTPClient, &dnsType);
            // See if we found a hostname anywhere which we could resolve
            if(!(SMTPClient.Server))
            {
                ResponseCode = SMTP_RESOLVE_ERROR;
                TransportState = TRANSPORT_HOME;
                break;
            }

            // check for a plain IP address
            if(TCPIP_Helper_StringToIPAddress(SMTPClient.Server, &SMTPServer))
            {
                TransportState = TRANSPORT_OBTAIN_SOCKET;
                break;
            }

            // use DNS to resolve the name
            TCPIP_DNS_Resolve(SMTPClient.Server, dnsType);
            SMTPTimer = SYS_TMR_TickCountGet();

            TransportState++;
            break;

        case TRANSPORT_NAME_RESOLVE:
            // Wait for the DNS server to return the requested IP address
            dnsRes = TCPIP_DNS_IsResolved((const char*)SMTPClient.Server,&SMTPServer);
            if(dnsRes == DNS_RES_PENDING)
            {
                break;
            }
            if(dnsRes < 0)
            {   // some error occurred
                ResponseCode = SMTP_RESOLVE_ERROR;
                TransportState = TRANSPORT_HOME;
                break;
            }
            
            // DNS_RES_OK
            TransportState++;
            // No need to break here

        case TRANSPORT_OBTAIN_SOCKET:
            // Connect a TCP socket to the remote SMTP server
            MySocket = TCPIP_TCP_ClientOpen(IP_ADDRESS_TYPE_IPV4, SMTPClient.ServerPort, (IP_MULTI_ADDRESS*)&SMTPServer.Val);

            // Abort operation if no TCP socket could be opened.
            // If this ever happens, you need to update your tcp_config.h
            if(MySocket == INVALID_SOCKET)
                break;

            TCPIP_TCP_SignalHandlerRegister(MySocket, TCPIP_TCP_SIGNAL_RX_DATA, _SMTPSocketRxSignalHandler, 0);

            TransportState++;
            SMTPTimer = SYS_TMR_TickCountGet();

            // No break; fall into TRANSPORT_SOCKET_OBTAINED
			
        case TRANSPORT_SOCKET_OBTAINED:
            if(!TCPIP_TCP_IsConnected(MySocket))
            {
                // Don't stick around in the wrong state if the
                // server was connected, but then disconnected us.
                // Also time out if we can't establish the connection
                // to the SMTP server
                if(SMTPFlags.bits.ConnectedOnce || ((SYS_TMR_TickCountGet()-SMTPTimer) > (TCPIP_SMTP_SERVER_REPLY_TIMEOUT * SYS_TMR_TickCounterFrequencyGet())))
                {
                    ResponseCode = SMTP_CONNECT_ERROR;
                    TransportState = TRANSPORT_CLOSE;
                }

                break;
            }
            SMTPFlags.bits.ConnectedOnce = true;

            // See if the server sent us anything
            while(TCPIP_TCP_GetIsReady(MySocket))
            {
                TCPIP_TCP_Get(MySocket, &i);
                switch(RXParserState)
                {
                    case RX_BYTE_0:
                    case RX_BYTE_1:
                    case RX_BYTE_2:
                        RXBuffer[RXParserState] = i;
                        RXParserState++;
                        break;
	
                    case RX_BYTE_3:
                        switch(i)
                        {
                            case ' ':
                                SMTPFlags.bits.RXSkipResponse = false;
                                RXParserState++;
                                break;
                            case '-':
                                SMTPFlags.bits.RXSkipResponse = true;
                                RXParserState++;
                                break;
                            case '\r':
                                RXParserState = RX_SEEK_LF;
                                break;
                        }
                        break;
	
					case RX_SEEK_CR:
                                            if(i == '\r')
                                                    RXParserState++;
                                            break;
	
					case RX_SEEK_LF:
						// If we received the whole command
						if(i == '\n')
						{
							RXParserState = RX_BYTE_0;

							if(!SMTPFlags.bits.RXSkipResponse)
							{
								// The server sent us a response code
								// Null terminate the ASCII reponse code so we can convert it to an integer
								RXBuffer[3] = 0;
								ResponseCode = atoi((char*)RXBuffer);

								// Handle the response
								switch(SMTPState)
								{
									case SMTP_HELO_ACK:
										if(ResponseCode >= 200u && ResponseCode <= 299u)
										{
											if(SMTPClient.Username)
												SMTPState = SMTP_AUTH_LOGIN;
											else
												SMTPState = SMTP_MAILFROM;
										}
										else
											SMTPState = SMTP_QUIT_INIT;
										break;


									case SMTP_AUTH_LOGIN_ACK:
									case SMTP_AUTH_USERNAME_ACK:
										if(ResponseCode == 334u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;

									case SMTP_AUTH_PASSWORD_ACK:
										if(ResponseCode == 235u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;

									case SMTP_HOME:
									case SMTP_MAILFROM_ACK:
									case SMTP_RCPTTO_ACK:
									case SMTP_RCPTTOCC_ACK:
									case SMTP_RCPTTOBCC_ACK:
										if(ResponseCode >= 200u && ResponseCode <= 299u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;
							
									case SMTP_DATA_ACK:
										if(ResponseCode == 354u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;
							
									case SMTP_DATA_BODY_ACK:
										if(ResponseCode >= 200u && ResponseCode <= 299u)
											SMTPFlags.bits.SentSuccessfully = true;
							
										SMTPState = SMTP_QUIT_INIT;
										break;

									// Default case needed to supress compiler diagnostics
									default:
										break;
								}
							}
						}
						else if(i != '\r')
							RXParserState--;
	
						break;
				}
			}

			// Generate new data in the TX buffer, as needed, if possible
			if(TCPIP_TCP_PutIsReady(MySocket) < 64u)
				break;

			switch(SMTPState)
			{
				case SMTP_HELO:
					if(SMTPClient.Username == NULL)
						TCPIP_TCP_StringPut(MySocket, (uint8_t*)"HELO MCHPBOARD\r\n");
					else    
						TCPIP_TCP_StringPut(MySocket, (uint8_t*)"EHLO MCHPBOARD\r\n");
					TCPIP_TCP_Flush(MySocket);
					SMTPState++;
					break;

				case SMTP_AUTH_LOGIN:
					// Note: This state is only entered from SMTP_HELO_ACK if the application 
					// has specified a Username to use (SMTPClient.Username is non-NULL)
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)"AUTH LOGIN\r\n");
					TCPIP_TCP_Flush(MySocket);
					SMTPState++;
					break;

				case SMTP_AUTH_USERNAME:
					// Base 64 encode and transmit the username.
                    RAMStrPtr = (uint8_t*)SMTPClient.Username;
                    w = strlen((char*)RAMStrPtr);

					while(w)
					{
						i = 0;
						while((i < w) && (i < sizeof(vBase64Buffer)*3/4))
						{
                            vBase64Buffer[i] = *RAMStrPtr++;
							i++;
						}
						w -= i;					
						TCPIP_Helper_Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer));
						TCPIP_TCP_ArrayPut(MySocket, vBase64Buffer, sizeof(vBase64Buffer));
					}
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)"\r\n");
					TCPIP_TCP_Flush(MySocket);
					SMTPState++;
					break;

				case SMTP_AUTH_PASSWORD:
					// Base 64 encode and transmit the password
                    RAMStrPtr = (uint8_t*)SMTPClient.Password;
                    w = strlen((char*)RAMStrPtr);

					while(w)
					{
						i = 0;
						while((i < w) && (i < sizeof(vBase64Buffer)*3/4))
						{
                            vBase64Buffer[i] = *RAMStrPtr++;
							i++;
						}
						w -= i;					
						TCPIP_Helper_Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer));
						TCPIP_TCP_ArrayPut(MySocket, vBase64Buffer, sizeof(vBase64Buffer));
					}
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)"\r\n");
					TCPIP_TCP_Flush(MySocket);
					SMTPState++;
					break;

				case SMTP_MAILFROM:
					// Send MAIL FROM header.  Note that this is for the SMTP server validation, 
					// not what actually will be displayed in the recipients mail client as a 
					// return address.
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)"MAIL FROM:<");
                    RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.From, &wAddressLength);
                    TCPIP_TCP_ArrayPut(MySocket, RAMStrPtr, wAddressLength);
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)">\r\n");
					TCPIP_TCP_Flush(MySocket);
					SMTPState++;
					break;

				case SMTP_RCPTTO_INIT:
					// See if there are any (To) recipients to process
					if(SMTPClient.To)
					{
						RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.To, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTO;
							break;
						}
					}
					
					SMTPState = SMTP_RCPTTOCC_INIT;
					break;

				case SMTP_RCPTTO:
				case SMTP_RCPTTOCC:
				case SMTP_RCPTTOBCC:
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)"RCPT TO:<");
                    TCPIP_TCP_ArrayPut(MySocket, RAMStrPtr, wAddressLength);
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)">\r\n");
					TCPIP_TCP_Flush(MySocket);
					SMTPState++;
					break;

				case SMTP_RCPTTO_ISDONE:
					// See if we have any more (To) recipients to process
					// If we do, we must roll back a couple of states
                    RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);
	
					if(wAddressLength)
					{
						SMTPState = SMTP_RCPTTO;
						break;
					}

					// All done with To field
					SMTPState++;
					//No break

				case SMTP_RCPTTOCC_INIT:
					// See if there are any Carbon Copy (CC) recipients to process
					if(SMTPClient.CC)
					{
						RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.CC, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTOCC;
							break;
						}
					}
					
					SMTPState = SMTP_RCPTTOBCC_INIT;
					break;

				case SMTP_RCPTTOCC_ISDONE:
					// See if we have any more Carbon Copy (CC) recipients to process
					// If we do, we must roll back a couple of states
                    RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

					if(wAddressLength)
					{
						SMTPState = SMTP_RCPTTOCC;
						break;
					}

					// All done with CC field
					SMTPState++;
					//No break

				case SMTP_RCPTTOBCC_INIT:
					// See if there are any Blind Carbon Copy (BCC) recipients to process
					if(SMTPClient.BCC)
					{
						RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.BCC, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTOBCC;
							break;
						}
					}

					// All done with BCC field
					SMTPState = SMTP_DATA;
					break;

				case SMTP_RCPTTOBCC_ISDONE:
					// See if we have any more Blind Carbon Copy (CC) recipients to process
					// If we do, we must roll back a couple of states
                    RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

					if(wAddressLength)
					{
						SMTPState = SMTP_RCPTTOBCC;
						break;
					}

					// All done with BCC field
					SMTPState++;
					//No break

				case SMTP_DATA:
					TCPIP_TCP_StringPut(MySocket, (uint8_t*)"DATA\r\n");
					SMTPState++;
					PutHeadersState = PUTHEADERS_FROM_INIT;
					TCPIP_TCP_Flush(MySocket);
					break;

				case SMTP_DATA_HEADER:
					while((PutHeadersState != PUTHEADERS_DONE) && (TCPIP_TCP_PutIsReady(MySocket) > 64u))
					{
						switch(PutHeadersState)
						{
							case PUTHEADERS_FROM_INIT:
								if(SMTPClient.From)
								{
									PutHeadersState = PUTHEADERS_FROM;
									TCPIP_TCP_StringPut(MySocket, (uint8_t*)"From: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_TO_INIT;
								}
								break;
								
							case PUTHEADERS_FROM:
                                SMTPClient.From = (char*)TCPIP_TCP_StringPut(MySocket, (uint8_t*)SMTPClient.From);
                                if(*SMTPClient.From == 0u)
                                    PutHeadersState = PUTHEADERS_TO_INIT;
								break;

							case PUTHEADERS_TO_INIT:
								if(SMTPClient.To)
								{
									PutHeadersState = PUTHEADERS_TO;
									TCPIP_TCP_StringPut(MySocket, (uint8_t*)"\r\nTo: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_CC_INIT;
								}
								break;
								
							case PUTHEADERS_TO:
                                SMTPClient.To = (char*)TCPIP_TCP_StringPut(MySocket, (uint8_t*)SMTPClient.To);
                                if(*SMTPClient.To == 0u)
                                    PutHeadersState = PUTHEADERS_CC_INIT;
								break;

							case PUTHEADERS_CC_INIT:
								if(SMTPClient.CC)
								{
									PutHeadersState = PUTHEADERS_CC;
									TCPIP_TCP_StringPut(MySocket, (uint8_t*)"\r\nCC: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_SUBJECT_INIT;
								}
								break;
								
							case PUTHEADERS_CC:
                                SMTPClient.CC = (char*)TCPIP_TCP_StringPut(MySocket, (uint8_t*)SMTPClient.CC);
                                if(*SMTPClient.CC == 0u)
                                    PutHeadersState = PUTHEADERS_SUBJECT_INIT;
								break;

							case PUTHEADERS_SUBJECT_INIT:
								if(SMTPClient.Subject)
								{
									PutHeadersState = PUTHEADERS_SUBJECT;
									TCPIP_TCP_StringPut(MySocket, (uint8_t*)"\r\nSubject: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_OTHER_INIT;
								}
								break;
								
							case PUTHEADERS_SUBJECT:
                                SMTPClient.Subject = (char*)TCPIP_TCP_StringPut(MySocket, (uint8_t*)SMTPClient.Subject);
                                if(*SMTPClient.Subject == 0u)
                                    PutHeadersState = PUTHEADERS_OTHER_INIT;
								break;

							case PUTHEADERS_OTHER_INIT:
								TCPIP_TCP_ArrayPut(MySocket, (uint8_t*)"\r\n", 2);
								if(SMTPClient.OtherHeaders)
								{
									PutHeadersState = PUTHEADERS_OTHER;
								}
								else
								{
									TCPIP_TCP_ArrayPut(MySocket, (uint8_t*)"\r\n", 2);
									PutHeadersState = PUTHEADERS_DONE;
									SMTPState++;
								}
								break;
								
							case PUTHEADERS_OTHER:
                                SMTPClient.OtherHeaders = (char*)TCPIP_TCP_StringPut(MySocket, (uint8_t*)SMTPClient.OtherHeaders);
                                if(*SMTPClient.OtherHeaders == 0u)
                                {
                                    TCPIP_TCP_ArrayPut(MySocket, (uint8_t*)"\r\n", 2);
                                    PutHeadersState = PUTHEADERS_DONE;
                                    SMTPState++;
                                }
								break;
							
							// Default case needed to supress compiler diagnostics
							default:
								break;
						}
					}
					TCPIP_TCP_Flush(MySocket);
					break;
		
				case SMTP_DATA_BODY_INIT:
					SMTPState++;
					RAMStrPtr = (uint8_t*)SMTPClient.Body;
					ROMStrPtr2 = (const uint8_t*)"\r\n.\r\n";
					CRPeriod.Pos = NULL;
					if(RAMStrPtr)
						CRPeriod.Pos = (uint8_t*)strstr((char*)RAMStrPtr, (const char*)"\r\n.");
					// No break here
		
				case SMTP_DATA_BODY:
					if(SMTPClient.Body)
					{
						if(*ROMStrPtr2)
						{
							// Put the application data, doing the transparancy replacement of "\r\n." with "\r\n.."
							while(CRPeriod.Pos)
							{
								CRPeriod.Pos += 3;
								RAMStrPtr += TCPIP_TCP_ArrayPut(MySocket, RAMStrPtr, CRPeriod.Pos-RAMStrPtr);
								if(RAMStrPtr == CRPeriod.Pos)
								{
									if(!TCPIP_TCP_Put(MySocket, '.'))
									{
										CRPeriod.Pos -= 3;
										break;
									}
								}
								else
								{
									CRPeriod.Pos -= 3;
									break;
								}
								CRPeriod.Pos = (uint8_t*)strstr((char*)RAMStrPtr, (const char*)"\r\n.");
							}
							
							// If we get down here, either all replacements have been made or there is no remaining space in the TCP output buffer
							RAMStrPtr = TCPIP_TCP_StringPut(MySocket, RAMStrPtr);
							ROMStrPtr2 = TCPIP_TCP_StringPut(MySocket, (uint8_t*)ROMStrPtr2);
							TCPIP_TCP_Flush(MySocket);
						}
					}
					else
					{
						if(SMTPFlags.bits.ReadyToFinish)
						{
							if(*ROMStrPtr2)
							{
								ROMStrPtr2 = TCPIP_TCP_StringPut(MySocket, (uint8_t*)ROMStrPtr2);
								TCPIP_TCP_Flush(MySocket);
							}
		
						}
					}

					if(*ROMStrPtr2 == 0u)
					{
						SMTPState++;
					}
					break;
		
				case SMTP_QUIT_INIT:
					SMTPState++;
					ROMStrPtr = (const uint8_t*)"QUIT\r\n";
					// No break here

				case SMTP_QUIT:
					if(*ROMStrPtr)
					{
						ROMStrPtr = TCPIP_TCP_StringPut(MySocket, (uint8_t*)ROMStrPtr);
						TCPIP_TCP_Flush(MySocket);
					}

					if(*ROMStrPtr == 0u)
					{
						TransportState = TRANSPORT_CLOSE;
					}
					break;
				
				// Default case needed to supress compiler diagnostics
				default:
					break;
			}
			break;

		case TRANSPORT_CLOSE:
			// Close the socket so it can be used by other modules
			TCPIP_TCP_Close(MySocket);
			MySocket = INVALID_SOCKET;

			// Go back to doing nothing
			TransportState = TRANSPORT_HOME;
			break;
	}
}
Exemplo n.º 3
0
// ddns_manager.h
void TCPIP_DDNS_Task(void)
{
    uint8_t                 i;
    uint16_t wPos;
    DNS_RESULT          dnsRes;

    static enum
    {
        SM_IDLE = 0u,
        SM_BEGIN_CHECKIP,
        SM_DNS_START_RESOLVE,
        SM_DNS_WAIT_RESOLVE,
        SM_CHECKIP_SKT_OBTAINED,
        SM_CHECKIP_FIND_DELIMITER,
        SM_CHECKIP_FIND_ADDRESS,
        SM_CHECKIP_DISCONNECT,
        SM_IP_UPDATE_HOME,
        SM_IP_UPDATE_WAIT_DNS,
        SM_IP_UPDATE_SKT_OBTAINED,

        /*
            HTTP request msg is divided into 6 parts
            SM_IP_UPDATE_REQ_A,B,C,D,E,F as the tcp ip tx
            buffer is only able to carry 200 bytes at a time.
        */

        SM_IP_UPDATE_REQ_A,             //0x8
        SM_IP_UPDATE_REQ_B,             //0x9
        SM_IP_UPDATE_REQ_C,             //0xa
        SM_IP_UPDATE_REQ_D,             //0xb
        SM_IP_UPDATE_REQ_E,             //0xc
        SM_IP_UPDATE_REQ_F,             //0xd

        SM_IPUPDATE_FIND_RESPONSE,      //0xe
        SM_IPUPDATE_PARSE_RESPONSE,     //0xf
        SM_IPUDATE_DISCONNECT,          //0x10
        SM_DONE,                        // Done, try again in 10 minutes
        SM_DNS_ERROR,                   // DNS resolver error, try again in 30 seconds
        SM_SKT_ERROR,                   // socket open error, try again in 30 seconds
        SM_SOFT_ERROR,                  // Soft error, try again in 30 seconds
        SM_SYSTEM_ERROR                 // System error, try again in 30 minutes
    } smDDNS = SM_IDLE;

    switch(smDDNS)
    {
        case SM_IDLE:

            // Wait for timeout to begin IP check
            if(SYS_TICK_Get() > dwUpdateAt)
                break;

            // Otherwise, continue to next state
            smDDNS = SM_BEGIN_CHECKIP;

        case SM_BEGIN_CHECKIP:

            // If a fatal error has occurred, abort to the SM_DONE state and keep
            // the error message.
            if(lastStatus >= DDNS_STATUS_ABUSE && lastStatus <= DDNS_STATUS_911)
            {
                smDDNS = SM_DONE;
                break;
            }

            // If DDNSClient is not properly configured, abort
            if(
                // Verify that each pointer is not null, and is not empty
                (DDNSClient.ROMPointers.Host && (!DDNSClient.Host.szROM || *DDNSClient.Host.szROM == '\0') ) ||
                (!DDNSClient.ROMPointers.Host && (!DDNSClient.Host.szRAM || *DDNSClient.Host.szRAM == '\0') ) ||
                (DDNSClient.ROMPointers.Username && (!DDNSClient.Username.szROM || *DDNSClient.Username.szROM == '\0') ) ||
                (!DDNSClient.ROMPointers.Username && (!DDNSClient.Username.szRAM || *DDNSClient.Username.szRAM == '\0') ) ||
                (DDNSClient.ROMPointers.Password && (!DDNSClient.Password.szROM || *DDNSClient.Password.szROM == '\0') ) ||
                (!DDNSClient.ROMPointers.Password && (!DDNSClient.Password.szRAM || *DDNSClient.Password.szRAM == '\0') ) ||
                (DDNSClient.ROMPointers.CheckIPServer && (!DDNSClient.CheckIPServer.szROM || *DDNSClient.CheckIPServer.szROM == '\0') ) ||
                (!DDNSClient.ROMPointers.CheckIPServer && (!DDNSClient.CheckIPServer.szRAM || *DDNSClient.CheckIPServer.szRAM == '\0') ) ||
                (DDNSClient.ROMPointers.UpdateServer && (!DDNSClient.UpdateServer.szROM || *DDNSClient.UpdateServer.szROM == '\0') ) ||
                (!DDNSClient.ROMPointers.UpdateServer && (!DDNSClient.UpdateServer.szRAM || *DDNSClient.UpdateServer.szRAM == '\0') )
            )
            {
                smDDNS = SM_SOFT_ERROR;
                lastStatus = DDNS_STATUS_INVALID;
                break;
            }

            // Start with an invalidated IP String
            vBuffer[0] = '\0';

            smDDNS++;
            break;

        case SM_DNS_START_RESOLVE:

            netH = TCPIP_STACK_NetDefaultGet();
            if(TCPIP_DNS_UsageBegin(netH) != DNS_RES_OK)
            {
                lastStatus = DDNS_STATUS_DNS_ERROR;
                smDDNS = SM_DNS_ERROR;
                break;
            }

            // resolve the remote server
            if(DDNSClient.ROMPointers.CheckIPServer)
            {
                TCPIP_DNS_Resolve((const char*)DDNSClient.CheckIPServer.szROM, DNS_TYPE_A);
            }
            else
            {
                TCPIP_DNS_Resolve((const char*)DDNSClient.CheckIPServer.szRAM, DNS_TYPE_A);
            }

            smDDNS++;
            break;

        case SM_DNS_WAIT_RESOLVE:

            if(DDNSClient.ROMPointers.CheckIPServer)
            {
                dnsRes = TCPIP_DNS_IsResolved((const char*)DDNSClient.CheckIPServer.szROM, &ddnsServerIP);
            }
            else
            {
                dnsRes = TCPIP_DNS_IsResolved((const char*)DDNSClient.CheckIPServer.szRAM, &ddnsServerIP);
            }

            if(dnsRes == DNS_RES_PENDING)
            {   // ongoing operation;
                break;
            }

            TCPIP_DNS_UsageEnd(netH);
            if(dnsRes < 0)
            {   // some DNS error occurred; retry later
                lastStatus = DDNS_STATUS_DNS_ERROR;
                smDDNS = SM_DNS_ERROR;
                break;
            }

            // server IP solved
            // open the client socket
            MySocket = TCPIP_TCP_ClientOpen(IP_ADDRESS_TYPE_IPV4, DDNSClient.CheckIPPort, (IP_MULTI_ADDRESS*)&ddnsServerIP);

            // If no socket available, try again later
            if(MySocket == INVALID_SOCKET)
            {
                lastStatus = DDNS_STATUS_SKT_ERROR;
                smDDNS = SM_SKT_ERROR;
                break;
            }

            // socket opened OK
            smDDNS++;
            DDnsTimer = SYS_TICK_Get();
            break;

        case SM_CHECKIP_SKT_OBTAINED:

            // Wait for the remote server to accept our connection request
            if(!TCPIP_TCP_IsConnected(MySocket))
            {
                // Time out if too much time is spent in this state
                if(SYS_TICK_Get()-DDnsTimer > 6*SYS_TICK_TicksPerSecondGet())
                {
                    // Close the socket so it can be used by other modules
                    // We will retry soon
                    TCPIP_TCP_Close(MySocket);
                    MySocket = INVALID_SOCKET;
                    lastStatus = DDNS_STATUS_CHECKIP_ERROR;
                    smDDNS = SM_SOFT_ERROR;
                }
                break;
            }

            DDnsTimer = SYS_TICK_Get();

            // Make certain the socket can be written to
            if(TCPIP_TCP_PutIsReady(MySocket) < 125u)//125 = size of TCP Tx buffer
                break;

            // Transmit the request to the server
            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"GET / HTTP/1.0\r\nHost: ");

            if(DDNSClient.ROMPointers.CheckIPServer)
            {
                TCPIP_TCP_StringPut(MySocket, DDNSClient.CheckIPServer.szROM);
            }
            else
            {
                TCPIP_TCP_StringPut(MySocket, DDNSClient.CheckIPServer.szRAM);
            }

            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"\r\nConnection: close\r\n\r\n");

            // Send the packet
            TCPIP_TCP_Flush(MySocket);
            smDDNS++;
            break;

        case SM_CHECKIP_FIND_DELIMITER:

            // Check if remote node is still connected.  If not, force to the disconnect state,
            // but don't break because data may still be waiting.
            if(!TCPIP_TCP_IsConnected(MySocket) || SYS_TICK_Get() - DDnsTimer > 6*SYS_TICK_TicksPerSecondGet())
                smDDNS = SM_CHECKIP_DISCONNECT;

            // Search out the "Address: " delimiter in the response
            wPos = TCPIP_TCP_ArrayFind(MySocket, (const uint8_t*)"Address: ", 9, 0, 0, false);

            // If not yet found, clear as much as possible and break
            if(wPos == 0xffff)
            {
                wPos = TCPIP_TCP_GetIsReady(MySocket);
                if(wPos > 9u)
                    TCPIP_TCP_ArrayGet(MySocket, NULL, wPos - 9);
                break;
            }

            // Clear up to and past that string
            TCPIP_TCP_ArrayGet(MySocket, NULL, wPos + 9);

            // Continue on to read the IP
            DDnsTimer = SYS_TICK_Get();
            smDDNS++;

        case SM_CHECKIP_FIND_ADDRESS:

            // Check if remote node is still connected.  If not, force to the disconnect state,
            // but don't break because data may still be waiting.
            if(!TCPIP_TCP_IsConnected(MySocket) || SYS_TICK_Get() - DDnsTimer > 6*SYS_TICK_TicksPerSecondGet())
                smDDNS = SM_CHECKIP_DISCONNECT;

            // Search out the "</body>" delimiter in the response
            wPos = TCPIP_TCP_ArrayFind(MySocket, (const uint8_t*)"</body>", 7, 0, 0, false);

            // If not yet found, break
            if(wPos == 0xffff)
                break;

            // Read and terminate that string as the IP address (preventing buffer overflows)
            if(wPos > 15u)
                wPos = 15;
            TCPIP_TCP_ArrayGet(MySocket, vBuffer, wPos);
            vBuffer[wPos] = '\0';

            // Parse the IP address that was read, invalidating on failure
            if(!TCPIP_Helper_StringToIPAddress((char*)vBuffer, &ipParsed))
                vBuffer[0] = '\0';

            // Continue on to close the socket

        case SM_CHECKIP_DISCONNECT:

            // Close the socket
            TCPIP_TCP_Close(MySocket);
            MySocket = INVALID_SOCKET;

            // Determine if an update is necessary
            if(vBuffer[0] == '\0')
            {// CheckIP Failed
                lastStatus = DDNS_STATUS_CHECKIP_ERROR;
                smDDNS = SM_SOFT_ERROR;
                break;
            }

            if( (ipParsed.Val ==lastKnownIP.Val) && (!bForceUpdate))
            {
                // IP address has not changed and no update is forced
                lastStatus = DDNS_STATUS_UNCHANGED;
                smDDNS = SM_DONE;
                break;
            }

            // Need to perform an update
            lastKnownIP = ipParsed;
            bForceUpdate = false;
            smDDNS++;
            break;

        case SM_IP_UPDATE_HOME:

            netH = TCPIP_STACK_NetDefaultGet();
            if(TCPIP_DNS_UsageBegin(netH) != DNS_RES_OK)
            {   // wait some more
                break;
            }

            // resolve the remote update server
            if(DDNSClient.ROMPointers.UpdateServer)
            {
                TCPIP_DNS_Resolve((const char*)DDNSClient.UpdateServer.szROM, DNS_TYPE_A);
            }
            else
            {
                TCPIP_DNS_Resolve((const char*)DDNSClient.UpdateServer.szRAM, DNS_TYPE_A);
            }

            smDDNS++;
            break;

        case SM_IP_UPDATE_WAIT_DNS:

            if(DDNSClient.ROMPointers.UpdateServer)
            {
                dnsRes = TCPIP_DNS_IsResolved((const char*)DDNSClient.UpdateServer.szROM, &ddnsUpdateIP);
            }
            else
            {
                dnsRes = TCPIP_DNS_IsResolved((const char*)DDNSClient.UpdateServer.szRAM, &ddnsUpdateIP);
            }

            if(dnsRes == DNS_RES_PENDING)
            {   // ongoing operation;
                break;
            }

            TCPIP_DNS_UsageEnd(netH);
            if(dnsRes < 0)
            {   // some DNS error occurred; retry later
                lastStatus = DDNS_STATUS_DNS_ERROR;
                smDDNS = SM_DNS_ERROR;
                break;
            }


            // update server IP solved
            // open the client socket to the update server
            MySocket = TCPIP_TCP_ClientOpen(IP_ADDRESS_TYPE_IPV4, DDNSClient.UpdatePort, (IP_MULTI_ADDRESS*)&ddnsUpdateIP);

            // If no socket available, try again later
            if(MySocket == INVALID_SOCKET)
            {
                lastStatus = DDNS_STATUS_SKT_ERROR;
                smDDNS = SM_SKT_ERROR;
                break;
            }

            // socket opened OK
            // Move on to the next state
            smDDNS++;
            DDnsTimer = SYS_TICK_Get();
            break;

        case SM_IP_UPDATE_SKT_OBTAINED:

            // Wait for the remote server to accept our connection request
            if(!TCPIP_TCP_IsConnected(MySocket))
            {
                // Time out if too much time is spent in this state
                if(SYS_TICK_Get() - DDnsTimer > 6*SYS_TICK_TicksPerSecondGet())
                {
                    // Close the socket so it can be used by other modules
                    // We will try again immediately
                    TCPIP_TCP_Close(MySocket);
                    MySocket = INVALID_SOCKET;
                    lastStatus = DDNS_STATUS_UPDATE_ERROR;
                    smDDNS--;
                }
                break;
            }

            // Reset timer and begin sending the request
            DDnsTimer = SYS_TICK_Get();
            smDDNS++;
            // No break needed...try to send first bit immediately.

        case SM_IP_UPDATE_REQ_A:

            // Check for lost connections or timeouts
            if(!TCPIP_TCP_IsConnected(MySocket) || (SYS_TICK_Get() - DDnsTimer > 10*SYS_TICK_TicksPerSecondGet()))
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            if(TCPIP_TCP_PutIsReady(MySocket) < 25u)  // 25 =~ 16+9
                break;

            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"GET /nic/update?hostname=");
            smDDNS++;
            // No break needed...try to send next bit immediately.

        case SM_IP_UPDATE_REQ_B:

            // Check for lost connections or timeouts
            if(!TCPIP_TCP_IsConnected(MySocket) || (SYS_TICK_Get() - DDnsTimer > 10*SYS_TICK_TicksPerSecondGet()))
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            // Try to write, verifying that space is available first
            if(DDNSClient.ROMPointers.Host)
            {
                if(TCPIP_TCP_PutIsReady(MySocket) < strlen((const char*)DDNSClient.Host.szROM))
                    break;
                TCPIP_TCP_StringPut(MySocket,DDNSClient.Host.szROM);
            }
            else
            {
                if(TCPIP_TCP_PutIsReady(MySocket) < strlen((char*)DDNSClient.Host.szRAM))
                    break;
                TCPIP_TCP_StringPut(MySocket,DDNSClient.Host.szRAM);
            }

            smDDNS++;
            // No break needed...try to send next bit immediately.

        case SM_IP_UPDATE_REQ_C:

            // Check for lost connections or timeouts
            if(!TCPIP_TCP_IsConnected(MySocket) || SYS_TICK_Get() - DDnsTimer > 10*SYS_TICK_TicksPerSecondGet())
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            if(TCPIP_TCP_PutIsReady(MySocket) < 70u)
                break;

            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"&myip=");
            TCPIP_TCP_StringPut(MySocket, vBuffer);
            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0");

            TCPIP_TCP_Flush(MySocket);
            smDDNS++;
            // No break needed...try to send next bit immediately.

        case SM_IP_UPDATE_REQ_D:

            // Check for lost connections or timeouts
            if(!TCPIP_TCP_IsConnected(MySocket) || SYS_TICK_Get() - DDnsTimer > 10*SYS_TICK_TicksPerSecondGet())
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            if(TCPIP_TCP_PutIsReady(MySocket) < 131u) // 131 =~ 8+23 + dynamic dns server hostname
                break;

            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"\r\nHost: ");//8

            if(DDNSClient.ROMPointers.UpdateServer)
                TCPIP_TCP_StringPut(MySocket,DDNSClient.UpdateServer.szROM);
            else
                TCPIP_TCP_StringPut(MySocket,DDNSClient.UpdateServer.szRAM);

            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"\r\nAuthorization: Basic ");//23

            TCPIP_TCP_Flush(MySocket);
            smDDNS++;
            // No break needed...try to send the next bit immediately.

        case SM_IP_UPDATE_REQ_E:

            // Check for lost connections or timeouts
            if(!TCPIP_TCP_IsConnected(MySocket) || SYS_TICK_Get() - DDnsTimer > 6*SYS_TICK_TicksPerSecondGet())
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            // User name and passwords for DynDNS.org can each be up to 24 characters
            // Base64 encoded data is always at least 25% bigger than the original
            if(TCPIP_TCP_PutIsReady(MySocket) < 100u)
                break;

            if(DDNSClient.ROMPointers.Username)
            {
                ROMStrPtr = (const char*)DDNSClient.Username.szROM;
                wPos = strlen(ROMStrPtr);
            }
            else
            {
                RAMStrPtr = (char*)DDNSClient.Username.szRAM;
                wPos = strlen((char*)RAMStrPtr);
            }

            i = 0;
            while(wPos)
            {
                while(i < wPos && i < 3u)
                {
                    if(DDNSClient.ROMPointers.Username)
                        vBuffer[i] = *ROMStrPtr++;
                    else
                        vBuffer[i] = *RAMStrPtr++;
                    i++;
                }
                wPos -= i;

                if(i == 3u)
                {
                    TCPIP_Helper_Base64Encode(vBuffer, i, vBuffer, 4);
                    TCPIP_TCP_ArrayPut(MySocket, vBuffer, 4);
                    i = 0;
                }
            }

            if(DDNSClient.ROMPointers.Password)
            {
                ROMStrPtr = (const char*)DDNSClient.Password.szROM;
                wPos = strlen(ROMStrPtr);
            }
            else
            {
                RAMStrPtr = (char*)DDNSClient.Password.szRAM;
                wPos = strlen((char*)RAMStrPtr);
            }

            // Increment for the ':' separator and i for bytes left in username
            wPos += i + 1;

            vBuffer[i++] = ':';

            while(wPos)
            {
                while(i < wPos && i < 3u)
                {
                    if(DDNSClient.ROMPointers.Password)
                        vBuffer[i] = *ROMStrPtr++;
                    else
                        vBuffer[i] = *RAMStrPtr++;
                    i++;
                }
                wPos -= i;
                TCPIP_Helper_Base64Encode(vBuffer, i, vBuffer, 4);
                TCPIP_TCP_ArrayPut(MySocket, vBuffer, 4);
                i = 0;
            }

            TCPIP_TCP_Flush(MySocket);
            smDDNS++;
            break;


        case SM_IP_UPDATE_REQ_F:

            // Check for lost connections or timeouts
            if(!TCPIP_TCP_IsConnected(MySocket) || SYS_TICK_Get() - DDnsTimer > 10*SYS_TICK_TicksPerSecondGet())
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            if(TCPIP_TCP_PutIsReady(MySocket) < 50u)
                break;

            TCPIP_TCP_StringPut(MySocket, (const uint8_t*)"\r\nUser-Agent: Microchip - TCPIPSTACK - "TCPIP_STACK_VERSION"\r\n\r\n");
            TCPIP_TCP_Flush(MySocket);
            smDDNS++;

            // Reset the timer to wait for a response
            DDnsTimer = SYS_TICK_Get();
            break;

        case SM_IPUPDATE_FIND_RESPONSE:
            // Locate the response string

            // Wait up to 10 seconds for a response
            if(SYS_TICK_Get() - DDnsTimer > 10*SYS_TICK_TicksPerSecondGet())
            {
                lastStatus = DDNS_STATUS_UPDATE_ERROR;
                smDDNS = SM_IPUDATE_DISCONNECT;
                break;
            }

            // According to HTTP, the response will start after the two CRLFs
            wPos = TCPIP_TCP_ArrayFind(MySocket, (const uint8_t*)"\r\n\r\n", 4, 0, 0, false);

            // If not yet found, eliminate everything up to
            if(wPos == 0xffff)
            {
                wPos = TCPIP_TCP_GetIsReady(MySocket);
                if(wPos > 4u)
                    TCPIP_TCP_ArrayGet(MySocket, NULL, wPos - 4);
                break;
            }

            TCPIP_TCP_ArrayGet(MySocket, NULL, wPos+4);
            smDDNS++;
            // No break...continue to next state immediately

        case SM_IPUPDATE_PARSE_RESPONSE:
            // Try to parse the response text

            // Wait up to 10 seconds for the remote server to disconnect
            // so we know all data has been received
            if(TCPIP_TCP_IsConnected(MySocket) && SYS_TICK_Get() - DDnsTimer < 10*SYS_TICK_TicksPerSecondGet())
                break;

            // Read the response code
            wPos = TCPIP_TCP_GetIsReady(MySocket);
            if(wPos > sizeof(vBuffer) - 1)
                wPos = sizeof(vBuffer) - 1;

            wPos = TCPIP_TCP_ArrayGet(MySocket, vBuffer, wPos);
            vBuffer[wPos] = '\0';
            for(i = 0; i < sizeof(vBuffer); i++)
                if(vBuffer[i] == ' ')
                    vBuffer[i] = '\0';

            for(lastStatus = 0; lastStatus < DDNS_STATUS_UPDATE_ERROR; lastStatus++)
                if(!strcmp((char*)vBuffer, (const char*)_updateIpSrvrResponse[lastStatus]))
                    break;

            smDDNS++;
            // No break...continue to finalization

        case SM_IPUDATE_DISCONNECT:
            // Close the socket so it can be used by other modules.
            if(MySocket != INVALID_SOCKET)
            {
                TCPIP_TCP_Close(MySocket);
                MySocket = INVALID_SOCKET;
            }

            // Determine what to do based on status
            if(lastStatus <= DDNS_STATUS_NUMHOST || lastStatus == DDNS_STATUS_UNCHANGED)
                smDDNS = SM_DONE;
            else if(lastStatus == DDNS_STATUS_911 || lastStatus == DDNS_STATUS_DNSERR)
                smDDNS = SM_SYSTEM_ERROR;
            else
                smDDNS = SM_SOFT_ERROR;

            smDDNS++;
            break;

        case SM_DONE:
            dwUpdateAt = SYS_TICK_Get() + 10*60*SYS_TICK_TicksPerSecondGet();   // 10 minutes
            smDDNS = SM_IDLE;
            break;

        case SM_SOFT_ERROR:
        case SM_DNS_ERROR:
        case SM_SKT_ERROR:
            dwUpdateAt = SYS_TICK_Get() + 30*SYS_TICK_TicksPerSecondGet();      // 30 seconds
            smDDNS = SM_IDLE;
            break;

        case SM_SYSTEM_ERROR:
            dwUpdateAt = SYS_TICK_Get() + 30*60*SYS_TICK_TicksPerSecondGet();       // 30 minutes
            smDDNS = SM_IDLE;
            break;
    }
}