使用Win32-Ada绑定:SNMP

时间:2019-07-04 15:57:23

标签: winapi snmp ada gnat

我的问题是关于软件包提供的Win32-Ada绑定的:

我想使用软件包Win32.Mgmtapi中定义的以下功能:

function SnmpMgrOidToStr
  (oid    : access Win32.Snmp.AsnObjectIdentifier;
   string : access Win32.LPSTR)
  return Win32.BOOL;

我可以访问类型为Variable_Binding的变量Win32.Snmp.a_RFC1157VarBind_t。软件包Win32.Snmp中定义了以下四种类型:

type AsnObjectIdentifier is record
   idLength : Win32.UINT;
   ids      : Win32.PUINT;
end record;

subtype AsnObjectName is AsnObjectIdentifier;

type RFC1157VarBind is record
   name  : AsnObjectName;
   value : AsnObjectSyntax;
end record;

type a_RFC1157VarBind_t is access all RFC1157VarBind;

缩短的示例代码:

with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;

procedure Test is
begin
   -- [...]

   declare
      Variable_Binding : aliased Win32.Snmp.a_RFC1157VarBind_t := (...);

      OID_String : access Win32.LPSTR;

      Return_Value : Win32.BOOL;
   begin
      Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
        (oid    => Variable_Binding.name, -- type is not compatible
         string => OID_String);
   end;
end Test;

我的三个问题:

  1. 函数SnmpMgrOidToStr需要参数name的访问类型。类型为name定义的组件RFC1157VarBind与该访问类型不兼容。将变量name的组件Variable_Binding转换为访问类型有哪些选择?
  2. 我假设函数SnmpMgrOidToStr为类型OID_String的变量Win32.LPSTR分配了所需的内存。如何将类型Win32.LPSTR转换为Ada字符串(String / Unbounded_String)?
  3. 与Ada字符串对话后如何释放分配的内存?

更新

我添加了完整的SNMP示例。这需要启用并配置的Windows SNMP服务。程序输出应为:

Opened session.
Converted string to object identifier.
Copied object identifier.
Sent request.
Type is: 4
WORKSTATION
system.sysName.0
Closed session.

函数SnmpMgrRequest的文档指出:

  

请注意,必须使用SnmpUtilMemAlloc函数分配由SnmpVarBindList结构指向的SnmpVarBind数组。

根据函数SnmpUtilMemAlloc的文档,应使用函数SnmpUtilMemFree释放分配的内存。

我认为我正在使用以下过程调用正确分配内存:

-- Allocate memory
-- Allocated memory is not freed properly
SnmpUtilMemAlloc (Win32.UINT (Win32.Snmp.RFC1157VarBind'Size * System.Storage_Unit));

新问题:

  1. 此过程调用正确吗?
  2. 如何使用功能SnmpUtilMemFree释放分配的内存?

目前,根据工具 Dr无法正确释放内存。内存(版本2.2.0-1):

Error #1: LEAK 1536 direct bytes 0x0418b850-0x0418be50 + 0 indirect bytes
# 0 replace_RtlAllocateHeap               [d:\drmemory_package\common\alloc_replace.c:3771]
# 1 KERNELBASE.dll!GlobalAlloc           +0x6d     (0x75404015 <KERNELBASE.dll+0x14015>)
# 2 snmpapi.dll!SnmpUtilMemAlloc         +0xf      (0x73db1ee8 <snmpapi.dll+0x1ee8>)
# 3 _ada_snmp_example                     [C:/Users/username/Desktop/SNMPExample/src/snmp_example.adb:123]
# 4 main                                  [C:\Users\username\Desktop\SNMPExample\obj/b__snmp_example.adb:259]

完整源代码:

with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Interfaces.C.Strings;
with System;

with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;

procedure SNMP_Example
is
   -- Imported functions and procedures
   procedure SnmpUtilMemAlloc (nBytes : Win32.UINT);

   pragma Import (Stdcall, SnmpUtilMemAlloc, "SnmpUtilMemAlloc");

   procedure SnmpUtilMemFree (pMem : Win32.LPVOID);

   pragma Import (Stdcall, SnmpUtilMemFree, "SnmpUtilMemFree");

   function SnmpUtilOidCpy
     (DestObjId : access Win32.Snmp.AsnObjectIdentifier;
      SrcObjId  : access Win32.Snmp.AsnObjectIdentifier)
      return Win32.INT;

   pragma Import (Stdcall, SnmpUtilOidCpy, "SnmpUtilOidCpy");

   procedure SnmpUtilOidFree (Obj : access Win32.Snmp.AsnObjectIdentifier);

   pragma Import (Stdcall, SnmpUtilOidFree, "SnmpUtilOidFree");

   procedure SnmpUtilVarBindFree (VarBind : access Win32.Snmp.RFC1157VarBind);

   pragma Import (Stdcall, SnmpUtilVarBindFree, "SnmpUtilVarBindFree");

   procedure SnmpUtilVarBindListFree (VarBindList : access Win32.Snmp.RFC1157VarBindList);

   pragma Import (Stdcall, SnmpUtilVarBindListFree, "SnmpUtilVarBindListFree");

   -- Conversion related
   function To_LPVOID is new Ada.Unchecked_Conversion (Win32.LPSTR, Win32.LPVOID);

   function To_PCSTR is new Ada.Unchecked_Conversion (Win32.PBYTE, Win32.PCSTR);

   -- Linker options
   pragma Linker_Options ("-lmgmtapi");
   pragma Linker_Options ("-lsnmpapi");

   -- Connection related (Ada)
   IP_Address        : constant String  := "127.0.0.1";
   Community_String  : constant String  := "public";
   Timeout_MS        : constant Integer := 500;
   Number_Of_Retries : constant Integer := 1;

   -- Connection related (C)
   IP_Address_C       : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (IP_Address);
   Community_String_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (Community_String);

   -- Connection related (Win32)
   IP_Address_Win32        : constant Win32.LPSTR := Win32.To_PSTR (IP_Address_C);
   Community_String_Win32  : constant Win32.LPSTR := Win32.To_PSTR (Community_String_C);
   Timeout_MS_Win32        : constant Win32.INT := Win32.INT (Timeout_MS);
   Number_Of_Retries_Win32 : constant Win32.INT := Win32.INT (Number_Of_Retries);

   -- Comparison of types related
   use type Interfaces.C.int;
   use type Win32.BOOL;
   use type Win32.Mgmtapi.LPSNMP_MGR_SESSION;

   -- Session
   SNMP_Session : Win32.Mgmtapi.LPSNMP_MGR_SESSION;

   -- Custom types
   type Counter_Type is mod 2**32 - 1;
   type Gauge_Type is mod 2**32 - 1;
begin
   -- Open session
   SNMP_Session := Win32.Mgmtapi.SnmpMgrOpen
     (lpAgentAddress   => IP_Address_Win32,
      lpAgentCommunity => Community_String_Win32,
      nTimeOut         => Timeout_MS_Win32,
      nRetries         => Number_Of_Retries_Win32);

   if SNMP_Session = null then
      Ada.Text_IO.Put_Line ("Failed to open session.");
   else
      Ada.Text_IO.Put_Line ("Opened session.");

      Send_Request : declare
         -- Request related (Ada)
         SNMP_Object_Identifier_Ada : constant String := ".1.3.6.1.2.1.1.5.0";

         -- Request related (C)
         SNMP_Object_Identifier_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (SNMP_Object_Identifier_Ada);

         -- Request related (Win32)
         SNMP_Object_Identifier_Win32 : constant Win32.LPSTR := Win32.To_PSTR (SNMP_Object_Identifier_C);

         SNMP_Object_Identifier : aliased Win32.Snmp.AsnObjectIdentifier;

         Variable_Bindings       : aliased Win32.Snmp.RFC1157VarBindList;
         Variable_Bindings_Entry : aliased Win32.Snmp.RFC1157VarBind;

         Error_Status : aliased Win32.Snmp.AsnInteger;
         Error_Index  : aliased Win32.Snmp.AsnInteger;

         Return_Value_String_To_OID_Conversion : Win32.BOOL;
         Return_Value_OID_Copy                 : Win32.INT;
         Return_Value_Request                  : Win32.INT;
         Return_Value_OID_To_String_Conversion : Win32.BOOL;
      begin
         Return_Value_String_To_OID_Conversion := Win32.Mgmtapi.SnmpMgrStrToOid
           (string => SNMP_Object_Identifier_Win32,
            oid    => SNMP_Object_Identifier'Access);

         if Return_Value_String_To_OID_Conversion = 0 then
            Ada.Text_IO.Put_Line ("Failed to convert string to object identifier.");
         else
            Ada.Text_IO.Put_Line ("Converted string to object identifier.");

            -- Allocate memory
            -- Allocated memory is not freed properly
            SnmpUtilMemAlloc (Win32.UINT (Win32.Snmp.RFC1157VarBind'Size * System.Storage_Unit));

            Return_Value_OID_Copy := SnmpUtilOidCpy
              (DestObjId => Variable_Bindings_Entry.name'Unrestricted_Access,
               SrcObjId  => SNMP_Object_Identifier'Access);

            if Return_Value_OID_Copy = 0 then
               Ada.Text_IO.Put_Line ("Failed to copy object identifier.");
            else
               Ada.Text_IO.Put_Line ("Copied object identifier.");

               -- Construct variable bindings entry
               Variable_Bindings_Entry.value.asnType := Win32.Snmp.ASN_NULL;

               -- Construct variable bindings
               Variable_Bindings.len  := 1;
               Variable_Bindings.list := Variable_Bindings_Entry'Unchecked_Access;

               Return_Value_Request := Win32.Mgmtapi.SnmpMgrRequest
                 (session          => SNMP_Session,
                  requestType      => Win32.Snmp.ASN_RFC1157_GETREQUEST,
                  variableBindings => Variable_Bindings'Access,
                  errorStatus      => Error_Status'Access,
                  errorIndex       => Error_Index'Access);

               if Return_Value_Request = 0 then
                  Ada.Text_IO.Put_Line ("Failed to send request.");
               else
                  Ada.Text_IO.Put_Line ("Sent request.");

                  Ada.Text_IO.Put_Line ("Type is:" & Win32.BYTE'Image (Variable_Bindings.list.value.asnType));

                  case Variable_Bindings.list.value.asnType is
                     when Win32.Snmp.ASN_INTEGER =>
                        Ada.Text_IO.Put_Line (Integer'Image (Integer (Variable_Bindings.list.value.asnValue.number)));
                     when Win32.Snmp.ASN_OCTETSTRING =>
                        Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (Win32.To_Chars_Ptr (To_PCSTR (Variable_Bindings.list.value.asnValue.string.stream))));
                     when Win32.Snmp.ASN_RFC1155_COUNTER =>
                        Ada.Text_IO.Put_Line (Counter_Type'Image (Counter_Type (Variable_Bindings.list.value.asnValue.counter)));
                     when Win32.Snmp.ASN_RFC1155_GAUGE =>
                        Ada.Text_IO.Put_Line (Gauge_Type'Image (Gauge_Type (Variable_Bindings.list.value.asnValue.counter)));
                     when Win32.Snmp.ASN_RFC1155_TIMETICKS =>
                        Ada.Text_IO.Put_Line (Duration'Image (Duration (Variable_Bindings.list.value.asnValue.ticks) / 100));
                     when others =>
                        Ada.Text_IO.Put_Line ("Unsupported type.");
                  end case;

                  -- Convert object identifier to string
                  Convert_OID_To_String : declare
                     OID_String_Win32 : aliased Win32.LPSTR;
                  begin
                     Return_Value_OID_To_String_Conversion := Win32.Mgmtapi.SnmpMgrOidToStr
                       (oid    => Variable_Bindings.list.name'Unrestricted_Access,
                        string => OID_String_Win32'Unchecked_Access);

                     if Return_Value_OID_To_String_Conversion = 0 then
                        Ada.Text_IO.Put_Line ("Failed to convert object identifier to string.");
                     else
                        Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (Win32.To_Chars_Ptr (OID_String_Win32)));
                     end if;

                     SnmpUtilMemFree (To_LPVOID (OID_String_Win32));
                  end Convert_OID_To_String;

                  -- Free memory
                  SnmpUtilOidFree (SNMP_Object_Identifier'Access);

                  SnmpUtilVarBindFree (Variable_Bindings_Entry'Access);

                  SnmpUtilVarBindListFree (Variable_Bindings'Access);

                  Interfaces.C.Strings.Free (IP_Address_C);

                  Interfaces.C.Strings.Free (Community_String_C);

                  Interfaces.C.Strings.Free (SNMP_Object_Identifier_C);
               end if;
            end if;
         end if;
      end Send_Request;

      Close_Session : declare
         Return_Value : constant Win32.BOOL := Win32.Mgmtapi.SnmpMgrClose (SNMP_Session);
      begin
         if Return_Value = 0 then
            Ada.Text_IO.Put_Line ("Failed to close session.");
         else
            Ada.Text_IO.Put_Line ("Closed session.");
         end if;
      end Close_Session;
   end if;
end SNMP_Example;

1 个答案:

答案 0 :(得分:2)

不幸的是,我无法创建一个最小的工作示例来测试代码,因此我不能保证以下代码会真正起作用:

test.adb

with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Interfaces.C.Strings;

with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;

procedure Test is

   pragma Linker_Options ("-lMgmtapi");    --  for SnmpMgrOidToStr
   pragma Linker_Options ("-lSnmpapi");    --  for SnmpUtilMemFree

begin

   -- [...]

   declare

      --  Field "name" in type "RFC1157VarBind" is not aliased so you cannot
      --  use 'Unchecked_Access. I see two options here:
      --
      --    (1) Use 'Unrestricted_Access on the field "name".
      --    (2) Copy "VarBind_Ptr.name" into a new aliased variable and use 'Unchecked_Access.
      --
      --  I *assume* that the library behind "SnmpMgrOidToStr" will not "store"
      --  our pointer and dereferece it after we left this block of code (resulting
      --  in an invalid dereference). If this assumption holds, then we
      --  can use 'Unchecked_Access.
      --
      --  As "VarBind_Ptr" is already an access type, its field must also have a
      --  memory address (depite it not being defined as aliased). Hence, I
      --  think it's safe to use the very permissive 'Unrestricted_Access
      --  attribute here to obtain a reference.

      VarBind_Ptr  : Win32.Snmp.a_RFC1157VarBind_t;

      OID            : aliased Win32.Snmp.AsnObjectIdentifier := VarBind_Ptr.name;
      OID_String_Ptr : aliased Win32.LPSTR;

      Return_Value : Win32.BOOL;

   begin

      --  Option 1
      Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
        (oid    => VarBind_Ptr.name'Unrestricted_Access,
         string => OID_String_Ptr'Unchecked_Access);

      --  Option 2
      Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
        (oid    => OID'Unchecked_Access,
         string => OID_String_Ptr'Unchecked_Access);


      declare

         --  Win32.ads contains a convenience function "To_Chars_Ptr" to
         --  convert "Win32.LPSTR" to "Interfaces.C.Strings.chars_ptr". Hence,
         --  we can convert "OID_String_Ptr" to an Ada string via "chars_ptr":
         --
         --     Win32.LPSTR ---> chars_ptr ---> String
         --
         --  Note that the character array to which "Win32.LPSTR" points is, in
         --  this particular case, null terminated (see [1]). This might not
         --  always be the case [3].

         use Ada.Text_IO;
         use Interfaces.C.Strings;

      begin
         Put_Line (Value (Win32.To_Chars_Ptr (OID_String_Ptr)));
      end;


      declare

         --  The documentation [1] states that the string returned by
         --  "SnmpMgrOidToStr" should be freed using "SnmpUtilMemFree" [2]. This
         --  function seems to be missing in the win32ada library (?), so we
         --  need to import it ourselves.

         procedure SnmpUtilMemFree (pMem : Win32.LPVOID)
           with Import, Convention => Stdcall, Link_Name => "SnmpUtilMemFree";           

         function To_LPVOID is
            new Ada.Unchecked_Conversion (Win32.LPSTR, Win32.LPVOID);

      begin
         SnmpUtilMemFree (To_LPVOID (OID_String_Ptr));
      end;

   end;

end Test;

参考文献:

  • MS文档:SnmpMgrOidToStr [1]
  • MS文档:SnmpUtilMemFree [2]
  • MS Docs:LPSTR [3]