Hurray MythTV!

Posted by

I've been quiet lately, mostly because I've been spending a bit (read: a lot) of time settings up my media center PC at home. But now that it's almost complete, I'm really loving it! I've set it up to record every episode of The Simpsons, Futurama and South Park as they happen... any with 600-odd GB of storage, that's a lot of cartoons :)

One of my favourite components that I bought with PC (I'll get to my other favourites in a minute :p) is the Antec Fusion Black case. It looks like some piece of retro hifi equipment, with it's LCD screen and enormous volumne knob, but in fact, it's actually really hi-tech. This thing is so quiet that the only way I know it's on is whether the little light on the power button is lit-up (my wife's computer makes more noise from the next room!)

I also bought a Logitech DiNovo Edge keyboard, which looks very stylish and matches perfectly with the shiny black finish on my Samsung TV. It's quite expensive, but for the living room, I think worth it. The integrated mouse pad thingy is nice, too. I can use the mouse from a distance, if I need to...

... which I generally don't, thanks to my Logitech Harmony 1000i remote! This was a complex beast to set up, but it's great for the talking-point factor :) I'm a little (actually, a lot) dissapointed with the interface for programming it. The interface runs entirely off the Logitech website -- which I guess is because they don't want people stealing their database of remote codes for the 100's of 1000's of devices they've got -- but woe unto you if you want to program it without an internet connection!

And MythTV! What a wonderful piece of software! I was tossing up between MythTV and Windows Media Center 2007 (I am, after all, a bit of a Windows guy...) but in the end, I decided that I wasn't going to pay AU $200-odd for the Vista Home Premium edition (the cheapest one that comes with Media Center!) -- I mean, what were Microsoft thinking there? Why would I pay for the second-most-expensive edition of Vista if I was only ever going to use it to run one program!?!

But I'm definitely glad I went with the Linux + MythTV setup. I haven't used Linux since my uni days, but it's progressed quite well in terms of usability since then. And there's an enormous community of people around MythTV (so much so, in fact, that I found a page on their website which describes the exact, step-by-step, process for installing the exact model of tuner card I bought -- mind you, I did set out to choose that card specifically because it was well-supported in Linux...)

The only thing to mar the experience so far is that the LCD screen on my case is not supported in Linux. The word is that they're not "planning" on supporting it yet either. The good news, however, is that I managed to patch LIRC and LCDproc to get it to work (I love open source!) I plan to release my patches here, hopefully later this evening, once I tidy it all up, but hopefully that'll keep all those others guys on that forum happy as well :-) As proof, here's a (crappy, sorry) picture showing that I was watching "My name is Earl" last night:

My name is Earl

Anyway, I gotta go... I've got 15 episodes of The Simpsons to catch up on...

How to get the IP of the SMTP client

Posted by

Much of what I've learned about IIS SMTP sinks has been largely trial-and-error, or reverse engineering. I am quite sure that the only reason for the existence of the SMTP server in IIS was to support Exchange and little else -- a guess that is only reinforced by the fact that this is no SMTP server in IIS7 included with Windows Vista (I assume it'll make a comback in Longhorn Server, though!)

In fact, I could never run our application on the SMTP server included with Windows XP anyway, because the properties exposed on the objects in the SMTP sinks were not the same between XP and Server 2003!

Anyway, this brings us back to the question asked by James: How do you work out the IP address of the SMTP client in an event sink?

It seems like a simple question, but you'll not find any documentation which says how to do it -- beleive me, I looked! However, what does help is a technique I described a while ago, where you simply loop from 1 to 20,000 or so and look for all the properties described on the MailMsgPropertyBag objects passed to your sink. These objects are basically just a mapping of numbers to values (though nobody tells you what the numbers [or their values] mean, of course!).

Using the above technique, I was able to work out that the IP address of the client maps to property #6 on the "session" parameter that is passed in. I use the IP address in my own code for doing lookups against backlists and so on. So, my HELO/EHLO sink handler looks something like this:


public void OnSmtpInCommand(object server, object session,
      MailMsg message, ISmtpInCommandContext context)
{
    MailMsgPropertyBag Server = new MailMsgPropertyBag(server);
    MailMsgPropertyBag Session = new MailMsgPropertyBag(session);
    SmtpInCommandContext Context = new SmtpInCommandContext(context);

    string ip = Session.GetStringA(6); // 6 is the id of the property...
    WriteLog("Client IP is: {0}", ip);

    // spam check, etc..
}

Hope this helps anyone looking for the same answer as James! It's been a while since I've done a technical post like this, and it's good to be back :-)

How to render botnets ineffective for spam

Posted by

The biggest problem in terms of spam is botnets. Most spam originates from zombie computers and botnets. What can be done to stop it?

The real problem is that spammers don't always want you to do anything with the email they send other than read it. Penny stock trading is a big focus of the spammers these days. They don't require that you do anything but buy the stock the spammer mentions. So the spammer doesn't really care what the message contains, as long as it mentions the name of the penny stock company they want you to buy shares in.

What this means is that if DKIM becomes ubiquitous, then spammers will no longer be able to put an address they control in the "From" address of an email. They're going to have to rely on their zombie's smarthost to send the email, and that means putting the "From" address of whoever owns the zombie computer in the email. The spammer doesn't really care, though, as long as the message contents are there. The same would generally be true if the spammer were trying to sell cheap meds. As long as the link to their website is there, they don't care what the "From" address is.

So what can we do about it? I mentioned a little while ago about Challenge-Response anti-spam. Some people pointed out to me that C-R is rather ineffective at the moment due to the fact that the challenge often ends up going to someone who never even sent the email in the first place -- essentially, the C-R is creating it's own spam! However, with validating email you can be pretty sure that the challenge will actually go to the person who sent the email. They may not have sent the email intentionally (if it was a spam) but they did send it.

Now, the bot could come up with a random user name, but with the same domain, but I don't think this would be common. Besides, most ISPs require you to authenticate your outbound email with a username/password, and that's generally already tied to a specific "From" address.

It's not going to stop botnets, but it should become quite obvious when you've been infected. You'll start getting lots and lots of email challenges to emails you didn't send!

There are lots of issues with challenge-response anti-spam at the moment, but I believe that validating email should remove many of those issues. I might have been a sensationalist in the title of this post, but I really do think that validating email (that is, DKIM) will have a big impact.

The problem with DateTime.

Posted by

The .NET Base Class Library team have a couple of posts on their blog, about the new DateTimeOffset class in the BCL (title "A Brief History of DateTime" and "A Brief History of DateTime Follow-up").

In those posts, they go to some lengths to describe the design descisions around the DateTime structure as well as the reasoning for the new DateTimeOffset structure (by the way, you should read the name of the new type as "Date, Time, Offset" -- as in, it contains a date, a time and an offset). Now, I've done quite a lot of work with DateTime in the past, and a lot of that work has revolved around presenting time-based information to users in different timezones from a central server. And I will be the first to admit that DateTimeOffset is a welcome addition (too bad I have to wait for version 3.5 of the framework!)

But some people seem to have trouble using DateTime in the manner for which it was intended. In my personal opinion, the only thing "wrong" with the implementation of DateTime as it stands is the fact that there are two methods for getting the "current" time: DateTime.Now and DateTime.UtcNow. If they'd simply not included the DateTime.Now property, I'm quite sure that most of the trouble people have with the class would be a thing of the past.

You see, in our application, DateTime.Now is basically a "banned" property. If ever you see DateTime.Now in the code, it is usually a bug. Essentially, all work we do with DateTimes is done in UTC and only at the very last minute are those dates converted to local time for display (usually not via ToLocalTime, simply because the person displaying the DateTime is hardly ever in the same timezone as the server on which the application is running).

DateTimeOffset solves some of the problems we have at the moment were we have to be quite careful when accepting DateTime values from users, because we have to ensure that they're in UTC before we do anything with them. But other than that particular scenario, the DateTime object is more than capable of doing everything that people seem to be having trouble with.

On relying too much on your tools, and a warning about BackgroundWorker.ReportProgress

Posted by

There's two parts to this post, both related by the fact that they both contributed to a bug in my code that's been around for a few weeks that I was unable to figure out until only today.

The first, and perhaps most obvious problem, was something I was not aware of about the BackgroundWorker.ReportProgress (and its corresponding BackgroundWorker.ProgressChanged event. You see, if an exception is thrown inside your ProgressChanged event handler, you'll take out your whole UI thread with a "0xC0000005" error code.

Now, this problem was hard for me to track down for a couple of reasons (the main one being that my brain apparently wasn't switched on...). First of all, the stack trace of the crash just looks it all happened in native code (which is a danger of native code in general -- it hides quite a lot). Second-of-all, I knew the error happened on the call to BackgroundWorker.ReportProgress, but I never thought to put a breakpoint in my ProgressChanged event handler, because I thought -- incoreectly -- that a simple "F10" should have been enough.

Of course it wasn't, and the reason is quite obvious in retrospect. The call to ReportProgress happens in the background thread, but ProgressChanged is fired in the UI thread. The debugger can't track such cross-thread calls (especially since there's a managed/native boundry in between).

Anyway, I finally figured out that the problem was that the type of the object I was passing in as my UserState was different to what the ProgressChanged event handler was expecting and it was throwing an InvalidCastException when I tried to cast it.

So that brings me on to the other part of this post: relying on your tools too much.

You see, I use ReSharper almost religiously and I believe it's background-compilation feature is God's gift to programmers.

One of the problems that it picks up is, if you add a local variable to a method that conflicts with (i.e. has the same name as) a member variable, it flags it as a warning. This is not technically a bug, of course, but it can lead to problems later... as we'll see ;)

Usually, when that happens, I'll just rename the variable to something else, or rethink my design a bit, because it can be a hassle later when you're trying to figure out "is this supposed to refer to the local variable or the member variable?" Some people come up with fancy naming conventions to get around it (like prefixing member variables with an underscore) but I don't like that, personally. It, to me, is just hiding a bigger design problem.

Now, that's the most common case -- adding a local with the same name as a member -- but what happens in the less-common case, where you add a new member variable which conflicts with a local variable somewhere else in your source?

I'll tell you what happens. ReSharper flags the local variable as a conflict. Now, I think it makes sense from a technical perspective -- the background compiler compiles the whole file in the background and it only knows the location of the local variable (at the point it compiles the method) not nessecarily the location of the variable it conflicts with -- but from a UX perspective, the result is less-than-ideal.

Because this brings us back to my original problem. You see, I had a local variable, which is what I was originally passing to my ReportProgress method. I then added a member variable with the same name, but because ReSharper didn't flag the member as a warning (and I didn't notice the new yellow line indicating a warning down near the local variable) I didn't worry about it straight away. It was only after I'd already implemented the rest of the feature that needed the member variable that I noticed the "conflicting variable" warning.

So I renamed the local variable, but because a) there was still a member with the same name and b) ReportProgress takes an object as it's UserState parameter, the ReportProgress was not flagged as an error. So the error only showed up after I ran the program.

I guess it could also imply that I'm not relying on my tools enough -- after all, I could have used the "Rename..." refactoring to rename the local automatically, but I was lazy and it was easier to just type in a new name. I guess I'll just be more careful next time.