• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * DNS-SD discovery backend for CUPS.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright © 2008-2018 by Apple Inc.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers.
13  */
14 
15 #include "backend-private.h"
16 #include <cups/array.h>
17 #ifdef HAVE_MDNSRESPONDER
18 #  include <dns_sd.h>
19 #endif /* HAVE_MDNSRESPONDER */
20 #ifdef HAVE_AVAHI
21 #  include <avahi-client/client.h>
22 #  include <avahi-client/lookup.h>
23 #  include <avahi-common/simple-watch.h>
24 #  include <avahi-common/domain.h>
25 #  include <avahi-common/error.h>
26 #  include <avahi-common/malloc.h>
27 #define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
28 #endif /* HAVE_AVAHI */
29 
30 
31 /*
32  * Device structure...
33  */
34 
35 typedef enum
36 {
37   CUPS_DEVICE_PRINTER = 0,		/* lpd://... */
38   CUPS_DEVICE_IPPS,			/* ipps://... */
39   CUPS_DEVICE_IPP,			/* ipp://... */
40   CUPS_DEVICE_FAX_IPP,			/* ipp://... */
41   CUPS_DEVICE_PDL_DATASTREAM,		/* socket://... */
42   CUPS_DEVICE_RIOUSBPRINT		/* riousbprint://... */
43 } cups_devtype_t;
44 
45 
46 typedef struct
47 {
48 #ifdef HAVE_MDNSRESPONDER
49   DNSServiceRef	ref;			/* Service reference for query */
50 #endif /* HAVE_MDNSRESPONDER */
51 #ifdef HAVE_AVAHI
52   AvahiRecordBrowser *ref;		/* Browser for query */
53 #endif /* HAVE_AVAHI */
54   char		*name,			/* Service name */
55 		*domain,		/* Domain name */
56 		*fullName,		/* Full name */
57 		*make_and_model,	/* Make and model from TXT record */
58 		*device_id,		/* 1284 device ID from TXT record */
59 		*uuid;			/* UUID from TXT record */
60   cups_devtype_t type;			/* Device registration type */
61   int		priority,		/* Priority associated with type */
62 		cups_shared,		/* CUPS shared printer? */
63 		sent;			/* Did we list the device? */
64 } cups_device_t;
65 
66 
67 /*
68  * Local globals...
69  */
70 
71 static int		job_canceled = 0;
72 					/* Set to 1 on SIGTERM */
73 #ifdef HAVE_AVAHI
74 static AvahiSimplePoll	*simple_poll = NULL;
75 					/* Poll information */
76 static int		got_data = 0;	/* Got data from poll? */
77 static int		browsers = 0;	/* Number of running browsers */
78 #endif /* HAVE_AVAHI */
79 
80 
81 /*
82  * Local functions...
83  */
84 
85 #ifdef HAVE_MDNSRESPONDER
86 static void		browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8);
87 static void		browse_local_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8);
88 #endif /* HAVE_MDNSRESPONDER */
89 #ifdef HAVE_AVAHI
90 static void		browse_callback(AvahiServiceBrowser *browser,
91 					AvahiIfIndex interface,
92 					AvahiProtocol protocol,
93 					AvahiBrowserEvent event,
94 					const char *serviceName,
95 					const char *regtype,
96 					const char *replyDomain,
97 					AvahiLookupResultFlags flags,
98 					void *context);
99 static void		client_callback(AvahiClient *client,
100 					AvahiClientState state,
101 					void *context);
102 #endif /* HAVE_AVAHI */
103 
104 static int		compare_devices(cups_device_t *a, cups_device_t *b);
105 static void		exec_backend(char **argv) _CUPS_NORETURN;
106 static cups_device_t	*get_device(cups_array_t *devices, const char *serviceName, const char *regtype, const char *replyDomain) _CUPS_NONNULL(1,2,3,4);
107 #ifdef HAVE_MDNSRESPONDER
108 static void		query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) _CUPS_NONNULL(1,5,9,11);
109 #elif defined(HAVE_AVAHI)
110 static int		poll_callback(struct pollfd *pollfds,
111 			              unsigned int num_pollfds, int timeout,
112 			              void *context);
113 static void		query_callback(AvahiRecordBrowser *browser,
114 				       AvahiIfIndex interface,
115 				       AvahiProtocol protocol,
116 				       AvahiBrowserEvent event,
117 				       const char *name, uint16_t rrclass,
118 				       uint16_t rrtype, const void *rdata,
119 				       size_t rdlen,
120 				       AvahiLookupResultFlags flags,
121 				       void *context);
122 #endif /* HAVE_MDNSRESPONDER */
123 static void		sigterm_handler(int sig);
124 static void		unquote(char *dst, const char *src, size_t dstsize) _CUPS_NONNULL(1,2);
125 
126 
127 /*
128  * 'main()' - Browse for printers.
129  */
130 
131 int					/* O - Exit status */
main(int argc,char * argv[])132 main(int  argc,				/* I - Number of command-line args */
133      char *argv[])			/* I - Command-line arguments */
134 {
135   const char	*name;			/* Backend name */
136   cups_array_t	*devices;		/* Device array */
137   cups_device_t	*device;		/* Current device */
138   char		uriName[1024];		/* Unquoted fullName for URI */
139 #ifdef HAVE_MDNSRESPONDER
140   int		fd;			/* Main file descriptor */
141   fd_set	input;			/* Input set for select() */
142   struct timeval timeout;		/* Timeout for select() */
143   DNSServiceRef	main_ref,		/* Main service reference */
144 		fax_ipp_ref,		/* IPP fax service reference */
145 		ipp_ref,		/* IPP service reference */
146 		ipp_tls_ref,		/* IPP w/TLS service reference */
147 		ipps_ref,		/* IPP service reference */
148 		local_fax_ipp_ref,	/* Local IPP fax service reference */
149 		local_ipp_ref,		/* Local IPP service reference */
150 		local_ipp_tls_ref,	/* Local IPP w/TLS service reference */
151 		local_ipps_ref,		/* Local IPP service reference */
152 		local_printer_ref,	/* Local LPD service reference */
153 		pdl_datastream_ref,	/* AppSocket service reference */
154 		printer_ref,		/* LPD service reference */
155 		riousbprint_ref;	/* Remote IO service reference */
156 #endif /* HAVE_MDNSRESPONDER */
157 #ifdef HAVE_AVAHI
158   AvahiClient	*client;		/* Client information */
159   int		error;			/* Error code, if any */
160 #endif /* HAVE_AVAHI */
161 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
162   struct sigaction action;		/* Actions for POSIX signals */
163 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
164 
165 
166  /*
167   * Don't buffer stderr, and catch SIGTERM...
168   */
169 
170   setbuf(stderr, NULL);
171 
172 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
173   sigset(SIGTERM, sigterm_handler);
174 #elif defined(HAVE_SIGACTION)
175   memset(&action, 0, sizeof(action));
176 
177   sigemptyset(&action.sa_mask);
178   action.sa_handler = sigterm_handler;
179   sigaction(SIGTERM, &action, NULL);
180 #else
181   signal(SIGTERM, sigterm_handler);
182 #endif /* HAVE_SIGSET */
183 
184  /*
185   * Check command-line...
186   */
187 
188   if (argc >= 6)
189     exec_backend(argv);
190   else if (argc != 1)
191   {
192     _cupsLangPrintf(stderr,
193                     _("Usage: %s job-id user title copies options [file]"),
194 		    argv[0]);
195     return (1);
196   }
197 
198  /*
199   * Only do discovery when run as "dnssd"...
200   */
201 
202   if ((name = strrchr(argv[0], '/')) != NULL)
203     name ++;
204   else
205     name = argv[0];
206 
207   if (strcmp(name, "dnssd"))
208     return (0);
209 
210  /*
211   * Create an array to track devices...
212   */
213 
214   devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
215 
216  /*
217   * Browse for different kinds of printers...
218   */
219 
220 #ifdef HAVE_MDNSRESPONDER
221   if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
222   {
223     perror("ERROR: Unable to create service connection");
224     return (1);
225   }
226 
227   fd = DNSServiceRefSockFD(main_ref);
228 
229   fax_ipp_ref = main_ref;
230   DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
231                    "_fax-ipp._tcp", NULL, browse_callback, devices);
232 
233   ipp_ref = main_ref;
234   DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
235                    "_ipp._tcp", NULL, browse_callback, devices);
236 
237   ipp_tls_ref = main_ref;
238   DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
239                    "_ipp-tls._tcp", NULL, browse_callback, devices);
240 
241   ipps_ref = main_ref;
242   DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
243                    "_ipps._tcp", NULL, browse_callback, devices);
244 
245   local_fax_ipp_ref = main_ref;
246   DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
247                    kDNSServiceInterfaceIndexLocalOnly,
248 		   "_fax-ipp._tcp", NULL, browse_local_callback, devices);
249 
250   local_ipp_ref = main_ref;
251   DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
252                    kDNSServiceInterfaceIndexLocalOnly,
253 		   "_ipp._tcp", NULL, browse_local_callback, devices);
254 
255   local_ipp_tls_ref = main_ref;
256   DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
257                    kDNSServiceInterfaceIndexLocalOnly,
258                    "_ipp-tls._tcp", NULL, browse_local_callback, devices);
259 
260   local_ipps_ref = main_ref;
261   DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
262                    kDNSServiceInterfaceIndexLocalOnly,
263 		   "_ipps._tcp", NULL, browse_local_callback, devices);
264 
265   local_printer_ref = main_ref;
266   DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
267                    kDNSServiceInterfaceIndexLocalOnly,
268                    "_printer._tcp", NULL, browse_local_callback, devices);
269 
270   pdl_datastream_ref = main_ref;
271   DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
272                    "_pdl-datastream._tcp", NULL, browse_callback, devices);
273 
274   printer_ref = main_ref;
275   DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
276                    "_printer._tcp", NULL, browse_callback, devices);
277 
278   riousbprint_ref = main_ref;
279   DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
280                    "_riousbprint._tcp", NULL, browse_callback, devices);
281 #endif /* HAVE_MDNSRESPONDER */
282 
283 #ifdef HAVE_AVAHI
284   if ((simple_poll = avahi_simple_poll_new()) == NULL)
285   {
286     fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr);
287     return (0);
288   }
289 
290   avahi_simple_poll_set_func(simple_poll, poll_callback, NULL);
291 
292   client = avahi_client_new(avahi_simple_poll_get(simple_poll),
293 			    0, client_callback, simple_poll, &error);
294   if (!client)
295   {
296     fputs("DEBUG: Unable to create Avahi client.\n", stderr);
297     return (0);
298   }
299 
300   browsers = 6;
301   avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
302 			    AVAHI_PROTO_UNSPEC,
303 			    "_fax-ipp._tcp", NULL, 0,
304 			    browse_callback, devices);
305   avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
306 			    AVAHI_PROTO_UNSPEC,
307 			    "_ipp._tcp", NULL, 0,
308 			    browse_callback, devices);
309   avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
310 			    AVAHI_PROTO_UNSPEC,
311 			    "_ipp-tls._tcp", NULL, 0,
312 			    browse_callback, devices);
313   avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
314 			    AVAHI_PROTO_UNSPEC,
315 			    "_ipps._tcp", NULL, 0,
316 			    browse_callback, devices);
317   avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
318 			    AVAHI_PROTO_UNSPEC,
319 			    "_pdl-datastream._tcp",
320 			    NULL, 0,
321 			    browse_callback,
322 			    devices);
323   avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
324 			    AVAHI_PROTO_UNSPEC,
325 			    "_printer._tcp", NULL, 0,
326 			    browse_callback, devices);
327 #endif /* HAVE_AVAHI */
328 
329  /*
330   * Loop until we are killed...
331   */
332 
333   while (!job_canceled)
334   {
335     int announce = 0;			/* Announce printers? */
336 
337 #ifdef HAVE_MDNSRESPONDER
338     FD_ZERO(&input);
339     FD_SET(fd, &input);
340 
341     timeout.tv_sec  = 0;
342     timeout.tv_usec = 500000;
343 
344     if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
345       continue;
346 
347     if (FD_ISSET(fd, &input))
348     {
349      /*
350       * Process results of our browsing...
351       */
352 
353       DNSServiceProcessResult(main_ref);
354     }
355     else
356       announce = 1;
357 
358 #elif defined(HAVE_AVAHI)
359     got_data = 0;
360 
361     if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
362     {
363      /*
364       * We've been told to exit the loop.  Perhaps the connection to
365       * Avahi failed.
366       */
367 
368       break;
369     }
370 
371     if (!got_data)
372       announce = 1;
373 #endif /* HAVE_MDNSRESPONDER */
374 
375 /*    fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
376 
377     if (announce)
378     {
379      /*
380       * Announce any devices we've found...
381       */
382 
383 #ifdef HAVE_MDNSRESPONDER
384       DNSServiceErrorType status;	/* DNS query status */
385 #endif /* HAVE_MDNSRESPONDER */
386       cups_device_t *best;		/* Best matching device */
387       char	device_uri[1024];	/* Device URI */
388       int	count;			/* Number of queries */
389       int	sent;			/* Number of sent */
390 
391       for (device = (cups_device_t *)cupsArrayFirst(devices),
392                best = NULL, count = 0, sent = 0;
393            device;
394 	   device = (cups_device_t *)cupsArrayNext(devices))
395       {
396         if (device->sent)
397 	  sent ++;
398 
399         if (device->ref)
400 	  count ++;
401 
402         if (!device->ref && !device->sent)
403 	{
404 	 /*
405 	  * Found the device, now get the TXT record(s) for it...
406 	  */
407 
408           if (count < 50)
409 	  {
410 	    fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
411 
412 #ifdef HAVE_MDNSRESPONDER
413 	    device->ref = main_ref;
414 
415 	    status = DNSServiceQueryRecord(&(device->ref),
416 				           kDNSServiceFlagsShareConnection,
417 				           0, device->fullName,
418 					   kDNSServiceType_TXT,
419 				           kDNSServiceClass_IN, query_callback,
420 				           device);
421             if (status != kDNSServiceErr_NoError)
422 	      fprintf(stderr,
423 	              "ERROR: Unable to query \"%s\" for TXT records: %d\n",
424 	              device->fullName, status);
425 	              			/* Users never see this */
426 	    else
427 	      count ++;
428 
429 #else
430 	    if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
431 	                                                AVAHI_PROTO_UNSPEC,
432 	                                                device->fullName,
433 	                                                AVAHI_DNS_CLASS_IN,
434 	                                                AVAHI_DNS_TYPE_TXT,
435 	                                                0,
436 				                        query_callback,
437 				                        device)) == NULL)
438 	      fprintf(stderr,
439 	              "ERROR: Unable to query \"%s\" for TXT records: %s\n",
440 	              device->fullName,
441 	              avahi_strerror(avahi_client_errno(client)));
442 	              			/* Users never see this */
443 	    else
444 	      count ++;
445 #endif /* HAVE_AVAHI */
446           }
447 	}
448 	else if (!device->sent)
449 	{
450 #ifdef HAVE_MDNSRESPONDER
451 	 /*
452 	  * Got the TXT records, now report the device...
453 	  */
454 
455 	  DNSServiceRefDeallocate(device->ref);
456 #else
457           avahi_record_browser_free(device->ref);
458 #endif /* HAVE_MDNSRESPONDER */
459 
460 	  device->ref = NULL;
461 
462           if (!best)
463 	    best = device;
464 	  else if (_cups_strcasecmp(best->name, device->name) ||
465 	           _cups_strcasecmp(best->domain, device->domain))
466           {
467 	    unquote(uriName, best->fullName, sizeof(uriName));
468 
469             if (best->uuid)
470 	      httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
471 	                       sizeof(device_uri), "dnssd", NULL, uriName, 0,
472 			       best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
473 			       best->uuid);
474 	    else
475 	      httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
476 	                      sizeof(device_uri), "dnssd", NULL, uriName, 0,
477 			      best->cups_shared ? "/cups" : "/");
478 
479 	    cupsBackendReport("network", device_uri, best->make_and_model,
480 	                      best->name, best->device_id, NULL);
481 	    best->sent = 1;
482 	    best       = device;
483 
484 	    sent ++;
485 	  }
486 	  else if (best->priority > device->priority ||
487 	           (best->priority == device->priority &&
488 		    best->type < device->type))
489           {
490 	    best->sent = 1;
491 	    best       = device;
492 
493 	    sent ++;
494 	  }
495 	  else
496 	  {
497 	    device->sent = 1;
498 
499 	    sent ++;
500 	  }
501         }
502       }
503 
504       if (best)
505       {
506 	unquote(uriName, best->fullName, sizeof(uriName));
507 
508 	if (best->uuid)
509 	  httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
510 			   sizeof(device_uri), "dnssd", NULL, uriName, 0,
511 			   best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
512 			   best->uuid);
513 	else
514 	  httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
515 			  sizeof(device_uri), "dnssd", NULL, uriName, 0,
516 			  best->cups_shared ? "/cups" : "/");
517 
518 	cupsBackendReport("network", device_uri, best->make_and_model,
519 			  best->name, best->device_id, NULL);
520 	best->sent = 1;
521 	sent ++;
522       }
523 
524       fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
525 
526 #ifdef HAVE_AVAHI
527       if (sent == cupsArrayCount(devices) && browsers == 0)
528 #else
529       if (sent == cupsArrayCount(devices))
530 #endif /* HAVE_AVAHI */
531 	break;
532     }
533   }
534 
535   return (CUPS_BACKEND_OK);
536 }
537 
538 
539 #ifdef HAVE_MDNSRESPONDER
540 /*
541  * 'browse_callback()' - Browse devices.
542  */
543 
544 static void
browse_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * serviceName,const char * regtype,const char * replyDomain,void * context)545 browse_callback(
546     DNSServiceRef       sdRef,		/* I - Service reference */
547     DNSServiceFlags     flags,		/* I - Option flags */
548     uint32_t            interfaceIndex,	/* I - Interface number */
549     DNSServiceErrorType errorCode,	/* I - Error, if any */
550     const char          *serviceName,	/* I - Name of service/device */
551     const char          *regtype,	/* I - Type of service */
552     const char          *replyDomain,	/* I - Service domain */
553     void                *context)	/* I - Devices array */
554 {
555   fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
556                   "interfaceIndex=%u, errorCode=%d, serviceName=\"%s\", "
557 		  "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
558           sdRef, flags, interfaceIndex, errorCode,
559 	  serviceName, regtype, replyDomain, context);
560 
561  /*
562   * Only process "add" data...
563   */
564 
565   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
566     return;
567 
568  /*
569   * Get the device...
570   */
571 
572   get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
573 }
574 
575 
576 /*
577  * 'browse_local_callback()' - Browse local devices.
578  */
579 
580 static void
browse_local_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * serviceName,const char * regtype,const char * replyDomain,void * context)581 browse_local_callback(
582     DNSServiceRef       sdRef,		/* I - Service reference */
583     DNSServiceFlags     flags,		/* I - Option flags */
584     uint32_t            interfaceIndex,	/* I - Interface number */
585     DNSServiceErrorType errorCode,	/* I - Error, if any */
586     const char          *serviceName,	/* I - Name of service/device */
587     const char          *regtype,	/* I - Type of service */
588     const char          *replyDomain,	/* I - Service domain */
589     void                *context)	/* I - Devices array */
590 {
591   cups_device_t	*device;		/* Device */
592 
593 
594   fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
595                   "interfaceIndex=%u, errorCode=%d, serviceName=\"%s\", "
596 		  "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
597           sdRef, flags, interfaceIndex, errorCode,
598 	  serviceName, regtype, replyDomain, context);
599 
600  /*
601   * Only process "add" data...
602   */
603 
604   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
605     return;
606 
607  /*
608   * Get the device...
609   */
610 
611   device = get_device((cups_array_t *)context, serviceName, regtype,
612                       replyDomain);
613 
614  /*
615   * Hide locally-registered devices...
616   */
617 
618   fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
619 	  device->fullName);
620   device->sent = 1;
621 }
622 #endif /* HAVE_MDNSRESPONDER */
623 
624 
625 #ifdef HAVE_AVAHI
626 /*
627  * 'browse_callback()' - Browse devices.
628  */
629 
630 static void
browse_callback(AvahiServiceBrowser * browser,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AvahiLookupResultFlags flags,void * context)631 browse_callback(
632     AvahiServiceBrowser    *browser,	/* I - Browser */
633     AvahiIfIndex           interface,	/* I - Interface index (unused) */
634     AvahiProtocol          protocol,	/* I - Network protocol (unused) */
635     AvahiBrowserEvent      event,	/* I - What happened */
636     const char             *name,	/* I - Service name */
637     const char             *type,	/* I - Registration type */
638     const char             *domain,	/* I - Domain */
639     AvahiLookupResultFlags flags,	/* I - Flags */
640     void                   *context)	/* I - Devices array */
641 {
642   AvahiClient *client = avahi_service_browser_get_client(browser);
643 					/* Client information */
644 
645 
646   (void)interface;
647   (void)protocol;
648   (void)context;
649 
650   switch (event)
651   {
652     case AVAHI_BROWSER_FAILURE:
653 	fprintf(stderr, "DEBUG: browse_callback: %s\n",
654 		avahi_strerror(avahi_client_errno(client)));
655 	avahi_simple_poll_quit(simple_poll);
656 	break;
657 
658     case AVAHI_BROWSER_NEW:
659        /*
660 	* This object is new on the network.
661 	*/
662 
663 	if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
664 	{
665 	 /*
666 	  * This comes from the local machine so ignore it.
667 	  */
668 
669 	  fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
670 	}
671 	else
672 	{
673 	 /*
674 	  * Create a device entry for it if it doesn't yet exist.
675 	  */
676 
677 	  get_device((cups_array_t *)context, name, type, domain);
678 	}
679 	break;
680 
681     case AVAHI_BROWSER_REMOVE:
682     case AVAHI_BROWSER_CACHE_EXHAUSTED:
683         break;
684 
685     case AVAHI_BROWSER_ALL_FOR_NOW:
686 	browsers--;
687 	break;
688   }
689 }
690 
691 
692 /*
693  * 'client_callback()' - Avahi client callback function.
694  */
695 
696 static void
client_callback(AvahiClient * client,AvahiClientState state,void * context)697 client_callback(
698     AvahiClient      *client,		/* I - Client information (unused) */
699     AvahiClientState state,		/* I - Current state */
700     void             *context)		/* I - User data (unused) */
701 {
702   (void)client;
703   (void)context;
704 
705  /*
706   * If the connection drops, quit.
707   */
708 
709   if (state == AVAHI_CLIENT_FAILURE)
710   {
711     fputs("DEBUG: Avahi connection failed.\n", stderr);
712     avahi_simple_poll_quit(simple_poll);
713   }
714 }
715 #endif /* HAVE_AVAHI */
716 
717 
718 /*
719  * 'compare_devices()' - Compare two devices.
720  */
721 
722 static int				/* O - Result of comparison */
compare_devices(cups_device_t * a,cups_device_t * b)723 compare_devices(cups_device_t *a,	/* I - First device */
724                 cups_device_t *b)	/* I - Second device */
725 {
726   return (strcmp(a->name, b->name));
727 }
728 
729 
730 /*
731  * 'exec_backend()' - Execute the backend that corresponds to the
732  *                    resolved service name.
733  */
734 
735 static void
exec_backend(char ** argv)736 exec_backend(char **argv)		/* I - Command-line arguments */
737 {
738   const char	*resolved_uri,		/* Resolved device URI */
739 		*cups_serverbin;	/* Location of programs */
740   char		scheme[1024],		/* Scheme from URI */
741 		*ptr,			/* Pointer into scheme */
742 		filename[1024];		/* Backend filename */
743 
744 
745  /*
746   * Resolve the device URI...
747   */
748 
749   job_canceled = -1;
750 
751   while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
752   {
753     _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
754     sleep(10);
755 
756     if (getenv("CLASS") != NULL)
757       exit(CUPS_BACKEND_FAILED);
758   }
759 
760  /*
761   * Extract the scheme from the URI...
762   */
763 
764   strlcpy(scheme, resolved_uri, sizeof(scheme));
765   if ((ptr = strchr(scheme, ':')) != NULL)
766     *ptr = '\0';
767 
768  /*
769   * Get the filename of the backend...
770   */
771 
772   if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
773     cups_serverbin = CUPS_SERVERBIN;
774 
775   snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
776 
777  /*
778   * Overwrite the device URI and run the new backend...
779   */
780 
781   setenv("DEVICE_URI", resolved_uri, 1);
782 
783   argv[0] = (char *)resolved_uri;
784 
785   fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
786 
787   execv(filename, argv);
788 
789   fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
790           strerror(errno));
791   exit(CUPS_BACKEND_STOP);
792 }
793 
794 
795 /*
796  * 'device_type()' - Get DNS-SD type enumeration from string.
797  */
798 
799 static cups_devtype_t			/* O - Device type */
device_type(const char * regtype)800 device_type(const char *regtype)	/* I - Service registration type */
801 {
802 #ifdef HAVE_AVAHI
803   if (!strcmp(regtype, "_ipp._tcp"))
804     return (CUPS_DEVICE_IPP);
805   else if (!strcmp(regtype, "_ipps._tcp") ||
806 	   !strcmp(regtype, "_ipp-tls._tcp"))
807     return (CUPS_DEVICE_IPPS);
808   else if (!strcmp(regtype, "_fax-ipp._tcp"))
809     return (CUPS_DEVICE_FAX_IPP);
810   else if (!strcmp(regtype, "_printer._tcp"))
811     return (CUPS_DEVICE_PDL_DATASTREAM);
812 #else
813   if (!strcmp(regtype, "_ipp._tcp."))
814     return (CUPS_DEVICE_IPP);
815   else if (!strcmp(regtype, "_ipps._tcp.") ||
816 	   !strcmp(regtype, "_ipp-tls._tcp."))
817     return (CUPS_DEVICE_IPPS);
818   else if (!strcmp(regtype, "_fax-ipp._tcp."))
819     return (CUPS_DEVICE_FAX_IPP);
820   else if (!strcmp(regtype, "_printer._tcp."))
821     return (CUPS_DEVICE_PRINTER);
822   else if (!strcmp(regtype, "_pdl-datastream._tcp."))
823     return (CUPS_DEVICE_PDL_DATASTREAM);
824 #endif /* HAVE_AVAHI */
825 
826   return (CUPS_DEVICE_RIOUSBPRINT);
827 }
828 
829 
830 /*
831  * 'get_device()' - Create or update a device.
832  */
833 
834 static cups_device_t *			/* O - Device */
get_device(cups_array_t * devices,const char * serviceName,const char * regtype,const char * replyDomain)835 get_device(cups_array_t *devices,	/* I - Device array */
836            const char   *serviceName,	/* I - Name of service/device */
837            const char   *regtype,	/* I - Type of service */
838            const char   *replyDomain)	/* I - Service domain */
839 {
840   cups_device_t	key,			/* Search key */
841 		*device;		/* Device */
842   char		fullName[kDNSServiceMaxDomainName];
843 					/* Full name for query */
844 
845 
846  /*
847   * See if this is a new device...
848   */
849 
850   key.name = (char *)serviceName;
851   key.type = device_type(regtype);
852 
853   for (device = cupsArrayFind(devices, &key);
854        device;
855        device = cupsArrayNext(devices))
856     if (_cups_strcasecmp(device->name, key.name))
857       break;
858     else if (device->type == key.type)
859     {
860       if (!_cups_strcasecmp(device->domain, "local.") &&
861           _cups_strcasecmp(device->domain, replyDomain))
862       {
863        /*
864         * Update the .local listing to use the "global" domain name instead.
865 	* The backend will try local lookups first, then the global domain name.
866 	*/
867 
868         free(device->domain);
869 	device->domain = strdup(replyDomain);
870 
871 #ifdef HAVE_MDNSRESPONDER
872 	DNSServiceConstructFullName(fullName, device->name, regtype,
873 	                            replyDomain);
874 #else /* HAVE_AVAHI */
875 	avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
876 				serviceName, regtype, replyDomain);
877 #endif /* HAVE_MDNSRESPONDER */
878 
879 	free(device->fullName);
880 	device->fullName = strdup(fullName);
881       }
882 
883       return (device);
884     }
885 
886  /*
887   * Yes, add the device...
888   */
889 
890   if ((device = calloc(1, sizeof(cups_device_t))) == NULL)
891   {
892     perror("DEBUG: Out of memory adding a device");
893     return (NULL);
894   }
895 
896   device->name     = strdup(serviceName);
897   device->domain   = strdup(replyDomain);
898   device->type     = key.type;
899   device->priority = 50;
900 
901   cupsArrayAdd(devices, device);
902 
903  /*
904   * Set the "full name" of this service, which is used for queries...
905   */
906 
907 #ifdef HAVE_MDNSRESPONDER
908   DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
909 #else /* HAVE_AVAHI */
910   avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain);
911 #endif /* HAVE_MDNSRESPONDER */
912 
913   device->fullName = strdup(fullName);
914 
915   return (device);
916 }
917 
918 
919 #ifdef HAVE_AVAHI
920 /*
921  * 'poll_callback()' - Wait for input on the specified file descriptors.
922  *
923  * Note: This function is needed because avahi_simple_poll_iterate is broken
924  *       and always uses a timeout of 0 (!) milliseconds.
925  *       (https://github.com/lathiat/avahi/issues/127)
926  */
927 
928 static int				/* O - Number of file descriptors matching */
poll_callback(struct pollfd * pollfds,unsigned int num_pollfds,int timeout,void * context)929 poll_callback(
930     struct pollfd *pollfds,		/* I - File descriptors */
931     unsigned int  num_pollfds,		/* I - Number of file descriptors */
932     int           timeout,		/* I - Timeout in milliseconds (unused) */
933     void          *context)		/* I - User data (unused) */
934 {
935   int	val;				/* Return value */
936 
937 
938   (void)timeout;
939   (void)context;
940 
941   val = poll(pollfds, num_pollfds, 500);
942 
943   if (val < 0)
944     fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
945   else if (val > 0)
946     got_data = 1;
947 
948   return (val);
949 }
950 #endif /* HAVE_AVAHI */
951 
952 
953 #ifdef HAVE_DNSSD
954 #  ifdef HAVE_MDNSRESPONDER
955 /*
956  * 'query_callback()' - Process query data.
957  */
958 
959 static void
query_callback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * fullName,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)960 query_callback(
961     DNSServiceRef       sdRef,		/* I - Service reference */
962     DNSServiceFlags     flags,		/* I - Data flags */
963     uint32_t            interfaceIndex,	/* I - Interface */
964     DNSServiceErrorType errorCode,	/* I - Error, if any */
965     const char          *fullName,	/* I - Full service name */
966     uint16_t            rrtype,		/* I - Record type */
967     uint16_t            rrclass,	/* I - Record class */
968     uint16_t            rdlen,		/* I - Length of record data */
969     const void          *rdata,		/* I - Record data */
970     uint32_t            ttl,		/* I - Time-to-live */
971     void                *context)	/* I - Device */
972 {
973 #  else
974 /*
975  * 'query_callback()' - Process query data.
976  */
977 
978 static void
979 query_callback(
980     AvahiRecordBrowser     *browser,	/* I - Record browser */
981     AvahiIfIndex           interfaceIndex,
982 					/* I - Interface index (unused) */
983     AvahiProtocol          protocol,	/* I - Network protocol (unused) */
984     AvahiBrowserEvent      event,	/* I - What happened? */
985     const char             *fullName,	/* I - Service name */
986     uint16_t               rrclass,	/* I - Record class */
987     uint16_t               rrtype,	/* I - Record type */
988     const void             *rdata,	/* I - TXT record */
989     size_t                 rdlen,	/* I - Length of TXT record */
990     AvahiLookupResultFlags flags,	/* I - Flags */
991     void                   *context)	/* I - Device */
992 {
993   AvahiClient		*client = avahi_record_browser_get_client(browser);
994 					/* Client information */
995 #  endif /* HAVE_MDNSRESPONDER */
996   char		*ptr;			/* Pointer into string */
997   cups_device_t	*device = (cups_device_t *)context;
998 					/* Device */
999   const uint8_t	*data,			/* Pointer into data */
1000 		*datanext,		/* Next key/value pair */
1001 		*dataend;		/* End of entire TXT record */
1002   uint8_t	datalen;		/* Length of current key/value pair */
1003   char		key[256],		/* Key string */
1004 		value[256],		/* Value string */
1005 		make_and_model[512],	/* Manufacturer and model */
1006 		model[256],		/* Model */
1007 		pdl[256],		/* PDL */
1008 		device_id[2048];	/* 1284 device ID */
1009 
1010 
1011 #  ifdef HAVE_MDNSRESPONDER
1012   fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1013                   "interfaceIndex=%u, errorCode=%d, fullName=\"%s\", "
1014 		  "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1015 		  "context=%p)\n",
1016           sdRef, flags, interfaceIndex, errorCode, fullName, rrtype, rrclass, rdlen, rdata, ttl, context);
1017 
1018  /*
1019   * Only process "add" data...
1020   */
1021 
1022   if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1023     return;
1024 
1025 #  else
1026   fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%u, "
1027                   "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1028 		  "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1029           browser, interfaceIndex, protocol, event, fullName, rrclass, rrtype, rdata, (unsigned)rdlen, flags, context);
1030 
1031  /*
1032   * Only process "add" data...
1033   */
1034 
1035   if (event != AVAHI_BROWSER_NEW)
1036   {
1037     if (event == AVAHI_BROWSER_FAILURE)
1038       fprintf(stderr, "ERROR: %s\n",
1039 	      avahi_strerror(avahi_client_errno(client)));
1040 
1041     return;
1042   }
1043 #  endif /* HAVE_MDNSRESPONDER */
1044 
1045  /*
1046   * Pull out the priority and make and model from the TXT
1047   * record and save it...
1048   */
1049 
1050   device_id[0]      = '\0';
1051   make_and_model[0] = '\0';
1052   pdl[0]            = '\0';
1053 
1054   strlcpy(model, "Unknown", sizeof(model));
1055 
1056   for (data = rdata, dataend = data + rdlen;
1057        data < dataend;
1058        data = datanext)
1059   {
1060    /*
1061     * Read a key/value pair starting with an 8-bit length.  Since the
1062     * length is 8 bits and the size of the key/value buffers is 256, we
1063     * don't need to check for overflow...
1064     */
1065 
1066     datalen = *data++;
1067 
1068     if (!datalen || (data + datalen) > dataend)
1069       break;
1070 
1071     datanext = data + datalen;
1072 
1073     for (ptr = key; data < datanext && *data != '='; data ++)
1074       *ptr++ = (char)*data;
1075     *ptr = '\0';
1076 
1077     if (data < datanext && *data == '=')
1078     {
1079       data ++;
1080 
1081       if (data < datanext)
1082 	memcpy(value, data, (size_t)(datanext - data));
1083       value[datanext - data] = '\0';
1084 
1085       fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
1086 	      key, value);
1087     }
1088     else
1089     {
1090       fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
1091 	      key);
1092       continue;
1093     }
1094 
1095     if (!_cups_strncasecmp(key, "usb_", 4))
1096     {
1097      /*
1098       * Add USB device ID information...
1099       */
1100 
1101       ptr = device_id + strlen(device_id);
1102       snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value);
1103     }
1104 
1105     if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
1106 	!_cups_strcasecmp(key, "usb_MANUFACTURER"))
1107       strlcpy(make_and_model, value, sizeof(make_and_model));
1108     else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
1109       strlcpy(model, value, sizeof(model));
1110     else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1111     {
1112       if (value[0] == '(')
1113       {
1114        /*
1115 	* Strip parenthesis...
1116 	*/
1117 
1118 	if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1119 	  *ptr = '\0';
1120 
1121 	strlcpy(model, value + 1, sizeof(model));
1122       }
1123       else
1124 	strlcpy(model, value, sizeof(model));
1125     }
1126     else if (!_cups_strcasecmp(key, "ty"))
1127     {
1128       strlcpy(model, value, sizeof(model));
1129 
1130       if ((ptr = strchr(model, ',')) != NULL)
1131 	*ptr = '\0';
1132     }
1133     else if (!_cups_strcasecmp(key, "pdl"))
1134       strlcpy(pdl, value, sizeof(pdl));
1135     else if (!_cups_strcasecmp(key, "priority"))
1136       device->priority = atoi(value);
1137     else if ((device->type == CUPS_DEVICE_IPP ||
1138 	      device->type == CUPS_DEVICE_IPPS ||
1139 	      device->type == CUPS_DEVICE_PRINTER) &&
1140 	     !_cups_strcasecmp(key, "printer-type"))
1141     {
1142      /*
1143       * This is a CUPS printer!
1144       */
1145 
1146       device->cups_shared = 1;
1147 
1148       if (device->type == CUPS_DEVICE_PRINTER)
1149 	device->sent = 1;
1150     }
1151     else if (!_cups_strcasecmp(key, "UUID"))
1152       device->uuid = strdup(value);
1153   }
1154 
1155   if (device->device_id)
1156     free(device->device_id);
1157 
1158   if (!device_id[0] && strcmp(model, "Unknown"))
1159   {
1160     if (make_and_model[0])
1161       snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1162 	       make_and_model, model);
1163     else if (!_cups_strncasecmp(model, "designjet ", 10))
1164       snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s;", model + 10);
1165     else if (!_cups_strncasecmp(model, "stylus ", 7))
1166       snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s;", model + 7);
1167     else if ((ptr = strchr(model, ' ')) != NULL)
1168     {
1169      /*
1170       * Assume the first word is the make...
1171       */
1172 
1173       memcpy(make_and_model, model, (size_t)(ptr - model));
1174       make_and_model[ptr - model] = '\0';
1175 
1176       snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1177 	       make_and_model, ptr + 1);
1178     }
1179   }
1180 
1181   if (device_id[0] &&
1182       !strstr(device_id, "CMD:") &&
1183       !strstr(device_id, "COMMAND SET:") &&
1184       (strstr(pdl, "application/pdf") ||
1185        strstr(pdl, "application/postscript") ||
1186        strstr(pdl, "application/vnd.hp-PCL") ||
1187        strstr(pdl, "image/")))
1188   {
1189     value[0] = '\0';
1190     if (strstr(pdl, "application/pdf"))
1191       strlcat(value, ",PDF", sizeof(value));
1192     if (strstr(pdl, "application/postscript"))
1193       strlcat(value, ",PS", sizeof(value));
1194     if (strstr(pdl, "application/vnd.hp-PCL"))
1195       strlcat(value, ",PCL", sizeof(value));
1196     for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/"))
1197     {
1198       char *valptr = value + strlen(value);
1199       					/* Pointer into value */
1200 
1201       if (valptr < (value + sizeof(value) - 1))
1202         *valptr++ = ',';
1203 
1204       ptr += 6;
1205       while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.')
1206       {
1207         if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
1208           *valptr++ = (char)toupper(*ptr++ & 255);
1209         else
1210           break;
1211       }
1212 
1213       *valptr = '\0';
1214     }
1215 
1216     ptr = device_id + strlen(device_id);
1217     snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "CMD:%s;", value + 1);
1218   }
1219 
1220   if (device_id[0])
1221     device->device_id = strdup(device_id);
1222   else
1223     device->device_id = NULL;
1224 
1225   if (device->make_and_model)
1226     free(device->make_and_model);
1227 
1228   if (make_and_model[0])
1229   {
1230     strlcat(make_and_model, " ", sizeof(make_and_model));
1231     strlcat(make_and_model, model, sizeof(make_and_model));
1232 
1233     if (!_cups_strncasecmp(make_and_model, "EPSON EPSON ", 12))
1234       _cups_strcpy(make_and_model, make_and_model + 6);
1235     else if (!_cups_strncasecmp(make_and_model, "HP HP ", 6))
1236       _cups_strcpy(make_and_model, make_and_model + 3);
1237     else if (!_cups_strncasecmp(make_and_model, "Lexmark International Lexmark ", 30))
1238       _cups_strcpy(make_and_model, make_and_model + 22);
1239 
1240     device->make_and_model = strdup(make_and_model);
1241   }
1242   else
1243     device->make_and_model = strdup(model);
1244 }
1245 #endif /* HAVE_DNSSD */
1246 
1247 
1248 /*
1249  * 'sigterm_handler()' - Handle termination signals.
1250  */
1251 
1252 static void
1253 sigterm_handler(int sig)		/* I - Signal number (unused) */
1254 {
1255   (void)sig;
1256 
1257   if (job_canceled)
1258     _exit(CUPS_BACKEND_OK);
1259   else
1260     job_canceled = 1;
1261 }
1262 
1263 
1264 /*
1265  * 'unquote()' - Unquote a name string.
1266  */
1267 
1268 static void
1269 unquote(char       *dst,		/* I - Destination buffer */
1270         const char *src,		/* I - Source string */
1271 	size_t     dstsize)		/* I - Size of destination buffer */
1272 {
1273   char	*dstend = dst + dstsize - 1;	/* End of destination buffer */
1274 
1275 
1276   while (*src && dst < dstend)
1277   {
1278     if (*src == '\\')
1279     {
1280       src ++;
1281       if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1282           isdigit(src[2] & 255))
1283       {
1284         *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1285 	src += 3;
1286       }
1287       else
1288         *dst++ = *src++;
1289     }
1290     else
1291       *dst++ = *src ++;
1292   }
1293 
1294   *dst = '\0';
1295 }
1296