Skip to content

Olduino Serves Cookies!

January 13, 2018

The various web and telnet servers that i’ve written for the olduino all use the IP address to distinguish between clients. This falls apart when the clients are coming from behind a firewall because they all have the same IP. I figured this was what cookies were for but they always seemed a bit mysterious. I googled around though and found this. It’s really pretty simple. If you include in the outbound header stream a sequence like:
Set-Cookie: yummy_cookie=choco
Then each succeeding transaction from that client contains the value as
Cookie: yummy_cookie=choco

My initial thought was just to assign each new client an arbitrary short id number and use that instead of the IP address . As a simpler starting point though, I decided to put the secret number in the cookie directly and get rid of the whole table. That’s all you really need to play the game although i’ve given up the ability to track the number of turns.

To incorporate sending the cookie, I changed the hard coded HTML header in doughnuts.h.

	static unsigned char hdr1a[]="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" //beginning of header
						"<html><body OnLoad=\"document.mf.G.focus();\">"
						"<span style=\"color:#0000A0\">\r\n"
						"<center><b>OLDUINO 1802 DOUGHNUTS SERVER 1.2</b>"
						"<br>(";

became

	static unsigned char hdr1aa[]="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n"
						"Set-Cookie: OS=";
	static unsigned char secret[]="541";
	static unsigned char hdr1ab[]="\r\n\r\n" //finshes off the http header and begins the body.
						"<html><body OnLoad=\"document.mf.G.focus();\">"
						"<span style=\"color:#0000A0\">\r\n"
						"<center><b>OLDUINO 1802 COOKIES SERVER 0.0</b>"
						"<br>(";

and changed the code in bagelsinit() to include

	setsecret(); //have to set the secret here so it can be sent in the header
	sendconst(hdr1aa); sendconst(secret); sendconst(hdr1ab);

The block below shows a header returned with three cookies in it. OS=218 is the secret,BC=00; OC=01; are left over from my playing around the other day. I thought cookies were supposed to go away when you closed the browser but these seem to be hanging in there.

GET / HTTP/1.1
Host: 169.254.180.2
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: BC=00; OC=01; OS=218

In the previous versions of bagels/doughnuts I only had to read the first 16 characters of any response to parse it. To retrieve the secret value from this gobbledegook I had to read the whole block (500-700 characters depending on the browser) and then fumble though it looking for the cookie. The following getsecret(size) routine gets called with every input and passed the size of the block (which is in the global variable buf). All it’s doing is scanning the last 100 bytes looking for the text “OS=” and taking the next 3 characters to be the secret. I’m sure there’s a more sophisticated approach but …

void getsecret(unsigned int rsize){
	unsigned char* secretcookie;
	secretcookie=strstr(buf+rsize-64,(unsigned char*)"OS=");
	if (secretcookie){
		memcpy(secret,secretcookie+3,3);
	}else{
		printf("secret cookie not found - substituting 999\n");
		strcpy((char *)secret,"999");
	}
}

I haven’t decided yet whether to bung more stuff into cookies or to go back to the id/table idea. Part of my goal is to make a simple messaging system and the id might be more suitable for that. Obviously this is highly susceptible to spoofing so there’s that.


/*****************************************************************************
//  File Name    : cookies.c
//  Version      : 0
//  Description  : Bagels Number Guessing Game for Wiznet w5500
//  Author       : Bill Rowe
//  Target       : Olduino
//  Compiler     : LCC1802
//  Created		 : Mar 12, 2015
*****************************************************************************/
//15-03-13 created based on bagels3 with change to doughnuts
//15-03-16 fixed logic error in response analysis
//17-01-12 adapting to use cookies instead of a table of secrets
#define nofloats
#include <nstdlib.h> //for printf etc.
#include <cpu1802spd4port7.h> //defines processor type, speed, and host port
#include <olduino.h> //for digitalRead, digitalWrite, delay
#include <hspi2.h> //hardware spi header
//end of olduino/1802 specific declares
#include "w5500data.h"  //wiznet definitions
#include "w5500code.h"  //wiznet code definitions
#include "cookies.h"
#define MAX_BUF 750					//buffer size to allow cookies to be retrieved
unsigned char buf[MAX_BUF];			//memory buffer for incoming & outgoing data
unsigned char guess [4]={0};
unsigned int pages=0; games=0; sessions=0;
char pnbuf[8];
union IPaddr thisip={182}; //the ip that a form/request came from
/*
 * strstr.c --
 *
 *	Source code for the "strstr" library routine.
 *
 * Copyright (c) 1988-1993 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: strstr.c,v 1.1.1.3 2003/03/06 00:09:04 landonf Exp $
 */

/*
 *----------------------------------------------------------------------
 *
 * strstr --
 *
 *	Locate the first instance of a substring in a string.
 *
 * Results:
 *	If string contains substring, the return value is the
 *	location of the first matching instance of substring
 *	in string.  If string doesn't contain substring, the
 *	return value is 0.  Matching is done on an exact
 *	character-for-character basis with no wildcards or special
 *	characters.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

unsigned char *
strstr(unsigned char *string,unsigned char * substring)
{
    unsigned char *a, *b;

    /* First scan quickly through the two strings looking for a
     * single-character match.  When it's found, then compare the
     * rest of the substring.
     */

    b = substring;
    if (*b == 0) {
	return string;
    }
    for ( ; *string != 0; string += 1) {
	if (*string != *b) {
	    continue;
	}
	a = string;
	while (1) {
	    if (*b == 0) {
		return string;
	    }
	    if (*a++ != *b++) {
		break;
	    }
	}
	b = substring;
    }
    return 0;
}

unsigned int tbrnd(){//random numbers ala tiny basic R:=R*2345+6789
	static unsigned int R=1;
	R=R*58653+13849;//R=R*2345+6789;
	//printf("%d\n",R);
	return R;
}
void setsecret(){//set the global secret to a 3 digit random number
	int i=0,d1=0,d2,d3;
	while(d1==0){
		d1=tbrnd()%10;
	}
	d2=tbrnd()%10;
	while(d1==d2){
		d2=tbrnd()%10;
	}
	d3=tbrnd()%10;
	while(d3==d1||d3==d2){
		d3=tbrnd()%10;
	}
	secret[0]=d1+0x30;secret[1]=d2+0x30;secret[2]=d3+0x30;secret[3]=0;
	printf("secret=%s\n",secret);
}
void bagelsinit(){
	int sendrc;
	games++;
	pages++;
	setsecret(); //have to set the secret here so it can be sent in the header
	sendconst(hdr1aa); sendconst(secret); sendconst(hdr1ab);
	send0s(itoa(pages,pnbuf)); sendconst(hdr1b); 	// Now Send the HTTP Response first part
	sendconst(Inst1); 	// Now Send the instructions
	sendconst(Inst2); 	// Now Finish the instructions
	sendconst(gform); 	// Now Send the rest of the page
	sendconst(olduinolink);
	sendconst(trlr); 	// Now Send the trailer
	printf("IP: %d.%d.%d.%d,secret %s\n",
		thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],secret);
}
void sendip(unsigned char * ip){
	char strbuf[4];
	send0s(itoa(ip[0],strbuf));
	sendlit(".");
	send0s(itoa(ip[1],strbuf));
	sendlit(".");
	send0s(itoa(ip[2],strbuf));
	sendlit(".");
	send0s(itoa(ip[3],strbuf));
}
void congratulate(){
	sendconst("Congrats!<p> ");
}
void sendrespform(int pico, int fermi){
	int sendrc,i;
	pages++;
	if (fermi==3){//if we're going to need a new secret
		setsecret();//set it now
	}
	sendconst(hdr1aa); sendconst(secret); sendconst(hdr1ab);
	send0s(itoa(pages,pnbuf)); sendconst(hdr1b); 	// Now Send the HTTP Response first part
	sendconst("Your Guess Was: "); 	// Now Send the response
	send0s((char *)guess);
	sendlit("  I say ");
	if (fermi==3){
		sendlit("YOU GOT IT!<P>");
		congratulate();
		sendconst(pform);
		sendconst(olduinolink);
		sendconst(trlr); 	// Now Send the rest of the page
	} else {//once we're here we're going to send the guess form
		if (pico<0){//flag for duplicate digits
			sendlit("<P>Oh, maybe I didn't tell you.  No two digits are the same.<P>");
		} else if ((pico+fermi)==0){
			sendlit("Cookies!");
		} else{
			for(i=0;i<pico;i++) sendlit("Pico ");
			for(i=0;i<fermi;i++) sendlit("Fermi ");
		}
		sendlit("<P>");
		sendconst(gform); 	// Now Send the guess form
		sendconst(Reminder);//send the reminder
		sendconst(Inst2);
		sendconst(olduinolink);
		sendconst(trlr); 	// Now Send the rest of the page
	}
}
unsigned int cpyguess(unsigned char *to,unsigned char *from){//copy the guess until space or length limit
	unsigned int len=0;
	while((len<3) && *from!=' ' &&*from!='+' &&*from!='%'){
		*to++=*from++;
		len++;
	}
	*to=0;
	return len;
}
void getsecret(unsigned int rsize){
	unsigned char* secretcookie;
	printf("buffer at %x, rsize %d, buf[rsize]=%cx\n",buf,rsize,buf[rsize]);
	secretcookie=strstr(buf+rsize-64,(unsigned char*)"OS=");
	if (secretcookie){
		printf("\nHow about this? **%s**\n",secretcookie);
		memcpy(secret,secretcookie+3,3);
	}else{
		printf("secret cookie not found - substituting 999\n");
		strcpy((char *)secret,"999");
	}
	printf("done retrieving secret\n");
}
void bagelsturn(){
	int i, /*ipslot,*/ pico=0, fermi=0;
	if(strcmp(secret,"999")){//as long as we have an actual secret
		//ipturns[thisipslot]++; //count the turn
		printf("Guess: %d.%d.%d.%d, secret %s\n",
			thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],secret);
		cpyguess(guess,buf+8);
		for (i=0;i<3;i++){
			if (guess[i]==secret[i]){
				fermi++;
			}
		}
		if (guess[0]==secret[1]||guess[0]==secret[2]){pico++;}
		if (guess[1]==secret[0]||guess[1]==secret[2]){pico++;}
		if (guess[2]==secret[0]||guess[2]==secret[1]){pico++;}
		if (guess[0]==guess[1]||guess[0]==guess[2]||guess[1]==guess[2]||strlen((char *)guess)!=3){pico=-1;}
		sendrespform(pico, fermi);
	}else{
		printf("Uninitialized secret for IP %d.%d.%d.%d\n",
			thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3]);
		bagelsinit();
	}
}

void handlesession(){	//handle a session once it's established
	unsigned int rsize,strncmpval; unsigned int rxrd;
	unsigned int tries=10;
	rsize=wizGetCtl16(SnRX_RSR); //get the size of the received data
	rxrd = wizGetCtl16(SnRX_RD);
	while(rsize==0 && tries-->0){
		delay(20);
		printf("re-size ");
		rsize=wizGetCtl16(SnRX_RSR); //retry size of the received data
	}
	rxrd = wizGetCtl16(SnRX_RD);
	printf("**rxsz=%d\n",rsize);
	printf("**rxrd=%d\n",rxrd);
	if (rsize>0){
		thisip.l=getip();
		if (recv0(buf,min(748,rsize))>0){ //get enough characters to distinguish the request
			buf[min(748,rsize)]=0;//ensure it's terminated
			printf("%s\n",buf);
  			if (strncmp((char *)buf,"POST /",6)==0){
  				bagelsinit(); //initialize game, send the form
			}
			else if (strncmp((char *)buf,"GET /favicon",12)==0){
  				sendnak(); //no favicon here
			}
  			else if (strncmp((char *)buf,"GET /?G=",8)==0){
				getsecret(rsize);
				bagelsturn();	//give player his turn
			}
  			else if (strncmp((char *)buf,"GET /T",6)==0){
 				//bagelspeek(); //show the IP table
			}
  			else if (strncmp((char *)buf,"GET /",5)==0){
 				bagelsinit(0); //initialize game, send the form
			}
  			else{
				printf("\nmystery meat\n");
 				bagelsinit(0); //initialize game, send the form
			}
		}
	}
	printf("flushing %d\n",rsize);
  	if (rsize>0) flush(rsize);	//get rid of the received data
	rxrd = wizGetCtl16(SnRX_RD);
	printf("after recv, rxrd=%d\n",rxrd);
	wizCmd(CR_DISCON);// Disconnect the connection- we're done here
	printf("done\n>\n");
	sessions++;
}

void main(void){
	unsigned char socket0status;
    unsigned char ip_addr[] = {169,254,180,2};//{192,168,1,182};//{10,0,0,180};//
    unsigned int SFWs=0; //count of times we've seen SOCK_FIN_WAIT
    printf("Olduino 1802 Cookies Server 0.0\n");
	delay(500);
    wiz_Init(ip_addr); //initialize the wiznet chip
	while(1){  // Loop forever
		socket0status=wizGetCtl8(SnSR); //socket 0 status
		switch (socket0status){
			case SOCK_CLOSED: //initial condition
				SFWs=0;
				socket0_init();	//initialize socket 0
				break;
			case SOCK_ESTABLISHED: //someone wants to talk to the server
				SFWs=0;
				handlesession();
				printf("%d sessions, %d games, %d pages\n",sessions,games,pages);
				break;
			//following are cases where we have to reset and reopen the socket
			case SOCK_FIN_WAIT:
				printf("SOCK_FIN_WAIT:");
				if (++SFWs>2){
					printf(" lost patience, closing\n");
					wizCmd(CR_CLOSE);
				}else{
					printf(" ignoring\n");
				}
				break;
			case SOCK_CLOSING: case SOCK_TIME_WAIT:
			case SOCK_CLOSE_WAIT: case SOCK_LAST_ACK:
				SFWs=0;
				wizCmd(CR_CLOSE);
				break;
		}
		tbrnd(); //cycle the generator
		delay(100);
	}
}
#include <olduino.c>
#include <nstdlib.c>
#include <hspi2.c>
#include "w5500code.c"

I always want the last word so that wordpress doesn’t eat my code!

Advertisements
Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: