Coverage Report

Created: 2025-07-11 06:21

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