No suitable destination host found: a Canon PIXMA that can't handle TLS 1.3
A Canon PIXMA TS5351 on the network, auto-discovered by cups-browsed, queue
shows up by itself, printer accepts jobs - and nothing ever comes out. No paper,
no error dialog, no feedback of any kind. If you got here from a search engine,
you’re probably looking at one of these:
No suitable destination host found by cups-browsed, retrying later
device for Canon_TS5300_series: implicitclass://Canon_TS5300_series/
device for Canon_TS5300_series: ///dev/null
…or Gnome cheerfully reporting the printer address as (null):631.
The root cause turned out to be a genuinely interesting one: the printer’s
embedded TLS server hangs up the moment a client offers TLS 1.3 - and every
current Linux TLS stack offers TLS 1.3 by default. The fix is one line in
client.conf - containing one keyword nobody would think to type, because
underneath the printer bug sits a second bug: since CUPS 2.4.12, on Debian and
Ubuntu, SSLOptions is silently thrown away. Here’s the trail, including the dead ends,
because the dead ends are where I lost the two hours - and one of those dead
ends was a confident, wrong diagnosis that I carried for most of them.
Step 1: confirm it’s a connection problem, not a printing problem
lpstat -p -d
lpstat -o
Printer enabled, accepting jobs, jobs submitted, jobs sitting there forever. That signature - submitted but silent - means discovery or connection, not rendering. Nothing is wrong with your PPD or your driver.
Step 2: find the printer’s real address, independent of CUPS
Before debugging anything, get an address you know is true, without asking Avahi or CUPS what they believe. You want two things: the IP, to probe with, and the printer’s mDNS hostname, which is what the final queue will actually use.
From the printer itself is the most reliable: Setup → Device settings → LAN settings → Confirm LAN settings, or print the network settings page from its menu. Failing that, your router’s DHCP client list will show it under some Canon-branded hostname.
From Linux:
avahi-browse -r _ipp._tcp
hostname = [246989000000.local]
address = [192.168.1.50]
Or just scan for anything listening on the IPP port:
nmap -p 631 --open 192.168.1.0/24
Aside: what exactly is 246989000000.local?
Worth a paragraph, because the fix in Step 7 leans on it.
It looks like a MAC address with the colons stripped - twelve digits, right
length - and that is a real convention: Brother advertises
BRWxxxxxxxxxxxx, which is literally the MAC. Here it isn’t. On this printer
the MAC is 74:38:B7:xx:xx:xx and the serial number is KMHH00159; neither
matches, in format or in content.
It’s a field of its own. The printer’s OK menu → System Information has a
Printer Name entry, sitting right next to the serial number, and it reads
exactly 246989000000. The mDNS hostname is that value with .local appended -
nothing more. Canon assigns it at the factory as an independent production
identifier; it is not derived from the MAC or the serial at runtime, which is
why you cannot reconstruct it from anything printed on the case.
Three properties that make it useful:
- Stable. It’s persistent system information, stored like the serial number. It survives reboots, firmware updates, DHCP leases, a factory network reset, and moving the printer to a different network.
- Unique per unit. The value identifies this one printer. The scheme - an
opaque numeric
Printer Namepublished over mDNS - is Canon’s own convention, not an industry standard; other vendors do their own thing. - Opaque. You have to read it, either from
avahi-browse -r _ipp._tcpabove or from OK menu → System Information → Printer Name on the device. Guessing is hopeless.
Stable and printer-specific is exactly what a static print queue wants.
Step 3: implicitclass:// is not the bug
I assumed it was. It isn’t. implicitclass:// is cups-browsed’s own
failover abstraction, and it appears whenever cups-browsed sees more than one
route to what it thinks is the same printer. This Canon advertises itself,
simultaneously, as:
PDL PrinterUNIX PrinterInternet Printer(plain IPP)Secure Internet Printer(IPPS - IPP over TLS)_uscan/_uscansfor scanning- and a separate set of all of the above per active network interface, so add a USB Ethernet dongle next to Wi-Fi and the list doubles
cups-browsed bundles that pile into one queue and picks a backend at print
time. Seeing implicitclass is normal. The failure is in what it does with
it.
The same goes for the whole Avahi rabbit hole - (null):631, IPv6 link-local
addresses without scope IDs, systemd-resolved and Avahi both answering mDNS,
NSS config, rate limiting, stray daemons. None of that was the bug. If
avahi-browse -r _ipp._tcp gives you a sane hostname = / address = pair,
name resolution works. Move on.
Step 4: read the error log
One check first, and be pedantic about it: make sure you are reading the log
of the CUPS that actually owns printing. A stock Ubuntu 26.04 desktop runs
two complete CUPS installations - the Ubuntu deb and the OpenPrinting snap -
each with its own daemon, its own config tree, its own logs and its own
command-line tools, and nothing on the box tells you which of the two your
lpstat or your ipptool is answering about. On this machine the deb is the
one that prints: it owns port 631 and /run/cups/cups.sock, while the snap
steps aside into a proxy mode beside it. Every path in the rest of this post is
the deb’s. It is worth being paranoid here, because every config file and log
below exists twice and only one copy of each is load-bearing - I edited the
wrong copy for a good stretch of that afternoon, and nothing anywhere warned
me. Two CUPSes: what a stock Ubuntu 26.04 desktop actually
runs is that story, with a five-second check for
telling them apart.
With that settled: the clue was sitting there the whole time. cupsd and the
backends it forks write to the error log; the deb’s cups-browsed logs to the
journal:
sudo tail -50 /var/log/cups/error_log
sudo journalctl -u cups-browsed -n 50
Unable to connect to 246989000000.local:631: A TLS fatal alert has been received.
There it is. Offered both plain IPP and IPPS, cups-browsed prefers the secure
one - and that TLS handshake fails, every single time. Read the error log
first. I didn’t.
Step 5: pinning it down with openssl s_client
This is the part I enjoyed. March through the protocol versions:
openssl s_client -connect 192.168.1.50:631 -showcerts # TLS 1.3 → handshake failure
openssl s_client -connect 192.168.1.50:631 -tls1_2 -showcerts # → handshake failure
openssl s_client -connect 192.168.1.50:631 -tls1_1 -showcerts # → no protocols available
openssl s_client -connect 192.168.1.50:631 -tls1 -showcerts # → no protocols available
Careful with the last two: “no protocols available” with 0 bytes exchanged is your own OpenSSL refusing to dial, not the printer refusing to answer. Force it to actually try:
openssl s_client -connect 192.168.1.50:631 -tls1 -showcerts -cipher "ALL:@SECLEVEL=0"
Now we get a real handshake attempt, and a much better error:
error:0A000152:SSL routines:final_renegotiate:unsafe legacy renegotiation disabled
That is a specific, documented OpenSSL 3.x behaviour: it will not complete a handshake with a server lacking secure renegotiation (RFC 5746, 2010) unless you explicitly say it’s fine. So say it’s fine:
openssl s_client -connect 192.168.1.50:631 -tls1 -cipher "ALL:@SECLEVEL=0" -legacy_renegotiation -showcerts
subject=CN=192.168.1.50
issuer=CN=CanonIJProductXXXXXXXXXXXXXXXX
Protocol: TLSv1
Cipher: AES256-SHA
Secure Renegotiation IS NOT supported
And the genuinely surprising bit - with the same override, TLS 1.2 negotiates a thoroughly modern suite:
openssl s_client -connect 192.168.1.50:631 -tls1_2 -cipher "ALL:@SECLEVEL=0" -legacy_renegotiation -showcerts
# → Protocol: TLSv1.2, Cipher: ECDHE-RSA-AES256-GCM-SHA384
So this is not a hopelessly ancient TLS stack. It does TLS 1.2 with ECDHE and AES-GCM. Its only defect, from where OpenSSL is standing, is a missing extension from fifteen years ago - which is exactly what you’d expect from an embedded TLS implementation shipped once and never touched again across a decade of firmware revisions.
That was my diagnosis and I was pleased with it. It is also wrong - not about the missing extension, which is real, but about it being the thing that breaks CUPS. The give-away is one word in the log line from Step 4: received. That alert came from the printer. Every override I had been reaching for changes what my client is willing to accept.
Step 6: the right instrument - gnutls-cli
Here is the thing I should have checked before Step 5, never mind before editing a single config file: CUPS does its TLS with GnuTLS, not OpenSSL.
$ readelf -d /usr/lib/x86_64-linux-gnu/libcups.so.2 | grep NEEDED | grep -E 'gnutls|ssl'
0x0000000000000001 (NEEDED) Shared library: [libgnutls.so.30]
Which makes the whole of Step 5 a sidestep. Not a useless one - openssl
s_client did prove that “cannot reach the printer” was really a TLS interop
problem, with CUPS and Avahi taken out of the picture, and that is worth
knowing. But every specific finding it produced was OpenSSL’s opinion of this
printer, and OpenSSL is not the program that was failing. unsafe legacy
renegotiation disabled is an error message that literally cannot appear in
CUPS, because the code that emits it is never executed. I built an entire theory
on a diagnostic from the wrong stack.
The trap is that OpenSSL genuinely is loaded into the process. libssl.so.3
and libcrypto.so.3 both show up in ldd $(which cups-browsed) - but they
arrive transitively through libldap, itself dragged in by libcurl-gnutls,
and they play no part in the IPP connection whatsoever. So every minute spent in
/etc/ssl/openssl.cnf was spent tuning a library that is mapped into the
address space and never called.
So throw away openssl s_client and re-run the same march with gnutls-cli,
which is the library CUPS uses:
sudo apt install gnutls-bin
gnutls-cli --insecure --priority 'NORMAL' -p 631 192.168.1.50
gnutls-cli --insecure --priority 'NORMAL:-VERS-TLS1.3' -p 631 192.168.1.50
gnutls-cli --insecure --priority 'NORMAL:%UNSAFE_RENEGOTIATION' -p 631 192.168.1.50
gnutls-cli --insecure --priority 'NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2' -p 631 192.168.1.50
--insecure on all four is deliberate: the certificate is self-signed, so
without it every probe aborts at verification and tells you nothing about the
handshake itself.
| priority string | outcome |
|---|---|
NORMAL |
*** Fatal error: A TLS fatal alert has been received. *** Received alert [40]: Handshake failed |
NORMAL:-VERS-TLS1.3 |
handshake completed |
NORMAL:%UNSAFE_RENEGOTIATION |
fails, identically to NORMAL |
NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2 |
handshake completed |
Three conclusions, and the first one demolishes Step 5’s.
The printer cannot tolerate a TLS 1.3 ClientHello. Alert 40 is
handshake_failure, and it is received - the printer sent it. Offer TLS 1.3
and it hangs up; take 1.3 out of the offer and it is perfectly happy. Note that
the first probe reproduces the CUPS log line from Step 4 character for character,
which is what “I am now debugging the actual failure” looks like.
Secure renegotiation was a red herring. GnuTLS defaults to
%PARTIAL_RENEGOTIATION: an initial handshake with a peer lacking RFC 5746 is
already permitted, and only a later renegotiation is refused. IPP does one
handshake and never renegotiates. So %UNSAFE_RENEGOTIATION changes nothing,
and the missing 2010 extension - real, and genuinely what OpenSSL objected to -
was never what CUPS was objecting to.
The certificate is fine.
- Certificate[0] info:
- subject `CN=192.168.1.50', issuer `CN=CanonIJProductXXXXXXXXXXXXXXXX',
RSA key 2048 bits, signed using RSA-SHA256,
activated `2018-01-01 00:00:00 UTC', expires `2037-12-31 23:59:59 UTC'
- Description: (TLS1.2-X.509)-(ECDHE-SECP256R1)-(RSA-SHA256)-(AES-256-GCM)
- Handshake was completed
RSA 2048, SHA-256, ECDHE, AES-256-GCM. Nothing here needs SECLEVEL=0, weak
DH, RC4 or SSLv3. The single thing that has to change is the protocol
ceiling - a cap, not a weakening. Every “legacy mode” I had been reaching for
was solving a problem I did not have.
Telling CUPS about it, attempt one: client.conf (the actual fix)
CUPS has a documented lever for exactly this, and the version tokens are sitting in the library:
$ strings /usr/lib/x86_64-linux-gnu/libcups.so.2 | grep -E '^(Min|Max)TLS'
MaxTLS1.0
MaxTLS1.1
MaxTLS1.2
MaxTLS1.3
MinTLS1.0
MinTLS1.1
MinTLS1.2
MinTLS1.3
So, in /etc/cups/client.conf:
SSLOptions NoSystem MinTLS1.2 MaxTLS1.2
Two things about the file before the line itself. No deb ships it - nothing
owns /etc/cups/client.conf, so it is a file you create, and a
revert-my-experiments pass will delete it again without trace:
$ dpkg -S /etc/cups/client.conf
dpkg-query: no path found matching pattern /etc/cups/client.conf
And libcups tries $HOME/.cups/client.conf first, never falling back to the
system copy if that one opens - so a stale file in your home directory silently
wins, and ls -la ~/.cups/client.conf is worth a look before you conclude
anything about the system one.
Now the line. NoSystem is load-bearing, and omitting it is what cost me
the afternoon: written the way every write-up and the documentation show it,
SSLOptions MinTLS1.2 MaxTLS1.2 does nothing whatsoever. Since CUPS 2.4.12
libcups builds its GnuTLS priority string as @SYSTEM,NORMAL:..., GnuTLS cannot
resolve that on a system with no [priorities] SYSTEM entry - Debian and Ubuntu
have none - and CUPS discards the resulting error, leaving the session on
GnuTLS’s default priorities with TLS 1.3 in them. Every SSLOptions value goes
in the bin, silently. NoSystem drops the @SYSTEM, prefix and the rest of the
line starts working. That is
OpenPrinting/cups#1677
upstream, and I have filed it against Ubuntu as LP
#2164820 - the
regression landed in 2.4.12, so 25.04, 25.10 and 26.04 LTS all ship an affected
libcups while 24.04 LTS predates it. It deserves a post of its own, and until
then treat NoSystem as the keyword you cannot leave out.
ipptool is the right probe for this, because it goes through libcups and reads
that same client.conf - so unlike gnutls-cli, it tests my configuration
and not just the printer:
$ ipptool -tv ipps://246989000000.local:631/ipp/print get-printer-attributes.test
Get printer attributes using get-printer-attributes [PASS]
status-code = successful-ok (successful-ok)
That is a pass, over ipps://, with nothing but a line in a config file. No
system-wide crypto policy, no environment variables, no LD_PRELOAD.
Write the same line into the snap’s tree as well
One more copy of that file, and it is worth doing even though nothing complains
if you skip it. A stock Ubuntu 26.04 desktop also carries the OpenPrinting CUPS
snap - it is what snapped applications such as Chromium print through - and
it has its own libcups with its own compiled-in ServerRoot, so it never opens
/etc/cups/client.conf. Cap both:
LINE='SSLOptions NoSystem MinTLS1.2 MaxTLS1.2'
# the deb: the daemon, its backends, and the classic client tools
echo "$LINE" | sudo tee -a /etc/cups/client.conf
# the snap: snapped applications that print
echo "$LINE" | sudo tee -a /var/snap/cups/common/etc/cups/client.conf
sudo systemctl restart cups
The snap ships its client.conf already, holding a single ServerName line, so
there is nothing to create there - only a line to append, and appending is safe:
its launcher rewrites the ServerName line on every start and copies everything
else back verbatim. Why there are two CUPSes on a stock desktop at all, and how
to tell which one owns printing on your box, is a post of its
own.
And with those two lines in place, it prints. Gnome’s print dialog, the auto-discovered queue, paper. No static queue, no system-wide crypto policy, no environment variables. The entire two-hour hunt was one missing keyword.
Two honest caveats about what I actually measured. I wrote the line into both
client.conf files at the same time, so “the deb’s copy is the one that matters
for jobs” is inference from the process tree - the deb cupsd forks the deb
backend, which reads /etc/cups/client.conf - and not something I separated by
experiment. And keep ipptool and printing apart as questions, because
conflating them is what cost me the afternoon: ipptool given an ipps:// URI
runs as me and connects to the printer directly, no daemon involved, while
printing goes through cupsd and the ipp backend it fork+execs per job. A
pass from the first is not a promise about the second.
One more thing to know before you spend time here: Ubuntu’s libcups is built
without debug printfs, so the usual trick of setting CUPS_DEBUG_LOG to dump
the priority string CUPS builds is unavailable:
$ strings /usr/lib/x86_64-linux-gnu/libcups.so.2 | grep CUPS_DEBUG_LOG
$ # nothing
Attempt two: the GnuTLS override - the detour that explained the first one
This is the road I actually took while client.conf still looked inert: stop
asking processes nicely and cap the protocol somewhere none of them can opt out
of. It works, and it is worth keeping in the post for one reason - why it works
is what eventually explained why attempt one didn’t. It bites below the priority
string, which is the one place a setting cannot be overridden by an application,
and, as it turns out, cannot be silently discarded by one either.
The obvious next idea is default-priority-string in /etc/gnutls/config.
libcups does ask GnuTLS for the system default - gnutls_set_default_priority(),
unconditionally, on every connection - and then normally overwrites the answer
with a string of its own, assembled from the SSLOptions values. Normally. On
this distro that overwrite is precisely the call that fails, so the default is
what you end up connected with after all. I never tested that knob and by the
time I understood the mechanism I had a better lever, so I’ll leave it at “not
as inert as I assumed”.
The priority string does settle the renegotiation question, though:
%UNSAFE_RENEGOTIATION is a priority-string modifier, and there is no way to
inject one into CUPS short of LD_PRELOAD.
But /etc/gnutls/config has a second section that is applied inside GnuTLS at
priority-parse time, below the priority string, where no application can
override it. Ubuntu already ships one - which is itself the proof that the
mechanism works:
[overrides]
disabled-version = tls1.0
disabled-version = tls1.1
disabled-version = dtls0.9
disabled-version = dtls1.0
Add one line:
disabled-version = tls1.3
restart the daemons:
sudo systemctl restart cups cups-browsed
…and it all works. gnutls-cli with an unmodified NORMAL priority now
connects - which is the demonstration that the override bites below the priority
string, and therefore reaches every GnuTLS caller regardless of what string it
sets. ipptool over ipps:// returns the attribute list:
$ ipptool -tv ipps://246989000000.local:631/ipp/print get-printer-attributes.test
Get printer attributes using get-printer-attributes [PASS]
RECEIVED: 324135 bytes in response
status-code = successful-ok (successful-ok)
And the auto-discovered cups-browsed queue - the one that had been swallowing
jobs all afternoon - prints.
Reaching every GnuTLS caller matters more than it sounds, because three
separate processes open TLS to this printer: cups-browsed during discovery,
cupsd itself, and /usr/lib/cups/backend/ipp, which cupsd forks at print
time. They all link libcups, so they all share the failure - and fixing only one
of them would have moved the wall rather than removed it.
I have since reverted it. disabled-version = tls1.3 in
/etc/gnutls/config applies to every GnuTLS consumer on the machine - wget,
curl-gnutls, glib-networking and with it a good chunk of Gnome. Holding all of
them at TLS 1.2 with ECDHE and AES-GCM is not dangerous, but it is a
system-wide change made to accommodate one appliance, and that’s the wrong shape
of fix.
The properly scoped version turned out to be attempt one plus one keyword, which
retired two fairly elaborate plans I had for narrowing this down. Recording them
in case you ever need to aim GnuTLS policy at a single unit: the library honours
GNUTLS_SYSTEM_PRIORITY_FILE, which replaces /etc/gnutls/config rather than
adding to it, so a private copy has to carry Ubuntu’s four disabled-version
lines too - and set GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1 beside it, or a
mistyped path fails silently and you’re debugging the printer again. A systemd
drop-in with Environment= then covers cupsd and cups-browsed, but cupsd
scrubs the environment of the filters and backends it forks, so the ipp
backend needs SetEnv - which lives in cups-files.conf, not cupsd.conf.
Step 7: the alternative, if you would rather not touch TLS at all
This is what I ran on while the TLS side was still a mystery, and it is still the
right answer if you’d rather not think about protocol versions at all - or if a
printer’s TLS turns out to be broken in some way no version cap fixes.
Stop letting cups-browsed choose. Add the printer by hand, over plain IPP,
at its mDNS hostname:
# drop the broken auto-discovered queue
sudo lpadmin -x Canon_TS5300_series
# static, plain-IPP queue
sudo lpadmin -p TS5351 -E -v ipp://246989000000.local:631/ipp/print -m everywhere
# test
lp -d TS5351 /etc/hostname
Instant, reliable, on both machines - because it never attempts a TLS handshake at all. The printer’s broken IPPS advertisement is simply never consulted. The trade is that IPP traffic on your LAN goes in the clear, which for a home network and a document you were about to print onto paper anyway is a trade I’d make without much thought.
cups-browsed will keep re-discovering the printer and parking its broken
implicitclass queue right next to your working one, so you end up with two
entries that look identical. You don’t need to disable the service for that -
just open Settings → Printers in Gnome, find your new TS5351 queue, and
set it as the default from its ⋮ menu. Jobs then go to the static plain-IPP
queue unless something explicitly asks for the other one, and cups-browsed
stays available for any other network printers you actually want
auto-discovered.
Use the hostname, not the IP. My first version of this queue pointed at
ipp://192.168.1.50:631/ipp/print and that is a latent bug: the printer gets
its address from DHCP, so sooner or later the lease moves and the static queue
silently stops working - which is the same symptom I had just spent two hours
chasing. 246989000000.local doesn’t move. You can paper over the IP version
with a DHCP reservation for the printer’s MAC, and that works, but it’s a second
piece of configuration to remember, in the router rather than on the machine
doing the printing, and it only holds for that one router - move the printer
somewhere else and you’re back to hunting down its new address.
The one thing the hostname costs is a runtime dependency on mDNS: Avahi has to
be running and multicast has to reach the printer at print time. That’s a real
consideration if your printer lives behind a VLAN boundary or on an AP that
filters multicast - in which case fall back to the IP plus a DHCP reservation.
On a flat home network it’s a non-issue, and worth noting: name resolution was
never the broken part here. Step 3’s Avahi rabbit hole was noise; avahi-browse
resolved this printer correctly the entire time.
And if you tried the /etc/gnutls/config override from Step 6, revert it -
there’s no reason to hold an entire machine at TLS 1.2 for the benefit of one
appliance.
What I’d do differently
- Establish which process is going to read the file you are editing. On a
stock Ubuntu 26.04 desktop
client.confexists in three places, and the tools you would naturally reach for to check your work answer about whichever CUPS shipped them rather than about the one holding your print queue. I spent a stretch of that afternoon editing a file no process on this machine ever opens - its own write-up. - Read the error log first.
A TLS fatal alert has been receivedwas sitting there from the very beginning. I went looking for an Avahi bug instead, because “printer not found” feels like discovery. implicitclass://is not an error. It’s normal multi-backend behaviour. Don’t build a theory on it.- Note who sent the alert. “A TLS fatal alert has been received” means the server rejected you. I read that line a dozen times and spent an hour relaxing my own client’s policy anyway, which by construction could not have been the problem.
- Probe with the library your program actually links.
openssl s_clientwas the right tool for establishing that this was a TLS interop problem at all - it takes CUPS and Avahi out of the picture. But the specific answer it gave was OpenSSL’s answer, not CUPS’s.lddthe binary,readelf -dthe library, then pick your probe:gnutls-clihere, notopenssl s_client. - Two TLS stacks routinely disagree about the same server. OpenSSL 3.x
refused this printer over RFC 5746; GnuTLS never cared, because its default
%PARTIAL_RENEGOTIATIONpermits the initial handshake and IPP never renegotiates. Same printer, same firmware, two entirely different objections- and only one of them was breaking my printing.
ipptoolis the end-to-end probe for CUPS.gnutls-clitells you what the printer will accept;ipptoolgoes through libcups and the sameclient.conf, so it tells you whether your configuration actually reached CUPS. Those are different questions and I needed both - a config that looks right but is never read would otherwise go unnoticed. Two caveats:ipptoolgiven anipps://URI goes straight to the printer, so it is a test of your client configuration and not of your print path; and it reads~/.cups/client.confbefore the system file, so check that one exists nowhere before you trust the result.- TLS 1.3 intolerance in embedded devices is a pattern, not a one-off.
Printers, switches, IoT gadgets: a stack that predates 1.3 and chokes on the
ClientHello rather than negotiating down. Worth trying
-VERS-TLS1.3early. - A silent no-op is worse than an error. I found the correct fix in the
first twenty minutes. It then sat in the right file, on the right machine,
doing nothing at all, because CUPS handed GnuTLS a priority string GnuTLS
could not parse and dropped the error on the floor. Everything after that was
a hunt for the process that was ignoring my configuration - a process which
did not exist. One
ifaround one return value would have turned this whole post into a paragraph.
The whole fix, start to finish
# 0. cap TLS for CUPS. NoSystem is not optional - see OpenPrinting/cups#1677
# and https://bugs.launchpad.net/ubuntu/+source/cups/+bug/2164820
LINE='SSLOptions NoSystem MinTLS1.2 MaxTLS1.2'
ls -la ~/.cups/client.conf # must not exist, or it silently wins
echo "$LINE" | sudo tee -a /etc/cups/client.conf
echo "$LINE" | sudo tee -a /var/snap/cups/common/etc/cups/client.conf
sudo systemctl restart cups
# or even better, REBOOT
# check it from the client side before trusting it
ipptool -tv ipps://246989000000.local:631/ipp/print get-printer-attributes.test
That’s it - the auto-discovered queue prints from there. If you’d rather stay off TLS altogether, the Step 7 route instead:
# 1. find the printer's mDNS hostname
# (or read it off the printer: OK menu -> System Information -> Printer Name)
avahi-browse -r _ipp._tcp
# 2. remove the broken auto-discovered queue
sudo lpadmin -x Canon_TS5300_series
# 3. add a static plain-IPP queue, by hostname - not by IP, which DHCP will move
sudo lpadmin -p TS5351 -E -v ipp://246989000000.local:631/ipp/print -m everywhere
# 4. test
lp -d TS5351 /etc/hostname
# 5. in Gnome Settings -> Printers, set TS5351 as the default queue
# (cups-browsed's duplicate can stay; it just won't be picked)
If you’re staring at (null):631 and an endless stream of No suitable
destination host found - hopefully this saves you the two hours it cost me.