File Coverage

blib/lib/Win32/IPHelper.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Win32::IPHelper;
2              
3 1     1   7789 use 5.006;
  1         4  
  1         48  
4 1     1   7 use strict;
  1         2  
  1         40  
5             #use warnings;
6 1     1   5 use Carp;
  1         8  
  1         103  
7              
8 1     1   1117 use Socket qw(inet_ntoa inet_aton);
  1         4675  
  1         291  
9 1     1   2068 use Win32;
  0            
  0            
10             use Win32::API;
11             use enum;
12             use Config ();
13              
14             require Exporter;
15              
16             our @ISA = qw(Exporter);
17              
18             # Items to export into callers namespace by default. Note: do not export
19             # names by default without a very good reason. Use EXPORT_OK instead.
20             # Do not simply export all your public functions/methods/constants.
21              
22             # This allows declaration use Win32::IPHelper ':all';
23             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
24             # will save memory.
25             our %EXPORT_TAGS = (
26             'all' => [ qw(
27             AddIPAddress DeleteIPAddress
28             GetIfEntry
29             GetAdaptersInfo
30             GetInterfaceInfo
31             GetAdapterIndex
32             IpReleaseAddress IpRenewAddress
33             GetTcpTable AllocateAndGetTcpExTableFromStack GetExtendedTcpTable GetTcpTableAuto
34             GetUdpTable AllocateAndGetUdpExTableFromStack GetExtendedUdpTable GetUdpTableAuto
35             GetNetworkParams
36             ) ]
37             );
38              
39             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
40              
41             our @EXPORT = qw();
42              
43             our $VERSION = '0.08';
44              
45             my $GetProcessHeap = new Win32::API ('Kernel32', 'GetProcessHeap', [], 'N') or croak 'can\'t find GetProcessHeap() function';
46             my $AddIPAddress = new Win32::API ('Iphlpapi', 'AddIPAddress', ['N', 'N', 'N', 'P', 'P'], 'N') or croak 'can\'t find AddIPAddress() function';
47             my $DeleteIPAddress = new Win32::API ('Iphlpapi', 'DeleteIPAddress', ['N'], 'N') or croak 'can\'t find DeleteIPAddress() function';
48             my $GetIfEntry = new Win32::API ('Iphlpapi', 'GetIfEntry', ['P'], 'N') or croak 'can\'t find GetIfEntry() function';
49             my $GetAdaptersInfo = new Win32::API ('Iphlpapi', 'GetAdaptersInfo', ['P', 'P'], 'N') or croak 'can\'t find GetAdaptersInfo() function';
50             my $GetInterfaceInfo = new Win32::API ('Iphlpapi', 'GetInterfaceInfo', ['P', 'P'], 'N') or croak 'can\'t find GetInterfaceInfo() function';
51             my $GetAdapterIndex = new Win32::API ('Iphlpapi', 'GetAdapterIndex', ['P', 'P'], 'N') or croak 'can\'t find GetAdapterIndex() function';
52             my $IpReleaseAddress = new Win32::API ('Iphlpapi', 'IpReleaseAddress', ['P'], 'N') or croak 'can\'t find IpReleaseAddress() function';
53             my $IpRenewAddress = new Win32::API ('Iphlpapi', 'IpRenewAddress', ['P'], 'N') or croak 'can\'t find IpRenewAddress() function';
54             my $GetTcpTable = new Win32::API ('Iphlpapi', 'GetTcpTable', ['P', 'P', 'N'], 'N') or croak 'can\'t find GetTcpTable() function';
55             my $GetUdpTable = new Win32::API ('Iphlpapi', 'GetUdpTable', ['P', 'P', 'N'], 'N') or croak 'can\'t find GetUdpTable() function';
56             my $GetNetworkParams = new Win32::API ('Iphlpapi', 'GetNetworkParams', ['P','P'], 'N') or croak 'can\'t find GetNetworkParams() function';;
57             # UNDOCUMENTED # Available only on Windows XP/2003
58             my $AllocateAndGetTcpExTableFromStack = new Win32::API ('Iphlpapi', 'AllocateAndGetTcpExTableFromStack', ['P', 'N', 'N', 'N', 'N'], 'N');# or croak 'AllocateAndGetTcpExTableFromStack() function is not available on this platform';
59             my $AllocateAndGetUdpExTableFromStack = new Win32::API ('Iphlpapi', 'AllocateAndGetUdpExTableFromStack', ['P', 'N', 'N', 'N', 'N'], 'N');# or croak 'AllocateAndGetUdpExTableFromStack() function is not available on this platform';
60             # Available only on Windows Server 2003 SP1, Server 2008, XP SP2, Vista
61             my $GetExtendedTcpTable = new Win32::API ('Iphlpapi', 'GetExtendedTcpTable', ['P', 'P', 'N', 'N', 'N', 'N'], 'N');
62             my $GetExtendedUdpTable = new Win32::API ('Iphlpapi', 'GetExtendedUdpTable', ['P', 'P', 'N', 'N', 'N', 'N'], 'N');
63              
64             my $PTR_SIZE = $Config::Config{ptrsize};
65              
66             # Preloaded methods go here.
67              
68             use enum qw(
69             NO_ERROR=0
70             TABLE_SIZE=2048
71             :MAX_INTERFACE_
72             NAME_LEN=256
73             :MAX_ADAPTER_
74             ADDRESS_LENGTH=8
75             DESCRIPTION_LENGTH=128
76             NAME=128
77             NAME_LENGTH=256
78             :ERROR_
79             SUCCESS=0
80             NOT_SUPPORTED=50
81             INVALID_PARAMETER=87
82             BUFFER_OVERFLOW=111
83             INSUFFICIENT_BUFFER=122
84             NO_DATA=232
85             :MAXLEN_
86             IFDESCR=256
87             PHYSADDR=8
88             :MAX_
89             HOSTNAME_LEN=128
90             DOMAIN_NAME_LEN=128
91             SCOPE_ID_LEN=256
92             :UDP_TABLE_
93             OWNER_PID=1
94             :TCP_TABLE_
95             OWNER_PID_ALL=5
96             :AF_
97             INET=2
98             INET6=23
99             );
100              
101             # TCP States
102             my %TCP_STATES = (
103             1 => 'CLOSED',
104             2 => 'LISTENING',
105             3 => 'SYN_SENT',
106             4 => 'SYN_RCVD',
107             5 => 'ESTABLISHED',
108             6 => 'FIN_WAIT1',
109             7 => 'FIN_WAIT2',
110             8 => 'CLOSE_WAIT',
111             9 => 'CLOSING',
112             10 => 'LAST_ACK',
113             11 => 'TIME_WAIT',
114             12 => 'DELETE_TCB'
115             );
116              
117             our $DEBUG = 0;
118              
119             #################################
120             # PUBLIC Functions (exportable) #
121             #################################
122              
123             #######################################################################
124             # Win32::IPHelper::AddIPAddress()
125             #
126             # The AddIPAddress function adds the specified IP address to the
127             # specified adapter.
128             #
129             #######################################################################
130             # Usage:
131             # $ret = AddIPAddress($Address, $IpMask, $IfIndex, \$NTEContext, \$NTEInstance);
132             #
133             # Output:
134             # $ret = 0 for success, a number for error
135             #
136             # Input:
137             # $Address = IP address to add
138             # $IpMask = Subnet Mask for IP address
139             # $IfIndex = adapter index
140             #
141             # Output:
142             # \$NTEContext = ref to Net Table Entry context
143             # \$NTEInstance = ref to Net Table Entry instance
144             #
145             #######################################################################
146             # function AddIPAddress
147             #
148             # The AddIPAddress function adds the specified IP address to the
149             # specified adapter.
150             #
151             #
152             # DWORD AddIPAddress(
153             # IPAddr Address, // IP address to add
154             # IPMask IpMask, // subnet mask for IP address
155             # DWORD IfIndex, // index of adapter
156             # PULONG NTEContext, // Net Table Entry context
157             # PULONG NTEInstance // Net Table Entry Instance
158             # );
159             #
160             #######################################################################
161             sub AddIPAddress
162             {
163             if (scalar(@_) ne 5)
164             {
165             croak 'Usage: AddIPAddress(\$Address, \$IpMask, \$IfIndex, \\\$NTEContext, \\\$NTEInstance)';
166             }
167              
168             my $Address = unpack('L', inet_aton(shift));
169             my $IpMask = unpack('L', inet_aton(shift));
170             my $IfIndex = shift;
171              
172             my $NTEContext = shift;
173             my $NTEInstance = shift;
174              
175             # $AddIPAddress = new Win32::API ('Iphlpapi', 'AddIPAddress', ['N', 'N', 'N', 'P', 'P'], 'N') or croak 'can\'t find AddIPAddress() function';
176              
177             # initialize area for the NTE data
178             $$NTEContext = pack("L", 0);
179             $$NTEInstance = pack("L", 0);
180              
181             # function call
182             my $ret = $AddIPAddress->Call($Address, $IpMask, $IfIndex, $$NTEContext, $$NTEInstance);
183              
184             if ($ret != NO_ERROR)
185             {
186             $DEBUG and carp sprintf "The call to AddIPAddress() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
187             }
188              
189             # unpack values...
190             $$NTEContext = unpack("L", $$NTEContext);
191             $$NTEInstance = unpack("L", $$NTEInstance);
192              
193             return $ret;
194             }
195              
196             #######################################################################
197             # Win32::IPHelper::DeleteIPAddress()
198             #
199             # The DeleteIPAddress function deletes an IP address previously added
200             # using AddIPAddress.
201             #
202             #######################################################################
203             # Usage:
204             # $ret = DeleteIPAddress($NTEContext);
205             #
206             # Output:
207             # $ret = 0 for success, a number for error
208             #
209             # Input:
210             # $NTEContext = Net Table Entry context
211             #
212             #######################################################################
213             # function DeleteIPAddress
214             #
215             # The DeleteIPAddress function deletes an IP address previously added
216             # using AddIPAddress.
217             #
218             #
219             # DWORD DeleteIPAddress(
220             # ULONG NTEContext // Net Table Entry context
221             # );
222             #
223             #######################################################################
224             sub DeleteIPAddress
225             {
226             if (scalar(@_) ne 1)
227             {
228             croak 'Usage: DeleteIPAddress(\$NTEContext)';
229             }
230              
231             my $NTEContext = pack("L", shift);
232              
233             # $DeleteIPAddress = new Win32::API ('Iphlpapi', 'DeleteIPAddress', ['N'], 'N') or croak 'can\'t find DeleteIPAddress() function';
234              
235             # function call
236             my $ret = $DeleteIPAddress->Call(unpack('L', $NTEContext));
237              
238             if ($ret != NO_ERROR)
239             {
240             $DEBUG and carp sprintf "The call to DeleteIPAddress() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
241             }
242              
243             return $ret;
244             }
245              
246             #######################################################################
247             # Win32::IPHelper::GetIfEntry()
248             #
249             # The GetIfEntry function retrieves information for the specified
250             # interface on the local computer.
251             #
252             #######################################################################
253             # Usage:
254             # $ret = GetIfEntry($IfIndex, \%pIfRow);
255             #
256             # Output:
257             # $ret = 0 for success, a number for error
258             #
259             # Input:
260             # $IfIndex = adapter index
261             #
262             # Output:
263             # \%pIfRow = ref to the data structure
264             #
265             #######################################################################
266             # function GetIfEntry
267             #
268             # The GetIfEntry function retrieves information for the specified
269             # interface on the local computer.
270             #
271             # DWORD GetIfEntry(
272             # PMIB_IFROW pIfRow // pointer to interface entry
273             # );
274             #
275             #
276             #######################################################################
277             sub GetIfEntry
278             {
279             if (scalar(@_) ne 2)
280             {
281             croak 'Usage: GetIfEntry(\$IfIndex, \\\%pIfRow)';
282             }
283              
284             my $IfIndex = shift;
285             my $buffer = shift;
286              
287             # $GetIfEntry = new Win32::API ('Iphlpapi', 'GetIfEntry', ['P'], 'N') or croak 'can\'t find GetIfEntry() function';
288              
289             my $lpBuffer;
290             $lpBuffer .= pack("C@".MAX_INTERFACE_NAME_LEN*2, 0);
291             $lpBuffer .= pack("L", $IfIndex);
292             $lpBuffer .= pack("L@".16, 0);
293             $lpBuffer .= pack("C@".MAXLEN_PHYSADDR, 0);
294             $lpBuffer .= pack("L@".64, 0);
295             $lpBuffer .= pack("C@".MAXLEN_IFDESCR, 0);
296              
297             # first call just to read the size
298             my $ret = $GetIfEntry->Call($lpBuffer);
299              
300             if ($ret != NO_ERROR)
301             {
302             $DEBUG and carp sprintf "The call to GetIfEntry() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
303             }
304             else
305             {
306             (undef, %$buffer) = _MIB_IFROW(\$lpBuffer, 0);
307             }
308              
309             return $ret;
310             }
311              
312             #######################################################################
313             # Win32::IPHelper::GetAdaptersInfo()
314             #
315             # The GetAdaptersInfo function retrieves adapter information for the
316             # local computer.
317             #
318             #######################################################################
319             # Usage:
320             # $ret = GetAdaptersInfo(\@IP_ADAPTER_INFO);
321             #
322             # Output:
323             # $ret = 0 for success, a number for error
324             #
325             # Input:
326             # \@array = reference to the array to be filled with decoded data
327             #
328             #######################################################################
329             # function GetAdaptersInfo
330             #
331             # The GetAdaptersInfo function retrieves adapter information for the
332             # local computer.
333             #
334             # DWORD GetAdaptersInfo(
335             # PIP_ADAPTER_INFO pAdapterInfo, // buffer to receive data
336             # PULONG pOutBufLen // size of data returned
337             # );
338             #
339             #######################################################################
340             sub GetAdaptersInfo
341             {
342             if (scalar(@_) ne 1)
343             {
344             croak 'Usage: GetAdaptersInfo(\\\@IP_ADAPTER_INFO)';
345             }
346              
347             my $buffer = shift;
348             my $base_size = 2048;
349              
350             # $GetAdaptersInfo = new Win32::API ('Iphlpapi', 'GetAdaptersInfo', ['P', 'P'], 'N') or croak 'can\'t find GetAdaptersInfo() function';
351              
352             # initialize area for the buffer size
353             my $lpBuffer = pack("L@".$base_size, 0);
354             my $lpSize = pack("L", $base_size);
355              
356             # first call just to read the size
357             my $ret = $GetAdaptersInfo->Call($lpBuffer, $lpSize);
358              
359             # check returned value...
360             if ($ret != NO_ERROR)
361             {
362             if ($ret == ERROR_BUFFER_OVERFLOW)
363             {
364             # initialize area for the buffer content
365             $base_size = unpack("L", $lpSize);
366             $lpBuffer = pack("L@".$base_size, 0);
367              
368             # second call to read data
369             $ret = $GetAdaptersInfo->Call($lpBuffer, $lpSize);
370             if ($ret != NO_ERROR)
371             {
372             $DEBUG and carp sprintf "The call to GetAdaptersInfo() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
373             return $ret;
374             }
375             }
376             else
377             {
378             $DEBUG and carp sprintf "The call to GetAdaptersInfo() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
379             return $ret;
380             }
381             }
382              
383             # decode data into the supplied buffer area
384             (undef, @$buffer) = _IP_ADAPTER_INFO(\$lpBuffer, 0);
385              
386             return 0;
387             }
388              
389              
390             #######################################################################
391             # Win32::IPHelper::GetInterfaceInfo()
392             #
393             # The GetInterfaceInfo function obtains a list of the network interface
394             # adapters on the local system.
395             #
396             #######################################################################
397             # Usage:
398             # $ret = GetInterfaceInfo(\%IP_INTERFACE_INFO);
399             #
400             # Output:
401             # $ret = 0 for success, a number for error
402             #
403             # Input:
404             # \%hash = reference to the hash to be filled with decoded data
405             #
406             #######################################################################
407             # function GetInterfaceInfo
408             #
409             # The GetInterfaceInfo function obtains a list of the network interface
410             # adapters on the local system.
411             #
412             # DWORD GetInterfaceInfo(
413             # PIP_INTERFACE_INFO pIfTable, // buffer to receive info
414             # PULONG dwOutBufLen // size of buffer
415             # );
416             #
417             #######################################################################
418             sub GetInterfaceInfo
419             {
420             if (scalar(@_) ne 1)
421             {
422             croak 'Usage: GetInterfaceInfo(\\\%IP_INTERFACE_INFO)';
423             }
424              
425             my $buffer = shift;
426             my $base_size = 2048;
427              
428             # $GetInterfaceInfo = new Win32::API ('Iphlpapi', 'GetInterfaceInfo', ['P', 'P'], 'N') or croak 'can\'t find GetInterfaceInfo() function';
429              
430             # initialize area for the buffer size
431             my $lpBuffer = pack("L@".$base_size, 0);
432             my $lpSize = pack("L", $base_size);
433              
434             # first call just to read the size
435             my $ret = $GetInterfaceInfo->Call($lpBuffer, $lpSize);
436              
437             # check returned value...
438             if ($ret != NO_ERROR)
439             {
440             if ($ret == ERROR_INSUFFICIENT_BUFFER)
441             {
442             # initialize area for the buffer content
443             $base_size = unpack("L", $lpSize);
444             $lpBuffer = pack("L@".$base_size, 0);
445              
446             # second call to read data
447             $ret = $GetInterfaceInfo->Call($lpBuffer, $lpSize);
448             if ($ret != NO_ERROR)
449             {
450             $DEBUG and carp sprintf "The call to GetInterfaceInfo() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
451             return $ret;
452             }
453             }
454             else
455             {
456             $DEBUG and carp sprintf "The call to GetInterfaceInfo() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
457             return $ret;
458             }
459             }
460              
461             # decode data into the supplied buffer area
462             (undef, %$buffer) = _IP_INTERFACE_INFO(\$lpBuffer, 0);
463              
464             return 0;
465             }
466              
467              
468             #######################################################################
469             # Win32::IPHelper::GetAdapterIndex(\$AdapterName, \$IfIndex)
470             #
471             # The GetAdapterIndex function obtains the index of an adapter, given
472             # its name.
473             #
474             #######################################################################
475             #
476             # Prototype
477             # DWORD GetAdapterIndex(
478             # LPWSTR AdapterName,
479             # PULONG IfIndex
480             # );
481             #
482             # Parameters
483             # AdapterName
484             # [in] Pointer to a Unicode string that specifies the name of the adapter.
485             # IfIndex
486             # [out] Pointer to a ULONG variable that points to the index of the adapter.
487             #
488             # Return Values
489             # If the function succeeds, the return value is NO_ERROR.
490             # If the function fails, use FormatMessage to obtain the message string for the returned error.
491             #
492             #######################################################################
493             sub GetAdapterIndex
494             {
495             if (scalar(@_) ne 2)
496             {
497             croak 'Usage: GetAdapterIndex(\\\$AdapterName, \\\$IfIndex)';
498             }
499              
500             my $AdapterName = shift;
501             my $IfIndex = shift;
502              
503             # prepare the buffer for IfIndex
504             $$IfIndex = pack('L', 0);
505              
506             # $GetAdapterIndex = new Win32::API ('Iphlpapi', 'GetAdapterIndex', ['P', 'P'], 'N') or croak 'can\'t find GetAdapterIndex() function';
507              
508             # function call
509             my $ret = $GetAdapterIndex->Call(_ToUnicodeSz('\DEVICE\TCPIP_'.$$AdapterName), $$IfIndex);
510              
511             if ($ret != NO_ERROR)
512             {
513             $DEBUG and carp sprintf "The call to GetAdapterIndex() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
514             return $ret;
515             }
516              
517             # unpack IfIndex for later use
518             $$IfIndex = unpack('L', $$IfIndex);
519              
520             return $ret;
521             }
522              
523              
524             #######################################################################
525             # Win32::IPHelper::IpReleaseAddress(\%IP_ADAPTER_INDEX_MAP)
526             #
527             # The IpReleaseAddress function releases an IP address previously
528             # obtained through Dynamic Host Configuration Protocol (DHCP).
529             #
530             #######################################################################
531             #
532             # Prototype
533             # DWORD IpReleaseAddress(
534             # PIP_ADAPTER_INDEX_MAP AdapterInfo
535             # );
536             #
537             # Parameters
538             # AdapterInfo
539             # [in] Pointer to an IP_ADAPTER_INDEX_MAP structure that
540             # specifies the adapter associated with the IP address to release.
541             #
542             # Return Values
543             # If the function succeeds, the return value is NO_ERROR.
544             # If the function fails, use FormatMessage to obtain the message string for the returned error.
545             #
546             #######################################################################
547             sub IpReleaseAddress
548             {
549             if (scalar(@_) ne 1)
550             {
551             croak 'Usage: IpReleaseAddress(\\\%IP_ADAPTER_INDEX_MAP)';
552             }
553              
554             my $AdapterInfo = shift;
555              
556             # prepare the IP_ADAPTER_INDEX_MAP structure
557             my $ip_adapter_index_map = pack("L", $$AdapterInfo{'Index'});
558             $ip_adapter_index_map .= pack("Z*@".(2 * MAX_ADAPTER_NAME), _ToUnicodeSz($$AdapterInfo{'Name'}));
559              
560             # $IpReleaseAddress = new Win32::API ('Iphlpapi', 'IpReleaseAddress', ['P'], 'N') or croak 'can\'t find IpReleaseAddress() function';
561              
562             # function call
563             my $ret = $IpReleaseAddress->Call($ip_adapter_index_map);
564              
565             if ($ret != NO_ERROR)
566             {
567             $DEBUG and carp sprintf "The call to IpReleaseAddress() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
568             }
569             return $ret;
570             }
571              
572              
573             #######################################################################
574             # Win32::IPHelper::IpRenewAddress(\%IP_ADAPTER_INDEX_MAP)
575             #
576             # The IpRenewAddress function renews a lease on an IP address previously
577             # obtained through Dynamic Host Configuration Protocol (DHCP).
578             #
579             #######################################################################
580             #
581             # Prototype
582             # DWORD IpRenewAddress(
583             # PIP_ADAPTER_INDEX_MAP AdapterInfo
584             # );
585             #
586             # Parameters
587             # AdapterInfo
588             # [in] Pointer to an IP_ADAPTER_INDEX_MAP structure that
589             # specifies the adapter associated with the IP address to renew.
590             #
591             # Return Values
592             # If the function succeeds, the return value is NO_ERROR.
593             # If the function fails, use FormatMessage to obtain the message string for the returned error.
594             #
595             #######################################################################
596             sub IpRenewAddress
597             {
598             if (scalar(@_) ne 1)
599             {
600             croak 'Usage: IpRenewAddress(\\\%IP_ADAPTER_INDEX_MAP)';
601             }
602              
603             my $AdapterInfo = shift;
604              
605             # prepare the IP_ADAPTER_INDEX_MAP structure
606             my $ip_adapter_index_map = pack("L", $$AdapterInfo{'Index'});
607             $ip_adapter_index_map .= pack("Z*@".(2 * MAX_ADAPTER_NAME), _ToUnicodeSz($$AdapterInfo{'Name'}));
608              
609             # $IpRenewAddress = new Win32::API ('Iphlpapi', 'IpRenewAddress', ['P'], 'N') or croak 'can\'t find IpRenewAddress() function';
610              
611             # function call
612             my $ret = $IpRenewAddress->Call($ip_adapter_index_map);
613              
614             if ($ret != NO_ERROR)
615             {
616             $DEBUG and carp sprintf "The call to IpRenewAddress() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
617             }
618             return $ret;
619             }
620              
621              
622             #######################################################################
623             # Win32::IPHelper::GetTcpTable(\@TCP_TABLE, $bOrder)
624             #
625             # The GetTcpTable function retrieves the TCP connection table.
626             #
627             #######################################################################
628             #
629             # Prototype
630             # DWORD GetTcpTable(
631             # PMIB_TCPTABLE pTcpTable,
632             # PDWORD pdwSize,
633             # BOOL bOrder
634             # );
635             #
636             # Parameters
637             # pTcpTable
638             # [out] Pointer to a buffer that receives the TCP connection table as a MIB_TCPTABLE structure.
639             # pdwSize
640             # [in, out] On input, specifies the size of the buffer pointed to by the pTcpTable parameter.
641             # On output, if the buffer is not large enough to hold the returned connection table, the function sets this parameter equal to the required buffer size.
642             # bOrder
643             # [in] Specifies whether the connection table should be sorted.
644             # If this parameter is TRUE, the table is sorted in the order of:
645             # 1 - Local IP address
646             # 2 - Local port
647             # 3 - Remote IP address
648             # 4 - Remote port
649             #
650             # Return Values
651             # If the function succeeds, the return value is NO_ERROR.
652             # If the function fails, use FormatMessage to obtain the message string for the returned error.
653             #
654             #######################################################################
655             sub GetTcpTable
656             {
657             if (scalar(@_) ne 2)
658             {
659             croak 'Usage: GetTcpTable(\\\@TCP_TABLE, \$bOrder)';
660             }
661              
662             my $TCPTABLE = shift;
663             my $order = shift(@_) ? 1 : 0;
664              
665             my $size = 2048;
666              
667             my $pTcpTable = pack("C@".$size, 0);
668             my $pdwSize = pack('L', $size);
669             my $bOrder = $order;
670              
671             # function call
672             my $ret = $GetTcpTable->Call($pTcpTable, $pdwSize, $bOrder);
673              
674             if ($ret != ERROR_SUCCESS)
675             {
676             if ($ret == ERROR_INSUFFICIENT_BUFFER)
677             {
678             my $multi = int(unpack('L', $pdwSize) / $size) + 1;
679             $size = $size * $multi;
680              
681             $pTcpTable = pack("C@".$size, 0);
682             $pdwSize = pack('L', $size);
683              
684             $ret = $GetTcpTable->Call($pTcpTable, $pdwSize, $bOrder);
685             if ($ret != ERROR_SUCCESS)
686             {
687             $DEBUG and carp sprintf "GetTcpTable() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
688             return $ret;
689             }
690             }
691             else
692             {
693             $DEBUG and carp sprintf "The call to GetTcpTable() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
694             return $ret;
695             }
696             }
697              
698             my $pos = 0;
699             my $elements = 0;
700             my $value;
701              
702             ($pos, $elements) = _shiftunpack(\$pTcpTable, $pos, 4, 'L');
703              
704             for (1..$elements)
705             {
706             my %hash;
707             my $data;
708              
709             ($pos, $data) = _shiftunpack(\$pTcpTable, $pos, 20, 'L a4 nx2 a4 nx2');
710              
711             my $cnt = 0;
712              
713             $hash{'State'} = $TCP_STATES{$data->[0]};
714             $hash{'LocalAddr'} = inet_ntoa($data->[1]);
715             $hash{'LocalPort'} = $data->[2];
716             $hash{'RemoteAddr'} = inet_ntoa($data->[3]);
717             $hash{'RemotePort'} = $data->[0] eq 2 ? 0 : $data->[4];
718              
719             push @$TCPTABLE, \%hash;
720             }
721             }
722              
723              
724             #######################################################################
725             # Win32::IPHelper::AllocateAndGetTcpExTableFromStack(\@TCP_EX_TABLE, $bOrder)
726             #
727             # UNDOCUMENTED # Retrieves the same list as GetTcpTable()
728             # with the addditional ProcessId for each connection
729             #
730             #######################################################################
731             #
732             # Prototype
733             # DWORD (WINAPI *pAllocateAndGetTcpExTableFromStack)(
734             # PMIB_TCPEXTABLE *pTcpTable, // buffer for the connection table
735             # BOOL bOrder, // sort the table?
736             # HANDLE heap,
737             # DWORD zero,
738             # DWORD flags
739             # );
740             #
741             # Parameters
742             # pTcpTable
743             # [out] Pointer to a TCP_EX_TABLE connection table structure
744             # bOrder
745             # [in] Specifies whether the connection table should be sorted.
746             # If this parameter is TRUE, the table is sorted in the order of:
747             # 1 - Local IP address
748             # 2 - Local port
749             # 3 - Remote IP address
750             # 4 - Remote port
751             # heap
752             # [in] Handle to the heap of the calling process, obtained by GetProcessHeap()
753             # zero
754             # [in] undocumented
755             # flags
756             # [in] undocumented
757             #
758             # Return Values
759             # If the function succeeds, the return value is NO_ERROR.
760             # If the function fails, use FormatMessage to obtain the message string for the returned error.
761             #
762             #######################################################################
763             sub AllocateAndGetTcpExTableFromStack
764             {
765             unless ($AllocateAndGetTcpExTableFromStack)
766             {
767             carp 'AllocateAndGetTcpExTableFromStack() function is not available on this platform';
768             return;
769             }
770              
771             if (scalar(@_) ne 2)
772             {
773             croak 'Usage: AllocateAndGetTcpExTableFromStack(\\\@TCP_EX_TABLE, \$bOrder)';
774             }
775              
776             my $TCPTABLE = shift;
777             my $order = shift(@_) ? 1 : 0;
778              
779             my $pTcpExTable = pack('L', 0);
780             my $bOrder = $order;
781             my $zero = 0;
782             my $flags = 2;
783              
784             # function call
785             my $ret = $AllocateAndGetTcpExTableFromStack->Call($pTcpExTable, $bOrder, _GetProcessHeap(), $zero, $flags);
786              
787             if ($ret != NO_ERROR)
788             {
789             $DEBUG and carp sprintf "The call to AllocateAndGetTcpExTableFromStack() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
790             return $ret;
791             }
792              
793             my $elements = unpack('L', unpack('P4', $pTcpExTable));
794             my $TcpExTable = unpack('P' . (24 * $elements + 4) , $pTcpExTable);
795              
796             my $pos = 0;
797             my $value;
798              
799             ($pos, $elements) = _shiftunpack(\$TcpExTable, $pos, 4, 'L');
800              
801             for (1..$elements)
802             {
803             my %hash;
804             my $data;
805              
806             ($pos, $data) = _shiftunpack(\$TcpExTable, $pos, 24, 'L a4 nx2 a4 nx2 L');
807              
808             $hash{'State'} = $TCP_STATES{$data->[0]};
809             $hash{'LocalAddr'} = inet_ntoa($data->[1]);
810             $hash{'LocalPort'} = $data->[2];
811             $hash{'RemoteAddr'} = inet_ntoa($data->[3]);
812             $hash{'RemotePort'} = $data->[0] eq 2 ? 0 : $data->[4];
813             $hash{'ProcessId'} = $data->[5];
814              
815             push @$TCPTABLE, \%hash;
816             }
817              
818             return $ret;
819             }
820              
821             #######################################################################
822             # Win32::IPHelper::GetExtendedTcpTable(\@TCP_EX_TABLE, $bOrder)
823             #
824             # Retrieves the same list as GetTcpTable()
825             # with the addditional ProcessId for each connection
826             #
827             # Since AllocateAndGetTcpExTableFromStack is deprecated.
828             # For Windows Server 2003 SP1, Server 2008, XP SP2, Vista
829             #
830             #######################################################################
831             #
832             # Prototype
833             # DWORD GetExtendedTcpTable(
834             # __out PVOID pTcpTable,
835             # __inout PDWORD pdwSize,
836             # __in BOOL bOrder,
837             # __in ULONG ulAf,
838             # __in TCP_TABLE_CLASS TableClass,
839             # __in ULONG Reserved
840             # );
841             #
842             # pTcpTable
843             # A pointer to the table structure that contains the filtered TCP endpoints.
844             #
845             # pdwSize
846             # The estimated size of the structure returned in pTcpTable, in bytes.
847             #
848             # bOrder
849             # A value that specifies whether the TCP endpoint table should be sorted.
850             #
851             # ulAf
852             # IPv4 or IPv6. Here it is IPv4.
853             #
854             # TCP_TABLE_CLASS
855             # The type of the TCP table structure to retrieve. Here it is TCP_TABLE_OWNER_PID_ALL.
856             #
857             # Reserved
858             # This value must be zero.
859             #
860             # Return Values
861             # If the function succeeds, the return value is NO_ERROR.
862             # If the function fails, use FormatMessage to obtain the message string for the returned error.
863             #
864             #######################################################################
865             sub GetExtendedTcpTable
866             {
867             unless ($GetExtendedTcpTable)
868             {
869             carp '$GetExtendedTcpTable() function is not available on this platform';
870             return;
871             }
872              
873             # IPv6
874             # my $nargv = @_;
875             # unless ( $nargv > 1 && $nargv < 4 ) {
876             # croak 'Usage: $GetExtendedTcpTable(\\\@TCP_EX_TABLE, \$bOrder, [\$ipv6])';
877             # }
878             unless ( @_ == 2 )
879             {
880             croak 'Usage: $GetExtendedTcpTable(\\\@TCP_EX_TABLE, \$bOrder)';
881             }
882            
883             my $TCPTABLE = shift;
884             my $bOrder = shift(@_) ? 1 : 0;
885             my $pdwSize = pack 'L', TABLE_SIZE;
886             my $pTcpExTable = pack 'L@'.TABLE_SIZE, 0;
887             my $ulAf = shift(@_) ? AF_INET6 : AF_INET; # preparation, AF_INET6 returns different table
888             my $TABLE_CLASS = TCP_TABLE_OWNER_PID_ALL;
889             my $Reserved = 0;
890              
891             # function call
892             my $ret = $GetExtendedTcpTable->Call(
893             $pTcpExTable,
894             $pdwSize,
895             $bOrder,
896             $ulAf,
897             $TABLE_CLASS,
898             $Reserved,
899             );
900              
901             if ($ret != NO_ERROR)
902             {
903             $DEBUG and carp sprintf "The call to GetExtendedTcpTable() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
904             return $ret;
905             }
906            
907             # Beware. Not for AF_INET6. Different table structure.
908             # Can't test yet.
909            
910             my ($pos,$elements) = _shiftunpack(\$pTcpExTable,0,4,'L');
911              
912             for (1..$elements)
913             {
914             my %hash;
915             my $data;
916            
917             ($pos, $data) = _shiftunpack(\$pTcpExTable,$pos,24,'L a4 nx2 a4 nx2 L');
918            
919             $hash{ State } = $TCP_STATES{$data->[0]};
920             $hash{ LocalAddr } = inet_ntoa($data->[1]);
921             $hash{ LocalPort } = $data->[2];
922             $hash{ RemoteAddr } = inet_ntoa($data->[3]);
923             $hash{ RemotePort } = $data->[0] eq 2 ? 0 : $data->[4];
924             $hash{ ProcessId } = $data->[5];
925            
926             push @$TCPTABLE, \%hash;
927             }
928              
929             return $ret;
930             }
931              
932             #
933             # wrapper for GetExtendedTcpTable/AllocateAndGetTcpExTableFromStack/GetTcpTable
934             #
935             sub GetTcpTableAuto
936             {
937             $GetExtendedTcpTable && goto &GetExtendedTcpTable;
938             $AllocateAndGetTcpExTableFromStack && goto &AllocateAndGetTcpExTableFromStack;
939             $GetTcpTable && goto &GetTcpTable;
940             carp 'GetTcpTableAuto() function is not available on this platform';
941             }
942              
943             #######################################################################
944             # Win32::IPHelper::GetUdpTable(\@UDP_TABLE, $bOrder)
945             #
946             # The GetUdpTable function retrieves the User Datagram Protocol (UDP) listener table.
947             #
948             #######################################################################
949             #
950             # Prototype
951             # DWORD GetUdpTable(
952             # PMIB_UDPTABLE pUdpTable,
953             # PDWORD pdwSize,
954             # BOOL bOrder
955             # );
956             #
957             # Parameters
958             # pTcpTable
959             # [out] Pointer to a buffer that receives the UDP listener table as a MIB_UDPTABLE structure.
960             # pdwSize
961             # [in, out] On input, specifies the size of the buffer pointed to by the pUdpTable parameter.
962             # On output, if the buffer is not large enough to hold the returned connection table,
963             # the function sets this parameter equal to the required buffer size.
964             # bOrder
965             # [in] Specifies whether the connection table should be sorted.
966             # If this parameter is TRUE, the table is sorted in the order of:
967             # 1 - Local IP address
968             # 2 - Local port
969             #
970             # Return Values
971             # If the function succeeds, the return value is NO_ERROR.
972             # If the function fails, use FormatMessage to obtain the message string for the returned error.
973             #
974             #######################################################################
975             sub GetUdpTable
976             {
977             if (scalar(@_) ne 2)
978             {
979             croak 'Usage: GetUdp(\\\@UDP_TABLE, \$bOrder)';
980             }
981              
982             my $UDPTABLE = shift;
983             my $order = shift(@_) ? 1 : 0;
984              
985             my $size = 2048;
986              
987             my $pUdpTable = pack("C@".$size, 0);
988             my $pdwSize = pack('L', $size);
989             my $bOrder = $order;
990              
991             # function call
992             my $ret = $GetUdpTable->Call($pUdpTable, $pdwSize, $bOrder);
993              
994             if ($ret != ERROR_SUCCESS)
995             {
996             if ($ret == ERROR_INSUFFICIENT_BUFFER)
997             {
998             my $multi = int(unpack('L', $pdwSize) / $size) + 1;
999             $size = $size * $multi;
1000              
1001             $pUdpTable = pack("C@".$size, 0);
1002             $pdwSize = pack('L', $size);
1003              
1004             $ret = $GetUdpTable->Call($pUdpTable, $pdwSize, $bOrder);
1005             if ($ret != ERROR_SUCCESS)
1006             {
1007             $DEBUG and carp sprintf "The call to GetUdpTable() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
1008             return $ret;
1009             }
1010             }
1011             else
1012             {
1013             $DEBUG and carp sprintf "The call to GetUdpTable() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
1014             return $ret;
1015             }
1016             }
1017              
1018             my $pos = 0;
1019             my $elements = 0;
1020             my $value;
1021              
1022             ($pos, $elements) = _shiftunpack(\$pUdpTable, $pos, 4, 'L');
1023              
1024             for (1..$elements)
1025             {
1026             my %hash;
1027             my $data;
1028              
1029             ($pos, $data) = _shiftunpack(\$pUdpTable, $pos, 8, 'a4 n');
1030              
1031             $hash{'LocalAddr'} = inet_ntoa($data->[0]);
1032             $hash{'LocalPort'} = $data->[1];
1033              
1034             push @$UDPTABLE, \%hash;
1035             }
1036             }
1037              
1038              
1039             #######################################################################
1040             # Win32::IPHelper::AllocateAndGetUdpExTableFromStack(\@UDP_EX_TABLE, $bOrder)
1041             #
1042             # UNDOCUMENTED # Retrieves the same list as GetUdpTable()
1043             # with the addditional ProcessId for each connection
1044             #
1045             #######################################################################
1046             #
1047             # Prototype
1048             # DWORD (WINAPI *pAllocateAndGetUdpExTableFromStack)(
1049             # PMIB_TCPEXTABLE *pUdpTable, // buffer for the connection table
1050             # BOOL bOrder, // sort the table?
1051             # HANDLE heap,
1052             # DWORD zero,
1053             # DWORD flags
1054             # );
1055             #
1056             # Parameters
1057             # pUdpTable
1058             # [out] Pointer to a UDP_EX_TABLE connection table structure
1059             # bOrder
1060             # [in] Sort the table by LocalAddr ?
1061             #
1062             # Return Values
1063             # If the function succeeds, the return value is NO_ERROR.
1064             # If the function fails, use FormatMessage to obtain the message string for the returned error.
1065             #
1066             #######################################################################
1067             sub AllocateAndGetUdpExTableFromStack
1068             {
1069             unless ($AllocateAndGetUdpExTableFromStack)
1070             {
1071             carp 'AllocateAndGetUdpExTableFromStack() function is not available on this platform';
1072             return;
1073             }
1074              
1075             if (scalar(@_) ne 2)
1076             {
1077             croak 'Usage: AllocateAndGetUdpExTableFromStack(\\\@UDP_EX_TABLE, \$bOrder)';
1078             }
1079              
1080             my $UDPTABLE = shift;
1081             my $order = shift(@_) ? 1 : 0;
1082              
1083             my $pUdpExTable = pack('L', 0);
1084             my $bOrder = $order;
1085             my $zero = 0;
1086             my $flags = 2;
1087              
1088             # function call
1089             my $ret = $AllocateAndGetUdpExTableFromStack->Call($pUdpExTable, $bOrder, _GetProcessHeap(), $zero, $flags);
1090              
1091             if ($ret != NO_ERROR)
1092             {
1093             $DEBUG and carp sprintf "The call to AllocateAndGetUdpExTableFromStack() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
1094             return $ret;
1095             }
1096              
1097             my $elements = unpack('L', unpack('P4', $pUdpExTable));
1098             my $UdpExTable = unpack('P' . (12 * $elements + 4) , $pUdpExTable);
1099              
1100             my $pos = 0;
1101             my $value;
1102              
1103             ($pos, $elements) = _shiftunpack(\$UdpExTable, $pos, 4, 'L');
1104              
1105             for (1..$elements)
1106             {
1107             my %hash;
1108             my $data;
1109              
1110             ($pos, $data) = _shiftunpack(\$UdpExTable, $pos, 12, 'a4 nx2 L');
1111              
1112             $hash{'LocalAddr'} = inet_ntoa($data->[0]);
1113             $hash{'LocalPort'} = $data->[1];
1114             $hash{'ProcessId'} = $data->[2];
1115              
1116             push @$UDPTABLE, \%hash;
1117             }
1118              
1119             return $ret;
1120             }
1121              
1122             #######################################################################
1123             # Win32::IPHelper::GetExtendedUdpTable(\@TCP_EX_TABLE, $bOrder)
1124             #
1125             # Retrieves the same list as GetUdpTable()
1126             # with the addditional ProcessId for each connection
1127             #
1128             # Since AllocateAndGetUdpExTableFromStack is deprecated.
1129             # For Windows Server 2003 SP1, Server 2008, XP SP2, Vista
1130             #
1131             #######################################################################
1132             #
1133             # Prototype
1134             # DWORD GetExtendedUdpTable(
1135             # __out PVOID pUdpTable,
1136             # __inout PDWORD pdwSize,
1137             # __in BOOL bOrder,
1138             # __in ULONG ulAf,
1139             # __in UDP_TABLE_CLASS TableClass,
1140             # __in ULONG Reserved
1141             # );
1142             #
1143             # pUdpTable
1144             # A pointer to the table structure that contains the filtered UDP endpoints.
1145             #
1146             # pdwSize
1147             # The estimated size of the structure returned in pUdpTable, in bytes.
1148             #
1149             # bOrder
1150             # A value that specifies whether the UDP endpoint table should be sorted.
1151             #
1152             # ulAf
1153             # IPv4 or IPv6. Here it is IPv4.
1154             #
1155             # UDP_TABLE_CLASS
1156             # The type of the UDP table structure to retrieve. Here it is UDP_TABLE_OWNER_PID.
1157             #
1158             # Reserved
1159             # This value must be zero.
1160             #
1161             # Return Values
1162             # If the function succeeds, the return value is NO_ERROR.
1163             # If the function fails, use FormatMessage to obtain the message string for the returned error.
1164             #
1165             #######################################################################
1166             sub GetExtendedUdpTable
1167             {
1168             unless ($GetExtendedUdpTable)
1169             {
1170             carp '$GetExtendedUcpTable() function is not available on this platform';
1171             return;
1172             }
1173              
1174             # IPv6
1175             # my $nargv = @_;
1176             # unless ( $nargv > 1 && $nargv < 4 ) {
1177             # croak 'Usage: $GetExtendedUdpTable(\\\@UDP_EX_TABLE, \$bOrder, [\$ipv6])';
1178             # }
1179             unless ( @_ == 2 )
1180             {
1181             croak 'Usage: $GetExtendedUdpTable(\\\@UDP_EX_TABLE, \$bOrder)';
1182             }
1183            
1184             my $UDPTABLE = shift;
1185             my $bOrder = shift(@_) ? 1 : 0;
1186             my $pdwSize = pack 'L', TABLE_SIZE;
1187             my $pTcpExTable = pack 'L@'.TABLE_SIZE, 0;
1188             my $ulAf = shift(@_) ? AF_INET6 : AF_INET; # preparation, AF_INET6 returns different table
1189             my $TABLE_CLASS = UDP_TABLE_OWNER_PID;
1190             my $Reserved = 0;
1191              
1192             # function call
1193             my $ret = $GetExtendedUdpTable->Call(
1194             $pTcpExTable,
1195             $pdwSize,
1196             $bOrder,
1197             $ulAf,
1198             $TABLE_CLASS,
1199             $Reserved,
1200             );
1201            
1202             if ($ret != NO_ERROR)
1203             {
1204             $DEBUG and carp sprintf "The call to GetExtendedUdpTable() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
1205             return $ret;
1206             }
1207              
1208             # Beware. Not for AF_INET6. Different table structure.
1209             # Can't test yet.
1210            
1211             my ($pos,$elements) = _shiftunpack(\$pTcpExTable,0,4,'L');
1212            
1213             for (1..$elements)
1214             {
1215             my %hash;
1216             my $data;
1217            
1218             ($pos,$data) = _shiftunpack(\$pTcpExTable,$pos,12,'a4 nx2 L');
1219            
1220             $hash{ LocalAddr } = inet_ntoa($data->[0]);
1221             $hash{ LocalPort } = $data->[1];
1222             $hash{ ProcessId } = $data->[2];
1223            
1224             push @$UDPTABLE, \%hash;
1225             }
1226            
1227             return $ret;
1228             }
1229              
1230             #
1231             # wrapper for GetExtendedTcpTable/AllocateAndGetTcpExTableFromStack/GetUdpTable
1232             #
1233             sub GetUdpTableAuto
1234             {
1235             $GetExtendedUdpTable && goto &GetExtendedUdpTable;
1236             $AllocateAndGetUdpExTableFromStack && goto &AllocateAndGetUdpExTableFromStack;
1237             $GetUdpTable && goto &GetUdpTable;
1238             carp 'GetUdpTableAuto() function is not available on this platform';
1239             }
1240              
1241             #######################################################################
1242             # Win32::IPHelper::GetNetworkParams()
1243             #
1244             # The GetNetworkParams function retrieves adapter information for the
1245             # local computer.
1246             #
1247             #######################################################################
1248             # Usage:
1249             # $ret = GetNetworkParams(\%FIXED_INFO);
1250             #
1251             # Output:
1252             # $ret = 0 for success, a number for error
1253             #
1254             # Input:
1255             # \%hash = reference to the hash to be filled with decoded data
1256             #
1257             #######################################################################
1258             # function GetNetworkParams
1259             #
1260             # The GetNetworkParams function retrieves network
1261             # parameters for the local computer.
1262             #
1263             # DWORD GetNetworkParams(
1264             # PFIXED_INFO pFixedInfo,
1265             # PULONG pOutBufLen
1266             # );
1267             #
1268             #######################################################################
1269             sub GetNetworkParams
1270             {
1271             if (scalar(@_) ne 1)
1272             {
1273             croak 'Usage: GetNetworkParams(\\\%FIXED_INFO)';
1274             }
1275              
1276             my $buffer = shift;
1277             my $base_size = 2048;
1278              
1279             # initialize area for the buffer size
1280             my $lpBuffer = pack("L@".$base_size, 0);
1281             my $lpSize = pack("L", $base_size);
1282              
1283             # first call just to read the size
1284             my $ret = $GetNetworkParams->Call($lpBuffer, $lpSize);
1285              
1286             # check returned value...
1287             if ($ret != NO_ERROR)
1288             {
1289             if ($ret == ERROR_BUFFER_OVERFLOW)
1290             {
1291             # initialize area for the buffer content
1292             $base_size = unpack("L", $lpSize);
1293             $lpBuffer = pack("L@".$base_size, 0);
1294              
1295             # second call to read data
1296             $ret = $GetNetworkParams->Call($lpBuffer, $lpSize);
1297             if ($ret != NO_ERROR)
1298             {
1299             $DEBUG and carp sprintf "The call to GetNetworkParams() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
1300             return $ret;
1301             }
1302             }
1303             else
1304             {
1305             $DEBUG and carp sprintf "The call to GetNetworkParams() returned %u: %s\n", $ret, Win32::FormatMessage($ret);
1306             return $ret;
1307             }
1308             }
1309              
1310             # decode data into the supplied buffer area
1311             (undef, %$buffer) = _FIXED_INFO(\$lpBuffer, 0);
1312              
1313             return 0;
1314             }
1315              
1316              
1317             ####################################
1318             # PRIVATE Functions (not exported) #
1319             ####################################
1320              
1321             #######################################################################
1322             # _MIB_IFROW()
1323             #
1324             # The MIB_IFROW structure stores information about a particular
1325             # interface.
1326             #
1327             #######################################################################
1328             # Usage:
1329             # ($pos, %hash) = _MIB_IFROW(\$buffer, $position);
1330             #
1331             # Output:
1332             # $pos = new position in buffer (for the next call)
1333             # %hash = the decoded data structure
1334             #
1335             # Input:
1336             # \$buffer = reference to the buffer to decode
1337             # $position = first byte to decode
1338             #
1339             #######################################################################
1340             # struct MIB_IFROW
1341             #
1342             # The MIB_IFROW structure stores information about a particular
1343             # interface.
1344             #
1345             # typedef struct _MIB_IFROW {
1346             # WCHAR wszName[MAX_INTERFACE_NAME_LEN];
1347             # DWORD dwIndex; // index of the interface
1348             # DWORD dwType; // type of interface
1349             # DWORD dwMtu; // max transmission unit
1350             # DWORD dwSpeed; // speed of the interface
1351             # DWORD dwPhysAddrLen; // length of physical address
1352             # BYTE bPhysAddr[MAXLEN_PHYSADDR]; // physical address of adapter
1353             # DWORD dwAdminStatus; // administrative status
1354             # DWORD dwOperStatus; // operational status
1355             # DWORD dwLastChange; // last time operational status changed
1356             # DWORD dwInOctets; // octets received
1357             # DWORD dwInUcastPkts; // unicast packets received
1358             # DWORD dwInNUcastPkts; // non-unicast packets received
1359             # DWORD dwInDiscards; // received packets discarded
1360             # DWORD dwInErrors; // erroneous packets received
1361             # DWORD dwInUnknownProtos; // unknown protocol packets received
1362             # DWORD dwOutOctets; // octets sent
1363             # DWORD dwOutUcastPkts; // unicast packets sent
1364             # DWORD dwOutNUcastPkts; // non-unicast packets sent
1365             # DWORD dwOutDiscards; // outgoing packets discarded
1366             # DWORD dwOutErrors; // erroneous packets sent
1367             # DWORD dwOutQLen; // output queue length
1368             # DWORD dwDescrLen; // length of bDescr member
1369             # BYTE bDescr[MAXLEN_IFDESCR]; // interface description
1370             # } MIB_IFROW,*PMIB_IFROW;
1371             #
1372             #######################################################################
1373             sub _MIB_IFROW
1374             {
1375             my ($buffer, $pos) = @_;
1376             my %hash;
1377              
1378             ($pos, $hash{'Name'}) = _shiftunpack($buffer, $pos, MAX_INTERFACE_NAME_LEN*2, "Z" . MAX_INTERFACE_NAME_LEN*2);
1379             ($pos, $hash{'Index'}) = _shiftunpack($buffer, $pos, 4, "L");
1380             ($pos, $hash{'Type'}) = _shiftunpack($buffer, $pos, 4, "L");
1381             ($pos, $hash{'Mtu'}) = _shiftunpack($buffer, $pos, 4, "L");
1382             ($pos, $hash{'Speed'}) = _shiftunpack($buffer, $pos, 4, "L");
1383             ($pos, $hash{'PhysAddrLen'}) = _shiftunpack($buffer, $pos, 4, "L");
1384             ($pos, $hash{'PhysAddr'}) = _shiftunpack($buffer, $pos, MAXLEN_PHYSADDR, "H" . MAXLEN_PHYSADDR * 2);
1385             ($pos, $hash{'AdminStatus'}) = _shiftunpack($buffer, $pos, 4, "L");
1386             ($pos, $hash{'OperStatus'}) = _shiftunpack($buffer, $pos, 4, "L");
1387             ($pos, $hash{'LastChange'}) = _shiftunpack($buffer, $pos, 4, "L");
1388             ($pos, $hash{'InOctets'}) = _shiftunpack($buffer, $pos, 4, "L");
1389             ($pos, $hash{'InUcastPkts'}) = _shiftunpack($buffer, $pos, 4, "L");
1390             ($pos, $hash{'InNUcastPkts'}) = _shiftunpack($buffer, $pos, 4, "L");
1391             ($pos, $hash{'InDiscards'}) = _shiftunpack($buffer, $pos, 4, "L");
1392             ($pos, $hash{'InErrors'}) = _shiftunpack($buffer, $pos, 4, "L");
1393             ($pos, $hash{'InUnknownProtos'}) = _shiftunpack($buffer, $pos, 4, "L");
1394             ($pos, $hash{'OutOctets'}) = _shiftunpack($buffer, $pos, 4, "L");
1395             ($pos, $hash{'OutUcastPkts'}) = _shiftunpack($buffer, $pos, 4, "L");
1396             ($pos, $hash{'OutNUcastPkts'}) = _shiftunpack($buffer, $pos, 4, "L");
1397             ($pos, $hash{'OutDiscards'}) = _shiftunpack($buffer, $pos, 4, "L");
1398             ($pos, $hash{'OutErrors'}) = _shiftunpack($buffer, $pos, 4, "L");
1399             ($pos, $hash{'OutQLen'}) = _shiftunpack($buffer, $pos, 4, "L");
1400             ($pos, $hash{'DescrLen'}) = _shiftunpack($buffer, $pos, 4, "L");
1401             ($pos, $hash{'Descr'}) = _shiftunpack($buffer, $pos, MAXLEN_IFDESCR, "Z" . MAXLEN_IFDESCR * 2);
1402              
1403             return ($pos, %hash);
1404             }
1405              
1406              
1407             #######################################################################
1408             # _IP_ADAPTER_INFO()
1409             #
1410             # Decodes an IP_ADAPTER_INFO data structure and returns data
1411             # into a Perl array
1412             #
1413             #######################################################################
1414             # Usage:
1415             # ($pos, @array) = _IP_ADAPTER_INFO(\$buffer, $position);
1416             #
1417             # Output:
1418             # $pos = new position in buffer (for the next call)
1419             # @array = the decoded data structure
1420             #
1421             # Input:
1422             # \$buffer = reference to the buffer to decode
1423             # $position = first byte to decode
1424             #
1425             #######################################################################
1426             # struct IP_ADAPTER_INFO
1427             #
1428             # The IP_ADAPTER_INFO structure contains information about a particular
1429             # network adapter on the local computer.
1430             #
1431             # typedef struct _IP_ADAPTER_INFO {
1432             # struct _IP_ADAPTER_INFO* Next;
1433             # DWORD ComboIndex;
1434             # char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
1435             # char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
1436             # UINT AddressLength;
1437             # BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
1438             # DWORD Index;
1439             # UINT Type;
1440             # UINT DhcpEnabled;
1441             # PIP_ADDR_STRING CurrentIpAddress;
1442             # IP_ADDR_STRING IpAddressList;
1443             # IP_ADDR_STRING GatewayList;
1444             # IP_ADDR_STRING DhcpServer;
1445             # BOOL HaveWins;
1446             # IP_ADDR_STRING PrimaryWinsServer;
1447             # IP_ADDR_STRING SecondaryWinsServer;
1448             # time_t LeaseObtained;
1449             # time_t LeaseExpires;
1450             # } IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
1451             #
1452             #######################################################################
1453             sub _IP_ADAPTER_INFO
1454             {
1455             my ($buffer, $pos) = @_;
1456             my $size = $PTR_SIZE + 636;
1457             my %hash;
1458             my @array;
1459             my $next;
1460              
1461             ($pos, $next) =_shiftunpack($buffer, $pos, $PTR_SIZE, "P".$size);
1462              
1463             ($pos, $hash{'ComboIndex'}) = _shiftunpack($buffer, $pos, 4, "L");
1464             ($pos, $hash{'AdapterName'}) = _shiftunpack($buffer, $pos, (MAX_ADAPTER_NAME_LENGTH + 4), "Z" . (MAX_ADAPTER_NAME_LENGTH + 4));
1465             ($pos, $hash{'Description'}) = _shiftunpack($buffer, $pos, (MAX_ADAPTER_DESCRIPTION_LENGTH + 4), "Z" . (MAX_ADAPTER_DESCRIPTION_LENGTH + 4));
1466             ($pos, $hash{'AddressLength'}) = _shiftunpack($buffer, $pos, 4, "L");
1467             ($pos, $hash{'Address'}) = _shiftunpack($buffer, $pos, MAX_ADAPTER_ADDRESS_LENGTH, "H" . MAX_ADAPTER_ADDRESS_LENGTH * 2);
1468             ($pos, $hash{'Index'}) = _shiftunpack($buffer, $pos, 4, "L");
1469             ($pos, $hash{'Type'}) = _shiftunpack($buffer, $pos, 4, "L");
1470             ($pos, $hash{'DhcpEnabled'}) = _shiftunpack($buffer, $pos, 4, "L");
1471              
1472             my $CurrentIpAddress;
1473             ($pos, $CurrentIpAddress) = _shiftunpack($buffer, $pos, 4, "P40");
1474             if ($CurrentIpAddress)
1475             {
1476             @{ $hash{'CurrentIpAddress'} } = _IP_ADDR_STRING(\$CurrentIpAddress, 0);
1477             }
1478              
1479             ($pos, @{ $hash{'IpAddressList'} }) = _IP_ADDR_STRING($buffer, $pos);
1480              
1481             ($pos, @{ $hash{'GatewayList'} }) = _IP_ADDR_STRING($buffer, $pos);
1482             ($pos, @{ $hash{'DhcpServer'} }) = _IP_ADDR_STRING($buffer, $pos);
1483              
1484             ($pos, $hash{'HaveWins'}) = _shiftunpack($buffer, $pos, 4, "L");
1485              
1486             ($pos, @{ $hash{'PrimaryWinsServer'} }) = _IP_ADDR_STRING($buffer, $pos);
1487             ($pos, @{ $hash{'SecondaryWinsServer'} }) = _IP_ADDR_STRING($buffer, $pos);
1488              
1489             ($pos, $hash{'LeaseObtained'}) =_shiftunpack($buffer, $pos, 4, "L");
1490             ($pos, $hash{'LeaseExpires'}) =_shiftunpack($buffer, $pos, 4, "L");
1491              
1492             push @array, \%hash;
1493              
1494             if ($next)
1495             {
1496             my ($pos, @results) = _IP_ADAPTER_INFO(\$next, 0);
1497             push @array, @results;
1498             }
1499              
1500             return ($pos, @array);
1501             }
1502              
1503             #######################################################################
1504             # _ADDR_STRING_STRUCT_SIZE
1505             #
1506             # Constant for the size of a IP_ADDR_STRING/PIP_ADDR_STRING struct.
1507             # This varies depending on whether it is running in 32bit or 64bit perl
1508             #
1509             #######################################################################
1510             # Usage:
1511             # $size = _ADDR_STRING_STRUCT_SIZE;
1512             #
1513             # Output:
1514             # $size - size of the structs
1515             #
1516             # Input:
1517             # None
1518             #######################################################################
1519             sub _ADDR_STRING_STRUCT_SIZE() {
1520             return $PTR_SIZE # struct *
1521             + 16 # char[16]
1522             + 16 # char[16]
1523             + 4; # DWORD
1524             }
1525              
1526             #######################################################################
1527             # _IP_ADDR_STRING()
1528             #
1529             # Decodes an _IP_ADDR_STRING data structure and returns data
1530             # into a Perl array
1531             #
1532             #######################################################################
1533             # Usage:
1534             # ($pos, @array) = _IP_ADDR_STRING(\$buffer, $position);
1535             #
1536             # Output:
1537             # $pos = new position in buffer (for the next call)
1538             # @array = the decoded data structure
1539             #
1540             # Input:
1541             # \$buffer = reference to the buffer to decode
1542             # $position = first byte to decode
1543             #
1544             #######################################################################
1545             # struct IP_ADDR_STRING
1546             #
1547             # The IP_ADDR_STRING structure represents a node in a linked-list
1548             # of IP addresses.
1549             #
1550             # typedef struct _IP_ADDR_STRING {
1551             # struct _IP_ADDR_STRING* Next;
1552             # IP_ADDRESS_STRING IpAddress;
1553             # IP_MASK_STRING IpMask;
1554             # DWORD Context;
1555             # } IP_ADDR_STRING, *PIP_ADDR_STRING;
1556             #
1557             #######################################################################
1558             sub _IP_ADDR_STRING
1559             {
1560             my ($buffer, $pos) = @_;
1561             my $size = _ADDR_STRING_STRUCT_SIZE;
1562             my %hash;
1563             my @array;
1564             my $next;
1565              
1566             ($pos, $next) = _shiftunpack($buffer, $pos, $PTR_SIZE, "P".$size);
1567              
1568             ($pos, $hash{'IpAddress'}) = _shiftunpack($buffer, $pos, 16, "Z16");
1569             ($pos, $hash{'IpMask'}) = _shiftunpack($buffer, $pos, 16, "Z16");
1570             ($pos, $hash{'Context'}) = _shiftunpack($buffer, $pos, 4, "L");
1571              
1572             push @array, \%hash;
1573              
1574             if ($next)
1575             {
1576             my ($pos, @results) = _IP_ADDR_STRING(\$next, 0);
1577             push @array, @results;
1578             }
1579              
1580             return ($pos, @array);
1581             }
1582              
1583             #######################################################################
1584             # _IP_ADAPTER_INDEX_MAP()
1585             #
1586             # Decodes an _IP_ADAPTER_INDEX_MAP data structure and returns data
1587             # into a Perl hash
1588             #
1589             #######################################################################
1590             # Usage:
1591             # ($pos, %hash) = _IP_ADAPTER_INDEX_MAP(\$buffer, $position);
1592             #
1593             # Output:
1594             # $pos = new position in buffer (for the next call)
1595             # %hash = the decoded data structure
1596             #
1597             # Input:
1598             # \$buffer = reference to the buffer to decode
1599             # $position = first byte to decode
1600             #
1601             #######################################################################
1602             # struct IP_ADAPTER_INDEX_MAP
1603             #
1604             # The IP_ADAPTER_INDEX_MAP structure pairs an adapter name with
1605             # the index of that adapter.
1606             #
1607             # typedef struct _IP_ADAPTER_INDEX_MAP {
1608             # ULONG Index // adapter index
1609             # WCHAR Name [MAX_ADAPTER_NAME]; // name of the adapter
1610             # } IP_ADAPTER_INDEX_MAP, * PIP_ADAPTER_INDEX_MAP;
1611             #
1612             #######################################################################
1613             sub _IP_ADAPTER_INDEX_MAP
1614             {
1615             my $size = 4 + 4;
1616             wantarray or return $size;
1617              
1618             my ($buffer, $pos) = @_;
1619             my %hash;
1620             my $NamePtr;
1621              
1622             ($pos, $hash{'Index'}) = _shiftunpack($buffer, $pos, 4, "L");
1623             ($pos, $hash{'Name'}) = _shiftunpackWCHAR($buffer, $pos, (2 * MAX_ADAPTER_NAME));
1624              
1625             return ($pos, %hash);
1626             }
1627              
1628              
1629             #######################################################################
1630             # _IP_INTERFACE_INFO()
1631             #
1632             # Decodes an _IP_INTERFACE_INFO data structure and returns data
1633             # into a Perl array
1634             #
1635             #######################################################################
1636             # Usage:
1637             # ($pos, @array) = _IP_INTERFACE_INFO(\$buffer, $position);
1638             #
1639             # Output:
1640             # $pos = new position in buffer (for the next call)
1641             # @array = the decoded data structure
1642             #
1643             # Input:
1644             # \$buffer = reference to the buffer to decode
1645             # $position = first byte to decode
1646             #
1647             #######################################################################
1648             # struct IP_INTERFACE_INFO
1649             #
1650             # The IP_INTERFACE_INFO structure contains a list of the network
1651             # interface adapters on the local system.
1652             #
1653             # typedef struct _IP_INTERFACE_INFO {
1654             # LONG NumAdapters; // number of adapters in array
1655             # IP_ADAPTER_INDEX_MAP Adapter[1]; // adapter indices and names
1656             # } IP_INTERFACE_INFO,*PIP_INTERFACE_INFO;
1657             #
1658             #######################################################################
1659             sub _IP_INTERFACE_INFO
1660             {
1661             my $size = 4 + 4;
1662             wantarray or return $size;
1663              
1664             my ($buffer, $pos) = @_;
1665             my %hash;
1666             my @array;
1667              
1668             ($pos, $hash{'NumAdapters'}) = _shiftunpack($buffer, $pos, 4, "l");
1669              
1670             for (my $cnt=0; $cnt < $hash{'NumAdapters'}; $cnt++)
1671             {
1672             my %map;
1673             ($pos, %map) = _IP_ADAPTER_INDEX_MAP($buffer, $pos);
1674             push @{ $hash{'Adapters'} }, \%map;
1675             }
1676              
1677             return ($pos, %hash);
1678             }
1679              
1680             #######################################################################
1681             # _FIXED_INFO()
1682             #
1683             # Decodes an FIXED_INFO data structure and returns data
1684             # into a Perl array
1685             #
1686             #######################################################################
1687             # Usage:
1688             # ($pos, %hash) = _FIXED_INFO(\$buffer, $position);
1689             #
1690             # Output:
1691             # $pos = new position in buffer (for the next call)
1692             # %hash = the decoded data structure
1693             #
1694             # Input:
1695             # \$buffer = reference to the buffer to decode
1696             # $position = first byte to decode
1697             #
1698             #######################################################################
1699             # FIXED_INFO structure for GetNetworkParams
1700             #
1701             # typedef struct {
1702             # char HostName[MAX_HOSTNAME_LEN + 4];
1703             # char DomainName[MAX_DOMAIN_NAME_LEN + 4];
1704             # PIP_ADDR_STRING CurrentDnsServer;
1705             # IP_ADDR_STRING DnsServerList;
1706             # UINT NodeType;
1707             # char ScopeId[MAX_SCOPE_ID_LEN + 4];
1708             # UINT EnableRouting;
1709             # UINT EnableProxy;
1710             # UINT EnableDns;
1711             # } FIXED_INFO,
1712             # *PFIXED_INFO;
1713             #
1714             #######################################################################
1715             sub _FIXED_INFO{
1716             my ($buffer, $pos) = @_;
1717             my %hash;
1718            
1719             ($pos, $hash{'HostName'}) = _shiftunpack($buffer, $pos, MAX_HOSTNAME_LEN + 4, "Z".(MAX_HOSTNAME_LEN + 4));
1720             ($pos, $hash{'DomainName'}) = _shiftunpack($buffer, $pos, MAX_DOMAIN_NAME_LEN + 4, "Z".(MAX_DOMAIN_NAME_LEN + 4));
1721            
1722             my $CurrentDnsServer;
1723             my $size = _ADDR_STRING_STRUCT_SIZE;
1724             ($pos, $CurrentDnsServer) = _shiftunpack($buffer, $pos, $PTR_SIZE, "P".$size);
1725             if ($CurrentDnsServer)
1726             {
1727             @{ $hash{'CurrentDnsServer'} } = _IP_ADDR_STRING(\$CurrentDnsServer, 0);
1728             }
1729             ($pos, @{ $hash{'DnsServersList'} }) = _IP_ADDR_STRING($buffer, $pos);
1730             ($pos, $hash{'NodeType'}) = _shiftunpack($buffer, $pos, 4, "L");
1731             ($pos, $hash{'ScopeId'}) = _shiftunpack($buffer, $pos, MAX_SCOPE_ID_LEN + 4, "Z".(MAX_SCOPE_ID_LEN + 4));
1732             ($pos, $hash{'EnableRouting'}) = _shiftunpack($buffer, $pos, 4, "L");
1733             ($pos, $hash{'EnableProxy'}) = _shiftunpack($buffer, $pos, 4, "L");
1734             ($pos, $hash{'EnableDns'}) = _shiftunpack($buffer, $pos, 4, "L");
1735            
1736             return ($pos, %hash);
1737             }
1738              
1739             #######################################################################
1740             # _shiftunpack
1741             #
1742             # Decodes a part of a given buffer and returns list data and new position
1743             #
1744             #######################################################################
1745             # Usage:
1746             # ($pos, @values) = _shiftunpack(\$buffer, $position, $size, $elements);
1747             #
1748             # Output:
1749             # $pos = new position in buffer (for the next call)
1750             # @values = the decoded data values
1751             #
1752             # Input:
1753             # \$buffer = reference to the buffer to decode
1754             # $position = first byte to decode
1755             # $size = number of bytes to decode
1756             # $elements = type of data to decode (see 'pack()' in Perl functions)
1757             #
1758             #######################################################################
1759             sub _shiftunpack
1760             {
1761             my ($buffer, $position, $size, $element) = @_;
1762              
1763             my $buf = substr($$buffer, $position, $size);
1764             my @values = unpack($element, $buf);
1765              
1766             $position += $size;
1767              
1768             if (scalar(@values) > 1)
1769             {
1770             return($position, \@values);
1771             }
1772             else
1773             {
1774             return($position, $values[0]);
1775             }
1776             }
1777              
1778              
1779             #######################################################################
1780             # _shiftunpackWCHAR
1781             #
1782             # Decodes a UNICODE part of a given buffer and returns data and new
1783             # position
1784             #
1785             #######################################################################
1786             # Usage:
1787             # ($pos, $value) = _shiftunpackWCHAR(\$buffer, $position, $size);
1788             #
1789             # Output:
1790             # $pos = new position in buffer (for the next call)
1791             # $value = the decoded data value
1792             #
1793             # Input:
1794             # \$buffer = reference to the buffer to decode
1795             # $position = first byte to decode
1796             # $size = number of bytes to decode
1797             #
1798             #######################################################################
1799             sub _shiftunpackWCHAR
1800             {
1801             my ($buffer, $position, $size) = @_;
1802              
1803             my $buf = substr($$buffer, $position, $size);
1804             my $value = pack( "C*", unpack("S*", $buf));
1805             $value = unpack("Z*", $value);
1806              
1807             $position += $size;
1808              
1809             return($position, $value);
1810             }
1811              
1812              
1813             #######################################################################
1814             # _debugbuffer
1815             #
1816             # Decodes and prints the content of a buffer
1817             #
1818             #######################################################################
1819             # Usage:
1820             # _debugbuffer(\$buffer);
1821             #
1822             # Input:
1823             # \$buffer = reference to the buffer to print
1824             #
1825             #######################################################################
1826             sub _debugbuffer
1827             {
1828             my $buffer = $_[0];
1829              
1830             my (@data) = unpack("C*", $$buffer);
1831              
1832             printf "Buffer size: %d\n", scalar(@data);
1833              
1834             my $cnt = 0;
1835              
1836             foreach my $i (@data)
1837             {
1838             my $char = '';
1839             if (32 <= $i and $i < 127)
1840             {
1841             $char = chr($i);
1842             }
1843             printf "%03d -> 0x%02x --> %03d ---> %s\n", $cnt++, $i, $i, $char;
1844             }
1845             }
1846              
1847              
1848             #######################################################################
1849             # WCHAR = _ToUnicodeChar(string)
1850             # converts a perl string in a 16-bit (pseudo) unicode string
1851             #######################################################################
1852             sub _ToUnicodeChar
1853             {
1854             my $string = shift or return(undef);
1855              
1856             $string =~ s/(.)/$1\x00/sg;
1857              
1858             return $string;
1859             }
1860              
1861              
1862             #######################################################################
1863             # WSTR = _ToUnicodeSz(string)
1864             # converts a perl string in a null-terminated 16-bit (pseudo) unicode string
1865             #######################################################################
1866             sub _ToUnicodeSz
1867             {
1868             my $string = shift or return(undef);
1869              
1870             return _ToUnicodeChar($string."\x00");
1871             }
1872              
1873              
1874             #######################################################################
1875             # string = _FromUnicode(WSTR)
1876             # converts a null-terminated 16-bit unicode string into a regular perl string
1877             #######################################################################
1878             sub _FromUnicode
1879             {
1880             my $string = shift or return(undef);
1881              
1882             $string = unpack("Z*", pack( "C*", unpack("S*", $string)));
1883              
1884             return($string);
1885             }
1886              
1887             #######################################################################
1888             # HANDLE = GetProcessHeap()
1889             # The GetProcessHeap function obtains a handle to the heap of the calling process.
1890             # This handle can then be used in subsequent calls to the heap functions.
1891             #######################################################################
1892             sub _GetProcessHeap
1893             {
1894             return $GetProcessHeap->Call();
1895             }
1896              
1897             1;
1898             __END__