Skip to content

Conversation

@bjacquin
Copy link
Contributor

@bjacquin bjacquin commented Dec 6, 2025

Short description

Abstract namespace is a Linux specific feature to bind a UNIX domain
socket without the need for any filesystem access, which is particularly
interesting to allow a process running in a chroot or with limited
privileges to access the webserver socket over UNIX domain socket.

The main difference from regular UNIX domain socket is that the first
byte of sun_path is set to zero.

Abstract namespace are defined by prefixing value with abns@.

  $ cat pdns.conf
  ..
  webserver=yes
  webserver-address=abns@pdns.http

  $ curl -sD - -o /dev/null --abstract-unix-socket pdns.http http://localhost/metrics
  *   Trying :0...
  * Established connection to localhost ( port 0) from  port 0
  * using HTTP/1.x
  > GET /metrics HTTP/1.1
  > Host: localhost
  > User-Agent: curl/8.16.0
  > Accept: */*
  >
  * Request completely sent off
  < HTTP/1.1 200 OK
  < Connection: close
  < Content-Length: 15040
  < Content-Type: text/plain; version=0.0.4
  <
  { [15040 bytes data]
  * shutting down connection #0

Checklist

I have:

  • read the CONTRIBUTING.md document
  • read and accepted the Developer Certificate of Origin document, including the AI Policy, and added a "Signed-off-by" to my commits
  • compiled this code
  • tested this code
  • included documentation (including possible behaviour changes)
  • documented the code
  • added or modified regression test(s)
  • added or modified unit test(s)

ret is first initialized to zero, and verification is performed to
ensure path length is not greater than size of sockaddr_un->sun_path,
thus we can reduce amount of data copied to actual size of path.

Signed-off-by: Bertrand Jacquin <bertrand@jacquin.bzh>
Returning sizeof(struct sockaddr_un) from SockaddrWrapper bind the
socket to full length of struct sockaddr_un

  bind(4, {sa_family=AF_UNIX, sun_path="/tmp/pdns.controlsocket"}, 110) = 0

This change now return a size relative to the actual content of sun_path
as specified in unix(7).

  bind(4, {sa_family=AF_UNIX, sun_path="/tmp/pdns.controlsocket"}, 25) = 0

Signed-off-by: Bertrand Jacquin <bertrand@jacquin.bzh>
@bjacquin bjacquin force-pushed the dev/beber/abns branch 5 times, most recently from 55b3330 to 07d56d5 Compare December 6, 2025 14:23
Abstract namespace is a Linux specific feature to bind a UNIX domain
socket without the need for any filesystem access, which is particularly
interesting to allow a process running in a chroot or with limited
privileges to access the webserver socket over UNIX domain socket.

The main difference from regular UNIX domain socket is that the first
byte of sun_path is set to zero.

Abstract namespace are defined by prefixing value with `abns@`.

  $ cat pdns.conf
  ..
  webserver=yes
  webserver-address=abns@pdns.http

  $ curl -sv --abstract-unix-socket pdns.http http://localhost/metrics
  *   Trying :0...
  * Established connection to localhost ( port 0) from  port 0
  * using HTTP/1.x
  > GET /metrics HTTP/1.1
  > Host: localhost
  > User-Agent: curl/8.16.0
  > Accept: */*
  >
  * Request completely sent off
  < HTTP/1.1 200 OK
  < Connection: close
  < Content-Length: 15033
  < Content-Type: text/plain; version=0.0.4
  <
  { [15033 bytes data]
  * shutting down connection #0

Signed-off-by: Bertrand Jacquin <bertrand@jacquin.bzh>
@coveralls
Copy link

coveralls commented Dec 6, 2025

Pull Request Test Coverage Report for Build 19989803359

Details

  • 6 of 15 (40.0%) changed or added relevant lines in 4 files are covered.
  • 65 unchanged lines in 13 files lost coverage.
  • Overall coverage decreased (-0.02%) to 73.298%

Changes Missing Coverage Covered Lines Changed/Added Lines %
pdns/misc.cc 4 5 80.0%
pdns/iputils.hh 0 8 0.0%
Files with Coverage Reduction New Missed Lines %
pdns/dnsdistdist/dnsdist-healthchecks.cc 2 59.64%
pdns/recursordist/sortlist.cc 2 72.94%
modules/gpgsqlbackend/spgsql.cc 3 68.18%
pdns/iputils.cc 3 59.73%
pdns/misc.hh 3 85.03%
pdns/opensslsigners.cc 3 61.34%
pdns/recursordist/test-syncres_cc2.cc 3 89.12%
pdns/dnsdistdist/dnsdist-tcp.cc 4 77.4%
pdns/signingpipe.cc 5 85.52%
pdns/recursordist/syncres.cc 6 80.99%
Totals Coverage Status
Change from base Build 19969176468: -0.02%
Covered Lines: 128701
Relevant Lines: 164812

💛 - Coveralls

@bjacquin
Copy link
Contributor Author

bjacquin commented Dec 6, 2025

I see that spell check is failing for this PR, I have tried a number of alternative to ignore abns, but I'm not able to find exactly what is wrong here. Could you please guide me ?

@jsoref
Copy link
Contributor

jsoref commented Dec 9, 2025

Hi, I'm the @check-spelling developer. I'm curious what alternatives you tried.

The goal is for you to load https://github.com/PowerDNS/pdns/actions/runs/19989803358/attempts/1#summary-57328879876 and use one of the To accept these unrecognized words as correct, options to add the items to the expect.txt file, but if the messaging isn't clear, then I probably need to work on improving it.

Comment on lines +20 to +21
* :ref:`setting-webserver-port`: Port to bind the webserver to (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket or abstract namespace).
* :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket or abstract namespace).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd reverse the logic and write

Suggested change
* :ref:`setting-webserver-port`: Port to bind the webserver to (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket or abstract namespace).
* :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket or abstract namespace).
* :ref:`setting-webserver-port`: Port to bind the webserver to (only relevant if :ref:`setting-webserver-address` is set to an IP address).
* :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver (only relevant if :ref:`setting-webserver-address` is set to an IP address).


Webserver/API access is only allowed from these subnets.
Ignored if ``webserver-address`` is set to a UNIX domain socket.
Ignored if ``webserver-address`` is set to a UNIX domain socket or abstract namespace.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly,

Suggested change
Ignored if ``webserver-address`` is set to a UNIX domain socket or abstract namespace.
Ignored unless ``webserver-address`` is set to an IP address.


The port where webserver/API will listen on.
Ignored if ``webserver-address`` is set to a UNIX domain socket.
Ignored if ``webserver-address`` is set to a UNIX domain socket or abstract namespace.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and

Suggested change
Ignored if ``webserver-address`` is set to a UNIX domain socket or abstract namespace.
Ignored unless ``webserver-address`` is set to an IP address.


* :ref:`setting-webserver`: If set to anything but 'no', a webserver is launched.
* :ref:`setting-webserver-address`: IP address (or UNIX domain socket path, from version 5.0.0 onward) to bind the webserver to. Defaults to 127.0.0.1, which implies that only the local computer is able to connect to the nameserver! To allow remote hosts to connect, change to 0.0.0.0 or the physical IP address of your nameserver.
* :ref:`setting-webserver-address`: IP address, or UNIX domain socket path (from version 5.0.0 onward) or abstract namespace (Linux only) to bind the webserver to. Defaults to 127.0.0.1, which implies that only the local computer is able to connect to the nameserver! To allow remote hosts to connect, change to 0.0.0.0 or the physical IP address of your nameserver.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* :ref:`setting-webserver-address`: IP address, or UNIX domain socket path (from version 5.0.0 onward) or abstract namespace (Linux only) to bind the webserver to. Defaults to 127.0.0.1, which implies that only the local computer is able to connect to the nameserver! To allow remote hosts to connect, change to 0.0.0.0 or the physical IP address of your nameserver.
* :ref:`setting-webserver-address`: IP address, UNIX domain socket path (from version 5.0.0 onward) or abstract namespace (Linux only) to bind the webserver to. Defaults to 127.0.0.1, which implies that only the local computer is able to connect to the nameserver! To allow remote hosts to connect, change to 0.0.0.0 or the physical IP address of your nameserver.

}

createSocketAndBind(AF_UNIX, (struct sockaddr*)& local, sizeof(local));
createSocketAndBind(AF_UNIX, (struct sockaddr*)& local, sizeof(local) - sizeof(local.sun_path) + fname.length());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am worried this change, which seems to be needed for abstract sockets, may break proper operation on regular unix sockets on some platforms.

So as a very minimum, this length computation (here and in SockaddrWrapper below) should be made conditional depending on whether this is an abstract socket or not.

return -1;

path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
if (path.find("abns@", 0, 5) == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments would not hurt here. Maybe

Suggested change
if (path.find("abns@", 0, 5) == 0) {
// If a Linux abstract socket is required, put its name in sun_path after a leading
// NUL byte.
if (path.find("abns@", 0, 5) == 0) {

return -1;

path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
if (path.find("abns@", 0, 5) == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, find here does not guarantee the match will occur at the beginning of the string.
Why not simply use

Suggested change
if (path.find("abns@", 0, 5) == 0) {
if (path.substr(0, 5) == "abns@") {


path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
if (path.find("abns@", 0, 5) == 0) {
path.copy(ret->sun_path + 1, path.length() -5, 5);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
path.copy(ret->sun_path + 1, path.length() -5, 5);
path.copy(ret->sun_path + 1, std::string::npos, 5);

would probably be simpler.


memset(ret, 0, sizeof(struct sockaddr_un));
ret->sun_family = AF_UNIX;
if (path.length() >= sizeof(ret->sun_path))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to check for the abstract socket prefix earlier, so that, at this point, the length check could be path.length() - 5 /* prefix len */ >= sizeof(ret->sun_path) - 1 for abstract sockets.

i.e.

#ifdef __linux__
bool isLinuxAbstractSocket = path.substr(0, 5) == "abns@";
#else
bool isLinuxAbstractSocket{false};
#endif

if (isLinuxAbstractSocket) {
  length check for abstract socket
  ret->sun_path setup for abstract socket
}
else {
  length check for regular unix socket
  ret->sun_path setup for regular unix socket
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants