Coverage Report

Created: 2025-11-24 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcups/cups/http-addr.c
Line
Count
Source
1
//
2
// HTTP address routines for CUPS.
3
//
4
// Copyright © 2021-2024 by OpenPrinting.
5
// Copyright © 2007-2021 by Apple Inc.
6
// Copyright © 1997-2006 by Easy Software Products, all rights reserved.
7
//
8
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
9
// information.
10
//
11
12
#include "cups-private.h"
13
#include <sys/stat.h>
14
#ifdef HAVE_RESOLV_H
15
#  include <resolv.h>
16
#endif // HAVE_RESOLV_H
17
#ifdef __APPLE__
18
#  include <CoreFoundation/CoreFoundation.h>
19
#  ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
20
#    include <SystemConfiguration/SystemConfiguration.h>
21
#  endif // HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
22
#endif // __APPLE__
23
24
25
//
26
// 'httpAddrClose()' - Close a socket created by @link httpAddrConnect@ or
27
//                     @link httpAddrListen@.
28
//
29
// Pass `NULL` for sockets created with @link httpAddrConnect@ and the
30
// listen address for sockets created with @link httpAddrListen@.  This function
31
// ensures that domain sockets are removed when closed.
32
//
33
34
bool          // O - `true` on success, `false` on failure
35
httpAddrClose(http_addr_t *addr,  // I - Listen address or `NULL`
36
              int         fd)   // I - Socket file descriptor
37
0
{
38
#ifdef _WIN32
39
  if (closesocket(fd))
40
#else
41
0
  if (close(fd))
42
0
#endif // _WIN32
43
0
    return (false);
44
45
0
#ifdef AF_LOCAL
46
0
  if (addr && addr->addr.sa_family == AF_LOCAL)
47
0
    return (!unlink(addr->un.sun_path));
48
0
#endif // AF_LOCAL
49
50
0
  return (true);
51
0
}
52
53
54
//
55
// 'httpGetAddress()' - Get the address of the connected peer of a connection.
56
//
57
// For connections created with @link httpConnect2@, the address is for the
58
// server.  For connections created with @link httpAccept@, the address is for
59
// the client.
60
//
61
// Returns `NULL` if the socket is currently unconnected.
62
//
63
64
http_addr_t *       // O - Connected address or `NULL`
65
httpGetAddress(http_t *http)    // I - HTTP connection
66
0
{
67
0
  return (http ? http->hostaddr : NULL);
68
0
}
69
70
71
//
72
// 'httpAddrGetFamily()' - Get the address family of an address.
73
//
74
75
int         // O - Address family
76
httpAddrGetFamily(http_addr_t *addr)  // I - Address
77
0
{
78
0
  if (addr)
79
0
    return (addr->addr.sa_family);
80
0
  else
81
0
    return (0);
82
0
}
83
84
85
//
86
// 'httpAddrGetLength()' - Return the length of the address in bytes.
87
//
88
89
size_t          // O - Length in bytes
90
httpAddrGetLength(
91
    const http_addr_t *addr)    // I - Address
92
0
{
93
0
  if (!addr)
94
0
    return (0);
95
96
0
#ifdef AF_INET6
97
0
  if (addr->addr.sa_family == AF_INET6)
98
0
    return (sizeof(addr->ipv6));
99
0
  else
100
0
#endif // AF_INET6
101
0
#ifdef AF_LOCAL
102
0
  if (addr->addr.sa_family == AF_LOCAL)
103
0
    return (offsetof(struct sockaddr_un, sun_path) + strlen(addr->un.sun_path) + 1);
104
0
  else
105
0
#endif // AF_LOCAL
106
0
  if (addr->addr.sa_family == AF_INET)
107
0
    return (sizeof(addr->ipv4));
108
0
  else
109
0
    return (0);
110
111
0
}
112
113
114
//
115
// 'httpAddrGetPort()' - Get the port number associated with an address.
116
//
117
118
int         // O - Port number
119
httpAddrGetPort(http_addr_t *addr)  // I - Address
120
0
{
121
0
  if (!addr)
122
0
    return (-1);
123
0
#ifdef AF_INET6
124
0
  else if (addr->addr.sa_family == AF_INET6)
125
0
    return (ntohs(addr->ipv6.sin6_port));
126
0
#endif // AF_INET6
127
0
  else if (addr->addr.sa_family == AF_INET)
128
0
    return (ntohs(addr->ipv4.sin_port));
129
0
  else
130
0
    return (0);
131
0
}
132
133
134
//
135
// 'httpAddrGetString()' - Convert an address to a numeric string.
136
//
137
138
char *          // O - Numeric address string
139
httpAddrGetString(
140
    const http_addr_t *addr,    // I - Address to convert
141
    char              *s,   // I - String buffer
142
    size_t            slen)   // I - Length of string buffer
143
0
{
144
0
  DEBUG_printf("httpAddrGetString(addr=%p, s=%p, slen=%u)", (void *)addr, (void *)s, (unsigned)slen);
145
146
  // Range check input...
147
0
  if (!addr || !s || slen <= 2)
148
0
  {
149
0
    if (s && slen >= 1)
150
0
      *s = '\0';
151
152
0
    return (NULL);
153
0
  }
154
155
0
#ifdef AF_LOCAL
156
0
  if (addr->addr.sa_family == AF_LOCAL)
157
0
  {
158
0
    if (addr->un.sun_path[0] == '/')
159
0
      cupsCopyString(s, addr->un.sun_path, (size_t)slen);
160
0
    else
161
0
      cupsCopyString(s, "localhost", (size_t)slen);
162
0
  }
163
0
  else
164
0
#endif // AF_LOCAL
165
0
  if (addr->addr.sa_family == AF_INET)
166
0
  {
167
0
    unsigned temp;      // Temporary address
168
169
0
    temp = ntohl(addr->ipv4.sin_addr.s_addr);
170
171
0
    snprintf(s, (size_t)slen, "%d.%d.%d.%d", (temp >> 24) & 255, (temp >> 16) & 255, (temp >> 8) & 255, temp & 255);
172
0
  }
173
0
#ifdef AF_INET6
174
0
  else if (addr->addr.sa_family == AF_INET6)
175
0
  {
176
0
    char  *sptr,      // Pointer into string
177
0
    temps[64];    // Temporary string for address
178
179
0
    if (getnameinfo(&addr->addr, (socklen_t)httpAddrGetLength(addr), temps, sizeof(temps), NULL, 0, NI_NUMERICHOST))
180
0
    {
181
      // If we get an error back, then the address type is not supported
182
      // and we should zero out the buffer...
183
0
      s[0] = '\0';
184
185
0
      return (NULL);
186
0
    }
187
0
    else if ((sptr = strchr(temps, '%')) != NULL)
188
0
    {
189
      // Convert "%zone" to "+zone" to match URI form...
190
0
      *sptr = '+';
191
0
    }
192
193
    // Add "[v1." and "]" around IPv6 address to convert to URI form.
194
0
    snprintf(s, (size_t)slen, "[v1.%s]", temps);
195
0
  }
196
0
#endif // AF_INET6
197
0
  else
198
0
  {
199
0
    cupsCopyString(s, "UNKNOWN", (size_t)slen);
200
0
  }
201
202
0
  DEBUG_printf("2httpAddrGetString: returning \"%s\"...", s);
203
204
0
  return (s);
205
0
}
206
207
208
//
209
// 'httpAddrIsAny()' - Check for the "any" address.
210
//
211
212
bool          // O - `true` if "any", `false` otherwise
213
httpAddrIsAny(const http_addr_t *addr)  // I - Address to check
214
0
{
215
0
  if (!addr)
216
0
    return (false);
217
218
0
#ifdef AF_INET6
219
0
  if (addr->addr.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&(addr->ipv6.sin6_addr)))
220
0
    return (true);
221
0
#endif // AF_INET6
222
223
0
  if (addr->addr.sa_family == AF_INET && ntohl(addr->ipv4.sin_addr.s_addr) == 0x00000000)
224
0
    return (true);
225
226
0
  return (false);
227
0
}
228
229
230
//
231
// 'httpAddrIsEqual()' - Compare two addresses.
232
//
233
234
bool          // O - `true` if equal, `false` if not
235
httpAddrIsEqual(
236
    const http_addr_t *addr1,   // I - First address
237
    const http_addr_t *addr2)   // I - Second address
238
0
{
239
0
  if (!addr1 && !addr2)
240
0
    return (true);
241
242
0
  if (!addr1 || !addr2)
243
0
    return (false);
244
245
0
  if (addr1->addr.sa_family != addr2->addr.sa_family)
246
0
    return (false);
247
248
0
#ifdef AF_LOCAL
249
0
  if (addr1->addr.sa_family == AF_LOCAL)
250
0
    return (!strcmp(addr1->un.sun_path, addr2->un.sun_path));
251
0
#endif // AF_LOCAL
252
253
0
#ifdef AF_INET6
254
0
  if (addr1->addr.sa_family == AF_INET6)
255
0
    return (!memcmp(&(addr1->ipv6.sin6_addr), &(addr2->ipv6.sin6_addr), 16));
256
0
#endif // AF_INET6
257
258
0
  return (addr1->ipv4.sin_addr.s_addr == addr2->ipv4.sin_addr.s_addr);
259
0
}
260
261
262
//
263
// 'httpAddrIsLocalhost()' - Check for the local loopback address.
264
//
265
266
bool          // O - `true` if local host, `false` otherwise
267
httpAddrIsLocalhost(
268
    const http_addr_t *addr)    // I - Address to check
269
0
{
270
0
  if (!addr)
271
0
    return (true);
272
273
0
#ifdef AF_INET6
274
0
  if (addr->addr.sa_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(addr->ipv6.sin6_addr)))
275
0
    return (true);
276
0
#endif // AF_INET6
277
278
0
#ifdef AF_LOCAL
279
0
  if (addr->addr.sa_family == AF_LOCAL)
280
0
    return (true);
281
0
#endif // AF_LOCAL
282
283
0
  if (addr->addr.sa_family == AF_INET && (ntohl(addr->ipv4.sin_addr.s_addr) & 0xff000000) == 0x7f000000)
284
0
    return (true);
285
286
0
  return (false);
287
0
}
288
289
290
//
291
// 'httpAddrListen()' - Create a listening socket bound to the specified address and port.
292
//
293
294
int         // O - Socket or `-1` on error
295
httpAddrListen(http_addr_t *addr, // I - Address to bind to
296
               int         port)  // I - Port number to bind to
297
0
{
298
0
  int   fd = -1,    // Socket
299
0
    val,      // Socket value
300
0
                status;     // Bind status
301
302
303
  // Range check input...
304
0
  if (!addr || port < 0)
305
0
    return (-1);
306
307
  // Make sure the network stack is initialized...
308
0
  httpInitialize();
309
310
  // Create the socket and set options...
311
0
  if ((fd = socket(addr->addr.sa_family, SOCK_STREAM, 0)) < 0)
312
0
  {
313
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
314
0
    return (-1);
315
0
  }
316
317
0
  val = 1;
318
0
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val)))
319
0
    DEBUG_printf("2httpAddrListen: setsockopt(SO_REUSEADDR) failed: %s", strerror(errno));
320
321
0
#ifdef IPV6_V6ONLY
322
0
  if (addr->addr.sa_family == AF_INET6)
323
0
  {
324
0
    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, CUPS_SOCAST &val, sizeof(val)))
325
0
      DEBUG_printf("2httpAddrListen: setsockopt(IPv6_V6ONLY) failed: %s", strerror(errno));
326
0
  }
327
0
#endif // IPV6_V6ONLY
328
329
  // Bind the socket...
330
0
#ifdef AF_LOCAL
331
0
  if (addr->addr.sa_family == AF_LOCAL)
332
0
  {
333
0
    mode_t  mask;     // Umask setting
334
335
    // Remove any existing domain socket file...
336
0
    if ((status = unlink(addr->un.sun_path)) < 0)
337
0
    {
338
0
      if (errno == ENOENT)
339
0
        status = 0;
340
0
      else
341
0
        DEBUG_printf("2httpAddrListen: Unable to unlink '%s': %s",
342
0
addr->un.sun_path, strerror(errno));
343
0
    }
344
345
0
    if (!status)
346
0
    {
347
      // Save the current umask and set it to 0 so that all users can access
348
      // the domain socket...
349
0
      mask = umask(0);
350
351
      // Bind the domain socket...
352
0
      if ((status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrGetLength(addr))) < 0)
353
0
  DEBUG_printf("1httpAddrListen: Unable to bind domain socket \"%s\": %s", addr->un.sun_path, strerror(errno));
354
 
355
      // Restore the umask and fix permissions...
356
0
      umask(mask);
357
0
    }
358
0
  }
359
0
  else
360
0
#endif // AF_LOCAL
361
0
  {
362
0
    httpAddrSetPort(addr, port);
363
364
0
    if ((status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrGetLength(addr))) < 0)
365
0
      DEBUG_printf("1httpAddrListen: Unable to bind network socket: %s", strerror(errno));
366
0
  }
367
368
0
  if (status)
369
0
  {
370
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
371
372
0
    close(fd);
373
374
0
    return (-1);
375
0
  }
376
377
  // Listen...
378
0
  if (listen(fd, INT_MAX))
379
0
  {
380
0
    DEBUG_printf("1httpAddrListen: Unable to listen on socket: %s", strerror(errno));
381
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
382
383
0
    close(fd);
384
385
0
    return (-1);
386
0
  }
387
388
  // Close on exec...
389
0
#ifndef _WIN32
390
0
  if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC))
391
0
    DEBUG_printf("2httpAddrListen: fcntl(F_SETFD, FD_CLOEXEC) failed: %s", strerror(errno));
392
0
#endif // !_WIN32
393
394
#ifdef SO_NOSIGPIPE
395
  // Disable SIGPIPE for this socket.
396
  val = 1;
397
  if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)))
398
    DEBUG_printf("2httpAddrListen: setsockopt(SO_NOSIGPIPE) failed: %s", strerror(errno));
399
#endif // SO_NOSIGPIPE
400
401
0
  return (fd);
402
0
}
403
404
405
//
406
// 'httpAddrLookup()' - Lookup the hostname associated with the address.
407
//
408
409
char *          // O - Host name
410
httpAddrLookup(
411
    const http_addr_t *addr,    // I - Address to lookup
412
    char              *name,    // I - Host name buffer
413
    size_t            namelen)    // I - Size of name buffer
414
0
{
415
0
  _cups_globals_t *cg = _cupsGlobals();
416
          // Global data
417
418
419
0
  DEBUG_printf("httpAddrLookup(addr=%p, name=%p, namelen=%u)", (void *)addr, (void *)name, (unsigned)namelen);
420
421
  // Range check input...
422
0
  if (!addr || !name || namelen <= 2)
423
0
  {
424
0
    if (name && namelen >= 1)
425
0
      *name = '\0';
426
427
0
    return (NULL);
428
0
  }
429
430
0
#ifdef AF_LOCAL
431
0
  if (addr->addr.sa_family == AF_LOCAL)
432
0
  {
433
0
    cupsCopyString(name, addr->un.sun_path, (size_t)namelen);
434
0
    return (name);
435
0
  }
436
0
#endif // AF_LOCAL
437
438
  // Optimize lookups for localhost/loopback addresses...
439
0
  if (httpAddrIsLocalhost(addr))
440
0
  {
441
0
    cupsCopyString(name, "localhost", (size_t)namelen);
442
0
    return (name);
443
0
  }
444
445
0
#ifdef HAVE_RES_INIT
446
  // If the previous lookup failed, re-initialize the resolver to prevent
447
  // temporary network errors from persisting.  This *should* be handled by
448
  // the resolver libraries, but apparently the glibc folks do not agree.
449
  //
450
  // We set a flag at the end of this function if we encounter an error that
451
  // requires reinitialization of the resolver functions.  We then call
452
  // res_init() if the flag is set on the next call here or in httpAddrLookup().
453
0
  if (cg->need_res_init)
454
0
  {
455
0
    res_init();
456
457
0
    cg->need_res_init = 0;
458
0
  }
459
0
#endif // HAVE_RES_INIT
460
461
  // Fall back on httpAddrGetString if getnameinfo fails...
462
0
  int error = getnameinfo(&addr->addr, (socklen_t)httpAddrGetLength(addr), name, (socklen_t)namelen, NULL, 0, 0);
463
464
0
  if (error)
465
0
  {
466
0
    if (error == EAI_FAIL)
467
0
      cg->need_res_init = 1;
468
469
0
    return (httpAddrGetString(addr, name, namelen));
470
0
  }
471
472
0
  DEBUG_printf("2httpAddrLookup: returning \"%s\"...", name);
473
474
0
  return (name);
475
0
}
476
477
478
//
479
// 'httpGetHostname()' - Get the FQDN for the connection or local system.
480
//
481
// When "http" points to a connected socket, return the hostname or address that
482
// was used in the call to @link httpConnect@ or the address of the client for
483
// the connection from @link httpAcceptConnection@.
484
//
485
// When "http" is `NULL`, return the FQDN for the local system.
486
//
487
488
const char *        // O - FQDN for connection or system
489
httpGetHostname(http_t *http,   // I - HTTP connection or NULL
490
                char   *s,    // I - String buffer for name
491
                size_t slen)    // I - Size of buffer
492
0
{
493
0
  if (http)
494
0
  {
495
    // Return the connection address...
496
0
    if (!s || slen <= 1)
497
0
    {
498
0
      if (http->hostname[0] == '/')
499
0
  return ("localhost");
500
0
      else
501
0
  return (http->hostname);
502
0
    }
503
0
    else if (http->hostname[0] == '/')
504
0
    {
505
0
      cupsCopyString(s, "localhost", (size_t)slen);
506
0
    }
507
0
    else
508
0
    {
509
0
      cupsCopyString(s, http->hostname, (size_t)slen);
510
0
    }
511
0
  }
512
0
  else
513
0
  {
514
    // Return the hostname...
515
0
    if (!s || slen <= 1)
516
0
      return (NULL);
517
518
0
    if (gethostname(s, (size_t)slen) < 0)
519
0
      cupsCopyString(s, "localhost", (size_t)slen);
520
521
0
    DEBUG_printf("1httpGetHostname: gethostname() returned \"%s\".", s);
522
523
0
    if (!strchr(s, '.'))
524
0
    {
525
#ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
526
      // The hostname is not a FQDN, so use the local hostname from the
527
      // SystemConfiguration framework...
528
      SCDynamicStoreRef sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("libcups"), NULL, NULL);
529
          // System configuration data
530
      CFStringRef local = sc ? SCDynamicStoreCopyLocalHostName(sc) : NULL;
531
          // Local host name
532
      char    localStr[1024]; // Local host name C string
533
534
      if (local && CFStringGetCString(local, localStr, sizeof(localStr), kCFStringEncodingUTF8))
535
      {
536
        // Append ".local." to the hostname we get...
537
        snprintf(s, (size_t)slen, "%s.local.", localStr);
538
        DEBUG_printf("1httpGetHostname: SCDynamicStoreCopyLocalHostName() returned \"%s\".", s);
539
      }
540
541
      if (local)
542
        CFRelease(local);
543
      if (sc)
544
        CFRelease(sc);
545
546
#else
547
      // The hostname is not a FQDN, so look it up...
548
0
      struct hostent  *host;    // Host entry to get FQDN
549
550
0
      if ((host = gethostbyname(s)) != NULL && host->h_name)
551
0
      {
552
        // Use the resolved hostname...
553
0
  cupsCopyString(s, host->h_name, (size_t)slen);
554
0
        DEBUG_printf("1httpGetHostname: gethostbyname() returned \"%s\".", s);
555
0
      }
556
0
#endif // HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
557
0
    }
558
559
    // Make sure .local hostnames end with a period...
560
0
    if (strlen(s) > 6 && !strcmp(s + strlen(s) - 6, ".local"))
561
0
      cupsConcatString(s, ".", (size_t)slen);
562
0
  }
563
564
  // Convert the hostname to lowercase as needed...
565
0
  if (s[0] != '/')
566
0
  {
567
0
    char  *ptr;     // Pointer into string
568
569
0
    for (ptr = s; *ptr; ptr ++)
570
0
      *ptr = (char)_cups_tolower((int)*ptr);
571
0
  }
572
573
  // Return the hostname with as much domain info as we have...
574
0
  return (s);
575
0
}
576
577
578
//
579
// 'httpResolveHostname()' - Resolve the hostname of the HTTP connection
580
//                           address.
581
//
582
583
const char *        // O - Resolved hostname or `NULL`
584
httpResolveHostname(http_t *http, // I - HTTP connection
585
                    char   *buffer, // I - Hostname buffer or `NULL` to use HTTP buffer
586
                    size_t bufsize) // I - Size of buffer
587
0
{
588
0
  if (!http)
589
0
    return (NULL);
590
591
0
  if (isdigit(http->hostname[0] & 255) || http->hostname[0] == '[')
592
0
  {
593
0
    char  temp[1024];   // Temporary string
594
595
0
    if (httpAddrLookup(http->hostaddr, temp, sizeof(temp)))
596
0
      cupsCopyString(http->hostname, temp, sizeof(http->hostname));
597
0
    else
598
0
      return (NULL);
599
0
  }
600
601
0
  if (buffer)
602
0
  {
603
0
    if (http->hostname[0] == '/')
604
0
      cupsCopyString(buffer, "localhost", bufsize);
605
0
    else
606
0
      cupsCopyString(buffer, http->hostname, bufsize);
607
608
0
    return (buffer);
609
0
  }
610
0
  else if (http->hostname[0] == '/')
611
0
  {
612
0
    return ("localhost");
613
0
  }
614
0
  else
615
0
  {
616
0
    return (http->hostname);
617
0
  }
618
0
}
619
620
621
//
622
// 'httpAddrSetPort()' - Set the port number associated with an address.
623
//
624
625
void
626
httpAddrSetPort(http_addr_t *addr,  // I - Address
627
                int         port) // I - Port
628
0
{
629
0
  if (!addr || port <= 0)
630
0
    return;
631
632
0
#ifdef AF_INET6
633
0
  if (addr->addr.sa_family == AF_INET6)
634
0
    addr->ipv6.sin6_port = htons(port);
635
0
  else
636
0
#endif // AF_INET6
637
0
  if (addr->addr.sa_family == AF_INET)
638
0
    addr->ipv4.sin_port = htons(port);
639
0
}