Coverage Report

Created: 2026-01-17 06:55

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