Coverage Report

Created: 2025-10-10 06:07

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
      status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrGetLength(addr));
353
354
      // Restore the umask and fix permissions...
355
0
      umask(mask);
356
0
    }
357
0
  }
358
0
  else
359
0
#endif // AF_LOCAL
360
0
  {
361
0
    httpAddrSetPort(addr, port);
362
363
0
    status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrGetLength(addr));
364
0
  }
365
366
0
  if (status)
367
0
  {
368
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
369
370
0
    close(fd);
371
372
0
    return (-1);
373
0
  }
374
375
  // Listen...
376
0
  if (listen(fd, INT_MAX))
377
0
  {
378
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
379
380
0
    close(fd);
381
382
0
    return (-1);
383
0
  }
384
385
  // Close on exec...
386
0
#ifndef _WIN32
387
0
  if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC))
388
0
    DEBUG_printf("2httpAddrListen: fcntl(F_SETFD, FD_CLOEXEC) failed: %s", strerror(errno));
389
0
#endif // !_WIN32
390
391
#ifdef SO_NOSIGPIPE
392
  // Disable SIGPIPE for this socket.
393
  val = 1;
394
  if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)))
395
    DEBUG_printf("2httpAddrListen: setsockopt(SO_NOSIGPIPE) failed: %s", strerror(errno));
396
#endif // SO_NOSIGPIPE
397
398
0
  return (fd);
399
0
}
400
401
402
//
403
// 'httpAddrLookup()' - Lookup the hostname associated with the address.
404
//
405
406
char *          // O - Host name
407
httpAddrLookup(
408
    const http_addr_t *addr,    // I - Address to lookup
409
    char              *name,    // I - Host name buffer
410
    size_t            namelen)    // I - Size of name buffer
411
0
{
412
0
  _cups_globals_t *cg = _cupsGlobals();
413
          // Global data
414
415
416
0
  DEBUG_printf("httpAddrLookup(addr=%p, name=%p, namelen=%u)", (void *)addr, (void *)name, (unsigned)namelen);
417
418
  // Range check input...
419
0
  if (!addr || !name || namelen <= 2)
420
0
  {
421
0
    if (name && namelen >= 1)
422
0
      *name = '\0';
423
424
0
    return (NULL);
425
0
  }
426
427
0
#ifdef AF_LOCAL
428
0
  if (addr->addr.sa_family == AF_LOCAL)
429
0
  {
430
0
    cupsCopyString(name, addr->un.sun_path, (size_t)namelen);
431
0
    return (name);
432
0
  }
433
0
#endif // AF_LOCAL
434
435
  // Optimize lookups for localhost/loopback addresses...
436
0
  if (httpAddrIsLocalhost(addr))
437
0
  {
438
0
    cupsCopyString(name, "localhost", (size_t)namelen);
439
0
    return (name);
440
0
  }
441
442
0
#ifdef HAVE_RES_INIT
443
  // If the previous lookup failed, re-initialize the resolver to prevent
444
  // temporary network errors from persisting.  This *should* be handled by
445
  // the resolver libraries, but apparently the glibc folks do not agree.
446
  //
447
  // We set a flag at the end of this function if we encounter an error that
448
  // requires reinitialization of the resolver functions.  We then call
449
  // res_init() if the flag is set on the next call here or in httpAddrLookup().
450
0
  if (cg->need_res_init)
451
0
  {
452
0
    res_init();
453
454
0
    cg->need_res_init = 0;
455
0
  }
456
0
#endif // HAVE_RES_INIT
457
458
  // Fall back on httpAddrGetString if getnameinfo fails...
459
0
  int error = getnameinfo(&addr->addr, (socklen_t)httpAddrGetLength(addr), name, (socklen_t)namelen, NULL, 0, 0);
460
461
0
  if (error)
462
0
  {
463
0
    if (error == EAI_FAIL)
464
0
      cg->need_res_init = 1;
465
466
0
    return (httpAddrGetString(addr, name, namelen));
467
0
  }
468
469
0
  DEBUG_printf("2httpAddrLookup: returning \"%s\"...", name);
470
471
0
  return (name);
472
0
}
473
474
475
//
476
// 'httpGetHostname()' - Get the FQDN for the connection or local system.
477
//
478
// When "http" points to a connected socket, return the hostname or address that
479
// was used in the call to @link httpConnect@ or the address of the client for
480
// the connection from @link httpAcceptConnection@.
481
//
482
// When "http" is `NULL`, return the FQDN for the local system.
483
//
484
485
const char *        // O - FQDN for connection or system
486
httpGetHostname(http_t *http,   // I - HTTP connection or NULL
487
                char   *s,    // I - String buffer for name
488
                size_t slen)    // I - Size of buffer
489
0
{
490
0
  if (http)
491
0
  {
492
    // Return the connection address...
493
0
    if (!s || slen <= 1)
494
0
    {
495
0
      if (http->hostname[0] == '/')
496
0
  return ("localhost");
497
0
      else
498
0
  return (http->hostname);
499
0
    }
500
0
    else if (http->hostname[0] == '/')
501
0
      cupsCopyString(s, "localhost", (size_t)slen);
502
0
    else
503
0
      cupsCopyString(s, http->hostname, (size_t)slen);
504
0
  }
505
0
  else
506
0
  {
507
    // Return the hostname...
508
0
    if (!s || slen <= 1)
509
0
      return (NULL);
510
511
0
    if (gethostname(s, (size_t)slen) < 0)
512
0
      cupsCopyString(s, "localhost", (size_t)slen);
513
514
0
    if (!strchr(s, '.'))
515
0
    {
516
#ifdef HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
517
      // The hostname is not a FQDN, so use the local hostname from the
518
      // SystemConfiguration framework...
519
      SCDynamicStoreRef sc = SCDynamicStoreCreate(kCFAllocatorDefault,
520
                                                  CFSTR("libcups"), NULL, NULL);
521
          // System configuration data
522
      CFStringRef local = sc ? SCDynamicStoreCopyLocalHostName(sc) : NULL;
523
          // Local host name
524
      char    localStr[1024]; // Local host name C string
525
526
      if (local && CFStringGetCString(local, localStr, sizeof(localStr),
527
                                      kCFStringEncodingUTF8))
528
      {
529
        // Append ".local." to the hostname we get...
530
        snprintf(s, (size_t)slen, "%s.local.", localStr);
531
      }
532
533
      if (local)
534
        CFRelease(local);
535
      if (sc)
536
        CFRelease(sc);
537
538
#else
539
      // The hostname is not a FQDN, so look it up...
540
0
      struct hostent  *host;    // Host entry to get FQDN
541
542
0
      if ((host = gethostbyname(s)) != NULL && host->h_name)
543
0
      {
544
        // Use the resolved hostname...
545
0
  cupsCopyString(s, host->h_name, (size_t)slen);
546
0
      }
547
0
#endif // HAVE_SCDYNAMICSTORECOPYCOMPUTERNAME
548
0
    }
549
550
    // Make sure .local hostnames end with a period...
551
0
    if (strlen(s) > 6 && !strcmp(s + strlen(s) - 6, ".local"))
552
0
      cupsConcatString(s, ".", (size_t)slen);
553
0
  }
554
555
  // Convert the hostname to lowercase as needed...
556
0
  if (s[0] != '/')
557
0
  {
558
0
    char  *ptr;     // Pointer into string
559
560
0
    for (ptr = s; *ptr; ptr ++)
561
0
      *ptr = (char)_cups_tolower((int)*ptr);
562
0
  }
563
564
  // Return the hostname with as much domain info as we have...
565
0
  return (s);
566
0
}
567
568
569
//
570
// 'httpResolveHostname()' - Resolve the hostname of the HTTP connection
571
//                           address.
572
//
573
574
const char *        // O - Resolved hostname or `NULL`
575
httpResolveHostname(http_t *http, // I - HTTP connection
576
                    char   *buffer, // I - Hostname buffer
577
                    size_t bufsize) // I - Size of buffer
578
0
{
579
0
  if (!http)
580
0
    return (NULL);
581
582
0
  if (isdigit(http->hostname[0] & 255) || http->hostname[0] == '[')
583
0
  {
584
0
    char  temp[1024];   // Temporary string
585
586
0
    if (httpAddrLookup(http->hostaddr, temp, sizeof(temp)))
587
0
      cupsCopyString(http->hostname, temp, sizeof(http->hostname));
588
0
    else
589
0
      return (NULL);
590
0
  }
591
592
0
  if (buffer)
593
0
  {
594
0
    if (http->hostname[0] == '/')
595
0
      cupsCopyString(buffer, "localhost", bufsize);
596
0
    else
597
0
      cupsCopyString(buffer, http->hostname, bufsize);
598
599
0
    return (buffer);
600
0
  }
601
0
  else if (http->hostname[0] == '/')
602
0
    return ("localhost");
603
0
  else
604
0
    return (http->hostname);
605
0
}
606
607
608
//
609
// 'httpAddrSetPort()' - Set the port number associated with an address.
610
//
611
612
void
613
httpAddrSetPort(http_addr_t *addr,  // I - Address
614
                int         port) // I - Port
615
0
{
616
0
  if (!addr || port <= 0)
617
0
    return;
618
619
0
#ifdef AF_INET6
620
0
  if (addr->addr.sa_family == AF_INET6)
621
0
    addr->ipv6.sin6_port = htons(port);
622
0
  else
623
0
#endif // AF_INET6
624
0
  if (addr->addr.sa_family == AF_INET)
625
0
    addr->ipv4.sin_port = htons(port);
626
0
}