blob: 5ba00a1fb636832e2244ba67e84c47f11fd0c845 [file] [log] [blame]
/*
* Network interface functions for the CUPS scheduler.
*
* Copyright © 2007-2018 by Apple Inc.
* Copyright © 1997-2006 by Easy Software Products, all rights reserved.
*
* Licensed under Apache License v2.0. See the file "LICENSE" for more
* information.
*/
/*
* Include necessary headers.
*/
#include <cups/http-private.h>
#include "cupsd.h"
#include <cups/getifaddrs-internal.h>
/*
* Local functions...
*/
static void cupsdNetIFFree(void);
static int compare_netif(cupsd_netif_t *a, cupsd_netif_t *b);
/*
* 'cupsdNetIFFind()' - Find a network interface.
*/
cupsd_netif_t * /* O - Network interface data */
cupsdNetIFFind(const char *name) /* I - Name of interface */
{
cupsd_netif_t key; /* Search key */
/*
* Update the interface list as needed...
*/
if (NetIFUpdate)
cupsdNetIFUpdate();
/*
* Search for the named interface...
*/
strlcpy(key.name, name, sizeof(key.name));
return ((cupsd_netif_t *)cupsArrayFind(NetIFList, &key));
}
/*
* 'cupsdNetIFFree()' - Free the current network interface list.
*/
static void
cupsdNetIFFree(void)
{
cupsd_netif_t *current; /* Current interface in array */
/*
* Loop through the interface list and free all the records...
*/
for (current = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
current;
current = (cupsd_netif_t *)cupsArrayNext(NetIFList))
{
cupsArrayRemove(NetIFList, current);
free(current);
}
}
/*
* 'cupsdNetIFUpdate()' - Update the network interface list as needed...
*/
void
cupsdNetIFUpdate(void)
{
int match; /* Matching address? */
cupsd_listener_t *lis; /* Listen address */
cupsd_netif_t *temp; /* New interface */
struct ifaddrs *addrs, /* Interface address list */
*addr; /* Current interface address */
char hostname[1024]; /* Hostname for address */
size_t hostlen; /* Length of hostname */
/*
* Only update the list if we need to...
*/
if (!NetIFUpdate)
return;
NetIFUpdate = 0;
/*
* Free the old interfaces...
*/
cupsdNetIFFree();
/*
* Make sure we have an array...
*/
if (!NetIFList)
NetIFList = cupsArrayNew((cups_array_func_t)compare_netif, NULL);
if (!NetIFList)
return;
/*
* Grab a new list of interfaces...
*/
if (getifaddrs(&addrs) < 0)
{
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdNetIFUpdate: Unable to get interface list - %s", strerror(errno));
return;
}
for (addr = addrs; addr != NULL; addr = addr->ifa_next)
{
/*
* See if this interface address is IPv4 or IPv6...
*/
if (addr->ifa_addr == NULL ||
(addr->ifa_addr->sa_family != AF_INET
#ifdef AF_INET6
&& addr->ifa_addr->sa_family != AF_INET6
#endif
) ||
addr->ifa_netmask == NULL || addr->ifa_name == NULL)
{
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdNetIFUpdate: Ignoring \"%s\".", addr->ifa_name);
continue;
}
/*
* Try looking up the hostname for the address as needed...
*/
if (HostNameLookups)
httpAddrLookup((http_addr_t *)(addr->ifa_addr), hostname,
sizeof(hostname));
else
{
/*
* Map the default server address and localhost to the server name
* and localhost, respectively; for all other addresses, use the
* numeric address...
*/
if (httpAddrLocalhost((http_addr_t *)(addr->ifa_addr)))
strlcpy(hostname, "localhost", sizeof(hostname));
else
httpAddrString((http_addr_t *)(addr->ifa_addr), hostname,
sizeof(hostname));
}
/*
* Create a new address element...
*/
hostlen = strlen(hostname);
if ((temp = calloc(1, sizeof(cupsd_netif_t) + hostlen)) == NULL)
{
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdNetIFUpdate: Unable to allocate memory for interface.");
break;
}
/*
* Copy all of the information...
*/
strlcpy(temp->name, addr->ifa_name, sizeof(temp->name));
temp->hostlen = hostlen;
memcpy(temp->hostname, hostname, hostlen + 1);
if (addr->ifa_addr->sa_family == AF_INET)
{
/*
* Copy IPv4 addresses...
*/
memcpy(&(temp->address), addr->ifa_addr, sizeof(struct sockaddr_in));
memcpy(&(temp->mask), addr->ifa_netmask, sizeof(struct sockaddr_in));
if (addr->ifa_dstaddr)
memcpy(&(temp->broadcast), addr->ifa_dstaddr,
sizeof(struct sockaddr_in));
}
#ifdef AF_INET6
else
{
/*
* Copy IPv6 addresses...
*/
memcpy(&(temp->address), addr->ifa_addr, sizeof(struct sockaddr_in6));
memcpy(&(temp->mask), addr->ifa_netmask, sizeof(struct sockaddr_in6));
if (addr->ifa_dstaddr)
memcpy(&(temp->broadcast), addr->ifa_dstaddr,
sizeof(struct sockaddr_in6));
}
#endif /* AF_INET6 */
if (!(addr->ifa_flags & IFF_POINTOPOINT) &&
!httpAddrLocalhost(&(temp->address)))
temp->is_local = 1;
/*
* Determine which port to use when advertising printers...
*/
for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
lis;
lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
{
match = 0;
if (httpAddrAny(&(lis->address)))
match = 1;
else if (addr->ifa_addr->sa_family == AF_INET &&
lis->address.addr.sa_family == AF_INET &&
(lis->address.ipv4.sin_addr.s_addr &
temp->mask.ipv4.sin_addr.s_addr) ==
(temp->address.ipv4.sin_addr.s_addr &
temp->mask.ipv4.sin_addr.s_addr))
match = 1;
#ifdef AF_INET6
else if (addr->ifa_addr->sa_family == AF_INET6 &&
lis->address.addr.sa_family == AF_INET6 &&
(lis->address.ipv6.sin6_addr.s6_addr[0] &
temp->mask.ipv6.sin6_addr.s6_addr[0]) ==
(temp->address.ipv6.sin6_addr.s6_addr[0] &
temp->mask.ipv6.sin6_addr.s6_addr[0]) &&
(lis->address.ipv6.sin6_addr.s6_addr[1] &
temp->mask.ipv6.sin6_addr.s6_addr[1]) ==
(temp->address.ipv6.sin6_addr.s6_addr[1] &
temp->mask.ipv6.sin6_addr.s6_addr[1]) &&
(lis->address.ipv6.sin6_addr.s6_addr[2] &
temp->mask.ipv6.sin6_addr.s6_addr[2]) ==
(temp->address.ipv6.sin6_addr.s6_addr[2] &
temp->mask.ipv6.sin6_addr.s6_addr[2]) &&
(lis->address.ipv6.sin6_addr.s6_addr[3] &
temp->mask.ipv6.sin6_addr.s6_addr[3]) ==
(temp->address.ipv6.sin6_addr.s6_addr[3] &
temp->mask.ipv6.sin6_addr.s6_addr[3]))
match = 1;
#endif /* AF_INET6 */
if (match)
{
temp->port = httpAddrPort(&(lis->address));
break;
}
}
/*
* Add it to the array...
*/
cupsArrayAdd(NetIFList, temp);
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdNetIFUpdate: \"%s\" = %s:%d",
temp->name, temp->hostname, temp->port);
}
freeifaddrs(addrs);
}
/*
* 'compare_netif()' - Compare two network interfaces.
*/
static int /* O - Result of comparison */
compare_netif(cupsd_netif_t *a, /* I - First network interface */
cupsd_netif_t *b) /* I - Second network interface */
{
return (strcmp(a->name, b->name));
}