Monday, December 27, 2010

Delayed Push Notification Service for Windows Phone

This is a multi part blog post the other posts in this series are:

  • Overview – you’re reading this now.

  • The push notification service – how to implement the delayed push notification service

  • The client library – not yet written.

In this post I discuss push notifications, and share my web service which implements a delayed push notification.

Push notifications are a web service that MS provides that can send a message to the phone. There are 3 flavors of push notifications:

  1. Raw notification: Raw bytes - ignored if your app isn't running, sent to your app if it's running. 

  2. Tile notification: Manipulate application tile, worth it's own blog post. 

  3. Toast notifications: If your app isn't running pop up a toast giving a message to the user. 

An interesting detail about the push notification web service is how the web service is addressed, and how the phone is identified. I'd expected something like this: But instead you get the more elegant:



// doesn't work like this.
proxy = CreateProxy("http://well-known-push-notification-service")
proxy.push-toast(id-for-running-app-phone-taken-from-phone, "toast text")
proxy.push-toast(id-for-running-app-phone-taken-from-phone, "toast text2")
// works like this.
proxy = CreateProxy(id-for-running-app-phone-taken-from-phone-with-built-in-service-url)
proxy.push-toast("toast text") // no need to specify phone/app id
proxy.push-toast("toast text2") // no need to specify phone/app id
An annoying limitiation of push notifications is you can't schedule them at arbitrary times. From MSDN:

sendNotificationRequest.Headers.Add("X-NotificationClass", ""); 

// Possible batching interval values:
// 3: The message is delivered by the Push Notification Service immediately.
// 13: The message is delivered by the Push Notification Service within 450 seconds.
// 23: The message is delivered by the Push Notification Service within 900 seconds.
As a result I decided to write a simple web service which will allow you to send your app push notifications.  The sequence diagram for this service is:



The API for this web service is:

http://{root}/push-toast
POST a query string with the following parameters:
{'url:'push notification url',
'message':'text of the toast'
'seconds':'time delay in seconds'
}

RETURN: a json object with:
{
'key' : 'an opaque value used when requesting the toast be cancelled '
'secret' : 'an opaque value used when requesting the toast be cancelled '
}

http://{root}/cancel-push
POST a query string with the following parameters:
{
'key' : 'an opaque value returned from post'
'secret' : 'an opaque value returned from post'
}
RETURN: not defined
If you want to use this web service you can ping me and I'll give you access. In future blog posts I'll talk about the code for the web service as well as the client library.  Leave a comment if you'd like to use this service and we can talk about whatever features you require.

Tuesday, December 14, 2010

PsychicDebugging - You can browse to www.google.com, but you can't ping it.

In my last message to the world I though I'd fixed my networking, but turns out I hadn't. Here's the state of affairs:

  • IE; Chrome and Firefox work perfectly.

  • ping www.google.com fails instantly with hostname not found

  • nslookup www.google.com works

  • From python socket.gethostaddr ('www.google.com',80) fails instantly.

This had me stumped, I assumed the web browsers must be calling different apis from ping.  A few invocations of cdb, and I realized the apps that worked are all 32 bit. That was the trick, my 64 bit NSP catalog was also corrupted; and thus all 64bit apps couldn’t resolve networking.   Luckily from yesterdays shenanigans I had exported the catalog from the registry and re-loaded it. 

To look at the catalog use the powershell:

PS HKLM:\> dir HKLM:\SYSTEM\CurrentControlSet\services\WinSock2\Parameters\NameSpace_Catalog5\Catalog_Entries64 | % {$_.GetValue("LibraryPath")}
c:\Program Files (x86)\Microsoft Firewall Client 2004\FwcWsp64.dll
%SystemRoot%\system32\NLAapi.dll
%SystemRoot%\System32\mswsock.dll
%SystemRoot%\System32\winrnr.dll
%SystemRoot%\system32\napinsp.dll
%SystemRoot%\system32\pnrpnsp.dll
%SystemRoot%\system32\pnrpnsp.dll
%SystemRoot%\system32\wshbth.dll
C:\Program Files\Common Files\Microsoft Shared\Windows Live\WLIDNSP.DLL
C:\Program Files\Common Files\Microsoft Shared\Windows Live\WLIDNSP.DLL
In summary if someone asks how it’s possible the browser works but they can’t ping www.google.com – try checking their 64 bit NSP catalog.

Saturday, December 11, 2010

Set a System Restore Point

Start -> Create a  restore point -> Create

A few days ago my event viewer service failed to start, and I went on a crazy goose chase that was most unpleasant.  I suspect my life would have been better if I had a working restore point, so I'm sharing my wisdom with you: create a restore point now.

Some surprising things I found on my journey:

  • Eventlog not starting because The Authentication Service is Unknown (Error:1747) => Winsock Catalog corruption.
  • netsh winsock reset should fix said corruption but didn't in my case.
  • Rumor is when netsh winsock reset doesn't work and you need to get tcp to reinstall to rebuild the catalog.
  • Honestly I don't know how I got this fixed, but in theory a restore point would have fixed it.

Summary: Go set a restore point now :)

Tuesday, November 23, 2010

The Scunthorpe problem

I ran across an obscure reference to Scunthorpe problem and had to investigate what this “problem” is.  It turns out Scunthorpe is a town in England, as are the following towns which share the same problem: Penistone, Lightwater.

No idea? Perhaps this interview question will make it clear: Design a simple system that can determine if a user name is obscene.

Friday, October 15, 2010

Facebook, OpenID and Decrypting SSL

I was excited to see Facebook (FB) supporting login via OpenID (FB is a relying party), and I decided to give it a whirl. Here I list the results of my investigation, which describe the odd use of OpenID, as well as my wire level analysis which I hope you find informative. This post doesn't go into details of how OpenID works, if you're interested in that leave a comment and I'll put up such a post.

FB uses OpenID in a way I've never seen before. In the "common" OpenID login model, you get a login page that shows you some sort of login via OpenID buttons. When you go to the FB login page there is no login via OpenID.   This confused me, but I went to my FB account settings and linked my google account to my FB account. (Attempts to link my MyOpenID account failed with a strange error message). 

After some trial and error I realized that if I was logged into my Google account and went to the FB page than I'd automatically get logged into FB.

Debugging SSL via Charles

I needed to look at the network layer to see what is going on. Some of you may want to investigate this with tshark, but much of the traffic runs over SSL, since we don't have the server certs that's a dead end.  Instead you should fire up Charles, an http(s) debugging proxy.  Charles can sit in the middle of your SSL connections, and relay SSL traffic by presenting ‘untrusted certs’. Visually it looks like this:

 

image

 

How Facebook OpenID login works at a high level:

  1. Facebook login page contains javascript that tells your browser to login to Yahoo and Google via OpenID
  2. Your browser tries to connect to Yahoo and Google OpenID Endpoints, which post results back to FB
  3. If the login via OpenID succeeds, and you have a linked account you get logged into FB.
  4. If the request fails or there is no linked account you see nothing.

The Facebook OpenID approach:

If you’re logged into your Google account, and then go to the FB page, you’ll be automatically logged into FB, otherwise nothing happens. This is by design as mentioned here.

This is an interesting implementation of login via OpenID. It has the following advantages:

  • It’s immune to phishing
  • It requires no UI when you’re logged into your OpenID provider

At the same time there are some big disadvantages:

  • There is a privacy leak as all OpenID providers supported by FB now know you’re trying to access FB and at what frequency and from what client
  • FB needs to connect to every OpenID provider that exists on login.
  • FB doesn’t allow you to specify which account you want to use to login if you have multiple.
  • You can’t link your FB account to OpenID by logging into OpenID
  • It’s hard to discover login is failing because you aren’t signed into an OpenID account.

I look forward to seeing how FB moves this forward.

How Facebook OpenID login works at a protocol level:

This is probably too geeky for most but I find it interesting so I’ll share it. Comment if you’d like to see more of this sort of analysis.

The first column is the return code 200 is success, 302 is a redirect. Second column is the DNS name, and third column is truncated request.

1) Facebook login page contains javascript that tells your browser to login to Yahoo and Google via OpenID

// Connect to FB
200    GET    www.facebook.com   

// JavaScript connects to FB Analytics system telling them what we're trying.
200    GET    pixel.facebook.com    /ajax/openid/metrics.php?metric=requestSent&immediate=true&context=background_login&openid_url=http%3A%2F%2Fyahoo.com%2F&asyncSignal=779    
200    GET    pixel.facebook.com    /ajax/openid/metrics.php?metric=requestSent&immediate=true&context=background_login&openid_url=http%3A%2F%2Fgmail.com&asyncSignal=9380    

// 2) Javascript tries to connect to Yahoo and Google OpenID Endpoints, which post results to: www.facebook.com/openeid/receiver

// 2.1) HTTPS connection to Google OpenID
302    GET    www.google.com    /accounts/o8/ud?openid.claimed_id=…

// 2.2) HTTPS connection to Yahoo OpenID
302    GET    open.login.yahooapis.com    /openid/op/auth?openid.claimed_id=…

// 2.3) Yahoo OpenID telling FB it failed.
302    GET    www.facebook.com    /openid/receiver.php?provider_id=1923581983856&openid.mode=setup_needed&…

// 2.4) Google OpenID telling FB it succeeded.
302    GET    www.facebook.com    /openid/receiver.php?provider_id=1010459756371&…openid.sig=…

// FB telling itself the Yahoo login failed (this gives FB the chance to add new paramters server side, which it does)
200    GET    www.facebook.com    /openid/receiver.php?provider_id=1923581983856&…

// FB recording stats that the yahoo login failed.
200    GET    pixel.facebook.com    /ajax/openid/metrics.php?metric=requestCanceled&immediate=true&context=background_login&openid_url=http%3A%2F%2Fyahoo.com…

// FB telling itself the Google Login Succeeded (this gives FB the chance to add new paramters server side, which it does)
200    GET    www.facebook.com    /openid/receiver.php?provider_id=1010459756371 …

// 3. If the login via OpenID succeeds, and you have a linked account you get logged into FB. (FB Logging in)
302    POST    www.facebook.com    /login.php    
200    GET    www.facebook.com    /     

The actual request if you’re feeling really geeky:

// More Details 2.1) HTTPS connection to Google OpenID

openid.claimed_id    http://specs.openid.net/auth/2.0/identifier_select
openid.ext0.mode    fetch_request
openid.ext0.required    email,first_name,last_name,country,language,dob
openid.ext0.type.country    http://axschema.org/contact/country/home
openid.ext0.type.dob    http://axschema.org/birthDate
openid.ext0.type.email    http://axschema.org/contact/email
openid.ext0.type.first_name    http://axschema.org/namePerson/first
openid.ext0.type.language    http://axschema.org/pref/language
openid.ext0.type.last_name    http://axschema.org/namePerson/last
openid.identity    http://specs.openid.net/auth/2.0/identifier_select
openid.mode    checkid_immediate
openid.ns    http://specs.openid.net/auth/2.0
openid.ns.ext0    http://openid.net/srv/ax/1.0
openid.ns.oauth    http://specs.openid.net/extensions/oauth/1.0
openid.ns.ui    http://specs.openid.net/extensions/ui/1.0
openid.oauth.consumer    www.facebook.com
openid.oauth.scope    http://www.google.com/m8/feeds/contacts/
openid.realm    https://www.facebook.com/
openid.return_to    https://www.facebook.com/openid/receiver.php?provider_id=1010459756371&protocol=http&context=background_login&request_id=1
openid.ui.icon    true
openid.ui.mode    popup

// More Details 2.4) Google OpenID telling FB it succeeded.
provider_id    1010459756371
protocol    http
context    background_login
request_id    1
openid.ns    http://specs.openid.net/auth/2.0
openid.mode    id_res
openid.op_endpoint    https://www.google.com/accounts/o8/ud
openid.response_nonce    2010-10-14T06:07:08ZPY_ztwISRBSIzA
openid.return_to    https://www.facebook.com/openid/receiver.php?provider_id=1010459756371&protocol=http&context=background_login&request_id=1
openid.assoc_handle    ...
openid.signed    op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,ns.ext1,ns.ext2,ext1.mode,ext1.type.first_name,ext1.value.first_name,ext1.type.email,ext1.value.email,ext1.type.language,ext1.value.language,ext1.type.last_name,ext1.value.last_name,ext2.scope,ext2.request_token
openid.sig    oCFFt53s1j4rD7kGOE8x1ycZMfU=
openid.identity    https://www.google.com/accounts/o8/id?id=...
openid.claimed_id    https://www.google.com/accounts/o8/id?id=...
openid.ns.ext1    http://openid.net/srv/ax/1.0
openid.ext1.mode    fetch_response
openid.ext1.type.first_name    http://axschema.org/namePerson/first
openid.ext1.value.first_name    Igor
openid.ext1.type.email    http://axschema.org/contact/email
openid.ext1.value.email    ...
openid.ext1.type.language    http://axschema.org/pref/language
openid.ext1.value.language    en
openid.ext1.type.last_name    http://axschema.org/namePerson/last
openid.ext1.value.last_name    ...
openid.ns.ext2    http://specs.openid.net/extensions/oauth/1.0
openid.ext2.scope    http://www.google.com/m8/feeds/contacts/
openid.ext2.request_token    4/bv0-X57lya6ESvrvYG3pQiAGuSzn
openid.ns.ext3    http://specs.openid.net/extensions/ui/1.0
openid.ext3.mode    popup

Friday, October 8, 2010

Taskmgr to PowerShell: Kill A Process

You know the drill, some app freezes. You try to close it, that doesn’t work.  You fire up taskmgr, sort by name, type in the name, and then hit the delete key to kill the process.  Easy with powershell:

Get-Process | ? {$_.Name -eq "firefox"} | kill


The real power here is the composability of powershell, for example you could also debug the process.



Get-Process | ? {$_.Name -eq "firefox"} | Debug-Process


Or if you’re not sure what’s going to happen you can always pass the –whatif qualifier:



PS C:\Users\igord\Downloads> Get-Process | ? {$_.Name -eq "firefox"} | Debug-Process -whatif
What if: Performing operation "Debug-Process" on Target "firefox (8204)"


Happy Powershelling

Wednesday, October 6, 2010

Photo Fusing in Windows Live Photo Gallery

On my camera, I can make the self timer take 8 photos 2 seconds apart. This leaves me with a 1 in 8  chance of getting a good picture. However, using the photo fuse tool in Windows Live Photogallery, I can do a visual merge of the photos to pick my favorite part of each photo.  For example, I used the following source photos:

IMG_1349IMG_1352IMG_1353IMG_1354

IMG_1355IMG_1356IMG_1357

To created this composite photo, (To which I also applied straiten and auto fix)

StaceyDaveAndFamFuse

That’s seriously cool!

Saturday, September 18, 2010

PowerShell: Tab Complete History

If you haven’t tried PowerTab or haven’t tried PowerTab recently stop, and check it out .  PowerTab makes powershell a real pleasure to use, and might be the feature that gets me over the powershell dip.

Once you’ve installed PowerTab , you can history complete by starting your command with a #, typing some letters and then hitting tab. For example:

powertabHistoryCompletion

Saturday, August 14, 2010

Batch to Powershell: Title

If you try to set the title of the window in powershell, 'title' won't work; but you can set the title using the $host object
 PS C:\> $Host.UI.RawUI.WindowTitle = "Donkey" 


You can also mess with other fun things like colors eg:

PS C:\>  $Host.UI.RawUI.BackgroundColor = [ConsoleColor]::Red 


If you're a big title user and have title burned into your muscle memory you can always create function called title e.g.:

 PS C:\Temp> function title ($t) {$host.UI.RawUI.WindowTitle=$t }
PS C:\Temp> title Donkey2


Have fun moving into powershell.

Wednesday, August 4, 2010

Powershell: Returning ScriptBlocks and Closures

Powershell lets you return scriptblocks (aka anonymous functions) from functions, for example:
function generateAdder ($n)
{
{
param ($x)
$n+$x
}
}


 
PS C:\> $add4 = generateAdder 4
PS C:\> & $add4 7
7

If you've used languages with closures you'd have expected to get back a function that adds 4 to a passed in value. You'd expect that because $n=4 was in lexical scope when we created the scriptblock to be returned. Recall powershell is dynamically scoped so we don't need to bind ($n) to a lexcial location, instead we can pick up $n from our current environment:

PS C:\> $n = "Hello"
PS C:\> & $add4 7
Hello7

What if you want to instantiate a closure with powershell instead? Luckily someone thought of that and you can do:

function generateAdder2 ($n)
{
{
param ($x)
$n+$x
}.GetNewClosure()
}


PS C:\> $add4 = generateAdder2 4
PS C:\> & $add4 7
11


Leave a comment if you use these features, I’m curious what you’d use them for.

Sunday, August 1, 2010

Batch to Powershell: dir /s

I often go looking for a file in batch, aka

c:\Program Files (x86)>dir /s fsi*
Volume in drive C has no label.
Volume Serial Number is 8EDE-D64E

Directory of c:\Program Files (x86)\Microsoft F#\v4.0

03/19/2010 02:02 PM 230,216 Fsi.exe
09/30/2009 08:08 PM 158 Fsi.exe.config
2 File(s) 230,374 bytes

Unfortunately this doesn't 'just work' in powershell. A quick search on the internet shows in powershell 'dir /s' becomes 'dir -r', but the following doesn't work either:

PS C:\Program Files (x86)> dir -r fsi*
PS C:\Program Files (x86)>

What's going on? Let's check the help:

PS C:\Program Files (x86)> help dir

NAME
Get-ChildItem

SYNOPSIS
Gets the items and child items in one or more specified locations.


SYNTAX
Get-ChildItem [[-Path] ] [[-Filter] ] [-Exclude ] [-For
ce] [-Include ] [-Name] [-Recurse] [-UseTransaction] []

Get-ChildItem [-LiteralPath] [[-Filter] ] [-Exclude ]
[-Force] [-Include ] [-Name] [-Recurse] [-UseTransaction] []


Ahh, path and filter are two separate arguments in powershell. This means we get 2 choices.



A) Pass a path to the dir command (pass ‘.’ for current directory):



PS C:\Program Files (x86)> dir . fsi* -r

Directory: C:\Program Files (x86)\Microsoft F#\v4.0


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/19/2010 2:02 PM 230216 Fsi.exe
-a--- 9/30/2009 8:08 PM 158 Fsi.exe.config
PS C:\Program Files (x86)>

B) Explicitly pass the -filter argument name (which we can abbreviate to -fi)

PS C:\Program Files (x86)> dir -r -fi fsi*


Directory: C:\Program Files (x86)\Microsoft F#\v4.0


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/19/2010 2:02 PM 230216 Fsi.exe
-a--- 9/30/2009 8:08 PM 158 Fsi.exe.config
PS C:\Program Files (x86)>


OK you’re thinking who cares, I can still do dir /s faster in batch? Ah, but you can’t do this:



 PS C:\Program Files (x86)> dir -r -fi fsi* | Where-Object {$_.Length -gt 200KB}


Directory: C:\Program Files (x86)\Microsoft F#\v4.0


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/19/2010 2:02 PM 230216 Fsi.exe
PS C:\Program Files (x86)>


Have fun, and remember read the docs when things don’t work as you’d expecxt:)

Saturday, July 17, 2010

F# Tricks: Concise BoolToInt

As a C# programmer, you'd probably write BoolToInt as follows in F#:
	let boolToInt3 b = 
if b = true then
1
elif b = false
0


Not a big win over C# yet, but a few things to point out about the concise syntax: Indentation implies blocks. There is no 'return' keyword. This function is statically typed, you don't see types since the compiler infers the types.



As a novice F# programmer a trick you learn is pattern matching. Pattern matching is like a case statement, but it's an expression and it's really powerful. I'll go into more details on pattern matching in future posts. For now, notice the elegance of the below:



	let boolToInt2 b = 
match b with
| true -> 1
| false -> 0

Pattern matching is so useful to F# there is a syntax for creating a function of one parameter that only pattern matches, using that syntax, we define boolToInt in the final form of:

	let boolToInt3 = function 
| true -> 1
| false -> 0

Thursday, June 10, 2010

F# Tricks: Collecting Some Optional Values

Skip this post if you have no idea what F# is, and think LINQ is stupid.

When you map inputs to outputs with a function that can return None, or Some Output, you get an annoying issue of having a list containing Nones, and Somes. For example:

// Contains both encrypted and un-encrypted files: 
let listOfFiles:Seq

// Return a decrypted File, or None if the file can't be decrypted.
let decryptFile file
match file with
| isFileDecryptable file -> Some DecryptFile file
| _ -> None

// decryptedFiles contains Nones, intertwined with some Some DecryptedFiles
let decryptedFiles = listOfFiles |> Seq.map decryptFile
Here are two ways to remedy that:

// To get a list of only decrypted files you could do:
let realDecryptedFiles = decryptedFiles |> Seq.filter Option.isSome |> Seq.map Option.get

// However, I prefer the more concise version of:
let realDecryptedFiles = decryptedFiles |> Seq.collect Option.toList
If you're an F# guru I'd love to hear your thoughts on this.

Friday, May 21, 2010

The power of shift clicking – Copy As Path

 

copyaspath

Have you ever been in explorer and needed to get the path of a file?

Turns out that feature is built into Windows 7! If you shift right click on a file, the menu contains a copy as path link.  Sweet!

Saturday, May 15, 2010

Making the FSharp interpreter (fsi) run in 64bit!

I love F#!  It’s all the power of functional programming with the training wheels of c# when you need it. All the prototype/REPL power of powershell and python, with the built in test cases I like to call static typing. All the power of static typing, with out the hassle of specifying types every where thanks to implicit typing.

But, there is one annoying problem, I crunch very large data sets and fsi.exe runs as a 32 bit application. This means when I load my data sets and iterate through them FSI takes up 1.6 GB of RAM (maximum for a 32bit app), and spends 100% of its time in GC.

An aside for you who are thinking: 1.6GB is enough RAM!? – Igor must be a really crappy programmer!!! 1.6GB could be plenty, but why should I spend time adding complexity to my code when I have a superb memory manager, a 64 bit address space and a 3 year old dev machine that has 16GB of RAM, and 8 processors?  Today, I get no value from optimizing my data set for memory utilization -- I’m better off making my data cruncher a 64 bit application.

Back to work.  First, lets open up the Visual Studio command prompt (it has the tools we need in its path) and find fsi.exe.

C:\Program Files (x86)\Microsoft F#\v4.0>dir fsi.*
Volume in drive C has no label.
Volume Serial Number is 8EDE-D64E

Directory of C:\Program Files (x86)\Microsoft F#\v4.0

03/19/2010 02:02 PM 230,216 Fsi.exe
09/30/2009 08:08 PM 158 Fsi.exe.config
2 File(s) 230,374 bytes

Lets make sure it's 32 bit - we do that with a tool called corflags.

C:\Program Files (x86)\Microsoft F#\v4.0>corflags fsi.exe
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 4.0.30319.1
Copyright (c) Microsoft Corporation. All rights reserved.

Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 11
ILONLY : 1
32BIT : 1
Signed : 1

Next we use corflags to make fsi 64bit. This will unsign the fsi.exe but this hasn't affected anything for me yet.

 

C:\Program Files (x86)\Microsoft F#\v4.0>corflags /32bit- /force fsi.exe
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 4.0.30319.1
Copyright (c) Microsoft Corporation. All rights reserved.

corflags : warning CF011 : The specified file is strong name signed. Using /For
ce will invalidate the signature of this image and will require the assembly to
be resigned.

Lets double check it worked:

 
C:\Program Files (x86)\Microsoft F#\v4.0>corflags fsi.exe
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 4.0.30319.1
Copyright (c) Microsoft Corporation. All rights reserved.

Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 9
ILONLY : 1
32BIT : 0
Signed : 1

Finally fire up F#, and it's now in 64 bit - yippee!

Friday, May 7, 2010

The joy's of batch - Delayed Expansion

 

If you can, skip batch and move strait to powershell. If you don't believe me, maybe this blog post will change your mind. In batch %ErrorLevel% is how you know if the last command succeeded:

C:\>echo %ERRORLEVEL%
0

It turns out if you set a variable that doesn't exist, this sets error code to 1. So

C:\>set DONKEYRIDING
Environment variable DONKEYRIDING not defined

C:\>echo %ERRORLEVEL%
1

Makes sense, batch isn't that bad you think. Now here's a pop quiz - What will you get when you run this batch file?

C:\>type foo.bat
if NOT "BATCH"=="OBVIOUS" (

echo %ERRORLEVEL%
set DONKEYRIDING
echo %ERRORLEVEL%

)

I"ll run it for you:

C:\>foo.bat

C:\>if NOT "BATCH" == "OBVIOUS" (
echo 0
set DONKEYRIDING
echo 0
)
0
Environment variable DONKEYRIDING not defined
0
C:\>

Not what you thunk huh? Maybe error level wasn't set - lets check

C:\>echo %ERRORLEVEL%
1

What the heck happened? Well, the if block is a statement, and variable substation happened when the statement was read.  You don't want this behavior and you’re probably not interested in the details, luckily batch has something called delayed variable expansion, which you expose using !ERRORLEVEL! so you need to change your batch file to be:

setlocal ENABLEDELAYEDEXPANSION
if NOT "BATCH"=="OBVIOUS" (
echo !ERRORLEVEL!
set DONKEYRIDING
echo !ERRORLEVEL!
)

Which gives the expected output:

C:\>foo2.bat

C:\>setlocal ENABLEDELAYEDEXPANSION

C:\>if NOT "BATCH" == "OBVIOUS" (
echo !ERRORLEVEL!
set DONKEYRIDING
echo !ERRORLEVEL!
)
0
Environment variable DONKEYRIDING not defined
1
C:\>

As I said, skip batch and go strait to powershell!

Saturday, February 27, 2010

How to determine the server certificate given an https wcf proxy?

One day when you are using https transports in WCF you might try to figure out what certificate the server is using. That is going to be the day you're glad you found my blog.
 
namespace WebClient
{
using System;
using System.IdentityModel.Tokens;
using System.ServiceModel;
using System.ServiceModel.Channels;

///
/// Interface implemented by a random https bound web service I found on the web.
///

[ServiceContract (Namespace="http://arcweb.esri.com/v2")]
interface IVersion
{
[OperationContract]
string getVersion();
}

class Program
{
static void Main(string[] args)
{
var httpsBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpointAddress = new EndpointAddress("https://arcweb.esri.com/services/v2/AccountInfo");
var serviceClient = new ChannelFactory<IVersion>(httpsBinding, endpointAddress).CreateChannel();

// Instantiating an OperationContextScope populates the OperationContext.Current property
using (OperationContextScope scope = new OperationContextScope(serviceClient as IContextChannel))
{
serviceClient.getVersion();
// Certificate not available until after an API call.
var myCertificate = (OperationContext.Current.IncomingMessageProperties.Security.TransportToken.SecurityToken as X509SecurityToken).Certificate;
Console.WriteLine(myCertificate);
}
}
}
}


I expect most folks will skip this post, but if you end up needing this tip leave a comment with what you’re up to -  I’m curious!

Saturday, February 13, 2010

PowerShell, PowerTab and Certificates

Powershell is good, but with an awesome tab completer Powershell is great.  Luckily said awesome Tab completer exists, and it is called PowerTab.  Powertab didn't work quite right in the certificate provider (and some other places) so I made some fixes and threw them up on CodePlex. A small demo below:

 powertab_in_cert_completer

Saturday, February 6, 2010

Better Certificate Management in Powershell via CertificateHelper

If you’ve read my previous post here, you know powershell can do some basic certificate management via the certificate provider. However, the certificate provider has some limitations. The certificate provider can’t create,delete,copy or import/export certificates.

This annoyed me so I’m creating a powershell module called CertificateHelper that will provide these missing features.

So far the module implements:

  • New-Certificate
  • Remove-Certificate 

CertHelper can be found on codeplex.

You install it like this:

(You must have hg installed)
PS C:\>cd $home\Documents\WindowsPowerShell\Modules
PS C:\Users\igord\Documents\WindowsPowerShell\Modules> hg clone https://hg01.codeplex.com/certificatehelper
destination directory: certificatehelper
requesting all changes
adding changesets
adding manifests
adding file changes
added 5 changesets with 8 changes to 4 files
updating to branch default
4 files updated, 0 files merged, 0 files removed, 0 files unresolved


Once installed, you can make it available in your powershell session like this:



PS C:\> Import-Module CertificateHelper


You can see the implemented commands like this:



PS C:\> dir function:\*-Certificate

CommandType Name Definition
----------- ---- ----------
Function New-Certificate param([parameter(Mandatory=$true)]...
Function Remove-Certificate param($certificatePath)...


A walk through of using the module is:



PS C:\> dir cert:\LocalMachine\My | ? {$_.Subject -like "*Dog*"}
PS C:\> New-Certificate cert:\LocalMachine\My DogFood
Succeeded
PS C:\> dir cert:\LocalMachine\My | ? {$_.Subject -like "*Dog*"}


Directory: Microsoft.PowerShell.Security\Certificate::LocalMachine\My


Thumbprint Subject
---------- -------
A229E9FF2AA9DC55D06A35D0BBB0D0A98FEAC1A3 CN=DogFood


PS C:\> Remove-Certificate cert:\LocalMachine\My\A229E9FF2AA9DC55D06A35D0BBB0D0A98FEAC1A3
PS C:\> dir cert:\LocalMachine\My | ? {$_.Subject -like "*Dog*"}
PS C:\>


This is a work in progress, so holler if you hit any issues, or want to prioritize the order in which I provide the features.

Saturday, January 30, 2010

Using tshark to find the man in the middle

This post is targeted at people that understand ip addresses, default gateways and have heard of arp, but don’t play with them often enough to realize how vulnerable we are to man in the middle attacks.

Back in the old days, the network hardware was often a hub, and hubs had a property that all the computers connected to a hub could see each others traffic.  This meant if my computer and tori-the-lori were on the same hub tori-the-lori could see all my network traffic. This sound like weak security.  In time the world invented switches, and now almost all networking uses switches. Switches differ from hubs in that computers only see traffic that is sent to them, not everyone's traffic.  This difference should fix the weak security right?   Well, as with most things security the devil is in the details. Lets dig in.

When a computer wants to talk another computer by IP address, it needs to find the MAC address for the IP address, this is done via ARP.  Lets have a look at my home network.

Background info:
    My machine is @  192.168.1.101
    Tori-The-Lori is another machine in my network @ 192.168.1.100
    My default gateway is @ 192.168.1.1

 C:\Users\igord>ipconfig | findstr 192.168.1.1 
IPv4 Address. . . . . . . . . . . : 192.168.1.101
Default Gateway . . . . . . . . . : 192.168.1.1

C:\Users\igord>ping -4 tori-the-lori
Pinging tori-the-lori [192.168.1.100] with 32 bytes of data:


Q: How does my machine know where to find 192.168.1.100?

A: 192.168.1.100 has a MAC address  - MAC addresses are stored in the arp table, lets look at the ARP table:



PS C:\> arp -a | findstr 192.168.1.100 
192.168.1.100 00-22-5f-7e-f5-79 dynamic


Q: Can I erase that entry from  ARP table?


A:  Yup



PS C:\> arp -d 192.168.1.100
PS C:\> arp -a | findstr 192.168.1.100


Q: If I delete the ARP entry how will my machine find 192.168.1.100 again?

A: Lets watch arp traffic in tshark :)



PS C:\Program Files (x86)\Wireshark> .\tshark -i 4 -R "arp"
Capturing on Microsoft
7.202265 IntelCor_2f:5a:22 -> Broadcast ARP Who has 192.168.1.100? Tell 192.168.1.101
7.207136 LiteonTe_7e:f5:79 -> IntelCor_2f:5a:22 ARP 192.168.1.100 is at 00:22:5f:7e:f5:79


Q: How does my machine get a packet to bing?


A: My machine uses DNS to get the IP address, then my machine uses the default gateway (192.168.1.1) to send the packet to bing(69.31.112.153).



Pinging a134.g.akamai.net [69.31.112.153] with 32 bytes of data:
Reply from 69.31.112.153: bytes=32 time=50ms TTL=54


Back in Wireshark:



PS C:\Program Files (x86)\Wireshark> .\tshark -i 4 -R "icmp" -T fields -e eth.src -e eth.dst -e ip.src -e ip.dst
Capturing on Microsoft
00:21:6a:2f:5a:22 00:08:54:87:86:9c 192.168.1.101 69.31.112.153
00:08:54:87:86:9c 00:21:6a:2f:5a:22 69.31.112.153 192.168.1.101


Notice the packet to and from bing's IP address is my default gateway:



PS C:\Program Files (x86)\Wireshark> arp -a  | findstr 192.168.1.1
Interface: 192.168.1.101 --- 0xe
192.168.1.1 00-08-54-87-86-9c dynamic


Q: Can someone evil say  they are 192.168.1.1?



A: Yup. I can transform my happy linux laptop, via these commands into an evil man in the middle:



#enable routing 
vmplanet@ubuntu-vm:~$ sudo sysctl -w net.ipv4.ip_forward=1

# tell 101 I’m really the default gateway.
vmplanet@ubuntu-vm:~$ sudo arpspoof -t 192.168.1.101 192.168.1.1 > /dev/null

#tell the default gateway I’m really 101.
vmplanet@ubuntu-vm:~$ sudo arpspoof -t 192.168.1.1 192.168.1.101 > /dev/null


Q: What do I see on my windows box?



A: I wouldn’t be looking, but if you were you’d see this:



PS C:\Program Files (x86)\Wireshark> .\tshark -i 4 -R "arp or icmp"
Capturing on Microsoft
0.697050 IntelCor_2f:5a:22 -> IntelCor_2f:5a:22 ARP 192.168.1.1 is at 00:21:6a:2f:5a:22
1.997779 192.168.1.103 -> 192.168.1.101 ICMP Redirect (Redirect for host)
2.698765 IntelCor_2f:5a:22 -> IntelCor_2f:5a:22 ARP 192.168.1.1 is at 00:21:6a:2f:5a:22
3.022153 192.168.1.103 -> 192.168.1.101 ICMP Redirect (Redirect for host)
3.584377 192.168.1.103 -> 192.168.1.101 ICMP Redirect (Redirect for host)
4.699856 IntelCor_2f:5a:22 -> IntelCor_2f:5a:22 ARP 192.168.1.1 is at 00:21:6a:2f:5a:22
4.765403 192.168.1.103 -> 192.168.1.101 ICMP Redirect (Redirect for host)
6.445970 192.168.1.103 -> 192.168.1.101 ICMP Redirect (Redirect for host)
6.555464 192.168.1.103 -> 192.168.1.1 ICMP Redirect (Redirect for host)
6.653009 192.168.1.103 -> 192.168.1.101 ICMP Redirect (Redirect for host)


Or maybe this:



PS C:\Program Files (x86)\Wireshark> arp -a | findstr 192.168.1.1
Interface: 192.168.1.101 --- 0xe
192.168.1.1 00-21-6a-2f-5a-22 dynamic
192.168.1.100 00-22-5f-7e-f5-79 dynamic
192.168.1.103 00-21-6a-2f-5a-22 dynamic


What the heck?  192.168.1.103 has now hijacked my ARP entry for the default gateway (compare to what 192.168.1.1 was above)



Unfortunately, when I ping bing.com things still look right:



Pinging a134.g.akamai.net [69.31.112.82] with 32 bytes of data:
Reply from 69.31.112.82: bytes=32 time=37ms TTL=54

PS C:\Program Files (x86)\Wireshark> .\tshark -i 4 -R "icmp"
Capturing on Microsoft
5.758262 192.168.1.101 -> 69.31.112.82 ICMP Echo (ping) request
5.794958 69.31.112.82 -> 192.168.1.101 ICMP Echo (ping) reply
6.760151 192.168.1.101 -> 69.31.112.82 ICMP Echo (ping) request
11.304182 192.168.1.101 -> 69.31.112.82 ICMP Echo (ping) request
16.304111 192.168.1.101 -> 69.31.112.82 ICMP Echo (ping) request


But when we look closely, like at the  the MAC addresses – we realize all are packets go the man in the middle :(



PS C:\Program Files (x86)\Wireshark> .\tshark -i 4 -R "icmp" -T fields -e eth.src -e eth.dst -e ip.src -e ip.dst -e icmp
Capturing on Microsoft
00:21:6a:2f:5a:22 00:08:54:87:86:9c 192.168.1.101 69.31.112.106 icmp
00:08:54:87:86:9c 00:21:6a:2f:5a:22 69.31.112.106 192.168.1.101 icmp
00:21:6a:2f:5a:22 00:08:54:87:86:9c 192.168.1.101 69.31.112.106 icmp
00:08:54:87:86:9c 00:21:6a:2f:5a:22 69.31.112.106 192.168.1.101 icmp
00:21:6a:2f:5a:22 00:21:6a:2f:5a:22 192.168.1.101 69.31.112.106 icmp


Now that I’ve shown you how easy it is to become a man in the middle you should be thinking about what you are doing so the man in the middle can’t see you.

Saturday, January 23, 2010

The whitespace and indentation debate

Nothing annoys me more than having to argue over whitespace and indentation. Where should we stick the braces? Spaces vs Tabs? Can't we find something more useful to argue over?

Long ago I read the only to end the pointless whitespace debate, is to have the compiler reject random whitespace. I thought that was a very good idea, and today I'll talk about it.

In the beginning whitespace didn't matter, it was there for the human, and the program ignored it.   But that caused an annoying problem - you ended up needing tokens like '{' ';' and '(' and then you needed to argue about how you arranged the code around those tokens.  For example:

ProcessIncomingDogs(List<Dog> dogs)
{
...
if (dogs>1)
{
RunAway(smallDogs,speed.Fast);
Log("SmallDogs Ran Away Fast");
}
Log("EveryOne Ran Away that needed to");
...
}


I'm happy to say we're making progress, python gets rid of the annoying braces and instead denotes blocks via whitespace instead.  As a result many python programmers feel that python looks like sudo code. For Example:



def ProcessIncomingDogs(dogs):
...
if dogs>1:
RunAway(smallDogs,speed.Fast)
Log("SmallDogs Ran Away Fast")
Log("EveryOne Ran Away that needed to");
...


This is good, but there is annoying problem with python. Which whitespace will you use? Spaces or Tabs? Since whitespace implies meaning, mixing spaces and tabs makes real python bugs.



I was very excited to discover that F#, which also relies heavily on whitespace, does not allow tabs for whitespace. I'm thrilled!  Finally the whitespace debate is over in F#. Hopefully more languages will follow F#'s lead. For example:



let ProcessIncomingDogs dogs =
...
if dogs > 1 then
RunAway smallDogs speed.Fast
Log "SmallDogs Ran Away Fast"
Log "EveryOne Ran Away that needed to"
...


By the way, if you are lucky enough to use C#, an excellent way to enforce consistent coding is StyleCop. It has a sane set of rules and decent tooling.  For example the StyleCop for Resharper plugin can perform as you type auto-correction to fit the StyleCop coding convention.



May you never have to waste your life arguing over whitespace and indentation again!

Friday, January 15, 2010

Salting your hash, chasing rainbows and cracking passwords

Henry Ford takes 3 of his division presidents out for diner to decide which of them will be the new CEO. As soon as they start eating Mr. Ford chooses Bob, the man to his left, to be the new CEO. The other division presidents are shocked, and ask why Bob was picked over them. Henry replies: Bob was the only man who tasted his food before salting it.

Unlike at dinner time, hashes should always be salted. A hash is a one way function that maps something, for this discussion a password, to a short string. The point of a hash is if you're given the hash, you can't figure out the password. A common scenario for hashes is checking users passwords. Instead of storing a users passowrd and checking the passwords match, you store the hash of the users password, and make sure a hash of the users password matches the hash you stored. The advantage of storing the hash is if someone steals your disk they don't get your user's passwords.

There's a rub though. What happens if two users have the same password? Then both passwords will have the same hash. Uhoh, now we know if multiple users have the same password. This is bad! Now lets say I'm a bad guy, and compute the hashes for all the common passwords ahead of time? This is called a dictionary attack. You might be asking yourself if a dictionary attack is feasible - how much space is needed to store said dictionary? A hash is usually 20 bytes, to make the math easier lets assume it's only 16 (2^4) bytes. Lets make a dictionary for an 8 digit password; each character can be uppercase, lowercase or a numbers (26+26+10) = 62 (~ 2^6) choices per character x 8 (2^3) characters.

= (2^6)^8 * 2^4 bytes

= 2^48 passwords * 2^4 bytes storage for hash.

= 2^52 bytes for storage

~ 4,000 TB for storage.

This is really big, and clearly not feasible. Fortunately for attackers there is a trick you can play called a rainbow table. A rainbow table is a time space trade off algorithm were you can do a lot of upfront computation to cut down on the amount of required storage in the dictionary. This technique is very effective. The rainbow table for Vista for the 8 character password I describe in this blog post is only 153 GB and you can buy it here.

To defend against rainbow table attacks, we add salt. Before hashing the password, you prepend some random bytes, which we call salt. Then we store the salt alongside the hash. To verify the users password you prepend the users password with the stored salt, do the hash, and check for a match.

This rainbow table attack can be used against my windows box – watch this:

1) create a user account with password dogfood.

c:\temp>net user sillyuser dogfood /add
The command completed successfully.

2) Dump password hash using tool called fgdump.

c:\temp>c:\bin_drop\fgdump.exe >junk

c:\temp>findstr sillyuser *
127.0.0.1.pwdump:sillyuser:1006:NO PASSWORD*********************:72B76234CCC8E047C6D12F2E391F5DF7:::


3) Lookup hashes for your password on a helpful website: http://lmcrack.com/index.php



ASCII : dogfood 
Hex. : 646f67666f6f64
LM : 655C44FFBF761281AAD3B435B51404EE
NTLM :72B76234CCC8E047C6D12F2E391F5DF7
MD5 : 5D6423C4CCDDCBBDF0FCFAF9234A72D0
MD4 : E840B5EB9173B2E137AB14A98742A285
SHA1 : 67A2C23E0AEF5D92CBE084E22AA8E9A9311322C8
SHA256 : E674F78D2BF55FD93C878F7FE14448ABE677E7C3160F820AC6A012E457520B81


4) Clean up



c:\temp>net user /delete sillyuser
c:\temp>del *.*
The command completed successfully.

Thursday, January 14, 2010

How do you thumbprint a certificate?

You often use thumbprints to find certificates, but what is the thumbprint?  The thumbprint is the hash of the certificate. In the case of the CLR’s X509Certificate2 class, the thumbprint is the SHA1 hash of the certificate. If you want to compute the thumbprint of a certificate yourself it’s pretty simple:

 
function get-CertThumbprint ($cert)
{
$sha = new-object System.Security.Cryptography.SHA1CNG
$hashOfRawBytesOfCertificate = $sha.ComputeHash($cert.RawData)
( $hashOfRawBytesOfCertificate| % {"{0:X}" -f $_} ) -join ""
}


 
PS cert:\LocaLMachine\My> dir


Directory: Microsoft.PowerShell.Security\Certificate::LocaLMachine\My


Thumbprint Subject
---------- -------
3BCA8A25A071300BD177E4C73135E54FA830039A CN=STS
08766D8B3DCDE5D633ED06AB1CB4DF4CCAECA533 CN=localhost

PS cert:\LocalMachine\My> $cert = get-item 08766D8B3DCDE5D633ED06AB1CB4DF4CCAECA533
PS cert:\LocalMachine\My> $cert.Thumbprint
08766D8B3DCDE5D633ED06AB1CB4DF4CCAECA533
PS cert:\LocalMachine\My> get-CertThumbprint $cert
8766D8B3DCDE5D633ED6AB1CB4DF4CCAECA533


If you’re wondering why you don’t use the subject name to identify a certificate, it’s because you can have lots of certificates with the same subject name.

Sunday, January 10, 2010

A better blog editor - Windows Live Writer

I've been complaining about the blogger online blog editor forever. Today I took Windows Live Writer out for a spin and I like it. It's free, and it works with blogger without a hitch. My favorite feature is its preview pane which shows an actual preview of your post in the blog, which the blogger editor doesn't do at all.

In my heart of hearts I’ve always believe a rich client application should be more powerful then web applications, and in this case it is.

Saturday, January 9, 2010

Keyboard shortcuts in Windows WYSIWG editors

I have a day job, and in that job I use Word, OneNote and Outlook.  For style I only use bold, italics, underline, headings 1-3 and lists. For some reason, I never learned the keyboard shortcuts for some of these, and thus I need the mouse to apply these styles. In case you suffer like me, here’s are the shortcuts that will set your mouse free.

Style Word OneNote
Heading 1 C-A-1 C-A-1
Heading N C-A-N C-A-N
Bulleted List C-S-L C-.
Numbered list ? C-/
Underline C-U C-U
Italics C-I C-I
Bold C-B C-B

Powershell is dynamically scoped, and that will confuse you.

Lets start with an example, as the concept of dynamic scoping is a big string for most programmers.

Python Program
x = 5
def printX():
print x
def setAndprintX():
x=7
printX()
printX()
setAndPrintX()
printX()





Output From Python




5
5
5

Powershell Program




$x = 5
function printX() { echo $x }
function setAndprintX()
{
$x=7
printX
}
printX
setAndprintX
printX





Output From Powershell




5
7
5








What is this dynamic scoping?


Most programs use static, also called lexical, scoping because it's easy to understand. You figure out what is in scope by looking at the source code. In the python example, the only value of x in scope is the global value of x.







By contrast, powershell uses dynamic scoping, in this model, you lookup up variables at runtime based on a scope stack. Each time you call a function you create a new scope, and copy all values from the parent scope into it. In the powershell example, when printX is called from setAndprintX we get the value of $x that was set in setAndprintX scope.






Why would you want dynamic scoping?



I can't come up with a good explanation of why you'd pick dynamic scoping over lexical scoping. My hunch was this is historical as it's how batch files and shell scripts work. Interestingly, Perl supports both dynamic scoping and lexical scoping. You can read a good article about it here. My synopsis of why Perl has dynamically scoped variables from the article:









  • In the beginning perl only supported global variables and that was painful.


  • Since it was cheap to implement Perl authors added the ability to created dynamically scoped variables (via:local keyword).


  • However, when perl authors got time, they added lexical scoped variables (via: my keyword).


  • Now people are told *not* to use dynamically scoped variables since they're weird.






Do you get other language features to make support for this easier:


Yes, you can write to a different scope explicitly:




$x = 3 # Write to local scope
$global:x = 3 # Write to global scope.





You can also execute your function without creating a new scope via '.' aka sourcing:




. func() # runs the function in local scope, and variables created are visible.





This is a fascinating , but why are we having this conversation?


Because I wanted to write:




function getPrintDogFunction()
{
function Nested1(){echo"dog"}
function Nested2(){Nested1}
Get-Command Nested2
}

$printDog = getPrintDogFunction
# Call PrintDog
& $printDog

# This Call fails saying can't find Nested1 - which makes sense it's not in scope.

So, I changed my code to the following:




# source getPrintDogFunction, which causes Nested1 and Nested2 to be created in my scope, and thus
# Nested1 is in scope when I call printDog

$printDog = . getPrintDogFunction
# Call $printDog
& $printDog





A few days pass, and I add a new feature, printCat. Via the power of cut and paste our code becomes:




function getPrintDogFunction()
{
function Nested1(){echo "dog"}
function Nested2(){Nested1}
Get-Command Nested2
}

function getPrintCatFunction()
{
function Nested1(){echo "cat"}
function Nested2(){Nested1}
Get-Command Nested2
}

$printDog = . getPrintDogFunction
$printCat = . getPrintCatFunction

# Print all the animals
& $printDog
& $printCat

# Grrr - this is printing cat cat.
# Worse yet depending on the order of these calls, the behavior changes.





To fix I use the following pattern:




function getPrintCatFunction()
{
function Nested2()
{
function Nested1(){echo "cat"}
Nested1
}
Get-Command Nested2
}

$printDog = getPrintDogFunction




And now the world makes sense.



Notes:


(1) If you've never heard of dynamic scoping, any language you pick will act the same.

Sunday, January 3, 2010

Syntax Highlighting Take 2

Readers of my blog complained that they can't see the code I was syntax highlighted in RSS readers like Google reader. The reason is moderately interesting so I'll explain it:



HTML likes to gobble up white space, so if you're pasting in source code you use the PRE (preformatted text) tag. PRE shows up in fixed width font and preserves spacing; however you can't place < or > in PRE tags since they denote HTML tags, instead you need to use &lt or &gt (character entity references if you speak techno babble). This is annoying, especially given the blogger text editor gets confused when you edit PRE tags with nested < and > signs.



Speaking of < and >, source code, and HTML files, you often want to write javascript in an HTML file, and you then hit the similar problem that you want to write x < 99, but can't since '<' isn't valid HTML. To solve this problem you can put a CDATA(*) section in your script blocks, which allows you to use write x < 99.



So, to avoid specifying > and <, the author of SyntaxHighlight came up with a clever solution: Put the source code you'd like to display in a non-javascript script block using CDATA, and then use javascript on the page to convert these blocks to tables for the web browser to display.



This works great in your web browser, but RSS readers don't run the javascript that converts the dummy script blocks into tables and thus you can't see the code.  



Luckily, SyntaxHighlighter also supports an usage where you use specify your code in PRE blocks.  It's more annoying for me (for the above reasons), but it's better for my beloved readers since the PRE elements 'degrade' to non syntax highlighted pre blocks (instead of nothing) when in an RSS reader.





For all my RSS reader friends here is the snippet from the last blog post:



Console.Writeline("Hello World!")
Python:

print "Hello World"




NOTES:



(*) CDATA,aka: character data, is not interpreted as part of the document.  In CDATA,  >  means greater than and does not have to be written as character entity references. Unfortunately CDATA can not be placed in PRE blocks.