Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/backend/dnssd.c
1090 views
1
/*
2
* DNS-SD discovery backend for CUPS.
3
*
4
* Copyright © 2021-2022 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 */
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
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=%d, 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
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=%d, 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
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
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 */
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
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 */
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 */
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(sizeof(cups_device_t), 1)) == 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 */
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
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=%d, 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=%d, "
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
1297