File Coverage

blib/lib/Socket/GetAddrInfo/Emul.pm
Criterion Covered Total %
statement 103 118 87.2
branch 37 74 50.0
condition 15 35 42.8
subroutine 9 9 100.0
pod 2 2 100.0
total 166 238 69.7


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2007-2011 -- leonerd@leonerd.org.uk
5              
6             package Socket::GetAddrInfo::Emul;
7              
8 1     1   5 use strict;
  1         1  
  1         29  
9 1     1   5 use warnings;
  1         2  
  1         41  
10              
11             our $VERSION = '0.22';
12              
13             # Load the actual code into Socket::GetAddrInfo
14             package # hide from indexer
15             Socket::GetAddrInfo;
16              
17 1     1   4 use Carp;
  1         2  
  1         49  
18              
19 1     1   956 use Socket;
  1         4416  
  1         793  
20 1     1   10 use Scalar::Util qw( dualvar );
  1         2  
  1         280  
21              
22             our @EXPORT_OK;
23              
24             =head1 NAME
25              
26             C - Pure Perl emulation of C and
27             C using IPv4-only legacy resolvers
28              
29             =head1 DESCRIPTION
30              
31             C attempts to provide the C and
32             C functions by some XS code that calls the real functions in
33             F. If for some reason this cannot be done; either there is no C
34             compiler, or F does not provide these functions, then they will be
35             emulated using the legacy resolvers C, etc... These emulations
36             are not a complete replacement of the real functions, because they only
37             support IPv4 (the C socket family). In this case, the following
38             restrictions will apply.
39              
40             =cut
41              
42             # These numbers borrowed from GNU libc's implementation, but since
43             # they're only used by our emulation, it doesn't matter if the real
44             # platform's values differ
45             BEGIN {
46 1     1   12 my %constants = (
47             AI_PASSIVE => 1,
48             AI_CANONNAME => 2,
49             AI_NUMERICHOST => 4,
50             AI_V4MAPPED => 8,
51             AI_ALL => 16,
52             AI_ADDRCONFIG => 32,
53             # RFC 2553 doesn't define this but Linux does - lets be nice and
54             # provide it since we can
55             AI_NUMERICSERV => 1024,
56              
57             EAI_BADFLAGS => -1,
58             EAI_NONAME => -2,
59             EAI_NODATA => -5,
60             EAI_FAMILY => -6,
61             EAI_SERVICE => -8,
62              
63             NI_NUMERICHOST => 1,
64             NI_NUMERICSERV => 2,
65             NI_NOFQDN => 4,
66             NI_NAMEREQD => 8,
67             NI_DGRAM => 16,
68              
69             # These are not gni() constants; they're extensions for the perl API /*
70             NIx_NOHOST => 1,
71             NIx_NOSERV => 2,
72              
73             # Constants we don't support. Export them, but croak if anyone tries to
74             # use them
75             AI_IDN => 64,
76             AI_CANONIDN => 128,
77             AI_IDN_ALLOW_UNASSIGNED => 256,
78             AI_IDN_USE_STD3_ASCII_RULES => 512,
79             NI_IDN => 32,
80             NI_IDN_ALLOW_UNASSIGNED => 64,
81             NI_IDN_USE_STD3_ASCII_RULES => 128,
82              
83             # Error constants we'll never return, so it doesn't matter what value
84             # these have, nor that we don't provide strings for them
85             EAI_SYSTEM => -11,
86             EAI_BADHINTS => -1000,
87             EAI_PROTOCOL => -1001
88             );
89              
90 1         5 require constant;
91 1         530 constant->import( $_ => $constants{$_} ) for keys %constants;
92 1         1582 push @EXPORT_OK, $_ for keys %constants;
93             }
94              
95             push @EXPORT_OK, qw(
96             getaddrinfo
97             getnameinfo
98             );
99              
100             my %errstr = (
101             # These strings from RFC 2553
102             EAI_BADFLAGS() => "invalid value for ai_flags",
103             EAI_NONAME() => "nodename nor servname provided, or not known",
104             EAI_NODATA() => "no address associated with nodename",
105             EAI_FAMILY() => "ai_family not supported",
106             EAI_SERVICE() => "servname not supported for ai_socktype",
107             );
108              
109             # Borrowed from Regexp::Common::net
110             my $REGEXP_IPv4_DECIMAL = qr/25[0-5]|2[0-4][0-9]|1?[0-9][0-9]{1,2}/;
111             my $REGEXP_IPv4_DOTTEDQUAD = qr/$REGEXP_IPv4_DECIMAL\.$REGEXP_IPv4_DECIMAL\.$REGEXP_IPv4_DECIMAL\.$REGEXP_IPv4_DECIMAL/;
112              
113             sub _makeerr
114             {
115 6     6   9 my ( $errno ) = @_;
116 6 50 0     22 my $errstr = $errno == 0 ? "" : ( $errstr{$errno} || $errno );
117 6         70 return dualvar( $errno, $errstr );
118             }
119              
120             =head2 getaddrinfo
121              
122             =over 4
123              
124             =item *
125              
126             If the C hint is supplied, it must be C. Any other value will
127             result in an error thrown by C.
128              
129             =item *
130              
131             The only supported C hint values are C, C,
132             C and C.
133              
134             The flags C and C are recognised but ignored, as they do
135             not apply to C lookups. Since this function only returns C
136             addresses, it does not need to probe the system for configured addresses in
137             other families, so the C flag is also ignored.
138              
139             Note that C is an extension not defined by RFC 2553, but is
140             provided by most OSes. It is possible (though unlikely) that even the native
141             XS implementation does not recognise this constant.
142              
143             =back
144              
145             =cut
146              
147             sub getaddrinfo
148             {
149 3     3 1 5979 my ( $node, $service, $hints ) = @_;
150            
151 3 100       10 $node = "" unless defined $node;
152              
153 3 50       17 $service = "" unless defined $service;
154              
155 3         9 my ( $family, $socktype, $protocol, $flags ) = @$hints{qw( family socktype protocol flags )};
156              
157 3   100     15 $family ||= AF_INET; # 0 == AF_UNSPEC, which we want too
158 3 50       9 $family == AF_INET or return _makeerr( EAI_FAMILY );
159              
160 3   50     6 $socktype ||= 0;
161              
162 3   50     14 $protocol ||= 0;
163              
164 3   50     11 $flags ||= 0;
165              
166 3         6 my $flag_passive = $flags & AI_PASSIVE; $flags &= ~AI_PASSIVE;
  3         4  
167 3         5 my $flag_canonname = $flags & AI_CANONNAME; $flags &= ~AI_CANONNAME;
  3         5  
168 3         4 my $flag_numerichost = $flags & AI_NUMERICHOST; $flags &= ~AI_NUMERICHOST;
  3         4  
169 3         5 my $flag_numericserv = $flags & AI_NUMERICSERV; $flags &= ~AI_NUMERICSERV;
  3         4  
170              
171             # These constants don't apply to AF_INET-only lookups, so we might as well
172             # just ignore them. For AI_ADDRCONFIG we just presume the host has ability
173             # to talk AF_INET. If not we'd have to return no addresses at all. :)
174 3         4 $flags &= ~(AI_V4MAPPED|AI_ALL|AI_ADDRCONFIG);
175              
176 3 50       8 $flags & (AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED|AI_IDN_USE_STD3_ASCII_RULES) and
177             croak "Socket::GetAddrInfo::Emul::getaddrinfo does not support IDN";
178              
179 3 50       7 $flags == 0 or return _makeerr( EAI_BADFLAGS );
180              
181 3 50 66     16 $node eq "" and $service eq "" and return _makeerr( EAI_NONAME );
182              
183 3         5 my $canonname;
184             my @addrs;
185 3 100       8 if( $node ne "" ) {
186 1 50 33     5 return _makeerr( EAI_NONAME ) if( $flag_numerichost and $node !~ m/^$REGEXP_IPv4_DOTTEDQUAD$/ );
187 1         179 ( $canonname, undef, undef, undef, @addrs ) = gethostbyname( $node );
188 1 50       10 defined $canonname or return _makeerr( EAI_NONAME );
189              
190 1 50       5 undef $canonname unless $flag_canonname;
191             }
192             else {
193 2 50       12 $addrs[0] = $flag_passive ? inet_aton( "0.0.0.0" )
194             : inet_aton( "127.0.0.1" );
195             }
196              
197 3         5 my @ports; # Actually ARRAYrefs of [ socktype, protocol, port ]
198 3         5 my $protname = "";
199 3 50       8 if( $protocol ) {
200 0         0 $protname = getprotobynumber( $protocol );
201             }
202              
203 3 50 33     31 if( $service ne "" and $service !~ m/^\d+$/ ) {
204 0 0       0 return _makeerr( EAI_NONAME ) if( $flag_numericserv );
205 0 0       0 getservbyname( $service, $protname ) or return _makeerr( EAI_SERVICE );
206             }
207              
208 3         8 foreach my $this_socktype ( SOCK_STREAM, SOCK_DGRAM, SOCK_RAW ) {
209 9 100 66     47 next if $socktype and $this_socktype != $socktype;
210              
211 3         4 my $this_protname = "raw";
212 3 50       9 $this_socktype == SOCK_STREAM and $this_protname = "tcp";
213 3 50       8 $this_socktype == SOCK_DGRAM and $this_protname = "udp";
214              
215 3 50 33     7 next if $protname and $this_protname ne $protname;
216              
217 3         4 my $port;
218 3 50       8 if( $service ne "" ) {
219 3 50       10 if( $service =~ m/^\d+$/ ) {
220 3         5 $port = "$service";
221             }
222             else {
223 0         0 ( undef, undef, $port, $this_protname ) = getservbyname( $service, $this_protname );
224 0 0       0 next unless defined $port;
225             }
226             }
227             else {
228 0         0 $port = 0;
229             }
230              
231 3   50     1097 push @ports, [ $this_socktype, scalar getprotobyname( $this_protname ) || 0, $port ];
232             }
233              
234 3         7 my @ret;
235 3         5 foreach my $addr ( @addrs ) {
236 3         7 foreach my $portspec ( @ports ) {
237 3         5 my ( $socktype, $protocol, $port ) = @$portspec;
238 3         31 push @ret, {
239             family => $family,
240             socktype => $socktype,
241             protocol => $protocol,
242             addr => pack_sockaddr_in( $port, $addr ),
243             canonname => undef,
244             };
245             }
246             }
247              
248             # Only supply canonname for the first result
249 3 50       11 if( defined $canonname ) {
250 0         0 $ret[0]->{canonname} = $canonname;
251             }
252              
253 3         8 return ( _makeerr( 0 ), @ret );
254             }
255              
256             =head2 getnameinfo
257              
258             =over 4
259              
260             =item *
261              
262             If the sockaddr family of C<$addr> is anything other than C, an error
263             will be thrown with C.
264              
265             =item *
266              
267             The only supported C<$flags> values are C, C,
268             C, C and C.
269              
270             =back
271              
272             =cut
273              
274             sub getnameinfo
275             {
276 3     3 1 4499 my ( $addr, $flags, $xflags ) = @_;
277              
278 3         6 my ( $port, $inetaddr );
279 3 50       4 eval { ( $port, $inetaddr ) = unpack_sockaddr_in( $addr ) }
  3         20  
280             or return _makeerr( EAI_FAMILY );
281              
282 3         7 my $family = AF_INET;
283              
284 3   50     9 $flags ||= 0;
285              
286 3         5 my $flag_numerichost = $flags & NI_NUMERICHOST; $flags &= ~NI_NUMERICHOST;
  3         4  
287 3         3 my $flag_numericserv = $flags & NI_NUMERICSERV; $flags &= ~NI_NUMERICSERV;
  3         4  
288 3         4 my $flag_nofqdn = $flags & NI_NOFQDN; $flags &= ~NI_NOFQDN;
  3         5  
289 3         5 my $flag_namereqd = $flags & NI_NAMEREQD; $flags &= ~NI_NAMEREQD;
  3         4  
290 3         4 my $flag_dgram = $flags & NI_DGRAM; $flags &= ~NI_DGRAM;
  3         4  
291              
292 3 50       8 $flags & (NI_IDN|NI_IDN_ALLOW_UNASSIGNED|NI_IDN_USE_STD3_ASCII_RULES) and
293             croak "Socket::GetAddrInfo::Emul::getnameinfo does not support IDN";
294              
295 3 50       9 $flags == 0 or return _makeerr( EAI_BADFLAGS );
296              
297 3   50     13 $xflags ||= 0;
298              
299 3         4 my $node;
300 3 50       12 if( $xflags & NIx_NOHOST ) {
    100          
301 0         0 $node = undef;
302             }
303             elsif( $flag_numerichost ) {
304 2         12 $node = inet_ntoa( $inetaddr );
305             }
306             else {
307 1         39 $node = gethostbyaddr( $inetaddr, $family );
308 1 50       8 if( !defined $node ) {
    50          
309 0 0       0 return _makeerr( EAI_NONAME ) if $flag_namereqd;
310 0         0 $node = inet_ntoa( $inetaddr );
311             }
312             elsif( $flag_nofqdn ) {
313 0         0 my ( $shortname ) = split m/\./, $node;
314 0         0 my ( $fqdn ) = gethostbyname $shortname;
315 0 0 0     0 $node = $shortname if defined $fqdn and $fqdn eq $node;
316             }
317             }
318              
319 3         4 my $service;
320 3 50       13 if( $xflags & NIx_NOSERV ) {
    100          
321 0         0 $service = undef;
322             }
323             elsif( $flag_numericserv ) {
324 2         4 $service = "$port";
325             }
326             else {
327 1 50       6 my $protname = $flag_dgram ? "udp" : "tcp";
328 1         64 $service = getservbyport( $port, $protname );
329 1 50       5 if( !defined $service ) {
330 0         0 $service = "$port";
331             }
332             }
333              
334 3         9 return ( _makeerr( 0 ), $node, $service );
335             }
336              
337             =head1 IDN SUPPORT
338              
339             This pure-perl emulation provides the IDN constants such as C and
340             C, but the C and C functions will croak if
341             passed these flags. This should allow a program to probe for their support,
342             and fall back to some other behaviour instead.
343              
344             =head1 AUTHOR
345              
346             Paul Evans
347              
348             =cut
349              
350             0x55AA;