File Coverage

blib/lib/Win32/Wlan/API.pm
Criterion Covered Total %
statement 24 115 20.8
branch 1 30 3.3
condition 0 6 0.0
subroutine 8 17 47.0
pod 0 9 0.0
total 33 177 18.6


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