=encoding utf8

=head1 NAME

Sidef::Types::Glob::Socket - Socket networking interface for Sidef

=head1 DESCRIPTION

The C<Socket> class provides an interface to socket networking operations in Sidef. A C<Socket> object itself is a lightweight, stateless helper: it is used to create sockets (via L</open> or L</socketpair>) and to reach the full set of constants and address-conversion helpers exported by Perl's core C<Socket> module — it does not itself represent an open connection.

Once you have an actual connection, you work with it through a L<Sidef::Types::Glob::SocketHandle> object (returned by L</open>, L</socketpair>, or C<SocketHandle>'s own C<accept>) — that's where C<bind>, C<listen>, C<accept>, C<connect>, C<send>, C<recv>, and the inherited file-handle methods (C<read>, C<print>, C<say>, C<close>, ...) live.

B<Constants and conversion helpers:> Every name exported by Perl's core L<Socket> module (address family constants like C<AF_INET>, socket type constants like C<SOCK_STREAM>, option constants like C<SO_REUSEADDR>, and helper functions such as C<inet_aton> or C<pack_sockaddr_in>) is automatically made available as a method on C<Socket> — called with no arguments for a constant, or with the appropriate arguments for a function. Arguments you pass in are unwrapped to plain values before being handed to the underlying Perl function, and results are wrapped back into the corresponding Sidef type. This means C<Socket.AF_INET>, C<Socket.SOCK_STREAM>, C<Socket.SO_REUSEADDR>, and so on are all available even though they aren't individually listed below — this documentation covers the ones you're most likely to need directly.

=head1 SYNOPSIS

    # Create and open a TCP socket
    var sock = Socket.open(Socket.PF_INET, Socket.SOCK_STREAM, 0) || die "Cannot open socket: #{$!}"

    # Resolve a hostname to its packed address
    var addr = Socket.inet_aton('127.0.0.1')

    # Pack a socket address
    var sockaddr = Socket.pack_sockaddr_in(80, addr)
    sock.connect(sockaddr) || die "Cannot connect: #{$!}"

    # Create a socket pair for IPC
    Socket.socketpair(\var reader, \var writer, Socket.PF_UNIX, Socket.SOCK_STREAM, 0)

=head1 INHERITS

Inherits methods from L<Sidef::Object::Object>.

=head1 CONSTRUCTION

=head2 new

    Socket.new

Creates a new, empty Socket helper object. This does not open any actual socket by itself — it takes no arguments, and any given are ignored. Use L</open> on the resulting object to actually create a socket.

=cut

=head1 CREATING SOCKETS

=head2 open

    self.open(domain, type, protocol)

Creates a new socket with the given domain (address family), type, and protocol, and returns it as a L<Sidef::Types::Glob::SocketHandle> — I<not> a boolean. Returns C<nil> on failure. C<domain>, C<type>, and C<protocol> should be plain numeric values, typically obtained from the constant methods below (e.g. C<Socket.PF_INET>, C<Socket.SOCK_STREAM>).

    var sock = Socket.open(Socket.PF_INET, Socket.SOCK_STREAM, 0)
    sock || die "Cannot open socket: #{$!}"

    var udp = Socket.open(Socket.PF_INET, Socket.SOCK_DGRAM, 0)

=cut

=head2 socketpair

    self.socketpair(var1, var2, domain, type, protocol)

Creates a pair of connected, unnamed sockets (typically for local inter-process communication) and assigns the two resulting L<Sidef::Types::Glob::SocketHandle> objects into C<var1> and C<var2>, which must be passed by reference. Returns a boolean indicating success.

    Socket.socketpair(\var parent, \var child, Socket.PF_UNIX, Socket.SOCK_STREAM, 0) \
        || die "socketpair failed: #{$!}"

    parent.say("hello from parent")
    say child.readline

=cut

=head1 HOST, NETWORK, PROTOCOL, AND SERVICE LOOKUPS

These methods query the traditional Unix host/network/protocol/service databases. Unlike the array-returning lookups you may have seen elsewhere in Sidef, each of these calls the underlying Perl function in a way that yields a I<single> value rather than a full record — see each method for exactly what that value is.

=head2 gethostbyname

    self.gethostbyname(name)

Resolves a hostname and returns its packed binary network address (not a hostname, and not a record). Returns C<nil> if the name can't be resolved.

    var addr = Socket.gethostbyname('localhost')
    say Socket.inet_ntoa(addr)     # e.g. "127.0.0.1"

=cut

=head2 gethostbyaddr

    self.gethostbyaddr(addr, addrtype)

Resolves a packed binary address back to a hostname string. C<addrtype> is an address family, typically C<Socket.AF_INET>. Returns C<nil> if the address can't be resolved.

    var addr = Socket.inet_aton('127.0.0.1')
    var name = Socket.gethostbyaddr(addr, Socket.AF_INET)
    say name    # e.g. "localhost"

=cut

=head2 gethostent

    self.gethostent

Returns the name of the next entry from the hosts database, or C<nil> when there are no more entries. Use L</sethostent> to rewind.

    Socket.sethostent(1)
    while (defined(var name = Socket.gethostent)) {
        say name
    }

=cut

=head2 sethostent

    self.sethostent(stayopen)

Opens and rewinds the hosts database. If C<stayopen> is true, the database connection is kept open between calls. Returns a boolean.

=cut

=head2 getnetbyname

    self.getnetbyname(name)

Looks up a network by name and returns its address as a single value. Returns C<nil> if not found.

    var net = Socket.getnetbyname('loopback')

=cut

=head2 getnetbyaddr

    self.getnetbyaddr(addr, addrtype)

Looks up a network by address and returns its name. Returns C<nil> if not found.

=cut

=head2 getnetent

    self.getnetent

Returns the next entry's name from the networks database, or C<nil> when exhausted. Use L</setnetent> to rewind.

=cut

=head2 setnetent

    self.setnetent(stayopen)

Opens and rewinds the networks database. If C<stayopen> is true, the connection is kept open between calls. Returns a boolean.

=cut

=head2 getprotobyname

    self.getprotobyname(name)

Looks up a protocol by name (e.g. C<'tcp'>) and returns its protocol I<number>. Returns C<nil> if not found.

    say Socket.getprotobyname('tcp')     # 6

=cut

=head2 getprotobynumber

    self.getprotobynumber(num)

Looks up a protocol by number and returns its I<name>. Returns C<nil> if not found.

    say Socket.getprotobynumber(6)     # "tcp"

=cut

=head2 getprotoent

    self.getprotoent

Returns the next entry's protocol number from the protocols database, or C<nil> when exhausted. Use L</setprotoent> to rewind.

=cut

=head2 setprotoent

    self.setprotoent(stayopen)

Opens and rewinds the protocols database. If C<stayopen> is true, the connection is kept open between calls. Returns a boolean.

=cut

=head2 getservbyname

    self.getservbyname(name, proto)

Looks up a service by name and protocol (e.g. C<'tcp'> or C<'udp'>) and returns its port number (as a string). Returns C<nil> if not found.

    say Socket.getservbyname('http', 'tcp')     # "80"

=cut

=head2 getservbyport

    self.getservbyport(port, proto)

Looks up a service by port number and protocol, returning its name. Returns C<nil> if not found.

    say Socket.getservbyport(22, 'tcp')     # "ssh"

=cut

=head2 getservent

    self.getservent

Returns the next entry's service name from the services database, or C<nil> when exhausted. Use L</setservent> to rewind.

=cut

=head2 setservent

    self.setservent(stayopen)

Opens and rewinds the services database. If C<stayopen> is true, the connection is kept open between calls. Returns a boolean.

=cut

=head1 MODERN ADDRESS RESOLUTION

=head2 getaddrinfo

    self.getaddrinfo(host, service)
    self.getaddrinfo(host, service, hints)

Performs modern, protocol-agnostic address resolution (supporting both IPv4 and IPv6). The returned array's first element is an error message (empty on success), followed by one entry per resolved address.

    var res = Socket.getaddrinfo('www.example.com', 'http')
    var err = res[0]

    if (err == '') {
        res.slice(1).each { |entry|
            say entry
        }
    }
    else {
        say "getaddrinfo failed: #{err}"
    }

=cut

=head2 getnameinfo

    self.getnameinfo(sockaddr)
    self.getnameinfo(sockaddr, flags)

Converts a socket address into a hostname and service name (the reverse of L</getaddrinfo>). Returns a three-element array: an error message (empty on success), the hostname, and the service name.

    var sockaddr = Socket.pack_sockaddr_in(80, Socket.inet_aton('127.0.0.1'))
    var res = Socket.getnameinfo(sockaddr)

    if (res[0] == '') {
        say "Host: #{res[1]}, Service: #{res[2]}"
    }

=cut

=head1 ADDRESS CONVERSION

=head2 inet_aton

    self.inet_aton(address)

Converts an IPv4 address from dotted-quad string format to packed binary format. Returns C<nil> on error.

    var addr = Socket.inet_aton('127.0.0.1')

=cut

=head2 inet_ntoa

    self.inet_ntoa(addr)

Converts a packed binary IPv4 address to dotted-quad string format — the reverse of L</inet_aton>.

    say Socket.inet_ntoa(Socket.inet_aton('192.168.1.1'))     # "192.168.1.1"

=cut

=head2 inet_pton

    self.inet_pton(af, address)

Converts an IPv4 or IPv6 address from string form to packed binary form. C<af> is C<Socket.AF_INET> or C<Socket.AF_INET6>. This is the protocol-agnostic successor to L</inet_aton>.

    var ipv4 = Socket.inet_pton(Socket.AF_INET, '192.168.1.1')
    var ipv6 = Socket.inet_pton(Socket.AF_INET6, '::1')

=cut

=head2 inet_ntop

    self.inet_ntop(af, addr)

Converts a packed binary address to string form — the reverse of L</inet_pton>.

    var addr = Socket.inet_pton(Socket.AF_INET6, '::1')
    say Socket.inet_ntop(Socket.AF_INET6, addr)     # "::1"

=cut

=head1 SOCKET ADDRESS PACKING/UNPACKING

=head2 pack_sockaddr_in

    self.pack_sockaddr_in(port, ip_addr)

Packs a port number and IPv4 address (from L</inet_aton>) into a binary C<sockaddr_in> structure, suitable for L<SocketHandle/bind> or L<SocketHandle/connect>.

    var sockaddr = Socket.pack_sockaddr_in(8080, Socket.inet_aton('127.0.0.1'))

=cut

=head2 unpack_sockaddr_in

    self.unpack_sockaddr_in(sockaddr)

Unpacks a C<sockaddr_in> structure, returning a C<[port, ip_addr]> array.

    var sockaddr = Socket.pack_sockaddr_in(8080, Socket.inet_aton('127.0.0.1'))
    var res = Socket.unpack_sockaddr_in(sockaddr)
    say "Port: #{res[0]}, IP: #{Socket.inet_ntoa(res[1])}"

=cut

=head2 pack_sockaddr_in6

    self.pack_sockaddr_in6(port, ip6_addr, scope_id=0, flowinfo=0)

Packs a port number and IPv6 address (from L</inet_pton>) into a binary C<sockaddr_in6> structure.

    var addr = Socket.inet_pton(Socket.AF_INET6, '::1')
    var sockaddr = Socket.pack_sockaddr_in6(8080, addr)

=cut

=head2 unpack_sockaddr_in6

    self.unpack_sockaddr_in6(sockaddr)

Unpacks a C<sockaddr_in6> structure, returning C<[port, ip6_addr, scope_id, flowinfo]>.

=cut

=head2 pack_sockaddr_un

    self.pack_sockaddr_un(path)

Packs a filesystem path into a Unix domain socket address structure.

    var sockaddr = Socket.pack_sockaddr_un('/tmp/my.sock')

=cut

=head2 unpack_sockaddr_un

    self.unpack_sockaddr_un(sockaddr)

Unpacks a Unix domain socket address structure, returning the filesystem path.

=cut

=head2 sockaddr_family

    self.sockaddr_family(sockaddr)

Extracts the address family from any packed socket address structure.

    var family = Socket.sockaddr_family(sockaddr)
    if ((family == Socket.AF_INET)) {
        say "IPv4 address"
    }

=cut

=head1 MULTICAST GROUP MEMBERSHIP

=head2 pack_ip_mreq

    self.pack_ip_mreq(multiaddr, interface)

Packs an IPv4 multicast group membership request, for use with L<SocketHandle/setsockopt>.

    var mreq = Socket.pack_ip_mreq(Socket.inet_aton('239.255.0.1'), Socket.inet_aton('0.0.0.0'))

=cut

=head2 unpack_ip_mreq

    self.unpack_ip_mreq(mreq)

Unpacks an C<ip_mreq> structure, returning C<[multiaddr, interface]>.

=cut

=head2 pack_ip_mreq_source

    self.pack_ip_mreq_source(multiaddr, source, interface)

Packs a source-specific IPv4 multicast group membership request.

=cut

=head2 unpack_ip_mreq_source

    self.unpack_ip_mreq_source(mreq_source)

Unpacks an C<ip_mreq_source> structure, returning C<[multiaddr, source, interface]>.

=cut

=head2 pack_ipv6_mreq

    self.pack_ipv6_mreq(multiaddr, interface_index)

Packs an IPv6 multicast group membership request.

    var mreq = Socket.pack_ipv6_mreq(Socket.inet_pton(Socket.AF_INET6, 'ff02::1'), 0)

=cut

=head2 unpack_ipv6_mreq

    self.unpack_ipv6_mreq(mreq)

Unpacks an C<ipv6_mreq> structure, returning C<[multiaddr, interface_index]>.

=cut

=head1 EXAMPLES

=head2 Simple TCP Client

    var sock = Socket.open(Socket.PF_INET, Socket.SOCK_STREAM, 0) || die "Cannot create socket: #{$!}"

    var sockaddr = Socket.pack_sockaddr_in(80, Socket.inet_aton('127.0.0.1'))
    sock.connect(sockaddr) || die "Cannot connect: #{$!}"

    sock.print("GET / HTTP/1.0\r\n\r\n")
    while (defined(var line = sock.readline)) {
        say line
    }
    sock.close

=head2 UDP Socket

    var sock = Socket.open(Socket.PF_INET, Socket.SOCK_DGRAM, 0) || die "Cannot create socket: #{$!}"

    var dest = Socket.pack_sockaddr_in(9999, Socket.inet_aton('192.168.1.100'))
    sock.send("Hello UDP", 0, dest)

=head2 Unix Domain Socket

    var sock = Socket.open(Socket.PF_UNIX, Socket.SOCK_STREAM, 0) || die "Cannot create socket: #{$!}"

    var sockaddr = Socket.pack_sockaddr_un('/tmp/my.sock')
    sock.connect(sockaddr) || die "Cannot connect: #{$!}"

=head2 IPv6 Socket

    var sock = Socket.open(Socket.PF_INET6, Socket.SOCK_STREAM, 0) || die "Cannot create socket: #{$!}"

    var addr = Socket.inet_pton(Socket.AF_INET6, '::1')
    var sockaddr = Socket.pack_sockaddr_in6(8080, addr)
    sock.connect(sockaddr) || die "Cannot connect: #{$!}"

=head1 SEE ALSO

=over 4

=item * L<Sidef::Types::Glob::SocketHandle> - The open connection object returned by L</open>

=item * L<Sidef::Types::Glob::FileHandle> - File I/O operations, inherited by SocketHandle

=item * L<Socket> - Perl's Socket module, which this class delegates to

=item * socket(2), connect(2), bind(2), listen(2), accept(2) - Underlying Unix socket system calls

=back

=cut
