/**************************************************************************** Function: WORD ChipKITUDPSendPacketURL(UDP_SOCKET hUDP, BYTE * rgbBuf, WORD cbBuff, const char * szURL, WORD port, unsigned int cSecTimeout) Description: Implementes the Arduino UDP SendPacket function. Parameters: hUDP - the UDP socket to use rgbBuf - a pointer to a buffer of bytes to send cbBuff - the number of bytes to send szURL - The URL to send the packet to. This is a hostname or string IP address port - the remote port to send it data to cSecTimeout - the ARP may take too long, after this many seconds it wil fail and return with 0 bytes sent. It may be somehow possible for the UDP transmit to fail as well and this function will abort if the timeout is exceeded. Returns: The actually number of bytes sent, this may be 0 if the ARP failed, or less than cbBuff if something went wrong. Remarks: If the ARP succeeds, the data will typically just be blasted on the wire as UDP is an unreliable protocol and will rarely fail to transmit ***************************************************************************/ WORD ChipKITUDPSendPacketURL(UDP_SOCKET hUDP, BYTE * rgbBuf, WORD cbBuff, const char * szURL, WORD port, unsigned int cSecTimeout) { DWORD t = 0; IP_ADDR ipAddr; // This will add full URL support (like HTTP=>port 80, TCPIP does not do this, so I am not implementing it for UDP either. // that is, the URL is really just a hostname #if 0 BYTE * szHostName[256]; // max allowed for a host name WORD cbHostNameBuff = sizeof(szHostName); WORD wPort = INVALID_UDP_PORT; // parse the URL if(ExtractURLFields((BYTE *) szURL, NULL, NULL, NULL, NULL, NULL, (BYTE *) szHostName, &cbHostNameBuff, &wPort, NULL, NULL) != 0 ) { return(0); } // see if we should get the port from the URL if(port == INVALID_UDP_PORT) { port = wPort; } #endif // but we do support DNS lookup t = TickGet(); while(!DNSBeginUsage()) { ChipKITPeriodicTasks(); if((TickGet() - t) >= (cSecTimeout * TICK_SECOND)) { return(0); } } DNSResolve((BYTE *) szURL, DNS_TYPE_A); t = TickGet(); while(!DNSIsResolved(&ipAddr)) { ChipKITPeriodicTasks(); if((TickGet() - t) >= (cSecTimeout * TICK_SECOND)) { DNSEndUsage(); return(0); } } // if we actually resolved the URL if(DNSEndUsage()) { return(UDPSendPacket(hUDP, rgbBuf, cbBuff, ipAddr, port, cSecTimeout)); } return(0); }
/***************************************************************************** Function: void SMTPTask(void) Summary: Performs any pending SMTP client tasks Description: This function handles periodic tasks associated with the SMTP client, such as processing initial connections and command sequences. Precondition: None Parameters: None Returns: None Remarks: This function acts as a task (similar to one in an RTOS). It performs its task in a co-operative manner, and the main application must call this function repeatedly to ensure that all open or new connections are served in a timely fashion. ***************************************************************************/ void SMTPTask(void) { BYTE i; WORD w; BYTE vBase64Buffer[4]; static DWORD Timer; static BYTE RXBuffer[4]; static ROM BYTE *ROMStrPtr, *ROMStrPtr2; static BYTE *RAMStrPtr; static WORD wAddressLength; WORD tmp; switch(TransportState) { case TRANSPORT_HOME: // SMTPBeginUsage() 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 SMTPSendMail() if(!SMTPFlags.bits.ReadyToStart) break; // Obtain ownership of the DNS resolution module if(!DNSBeginUsage()) break; // Obtain the IP address associated with the SMTP mail server if(SMTPClient.Server.szRAM || SMTPClient.Server.szROM) { if(SMTPClient.ROMPointers.Server) DNSResolveROM(SMTPClient.Server.szROM, DNS_TYPE_A); else DNSResolve(SMTPClient.Server.szRAM, DNS_TYPE_A); } else { // If we don't have a mail server, try to send the mail // directly to the destination SMTP server if(SMTPClient.To.szRAM && !SMTPClient.ROMPointers.To) { SMTPClient.Server.szRAM = (BYTE*)strchr((char*)SMTPClient.To.szRAM, '@'); SMTPClient.ROMPointers.Server = 0; } else if(SMTPClient.To.szROM && SMTPClient.ROMPointers.To) { SMTPClient.Server.szROM = (ROM BYTE*)strchrpgm((ROM char*)SMTPClient.To.szROM, '@'); SMTPClient.ROMPointers.Server = 1; } if(!(SMTPClient.Server.szRAM || SMTPClient.Server.szROM)) { if(SMTPClient.CC.szRAM && !SMTPClient.ROMPointers.CC) { SMTPClient.Server.szRAM = (BYTE*)strchr((char*)SMTPClient.CC.szRAM, '@'); SMTPClient.ROMPointers.Server = 0; } else if(SMTPClient.CC.szROM && SMTPClient.ROMPointers.CC) { SMTPClient.Server.szROM = (ROM BYTE*)strchrpgm((ROM char*)SMTPClient.CC.szROM, '@'); SMTPClient.ROMPointers.Server = 1; } } if(!(SMTPClient.Server.szRAM || SMTPClient.Server.szROM)) { if(SMTPClient.BCC.szRAM && !SMTPClient.ROMPointers.BCC) { SMTPClient.Server.szRAM = (BYTE*)strchr((char*)SMTPClient.BCC.szRAM, '@'); SMTPClient.ROMPointers.Server = 0; } else if(SMTPClient.BCC.szROM && SMTPClient.ROMPointers.BCC) { SMTPClient.Server.szROM = (ROM BYTE*)strchrpgm((ROM char*)SMTPClient.BCC.szROM, '@'); SMTPClient.ROMPointers.Server = 1; } } // See if we found a hostname anywhere which we could resolve if(!(SMTPClient.Server.szRAM || SMTPClient.Server.szROM)) { DNSEndUsage(); ResponseCode = SMTP_RESOLVE_ERROR; TransportState = TRANSPORT_HOME; break; } // Skip over the @ sign and resolve the host name if(SMTPClient.ROMPointers.Server) { SMTPClient.Server.szROM++; DNSResolveROM(SMTPClient.Server.szROM, DNS_TYPE_MX); } else { SMTPClient.Server.szRAM++; DNSResolve(SMTPClient.Server.szRAM, DNS_TYPE_MX); } } Timer = TickGet(); TransportState++; break; case TRANSPORT_NAME_RESOLVE: // Wait for the DNS server to return the requested IP address if(!DNSIsResolved(&SMTPServer)) { // Timeout after 6 seconds of unsuccessful DNS resolution if(TickGet() - Timer > 6*TICK_SECOND) { ResponseCode = SMTP_RESOLVE_ERROR; TransportState = TRANSPORT_HOME; DNSEndUsage(); } break; } // Release the DNS module, we no longer need it if(!DNSEndUsage()) { // An invalid IP address was returned from the DNS // server. Quit and fail permanantly if host is not valid. ResponseCode = SMTP_RESOLVE_ERROR; TransportState = TRANSPORT_HOME; break; } TransportState++; // No need to break here case TRANSPORT_OBTAIN_SOCKET: // Connect a TCP socket to the remote SMTP server MySocket = TCPOpen(SMTPServer.Val, TCP_OPEN_IP_ADDRESS, SMTPClient.ServerPort, TCP_PURPOSE_DEFAULT); // Abort operation if no TCP sockets are available // If this ever happens, add some more // TCP_PURPOSE_DEFAULT sockets in TCPIPConfig.h if(MySocket == INVALID_SOCKET) break; TransportState++; Timer = TickGet(); // No break; fall into TRANSPORT_SOCKET_OBTAINED #if defined(STACK_USE_SSL_CLIENT) case TRANSPORT_SECURING_SOCKET: if(!TCPIsConnected(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((LONG)(TickGet()-Timer) > (LONG)(SMTP_SERVER_REPLY_TIMEOUT)) { ResponseCode = SMTP_CONNECT_ERROR; TransportState = TRANSPORT_CLOSE; } break; } SMTPFlags.bits.ConnectedOnce = TRUE; // Start SSL if needed for this connection if(SMTPClient.UseSSL && !TCPStartSSLClient(MySocket,NULL)) break; // Move on to main state Timer = TickGet(); TransportState++; break; #endif case TRANSPORT_SOCKET_OBTAINED: if(!TCPIsConnected(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 || ((LONG)(TickGet()-Timer) > (LONG)(SMTP_SERVER_REPLY_TIMEOUT))) { ResponseCode = SMTP_CONNECT_ERROR; TransportState = TRANSPORT_CLOSE; } break; } SMTPFlags.bits.ConnectedOnce = TRUE; #if defined(STACK_USE_SSL_CLIENT) // Make sure the SSL handshake has completed if(SMTPClient.UseSSL && TCPSSLIsHandshaking(MySocket)) break; #endif // See if the server sent us anything while(TCPIsGetReady(MySocket)) { TCPGet(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.szRAM || SMTPClient.Username.szROM) 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: tmp = SMTPState; 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(TCPIsPutReady(MySocket) < 64u) break; switch(SMTPState) { case SMTP_HELO: if(SMTPClient.Username.szROM == NULL) TCPPutROMString(MySocket, (ROM BYTE*)"HELO MCHPBOARD\r\n"); else TCPPutROMString(MySocket, (ROM BYTE*)"EHLO MCHPBOARD\r\n"); TCPFlush(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 (either SMTPClient.Username.szROM or // SMTPClient.Username.szRAM is non-NULL) TCPPutROMString(MySocket, (ROM BYTE*)"AUTH LOGIN\r\n"); TCPFlush(MySocket); SMTPState++; break; case SMTP_AUTH_USERNAME: // Base 64 encode and transmit the username. if(SMTPClient.ROMPointers.Username) { ROMStrPtr = SMTPClient.Username.szROM; w = strlenpgm((ROM char*)ROMStrPtr); } else { RAMStrPtr = SMTPClient.Username.szRAM; w = strlen((char*)RAMStrPtr); } while(w) { i = 0; while((i < w) && (i < sizeof(vBase64Buffer)*3/4)) { if(SMTPClient.ROMPointers.Username) vBase64Buffer[i] = *ROMStrPtr++; else vBase64Buffer[i] = *RAMStrPtr++; i++; } w -= i; Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer)); TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer)); } TCPPutROMString(MySocket, (ROM BYTE*)"\r\n"); TCPFlush(MySocket); SMTPState++; break; case SMTP_AUTH_PASSWORD: // Base 64 encode and transmit the password if(SMTPClient.ROMPointers.Password) { ROMStrPtr = SMTPClient.Password.szROM; w = strlenpgm((ROM char*)ROMStrPtr); } else { RAMStrPtr = SMTPClient.Password.szRAM; w = strlen((char*)RAMStrPtr); } while(w) { i = 0; while((i < w) && (i < sizeof(vBase64Buffer)*3/4)) { if(SMTPClient.ROMPointers.Password) vBase64Buffer[i] = *ROMStrPtr++; else vBase64Buffer[i] = *RAMStrPtr++; i++; } w -= i; Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer)); TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer)); } TCPPutROMString(MySocket, (ROM BYTE*)"\r\n"); TCPFlush(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. TCPPutROMString(MySocket, (ROM BYTE*)"MAIL FROM:<"); if(SMTPClient.ROMPointers.From) { ROMStrPtr = FindROMEmailAddress(SMTPClient.From.szROM, &wAddressLength); TCPPutROMArray(MySocket, ROMStrPtr, wAddressLength); } else { RAMStrPtr = FindEmailAddress(SMTPClient.From.szRAM, &wAddressLength); TCPPutArray(MySocket, RAMStrPtr, wAddressLength); } TCPPutROMString(MySocket, (ROM BYTE*)">\r\n"); TCPFlush(MySocket); SMTPState++; break; case SMTP_RCPTTO_INIT: // See if there are any (To) recipients to process if(SMTPClient.To.szRAM && !SMTPClient.ROMPointers.To) { RAMStrPtr = FindEmailAddress(SMTPClient.To.szRAM, &wAddressLength); if(wAddressLength) { SMTPState = SMTP_RCPTTO; break; } } if(SMTPClient.To.szROM && SMTPClient.ROMPointers.To) { ROMStrPtr = FindROMEmailAddress(SMTPClient.To.szROM, &wAddressLength); if(wAddressLength) { SMTPState = SMTP_RCPTTO; break; } } SMTPState = SMTP_RCPTTOCC_INIT; break; case SMTP_RCPTTO: case SMTP_RCPTTOCC: case SMTP_RCPTTOBCC: TCPPutROMString(MySocket, (ROM BYTE*)"RCPT TO:<"); if( (SMTPClient.ROMPointers.To && (SMTPState == SMTP_RCPTTO)) || (SMTPClient.ROMPointers.CC && (SMTPState == SMTP_RCPTTOCC)) || (SMTPClient.ROMPointers.BCC && (SMTPState == SMTP_RCPTTOBCC)) ) TCPPutROMArray(MySocket, ROMStrPtr, wAddressLength); else TCPPutArray(MySocket, RAMStrPtr, wAddressLength); TCPPutROMString(MySocket, (ROM BYTE*)">\r\n"); TCPFlush(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 if(SMTPClient.ROMPointers.To) ROMStrPtr = FindROMEmailAddress(ROMStrPtr+wAddressLength, &wAddressLength); else 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.szRAM && !SMTPClient.ROMPointers.CC) { RAMStrPtr = FindEmailAddress(SMTPClient.CC.szRAM, &wAddressLength); if(wAddressLength) { SMTPState = SMTP_RCPTTOCC; break; } } if(SMTPClient.CC.szROM && SMTPClient.ROMPointers.CC) { ROMStrPtr = FindROMEmailAddress(SMTPClient.CC.szROM, &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 if(SMTPClient.ROMPointers.CC) ROMStrPtr = FindROMEmailAddress(ROMStrPtr+wAddressLength, &wAddressLength); else 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.szRAM && !SMTPClient.ROMPointers.BCC) { RAMStrPtr = FindEmailAddress(SMTPClient.BCC.szRAM, &wAddressLength); if(wAddressLength) { SMTPState = SMTP_RCPTTOBCC; break; } } if(SMTPClient.BCC.szROM && SMTPClient.ROMPointers.BCC) { ROMStrPtr = FindROMEmailAddress(SMTPClient.BCC.szROM, &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 if(SMTPClient.ROMPointers.BCC) ROMStrPtr = FindROMEmailAddress(ROMStrPtr+wAddressLength, &wAddressLength); else RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength); if(wAddressLength) { SMTPState = SMTP_RCPTTOBCC; break; } // All done with BCC field SMTPState++; //No break case SMTP_DATA: TCPPutROMString(MySocket, (ROM BYTE*)"DATA\r\n"); SMTPState++; PutHeadersState = PUTHEADERS_FROM_INIT; TCPFlush(MySocket); break; case SMTP_DATA_HEADER: while((PutHeadersState != PUTHEADERS_DONE) && (TCPIsPutReady(MySocket) > 64u)) { switch(PutHeadersState) { case PUTHEADERS_FROM_INIT: if(SMTPClient.From.szRAM || SMTPClient.From.szROM) { PutHeadersState = PUTHEADERS_FROM; TCPPutROMString(MySocket, (ROM BYTE*)"From: "); } else { PutHeadersState = PUTHEADERS_TO_INIT; } break; case PUTHEADERS_FROM: if(SMTPClient.ROMPointers.From) { SMTPClient.From.szROM = TCPPutROMString(MySocket, SMTPClient.From.szROM); if(*SMTPClient.From.szROM == 0u) PutHeadersState = PUTHEADERS_TO_INIT; } else { SMTPClient.From.szRAM = TCPPutString(MySocket, SMTPClient.From.szRAM); if(*SMTPClient.From.szRAM == 0u) PutHeadersState = PUTHEADERS_TO_INIT; } break; case PUTHEADERS_TO_INIT: if(SMTPClient.To.szRAM || SMTPClient.To.szROM) { PutHeadersState = PUTHEADERS_TO; TCPPutROMString(MySocket, (ROM BYTE*)"\r\nTo: "); } else { PutHeadersState = PUTHEADERS_CC_INIT; } break; case PUTHEADERS_TO: if(SMTPClient.ROMPointers.To) { SMTPClient.To.szROM = TCPPutROMString(MySocket, SMTPClient.To.szROM); if(*SMTPClient.To.szROM == 0u) PutHeadersState = PUTHEADERS_CC_INIT; } else { SMTPClient.To.szRAM = TCPPutString(MySocket, SMTPClient.To.szRAM); if(*SMTPClient.To.szRAM == 0u) PutHeadersState = PUTHEADERS_CC_INIT; } break; case PUTHEADERS_CC_INIT: if(SMTPClient.CC.szRAM || SMTPClient.CC.szROM) { PutHeadersState = PUTHEADERS_CC; TCPPutROMString(MySocket, (ROM BYTE*)"\r\nCC: "); } else { PutHeadersState = PUTHEADERS_SUBJECT_INIT; } break; case PUTHEADERS_CC: if(SMTPClient.ROMPointers.CC) { SMTPClient.CC.szROM = TCPPutROMString(MySocket, SMTPClient.CC.szROM); if(*SMTPClient.CC.szROM == 0u) PutHeadersState = PUTHEADERS_SUBJECT_INIT; } else { SMTPClient.CC.szRAM = TCPPutString(MySocket, SMTPClient.CC.szRAM); if(*SMTPClient.CC.szRAM == 0u) PutHeadersState = PUTHEADERS_SUBJECT_INIT; } break; case PUTHEADERS_SUBJECT_INIT: if(SMTPClient.Subject.szRAM || SMTPClient.Subject.szROM) { PutHeadersState = PUTHEADERS_SUBJECT; TCPPutROMString(MySocket, (ROM BYTE*)"\r\nSubject: "); } else { PutHeadersState = PUTHEADERS_OTHER_INIT; } break; case PUTHEADERS_SUBJECT: if(SMTPClient.ROMPointers.Subject) { SMTPClient.Subject.szROM = TCPPutROMString(MySocket, SMTPClient.Subject.szROM); if(*SMTPClient.Subject.szROM == 0u) PutHeadersState = PUTHEADERS_OTHER_INIT; } else { SMTPClient.Subject.szRAM = TCPPutString(MySocket, SMTPClient.Subject.szRAM); if(*SMTPClient.Subject.szRAM == 0u) PutHeadersState = PUTHEADERS_OTHER_INIT; } break; case PUTHEADERS_OTHER_INIT: TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2); if(SMTPClient.OtherHeaders.szRAM || SMTPClient.OtherHeaders.szROM) { PutHeadersState = PUTHEADERS_OTHER; } else { TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2); PutHeadersState = PUTHEADERS_DONE; SMTPState++; } break; case PUTHEADERS_OTHER: if(SMTPClient.ROMPointers.OtherHeaders) { SMTPClient.OtherHeaders.szROM = TCPPutROMString(MySocket, SMTPClient.OtherHeaders.szROM); if(*SMTPClient.OtherHeaders.szROM == 0u) { TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2); PutHeadersState = PUTHEADERS_DONE; SMTPState++; } } else { SMTPClient.OtherHeaders.szRAM = TCPPutString(MySocket, SMTPClient.OtherHeaders.szRAM); if(*SMTPClient.OtherHeaders.szRAM == 0u) { TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2); PutHeadersState = PUTHEADERS_DONE; SMTPState++; } } break; // Default case needed to supress compiler diagnostics default: break; } } TCPFlush(MySocket); break; case SMTP_DATA_BODY_INIT: SMTPState++; RAMStrPtr = SMTPClient.Body.szRAM; ROMStrPtr2 = (ROM BYTE*)"\r\n.\r\n"; CRPeriod.Pos = NULL; if(RAMStrPtr) CRPeriod.Pos = (BYTE*)strstrrampgm((char*)RAMStrPtr, (ROM char*)"\r\n."); // No break here case SMTP_DATA_BODY: if(SMTPClient.Body.szRAM || SMTPClient.Body.szROM) { if(*ROMStrPtr2) { // Put the application data, doing the transparancy replacement of "\r\n." with "\r\n.." while(CRPeriod.Pos) { CRPeriod.Pos += 3; RAMStrPtr += TCPPutArray(MySocket, RAMStrPtr, CRPeriod.Pos-RAMStrPtr); if(RAMStrPtr == CRPeriod.Pos) { if(!TCPPut(MySocket, '.')) { CRPeriod.Pos -= 3; break; } } else { CRPeriod.Pos -= 3; break; } CRPeriod.Pos = (BYTE*)strstrrampgm((char*)RAMStrPtr, (ROM 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 = TCPPutString(MySocket, RAMStrPtr); ROMStrPtr2 = TCPPutROMString(MySocket, ROMStrPtr2); TCPFlush(MySocket); } } else { if(SMTPFlags.bits.ReadyToFinish) { if(*ROMStrPtr2) { ROMStrPtr2 = TCPPutROMString(MySocket, ROMStrPtr2); TCPFlush(MySocket); } } } if(*ROMStrPtr2 == 0u) { SMTPState++; } break; case SMTP_QUIT_INIT: SMTPState++; ROMStrPtr = (ROM BYTE*)"QUIT\r\n"; // No break here case SMTP_QUIT: if(*ROMStrPtr) { ROMStrPtr = TCPPutROMString(MySocket, ROMStrPtr); TCPFlush(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 TCPDisconnect(MySocket); MySocket = INVALID_SOCKET; // Go back to doing nothing TransportState = TRANSPORT_HOME; break; } }
/********************************************************************* * Function: LONG ICMPGetReply(void) * * PreCondition: ICMPBeginUsage() returned TRUE and ICMPSendPing() * was called * * Input: None * * Output: -3: Could not resolve hostname (DNS timeout or * hostname invalid) * -2: No response received yet * -1: Operation timed out (longer than ICMP_TIMEOUT) * has elapsed. * >=0: Number of TICKs that elapsed between * initial ICMP transmission and reception of * a valid echo. * * Side Effects: None * * Overview: None * * Note: None ********************************************************************/ LONG ICMPGetReply(void) { ICMP_PACKET ICMPPacket; switch(ICMPState) { #if defined(STACK_USE_DNS) case SM_DNS_SEND_QUERY: // Obtain DNS module ownership if(!DNSBeginUsage()) break; // Send DNS query if(ICMPFlags.bRemoteHostIsROM) DNSResolveROM(StaticVars.RemoteHost.szROM, DNS_TYPE_A); else DNSResolve(StaticVars.RemoteHost.szRAM, DNS_TYPE_A); ICMPState = SM_DNS_GET_RESPONSE; break; case SM_DNS_GET_RESPONSE: // See if DNS is done, and if so, get the remote IP address if(!DNSIsResolved(&StaticVars.ICMPRemote.IPAddr)) break; // Free the DNS module DNSEndUsage(); // Return error code if the DNS query failed if(StaticVars.ICMPRemote.IPAddr.Val == 0x00000000ul) { ICMPState = SM_IDLE; return -3; } ICMPState = SM_ARP_SEND_QUERY; // No break; #endif case SM_ARP_SEND_QUERY: ARPResolve(&StaticVars.ICMPRemote.IPAddr); ICMPState = SM_ARP_GET_RESPONSE; break; case SM_ARP_GET_RESPONSE: // See if the ARP reponse was successfully received if(!ARPIsResolved(&StaticVars.ICMPRemote.IPAddr, &StaticVars.ICMPRemote.MACAddr)) break; ICMPState = SM_ICMP_SEND_ECHO_REQUEST; // No break; case SM_ICMP_SEND_ECHO_REQUEST: if(!IPIsTxReady()) break; // Set up the ping packet ICMPPacket.vType = 0x08; // 0x08: Echo (ping) request ICMPPacket.vCode = 0x00; ICMPPacket.wChecksum = 0x0000; ICMPPacket.wIdentifier = 0xEFBE; wICMPSequenceNumber++; ICMPPacket.wSequenceNumber = wICMPSequenceNumber; ICMPPacket.wData = 0x2860; ICMPPacket.wChecksum = CalcIPChecksum((BYTE*)&ICMPPacket, sizeof(ICMPPacket)); // Record the current time. This will be used as a basis for // finding the echo response time, which exludes the ARP and DNS // steps ICMPTimer = TickGet(); // Position the write pointer for the next IPPutHeader operation MACSetWritePtr(BASE_TX_ADDR + sizeof(ETHER_HEADER)); // Create IP header in TX memory IPPutHeader(&StaticVars.ICMPRemote, IP_PROT_ICMP, sizeof(ICMPPacket)); MACPutArray((BYTE*)&ICMPPacket, sizeof(ICMPPacket)); MACFlush(); // Echo sent, advance state ICMPState = SM_ICMP_GET_ECHO_RESPONSE; break; case SM_ICMP_GET_ECHO_RESPONSE: // See if the echo was successfully received if(ICMPFlags.bReplyValid) return (LONG)ICMPTimer; break; // SM_IDLE or illegal/impossible state: default: return -1; } // See if the DNS/ARP/echo request timed out if(TickGet() - ICMPTimer > ICMP_TIMEOUT) { // Free DNS module if we have it in use #if defined(STACK_USE_DNS) if(ICMPState == SM_DNS_GET_RESPONSE) DNSEndUsage(); #endif // Stop ICMP echo test and return error to caller ICMPState = SM_IDLE; return -1; } // Still working. No response to report yet. return -2; }
/****************************************************************************** Function: void UDPTask(void) Summary: Performs periodic UDP tasks. Description: This function performs any required periodic UDP tasks. Each socket's state machine is checked, and any elapsed timeout periods are handled. Precondition: UDP is initialized. Parameters: None Returns: None ******************************************************************************/ void UDPTask(void) { UDP_SOCKET ss; for ( ss = 0; ss < MAX_UDP_SOCKETS; ss++ ) { // need to put Extra check if UDP has opened or NOT if((UDPSocketInfo[ss].smState == UDP_OPENED) || (UDPSocketInfo[ss].smState == UDP_CLOSED)) continue; // A timeout has occured. Respond to this timeout condition // depending on what state this socket is in. switch(UDPSocketInfo[ss].smState) { #if defined(STACK_CLIENT_MODE) #if defined(STACK_USE_DNS) case UDP_DNS_RESOLVE: if(DNSBeginUsage()) { // call DNS Resolve function and move to UDP next State machine UDPSocketInfo[ss].smState = UDP_DNS_IS_RESOLVED; if(UDPSocketInfo[ss].flags.bRemoteHostIsROM) DNSResolveROM((ROM BYTE*)(ROM_PTR_BASE)UDPSocketInfo[ss].remote.remoteHost, DNS_TYPE_A); else DNSResolve((BYTE*)(PTR_BASE)UDPSocketInfo[ss].remote.remoteHost, DNS_TYPE_A); } break; case UDP_DNS_IS_RESOLVED: { IP_ADDR ipResolvedDNSIP; // See if DNS resolution has finished. Note that if the DNS // fails, the &ipResolvedDNSIP will be written with 0x00000000. // MyTCB.remote.dwRemoteHost is unioned with // MyTCB.remote.niRemoteMACIP.IPAddr, so we can't directly write // the DNS result into MyTCB.remote.niRemoteMACIP.IPAddr. We // must copy it over only if the DNS is resolution step was // successful. if(DNSIsResolved(&ipResolvedDNSIP)) { if(DNSEndUsage()) { UDPSocketInfo[ss].remote.remoteNode.IPAddr.Val = ipResolvedDNSIP.Val; UDPSocketInfo[ss].smState = UDP_GATEWAY_SEND_ARP; UDPSocketInfo[ss].retryCount = 0; UDPSocketInfo[ss].retryInterval = (TICK_SECOND/4)/256; } else { UDPSocketInfo[ss].smState = UDP_DNS_RESOLVE; } } } break; #endif // #if defined(STACK_USE_DNS) case UDP_GATEWAY_SEND_ARP: // Obtain the MAC address associated with the server's IP address //(either direct MAC address on same subnet, or the MAC address of the Gateway machine) UDPSocketInfo[ss].eventTime = (WORD)TickGetDiv256(); ARPResolve(&UDPSocketInfo[ss].remote.remoteNode.IPAddr); UDPSocketInfo[ss].smState = UDP_GATEWAY_GET_ARP; break; case UDP_GATEWAY_GET_ARP: if(!ARPIsResolved(&UDPSocketInfo[ss].remote.remoteNode.IPAddr, &UDPSocketInfo[ss].remote.remoteNode.MACAddr)) { // Time out if too much time is spent in this state // Note that this will continuously send out ARP // requests for an infinite time if the Gateway // never responds if((WORD)TickGetDiv256() - UDPSocketInfo[ss].eventTime> (WORD)UDPSocketInfo[ss].retryInterval) { // Exponentially increase timeout until we reach 6 attempts then stay constant if(UDPSocketInfo[ss].retryCount < 6u) { UDPSocketInfo[ss].retryCount++; UDPSocketInfo[ss].retryInterval <<= 1; } // Retransmit ARP request UDPSocketInfo[ss].smState = UDP_GATEWAY_SEND_ARP; } } else { UDPSocketInfo[ss].smState = UDP_OPENED; } break; default: case UDP_OPENED: case UDP_CLOSED: // not used break; #endif // #if defined(STACK_CLIENT_MODE) } } }
/********************************************************************* * Function: void BerkeleyTCPClientDemo(void) * * PreCondition: Stack is initialized() * * Input: None * * Output: None * * Side Effects: None * * Overview: None * * Note: None ********************************************************************/ void BerkeleyTCPClientDemo(void) { #if defined(STACK_USE_DNS) static SOCKET bsdClientSocket; static struct sockaddr_in addr; char recvBuffer[9]; int i; int addrlen; static enum { DNS_START_RESOLUTION = 0, DNS_GET_RESULT, BSD_START, BSD_CONNECT, BSD_SEND, BSD_OPERATION, BSD_CLOSE, BSD_DONE } BSDClientState = BSD_DONE; switch(BSDClientState) { case DNS_START_RESOLUTION: if(DNSBeginUsage()) { //ML DNSResolveROM(ServerName, DNS_TYPE_A); DNSResolve(ServerName, DNS_TYPE_A); BSDClientState = DNS_GET_RESULT; } break; case DNS_GET_RESULT: if(!DNSIsResolved((IP_ADDR*)&addr.sin_addr.S_un.S_addr)) break; if(!DNSEndUsage()) { #if defined(STACK_USE_UART) putrsUART((ROM char*)"Could not resolve ServerName[] to IP address.\r\n"); #endif BSDClientState = BSD_DONE; break; } BSDClientState = BSD_START; // No break; here. case BSD_START: // Create a socket for this client to connect with if((bsdClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET ) return; #if defined(STACK_USE_UART) putrsUART((ROM char*)"\r\n\r\nConnecting using Berkeley Sockets TCP API...\r\n"); putrsUART((ROM char*)" Note: this demo will do nothing if an underlying TCP_PURPOSE_BERKELEY_CLIENT type \r\n" " socket is unavailable, as declared by the TCPSocketInitializer[] array in \r\n" " TCPIPConfig.h.\r\n\r\n"); #endif BSDClientState = BSD_CONNECT; break; case BSD_CONNECT: // addr.sin_addr.S_un.S_addr destination IP address was set earlier in DNS step addr.sin_port = PORTNUM; addrlen = sizeof(struct sockaddr); if(connect( bsdClientSocket, (struct sockaddr*)&addr, addrlen) < 0) return; BSDClientState = BSD_SEND; // No break needed case BSD_SEND: //send TCP data send(bsdClientSocket, (const char*)sendRequest, strlen((char*)sendRequest), 0); BSDClientState = BSD_OPERATION; break; case BSD_OPERATION: // Obtian and print the server reply while(1) { i = recv(bsdClientSocket, recvBuffer, sizeof(recvBuffer)-1, 0); //get the data from the recv queue if(i == 0) break; if(i< 0) //error condition { BSDClientState = BSD_CLOSE; break; } #if defined(STACK_USE_UART) recvBuffer[i] = '\0'; // Null terminate data putsUART((char*)recvBuffer); #endif if(BSDClientState == BSD_OPERATION) break; } break; case BSD_CLOSE: closesocket(bsdClientSocket); BSDClientState = BSD_DONE; // No break needed case BSD_DONE: if(BUTTON2_IO == 0u) BSDClientState = DNS_START_RESOLUTION; break; default: return; } //#if defined(STACK_USE_DNS) #else #warning You must define STACK_USE_DNS for BerkeleyTCPClientDemo to work #endif }
/***************************************************************************** Function: void BerkeleyUDPClientDemo(void) Summary: Periodically checks the current time from a pool of servers. Description: This function periodically checks a pool of time servers to obtain the current date/time. Precondition: UDP is initialized. Parameters: None Returns: None Remarks: This function requires once available UDP socket while processing, but frees that socket when the SNTP module is idle. ***************************************************************************/ void BerkeleyUDPClientDemo(void) { #if defined(STACK_USE_DNS) NTP_PACKET pkt; int i; static DWORD dwServerIP; static DWORD dwTimer; static SOCKET bsdUdpClient; int addrlen = sizeof(struct sockaddr_in); static struct sockaddr_in udpaddr; static enum { SM_HOME = 0, SM_NAME_RESOLVE, SM_CREATE_SOCKET, //SM_BIND, // Not required since we are sending the first packet SM_UDP_SEND, SM_UDP_RECV, SM_SHORT_WAIT, SM_WAIT } SNTPState = SM_HOME; switch(SNTPState) { case SM_HOME: // Obtain ownership of the DNS resolution module if(!DNSBeginUsage()) break; // Obtain the IP address associated with the server name // DNSResolveROM((ROM BYTE*)NTP_SERVER, DNS_TYPE_A); DNSResolve(( BYTE*)NTP_SERVER, DNS_TYPE_A); dwTimer = TickGet(); SNTPState = SM_NAME_RESOLVE; break; case SM_NAME_RESOLVE: // Wait for DNS resolution to complete if(!DNSIsResolved((IP_ADDR*)&dwServerIP)) { if((TickGet() - dwTimer) > (5 * TICK_SECOND)) { DNSEndUsage(); dwTimer = TickGetDiv64K(); SNTPState = SM_SHORT_WAIT; } break; } // Obtain DNS resolution result if(!DNSEndUsage()) { // No valid IP address was returned from the DNS // server. Quit and fail for a while if host is not valid. dwTimer = TickGetDiv64K(); SNTPState = SM_SHORT_WAIT; break; } SNTPState = SM_CREATE_SOCKET; // No break needed case SM_CREATE_SOCKET: bsdUdpClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(bsdUdpClient == INVALID_SOCKET) return; // No explicit binding is necessary since we are going to be the first one // sending a UDP packet. sendto() will do implicit binding. Explicit binding // is necessary only when creating UDP servers that will be receiving the first // packet. // SNTPState = SM_BIND; // // No break needed // // case SM_BIND: // udpaddr.sin_port = 0; // udpaddr.sin_addr.S_un.S_addr = IP_ADDR_ANY; // if( bind(bsdUdpClient, (struct sockaddr*)&udpaddr, addrlen) == SOCKET_ERROR ) // break; // SNTPState = SM_UDP_SEND; // No break needed case SM_UDP_SEND: // Transmit a time request packet memset(&pkt, 0, sizeof(pkt)); pkt.flags.versionNumber = 3; // NTP Version 3 pkt.flags.mode = 3; // NTP Client pkt.orig_ts_secs = swapl(NTP_EPOCH); udpaddr.sin_port = NTP_SERVER_PORT; udpaddr.sin_addr.S_un.S_addr = dwServerIP; if(sendto(bsdUdpClient, (const char*)&pkt, sizeof(pkt), 0, (struct sockaddr*)&udpaddr, addrlen)>0) { dwTimer = TickGet(); SNTPState = SM_UDP_RECV; } break; case SM_UDP_RECV: // Look for a response time packet i = recvfrom(bsdUdpClient, (char*)&pkt, sizeof(pkt), 0, (struct sockaddr*)&udpaddr, &addrlen); if(i < (int)sizeof(pkt)) { if((TickGet()) - dwTimer > NTP_REPLY_TIMEOUT) { // Abort the request and wait until the next timeout period closesocket(bsdUdpClient); dwTimer = TickGetDiv64K(); SNTPState = SM_SHORT_WAIT; break; } break; } closesocket(bsdUdpClient); dwTimer = TickGetDiv64K(); SNTPState = SM_WAIT; // Set out local time to match the returned time dwLastUpdateTick = TickGet(); dwSNTPSeconds = swapl(pkt.tx_ts_secs) - NTP_EPOCH; // Do rounding. If the partial seconds is > 0.5 then add 1 to the seconds count. if(((BYTE*)&pkt.tx_ts_fraq)[0] & 0x80) dwSNTPSeconds++; break; case SM_SHORT_WAIT: // Attempt to requery the NTP server after a specified NTP_FAST_QUERY_INTERVAL time (ex: 8 seconds) has elapsed. if(TickGetDiv64K() - dwTimer > (NTP_FAST_QUERY_INTERVAL/65536ull)) SNTPState = SM_HOME; break; case SM_WAIT: // Requery the NTP server after a specified NTP_QUERY_INTERVAL time (ex: 10 minutes) has elapsed. if(TickGetDiv64K() - dwTimer > (NTP_QUERY_INTERVAL/65536ull)) SNTPState = SM_HOME; break; } //#if defined(STACK_USE_DNS) #else #warning You must define STACK_USE_DNS for BerkeleyUDPClientDemo to work #endif }
/***************************************************************************** Function: void BerkeleyUDPClientDemo(uint32_t localIP) Summary: Periodically checks the current time from a pool of servers. Description: This function periodically checks a pool of time servers to obtain the current date/time. Precondition: UDP is initialized. Parameters: localIP - the local interface to use Returns: None Remarks: This function requires once available UDP socket while processing, but frees that socket when the SNTP module is idle. ***************************************************************************/ void BerkeleyUDPClientDemo(uint32_t localIP) { #if defined(TCPIP_STACK_USE_DNS) NTP_PACKET pkt; int i; static uint32_t dwServerIP; static SYS_TICK udpTick; static SOCKET bsdUdpClient; int udpSkt; int addrlen = sizeof(struct sockaddr_in); static struct sockaddr_in udpaddr; DNS_RESULT dnsRes; static enum { SM_HOME = 0, SM_NAME_RESOLVE, SM_CREATE_SOCKET, SM_BIND, SM_UDP_SEND, SM_UDP_RECV, SM_SHORT_WAIT, SM_WAIT } SNTPState = SM_HOME; switch(SNTPState) { case SM_HOME: // Obtain ownership of the DNS resolution module if(DNSBeginUsage(0) != DNS_RES_OK) { break; } // Obtain the IP address associated with the server name DNSResolve(NTP_SERVER, DNS_TYPE_A); udpTick = SYS_TICK_Get(); SNTPState = SM_NAME_RESOLVE; break; case SM_NAME_RESOLVE: // Wait for DNS resolution to complete dnsRes = DNSIsResolved(NTP_SERVER, (IP_ADDR*)&dwServerIP); if(dnsRes == DNS_RES_PENDING) { // still waiting break; } // release the DNS module DNSEndUsage(0); if(dnsRes < 0) { // some error udpTick = SYS_TICK_Get(); SNTPState = SM_SHORT_WAIT; break; } // DNS_RES_OK SNTPState = SM_CREATE_SOCKET; // No break needed case SM_CREATE_SOCKET: udpSkt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(udpSkt == SOCKET_ERROR) { return; } else { bsdUdpClient = (SOCKET)udpSkt; } // Usually no explicit binding is necessary since we are going to be the first one // sending a UDP packet. sendto() will do implicit binding. Explicit binding // is necessary only when creating UDP servers that will be receiving the first // packet. // However, when using this example with multi-homed hosts we want to use // a specified network connection specified by the localIP parameter SNTPState = SM_BIND; // No break needed case SM_BIND: udpaddr.sin_port = 0; udpaddr.sin_addr.S_un.S_addr = localIP; if( bind(bsdUdpClient, (struct sockaddr*)&udpaddr, addrlen) == SOCKET_ERROR ) break; SNTPState = SM_UDP_SEND; // No break needed case SM_UDP_SEND: // Transmit a time request packet memset(&pkt, 0, sizeof(pkt)); pkt.flags.versionNumber = 3; // NTP Version 3 pkt.flags.mode = 3; // NTP Client pkt.orig_ts_secs = swapl(NTP_EPOCH); udpaddr.sin_port = NTP_SERVER_PORT; udpaddr.sin_addr.S_un.S_addr = dwServerIP; if(sendto(bsdUdpClient, (const char*)&pkt, sizeof(pkt), 0, (struct sockaddr*)&udpaddr, addrlen)>0) { udpTick = SYS_TICK_Get(); SNTPState = SM_UDP_RECV; } break; case SM_UDP_RECV: // Look for a response time packet i = recvfrom(bsdUdpClient, (char*)&pkt, sizeof(pkt), 0, (struct sockaddr*)&udpaddr, &addrlen); if(i < (int)sizeof(pkt)) { if((SYS_TICK_Get()) - udpTick > NTP_REPLY_TIMEOUT * SYS_TICK_TicksPerSecondGet()) { // Abort the request and wait until the next timeout period closesocket(bsdUdpClient); udpTick = SYS_TICK_Get(); SNTPState = SM_SHORT_WAIT; break; } break; } closesocket(bsdUdpClient); udpTick = SYS_TICK_Get(); SNTPState = SM_WAIT; // Set out local time to match the returned time dwLastUpdateTick = SYS_TICK_Get(); dwSNTPSeconds = swapl(pkt.tx_ts_secs) - NTP_EPOCH; // Do rounding. If the partial seconds is > 0.5 then add 1 to the seconds count. if(((uint8_t*)&pkt.tx_ts_fraq)[0] & 0x80) dwSNTPSeconds++; break; case SM_SHORT_WAIT: // Attempt to requery the NTP server after a specified NTP_FAST_QUERY_INTERVAL time (ex: 8 seconds) has elapsed. if(SYS_TICK_Get() - udpTick > (NTP_FAST_QUERY_INTERVAL * SYS_TICK_TicksPerSecondGet() )) SNTPState = SM_HOME; break; case SM_WAIT: // Requery the NTP server after a specified NTP_QUERY_INTERVAL time (ex: 10 minutes) has elapsed. if(SYS_TICK_Get() - udpTick > (NTP_QUERY_INTERVAL * SYS_TICK_TicksPerSecondGet())) SNTPState = SM_HOME; break; } //#if defined(TCPIP_STACK_USE_DNS) #else #warning You must define TCPIP_STACK_USE_DNS for BerkeleyUDPClientDemo to work #endif }
/***************************************************************************** Function: void GenericTCPClient(void) Summary: Implements a simple HTTP client (over TCP). Description: This function implements a simple HTTP client, which operates over TCP. The function is called periodically by the stack, and waits for BUTTON1 to be pressed. When the button is pressed, the application opens a TCP connection to an Internet search engine, performs a search for the word "Microchip" on "microchip.com", and prints the resulting HTML page to the UART. This example can be used as a model for many TCP and HTTP client applications. Precondition: TCP is initialized. Parameters: None Returns: None ***************************************************************************/ void GenericTCPClient(void) { uint8_t i; uint16_t w; DNS_RESULT dnsRes; uint8_t vBuffer[9]; static TCPIP_NET_HANDLE netH; static uint32_t clientTimer; static TCP_SOCKET MySocket = INVALID_SOCKET; static uint32_t nAttempts =0; static enum _GenericTCPExampleState { SM_HOME = 0, SM_WAIT_DNS, SM_DNS_RESOLVED, SM_SOCKET_OBTAINED, SM_PROCESS_RESPONSE, SM_DISCONNECT, SM_DONE } GenericTCPExampleState = SM_DONE; //DBPRINTF(" Starting TCP client\n"); switch(GenericTCPExampleState) { case SM_HOME: DBPRINTF(" SM_HOME\n"); netH = TCPIP_STACK_GetDefaultNet(); if(DNSBeginUsage(netH) != DNS_RES_OK) { break; } DNSResolve(ServerName, DNS_TYPE_A); GenericTCPExampleState++; break; case SM_WAIT_DNS: DBPRINTF(" SM_WAIT_DNS\n"); dnsRes = DNSIsResolved(ServerName, &serverIP); if(dnsRes == DNS_RES_PENDING) { // ongoing operation; break; } else if(dnsRes < 0) { // some DNS error occurred; retry DBPRINTF((const char*)"\r\n\r\nGeneric TCP client: DNS name resolving failed...\r\n"); TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState = SM_HOME; nAttempts++; if(nAttempts>8) // After 8 attempts give-up { GenericTCPExampleState = SM_DONE; nAttempts=0; } } else { clientTimer = SYS_TICK_Get(); GenericTCPExampleState++; } DNSEndUsage(netH); break; case SM_DNS_RESOLVED: DBPRINTF(" SM_DNS_RESOLVED\n"); // Connect the socket to the remote TCP server MySocket = TCPOpenClient(IP_ADDRESS_TYPE_IPV4, ServerPort, (IP_MULTI_ADDRESS*)&serverIP); // 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) { // retry break; } GenericTCPExampleState++; clientTimer = SYS_TICK_Get(); break; case SM_SOCKET_OBTAINED: DBPRINTF(" SM_SOCKET_OBTAINED\n"); // Wait for the remote server to accept our connection request if(!TCPIsConnected(MySocket)) { // Time out if more than 5 seconds is spent in this state if((SYS_TICK_Get()-clientTimer) > 5 * SYS_TICK_TicksPerSecondGet() ) { // Close the socket so it can be used by other modules TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState--; DBPRINTF((const char*)"\r\n\r\nGeneric TCP client: Failed connecting to the remote server...\r\n"); } break; } clientTimer = SYS_TICK_Get(); // Make certain the socket can be written to if(TCPIsPutReady(MySocket) < 125u) break; // Place the application protocol data into the transmit buffer. For this example, we are connected to an HTTP server, so we'll send an HTTP GET request. TCPPutString(MySocket, (const uint8_t*)"GET "); TCPPutString(MySocket, RemoteURL); TCPPutString(MySocket, (const uint8_t*)" HTTP/1.0\r\nHost: "); TCPPutString(MySocket, (const uint8_t*)ServerName); TCPPutString(MySocket, (const uint8_t*)"\r\nConnection: close\r\n\r\n"); // Send the packet TCPFlush(MySocket); GenericTCPExampleState++; break; case SM_PROCESS_RESPONSE: //DBPRINTF(" SM_PROCESS_RESPONSE\n"); // Check to see if the remote node has disconnected from us or sent us any application data // If application data is available, write it to the UART if(!TCPIsConnected(MySocket)) { GenericTCPExampleState = SM_DISCONNECT; // Do not break; We might still have data in the TCP RX FIFO waiting for us } // Get count of RX bytes waiting w = TCPIsGetReady(MySocket); // Obtian and print the server reply i = sizeof(vBuffer)-1; vBuffer[i] = '\0'; while(w) { if(w < i) { i = w; vBuffer[i] = '\0'; } w -= TCPGetArray(MySocket, vBuffer, i); #if defined(GENERIC_TCP_CLIENT_ENABLE_UART_DUMP) DBPRINTF((char*)vBuffer); #endif // SYS_CONSOLE_MESSAGE is a blocking call which will slow down the rest of the stack // if we shovel the whole TCP RX FIFO into the serial port all at once. // Therefore, let's break out after only one chunk most of the time. The // only exception is when the remote node disconncets from us and we need to // use up all the data before changing states. if(GenericTCPExampleState == SM_PROCESS_RESPONSE) break; } break; case SM_DISCONNECT: DBPRINTF(" SM_DISCONNECT\n"); // Close the socket so it can be used by other modules // For this application, we wish to stay connected, but this state will still get entered if the remote server decides to disconnect TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState = SM_DONE; break; case SM_DONE: //DBPRINTF(" SM_DONE\n"); // Do nothing unless the user pushes BUTTON1 and wants to restart the whole connection/download process // On many boards, SYS_USERIO_BUTTON_0 is assigned to sw1 // SYS_USERIO_BUTTON_1=sw2 and SYS_USERIO_BUTTON_2=sw3 if(SYS_USERIO_ButtonGet((SYS_USERIO_BUTTON_1),SYS_USERIO_BUTTON_ASSERTED)) GenericTCPExampleState = SM_HOME; break; } }
/***************************************************************************** Function: void GenericSSLClient(void) Summary: Implements a simple HTTP client (over TCP). Description: This function implements a simple HTTP client, which operates over TCP. The function is called periodically by the stack, and waits for BUTTON1 to be pressed. When the button is pressed, the application opens a TCP connection to an Internet search engine, performs a search for the word "Microchip" on "microchip.com", and prints the resulting HTML page to the UART. To add this to an existing application, make the call to GenericSSLClient from StackTasks. This example can be used as a model for many TCP and HTTP client applications. Precondition: TCP is initialized. Parameters: None Returns: None ***************************************************************************/ void GenericSSLClient(void) { uint8_t i; uint16_t w; DNS_RESULT dnsRes; uint8_t vBuffer[9]; static TCPIP_NET_HANDLE netH; static uint32_t clientTimer; static TCP_SOCKET MySocket = INVALID_SOCKET; static enum _GenericTCPExampleState { SM_HOME = 0, SM_WAIT_DNS, SM_DNS_RESOLVED, SM_SOCKET_OBTAINED, SM_START_SSL, SM_PROCESS_RESPONSE, SM_DISCONNECT, SM_DONE } GenericTCPExampleState = SM_DONE; switch(GenericTCPExampleState) { case SM_HOME: netH = TCPIP_STACK_GetDefaultNet(); dnsRes = DNSBeginUsage(netH); if(dnsRes != DNS_RES_OK) break; DNSResolve(SSLServerName, DNS_TYPE_A); GenericTCPExampleState++; break; case SM_WAIT_DNS: dnsRes = DNSIsResolved(SSLServerName, &serverIP_SSL); if(dnsRes == DNS_RES_PENDING) { // ongoing operation; break; } else if(dnsRes < 0) { // some DNS error occurred; retry SYS_CONSOLE_MESSAGE((const char*)"\r\n\r\nDNS name resolving failed...\r\n"); TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState = SM_HOME; } else { clientTimer = SYS_TICK_Get(); GenericTCPExampleState++; } DNSEndUsage(netH); break; case SM_DNS_RESOLVED: // Connect the socket to the remote TCP server MySocket = TCPOpenClient(IP_ADDRESS_TYPE_IPV4, SSLServerPort, (IP_MULTI_ADDRESS*)&serverIP_SSL); // 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) { // retry break; } SYS_CONSOLE_MESSAGE((const char*)"\r\n\r\nConnecting using Microchip TCP API...\r\n"); GenericTCPExampleState++; clientTimer = SYS_TICK_Get(); break; case SM_SOCKET_OBTAINED: // Wait for the remote server to accept our connection request if(!TCPIsConnected(MySocket)) { // Time out if more than 5 seconds is spent in this state if((SYS_TICK_Get()-clientTimer) > 5 * SYS_TICK_TicksPerSecondGet() ) { // Close the socket so it can be used by other modules TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState--; SYS_CONSOLE_MESSAGE((const char*)"\r\n\r\nFailed connecting to the remote server...\r\n"); } break; } clientTimer = SYS_TICK_Get(); if(!TCPStartSSLClient(MySocket,(uint8_t *)"thishost")) break; GenericTCPExampleState++; break; case SM_START_SSL: if (TCPSSLIsHandshaking(MySocket)) { // Handshaking may fail if the SSL_RSA_CLIENT_SIZE is not large enough // for the server’s certificate if(SYS_TICK_Get()-clientTimer > 10*SYS_TICK_TicksPerSecondGet()) { // Close the socket so it can be used by other modules TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState=SM_HOME; } break; } // Make certain the socket can be written to if(TCPIsPutReady(MySocket) < 125u) break; // Place the application protocol data into the transmit buffer. For this example, we are connected to an HTTP server, so we'll send an HTTP GET request. TCPPutString(MySocket, (const uint8_t*)"GET "); TCPPutString(MySocket, SSLRemoteURL); TCPPutString(MySocket, (const uint8_t*)" HTTP/1.0\r\nHost: "); TCPPutString(MySocket, (const uint8_t*)SSLServerName); TCPPutString(MySocket, (const uint8_t*)"\r\nConnection: close\r\n\r\n"); // Send the packet TCPFlush(MySocket); GenericTCPExampleState++; break; case SM_PROCESS_RESPONSE: // Check to see if the remote node has disconnected from us or sent us any application data // If application data is available, write it to the UART if(!TCPIsConnected(MySocket)) { GenericTCPExampleState = SM_DISCONNECT; // Do not break; We might still have data in the TCP RX FIFO waiting for us } // Get count of RX bytes waiting w = TCPIsGetReady(MySocket); // Obtian and print the server reply i = sizeof(vBuffer)-1; vBuffer[i] = '\0'; while(w) { if(w < i) { i = w; vBuffer[i] = '\0'; } w -= TCPGetArray(MySocket, vBuffer, i); SYS_CONSOLE_MESSAGE((char*)vBuffer); // SYS_CONSOLE_MESSAGE is a blocking call which will slow down the rest of the stack // if we shovel the whole TCP RX FIFO into the serial port all at once. // Therefore, let's break out after only one chunk most of the time. The // only exception is when the remote node disconncets from us and we need to // use up all the data before changing states. if(GenericTCPExampleState == SM_PROCESS_RESPONSE) break; } break; case SM_DISCONNECT: // Close the socket so it can be used by other modules // For this application, we wish to stay connected, but this state will still get entered if the remote server decides to disconnect TCPClose(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState = SM_DONE; break; case SM_DONE: // Do nothing unless the user pushes BUTTON1 and wants to restart the whole connection/download process if(BUTTON1_IO == 0u) GenericTCPExampleState = SM_HOME; break; } }
/***************************************************************************** Function: void SMTPClientTask(void) Summary: Performs any pending SMTP client tasks Description: This function handles periodic tasks associated with the SMTP client, such as processing initial connections and command sequences. Precondition: None Parameters: None Returns: None Remarks: This function acts as a task (similar to one in an RTOS). It performs its task in a co-operative manner, and the main application must call this function repeatedly to ensure that all open or new connections are served in a timely fashion. ***************************************************************************/ void SMTPClientTask(void) { uint8_t i; uint16_t w; uint8_t vBase64Buffer[4]; static SYS_TICK SMTPTimer; static uint8_t RXBuffer[4]; static const uint8_t *ROMStrPtr, *ROMStrPtr2; static const uint8_t *RAMStrPtr; static uint16_t wAddressLength; DNS_RESULT dnsRes; switch(TransportState) { case TRANSPORT_HOME: // SMTPBeginUsage() 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 SMTPSendMail() if(!SMTPFlags.bits.ReadyToStart) break; // Obtain ownership of the DNS resolution module if(DNSBeginUsage(0) != DNS_RES_OK) { break; } // Obtain the IP address associated with the SMTP mail server if(SMTPClient.Server) { DNSResolve((const char*)SMTPClient.Server, DNS_TYPE_A); } else { // If we don't have a mail server, try to send the mail // directly to the destination SMTP server if(SMTPClient.To) { SMTPClient.Server = strchr((char*)SMTPClient.To, '@'); } if(!(SMTPClient.Server)) { if(SMTPClient.CC) { SMTPClient.Server = strchr((char*)SMTPClient.CC, '@'); } } if(!(SMTPClient.Server)) { if(SMTPClient.BCC) { SMTPClient.Server = strchr((char*)SMTPClient.BCC, '@'); } } // See if we found a hostname anywhere which we could resolve if(!(SMTPClient.Server)) { DNSEndUsage(0); ResponseCode = SMTP_RESOLVE_ERROR; TransportState = TRANSPORT_HOME; break; } // Skip over the @ sign and resolve the host name SMTPClient.Server++; DNSResolve((const char*)SMTPClient.Server, DNS_TYPE_MX); } SMTPTimer = SYS_TICK_Get(); TransportState++; break; case TRANSPORT_NAME_RESOLVE: // Wait for the DNS server to return the requested IP address dnsRes = DNSIsResolved((const char*)SMTPClient.Server, &SMTPServer); if(dnsRes == DNS_RES_PENDING) { break; } // Release the DNS module DNSEndUsage(0); 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 = TCPOpenClient(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; TransportState++; SMTPTimer = SYS_TICK_Get(); // No break; fall into TRANSPORT_SOCKET_OBTAINED #if defined(TCPIP_STACK_USE_SSL_CLIENT) case TRANSPORT_SECURING_SOCKET: if(!TCPIsConnected(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((SYS_TICK_Get()-SMTPTimer) > (SMTP_SERVER_REPLY_TIMEOUT * SYS_TICK_TicksPerSecondGet())) { ResponseCode = SMTP_CONNECT_ERROR; TransportState = TRANSPORT_CLOSE; } break; } SMTPFlags.bits.ConnectedOnce = true; // Start SSL if needed for this connection if(SMTPClient.UseSSL && !TCPStartSSLClient(MySocket,NULL)) break; // Move on to main state SMTPTimer = SYS_TICK_Get(); TransportState++; break; #endif case TRANSPORT_SOCKET_OBTAINED: if(!TCPIsConnected(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_TICK_Get()-SMTPTimer) > (SMTP_SERVER_REPLY_TIMEOUT * SYS_TICK_TicksPerSecondGet()))) { ResponseCode = SMTP_CONNECT_ERROR; TransportState = TRANSPORT_CLOSE; } break; } SMTPFlags.bits.ConnectedOnce = true; #if defined(TCPIP_STACK_USE_SSL_CLIENT) // Make sure the SSL handshake has completed if(SMTPClient.UseSSL && TCPSSLIsHandshaking(MySocket)) break; #endif // See if the server sent us anything while(TCPIsGetReady(MySocket)) { TCPGet(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(TCPIsPutReady(MySocket) < 64u) break; switch(SMTPState) { case SMTP_HELO: if(SMTPClient.Username == NULL) TCPPutString(MySocket, (uint8_t*)"HELO MCHPBOARD\r\n"); else TCPPutString(MySocket, (uint8_t*)"EHLO MCHPBOARD\r\n"); TCPFlush(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) TCPPutString(MySocket, (uint8_t*)"AUTH LOGIN\r\n"); TCPFlush(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)); TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer)); } TCPPutString(MySocket, (uint8_t*)"\r\n"); TCPFlush(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)); TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer)); } TCPPutString(MySocket, (uint8_t*)"\r\n"); TCPFlush(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. TCPPutString(MySocket, (uint8_t*)"MAIL FROM:<"); RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.From, &wAddressLength); TCPPutArray(MySocket, RAMStrPtr, wAddressLength); TCPPutString(MySocket, (uint8_t*)">\r\n"); TCPFlush(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: TCPPutString(MySocket, (uint8_t*)"RCPT TO:<"); TCPPutArray(MySocket, RAMStrPtr, wAddressLength); TCPPutString(MySocket, (uint8_t*)">\r\n"); TCPFlush(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: TCPPutString(MySocket, (uint8_t*)"DATA\r\n"); SMTPState++; PutHeadersState = PUTHEADERS_FROM_INIT; TCPFlush(MySocket); break; case SMTP_DATA_HEADER: while((PutHeadersState != PUTHEADERS_DONE) && (TCPIsPutReady(MySocket) > 64u)) { switch(PutHeadersState) { case PUTHEADERS_FROM_INIT: if(SMTPClient.From) { PutHeadersState = PUTHEADERS_FROM; TCPPutString(MySocket, (uint8_t*)"From: "); } else { PutHeadersState = PUTHEADERS_TO_INIT; } break; case PUTHEADERS_FROM: SMTPClient.From = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.From); if(*SMTPClient.From == 0u) PutHeadersState = PUTHEADERS_TO_INIT; break; case PUTHEADERS_TO_INIT: if(SMTPClient.To) { PutHeadersState = PUTHEADERS_TO; TCPPutString(MySocket, (uint8_t*)"\r\nTo: "); } else { PutHeadersState = PUTHEADERS_CC_INIT; } break; case PUTHEADERS_TO: SMTPClient.To = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.To); if(*SMTPClient.To == 0u) PutHeadersState = PUTHEADERS_CC_INIT; break; case PUTHEADERS_CC_INIT: if(SMTPClient.CC) { PutHeadersState = PUTHEADERS_CC; TCPPutString(MySocket, (uint8_t*)"\r\nCC: "); } else { PutHeadersState = PUTHEADERS_SUBJECT_INIT; } break; case PUTHEADERS_CC: SMTPClient.CC = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.CC); if(*SMTPClient.CC == 0u) PutHeadersState = PUTHEADERS_SUBJECT_INIT; break; case PUTHEADERS_SUBJECT_INIT: if(SMTPClient.Subject) { PutHeadersState = PUTHEADERS_SUBJECT; TCPPutString(MySocket, (uint8_t*)"\r\nSubject: "); } else { PutHeadersState = PUTHEADERS_OTHER_INIT; } break; case PUTHEADERS_SUBJECT: SMTPClient.Subject = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.Subject); if(*SMTPClient.Subject == 0u) PutHeadersState = PUTHEADERS_OTHER_INIT; break; case PUTHEADERS_OTHER_INIT: TCPPutArray(MySocket, (uint8_t*)"\r\n", 2); if(SMTPClient.OtherHeaders) { PutHeadersState = PUTHEADERS_OTHER; } else { TCPPutArray(MySocket, (uint8_t*)"\r\n", 2); PutHeadersState = PUTHEADERS_DONE; SMTPState++; } break; case PUTHEADERS_OTHER: SMTPClient.OtherHeaders = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.OtherHeaders); if(*SMTPClient.OtherHeaders == 0u) { TCPPutArray(MySocket, (uint8_t*)"\r\n", 2); PutHeadersState = PUTHEADERS_DONE; SMTPState++; } break; // Default case needed to supress compiler diagnostics default: break; } } TCPFlush(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 += TCPPutArray(MySocket, RAMStrPtr, CRPeriod.Pos-RAMStrPtr); if(RAMStrPtr == CRPeriod.Pos) { if(!TCPPut(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 = TCPPutString(MySocket, RAMStrPtr); ROMStrPtr2 = TCPPutString(MySocket, (uint8_t*)ROMStrPtr2); TCPFlush(MySocket); } } else { if(SMTPFlags.bits.ReadyToFinish) { if(*ROMStrPtr2) { ROMStrPtr2 = TCPPutString(MySocket, (uint8_t*)ROMStrPtr2); TCPFlush(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 = TCPPutString(MySocket, (uint8_t*)ROMStrPtr); TCPFlush(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 TCPClose(MySocket); MySocket = INVALID_SOCKET; // Go back to doing nothing TransportState = TRANSPORT_HOME; break; } }
/********************************************************************* * Function: void GenericTCPClient(void) * * PreCondition: Stack is initialized() * * Input: None * * Output: None * * Side Effects: None * * Overview: None * * Note: None ********************************************************************/ void GenericTCPClient(void) { BYTE i; BYTE *StringPtr; static TICK Timer; static TCP_SOCKET MySocket = INVALID_SOCKET; static NODE_INFO Server; static enum _GenericTCPExampleState { SM_HOME = 0, SM_NAME_RESOLVE, SM_ARP_START_RESOLVE, SM_ARP_RESOLVE, SM_SOCKET_OBTAIN, SM_SOCKET_OBTAINED, SM_PROCESS_RESPONSE, SM_DISCONNECT, SM_DONE } GenericTCPExampleState = SM_DONE; switch(GenericTCPExampleState) { case SM_HOME: // Obtain ownership of the DNS resolution module if(!DNSBeginUsage()) break; // Obtain the IP address associated with the common ServerName DNSResolve(ServerName, DNS_TYPE_A); GenericTCPExampleState++; break; case SM_NAME_RESOLVE: // Wait for the DNS server to return the requested IP address if(!DNSIsResolved(&Server.IPAddr)) break; // Release the DNS module, we no longer need it if(!DNSEndUsage()) { // An invalid IP address was returned from the DNS // server. Quit and fail permanantly if host is not valid. GenericTCPExampleState = SM_DONE; break; } GenericTCPExampleState++; case SM_ARP_START_RESOLVE: // Obtain the MAC address associated with the server's IP address (either direct MAC address on same subnet, or the MAC address of the Gateway machine) ARPResolve(&Server.IPAddr); Timer = TickGet(); GenericTCPExampleState++; break; case SM_ARP_RESOLVE: // Wait for the MAC address to finish being obtained if(!ARPIsResolved(&Server.IPAddr, &Server.MACAddr)) { // Time out if too much time is spent in this state if(TickGet()-Timer > 1*TICK_SECOND) { // Retransmit ARP request GenericTCPExampleState--; } break; } GenericTCPExampleState++; case SM_SOCKET_OBTAIN: // Connect a socket to the remote TCP server MySocket = TCPConnect(&Server, ServerPort); // Abort operation if no TCP sockets are available // If this ever happens, incrementing MAX_TCP_SOCKETS in // StackTsk.h may help (at the expense of more global memory // resources). if(MySocket == INVALID_SOCKET) break; GenericTCPExampleState++; Timer = TickGet(); break; case SM_SOCKET_OBTAINED: // Wait for the remote server to accept our connection request if(!TCPIsConnected(MySocket)) { // Time out if too much time is spent in this state if(TickGet()-Timer > 5*TICK_SECOND) { // Close the socket so it can be used by other modules TCPDisconnect(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState--; } break; } Timer = TickGet(); // Make certain the socket can be written to if(!TCPIsPutReady(MySocket)) break; // Place the application protocol data into the transmit buffer. For this example, we are connected to an HTTP server, so we'll send an HTTP GET request. TCPPutROMString(MySocket, (ROM BYTE*)"GET "); TCPPutROMString(MySocket, RemoteURL); TCPPutROMString(MySocket, (ROM BYTE*)" HTTP/1.1\r\nHost: "); TCPPutString(MySocket, ServerName); TCPPutROMString(MySocket, (ROM BYTE*)"\r\n\r\n"); // Send the packet TCPFlush(MySocket); GenericTCPExampleState++; case SM_PROCESS_RESPONSE: // Check to see if the remote node has disconnected from us or sent us any application data // If application data is available, write it to the UART if(!TCPIsConnected(MySocket)) { GenericTCPExampleState++; } if(!TCPIsGetReady(MySocket)) break; // Obtain the server reply while(TCPGet(MySocket, &i)) { while(BusyUART()); WriteUART(i); } break; case SM_DISCONNECT: // Close the socket so it can be used by other modules // For this application, we wish to stay connected, but this state will still get entered if the remote server decides to disconnect TCPDisconnect(MySocket); MySocket = INVALID_SOCKET; GenericTCPExampleState = SM_DONE; break; case SM_DONE: // Do nothing unless the user pushes BUTTON1 and wants to restart the whole connection/download process if(BUTTON1_IO == 0u) GenericTCPExampleState = SM_HOME; break; } }
/***************************************************************************** Function: void PingDemoTask (void) Summary: Handles state machine for ping demo processes. Description: This function performs state processing for the ping demo. This function can be used as a model for applications requiring Ping6 capabilities to check if a host is reachable. Precondition: None. Parameters: None Returns: None ***************************************************************************/ void PingDemoTask (void) { switch (pingState) { #if defined (TCPIP_STACK_USE_ICMP_CLIENT) case STATE_DNS_SEND_QUERY_IPV4: if (!DNSBeginUsage(pNetIf)) return; if (DNSResolve((const char *)targetHostName, DNS_TYPE_A) != DNS_RES_OK) return; pingTimer = SYS_TICK_Get() + (SYS_TICK_ResolutionGet() * TCPIP_PING_DNS_TIMEOUT); pingState = STATE_DNS_GET_RESPONSE_IPV4; break; case STATE_DNS_GET_RESPONSE_IPV4: { DNS_RESULT res; if ((long)(SYS_TICK_Get() - pingTimer) > 0) { SYS_OUT_MESSAGE_LINE("Couldn't resolve", 2); DNSEndUsage(pNetIf); pingState = STATE_IDLE; return; } res = DNSIsResolved((const char *)targetHostName, &targetAddressIPv4); switch (res) { case DNS_RES_OK: DNSEndUsage(pNetIf); pingState = STATE_RESOLVE_ARP; break; case DNS_RES_PENDING: break; default: SYS_OUT_MESSAGE_LINE ("Couldn't resolve", 1); DNSEndUsage(pNetIf); pingState = STATE_IDLE; break; } } break; case STATE_RESOLVE_ARP: if ((targetAddressIPv4.Val & pNetIf->MyMask.Val) == pNetIf->MyMask.Val) firstHopAddress.Val = targetAddressIPv4.Val; else firstHopAddress.Val = pNetIf->MyGateway.Val; ARPResolve(pNetIf, &firstHopAddress); pingTimer = SYS_TICK_Get(); pingState = STATE_ARP_RESOLVED; break; case STATE_ARP_RESOLVED: if(!ARPIsResolved(pNetIf, &firstHopAddress, &targetMACAddr)) { if(SYS_TICK_Get() - pingTimer > (TCPIP_PING_DNS_TIMEOUT * SYS_TICK_TicksPerSecondGet())) { SYS_OUT_MESSAGE_LINE ("Couldn't ARP", 1); pingState = STATE_IDLE; } break; } pingState = STATE_SEND_ECHO_REQUEST_IPV4; break; #endif #if defined (TCPIP_STACK_USE_IPV6) case STATE_DNS_SEND_QUERY_IPV6: if (!DNSBeginUsage(pNetIf)) return; if (DNSResolve((const char *)targetHostName, DNS_TYPE_AAAA) != DNS_RES_OK) return; pingTimer = SYS_TICK_Get() + (SYS_TICK_ResolutionGet() * TCPIP_PING_DNS_TIMEOUT); pingState = STATE_DNS_GET_RESPONSE_IPV6; break; case STATE_DNS_GET_RESPONSE_IPV6: { DNS_RESULT res; if ((long)(SYS_TICK_Get() - pingTimer) > 0) { SYS_OUT_MESSAGE_LINE ("Couldn't resolve", 1); DNSEndUsage(pNetIf); pingState = STATE_IDLE; return; } res = DNSIsResolved((const char *)targetHostName, &targetAddressIPv6); switch (res) { case DNS_RES_OK: DNSEndUsage(pNetIf); pingState = STATE_SEND_ECHO_REQUEST_IPV6; break; case DNS_RES_PENDING: break; default: SYS_OUT_MESSAGE_LINE ("Couldn't resolve", 1); DNSEndUsage(pNetIf); pingState = STATE_IDLE; break; } } break; #endif #if defined(TCPIP_STACK_USE_ICMP_CLIENT) case STATE_SEND_ECHO_REQUEST_IPV4: { NODE_INFO info; info.IPAddr = targetAddressIPv4; memcpy (&info.MACAddr, &targetMACAddr, sizeof (MAC_ADDR)); ICMPSendEchoRequest (&info, ++wICMPSequenceNumber, 0xBEEF); // Record the current time. This will be used as a basis for // finding the echo response time, which exludes the ARP and DNS // steps pingTimer = SYS_TICK_Get(); pingCount++; SYS_OUT_MESSAGE_LINE ("Pinging...", 1); // Echo sent, advance state pingState = STATE_GET_RESPONSE_IPV4; } break; #endif #if defined (TCPIP_STACK_USE_IPV6) case STATE_SEND_ECHO_REQUEST_IPV6: { IP_PACKET * pkt; IPV6_ADDR_STRUCT * localAddress; localAddress = TCPIP_IPV6_DAS_SelectSourceAddress (pNetIf, &targetAddressIPv6, NULL); if (localAddress == NULL) { SYS_OUT_MESSAGE_LINE ("No local addr!", 1); pingState = STATE_IDLE; break; } pkt = TCPIP_ICMPV6_PutHeaderEchoRequest (pNetIf, &localAddress->address, &targetAddressIPv6, ICMPV6_INFO_ECHO_REQUEST, 0xEFBE, ++wICMPSequenceNumber); if (TCPIP_IP_IsTxPutReady(pkt, 4) < 4) { TCPIP_IP_FreePacket (pkt); return; } TCPIP_IP_PutArray (pkt, (uint8_t *)&miscData, sizeof (uint32_t)); // Just let the IPv6 module figure out the next hop neighbor and its MAC address TCPIP_ICMPV6_Flush (pkt); // Record the current time. This will be used as a basis for // finding the echo response time, which exludes the ARP and DNS // steps pingTimer = SYS_TICK_Get(); pingCount++; SYS_OUT_MESSAGE_LINE ("Pinging...", 1); // Echo sent, advance state pingState = STATE_GET_RESPONSE_IPV6; } break; #endif #if defined (TCPIP_STACK_USE_ICMP_CLIENT) case STATE_GET_RESPONSE_IPV4: if ((long)(SYS_TICK_Get() - pingTimer) > (SYS_TICK_ResolutionGet() * TCPIP_PING_TIMEOUT)) { SYS_OUT_MESSAGE_LINE("Ping timeout", 1); pingState = STATE_IDLE; } break; #endif #if defined (TCPIP_STACK_USE_IPV6) case STATE_GET_RESPONSE_IPV6: if ((long)(SYS_TICK_Get() - pingTimer) > (SYS_TICK_ResolutionGet() * TCPIP_PING_TIMEOUT)) { SYS_OUT_MESSAGE_LINE ("Ping timeout", 1); pingState = STATE_IDLE; } break; #endif default: case STATE_IDLE: break; } }