void putulhexUART(uint32_t dw) { SYS_MESSAGE('0'); SYS_MESSAGE('x'); SYS_MESSAGE(btohexa_high(((uint8_t*)&dw)[3])); SYS_MESSAGE(btohexa_low(((uint8_t*)&dw)[3])); SYS_MESSAGE(btohexa_high(((uint8_t*)&dw)[2])); SYS_MESSAGE(btohexa_low(((uint8_t*)&dw)[2])); SYS_MESSAGE(btohexa_high(((uint8_t*)&dw)[1])); SYS_MESSAGE(btohexa_low(((uint8_t*)&dw)[1])); SYS_MESSAGE(btohexa_high(((uint8_t*)&dw)[0])); SYS_MESSAGE(btohexa_low(((uint8_t*)&dw)[0])); }
void BigIntPrint(const BIGINT *a) { uint16_t w; uint8_t v; BIGINT_DATA_TYPE *ptr; for(ptr = a->ptrMSBMax; ptr >= a->ptrLSB; ptr--) { TCPIP_UINT16_VAL wv; wv.Val = *ptr; SYS_MESSAGE(btohexa_high(wv.v[1])); SYS_MESSAGE(btohexa_low(wv.v[1])); SYS_MESSAGE(btohexa_high(wv.v[0])); SYS_MESSAGE(btohexa_low(wv.v[0])); } }
/**************************************************************************************************** Function: void AnnounceIP(void) Summary: Transmits an Announce packet. Conditions: Stack is initialized() Return: None Side Effects: None Description: AnnounceIP opens a UDP socket and transmits a broadcast packet to port \30303. If a computer is on the same subnet and a utility is looking for packets on the UDP port, it will receive the broadcast. For this application, it is used to announce the change of this board's IP address. The messages can be viewed with the TCP/IP Discoverer software tool. Remarks: A UDP socket must be available before this function is called. It is freed at the end of the function. MAX_UDP_SOCKETS may need to be increased if other modules use UDP sockets. ****************************************************************************************************/ void AnnounceIP(void) { UDP_SOCKET MySocket; BYTE i; if(!MACIsLinked()) // Check for link before blindly opening and transmitting (similar to DHCP case) return; // Open a UDP socket for outbound broadcast transmission //MySocket = UDPOpen(2860, NULL, ANNOUNCE_PORT); MySocket = UDPOpenEx(0,UDP_OPEN_SERVER,2860, ANNOUNCE_PORT); LED1_IO = 0; // Abort operation if no UDP sockets are available // If this ever happens, incrementing MAX_UDP_SOCKETS in // StackTsk.h may help (at the expense of more global memory // resources). if(MySocket == INVALID_UDP_SOCKET) return; // Make certain the socket can be written to while(!UDPIsPutReady(MySocket)); // Begin sending our MAC address in human readable form. // The MAC address theoretically could be obtained from the // packet header when the computer receives our UDP packet, // however, in practice, the OS will abstract away the useful // information and it would be difficult to obtain. It also // would be lost if this broadcast packet were forwarded by a // router to a different portion of the network (note that // broadcasts are normally not forwarded by routers). UDPPutArray((BYTE*)AppConfig.NetBIOSName, sizeof(AppConfig.NetBIOSName)-1); UDPPut('\r'); UDPPut('\n'); // Convert the MAC address bytes to hex (text) and then send it i = 0; while(1) { UDPPut(btohexa_high(AppConfig.MyMACAddr.v[i])); UDPPut(btohexa_low(AppConfig.MyMACAddr.v[i])); if(++i == 6u) break; UDPPut('-'); } // Send some other human readable information. UDPPutROMString((ROM BYTE*)"\r\nDHCP/Power event occurred"); // Send the packet UDPFlush(); // Close the socket so it can be used by other modules UDPClose(MySocket); }
void BigIntPrintROM(BIGINT_ROM *a) { ROM BIGINT_DATA_TYPE *ptr; for(ptr = a->ptrMSB; ptr >= a->ptrLSB; ptr--) { while(BusyUART()); putcUART(btohexa_high(*ptr)); while(BusyUART()); putcUART(btohexa_low(*ptr)); } }
void BigIntPrint(const BIGINT *a) { BIGINT_DATA_TYPE *ptr; for(ptr = a->ptrMSBMax; ptr >= a->ptrLSB; ptr--) { while(BusyUART()); putcUART(btohexa_high(*ptr)); while(BusyUART()); putcUART(btohexa_low(*ptr)); } }
bool TCPIP_Helper_IPv6AddressToString (const IPV6_ADDR * v6Addr, char* buff, size_t buffSize) { if(v6Addr && buff && buffSize >= 41) { uint8_t i, j; char k; char* str = buff; for (i = 0; i < 8; i++) { j = false; k = btohexa_high(v6Addr->v[(i<<1)]); if (k != '0') { j = true; *str++ = k; } k = btohexa_low(v6Addr->v[(i<<1)]); if (k != '0' || j == true) { j = true; *str++ = k; } k = btohexa_high(v6Addr->v[1 + (i<<1)]); if (k != '0' || j == true) *str++ = k; k = btohexa_low(v6Addr->v[1 + (i<<1)]); *str++ = k; if (i != 7) *str++ = ':'; } *str = 0; return true; } return false; }
void putulhexUART(DWORD dw) { while(BusyUART()); putcUART('0'); while(BusyUART()); putcUART('x'); while(BusyUART()); putcUART(btohexa_high(((BYTE*)&dw)[3])); while(BusyUART()); putcUART(btohexa_low(((BYTE*)&dw)[3])); while(BusyUART()); putcUART(btohexa_high(((BYTE*)&dw)[2])); while(BusyUART()); putcUART(btohexa_low(((BYTE*)&dw)[2])); while(BusyUART()); putcUART(btohexa_high(((BYTE*)&dw)[1])); while(BusyUART()); putcUART(btohexa_low(((BYTE*)&dw)[1])); while(BusyUART()); putcUART(btohexa_high(((BYTE*)&dw)[0])); while(BusyUART()); putcUART(btohexa_low(((BYTE*)&dw)[0])); }
void BigIntPrint(const BIGINT *a) { WORD w; BYTE v; BIGINT_DATA_TYPE *ptr; for(ptr = a->ptrMSBMax; ptr >= a->ptrLSB; ptr--) { WORD_VAL wv; wv.Val = *ptr; while(BusyUART()); putcUART(btohexa_high(wv.v[1])); while(BusyUART()); putcUART(btohexa_low(wv.v[1])); while(BusyUART()); putcUART(btohexa_high(wv.v[0])); while(BusyUART()); putcUART(btohexa_low(wv.v[0])); } }
////////////////////////////////////////////////////////////////////////////////////////// // NOTE: The following HTTP code pretains to the old HTTP server. // Upgrading to HTTP2 is *strongly* recommended for all new designs. // Custom control of HTTP2 is implemented in CustomHTTPApp.c ////////////////////////////////////////////////////////////////////////////////////////// WORD HTTPGetVar(BYTE var, WORD ref, BYTE* val) { // Temporary variables designated for storage of a whole return // result to simplify logic needed since one byte must be returned // at a time. static BYTE VarString[25]; #if defined(ENABLE_REMOTE_CONFIG) static BYTE VarStringLen; BYTE *VarStringPtr; BYTE i; BYTE *DataSource; #endif // Identify variable switch(var) { case VAR_LED0: *val = LED0_IO ? '1':'0'; break; case VAR_LED1: *val = LED1_IO ? '1':'0'; break; case VAR_LED2: *val = LED2_IO ? '1':'0'; break; case VAR_LED3: *val = LED3_IO ? '1':'0'; break; case VAR_LED4: *val = LED4_IO ? '1':'0'; break; case VAR_LED5: *val = LED5_IO ? '1':'0'; break; case VAR_LED6: *val = LED6_IO ? '1':'0'; break; case VAR_LED7: *val = LED7_IO ? '1':'0'; break; case VAR_ANAIN_AN0: *val = AN0String[(BYTE)ref]; if(AN0String[(BYTE)ref] == '\0') return HTTP_END_OF_VAR; else if(AN0String[(BYTE)++ref] == '\0' ) return HTTP_END_OF_VAR; return ref; // case VAR_ANAIN_AN1: // *val = AN1String[(BYTE)ref]; // if(AN1String[(BYTE)ref] == '\0') // return HTTP_END_OF_VAR; // else if(AN1String[(BYTE)++ref] == '\0' ) // return HTTP_END_OF_VAR; // return ref; case VAR_DIGIN0: *val = BUTTON0_IO ? '1':'0'; break; case VAR_DIGIN1: *val = BUTTON1_IO ? '1':'0'; break; case VAR_DIGIN2: *val = BUTTON2_IO ? '1':'0'; break; case VAR_DIGIN3: *val = BUTTON3_IO ? '1':'0'; break; case VAR_STACK_VERSION: if(ref == HTTP_START_OF_VAR) { strncpypgm2ram((char*)VarString, (ROM char*)TCPIP_STACK_VERSION, sizeof(VarString)); } *val = VarString[(BYTE)ref]; if(VarString[(BYTE)ref] == '\0') return HTTP_END_OF_VAR; else if(VarString[(BYTE)++ref] == '\0' ) return HTTP_END_OF_VAR; return ref; case VAR_STACK_DATE: if(ref == HTTP_START_OF_VAR) { strncpypgm2ram((char*)VarString, (ROM char*)(__DATE__ " " __TIME__), sizeof(VarString)); } *val = VarString[(BYTE)ref]; if(VarString[(BYTE)ref] == '\0') return HTTP_END_OF_VAR; else if(VarString[(BYTE)++ref] == '\0' ) return HTTP_END_OF_VAR; return ref; #if defined(ENABLE_REMOTE_CONFIG) case VAR_MAC_ADDRESS: if ( ref == HTTP_START_OF_VAR ) { VarStringLen = 2*6+5; // 17 bytes: 2 for each of the 6 address bytes + 5 octet spacers // Format the entire string i = 0; VarStringPtr = VarString; while(1) { *VarStringPtr++ = btohexa_high(AppConfig.MyMACAddr.v[i]); *VarStringPtr++ = btohexa_low(AppConfig.MyMACAddr.v[i]); if(++i == 6) break; *VarStringPtr++ = '-'; } } // Send one byte back to the calling function (the HTTP Server) *val = VarString[(BYTE)ref]; if ( (BYTE)++ref == VarStringLen ) return HTTP_END_OF_VAR; return ref; case VAR_IP_ADDRESS: case VAR_SUBNET_MASK: case VAR_GATEWAY_ADDRESS: // Check if ref == 0 meaning that the first character of this // variable needs to be returned if ( ref == HTTP_START_OF_VAR ) { // Decide which 4 variable bytes to send back if(var == VAR_IP_ADDRESS) DataSource = (BYTE*)&AppConfig.MyIPAddr; else if(var == VAR_SUBNET_MASK) DataSource = (BYTE*)&AppConfig.MyMask; else if(var == VAR_GATEWAY_ADDRESS) DataSource = (BYTE*)&AppConfig.MyGateway; // Format the entire string VarStringPtr = VarString; i = 0; while(1) { uitoa((WORD)*DataSource++, VarStringPtr); VarStringPtr += strlen(VarStringPtr); if(++i == 4) break; *VarStringPtr++ = '.'; } VarStringLen = strlen(VarString); } // Send one byte back to the calling function (the HTTP Server) *val = VarString[(BYTE)ref]; // If this is the last byte to be returned, return // HTTP_END_OF_VAR so the HTTP server won't keep calling this // application callback function if ( (BYTE)++ref == VarStringLen ) return HTTP_END_OF_VAR; return ref; case VAR_DHCP_TRUE: case VAR_DHCP_FALSE: // Check if ref == 0 meaning that the first character of this // variable needs to be returned if ( ref == HTTP_START_OF_VAR ) { if((var == VAR_DHCP_TRUE) ^ AppConfig.Flags.bIsDHCPEnabled) return HTTP_END_OF_VAR; VarStringLen = 7; memcpypgm2ram(VarString, (ROM void *)"checked", 7); } *val = VarString[(BYTE)ref]; if ( (BYTE)++ref == VarStringLen ) return HTTP_END_OF_VAR; return ref; #endif // #if defined(ENABLE_REMOTE_CONFIG) } return HTTP_END_OF_VAR; }
void TCPTXPerformanceTask(void) { static TCP_SOCKET MySocket = INVALID_SOCKET; static DWORD dwTimeStart; static DWORD dwBytesSent; static DWORD_VAL dwVLine; BYTE vBuffer[10]; static BYTE vBytesPerSecond[12]; WORD w; DWORD dw; QWORD qw; // Start the TCP server, listening on PERFORMANCE_PORT if(MySocket == INVALID_SOCKET) { MySocket = TCPOpen(0, TCP_OPEN_SERVER, TX_PERFORMANCE_PORT, TCP_PURPOSE_TCP_PERFORMANCE_TX); // Abort operation if no TCP socket of type TCP_PURPOSE_TCP_PERFORMANCE_TEST is available // If this ever happens, you need to go add one to TCPIPConfig.h if(MySocket == INVALID_SOCKET) return; dwVLine.Val = 0; dwTimeStart = TickGet(); vBytesPerSecond[0] = 0; // Initialize empty string right now dwBytesSent = 0; } // See how many bytes we can write to the TX FIFO // If we can't fit a single line of data in, then // lets just wait for now. w = TCPIsPutReady(MySocket); if(w < 12+27+5+32u) return; vBuffer[0] = '0'; vBuffer[1] = 'x'; // Transmit as much data as the TX FIFO will allow while(w >= 12+27+5+32u) { // Convert line counter to ASCII hex string vBuffer[2] = btohexa_high(dwVLine.v[3]); vBuffer[3] = btohexa_low(dwVLine.v[3]); vBuffer[4] = btohexa_high(dwVLine.v[2]); vBuffer[5] = btohexa_low(dwVLine.v[2]); vBuffer[6] = btohexa_high(dwVLine.v[1]); vBuffer[7] = btohexa_low(dwVLine.v[1]); vBuffer[8] = btohexa_high(dwVLine.v[0]); vBuffer[9] = btohexa_low(dwVLine.v[0]); dwVLine.Val++; // Place all data in the TCP TX FIFO TCPPutArray(MySocket, vBuffer, sizeof(vBuffer)); dw = TickGet() - dwTimeStart; // Calculate exact bytes/second, less truncation if((dwVLine.v[0] & 0x3F) == 0x00) { qw = (QWORD)dwBytesSent * (TICK_SECOND/100); qw /= dw; ultoa((DWORD)qw, vBytesPerSecond); } TCPPutROMString(MySocket, (ROM BYTE*)": We are currently achieving "); TCPPutROMArray(MySocket, (ROM BYTE*)" ", 5-strlen((char*)vBytesPerSecond)); TCPPutString(MySocket, vBytesPerSecond); TCPPutROMString(MySocket, (ROM BYTE*)"00 bytes/second TX throughput.\r\n"); if(dw > TICK_SECOND) { dwBytesSent >>= 1; dwTimeStart += dw>>1; } w -= 12+27+5+32; dwBytesSent += 12+27+5+32; }
/********************************************************************* * Function: void DiscoveryTask(void) * * Summary: Announce callback task. * * PreCondition: Stack is initialized() * * Input: None * * Output: None * * Side Effects: None * * Overview: Recurring task used to listen for Discovery * messages on the specified ANNOUNCE_PORT. These * messages can be sent using the Microchip Device * Discoverer tool. If one is received, this * function will transmit a reply. * * Note: A UDP socket must be available before this * function is called. It is freed at the end of * the function. MAX_UDP_SOCKETS may need to be * increased if other modules use UDP sockets. ********************************************************************/ void DiscoveryTask(void) { static enum { DISCOVERY_HOME = 0, DISCOVERY_LISTEN, DISCOVERY_REQUEST_RECEIVED, DISCOVERY_DISABLED } DiscoverySM = DISCOVERY_HOME; static UDP_SOCKET MySocket; BYTE i; switch(DiscoverySM) { case DISCOVERY_HOME: // Open a UDP socket for inbound and outbound transmission // Since we expect to only receive broadcast packets and // only send unicast packets directly to the node we last // received from, the remote NodeInfo parameter can be anything MySocket = UDPOpen(ANNOUNCE_PORT, NULL, ANNOUNCE_PORT); if(MySocket == INVALID_UDP_SOCKET) return; else DiscoverySM++; break; case DISCOVERY_LISTEN: // Do nothing if no data is waiting if(!UDPIsGetReady(MySocket)) return; // See if this is a discovery query or reply UDPGet(&i); UDPDiscard(); if(i != 'D') return; // We received a discovery request, reply when we can DiscoverySM++; // Change the destination to the unicast address of the last received packet memcpy((void*)&UDPSocketInfo[MySocket].remoteNode, (const void*)&remoteNode, sizeof(remoteNode)); // No break needed. If we get down here, we are now ready for the DISCOVERY_REQUEST_RECEIVED state case DISCOVERY_REQUEST_RECEIVED: if(!UDPIsPutReady(MySocket)) return; // Begin sending our MAC address in human readable form. // The MAC address theoretically could be obtained from the // packet header when the computer receives our UDP packet, // however, in practice, the OS will abstract away the useful // information and it would be difficult to obtain. It also // would be lost if this broadcast packet were forwarded by a // router to a different portion of the network (note that // broadcasts are normally not forwarded by routers). UDPPutArray((BYTE*)AppConfig.NetBIOSName, sizeof(AppConfig.NetBIOSName)-1); UDPPut('\r'); UDPPut('\n'); // Convert the MAC address bytes to hex (text) and then send it i = 0; while(1) { UDPPut(btohexa_high(AppConfig.MyMACAddr.v[i])); UDPPut(btohexa_low(AppConfig.MyMACAddr.v[i])); if(++i == 6u) break; UDPPut('-'); } UDPPut('\r'); UDPPut('\n'); // Send the packet UDPFlush(); // Listen for other discovery requests DiscoverySM = DISCOVERY_LISTEN; break; case DISCOVERY_DISABLED: break; } }
/********************************************************************* * Function: void AnnounceIP(void) * * PreCondition: Stack is initialized() * * Input: None * * Output: None * * Side Effects: None * * Overview: AnnounceIP opens a UDP socket and transmits a * broadcast packet to port 30303. If a computer is * on the same subnet and a utility is looking for * packets on the UDP port, it will receive the * broadcast. For this application, it is used to * announce the change of this board's IP address. * The messages can be viewed with the MCHPDetect.exe * program. * * Note: A UDP socket must be available before this * function is called. It is freed at the end of * the function. MAX_UDP_SOCKETS may need to be * increased if other modules use UDP sockets. ********************************************************************/ void AnnounceIP(void) { UDP_SOCKET MySocket; NODE_INFO Remote; BYTE i; // Set the socket's destination to be a broadcast over our IP // subnet // Set the MAC destination to be a broadcast memset(&Remote, 0xFF, sizeof(Remote)); // Open a UDP socket for outbound transmission MySocket = UDPOpen(2860, &Remote, ANNOUNCE_PORT); // Abort operation if no UDP sockets are available // If this ever happens, incrementing MAX_UDP_SOCKETS in // StackTsk.h may help (at the expense of more global memory // resources). if( MySocket == INVALID_UDP_SOCKET ) { #if (DEBUG_ANNOUNCE >= LOG_ERROR) debugPutMsg(1); //@mxd:1:Could not open UDP socket #endif return; } // Make certain the socket can be written to while( !UDPIsPutReady(MySocket) ) FAST_USER_PROCESS(); // Begin sending our MAC address in human readable form. // The MAC address theoretically could be obtained from the // packet header when the computer receives our UDP packet, // however, in practice, the OS will abstract away the useful // information and it would be difficult to obtain. It also // would be lost if this broadcast packet were forwarded by a // router to a different portion of the network (note that // broadcasts are normally not forwarded by routers). for(i=0; i < 15; i++) //First 15 (0-14) characters are NetBIOS name, 16th character is 0x00 { UDPPut(NETBIOS_NAME_GETCHAR(i)); } UDPPut('\r'); UDPPut('\n'); // Convert the MAC address bytes to hex (text) and then send it i = 0; while(1) { UDPPut(btohexa_high(AppConfig.MyMACAddr.v[i])); UDPPut(btohexa_low(AppConfig.MyMACAddr.v[i])); if(++i == 6) break; UDPPut('-'); } UDPPut('\r'); UDPPut('\n'); // Send some other human readable information. UDPPut('D'); UDPPut('H'); UDPPut('C'); UDPPut('P'); UDPPut('/'); UDPPut('P'); UDPPut('o'); UDPPut('w'); UDPPut('e'); UDPPut('r'); UDPPut(' '); UDPPut('e'); UDPPut('v'); UDPPut('e'); UDPPut('n'); UDPPut('t'); UDPPut(' '); UDPPut('o'); UDPPut('c'); UDPPut('c'); UDPPut('u'); UDPPut('r'); UDPPut('r'); UDPPut('e'); UDPPut('d'); // Send the packet UDPFlush(); // Close the socket so it can be used by other modules UDPClose(MySocket); }
/********************************************************************* * Function: static void HTTPProcess(void) * * PreCondition: HTTPInit() called and curHTTP loaded * * Input: None * * Output: None * * Side Effects: None * * Overview: Serves the current HTTP connection in curHTTP * * Note: None ********************************************************************/ static void HTTPProcess(void) { WORD lenA, lenB; BYTE c, i; BOOL isDone; BYTE *ext; BYTE buffer[HTTP_MAX_HEADER_LEN+1]; do { isDone = TRUE; // If a socket is disconnected at any time // forget about it and return to idle state. if(TCPWasReset(sktHTTP)) { smHTTP = SM_HTTP_IDLE; // Make sure any opened files are closed if(curHTTP.file != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.file); curHTTP.file = MPFS_INVALID_HANDLE; } if(curHTTP.offsets != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.offsets); curHTTP.offsets = MPFS_INVALID_HANDLE; } // Adjust the TCP FIFOs for optimal reception of // the next HTTP request from the browser TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_GIVE_REST_TO_RX | TCP_ADJUST_PRESERVE_RX); } switch(smHTTP) { case SM_HTTP_IDLE: // Check how much data is waiting lenA = TCPIsGetReady(sktHTTP); // If a connection has been made, then process the request if(lenA) {// Clear out state info and move to next state curHTTP.ptrData = curHTTP.data; smHTTP = SM_HTTP_PARSE_REQUEST; curHTTP.isAuthorized = 0xff; curHTTP.hasArgs = FALSE; curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; curHTTP.callbackPos = 0xffffffff; curHTTP.byteCount = 0; } // In all cases, we break // For new connections, this waits for the buffer to fill break; case SM_HTTP_PARSE_REQUEST: // Verify the entire first line is in the FIFO if(TCPFind(sktHTTP, '\n', 0, FALSE) == 0xffff) {// First line isn't here yet if(TCPGetRxFIFOFree(sktHTTP) == 0) {// If the FIFO is full, we overflowed curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; } if(TickGet() > curHTTP.callbackID) {// A timeout has occurred TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_DISCONNECT; isDone = FALSE; } break; } // Reset the watchdog timer curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; // Determine the request method lenA = TCPFind(sktHTTP, ' ', 0, FALSE); if(lenA > 5) lenA = 5; TCPGetArray(sktHTTP, curHTTP.data, lenA+1); if ( memcmppgm2ram(curHTTP.data, (ROM void*)"GET", 3) == 0) curHTTP.httpStatus = HTTP_GET; #if defined(HTTP_USE_POST) else if ( memcmppgm2ram(curHTTP.data, (ROM void*)"POST", 4) == 0) curHTTP.httpStatus = HTTP_POST; #endif else {// Unrecognized method, so return not implemented curHTTP.httpStatus = HTTP_NOT_IMPLEMENTED; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Find end of filename lenA = TCPFind(sktHTTP, ' ', 0, FALSE); lenB = TCPFindEx(sktHTTP, '?', 0, lenA, FALSE); lenA = mMIN(lenA, lenB); // If the file name is too long, then reject the request if(lenA > HTTP_MAX_DATA_LEN - HTTP_DEFAULT_LEN - 1) { curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Read in the filename and decode lenB = TCPGetArray(sktHTTP, curHTTP.data, lenA); curHTTP.data[lenB] = '\0'; HTTPURLDecode(curHTTP.data); // Check if this is an MPFS Upload #if defined(HTTP_MPFS_UPLOAD) if(memcmppgm2ram(&curHTTP.data[1], HTTP_MPFS_UPLOAD, strlenpgm(HTTP_MPFS_UPLOAD)) == 0) {// Read remainder of line, and bypass all file opening, etc. #if defined(HTTP_USE_AUTHENTICATION) curHTTP.isAuthorized = HTTPAuthenticate(NULL, NULL, &curHTTP.data[1]); #endif if(curHTTP.httpStatus == HTTP_GET) curHTTP.httpStatus = HTTP_MPFS_FORM; else curHTTP.httpStatus = HTTP_MPFS_UP; smHTTP = SM_HTTP_PARSE_HEADERS; isDone = FALSE; break; } #endif // If the last character is a not a directory delimiter, then try to open the file // String starts at 2nd character, because the first is always a '/' if(curHTTP.data[lenB-1] != '/') curHTTP.file = MPFSOpen(&curHTTP.data[1]); // If the open fails, then add our default name and try again if(curHTTP.file == MPFS_INVALID_HANDLE) { // Add the directory delimiter if needed if(curHTTP.data[lenB-1] != '/') curHTTP.data[lenB++] = '/'; // Add our default file name // If this is a loopback, then it's an SSL connection if(TCPIsLoopback(sktHTTP)) { strcpypgm2ram((void*)&curHTTP.data[lenB], HTTPS_DEFAULT_FILE); lenB += strlenpgm(HTTPS_DEFAULT_FILE); } else { strcpypgm2ram((void*)&curHTTP.data[lenB], HTTP_DEFAULT_FILE); lenB += strlenpgm(HTTP_DEFAULT_FILE); } // Try to open again curHTTP.file = MPFSOpen(&curHTTP.data[1]); } // Find the extension in the filename for(ext = curHTTP.data + lenB-1; ext != curHTTP.data; ext--) if(*ext == '.') break; // Compare to known extensions to determine Content-Type ext++; for(curHTTP.fileType = HTTP_TXT; curHTTP.fileType < HTTP_UNKNOWN; curHTTP.fileType++) if(!stricmppgm2ram(ext, (ROM void*)httpFileExtensions[curHTTP.fileType])) break; // Perform first round authentication (pass file name only) #if defined(HTTP_USE_AUTHENTICATION) curHTTP.isAuthorized = HTTPAuthenticate(NULL, NULL, &curHTTP.data[1]); #endif // If the file was found, see if it has an index if(curHTTP.file != MPFS_INVALID_HANDLE && (MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_HASINDEX) ) { curHTTP.data[lenB-1] = '#'; curHTTP.offsets = MPFSOpen(&curHTTP.data[1]); } // Read GET args, up to buffer size - 1 lenA = TCPFind(sktHTTP, ' ', 0, FALSE); if(lenA != 0) { curHTTP.hasArgs = TRUE; // Trash the '?' TCPGet(sktHTTP, &c); // Verify there's enough space lenA--; if(lenA >= HTTP_MAX_DATA_LEN - 2) { curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Read in the arguments and '&'-terminate in anticipation of cookies curHTTP.ptrData += TCPGetArray(sktHTTP, curHTTP.data, lenA); *(curHTTP.ptrData++) = '&'; } // Clear the rest of the line lenA = TCPFind(sktHTTP, '\n', 0, FALSE); TCPGetArray(sktHTTP, NULL, lenA + 1); // Move to parsing the headers smHTTP = SM_HTTP_PARSE_HEADERS; // No break, continue to parsing headers case SM_HTTP_PARSE_HEADERS: // Loop over all the headers while(1) { // Make sure entire line is in the FIFO lenA = TCPFind(sktHTTP, '\n', 0, FALSE); if(lenA == 0xffff) {// If not, make sure we can receive more data if(TCPGetRxFIFOFree(sktHTTP) == 0) {// Overflow curHTTP.httpStatus = HTTP_OVERFLOW; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; } if(TickGet() > curHTTP.callbackID) {// A timeout has occured TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_DISCONNECT; isDone = FALSE; } break; } // Reset the watchdog timer curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; // If a CRLF is immediate, then headers are done if(lenA == 1) {// Remove the CRLF and move to next state TCPGetArray(sktHTTP, NULL, 2); smHTTP = SM_HTTP_AUTHENTICATE; isDone = FALSE; break; } // Find the header name, and use isDone as a flag to indicate a match lenB = TCPFindEx(sktHTTP, ':', 0, lenA, FALSE) + 2; isDone = FALSE; // If name is too long or this line isn't a header, ignore it if(lenB > sizeof(buffer)) { TCPGetArray(sktHTTP, NULL, lenA+1); continue; } // Read in the header name TCPGetArray(sktHTTP, buffer, lenB); buffer[lenB-1] = '\0'; lenA -= lenB; // Compare header read to ones we're interested in for(i = 0; i < HTTP_NUM_HEADERS; i++) { if(strcmppgm2ram((char*)buffer, (ROM char *)HTTPRequestHeaders[i]) == 0) {// Parse the header and stop the loop HTTPHeaderParseLookup(i); isDone = TRUE; break; } } // Clear the rest of the line, and call the loop again if(isDone) {// We already know how much to remove unless a header was found lenA = TCPFind(sktHTTP, '\n', 0, FALSE); } TCPGetArray(sktHTTP, NULL, lenA+1); } break; case SM_HTTP_AUTHENTICATE: #if defined(HTTP_USE_AUTHENTICATION) // Check current authorization state if(curHTTP.isAuthorized < 0x80) {// 401 error curHTTP.httpStatus = HTTP_UNAUTHORIZED; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; #if defined(HTTP_NO_AUTH_WITHOUT_SSL) if(!TCPIsLoopback(sktHTTP)) curHTTP.httpStatus = HTTP_SSL_REQUIRED; #endif break; } #endif // Parse the args string *curHTTP.ptrData = '\0'; curHTTP.ptrData = HTTPURLDecode(curHTTP.data); // If this is an MPFS upload form request, bypass to headers #if defined(HTTP_MPFS_UPLOAD) if(curHTTP.httpStatus == HTTP_MPFS_FORM) { smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } #endif // Move on to GET args, unless there are none smHTTP = SM_HTTP_PROCESS_GET; if(!curHTTP.hasArgs) smHTTP = SM_HTTP_PROCESS_POST; isDone = FALSE; curHTTP.hasArgs = FALSE; break; case SM_HTTP_PROCESS_GET: // Run the application callback HTTPExecuteGet() if(HTTPExecuteGet() == HTTP_IO_WAITING) {// If waiting for asynchronous process, return to main app break; } // Move on to POST data smHTTP = SM_HTTP_PROCESS_POST; case SM_HTTP_PROCESS_POST: #if defined(HTTP_USE_POST) // See if we have any new data if(TCPIsGetReady(sktHTTP) == curHTTP.callbackPos) { if(TickGet() > curHTTP.callbackID) {// If a timeout has occured, disconnect TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_DISCONNECT; isDone = FALSE; break; } } if(curHTTP.httpStatus == HTTP_POST #if defined(HTTP_MPFS_UPLOAD) || (curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR) #endif ) { // Run the application callback HTTPExecutePost() #if defined(HTTP_MPFS_UPLOAD) if(curHTTP.httpStatus >= HTTP_MPFS_UP && curHTTP.httpStatus <= HTTP_MPFS_ERROR) { c = HTTPMPFSUpload(); if(c == HTTP_IO_DONE) { smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } } else #endif c = HTTPExecutePost(); // If waiting for asynchronous process, return to main app if(c == HTTP_IO_WAITING) {// return to main app and make sure we don't get stuck by the watchdog curHTTP.callbackPos = TCPIsGetReady(sktHTTP) - 1; break; } else if(c == HTTP_IO_NEED_DATA) {// If waiting for more data curHTTP.callbackPos = TCPIsGetReady(sktHTTP); curHTTP.callbackID = TickGet() + HTTP_TIMEOUT*TICK_SECOND; // If more is expected and space is available, return to main app if(curHTTP.byteCount > 0 && TCPGetRxFIFOFree(sktHTTP) != 0) break; else {// Handle cases where application ran out of data or buffer space curHTTP.httpStatus = HTTP_INTERNAL_SERVER_ERROR; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } } } #endif // We're done with POST smHTTP = SM_HTTP_PROCESS_REQUEST; // No break, continue to sending request case SM_HTTP_PROCESS_REQUEST: // Check for 404 if(curHTTP.file == MPFS_INVALID_HANDLE) { curHTTP.httpStatus = HTTP_NOT_FOUND; smHTTP = SM_HTTP_SERVE_HEADERS; isDone = FALSE; break; } // Set up the dynamic substitutions curHTTP.byteCount = 0; if(curHTTP.offsets == MPFS_INVALID_HANDLE) {// If no index file, then set next offset to huge curHTTP.nextCallback = 0xffffffff; } else {// Read in the next callback index MPFSGetLong(curHTTP.offsets, &(curHTTP.nextCallback)); } // Move to next state smHTTP = SM_HTTP_SERVE_HEADERS; case SM_HTTP_SERVE_HEADERS: // We're in write mode now: // Adjust the TCP FIFOs for optimal transmission of // the HTTP response to the browser TCPAdjustFIFOSize(sktHTTP, 1, 0, TCP_ADJUST_GIVE_REST_TO_TX); // Send headers TCPPutROMString(sktHTTP, (ROM BYTE*)HTTPResponseHeaders[curHTTP.httpStatus]); // If this is a redirect, print the rest of the Location: header if(curHTTP.httpStatus == HTTP_REDIRECT) { TCPPutString(sktHTTP, curHTTP.data); TCPPutROMString(sktHTTP, (ROM BYTE*)"\r\n\r\n304 Redirect: "); TCPPutString(sktHTTP, curHTTP.data); TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CRLF); } // If not GET or POST, we're done if(curHTTP.httpStatus != HTTP_GET && curHTTP.httpStatus != HTTP_POST) {// Disconnect smHTTP = SM_HTTP_DISCONNECT; break; } // Output the content type, if known if(curHTTP.fileType != HTTP_UNKNOWN) { TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Type: "); TCPPutROMString(sktHTTP, (ROM BYTE*)httpContentTypes[curHTTP.fileType]); TCPPutROMString(sktHTTP, HTTP_CRLF); } // Output the gzip encoding header if needed if(MPFSGetFlags(curHTTP.file) & MPFS2_FLAG_ISZIPPED) { TCPPutROMString(sktHTTP, (ROM BYTE*)"Content-Encoding: gzip\r\n"); } // Output the cache-control TCPPutROMString(sktHTTP, (ROM BYTE*)"Cache-Control: "); if(curHTTP.httpStatus == HTTP_POST || curHTTP.nextCallback != 0xffffffff) {// This is a dynamic page or a POST request, so no cache TCPPutROMString(sktHTTP, (ROM BYTE*)"no-cache"); } else {// This is a static page, so save it for the specified amount of time TCPPutROMString(sktHTTP, (ROM BYTE*)"max-age="); TCPPutROMString(sktHTTP, (ROM BYTE*)HTTP_CACHE_LEN); } TCPPutROMString(sktHTTP, HTTP_CRLF); // Check if we should output cookies if(curHTTP.hasArgs) smHTTP = SM_HTTP_SERVE_COOKIES; else {// Terminate the headers TCPPutROMString(sktHTTP, HTTP_CRLF); smHTTP = SM_HTTP_SERVE_BODY; } // Move to next stage isDone = FALSE; break; case SM_HTTP_SERVE_COOKIES: #if defined(HTTP_USE_COOKIES) // If the TX FIFO runs out of space, the client will never get CRLFCRLF // Avoid writing huge cookies - keep it under a hundred bytes max // Write cookies one at a time as space permits for(curHTTP.ptrRead = curHTTP.data; curHTTP.hasArgs != 0; curHTTP.hasArgs--) { // Write the header TCPPutROMString(sktHTTP, (ROM BYTE*)"Set-Cookie: "); // Write the name, URL encoded, one character at a time while((c = *(curHTTP.ptrRead++))) { if(c == ' ') TCPPut(sktHTTP, '+'); else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') { TCPPut(sktHTTP, '%'); TCPPut(sktHTTP, btohexa_high(c)); TCPPut(sktHTTP, btohexa_low(c)); } else TCPPut(sktHTTP, c); } TCPPut(sktHTTP, '='); // Write the value, URL encoded, one character at a time while((c = *(curHTTP.ptrRead++))) { if(c == ' ') TCPPut(sktHTTP, '+'); else if(c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') { TCPPut(sktHTTP, '%'); TCPPut(sktHTTP, btohexa_high(c)); TCPPut(sktHTTP, btohexa_low(c)); } else TCPPut(sktHTTP, c); } // Finish the line TCPPutROMString(sktHTTP, HTTP_CRLF); } #endif // We're done, move to next state TCPPutROMString(sktHTTP, HTTP_CRLF); smHTTP = SM_HTTP_SERVE_BODY; case SM_HTTP_SERVE_BODY: isDone = FALSE; // Try to send next packet if(HTTPSendFile()) {// If EOF, then we're done so close and disconnect MPFSClose(curHTTP.file); curHTTP.file = MPFS_INVALID_HANDLE; smHTTP = SM_HTTP_DISCONNECT; isDone = TRUE; } // If the TX FIFO is full, then return to main app loop if(TCPIsPutReady(sktHTTP) == 0) isDone = TRUE; break; case SM_HTTP_SEND_FROM_CALLBACK: isDone = TRUE; // Check that at least the minimum bytes are free if(TCPIsPutReady(sktHTTP) < HTTP_MIN_CALLBACK_FREE) break; // Fill TX FIFO from callback HTTPPrint(curHTTP.callbackID); if(curHTTP.callbackPos == 0) {// Callback finished its output, so move on isDone = FALSE; smHTTP = SM_HTTP_SERVE_BODY; }// Otherwise, callback needs more buffer space, so return and wait break; case SM_HTTP_DISCONNECT: // Loopbacks have no wait state, so all data must be retrieved first if(TCPIsLoopback(sktHTTP) && TCPGetTxFIFOFull(sktHTTP) != 0) break; // Make sure any opened files are closed if(curHTTP.file != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.file); curHTTP.file = MPFS_INVALID_HANDLE; } if(curHTTP.offsets != MPFS_INVALID_HANDLE) { MPFSClose(curHTTP.offsets); curHTTP.offsets = MPFS_INVALID_HANDLE; } TCPDisconnect(sktHTTP); smHTTP = SM_HTTP_IDLE; break; } } while(!isDone); }