Coverage Report

Created: 2026-10-28 06:35

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