The Frame Stomper

searchengine intentionally obscured Everyone who uses frames to build up his website is familiar with this: you design a nice frameset, searchengines crawl your site for content and interested people are pointed to your content:

One single frame is displayed.

The rest of the frameset is blown into oblivion. For some people: no big deal. They litter there links and URL's throughout all of their files. Easy when making the pages, but terrible for maintenance. So I use three frames as standard:

And it's not good when only a single frame from a frameset is loaded. So I had a URL at the bottom of each page so that the visitor could reload the frameset. But that always lead to undesired situations since the index.html file only had space for the main.html file of that subject. A cumbersome situation.
In the meantime I did see some JavaScript routines to enforce the loading of the omitted frames, but JavaScript is an interpreted language so it is not the method of my choice. I don't want to slow down the server too much.

So my Holy Task was to create the Frame Stomper!

How it's done

Below are the top 12 lines from this HTML document:

<!doctype html>
<html lang="en">

 <head>
  <script>
   <!--
    if (parent.location.href == self.location.href)
     { window.location.href =
	'http://verhoeven272.nl/cgi-bin/FS?fruttenboel%2Fcgi&CGI+with+Mocka&cgitop&framestomper&cgicontent'
     }
   // -->
  </script>
   
The piece of JavaScript checks if the frame is part of a frameset, or loaded itself in the parent frame. If so, it starts the executable called

http://verhoeven272.nl/cgi-bin/FS

Behind the filename is a question mark. This is a separator for the parameter string:

"fruttenboel%2Fcgi&CGI+with+Mocka&cgitop&cgimain&cgicontent"

The parameter string is 'escaped' since the webserver cannot pass certain symbols that trigger specific actions at some clients. So the strange looking tokens are in the parameter string: This all results in the following set of parameters being passed to the executable:
  1. fruttenboel/cgi
  2. CGI with Mocka
  3. cgitop
  4. cgimain
  5. cgicontent
The executable does the following:
  1. The relative path for the target files is constructed by sandwiching the first parameter between two slashes (in this case: '/fruttenboel/cgi/')
  2. The title for the frameset is determined
  3. The name of the topframe is constructed by:
    1. prepending the relative pathname to 'cgitop'
    2. appending '.html' to the previous filename
    resulting in '/fruttenboel/cgi/cgitop.html'
  4. Repeat this for 'cgimain'
  5. Repeat this for 'cgicontent'
  6. Use all variable elements to construct a custom 'index.html' file

Source of the Frame Stomper.

Below is the sourcecode for FS.mod (the actual name of the Frame Stomper). It's not a big file. The interesting sections are GetChar, GetWord and ConvertHex. The rest is rather straightforward.
The parameters are retrieved from the Query_String CGI environment variable as if it was some kind of command line. The procedure 'CreateHTML' creates the HTML code for the file 'index.html' that sets up the frameset and fills it with the right topframe, content frame and subject frame.

MODULE FS2;

(*  FrameStomper new style with improved root file handling.	March 10, 2007		*)
(*  Changed it after the attack of 02-01-2008.			January 12, 2008	*)

IMPORT ASCII, InOut, cgi, NumConv, Strings;


VAR	content, path, Title		: Strings.String;
	Frame				: ARRAY [0..7] OF Strings.String;
	ok, Exhausted, error		: BOOLEAN;
	i, j				: CARDINAL;
	ch				: CHAR;


PROCEDURE ConvertHex;

VAR	  str	     : ARRAY [0..1] OF CHAR;
	  num	     : CARDINAL;

BEGIN
   str [0] := content [i];
   str [1] := content [i+1];
   INC (i, 2);
   NumConv.Str2Num (num, 16, str, ok);
   IF  NOT ok  THEN
      InOut.WriteString ("Error in number : ");
      InOut.WriteString (str);
      InOut.WriteLn;
      InOut.WriteBf;
      HALT
   END;
   ch := CHR (num)
END ConvertHex;


PROCEDURE GetChar;

BEGIN
   ch := content [i];
   INC (i);
   IF  ch = '+'  THEN
      ch := ' '
   ELSIF  ch = '%'  THEN
      ConvertHex
   ELSIF  (ch = 0C) OR (i = HIGH (content))  THEN
      Exhausted := TRUE
   END
END GetChar;


PROCEDURE GetWord (VAR  str : Strings.String);

BEGIN
   j := 0;
   LOOP
      GetChar;
      IF  Exhausted OR (ch = '&')  THEN  EXIT  END;
      str [j] := ch;
      INC (j)
   END;
   str [j] := 0C
END GetWord;


PROCEDURE ProcessGet;

VAR   str 	: Strings.String;
      index	: CARDINAL;

BEGIN
   index := 0;
   REPEAT
      GetWord (str);
      IF  index = 0  THEN
         path := '/';
	 IF  str [0] # '.'  THEN
	    Strings.Append (path, str);
	    Strings.Append (path, '/')
	 END
      ELSIF  index = 1  THEN
         Title := str
      ELSE
         Frame [index - 2] := str
      END;
      INC (index);
      IF  (Condition removed)  THEN  RETURN  END
   UNTIL Exhausted;
END ProcessGet;


PROCEDURE CreateHTML;

BEGIN
   InOut.WriteString ('<html><head><title>');
   InOut.WriteString (Title);
   InOut.WriteString ('</title></head>');
   InOut.WriteLn;
   InOut.WriteString ('<frameset rows="60, *" border="0">');
   InOut.WriteLn;
   InOut.WriteString ('<frame src="');
   InOut.WriteString (path);
   InOut.WriteString (Frame [0]);
   InOut.WriteString ('.html" scrolling="no" noresize="yes">');
   InOut.WriteLn;
   InOut.WriteString ('<frameset cols="*, 16%" border="0">');
   InOut.WriteLn;
   InOut.WriteString ('<frame src="');
   InOut.WriteString (path);
   InOut.WriteString (Frame [1]);
   InOut.WriteString ('.html" name="main" marginheight="0" marginwidth="0">');
   InOut.WriteLn;
   InOut.WriteString ('<frame src="');
   InOut.WriteString (path);
   InOut.WriteString (Frame [2]);
   InOut.WriteString ('.html" name="sidebar">');
   InOut.WriteLn;
   InOut.WriteString ('</frameset></frameset></html>')
END CreateHTML;


PROCEDURE Init;

BEGIN
   Exhausted := FALSE;
   error := FALSE;
   Title := "Framestomper at work";
   FOR  i := 0  TO  7  DO  Frame [i] := "main.html"  END;
   i := 0
END Init;


BEGIN
   Init;
   cgi.InformServer (cgi.Html);
   IF  cgi.GetEnvVar (Condition removed)  = FALSE  THEN  error := TRUE  END;
   IF  Strings.StrEq (Condition removed)  = FALSE  THEN  error := TRUE  END;
   IF  cgi.GetEnvVar (Condition removed)  = FALSE  THEN  error := TRUE  END;
   IF  cgi.GetEnvVar (Condition removed)  = FALSE  THEN  error := TRUE  END;
   IF  error = FALSE  THEN
      ProcessGet;
      CreateHTML
   ELSE
      InOut.WriteString ("<html><head><title>Framestomper Abused</title></head>");
      InOut.WriteString ("<body><center><h1>You have abused the FrameStomper!<p></h1></center>");
      InOut.WriteString ("<p>Where would you like to go?<p>");
      InOut.WriteString ("        ");
      InOut.WriteString ('<a href="http://www.verhoeven272.nl">Visit the family site</a><p>');
      InOut.WriteString ("        ");
      InOut.WriteString ('<a href="http://fruttenboel.verhoeven272.nl">Visit the technical site</a><p>'); 
      InOut.WriteString ("        ");
      InOut.WriteString ('<a href="http://www.politie.nl">Prison</a><p>');
      InOut.WriteString ('<a href="mailto:jan@verhoeven272.nl?subject=framestomper perils">Mail me about the framestomper</a>');
      InOut.WriteString ('<p><hr><p>Copyright Jan Verhoeven, 2008'); 
      InOut.WriteString ("</body></html>")
   END;
   InOut.WriteLn;
   InOut.WriteBf
END FS2.
   
As you can see, the program makes checks on a lot of items. And although I am a believer in open source software I took the liberty to hide the security measures in 'Condition removed' phrases in order to make sure my webhost will run errorfree.
It wouldn't be impossible to tinker as long as is needed to make the security foolproof (better: exWarsawpact country proof) but then the size of the executable would run in the hundreds of kilobytes and it would be faster to use a perl or python script. So I chose for obscuring the safeties.

This kind of program needs to be failsafe. If an error condition occurs it must lead to a controlled state of the executable. The program runs on an unattended webserver so there is no one to check for a runaway program with ps aux followed by a kill -9.
I don't just get a string from the environment. I first check if that strings is available. And if it isn't, the program is set into an error condition before it starts creating nonsense data thereby possibly crashing (or at least slowing down) the webserver. In this context: check out the section called 'CGI loop'.
The Croatians tried to break the old framestomper. They failed, but it caused me to increase the security a few steps up. Silly Yugo's.

Making it work.

For details about how to develop CGI executables I refer to the webpage 'Debugging CGI' which is also in this section. In short, this is the bottomline:

  1. Compile your source
  2. Copy the executable to /usr/lib/cgi-bin/ (on my Debian system, as root)
  3. Make the file executable with 'chown root:root FS'
  4. Construct the Javascript lines in the target files
  5. If it all works:
  6. Cross your fingers...
This ought to do the tricks...

In the download section is the source for FS.mod as a tar.gz file. I have also included the related FS executable to store and run on your own server or hosted webspace. Please do not use the executable in my webspace. It won't run on your site.

A dedicated Google Stomper..

The FrameStomper 'as-is' will only serve HTML files in a frameset. But I also have several gzipped files that I want served. And the FrameStomper will not do this. So I made a dedicated FrameStomper-gz. It is identical to the FrameStomper which is published above. Only the 'PROCEDURE CreateHTML' is slightly different, as you can see here:

PROCEDURE CreateHTML;

BEGIN
   WriteString ('<html><head><title>');                         WriteString (Title);
   WriteString ('</title></head>');                             WriteLn;
   WriteString ('<frameset rows="60, *" border="0">');          WriteLn;
   WriteString ('<frame src="');
   WriteString (path);                                          WriteString (Frame [0]);
   WriteString ('.html" scrolling="no" noresize="yes">');       WriteLn;
   WriteString ('<frameset cols="*, 16%" border="0">');         WriteLn;
   WriteString ('<frame src="');
   WriteString (path);                                          WriteString (Frame [1]);
   WriteString ('.html.gz" name="main" marginheight="0" marginwidth="0">');
   WriteLn;
   WriteString ('<frame src="');
   WriteString (path);                                          WriteString (Frame [2]);
   WriteString ('.html" name="sidebar">');                      WriteLn;
   WriteString ('</frameset></frameset></html>');               WriteLn;
   InOut.WriteBf
END CreateHTML;
   
Only the line just above the single 'WriteLn' command has been changed. This new version of the FrameStomper has been running several weeks now and operates without any problems on the machine of my webhost.

You want to have one too.

This version of the frame Stomper only works if you use a frameset like mine. The text in red must be passed to the CGI program by the client.

<html> 
  <head> 
    <title>Your title</title> 
  </head>
  <frameset rows="60, *" border="0">
    <frame src="Frame1.html" scrolling="no" noresize="yes">
    <frameset cols="*, 16%" border="0">
      <frame src="Frame2.html" name="main" marginheight="0" marginwidth="0"> 
      <frame src="Frame3.html" name="sidebar">
    </frameset>
  </frameset>
</html>
   
So, if this is the case, you can just download the free version of the FrameStomper in the file FrameStomper.tar.gz which is available in the download section. In all other cases there are two ways to obtain a dedicated Frame Stomper for your style:
  1. Change the sources of FS.mod and recompile (as described above)
  2. Buy a dedicated copy from me. I charge € 10 or the equivalent in your currency for each copy I sell. Order now
If you have your FrameStomper, put it in the cgi-bin of your webserver, make it executable and add the Javascript code fragment in the HEAD section of your HTML documents.

One thing: the Frame Stomper only works on webservers running Linux!

Also: your amount of webtraffic will go up since the accompanying frames are now loaded as well. What happens is:
  1. Active frame is loaded
  2. FrameStomper kicks in and serves a custom 'index.html' file
  3. Topframe is loaded by client
  4. Content frame is loaded by client
  5. Requested frame is loaded by client
So if you're on a budget with your bandwidth, think twice. In most cases it won't be a burden.

Page created 8 April 2006

Page equipped with googleBuster technology