/* * 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 #include "cupsd.h" #include /* * 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)); }