File Coverage

blib/lib/Win32/Wlan/API.pm
Criterion Covered Total %
statement 30 131 22.9
branch 1 32 3.1
condition 0 9 0.0
subroutine 10 21 47.6
pod 0 11 0.0
total 41 204 20.1


line stmt bran cond sub pod time code
1             package Win32::Wlan::API;
2 1     1   7 use strict;
  1         2  
  1         31  
3 1     1   6 use Carp qw(croak);
  1         2  
  1         43  
4 1     1   5 use Config;
  1         2  
  1         38  
5              
6 1     1   6 use Encode qw(decode);
  1         2  
  1         87  
7              
8 1     1   9 use Exporter 'import';
  1         1  
  1         55  
9              
10 1     1   6 use vars qw($VERSION $wlan_available %API @signatures @EXPORT_OK);
  1         2  
  1         200  
11             $VERSION = '0.07';
12              
13             # This is used to determine whether we have a 64bit Win32::API
14             # or a 32bit Win32::API - a pointer is 8 or 4 bytes wide
15 0     0 0 0 sub Zero() { "\0" x $Config{ptrsize} };
16              
17             BEGIN {
18 1     1   8 @signatures = (
19             ['WlanOpenHandle' => 'IIPP' => 'I'],
20             ['WlanCloseHandle' => 'II' => 'I'],
21             ['WlanFreeMemory' => 'I' => 'I'],
22             ['WlanEnumInterfaces' => 'IIP' => 'I'],
23             ['WlanQueryInterface' => 'IPIIPPI' => 'I'],
24             ['WlanGetAvailableNetworkList' => 'IPIIP' => 'N'],
25             );
26              
27 1         3 @EXPORT_OK = (qw<$wlan_available WlanQueryCurrentConnection>, map { $_->[0] } @signatures);
  6         33  
28             };
29              
30 1     1   30 use constant ERROR_NDIS_DOT11_POWER_STATE_INVALID => 0x80342002;
  1         3  
  1         102  
31              
32             use constant {
33 1         1720 not_ready => 0,
34             connected => 1,
35             ad_hoc_network_formed => 2,
36             disconnecting => 3,
37             disconnected => 4,
38             associating => 5,
39             discovering => 6,
40             authenticating => 7
41 1     1   7 };
  1         2  
42              
43             if (! load_functions()) {
44             # Wlan functions are not available
45             $wlan_available = 0;
46             } else {
47             $wlan_available = 1;
48             };
49              
50             sub Win32_Error_String {
51 0     0 0 0 my( $rc ) = @_;
52 0   0     0 return eval { require Win32; Win32::FormatMessage($rc) } || "Error $rc"
53             }
54              
55             sub unpack_struct {
56             # Unpacks a string into a hash
57             # according to a key/unpack template structure
58 0     0 0 0 my $desc = shift;
59 0         0 my @keys;
60 0         0 my $template = '';
61              
62 0         0 for (0..$#{$desc}) {
  0         0  
63 0 0       0 if ($_ % 2) {
    0          
64 0         0 $template .= $desc->[ $_ ]
65             } elsif ($desc->[ $_ ] ne '') {
66 0         0 push @keys, $desc->[ $_ ]
67             };
68             };
69              
70 0         0 my %res;
71 0         0 @res{ @keys } = unpack $template, shift;
72 0         0 %res
73             }
74              
75             sub WlanOpenHandle {
76 0 0   0 0 0 croak "Wlan functions are not available" unless $wlan_available;
77 0         0 my $version = Zero;
78 0         0 my $handle = Zero;
79 0         0 my $rc;
80 0         0 $wlan_available = 0; # Assume unavailibility
81 0 0       0 ($rc = $API{ WlanOpenHandle }->Call(2,0,$version,$handle)) == 0
82             or croak Win32_Error_String($rc);
83 0         0 $wlan_available = 1; # Ok, finally
84 0         0 my $h = unpack "V", $handle;
85 0         0 $h
86             };
87              
88             sub WlanCloseHandle {
89 0 0   0 0 0 croak "Wlan functions are not available" unless $wlan_available;
90 0         0 my ($handle) = @_;
91 0 0       0 $API{ WlanCloseHandle }->Call($handle,0) == 0
92             or croak $^E;
93             };
94              
95             sub WlanFreeMemory {
96 0 0   0 0 0 croak "Wlan functions are not available" unless $wlan_available;
97 0         0 my ($block) = @_;
98 0         0 $API{ WlanFreeMemory }->Call($block);
99             };
100              
101             sub _unpack_counted_array {
102 0     0   0 my ($pointer,$template,$size) = @_;
103 0         0 my $info = unpack 'P8', $pointer;
104 0         0 my ($count,$curr) = unpack 'VV', $info;
105 0         0 my $data = unpack "P" . (8+$count*$size), $pointer;
106 0         0 my @items = unpack "x8 ($template)$count", $data;
107 0         0 my @res;
108 0 0       0 if ($count) {
109 0         0 my $elements_per_item = @items / $count;
110 0         0 while (@items) {
111 0         0 push @res, [splice @items, 0, $elements_per_item ]
112             };
113             };
114             @res
115 0         0 };
116              
117             sub WlanEnumInterfaces {
118 0 0   0 0 0 croak "Wlan functions are not available" unless $wlan_available;
119 0         0 my ($handle) = @_;
120 0         0 my $interfaces = Zero;
121 0 0       0 $API{ WlanEnumInterfaces }->Call($handle,0,$interfaces) == 0
122             or croak $^E;
123 0         0 my @items = _unpack_counted_array($interfaces,'a16 a512 V',16+512+4);
124             @items = map {
125             # First element is the GUUID of the interface
126             # Name is in 16bit UTF
127 0         0 $_->[1] = decode('UTF-16LE' => $_->[1]);
  0         0  
128 0         0 $_->[1] =~ s/\0+$//;
129             # The third element is the status of the interface
130              
131             +{
132 0         0 guuid => $_->[0],
133             name => $_->[1],
134             status => $_->[2],
135             };
136             } @items;
137              
138 0         0 $interfaces = unpack 'V', $interfaces;
139 0         0 WlanFreeMemory($interfaces);
140             @items
141 0         0 };
142              
143             sub WlanQueryInterface {
144 0 0   0 0 0 croak "Wlan functions are not available" unless $wlan_available;
145 0         0 my ($handle,$interface,$op) = @_;
146 0         0 my $size = Zero;
147 0         0 my $data = Zero;
148 0 0       0 $API{ WlanQueryInterface }->Call($handle, $interface, $op, 0, $size, $data, 0) == 0
149             or return;
150              
151 0         0 $size = unpack 'V', $size;
152 0         0 my $payload = unpack "P$size", $data;
153              
154 0         0 $data = unpack 'V', $data;
155 0         0 WlanFreeMemory($data);
156 0         0 $payload
157             };
158              
159             =head2 C<< WlanCurrentConnection( $handle, $interface ) >>
160              
161             Returns a hashref containing the following keys
162              
163             =over 4
164              
165             =item *
166              
167             C<< state >> - state of the interface
168              
169             One of the following
170              
171             Win32::Wlan::API::not_ready => 0,
172             Win32::Wlan::API::connected => 1,
173             Win32::Wlan::API::ad_hoc_network_formed => 2,
174             Win32::Wlan::API::disconnecting => 3,
175             Win32::Wlan::API::disconnected => 4,
176             Win32::Wlan::API::associating => 5,
177             Win32::Wlan::API::discovering => 6,
178             Win32::Wlan::API::authenticating => 7
179              
180             =item *
181              
182             C<< mode >>
183              
184             =item *
185              
186             C<< profile_name >>
187              
188             C<< bss_type >>
189              
190             infrastructure = 1,
191             independent = 2,
192             any = 3
193              
194             =item *
195              
196             auth_algorithm
197              
198             DOT11_AUTH_ALGO_80211_OPEN = 1,
199             DOT11_AUTH_ALGO_80211_SHARED_KEY = 2,
200             DOT11_AUTH_ALGO_WPA = 3,
201             DOT11_AUTH_ALGO_WPA_PSK = 4,
202             DOT11_AUTH_ALGO_WPA_NONE = 5,
203             DOT11_AUTH_ALGO_RSNA = 6, # wpa2
204             DOT11_AUTH_ALGO_RSNA_PSK = 7, # wpa2
205             DOT11_AUTH_ALGO_IHV_START = 0x80000000,
206             DOT11_AUTH_ALGO_IHV_END = 0xffffffff
207              
208             =item *
209              
210             cipher_algorithm
211              
212             DOT11_CIPHER_ALGO_NONE = 0x00,
213             DOT11_CIPHER_ALGO_WEP40 = 0x01,
214             DOT11_CIPHER_ALGO_TKIP = 0x02,
215             DOT11_CIPHER_ALGO_CCMP = 0x04,
216             DOT11_CIPHER_ALGO_WEP104 = 0x05,
217             DOT11_CIPHER_ALGO_WPA_USE_GROUP = 0x100,
218             DOT11_CIPHER_ALGO_RSN_USE_GROUP = 0x100,
219             DOT11_CIPHER_ALGO_WEP = 0x101,
220             DOT11_CIPHER_ALGO_IHV_START = 0x80000000,
221             DOT11_CIPHER_ALGO_IHV_END = 0xffffffff
222              
223             =back
224              
225             =cut
226              
227             sub WlanQueryCurrentConnection {
228 0     0 0 0 my ($handle,$interface) = @_;
229 0   0     0 my $info = WlanQueryInterface($handle,$interface,7) || '';
230              
231 0         0 my @WLAN_CONNECTION_ATTRIBUTES = (
232             state => 'V',
233             mode => 'V',
234             profile_name => 'a512',
235             # WLAN_ASSOCIATION_ATTRIBUTES
236             ssid_len => 'V',
237             ssid => 'a32',
238             bss_type => 'V',
239             mac_address => 'a6',
240             dummy => 'a2', # ???
241             phy_type => 'V',
242             phy_index => 'V',
243             signal_quality => 'V',
244             rx_rate => 'V',
245             tx_rate => 'V',
246             security_enabled => 'V', # BOOL
247             onex_enabled => 'V', # BOOL
248             auth_algorithm => 'V',
249             cipher_algorithm => 'V',
250             );
251              
252 0         0 my %res = unpack_struct(\@WLAN_CONNECTION_ATTRIBUTES, $info);
253              
254 0   0     0 $res{ profile_name } = decode('UTF-16LE', $res{ profile_name }) || '';
255 0         0 $res{ profile_name } =~ s/\0+$//;
256 0         0 $res{ ssid } = substr $res{ ssid }, 0, $res{ ssid_len };
257              
258 0         0 $res{ mac_address } = sprintf "%02x:%02x:%02x:%02x:%02x:%02x", unpack 'C*', $res{ mac_address };
259              
260 0         0 %res
261             }
262              
263             sub WlanGetAvailableNetworkList {
264 0     0 0 0 my ($handle,$interface,$flags) = @_;
265 0   0     0 $flags ||= 0;
266 0         0 my $list = Zero;
267 0         0 my $rc = $API{ WlanGetAvailableNetworkList }->Call($handle,$interface,$flags,0,$list);
268 0 0       0 if( $rc == ERROR_NDIS_DOT11_POWER_STATE_INVALID()) {
269 0         0 return;
270             }
271 0 0       0 if( $rc != 0 ) {
272 0         0 croak Win32_Error_String($rc);
273             }
274             # name ssid_len ssid bss bssids connectable
275 0         0 my @items = _unpack_counted_array($list, join( '',
276             'a512', # name
277             'V', # ssid_len
278             'a32', # ssid
279             'V', # bss
280             'V', # bssids
281             'V', # connectable
282             'V', # notConnectableReason,
283             'V', # PhysTypes
284             'V8', # PhysType elements
285             'V', # More PhysTypes
286             'V', # wlanSignalQuality from 0=-100dbm to 100=-50dbm, linear
287             'V', # bSecurityEnabled;
288             'V', # dot11DefaultAuthAlgorithm;
289             'V', # dot11DefaultCipherAlgorithm;
290             'V', # dwFlags
291             'V', # dwReserved;
292             ), 512+4+32+20*4);
293 0         0 for (@items) {
294 0         0 my %info;
295 0         0 @info{qw( name ssid_len ssid bss bssids connectable notConnectableReason
296             phystype_count )} = splice @$_, 0, 8;
297 0         0 $info{ phystypes }= [splice @$_, 0, 8];
298 0         0 @info{qw( has_more_phystypes
299             signal_quality
300             security_enabled
301             default_auth_algorithm
302             default_cipher_algorithm
303             flags
304             reserved
305             )} = @$_;
306              
307             # Decode the elements
308 0         0 $info{ ssid } = substr( $info{ ssid }, 0, $info{ ssid_len });
309 0         0 $info{ name } = decode('UTF-16LE', $info{ name });
310 0         0 $info{ name } =~ s/\0+$//;
311 0         0 splice @{$info{ phystypes }}, $info{ phystype_count };
  0         0  
312              
313 0         0 $_ = \%info;
314             };
315              
316 0         0 $list = unpack 'V', $list;
317 0         0 WlanFreeMemory($list);
318             @items
319 0         0 }
320              
321             sub load_functions {
322 1     1 0 2 my $ok = eval {
323 1         165 require Win32::API;
324 0         0 1
325             };
326 1 50       9 return if ! $ok;
327 0           for my $sig (@signatures) {
328 0           $API{ $sig->[0] } = eval {
329 0           Win32::API->new( 'wlanapi.dll', @$sig );
330             };
331 0 0         if (! $API{ $sig->[0] }) {
332             return
333 0           };
334             };
335 0           1
336             };
337              
338             1;
339              
340             __END__