How do I properly convert an IP Address gotten via OleSNMP to a useable value?
I'm trying to write a Powershell script to query devices with SNMP and then display the data. Stuff works fine for simple types, but I'm having problems with the IP Addresses (and MAC addresses, but let's stick to IP for now)
Here's what I have (simplified to the problem space):
param ($ipaddr='10.1.128.114', $community='Public')
$snmp = New-Object -ComObject oleprn.OleSNMP
$snmp.open($ipaddr, $community)
$result = $snmp.get(".1.3.6.1.2.1.4.20.1.1.10.1.128.114")
$enc = [system.Text.Encoding]::ASCII
$bytes = $enc.GetBytes($result)
write-host( "bytes:" + $bytes)
Which outputs:
bytes:10 1 63 114
When I expected
bytes:10 1 128 114
For contrast, the snmp-get outputs:
$ snmpget 10.1.128.114 -c Public -v2c -On .1.3.6.1.2.1.4.20.1.1.10.1.128.114
.1.3.6.1.2.1.4.20.1.1.10.1.128.114 = IpAddress: 10.1.128.114
And yes, I realize that in my final script I'll have to walk the table instead of using a direct "get" but I need to fix my parsing first.
As mentioned in the comments, the ASCII encoding substitutes characters outside its valid range with a question mark ?, which is ASCII character 63.
More info is available in the documentation for ASCIIEncoding.GetBytes - search for "?" and you'll find this:
ASCIIEncoding does not provide error detection. Any Unicode character greater than U+007F is encoded as the ASCII question mark ("?").
Note, 0x7F is 127, so since [char] 128 is outside this range, it's being converted to the byte equivalent of ? (which is 63) when you call GetBytes.
Your code is basically doing this this:
$result = ([char] 10) + ([char] 1) + ([char] 128) + ([char] 114)
$encoding = [System.Text.Encoding]::ASCII
$bytes = $encoding.GetBytes($result)
$bytes
# 10
# 1
# 63 (i.e. "?")
# 114
You need to use an encoder which will convert characters higher than 0x7F into the equivalent bytes - something like iso-8859-1 seems to work:
$result = ([char] 10) + ([char] 1) + ([char] 128) + ([char] 114)
$encoding = [System.Text.Encoding]::GetEncoding("iso-8859-1")
$bytes = $encoding.GetBytes($result)
$bytes
# 10
# 1
# 128
# 114
Related
I am trying to process a flag from the MECM command Get-CMTaskSequenceDeployment called 'AdvertFlags'.
The information from Microsoft in relation to this value is HERE
The value returned is designated as : Data type: UInt32
In the table of flags, the one I need to check is listed as :
Hexadecimal (Bit)
Description
0x00000020 (5)
IMMEDIATE. Announce the advertisement to the user immediately.
As part of my Powershell script I am trying to ascertain if this flag is set.
I can see by converting it to Binary that a particular bit gets set.
When the settings is enabled:
DRIVE:\> [convert]::ToString((Get-CMTaskSequenceDeployment -AdvertisementID ABC20723).AdvertFlags, 2)
100110010000000000100000
When the setting is disabled:
DRIVE:\> [convert]::ToString((Get-CMTaskSequenceDeployment -AdvertisementID ABC20723).AdvertFlags, 2)
100110010000000000000000
The 6th bit is changed. Great! So far though, I've been unable to find a way to check if this bit is set. I suspected something in the bitwise operators (-band -bor etc) would help me here but I've been unable to get it to work.
Any bitwise operation I try returns an error:
"System.UInt64". Error: "Value was either too large or too small for a UInt64."
I mean, I can compare the string literally, but other options may be changed at any point.
Any help greatly appreciated.
EDIT: Just as an example of the error I am seeing, I can see that the bit that is set is '32' and from my limited understanding I should be able to:
PS:\> '100110010000000000100000' -band '32'
Cannot convert value "100110010000000000100000" to type "System.UInt64". Error: "Value was either too large or too small for a UInt64."
At line:1 char:1
+ '100110010000000000100000' -band '32'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastIConvertible
But I just always return an error
To test bit6 in
$AdvertFlags = (Get-CMTaskSequenceDeployment -AdvertisementID ABC20723).AdvertFlags
Should simply be:
if ($AdvertFlags -band 32) { 'bit6 is set' } else { 'bit6 is not set' }
I do not have access to a deployment environment with Get-CMTaskSequenceDeployment cmdlet, nevertheless to confirm what I am stating:
$AdvertFlags = [Convert]::ToUInt32("100110010000000000100000", 2)
$AdvertFlags
10027040
if ($AdvertFlags -band 32) { 'bit6 is set' } else { 'bit6 is not set' }
bit6 is set
$AdvertFlags = [Convert]::ToUInt32("100110010000000000000000", 2)
$AdvertFlags
10027008
if ($AdvertFlags -band 32) { 'bit6 is set' } else { 'bit6 is not set' }
bit6 is not set
Your self-answer using [bigint]'100110010000000000100000' -band "32" to test for bit6 is merely a coincident that it returns the expected value:
10027035..10027045 |ForEach-Object {
$Binary = [convert]::ToString($_, 2)
[pscustomobject]#{
Binary = $Binary
bAnd = $_ -bAnd 32
Bigint = [bigint]$Binary -band "32"
}
}
Yields:
Binary bAnd Bigint
------ ---- ------
100110010000000000011011 0 0
100110010000000000011100 0 0
100110010000000000011101 0 0
100110010000000000011110 0 32 # ← incorrect
100110010000000000011111 0 32 # ← incorrect
100110010000000000100000 32 32
100110010000000000100001 32 32
100110010000000000100010 32 32
100110010000000000100011 32 32
100110010000000000100100 32 0 # ← incorrect
100110010000000000100101 32 0 # ← incorrect
enumerations as flags
But PowerShell has an even nicer way to test them by name:
[Flags()] enum AdvertFlags {
IMMEDIATE = 0x00000020 # Announce the advertisement to the user immediately.
ONSYSTEMSTARTUP = 0x00000100 # Announce the advertisement to the user on system startup.
ONUSERLOGON = 0x00000200 # Announce the advertisement to the user on logon.
ONUSERLOGOFF = 0x00000400 # Announce the advertisement to the user on logoff.
OPTIONALPREDOWNLOAD = 0x00001000 # If the selected architecture and language matches that of the client, the package content will be downloaded in advance
WINDOWS_CE = 0x00008000 # The advertisement is for a device client.
ENABLE_PEER_CACHING = 0x00010000 # This information applies to System Center 2012 Configuration Manager SP1 or later, and System Center 2012 R2 Configuration Manager or later.
DONOT_FALLBACK = 0x00020000 # Do not fall back to unprotected distribution points.
ENABLE_TS_FROM_CD_AND_PXE = 0x00040000 # The task sequence is available to removable media and the pre-boot execution environment (PXE) service point.
APTSINTRANETONLY = 0x00080000 #
OVERRIDE_SERVICE_WINDOWS = 0x00100000 # Override maintenance windows in announcing the advertisement to the user.
REBOOT_OUTSIDE_OF_SERVICE_WINDOWS = 0x00200000 # Reboot outside of maintenance windows.
WAKE_ON_LAN_ENABLED = 0x00400000 # Announce the advertisement to the user with Wake On LAN enabled.
SHOW_PROGRESS = 0x00800000 # Announce the advertisement to the user showing task sequence progress.
NO_DISPLAY = 0x02000000 # The user should not run programs independently of the assignment.
ONSLOWNET = 0x04000000 # Assignments are mandatory over a slow network connection.
TARGETTOWINPE = 0x10000000 # Target this deployment to WinPE only.
HIDDENINWINPE = 0x20000000 # Target this deployment to WinPE only but hide in WinPE. It can only be used by TS variable SMSTSPreferredAdvertID.
}
# $AdvertFlags = [AdvertFlags](Get-CMTaskSequenceDeployment -AdvertisementID ABC20723).AdvertFlags
$AdvertFlags = [AdvertFlags][Convert]::ToUInt32("100110010000000000100000", 2)
# or: $AdvertFlags = [AdvertFlags]('IMMEDIATE', 'ENABLE_PEER_CACHING', 'APTSINTRANETONLY', 'OVERRIDE_SERVICE_WINDOWS', 'SHOW_PROGRESS')
$AdvertFlags
IMMEDIATE, ENABLE_PEER_CACHING, APTSINTRANETONLY, OVERRIDE_SERVICE_WINDOWS, SHOW_PROGRESS
$AdvertFlags -bAnd [AdvertFlags]'IMMEDIATE'
IMMEDIATE
EDIT: My answer here is incorrect as noted above. Leaving here for prosperity!
As always I BELEIVE I found the answer minutes after posting (After spending a couple hours on this!).
By adjusting the type to [bigint] the comparison was able to complete and return the expected answer:
DRIVE:\> [bigint]'100110010000000000100000' -band "32"
32
So a simple:
If (([bigint]'100110010000000000100000' -band "32") -gt 0){$true}else{$false}
True
and:
If (([bigint]'100110010000000000000000' -band "32") -gt 0){$true}else{$false}
False
Solves my issue. Feel free to give any extra advice if this is not the ideal way to proceed.
I though PS would be smarted when auto defining types etc. This is targeting PS5 on Server 2012 R2 though.
I've had the pleasure to get the assignment of posting emojis in Powershell, the only problem is they have to be on the same line, and there are three. This is, my first assignment, and we have no prior teaching in this subject so after googling and searching YouTube, my best shot was this below, however, it came with some error saying something about either too high value, or too low value.
Full error text: Exception calling "ToInt32" with "2" argument (s): "The value was either too large or too small to a UInt32. "
At C: \ Users \ EG \ Downloads \ Herningsholm \ Powershell H1 \ Hardware Information.ps1: 3 char: 5
$ UnicodeInt = [System.Convert] :: toInt32 ($ StrippedUnicode, 16)
CategoryInfo: NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId: OverflowException
$FullUnicode = ('U+1F60E') + ('U+1F436') + ('U+1F642')
$StrippedUnicode = $FullUnicode -replace 'U\+',''
$UnicodeInt = [System.Convert]::toInt32($StrippedUnicode,16)
[System.Char]::ConvertFromUtf32($UnicodeInt)
Try this out:
Full emoji list > here
# saves unicode for each emoji https://unicode.org/emoji/charts/full-emoji-list.html
$FullUnicode0 = 'U+1F606'
$FullUnicode1 = 'U+1F605'
$FullUnicode2 = 'U+1F605'
# removes the U+ bit
$StrippedUnicode0 = $FullUnicode0 -replace 'U\+',''
$StrippedUnicode1 = $FullUnicode1 -replace 'U\+',''
$StrippedUnicode2 = $FullUnicode2 -replace 'U\+',''
# Converts the value of the specified object to a 32-bit signed integer
$UnicodeInt0 = [System.Convert]::toInt32($StrippedUnicode0,16)
$UnicodeInt1 = [System.Convert]::toInt32($StrippedUnicode1,16)
$UnicodeInt2 = [System.Convert]::toInt32($StrippedUnicode2,16)
# Converts the specified Unicode code point into a UTF-16 encoded string so that you have an emoji
$Emoji0 = [System.Char]::ConvertFromUtf32($UnicodeInt0)
$Emoji1 = [System.Char]::ConvertFromUtf32($UnicodeInt1)
$Emoji2 = [System.Char]::ConvertFromUtf32($UnicodeInt2)
write-host "$($Emoji0), $($Emoji1), $($Emoji2)"
I have a multipart file received from a server and I need to pick out the pdf part from it. I tried with removing the first x lines and the last 2 with
$content=Get-Content $originalfile
$content[0..($content.length-3)] |$outfile
but it corrupts the binary data, so what is the way to get the binary part from the file?
MIME-Version: 1.0
Content-Type: multipart/related; boundary=MIME_Boundary;
start="<6624867311297537120--4d6a31bb.16a77205e4d.3282>";
type="text/xml"
--MIME_Boundary
Content-ID: <6624867311297537120--4d6a31bb.16a77205e4d.3282>
Content-Type: text/xml; charset=utf-8
Content-Transfer-Encoding: 8bit
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"/>
--MIME_Boundary
Content-ID:
Content-Type: application/xml
Content-Disposition: form-data; name="metadata"
<?xml version="1.0" encoding="ISO-8859-1"?>
<metadata><contentLength>64288</contentLength><etag>7e3da21f7ed1b434def94f4b</etag><contentType>application/octet-stream</contentType><properties><property><key>Account</key><value>finance</value></property><property><key>Business Unit</key><value>EU DEBMfg</value></property><property><key>Document Type</key><value>PAYABLES</value></property><property><key>Filename</key><value>test-pdf.pdf</value></property></properties></metadata>
--MIME_Boundary
Content-ID:
Content-Type: application/octet-stream
Content-Disposition: form-data; name="content"
%PDF-1.6
%âãÏÓ
37 0 obj <</Linearized 1/L 20597/O 40/E 14115/N 1/T 19795/H [ 1005 215]>>
endobj
xref
37 34
0000000016 00000 n
0000001386 00000 n
0000001522 00000 n
0000001787 00000 n
0000002250 00000 n
.
.
.
0000062787 00000 n
0000063242 00000 n
trailer
<<
/Size 76
/Prev 116
/Root 74 0 R
/Encrypt 38 0 R
/Info 75 0 R
/ID [ <C21F21EA44C1E2ED2581435FA5A2DCCE> <3B7296EB948466CB53FB76CC134E3E76> ]
>>
startxref
63926
%%EOF
--MIME_Boundary-
You need to read the file as a series of bytes and treat it as a binary file.
Next, to parse out the PDF part of the file, you need to read it again as String, so you can perform Regular Expression on it.
The String should be in an encoding that does not alter the bytes in any way, and for that, there is the special encoding Codepage 28591 (ISO 8859-1) with which the bytes in the original file are used as-is.
To do this, I've written the following helper function:
function ConvertTo-BinaryString {
# converts the bytes of a file to a string that has a
# 1-to-1 mapping back to the file's original bytes.
# Useful for performing binary regular expressions.
Param (
[Parameter(Mandatory = $True, ValueFromPipeline = $True, Position = 0)]
[ValidateScript( { Test-Path $_ -PathType Leaf } )]
[String]$Path
)
$Stream = New-Object System.IO.FileStream -ArgumentList $Path, 'Open', 'Read'
# Note: Codepage 28591 (ISO 8859-1) returns a 1-to-1 char to byte mapping
$Encoding = [Text.Encoding]::GetEncoding(28591)
$StreamReader = New-Object System.IO.StreamReader -ArgumentList $Stream, $Encoding
$BinaryText = $StreamReader.ReadToEnd()
$StreamReader.Close()
$Stream.Close()
return $BinaryText
}
Using the above function, you should be able to get the binary part from the multipart file like this:
$inputFile = 'D:\blah.txt'
$outputFile = 'D:\blah.pdf'
# read the file as byte array
$fileBytes = [System.IO.File]::ReadAllBytes($inputFile)
# and again as string where every byte has a 1-to-1 mapping to the file's original bytes
$binString = ConvertTo-BinaryString -Path $inputFile
# create your regex, all as ASCII byte characters: '%PDF.*%%EOF[\r?\n]{0,2}'
$regex = [Regex]'(?s)(\x25\x50\x44\x46[\x00-\xFF]*\x25\x25\x45\x4F\x46[\x0D\x0A]{0,2})'
$match = $regex.Match($binString)
# use a MemoryStream object to store the result
$stream = New-Object System.IO.MemoryStream
$stream.Write($fileBytes, $match.Index, $match.Length)
# save the binary data of the match as a series of bytes
[System.IO.File]::WriteAllBytes($outputFile, $stream.ToArray())
# clean up
$stream.Dispose()
Regex details:
( Match the regular expression below and capture its match into backreference number 1
\x25 Match the ASCII or ANSI character with position 0x25 (37 decimal => %) in the character set
\x50 Match the ASCII or ANSI character with position 0x50 (80 decimal => P) in the character set
\x44 Match the ASCII or ANSI character with position 0x44 (68 decimal => D) in the character set
\x46 Match the ASCII or ANSI character with position 0x46 (70 decimal => F) in the character set
[\x00-\xFF] Match a single character in the range between ASCII character 0x00 (0 decimal) and ASCII character 0xFF (255 decimal)
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
\x25 Match the ASCII or ANSI character with position 0x25 (37 decimal => %) in the character set
\x25 Match the ASCII or ANSI character with position 0x25 (37 decimal => %) in the character set
\x45 Match the ASCII or ANSI character with position 0x45 (69 decimal => E) in the character set
\x4F Match the ASCII or ANSI character with position 0x4F (79 decimal => O) in the character set
\x46 Match the ASCII or ANSI character with position 0x46 (70 decimal => F) in the character set
[\x0D\x0A] Match a single character present in the list below
ASCII character 0x0D (13 decimal)
ASCII character 0x0A (10 decimal)
{0,2} Between zero and 2 times, as many times as possible, giving back as needed (greedy)
)
First time attempting to work with UDP. I'm harvesting lat/long from a NMEA string in a UDP broadcast. The code below works fine in the ISE but hangs up in the console. I'm running both with Admin rights. Other posts on the topic recommend dot-sourcing, no difference. I suspect it's something to do with how it's handling variables but have hit a wall. Thanks in advance for help.
##########################################################################
#
# Read $GPGGA string from UDP broadcast on port 40181
#
# Convert Degree-Minutes-Seconds format to decimal lat/long
#
# UDP code harvested from http://winpowershell.blogspot.com/2010/01/powershell-udp-clientserver.html
#
#
##########################################################################
# Form a connection point
$endpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0)
Write-Host "Endpoint: " $endpoint
# Open UDP client
$udpclient = new-Object system.Net.Sockets.Udpclient(40181)
Write-Host "UDP client: " $udpclient
# Grab the datagram
$content = $udpclient.Receive([ref]$endpoint)
Write-Host "Content: " $content
# Put the datagram in $udpstring as type string
$udpstring = [Text.Encoding]::ASCII.GetString($content)
Write-Host "UDP string: " $udpstring
# Close UDP client
$udpclient.Close()
# Get Degree Min Sec format latitude from UDP string
# Explicit typing required in order to do math later
[Double]$DMSlatdeg = $udpstring.Substring(17,2)
$DMSlatmin = $udpstring.Substring(19,2)
# Get North South directional from UDP string
$directNS = $udpstring.Substring(29,1)
# Get Degree Min Sec format longitude from UDP string
# Explicit typing required in order to do math later
[Double]$DMSlongdeg = $udpstring.Substring(31,3)
$DMSlongmin = $udpstring.Substring(34,2)
# Get East West directional from UDP string
$directEW = $udpstring.Substring(44,1)
# Add decimal latitude minutes value to degrees
$declat = ($DMSlatdeg + ($DMSlatmin/60))
# Add decimal longitude minutes value to degrees
$declong = ($DMSlongdeg + ($DMSlongmin/60))
# Output formatted to three decimal places - variable value not changed
Write-Host "Decimal lat: " $declat.ToString("#.###") $directNS
Write-Host "Decimal long: " $declong.ToString("#.###") $directEW
In ISE I get the correct output:
Endpoint: 0.0.0.0:0
UDP client: System.Net.Sockets.UdpClient
Content: 36 71 80 71 71 65 44 [etc]
UDP string: $GPGGA,160516.13,[etc]
Decimal lat: [correct value] N
Decimal long: [correct value] W
In the console, it gets out the first two statements and hangs:
Endpoint: 0.0.0.0:0
UDP client: System.Net.Sockets.UdpClient
Is there something wrong with the way I'm setting $content?
I have this script to encrypt and decrypt text.
Why is it that when converting the decrypted text byte array to ASCII there is a space in between each character?
#Encrypt:
$unencryptedData = "passwordToEncrypt"
$pfxPassword = "P#ssw0rd1"
$certLocation = "D:\Ava\CA\Scripts\Encryption\PFXfiles\f-signed.pfx"
$cert = New-Object 'System.Security.Cryptography.X509Certificates.X509Certificate2'($certLocation, $pfxPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$publicKey = $cert.PublicKey.Key.ToXmlString($false)
$privateKey = $cert.PrivateKey.ToXmlString($true)
$unencryptedDataAsByteArray = [System.Text.Encoding]::Unicode.GetBytes($unencryptedData)
$keySize = 16384
$rsaProvider = New-Object System.Security.Cryptography.RSACryptoServiceProvider($keySize)
$rsaProvider.FromXmlString($publicKey)
$encryptedDataAsByteArray = $rsaProvider.Encrypt($unencryptedDataAsByteArray, $false)
$encryptedDataAsString = [System.Convert]::ToBase64String($encryptedDataAsByteArray)
Write-Host "Encrypted password = $encryptedDataAsString"
#Decrypt:
$rsaProvider.FromXmlString($privateKey)
$encryptedDataAsByteArray = [System.Convert]::FromBase64String($encryptedDataAsString)
$decryptedDataAsByteArray = $rsaProvider.Decrypt($encryptedDataAsByteArray, $false)
$decryptedDataAsString = [System.Text.Encoding]::ASCII.GetString($decryptedDataAsByteArray)
###### "p a s s w o r d T o E n c r y p t " ######
#$decryptedDataAsString = [System.Text.Encoding]::Unicode.GetString($decryptedDataAsByteArray)
###### "passwordToEncrypt" ######
Write-Host "Decrypted password = $decryptedDataAsString"
Consult Character Encodings in the .NET Framework. [System.Text.Encoding]::Unicode is UTF-16LE so the character A is encoded as the 16-bit value 0x0041, bytes 0x41 0x00. [System.Text.Encoding]::ASCII is an 8-bit encoding so when you decode 0x41 0x00 with ASCII you get the characters A and NUL (not space) .
You have to decode your byte array with the same encoding you encoded it in.
In the line:
$unencryptedDataAsByteArray = [System.Text.Encoding]::Unicode.GetBytes($unencryptedData)
You are setting the unencrypted byte array to a Unicode string. This means 2 bytes in the array for every character in the string. When it is later decrypted, it is still 2 bytes per character.
You need to decrypt it back in reverse order. First, decrypt it back to Unicode. Then, if you need to go to ASCII, use one of the .Net Encoding.Convert methods.