Small Internet Protocol Stack using a standard serial port.

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ppp-blinky.cpp Source File

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