Wednesday, April 2, 2008

HP RDYMSG DISPLAY is fun

It was April 01 a few days ago, and I decided to pull a small prank on the office. Unfortunately, it was a little subtle(No one has commented yet), but it was fun to put the code together.

The prank is based on some old l0pht code hanging around on this web site: http://www.irongeek.com/i.php?page=security/hphack which sent some HP Printer Job Language code to a networked printer, changing the "READY" prompt to whatever you choose.

I guess the next logical step is to interrogate the Active Directory and automate the process ... or more evil would be to interrogate an IP range, since that would give you access to printers you cannot access via the AD. Evil.

The entire HP command set I found here: http://printers.necsam.com/public/printers/pclcodes/pcl5hp.htm

Guys, don't do this at work.

As always, blogger eats non breaking spaces, spaces and pretty much any type of indentation I can think of. [Edit and replace all spaces with  ]


using System;
//using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace HPPrnDisp
{
    class hp
    {
        const int HP_PJL_PORT = 9100;
        const string HP_RDYMSG_HEADER = "\u001B%-12345X@PJL RDYMSG DISPLAY = \"";
        const string HP_RDYMSG_FOOTER = "\"\r\n\u001B%-12345X\r\n";

        // Changes the READY display text for a range of HP Printers
        public static int Main(string[] args)
        {

            Console.WriteLine("HP Display Hack -- sili@l0pht.com 12/8/97");
            Console.WriteLine("-- Re-coded by Kent Bolton 02 APR 2008 --");


            // Ensure that there are only two arguments
            if ( args.Length != 2 ) {
                Console.WriteLine("Usage: printer \"message\"");
                Console.WriteLine("\tMessage can be up to 16 characters long (44 on 5si's)");
                return 1;
            }

            // Process the arguments
            String host = args[0];
            String message = args[1];

            // No more than 44 chars - not sure what happens if there are more
            if (message.Length > 44)
            {
                message = message.Substring(0, 44);
            }

            // Let the user know we know what is needed
            Console.WriteLine("Hostname: {0}", host);
            Console.WriteLine("Message : {0}", message);

            // Set up the IP Connections ...
            IPAddress addr = null;
            IPEndPoint endPoint = null;
            try
            {
                addr = Dns.GetHostEntry(host).AddressList[0];
                endPoint = new IPEndPoint(addr, HP_PJL_PORT);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknown host: " + e.ToString());
                return 1;
            }

            // Format strings
            StringBuilder textOut = new StringBuilder(100);
            textOut.Append(HP_RDYMSG_HEADER);
            textOut.Append (message);
            textOut.Append(HP_RDYMSG_FOOTER);

            // Now everything is set up, push the string through as an ASCII byte stream
            Console.WriteLine("Connecting ...");
            try
            {
                // Prepare basic socket and encoding stuff
                Socket sock = null;
                System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

                // Open socket and pump out the bits
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                sock.Connect(endPoint);
                sock.Send(encoding.GetBytes(textOut.ToString()));
                sock.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("IO error: " + e.ToString());
                return 1;
            }

            // Happy message and bail with no error code
            Console.WriteLine("Sent {0} bytes", textOut.Length);
            return 0;


        }
    }
}


Monday, March 31, 2008

VS2008 Express - Launching nUnit on F5

Here is the How To:

Add these two lines into the section of your project .csproj.user file.

<StartAction>Program</StartAction>

<StartProgram>C:\DEV TOOLS\Program Files\NUnit 2.4.3\bin\nunit.exe</StartProgram>


Pressing F5 will launch NUnit now and start debugging.


Thursday, March 27, 2008

Web development 2008 ! We've come a long way ! Now where’s Notepad ?

Darren, our framework developer who plays with all the new cool tech had a play with VS2008 web development, and the results were not very pretty.

Today I had a simple task; Make a small application to demonstrate the new features in Visual Studio 2008 for Webform development. I had attended the Brisbane "Heroes Happen 2008" Microsoft launch event the previous day and thought "A quick 20 minute job". Hmmmm...

The Agenda...

  1. The impressive UpdatePanel
  2. Intellisense in JavaScript editing
  3. Debugging JavaScript in Visual Studio

I should say at this point that I am a dyed-in-the-wool Winforms developer although I have developed a fair few websites in my time. I've built web apps (mostly modestly sized affairs) using everything from Visual Interdev (remember that?), Notepad, and all .NET Visual Studio editions. I've done it but I have to say I don't like it. Why? If I'm brutally honest I hate the pace of development, the fact the tools aren't properly integrated and browsers that can't agree how to render anything useful; These are just my top 3 hates.

Anyway, yesterday I was truly impressed with the demos and found myself thinking maybe web development could be fun at last...

How wrong I was.

I had about 90% of the demo complete well within my initial estimate and then proceeded to put the breakpoint on the JavaScript code in the page and entered a world of pain. I use Firefox as my main browser and, naturally, VS 2008 JavaScript debugging doesn't work there. Okay, set IE 7 as my default browser for the web project, problem solved... well, no. Hmmm... Reboot? No. Repair install of Visual Studio? No. Another reboot? No. Go moan to someone else about how s@#t web development is? Worked like a charm.

Somebody please explain how bringing someone over to your PC and showing them how something doesn't work can suddenly kick it into life. I say this, but it didn't work the *first* time, just the second and every time since.

Its things like this that give me killer headaches and make me want to go back to the nice cosy world of framework development...

Aside from a nice preview of the 'design' and the (admittedly) cool CSS stuff there aren't a lot of compelling reasons to spend a large wad of cash rather than just use notepad. I mean, if I'm going to get stressed and generally harassed by technology that isn't reliable, doesn't work and then magically cooperates I may as well start in the position of expecting things to be tough and just use everyone's favourite free web development tool.

PS I don’t really mean that we should be using notepad because it would be really frustrating. The issue is that Visual Studio 2008 is so close, but so frustratingly far from being a great web development environment.

PPS I love everything else about Visual Studio.

Wednesday, March 26, 2008

FXCop and string culture

Rules are great, unless blindly followed.

I thought I would just send this out as a reminder. While everyone is eager to do right by FxCop we should not forget to use the Invariant Culture where appropriate.

FxCop will complain about any string formatting without an explicit culture. If you are formatting for internal use and not for display you really should consider using CultureInfo.InvariantCulture. This is particularly important if you plan to consume the string with software. Using the current culture would mean the string potentially could not be consumed by another server/PC. For example, if you used BCMax in Dubai it would have a different culture to the StrataMax server running here.

So if it is an internal string not for display or to be interpreted by software anywhere consider doing this:

receiptNumber.ToString("#", CultureInfo.InvariantCulture);

Instead of this:

receiptNumber.ToString(CultureInfo.CurrentCulture);


Thursday, March 6, 2008

Exception handling in ASP.Net with Anthem and Web Services

Dorian, our CIO, has been busily hammering the VB.NET based shopping cart selected for a large client into shape.

Anthem.NET is a free, cross-browser AJAX toolkit for the ASP.NET development environment that works with both ASP.NET 1.1 and 2.0. http://anthem-dot-net.sourceforge.net/

Please ignore lack of indenting ... blogger keeps eating my directives. Kent Bolton

Before Anthem it was enough to put an error handler in the Global.asax on the Application_Error event to log the error and use the tag in the web.config to redirect errors to a common error page.

Anthem complicates things.

In Anthem it is possible to have Ajax call backs to the web page, controls or even custom methods decorated. Exceptions could be raised on the methods called or even on methods not directly called by these methods such as load events for controls that Anthem causes to load dynamically.

If an error occurs during an Anthem Callback the exception will not be caught by Application_Error and won't follow instructions. On the server side the only thing you can do to catch an Anthem Error is catch it on the page using a Page_Error method. This method will also catch regular page exceptions thus preventing Application_Error from firing. It may look something like this:

Private Sub Page_Error(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Error
Response.Redirect("http://somewhere/")
End Sub

In order to avoid putting this method on every page it must be placed on the base page class for the entire website. There is a small downside to this which is a page can no longer define its own Page_Error method because control would be lost in the event execution chain when a Response.Redirect is started (it aborts the thread). This is however a necessary evil as there doesn’t seem to be another way to catch Anthem exceptions.

Because we are on the topic of Anthem and error handling here is a couple more useful facts. You can catch errors in Anthem call backs on the client by adding a JavaScript method to pages calls Anthem_Error. If a server side error occurs in an Anthem callback any previously registered JavaScript to execute will still be executed. For example if you had an Anthem button which looked like this:

Protected Sub btnNext_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles btnNext.Click

SomeHiddenPanel.Visible = True

Anthem.Manager.AddScriptForClientSideEval("if (typeof document.someform != ""undefined"") { documentsomeform.submit(); }")

End Sub

And after this code executed a server side error occurred (perhaps in rendering SomeHiddenPanel) the JavaScript added would still be executed. It is important to write such JavaScript to be tolerate of an error occurring. The code above checks for the existence of the form which SomeHiddenPanel would make visible before it calls submit().

Web services are another nasty for exception handling. Basically, you need to try catch every web service method and handle all exceptions. The same error logging routine could be use by web services and page errors if it was tolerant of information that may not be present on web service requests. View state is one example. Don’t expect to find that in a web service.

Tuesday, February 26, 2008

Preventing Web Caching in ASP.NET

We are working on a shopping cart application in ASP.NET. that needed to prevent web caching. The code is in VB.NET, since the best practice source we bought was VB.NET based.

With the proliferation of browsers, and the variety of ways that they interpret directives, a number of strategies have to be employed.

Caching can occur in any of the following locations:
  • Any – cache anywhere including client, server and proxy server.
  • Client – cache in client browser.
  • Downstream – any cacheable devices but not the original server that participates in the request.
  • Server – cache on the web server
  • None – no cache
(Please ignore the lack of indenting ... blogger kept losing my directives

Public Shared Sub NoCachePage(ByVal currentPage As System.Web.UI.Page)

' This will turn off the cache for the above locations

currentPage.Response.Cache.SetCacheability (Web.HttpCacheability.NoCache)
currentPage.Response.Cache.SetNoStore()
currentPage.Response.Cache.SetExpires (DateTime.Now().AddDays(-366))
currentPage.Response.Cache.SetMaxAge (
New TimeSpan(0))
currentPage.Response.Cache.AppendCacheExtension (
"must-revalidate, proxy-revalidate")

' This is to explicitly add to header to avoid being cached in the client browser, without the above code the page might still be cached on the server but just not in the client. For example cache is set to Server and the following codes are also added

Dim Expires As New Web.UI.HtmlControls.HtmlMeta()
Expires.Name =
"Expires"
Expires.Content =
"0"
currentPage.Header.Controls.Add(Expires)

Dim CacheControl As New Web.UI.HtmlControls.HtmlMeta()
CacheControl.Name =
"Cache-Control"
CacheControl.Content =
"no-cache"
currentPage.Header.Controls.Add(CacheControl)

Dim Pragma As New Web.UI.HtmlControls.HtmlMeta()
Pragma.Name =
"Pragma"
Pragma.Content =
"no-cache"
currentPage.Header.Controls.Add(Pragma)

End Sub

Friday, February 22, 2008

Spawning a console application and tracking its Standard Output

Toby Cosham, our Accounting Guru, has the first tech oriented blog post to contribute, helping us achieve our milestone of at least a post a week. Thanks Toby.

Sometimes programs we write must interact with a console application that does not integrate easily into our framework. The application may be designed for use in a manual environment, but you need to automate it. Any errors produced may be displayed only on the screen via Standard Output.

This happened to me recently. My program was written using the .NET framework. It is pretty simple to run another program from the .NET framework. I used System.Diagnostics.Process to launch the other software (I’ll call it ABCD) using Process.Start, and waited for it to complete using Process.WaitForExit.

The purpose of ABCD is to send and receive data over the internet. It is a command driven program, with an interface similar to FTP. After testing its responses, it became apparent that it was unreliable. Not only could it return errors about failing to connect to its server, but also logon failure and timeouts. In addition, there were some more obscure, but regular, failures – corrupted stream and handshake failure. There was no way to predict these failures, but trying again immediately almost always worked.

The requirements for my program included executing ABCD many times each day, starting at 1am and finishing at 7pm. If it did not work, immediate action needed to be taken. At MaxSoft, we have a library that handles sending the SMS, so sending the message was not a problem. However, since the SMS was to our CIO, it was important to identify the problem and attempt to rectify it first.

ABCD does not return an error code. It does not log its results to a database, or a file. The only way it notifies its results is directly to the screen. In addition, when it downloads files as a batch, the batch can contain multiple files of the same name. The first file gets overwritten by the second file with the same name. This meant that I could not pipe the standard output of ABCD to a file and interpret it after it had executed – some files would have been lost by then.

The answer was to use some of the features of System.Diagnostics.Process. This class allows for the Standard Output (which normally is displayed on the screen) to be captured as it occurs. It also allows for Standard Error to be captured, and for Standard Input to be written to. I passed the commands to ABCD by writing to the StandardInput stream, allowing me to protect the user id and password.

I then set Process.EnableRaisingEvent to true and called Process.BeginOutputReadLine. This activates the OutputDataReceived event, which I set to call my OutputDataReceived function.

I set up an array of strings for the expected result. The output was the same all the time, except the list of files produced, which I added as a place-holder string to the expected result. Each time OutputDataReceived was called, I compared the text that had been added to StandardOutput to the next item in the expected results. If it matched, I removed that section from the expected results and exited the OutputDataReceived function. If it did not match, I recorded the error and exited the function. When Process.WaitForExit completes, this error information is checked to determine if the process has run correctly or not. The placeholder string is used to identify when the list of files is being processed. This is only removed from the expected result when the following expected result is received.

Having written this, I was getting all errors reported. I wanted to reduce the calls our CIO received early in the morning (So did he), so I processed the error message. If any of the three errors that could be retried occurred, I looped back to where ABCD was being set up and ran it again. Along with providing the maximum number of retries, this eliminated alerts being generated on transient errors. If any other error occurred, ABCD was not rerun, and the alert was sent straight away.

Issues that I had resolved include:

- calling a console application using System.Diagnostics.Process

Process p = new Process();
ProcessStartInfo si = new ProcessStartInfo(application);
p.StartInfo = si;
si.Arguments = arguments;
si.UseShellExecute = false;
p.Start();


- retrieving and interpreting text output to the screen

si.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceived);
p.BeginOutputReadLine();


-
timeouts and other retryable errors

void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null)
return;

// check e.Data for retryable errors and set flag indicating retry required
}


- preventing the console application for running forever by using WaitForExit with a time limit

if (!p.WaitForExit(Config.TimeoutSeconds * 1000))
{
p.Kill();
}
p.Close();