--- /dev/null
+[func]* fdupont
+ Modified the behavior of required client classes
+ configured without a test expression: they are now
+ unconditionally added as they always evaluate to
+ true (vs false previously).
+ (Gitlab #3388)
and shared network, i.e. in the same order from the way in which
``option-data`` is processed.
+Since Kea version 2.7.4 required client classes configured without
+a test expression are unconditionally added, i.e. they are considered
+to always be evaluated to ``true``.
+
.. note::
Vendor-Identifying Vendor Options are a special case: for all other
(pd-)pool, subnet, and shared network, i.e. in the same order from the
way in which ``option-data`` is processed.
+Since Kea version 2.7.4 required client classes configured without
+a test expression are unconditionally added, i.e. they are considered
+to always be evaluated to ``true``.
+
.. _dhcp6-ddns-config:
DDNS for DHCPv6
extern const isc::log::MessageID DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION = "DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION";
extern const isc::log::MessageID DHCP4_CLASS_ASSIGNED = "DHCP4_CLASS_ASSIGNED";
extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED = "DHCP4_CLASS_UNCONFIGURED";
-extern const isc::log::MessageID DHCP4_CLASS_UNDEFINED = "DHCP4_CLASS_UNDEFINED";
-extern const isc::log::MessageID DHCP4_CLASS_UNTESTABLE = "DHCP4_CLASS_UNTESTABLE";
extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES = "DHCP4_CLIENTID_IGNORED_FOR_LEASES";
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA = "DHCP4_CLIENT_FQDN_DATA";
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS = "DHCP4_CLIENT_FQDN_PROCESS";
extern const isc::log::MessageID DHCP4_REQUEST = "DHCP4_REQUEST";
extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_ERROR = "DHCP4_REQUIRED_CLASS_EVAL_ERROR";
extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_RESULT = "DHCP4_REQUIRED_CLASS_EVAL_RESULT";
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNDEFINED = "DHCP4_REQUIRED_CLASS_UNDEFINED";
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNTESTABLE = "DHCP4_REQUIRED_CLASS_UNTESTABLE";
extern const isc::log::MessageID DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED = "DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED";
extern const isc::log::MessageID DHCP4_RESERVED_HOSTNAME_ASSIGNED = "DHCP4_RESERVED_HOSTNAME_ASSIGNED";
extern const isc::log::MessageID DHCP4_RESPONSE_DATA = "DHCP4_RESPONSE_DATA";
"DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION", "%1: client packet has been assigned to the following classes: %2",
"DHCP4_CLASS_ASSIGNED", "%1: client packet has been assigned to the following class: %2",
"DHCP4_CLASS_UNCONFIGURED", "%1: client packet belongs to an unconfigured class: %2",
- "DHCP4_CLASS_UNDEFINED", "required class %1 has no definition",
- "DHCP4_CLASS_UNTESTABLE", "required class %1 has no test expression",
"DHCP4_CLIENTID_IGNORED_FOR_LEASES", "%1: not using client identifier for lease allocation for subnet %2",
"DHCP4_CLIENT_FQDN_DATA", "%1: Client sent FQDN option: %2",
"DHCP4_CLIENT_FQDN_PROCESS", "%1: processing Client FQDN option",
"DHCP4_REQUEST", "%1: server is processing DHCPREQUEST with hint=%2",
"DHCP4_REQUIRED_CLASS_EVAL_ERROR", "%1: Expression '%2' evaluated to %3",
"DHCP4_REQUIRED_CLASS_EVAL_RESULT", "%1: Expression '%2' evaluated to %3",
+ "DHCP4_REQUIRED_CLASS_UNDEFINED", "required class %1 has no definition",
+ "DHCP4_REQUIRED_CLASS_UNTESTABLE", "required class %1 has no test expression",
"DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED", "Multi-threading is enabled and host reservations lookup is always performed first.",
"DHCP4_RESERVED_HOSTNAME_ASSIGNED", "%1: server assigned reserved hostname %2",
"DHCP4_RESPONSE_DATA", "%1: responding with packet %2 (type %3), packet details: %4",
extern const isc::log::MessageID DHCP4_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION;
extern const isc::log::MessageID DHCP4_CLASS_ASSIGNED;
extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED;
-extern const isc::log::MessageID DHCP4_CLASS_UNDEFINED;
-extern const isc::log::MessageID DHCP4_CLASS_UNTESTABLE;
extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES;
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA;
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS;
extern const isc::log::MessageID DHCP4_REQUEST;
extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_ERROR;
extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_EVAL_RESULT;
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNDEFINED;
+extern const isc::log::MessageID DHCP4_REQUIRED_CLASS_UNTESTABLE;
extern const isc::log::MessageID DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED;
extern const isc::log::MessageID DHCP4_RESERVED_HOSTNAME_ASSIGNED;
extern const isc::log::MessageID DHCP4_RESPONSE_DATA;
before the classification was added to Kea is used, or class naming is
inconsistent.
-% DHCP4_CLASS_UNDEFINED required class %1 has no definition
-Logged at debug log level 40.
-This debug message informs that a class is listed for required evaluation but
-has no definition.
-
-% DHCP4_CLASS_UNTESTABLE required class %1 has no test expression
-Logged at debug log level 40.
-This debug message informs that a class was listed for required evaluation but
-its definition does not include a test expression to evaluate.
-
% DHCP4_CLIENTID_IGNORED_FOR_LEASES %1: not using client identifier for lease allocation for subnet %2
Logged at debug log level 50.
This debug message is issued when the server is processing the DHCPv4 message
been successfully evaluated. The client class name and the result value of the
evaluation are printed.
+% DHCP4_REQUIRED_CLASS_UNDEFINED required class %1 has no definition
+Logged at debug log level 40.
+This debug message informs that a class is listed for required evaluation but
+has no definition. The class is ignored.
+
+% DHCP4_REQUIRED_CLASS_UNTESTABLE required class %1 has no test expression
+Logged at debug log level 40.
+This debug message informs that a class was listed for required evaluation but
+its definition does not include a test expression to evaluate. The class is
+unconditionally added to the query.
+
% DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED Multi-threading is enabled and host reservations lookup is always performed first.
This is a message informing that host reservations lookup is performed before
lease lookup when multi-threading is enabled overwriting configured value.
for (auto const& cclass : classes) {
const ClientClassDefPtr class_def = dict->findClass(cclass);
if (!class_def) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNDEFINED)
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
+ DHCP4_REQUIRED_CLASS_UNDEFINED)
.arg(cclass);
+ // Ignore it as it can't have an attached action
continue;
}
const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
- // Nothing to do without an expression to evaluate
+ // Add a class without an expression to evaluate
if (!expr_ptr) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_UNTESTABLE)
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
+ DHCP4_REQUIRED_CLASS_UNTESTABLE)
.arg(cclass);
+ query->addClass(cclass);
continue;
}
// Evaluate the expression which can return false (no match),
EXPECT_EQ("10.0.0.3", addrs[0].toText());
}
+// This test checks a required class without a test entry can be
+// unconditionally added.
+TEST_F(ClassifyTest, requiredNoTest) {
+ std::string config =
+ "{"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"valid-lifetime\": 600,"
+ "\"client-classes\": ["
+ " {"
+ " \"name\": \"for-network\","
+ " \"option-data\": [ {"
+ " \"name\": \"domain-name-servers\","
+ " \"data\": \"10.0.0.3\""
+ " } ]"
+ " }"
+ "],"
+ "\"shared-networks\": [ {"
+ " \"name\": \"frog\","
+ " \"require-client-classes\": [ \"for-network\" ],"
+ " \"subnet4\": [ { "
+ " \"subnet\": \"10.0.0.0/24\","
+ " \"id\": 1,"
+ " \"pools\": [ { "
+ " \"pool\": \"10.0.0.10-10.0.0.100\""
+ " } ]"
+ " } ]"
+ "} ]"
+ "}";
+
+ // Create a client requesting domain-name-servers option
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ client.requestOptions(DHO_DOMAIN_NAME_SERVERS);
+
+ // Load the config and perform a DORA
+ configure(config, *client.getServer());
+ ASSERT_NO_THROW(client.doDORA());
+
+ // Check response
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+ EXPECT_EQ("10.0.0.10", resp->getYiaddr().toText());
+
+ // Check domain-name-servers option
+ OptionPtr opt = resp->getOption(DHO_DOMAIN_NAME_SERVERS);
+ ASSERT_TRUE(opt);
+ Option4AddrLstPtr servers =
+ boost::dynamic_pointer_cast<Option4AddrLst>(opt);
+ ASSERT_TRUE(servers);
+ auto addrs = servers->getAddresses();
+ ASSERT_EQ(1, addrs.size());
+ EXPECT_EQ("10.0.0.3", addrs[0].toText());
+}
+
+// This test checks a required class which is not defined is ignored.
+TEST_F(ClassifyTest, requiredNoDefined) {
+ std::string config =
+ "{"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"valid-lifetime\": 600,"
+ "\"shared-networks\": [ {"
+ " \"name\": \"frog\","
+ " \"require-client-classes\": [ \"for-network\" ],"
+ " \"subnet4\": [ { "
+ " \"subnet\": \"10.0.0.0/24\","
+ " \"id\": 1,"
+ " \"pools\": [ { "
+ " \"pool\": \"10.0.0.10-10.0.0.100\""
+ " } ]"
+ " } ]"
+ "} ]"
+ "}";
+
+ // Create a client requesting domain-name-servers option
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ client.requestOptions(DHO_DOMAIN_NAME_SERVERS);
+
+ // Load the config and perform a DORA
+ configure(config, *client.getServer());
+ ASSERT_NO_THROW(client.doDORA());
+
+ // Check response
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+ EXPECT_EQ("10.0.0.10", resp->getYiaddr().toText());
+
+ // Check domain-name-servers option
+ OptionPtr opt = resp->getOption(DHO_DOMAIN_NAME_SERVERS);
+ EXPECT_FALSE(opt);
+}
+
// This test checks the handling for the DROP special class.
TEST_F(ClassifyTest, dropClass) {
Dhcp4Client client(Dhcp4Client::SELECTING);
extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION = "DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION";
extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED = "DHCP6_CLASS_ASSIGNED";
extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED = "DHCP6_CLASS_UNCONFIGURED";
-extern const isc::log::MessageID DHCP6_CLASS_UNDEFINED = "DHCP6_CLASS_UNDEFINED";
-extern const isc::log::MessageID DHCP6_CLASS_UNTESTABLE = "DHCP6_CLASS_UNTESTABLE";
extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE = "DHCP6_CONFIG_COMPLETE";
extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL = "DHCP6_CONFIG_LOAD_FAIL";
extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE = "DHCP6_CONFIG_PACKET_QUEUE";
extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID = "DHCP6_RELEASE_PD_FAIL_WRONG_IAID";
extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_ERROR = "DHCP6_REQUIRED_CLASS_EVAL_ERROR";
extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_RESULT = "DHCP6_REQUIRED_CLASS_EVAL_RESULT";
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNDEFINED = "DHCP6_REQUIRED_CLASS_UNDEFINED";
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNTESTABLE = "DHCP6_REQUIRED_CLASS_UNTESTABLE";
extern const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL = "DHCP6_REQUIRED_OPTIONS_CHECK_FAIL";
extern const isc::log::MessageID DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED = "DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED";
extern const isc::log::MessageID DHCP6_RESPONSE_DATA = "DHCP6_RESPONSE_DATA";
"DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION", "%1: client packet has been assigned to the following classes: %2",
"DHCP6_CLASS_ASSIGNED", "%1: client packet has been assigned to the following class: %2",
"DHCP6_CLASS_UNCONFIGURED", "%1: client packet belongs to an unconfigured class: %2",
- "DHCP6_CLASS_UNDEFINED", "required class %1 has no definition",
- "DHCP6_CLASS_UNTESTABLE", "required class %1 has no test expression",
"DHCP6_CONFIG_COMPLETE", "DHCPv6 server has completed configuration: %1",
"DHCP6_CONFIG_LOAD_FAIL", "configuration error using file: %1, reason: %2",
"DHCP6_CONFIG_PACKET_QUEUE", "DHCPv6 packet queue info after configuration: %1",
"DHCP6_RELEASE_PD_FAIL_WRONG_IAID", "%1: client tried to release prefix %2/%3, but it used wrong IAID (expected %4, but got %5)",
"DHCP6_REQUIRED_CLASS_EVAL_ERROR", "%1: Expression '%2' evaluated to %3",
"DHCP6_REQUIRED_CLASS_EVAL_RESULT", "%1: Expression '%2' evaluated to %3",
+ "DHCP6_REQUIRED_CLASS_UNDEFINED", "required class %1 has no definition",
+ "DHCP6_REQUIRED_CLASS_UNTESTABLE", "required class %1 has no test expression",
"DHCP6_REQUIRED_OPTIONS_CHECK_FAIL", "%1: %2 message received from %3 failed the following check: %4",
"DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED", "Multi-threading is enabled and host reservations lookup is always performed first.",
"DHCP6_RESPONSE_DATA", "%1: responding with packet %2 (type %3), packet details: %4",
extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION;
extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED;
extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED;
-extern const isc::log::MessageID DHCP6_CLASS_UNDEFINED;
-extern const isc::log::MessageID DHCP6_CLASS_UNTESTABLE;
extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE;
extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL;
extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE;
extern const isc::log::MessageID DHCP6_RELEASE_PD_FAIL_WRONG_IAID;
extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_ERROR;
extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_EVAL_RESULT;
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNDEFINED;
+extern const isc::log::MessageID DHCP6_REQUIRED_CLASS_UNTESTABLE;
extern const isc::log::MessageID DHCP6_REQUIRED_OPTIONS_CHECK_FAIL;
extern const isc::log::MessageID DHCP6_RESERVATIONS_LOOKUP_FIRST_ENABLED;
extern const isc::log::MessageID DHCP6_RESPONSE_DATA;
before the classification was added to Kea is used, or class naming is
inconsistent.
-% DHCP6_CLASS_UNDEFINED required class %1 has no definition
-Logged at debug log level 40.
-This debug message informs that a class is listed for required evaluation but
-has no definition.
-
-% DHCP6_CLASS_UNTESTABLE required class %1 has no test expression
-Logged at debug log level 40.
-This debug message informs that a class was listed for required evaluation but
-its definition does not include a test expression to evaluate.
-
% DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1
This is an informational message announcing the successful processing of a
new configuration. it is output during server startup, and when an updated
been successfully evaluated. The client class name and the result value of the
evaluation are printed.
+% DHCP6_REQUIRED_CLASS_UNDEFINED required class %1 has no definition
+Logged at debug log level 40.
+This debug message informs that a class is listed for required evaluation but
+has no definition. The class is ignored.
+
+% DHCP6_REQUIRED_CLASS_UNTESTABLE required class %1 has no test expression
+Logged at debug log level 40.
+This debug message informs that a class was listed for required evaluation but
+its definition does not include a test expression to evaluate. The class is
+unconditionally added to the query.
+
% DHCP6_REQUIRED_OPTIONS_CHECK_FAIL %1: %2 message received from %3 failed the following check: %4
Logged at debug log level 40.
This message indicates that received DHCPv6 packet is invalid. This may be due
for (auto const& cclass : classes) {
const ClientClassDefPtr class_def = dict->findClass(cclass);
if (!class_def) {
- LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNDEFINED)
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
+ DHCP6_REQUIRED_CLASS_UNDEFINED)
.arg(cclass);
+ // Ignore it as it can't have an attached action
continue;
}
const ExpressionPtr& expr_ptr = class_def->getMatchExpr();
- // Nothing to do without an expression to evaluate
+ // Add a class without an expression to evaluate
if (!expr_ptr) {
- LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_UNTESTABLE)
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
+ DHCP6_REQUIRED_CLASS_UNTESTABLE)
.arg(cclass);
+ pkt->addClass(cclass);
continue;
}
// Evaluate the expression which can return false (no match),
EXPECT_EQ("2001:db8:1::3", addrs[0].toText());
}
+// This test checks a required class without a test entry can be
+// unconditionally added.
+TEST_F(ClassifyTest, requiredNoTest) {
+ std::string config =
+ "{"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"client-classes\": ["
+ " {"
+ " \"name\": \"for-network\","
+ " \"option-data\": [ {"
+ " \"name\": \"dns-servers\","
+ " \"data\": \"2001:db8:1::3\""
+ " } ]"
+ " }"
+ "],"
+ "\"shared-networks\": [ {"
+ " \"name\": \"frog\","
+ " \"interface\": \"eth1\","
+ " \"require-client-classes\": [ \"for-network\" ],"
+ " \"subnet6\": [ { "
+ " \"subnet\": \"2001:db8:1::/64\","
+ " \"id\": 1,"
+ " \"pools\": [ { "
+ " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\""
+ " } ]"
+ " } ]"
+ "} ],"
+ "\"valid-lifetime\": 600"
+ "}";
+
+ // Create a client requesting dns-servers option
+ Dhcp6Client client;
+ client.setInterface("eth1");
+ client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+ client.requestOption(D6O_NAME_SERVERS);
+
+ // Load the config and perform a SARR
+ configure(config, *client.getServer());
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Check response
+ EXPECT_EQ(1, client.getLeaseNum());
+ Pkt6Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // Check dns-servers option
+ OptionPtr opt = resp->getOption(D6O_NAME_SERVERS);
+ ASSERT_TRUE(opt);
+ Option6AddrLstPtr servers =
+ boost::dynamic_pointer_cast<Option6AddrLst>(opt);
+ ASSERT_TRUE(servers);
+ auto addrs = servers->getAddresses();
+ ASSERT_EQ(1, addrs.size());
+ EXPECT_EQ("2001:db8:1::3", addrs[0].toText());
+}
+
+// This test checks a required class which is not defined is ignored.
+TEST_F(ClassifyTest, requiredNoDefined) {
+ std::string config =
+ "{"
+ "\"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"shared-networks\": [ {"
+ " \"name\": \"frog\","
+ " \"interface\": \"eth1\","
+ " \"require-client-classes\": [ \"for-network\" ],"
+ " \"subnet6\": [ { "
+ " \"subnet\": \"2001:db8:1::/64\","
+ " \"id\": 1,"
+ " \"pools\": [ { "
+ " \"pool\": \"2001:db8:1::1 - 2001:db8:1::64\""
+ " } ]"
+ " } ]"
+ "} ],"
+ "\"valid-lifetime\": 600"
+ "}";
+
+ // Create a client requesting dns-servers option
+ Dhcp6Client client;
+ client.setInterface("eth1");
+ client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+ client.requestOption(D6O_NAME_SERVERS);
+
+ // Load the config and perform a SARR
+ configure(config, *client.getServer());
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Check response
+ EXPECT_EQ(1, client.getLeaseNum());
+ Pkt6Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // Check dns-servers option
+ OptionPtr opt = resp->getOption(D6O_NAME_SERVERS);
+ EXPECT_FALSE(opt);
+}
+
// This test checks the complex membership from HA with server1 telephone.
TEST_F(ClassifyTest, server1Telephone) {
// Create a client.