Analyze Email Message Headers with PowerShell—Part 2

Analyze Email Message Headers with PowerShell—Part 2

  • Comments 1
  • Likes

Summary: Learn how to use Windows PowerShell to analyze email headers and obtain detailed origination information.

 

Microsoft Scripting Guy Ed Wilson here. Guest Blogger Thiyagu continues with part 2 of his post about analyzing email headers with Windows PowerShell. See yesterdays’ post for information about Thiyagu and for the first part of the article.

In part 1, we saw how to use Regex and parse the email message header. So far, we have a PSObject with all the required properties parsed from the email message header. Sometimes with certain types of headers, you will have another set of headers. Instead of having just the “Received: from,” there will also be one named “Received: by.” I also wrote another function, just like this one to process that information as well as to build a custom PSObject.

Both of these functions will return a collection of PSObjects. Remember in part 1, I mentioned that we should be reading the email message header from bottom up, but we did not do that. We were doing this from top down. You can use this approach to reverse an array of objects: 

$byObjects = $byObjects[($byObjects.Length-1)..0] # Reversing the Array 

Alternatively, for large arrays, using the static Reverse method from the Array class will have much better performance. This technique is shown here: 

[Array]::Reverse($byObjects) 

Now we have two objects: one from “Received: from” and another from “Received: by.” “Received:  by” sections come first and then “Received:  from.” Now, enter the main function, which is going to do all the work. 

The main function takes both the receivedby and the receivedfrom objects. The function starts with the receivedby object first, because it is the first part that starts. It then takes the server name and the protocol (if available) and continues on to the next object in the array of objects that is passed. The function now starts its calculation, and compares the time of the previous entry with the time of the current entry. The function obtains the difference between the two time stamps and calculates the number of seconds. 

After it has this information, it is stored in a variable called $delay. This delay value is later added to the final PSObject, which will have all the info gathered from the headers. Here is what the function looks like; I am posting only the partial function here to show you the portion that matters: 

if($fromObjects)

                {                             

                 $fromObjects = $fromObjects[($fromObjects.Length-1)..0] #Reversing the Array

                 for($index = 0;$index -lt $fromobjects.Count;$index++)

                                {                                             

                               

                                                                $hop = $hop + 1

                                                                $receivedfrom = $fromobjects[$index].ReceivedFromFrom

                                                                $receivedby = $fromobjects[$index].ReceivedFromBy

                                                                $with = $fromobjects[$index].ReceivedFromWith

                                                                $time = $fromobjects[$index].ReceivedFromTime

                                                                $time = $time.touniversaltime()                                                              

                                                                if($prevTime)

                                                                {

                                                                                $delay = $time - $prevTime

                                                                                $delay = $delay | Select-Object -ExpandProperty totalseconds

                                                                }

                                                                else

                                                                {

                                                                                $delay = "*"

                                                                }                                                             

                                                                $prevTime = $time

                                                                $finalHash = @{

                                                                                Hop   = $hop

                                                                                Delay = $delay

                                                                                From  = $receivedfrom

                                                                                By             = $receivedby

                                                                                With  = $with

                                                                                Time  = $time

                                                                                }                                                             

                                                                $obj = New-Object -TypeName PSObject -Property $finalHash

                                                                $finalArray += $obj                                               

                                }                 

                }

 

These are all the fields we need:

  • Hop
  • Delay
  • From
  • By
  • With
  • Time

Also notice that I am converting all the times to universal time (UTC), so this way we have one set of times instead of all different time zones such IST, PDT, and EST. 

Delay is calculated by subtracting the time of the previous hop from the current time value: 

$delay = $time - $prevTime

$delay = $delay.totalseconds 

Then I just get the totalseconds property. Now let us just build a PSObject with all these parameters and then we are good to go. Finally it out that PSObject onto a gridview and here is what I get: 

Image of gridview

You can see now easily that on hop two, it took eight seconds, and in the next hop, it took around five seconds. Finally, there was delay of six seconds to submit to the server “incoming.green.com”. I converted the script to an advanced function, which takes in Filename as an input to parse the information. You can use it like a cmdlet and I have included some examples as well. The complete script containing the advanced function can be downloaded from the Scripting Guys Script Repository

Usually Level 1 teams do this type of job of checking these headers. I think we should give them a tool to do this work, instead of giving them this big Windows PowerShell script. Therefore, here is how I put together the GUI piece for this tool. I used ShowUI to build this tool in 15 lines. Here is the code: 

[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null

$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog

$OpenFileDialog.ShowDialog() | Out-Null

$filename = $openfiledialog.FileName

$data = c:\Scripts\MSGHeaderProcessor\Parse-EmailHeader.ps1 -InputFileName $filename

New-Window -Title "Email Header Parser" -SizeToContent WidthAndHeight -Show {                      

New-DataGrid -Name MessageHeader -Columns {

                                New-DataGridTextColumn -Header "Hop" -Binding {Binding -Path "Hop"}

                                New-DataGridTextColumn -Header "Delay(Seconds)" -Binding {Binding -Path "Delay"}

                                New-DataGridTextColumn -Header "From" -Binding {Binding -Path "From"}

                                New-DataGridTextColumn -Header "By" -Binding {Binding -Path "By"}

                                New-DataGridTextColumn -Header "With" -Binding {Binding -Path "With"}

                                New-DataGridTextColumn -Header "Time(UTC)" -Binding {Binding -Path "Time"}

                } -ItemsSource $data     

 

The first four lines are about showing the Windows Open Dialog box and getting the file name that the user wants to parse. I then call the script with that file name as the input and store the value in the $data variable, which now is the PSObjects collection. 

Next comes the ShowUI code. I create a new window and inside that I create a datagrid. Inside the datagrid, I add one datagridcolumn for each of the properties of the PSObject. The interesting thing here is the –Binding parameter: 

-Binding {Binding –Path “Hop”} 

The parameter checks the itemsource property of the datagrid, it uses the Hop property of the object there, and then it populates that information in the grid. The output from the ShowUI code is shown in the following figure. 

Image of output from ShowUI code

Therefore, in 15 lines we could make a reusable tool and distribute it to the teams. The complete code can be downloaded from the Scripting Guys Script Repository. I have only uploaded the Advanced function there; you can use the ShowUI code in the article to get the GUI part of it. 

Note   I have tested this with many headers, and it works well with Microsoft Header Version 2.0 headers, but I have not done testing on any old or different types of headers. If you guys notice any parsing issue on any header, please let me know.

 

 

I want to thank Thiyag for an awesome two-part series of articles about parsing email headers. Well done!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hello Thiyagu,

    this really a good idea tu use ShowUI for this purpose!

    Even if I dislike GUIs :-) I would definitely prefer this alternative!

    Klaus.