Re: [systemd-devel] How to get a useful peer address when doing accept(3, ...) on a systemd supplied listening socket

2022-10-27 Thread Klaus Ebbe Grue
Hi Mantas


> I have a feeling it "changes" because you're trying to give the whole
> struct sockaddr to inet_pton() instead of giving just the .sin6_addr field,
> so your program is trying to interpret the *port number*
> (i.e. the .sin6_port which precedes .sin_addr) as part of the address...

That was exactly my mistake! Thanks.

At some point in my hopeless odyssey down a wrong track I tried giving the 
whole struct to inet_ntop() and forgot to change it back when I got everything 
else to work.

Thanks for your help and sorry for the inconvenience.

Cheers,
Klaus



Re: [systemd-devel] How to get a useful peer address when doing accept(3, ...) on a systemd supplied listening socket

2022-10-27 Thread Mantas Mikulėnas
On Thu, Oct 27, 2022 at 1:51 PM Klaus Ebbe Grue  wrote:

> Hi systemd-devel,
>
> Sorry to bug you with another user question.
>
> I have a socket activated daemon, call it mydaemon, and I have trouble
> finding out who connects to it.
>
>
> mydaemon.socket contains:
>
>
>   [Socket]
>   ListenStream=
>
> When I connect using IPv4 using
>
>   nc -4 localhost 
>
> then mydaemon does
>
>   sockaddr_in6 peer;
>   socklen_t peer_size=sizeof(peer);
>   accept(3,(struct sockaddr *),sizeof(peer))
>
> Afterwards, peer.sin6_family is AF_INET6 and peer.sin6_addr contains some
> gibberish like a00:e5ae::
>

If you specify nothing for the listen address, systemd will assume the IPv6
address [::] as the default, and will create an AF_INET6 socket bound to
[::]:.

Due to Linux's default "bind both families" magic, it will actually be
bound to both [::]: *and* 0.0.0.0:, so it will accept IPv4
connections – but you'll receive them in the form of AF_INET6 sockets, so
the peer address of your v4 client indeed has family AF_INET6 but contains
a "v6-mapped" IPv4 address such as [:::10.0.229.174] aka
[:::a00:e5ae].

The alternative would be to specify both ListenStream=[::]: and
ListenStream=0.0.0.0: (as well as BindIPv6Only=ipv6-only), which would
cause you to receive *two* socket FDs – one purely for IPv6 clients, the
other for IPv4 – that you'd have to put into poll() or some other loop for
accepting clients.

You can extract the IPv4 address by detecting the [:::0:0/96] prefix
and stripping away the first 12 bytes. (There's also a magic option for
getsockopt() listed in ipv6(7) that can convert such a "v6-mapped" socket
to a "real" AF_INET socket, but it's rarely needed.)


>
> If I connect more than once, the gibberish changes from connection to
> connection.
>

I have a feeling it "changes" because you're trying to give the whole
struct sockaddr to inet_pton() instead of giving just the .sin6_addr field,
so your program is trying to interpret the *port number* (i.e. the
.sin6_port which precedes .sin_addr) as part of the address...

But please show your entire code, otherwise this is all just guessing.

Here's a working example that I've just tested with
`systemd-socket-activate --listen=`:
https://gist.github.com/grawity/63369273742f23b596d764cb6d45feb7


>
> If mydaemon creates the listening socket, I can easily get the peer
> address.
>
> I suspect that when systemd creates the listening socket then
> accept(3,...) returns a socket which is connected to a local socket created
> by systemd.
>
> QUESTION: Is that suspicion correct?
>

No, it isn't.

>

-- 
Mantas Mikulėnas


[systemd-devel] How to get a useful peer address when doing accept(3, ...) on a systemd supplied listening socket

2022-10-27 Thread Klaus Ebbe Grue
Hi systemd-devel,

Sorry to bug you with another user question.

I have a socket activated daemon, call it mydaemon, and I have trouble finding 
out who connects to it.


mydaemon.socket contains:


  [Socket]
  ListenStream=

When I connect using IPv4 using

  nc -4 localhost 

then mydaemon does

  sockaddr_in6 peer;
  socklen_t peer_size=sizeof(peer);
  accept(3,(struct sockaddr *),sizeof(peer))


Afterwards, peer.sin6_family is AF_INET6 and peer.sin6_addr contains some 
gibberish like a00:e5ae::


If I connect more than once, the gibberish changes from connection to 
connection.


Something similar happens if I connect using IPv6.


If I change mydaemon.socket to


  [Socket]
  ListenStream=0.0.0.0:

Then peer.sin6_family becomes AF_INET as it should. But if peer is cast to 
struct sockaddr_in then peer.sin_addr still contains gibberish like 2.0.191.150 
(I expected something like 127.0.0.1 or 192.168.0.99).

When I connect from other machines, the peer address still is gibberish.

If mydaemon creates the listening socket, I can easily get the peer address.

I suspect that when systemd creates the listening socket then accept(3,...) 
returns a socket which is connected to a local socket created by systemd.

QUESTION: Is that suspicion correct? And if yes, is there are way to recover 
the address of the actually connecting peer?

Cheers,
Klaus