-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
For local services use "localhost" in device URI of temporary queue #353
Conversation
I would like to do some regression testing before we merge this… |
To get more testing feedback I have applied this patch to Ubuntu's CUPS package (version 2.4.1op1-1ubuntu2) now. So should it cause regressions, I should get bug reports on Ubuntu (Jammy Jellyfish, 22.04). |
Updated the PR as CUPS crashed with it when not sharing printers ( |
This is now in Ubuntu 22.04 LTS (Jammy Jellyfish) for 2 months without any further complaints. What about merging it? |
Hi @tillkamppeter , if I understand the issue correctly, you cannot print via GTK dialog to IPP services which are advertised on localhost (such as IPP services generated by ipp-usb for IPP-over-USB supported USB printers), correct? If it is the case, then it looks like an OS issue - because I can print to such queues via GTK on Fedora (at least on 36, because my laptop at the office has Fedora 36, but it is the first release I've tried our Canon printer, which can do IPP-over-USB, with, so maybe earlier Fedoras/GTK can do that as well). I've tried printing via temp queue based on device from snapped ps-printer-app, and printing worked as well. No cups-browsed was needed. I have the following versions of GTK, nss-mdns and Avahi:
gtk3 and nss-mdns are vanillas from upstream, but avahi has several downstream patches because of upstream unresponsiveness, so maybe some of them can make it working for Debian/Ubuntu. I would wait with merging until an investigation regarding mDNS resolving happens in Debian/Ubuntu, because IMHO changing the current behavior which works for some distributions can create other regressions... |
@zdohnal could you run the following commands and post the output here:
|
Here are my findings I see that my .local hostname for my machine has 3 addresses when I'm connected - ipv4, ipv6 and localhost (127.0.0.1). If I'm disconnected, the .local hostname is resolved only as localhost and thus advertised only such as. |
I have looked into your file. Things look like as they would come out also on my system.
I do not see the localhost (127.0.0.1) as you say. How did you find that for your .local host name? |
Is CUPS working for you as expected (print with GTK dialog to temporary CUPS queue for local-only service) both when connected and when not connected? |
If you use the name "fedora.local" on your machine, is it resolving to 127.0.0.1 or to your machine's network IP address? |
Aha, I was so focused on services being showed on localhost, so I have missed localhost address is missing from the
Yes - for both sources I've tried - ipp-usb and ps-printer-app. Resolving switched correctly to 127.0.0.1 if I'm disconnected.
Looks like it depends whether I'm connected or not - if I have network IP address, then it is used in
when I'm disconnected however (i.e. I don't have network IP, only localhost IP):
I guess it happens because we have in /etc/nsswitch.conf:
IIUC
However from Fedora printing stack view it works correctly, at least on Fedora. |
At Ubuntu we have only
in So I will try CUPS without this patch but first adding I will also discuss my result with the network experts at Canonical then. |
@zdohnal thank you very much for this hint. |
First I removed the patch from CUPS. With patch I can print to temporary queues for localhost-provided IPP services from the GTK print dialog, without I cannot. @zdohnal @pemensik I have tried. For that I had to install the Ubuntu/Debian packages libnss-myhostname and libnss-resolve which also added the appropriate entries in Unfortunately the problem did not go away, I still cannot print to temporary queues for localhost-provided IPP services from the GTK print dialog. I have also modified the automatically created line in
to
Also does not help. |
Why should local services on localhost require any kind of discovery other than registration to avahi or CUPS? If services running on the same host need multicast resolution or special plugins, there is probably wrong design somewhere. There should be a simple registration method for local services. Be it DBus or just unix socket. But services running locally should not require sending multicast queries to network. |
It is possible to test also each plugin separately, if you are in doubt. Try
I think when something should resolve to 127.0.0.1 address, its name should be always |
Althought off-topic, but to clear this - AFAIK we use the mDNS for local services to unify the processing path for as many devices as possible. So instead of having different processing paths for different networks, local networks and localhost services, we use the same processing for devices at local networks and at localhost (devices from different networks will have a different process - similar as it has now, but internals will be different). But this is really off-topic to this - in case you want to discuss printing stack architecture, feel free to write to printing-architecture@lists.linux-foundation.org. |
Is loopback interface used to send mdns queries as well then? |
@pemensik hmm, I thought <my_hostname>.local hostname could be resolved to 127.0.0.1 since it is a hostname for your local machine, but if I apply logic from classic DNS, every hostname has a specific IP address which is not localhost... so probably resolved (@pemensik please correct me who does 'myhostname' in Fedora - my guess is resolved) takes the first available address for the hostname and uses it. I can see from logs that if I'm connected the hostname is resolved to my local address:
So though it works, we should really use only localhost for local IPP services. @tillkamppeter I'll do some regression testing in Fedora with the patch and I'll let you know. |
@zdohnal |
No, but Avahi keeps track of services that are registered for the loopback interface and will return them in a query, and all of those queries happen via Avahi's D-Bus interface... |
No, I think it tries to export ALL addresses detected on the system, not only small subset. I think
Agreed.
Great, so Avahi just maintains the list for localhost the same way it does for the network. Hides implementation difference. Just note that collision resolution on mdns can modify local hostname exported to avahi. It should not take just hostname from the system, but hostname used by Avahi. |
scheduler/ipp.c
Outdated
if (strcmp(nameptr + server_name_len - 7, ".local.") == 0) | ||
server_name_len -= 7; | ||
|
||
if (host_len == server_name_len && strncmp(host, nameptr, host_len) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If length is already known, use memcmp(host, nameptr, host_len) == 0. It should be faster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should actually be comparing with strncasecmp...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct. domain names are case insensitive.
scheduler/ipp.c
Outdated
sizeof(host), &port, resource, sizeof(resource)); | ||
|
||
host_len = strlen(host); | ||
if (strcmp(host + host_len - 6, ".local") == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we hardcode local.
domain? Perhaps domain should be fetched somehow from Avahi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are comparing to Avahi's DNS-SD hostname, so we only care about .local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt it is common, but mdns nss plugin can configure also different domains than .local. Also, can there be a difference between name ending with dot and without it? In DNS, trailing dot means that name does not get applied search domains from /etc/resolv.conf. Can something like that happen in Avahi? I think trailing dot could be squashed by:
if (host_len>1 && host[host_len-1] == '.')
host[--host_len] = '\0';
Then it would need just single comparison for local instead of two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments. But I am still not happy with this...
scheduler/ipp.c
Outdated
sizeof(host), &port, resource, sizeof(resource)); | ||
|
||
host_len = strlen(host); | ||
if (strcmp(host + host_len - 6, ".local") == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are comparing to Avahi's DNS-SD hostname, so we only care about .local.
@michaelrsweet before changing/optimizing anything of the code, I want to compare the host name in the requested device URI for the local print queue creation by the How do I have to compare actually? When can I consider the host name cupsd is running under and the host name in the requested device URI as equal? Or is it all OK if I simply switch to case-insensitive comparing, as you suggest in your first comment? |
@pemensik We do try to remove the trailing dot when resolving an mDNS service, but the code here should also check for ".local." on the end. |
@michaelrsweet So how has the comparison yo look like? I Know now that it has to be case-insensitive and with and without dot at the end can be treated equal. Now should I also treat "NAME" and "NAME.local" equal or not? @michaelrsweet and is the correction of the comparison the only code change needed? |
I wouldn't treat "NAME" and "NAME.local" as the same, since "NAME" doesn't identify a specific domain and might resolve to a different address.
I'm still not 100% happy with this change since it depends on the server to detect a local service vs. the client-side code which will also know whether any of the resolved addresses of the service is 127.0.0.1 or ::1 before sending the Create-Local-Printer request... |
@michaelrsweet OK, so what I can only do is to make the comparison case-insensitive and consider only equal if the maximum difference between the host names is a trailing dot or no trailing dot. I do not know ehetjer more can be done server-side. And we cannot bet on the client as as we see with the GTK dialog the client developers do not necessarily use the CUPS APIs (I hope this will improve when the CPDB gets widely adopted). @michaelrsweet WDYT? Should we for the time being do the case-insensitive comparison plus treatment as equal with or without trailing dot but you will look into a better solution which will be put as soon as you have it? |
@tillkamppeter For now, sure. |
@michaelrsweet I have updated the code now. If our CUPS actually has a |
To print to a temporary CUPS queue for a discovered IPP printer the client has to send an IPP_OP_CUPS_CREATE_LOCAL_PRINTER IPP request to the CUPS daemon with the details for creating the temporary queue. CUPS then calls its create_local_printer() function to actually create the queue. This is already automatically and correctly done if the client uses the convenience API of libcups. The GTK print dialog does not use the convenience API though. It displays all IPP printers for which CUPS can create temporary queues correctly, but it fails on services which are only available on the local machine and not on the network (loopback interface, "localhost") whereas printing via temporary queue on network/remote services works perfectly. The problem is that Avahi advertises the local services not with the "localhost" host name but with the network host name of the local machine. The libcups convenience API functions can cope with this and send correct device URIs with the "localhost" host name to CUPS and so the temporary queue gets created correctly. The GTK dialog wants fully asynchronous CUPS operation and throws the libcups API completely overboard doing all by itself, and sending incorrect device URIs with the network hostname for the local services. CUPS creates the temporary queue with this URI then and fails to do the get-printer-attributes request on the printer to generate the PPD file, leaving the temporary queue in a non-functional state. This commit correct/works around this by checking whether the device URI's host name is the same as the DNS-SD host name of the local machine and if so, replaces the device URI's host name by "localhost". Now one can select local services like IPP-over-USB printers or Printer Applications in the GTK print dialog and printing on them through an auto-generated temporary CUPS queue works. I decided to fix/work around this problem in CUPS as we see that no every print client developer uses the libcups convenience API.
If cupsd is running without Browsing (when not sharing printers) the global variable DNSSDHostName of the scheduler is not set, staying NULL. Therefore the previous commit causes a crash with this configuration of CUPS. This commit does a NULL check on DNSSDHostName and falls back to ServerName if needed, where it also does a NULL check and if this is also NULL, it refrains from any attempt to correct the device URI's hostname to "localhost". As ServerName usually contains the server's hostname without ".local" suffix but the device URIs coming from Avahi usually have a suffixed host name (and there are also the ".local" and ".local." variants of the suffix), we consider hostnames also as equal if one has the suffix, the other not, or if we have the variants with and without trailing dot. In addition, there are also HAVE_DNSSD conditionals added around the code using DNSSDHostName now.
Improved the comparison of the device URI host name with the CUPS server's host name to find out whether the temporary queue is for a local IPP service. - Compare case-insensitively as host names are case-insensitive - If we have the DNS-SD host name (DNSSDHostName) of the CUPS server, consider names equal if they only differ by the presence or absense of a trailing dot ('.') - If we only have the ServerName (for example with "Browsing = Off", not sharing printers), consider also a server name without ".local" equal to a device URI host name with ".local" as equal.
@tillkamppeter I'll rebase your branch to get the latest cups master branch + your fix, so I can test the latest CUPS master branch with your fix. |
39a23d7
to
e5a6bc2
Compare
Test results - printing via lp using queue based on ps-printer-app (Fedora 37 - Rawhide):
we use localhost anyway. If I print from evince (using via ps-printer-app):
Printing via device created by ipp-usb from evince:
All jobs ended successfully. I don't have an IPP Everywhere capable network printer in local network at the office yet (networking still needs to be adjust...), so I'll do the verification with network printer at home. |
Printing to network printer from evince works as well, using the correct mDNS address. |
I have tested the PR with DNSSDHostName and ServerName combinations as well, all prints well from evince via temp queue. The code LGTM, merging for now until we have more complex solution. |
Thank you for the patch, @tillkamppeter ! |
On 08. 06. 22 7:08, zdohnal wrote:
I do not see the localhost (127.0.0.1) as you say. How did you
find that for your .local host name?
Aha, I was so focused on services being showed on localhost, so I have
missed localhost address is missing from the |host| output - since
avahi-browse shows 3 different addresses for the same .local hostname.
Is CUPS working for you as expected (print with GTK dialog to
temporary CUPS queue for local-only service) both when connected
and when not connected?
Yes - for both sources I've tried - ipp-usb and ps-printer-app.
Resolving switched correctly to 127.0.0.1 if I'm disconnected.
If you use the name "fedora.local" on your machine, is it
resolving to 127.0.0.1 or to your machine's network IP address?
Looks like it depends whether I'm connected or not - if I have network
IP address, then it is used in |ping|:
|$ ping fedora.local PING fedora.local (<my IP>) 56(84) bytes of data. |
when I'm disconnected however (i.e. I don't have network IP, only
localhost IP):
|$ ping fedora.local PING fedora.local (127.0.0.1) 56(84) bytes of data.|
|Why don't you use just 'localhost'? Is there local caching server,
which does not provide such name?||| It depends on /etc/hosts entry on
Fedora with dnsmasq for example.
I guess it happens because we have in /etc/nsswitch.conf:
|hosts: files myhostname mdns4_minimal [NOTFOUND=return] resolve
[!UNAVAIL=return] dns |
IIUC |myhostname| takes care of my .local hostname and that is
controlled by systemd-resolved. Then any .local hostnames from the
network itself (any network printers in the local network) are
resolved by nss-mdns+Avahi.
This is true only for resolution via glibc, ie. getent hosts <hostname>.
Any clients querying directly DNS will not see those names, unless dns
cache queries provides them. ping uses glibc resolution. But I think
myhostname does not depend on systemd-resolved or require it to
function. It just translates local IPs to hostname and in reverse.
|host| probably does not count localhost address in - ?probably
because some discrepancies between resolved and bind API? - @pemensik
<https://github.com/pemensik> , our Fedora/RHEL bind maintainer -
could know more details.
host and other tools from bind-utils work on DNS only. dig does use
system getaddrinfo() call just on @localhost. But other names are
resolved according to whatever /etc/resolv.conf nameservers are
specified. systemd-resolved brings a confusion into that, because it
also provides mdns and llmnr multicast lookups on domain port, where
they do not belong IMO. But that should not be relied on.
I think printer applications should listen on localhost by default and
you should use classic localhost name to connect without network.
*.local should always use mdns and should not rely on caching hack if
they are without a network.
Message ID: ***@***.***>
--
Petr Menšík
Software Engineer, RHEL
Red Hat,http://www.redhat.com/
PGP: DFCF908DB7C87E8E529925BC4931CA5B6C9FC5CB
|
To print to a temporary CUPS queue for a discovered IPP printer the client has to send an
IPP_OP_CUPS_CREATE_LOCAL_PRINTER
IPP request to the CUPS daemon with the details for creating the temporary queue. CUPS then calls itscreate_local_printer()
function to actually create the queue. This is already automatically and correctly done if the client uses the convenience API of libcups.The GTK print dialog does not use the convenience API though. It displays all IPP printers for which CUPS can create temporary queues correctly, but it fails on services which are only available on the local machine and not on the network (loopback interface, "localhost") whereas printing via temporary queue on network/remote services works perfectly.
The problem is that Avahi advertises the local services not with the "localhost" host name but with the network host name of the local machine. The libcups convenience API functions can cope with this and send correct device URIs with the "localhost" host name to CUPS and so the temporary queue gets created correctly.
The GTK dialog wants fully asynchronous CUPS operation and throws the libcups API completely overboard doing all by itself, and sending incorrect device URIs with the network hostname for the local services. CUPS creates the temporary queue with this URI then and fails to do the get-printer-attributes request on the printer to generate the PPD file, leaving the temporary queue in a non-functional state.
This commit correct/works around this by checking whether the device URI's host name is the same as the DNS-SD host name of the local machine and if so, replaces the device URI's host name by "localhost".
Now one can select local services like IPP-over-USB printers or Printer Applications in the GTK print dialog and printing on them through an auto-generated temporary CUPS queue works.
I decided to fix/work around this problem in CUPS as we see that no every print client developer uses the libcups convenience API.
In addition, in the
create_local_printer()
we check thedevice_uri
IPP attribute also whether it is not empty and fail if it is empty.