mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-05 00:38:07 +02:00
hosts: always check and update /etc/hosts if needed (rh #569914)
If the hostname was changed while NM wasn't running, and thus /etc/hosts was out of sync with the new hostname, NM wouldn't make sure that the new hostname was mapped in /etc/hosts. Make sure that happens and add a bunch of testcases for /etc/hosts rewriting.
This commit is contained in:
parent
a542bba20a
commit
e8b5bcca56
7 changed files with 708 additions and 188 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -92,6 +92,7 @@ libnm-util/tests/test-general
|
|||
libnm-util/tests/test-need-secrets
|
||||
libnm-util/tests/test-setting-8021x
|
||||
src/tests/test-dhcp-options
|
||||
src/tests/test-policy-hosts
|
||||
|
||||
system-settings/plugins/keyfile/tests/test-keyfile
|
||||
system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh
|
||||
|
|
|
|||
|
|
@ -30,10 +30,14 @@ INCLUDES = -I${top_srcdir} \
|
|||
-I${top_srcdir}/callouts
|
||||
|
||||
###########################################
|
||||
# DHCP test library
|
||||
# Test libraries
|
||||
###########################################
|
||||
|
||||
noinst_LTLIBRARIES = libtest-dhcp.la
|
||||
noinst_LTLIBRARIES = libtest-dhcp.la libtest-policy-hosts.la
|
||||
|
||||
###########################################
|
||||
# DHCP test library
|
||||
###########################################
|
||||
|
||||
libtest_dhcp_la_SOURCES = \
|
||||
nm-ip4-config.c \
|
||||
|
|
@ -53,6 +57,20 @@ libtest_dhcp_la_LIBADD = \
|
|||
$(DBUS_LIBS) \
|
||||
$(LIBNL_LIBS)
|
||||
|
||||
###########################################
|
||||
# Hosts policy test library
|
||||
###########################################
|
||||
|
||||
libtest_policy_hosts_la_SOURCES = \
|
||||
nm-policy-hosts.c \
|
||||
nm-policy-hosts.h
|
||||
|
||||
libtest_policy_hosts_la_CPPFLAGS = \
|
||||
$(GLIB_CFLAGS)
|
||||
|
||||
libtest_policy_hosts_la_LIBADD = \
|
||||
$(GLIB_LIBS)
|
||||
|
||||
|
||||
###########################################
|
||||
# NetworkManager
|
||||
|
|
@ -99,6 +117,8 @@ NetworkManager_SOURCES = \
|
|||
NetworkManager.c \
|
||||
NetworkManagerPolicy.c \
|
||||
NetworkManagerPolicy.h \
|
||||
nm-policy-hosts.c \
|
||||
nm-policy-hosts.h \
|
||||
NetworkManagerUtils.c \
|
||||
NetworkManagerUtils.h \
|
||||
NetworkManagerSystem.c \
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright (C) 2004 - 2008 Red Hat, Inc.
|
||||
* Copyright (C) 2004 - 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2007 - 2008 Novell, Inc.
|
||||
*/
|
||||
|
||||
|
|
@ -41,6 +41,7 @@
|
|||
#include "nm-named-manager.h"
|
||||
#include "nm-vpn-manager.h"
|
||||
#include "nm-modem.h"
|
||||
#include "nm-policy-hosts.h"
|
||||
|
||||
typedef struct LookupThread LookupThread;
|
||||
|
||||
|
|
@ -252,195 +253,64 @@ get_best_device (NMManager *manager, NMActRequest **out_req)
|
|||
return best;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_localhost_mapping (const char *str)
|
||||
{
|
||||
return (!strncmp (str, "127.0.0.1", strlen ("127.0.0.1")) && strstr (str, "localhost"));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
find_token (const char *line, const char *token)
|
||||
{
|
||||
const char *start = line, *p = line;
|
||||
|
||||
g_return_val_if_fail (line != NULL, FALSE);
|
||||
g_return_val_if_fail (token != NULL, FALSE);
|
||||
g_return_val_if_fail (strlen (token) > 0, FALSE);
|
||||
|
||||
/* Walk through the line to find the next whitespace character */
|
||||
while (p <= line + strlen (line)) {
|
||||
if (isblank (*p) || (*p == '\0')) {
|
||||
/* Token starts with 'start' and ends with 'end' */
|
||||
if ((p > start) && *start && !strncmp (start, token, (p - start)))
|
||||
return TRUE; /* found */
|
||||
|
||||
/* not found; advance start and continue looking */
|
||||
start = p + 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Testcase for find_token; break it out and add it to the testsuite */
|
||||
|
||||
typedef struct {
|
||||
const char *line;
|
||||
const char *token;
|
||||
gboolean expected;
|
||||
} Foo;
|
||||
|
||||
static Foo foo[] = {
|
||||
{ "127.0.0.1\tfoobar\tblah", "blah", TRUE },
|
||||
{ "", "blah", FALSE },
|
||||
{ "1.1.1.1\tbork\tfoo", "blah", FALSE },
|
||||
{ "127.0.0.1 foobar\tblah", "blah", TRUE },
|
||||
{ "127.0.0.1 foobar blah", "blah", TRUE },
|
||||
{ "192.168.1.1 blah borkbork", "blah", TRUE },
|
||||
{ "192.168.1.1 foobar\tblah borkbork", "blah", TRUE },
|
||||
{ "192.168.1.1\tfoobar\tblah\tborkbork", "blah", TRUE },
|
||||
{ "192.168.1.1 \tfoobar \tblah \tborkbork\t ", "blah", TRUE },
|
||||
{ "\t\t\t\t \t\t\tasdfadf a\t\t\t\t\t \t\t\t\t\t ", "blah", FALSE },
|
||||
{ NULL, NULL, FALSE }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Foo *iter = &foo[0];
|
||||
|
||||
while (iter->line) {
|
||||
if (find_token (iter->line, iter->token) != iter->expected) {
|
||||
g_message ("Failed: '%s' <= '%s' (%d)", iter->line, iter->token, iter->expected);
|
||||
return 1;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
|
||||
g_message ("Success");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define FALLBACK_HOSTNAME "localhost.localdomain"
|
||||
|
||||
static gboolean
|
||||
update_etc_hosts (const char *hostname)
|
||||
update_etc_hosts (const char *hostname, gboolean *out_changed)
|
||||
{
|
||||
char *contents = NULL;
|
||||
char **lines = NULL, **line, **host_mapping = NULL;
|
||||
char **lines = NULL;
|
||||
GError *error = NULL;
|
||||
gboolean initial_comments = TRUE;
|
||||
gboolean added = FALSE;
|
||||
GString *new_contents = NULL;
|
||||
gsize contents_len = 0;
|
||||
GString *new_contents;
|
||||
gboolean success = FALSE;
|
||||
|
||||
g_return_val_if_fail (hostname != NULL, FALSE);
|
||||
g_return_val_if_fail (out_changed != NULL, FALSE);
|
||||
|
||||
if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) {
|
||||
nm_warning ("%s: couldn't read " SYSCONFDIR "/hosts: (%d) %s",
|
||||
__func__, error ? error->code : 0,
|
||||
(error && error->message) ? error->message : "(unknown)");
|
||||
if (error)
|
||||
g_error_free (error);
|
||||
} else {
|
||||
lines = g_strsplit_set (contents, "\n\r", 0);
|
||||
g_free (contents);
|
||||
}
|
||||
|
||||
new_contents = g_string_sized_new (contents_len ? contents_len + 100 : 200);
|
||||
if (!new_contents) {
|
||||
nm_warning ("%s: not enough memory to update " SYSCONFDIR "/hosts", __func__);
|
||||
g_clear_error (&error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Two-pass modification of /etc/hosts:
|
||||
*
|
||||
* 1) Look for a non-comment, non-localhost line that contains the current
|
||||
* hostname. Mark that line.
|
||||
*
|
||||
* 2) For each line in the existing /etc/hosts, add it to the new /etc/hosts
|
||||
* unless it starts with 127.0.0.1 and is right after the initial comments
|
||||
* (if any) and contains "localhost".
|
||||
*/
|
||||
/* Get the new /etc/hosts contents */
|
||||
lines = g_strsplit_set (contents, "\n\r", 0);
|
||||
new_contents = nm_policy_get_etc_hosts ((const char **) lines,
|
||||
contents_len,
|
||||
hostname,
|
||||
FALLBACK_HOSTNAME,
|
||||
&error);
|
||||
g_strfreev (lines);
|
||||
g_free (contents);
|
||||
|
||||
/* Find any existing hostname mapping */
|
||||
for (line = lines; lines && *line; line++) {
|
||||
/* Look for any line that (a) contains the current hostname, and
|
||||
* (b) does not start with '127.0.0.1' and contain 'localhost'.
|
||||
*/
|
||||
if ( strlen (*line)
|
||||
&& (*line[0] != '#')
|
||||
&& find_token (*line, hostname)
|
||||
&& !is_localhost_mapping (*line)) {
|
||||
host_mapping = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (new_contents) {
|
||||
nm_info ("Updating /etc/hosts with new system hostname");
|
||||
|
||||
/* Construct the new hosts file; replace any 127.0.0.1 entry that is at the
|
||||
* beginning of the file or right after initial comments and contains
|
||||
* the string 'localhost'. If there is no 127.0.0.1 entry at the beginning
|
||||
* or after initial comments that contains 'localhost', add one there
|
||||
* and ignore any other 127.0.0.1 entries that contain 'localhost'.
|
||||
*/
|
||||
for (line = lines, initial_comments = TRUE; lines && *line; line++) {
|
||||
gboolean add_line = TRUE;
|
||||
|
||||
/* This is the first line after the initial comments */
|
||||
if (strlen (*line) && initial_comments && (*line[0] != '#')) {
|
||||
initial_comments = FALSE;
|
||||
|
||||
/* If some other line contained the hostname, make a simple
|
||||
* localhost mapping and assume the user knows what they are doing
|
||||
* with their manual hostname entry. Otherwise if the hostname
|
||||
* wasn't found somewhere else, add it to the localhost mapping line
|
||||
* to make sure it's mapped to something.
|
||||
*/
|
||||
if (host_mapping)
|
||||
g_string_append (new_contents, "127.0.0.1");
|
||||
else
|
||||
g_string_append_printf (new_contents, "127.0.0.1\t%s", hostname);
|
||||
|
||||
if (strcmp (hostname, FALLBACK_HOSTNAME))
|
||||
g_string_append_printf (new_contents, "\t" FALLBACK_HOSTNAME);
|
||||
|
||||
g_string_append (new_contents, "\tlocalhost\n");
|
||||
added = TRUE;
|
||||
|
||||
/* Don't add the entry if it's supposed to be the actual localhost reverse mapping */
|
||||
if (is_localhost_mapping (*line))
|
||||
add_line = FALSE;
|
||||
g_clear_error (&error);
|
||||
/* And actually update /etc/hosts */
|
||||
if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) {
|
||||
nm_warning ("%s: couldn't update " SYSCONFDIR "/hosts: (%d) %s",
|
||||
__func__, error ? error->code : 0,
|
||||
(error && error->message) ? error->message : "(unknown)");
|
||||
g_clear_error (&error);
|
||||
} else {
|
||||
success = TRUE;
|
||||
*out_changed = TRUE;
|
||||
}
|
||||
|
||||
if (add_line) {
|
||||
g_string_append (new_contents, *line);
|
||||
/* Only append the new line if this isn't the last line in the file */
|
||||
if (*(line+1))
|
||||
g_string_append_c (new_contents, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
/* Hmm, /etc/hosts was empty for some reason */
|
||||
if (!added) {
|
||||
g_string_append (new_contents, "# Do not remove the following line, or various programs\n");
|
||||
g_string_append (new_contents, "# that require network functionality will fail.\n");
|
||||
g_string_append (new_contents, "127.0.0.1\t" FALLBACK_HOSTNAME "\tlocalhost\n");
|
||||
}
|
||||
|
||||
error = NULL;
|
||||
if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) {
|
||||
nm_warning ("%s: couldn't update " SYSCONFDIR "/hosts: (%d) %s",
|
||||
__func__, error ? error->code : 0,
|
||||
(error && error->message) ? error->message : "(unknown)");
|
||||
if (error)
|
||||
g_error_free (error);
|
||||
} else
|
||||
g_string_free (new_contents, TRUE);
|
||||
} else if (!error) {
|
||||
/* No change required */
|
||||
success = TRUE;
|
||||
} else {
|
||||
nm_warning ("%s: couldn't read " SYSCONFDIR "/hosts: (%d) %s",
|
||||
__func__, error->code, error->message ? error->message : "(unknown)");
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_string_free (new_contents, TRUE);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
@ -450,6 +320,7 @@ set_system_hostname (const char *new_hostname, const char *msg)
|
|||
char old_hostname[HOST_NAME_MAX + 1];
|
||||
int ret = 0;
|
||||
const char *name = new_hostname ? new_hostname : FALLBACK_HOSTNAME;
|
||||
gboolean set_hostname = TRUE, changed = FALSE;
|
||||
|
||||
old_hostname[HOST_NAME_MAX] = '\0';
|
||||
errno = 0;
|
||||
|
|
@ -458,30 +329,40 @@ set_system_hostname (const char *new_hostname, const char *msg)
|
|||
nm_warning ("%s: couldn't get the system hostname: (%d) %s",
|
||||
__func__, errno, strerror (errno));
|
||||
} else {
|
||||
/* Do nothing if the hostname isn't actually changing */
|
||||
/* Don't set the hostname if it isn't actually changing */
|
||||
if ( (new_hostname && !strcmp (old_hostname, new_hostname))
|
||||
|| (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME)))
|
||||
set_hostname = FALSE;
|
||||
}
|
||||
|
||||
if (set_hostname) {
|
||||
nm_info ("Setting system hostname to '%s' (%s)", name, msg);
|
||||
ret = sethostname (name, strlen (name));
|
||||
if (ret != 0) {
|
||||
nm_warning ("%s: couldn't set the system hostname to '%s': (%d) %s",
|
||||
__func__, name, errno, strerror (errno));
|
||||
return;
|
||||
}
|
||||
|
||||
nm_info ("Setting system hostname to '%s' (%s)", name, msg);
|
||||
|
||||
ret = sethostname (name, strlen (name));
|
||||
if (ret == 0) {
|
||||
if (!update_etc_hosts (name)) {
|
||||
/* error updating /etc/hosts; fallback to localhost.localdomain */
|
||||
nm_info ("Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)");
|
||||
ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME));
|
||||
if (ret != 0) {
|
||||
nm_warning ("%s: couldn't set the fallback system hostname (%s): (%d) %s",
|
||||
__func__, FALLBACK_HOSTNAME, errno, strerror (errno));
|
||||
}
|
||||
}
|
||||
nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL);
|
||||
} else {
|
||||
nm_warning ("%s: couldn't set the system hostname to '%s': (%d) %s",
|
||||
__func__, name, errno, strerror (errno));
|
||||
}
|
||||
|
||||
/* But still always try updating /etc/hosts just in case the hostname
|
||||
* changed while NM wasn't running; we need to make sure that /etc/hosts
|
||||
* has valid mappings for '127.0.0.1' and the current system hostname. If
|
||||
* those exist, update_etc_hosts() will just return and won't touch
|
||||
* /etc/hosts at all.
|
||||
*/
|
||||
if (!update_etc_hosts (name, &changed)) {
|
||||
/* error updating /etc/hosts; fallback to localhost.localdomain */
|
||||
nm_info ("Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)");
|
||||
ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME));
|
||||
if (ret != 0) {
|
||||
nm_warning ("%s: couldn't set the fallback system hostname (%s): (%d) %s",
|
||||
__func__, FALLBACK_HOSTNAME, errno, strerror (errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
173
src/nm-policy-hosts.c
Normal file
173
src/nm-policy-hosts.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* NetworkManager -- Network link manager
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright (C) 2004 - 2010 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "nm-policy-hosts.h"
|
||||
|
||||
gboolean
|
||||
nm_policy_hosts_find_token (const char *line, const char *token)
|
||||
{
|
||||
const char *start = line, *p = line;
|
||||
|
||||
g_return_val_if_fail (line != NULL, FALSE);
|
||||
g_return_val_if_fail (token != NULL, FALSE);
|
||||
g_return_val_if_fail (strlen (token) > 0, FALSE);
|
||||
|
||||
/* Walk through the line to find the next whitespace character */
|
||||
while (p <= line + strlen (line)) {
|
||||
if (isblank (*p) || (*p == '\0')) {
|
||||
/* Token starts with 'start' and ends with 'end' */
|
||||
if ((p > start) && *start && (p - start == strlen (token)) && !strncmp (start, token, (p - start)))
|
||||
return TRUE; /* found */
|
||||
|
||||
/* not found; advance start and continue looking */
|
||||
start = p + 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_local_mapping (const char *str, const char *hostname)
|
||||
{
|
||||
return ( !strncmp (str, "127.0.0.1", strlen ("127.0.0.1"))
|
||||
&& nm_policy_hosts_find_token (str, hostname ? hostname : "localhost"));
|
||||
}
|
||||
|
||||
GString *
|
||||
nm_policy_get_etc_hosts (const char **lines,
|
||||
gsize existing_len,
|
||||
const char *hostname,
|
||||
const char *fallback_hostname,
|
||||
GError **error)
|
||||
{
|
||||
GString *contents = NULL;
|
||||
const char **line;
|
||||
gboolean found_host_nonlocal = FALSE;
|
||||
gboolean found_host = FALSE;
|
||||
gboolean found_localhost = FALSE;
|
||||
gboolean initial_comments = TRUE;
|
||||
gboolean added = FALSE;
|
||||
|
||||
g_return_val_if_fail (lines != NULL, FALSE);
|
||||
g_return_val_if_fail (hostname != NULL, FALSE);
|
||||
|
||||
/* /etc/hosts needs at least two things:
|
||||
*
|
||||
* 1) current hostname mapped to any address
|
||||
* 2) 'localhost' mapped to 127.0.0.1
|
||||
*
|
||||
* If both these conditions exist in /etc/hosts, we don't need to bother
|
||||
* updating the file.
|
||||
*/
|
||||
|
||||
/* Look for the two cases from above */
|
||||
for (line = lines; lines && *line; line++) {
|
||||
if (strlen (*line) && (*line[0] != '#')) {
|
||||
if (nm_policy_hosts_find_token (*line, hostname)) {
|
||||
if (!is_local_mapping (*line, "localhost")) {
|
||||
/* hostname is not on a 127.0.0.1 line or the line does not
|
||||
* contain 'localhost'.
|
||||
*/
|
||||
found_host_nonlocal = TRUE;
|
||||
}
|
||||
found_host = TRUE;
|
||||
}
|
||||
|
||||
if (is_local_mapping (*line, "localhost")) {
|
||||
/* a 127.0.0.1 line containing 'localhost' */
|
||||
found_localhost = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_localhost && found_host)
|
||||
return NULL; /* No update required */
|
||||
}
|
||||
|
||||
contents = g_string_sized_new (existing_len ? existing_len + 100 : 200);
|
||||
if (!contents) {
|
||||
g_set_error_literal (error, 0, 0, "not enough memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Construct the new hosts file; replace any 127.0.0.1 entry that is at the
|
||||
* beginning of the file or right after initial comments and contains
|
||||
* the string 'localhost'. If there is no 127.0.0.1 entry at the beginning
|
||||
* or after initial comments that contains 'localhost', add one there
|
||||
* and ignore any other 127.0.0.1 entries that contain 'localhost'.
|
||||
*/
|
||||
for (line = lines, initial_comments = TRUE; lines && *line; line++) {
|
||||
gboolean add_line = TRUE;
|
||||
|
||||
/* This is the first line after the initial comments */
|
||||
if (strlen (*line) && initial_comments && (*line[0] != '#')) {
|
||||
initial_comments = FALSE;
|
||||
|
||||
/* If some other line contained the hostname but not 'localhost',
|
||||
* make a simple localhost mapping and assume the user knows what
|
||||
* they are doing with their manual hostname entry. Otherwise if
|
||||
* the hostname wasn't found somewhere else, add it to the localhost
|
||||
* mapping line to make sure it's mapped to something.
|
||||
*/
|
||||
if (found_host_nonlocal)
|
||||
g_string_append (contents, "127.0.0.1");
|
||||
else
|
||||
g_string_append_printf (contents, "127.0.0.1\t%s", hostname);
|
||||
|
||||
if (strcmp (hostname, fallback_hostname)) {
|
||||
g_string_append_printf (contents, "\t%s", fallback_hostname);
|
||||
/* Don't add a standalone 'localhost.localdomain' 127 mapping */
|
||||
if (is_local_mapping (*line, fallback_hostname))
|
||||
add_line = FALSE;
|
||||
}
|
||||
|
||||
g_string_append (contents, "\tlocalhost\n");
|
||||
added = TRUE;
|
||||
|
||||
/* Don't add the original line if it is a 'localhost' mapping */
|
||||
if (is_local_mapping (*line, "localhost"))
|
||||
add_line = FALSE;
|
||||
}
|
||||
|
||||
if (add_line) {
|
||||
g_string_append (contents, *line);
|
||||
/* Only append the new line if this isn't the last line in the file */
|
||||
if (*(line+1))
|
||||
g_string_append_c (contents, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
/* Hmm, /etc/hosts was empty for some reason */
|
||||
if (!added) {
|
||||
g_string_append (contents, "# Do not remove the following line, or various programs\n");
|
||||
g_string_append (contents, "# that require network functionality will fail.\n");
|
||||
g_string_append_printf (contents, "127.0.0.1\t%s\tlocalhost\n", fallback_hostname);
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
36
src/nm-policy-hosts.h
Normal file
36
src/nm-policy-hosts.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/* NetworkManager -- Network link manager
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright (C) 2004 - 2010 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
#ifndef NM_POLICY_HOSTS_H
|
||||
#define NM_POLICT_HOSTS_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
GString *nm_policy_get_etc_hosts (const char **lines,
|
||||
gsize existing_len,
|
||||
const char *hostname,
|
||||
const char *fallback_hostname,
|
||||
GError **error);
|
||||
|
||||
/* Only for testcases; don't use outside of nm-policy-hosts.c */
|
||||
gboolean nm_policy_hosts_find_token (const char *line, const char *token);
|
||||
|
||||
#endif /* NM_POLICY_HOSTS_H */
|
||||
|
||||
|
|
@ -6,7 +6,9 @@ INCLUDES = \
|
|||
-I$(top_srcdir)/src \
|
||||
-I$(top_builddir)/src
|
||||
|
||||
noinst_PROGRAMS = test-dhcp-options
|
||||
noinst_PROGRAMS = test-dhcp-options test-policy-hosts
|
||||
|
||||
####### DHCP options test #######
|
||||
|
||||
test_dhcp_options_SOURCES = \
|
||||
test-dhcp-options.c
|
||||
|
|
@ -24,10 +26,23 @@ test_dhcp_options_LDADD = \
|
|||
$(GLIB_LIBS) \
|
||||
$(DBUS_LIBS)
|
||||
|
||||
####### policy /etc/hosts test #######
|
||||
|
||||
test_policy_hosts_SOURCES = \
|
||||
test-policy-hosts.c
|
||||
|
||||
test_policy_hosts_CPPFLAGS = \
|
||||
$(GLIB_CFLAGS)
|
||||
|
||||
test_policy_hosts_LDADD = \
|
||||
$(top_builddir)/src/libtest-policy-hosts.la \
|
||||
$(GLIB_LIBS)
|
||||
|
||||
if WITH_TESTS
|
||||
|
||||
check-local: test-dhcp-options
|
||||
$(abs_builddir)/test-dhcp-options
|
||||
$(abs_builddir)/test-policy-hosts
|
||||
|
||||
endif
|
||||
|
||||
|
|
|
|||
394
src/tests/test-policy-hosts.c
Normal file
394
src/tests/test-policy-hosts.c
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright (C) 2010 Red Hat, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nm-policy-hosts.h"
|
||||
|
||||
#define FALLBACK_HOSTNAME "localhost.localdomain"
|
||||
|
||||
static void
|
||||
test_generic (const char *before,
|
||||
const char *after,
|
||||
const char *hostname,
|
||||
gboolean expect_error)
|
||||
{
|
||||
char **lines;
|
||||
GString *newc;
|
||||
GError *error = NULL;
|
||||
|
||||
/* Get the new /etc/hosts contents */
|
||||
lines = g_strsplit_set (before, "\n\r", 0);
|
||||
newc = nm_policy_get_etc_hosts ((const char **) lines,
|
||||
strlen (before),
|
||||
hostname,
|
||||
FALLBACK_HOSTNAME,
|
||||
&error);
|
||||
g_strfreev (lines);
|
||||
|
||||
if (expect_error) {
|
||||
g_assert (newc == NULL);
|
||||
g_assert (error != NULL);
|
||||
g_clear_error (&error);
|
||||
} else if (after == NULL) {
|
||||
/* No change to /etc/hosts required */
|
||||
g_assert (newc == NULL);
|
||||
g_assert (error == NULL);
|
||||
} else {
|
||||
g_assert (newc != NULL);
|
||||
g_assert (error == NULL);
|
||||
|
||||
#if 0
|
||||
g_message ("\n--------------------------------------\n"
|
||||
"%s"
|
||||
"--------------------------------------",
|
||||
newc->str);
|
||||
#endif
|
||||
g_assert (strlen (newc->str) == strlen (after));
|
||||
g_assert (strcmp (newc->str, after) == 0);
|
||||
g_string_free (newc, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *generic_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_generic (void)
|
||||
{
|
||||
test_generic (generic_before, NULL, "localhost.localdomain", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *generic_no_boilerplate_before = \
|
||||
"127.0.0.1 localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_generic_no_boilerplate (void)
|
||||
{
|
||||
test_generic (generic_no_boilerplate_before, NULL, "localhost.localdomain", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *generic_no_boilerplate_no_lh_before = \
|
||||
"127.0.0.1 localhost.localdomain\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static const char *generic_no_boilerplate_no_lh_after = \
|
||||
"127.0.0.1 localhost\n"
|
||||
"127.0.0.1 localhost.localdomain\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_generic_no_boilerplate_no_lh (void)
|
||||
{
|
||||
test_generic (generic_no_boilerplate_no_lh_before,
|
||||
generic_no_boilerplate_no_lh_after,
|
||||
"localhost.localdomain",
|
||||
FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
|
||||
static const char *generic_no_boilerplate_no_lh_no_host_before = \
|
||||
"127.0.0.1 localhost.localdomain\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static const char *generic_no_boilerplate_no_lh_no_host_after = \
|
||||
"127.0.0.1 comet localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_generic_no_boilerplate_no_lh_no_host (void)
|
||||
{
|
||||
test_generic (generic_no_boilerplate_no_lh_no_host_before,
|
||||
generic_no_boilerplate_no_lh_no_host_after,
|
||||
"comet",
|
||||
FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
static const char *named_generic_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 playboy localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_named_generic (void)
|
||||
{
|
||||
test_generic (named_generic_before, NULL, "playboy", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *named_non127_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"192.168.1.2 tomcat\n";
|
||||
|
||||
static void
|
||||
test_hosts_named_non127 (void)
|
||||
{
|
||||
test_generic (named_non127_before, NULL, "tomcat", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *named2_non127_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"192.168.1.2 tomcat\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"127.0.0.1 srx.main.ebayrtm.com\n"
|
||||
"127.0.0.1 cdn5.tribalfusion.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_named2_non127 (void)
|
||||
{
|
||||
test_generic (named2_non127_before, NULL, "tomcat", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *named_no_lh_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"192.168.1.2 tomcat\n";
|
||||
|
||||
static const char *named_no_lh_after = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"192.168.1.2 tomcat\n";
|
||||
|
||||
static void
|
||||
test_hosts_named_no_localhost (void)
|
||||
{
|
||||
test_generic (named_no_lh_before, named_no_lh_after, "tomcat", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *no_lh_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 tomcat\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static const char *no_lh_after = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain localhost\n"
|
||||
"127.0.0.1 tomcat\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_no_localhost (void)
|
||||
{
|
||||
test_generic (no_lh_before, no_lh_after, "tomcat", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *named_last_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 sparcbook.ausil.us\n"
|
||||
"::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 sparcbook.ausil.us\n";
|
||||
|
||||
static void
|
||||
test_hosts_named_last (void)
|
||||
{
|
||||
test_generic (named_last_before, NULL, "sparcbook.ausil.us", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *no_host_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"127.0.0.1 srx.main.ebayrtm.com\n"
|
||||
"127.0.0.1 cdn5.tribalfusion.com\n"
|
||||
"127.0.0.1 a.tribalfusion.com\n";
|
||||
|
||||
static const char *no_host_after = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 comet localhost.localdomain localhost\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"127.0.0.1 srx.main.ebayrtm.com\n"
|
||||
"127.0.0.1 cdn5.tribalfusion.com\n"
|
||||
"127.0.0.1 a.tribalfusion.com\n";
|
||||
|
||||
static void
|
||||
test_hosts_no_host (void)
|
||||
{
|
||||
test_generic (no_host_before, no_host_after, "comet", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
static const char *long_before = \
|
||||
"# Do not remove the following line, or various programs\n"
|
||||
"# that require network functionality will fail.\n"
|
||||
"127.0.0.1 localhost.localdomain localhost comet\n"
|
||||
"::1 localhost6.localdomain6 localhost6\n"
|
||||
"\n"
|
||||
"127.0.0.1 lcmd.us.intellitxt.com\n"
|
||||
"127.0.0.1 adserver.adtech.de\n"
|
||||
"127.0.0.1 a.as-us.falkag.net\n"
|
||||
"127.0.0.1 a.as-eu.falkag.net\n"
|
||||
"127.0.0.1 ads.doubleclick.com\n"
|
||||
"\n"
|
||||
"# random comment\n"
|
||||
"127.0.0.1 m1.2mdn.net\n"
|
||||
"127.0.0.1 ds.serving-sys.com\n"
|
||||
"127.0.0.1 pagead2.googlesyndication.com\n"
|
||||
"127.0.0.1 ad.doubleclick.com\n"
|
||||
"127.0.0.1 ad.doubleclick.net\n"
|
||||
"127.0.0.1 oascentral.movietickets.com\n"
|
||||
"127.0.0.1 view.atdmt.com\n"
|
||||
"127.0.0.1 ads.chumcity.com\n"
|
||||
"127.0.0.1 ads.as4x.tmcs.net\n"
|
||||
"127.0.0.1 n4403ad.doubleclick.net\n"
|
||||
"127.0.0.1 www.assoc-amazon.com\n"
|
||||
"127.0.0.1 s25.sitemeter.com\n"
|
||||
"127.0.0.1 adlog.com.com\n"
|
||||
"127.0.0.1 ahs.laptopmag.com\n"
|
||||
"127.0.0.1 altfarm.mediaplex.com\n"
|
||||
"127.0.0.1 ads.addynamix.com\n"
|
||||
"127.0.0.1 srx.main.ebayrtm.com\n"
|
||||
"127.0.0.1 cdn5.tribalfusion.com\n"
|
||||
"127.0.0.1 a.tribalfusion.com\n";
|
||||
|
||||
|
||||
static void
|
||||
test_hosts_long (void)
|
||||
{
|
||||
test_generic (long_before, NULL, "comet", FALSE);
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
typedef struct {
|
||||
const char *line;
|
||||
const char *token;
|
||||
gboolean expected;
|
||||
} Foo;
|
||||
|
||||
static Foo foo[] = {
|
||||
/* Using \t here to easily differentiate tabs vs. spaces for testing */
|
||||
{ "127.0.0.1\tfoobar\tblah", "blah", TRUE },
|
||||
{ "", "blah", FALSE },
|
||||
{ "1.1.1.1\tbork\tfoo", "blah", FALSE },
|
||||
{ "127.0.0.1 foobar\tblah", "blah", TRUE },
|
||||
{ "127.0.0.1 foobar blah", "blah", TRUE },
|
||||
{ "127.0.0.1 localhost", "localhost.localdomain", FALSE },
|
||||
{ "192.168.1.1 blah borkbork", "blah", TRUE },
|
||||
{ "192.168.1.1 foobar\tblah borkbork", "blah", TRUE },
|
||||
{ "192.168.1.1\tfoobar\tblah\tborkbork", "blah", TRUE },
|
||||
{ "192.168.1.1 \tfoobar \tblah \tborkbork\t ", "blah", TRUE },
|
||||
{ "\t\t\t\t \t\t\tasdfadf a\t\t\t\t\t \t\t\t\t\t ", "blah", FALSE },
|
||||
{ NULL, NULL, FALSE }
|
||||
};
|
||||
|
||||
static void
|
||||
test_find_token (void)
|
||||
{
|
||||
Foo *iter = &foo[0];
|
||||
|
||||
while (iter->line) {
|
||||
gboolean found;
|
||||
|
||||
found = nm_policy_hosts_find_token (iter->line, iter->token);
|
||||
if (found != iter->expected) {
|
||||
g_warning ("find-token: unexpected token result %d for '%s' <= '%s' (expected %d)",
|
||||
found, iter->line, iter->token, iter->expected);
|
||||
}
|
||||
g_assert (found == iter->expected);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*TCFunc)(void);
|
||||
|
||||
#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
GTestSuite *suite;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
suite = g_test_get_root ();
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_find_token, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_generic, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh_no_host, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_named_generic, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_named_non127, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_named2_non127, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_named_no_localhost, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_no_localhost, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_named_last, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_no_host, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_hosts_long, NULL));
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue