Getting Generic Errors Trying To SNMP To Cisco Switches Using SNMPSharpNET - vb.net

I am trying to integrate into SNMP scanning with my application and have delved into Google to try and find examples, etc. I have thus come across the SNMPSharpNet DLL which has allowed me to start contacting devices using SNMP which is from this website.
However, I have two issues that are similary related:
I tried to refer to this website to determine what oID to use when trying to scan a Cisco Catalyst 2960 Switch but it returned nothing (no errors and no results). The only way I could get it to work correctly (pull everything) was to use an oID of 1. This then sets off to pull back everything out of the switch, so I could then use it as a reference to determine specific oIDs for specific required data.
Which leads me to my next question.... using an oID of 1 does seem to work, however, part way through it errors out with "The agent responded with an error" which doesn't really tell me anything. I get it everytime with attempting SNMP on different devices and it's not pulling back all of the data.
My code looks like this:
Sub GetNextResult()
Dim host As String = "xx.xx.xx.xx"
Dim community As String = "public"
Dim requestOid() As String
Dim result As Dictionary(Of Oid, AsnType)
Dim rootOid As Oid = New Oid("1")
Dim nextOid As Oid = rootOid
Dim keepGoing As Boolean = True
requestOid = New String() {rootOid.ToString()}
Dim snmp As SimpleSnmp = New SimpleSnmp(host, community)
snmp.SuppressExceptions = False
If Not snmp.Valid Then
Console.WriteLine("Invalid hostname/community.")
Exit Sub
End If
While keepGoing
result = snmp.GetNext(SnmpVersion.Ver1, New String() {nextOid.ToString()})
If result IsNot Nothing Then
Dim kvp As KeyValuePair(Of Oid, AsnType)
For Each kvp In result
If rootOid.IsRootOf(kvp.Key) Then
Console.WriteLine("{0}: ({1}) {2}", kvp.Key.ToString(), _
SnmpConstants.GetTypeName(kvp.Value.Type), _
kvp.Value.ToString())
nextOid = kvp.Key
Else
keepGoing = False
End If
Next
Else
Console.WriteLine("No results received.")
keepGoing = False
End If
End While
End Sub
I guess my question is: Is there some sort of reference I could refer to get a list of the different oIDs required for specific information? Or if not, can I continue to use an oID of 1 and try to fix why it continually errors out with a generic error?
Any help appreciated thanks.

If you happen to know what is a MIB browser, use it to check out standard MIB documents, and then you see that the OID of "iso" is the root of most OIDs in use. That guarantees that your WALK operation indeed dumps out the items you want.
I don't have a Cisco Catalyst 2960 so could not exactly reproduce what you mean by "it returned nothing". Don't expect a device to implement every OIDs list in a site such as OIDVIEW, as what you should resort to is always the device manual and vendor materials.
I checked snmpsharpnet documentation and found out that your code was derived from there. Sadly Milan failed to provide a WALK function, and the code fragment listed at http://www.snmpsharpnet.com/?page_id=108 can be misleading and therefore you get your second question.
The SNMP error is expected, as GET-NEXT should hit a NoSuchName error to indicate that all available OIDs are dumped out. However, the sample code from snmpsharpnet does not tell its users that's something to be expected. The GET-BULK based WALK sample is correct, as there will be no NoSuchName error.
(Not an advertisement though) For your reference, #SNMP has its Messenger.Walk and Messenger.BulkWalk methods (looks similar) that also shows how to make WALK operations.

Related

Scanning GlobalCatalog via System.DirectoryServices (VB.NET) throws occasional error

Objective : Create a simple VB.NET application to scan the GlobalCatalog with a basic filter, limited to predefined properties and write the results to a text file.
Method : Existing code below - this "works" but occasionally throws an exception : "System.DirectoryServices.SearchResultCollection.ResultsEnumerator.MoveNext() : More data is available"
Some browsing leads me to think (open to correction) that the issue is caused by attempting to retrieve large volumes of records (in my case roughly 400k) via the DirectorySearcher, despite the results being paginated, and that the solution may be to switch the existing System.DirectoryServices method for something utilising System.DirectoryServices.Protocols. See this SO thread leading to this article.
However, all the responses I've found, both the links above and others from extensive searching, only provide code snippets in C#, and only seem to query a single record (e.g. retrieve properties based on a specific distinguishedName or login)
I need to retrieve a tonne of records, as quickly and efficiently as possible, using VB.NET. I like the DirectoryServices method because it gives me an easy handle on the GlobalCatalog without having to provide domains or passwords - I can jump straight into the searcher and start specifying filters and properties. And it generally works - but I need it to work every time.
Can anybody advise how I might adapt this code to circumvent the occasional exception and pull back all the data I need in the best possible way?
Imports System.DirectoryServices
Public Sub ScanGlobalCatalog()
Dim searcher As DirectorySearcher = ActiveDirectory.Forest.GetCurrentForest.FindGlobalCatalog.GetDirectorySearcher
Try
With searcher
.Filter = "(&(|(objectClass=user)(objectClass=group))(proxyAddresses=*))"
.PageSize = 1000
.SearchScope = SearchScope.Subtree
.CacheResults = False
.PropertiesToLoad.Add("sAMAccountName")
.PropertiesToLoad.Add("distinguishedName")
.PropertiesToLoad.Add("displayName")
.PropertiesToLoad.Add("proxyAddresses")
End With
For Each result As SearchResult In searcher.FindAll()
Dim properties As ResultPropertyCollection = result.Properties
Dim sAMAccountName As ResultPropertyValueCollection = properties("sAMAccountName")
Dim distinguishedName As ResultPropertyValueCollection = properties("distinguishedName")
Dim displayName As ResultPropertyValueCollection = properties("displayName")
Dim proxyAddresses As ResultPropertyValueCollection = properties("proxyAddresses")
' Check / process / write each property to the output file...
Next
Catch ex As Exception
' Do something...
End Try
End Sub
Thanks Vesper!
Added as shown and it doesn't seem to be happening any more (I believe setting .SizeLimit to 0 equates to 'limitless' but again, open to correction from those with greater knowledge than I...)
With searcher
.Filter = "(&(|(objectClass=user)(objectClass=group))(proxyAddresses=*))"
.PageSize = 1000
.SizeLimit = 0
.SearchScope = SearchScope.Subtree
.CacheResults = False
.PropertiesToLoad.Add("sAMAccountName")
.PropertiesToLoad.Add("distinguishedName")
.PropertiesToLoad.Add("displayName")
.PropertiesToLoad.Add("proxyAddresses")
End With
Have been running the script as a service at 15 minute intervals for the last 20 hours or so and I can see 5 or 6 'fails' in the event log - however, previously this would have caused a fatal termination (the service would simply discontinue); now it simply reports the exception and tries again on the next iteration.
The fails are clumped together (consecutive 'runs' within the same hour / hour and a half) and the same service has run uninterrupted and error-free for around 15 hours now, leading me to suspect those fails may have coincided with some kind of maintenance being carried out on the server or some kind of record update mid-read which was throwing the exception. Again, would welcome any insight or opinions on this behaviour.
But I can live with 'occasional' exceptions like this as long as the script generally runs okay and doesn't fall over permanently if/when they arise.
Thanks again for the advice Vesper!

Make pc specific hash in vb.net

I want to make a program, which generate the same string each time, and it must be different on any each pc. So like HWID. After I have the string I send it into a php file on a remote host, the php handle it, and store it in the database.
On the first run it will make a new row in the table, but after 2nd run, it will select the row where the POST-ed hash = the hash in the table, and it has banned - not banned function. So if I give back 0 the pc is not banned, so program start to run, if I give back 1 the program close.
This is all made, my problem is, I generate hwid from processorid, and send it to the php. the processorid can be the same on different computers sometimes. So if I give fake ban, the users will be angry for me...
The question is:
How to generate a hash, which will be always the same on the pc which run the application, but different on each pc?
I know I can make it if I store a special id on the pc for example in the registry, but if somebody reinstall the pc, he can use again the service. If I generate hwid, it will takes him more time to find out how to access again to the service.
I dont think this really has anything to do with PHP, but entirely about the client side steps.
To do what it sounds like you want, you want to use a hardware signature made up of several things so that if one or two are unavailable, the result is still valid. This will use a form of the WMI polling procedure from the answer on your last question:
Private Shared Function GetHardwareItemInfo(item As String, wmiclass As String) As String
Dim data As String = ""
Dim query As String = String.Format("Select {0} From {1}", item, wmiclass)
Using mbs As ManagementObjectSearcher = New ManagementObjectSearcher(query)
For Each mo As ManagementObject In mbs.Get
For Each pd As PropertyData In mo.Properties
' should be only one
If String.Compare(pd.Name, item, StringComparison.InvariantCultureIgnoreCase) = 0 Then
' value is object, test for Nothing
If pd.Value IsNot Nothing Then
data = pd.Value.ToString
End If
Exit For
End If
Next
Next
End Using
Return data
End Function
This allows you to poll for different items in different wmi classes using the same code. Example:
' get the serialnumber item from the baseboard class:
answer = GetHardwareItemInfo("serialNumber", "Win32_BaseBoard")
For a hardware signature:
Get and store the info for each item
Combine them into one string
Convert the string to a byte array
Use crypto to hash the byte array
convert the result to a base64 string
There are other ways. For instance you could encode the result as a Hex string, but the above is what the code shows. First, these are the namespaces you need:
Imports System.Security.Cryptography
Imports System.Management
Imports System.Text
Then the procedure to get the stuff using the GetHardwareItemInfo method above:
' place to store bits of data
Dim HWStuff As New List(Of String)
Dim answer As String
' get and store some info
answer = GetHardwareItemInfo("serialNumber", "Win32_BaseBoard")
HWStuff.Add(answer)
answer = GetHardwareItemInfo("uuid", "win32_ComputerSystemProduct")
HWStuff.Add(answer)
answer = GetHardwareItemInfo("serialNumber", "Win32_OperatingSystem")
HWStuff.Add(answer)
'...etc
' glue the bits together into one string
Dim HWSig = String.Join("", HWStuff)
Dim byteHash As Byte()
' create crypto hasher
Using hasher = New SHA1Managed()
' convert the string to bytes
Dim tmpBytes = Encoding.UTF8.GetBytes(HWSig)
'hash the bytes
byteHash = hasher.ComputeHash(tmpBytes)
End Using
' encode as B64 string.
Dim HWHash = Convert.ToBase64String(byteHash)
Console.WriteLine(HWHash)
Result:
MUjeLeZtbTQ3Rc8zgFquBkOwFzA=
You could glue the string together as you get answers. But during development it helps to see the candidate info before you decide to use it or not.
Notes:
There are many many things to choose from. See WMI Win32 Classes.
Not everything needs to come from WMI. the LocalMachine name might be a good one (I have no idea of the context for this) as is the Windows Activation Key.
Other crypto hashers will produce longer hashes
This is far from foolproof.
Some things can be spoofed - the Win OS Serial number can be changed in the registry. You dont really care if the values are right, just that they do not change.
This is not copy protection. Someone could sniff out the token(s) sent from a legitimate system(s), then patch your app to send that token only.
if I store a special id...
No. Do not write anything down. Its impossible to keep a secret from the user on their own PC. Dont store the hash either - generate it every time. If you write it down it is easier to copy that value to a different machine.
I give fake ban, the users will be angry for me...
Since it sounds like you are working from a blacklist rather than a whitelist, you dont have to worry about the hash failing. The worst that will happen is that a system which should be denied access will get access. If you want to further reduce the chance of a match, use SHA512Managed; it will produce a longer hash though.
If a user changes one of the parts you are polling, they will still get in - it is quite unlikely that the hash from 2 systems will match (one white, one black).

How to make SendKeys act Synchronously in IBM Host Access Library

I use the IBM Host Access Class Library for COM Automation as a way to communicate with an IBM AS400 (aka iSeries, IBM i, green screen, 5250) through a terminal emulator. I notice that when you issue a "SendKeys" instruction, control returns to your application before the IBM emulator finishes with the command. This can lead to timing problems because you might then send another "SendKeys" instruction before the system is ready to accept it.
For example:
Imports AutPSTypeLibrary
Imports AutConnListTypeLibrary
Imports AutSessTypeLibrary
Sub Example
Dim connections As New AutConnList
connections.Refresh()
If connections.Count < 1 Then Throw New InvalidOperationException("No AS400 screen can currently be found.")
Dim connection As IAutConnInfo = DirectCast(connections(1), IAutConnInfo)
_Session = New AutSess2
_Session.SetConnectionByHandle(connection.Handle)
Dim _Presentation As AutPS = DirectCast(_Session.autECLPS, AutPS)
_Presentation.SendKeys("PM70[enter]", 22, 8)
_Presentation.SendKeys("ND71221AD[enter]", 22, 20)
End Sub
would work correctly when stepping through code in a debugger, but would fail when running normally because the second instruction was sent too soon.
One way to work with this is to put a timer or loop after each command to slow the calling program down. I consider this less than ideal because the length of time is not always predictable, you will often be waiting longer than necessary to accommodate an occasional hiccup. This slows down the run time of the entire process.
Another way to work around this is to wait until there is a testable condition on the screen as a result of your sent command. This will work sometimes, but some commands do not cause a screen change to test and if you are looking to abstract your command calling into a class or subroutine, you would have to pass in what screen condition to be watching for.
What I would like to find is one of the "Wait" methods that will work in the general case. Options like the autECLScreenDesc class seem like they have to be tailored to very specific conditions.
The autECLPS (aka AutPS) class has a number of Wait methods (Wait, WaitForCursor, WaitWhileCursor, WaitForString, WaitWhileString, WaitForStringInRect, WaitWhileStringInRect, WaitForAttrib, WaitWhileAttrib, WaitForScreen, WaitWhileScreen) but they also seem to be waiting for specific conditions and do not work for the general case. The general case it important to me because I am actually trying to write a general purpose field update subroutine that can be called from many places inside and outside of my .dll.
This example is written in VB.NET, but I would expect the same behavior from C#, C++, VB6, Java; really anything that uses IBM's Personal Communications for Windows, Version 6.0
Host Access Class Library.
The "Operator Information Area" class seems to provide a solution for this problem.
My general case seems to be working correctly with this implementation:
Friend Sub PutTextWithEnter(ByVal field As FieldDefinition, ByVal value As String)
If IsNothing(field) Then Throw New ArgumentNullException("field")
If IsNothing(value) Then Throw New ArgumentNullException("value")
_Presentation.SendKeys(Mid(value.Trim, 1, field.Length).PadRight(field.Length) & "[enter]", field.Row, field.Column)
WaitForEmulator(_Session.Handle)
End Sub
Private Sub WaitForEmulator(ByVal EmulatorHandle As Integer)
Dim Oia As New AutOIATypeLibrary.AutOIA
Oia.SetConnectionByHandle(EmulatorHandle)
Oia.WaitForInputReady()
Oia.WaitForAppAvailable()
End Sub
I give thanks to a user named "khieyzer" on this message board for pointing our this clean and general-purpose solution.
Edit:
After a few weeks debugging and working through timing and resource release issues, this method now reads like:
Private Sub WaitForEmulator(ByRef NeededReset As Boolean)
Dim Oia As New AutOIA
Oia.SetConnectionByHandle(_Presentation.Handle)
Dim inhibit As InhibitReason = Oia.InputInhibited
If inhibit = InhibitReason.pcOtherInhibit Then
_Presentation.SendKeys("[reset]")
NeededReset = True
WaitForEmulator(NeededReset)
Marshal.ReleaseComObject(Oia)
Exit Sub
End If
If Not Oia.WaitForInputReady(6000) Then
If Oia.InputInhibited = InhibitReason.pcOtherInhibit Then
_Presentation.SendKeys("[reset]")
NeededReset = True
WaitForEmulator(NeededReset)
Marshal.ReleaseComObject(Oia)
Exit Sub
Else
Marshal.ReleaseComObject(Oia)
Throw New InvalidOperationException("The system has stopped responding.")
End If
End If
Oia.WaitForInputReady()
Oia.WaitForAppAvailable()
Marshal.ReleaseComObject(Oia)
End Sub

Reading and changing fields in SAP with RFC via VB .NET

I'm currently trying to figure out the basics of remote function calls via vb .NET. I feel pretty helpless however, because their just isn't any useful documentation for the most simple of tasks.
What I'm trying to do atm is starting the transaction CO13, write the confirmation number in the appropriate field and cancel the order. Even this simple tasks turned out to be a pain in the ass. I'm still not sure how to access and modify the contents of a specific field. There are some examples with tables for excel in the net, but hat's about it. What I have so far is this (login is working and in another function):
Public Function stornieren() As Boolean
Dim ordernr As String
Dim confirmationnr
Dim confirmation As Object
Dim R3 As Object
Dim CO13 As Object
Dim result
R3 = CreateObject("SAP.Functions")
ordernr = TextBox3.Text
confirmationnr = TextBox4.Text
CO13 = R3.Add("RFC_CALL_TRANSACTION_USING")
CO13.exports("TCODE") = "CO13"
CO13.exports("MODE") = "S"
confirmation = CO13.exports("RUECK")
confirmation.value = confirmationnr
result = CO13.call
End Function
RUECK is the Field Name. I want to write the value of "confirmationnr" into the field RUECK. "confirmation.value = confirmationnr" throws the error message "the object variable could not be determined" and "NullReferenceException" was not handled. Sounds to me like the object is empty.
Thanks in advance.
EDIT: Now trying via BAPIs and particularly BAPI_PRODORDCONF_CANCEL. I have no idea about the syntax though. Any help would be appreciated.

Better socket communication system

Currently, the client is sending messages like this:
Public Function checkMD5(ByVal userID As Integer, ByVal gameID As Integer, ByVal file As String, ByVal fileFull As String) As String
Dim make As New CMakeMSG
Dim md5 As New CMD5
make.append("checkfileMD5")
make.append(userID)
make.append(containerID)
make.append(file)
make.append(md5.GenerateFileHash(fileFull))
Return SocketSendAndReceiveMSG(make.makestring)
End Function
The server may receive something like this:
checkfileMD5-MSGDelimit0-12-MSGDelimit1-54-MSGDelimit2-filename.txt-MSGDelimit3-*md5hash*
Which it then reads out:
Private _message As String
Public Function handleMessage() As String
Dim brokenMessage As New ArrayList
brokenMessage = breakDown() 'Split to ArrayList
If brokenMessage(0) = "checkfileMD5" Then
Try
If brokenMessage.Count > 5 Then
Return "0-structureMessedUp"
End If
Return CompareFileMD5(brokenMessage(1), brokenMessage(2), brokenMessage(3), brokenMessage(4))
Catch ex As Exception
Return "0-structureMessedUp"
End Try
End If
End Function
So what it does is take the received message, and split it to an array using the -MSGDelimit- as a delimiter. So in this case the CompareFileMD5() function would receive 12,54,filename.txt,*md5hash*. And based on that it can return to the client whether or not the MD5 matched.
Sure, it works, but it feels sloppy and code on the server gets really messy.
Here's the less relevant functions from the above code (doubt it matters, but you never know):
Private Function breakDown() As ArrayList
Try
Dim theArray As New ArrayList
Dim copymsg As String = _message
Dim counter As Integer = 0
Do Until Not copymsg.Contains("-MSGDelimit")
Dim found As String
found = copymsg.Substring(0, copymsg.IndexOf("-MSGDelimit" & counter & "-"))
theArray.Add(found)
copymsg = copymsg.Replace(found & "-MSGDelimit" & counter & "-", "")
counter += 1
Loop
theArray.Add(copymsg)
Return theArray
Catch ex As Exception
Module1.msg(ex.Message)
End Try
End Function
Private Function CompareFileMD5(ByVal userID As Integer, ByVal gameID As Integer, ByVal filename As String, ByVal source As String) As String
Try
Dim tryFindFile As String = Module1.filedatabase.findfile(userID, gameID, filename)
If Not tryFindFile = "notFound" Then
Dim fileFull As String = tryFindFile & "\" & filename
Dim md5 As New CMD5
If md5.GenerateFileHash(fileFull) = source Then
Return "Match"
Else
Return "NoMatch"
End If
Else
Return "notFound"
End If
Catch ex As Exception
Module1.msg("0")
Return "0"
End Try
End Function
So, any advice on how to handle it better/cleaner/more professional?
Depending on the application, your current solution may be perfectly fine. There are a couple of things that do stand out a little bit:
The "protocol" is a bit heavy in terms of the amount of data sent. The delimiters between the data pieces adds quite a bit of overhead. In the example, it makes up maybe 50% of the payload. In addition, sending all data as text potentially makes the payload larger than absolutely necessary. All of this, though, is not necessarily a problem. If the traffic between the client and server is relatively light, then the extra data on the wire may not be a problem at all. For a request of this size (with or without the relatively high delimiter overhead), the main cost will be round trip costs and would likely change very little by reducing the size of this packet by half. If, though, there are requests with thousands of pieces of data, then reducing the payload size would help.
The use of the delimiters as shown is potentially ambiguous depending on the data sent. It is unlikely given the length and format of the delimiters, but it's something to keep in mind if there ever exists the possibility of having actual data that "looks" like a delimiter.
Assuming that the example shown is one of many similar protocols, I would be inclined to go a different route. One possibility would be to bundle up the request as a JSON object. There are existing packages available to create and read JSON. One example is Json.NET. JSON has a well-defined structure, it is easy for a human to read and verify, and it can be expanded easily. And depending on the data that you send, it would probably a little more lightweight than the current format. And (maybe the part you are interested in), it would maybe seem more "professional".
A couple of additional things that I would do (personal opinion):
Possibly add a client version to the data being sent so the server will know if it "recognizes" the request. Start the client version at some value (e.g., 1). If there are updates to the protocol format (e.g., different data, different structure), change the version to 2 in that release of the software. Then the server can look at the version number to see if it recognizes it. If it is the first version of the server and sees version 2, then it can return an error indicating the server needs to be updated. This is not necessary if you can guarantee that the client and server releases are always matched (sometimes this is hard in practice).
Use an integer value for the request type instead of a string ('checkFileMD5'). If there are going to be a large number of request types, the server can dispatch the request a little more efficiently (maybe) based on an integer value.