Coverage Report

Created: 2026-01-09 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcups/cups/dnssd.c
Line
Count
Source
1
//
2
// DNS-SD API functions for CUPS.
3
//
4
// Copyright © 2022-2025 by OpenPrinting.
5
//
6
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
7
// information.
8
//
9
10
#include "cups-private.h"
11
#include "dnssd.h"
12
13
#ifdef __APPLE__
14
#  include <nameser.h>
15
#  include <CoreFoundation/CoreFoundation.h>
16
#  include <SystemConfiguration/SystemConfiguration.h>
17
#endif // __APPLE__
18
#ifdef HAVE_MDNSRESPONDER
19
#  include <dns_sd.h>
20
#  if _WIN32
21
#    include <winsock2.h>
22
#    define poll WSAPoll
23
#  else
24
#    include <poll.h>
25
#  endif // _WIN32
26
#elif _WIN32
27
#  include <windns.h>
28
#  pragma comment(lib, "dnsapi.lib")    // Link in dnsapi library...
29
#else // HAVE_AVAHI
30
#  include <avahi-client/client.h>
31
#  include <avahi-client/lookup.h>
32
#  include <avahi-client/publish.h>
33
#  include <avahi-common/alternative.h>
34
#  include <avahi-common/domain.h>
35
#  include <avahi-common/error.h>
36
#  include <avahi-common/malloc.h>
37
#  include <avahi-common/simple-watch.h>
38
0
#  define AVAHI_DNS_TYPE_LOC 29    // Per RFC 1876
39
#  include <net/if.h>
40
#endif // HAVE_MDNSRESPONDER
41
42
43
//
44
// Constants...
45
//
46
47
0
#define _CUPS_DNSSD_MAX   50  // Maximum number of browsers/services/etc.
48
49
50
//
51
// Private structures...
52
//
53
54
struct _cups_dnssd_s      // DNS-SD context
55
{
56
  cups_rwlock_t   rwlock;   // R/W lock for context
57
  size_t      config_changes; // Number of hostname/network changes
58
  cups_dnssd_error_cb_t cb;   // Error callback function
59
  void      *cb_data; // Error callback data
60
  cups_array_t    *browses, // Browse requests
61
      *queries, // Query requests
62
      *resolves,  // Resolve requests
63
      *services;  // Registered services
64
65
#ifdef HAVE_MDNSRESPONDER
66
  DNSServiceRef   ref;    // Master service reference
67
  char      hostname[256];  // Current mDNS hostname
68
  DNSServiceRef   hostname_ref; // Hostname monitoring reference
69
  cups_thread_t   monitor;    // Monitoring thread
70
71
#elif _WIN32
72
  char      hostname[256];  // Current mDNS hostname
73
74
#else // HAVE_AVAHI
75
  cups_mutex_t    mutex;    // Avahi poll mutex
76
  bool      in_callback;  // Doing a callback?
77
  AvahiClient   *client;  // Avahi client connection
78
  AvahiSimplePoll *poll;    // Avahi poll class
79
  cups_thread_t   monitor;  // Monitoring thread
80
  AvahiDomainBrowser  *dbrowser;  // Domain browser
81
  size_t    num_domains;  // Number of domains
82
  char      domains[_CUPS_DNSSD_MAX][256];
83
          // Domains
84
#endif // HAVE_MDNSRESPONDER
85
};
86
87
struct _cups_dnssd_browse_s   // DNS-SD browse request
88
{
89
  cups_dnssd_t    *dnssd;   // DNS-SD context
90
  cups_dnssd_browse_cb_t cb;    // Browse callback
91
  void      *cb_data; // Browse callback data
92
93
#ifdef HAVE_MDNSRESPONDER
94
  DNSServiceRef   ref;    // Browse reference
95
96
#elif _WIN32
97
  size_t    num_browsers; // Number of browsers
98
  struct
99
  {         // Browsers
100
    WCHAR   name[256];    // Browse name as a UTF-16 string
101
    DNS_SERVICE_BROWSE_REQUEST req;   // Browse request
102
    DNS_SERVICE_CANCEL  cancel;     // Cancellation structure
103
  }     browsers[_CUPS_DNSSD_MAX];
104
105
#else // HAVE_AVAHI
106
  size_t    num_browsers; // Number of browsers
107
  AvahiServiceBrowser *browsers[_CUPS_DNSSD_MAX];
108
          // Browsers
109
#endif // HAVE_MDNSRESPONDER
110
};
111
112
struct _cups_dnssd_query_s    // DNS-SD query request
113
{
114
  cups_dnssd_t    *dnssd;   // DNS-SD context
115
  cups_dnssd_query_cb_t cb;   // Query callback
116
  void      *cb_data; // Query callback data
117
118
#ifdef HAVE_MDNSRESPONDER
119
  DNSServiceRef   ref;    // Query reference
120
121
#elif _WIN32
122
  WCHAR     fullname[256];  // Query full name as a UTF-16 string
123
  MDNS_QUERY_REQUEST  req;    // Query request
124
  MDNS_QUERY_HANDLE handle;   // Query handle
125
  DNS_QUERY_RESULT  res;    // Query result
126
127
#else // HAVE_AVAHI
128
  AvahiRecordBrowser  *browser; // Browser
129
#endif // HAVE_MDNSRESPONDER
130
};
131
132
struct _cups_dnssd_resolve_s    // DNS-SD resolve request
133
{
134
  cups_dnssd_t    *dnssd;   // DNS-SD context
135
  cups_dnssd_resolve_cb_t cb;   // Resolve callback
136
  void      *cb_data; // Resolve callback data
137
138
#ifdef HAVE_MDNSRESPONDER
139
  DNSServiceRef   ref;    // Resolve reference
140
141
#elif _WIN32
142
  WCHAR     fullname[256];  // Full name as a UTF-16 string
143
  DNS_SERVICE_RESOLVE_REQUEST req;  // Resolve request
144
  DNS_SERVICE_CANCEL  cancel;   // Cancellation structure
145
146
#else // HAVE_AVAHI
147
  AvahiServiceResolver  *resolver;  // Resolver
148
#endif // HAVE_MDNSRESPONDER
149
};
150
151
#if _WIN32
152
struct _win32_srv_s     // Service
153
{
154
  DNS_SERVICE_REGISTER_REQUEST req; // Registration request
155
  DNS_SERVICE_CANCEL  cancel;   // Cancellation structure
156
  WCHAR   fullname[256];    // Full service name
157
  WCHAR   hostname[256];    // Hostname
158
  WCHAR   *txt;     // TXT key/value string buffer
159
};
160
#endif // _WIN32
161
162
struct _cups_dnssd_service_s    // DNS-SD service registration
163
{
164
  cups_dnssd_t    *dnssd;   // DNS-SD context
165
  char      *name;    // Service name
166
  uint32_t    if_index; // Interface index
167
  cups_dnssd_service_cb_t cb;   // Service callback
168
  void      *cb_data; // Service callback data
169
  unsigned char   loc[16];  // LOC record data
170
  bool      loc_set;  // Is the location data set?
171
172
#ifdef HAVE_MDNSRESPONDER
173
  size_t    num_refs; // Number of service references
174
  DNSServiceRef   refs[_CUPS_DNSSD_MAX];
175
          // Service references
176
  DNSRecordRef    loc_refs[_CUPS_DNSSD_MAX];
177
          // Service location records
178
179
#elif _WIN32
180
  size_t    num_srvs; // Number of services
181
  struct _win32_srv_s srvs[_CUPS_DNSSD_MAX];
182
          // Services
183
184
#else // HAVE_AVAHI
185
  AvahiEntryGroup *group;   // Group of services under this name
186
#endif // HAVE_MDNSRESPONDER
187
};
188
189
190
//
191
// Local functions...
192
//
193
194
static void   delete_browse(cups_dnssd_browse_t *browse);
195
static void   delete_query(cups_dnssd_query_t *query);
196
static void   delete_resolve(cups_dnssd_resolve_t *resolve);
197
static void   delete_service(cups_dnssd_service_t *service);
198
static void   report_error(cups_dnssd_t *dnssd, const char *message, ...) _CUPS_FORMAT(2,3);
199
200
#ifdef HAVE_MDNSRESPONDER
201
static void   *mdns_monitor(cups_dnssd_t *dnssd);
202
static void DNSSD_API mdns_browse_cb(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType error, const char *name, const char *regtype, const char *domain, cups_dnssd_browse_t *browse);
203
static void DNSSD_API mdns_hostname_cb(DNSServiceRef ref, DNSServiceFlags flags, uint32_t if_index, DNSServiceErrorType error, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, cups_dnssd_t *dnssd);
204
static void DNSSD_API mdns_query_cb(DNSServiceRef ref, DNSServiceFlags flags, uint32_t if_index, DNSServiceErrorType error, const char *name, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, cups_dnssd_query_t *query);
205
static void DNSSD_API mdns_resolve_cb(DNSServiceRef ref, DNSServiceFlags flags, uint32_t if_index, DNSServiceErrorType error, const char *fullname, const char *host, uint16_t port, uint16_t txtlen, const unsigned char *txt, cups_dnssd_resolve_t *resolve);
206
static void DNSSD_API mdns_service_cb(DNSServiceRef ref, DNSServiceFlags flags, DNSServiceErrorType error, const char *name, const char *regtype, const char *domain, cups_dnssd_service_t *service);
207
static const char *mdns_strerror(DNSServiceErrorType errorCode);
208
static cups_dnssd_flags_t mdns_to_cups(DNSServiceFlags flags, DNSServiceErrorType error);
209
210
#elif _WIN32
211
static void   win32_browse_cb(DWORD status, PVOID context, PDNS_RECORD record);
212
static void   win32_query_cb(PVOID context, PMDNS_QUERY_HANDLE handle, PDNS_QUERY_RESULT result);
213
static void   win32_resolve_cb(DWORD status, PVOID context, PDNS_SERVICE_INSTANCE instance);
214
static void   win32_service_cb(DWORD status, PVOID context, PDNS_SERVICE_INSTANCE instance);
215
static void   win32_utf8cpy(char *dst, const WCHAR *src, size_t dstsize);
216
static void   win32_wstrcpy(WCHAR *dst, const char *src, size_t dstsize);
217
218
#else // HAVE_AVAHI
219
static void   avahi_browse_cb(AvahiServiceBrowser *browser, AvahiIfIndex if_index, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, cups_dnssd_browse_t *browse);
220
static void   avahi_client_cb(AvahiClient *c, AvahiClientState state, cups_dnssd_t *dnssd);
221
static void   avahi_domain_cb(AvahiDomainBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *domain, AvahiLookupResultFlags flags, cups_dnssd_t *dnssd);
222
static AvahiIfIndex avahi_if_index(uint32_t if_index);
223
static void   avahi_lock(cups_dnssd_t *dnssd, const char *name);
224
static void   *avahi_monitor(cups_dnssd_t *dnssd);
225
static int    avahi_poll_cb(struct pollfd *ufds, unsigned int nfds, int timeout, cups_dnssd_t *dnssd);
226
static void   avahi_query_cb(AvahiRecordBrowser *browser, AvahiIfIndex if_index, AvahiProtocol protocol, AvahiBrowserEvent event, const char *fullName, uint16_t rrclass, uint16_t rrtype, const void *rdata, size_t rdlen, AvahiLookupResultFlags flags, cups_dnssd_query_t *query);
227
static void   avahi_resolve_cb(AvahiServiceResolver *resolver, AvahiIfIndex if_index, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txtrec, AvahiLookupResultFlags flags, cups_dnssd_resolve_t *resolve);
228
static void   avahi_service_cb(AvahiEntryGroup *srv, AvahiEntryGroupState state, cups_dnssd_service_t *service);
229
static void   avahi_unlock(cups_dnssd_t *dnssd, const char *name);
230
#endif // HAVE_MDNSRESPONDER
231
232
233
//
234
// 'cupsDNSSDAssembleFullName()' - Create a full service name from the instance
235
//                                 name, registration type, and domain.
236
//
237
// This function combines an instance name ("Example Name"), registration type
238
// ("_ipp._tcp"), and domain ("local.") to create a properly escaped full
239
// service name ("Example\032Name._ipp._tcp.local.").
240
//
241
242
bool          // O - `true` on success, `false` on failure
243
cupsDNSSDAssembleFullName(
244
    char       *fullname,   // I - Buffer for full name
245
    size_t     fullsize,      // I - Size of buffer
246
    const char *name,     // I - Service instance name
247
    const char *type,     // I - Registration type
248
    const char *domain)     // I - Domain
249
0
{
250
0
  if (!fullname || !fullsize || !name || !type)
251
0
    return (false);
252
253
#ifdef HAVE_MDNSRESPONDER
254
  if (fullsize < kDNSServiceMaxDomainName)
255
    return (false);
256
257
  return (DNSServiceConstructFullName(fullname, name, type, domain) == kDNSServiceErr_NoError);
258
259
#elif _WIN32
260
  char    *fullptr,   // Pointer into full name
261
    *fullend;   // End of full name
262
263
  for (fullptr = fullname, fullend = fullname + fullsize - 1; *name; name ++)
264
  {
265
    if (*name == ' ' || *name == '\\' || (*name & 0x80))
266
    {
267
      if ((fullend - fullptr) < 4)
268
        return (false);
269
270
      snprintf(fullptr, fullend - fullptr + 1, "\\%03d", *name & 255);
271
      fullptr += strlen(fullptr);
272
    }
273
    else
274
    {
275
      *fullptr++ = *name;
276
    }
277
  }
278
279
  snprintf(fullptr, fullend - fullptr + 1, ".%s.%s", type, domain ? domain : "local");
280
281
  return (true);
282
283
#else // HAVE_AVAHI
284
0
  return (!avahi_service_name_join(fullname, fullsize, name, type, domain));
285
0
#endif // HAVE_MDNSRESPONDER
286
0
}
287
288
289
//
290
// 'cupsDNSSDBrowseDelete()' - Cancel and delete a browse request.
291
//
292
293
void
294
cupsDNSSDBrowseDelete(
295
    cups_dnssd_browse_t *browse)  // I - Browse request
296
0
{
297
0
  if (browse)
298
0
  {
299
0
    cups_dnssd_t *dnssd = browse->dnssd;
300
301
0
    DEBUG_puts("2cupsDNSSDBrowseDelete: Write locking rwlock.");
302
0
    cupsRWLockWrite(&dnssd->rwlock);
303
304
0
    cupsArrayRemove(dnssd->browses, browse);
305
306
0
    DEBUG_puts("2cupsDNSSDBrowseDelete: Unlocking rwlock.");
307
0
    cupsRWUnlock(&dnssd->rwlock);
308
0
  }
309
0
}
310
311
312
//
313
// 'cupsDNSSDBrowseGetContext()' - Get the DNS-SD context for the browse request.
314
//
315
316
cups_dnssd_t *        // O - Context or `NULL`
317
cupsDNSSDBrowseGetContext(
318
    cups_dnssd_browse_t *browse)  // I - Browse request
319
0
{
320
0
  return (browse ? browse->dnssd : NULL);
321
0
}
322
323
324
//
325
// 'cupsDNSSDBrowseNew()' - Create a new DNS-SD browse request.
326
//
327
// This function creates a new DNS-SD browse request for the specified service
328
// types and optional domain and interface index.  The "types" argument can be a
329
// single service type ("_ipp._tcp") or a service type and comma-delimited list
330
// of sub-types ("_ipp._tcp,_print,_universal").
331
//
332
// Newly discovered services are reported using the required browse callback
333
// function, with the "flags" argument set to `CUPS_DNSSD_FLAGS_ADD` for newly
334
// discovered services, `CUPS_DNSSD_FLAGS_NONE` for removed services, or
335
// `CUPS_DNSSD_FLAGS_ERROR` on an error:
336
//
337
// ```
338
// void
339
// browse_cb(
340
//     cups_dnssd_browse_t *browse,
341
//     void                *cb_data,
342
//     cups_dnssd_flags_t  flags,
343
//     uint32_t            if_index,
344
//     const char          *name,
345
//     const char          *regtype,
346
//     const char          *domain)
347
// {
348
//     // Process added/removed service
349
// }
350
// ```
351
//
352
353
cups_dnssd_browse_t *     // O - Browse request or `NULL` on error
354
cupsDNSSDBrowseNew(
355
    cups_dnssd_t           *dnssd,  // I - DNS-SD context
356
    uint32_t               if_index,  // I - Interface index, `CUPS_DNSSD_IF_INDEX_ANY`, or `CUPS_DNSSD_IF_INDEX_LOCAL`
357
    const char             *types,  // I - Service types
358
    const char             *domain, // I - Domain name or `NULL` for default
359
    cups_dnssd_browse_cb_t browse_cb, // I - Browse callback function
360
    void                   *cb_data)  // I - Browse callback data
361
0
{
362
0
  cups_dnssd_browse_t *browse;  // Browse request
363
364
365
  // Range check input...
366
0
  if (!dnssd || !types || !browse_cb)
367
0
    return (NULL);
368
369
  // Allocate memory for the browser...
370
0
  if ((browse = (cups_dnssd_browse_t *)calloc(1, sizeof(cups_dnssd_browse_t))) == NULL)
371
0
    return (NULL);
372
373
0
  browse->dnssd   = dnssd;
374
0
  browse->cb      = browse_cb;
375
0
  browse->cb_data = cb_data;
376
377
0
  DEBUG_puts("2cupsDNSSDBrowseNew: Write locking rwlock.");
378
0
  cupsRWLockWrite(&dnssd->rwlock);
379
380
0
  if (!dnssd->browses)
381
0
  {
382
    // Create an array of browsers...
383
0
    if ((dnssd->browses = cupsArrayNew(NULL, NULL, NULL, 0, NULL, (cups_afree_cb_t)delete_browse)) == NULL)
384
0
    {
385
      // Unable to create...
386
0
      free(browse);
387
0
      browse = NULL;
388
0
      goto done;
389
0
    }
390
0
  }
391
392
#ifdef HAVE_MDNSRESPONDER
393
  DNSServiceErrorType error;    // Error, if any
394
395
  browse->ref = dnssd->ref;
396
  if ((error = DNSServiceBrowse(&browse->ref, kDNSServiceFlagsShareConnection, if_index, types, domain, (DNSServiceBrowseReply)mdns_browse_cb, browse)) != kDNSServiceErr_NoError)
397
  {
398
    report_error(dnssd, "Unable to create DNS-SD browse request: %s", mdns_strerror(error));
399
    free(browse);
400
    browse = NULL;
401
    goto done;
402
  }
403
404
#elif _WIN32
405
  DNS_STATUS  status;     // DNS request status
406
  size_t  i,      // Looping var
407
    count;      // Number of types
408
  const char  *base,      // Base query name
409
    *subtype;   // Subtype
410
  char    typename[256];    // Current query name
411
  cups_array_t  *tarray;    // Types array
412
413
414
  if ((tarray = cupsArrayNewStrings(types, ',')) == NULL)
415
  {
416
    report_error(dnssd, "Unable to create types array: %s", strerror(errno));
417
    free(browse);
418
    browse = NULL;
419
    goto done;
420
  }
421
422
  base  = (const char *)cupsArrayGetElement(tarray, 0);
423
  count = cupsArrayGetCount(tarray);
424
425
  if (count == 1)
426
    count ++;
427
428
  for (i = 1; i < count; i ++)
429
  {
430
    subtype = (const char *)cupsArrayGetElement(tarray, i);
431
432
    if (subtype)
433
      snprintf(typename, sizeof(typename), "%s._sub.%s.local", subtype, base);
434
    else
435
      snprintf(typename, sizeof(typename), "%s.local", base);
436
437
    browse->browsers[i].req.Version         = DNS_QUERY_REQUEST_VERSION1;
438
    browse->browsers[i].req.InterfaceIndex  = if_index;
439
    browse->browsers[i].req.pBrowseCallback = win32_browse_cb;
440
    browse->browsers[i].req.pQueryContext   = browse;
441
    browse->browsers[i].req.QueryName       = browse->browsers[i].name;
442
443
    win32_wstrcpy(browse->browsers[i].name, typename, sizeof(browse->browsers[i].name));
444
445
    if ((status = DnsServiceBrowse(&browse->browsers[i].req, &browse->browsers[i].cancel)) != DNS_REQUEST_PENDING)
446
    {
447
      report_error(dnssd, "Unable to create browser: %u", status);
448
      while (i > 0)
449
      {
450
        i --;
451
        DnsServiceBrowseCancel(&browse->browsers[i].cancel);
452
      }
453
      free(browse);
454
      browse = NULL;
455
      cupsArrayDelete(tarray);
456
      goto done;
457
    }
458
  }
459
460
  cupsArrayDelete(tarray);
461
462
#else // HAVE_AVAHI
463
0
  size_t  i, j,   // Looping vars
464
0
    count;    // Number of types
465
0
  const char  *base,    // Base query name
466
0
    *subtype; // Subtype
467
0
  char    typename[256];  // Current query name
468
0
  cups_array_t  *tarray;  // Types array
469
470
471
0
  if ((tarray = cupsArrayNewStrings(types, ',')) == NULL)
472
0
  {
473
0
    report_error(dnssd, "Unable to create types array: %s", strerror(errno));
474
0
    free(browse);
475
0
    browse = NULL;
476
0
    goto done;
477
0
  }
478
479
0
  base  = (const char *)cupsArrayGetElement(tarray, 0);
480
0
  count = cupsArrayGetCount(tarray);
481
482
0
  if (count == 1)
483
0
    count ++;
484
485
0
  avahi_lock(dnssd, "cupsDNSSDBrowseNew");
486
487
0
  for (i = 1; i < count && browse->num_browsers < _CUPS_DNSSD_MAX; i ++)
488
0
  {
489
0
    subtype = (const char *)cupsArrayGetElement(tarray, i);
490
491
0
    if (subtype)
492
0
      snprintf(typename, sizeof(typename), "%s._sub.%s", subtype, base);
493
0
    else
494
0
      cupsCopyString(typename, base, sizeof(typename));
495
496
0
    if ((browse->browsers[browse->num_browsers] = avahi_service_browser_new(dnssd->client, avahi_if_index(if_index), AVAHI_PROTO_UNSPEC, typename, domain, /*flags*/0, (AvahiServiceBrowserCallback)avahi_browse_cb, browse)) != NULL)
497
0
    {
498
0
      browse->num_browsers ++;
499
0
    }
500
0
    else
501
0
    {
502
0
      report_error(dnssd, "Unable to create DNS-SD browse request: %s", avahi_strerror(avahi_client_errno(dnssd->client)));
503
0
      while (browse->num_browsers > 0)
504
0
      {
505
0
        browse->num_browsers --;
506
0
        avahi_service_browser_free(browse->browsers[browse->num_browsers]);
507
0
      }
508
0
      free(browse);
509
0
      browse = NULL;
510
511
0
      cupsArrayDelete(tarray);
512
513
0
      goto avahi_done;
514
0
    }
515
516
0
    if (!domain && dnssd->num_domains > 0)
517
0
    {
518
      // Add browsers for all domains...
519
0
      for (j = 0; j < dnssd->num_domains && browse->num_browsers < _CUPS_DNSSD_MAX; j ++)
520
0
      {
521
0
  if ((browse->browsers[browse->num_browsers] = avahi_service_browser_new(dnssd->client, avahi_if_index(if_index), AVAHI_PROTO_UNSPEC, typename, dnssd->domains[i], /*flags*/0, (AvahiServiceBrowserCallback)avahi_browse_cb, browse)) != NULL)
522
0
    browse->num_browsers ++;
523
0
      }
524
0
    }
525
0
  }
526
527
0
  cupsArrayDelete(tarray);
528
529
0
  avahi_done:
530
531
0
  avahi_unlock(dnssd, "cupsDNSSDBrowseNew");
532
0
#endif // HAVE_MDNSRESPONDER
533
534
0
  DEBUG_printf("2cupsDNSSDBrowseNew: Adding browse=%p", (void *)browse);
535
0
  cupsArrayAdd(dnssd->browses, browse);
536
537
0
  done:
538
539
0
  DEBUG_puts("2cupsDNSSDBrowseNew: Unlocking rwlock.");
540
0
  cupsRWUnlock(&dnssd->rwlock);
541
542
0
  return (browse);
543
0
}
544
545
546
547
//
548
// 'cupsDNSSDCopyComputerName()' - Copy the current human-readable name for the system.
549
//
550
// This function copies the current human-readable name ("My Computer") to the
551
// provided buffer.  The "dnssd" argument is a DNS-SD context created with
552
// @link cupsDNSSDNew@.  The "buffer" argument points to a character array of
553
// at least 128 bytes and the "bufsize" argument specifies the actual size of
554
// the array.
555
//
556
557
char *          // O - Computer name or `NULL` on error
558
cupsDNSSDCopyComputerName(
559
    cups_dnssd_t *dnssd,    // I - DNS-SD context
560
    char         *buffer,   // I - Computer name buffer
561
    size_t       bufsize)   // I - Size of computer name buffer (at least 128 bytes)
562
0
{
563
  // Range check input...
564
0
  if (buffer)
565
0
    *buffer = '\0';
566
567
0
  if (!dnssd || !buffer || bufsize < 128)
568
0
    return (NULL);
569
570
  // Copy the current computer name...
571
#ifdef __APPLE__
572
  SCDynamicStoreRef sc;     // Context for dynamic store
573
  CFStringEncoding nameEncoding;  // Encoding of computer name
574
  CFStringRef nameRef;    // Computer name CFString
575
576
  if ((sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("libcups"), NULL, NULL)) != NULL)
577
  {
578
    // Get the computer name from the dynamic store...
579
    if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL)
580
    {
581
      if (!CFStringGetCString(nameRef, buffer, (CFIndex)bufsize, kCFStringEncodingUTF8))
582
        *buffer = '\0';
583
584
      CFRelease(nameRef);
585
    }
586
587
    CFRelease(sc);
588
  }
589
590
#elif defined(HAVE_MDNSRESPONDER)
591
  char  *bufptr;      // Pointer into name
592
593
  DEBUG_puts("2cupsDNSSDCopyComputerName: Read locking rwlock.");
594
  cupsRWLockRead(&dnssd->rwlock);
595
596
  cupsCopyString(buffer, dnssd->hostname, bufsize);
597
598
  DEBUG_puts("2cupsDNSSDCopyComputerName: Unlocking rwlock.");
599
  cupsRWUnlock(&dnssd->rwlock);
600
601
  if ((bufptr = strchr(buffer, '.')) != NULL)
602
    *bufptr = '\0';
603
604
#elif _WIN32
605
  DWORD size = (DWORD)bufsize;    // Size for GetComputerNameA
606
607
  GetComputerNameA(buffer, &size);
608
609
#else // HAVE_AVAHI
610
0
  cupsCopyString(buffer, avahi_client_get_host_name(dnssd->client), bufsize);
611
0
#endif // __APPLE__
612
613
0
  return (buffer);
614
0
}
615
616
617
//
618
// 'cupsDNSSDCopyHostName()' - Copy the current mDNS hostname for the system.
619
//
620
// This function copies the current mDNS hostname ("hostname.local") to the
621
// provided buffer.  The "dnssd" argument is a DNS-SD context created with
622
// @link cupsDNSSDNew@.  The "buffer" argument points to a character array of
623
// at least 70 bytes and the "bufsize" argument specifies the actual size of
624
// the array.
625
//
626
627
char *          // O - mDNS hostname or `NULL` on error
628
cupsDNSSDCopyHostName(
629
    cups_dnssd_t *dnssd,    // I - DNS-SD context
630
    char         *buffer,   // I - Hostname buffer
631
    size_t       bufsize)   // I - Size of hostname buffer (at least 70 bytes)
632
0
{
633
  // Range check input...
634
0
  if (!dnssd || !buffer || bufsize < 70)
635
0
  {
636
0
    if (buffer)
637
0
      *buffer = '\0';
638
639
0
    return (NULL);
640
0
  }
641
642
  // Copy the current hostname...
643
#ifdef HAVE_MDNSRESPONDER
644
  DEBUG_puts("2cupsDNSSDCopyHostName: Read locking rwlock.");
645
  cupsRWLockRead(&dnssd->rwlock);
646
647
  cupsCopyString(buffer, dnssd->hostname, bufsize);
648
649
  DEBUG_puts("2cupsDNSSDCopyHostName: Unlocking rwlock.");
650
  cupsRWUnlock(&dnssd->rwlock);
651
652
#elif _WIN32
653
  cupsCopyString(buffer, dnssd->hostname, bufsize);
654
655
#else // HAVE_AVAHI
656
0
  cupsCopyString(buffer, avahi_client_get_host_name_fqdn(dnssd->client), bufsize);
657
0
#endif // HAVE_MDNSRESPONDER
658
659
0
  return (buffer);
660
0
}
661
662
663
//
664
// 'cupsDNSSDDecodeTXT()' - Decode a TXT record into key/value pairs.
665
//
666
// This function converts the DNS TXT record encoding of key/value pairs into
667
// `cups_option_t` elements that can be accessed using the @link cupsGetOption@
668
// function and freed using the @link cupsFreeOptions@ function.
669
//
670
671
size_t          // O - Number of key/value pairs
672
cupsDNSSDDecodeTXT(
673
    const unsigned char *txtrec,  // I - TXT record data
674
    uint16_t            txtlen,   // I - TXT record length
675
    cups_option_t       **txt)    // O - Key/value pairs
676
0
{
677
0
  size_t  num_txt = 0;    // Number of key/value pairs
678
0
  unsigned char keylen;     // Length of key/value
679
0
  char    key[256],   // Key/value buffer
680
0
    *value;     // Pointer to value
681
0
  const unsigned char *txtptr,    // Pointer into TXT record data
682
0
    *txtend;    // End of TXT record data
683
684
685
  // Range check input...
686
0
  if (txt)
687
0
    *txt = NULL;
688
0
  if (!txtrec || !txtlen || !txt)
689
0
    return (0);
690
691
  // Loop through the record...
692
0
  for (txtptr = txtrec, txtend = txtrec + txtlen; txtptr < txtend; txtptr += keylen)
693
0
  {
694
    // Format is a length byte followed by "key=value"
695
0
    keylen = *txtptr++;
696
0
    if (keylen == 0 || (txtptr + keylen) > txtend)
697
0
      break;       // Bogus length
698
699
    // Copy the data to a C string...
700
0
    memcpy(key, txtptr, keylen);
701
0
    key[keylen] = '\0';
702
703
0
    if ((value = strchr(key, '=')) != NULL)
704
0
    {
705
      // Got value separator, add it...
706
0
      *value++ = '\0';
707
708
0
      num_txt = cupsAddOption(key, value, num_txt, txt);
709
0
    }
710
0
    else
711
0
    {
712
      // No value, stop...
713
0
      break;
714
0
    }
715
0
  }
716
717
  // Return the number of pairs we parsed...
718
0
  return (num_txt);
719
720
0
}
721
722
723
//
724
// 'cupsDNSSDDelete()' - Delete a DNS-SD context and all its requests.
725
//
726
727
void
728
cupsDNSSDDelete(cups_dnssd_t *dnssd)  // I - DNS-SD context
729
0
{
730
0
  if (!dnssd)
731
0
    return;
732
733
0
  DEBUG_puts("2cupsDNSSDDelete: Write locking rwlock.");
734
0
  cupsRWLockWrite(&dnssd->rwlock);
735
736
0
  cupsArrayDelete(dnssd->browses);
737
0
  cupsArrayDelete(dnssd->queries);
738
0
  cupsArrayDelete(dnssd->resolves);
739
0
  cupsArrayDelete(dnssd->services);
740
741
0
  DEBUG_puts("2cupsDNSSDDelete: Unlocking rwlock.");
742
0
  cupsRWUnlock(&dnssd->rwlock);
743
744
#ifdef HAVE_MDNSRESPONDER
745
  cupsThreadCancel(dnssd->monitor);
746
  cupsThreadWait(dnssd->monitor);
747
  DNSServiceRefDeallocate(dnssd->ref);
748
749
#elif _WIN32
750
751
#else // HAVE_AVAHI
752
0
  avahi_domain_browser_free(dnssd->dbrowser);
753
754
0
  cupsThreadCancel(dnssd->monitor);
755
0
  cupsThreadWait(dnssd->monitor);
756
757
0
  avahi_simple_poll_free(dnssd->poll);
758
0
#endif // HAVE_MDNSRESPONDER
759
760
0
  cupsRWDestroy(&dnssd->rwlock);
761
0
  free(dnssd);
762
0
}
763
764
765
//
766
// 'cupsDNSSDGetConfigChanges()' - Get the number of host name/network
767
//                                 configuration changes seen.
768
//
769
// This function returns the number of host name or network configuration
770
// changes that have been seen since the context was created.  The value can be
771
// used to track when local services need to be updated.  Registered services
772
// will also get a callback with the `CUPS_DNSSD_FLAGS_HOST_CHANGE` bit set in
773
// the "flags" argument for host name changes and/or
774
// `CUPS_DNSSD_FLAGS_NETWORK_CHANGE` for network changes.
775
//
776
777
size_t          // O - Number of host name changes
778
cupsDNSSDGetConfigChanges(
779
    cups_dnssd_t *dnssd)    // I - DNS-SD context
780
0
{
781
0
  size_t  config_changes = 0;
782
783
784
0
  if (dnssd)
785
0
  {
786
0
    cupsRWLockRead(&dnssd->rwlock);
787
0
    config_changes = dnssd->config_changes;
788
0
    cupsRWUnlock(&dnssd->rwlock);
789
0
  }
790
791
0
  return (config_changes);
792
0
}
793
794
795
//
796
// 'cupsDNSSDNew()' - Create a new DNS-SD context.
797
//
798
// This function creates a new DNS-SD context for browsing, querying, resolving,
799
// and/or registering services.  Call @link cupsDNSSDDelete@ to stop any pending
800
// browses, queries, or resolves, unregister any services, and free the DNS-SD
801
// context.
802
//
803
804
cups_dnssd_t *        // O - DNS-SD context
805
cupsDNSSDNew(
806
    cups_dnssd_error_cb_t error_cb, // I - Error callback function
807
    void                  *cb_data) // I - Error callback data
808
0
{
809
0
  cups_dnssd_t  *dnssd;     // DNS-SD context
810
811
812
0
  DEBUG_printf("cupsDNSSDNew(error_cb=%p, cb_data=%p)", (void *)error_cb, cb_data);
813
814
  // Allocate memory...
815
0
  if ((dnssd = (cups_dnssd_t *)calloc(1, sizeof(cups_dnssd_t))) == NULL)
816
0
  {
817
0
    DEBUG_puts("2cupsDNSSDNew: Unable to allocate memory, returning NULL.");
818
0
    return (NULL);
819
0
  }
820
821
  // Save the error callback...
822
0
  dnssd->cb      = error_cb;
823
0
  dnssd->cb_data = cb_data;
824
825
  // Initialize the rwlock...
826
0
  cupsRWInit(&dnssd->rwlock);
827
828
  // Setup the DNS-SD connection and monitor thread...
829
#ifdef HAVE_MDNSRESPONDER
830
  DNSServiceErrorType error;    // Error code
831
832
  if ((error = DNSServiceCreateConnection(&dnssd->ref)) != kDNSServiceErr_NoError)
833
  {
834
    // Unable to create connection...
835
    report_error(dnssd, "Unable to initialize DNS-SD: %s", mdns_strerror(error));
836
    cupsDNSSDDelete(dnssd);
837
    DEBUG_puts("2cupsDNSSDNew: Unable to create DNS-SD thread - returning NULL.");
838
    return (NULL);
839
  }
840
841
  // Monitor for hostname changes...
842
  httpGetHostname(NULL, dnssd->hostname, sizeof(dnssd->hostname));
843
  dnssd->hostname_ref = dnssd->ref;
844
  if ((error = DNSServiceQueryRecord(&dnssd->hostname_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, "1.0.0.127.in-addr.arpa.", kDNSServiceType_PTR, kDNSServiceClass_IN, (DNSServiceQueryRecordReply)mdns_hostname_cb, dnssd)) != kDNSServiceErr_NoError)
845
  {
846
    report_error(dnssd, "Unable to query PTR record for local hostname: %s", mdns_strerror(error));
847
    dnssd->hostname_ref = NULL;
848
  }
849
850
  // Start the background monitoring thread...
851
  if ((dnssd->monitor = cupsThreadCreate((void *(*)(void *))mdns_monitor, dnssd)) == 0)
852
  {
853
    report_error(dnssd, "Unable to create DNS-SD thread: %s", strerror(errno));
854
    cupsDNSSDDelete(dnssd);
855
    DEBUG_puts("2cupsDNSSDNew: Unable to create DNS-SD thread - returning NULL.");
856
    return (NULL);
857
  }
858
859
  DEBUG_printf("2cupsDNSSDNew: dnssd->monitor=%p", (void *)dnssd->monitor);
860
861
#elif _WIN32
862
  char  compname[256];      // Computer name
863
  DWORD compsize = sizeof(compname);  // Size of computer name buffer
864
865
  GetComputerNameA(compname, &compsize);
866
867
  snprintf(dnssd->hostname, sizeof(dnssd->hostname), "%s.local", compname);
868
869
#else // HAVE_AVAHI
870
0
  int error;        // Error code
871
872
  // Initialize the mutex used to control access to the socket
873
0
  cupsMutexInit(&dnssd->mutex);
874
875
  // Create a polled interface for Avahi requests...
876
0
  if ((dnssd->poll = avahi_simple_poll_new()) == NULL)
877
0
  {
878
    // Unable to create the background thread...
879
0
    report_error(dnssd, "Unable to initialize DNS-SD: %s", strerror(errno));
880
0
    cupsDNSSDDelete(dnssd);
881
0
    DEBUG_puts("2cupsDNSSDNew: Unable to create simple poll - returning NULL.");
882
0
    return (NULL);
883
0
  }
884
885
0
  avahi_simple_poll_set_func(dnssd->poll, (AvahiPollFunc)avahi_poll_cb, dnssd);
886
887
0
  DEBUG_printf("2cupsDNSSDNew: dnssd->poll=%p", (void *)dnssd->poll);
888
889
0
  if ((dnssd->client = avahi_client_new(avahi_simple_poll_get(dnssd->poll), AVAHI_CLIENT_NO_FAIL, (AvahiClientCallback)avahi_client_cb, dnssd, &error)) == NULL)
890
0
  {
891
    // Unable to create the client...
892
0
    report_error(dnssd, "Unable to initialize DNS-SD: %s", avahi_strerror(error));
893
0
    avahi_simple_poll_free(dnssd->poll);
894
0
    cupsDNSSDDelete(dnssd);
895
0
    DEBUG_puts("2cupsDNSSDNew: Unable to create Avahi client - returning NULL.");
896
0
    return (NULL);
897
0
  }
898
899
0
  DEBUG_printf("2cupsDNSSDNew: dnssd->client=%p", (void *)dnssd->client);
900
901
0
  if ((dnssd->monitor = cupsThreadCreate((void *(*)(void *))avahi_monitor, dnssd)) == 0)
902
0
  {
903
0
    report_error(dnssd, "Unable to create DNS-SD thread: %s", strerror(errno));
904
0
    cupsDNSSDDelete(dnssd);
905
0
    DEBUG_puts("2cupsDNSSDNew: Unable to create DNS-SD thread - returning NULL.");
906
0
    return (NULL);
907
0
  }
908
909
0
  DEBUG_printf("2cupsDNSSDNew: dnssd->monitor=%p", (void *)dnssd->monitor);
910
911
0
  avahi_lock(dnssd, "cupsDNSSDNew");
912
913
0
  dnssd->dbrowser = avahi_domain_browser_new(dnssd->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, /*domain*/NULL, AVAHI_DOMAIN_BROWSER_BROWSE, /*flags*/0, (AvahiDomainBrowserCallback)avahi_domain_cb, dnssd);
914
915
0
  avahi_unlock(dnssd, "cupsDNSSDNew");
916
0
#endif // HAVE_MDNSRESPONDER
917
918
0
  DEBUG_printf("2cupsDNSSDNew: Returning %p.", (void *)dnssd);
919
920
0
  return (dnssd);
921
0
}
922
923
924
//
925
// 'cupsDNSSDQueryDelete()' - Cancel and delete a query request.
926
//
927
928
void
929
cupsDNSSDQueryDelete(
930
    cups_dnssd_query_t *query)    // I - Query request
931
0
{
932
0
  if (query)
933
0
  {
934
0
    cups_dnssd_t *dnssd = query->dnssd;
935
936
0
    DEBUG_puts("2cupsDNSSDQueryDelete: Write locking rwlock.");
937
0
    cupsRWLockWrite(&dnssd->rwlock);
938
939
0
    cupsArrayRemove(dnssd->queries, query);
940
941
0
    DEBUG_puts("2cupsDNSSDQueryDelete: Unlocking rwlock.");
942
0
    cupsRWUnlock(&dnssd->rwlock);
943
0
  }
944
0
}
945
946
947
//
948
// 'cupsDNSSDQueryGetContext()' - Get the DNS-SD context for the query request.
949
//
950
951
cups_dnssd_t *        // O - DNS-SD context or `NULL`
952
cupsDNSSDQueryGetContext(
953
    cups_dnssd_query_t *query)    // I - Query request
954
0
{
955
0
  return (query ? query->dnssd : NULL);
956
0
}
957
958
959
//
960
// 'cupsDNSSDQueryNew()' - Create a new query request.
961
//
962
// This function creates a new DNS-SD query request for the specified full
963
// service name and DNS record type.  The "fullname" argument specifies the
964
// full DNS name of the service (instance name, type, and domain) being queried.
965
// Responses to the query are reported using the required query callback
966
// function with the "flags" argument set to `CUPS_DNSSD_FLAGS_NONE` on success
967
// or `CUPS_DNSSD_FLAGS_ERROR` on error:
968
//
969
// ```
970
// void
971
// query_cb(
972
//     cups_dnssd_query_t *query,
973
//     void               *cb_data,
974
//     cups_dnssd_flags_t flags,
975
//     uint32_t           if_index,
976
//     const char         *fullname,
977
//     uint16_t           rrtype,
978
//     const void         *qdata,
979
//     uint16_t           qlen)
980
// {
981
//     // Process query record
982
// }
983
// ```
984
//
985
986
cups_dnssd_query_t *      // O - Query request or `NULL` on error
987
cupsDNSSDQueryNew(
988
    cups_dnssd_t          *dnssd, // I - DNS-SD context
989
    uint32_t              if_index, // I - Interface index or `CUPS_DNSSD_IF_INDEX_ANY` or `CUPS_DNSSD_IF_INDEX_LOCAL`
990
    const char            *fullname,  // I - Full DNS name including types and domain
991
    uint16_t              rrtype, // I - Record type to query (`CUPS_DNSSD_RRTYPE_TXT`, etc.)
992
    cups_dnssd_query_cb_t query_cb, // I - Query callback function
993
    void                  *cb_data) // I - Query callback data
994
0
{
995
0
  cups_dnssd_query_t  *query;   // Query request
996
997
998
0
  DEBUG_printf("cupsDNSSDQueryNew(dnssd=%p, if_index=%u, fullname=\"%s\", rrtype=%u, query_cb=%p, cb_data=%p)", (void *)dnssd, if_index, fullname, rrtype, query_cb, cb_data);
999
1000
  // Range check input...
1001
0
  if (!dnssd || !fullname || !query_cb)
1002
0
    return (NULL);
1003
1004
  // Allocate memory for the resolver...
1005
0
  if ((query = (cups_dnssd_query_t *)calloc(1, sizeof(cups_dnssd_query_t))) == NULL)
1006
0
    return (NULL);
1007
1008
0
  query->dnssd   = dnssd;
1009
0
  query->cb      = query_cb;
1010
0
  query->cb_data = cb_data;
1011
1012
0
  DEBUG_puts("2cupsDNSSDQueryNew: Write locking rwlock.");
1013
0
  cupsRWLockWrite(&dnssd->rwlock);
1014
1015
0
  if (!dnssd->queries)
1016
0
  {
1017
    // Create an array of resolvers...
1018
0
    DEBUG_puts("2cupsDNSSDQueryNew: Creating queries array.");
1019
0
    if ((dnssd->queries = cupsArrayNew(NULL, NULL, NULL, 0, NULL, (cups_afree_cb_t)delete_query)) == NULL)
1020
0
    {
1021
      // Unable to create...
1022
0
      free(query);
1023
0
      query = NULL;
1024
0
      goto done;
1025
0
    }
1026
0
  }
1027
1028
#ifdef HAVE_MDNSRESPONDER
1029
  DNSServiceErrorType error;    // Error, if any
1030
1031
  query->ref = dnssd->ref;
1032
  if ((error = DNSServiceQueryRecord(&query->ref, kDNSServiceFlagsShareConnection, if_index, fullname, rrtype, kDNSServiceClass_IN, (DNSServiceQueryRecordReply)mdns_query_cb, query)) != kDNSServiceErr_NoError)
1033
  {
1034
    report_error(dnssd, "Unable to create DNS-SD query request: %s", mdns_strerror(error));
1035
    free(query);
1036
    query = NULL;
1037
    goto done;
1038
  }
1039
1040
#elif _WIN32
1041
  DNS_STATUS  status;   // DNS request status
1042
1043
  win32_wstrcpy(query->fullname, fullname, sizeof(query->fullname));
1044
1045
  query->req.Version        = DNS_QUERY_REQUEST_VERSION1;
1046
  query->req.InterfaceIndex = if_index;
1047
  query->req.Query          = query->fullname;
1048
  query->req.QueryType      = rrtype;
1049
  query->req.pQueryCallback = win32_query_cb;
1050
  query->req.pQueryContext  = query;
1051
1052
  if ((status = DnsStartMulticastQuery(&query->req, &query->handle)) != ERROR_SUCCESS)
1053
  {
1054
    report_error(dnssd, "Unable to start mDNS query request: %d", status);
1055
    free(query);
1056
    query = NULL;
1057
    goto done;
1058
  }
1059
1060
#else // HAVE_AVAHI
1061
0
  avahi_lock(dnssd, "cupsDNSSDQueryNew");
1062
1063
0
  query->browser = avahi_record_browser_new(dnssd->client, avahi_if_index(if_index), AVAHI_PROTO_UNSPEC, fullname, AVAHI_DNS_CLASS_IN, rrtype, 0, (AvahiRecordBrowserCallback)avahi_query_cb, query);
1064
1065
0
  avahi_unlock(dnssd, "cupsDNSSDQueryNew");
1066
1067
0
  if (!query->browser)
1068
0
  {
1069
0
    report_error(dnssd, "Unable to create DNS-SD query request: %s", avahi_strerror(avahi_client_errno(dnssd->client)));
1070
0
    free(query);
1071
0
    query = NULL;
1072
0
    goto done;
1073
0
  }
1074
0
#endif // HAVE_MDNSRESPONDER
1075
1076
0
  DEBUG_printf("2cupsDNSSDQueryNew: Adding query=%p", (void *)query);
1077
0
  cupsArrayAdd(dnssd->queries, query);
1078
1079
0
  done:
1080
1081
0
  DEBUG_puts("2cupsDNSSDQueryNew: Unlocking rwlock.");
1082
0
  cupsRWUnlock(&dnssd->rwlock);
1083
1084
0
  return (query);
1085
0
}
1086
1087
1088
1089
//
1090
// 'cupsDNSSDResolveDelete()' - Cancel and free a resolve request.
1091
//
1092
1093
void
1094
cupsDNSSDResolveDelete(
1095
    cups_dnssd_resolve_t *res)    // I - Resolve request
1096
0
{
1097
0
  if (res)
1098
0
  {
1099
0
    cups_dnssd_t *dnssd = res->dnssd;
1100
1101
0
    DEBUG_puts("2cupsDNSSDResolveDelete: Write locking rwlock.");
1102
0
    cupsRWLockWrite(&dnssd->rwlock);
1103
1104
0
    cupsArrayRemove(dnssd->resolves, res);
1105
1106
0
    DEBUG_puts("2cupsDNSSDResolveDelete: Unlocking rwlock.");
1107
0
    cupsRWUnlock(&dnssd->rwlock);
1108
0
  }
1109
0
}
1110
1111
1112
//
1113
// 'cupsDNSSDResolveGetContext()' - Get the DNS-SD context for the resolve request.
1114
//
1115
1116
cups_dnssd_t *        // O - DNS-SD context or `NULL`
1117
cupsDNSSDResolveGetContext(
1118
    cups_dnssd_resolve_t *resolve)  // I - Resolve request
1119
0
{
1120
0
  return (resolve ? resolve->dnssd : NULL);
1121
0
}
1122
1123
1124
//
1125
// 'cupsDNSSDResolveNew()' - Create a new DNS-SD resolve request.
1126
//
1127
// This function creates a new DNS-SD resolver for the specified instance name,
1128
// service type, and optional domain and interface index.  Resikved services
1129
// are reported using the required resolve callback function, with the "flags"
1130
// argument set to `CUPS_DNSSD_FLAGS_NONE` on success or
1131
// `CUPS_DNSSD_FLAGS_ERROR` on error:
1132
//
1133
// ```
1134
// void
1135
// resolve_cb(
1136
//     cups_dnssd_resolve_t *resolve,
1137
//     void                 *cb_data,
1138
//     cups_dnssd_flags_t   flags,
1139
//     uint32_t             if_index,
1140
//     const char           *fullname,
1141
//     const char           *host,
1142
//     uint16_t             port,
1143
//     size_t               num_txt,
1144
//     cups_option_t        *txt)
1145
// {
1146
//     // Process resolved service
1147
// }
1148
// ```
1149
//
1150
1151
cups_dnssd_resolve_t *      // O - Resolve request or `NULL` on error
1152
cupsDNSSDResolveNew(
1153
    cups_dnssd_t            *dnssd, // I - DNS-SD context
1154
    uint32_t                if_index, // I - Interface index or `CUPS_DNSSD_IF_INDEX_ANY` or `CUPS_DNSSD_IF_INDEX_LOCAL`
1155
    const char              *name,  // I - Service name
1156
    const char              *type,  // I - Service type
1157
    const char              *domain,  // I - Domain name or `NULL` for default
1158
    cups_dnssd_resolve_cb_t resolve_cb, // I - Resolve callback function
1159
    void                    *cb_data) // I - Resolve callback data
1160
0
{
1161
0
  cups_dnssd_resolve_t  *resolve; // Resolve request
1162
1163
1164
0
  DEBUG_printf("cupsDNSSDResolveNew(dnssd=%p, if_index=%u, name=\"%s\", type=\"%s\", domain=\"%s\", resolve_cb=%p, cb_data=%p)", (void *)dnssd, (unsigned)if_index, name, type, domain, (void *)resolve_cb, cb_data);
1165
1166
  // Range check input...
1167
0
  if (!dnssd || !name || !type || !resolve_cb)
1168
0
  {
1169
0
    DEBUG_puts("2cupsDNSSDResolveNew: Bad arguments, returning NULL.");
1170
0
    return (NULL);
1171
0
  }
1172
1173
  // Allocate memory for the resolver...
1174
0
  if ((resolve = (cups_dnssd_resolve_t *)calloc(1, sizeof(cups_dnssd_resolve_t))) == NULL)
1175
0
  {
1176
0
    DEBUG_printf("2cupsDNSSDResolveNew: Unable to allocate memory: %s", strerror(errno));
1177
0
    return (NULL);
1178
0
  }
1179
1180
0
  resolve->dnssd   = dnssd;
1181
0
  resolve->cb      = resolve_cb;
1182
0
  resolve->cb_data = cb_data;
1183
1184
#ifdef HAVE_MDNSRESPONDER
1185
  DNSServiceErrorType error;    // Error, if any
1186
1187
  resolve->ref = dnssd->ref;
1188
  if ((error = DNSServiceResolve(&resolve->ref, kDNSServiceFlagsShareConnection, if_index, name, type, domain, (DNSServiceResolveReply)mdns_resolve_cb, resolve)) != kDNSServiceErr_NoError)
1189
  {
1190
    report_error(dnssd, "Unable to create DNS-SD resolve request: %s", mdns_strerror(error));
1191
    free(resolve);
1192
    return (NULL);
1193
  }
1194
1195
#elif _WIN32
1196
  DNS_STATUS  status;     // Status of resolve
1197
  char    fullname[256];    // Full service name
1198
1199
  snprintf(fullname, sizeof(fullname), "%s.%s.%s", name, type, domain ? domain : "local");
1200
1201
  win32_wstrcpy(resolve->fullname, fullname, sizeof(resolve->fullname));
1202
1203
  resolve->req.Version                    = DNS_QUERY_REQUEST_VERSION1;
1204
  resolve->req.InterfaceIndex             = if_index;
1205
  resolve->req.QueryName                  = resolve->fullname;
1206
  resolve->req.pResolveCompletionCallback = win32_resolve_cb;
1207
  resolve->req.pQueryContext              = resolve;
1208
1209
  if ((status = DnsServiceResolve(&resolve->req, &resolve->cancel)) != DNS_REQUEST_PENDING)
1210
  {
1211
    report_error(dnssd, "Unable to create DNS-SD resolve request: %d", status);
1212
    free(resolve);
1213
    return (NULL);
1214
  }
1215
1216
#else // HAVE_AVAHI
1217
0
  avahi_lock(dnssd, "cupsDNSSDResolveNew");
1218
1219
0
  resolve->resolver = avahi_service_resolver_new(dnssd->client, avahi_if_index(if_index), AVAHI_PROTO_UNSPEC, name, type, domain, AVAHI_PROTO_UNSPEC, /*flags*/0, (AvahiServiceResolverCallback)avahi_resolve_cb, resolve);
1220
1221
0
  avahi_unlock(dnssd, "cupsDNSSDResolveNew");
1222
1223
0
  if (!resolve->resolver)
1224
0
  {
1225
0
    report_error(dnssd, "Unable to create DNS-SD resolve request: %s", avahi_strerror(avahi_client_errno(dnssd->client)));
1226
0
    free(resolve);
1227
0
    return (NULL);
1228
0
  }
1229
0
#endif // HAVE_MDNSRESPONDER
1230
1231
0
  DEBUG_puts("2cupsDNSSDResolveNew: Write locking rwlock.");
1232
0
  cupsRWLockWrite(&dnssd->rwlock);
1233
1234
0
  if (!dnssd->resolves)
1235
0
  {
1236
    // Create an array of resolvers...
1237
0
    DEBUG_puts("2cupsDNSSDResolveNew: Creating resolver array.");
1238
0
    if ((dnssd->resolves = cupsArrayNew(NULL, NULL, NULL, 0, NULL, (cups_afree_cb_t)delete_resolve)) == NULL)
1239
0
    {
1240
      // Unable to create...
1241
0
      DEBUG_printf("2cupsDNSSDResolveNew: Unable to allocate memory: %s", strerror(errno));
1242
0
      free(resolve);
1243
0
      resolve = NULL;
1244
1245
0
      goto done;
1246
0
    }
1247
0
  }
1248
1249
0
  DEBUG_printf("2cupsDNSSDResolveNew: Adding resolver %p.", (void *)resolve);
1250
0
  cupsArrayAdd(dnssd->resolves, resolve);
1251
1252
0
  done:
1253
1254
0
  DEBUG_puts("2cupsDNSSDResolveNew: Unlocking rwlock.");
1255
0
  cupsRWUnlock(&dnssd->rwlock);
1256
1257
0
  return (resolve);
1258
0
}
1259
1260
1261
//
1262
// 'cupsDNSSDSeparateFullName()' - Separate a full service name into an instance
1263
//                                 name, registration type, and domain.
1264
//
1265
// This function separates a full service name such as
1266
// "Example\032Name._ipp._tcp.local.") into its instance name ("Example Name"),
1267
// registration type ("_ipp._tcp"), and domain ("local.").
1268
//
1269
1270
bool          // O - `true` on success, `false` on error
1271
cupsDNSSDSeparateFullName(
1272
    const char *fullname,   // I - Full service name
1273
    char       *name,     // I - Instance name buffer
1274
    size_t     namesize,      // I - Size of instance name buffer
1275
    char       *type,     // I - Registration type buffer
1276
    size_t     typesize,      // I - Size of registration type buffer
1277
    char       *domain,     // I - Domain name buffer
1278
    size_t     domainsize)    // I - Size of domain name buffer
1279
0
{
1280
  // Range check input..
1281
0
  if (!fullname || !name || !namesize || !type || !typesize || !domain || !domainsize)
1282
0
  {
1283
0
    if (name)
1284
0
      *name = '\0';
1285
0
    if (type)
1286
0
      *type = '\0';
1287
0
    if (domain)
1288
0
      *domain = '\0';
1289
1290
0
    return (false);
1291
0
  }
1292
1293
#if _WIN32 || defined(HAVE_MDNSRESPONDER)
1294
  bool  ret = true;     // Return value
1295
  char  *ptr,       // Pointer into name/type/domain
1296
  *end;       // Pointer to end of name/type/domain
1297
1298
  // Get the service name...
1299
  for (ptr = name, end = name + namesize - 1; *fullname; fullname ++)
1300
  {
1301
    if (*fullname == '.')
1302
    {
1303
      // Service type separator...
1304
      break;
1305
    }
1306
    else if (*fullname == '\\' && isdigit(fullname[1] & 255) && isdigit(fullname[2] & 255) && isdigit(fullname[3] & 255))
1307
    {
1308
      // Escaped character
1309
      if (ptr < end)
1310
        *ptr++ = (fullname[1] - '0') * 100 + (fullname[2] - '0') * 10 + fullname[3] - '0';
1311
      else
1312
        ret = false;
1313
1314
      fullname += 3;
1315
    }
1316
    else if (ptr < end)
1317
      *ptr++ = *fullname;
1318
    else
1319
      ret = false;
1320
  }
1321
  *ptr = '\0';
1322
1323
  if (*fullname)
1324
    fullname ++;
1325
1326
  // Get the type...
1327
  for (ptr = type, end = type + typesize - 1; *fullname; fullname ++)
1328
  {
1329
    if (*fullname == '.' && fullname[1] != '_')
1330
    {
1331
      // Service type separator...
1332
      break;
1333
    }
1334
    else if (*fullname == '\\' && isdigit(fullname[1] & 255) && isdigit(fullname[2] & 255) && isdigit(fullname[3] & 255))
1335
    {
1336
      // Escaped character
1337
      if (ptr < end)
1338
        *ptr++ = (fullname[1] - '0') * 100 + (fullname[2] - '0') * 10 + fullname[3] - '0';
1339
      else
1340
        ret = false;
1341
1342
      fullname += 3;
1343
    }
1344
    else if (ptr < end)
1345
      *ptr++ = *fullname;
1346
    else
1347
      ret = false;
1348
  }
1349
  *ptr = '\0';
1350
1351
  if (*fullname)
1352
    fullname ++;
1353
1354
  // Get the domain...
1355
  for (ptr = domain, end = domain + domainsize - 1; *fullname; fullname ++)
1356
  {
1357
    if (*fullname == '\\' && isdigit(fullname[1] & 255) && isdigit(fullname[2] & 255) && isdigit(fullname[3] & 255))
1358
    {
1359
      // Escaped character
1360
      if (ptr < end)
1361
        *ptr++ = (fullname[1] - '0') * 100 + (fullname[2] - '0') * 10 + fullname[3] - '0';
1362
      else
1363
        ret = false;
1364
1365
      fullname += 3;
1366
    }
1367
    else if (ptr < end)
1368
      *ptr++ = *fullname;
1369
    else
1370
      ret = false;
1371
  }
1372
  *ptr = '\0';
1373
1374
  return (ret);
1375
1376
#else // HAVE_AVAHI
1377
0
  return (!avahi_service_name_split(fullname, name, namesize, type, typesize, domain, domainsize));
1378
0
#endif // _WIN32 || HAVE_MDNSRESPONDER
1379
0
}
1380
1381
1382
//
1383
// 'cupsDNSSDServiceAdd()' - Add a service instance.
1384
//
1385
// This function adds a service instance for the specified service types,
1386
// domain, host, and port.  The "types" argument can be a single service type
1387
// ("_ipp._tcp") or a service type and comma-delimited list of sub-types
1388
// ("_ipp._tcp,_print,_universal").
1389
//
1390
// Call the @link cupsDNSSDServicePublish@ function after all service instances
1391
// have been added.
1392
//
1393
1394
bool          // O - `true` on success, `false` on failure
1395
cupsDNSSDServiceAdd(
1396
    cups_dnssd_service_t *service,  // I - Service
1397
    const char           *types,  // I - Service types
1398
    const char           *domain, // I - Domain name or `NULL` for default
1399
    const char           *host,   // I - Host name or `NULL` for default
1400
    uint16_t             port,    // I - Port number or `0` for none
1401
    size_t               num_txt, // I - Number of TXT record values
1402
    cups_option_t        *txt)    // I - TXT record values
1403
0
{
1404
0
  bool    ret = true;   // Return value
1405
0
  size_t  i;      // Looping var
1406
1407
1408
0
  DEBUG_printf("cupsDNSSDServiceAdd(service=%p, types=\"%s\", domain=\"%s\", host=\"%s\", port=%u, num_txt=%u, txt=%p)", (void *)service, types, domain, host, port, (unsigned)num_txt, (void *)txt);
1409
1410
  // Range check input...
1411
0
  if (!service || !types)
1412
0
    return (false);
1413
1414
#ifdef HAVE_MDNSRESPONDER
1415
  DNSServiceErrorType error;    // Error, if any
1416
  TXTRecordRef    txtrec,   // TXT record
1417
      *txtptr = NULL; // Pointer to TXT record, if any
1418
1419
  // Limit number of services with this name...
1420
  if (service->num_refs >= (sizeof(service->refs) / sizeof(service->refs[0])))
1421
  {
1422
    report_error(service->dnssd, "Unable to create DNS-SD service registration: Too many services with this name.");
1423
    ret = false;
1424
    goto done;
1425
  }
1426
1427
  // Create the TXT record as needed...
1428
  if (num_txt)
1429
  {
1430
    TXTRecordCreate(&txtrec, 1024, NULL);
1431
    for (i = 0; i < num_txt; i ++)
1432
      TXTRecordSetValue(&txtrec, txt[i].name, (uint8_t)strlen(txt[i].value), txt[i].value);
1433
1434
    txtptr = &txtrec;
1435
  }
1436
1437
  service->refs[service->num_refs] = service->dnssd->ref;
1438
  if ((error = DNSServiceRegister(service->refs + service->num_refs, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, service->if_index, service->name, types, domain, host, htons(port), txtptr ? TXTRecordGetLength(txtptr) : 0, txtptr ? TXTRecordGetBytesPtr(txtptr) : NULL, (DNSServiceRegisterReply)mdns_service_cb, service)) != kDNSServiceErr_NoError)
1439
  {
1440
    if (txtptr)
1441
      TXTRecordDeallocate(txtptr);
1442
1443
    report_error(service->dnssd, "Unable to create DNS-SD service registration: %s", mdns_strerror(error));
1444
    ret = false;
1445
    goto done;
1446
  }
1447
1448
  if (txtptr)
1449
    TXTRecordDeallocate(txtptr);
1450
1451
  if (service->loc_set)
1452
  {
1453
    if ((error = DNSServiceAddRecord(service->refs[service->num_refs], service->loc_refs + service->num_refs, 0, kDNSServiceType_LOC, sizeof(service->loc), service->loc, 0)) != kDNSServiceErr_NoError)
1454
    {
1455
      report_error(service->dnssd, "Unable to add DNS-SD service location data: %s", mdns_strerror(error));
1456
    }
1457
  }
1458
1459
  service->num_refs ++;
1460
1461
#elif _WIN32
1462
  DWORD   status;     // Status of call
1463
  struct _win32_srv_s *srv;   // Service
1464
  size_t  j,      // Looping var
1465
    count,      // Number of types
1466
    length;     // Length of TXT key/value pairs
1467
  WCHAR   *ptr,     // Pointer into TXT buffer
1468
    *end,     // End of TXT buffer
1469
    *keys[256],   // TXT key strings
1470
    *values[256];   // TXT value strings
1471
  const char  *base;      // Base service type
1472
  char    fullname[256];    // Full service instance name
1473
  cups_array_t  *tarray;    // Types array
1474
1475
  if (port == 0)
1476
  {
1477
    // Windows DNS-SD support doesn't allow for so-called "flagship naming",
1478
    // just ignore services on port 0 for now...
1479
    // TODO: Report bug to Microsoft about lack of support for services on port 0.
1480
    return (true);
1481
  }
1482
1483
  if ((tarray = cupsArrayNewStrings(types, ',')) == NULL)
1484
  {
1485
    report_error(service->dnssd, "Unable to create types array: %s", strerror(errno));
1486
    ret = false;
1487
    goto done;
1488
  }
1489
1490
  base  = (const char *)cupsArrayGetElement(tarray, 0);
1491
  count = cupsArrayGetCount(tarray);
1492
1493
  // TODO: Figure out how WinDNS wants sub-types registered, yields invalid argument error for sub-type
1494
  for (i = 0; i < count && i < 1; i ++)
1495
  {
1496
    // Get the fullname...
1497
    if (i)
1498
      snprintf(fullname, sizeof(fullname), "%s.%s._sub.%s.%s", service->name, (const char*)cupsArrayGetElement(tarray, i), base, domain ? domain : "local");
1499
    else
1500
      snprintf(fullname, sizeof(fullname), "%s.%s.%s", service->name, base, domain ? domain : "local");
1501
1502
    DEBUG_printf("cupsDNSSDServiceAdd: Adding fullname=\"%s\"", fullname);
1503
1504
    // Get the service...
1505
    if (service->num_srvs >= _CUPS_DNSSD_MAX)
1506
    {
1507
      report_error(service->dnssd, "Too many services for this name.");
1508
      ret = false;
1509
      cupsArrayDelete(tarray);
1510
      goto done;
1511
    }
1512
1513
    srv = service->srvs + service->num_srvs;
1514
1515
    // Initialize values...
1516
    srv->req.Version        = DNS_QUERY_REQUEST_VERSION1;
1517
    srv->req.InterfaceIndex = service->if_index;
1518
1519
    win32_wstrcpy(srv->fullname, fullname, sizeof(srv->fullname));
1520
    if (host)
1521
      win32_wstrcpy(srv->hostname, host, sizeof(srv->hostname));
1522
    else
1523
      win32_wstrcpy(srv->hostname, service->dnssd->hostname, sizeof(srv->hostname));
1524
1525
    for (j = 0, length = 0; j < num_txt; j ++)
1526
      length += strlen(txt[j].name) + strlen(txt[j].value) + 2;
1527
1528
    DEBUG_printf("cupsDNSSDServiceAdd: TXT length=%lu", (unsigned long)length);
1529
1530
    if (length > 0)
1531
    {
1532
      srv->txt = calloc(length, sizeof(WCHAR));
1533
1534
      for (j = 0, ptr = srv->txt, end = srv->txt + length; j < num_txt; j ++)
1535
      {
1536
        win32_wstrcpy(ptr, txt[j].name, (size_t)(end - ptr) * sizeof(WCHAR));
1537
        keys[j] = ptr;
1538
        ptr     += strlen(txt[j].name) + 1;
1539
1540
        win32_wstrcpy(ptr, txt[j].value, (size_t)(end - ptr) * sizeof(WCHAR));
1541
        values[j] = ptr;
1542
        ptr     += strlen(txt[j].value) + 1;
1543
      }
1544
    }
1545
1546
    if ((srv->req.pServiceInstance = DnsServiceConstructInstance(srv->fullname, srv->hostname, /*pIp4*/NULL, /*pIp6*/NULL, port, 0, 0, (DWORD)num_txt, keys, values)) == NULL)
1547
    {
1548
      report_error(service->dnssd, "Unable to allocate memory for '%s' for host '%s', port %d.", fullname, host ? host : service->dnssd->hostname, port);
1549
      ret = false;
1550
      cupsArrayDelete(tarray);
1551
      goto done;
1552
    }
1553
1554
    srv->req.pRegisterCompletionCallback = win32_service_cb;
1555
    srv->req.pQueryContext               = service;
1556
    srv->req.unicastEnabled              = domain && strcmp(domain, "local") != 0;
1557
1558
    DEBUG_printf("cupsDNSSDServiceAdd: unicastEnabled=%s", srv->req.unicastEnabled ? "true" : "false");
1559
1560
    if ((status = DnsServiceRegister(&srv->req, &srv->cancel)) != DNS_REQUEST_PENDING)
1561
    {
1562
      report_error(service->dnssd, "Unable to register '%s': %d", fullname, status);
1563
      ret = false;
1564
      DnsServiceFreeInstance(srv->req.pServiceInstance);
1565
      cupsArrayDelete(tarray);
1566
      goto done;
1567
    }
1568
1569
    // Register...
1570
    service->num_srvs ++;
1571
  }
1572
1573
  cupsArrayDelete(tarray);
1574
1575
#else // HAVE_AVAHI
1576
0
  int     error;    // Error code
1577
0
  AvahiStringList *txtrec = NULL; // TXT record string list
1578
0
  char      *regtype, // Registration type
1579
0
      *subtypes;  // Subtypes (if any)
1580
1581
  // Avahi has trouble with hostnames for services that are only available on
1582
  // the loopback interface/localhost...
1583
0
  if (service->if_index == CUPS_DNSSD_IF_INDEX_LOCAL || (host && !_cups_strcasecmp(host, "localhost")))
1584
0
    host = NULL;
1585
1586
  // Build the string list from the TXT array...
1587
0
  for (i = 0; i < num_txt; i ++)
1588
0
    txtrec = avahi_string_list_add_printf(txtrec, "%s=%s", txt[i].name, txt[i].value);
1589
1590
  // Copy the registration type...
1591
0
  if ((regtype = strdup(types)) == NULL)
1592
0
  {
1593
0
    report_error(service->dnssd, "Unable to duplicate registration types: %s", strerror(errno));
1594
0
    ret = false;
1595
0
    goto done;
1596
0
  }
1597
1598
0
  if ((subtypes = strchr(regtype, ',')) != NULL)
1599
0
    *subtypes++ = '\0';
1600
1601
  // Add the service entry...
1602
0
  avahi_lock(service->dnssd, "cupsDNSSDServiceAdd");
1603
1604
0
  error = avahi_entry_group_add_service_strlst(service->group, avahi_if_index(service->if_index), AVAHI_PROTO_UNSPEC, /*flags*/0, service->name, regtype, domain, host, port, txtrec);
1605
1606
0
  if (error < 0)
1607
0
  {
1608
0
    report_error(service->dnssd, "Unable to register '%s.%s': %s", service->name, regtype, avahi_strerror(error));
1609
0
    ret = false;
1610
0
  }
1611
0
  else if (subtypes)
1612
0
  {
1613
0
    char  subtype[256];   // Subtype string
1614
0
    char  *start, *end;   // Pointers into sub-types...
1615
1616
0
    DEBUG_printf("cupsDNSSDServiceAdd: Registered '%s.%s.%s'.", service->name, regtype, domain);
1617
1618
0
    for (start = subtypes; ret && start && *start; start = end)
1619
0
    {
1620
0
      if ((end = strchr(start, ',')) != NULL)
1621
0
  *end++ = '\0';
1622
0
      else
1623
0
  end = start + strlen(start);
1624
1625
0
      snprintf(subtype, sizeof(subtype), "%s._sub.%s", start, regtype);
1626
0
      if ((error = avahi_entry_group_add_service_subtype(service->group, avahi_if_index(service->if_index), AVAHI_PROTO_UNSPEC, /*flags*/0, service->name, regtype, domain, subtype)) < 0)
1627
0
      {
1628
0
        report_error(service->dnssd, "Unable to register '%s.%s': %s", service->name, subtype, avahi_strerror(error));
1629
0
        ret = false;
1630
0
      }
1631
1632
0
      DEBUG_printf("cupsDNSSDServiceAdd: Registered '%s.%s.%s'.", service->name, subtype, domain);
1633
0
    }
1634
0
  }
1635
1636
0
  avahi_unlock(service->dnssd, "cupsDNSSDServiceAdd");
1637
1638
0
  free(regtype);
1639
1640
0
  if (txtrec)
1641
0
    avahi_string_list_free(txtrec);
1642
0
#endif // HAVE_MDNSRESPONDER
1643
1644
0
  done:
1645
1646
0
  DEBUG_printf("2cupsDNSSDServiceAdd: Returning %s.", ret ? "true" : "false");
1647
0
  return (ret);
1648
0
}
1649
1650
1651
//
1652
// 'cupsDNSSDServiceDelete()' - Cancel and free a service registration.
1653
//
1654
1655
void
1656
cupsDNSSDServiceDelete(
1657
    cups_dnssd_service_t *service)  // I - Service
1658
0
{
1659
0
  DEBUG_printf("cupsDNSSDServiceDelete(service=%p)", (void *)service);
1660
1661
0
  if (service)
1662
0
  {
1663
0
    cups_dnssd_t *dnssd = service->dnssd;
1664
1665
0
    DEBUG_puts("2cupsDNSSDServiceDelete: Write locking rwlock.");
1666
0
    cupsRWLockWrite(&dnssd->rwlock);
1667
1668
0
    cupsArrayRemove(dnssd->services, service);
1669
1670
0
    DEBUG_puts("2cupsDNSSDServiceDelete: Unlocking rwlock.");
1671
0
    cupsRWUnlock(&dnssd->rwlock);
1672
0
  }
1673
0
}
1674
1675
1676
//
1677
// 'cupsDNSSDServiceGetContext()' - Get the DNS-SD context for the service
1678
//                                  registration.
1679
//
1680
1681
cups_dnssd_t *        // O - DNS-SD context or `NULL`
1682
cupsDNSSDServiceGetContext(
1683
    cups_dnssd_service_t *service)  // I - Service registration
1684
0
{
1685
0
  return (service ? service->dnssd : NULL);
1686
0
}
1687
1688
1689
//
1690
// 'cupsDNSSDServiceGetName()' - Get the service instance name for the service registration.
1691
//
1692
1693
const char *        // O - Service instance name
1694
cupsDNSSDServiceGetName(
1695
    cups_dnssd_service_t *service)  // I - Service registration
1696
0
{
1697
0
  return (service ? service->name : NULL);
1698
0
}
1699
1700
1701
//
1702
// 'cupsDNSSDServiceNew()' - Create a new named service.
1703
//
1704
// This function creates a new DNS-SD service registration for the given service
1705
// instance name and interface.  Specific services using the name are added
1706
// using the @link cupsDNSSDServiceAdd@ function.
1707
//
1708
// The required service callback is called for select events, with the "flags"
1709
// argument set to `CUPS_DNSSD_FLAGS_NONE` for a successful registration,
1710
// `CUPS_DNSSD_FLAGS_COLLISION` when there is a name collision, or
1711
// `CUPS_DNSSD_FLAGS_ERROR` when there is a problem completing the service
1712
// registration.
1713
//
1714
1715
cups_dnssd_service_t *      // O - Service or `NULL` on error
1716
cupsDNSSDServiceNew(
1717
    cups_dnssd_t            *dnssd, // I - DNS-SD context
1718
    uint32_t                if_index, // I - Interface index, `CUPS_DNSSD_IF_INDEX_ANY`, or `CUPS_DNSSD_IF_INDEX_LOCAL`
1719
    const char              *name,  // I - Name of service
1720
    cups_dnssd_service_cb_t cb,   // I - Service registration callback function
1721
    void                    *cb_data) // I - Service registration callback data
1722
0
{
1723
0
  cups_dnssd_service_t  *service; // Service registration
1724
1725
1726
0
  DEBUG_printf("cupsDNSSDServiceNew(dnssd=%p, if_index=%u, name=\"%s\", cb=%p, cb_data=%p)", (void *)dnssd, (unsigned)if_index, name, (void *)cb, cb_data);
1727
1728
  // Range check input...
1729
0
  if (!dnssd || !name || !cb)
1730
0
    return (NULL);
1731
1732
  // Allocate memory for the service...
1733
0
  if ((service = (cups_dnssd_service_t *)calloc(1, sizeof(cups_dnssd_service_t))) == NULL)
1734
0
    return (NULL);
1735
1736
0
  service->dnssd    = dnssd;
1737
0
  service->cb       = cb;
1738
0
  service->cb_data  = cb_data;
1739
0
  service->name     = strdup(name);
1740
0
  service->if_index = if_index;
1741
1742
#ifdef HAVE_MDNSRESPONDER
1743
#elif _WIN32
1744
#else // HAVE_AVAHI
1745
0
  avahi_lock(dnssd, "cupsDNSSDServiceNew");
1746
1747
0
  service->group = avahi_entry_group_new(dnssd->client, (AvahiEntryGroupCallback)avahi_service_cb, service);
1748
1749
0
  avahi_unlock(dnssd, "cupsDNSSDServiceNew");
1750
1751
0
  if (!service->group)
1752
0
  {
1753
0
    report_error(dnssd, "Unable to create DNS-SD service registration: %s", avahi_strerror(avahi_client_errno(dnssd->client)));
1754
0
    free(service->name);
1755
0
    free(service);
1756
0
    service = NULL;
1757
0
    return (NULL);
1758
0
  }
1759
1760
0
  DEBUG_printf("2cupsDNSSDServiceNew: service->group=%p", service->group);
1761
0
#endif // HAVE_MDNSRESPONDER
1762
1763
0
  DEBUG_puts("2cupsDNSSDServiceNew: Write locking rwlock.");
1764
0
  cupsRWLockWrite(&dnssd->rwlock);
1765
1766
0
  if (!dnssd->services)
1767
0
  {
1768
    // Create an array of services...
1769
0
    if ((dnssd->services = cupsArrayNew(NULL, NULL, NULL, 0, NULL, (cups_afree_cb_t)delete_service)) == NULL)
1770
0
    {
1771
      // Unable to create...
1772
0
      free(service->name);
1773
0
      free(service);
1774
0
      service = NULL;
1775
0
      goto done;
1776
0
    }
1777
0
  }
1778
1779
0
  DEBUG_printf("2cupsDNSSDServiceNew: Adding service %p.", (void *)service);
1780
0
  cupsArrayAdd(dnssd->services, service);
1781
1782
0
  done:
1783
1784
0
  DEBUG_puts("2cupsDNSSDServiceNew: Unlocking rwlock.");
1785
0
  cupsRWUnlock(&dnssd->rwlock);
1786
1787
0
  DEBUG_printf("2cupsDNSSDServiceNew: Returning %p.", (void *)service);
1788
0
  return (service);
1789
0
}
1790
1791
1792
//
1793
// 'cupsDNSSDServicePublish()' - Publish a service.
1794
//
1795
// This function publishes the DNS-SD services added using the
1796
// @link cupsDNSSDServiceAdd@ function.
1797
//
1798
1799
bool          // O - `true` on success, `false` on failure
1800
cupsDNSSDServicePublish(
1801
    cups_dnssd_service_t *service)  // I - Service
1802
0
{
1803
0
  bool    ret = true;   // Return value
1804
1805
1806
0
  DEBUG_printf("cupsDNSSDServicePublish(service=%p)", (void *)service);
1807
1808
#if _WIN32
1809
  (void)service;
1810
#elif defined(HAVE_MDNSRESPONDER)
1811
  (void)service;
1812
#else // HAVE_AVAHI
1813
0
  avahi_lock(service->dnssd, "cupsDNSSDServicePublish");
1814
1815
0
  avahi_entry_group_commit(service->group);
1816
1817
0
  avahi_unlock(service->dnssd, "cupsDNSSDServicePublish");
1818
0
#endif // _WIN32
1819
1820
0
  DEBUG_printf("2cupsDNSSDServicePublish: Returning %s.", ret ? "true" : "false");
1821
0
  return (ret);
1822
0
}
1823
1824
1825
//
1826
// 'cupsDNSSDServiceSetLocation()' - Set the geolocation (LOC record) of a
1827
//                                   service.
1828
//
1829
// This function sets the geolocation of a service using a 'geo:' URI (RFC 5870)
1830
// of the form
1831
// 'geo:LATITUDE,LONGITUDE[,ALTITUDE][;crs=CRSLABEL][;u=UNCERTAINTY]'.  The
1832
// specified coordinates and uncertainty are converted into a DNS LOC record
1833
// for the service name label.  Only the "wgs84" CRSLABEL string is supported.
1834
//
1835
// You must call this function prior to @link cupsDNSSDServiceAdd@.
1836
//
1837
1838
bool          // O - `true` on success, `false` on failure
1839
cupsDNSSDServiceSetLocation(
1840
    cups_dnssd_service_t *service,  // I - Service
1841
    const char           *geo_uri)  // I - Geolocation as a 'geo:' URI
1842
0
{
1843
0
  bool    ret = true;   // Return value
1844
0
  const char  *geo_ptr;   // Pointer into 'geo;' URI
1845
0
  double  lat = 0.0, lon = 0.0; // Latitude and longitude in degrees
1846
0
  double  alt = 0.0;    // Altitude in meters
1847
0
  double  u = 5.0;    // Uncertainty in meters
1848
0
  unsigned int  lat_ksec, lon_ksec; // Latitude and longitude in thousandths of arc seconds, biased by 2^31
1849
0
  unsigned int  alt_cm;     // Altitude in centimeters, biased by 10,000,000cm
1850
0
  unsigned char prec;     // Precision value
1851
1852
1853
  // Range check input...
1854
0
  if (!service || !geo_uri)
1855
0
    return (false);
1856
1857
  // See if this is a WGS-84 coordinate...
1858
0
  if ((geo_ptr = strstr(geo_uri, ";crs=")) != NULL && strncmp(geo_ptr + 5, "wgs84", 5))
1859
0
  {
1860
    // Not WGS-84...
1861
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Only WGS-84 coordinates are supported."), true);
1862
0
    return (false);
1863
0
  }
1864
1865
  // Pull apart the "geo:" URI and convert to the integer representation for
1866
  // the LOC record...
1867
0
  sscanf(geo_uri, "geo:%lf,%lf,%lf", &lat, &lon, &alt);
1868
0
  lat_ksec = (unsigned)((int)(lat * 3600000.0) + 0x40000000 + 0x40000000);
1869
0
  lon_ksec = (unsigned)((int)(lon * 3600000.0) + 0x40000000 + 0x40000000);
1870
0
  alt_cm   = (unsigned)((int)(alt * 100.0) + 10000000);
1871
1872
0
  if ((geo_ptr = strstr(geo_uri, ";u=")) != NULL)
1873
0
    u = strtod(geo_ptr + 3, NULL);
1874
1875
0
  if (u < 0.0)
1876
0
    u = 0.0;
1877
1878
0
  for (prec = 0, u = u * 100.0; u >= 10.0 && prec < 15; u = u * 0.01)
1879
0
    prec ++;
1880
1881
0
  if (u < 10.0)
1882
0
    prec |= (unsigned char)((int)u << 4);
1883
0
  else
1884
0
    prec |= (unsigned char)0x90;
1885
1886
  // Build the LOC record...
1887
0
  service->loc[0]  = 0x00;    // Version
1888
0
  service->loc[1]  = 0x51;    // Size (50cm)
1889
0
  service->loc[2]  = prec;    // Horizontal precision
1890
0
  service->loc[3]  = prec;    // Vertical precision
1891
1892
0
  service->loc[4]  = (unsigned char)(lat_ksec >> 24);
1893
          // Latitude (32-bit big-endian)
1894
0
  service->loc[5]  = (unsigned char)(lat_ksec >> 16);
1895
0
  service->loc[6]  = (unsigned char)(lat_ksec >> 8);
1896
0
  service->loc[7]  = (unsigned char)(lat_ksec);
1897
1898
0
  service->loc[8]  = (unsigned char)(lon_ksec >> 24);
1899
          // Latitude (32-bit big-endian)
1900
0
  service->loc[9]  = (unsigned char)(lon_ksec >> 16);
1901
0
  service->loc[10] = (unsigned char)(lon_ksec >> 8);
1902
0
  service->loc[11] = (unsigned char)(lon_ksec);
1903
1904
0
  service->loc[12] = (unsigned char)(alt_cm >> 24);
1905
          // Altitude (32-bit big-endian)
1906
0
  service->loc[13] = (unsigned char)(alt_cm >> 16);
1907
0
  service->loc[14] = (unsigned char)(alt_cm >> 8);
1908
0
  service->loc[15] = (unsigned char)(alt_cm);
1909
1910
0
  service->loc_set = true;
1911
1912
#ifdef HAVE_MDNSRESPONDER
1913
  // Add LOC record in cupsDNSSDServiceAdd()
1914
1915
#elif _WIN32
1916
  // Add LOC record in cupsDNSSDServiceAdd()
1917
1918
#else // HAVE_AVAHI
1919
  // Add LOC record now...
1920
0
  int error;        // Error code
1921
1922
0
  avahi_lock(service->dnssd, "cupsDNSSDServiceSetLocation");
1923
1924
0
  error = avahi_entry_group_add_record(service->group, avahi_if_index(service->if_index), AVAHI_PROTO_UNSPEC, /*flags*/0, service->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_LOC, /*ttl*/75 * 60, service->loc, sizeof(service->loc));
1925
1926
0
  avahi_unlock(service->dnssd, "cupsDNSSDServiceSetLocation");
1927
1928
0
  if (error < 0)
1929
0
  {
1930
0
    report_error(service->dnssd, "Unable to register LOC record for '%s': %s", service->name, avahi_strerror(error));
1931
0
    ret = false;
1932
0
  }
1933
0
#endif // HAVE_MDNSRESPONDER
1934
1935
0
  return (ret);
1936
0
}
1937
1938
1939
//
1940
// 'delete_browse()' - Delete a browse request.
1941
//
1942
1943
static void
1944
delete_browse(
1945
    cups_dnssd_browse_t *browse)  // I - Browse request
1946
0
{
1947
#ifdef HAVE_MDNSRESPONDER
1948
  DNSServiceRefDeallocate(browse->ref);
1949
1950
#elif _WIN32
1951
  size_t  i;      // Looping var
1952
1953
  for (i = 0; i < browse->num_browsers; i ++)
1954
    DnsServiceBrowseCancel(&browse->browsers[i].cancel);
1955
1956
#else // HAVE_AVAHI
1957
0
  size_t  i;      // Looping var
1958
1959
0
  avahi_lock(browse->dnssd, "delete_browse");
1960
1961
0
  for (i = 0; i < browse->num_browsers; i ++)
1962
0
    avahi_service_browser_free(browse->browsers[i]);
1963
1964
0
  avahi_unlock(browse->dnssd, "delete_browse");
1965
0
#endif // HAVE_MDNSRESPONDER
1966
1967
0
  free(browse);
1968
0
}
1969
1970
1971
//
1972
// 'delete_query()' - Delete a query request.
1973
//
1974
1975
static void
1976
delete_query(
1977
    cups_dnssd_query_t *query)    // I - Query request
1978
0
{
1979
#ifdef HAVE_MDNSRESPONDER
1980
  DNSServiceRefDeallocate(query->ref);
1981
1982
#elif _WIN32
1983
  DnsStopMulticastQuery(&query->handle);
1984
1985
#else // HAVE_AVAHI
1986
0
  avahi_lock(query->dnssd, "delete_query");
1987
1988
0
  avahi_record_browser_free(query->browser);
1989
1990
0
  avahi_unlock(query->dnssd, "delete_query");
1991
0
#endif // HAVE_MDNSRESPONDER
1992
0
}
1993
1994
1995
//
1996
// 'delete_resolve()' - Delete a resolve request.
1997
//
1998
1999
static void
2000
delete_resolve(
2001
    cups_dnssd_resolve_t *resolve)  // I - Resolve request
2002
0
{
2003
#ifdef HAVE_MDNSRESPONDER
2004
  DNSServiceRefDeallocate(resolve->ref);
2005
2006
#elif _WIN32
2007
  DnsServiceResolveCancel(&resolve->cancel);
2008
2009
#else // HAVE_AVAHI
2010
0
  avahi_lock(resolve->dnssd, "delete_resolve");
2011
2012
0
  avahi_service_resolver_free(resolve->resolver);
2013
2014
0
  avahi_unlock(resolve->dnssd, "delete_resolve");
2015
0
#endif // HAVE_MDNSRESPONDER
2016
2017
0
}
2018
2019
2020
//
2021
// 'delete_service()' - Delete a service registration.
2022
//
2023
2024
static void
2025
delete_service(
2026
    cups_dnssd_service_t *service)  // I - Service
2027
0
{
2028
0
  free(service->name);
2029
2030
#ifdef HAVE_MDNSRESPONDER
2031
  size_t  i;      // Looping var
2032
2033
  for (i = 0; i < service->num_refs; i ++)
2034
    DNSServiceRefDeallocate(service->refs[i]);
2035
2036
#elif _WIN32
2037
  size_t  i;      // Looping var
2038
2039
  for (i = 0; i < service->num_srvs; i ++)
2040
  {
2041
    DnsServiceRegisterCancel(&service->srvs[i].cancel);
2042
    DnsServiceFreeInstance(service->srvs[i].req.pServiceInstance);
2043
    free(service->srvs[i].txt);
2044
  }
2045
2046
#else // HAVE_AVAHI
2047
0
  avahi_lock(service->dnssd, "delete_service");
2048
2049
0
  avahi_entry_group_free(service->group);
2050
2051
0
  avahi_unlock(service->dnssd, "delete_service");
2052
0
#endif // HAVE_MDNSRESPONDER
2053
2054
0
  free(service);
2055
0
}
2056
2057
2058
//
2059
// 'report_error()' - Report an error.
2060
//
2061
2062
static void
2063
report_error(cups_dnssd_t *dnssd, // I - DNS-SD context
2064
             const char   *message, // I - printf-style message string
2065
             ...)     // I - Additional arguments as needed
2066
0
{
2067
0
  va_list ap;     // Pointer to arguments
2068
0
  char    buffer[8192];   // Formatted message
2069
2070
2071
  // Format the message...
2072
0
  va_start(ap, message);
2073
0
  vsnprintf(buffer, sizeof(buffer), message, ap);
2074
0
  va_end(ap);
2075
2076
0
  DEBUG_printf("cupsDNSSD:report_error: %s", buffer);
2077
2078
  // Send it...
2079
0
  if (dnssd->cb)
2080
0
    (dnssd->cb)(dnssd->cb_data, buffer);
2081
0
  else
2082
0
    fprintf(stderr, "%s\n", buffer);
2083
0
}
2084
2085
2086
#ifdef HAVE_MDNSRESPONDER
2087
//
2088
// 'mdns_browse_cb()' - Handle DNS-SD browse callbacks from mDNSResponder.
2089
//
2090
2091
static void
2092
mdns_browse_cb(
2093
    DNSServiceRef       ref,    // I - Service reference
2094
    DNSServiceFlags     flags,    // I - Browse flags
2095
    uint32_t            if_index, // I - Interface index
2096
    DNSServiceErrorType error,    // I - Error code, if any
2097
    const char          *name,    // I - Service name
2098
    const char          *regtype, // I - Registration type
2099
    const char          *domain,  // I - Domain
2100
    cups_dnssd_browse_t *browse)  // I - Browse request
2101
{
2102
  char  temp[256],      // Temporary string
2103
  *tempptr;     // Pointer into temporary string
2104
2105
2106
  (void)ref;
2107
2108
  if (error != kDNSServiceErr_NoError)
2109
    report_error(browse->dnssd, "DNS-SD browse error: %s", mdns_strerror(error));
2110
2111
  // Strip trailing dot from registration/service type...
2112
  cupsCopyString(temp, regtype, sizeof(temp));
2113
  if ((tempptr = temp + strlen(temp) - 1) >= temp && *tempptr == '.')
2114
    *tempptr = '\0';
2115
2116
  // Strip leading dot from domain...
2117
  if (domain && *domain == '.')
2118
    domain ++;        // Eliminate leading period
2119
2120
  // Call the browse callback...
2121
  (browse->cb)(browse, browse->cb_data, mdns_to_cups(flags, error), if_index, name, temp, domain);
2122
}
2123
2124
2125
//
2126
// 'mdns_hostname_cb()' - Track changes to the mDNS hostname...
2127
//
2128
2129
static void DNSSD_API
2130
mdns_hostname_cb(
2131
    DNSServiceRef       ref,    // I - Service reference (unsued)
2132
    DNSServiceFlags     flags,    // I - Flags (unused)
2133
    uint32_t            if_index, // I - Interface index (unused)
2134
    DNSServiceErrorType error,    // I - Error code, if any
2135
    const char          *fullname,  // I - Search name (unused)
2136
    uint16_t            rrtype,   // I - Record type (unused)
2137
    uint16_t            rrclass,  // I - Record class (unused)
2138
    uint16_t            rdlen,    // I - Record data length
2139
    const void          *rdata,   // I - Record data
2140
    uint32_t            ttl,    // I - Time-to-live (unused)
2141
    cups_dnssd_t        *dnssd)   // I - DNS-SD context
2142
{
2143
  uint8_t *rdataptr,    // Pointer into record data
2144
    lablen;     // Length of current label
2145
  char    temp[1024],   // Temporary hostname string
2146
    *tempptr;   // Pointer into temporary string
2147
2148
2149
  (void)ref;
2150
  (void)flags;
2151
  (void)if_index;
2152
  (void)fullname;
2153
  (void)rrtype;
2154
  (void)rrclass;
2155
  (void)ttl;
2156
2157
  // Check for errors...
2158
  if (error != kDNSServiceErr_NoError)
2159
    return;
2160
2161
  // Copy the hostname from the PTR record...
2162
  for (rdataptr = (uint8_t *)rdata, tempptr = temp; rdlen > 0 && tempptr < (temp + sizeof(temp) - 2); rdlen -= lablen, rdataptr += lablen)
2163
  {
2164
    lablen = *rdataptr++;
2165
    rdlen --;
2166
2167
    if (!rdlen || rdlen < lablen)
2168
      break;
2169
2170
    if (tempptr > temp)
2171
      *tempptr++ = '.';
2172
2173
    if (lablen < (sizeof(temp) - (size_t)(tempptr - temp)))
2174
    {
2175
      memcpy(tempptr, rdataptr, lablen);
2176
      tempptr += lablen;
2177
    }
2178
  }
2179
2180
  *tempptr = '\0';
2181
2182
  // Ignore localhost...
2183
  if (!strcmp(temp, "localhost"))
2184
    return;
2185
2186
  // Look for changes to the hostname...
2187
  DEBUG_puts("4mdns_hostname_cb: Write locking rwlock.");
2188
  cupsRWLockWrite(&dnssd->rwlock);
2189
  if (strcmp(temp, dnssd->hostname))
2190
  {
2191
    cups_dnssd_service_t *service;  // Current service
2192
2193
    // Copy the new hostname...
2194
    cupsCopyString(dnssd->hostname, temp, sizeof(dnssd->hostname));
2195
    dnssd->config_changes ++;
2196
2197
    // Notify services of the change...
2198
    for (service = (cups_dnssd_service_t *)cupsArrayGetFirst(dnssd->services); service; service = (cups_dnssd_service_t *)cupsArrayGetNext(dnssd->services))
2199
      (service->cb)(service, service->cb_data, CUPS_DNSSD_FLAGS_HOST_CHANGE);
2200
  }
2201
  DEBUG_puts("4mdns_hostname_cb: Unlocking rwlock.");
2202
  cupsRWUnlock(&dnssd->rwlock);
2203
}
2204
2205
2206
//
2207
// 'mdns_monitor()' - Monitor DNS-SD messages from mDNSResponder.
2208
//
2209
2210
static void *       // O - Return value (always `NULL`)
2211
mdns_monitor(cups_dnssd_t *dnssd) // I - DNS-SD context
2212
{
2213
  DNSServiceErrorType error;    // Current error
2214
  struct pollfd   polldata; // Polling data
2215
2216
  polldata.fd     = DNSServiceRefSockFD(dnssd->ref);
2217
  polldata.events = POLLERR | POLLHUP | POLLIN;
2218
2219
  for (;;)
2220
  {
2221
#  ifndef _WIN32
2222
    if (poll(&polldata, 1, 1000) < 0 && errno != EINTR && errno != EAGAIN)
2223
      break;
2224
2225
    if (!(polldata.revents & POLLIN))
2226
      continue;
2227
#  endif // !_WIN32
2228
2229
    if ((error = DNSServiceProcessResult(dnssd->ref)) != kDNSServiceErr_NoError)
2230
    {
2231
      report_error(dnssd, "Unable to read response from DNS-SD service: %s", mdns_strerror(error));
2232
      break;
2233
    }
2234
  }
2235
2236
  return (NULL);
2237
}
2238
2239
2240
//
2241
// 'mdns_query_cb()' - Handle DNS-SD query callbacks from mDNSResponder.
2242
//
2243
2244
static void
2245
mdns_query_cb(
2246
    DNSServiceRef       ref,    // I - Service reference
2247
    DNSServiceFlags     flags,    // I - Query flags
2248
    uint32_t            if_index, // I - Interface index
2249
    DNSServiceErrorType error,    // I - Error code, if any
2250
    const char          *name,    // I - Service name
2251
    uint16_t            rrtype,   // I - Record type
2252
    uint16_t            rrclass,  // I - Record class
2253
    uint16_t            rdlen,    // I - Response length
2254
    const void          *rdata,   // I - Response data
2255
    uint32_t            ttl,    // I - Time-to-live value
2256
    cups_dnssd_query_t  *query)   // I - Query request
2257
{
2258
  (void)ref;
2259
  (void)rrclass;
2260
  (void)ttl;
2261
2262
  if (error != kDNSServiceErr_NoError)
2263
    report_error(query->dnssd, "DNS-SD query error: %s", mdns_strerror(error));
2264
2265
  (query->cb)(query, query->cb_data, mdns_to_cups(flags, error), if_index, name, rrtype, rdata, rdlen);
2266
}
2267
2268
2269
//
2270
// 'mdns_resolve_cb()' - Handle DNS-SD resolution callbacks from mDNSResponder.
2271
//
2272
2273
static void
2274
mdns_resolve_cb(
2275
    DNSServiceRef        ref,   // I - Service reference
2276
    DNSServiceFlags      flags,   // I - Registration flags
2277
    uint32_t             if_index,  // I - Interface index
2278
    DNSServiceErrorType  error,   // I - Error code, if any
2279
    const char           *fullname, // I - Full name of service
2280
    const char           *host,   // I - Hostname of service
2281
    uint16_t             port,    // I - Port number in network byte order
2282
    uint16_t             txtlen,  // I - Length of TXT record
2283
    const unsigned char  *txtrec, // I - TXT record
2284
    cups_dnssd_resolve_t *resolve)  // I - Resolve request
2285
{
2286
  size_t  num_txt;    // Number of TXT key/value pairs
2287
  cups_option_t *txt;     // TXT key/value pairs
2288
2289
2290
  (void)ref;
2291
2292
  if (error != kDNSServiceErr_NoError)
2293
    report_error(resolve->dnssd, "DNS-SD resolve error: %s", mdns_strerror(error));
2294
2295
  num_txt = cupsDNSSDDecodeTXT(txtrec, txtlen, &txt);
2296
2297
  (resolve->cb)(resolve, resolve->cb_data, mdns_to_cups(flags, error), if_index, fullname, host, ntohs(port), num_txt, txt);
2298
2299
  cupsFreeOptions(num_txt, txt);
2300
}
2301
2302
2303
//
2304
// 'mdns_service_cb()' - Handle DNS-SD service registration callbacks from
2305
//                       mDNSResponder.
2306
//
2307
2308
static void
2309
mdns_service_cb(
2310
    DNSServiceRef        ref,   // I - Service reference
2311
    DNSServiceFlags      flags,   // I - Registration flags
2312
    DNSServiceErrorType  error,   // I - Error code, if any
2313
    const char           *name,   // I - Service name
2314
    const char           *regtype,  // I - Registration type
2315
    const char           *domain, // I - Domain
2316
    cups_dnssd_service_t *service)  // I - Service registration
2317
{
2318
  (void)ref;
2319
  (void)name;
2320
  (void)regtype;
2321
  (void)domain;
2322
2323
  if (error != kDNSServiceErr_NoError)
2324
    report_error(service->dnssd, "DNS-SD service registration error: %s", mdns_strerror(error));
2325
2326
  (service->cb)(service, service->cb_data, mdns_to_cups(flags, error));
2327
}
2328
2329
2330
//
2331
// 'mdns_strerror()' - Convert an error code to a string.
2332
//
2333
2334
static const char *     // O - Error message
2335
mdns_strerror(
2336
    DNSServiceErrorType errorCode)  // I - Error code
2337
{
2338
  switch (errorCode)
2339
  {
2340
    case kDNSServiceErr_NoError :
2341
        return ("No error");
2342
2343
    case kDNSServiceErr_Unknown :
2344
    default :
2345
        return ("Unknown error");
2346
2347
    case kDNSServiceErr_NoSuchName :
2348
        return ("Name not found");
2349
2350
    case kDNSServiceErr_NoMemory :
2351
        return ("Out of memory");
2352
2353
    case kDNSServiceErr_BadParam :
2354
        return ("Bad argument");
2355
2356
    case kDNSServiceErr_BadReference :
2357
        return ("Bad service reference");
2358
2359
    case kDNSServiceErr_BadState :
2360
        return ("Bad state");
2361
2362
    case kDNSServiceErr_BadFlags :
2363
        return ("Bad flags argument");
2364
2365
    case kDNSServiceErr_Unsupported :
2366
        return ("Unsupported feature");
2367
2368
    case kDNSServiceErr_NotInitialized :
2369
        return ("Not initialized");
2370
2371
    case kDNSServiceErr_AlreadyRegistered :
2372
        return ("Name already registered");
2373
2374
    case kDNSServiceErr_NameConflict :
2375
        return ("Name conflicts");
2376
2377
    case kDNSServiceErr_Invalid :
2378
        return ("Invalid argument");
2379
2380
    case kDNSServiceErr_Firewall :
2381
        return ("Firewall prevents access");
2382
2383
    case kDNSServiceErr_Incompatible :
2384
        return ("Client library incompatible with background daemon");
2385
2386
    case kDNSServiceErr_BadInterfaceIndex :
2387
        return ("Bad interface index");
2388
2389
    case kDNSServiceErr_Refused :
2390
        return ("Connection refused");
2391
2392
    case kDNSServiceErr_NoSuchRecord :
2393
        return ("DNS record not found");
2394
2395
    case kDNSServiceErr_NoAuth :
2396
        return ("No authoritative answer");
2397
2398
    case kDNSServiceErr_NoSuchKey :
2399
        return ("TXT record key not found");
2400
2401
    case kDNSServiceErr_NATTraversal :
2402
        return ("Unable to traverse via NAT");
2403
2404
    case kDNSServiceErr_DoubleNAT :
2405
        return ("Double NAT is in use");
2406
2407
    case kDNSServiceErr_BadTime :
2408
        return ("Bad time value");
2409
2410
    case kDNSServiceErr_BadSig :
2411
        return ("Bad signal");
2412
2413
    case kDNSServiceErr_BadKey :
2414
        return ("Bad TXT record key");
2415
2416
    case kDNSServiceErr_Transient :
2417
        return ("Transient error");
2418
2419
    case kDNSServiceErr_ServiceNotRunning :
2420
        return ("Background daemon not running");
2421
2422
    case kDNSServiceErr_NATPortMappingUnsupported :
2423
        return ("NAT doesn't support PCP, NAT-PMP or UPnP");
2424
2425
    case kDNSServiceErr_NATPortMappingDisabled :
2426
        return ("NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator");
2427
2428
    case kDNSServiceErr_NoRouter :
2429
        return ("No router configured, probably no network connectivity");
2430
2431
    case kDNSServiceErr_PollingMode :
2432
        return ("Polling error");
2433
2434
    case kDNSServiceErr_Timeout :
2435
        return ("Timeout");
2436
2437
#if !_WIN32 // Bonjour SDK for Windows doesn't define this...
2438
    case kDNSServiceErr_DefunctConnection :
2439
        return ("Connection lost");
2440
#endif // !_WIN32
2441
  }
2442
}
2443
2444
2445
//
2446
// 'mdns_to_cups()' - Convert mDNSResponder flags to CUPS DNS-SD flags...
2447
//
2448
2449
static cups_dnssd_flags_t   // O - CUPS DNS-SD flags
2450
mdns_to_cups(
2451
    DNSServiceFlags     flags,    // I - mDNSResponder flags
2452
    DNSServiceErrorType error)    // I - mDNSResponder error code
2453
{
2454
  cups_dnssd_flags_t  cups_flags = CUPS_DNSSD_FLAGS_NONE;
2455
          // CUPS DNS-SD flags
2456
2457
2458
  if (flags & kDNSServiceFlagsAdd)
2459
    cups_flags |= CUPS_DNSSD_FLAGS_ADD;
2460
  if (flags & kDNSServiceFlagsMoreComing)
2461
    cups_flags |= CUPS_DNSSD_FLAGS_MORE;
2462
  if (error != kDNSServiceErr_NoError)
2463
    cups_flags |= CUPS_DNSSD_FLAGS_ERROR;
2464
2465
  return (cups_flags);
2466
}
2467
2468
2469
#elif _WIN32
2470
//
2471
// 'win32_browse_cb()' - Handle browse callbacks from WinDNS.
2472
//
2473
2474
static void
2475
win32_browse_cb(
2476
    DWORD       status,     // I - Status
2477
    PVOID       context,      // I - Browser
2478
    PDNS_RECORD records)      // I - Record list
2479
{
2480
  cups_dnssd_browse_t *browse = (cups_dnssd_browse_t *)context;
2481
  PDNS_RECORD record;     // Current DNS record
2482
  char        fullname[256],    // Full service instance name
2483
        name[256] = "",   // Service name
2484
        type[256] = "",   // Service type
2485
        domain[256] = "";   // Domain name
2486
2487
2488
  for (record = records; record; record = record->pNext)
2489
  {
2490
    if (record->wType == DNS_TYPE_PTR)
2491
    {
2492
      win32_utf8cpy(fullname, record->Data.PTR.pNameHost, sizeof(fullname));
2493
      cupsDNSSDSeparateFullName(fullname, name, sizeof(name), type, sizeof(type), domain, sizeof(domain));
2494
      break;
2495
    }
2496
  }
2497
2498
  (browse->cb)(browse, browse->cb_data, status == ERROR_SUCCESS ? CUPS_DNSSD_FLAGS_ADD : CUPS_DNSSD_FLAGS_ERROR, /*if_index*/0, name, type, domain);
2499
2500
  DnsRecordListFree(records, DnsFreeRecordList);
2501
}
2502
2503
2504
//
2505
// 'win32_query_cb()' - Handle query callbacks from WinDNS.
2506
//
2507
2508
static void
2509
win32_query_cb(
2510
    PVOID              context,   // I - Pointer to query
2511
    PMDNS_QUERY_HANDLE handle,    // I - Query handle
2512
    PDNS_QUERY_RESULT  result)    // I - Query result
2513
{
2514
  cups_dnssd_query_t  *query = (cups_dnssd_query_t *)context;
2515
          // Query
2516
  char          fullname[256];  // Full instance name of query
2517
2518
2519
  win32_utf8cpy(fullname, query->fullname, sizeof(fullname));
2520
2521
  if (result && result->pQueryRecords)
2522
  {
2523
    PDNS_RECORD record;     // Current DNS record
2524
2525
    for (record = result->pQueryRecords; record; record = record->pNext)
2526
      (query->cb)(query, query->cb_data, CUPS_DNSSD_FLAGS_ADD, /*if_index*/0, fullname, record->wType, &record->Data, record->wDataLength);
2527
2528
    DnsRecordListFree(result->pQueryRecords, DnsFreeRecordList);
2529
  }
2530
}
2531
2532
2533
//
2534
// 'win32_resolve_cb()' - Handle resolve callbacks from WinDNS.
2535
//
2536
2537
static void
2538
win32_resolve_cb(
2539
    DWORD                 status, // I - Status
2540
    PVOID                 context,  // I - Resolver
2541
    PDNS_SERVICE_INSTANCE instance) // I - Service instance
2542
{
2543
  cups_dnssd_resolve_t  *resolve = (cups_dnssd_resolve_t *)context;
2544
          // Resolver
2545
2546
2547
  if (status == ERROR_SUCCESS)
2548
  {
2549
    char      fullname[256],  // Full instance name
2550
            hostname[256],  // Hostname
2551
      txtname[256], // TXT name
2552
      txtvalue[256];  // TXT value
2553
    DWORD   i;    // Looping var
2554
    size_t    num_txt = 0;  // Number of TXT values
2555
    cups_option_t *txt = NULL;  // TXT values
2556
2557
    win32_utf8cpy(fullname, instance->pszInstanceName, sizeof(fullname));
2558
    win32_utf8cpy(hostname, instance->pszHostName, sizeof(hostname));
2559
2560
    for (i = 0; i < instance->dwPropertyCount; i ++)
2561
    {
2562
      win32_utf8cpy(txtname, instance->keys[i], sizeof(txtname));
2563
      win32_utf8cpy(txtvalue, instance->values[i], sizeof(txtvalue));
2564
2565
      num_txt = cupsAddOption(txtname, txtvalue, num_txt, &txt);
2566
    }
2567
2568
    (resolve->cb)(resolve, resolve->cb_data, CUPS_DNSSD_FLAGS_ADD, instance->dwInterfaceIndex, fullname, hostname, instance->wPort, num_txt, txt);
2569
2570
    cupsFreeOptions(num_txt, txt);
2571
  }
2572
  else
2573
  {
2574
    (resolve->cb)(resolve, resolve->cb_data, CUPS_DNSSD_FLAGS_ERROR, /*if_index*/0, /*fullname*/NULL, /*host*/NULL, /*port*/0, /*num_txt*/0, /*txt*/NULL);
2575
  }
2576
}
2577
2578
2579
//
2580
// 'win32_service_cb()' - Handle service registration callbacks from WinDNS.
2581
//
2582
2583
static void
2584
win32_service_cb(
2585
    DWORD                 status, // I - Status
2586
    PVOID                 context,  // I - Service
2587
    PDNS_SERVICE_INSTANCE instance) // I - New instance
2588
{
2589
  cups_dnssd_service_t *service = (cups_dnssd_service_t *)context;
2590
          // Service
2591
2592
  (service->cb)(service, service->cb_data, status == ERROR_SUCCESS ? CUPS_DNSSD_FLAGS_ADD : CUPS_DNSSD_FLAGS_ERROR);
2593
2594
  if (instance)
2595
    DnsServiceFreeInstance(instance);
2596
}
2597
2598
2599
//
2600
// 'win32_utf8cpy()' - Copy a UTF-16 string to a UTF-8 string.
2601
//
2602
2603
static void
2604
win32_utf8cpy(char        *dst,   // I - Destination string
2605
              const WCHAR *src,   // I - Source string
2606
        size_t      dstsize)  // I - Size of destination string
2607
{
2608
  int ch;       // Current character
2609
2610
2611
  // Loop until we run out of characters or buffer space...
2612
  while (*src && dstsize > 4)
2613
  {
2614
    // Get the current character...
2615
    ch = *src++;
2616
2617
    if (ch >= 0xd800 && ch <= 0xdbff && *src >= 0xdc00 && *src <= 0xdfff)
2618
    {
2619
      // Convert UTF-16 to unicode...
2620
      ch = ((ch - 0xd800) << 10) | (*src++ - 0xdc00);
2621
    }
2622
2623
    if (ch < 0x80)
2624
    {
2625
      *dst++ = ch;
2626
    }
2627
    else if (ch < 0x800)
2628
    {
2629
      *dst++ = 0xc0 | (ch >> 6);
2630
      *dst++ = 0x80 | (ch & 0x3f);
2631
    }
2632
    else if (ch < 0x10000)
2633
    {
2634
      *dst++ = 0xe0 | (ch >> 12);
2635
      *dst++ = 0x80 | ((ch >> 6) & 0x3f);
2636
      *dst++ = 0x80 | (ch & 0x3f);
2637
    }
2638
    else
2639
    {
2640
      *dst++ = 0xf0 | (ch >> 18);
2641
      *dst++ = 0x80 | ((ch >> 12) & 0x3f);
2642
      *dst++ = 0x80 | ((ch >> 6) & 0x3f);
2643
      *dst++ = 0x80 | (ch & 0x3f);
2644
    }
2645
  }
2646
2647
  // Nul-terminate the destination...
2648
  *dst = '\0';
2649
}
2650
2651
2652
//
2653
// 'win32_wstrcpy()' - Copy a UTF-8 string to a UTF-16 string.
2654
//
2655
2656
static void
2657
win32_wstrcpy(WCHAR      *dst,    // I - Destination string
2658
              const char *src,    // I - Source string
2659
              size_t     dstsize) // I - Size of destination
2660
{
2661
  int ch;       // Current character
2662
2663
2664
  // Adjust size from bytes to words...
2665
  dstsize /= sizeof(WCHAR);
2666
2667
  // Loop until we run out of characters or buffer space...
2668
  while (*src && dstsize > 1)
2669
  {
2670
    // Get the current character...
2671
    if ((*src & 0xe0) == 0xc0)
2672
    {
2673
      // Two-byte UTF-8...
2674
      if ((src[1] & 0xc0) != 0x80)
2675
        break;
2676
2677
      ch = ((src[0] & 0x1f) << 6) | (src[1] & 0x3f);
2678
      src += 2;
2679
    }
2680
    else if ((*src & 0xf0) == 0xe0)
2681
    {
2682
      // Three-byte UTF-8...
2683
      if ((src[1] & 0xc0) != 0x80 || (src[2] & 0xc0) != 0x80)
2684
        break;
2685
2686
      ch = ((src[0] & 0x1f) << 12) | ((src[1] & 0x3f) << 6) | (src[2] & 0x3f);
2687
      src += 3;
2688
    }
2689
    else if ((*src & 0xf8) == 0xf0)
2690
    {
2691
      // Four-byte UTF-8...
2692
      if ((src[1] & 0xc0) != 0x80 || (src[2] & 0xc0) != 0x80 || (src[3] & 0xc0) != 0x80)
2693
        break;
2694
2695
      ch = ((src[0] & 0x1f) << 18) | ((src[1] & 0x3f) << 12) | ((src[2] & 0x3f) << 6) | (src[3] & 0x3f);
2696
      src += 4;
2697
    }
2698
    else
2699
    {
2700
      // US ASCII...
2701
      ch = *src++;
2702
    }
2703
2704
    // Map it to UTF-16...
2705
    if (ch < 0x10000)
2706
    {
2707
      // One-word UTF-16...
2708
      *dst++ = ch;
2709
      dstsize --;
2710
    }
2711
    else if (dstsize > 2)
2712
    {
2713
      // Two-word UTF-16...
2714
      *dst++ = 0xd800 | ((ch >> 12) & 0x3ff);
2715
      *dst++ = 0xdc00 | (ch & 0x3ff);
2716
2717
      dstsize -= 2;
2718
    }
2719
    else
2720
    {
2721
      // Terminate early...
2722
      break;
2723
    }
2724
  }
2725
2726
  // Nul-terminate the destination...
2727
  *dst = '\0';
2728
}
2729
2730
2731
#else // HAVE_AVAHI
2732
#  ifdef DEBUG
2733
static const char * const avahi_events[] =  // Event names
2734
{
2735
  "AVAHI_BROWSER_NEW",
2736
  "AVAHI_BROWSER_REMOVE",
2737
  "AVAHI_BROWSER_CACHE_EXHAUSTED",
2738
  "AVAHI_BROWSER_ALL_FOR_NOW",
2739
  "AVAHI_BROWSER_FAILURE"
2740
};
2741
#  endif // DEBUG
2742
2743
2744
//
2745
// 'avahi_browse_cb()' - Handle browse callbacks from Avahi.
2746
//
2747
2748
static void
2749
avahi_browse_cb(
2750
    AvahiServiceBrowser    *browser,  // I - Avahi browser
2751
    AvahiIfIndex           if_index,  // I - Interface index
2752
    AvahiProtocol          protocol,  // I - Network protocol (unused)
2753
    AvahiBrowserEvent      event, // I - What happened
2754
    const char             *name, // I - Service name
2755
    const char             *type, // I - Service type
2756
    const char             *domain, // I - Domain
2757
    AvahiLookupResultFlags flags, // I - Flags
2758
    cups_dnssd_browse_t    *browse) // I - CUPS browse request
2759
0
{
2760
0
  cups_dnssd_flags_t  cups_flags; // CUPS DNS-SD flags
2761
2762
2763
0
  DEBUG_printf("3avahi_browse_cb(browser=%p, if_index=%u, protocol=%u, event=%d, name=\"%s\", type=\"%s\", domain=\"%s\", flags=%u, browse=%p)", (void *)browser, (unsigned)if_index, (unsigned)protocol, event, name, type, domain, (unsigned)flags, (void *)browse);
2764
2765
0
  (void)protocol;
2766
0
  (void)flags;
2767
2768
0
  if (!name)
2769
0
    return;
2770
2771
0
  switch (event)
2772
0
  {
2773
0
    case AVAHI_BROWSER_NEW :
2774
0
        cups_flags = CUPS_DNSSD_FLAGS_ADD;
2775
0
        break;
2776
0
    case AVAHI_BROWSER_REMOVE :
2777
0
        cups_flags = CUPS_DNSSD_FLAGS_NONE;
2778
0
        break;
2779
0
    case AVAHI_BROWSER_FAILURE :
2780
0
        cups_flags = CUPS_DNSSD_FLAGS_ERROR;
2781
0
        break;
2782
2783
0
    default :
2784
        // Other events don't get passed through...
2785
0
        return;
2786
0
  }
2787
2788
0
  browse->dnssd->in_callback = true;
2789
0
  (browse->cb)(browse, browse->cb_data, cups_flags, (uint32_t)if_index, name, type, domain);
2790
0
  browse->dnssd->in_callback = false;
2791
0
}
2792
2793
2794
//
2795
// 'avahi_client_cb()' - Client callback for Avahi.
2796
//
2797
// Called whenever the client or server state changes...
2798
//
2799
2800
static void
2801
avahi_client_cb(
2802
    AvahiClient      *c,    // I - Client
2803
    AvahiClientState state,   // I - Current state
2804
    cups_dnssd_t     *dnssd)    // I - DNS-SD context
2805
0
{
2806
0
  if (!c)
2807
0
    return;
2808
2809
0
  if (state == AVAHI_CLIENT_FAILURE)
2810
0
  {
2811
0
    if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
2812
0
      report_error(dnssd, "Avahi server crashed.");
2813
0
  }
2814
0
  else if (state == AVAHI_CLIENT_S_RUNNING)
2815
0
  {
2816
    // Let the services know the hostname has changed...
2817
0
    cups_dnssd_service_t *service;  // Current service
2818
2819
0
    DEBUG_puts("4avahi_client_cb: Write locking rwlock.");
2820
0
    cupsRWLockWrite(&dnssd->rwlock);
2821
2822
0
    dnssd->config_changes ++;
2823
2824
0
    for (service = (cups_dnssd_service_t *)cupsArrayGetFirst(dnssd->services); service; service = (cups_dnssd_service_t *)cupsArrayGetNext(dnssd->services))
2825
0
      (service->cb)(service, service->cb_data, CUPS_DNSSD_FLAGS_HOST_CHANGE);
2826
2827
0
    DEBUG_puts("4avahi_client_cb: Unlocking rwlock.");
2828
0
    cupsRWUnlock(&dnssd->rwlock);
2829
0
  }
2830
0
}
2831
2832
2833
//
2834
// 'avahi_domain_cb()' - Domain callback.
2835
//
2836
2837
static void
2838
avahi_domain_cb(
2839
    AvahiDomainBrowser     *b,    // I - Browser (not used)
2840
    AvahiIfIndex           interface, // I - Network interface (not used)
2841
    AvahiProtocol          protocol,  // I - Protocol (not used)
2842
    AvahiBrowserEvent      event, // I - Event
2843
    const char             *domain, // I - Domain name
2844
    AvahiLookupResultFlags flags, // I - Lookup flags (not used)
2845
    cups_dnssd_t           *dnssd)  // I - DNS-SD context
2846
0
{
2847
0
  size_t  i;      // Looping var
2848
2849
2850
0
  (void)b;
2851
0
  (void)interface;
2852
0
  (void)protocol;
2853
0
  (void)flags;
2854
2855
0
  DEBUG_printf("3avahi_domain_cb(..., event=%s, domain=\"%s\", ...)", avahi_events[event], domain);
2856
2857
0
  if (!domain || !strcmp(domain, "."))
2858
0
    return;
2859
2860
0
  DEBUG_puts("4avahi_domain_cb: Write locking rwlock.");
2861
0
  cupsRWLockWrite(&dnssd->rwlock);
2862
2863
0
  if (event == AVAHI_BROWSER_NEW)
2864
0
  {
2865
    // Add a domain - see if the domain is new to us...
2866
0
    for (i = 0; i < dnssd->num_domains; i ++)
2867
0
    {
2868
0
      if (!_cups_strcasecmp(dnssd->domains[i], domain))
2869
0
        break;
2870
0
    }
2871
2872
0
    if (i >= dnssd->num_domains && dnssd->num_domains < (sizeof(dnssd->domains) / sizeof(dnssd->domains[0])))
2873
0
    {
2874
      // New, copy the domain name...
2875
0
      cupsCopyString(dnssd->domains[i], domain, sizeof(dnssd->domains[0]));
2876
0
      dnssd->num_domains ++;
2877
0
      DEBUG_printf("4avahi_domain_cb: Added domain \"%s\", num_domains=%u", domain, (unsigned)dnssd->num_domains);
2878
0
    }
2879
0
  }
2880
0
  else if (event == AVAHI_BROWSER_REMOVE)
2881
0
  {
2882
    // Remove the domain...
2883
0
    for (i = 0; i < dnssd->num_domains; i ++)
2884
0
    {
2885
0
      if (!_cups_strcasecmp(dnssd->domains[i], domain))
2886
0
      {
2887
0
        dnssd->num_domains --;
2888
0
        if (i < dnssd->num_domains)
2889
0
          memmove(dnssd->domains[i], dnssd->domains[i + 1], (dnssd->num_domains - i) * sizeof(dnssd->domains[0]));
2890
2891
0
        DEBUG_printf("4avahi_domain_cb: Removed domain \"%s\", num_domains=%u", domain, (unsigned)dnssd->num_domains);
2892
0
        break;
2893
0
      }
2894
0
    }
2895
0
  }
2896
2897
0
  DEBUG_puts("4avahi_domain_cb: Unlocking rwlock.");
2898
0
  cupsRWUnlock(&dnssd->rwlock);
2899
0
}
2900
2901
2902
//
2903
// 'avahi_if_index()' - Convert the DNS-SD interface index to an Avahi interface index.
2904
//
2905
2906
static AvahiIfIndex     // O - Avahi interface index
2907
avahi_if_index(uint32_t if_index) // I - DNS-SD interface index
2908
0
{
2909
0
  if (if_index == CUPS_DNSSD_IF_INDEX_ANY)
2910
0
    return (AVAHI_IF_UNSPEC);
2911
0
  else if (if_index == CUPS_DNSSD_IF_INDEX_LOCAL)
2912
0
    return (if_nametoindex("lo"));
2913
0
  else
2914
0
    return ((int)if_index);
2915
0
}
2916
2917
2918
//
2919
// 'avahi_lock()' - Lock the access mutex.
2920
//
2921
2922
static void
2923
avahi_lock(cups_dnssd_t *dnssd,   // I - DNS-SD context
2924
           const char   *name)    // I - Who is locking?
2925
0
{
2926
0
  (void)name;
2927
2928
0
  if (!dnssd->in_callback)
2929
0
  {
2930
0
    DEBUG_printf("2avahi_lock: Locking mutex for %s.", name);
2931
0
    cupsMutexLock(&dnssd->mutex);
2932
0
  }
2933
0
}
2934
2935
2936
//
2937
// 'avahi_monitor()' - Background thread for Avahi.
2938
//
2939
2940
static void *       // O - Exit status
2941
avahi_monitor(cups_dnssd_t *dnssd)  // I - DNS-SD context
2942
0
{
2943
0
  DEBUG_printf("3avahi_monitor(dnssd=%p)", (void *)dnssd);
2944
2945
0
  DEBUG_puts("4avahi_monitor: Locking mutex.");
2946
0
  cupsMutexLock(&dnssd->mutex);
2947
2948
0
  DEBUG_puts("4avahi_monitor: Running poll loop.");
2949
0
  avahi_simple_poll_loop(dnssd->poll);
2950
2951
0
  DEBUG_puts("4avahi_monitor: Unlocking mutex.");
2952
0
  cupsMutexUnlock(&dnssd->mutex);
2953
2954
0
  return (NULL);
2955
0
}
2956
2957
2958
//
2959
// 'avahi_poll_cb()' - Poll callback for Avahi event handler...
2960
//
2961
2962
static int        // O - Number of file descriptors or `-1` on error
2963
avahi_poll_cb(struct pollfd *ufds,  // I - File descriptors for poll
2964
              unsigned int  nfds, // I - Number of file descriptors
2965
              int           timeout,  // I - Timeout in milliseconds
2966
              cups_dnssd_t  *dnssd) // I - DNS-SD context
2967
0
{
2968
0
  int ret;        // Return value
2969
2970
2971
0
  DEBUG_printf("3avahi_poll_cb(ufds=%p, nfds=%u, timeout=%d, dnssd=%p)", (void *)ufds, nfds, timeout, (void *)dnssd);
2972
2973
0
  DEBUG_puts("4avahi_poll_cb: Unlocking mutex.");
2974
0
  cupsMutexUnlock(&dnssd->mutex);
2975
2976
0
  DEBUG_puts("4avahi_poll_cb: Polling sockets...");
2977
0
  ret = poll(ufds, nfds, timeout);
2978
0
  DEBUG_printf("4avahi_poll_cb: poll() returned %d...", ret);
2979
2980
0
  DEBUG_puts("4avahi_poll_cb: Locking mutex.");
2981
0
  cupsMutexLock(&dnssd->mutex);
2982
2983
0
  return (ret);
2984
0
}
2985
2986
2987
//
2988
// 'avahi_query_cb()' - Query callback for Avahi.
2989
//
2990
2991
static void
2992
avahi_query_cb(
2993
    AvahiRecordBrowser     *browser,  // I - Browser
2994
    AvahiIfIndex           if_index,  // I - Interface index
2995
    AvahiProtocol          protocol,  // I - Network protocol (not used)
2996
    AvahiBrowserEvent      event, // I - What happened
2997
    const char             *fullname, // I - Full service name
2998
    uint16_t               rrclass, // I - Record class (not used)
2999
    uint16_t               rrtype,  // I - Record type
3000
    const void             *rdata,  // I - Record data
3001
    size_t                 rdlen, // I - Size of record data
3002
    AvahiLookupResultFlags flags, // I - Flags
3003
    cups_dnssd_query_t     *query)  // I - Query request
3004
0
{
3005
0
  (void)browser;
3006
0
  (void)protocol;
3007
0
  (void)rrclass;
3008
3009
0
  DEBUG_printf("3avahi_query_cb(..., event=%s, fullname=\"%s\", ..., query=%p)", avahi_events[event], fullname, query);
3010
3011
0
  query->dnssd->in_callback = true;
3012
0
  (query->cb)(query, query->cb_data, event == AVAHI_BROWSER_FAILURE ? CUPS_DNSSD_FLAGS_ERROR : event == AVAHI_BROWSER_NEW ? CUPS_DNSSD_FLAGS_ADD : CUPS_DNSSD_FLAGS_NONE, (uint32_t)if_index, fullname, rrtype, rdata, rdlen);
3013
0
  query->dnssd->in_callback = false;
3014
0
}
3015
3016
3017
//
3018
// 'avahi_resolve_cb()' - Resolver callback for Avahi.
3019
//
3020
3021
static void
3022
avahi_resolve_cb(
3023
    AvahiServiceResolver   *resolver, // I - Service resolver
3024
    AvahiIfIndex           if_index,  // I - Interface index
3025
    AvahiProtocol          protocol,  // I - Network protocol (not used)
3026
    AvahiResolverEvent     event, // I - What happened
3027
    const char             *name, // I - Service name
3028
    const char             *type, // I - Service type
3029
    const char             *domain, // I - Domain
3030
    const char             *host, // I - Host name
3031
    const AvahiAddress     *address,  // I - Address
3032
    uint16_t               port,  // I - Port number
3033
    AvahiStringList        *txtrec, // I - TXT record
3034
    AvahiLookupResultFlags flags, // I - Flags
3035
    cups_dnssd_resolve_t   *resolve)  // I - Resolve request
3036
0
{
3037
0
  AvahiStringList *txtpair;   // Current pair
3038
0
  size_t  num_txt = 0;    // Number of TXT key/value pairs
3039
0
  cups_option_t *txt = NULL;   // TXT key/value pairs
3040
0
  char    fullname[1024];   // Full service name
3041
3042
3043
0
  DEBUG_printf("3avahi_resolve_cb(resolver=%p, if_index=%d, protocol=%d, event=%s, name=\"%s\", type=\"%s\", domain=\"%s\", host=\"%s\", address=%p, port=%u, txtrec=%p, flags=%u, resolve=%p)", (void *)resolver, if_index, protocol, avahi_events[event], name, type, domain, host, (void *)address, (unsigned)port, (void *)txtrec, (unsigned)flags, (void *)resolve);
3044
3045
0
  if (!resolver)
3046
0
    return;
3047
3048
0
  (void)protocol;
3049
0
  (void)flags;
3050
3051
  // Map the addresses "127.0.0.1" (IPv4) and "::1" (IPv6) to "localhost" to work around a well-known Avahi registration bug for local-only services (Issue #970)
3052
0
  if (address && address->proto == AVAHI_PROTO_INET && address->data.ipv4.address == htonl(0x7f000001))
3053
0
  {
3054
0
    DEBUG_puts("4avahi_resolve_cb: Mapping 127.0.0.1 to localhost.");
3055
0
    host = "localhost";
3056
0
  }
3057
0
  else if (address && address->proto == AVAHI_PROTO_INET6 && address->data.ipv6.address[0] == 0 && address->data.ipv6.address[1] == 0 && address->data.ipv6.address[2] == 0 && address->data.ipv6.address[3] == 0 && address->data.ipv6.address[4] == 0 && address->data.ipv6.address[5] == 0 && address->data.ipv6.address[6] == 0 && address->data.ipv6.address[7] == 0 && address->data.ipv6.address[8] == 0 && address->data.ipv6.address[9] == 0 && address->data.ipv6.address[10] == 0 && address->data.ipv6.address[11] == 0 && address->data.ipv6.address[12] == 0 && address->data.ipv6.address[13] == 0 && address->data.ipv6.address[14] == 0 && address->data.ipv6.address[15] == 1)
3058
0
  {
3059
0
    DEBUG_puts("4avahi_resolve_cb: Mapping ::1 to localhost.");
3060
0
    host = "localhost";
3061
0
  }
3062
3063
  // Convert TXT key/value pairs into CUPS option array...
3064
0
  for (txtpair = txtrec; txtpair; txtpair = avahi_string_list_get_next(txtpair))
3065
0
  {
3066
0
    char *key, *value;      // Key and value
3067
3068
0
    avahi_string_list_get_pair(txtpair, &key, &value, NULL);
3069
3070
0
    DEBUG_printf("4avahi_resolve_cb: txt[%u].name=\"%s\", .value=\"%s\"", (unsigned)num_txt, key, value);
3071
0
    num_txt = cupsAddOption(key, value, num_txt, &txt);
3072
3073
0
    avahi_free(key);
3074
0
    avahi_free(value);
3075
0
  }
3076
0
  DEBUG_printf("4avahi_resolve_cb: num_txt=%u", (unsigned)num_txt);
3077
3078
  // Create a full name for the service...
3079
0
  cupsDNSSDAssembleFullName(fullname, sizeof(fullname), name, type, domain);
3080
0
  DEBUG_printf("4avahi_resolve_cb: fullname=\"%s\"", fullname);
3081
3082
  // Do the resolve callback and free the TXT record stuff...
3083
0
  (resolve->cb)(resolve, resolve->cb_data, event == AVAHI_RESOLVER_FAILURE ? CUPS_DNSSD_FLAGS_ERROR : CUPS_DNSSD_FLAGS_NONE, (uint32_t)if_index, fullname, host, port, num_txt, txt);
3084
3085
0
  cupsFreeOptions(num_txt, txt);
3086
0
}
3087
3088
3089
//
3090
// 'avahi_service_cb()' - Service callback for Avahi.
3091
//
3092
3093
static void
3094
avahi_service_cb(
3095
    AvahiEntryGroup      *srv,    // I - Service
3096
    AvahiEntryGroupState state,   // I - Registration state
3097
    cups_dnssd_service_t *service)  // I - Service registration
3098
0
{
3099
#  ifdef DEBUG
3100
  static const char * const avahi_states[] =
3101
  {         // AvahiEntryGroupState strings
3102
    "AVAHI_ENTRY_GROUP_UNCOMMITED",
3103
    "AVAHI_ENTRY_GROUP_REGISTERING",
3104
    "AVAHI_ENTRY_GROUP_ESTABLISHED",
3105
    "AVAHI_ENTRY_GROUP_COLLISION",
3106
    "AVAHI_ENTRY_GROUP_FAILURE"
3107
  };
3108
#  endif // DEBUG
3109
3110
3111
0
  (void)srv;
3112
3113
0
  DEBUG_printf("3avahi_service_cb(srv=%p, state=%s, service=%p)", srv, avahi_states[state], service);
3114
3115
0
  (service->cb)(service, service->cb_data, state == AVAHI_ENTRY_GROUP_COLLISION ? CUPS_DNSSD_FLAGS_COLLISION : CUPS_DNSSD_FLAGS_NONE);
3116
0
}
3117
3118
3119
//
3120
// 'avahi_unlock()' - Unlock the access mutex.
3121
//
3122
3123
static void
3124
avahi_unlock(cups_dnssd_t *dnssd, // I - DNS-SD context
3125
             const char   *name)  // I - Who is unlocking?
3126
0
{
3127
0
  (void)name;
3128
3129
0
  if (!dnssd->in_callback)
3130
0
  {
3131
0
    DEBUG_printf("2avahi_unlock: Unlocking mutex for %s.", name);
3132
0
    cupsMutexUnlock(&dnssd->mutex);
3133
3134
0
    avahi_simple_poll_wakeup(dnssd->poll);
3135
0
  }
3136
0
}
3137
#endif // HAVE_MDNSRESPONDER