Nicolas Nackel
/
PPP-Blinky
Small Internet Protocol Stack using a standard serial port.
Embed:
(wiki syntax)
Show/hide line numbers
ppp-blinky.cpp
Go to the documentation of this file.
00001 // PPP-Blinky - "The Most Basic Internet Thing" 00002 // A Tiny IPv4 Stack and HTTP Webserver Using Windows XP/7/8/10/Linux Dial-Up Networking Over A Serial Port. 00003 // Aka PPP over serial (PPPOS) 00004 // Receives UDP packets and responds to ping (ICMP Echo requests) 00005 // WebSocket Service - see https://en.wikipedia.org/wiki/WebSocket 00006 00007 // Copyright 2016/2017/2018/2019/2020 Nicolas Nackel aka Nixnax. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00008 00009 // Notes and Instructions 00010 // http://bit.ly/PPP-Blinky-Instructions 00011 // http://bit.ly/win-rasdial-config 00012 00013 // Important - ensure that you are using the latest mbed firmware for your specific mbed target board 00014 00015 // Handy reading material 00016 // https://technet.microsoft.com/en-us/library/cc957992.aspx 00017 // https://en.wikibooks.org/wiki/Serial_Programming/IP_Over_Serial_Connections 00018 // http://atari.kensclassics.org/wcomlog.htm 00019 // https://en.wikipedia.org/wiki/Favicon (see also html5 <link> tag) 00020 00021 // Handy tools 00022 // https://ttssh2.osdn.jp/index.html.en - Tera Term, a good terminal program to monitor the debug output from the second serial port with! 00023 // https://www.microsoft.com/en-us/download/details.aspx?id=4865 - Microsoft network monitor - real-time monitoring of PPP packets 00024 // http://pingtester.net/ - nice tool for high rate ping testing 00025 // http://www.sunshine2k.de/coding/javascript/crc/crc_js.html - Correctly calculates the 16-bit FCS (crc) on our frames (Choose CRC16_CCITT_FALSE), then custom relected-in=1, reflected-out=1 00026 // https://technet.microsoft.com/en-us/sysinternals/pstools.aspx - psping for fast testing of ICMP ping function 00027 // https://eternallybored.org/misc/netcat/ - use netcat -u 172.10.10.1 80 to send/receive UDP packets from PPP-Blinky 00028 // Windows Powershell invoke-webrequest command - use it to stress test the webserver like this: while (1){ invoke-webrequest -uri 172.10.10.1/x } 00029 00030 // Connecting PPP-Blinky to Linux 00031 // PPP-Blinky can be made to talk to Linux - tested on Fedora - the following command, which uses pppd, works: 00032 // pppd /dev/ttyACM0 115200 debug dump local passive noccp novj nodetach nocrtscts 172.10.10.1:172.10.10.2 00033 // in the above command 172.10.10.1 is the adapter IP, and 172.10.10.2 is the IP of PPP-Blinky. 00034 // See also https://en.wikipedia.org/wiki/Point-to-Point_Protocol_daemon 00035 00036 // Special pages when PPP-Blinky is running 00037 // 172.10.10.2 root page 00038 // 172.10.10.2/x returns the number of ppp frames sent - this is handy for testing 00039 // 172.10.10.2/xb also returns number of ppp frames sent, but issues a fast refresh meta command. This allows you to use your browser to benchmark page load speed 00040 // 172.10.10.2/ws a simple WebSocket demo 00041 // http://jsfiddle.net/d26cyuh2/ more complete WebSocket demo in JSFiddle, showing cross-domain access 00042 00043 // Ok, enough talking, time to check out some code!! 00044 00045 #include "ppp-blinky.h" 00046 00047 // The #define below enables/disables a second (OPTIONAL) serial port that prints out interesting diagnostic messages. 00048 // Change to SERIAL_PORT_MONITOR_YES to enable diagnostics messages. You need to wire a second serial port to your mbed hardware to monitor the debug output. 00049 // Using the second serial port will slow down packet response time 00050 // Note - the LPC11U24 does NOT have a second serial port 00051 00052 #define SERIAL_PORT_MONITOR_NO /* change to SERIAL_PORT_MONITOR_YES for debug messages */ 00053 00054 // here we define the OPTIONAL, second debug serial port for various mbed target boards 00055 #ifdef SERIAL_PORT_MONITOR_YES 00056 #if defined(TARGET_LPC1768) 00057 RawSerial xx(p9, p10); // Second serial port on LPC1768 - not required to run, if you get compile error here, change #define SERIAL_PORT_MONITOR_YES to #define SERIAL_PORT_MONITOR_NO 00058 #elif defined(TARGET_NUCLEO_F446RE) || defined(TARGET_NUCLEO_L152RE) || defined(TARGET_NUCLEO_L053R8) || defined(TARGET_NUCLEO_L476RG) || defined(TARGET_NUCLEO_F401RE) 00059 RawSerial xx(PC_10, PC_11); // Second serial port on NUCLEO boards - not required to run, if you get compile error here, change #define SERIAL_PORT_MONITOR_YES to #define SERIAL_PORT_MONITOR_NO 00060 #elif defined(TARGET_NUCLEO_L432KC) 00061 RawSerial xx(PA_9, PA_10); // Second serial port on NUCLEO-L432KC - not required to run, if you get compile error here, change #define SERIAL_PORT_MONITOR_YES to #define SERIAL_PORT_MONITOR_NO 00062 #elif defined(TARGET_LPC11U24) 00063 #error The LPC11U24 does not have a second serial port to use for debugging - change SERIAL_PORT_MONITOR_YES back to SERIAL_PORT_MONITOR_NO 00064 #elif defined (TARGET_KL46Z) || (TARGET_KL25Z) 00065 RawSerial xx(PTE0,PTE1); // Second serial port on FRDM-KL46Z board 00066 #elif defined(TARGET_KW41Z) 00067 RawSerial xx(PTC3, PTC2); // Second serial port on FRDM-KW41Z board 00068 #elif defined(YOUR_TARGET_BOARD_NAME_HERE) 00069 // change the next line to YOUR target board's second serial port pin definition if it's not present - and if it works, please send it to me - thanks!!! 00070 RawSerial xx(p9, p10); // change this to YOUR board's second serial port pin definition - and please send it to me if it works!!! 00071 #else 00072 #error Add your target board's second serial port here if you want to use debugging - or simply change SERIAL_PORT_MONITOR_YES to SERIAL_PORT_MONITOR_NO 00073 #endif 00074 #define debugPrintf(x...) xx.printf (x) /* if we have a serial port we can print debug messages */ 00075 #define debugPutc(x...) xx.putc(x) 00076 #define debugPuts(x...) xx.puts(x) 00077 #define debugBaudRate(x...) xx.baud(x) 00078 #else 00079 // if we don't have a debug port the debug print functions do nothing 00080 #define debugPrintf(x...) {} 00081 #define debugPutc(x...) {} 00082 #define debugPuts(x...) {} 00083 #define debugBaudRate(x...) {} 00084 #endif 00085 00086 // verbosity flags used in debug printouts - change to 1 to see increasingly more detailed debug info. 00087 #define v0 1 00088 #define v1 0 00089 #define v2 0 00090 #define IP_HEADER_DUMP_YES /* YES for ip header dump */ 00091 #define TCP_HEADER_DUMP_YES /* YES for tcp header dump */ 00092 #define DUMP_RECEIVED_PPP_FRAMES_NO /* YES to dump received PPP frames */ 00093 00094 // below is the webpage we serve when we get an HTTP request to root (/) 00095 // keep size under ~1400 bytes to fit into a single PPP packet 00096 // the <link rel="icon" ...> tag stops browsers from asking for file favicon.ico 00097 00098 const static char rootWebPage[] = "\ 00099 <!DOCTYPE html>\ 00100 <html>\ 00101 <!--Comment-->\ 00102 <head>\ 00103 <title>mbed PPP-Blinky</title>\r\n\ 00104 <link rel=\"icon\" href=\"data:image/gif;base64,R0lGODlhAQABAIABAP8AAAAAACwAAAAAAQABAAACAkQBADs=\">\ 00105 <style>\ 00106 body {font-family:sans-serif; font-size:140%; text-align:center; color:#807070;}\ 00107 </style>\r\n\ 00108 </head>\ 00109 <body>\ 00110 <h1>mbed PPP-Blinky Up and Running</h1>\ 00111 <h1><a href=\"http://bit.ly/pppBlink2\">Source on mbed</a></h1>\ 00112 <h1><a href=\"/w\">WebSocket Test</a></h1>\ 00113 <h1><a href=\"/x?Parameter1=Example\">Benchmark 1</a></h1>\ 00114 <h1><a href=\"/xb\">Benchmark 2</a></h1>\ 00115 <h1><a href=\"http://bit.ly/websocket-demo-jsfiddle\">JSFiddle Demo</a></h1>\ 00116 <h1><a href=\"/xt\">Toggle LED</a></h1>\ 00117 <h1>TCP, LCP & Ping Count: 0000000000</h1>\ 00118 <h1>System Core Clock: 0000000000</h1>\ 00119 </body>\ 00120 </html>\r\n"; // size = 718 bytes + 1 null byte = 719 bytes 00121 00122 // this is a websocket demo html page we serve when GET /ws is requested 00123 // it contains some javascript to handle the websocket activities 00124 // the <link rel="icon" ...> tag stops browsers from asking for file favicon.ico 00125 const static char webSocketPage[] = "\ 00126 <!DOCTYPE html>\ 00127 <html>\ 00128 <head>\ 00129 <title>mbed PPP-Blinky WebSocket</title>\ 00130 <link rel=\"icon\" href=\"data:image/gif;base64,R0lGODlhAQABAIABAP8AAAAAACwAAAAAAQABAAACAkQBADs=\">\ 00131 <script>\ 00132 window.onload=function(){\ 00133 var url=\"ws://172.10.10.2\";\ 00134 var sts=document.getElementById(\"sts\");\ 00135 var btn=document.getElementById(\"btn\");\ 00136 var ctr=0;\ 00137 function show(text){sts.textContent=text;}\ 00138 btn.onclick=function(){\ 00139 if(btn.textContent==\"Connect\"){\ 00140 x=new WebSocket(url);\ 00141 x.onopen=function(){\ 00142 show(\"Connected to PPP-Blinky WebSocket service at: \"+url);\ 00143 btn.textContent=\"Send \\\"\"+ctr+\"\\\"\";\ 00144 };\ 00145 x.onclose=function(){show(\"WebSocket closed\");};\ 00146 x.onmessage=function(msg){show(\"PPP-Blinky WebSocket service responded with: \\\"\"+msg.data+\"\\\"\");};\ 00147 } else {\ 00148 x.send(ctr);\ 00149 ctr=ctr+1;\ 00150 btn.textContent=\"Send \\\"\"+ctr+\"\\\"\";\ 00151 }\ 00152 };\ 00153 };\ 00154 </script>\ 00155 <style>\ 00156 body {font-family:sans-serif; font-size:140%; text-align:center; color:#807070;}\ 00157 button {font-size:140%; background-color:#7eeeee; border-radius:15px; border:none; margin-top:20px; }\ 00158 </style>\ 00159 <body>\ 00160 <h1>mbed PPP-Blinky WebSocket Test</h1>\ 00161 <h1><div id=\"sts\">Idle</div><h1>\ 00162 <button id=\"btn\">Connect</button>\ 00163 </body>\ 00164 </html>"; // size = 1037 bytes + 1 null byte = 1038 bytes 00165 00166 // The serial port on your mbed hardware. Your PC should be configured to view this port as a standard dial-up networking modem. 00167 // On Windows the model type of the modem should be selected as "Communications cable between two computers" 00168 // The modem baud rate should be set to 115200 baud 00169 // See instructions at the top. 00170 // On a typical mbed hardware platform this serial port is a USB virtual com port (VCP) and the USB serial driver is supplied by the board vendor. 00171 // Stream does not allow putc/getc in an interrupt routine, making serial interrupts useless. RawSerial allows use of putc/getc in interrupt. 00172 RawSerial pc (USBTX, USBRX); // usb virtual com port for mbed hardware 00173 00174 DigitalOut led1(LED1); // this led toggles when a packet is received 00175 00176 pppType ppp; // our global - definitely not thread safe 00177 00178 /// Initialize the ppp structure and clear the receive buffer 00179 void pppInitStruct() 00180 { 00181 memset( (void *)ppp.rx.buf, 0, RXBUFLEN+1); // one extra byte at the end which always remains zero so strstr() cannot exceed the buffer 00182 ppp.online=0; 00183 ppp.rx.tail=0; 00184 ppp.rx.rtail=0; 00185 ppp.rx.head=0; 00186 ppp.rx.buflevel=0; 00187 ppp.rx.maxbuflevel=0; 00188 ppp.rx.bufferfull=0; 00189 ppp.pkt.len=0; 00190 ppp.ipData.ident=11111; // easy to recognize in ip packet dumps 00191 ppp.ledState=0; 00192 ppp.hdlc.frameStartIndex=0; 00193 ppp.responseCounter=0; 00194 ppp.pppCount=0; // number of ICMP pings we have received 00195 ppp.firstFrame=1; 00196 ppp.ppp = (pppHeaderType *)ppp.pkt.buf; // pointer to ppp header 00197 ppp.ip = (ipHeaderType *)(ppp.pkt.buf+4); // pointer to IP header 00198 } 00199 00200 /// Toggle the LED 00201 void led1Toggle() 00202 { 00203 led1 = (ppp.ledState >> 0) & 1; // use first bit, in other words toggle LED only every packet 00204 ppp.ledState++; 00205 } 00206 00207 /// Returns 1 after a connect message, 0 at startup or after a disconnect message 00208 int connectedPpp() 00209 { 00210 return ppp.online; 00211 } 00212 00213 /// PPP serial port receive interrupt handler. 00214 /// Check for available characters from the PC and read them into our own circular serial receive buffer at ppp.rx.buf. 00215 /// Also, if we are offline and a 0x7e frame start character is seen, we go online immediately 00216 void pppReceiveHandler() 00217 { 00218 char ch; 00219 while ( pc.readable() ) { 00220 int localHead = (ppp.rx.head+1); // local head index 00221 if (localHead > RXBUFLEN) localHead=0; // increment and wrap head index 00222 if ( localHead == ppp.rx.rtail ) { 00223 ppp.rx.bufferfull = 1; // set flag if rx buffer is full 00224 #define BUFFER_FULL_TRAP_YES 00225 #ifdef BUFFER_FULL_TRAP_YES 00226 while(1) { 00227 //led1Toggle(); // flash LED at a slow rate to indicate buffer overfloe 00228 wait_ms(150); 00229 } 00230 #else 00231 return; // don't read if rx buffer is full 00232 #endif 00233 } 00234 ch = pc.getc(); // read new character 00235 ppp.rx.buf[ppp.rx.head] = ch; // insert in our receive buffer 00236 if (ch == 0x7E) { // check for ppp frame start character 00237 ppp.online = 1; // if we see a frame sync character we immediately change status to online 00238 } 00239 ppp.rx.head = localHead; // update real head pointer 00240 ppp.rx.buflevel++; // update buffer level counter 00241 if ( ppp.rx.buflevel > ppp.rx.maxbuflevel ) { 00242 ppp.rx.maxbuflevel = ppp.rx.buflevel; // remember the maximum usage of rx buffer 00243 } 00244 } 00245 } 00246 00247 /// update the cumulative PPP FCS (frame check sequence) 00248 void fcsDo(register int x) 00249 { 00250 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00251 x>>=1; 00252 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00253 x>>=1; 00254 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00255 x>>=1; 00256 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00257 x>>=1; 00258 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00259 x>>=1; 00260 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00261 x>>=1; 00262 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00263 x>>=1; 00264 ppp.fcs=((ppp.fcs^x)&1) ? (ppp.fcs>>1)^0x8408 : ppp.fcs>>1; 00265 } 00266 00267 /// calculate the PPP FCS (frame check sequence) on an entire block of memory 00268 int fcsBuf(char * buf, int size) // crc on an entire block of memory 00269 { 00270 ppp.fcs=0xffff; // fcs initial value 00271 for(int i=0; i<size; i++)fcsDo(*buf++); 00272 return ppp.fcs; 00273 } 00274 00275 /// Get one character from our received PPP buffer 00276 int pc_getBuf() 00277 { 00278 int x = ppp.rx.buf[ ppp.rx.tail ]; 00279 int temptail = ppp.rx.tail+1; 00280 if (temptail > RXBUFLEN) temptail=0; 00281 ppp.rx.buflevel--; 00282 ppp.rx.tail = temptail; 00283 return x; 00284 } 00285 00286 /// Dump a PPP frame to the debug serial port 00287 /// Note - the hex output of dumpPPPFrame() can be imported into WireShark 00288 /// Capture the frame's hex output in your terminal program and save as a text file 00289 /// In WireShark, use "Import Hex File". Import options are: Offset=None, Protocol=PPP. 00290 void dumpPPPFrame() 00291 { 00292 char pbuf[100]; 00293 for(int i=0; i<ppp.pkt.len; i++) { 00294 00295 sprintf(pbuf, "%02x ", ppp.pkt.buf[i]); 00296 00297 debugPuts(pbuf); 00298 } 00299 00300 sprintf(pbuf, " CRC=%04x Len=%d\n", ppp.pkt.crc, ppp.pkt.len); 00301 00302 debugPuts(pbuf); 00303 } 00304 00305 /// Process a received PPP frame 00306 void processPPPFrame(int start, int end) 00307 { 00308 if(start==end) return; // ignore empty frames 00309 // led1Toggle(); // change led1 state on received frames 00310 ppp.pppCount++; // count any non-empty frame 00311 ppp.fcs=0xffff; // fcs initial value 00312 char * dest = ppp.pkt.buf; 00313 ppp.pkt.len=0; 00314 int unstuff=0; 00315 int idx = start; 00316 char bufchar; 00317 while(1) { 00318 bufchar = ppp.rx.buf[idx]; 00319 if (unstuff==0) { 00320 if (bufchar==0x7d) unstuff=1; 00321 else { 00322 *dest = bufchar; 00323 fcsDo(*dest); 00324 dest++; 00325 ppp.pkt.len++; 00326 } 00327 } else { // unstuff characters prefixed with 0x7d 00328 *dest = bufchar^0x20; 00329 fcsDo(*dest); 00330 dest++; 00331 ppp.pkt.len++; 00332 unstuff=0; 00333 } 00334 idx = idx+1; 00335 if (idx > RXBUFLEN) idx=0; 00336 if (idx == end) break; 00337 } 00338 ppp.pkt.crc = ppp.fcs & 0xffff; 00339 #ifdef DUMP_RECEIVED_PPP_FRAMES_YES 00340 dumpPPPFrame(); // hex dump ALL ppp frames to the debug port 00341 #endif 00342 if (ppp.pkt.crc == 0xf0b8) { // check for good CRC 00343 determinePacketType(); 00344 } else { 00345 #define REPORT_FCS_ERROR_YES 00346 #ifdef REPORT_FCS_ERROR_YES 00347 char pbuf[100]; // local print buffer 00348 sprintf(pbuf, "\nPPP FCS(crc) Error CRC=%x Length = %d\n",ppp.pkt.crc,ppp.pkt.len); // print a debug line 00349 debugPuts( pbuf ); 00350 #define DUMP_PPP_FRAME_ON_ERROR_NO 00351 #ifdef DUMP_PPP_FRAME_ON_ERROR_YES 00352 dumpPPPFrame(); // dump frames with errors in them 00353 #endif 00354 #endif 00355 } 00356 } 00357 00358 /// do PPP HDLC-like handling of special (flag) characters 00359 void hdlcPut(int ch) 00360 { 00361 if ( (ch<0x20) || (ch==0x7d) || (ch==0x7e) /* || (ch>0x7f) */ ) { 00362 pc.putc(0x7d); 00363 pc.putc(ch^0x20); // these characters need special handling 00364 } else { 00365 pc.putc(ch); 00366 } 00367 } 00368 00369 /// send a PPP frame in HDLC format 00370 void sendPppFrame() 00371 { 00372 ppp.responseCounter++; // count the number of ppp frames we send 00373 int crc = fcsBuf(ppp.pkt.buf, ppp.pkt.len-2); // update crc 00374 ppp.pkt.buf[ ppp.pkt.len-2 ] = (~crc>>0); // fcs lo (crc) 00375 ppp.pkt.buf[ ppp.pkt.len-1 ] = (~crc>>8); // fcs hi (crc) 00376 pc.putc(0x7e); // hdlc start-of-frame "flag" 00377 for(int i=0; i<ppp.pkt.len; i++) { 00378 // wait_us(86); // wait one character time 00379 00380 hdlcPut( ppp.pkt.buf[i] ); // send a character 00381 } 00382 pc.putc(0x7e); // hdlc end-of-frame "flag" 00383 } 00384 00385 /// convert a network ip address in the buffer to an integer (IP adresses are big-endian, i.e most significant byte first) 00386 int bufferToIP(char * buffer) 00387 { 00388 int result=0; 00389 for(int i=0; i<4; i++) result = (result<<8)|(*buffer++ & 0xff); 00390 return result; 00391 } 00392 00393 /// convert 4-byte ip address to 32-bit 00394 unsigned int integersToIp( int a, int b, int c, int d) 00395 { 00396 return a<<24 | b<<16 | c<<8 | d; 00397 } 00398 00399 /// handle IPCP configuration requests 00400 void ipcpConfigRequestHandler() 00401 { 00402 debugPuts("Their IPCP Config Req, Our Ack\n"); 00403 if(ppp.ipcp->request[0]==3) { 00404 ppp.hostIP = bufferToIP(ppp.pkt.buf+10); 00405 debugPrintf("Host IP = %d.%d.%d.%d (%08x)\n", ppp.ipcp->request[2],ppp.ipcp->request[3],ppp.ipcp->request[4],ppp.ipcp->request[5],ppp.hostIP); 00406 } 00407 00408 ppp.ipcp->code=2; // change code to ack 00409 sendPppFrame(); // acknowledge everything they ask for - assume it's IP addresses 00410 00411 debugPuts("Our IPCP Ask (no options)\n"); 00412 ppp.ipcp->code=1; // change code to request 00413 ppp.ipcp->lengthR = __REV16( 4 ); // 4 is minimum length - no options in this request 00414 ppp.pkt.len=4+4+2; // no options in this request shortest ipcp packet possible (4 ppp + 4 ipcp + 2 crc) 00415 sendPppFrame(); // send our request 00416 } 00417 00418 /// handle IPCP acknowledge (do nothing) 00419 void ipcpAckHandler() 00420 { 00421 debugPuts("Their IPCP Grant\n"); 00422 } 00423 00424 /// Handle IPCP NACK by sending our suggested IP address if there is an IP involved. 00425 /// This is how Linux responds to an IPCP request with no options - Windows assumes any IP address on the submnet is OK. 00426 void ipcpNackHandler() 00427 { 00428 debugPuts("Their IPCP Nack\n"); 00429 if (ppp.ipcp->request[0]==3) { // check if the NACK contains an IP address parameter 00430 ppp.ipcp->code=1; // assume the NACK contains our "suggested" IP address 00431 sendPppFrame(); // let's request this IP address as ours 00432 debugPuts("Our IPCP ACK (received an IP)\n"); 00433 } else { // if it's not an IP nack we ignore it 00434 debugPuts("IPCP Nack Ignored\n"); 00435 } 00436 } 00437 00438 /// handle all other IPCP requests (by ignoring them) 00439 void ipcpDefaultHandler() 00440 { 00441 debugPuts("Their IPCP Other\n"); 00442 } 00443 00444 /// process an incoming IPCP packet 00445 void IPCPframe() 00446 { 00447 int action = ppp.ipcp->code; // packet type is here 00448 switch (action) { 00449 case 1: 00450 ipcpConfigRequestHandler(); 00451 break; 00452 case 2: 00453 ipcpAckHandler(); 00454 break; 00455 case 3: 00456 ipcpNackHandler(); 00457 break; 00458 default: 00459 ipcpDefaultHandler(); 00460 } 00461 } 00462 00463 /// perform a 16-bit checksum. if the byte count is odd, stuff in an extra zero byte. 00464 unsigned int dataCheckSum(char * ptr, int len, int restart) 00465 { 00466 unsigned int i,hi,lo; 00467 unsigned char placeHolder; 00468 if (restart) ppp.sum=0; 00469 if (len&1) { 00470 placeHolder = ptr[len]; 00471 ptr[len]=0; // if the byte count is odd, insert one extra zero byte is after the last real byte because we sum byte PAIRS 00472 } 00473 i=0; 00474 while ( i<len ) { 00475 00476 hi = ptr[i++]; 00477 lo = ptr[i++]; 00478 ppp.sum = ppp.sum + ((hi<<8)|lo); 00479 } 00480 if (len&1) { 00481 ptr[len] = placeHolder; // restore the extra byte we made zero 00482 } 00483 ppp.sum = (ppp.sum & 0xffff) + (ppp.sum>>16); 00484 ppp.sum = (ppp.sum & 0xffff) + (ppp.sum>>16); // sum one more time to catch any carry from the carry 00485 return ~ppp.sum; 00486 } 00487 00488 /// perform the checksum on an IP header 00489 void IpHeaderCheckSum() 00490 { 00491 ppp.ip->checksumR=0; // zero the checsum in the IP header 00492 int len = 4 * ppp.ip->headerLength; // length of IP header in bytes 00493 unsigned int sum = dataCheckSum(ppp.ipStart,len,1); 00494 ppp.ip->checksumR = __REV16( sum ); // insert fresh checksum 00495 } 00496 00497 /// swap the IP source and destination addresses 00498 void swapIpAddresses() 00499 { 00500 unsigned int tempHold; 00501 tempHold = ppp.ip->srcAdrR; // tempHold <- source IP 00502 ppp.ip->srcAdrR = ppp.ip->dstAdrR; // source <- dest 00503 ppp.ip->dstAdrR = tempHold; // dest <- tempHold*/ 00504 } 00505 00506 /// swap the IP source and destination ports 00507 void swapIpPorts() 00508 { 00509 int headerSizeIP = 4 * (ppp.ip->headerLength); // calculate size of IP header 00510 char * ipSrcPort = ppp.ipStart + headerSizeIP + 0; // ip source port location 00511 char * ipDstPort = ppp.ipStart + headerSizeIP + 2; // ip destin port location 00512 char tempHold[2]; 00513 memcpy(tempHold, ipSrcPort,2); // tempHold <- source 00514 memcpy(ipSrcPort,ipDstPort,2); // source <- dest 00515 memcpy(ipDstPort,tempHold, 2); // dest <- tempHold 00516 } 00517 00518 /// Build the "pseudo header" required for UDP and TCP, then calculate its checksum 00519 void checkSumPseudoHeader( unsigned int packetLength ) 00520 { 00521 // this header contains the most important parts of the IP header, i.e. source and destination address, protocol number and data length. 00522 pseudoIpHeaderType pseudoHeader; // create pseudo header 00523 pseudoHeader.srcAdrR = ppp.ip->srcAdrR; // copy in ip source address 00524 pseudoHeader.dstAdrR = ppp.ip->dstAdrR; // copy in ip dest address 00525 pseudoHeader.zero = 0; // zero byte 00526 pseudoHeader.protocol = ppp.ip->protocol; // protocol number (udp or tcp) 00527 pseudoHeader.lengthR = __REV16( packetLength ); // size of tcp or udp packet 00528 dataCheckSum(pseudoHeader.start, 12, 1); // calculate this header's checksum 00529 } 00530 00531 /// initialize an IP packet to send 00532 void initIP (unsigned int srcIp, unsigned int dstIp, unsigned int srcPort, unsigned int dstPort, unsigned int protocol) 00533 { 00534 ppp.ppp->address = 0xff; 00535 ppp.ppp->control = 3; 00536 ppp.ppp->protocolR = __REV16( 0x0021 ); 00537 ppp.ip->version = 4; 00538 ppp.ip->headerLength = 5; // 5 words = 20 bytes 00539 ppp.ip->identR = __REV16(ppp.ipData.ident++); // insert our ident 00540 ppp.ip->dontFragment=1; 00541 ppp.ip->ttl=128; 00542 ppp.ip->protocol = protocol; // udp 00543 ppp.ip->srcAdrR = __REV(srcIp); 00544 ppp.ip->dstAdrR = __REV(dstIp); 00545 ppp.udpStart = ppp.ipStart + 20; // calculate start of udp header 00546 ppp.udp->srcPortR = __REV16(srcPort); // source port 00547 ppp.udp->dstPortR = __REV16(dstPort); // dest port 00548 } 00549 00550 00551 /// Build a UDP packet from scratch 00552 void sendUdp(unsigned int srcIp, unsigned int dstIp, unsigned int srcPort, unsigned int dstPort, char * message,int msgLen) 00553 { 00554 struct { 00555 unsigned int ipAll; // length of entire ip packet 00556 unsigned int ipHeader; // length of ip header 00557 unsigned int udpAll; // length of entire udp packet 00558 unsigned int udpData; // length of udp data segment 00559 } len; 00560 len.ipHeader = 20; // ip header length 00561 len.udpData = msgLen; // udp data size 00562 len.udpAll = len.udpData+8; // update local udp packet length 00563 len.ipAll = len.ipHeader + len.udpAll; // update IP Length 00564 initIP(srcIp, dstIp, srcPort, dstPort, 17); // init a UDP packet 00565 ppp.ip->lengthR = __REV16(len.ipAll); // update IP length in buffer 00566 ppp.udpStart = ppp.ipStart + len.ipHeader; // calculate start of udp header 00567 memcpy( ppp.udp->data, message, len.udpData ); // copy the message to the buffer 00568 ppp.udp->lengthR = __REV16(len.udpAll); // update UDP length in buffer 00569 ppp.pkt.len = len.ipAll+2+4; // update ppp packet length 00570 IpHeaderCheckSum(); // refresh IP header checksum 00571 checkSumPseudoHeader( len.udpAll ); // get the UDP pseudo-header checksum 00572 ppp.udp->checksumR = 0; // before TCP checksum calculations the checksum bytes must be set cleared 00573 unsigned int pseudoHeaderSum=dataCheckSum(ppp.udpStart,len.udpAll, 0); // continue the TCP checksum on the whole TCP packet 00574 ppp.udp->checksumR = __REV16( pseudoHeaderSum); // tcp checksum done, store it in the TCP header 00575 sendPppFrame(); // send the UDP message back 00576 } 00577 00578 /// Process an incoming UDP packet. 00579 /// If the packet starts with the string "echo " or "test" we echo back a special packet 00580 void UDPpacket() 00581 { 00582 struct { 00583 unsigned int all; // length of entire ip packet 00584 unsigned int header; // length of ip header 00585 } ipLength; 00586 00587 struct { 00588 unsigned int all; // length of entire udp packet 00589 unsigned int data; // length of udp data segment 00590 } udpLength; 00591 00592 ipLength.header = 4 * ppp.ip->headerLength; // length of ip header 00593 ppp.udpStart = ppp.ipStart + ipLength.header; // calculate start of udp header 00594 udpLength.all = __REV16( ppp.udp->lengthR ); // size of udp packet 00595 udpLength.data = udpLength.all - 8; // size of udp data 00596 00597 #ifdef SERIAL_PORT_MONITOR_YES 00598 char * srcIP = ppp.ip->srcAdrPtr; // IP source 00599 char * dstIP = ppp.ip->dstAdrPtr; //IP destination 00600 00601 unsigned int udpSrcPort = __REV16( ppp.udp->srcPortR ); // integer of UDP source port 00602 unsigned int udpDstPort = __REV16( ppp.udp->dstPortR ); // integer of UDP dest port 00603 00604 if(v0) { 00605 debugPrintf("UDP %d.%d.%d.%d:%d ", srcIP[0],srcIP[1],srcIP[2],srcIP[3],udpSrcPort); 00606 debugPrintf("%d.%d.%d.%d:%d ", dstIP[0],dstIP[1],dstIP[2],dstIP[3],udpDstPort); 00607 debugPrintf("Len %03d", udpLength); 00608 } 00609 if (v1) { 00610 int printSize = udpLength.data; 00611 if (printSize > 20) printSize = 20; // print only first 20 characters 00612 for (int i=0; i<printSize; i++) { 00613 char ch = ppp.udp->data[i]; 00614 if (ch>31 && ch<127) { 00615 debugPrintf("%c", ch); 00616 } else { 00617 debugPuts("_"); 00618 } 00619 } 00620 } 00621 if (v0) debugPuts("\n"); 00622 #endif 00623 int echoFound = !strncmp(ppp.udp->data,"echo ",5); // true if UDP message starts with "echo " 00624 int testFound = !strncmp(ppp.udp->data,"test",4); // true if UDP message starts with "test" 00625 if ( (echoFound) || (testFound)) { // if the UDP message starts with "echo " or "test" we answer back 00626 if (echoFound) { 00627 swapIpAddresses(); // swap IP source and destination 00628 swapIpPorts(); // swap IP source and destination ports 00629 memcpy(ppp.udp->data,"Got{",4); // in the UDP data modify "echo" to "Got:" 00630 int n=0; 00631 #define IDENTIFY_UDP_SERVER_NO 00632 #ifdef IDENTIFY_UDP_SERVER_YES 00633 n=n+sprintf(n+ppp.udp->data+udpLength.data, "} UDP Server: PPP-Blinky\n"); // an appendix 00634 #endif 00635 udpLength.data = udpLength.data + n; // update udp data size with the size of the appendix 00636 // we may have changed data length, update all the lengths 00637 udpLength.all = udpLength.data+8; // update local udp packet length 00638 ipLength.all = ipLength.header + udpLength.all; // update IP Length 00639 ppp.ip->lengthR = __REV16(ipLength.all); // update IP length in buffer 00640 ppp.udp->lengthR = __REV16(udpLength.all); // update UDP length in buffer 00641 ppp.pkt.len = ipLength.all+2+4; // update ppp packet length 00642 IpHeaderCheckSum(); // refresh IP header checksum 00643 checkSumPseudoHeader( udpLength.all ); // get the UDP pseudo-header checksum 00644 ppp.udp->checksumR = 0; // before TCP checksum calculations the checksum bytes must be set cleared 00645 unsigned int pseudoHeaderSum=dataCheckSum(ppp.udpStart,udpLength.all, 0); // continue the TCP checksum on the whole TCP packet 00646 ppp.udp->checksumR = __REV16( pseudoHeaderSum); // tcp checksum done, store it in the TCP header 00647 sendPppFrame(); // send the UDP message back 00648 } else if ( testFound ) { 00649 unsigned int sI = __REV( ppp.ip->srcAdrR ); 00650 unsigned int dI = __REV( ppp.ip->dstAdrR ); 00651 unsigned int sp = __REV16( ppp.udp->srcPortR ); 00652 unsigned int dp = __REV16( ppp.udp->dstPortR ); 00653 int n=sprintf(ppp.pkt.buf+200,"Response Count %d\n", ppp.responseCounter); 00654 sendUdp(dI,sI,dp,sp,ppp.pkt.buf+200,n); // build a udp packet from the ground up 00655 } 00656 } 00657 } 00658 00659 /// UDP demo that sends a udp packet containing a character received from the second debug serial port. 00660 /// Sends a 48 byte IP/UDP header for every 1 byte of data so line-mode would probably be better. 00661 /// If you want ip packets from ppp blinky to be routed to other networks, ensure you have ip routing enabled. 00662 /// See http://www.wikihow.com/Enable-IP-Routing. 00663 /// Also ensure that the firewall on the receiving machine has the receiving UDP port (12345 in this example) enabled. 00664 /// The netcat UDP receive command on the remote host would be: nc -ul 12345 00665 void sendUdpData() 00666 { 00667 #ifdef SERIAL_PORT_MONITOR_YES 00668 if (ppp.online) { 00669 if (xx.readable()) { 00670 char inchar = xx.getc(); 00671 xx.putc( inchar ); // echo the received character on the debug serial port 00672 sendUdp(integersToIp(172,10,10,2), integersToIp(192,168,0,109), 1, 12345, &inchar, 1); // send a 1 byte UDP message to a remote machine at IP 192.168.0.109:12345 00673 } 00674 } 00675 #endif 00676 } 00677 00678 /// handle a PING ICMP (internet control message protocol) packet 00679 void ICMPpacket() // internet control message protocol 00680 { 00681 struct { 00682 unsigned int all; // length of entire ip packet 00683 unsigned int header; // length of ip header 00684 } ipLength; 00685 struct { 00686 unsigned int all; // length of entire udp packet 00687 unsigned int data; // length of udp data segment 00688 } icmpLength; 00689 ipLength.all = __REV16( ppp.ip->lengthR ); // length of ip packet 00690 ipLength.header = 4 * ppp.ip->headerLength; // length of ip header 00691 ppp.icmpStart = ppp.ipStart + ipLength.header; // calculate start of udp header 00692 icmpLength.all = ipLength.all - ipLength.header; // length of icmp packet 00693 icmpLength.data = icmpLength.all - 8; // length of icmp data 00694 #define ICMP_TYPE_PING_REQUEST 8 00695 if ( ppp.icmp->type == ICMP_TYPE_PING_REQUEST ) { 00696 ppp.ip->ttl--; // decrement time to live (so we have to update header checksum) 00697 #ifdef SERIAL_PORT_MONITOR_YES 00698 char * srcAdr = ppp.ip->srcAdrPtr; 00699 char * dstAdr = ppp.ip->dstAdrPtr; 00700 int icmpIdent = __REV16( ppp.ip->identR ); // byte reversed - big endian 00701 int icmpSequence = __REV16( ppp.icmp->sequenceR ); // byte reversed - big endian 00702 if(1) { 00703 char pbuf[100]; 00704 00705 sprintf(pbuf, "ICMP PING %d.%d.%d.%d %d.%d.%d.%d ", srcAdr[0],srcAdr[1],srcAdr[2],srcAdr[3],dstAdr[0],dstAdr[1],dstAdr[2],dstAdr[3]); 00706 debugPuts( pbuf ); 00707 00708 sprintf(pbuf, "Ident %04x Sequence %04d \n",icmpIdent,icmpSequence); 00709 00710 debugPuts( pbuf ); 00711 } 00712 #endif 00713 swapIpAddresses(); // swap the IP source and destination addresses 00714 IpHeaderCheckSum(); // new ip header checksum (required because we changed TTL) 00715 #define ICMP_TYPE_ECHO_REPLY 0 00716 ppp.icmp->type = ICMP_TYPE_ECHO_REPLY; // icmp echo reply 00717 ppp.icmp->checkSumR = 0; // zero the checksum for recalculation 00718 unsigned int sum = dataCheckSum(ppp.icmpStart, icmpLength.all, 1); // icmp checksum 00719 ppp.icmp->checkSumR = __REV16( sum ); // save big-endian icmp checksum 00720 #define DUMP_ICMP_PACKETS_NO 00721 #ifdef DUMP_ICMP_PACKETS_YES 00722 int printSize = icmpLength.data; // exclude size of icmp header 00723 if (printSize > 10) printSize = 10; // print up to 20 characters 00724 for (int i=0; i<printSize; i++) { 00725 char ch = ppp.icmp->data[i]; 00726 if (ch>31 && ch<127) { 00727 debugPutc(ch); 00728 } else { 00729 debugPutc('_'); // for non-printable characters 00730 } 00731 } 00732 debugPutc('\n'); 00733 #endif 00734 sendPppFrame(); // reply to the ping 00735 } else { 00736 if (v0) { 00737 debugPrintf("ICMP type=%x \n", ppp.icmp->type); 00738 } 00739 } 00740 } 00741 00742 /// handle an IGMP (internet group managment protocol) packet (by ignoring it) 00743 void IGMPpacket() 00744 { 00745 if (v0) debugPrintf("IGMP type=%d \n", ppp.pkt.buf[28]); 00746 } 00747 00748 /// dump the header of an IP pakcet on the (optional) debug serial port 00749 void dumpHeaderIP (int outGoing) 00750 { 00751 #if defined(IP_HEADER_DUMP_YES) && defined(SERIAL_PORT_MONITOR_YES) 00752 00753 int IPv4Id = __REV16(ppp.ip->identR); 00754 char pbuf[100]; // local print buffer 00755 int n=0; 00756 n=n+sprintf(pbuf+n, outGoing ? "\x1b[34m" : "\x1b[30m" ); // VT100 color code, print black for incoming, blue for outgoing headers 00757 n=n+sprintf(pbuf+n, "%05d ",IPv4Id); // IPv4Id is a good way to correlate our dumps with net monitor or wireshark traces 00758 #define DUMP_FULL_IP_ADDRESS_YES 00759 #ifdef DUMP_FULL_IP_ADDRESS_YES 00760 char * srcAdr = ppp.ip->srcAdrPtr; 00761 char * dstAdr = ppp.ip->dstAdrPtr; 00762 n=n+sprintf(pbuf+n, " %d.%d.%d.%d %d.%d.%d.%d ",srcAdr[0],srcAdr[1],srcAdr[2],srcAdr[3], dstAdr[0],dstAdr[1],dstAdr[2],dstAdr[3]); // full ip addresses 00763 #endif 00764 debugPuts( pbuf ); 00765 #ifndef TCP_HEADER_DUMP_YES 00766 debugPuts('\x1b[30m\n'); // if there's no TCP header dump we terminate the line with \n and VT100 code for black 00767 #endif 00768 #endif 00769 } 00770 00771 /// dump a TCP header on the optional debug serial port 00772 void dumpHeaderTCP(int outGoing) 00773 { 00774 #if defined(TCP_HEADER_DUMP_YES) && defined(SERIAL_PORT_MONITOR_YES) 00775 char flagString[9]; // text string presenting the 8 most important TCP flags 00776 #define PRINT_ALL_TCP_FLAGS_YES 00777 #ifdef PRINT_ALL_TCP_FLAGS_YES 00778 memset(flagString,'.', 8); // fill string with "........" 00779 if (ppp.tcp->flag.fin) flagString[7]='F'; 00780 if (ppp.tcp->flag.syn) flagString[6]='S'; 00781 if (ppp.tcp->flag.rst) flagString[5]='R'; 00782 if (ppp.tcp->flag.psh) flagString[4]='P'; 00783 if (ppp.tcp->flag.ack) flagString[3]='A'; 00784 if (ppp.tcp->flag.urg) flagString[2]='U'; 00785 if (ppp.tcp->flag.ece) flagString[1]='E'; 00786 if (ppp.tcp->flag.cwr) flagString[0]='C'; 00787 flagString[8]=0; // null terminate string 00788 #else 00789 if (ppp.tcp->flag.ack) flagString[0]='A'; // choose only the most important flag to print 00790 if (ppp.tcp->flag.syn) flagString[0]='S'; 00791 if (ppp.tcp->flag.fin) flagString[0]='F'; 00792 if (ppp.tcp->flag.psh) flagString[0]='P'; 00793 if (ppp.tcp->flag.rst) flagString[0]='R'; 00794 flagString[1]=0; // null terminate string 00795 #endif 00796 debugPuts( flagString ); 00797 #define EVERY_PACKET_ON_A_NEW_LINE_YES 00798 #ifdef EVERY_PACKET_ON_A_NEW_LINE_YES 00799 debugPuts("\x1b[30m\n"); // write a black color and newline after every packet 00800 #else 00801 debugPuts("\x1b[30m"); // write a black color after every packet 00802 #endif 00803 if( outGoing && ppp.tcp->flag.fin ) { // ACK/FIN - if this is an outgoing FIN it's the end of a tcp conversation 00804 debugPutc('\n'); // insert an extra new line to mark the end (except for final ack) of an HTTP conversation 00805 } 00806 #endif 00807 } 00808 00809 /// Encode a buffer in base-64 00810 const static char lut [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 00811 void enc64(char * in, char * out, int len) 00812 { 00813 int i,j,a,b,c; 00814 i=0; 00815 j=0; 00816 while(1) { 00817 if (i<len) { 00818 a = in[i++]; 00819 out[j++] = lut[ ( (a >> 2) & 0x3f) ]; 00820 } else break; 00821 if (i<len) { 00822 b = in[i++]; 00823 out[j++] = lut[ ( (a << 4) & 0x30) | ( (b >> 4) & 0x0f) ]; 00824 out[j++] = lut[ ( (b << 2) & 0x3c) ]; 00825 } else out[j++] = '='; 00826 if (i<len) { 00827 c = in[i++]; 00828 j--; 00829 out[j++] = lut[ ( (b << 2) & 0x3c) | ( (c >> 6) & 0x03) ]; 00830 out[j++] = lut[ ( (c >> 0) & 0x3f) ]; 00831 } else out[j++] = '='; 00832 } 00833 out[j]=0; 00834 } 00835 00836 /// Handle a request for an http websocket. 00837 /// We end up here if we enter the following javascript in a web browser console: x = new WebSocket("ws://172.10.10.2"); 00838 int webSocketHandler(char * dataStart) 00839 { 00840 int n=0; // byte counter 00841 char * key = strstr(dataStart, "Sec-WebSocket-Key:"); // search for the key in the payload 00842 if (key != NULL) { 00843 key = key + 18; // skip over the key ident string "Sec-WebSocket-Key:" 00844 if (v0) debugPuts("WebSocket Request\n"); 00845 while ( strchr(lut, *key) == NULL) key++; // skip non-valid base-64 characters (whitespace) 00846 char challenge [80]; 00847 int i=0; 00848 char * copyTo = challenge; 00849 while (strchr(lut, *key) != NULL) { // copy while we see valid base-64 characters 00850 if (i++ >40) break; // prevent buffer overflow 00851 *copyTo++ = *key++; // copy next valid base-64 character 00852 } 00853 strcpy(copyTo,"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); // append websocket gui code 00854 #define DUMP_WEBSOCKET_CHALLENGE_NO 00855 #ifdef DUMP_WEBSOCKET_CHALLENGE_YES 00856 debugPrintf("Challenge is %s\n", challenge); // the string we hash for the challenge 00857 #endif 00858 char shaOutput [20]; // sha1 output 00859 sha1( shaOutput, challenge, strlen(challenge)); // hash the challenge 00860 char encOut[50]; 00861 enc64( shaOutput, encOut, 20); // base-64 encode 00862 char * versionstring = strstr(dataStart, "Sec-WebSocket-Version:"); 00863 char * version = challenge; 00864 strncpy(version, versionstring,70); // copy their version string 00865 *strchr(version,'\r')=0; // null terminate so we can sprintf it 00866 memset(dataStart,0,500); // blank out old data before sending the websocket response header 00867 n=n+sprintf(dataStart+n, "HTTP/1.1 101 Switching Protocols\r\n"); 00868 n=n+sprintf(dataStart+n, "Upgrade: websocket\r\n"); 00869 n=n+sprintf(dataStart+n, "Connection: Upgrade\r\n"); 00870 n=n+sprintf(dataStart+n, "Sec-WebSocket-Accept: %s\r\n",encOut); 00871 n=n+sprintf(dataStart+n, "%s\r\n",version); 00872 #define SHOW_MBED_IN_RESPONSE_YES // change to SHOW_MBED_IN_RESPONSE_NO to exclude 00873 #ifdef SHOW_MBED_IN_RESPONSE_YES 00874 n=n+sprintf(dataStart+n, "mbed-Code: PPP-Blinky\r\n"); 00875 #endif 00876 n=n+sprintf(dataStart+n, "\r\n"); // websocket response header ending 00877 } 00878 return n; // this response should satisfy a web browser's websocket protocol request 00879 } 00880 00881 #define TCP_FLAG_ACK (1<<4) 00882 #define TCP_FLAG_SYN (1<<1) 00883 #define TCP_FLAG_PSH (1<<3) 00884 #define TCP_FLAG_RST (1<<2) 00885 #define TCP_FLAG_FIN (1<<0) 00886 00887 /// respond to an HTTP request 00888 int httpResponse(char * dataStart, int * flags) 00889 { 00890 int n=0; // number of bytes we have printed so far 00891 n = webSocketHandler( dataStart ); // test for and handle WebSocket upgrade requests 00892 if (n>0) return n; // if n>0 we already have a response, so return 00893 00894 int nHeader; // byte size of HTTP header 00895 int contentLengthStart; // index where HTML starts 00896 int httpGet5,httpGet6,httpGetx, httpGetRoot; // temporary storage of strncmp results 00897 *flags = TCP_FLAG_ACK | TCP_FLAG_FIN; // the default case is that we close the connection 00898 00899 httpGetRoot = strncmp(dataStart, "GET / HTTP/1.", 13); // found a GET to the root directory 00900 httpGetx = strncmp(dataStart, "GET /x", 6); // found a GET to /x which we will treat special (anything starting with /x, e.g. /x, /xyz, /xABC?pqr=123 00901 httpGet5 = dataStart[5]; // the first character in the path name, we use it for special functions later on 00902 httpGet6 = dataStart[6]; // the second character in the path name, we use it for special functions later on 00903 // for example, you could try this using netcat (nc): echo "GET /x" | nc 172.10.10.2 00904 n=n+sprintf(n+dataStart,"HTTP/1.1 "); // start of response header 00905 if( (httpGetRoot==0) || (httpGetx==0) || (httpGet5 == 'w' ) ) { 00906 n=n+sprintf(n+dataStart,"200 OK\r\n"); // 200 OK header 00907 } else { 00908 n=n+sprintf(n+dataStart,"404 Not Found\r\n"); // 404 header 00909 } 00910 #define SHOWSERVERNAME_NO 00911 #ifdef SHOWSERVERNAME_YES 00912 n=n+sprintf(n+dataStart,"Server: mbed PPP-Blinky\r\n"); // Server in response header 00913 #endif 00914 #define SHOWMAXBUFLEVEL_NO 00915 #ifdef SHOWMAXBUFLEVEL_YES 00916 // if this is enabled then the maximum value of the serial port receive buffer will show up in the http headers of your browser - nice for debugging 00917 n=n+sprintf(n+dataStart,"Maxbuflevel: %d\r\n", ppp.rx.maxbuflevel); 00918 #endif 00919 #define SHOWPPPCOUNT_NO 00920 #ifdef SHOWPPPCOUNT_YES 00921 // if this is enabled then the received PPP packet count will show up in the http headers of your browser - disabled for now because it's also displayed in the root page 00922 n=n+sprintf(n+dataStart,"PPP-RX-Packets: %d\r\n", ppp.pppCount); 00923 #endif 00924 n=n+sprintf(n+dataStart,"Content-Type: text/html; charset=us-ascii\r\n"); // http header must end with empty line (\r\n) 00925 n=n+sprintf(n+dataStart,"Content-Length: ?????\r\n\r\n"); // leave five spaces for content length - will be updated later 00926 contentLengthStart = n-9; // remember where Content-Length starts in the buffer 00927 nHeader=n; // size of HTTP header and start of payload 00928 if( httpGetRoot == 0 ) { 00929 // this is where we insert our web page into the buffer 00930 memcpy(n+dataStart,rootWebPage,sizeof(rootWebPage)); 00931 n = n + sizeof(rootWebPage)-1; // one less than sizeof because we don't count the null byte at the end 00932 // naximum length of pppCount (maximum number of digits of an unsigned 32-bit int) 00933 #define PPPCOUNTMAXLEN (10) 00934 char pppCountString[PPPCOUNTMAXLEN+1]; // space for the ten digits and a string terminator 0 00935 snprintf(pppCountString,PPPCOUNTMAXLEN+1,"%*d",PPPCOUNTMAXLEN,ppp.pppCount); // print number of received PPP packets right justified in 10 spaces 00936 char * tenNulls = "0000000000"; 00937 char * pppCountStart = strstr(dataStart,tenNulls); // find the ten zeros in the page - this is where we must paste in the PPP Rx packet count 00938 if ( pppCountStart != NULL ) 00939 memcpy(pppCountStart, pppCountString, PPPCOUNTMAXLEN); // copy ping count over the ten zeros in the page 00940 snprintf(pppCountString,PPPCOUNTMAXLEN+1,"%*d",PPPCOUNTMAXLEN,SystemCoreClock); // print System Core Clock speed 00941 char * coreClockStart = strstr(dataStart,tenNulls); // find the ten zeros in the page - this is where we must paste in the core clock speed 00942 if ( coreClockStart != NULL ) 00943 memcpy(coreClockStart, pppCountString, PPPCOUNTMAXLEN); // copy core clock speed over the ten zeros in the page 00944 } else if ( httpGet5 == 'w' ) { // "w" is a special page for websocket demo 00945 memcpy(n+dataStart,webSocketPage,sizeof(webSocketPage)); 00946 n = n + sizeof(webSocketPage)-1; // one less than size 00947 *flags = TCP_FLAG_ACK | TCP_FLAG_PSH; // for a websocket page we do NOT close the connection 00948 } else { 00949 if (httpGetx == 0) { // the page request started with "GET /x" - here we treat anything starting with /x special: 00950 #define W3C_COMPLIANT_RESPONSE_NO 00951 // change the above to W3C_COMPLIANT_RESPONSE_YES if you want a W3C.org compliant HTTP response 00952 #ifdef W3C_COMPLIANT_RESPONSE_YES 00953 n=n+sprintf(n+dataStart,"<!DOCTYPE html><title>mbed PPP-Blinky</title>"); // html title (W3C.org required elements) 00954 #endif 00955 if( (ppp.ledState & 1) == 0) // check lowest bit of LED state and respond with red or green favicon - this stops browsers from asking for favicon.ico file 00956 n=n+sprintf(n+dataStart,"<link rel=\"icon\" id=\"red-pixel\" href=\"data:image/gif;base64,R0lGODlhAQABAIABAP8AAAAAACwAAAAAAQABAAACAkQBADs\">"); // data url containing 1 red pixel gif 00957 else 00958 n=n+sprintf(n+dataStart,"<link rel=\"icon\" id=\"grn-pixel\" href=\"data:image/gif;base64,R0lGODlhAQABAIABAAD/AAAAACwAAAAAAQABAAACAkQBADs=\">"); // data url containing 1 green pixel gif 00959 if( httpGet6 == 'b' ) // if the fetched page is "xb" send a meta command to let the browser continuously reload 00960 n=n+sprintf(n+dataStart, "<meta http-equiv=\"refresh\" content=\"0\">"); // reload loop - handy for benchmarking 00961 // /x is a very short page, in fact, it is only a decimal number showing the http Page count 00962 if (httpGet6 == 't' ) // if the fetched pages is "xt" toggle the LEd 00963 led1Toggle(); 00964 #ifdef W3C_COMPLIANT_RESPONSE_YES 00965 n=n+sprintf(n+dataStart,"<body>%d</body>",ppp.responseCounter); // body = the http frame count 00966 #else 00967 n=n+sprintf(n+dataStart,"%d",ppp.responseCounter); // not really valid html but most browsers and curl are ok with it 00968 #endif 00969 } else { 00970 // all other requests get a Not Found response 00971 n=n+sprintf(n+dataStart,"<!DOCTYPE html><title>mbed PPP-Blinky</title>"); // html title (W3C.org required elements) 00972 n=n+sprintf(n+dataStart,"<body>404 Not Found</body>"); // not found message 00973 } 00974 } 00975 #define CONTENTLENGTHSIZE 5 00976 char contentLengthString[CONTENTLENGTHSIZE+1]; 00977 snprintf(contentLengthString,CONTENTLENGTHSIZE+1,"%*d",CONTENTLENGTHSIZE,n-nHeader); // print Content-Length with leading spaces and fixed width equal to csize 00978 memcpy(dataStart+contentLengthStart, contentLengthString, CONTENTLENGTHSIZE); // copy Content-Length to it's place in the send buffer 00979 return n; // total byte size of our response 00980 } 00981 00982 /// Handle TCP data that is not an HTTP GET. 00983 /// This is handy when for example you want to use netcat (nc.exe) to talk to PPP-Blinky. 00984 /// This could also be a websocket receive event - especially if the first byte is 0x81 (websocket data push) 00985 int tcpResponse(char * dataStart, int len, int * outFlags) 00986 { 00987 int n=0; // number of bytes we have printed so far 00988 if (dataStart[0] == 0x81) { // check if this is a websocket push message 00989 char mask [4]; 00990 memcpy ( mask, dataStart+2, 4); // websocket messages are "masked", so first we obtain the 4-byte mask 00991 int websocketMessageSize = len - 6; // 1 byte prefix (0x81), 1 byte, 4 bytes mask = 6 bytes 00992 if((dataStart[1]&0x80)==0x80) // test if the mask bit is set, which means all data is xor'ed with the mask 00993 for (int i=0; i<websocketMessageSize; i++) dataStart[i+6]^= mask[i%4]; // unmask each byte with one of the mask bytes 00994 dataStart[1] = len-2; // add four extra bytes to the message length because we don't use mask bytes for the send 00995 memcpy(dataStart+2, "Got:",4); // insert our own text into the four mask bytes 00996 n = len; // our response size remains exactly the same length as what we received 00997 } else if ( (dataStart[0]==0x88) && (dataStart[1]==0x80) && (len == 6) ) { // test for a websocket close request 00998 n=2; // our close command is only two bytes long because we don't use the four mask bytes 00999 dataStart[1]=0; // we don't have mask bytes on 01000 } else { 01001 if ( len > 1 ) { // we assume a length of 1 is a keep-alive or push packet 01002 if (v1) debugPuts("TCP data received\n"); // all other tcp push packets 01003 } 01004 } 01005 return n; // total byte size of our response 01006 } 01007 01008 /// dump the TCP data to the debug serial port 01009 void dumpDataTCP(int outGoing) 01010 { 01011 #ifdef SERIAL_PORT_MONITOR_YES 01012 if (v2) { 01013 int packetLengthIp = __REV16(ppp.ip->lengthR ); // size of ip packet 01014 int headerSizeIp = 4 * ppp.ip->headerLength; // size of ip header 01015 ppp.tcpStart = ppp.ipStart + headerSizeIp; // calculate where the TCP header starts 01016 int headerSizeTcp = 4 * (ppp.tcp->offset); // tcp "offset" for start of data is also the header size 01017 ppp.tcpData = ppp.tcpStart + headerSizeTcp; // start of tcp data 01018 int tcpSize = packetLengthIp - headerSizeIp; // tcp size = size of ip payload 01019 int tcpDataSize = tcpSize - headerSizeTcp; // size of data block after TCP header 01020 char pbuf[100]; // local print buffer 01021 int n=0; 01022 01023 n=n+sprintf(pbuf+n, outGoing ? "\x1b[34m" : "\x1b[30m" ); // VT100 color code, print black for incoming, blue for outgoing headers 01024 01025 n=n+sprintf(pbuf+n, "IP:%d ipHeader:%d tcpHeader:%d tcpData:%d\n", packetLengthIp, headerSizeIp, headerSizeTcp, tcpDataSize); // 1 for more verbose 01026 if (n>95) debugPuts("n>pbuf overflow in dumpDataTCP()\n"); 01027 01028 debugPuts( pbuf ); 01029 if (tcpDataSize > 0) { 01030 ppp.tcpData[tcpDataSize]=0; // insert a null after the data so debug printf stops printing after the data 01031 debugPuts( ppp.tcpData ); // print the tcp payload data 01032 debugPuts("\n"); 01033 } 01034 debugPuts( "\x1b[30m" ); // VT100 color code, print black 01035 } 01036 #endif 01037 } 01038 01039 /// handle an incoming TCP packet 01040 /// use the first few bytes to figure out if it's a websocket, an http request or just pure incoming TCP data 01041 void tcpHandler() 01042 { 01043 int packetLengthIp = __REV16(ppp.ip->lengthR ); // size of ip packet 01044 int headerSizeIp = 4 * ppp.ip->headerLength; // size of ip header 01045 ppp.tcpStart = ppp.ipStart + headerSizeIp; // calculate TCP header start 01046 int tcpSize = packetLengthIp - headerSizeIp; // tcp size = size of ip payload 01047 int headerSizeTcp = 4 * (ppp.tcp->offset); // tcp "offset" for start of data is also the header size 01048 char * tcpDataIn = ppp.tcpStart + headerSizeTcp; // start of TCP data after TCP header 01049 int tcpDataSize = tcpSize - headerSizeTcp; // size of data block after TCP header 01050 01051 unsigned int seq_in = __REV(ppp.tcp->seqTcpR); // incoming sequence number 01052 unsigned int ack_in = __REV(ppp.tcp->ackTcpR); // incoming acknowledge number 01053 unsigned int ack_out = seq_in + tcpDataSize; // calculate the acknowledge based on size of received packet 01054 unsigned int seq_out = ack_in; // adopt their version of our sequence number as our sequence number 01055 01056 // first we shorten the TCP response header to only 20 bytes. This means we ignore all TCP option requests 01057 headerSizeIp=20; 01058 ppp.ip->headerLength = headerSizeIp/4; // ip header is 20 bytes long 01059 ppp.ip->lengthR = __REV(40); // 20 ip header + 20 tcp header 01060 headerSizeTcp = 20; // shorten outgoing TCP header size to 20 bytes (no data) 01061 ppp.tcpStart = ppp.ipStart + headerSizeIp; // recalc TCP header start 01062 ppp.tcp->offset = (headerSizeTcp/4); 01063 char * tcpDataOut = ppp.tcpStart + headerSizeTcp; // start of outgoing data 01064 01065 int dataLen = 0; // most of our responses will have zero TCP data, only a header 01066 int flagsOut = TCP_FLAG_ACK; // the default case is an ACK packet 01067 01068 ppp.tcp->windowR = __REV16( 1200 ); // set tcp window size to 1200 bytes 01069 01070 // A sparse TCP flag interpreter that implements stateless TCP connections 01071 01072 switch ( ppp.tcp->flag.All ) { 01073 case TCP_FLAG_SYN: 01074 flagsOut = TCP_FLAG_SYN | TCP_FLAG_ACK; // something wants to connect - acknowledge it 01075 seq_out = seq_in+0x10000000U; // create a new sequence number using their sequence as a starting point, increase the highest digit 01076 ack_out++; // for SYN flag we have to increase the sequence by 1 01077 break; 01078 case TCP_FLAG_ACK: 01079 case TCP_FLAG_ACK | TCP_FLAG_PSH: 01080 if (tcpDataSize == 0) return; // handle zero-size ack messages by ignoring them 01081 // if ( (ppp.tcp->flag.All == TCP_FLAG_ACK) && (tcpDataSize == 0)) return; // handle zero-size ack messages by ignoring them 01082 if ( (strncmp(tcpDataIn, "GET /", 5) == 0) ) { // check for an http GET command 01083 flagsOut = TCP_FLAG_ACK | TCP_FLAG_PSH; // we have data, set the PSH flag 01084 dataLen = httpResponse(tcpDataOut, &flagsOut); // send an http response 01085 } else { 01086 dataLen = tcpResponse(tcpDataOut,tcpDataSize, &flagsOut); // not an http GET, handle as a tcp connection 01087 if (dataLen > 0) flagsOut = TCP_FLAG_ACK | TCP_FLAG_PSH; // if we have any data set the PSH flag 01088 } 01089 break; 01090 case TCP_FLAG_FIN: 01091 case TCP_FLAG_FIN | TCP_FLAG_ACK: 01092 case TCP_FLAG_FIN | TCP_FLAG_PSH | TCP_FLAG_ACK: 01093 flagsOut = TCP_FLAG_ACK | TCP_FLAG_FIN; // set outgoing FIN flag to ask them to close from their side 01094 ack_out++; // for FIN flag we have to increase the sequence by 1 01095 break; 01096 default: 01097 return; // ignore all other packets 01098 } // switch 01099 01100 // The TCP flag handling is now done 01101 // first we swap source and destination TCP addresses and insert the new ack and seq numbers 01102 swapIpAddresses(); // swap IP source and destination addresses 01103 swapIpPorts(); // swap IP source and destination ports 01104 01105 ppp.tcp->ackTcpR = __REV( ack_out ); // byte reversed - tcp/ip messages are big-endian (high byte first) 01106 ppp.tcp->seqTcpR = __REV( seq_out ); // byte reversed - tcp/ip messages are big-endian (high byte first) 01107 01108 ppp.tcp->flag.All = flagsOut; // update the TCP flags 01109 01110 // recalculate all the header sizes 01111 tcpSize = headerSizeTcp + dataLen; // tcp packet size 01112 int newPacketSize = headerSizeIp + tcpSize; // calculate size of the outgoing packet 01113 ppp.ip->lengthR = __REV16 ( newPacketSize ); 01114 ppp.pkt.len = newPacketSize+4+2; // ip packet length + 4-byte ppp prefix (ff 03 00 21) + 2 fcs (crc) bytes bytes at the end of the packet 01115 01116 // the header is all set up, now do the IP and TCP checksums 01117 IpHeaderCheckSum(); // calculate new IP header checksum 01118 checkSumPseudoHeader( tcpSize ); // get the TCP pseudo-header checksum 01119 ppp.tcp->checksumR = 0; // before TCP checksum calculations the checksum bytes must be set cleared 01120 unsigned int pseudoHeaderSum=dataCheckSum(ppp.tcpStart,tcpSize, 0); // continue the TCP checksum on the whole TCP packet 01121 ppp.tcp->checksumR = __REV16( pseudoHeaderSum); // tcp checksum done, store it in the TCP header 01122 01123 dumpHeaderIP(1); // dump outgoing IP header before sending the frame 01124 dumpHeaderTCP(1); // dump outgoing TCP header before sending the frame 01125 dumpDataTCP(1); // dump outgoing TCP data before sending the frame 01126 #define WAIT_BEFORE_IP_SEND_NO 01127 #ifdef WAIT_BEFORE_IP_SEND_YES 01128 wait_ms(45); // 45 ms delay before sending frame 01129 #endif 01130 sendPppFrame(); // All preparation complete - send the TCP response 01131 #define DUMP_TRANSMITTED_PPP_NO 01132 #ifdef DUMP_TRANSMITTED_PPP_YES 01133 dumpPPPFrame(); // dump every transmitted ppp frame to debug port 01134 #endif 01135 memset(ppp.pkt.buf+44,0,500); // flush out traces of previous data that we may scan for 01136 } 01137 01138 /// handle an incoming TCP packet 01139 void TCPpacket() 01140 { 01141 dumpHeaderIP(0); // dump incoming packet IP header 01142 dumpHeaderTCP(0); // dump incoming packet TCP header 01143 dumpDataTCP(0); // dump incoming packet data 01144 tcpHandler(); 01145 } 01146 01147 /// handle the remaining IP protocols by ignoring them 01148 void otherProtocol() 01149 { 01150 debugPuts("Other IP protocol"); 01151 } 01152 01153 /// process an incoming IP packet 01154 void IPframe() 01155 { 01156 int protocol = ppp.ip->protocol; 01157 switch (protocol) { 01158 case 1: 01159 ICMPpacket(); 01160 break; 01161 case 2: 01162 IGMPpacket(); 01163 break; 01164 case 17: 01165 UDPpacket(); 01166 break; 01167 case 6: 01168 TCPpacket(); 01169 break; 01170 default: 01171 otherProtocol(); 01172 } 01173 } 01174 01175 /// process LCP packets 01176 void LCPframe() 01177 { 01178 int code = ppp.lcp->code; 01179 switch (code) { 01180 case 1: // LCP configuration request 01181 debugPuts("LCP Config"); 01182 if ( __REV16( ppp.lcp->lengthR ) == 4 ) { 01183 debugPuts("LCP Ack\n"); 01184 ppp.lcp->code=2; // acknowledge zero configuration request 01185 sendPppFrame(); 01186 ppp.lcp->code=1; // request no options 01187 sendPppFrame(); 01188 } else { 01189 debugPuts("LCP request reject\n"); 01190 ppp.lcp->code=4; // allow only "no options" which means Maximum Receive Unit (MRU) is default 1500 bytes 01191 sendPppFrame(); 01192 } 01193 break; 01194 case 2: // LCP configuration acknowledge 01195 debugPuts("LCP Ack\n"); // don't do anything, i.e. ignore 01196 break; 01197 case 5: // LCP end 01198 ppp.lcp->code=6; // acknowledge 01199 sendPppFrame(); // acknowledge 01200 ppp.online=0; // start hunting for connect string again 01201 pppInitStruct(); // flush the receive buffer 01202 debugPuts("LCP End (Disconnect from host)\n"); 01203 break; // end connection 01204 default: 01205 debugPuts("LCP Other\n"); 01206 } 01207 } 01208 01209 /// discard packets that are not IP, IPCP, or LCP 01210 void discardedFrame() 01211 { 01212 if (v0) debugPrintf("Frame is not IP, IPCP or LCP: %02x %02x %02x %02x\n", ppp.pkt.buf[0],ppp.pkt.buf[1],ppp.pkt.buf[2],ppp.pkt.buf[3]); 01213 } 01214 01215 /// determine the packet type (IP, IPCP or LCP) of incoming packets 01216 void determinePacketType() 01217 { 01218 if ( ppp.ppp->address != 0xff ) { 01219 debugPuts("Unexpected: PPP address != ff\n"); 01220 return; 01221 } 01222 if ( ppp.ppp->control != 3 ) { 01223 debugPuts("Unexpected: PPP control != 3\n"); 01224 return; 01225 } 01226 unsigned int protocol = __REV16( ppp.ppp->protocolR ); 01227 switch ( protocol ) { 01228 case 0xc021: 01229 LCPframe(); 01230 break; // link control 01231 case 0x8021: 01232 IPCPframe(); 01233 break; // IP control 01234 case 0x0021: 01235 IPframe(); 01236 break; // IP itself 01237 default: 01238 discardedFrame(); 01239 } 01240 } 01241 01242 01243 /// SHA1 library 01244 /* 01245 SHA-1 in C 01246 By Steve Reid <steve@edmweb.com> 01247 100% Public Domain 01248 */ 01249 01250 #define SHA1HANDSOFF 01251 01252 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 01253 01254 /* blk0() and blk() perform the initial expand. */ 01255 /* I got the idea of expanding during the round function from SSLeay */ 01256 #if BYTE_ORDER == LITTLE_ENDIAN 01257 #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 01258 |(rol(block->l[i],8)&0x00FF00FF)) 01259 #elif BYTE_ORDER == BIG_ENDIAN 01260 #define blk0(i) block->l[i] 01261 #else 01262 #error "Endianness not defined!" 01263 #endif 01264 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 01265 ^block->l[(i+2)&15]^block->l[i&15],1)) 01266 01267 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 01268 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 01269 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 01270 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 01271 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 01272 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 01273 01274 01275 /* Hash a single 512-bit block. This is the core of the algorithm. */ 01276 01277 void SHA1Transform( 01278 uint32_t state[5], 01279 const unsigned char buffer[64] 01280 ) 01281 { 01282 uint32_t a, b, c, d, e; 01283 01284 typedef union { 01285 unsigned char c[64]; 01286 uint32_t l[16]; 01287 } CHAR64LONG16; 01288 01289 #ifdef SHA1HANDSOFF 01290 CHAR64LONG16 block[1]; /* use array to appear as a pointer */ 01291 01292 memcpy(block, buffer, 64); 01293 #else 01294 /* The following had better never be used because it causes the 01295 * pointer-to-const buffer to be cast into a pointer to non-const. 01296 * And the result is written through. I threw a "const" in, hoping 01297 * this will cause a diagnostic. 01298 */ 01299 CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; 01300 #endif 01301 /* Copy context->state[] to working vars */ 01302 a = state[0]; 01303 b = state[1]; 01304 c = state[2]; 01305 d = state[3]; 01306 e = state[4]; 01307 /* 4 rounds of 20 operations each. Loop unrolled. */ 01308 R0(a, b, c, d, e, 0); 01309 R0(e, a, b, c, d, 1); 01310 R0(d, e, a, b, c, 2); 01311 R0(c, d, e, a, b, 3); 01312 R0(b, c, d, e, a, 4); 01313 R0(a, b, c, d, e, 5); 01314 R0(e, a, b, c, d, 6); 01315 R0(d, e, a, b, c, 7); 01316 R0(c, d, e, a, b, 8); 01317 R0(b, c, d, e, a, 9); 01318 R0(a, b, c, d, e, 10); 01319 R0(e, a, b, c, d, 11); 01320 R0(d, e, a, b, c, 12); 01321 R0(c, d, e, a, b, 13); 01322 R0(b, c, d, e, a, 14); 01323 R0(a, b, c, d, e, 15); 01324 R1(e, a, b, c, d, 16); 01325 R1(d, e, a, b, c, 17); 01326 R1(c, d, e, a, b, 18); 01327 R1(b, c, d, e, a, 19); 01328 R2(a, b, c, d, e, 20); 01329 R2(e, a, b, c, d, 21); 01330 R2(d, e, a, b, c, 22); 01331 R2(c, d, e, a, b, 23); 01332 R2(b, c, d, e, a, 24); 01333 R2(a, b, c, d, e, 25); 01334 R2(e, a, b, c, d, 26); 01335 R2(d, e, a, b, c, 27); 01336 R2(c, d, e, a, b, 28); 01337 R2(b, c, d, e, a, 29); 01338 R2(a, b, c, d, e, 30); 01339 R2(e, a, b, c, d, 31); 01340 R2(d, e, a, b, c, 32); 01341 R2(c, d, e, a, b, 33); 01342 R2(b, c, d, e, a, 34); 01343 R2(a, b, c, d, e, 35); 01344 R2(e, a, b, c, d, 36); 01345 R2(d, e, a, b, c, 37); 01346 R2(c, d, e, a, b, 38); 01347 R2(b, c, d, e, a, 39); 01348 R3(a, b, c, d, e, 40); 01349 R3(e, a, b, c, d, 41); 01350 R3(d, e, a, b, c, 42); 01351 R3(c, d, e, a, b, 43); 01352 R3(b, c, d, e, a, 44); 01353 R3(a, b, c, d, e, 45); 01354 R3(e, a, b, c, d, 46); 01355 R3(d, e, a, b, c, 47); 01356 R3(c, d, e, a, b, 48); 01357 R3(b, c, d, e, a, 49); 01358 R3(a, b, c, d, e, 50); 01359 R3(e, a, b, c, d, 51); 01360 R3(d, e, a, b, c, 52); 01361 R3(c, d, e, a, b, 53); 01362 R3(b, c, d, e, a, 54); 01363 R3(a, b, c, d, e, 55); 01364 R3(e, a, b, c, d, 56); 01365 R3(d, e, a, b, c, 57); 01366 R3(c, d, e, a, b, 58); 01367 R3(b, c, d, e, a, 59); 01368 R4(a, b, c, d, e, 60); 01369 R4(e, a, b, c, d, 61); 01370 R4(d, e, a, b, c, 62); 01371 R4(c, d, e, a, b, 63); 01372 R4(b, c, d, e, a, 64); 01373 R4(a, b, c, d, e, 65); 01374 R4(e, a, b, c, d, 66); 01375 R4(d, e, a, b, c, 67); 01376 R4(c, d, e, a, b, 68); 01377 R4(b, c, d, e, a, 69); 01378 R4(a, b, c, d, e, 70); 01379 R4(e, a, b, c, d, 71); 01380 R4(d, e, a, b, c, 72); 01381 R4(c, d, e, a, b, 73); 01382 R4(b, c, d, e, a, 74); 01383 R4(a, b, c, d, e, 75); 01384 R4(e, a, b, c, d, 76); 01385 R4(d, e, a, b, c, 77); 01386 R4(c, d, e, a, b, 78); 01387 R4(b, c, d, e, a, 79); 01388 /* Add the working vars back into context.state[] */ 01389 state[0] += a; 01390 state[1] += b; 01391 state[2] += c; 01392 state[3] += d; 01393 state[4] += e; 01394 /* Wipe variables */ 01395 a = b = c = d = e = 0; 01396 #ifdef SHA1HANDSOFF 01397 memset(block, '\0', sizeof(block)); 01398 #endif 01399 } 01400 01401 01402 /* SHA1Init - Initialize new context */ 01403 01404 void SHA1Init( 01405 SHA1_CTX * context 01406 ) 01407 { 01408 /* SHA1 initialization constants */ 01409 context->state[0] = 0x67452301; 01410 context->state[1] = 0xEFCDAB89; 01411 context->state[2] = 0x98BADCFE; 01412 context->state[3] = 0x10325476; 01413 context->state[4] = 0xC3D2E1F0; 01414 context->count[0] = context->count[1] = 0; 01415 } 01416 01417 01418 /* Run your data through this. */ 01419 01420 void SHA1Update( 01421 SHA1_CTX * context, 01422 const unsigned char *data, 01423 uint32_t len 01424 ) 01425 { 01426 uint32_t i; 01427 01428 uint32_t j; 01429 01430 j = context->count[0]; 01431 if ((context->count[0] += len << 3) < j) 01432 context->count[1]++; 01433 context->count[1] += (len >> 29); 01434 j = (j >> 3) & 63; 01435 if ((j + len) > 63) { 01436 memcpy(&context->buffer[j], data, (i = 64 - j)); 01437 SHA1Transform(context->state, context->buffer); 01438 for (; i + 63 < len; i += 64) { 01439 SHA1Transform(context->state, &data[i]); 01440 } 01441 j = 0; 01442 } else 01443 i = 0; 01444 memcpy(&context->buffer[j], &data[i], len - i); 01445 } 01446 01447 01448 /* Add padding and return the message digest. */ 01449 01450 void SHA1Final( 01451 unsigned char digest[20], 01452 SHA1_CTX * context 01453 ) 01454 { 01455 unsigned i; 01456 01457 unsigned char finalcount[8]; 01458 01459 unsigned char c; 01460 01461 for (i = 0; i < 8; i++) { 01462 finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ 01463 } 01464 01465 c = 0200; 01466 SHA1Update(context, &c, 1); 01467 while ((context->count[0] & 504) != 448) { 01468 c = 0000; 01469 SHA1Update(context, &c, 1); 01470 } 01471 SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 01472 for (i = 0; i < 20; i++) { 01473 digest[i] = (unsigned char) 01474 ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); 01475 } 01476 /* Wipe variables */ 01477 memset(context, '\0', sizeof(*context)); 01478 memset(&finalcount, '\0', sizeof(finalcount)); 01479 } 01480 01481 void sha1( 01482 char *hash_out, 01483 const char *str, 01484 int len) 01485 { 01486 SHA1_CTX ctx; 01487 unsigned int ii; 01488 01489 SHA1Init(&ctx); 01490 for (ii=0; ii<len; ii+=1) 01491 SHA1Update(&ctx, (const unsigned char*)str + ii, 1); 01492 SHA1Final((unsigned char *)hash_out, &ctx); 01493 // hash_out[20] = '\0'; 01494 } 01495 01496 01497 /// a sniffer tool to assist in figuring out where in the code we are having characters in the input buffer 01498 void sniff() 01499 { 01500 if ( pc.readable() ) debugPuts( "Sniff - Char available!\n" ); // if this prints anything it means there is a character in the serial receive buffer 01501 } 01502 01503 /// scan the PPP serial input stream for frame start markers 01504 void waitForPppFrame() 01505 { 01506 while(1) { 01507 if ( ppp.rx.head != ppp.rx.tail ) { // check for something in the receive buffer 01508 int oldTail = ppp.rx.tail; // remember where the character is located in the buffer 01509 int rx = pc_getBuf(); // get the character 01510 if (rx==0x7e) { // check for frame start/end character 0x7e 01511 if (ppp.firstFrame) { // is this the start of the first frame 01512 ppp.rx.rtail = ppp.rx.tail; // update real-time tail with the virtual tail 01513 ppp.hdlc.frameStartIndex = ppp.rx.tail; // remember where first frame started 01514 ppp.firstFrame=0; // clear first frame flag 01515 } else { 01516 ppp.hdlc.frameEndIndex=oldTail; // mark the frame end character 01517 processPPPFrame(ppp.hdlc.frameStartIndex, ppp.hdlc.frameEndIndex); // process the frame 01518 ppp.rx.rtail = ppp.rx.tail; // update real-time tail with the virtual tail 01519 ppp.hdlc.frameStartIndex = ppp.rx.tail; // remember where next frame started 01520 break; 01521 } 01522 } 01523 } 01524 // change below to YES to enable the serial to UDP demo 01525 #define SERIAL_TO_UDP_NO 01526 #ifdef SERIAL_TO_UDP_YES 01527 sendUdpData(); // demo that sends characters received on the DEBUG serial port via UDP to another host 01528 #endif 01529 } 01530 } 01531 01532 01533 /// Wait for a dial-up modem connect command ("CLIENT") from the host PC, if found, we set ppp.online to true, which starts the IP packet scanner. 01534 // Note: a 0x7E in the input stream (ppp start of frame character) will also set ppp.online to true - see the code in pppReceiveHandler() 01535 void waitForPcConnectString() 01536 { 01537 while(ppp.online == 0) { 01538 // search for Windows Dialup Networking "Direct Connection Between Two Computers" expected connect string 01539 char * found1 = strstr( (char *)ppp.rx.buf, "CLIENT" ); 01540 if (found1 != NULL) { 01541 if (v0) debugPuts("Connected: Found connect string \"CLIENT\", sent \"CLIENTSERVER\"\n"); 01542 // respond with Windows Dialup networking expected "Direct Connection Between Two Computers" response string 01543 pc.puts("CLIENTSERVER"); 01544 ppp.online=1; // we are connected - set flag so we stop looking for the connect string 01545 } 01546 } 01547 } 01548 01549 /// Initialize PPP data structure and set serial port(s) baud rate(s) 01550 void initializePpp() 01551 { 01552 #ifdef SERIAL_PORT_MONITOR_YES 01553 debugBaudRate(115200); // baud rate for (optional) debug serial port 01554 debugPuts("\x1b[2J\x1b[H\x1b[30m"); // VT100 codes for clear_screen, home, black_text - Tera Term is a handy VT100 terminal 01555 wait_ms(200); // a brief wait so a human can see the reset event 01556 debugPuts("mbed PPP-Blinky HTTP & WebSocket server ready :)\n"); 01557 #endif 01558 pppInitStruct(); // initialize all the variables/properties/buffers 01559 pc.baud(115200); // pc serial port acting as a dial-up modem - for PPP traffic 01560 pc.attach(&pppReceiveHandler, RawSerial::RxIrq); // set up serial port receive interrupt handler 01561 } 01562
Generated on Thu Jul 14 2022 20:53:47 by 1.7.2