Quick and dirty Powershell + Growl host watch

Needing a quick and dirty growl notification to know when a person restarted their computer, so I wrote up this originally one line script.

. '\libs\growl\Send-Growl 3.1.ps1';	#http://poshcode.org/1464
					#Might need to modify the file a little.

$str = @("down", "up");
$last_ds = $false;
while(1) {
	$new_ds = Test-Connection TargetHost -Quiet -Count 2;
	if($new_ds -ne $last_ds) {
		Send-Growl -Caption "Watchtower" -Message ("TargetHost is {0}" -f $str[$new_ds]);
	}
	$last_ds = $new_ds;
	sleep 5;
}

 
Just copy and paste in ISE, modify and away you go!
 

Bonus points protip: Run in the background.

start-process powershell @("Hidden", "-File" "\path\to\above\script.ps1") -WindowStyle Hidden

poshttpd: PowerShell based scripting for the web

For a while now I’ve wanted to make use of PowerShell to do things like what you would use PHP for with Apache, but nothing I found worked, not even cgi-bin stuff. So I figured, why not just build my own? By using stuff like HttpListener, it’s pretty easy to get started and worry more about what the daemon will do with requests then how to handle the conversations.

Git logs and such can be found here and you can get the code by git cloning http://plan.snoj.us/repo/poshttpd.git

First commit comments

=0.0.1=
*Two “engines”.
**One passthru for static files like images and html.
**The second is for PowerShell scripts.

To build, you must first edit the rootDir variables in main.cs as well as the BG.prefix (if you desire), run buildntest.ps1. It will build main.cs and start poshttpd.exe.

After it’s built and running, open your brower (default: http://localhost:81) and run your PowerShell script. There is an example script in /sites/.

Hurricane Electric 6in4 Windows startup script

Updates HE public IP records for your tunnel (for instance at the coffee shop), finds the first available interface that is connected and creates a v6v4tunnel using that IP.

See script for where you need to edit.

function fastpingtest {
    $ping = New-Object System.Net.NetworkInformation.Ping;
    $ping.Send("8.8.8.8", 1000).status -eq "success";
}
$endtime = [datetime]::Now.AddMinutes(1);
$mapipv6 = $false; 
while([datetime]::Now -lt $endtime) {
    if(fastpingtest) { $mapipv6 = $true; break; }
}

if($mapipv6) {
    $wc = New-Object net.webclient;
    $url= "https://ipv4.tunnelbroker.net/ipv4_end.php?ip=AUTO&pass={1}&apikey={0}&tid={2}";

    $values = "USERID", "PASSWORDMD5HASH", TUNNELID;
    $wc.DownloadString(($url -f $values));
    
    #get connected interface
    $interface = netsh interface ipv4 show interface | findstr /c:" connected" | ?{!$_.contains("Loopback");} | %{[regex]::Split($_, "( )+") | ?{$_.trim().length -gt 0} | %{$_.trim()}; }
    $interface_ip = (netsh interface ipv4 show address $interface[0] | findstr /c:"IP Address" | select -First 1).split(":")[1].trim()
    netsh interface teredo set state disabled
    netsh interface ipv6 add v6v4tunnel IP6Tunnel $interface_ip HEIPv4ENDPOINT
    netsh interface ipv6 add address IP6Tunnel YOURIPv6ADDRESS
    netsh interface ipv6 add route ::/0 IP6Tunnel HEIPv6ADDRESS
}

IPv6 range/subnet calculater for powershell

From what I can tell, there aren’t many…if any, IPv6 address range calculators. While I read it is recommended that you use the entire /64 block, I don’t think it’s always necessary to do so. Besides, what happens when you want to route only a portion of the block to one place?

Example usage:

ipv6range.ps1 2001:470:1f10:60::10 64 | ft ipaddress*
ipv6range.ps1 2003:5:1f:fa0::10 104 | ft ipaddress* #ipv4 /8 equivalent
ipv6range.ps1 2003:5:1f:fa0::10 112 | ft ipaddress* #ipv4 /16 equivalent
ipv6range.ps1 2003:5:1f:fa0::10 120 | ft ipaddress* #ipv4 /24 equivalent
ipv6range.ps1 2003:5:1f:fa0::10 124 | ft ipaddress* #ipv4 /28 equivalent

….and the code….

param(
    [net.ipaddress]$Addr,
    [int]$netmask = 64,
    [switch]$ForceListing

)

if($Addr.AddressFamily -ne 'InterNetworkV6') { throw "`$Addr must be a valid IPv6 address."; }

<#
Create a subnet mask based on a CIDR input.
#>
function subnet {
    param(
        [int]$netmask
    )

    if($netmask -gt 128) { throw "`$netmask cannot be greater than 128"; }

    $mask = (@($true) * $netmask) + (@($false) * (128-$netmask));
    return New-Object Collections.BitArray @(,$mask);
}

<#
Convert a BitArray into a byte array for easy conversion into an IPAddress.
#>
function bit2byte {
    param(
        [Collections.BitArray]$bitArray
    )

    for($i = 0; $i -lt $bitArray.length; $i+=8) {
        [convert]::ToByte([string]::Join("", ([string[]][byte[]]($bitArray[$i..($i+7)]))), 2)
    }
}

<#
convert an ip into a BitArray for easy bitwise operations.
#>
function ip2bit {
    param([net.ipaddress]$addr)

    $b = $addr.GetAddressBytes();
    $bits = @();
    foreach($a in $b) {
        $t = [convert]::ToString($a,2).padleft(8,"0") #[7..0];
        $bits += [string]::join("",$t);
    }
    $nbits = ($bits | %{[char[]]$_} | %{[bool]::Parse("$_".replace("1","true").replace("0","false"))});
    return New-Object collections.bitarray @(,$nbits)
}

<#
Increment an ipv6 address.
#>
function inc {
    param([net.ipaddress]$addr)

    $b = $addr.GetAddressBytes();

    for($i = $b.length-1; $i -ge 0; $i--) {
        if($b[$i] -gt 254) { continue; }
        $b[$i]++;
        break;
    }
    New-Object net.ipaddress @(,$b);
}

<#
Decrement an ipv6 address.
#>
function dec {
    param([net.ipaddress]$addr)

    $b = $addr.GetAddressBytes();

    for($i = $b.length-1; $i -ge 0; $i--) {
        if($b[$i] -eq 0) { continue; }
        $b[$i]--;
        break;
    }
    New-Object net.ipaddress @(,$b);
}

$ipArr = new-object collections.bitarray @(,(ip2bit $Addr.GetAddressBytes()))

$netBits = New-Object collections.bitarray @(,(subnet $netmask))
$hostBits = (New-Object collections.bitarray $netBits).Xor((New-Object collections.bitarray 128, $true)) #.xor((New-Object collections.bitarray 128, $true))

$netId = New-Object net.ipaddress @(, (bit2byte (New-Object collections.bitarray $ipArr).And($netBits)));
$netBcast = New-Object net.ipaddress @(, (bit2byte (New-Object collections.bitarray $ipArr).Or($hostBits)));

$numHosts = [math]::Pow(2, ($hostBits | ?{$_} | measure).count)

if($numHosts -gt 256 -and !$ForceListing.ispresent) {
    #well, if we have more than this, just output the (ipv4 equivalent) network id and broadcast address.
    $netId
    $netBcast
} else {
    Add-Member -PassThru -Force -InputObject $netId -MemberType NoteProperty -Name IsUsable -Value $false;
    try {
        $lastAddr = $netId;
        for($i = 0; $i -lt $numHosts-2; $i ++) {
            $lastAddr = inc $lastAddr
            Add-Member -PassThru -Force -InputObject $lastAddr -MemberType NoteProperty -Name IsUsable -Value $true;
        }
    } catch {}
    Add-Member -PassThru -Force -InputObject $netBcast -MemberType NoteProperty -Name IsUsable -Value $false;
    #$res
}

Notes:

  • Not commented well. RTCFA (read the code for answers)! >:(
  • Only increments despite there being a decrement function.
  • If the number of host addresses is less then 256, it will always display the complete listing.

Powershell: Wake up on Lan 0.3

Nick asked, “This script looks good. Would you mind adding comments to it to describe its logic? Also, it doesn’t appear to like having colons in MAC addresses even though colons is pretty standard. Thanks for sharing your script!” (link).

Well Nick, here you are! I hope it helps!

param (
    [parameter(Position=0,
        Mandatory = $true,
        ParameterSetName="bymac",
        HelpMessage="Wake up a machine by known mac address.")]
    [string]
    $targetMac,
    [parameter(Position=0,
        mandatory = $true,
        ParameterSetName="byip",
        HelpMessage="Wake up a machine by last known IP address.")]
    [Net.IPAddress]
    $targetIP,
    [parameter(Position=1)]
    $network = [net.ipaddress]::Broadcast,
    [parameter(Position=2)]
    $subnet = [net.ipaddress]::Broadcast
)

try {
    #Convert -network and -subnet to Net.IPAddress objects if necessary.
    if($network.gettype().equals([string])) {
        $network = [net.ipaddress]::Parse($network);
    }
    if($subnet.gettype().equals([string])) {
        $subnet = [net.ipaddress]::Parse($subnet);
    }
    
    #find the broadcast address for the -network
    #eg. the broadcast address for the ip 192.168.1.101/255.255.255.0 is 192.168.1.255.
    $broadcast = new-object net.ipaddress (([system.net.ipaddress]::parse("255.255.255.255").address -bxor $subnet.address -bor $network.address))
    
    #attempt to figure out if the first value is an ip or mac address.
    #if we do this: wol.ps1 192.168.1.255
    #instead of this: wol.ps1 -targetIP 192.168.1.255
    try {
        $targetIP = [net.ipaddress]::Parse($targetMac.toupper()).tostring() # | Out-Null;
    } catch {
        #$_;
        try {
            [Net.NetworkInformation.PhysicalAddress]::Parse($targetMac.toupper().replace(".","").replace(":","").replace("-","")) | Out-Null;
        } catch {
            #$_;
        }
    }
    
    #Find our target mac address.
    #$targetIP isn't set in the above try..catch if it's a mac address.
    if($targetIP) {
        try {
            $nmac = (arp -a $targetIP | ? {$_ -imatch $targetIP}).split(" ") | ? {$_.trim().length -gt 0;} | %{ $_.trim(); };
            $targetMac = $nmac[1];
        }catch{
            throw "IP address is unknown.";
        }
    }
    
    $mac = [Net.NetworkInformation.PhysicalAddress]::Parse($targetMac.toupper().replace(".","").replace(":","").replace("-",""))
    
    <#"targetmac: " + $targetMac;
    "targetip: " + $targetip;
    "mac: " + $mac;#>
    
    #Setup 3 different endpoints for the common WOL ports.
    $u = New-Object net.sockets.udpclient
    $ep = New-Object net.ipendpoint $broadcast, 0
    $ep2 = New-Object net.ipendpoint $broadcast, 7
    $ep3 = New-Object net.ipendpoint $broadcast, 9
    
    #setup the payload.
    #6 bytes of 255 followed by the target mac 16 times.
    $payload = [byte[]]@(255,255,255,255,255,255);
    $payload += ($mac.GetAddressBytes()*16)
    
    #send it out a few times.
    for($i = 0; $i -lt 10; $i++) {
        $u.Send($payload, $payload.Length, $ep) | Out-Null
        $u.Send($payload, $payload.Length, $ep2) | Out-Null
        $u.Send($payload, $payload.Length, $ep3) | Out-Null
        "$(date) - WOL payload $($i+1)/10 sent to $targetMac on $broadcast/$subnet" | Write-Debug;
        sleep -Milliseconds 10;
    }
} catch {
    $_ | Write-Error;
}
#Win32_NetworkAdapter
#Win32_OperatingSystem
#Win32_NetworkClient
#Win32_SystemNetworkConnections

Traceroute with starting TTLs

Tracing stuff with tracert or traceroute you can’t specify a starting TTL. So there will always be routers that show up and waste time.

This code is pretty rudimentary. Hopefully I’ll be able to expand on it to include ipv6 and make it faster.

param(
    $targetIP,
    $BeginHop = 1,
    $EndHop = 30,
    $timeout = 1000,
    [switch]$GetHostname
)

$addrtype = [System.Net.Sockets.AddressFamily]::InterNetwork;
if($v6.ispresent) {
    $addrtype = [System.Net.Sockets.AddressFamily]::InterNetworkV6;
}

$targetIPActual = $null;
if(![net.ipaddress]::TryParse($targetIP, [ref]$targetIPActual)) {
    $target = [net.dns]::GetHostEntry($targetIP);
    $targetIPActual = $target.addresslist | where {$_.addressfamily -eq $addrtype} | select -First 1
} else {
    $target = New-Object psobject -Property @{"HostName" = $targetIP.tostring()}
}

for($i = $BeginHop; $i -lt $EndHop; $i++) {
    
    $ping = new-object System.Net.NetworkInformation.ping;
    $pingo = new-object System.Net.NetworkInformation.PingOptions $i, $true;
    $sendbytes = @([byte][char]'a'..[byte][char]'z');
    $pr = $ping.Send($targetIPActual, $timeout, $sendbytes, $pingo);
    try {
        $rtn = New-Object psobject -Property @{
            "IP" = $pr.Address;
            "RoundtripTime" = $pr.RoundtripTime;
            "Status" = $pr.Status;
        }
    } catch {
        $rtn = New-Object psobject -Property @{
            "IP" = "*";
            "RoundtripTime" = $pr.RoundtripTime;
            "Status" = $pr.Status;
        }
    }
    
    try {
        if($GetHostname.ispresent) {
            Add-Member -InputObject $rtn -MemberType NoteProperty -Name Hostname -Value ([net.dns]::GetHostEntry($pr.Address).hostname)
        }
    } catch{}
    
    $rtn;
    
    #$pr
    try { 
        if($pr.Address.tostring() -eq $targetIPActual) { break; }
    } catch{}
}

Powershell: Wake up on Lan – redux

In this revision, I’ve added support for IP based wake ups. However, the IP needs to be in the local ARP table.

param (
    [parameter(Position=0,
        Mandatory = $true,
        ParameterSetName="bymac",
        HelpMessage="Wake up a machine by known mac address.")]
    [string]
    $targetMac,
    [parameter(Position=0,
        mandatory = $true,
        ParameterSetName="byip",
        HelpMessage="Wake up a machine by last known IP address.")]
    [Net.IPAddress]
    $targetIP,
    [parameter(Position=1)]
    $network = [net.ipaddress]::Broadcast,
    [parameter(Position=2)]
    $subnet = [net.ipaddress]::Broadcast
)

try {
    if($network.gettype().equals([string])) {
        $network = [net.ipaddress]::Parse($network);
    }
    if($subnet.gettype().equals([string])) {
        $subnet = [net.ipaddress]::Parse($subnet);
    }
    
    $broadcast = new-object net.ipaddress (([system.net.ipaddress]::parse("255.255.255.255").address -bxor $subnet.address -bor $network.address))
    
    try {
        $targetIP = [net.ipaddress]::Parse($targetMac.toupper()).tostring() # | Out-Null;
        
        
    } catch {
        #$_;
        try {
            [Net.NetworkInformation.PhysicalAddress]::Parse($targetMac.toupper()) | Out-Null;
        } catch {
            #$_;
        }
    }
    if($targetIP) {
        try {
            $nmac = (arp -a $targetIP | ? {$_ -imatch $targetIP}).split(" ") | ? {$_.trim().length -gt 0;} | %{ $_.trim(); };
            $targetMac = $nmac[1];
        }catch{
            throw "IP address is unknown.";
        }
    }
    
    $mac = [Net.NetworkInformation.PhysicalAddress]::Parse($targetMac.toupper().replace(".",""))
    
    $u = New-Object net.sockets.udpclient
    $ep = New-Object net.ipendpoint $broadcast, 0
    $ep2 = New-Object net.ipendpoint $broadcast, 7
    $ep3 = New-Object net.ipendpoint $broadcast, 9

    $payload = [byte[]]@(255,255,255,255,255,255);
    $payload += ($mac.GetAddressBytes()*16)

    for($i = 0; $i -lt 10; $i++) {
        $u.Send($payload, $payload.Length, $ep) | Out-Null
        $u.Send($payload, $payload.Length, $ep2) | Out-Null
        $u.Send($payload, $payload.Length, $ep3) | Out-Null
        "$(date) - WOL payload $($i+1)/10 sent to $targetMac on $broadcast/$subnet" | Write-Debug;
        sleep -Milliseconds 10;
    }
} catch {
    $_ | Write-Error;
}

Visio to PHPWeathermap

It’s been awhile, but I haven’t been too lazy! I’ve got some projects currently in the works and a new server that I’m getting ready.

In the mean time though, check out this super handy (to me at least) Visio to PHPWeathermap converter. At this time, it only gets network nodes and links them based on what’s in the Visio file. It does not yet know how to create links using the VIA config or how to differentiate between straight lines and curved….yet

To get a shape to show in Weathermap, you have to set the shape’s IP address (found under shape data) or manually add “Node” and “Label” to User-defined cells for things like internet clouds.

After converting the file, you’ll need to manually add icons, targets, and anything else you’ll want done.

<#
.SYNOPSIS
Convert Visio network map to a PHP Weathermap config file.
.Description
Visio is required to run.

Sends resulting config to STDOUT.

.Parameter VisioMap
Path of the Visio map to convert.
#>
param(
    $VisioMap
)

if($host.Name -imatch "ise") {
    #for testing in ISE
    $VisioMap = $env:visiomap
} else {
    if(!$VisioMap) { 
        $VisioMap = Read-Host -Prompt "Filepath";
    }
}

#get all child nodes of a shape if they have an ipaddress or node value set.
function getNodes {
    param($shape);
    
    $rtn = @();
    if($shape.shapes) {
        $rtn += $shape.shapes | %{ getNodes $_; }
    }
    if($shape.CellExists) {
        if(([bool]$shape.CellExists("Prop.IPAddress",1)) -or ([bool]$shape.CellExists("User.Node",1))) {
             $rtn += $shape;
        }
    }
    return $rtn;
}

#target shape not have an ipaddress or node cell? find out if a parent does!
#returns false if there is no parent with those cells.
function getParent {
    param($shape)
    
    try {
        if(([bool]$shape.CellExists("Prop.IPAddress",1)) -or ([bool]$shape.CellExists("User.Node",1))) {
            return $shape;
        } else {
            return (getParent $shape.ContainingShape);
        }
    }
    catch {
        return $false;
    }
}

try {
    #for when I'm testing.
    if($application -eq $null) {
        $application = New-Object -ComObject Visio.Application
        $doc = $application.Documents.Open($VisioMap);
    }
    $height = [math]::Ceiling($doc.Pages.Item(1).PageSheet.cells("PageHeight").ResultIU * 100)
    $width = [math]::Ceiling($doc.Pages.Item(1).PageSheet.cells("PageWidth").ResultIU * 100)

    "HEIGHT " + $height
    "WIDTH " + $width
    
    $ioi = (getNodes $doc.Pages.Item(1)) | ?{$_.name} | %{ $_ }

    $ntpl = @"
NODE node{@id}
	LABEL {@label}
	POSITION {@x} {@y}
"@;
    foreach($i in $ioi) {
        try {
            $rv = @{
                "id" = "";
                "ipaddress" = "";
                "label" = "";
            };
            
            if([bool]$i.CellExistsU("Prop.IPAddress",0)) {
                $ipaddress = [net.ipaddress]::Parse($i.cells("Prop.IPAddress").formula.trim('"'));
                
                $rv["label"] = $i.Cells("Prop.NetworkName").formula.trim('"');
                if($i.Cells("Prop.NetworkName").formula.trim('"').length -eq 0) {
                    $rv["label"] = $ipaddress.ToString();
                }
                
                $rv['id'] = $ipaddress.address;
                $rv["ipaddress"] = $ipaddress.ToString();
            } elseif([bool]$i.CellExistsU("User.Node",0) -and [bool]$i.CellExistsU("User.Label",0)) {
                $rv["id"] = $i.name;
                
                $rv["label"] = $i.Cells("User.Label").formula.trim('"');
                if($i.Cells("User.Label").formula.trim('"').length -eq 0) {
                    $rv["label"] = $i.Name;
                }
            }
            $rv.Add("x", [math]::Ceiling(($i.Cells("PinX").ResultIU * 100)))
            $rv.Add("y", [math]::Ceiling(($i.Cells("PinY").ResultIU * 100)))
            
            
            $node = $ntpl;
            foreach($k in $rv.Keys) {
                $node = $node.Replace("{@$k}", $rv[$k]);
            }
            $node
        } catch {
            $err = $_;
            $err;
            $i.name | Write-Debug;
        }
    }

    $ltpl = @"
LINK {@fromNodeId}-{@toNodeId}
    #from: {@fromLabel}
    #to: {@toLabel}
	WIDTH 5
	ARROWSTYLE compact
	NODES {@fromNodeId} {@toNodeId}
"@
    foreach($wire in $doc.Pages.item(1).Shapes | where {$_.connects.count -gt 0}) {
        try {
            $rv = @{};
            
            $from = $wire.Connects.Item(1).tocell.shape;
            $to = $wire.Connects.Item(2).tocell.shape;
            
            #Find the parent.
            if(![bool]$from.CellExists("Prop.IPAddress",1) -or
                ![bool]$from.CellExists("User.Node",1)) {
                $from = getParent $from;
            }
            
            if(![bool]$to.CellExists("Prop.IPAddress",1) -or
                ![bool]$to.CellExists("User.Node",1)) {
                $to = getParent $to;
            }    
            
            if([bool]$from.CellExists("Prop.IPAddress",1)) {
                $fromIpaddress = [net.ipaddress]::Parse($from.cells("Prop.IPAddress").formula.trim('"'));
                
                $rv.Add("fromNodeId", "node" + $fromIpaddress.Address)
                $rv.Add("fromLabel", $fromIpaddress.tostring())
            } elseif([bool]$from.CellExists("User.Node",1)) {
            
                $rv.Add("fromNodeId", "node" + $from.Name)
                try {
                    $rv.Add("fromLabel", $from.cells("User.Label").formula.trim('"'));
                } catch {
                    $rv.Add("fromLabel", $from.Name)
                }
            } else {
                $rv.Add("fromNodeId", "node" + $from.name)
                $rv.Add("fromLabel", $from.name)
            }
            
            if([bool]$to.CellExists("Prop.IPAddress",1)) {
                $toIpaddress = [net.ipaddress]::Parse($to.cells("Prop.IPAddress").formula.trim('"'));
                
                $rv.Add("toNodeId", "node" + $toIpaddress.Address)
                $rv.Add("toLabel", $toIpaddress.tostring())
            } elseif([bool]$to.CellExists("User.Node",1)) {
            
                $rv.Add("toNodeId", "node" + $to.name)
                try {
                    $rv.Add("toLabel", $to.cells("User.Label").formula.trim('"'));
                } catch {
                    $rv.Add("toLabel", $to.name)
                }
            } else {
                $rv.Add("toNodeId", "node" + $to.name)
                $rv.Add("toLabel", $to.name)
            }
            
            #$rv.Count | Write-Debug;
            $link = $ltpl;
            foreach($k in $rv.Keys) {
                "{@$k} = " + $rv[$k] | Write-Debug;
                $link = $link.Replace("{@$k}", $rv[$k]);
            }
            $link
        }catch{
            $_;
            $wire.name | Write-Debug
        }
    }
}
catch {
    $_
} finally {
    #not testing? quit!
    if(!($Host.Name -imatch "ise")) {
        $application.Quit();
        $application = $null
    } 
}

[Edit] Forgot to set $VisioMap with Read-Host if it were empty!