/* * * Connection Manager * * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #define CONNMAN_API_SUBJECT_TO_CHANGE #include #include #include #include static in_addr_t loopback_address; static in_addr_t loopback_netmask; static char system_hostname[HOST_NAME_MAX + 1]; static void create_hostname(void) { const char *name = "localhost"; if (sethostname(name, strlen(name)) < 0) connman_error("Failed to set hostname to %s", name); strncpy(system_hostname, name, HOST_NAME_MAX); } static int setup_hostname(void) { char name[HOST_NAME_MAX + 1]; memset(system_hostname, 0, sizeof(system_hostname)); if (gethostname(system_hostname, HOST_NAME_MAX) < 0) { connman_error("Failed to get current hostname"); return -EIO; } if (strlen(system_hostname) > 0 && strcmp(system_hostname, "(none)") != 0) connman_info("System hostname is %s", system_hostname); else create_hostname(); memset(name, 0, sizeof(name)); if (getdomainname(name, HOST_NAME_MAX) < 0) { connman_error("Failed to get current domainname"); return -EIO; } if (strlen(name) > 0 && strcmp(name, "(none)") != 0) connman_info("System domainname is %s", name); return 0; } static gboolean valid_loopback(int sk, struct ifreq *ifr) { struct sockaddr_in *addr; int err; char buf[INET_ADDRSTRLEN]; /* It is possible to end up in situations in which the * loopback interface is up but has no valid address. In that * case, we expect EADDRNOTAVAIL and should return FALSE. */ err = ioctl(sk, SIOCGIFADDR, ifr); if (err < 0) { err = -errno; connman_error("Getting address failed (%s)", strerror(-err)); return err != -EADDRNOTAVAIL ? TRUE : FALSE; } addr = (struct sockaddr_in *) &ifr->ifr_addr; if (addr->sin_addr.s_addr != loopback_address) { connman_warn("Invalid loopback address %s", inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf))); return FALSE; } err = ioctl(sk, SIOCGIFNETMASK, ifr); if (err < 0) { err = -errno; connman_error("Getting netmask failed (%s)", strerror(-err)); return TRUE; } addr = (struct sockaddr_in *) &ifr->ifr_netmask; if (addr->sin_addr.s_addr != loopback_netmask) { connman_warn("Invalid loopback netmask %s", inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf))); return FALSE; } return TRUE; } static int setup_loopback(void) { struct ifreq ifr; struct sockaddr_in addr; int sk, err; sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sk < 0) return -errno; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, "lo"); if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { err = -errno; goto done; } if (ifr.ifr_flags & IFF_UP) { connman_info("Checking loopback interface settings"); if (valid_loopback(sk, &ifr) == TRUE) { err = -EALREADY; goto done; } connman_warn("Correcting wrong lookback settings"); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = loopback_address; memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr)); err = ioctl(sk, SIOCSIFADDR, &ifr); if (err < 0) { err = -errno; connman_error("Setting address failed (%s)", strerror(-err)); goto done; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = loopback_netmask; memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask)); err = ioctl(sk, SIOCSIFNETMASK, &ifr); if (err < 0) { err = -errno; connman_error("Setting netmask failed (%s)", strerror(-err)); goto done; } if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { err = -errno; goto done; } ifr.ifr_flags |= IFF_UP; if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) { err = -errno; connman_error("Activating loopback interface failed (%s)", strerror(-err)); goto done; } done: close(sk); return err; } static const char *loopback_get_hostname(void) { return system_hostname; } static int loopback_set_hostname(const char *hostname) { const char *ptr; int err, len; if (g_strcmp0(hostname, "") == 0) return 0; len = strlen(hostname); if (connman_inet_check_hostname(hostname, len) == FALSE) return -EINVAL; if ((ptr = strstr(hostname, ".")) != NULL) len = ptr - hostname; if (sethostname(hostname, len) < 0) { err = -errno; connman_error("Failed to set hostname to %s", hostname); return err; } connman_info("Setting hostname to %s", hostname); return 0; } static int loopback_set_domainname(const char *domainname) { int err, len; len = strlen(domainname); if (connman_inet_check_hostname(domainname, len) == FALSE) return -EINVAL; if (setdomainname(domainname, len) < 0) { err = -errno; connman_error("Failed to set domainname to %s", domainname); return err; } connman_info("Setting domainname to %s", domainname); return 0; } static struct connman_utsname_driver loopback_driver = { .name = "loopback", .get_hostname = loopback_get_hostname, .set_hostname = loopback_set_hostname, .set_domainname = loopback_set_domainname, }; static int loopback_init(void) { loopback_address = inet_addr("127.0.0.1"); loopback_netmask = inet_addr("255.0.0.0"); setup_loopback(); setup_hostname(); connman_utsname_driver_register(&loopback_driver); return 0; } static void loopback_exit(void) { connman_utsname_driver_unregister(&loopback_driver); } CONNMAN_PLUGIN_DEFINE(loopback, "Loopback device plugin", VERSION, CONNMAN_PLUGIN_PRIORITY_HIGH, loopback_init, loopback_exit)