=head1 NAME

Socket::More - Scoped listening/passive addresses and network utility routines

=head1 SYNOPSIS

Bring into your namespace. 

	use v5.36;
	use Socket::More;

Flexible way to create interface scoped passive (listen) address across
families. Special 'unix' interface for ease of use. All invalid combinations of
family, port  and paths are discarded:

	# Create passive (listening) sockets for selected interfaces
  #
	my @passive=sockaddr_passive {
		interface=>	["eth0", "unix"],
		port=>		[5566, 7788],
		path=> "path_to_sock"
	};
		
	#All invalid family/interface/port/path  combinations are filtered out
	#leaving only valid info for socket creation and binding:
  #
	for(@passive){
		say $_->{address};
		socket my $socket, $_->{family}, $_->{socktype}, 0;
		bind $socket $_->{addr};
	}

Please see EXAMPLES section for more.

=head1 DESCRIPTION

Intended as an alternative for L<Socket>, implementing only 'modern subset' of
routines. It providing extra routines to make listening addresses easy, to
solve problems like this:

	'listen on interfaces eth0 and eth1, using IPv6 and port numbers 9090
	and 9091, but limit to link local addresses, and stream types'.

	'listen on eth0 and unix, port 1000 and path test.sock, using datagram
	type sockets,'.

	'listen on all interfaces on port 8080 and 8081, but only on link local
	ipv6 address'

It also is a umbrella package, which reexports  L<Socket::More::Constants>,
L<Socket::More::Lookup> and L<Socket::More::Interface> for you.

It also makes it easy to generate 'random' ports to bind to, B<before> your
program binds, to aid in testing server scenarios.

Note this is a subset of L<Socket> functionality.  The 'old school' inet_* functions are
deliberately not included, to encourage the usage of getnameinfo/getaddrinfo.


From version v0.5.0, the module has been decomposed to to separate modules on
CPAN for targeted usage:
  
=over

=item L<Socket::More> (This Module)

=over 

=item wrapper over C<socket> to make it more flexible

=item Methods for creating address structure for listening (passive)
sockets using a query and consise command line syntax 

=item String/constant mapping

=item General pack/unpack of address structures

=item Address family and socket types as string names 

=item Imports and rexports the modules listed below. 

=back

=item L<Socket::More::Constants>

Contains all the networking constants (ie C<AF_INET>, C<NI_NUMERICHOST>, etc)
for your platform.

=item L<Socket::More::Lookup>

Implements and exports C<getaddrinfo>, C<getnameinfo> and C<gai_strerror>, with
a different calling convention then Perl core Socket implementation.  More like
C<sysread> convention

=item L<Socket::More::Interface>

Implements and exports C<getifaddrs>, C<if_nametoindex>, C<if_indextoname> and
C<if_nameindex> to query the interfaces of your system

=back


Other packages/distributions not reexported but part of the family:

=over 

=item L<Socket::More::Resolver>


Non blocking and event loop integration of system resolver functions.

=item L<Socket::More::IPRanges>

Grouping information on IP addresses

=back


=head1 MOTIVATION

I wanted an easy way to listen on a particular interface ONLY.  The normal way
of wild card addresses "0.0.0.0" or "::", will listen on all interfaces. Any
restrictions on connecting sockets will either need to be implemented in the
firewall or in application code accepting and then closing the connection. This
is a waste of resources and a potential security problem.

Manually creating the multitude of potential addresses on the same interface
(especially for IPv6) is a pain to maintain. This module reduces the effort by
generating all combinations of parameters and then filters out what doesn't
make sense and what you don't want. See C<sockaddr_passive> below for more
information.


=head1 API

From version v0.5.0 the structure of the module has been refactored into other
modules. The same API is accessible from this module, as it imports them and
reexports their subroutines/constants. If you don't need the easy listening
features of this module, then you can use these modules independently.

=head2 getifaddrs (L<Socket::More::Interface>)

=head2 if_nametoindex (L<Socket::More::Interface>)

=head2 if_indextoname (L<Socket::More::Interface>)

=head2 if_nameindex (L<Socket::More::Interface>)


=head2 getaddrinfo (L<Socket::More::Lookup>)

=head2 getnameinfo (L<Socket::More::Lookup>)

=head2 gai_strerror (L<Socket::More::Lookup>)


=head2 family_to_string

	my $string=family_to_string($family);

Returns a string label representing an address family C<$family>. For example,
calling with constant C<AF_INET>, will return a string C<"AF_INET">


=head2 string_to_family

	my @family=string_to_family($pattern);

Performs a match of all AF_.* names against C<$pattern>. Returns a list of
integer constants for the corresponding address family that matched. Returns an
empty list if the patten/string does not match.  The match is performed
insensitive to case

For example calling with C<"INET"> will return a list of two elements,
C<AF_INET> and C<AF_INET6>.

This is useful for handling address families supplied from the command line, as
abbreviated names can be matched.


=head2 socktype_to_string or sock_to_string

	my $string=socktype_to_string($type);

Returns a string label representing a socket type C<$type>. For example,
calling with the integer constant C<SOCK_STREAM>, will return a string
C<"SOCK_STREAM">

=head2 string_to_socktype  or string_to_type

	my @type=string_to_socktype($string);

Performs a match of all SOCK_.* names against C<$pattern>. Returns a list of
integers for the corresponding socket types that matched. Returns an empty list
if the patten/string does not match. The match is performed insensitive to
case.

For example calling with C<"STREAM"> will return a list of one element,
C<SOCK_STREAM>.

This is useful for handling address families supplied from the command line, as
abbreviated names can be matched.


=head2 sockaddr_passive

	my @interfaces=sockadd_passive $specification;

Returns a list of 'interface' structures (similar to getifaddr above) which
provide meta data and packed address structures suitable for passive use (i.e
bind) and matching the C<$specification>. The resulting data is sorted by
interface name, then by family and finally by type.

It has some overlapping function of C<getaddrinfo>, however it is specifically
for creating addresses for binding, allows the use of interface names and
operates with UNIX domain configurations through a synthetic  'unix' interface.

From B<v0.5.0> the results will return interface information in an addition
field.

A specification hash has optional keys which dictate what addresses are
generated and filtered:

	{
		interface=>"en",
		family=>"INET",
		port=>[1234]
		...
	}

The only required keys are C<port> and/or C<path>. These are used in the
address generation and not as a filter. Without at least one of these keys, no
results will be generated. 

Other keys like C<interface>, C<family>  and C<socktype> for example are used
to restrict addresses created to the match 

Keys like C<address> and C<group> are a filter which are directly matched
against the address and group.

Keys themselves can be shortened all the way down to the shortest unique
substring. So instead of 'interface', it could be 'inter', 'int' or just 'i'
for example. This aids in usage from the command line. The shortest unique keys
are:

	{
		i=>...		#interface
		f=>...		#family
		po=>...		#port
		pa=>...		#path
		a=>...		#address
		s=>...		#socktype
		g=>...		#group
	}


It can include the following keys:
	
=over 

=item interface
		
	examples: 
	interface=>"eth0"
	interface=>"eth\d*";
	interface=>["eth0", "lo"];
	interface=>"unix";
	interface=>["unix", "lo"];

A string or array ref of strings which are used as regex to match interface
names currently available.

=item family

	examples: family=>AF_INET family=>[AF_INET, AF_INET6, AF_UNIX]

A integer or array ref of integers representing the family type an interface
supports.

B<From v0.4.0:> Also can be a string or array ref of strings, which are matched
against supported families. See C<parse_passive_spec> for matching details

=item socktype (was type)

	examples: socktype=>SOCK_STREAM socktype=>[SOCK_STREAM, SOCK_DGRAM]

A integer or array ref of integers representing the socket type an interface
supports.

B<From v0.4.0:> Also can be a string or array ref of strings, which are matched
against supported socket types. See C<parse_passive_spec> for matching details


=item port

	examples: port=>55554 port=>[12345,12346]

The ports used in generating a passive address. Only applied to AF_INET*
families. Ignored for others.

Either C<port> or C<path> are required, otherwise no addresses will be
generated.

=item path

	examples: path=>"path_to_socket.sock" path=>["path_to_socket1.sock",
	"path_to_socket2.sock"]

The path used in generating a passive address. Only applied to AF_UNIX
families. Ignored for others.

Either C<port> or C<path> are required, otherwise no addresses will be
generated.

B<NOTE> The actual path resulting from the specification will have a '_D' or
'_S' appended to the path. This is done to ensure sockets of different type
don't attempt to use the same path.


=item address

	exmples: 
		address=>"192\.168\.1\.1" 
		address=>"169\.254\."

As string used to match the textual representation of an address. In the
special case of '0.0.0.0" or "::", any interface specification is ignored.

=item group

	examples:
		group=>"PRIVATE'

The group the address belongs to as per L<Net::IP>

=item data

	examples: 
	data=>[$scalar]
	data=>[{ ca=>$ca_path, pkey=>$p_path}]

A user field which will be included in each item in the output list. 

B<NOTE> It is recommended this value is an array ref, wrapping actual data. This
makes it more consistent when the data key is parsed from the command line

=back

=head2 parse_passive_spec

	my @spec=parse_passive_spec($string);

Parses a concise string intended to be supplied as a command line argument. The
string consists of one or more fields separated by commas.

The fields are in key value pairs in the form

	key=value

C<key> can be any key used in a specification for C<sockaddr_passive>, and
C<value> is interpreted as a path, number or a string (regex), depending on the
key.



C<port> and C<path> keys take literal values.

C<family> and C<socktype> keys take regex values, which match against the
family/socktype names (using C<string_to_sock> and C<string_to_family>) and are
replaced with the integer values internally.

Other keys treat the value as a string/regex to match against.

The keys can be used repeatedly within multiple fields. For example that means
the following  will match interfaces eth0, eth1 and lo.

	in=>eth0,port=1000,in='lo|eth1'

Only the first "=" within a field is split. this allows the data field itself
to take more key value pairs:

	eg:
	data=key1=value,data=key2=another
	data=ca=ca_path.pem,data=key=private.pem


B<NOTE> Because repeat C<data> keys can be used, the specification generated from
C<parse_passive_spec> will contain a C<data> key with an array as its value.


For example, the following parse a C<sockaddr_passive> specification which would
match SOCK_STREAM sockets, for both AF_INET and AF_INET6 families, on all
available interfaces.

	family=INET,socktype=STREAM #Full key name
	f=INET,t=STREAM		#Shortest unique string for keys

The special case of a field not in key value format (i.e. with out a '='), is
interpreted as the plack compatible listen switch argument.

	HOST:PORT		#INET/INET6 address and port
	:PORT 			#wildcard address and port
	PATH			#UNIX socket path
	
The C<HOST> portion is assinged to the C<address> field. The C<PORT> portion is
assigned to the C<port> field. If a C<PORT> is specified without a C<HOST>,
then the C<address> field is set to C<["0.0.0.0", "::"]> which disables
interface matching, but will listen on all INET addresses.

B<NOTE> This behaviour may change in later versions, as  "::" supports both INET
and INET6.

B<NOTE> to specify an IPv6 literal on the command line, it is contained in a pair
of [] and will need to be escaped or quoted in the shell

=head2 socket

	socket $socket, $domain, $socktype, $proto
  socket $socket, $hash


	example:
		die "$!" unless socket my $socket, AF_INET, SOCK_STREAM,0;

		die "$!" unless socket my $socket, {family=>AF_INET, protocol=>0, socktype=>SOCK_STREAM};

A wrapper around C<CORE::socket>.  It checks if the C<DOMAIN> is a number.  If
so, it simply calls C<CORE::socket> with the supplied arguments.

Otherwise it assumes C<DOMAIN> is a packed sockaddr structure and extracts the
domain/family field using C<sockaddr_family>. This value is then used as the
C<DOMAIN> value in a call to C<CORE::socket>.

Return values are as per C<CORE::socket>. Please refer to L<perldoc -f socket>
for more.

=head2 has_IPv4_interface

	has_IPv4_interface;

Returns true if at least one IPv4 interface was found. False otherwise.

=head2 has_IPv6_interface

	has_IPv6_interface;

Returns true if at least one IPv6 interface was found. False otherwise.


=head2 reify_ports

  reify_ports $specs, ...

  example:
    reify_ports {address=>"127.0.0.1", port=>0}


Iterates through list of specifications and replacing C<port> fields equal to 0
(any port), with a 'random' one supplied by the operating system. This performs
a C<sockaddr_passive> call to to 'flatten' any internal structures in the
specifications provided. 

This works by taking the first entry which results in a 0 port number, creating a
socket and binding it. The 0 port will result in the OS choosing a port for
use.  The resulting port is extracted from the socket (getsocketname) and
replaces the 0 port value in B<all> the specification entries. The socket has
C<SO_REUSEADDR> applied to ensure it can be bound again immediately.

If the specifications request two or more  0 ports in otherwise identical
specifications, it is up the user to choose how to handle any duplicate bind
complications (i.e C<SO_REUSEPORT>)

B<NOTE:> There is a chance that another program can use the port number
returned after a call to C<reify_ports>.

B<NOTE:> The interface/address tested to generate the random port might return
a port which is already in use on other interfaces.


=head2 reify_ports_unshared

    reify_ports_chaos $specs, ...

  example:
    reify_ports {address=>"127.0.0.1", port=>[0,0]};
    reify_ports {address=>"127.0.0.1", port=>0}, {port=>0};


Operates like C<reify_ports> with the exception that all 0 port entries in the
specifications cause a query to the OS. The port numbers are not explicitly
'shared' between specifications, thus returning potentially (most likely)
different port numbers for each entry.


=head1 EXAMPLES

Please checkout 'cli.pl' in the examples directory of this distribution. It
demonstrates many of the features of this module by using the
C<sockaddr_passive>, C<parse_passive_spec>, C<family_to_string> and
C<sock_to_string> functions. It requires C<Text::Table> in addition to this
module.

It takes user input from the command line using one or more C<-l> parameters
via L<Getopt::Long>. These are parsed into passive specifications, which are
then executed to generate list of passive structures matching the
specification. The results are converted into nice text table output.
	
The following shows the example outputs running this program with different
inputs.

=head2 Run1

Any interface, AF_INET6 only, stream or datagram on port 1000:

	perl examples/cli.pl -l '[::]':1000

	Interface Address Family   Group       Port Path Type	     Data
	::        ::      AF_INET6 UNSPECIFIED 1000      SOCK_STREAM
	::        ::      AF_INET6 UNSPECIFIED 1000      SOCK_DGRAM


=head2 Run2

Any interface, AF_INET only, stream or datagram on port 1000:

	->perl examples/cli.pl -l 0.0.0.0:1000
	Interface Address Family  Group   Port Path Type	Data
	0.0.0.0   0.0.0.0 AF_INET PRIVATE 1000      SOCK_STREAM
	0.0.0.0   0.0.0.0 AF_INET PRIVATE 1000      SOCK_DGRAM

=head2 Run3

Any interface, AF_INET only, stream or datagram on port 1000, with data:

	perl examples/cli.pl -l 0.0.0.0:1000,data='ca_path=ca_path.pem;key=key_path'
	Interface Address Family  Group   Port Path Type        Data
	0.0.0.0   0.0.0.0 AF_INET PRIVATE 1000      SOCK_STREAM ca_path=ca_path.pem;key=key_path
	0.0.0.0   0.0.0.0 AF_INET PRIVATE 1000      SOCK_DGRAM  ca_path=ca_path.pem;key=key_path

=head2 Run4

On interface en0, port 1000, stream or datagram types and only private or link
local addresses:


	perl examples/cli.pl -l interface=en0,port=1000,group='pri|link'

	Interface Address                   Family   Group              Port Path Type        Data
	en0       192.168.1.103             AF_INET  PRIVATE            1000      SOCK_STREAM
	en0       192.168.1.103             AF_INET  PRIVATE            1000      SOCK_DGRAM 
	en0       fe80::1086:a38e:8f5d:38e2 AF_INET6 LINK-LOCAL-UNICAST 1000      SOCK_STREAM
	en0       fe80::1086:a38e:8f5d:38e2 AF_INET6 LINK-LOCAL-UNICAST 1000      SOCK_DGRAM 

=head2 Run5

On interface en0,lo and unix, port 1000, path mypath.sock, and stream type only

	perl examples/cli.pl -l interface='en0|lo|unix',port=1000,path=mypath.sock,socktype=stream

	Interface Address                   Family   Group              Port Path          Type        Data
	en0       192.168.1.103             AF_INET  PRIVATE            1000               SOCK_STREAM
	en0       fe80::1086:a38e:8f5d:38e2 AF_INET6 LINK-LOCAL-UNICAST 1000               SOCK_STREAM
	lo0       fe80::1                   AF_INET6 LINK-LOCAL-UNICAST 1000               SOCK_STREAM
	unix      mypath.sock_S             AF_UNIX  UNIX                    mypath.sock_S SOCK_STREAM

=head2 Run6

Shortened keys. Multiple listeners on command line:

First specification:	Interface en0, port 1000, only AF_INET and stream 

Second specification:	Interface lo or unix, AF_INET or UNIX types, po 2000
for inet and path test.sock for unix, datagram type only

	perl examples/cli.pl -l i='en0',po=1000,f='inet$',t=stream -l i='lo|unix',f='inet$|unix',po=2000,pa="test.sock",t=dgram

	Interface Address       Family  Group    Port Path        Type 	      Data
	en0       192.168.1.103 AF_INET PRIVATE  1000             SOCK_STREAM
	lo0       127.0.0.1     AF_INET LOOPBACK 2000             SOCK_DGRAM
	unix      test.sock_D   AF_UNIX UNIX          test.sock_D SOCK_DGRAM

=head2 RUN7

Interface en0 and lo, port 1010, private or link local group, multiple data keys

	examples/cli.pl -l in=en0,in=lo,po=1010,gr='PRI|link',data=ca=test,data=key=path

	Interface Address                   Family   Group              Port Path Type        Data            
	en0       192.168.1.103             AF_INET  PRIVATE            1010      SOCK_STREAM ca=test,key=path
	en0       192.168.1.103             AF_INET  PRIVATE            1010      SOCK_DGRAM  ca=test,key=path
	en0       fe80::1086:a38e:8f5d:38e2 AF_INET6 LINK-LOCAL-UNICAST 1010      SOCK_STREAM ca=test,key=path
	en0       fe80::1086:a38e:8f5d:38e2 AF_INET6 LINK-LOCAL-UNICAST 1010      SOCK_DGRAM  ca=test,key=path
	lo0       fe80::1                   AF_INET6 LINK-LOCAL-UNICAST 1010      SOCK_STREAM ca=test,key=path
	lo0       fe80::1                   AF_INET6 LINK-LOCAL-UNICAST 1010      SOCK_DGRAM  ca=test,key=path


=head1 SEE ALSO

Other modules provide network interface queries:
L<Net::Interface> seems broken at the time of writing
L<IO::Interface> works with IPv4 addressing only?

=head1 AUTHOR

Ruben Westerberg, E<lt>drclaw@mac.com<gt>

=head1 REPOSITORTY and BUGS

Please report any bugs via git hub: L<http://github.com/drclaw1394/perl-socket-more>


=head1 COPYRIGHT AND LICENSE

Copyright (C) 2023 by Ruben Westerberg

This library is free software; you can redistribute it and/or modify it under
the same terms as Perl or the MIT license.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE.

=cut
