How to get the IIS SMTP Server Instance in a Transport Sink

Posted by

OK, so I've been having some fun with IIS's SMTP server these last couple of days. I've got these event sinks which I've written in C# so that I can do custom processing when email arrives for specific users (it also provides some simple spam filtering and stuff as well). Anyway, one of the requirements is that we have multiple SMTP server instances per machine, and I want them to be able to process things differently (because they're different domains).

Now, the SMTP server has two main kinds of event sinks: Command sinks and Transport sinks. Command sinks get called when a connected client issues an SMTP command (e.g. 'HELO', 'MAIL FROM', 'RCPT TO', etc). Transport sinks get called when a message is about to be delivered. Now, in the command sinks we do some simple checking that the message is for users we know about and stuff, but nothing really interesting. The interesting stuff happens in the transport sink when we take the message and actually perform our custom processing on it.

In the command sinks, you actually get passed a reference to a "context" object which gives you certain parameters about the server and you can in fact get the current server instance number from there. However, the transport sinks get a MailMsg object, and that's it. But I want to know the server instance from the transport sink, so what I'd been doing in the past was simply setting a property on the message in the command sinks, then referencing that in the transport sink.

Now, this worked OK when all your messages go through the command sinks to begin with, but not all messages do that. For example, IIS will generate delivery status notifications if it can't connect to a remote server. These DSNs will, obviously, completely bypass the command sinks because they're generated internally (and hence no commands are issued!). But the bigger problem is messages that get copied directly to the Pickup folder. These don't go through the command sinks either, so we can't work out the server instance there, either. But the Pickup folder is very useful, because it's heaps quicker to write messages there than it is to connect to the server via TCP/IP.

Now, there may be other ways a message can arrive at a transport sink, but so far I have only encountered those three. So I needed a way to get the server instance from within a transport sink, no matter how the message arrived at the sink. The IMailMsgProperies interface that is implemented by the MailMsg class has a GetProperty method which takes an integer property "ID" and returns whatever the property is. Now, the actually properties are not documented at all (that I could find) so I had to do a bit of trial and error to work out what properties are actually set on the messages. Basically, I just put a loop from 1 to 20,000 in my transport sink trying to see the value of the property with that ID.

Unfortunately, messages coming in via the three methods I mentioned all had a different set of properties. First, messages that came in via the command sink didn't have anything obviously useful to me, but remember that I explicitly put the server instance number in property #1, so I could just use that. But what I did find was that messages that came in via the Pickup folder had the default domain of the server in property #4106. The DSN messages had the RFC822 Message-ID header value in property #4128 (The Message-ID has the default domain after the @-sign).

So, I came up with the following code to work out the instance number. Notice that when I get the default domain from method #2 & #3, I have to go through the IIS metabase to get the instance number from that.


int GetServerInstance(MailMsg msg)
{
  // This is case #1 - the message came in via a an inbound command sink
  string propValue = GetStringA(1, msg);
  if (propValue != null && propValue != string.Empty)
  {
    return int.Parse(propValue);
  }

  // This is case #2 - the message came in
  // via the Pickup directory
  string domain = GetStringA(4106, msg);
  if (domain == null || domain == string.Empty)
  {
    // This is case #3 - the message came in
    // via an IIS-generated DSN.
    propValue = GetStringA(4128, msg);
    int atSign = propValue.LastIndexOf('@');
    domain = propValue.Substring(atSign);
  }

  // For both case #2 and #3, we need to get the
  // instance by looking up the domain we found
  // in the IIS metabase and working backwards.
  propValue = null;
  using (DirectoryEntry deSvcs =
         new DirectoryEntry("IIS://localhost/SmtpSvc"))
  {
    foreach (DirectoryEntry de in deSvcs.Children)
    {
      string thisDomain = de.Properties["DefaultDomain"].Value.ToString();
      if (de.SchemaClassName == "IIsSmtpServer" && thisDomain == domain)
      {
        propValue = de.Name;
        break;
      }
    }
  }

  return int.Parse(propValue);
}

Just a note, there's no error checking here and the GetStringA method is a wrapper around the GetProperty method, which I pinched by looking at the Reflector code for the C# wrapper objects :)

So, with the code above, I am able to work out which server instance the message is from. The really good thing is that IIS creates exactly one transport sink class per server instance, and keeps that object around for ever, so I can just cache the value and this code only runs once.

Wallpaper Changer bugfix (maybe)

Posted by

Thanks to michaels for the comments on Wallpaper Changer. He mentions an out of memory exception when browsing the previews with more than 50 images. On my machine, I've got 68 wallpapers, and I've never seen an OOM exception, so I'm not sure what's happening there. Perhaps your wallpapers are bigger than mine? Mine around about 1MB JPEGs (on average) usually about 2560x1600.

Anyway, michaels, you didn't leave any contact info, so if you want to mail me (wallpaperchanger (a) codeka.com) with your details, I can try to figure out what's going on. In the meantime I uploaded a new version which uses a slightly different method for generating the preview. If you're still getting OOM errors, then please drop me a line at the address I just mentioned and I'll see what I can do.

Gmail and a Pocket PC

Posted by

We just got an O2 Xda IIs at work, so you can expect a few posts from me as I explore it's features :)

Anyway, I wanted to get it to download mails from Gmail with Pocket Outlook. It was actually quite easy to set up, just set the servers up and away it went. But there was a problem, whenever it synced with Gmail, it deleted all the messages that were already in the Inbox! Pretty damn useless if you ask me.

After a bit of googling (there's a joke there somewhere), I found this post. In it, Ej mentions that you can tell Pocket Outlook to only download the headers (plus a bit of the body) and that seemed to work for me. But he also mentions that it still downloads the whole body, even though you only tell it to download 2KB (or whatever). But that didn't seem to be the case for me. Perhaps I have a newer version of Pocket Outlook, or maybe Gmail has been updated since that post. Anyway, I changed it to 25KB, since I don't really care about mails bigger than that.

I don't know if it's a bug in Pocket Outlook or in Gmail's POP3 implementation, but if I had to guess, I'd have to side with Pocket Outlook on this one. It doesn't do this with our internal POP3 server, and apparently it works OK with Yahoo (though I haven't tried) so there must be something that Gmail's POP3 implementation does that Pocket Outlook doesn't understand.

Well anyway, it's working "good enough" for me now, and hopefully someone else will find this useful...

Fixed Speed Cameras

Posted by

Bruce Schneier points out in his blog New Risks of Automatic Speedtraps that fixed speed cameras can be more dangerous than no speed camera at all because all that happens is that drivers slow down at the speed trap, and bunch up.

Now, we've had fixed speed cameras here in Australia for several years now, and I gotta agree. The only thing that happens is you're travelling along at 110kph, then at the speed camera you slow down to 90, then when you're out of range, you just speed up again. This slow-down is actually quite dangerous, because the driver behind you doesn't really know how quickly you'll slow down, or he may not even notice the camera and therefore isn't expecting you to slow down at all (though that's rare now that most of Sydney knows where all the fixed cameras are anyway).

One thing that's being thought of here, though, is instead of a single fixed camera that measures your instantaneous speed, you place a number of cameras along a section of road. The cameras note the times you pass each point, and some software works out your average speed over a long distance. If your average speed is above the limit, bam, you're nicked.

Now, this has a few advantages. First, it allows for small bursts of speed if you're over taking someone for instance. Second, it eliminates the sudden braking at the speed camera, because you gotta stay below the limit for longer. It should also be cheaper to install and run, because even though you need more cameras, cameras are cheap - the expensive bit is the maintenance on the radar which measures your speed. Software is cheap (to reproduce anyway, initial development may be expensive)

Of course, many people believe that fixed speed cameras are little more than revenue-raising machines for the police, and I tend to agree: most fixed speed cameras in Sydney generate millions of dollars a year in fines, with almost no effort.

Anyway, time will tell if this scheme will more effective than the instantaneous speed traps...

I'm Honoured

Posted by

Wow, my first bout of comment spam! I'm honoured :)

'Course, I've deleted it all, so it was a bit of a waste of time for the spammer. Oh well, what the hell!