1 // Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 #include <config.h>
7 #include <util/watch_socket.h>
8 
9 #include <gtest/gtest.h>
10 
11 #include <sys/select.h>
12 #include <sys/ioctl.h>
13 
14 #ifdef HAVE_SYS_FILIO_H
15 // FIONREAD is here on Solaris
16 #include <sys/filio.h>
17 #endif
18 
19 using namespace std;
20 using namespace isc;
21 using namespace isc::util;
22 
23 namespace {
24 
25 /// @brief Returns the result of select() given an fd to check for read status.
26 ///
27 /// @param fd_to_check The file descriptor to test
28 ///
29 /// @return Returns less than one on an error, 0 if the fd is not ready to
30 /// read, > 0 if it is ready to read.
selectCheck(int fd_to_check)31 int selectCheck(int fd_to_check) {
32     fd_set read_fds;
33     int maxfd = 0;
34 
35     FD_ZERO(&read_fds);
36 
37     // Add this socket to listening set
38     FD_SET(fd_to_check,  &read_fds);
39     maxfd = fd_to_check;
40 
41     struct timeval select_timeout;
42     select_timeout.tv_sec = 0;
43     select_timeout.tv_usec = 0;
44 
45     return (select(maxfd + 1, &read_fds, NULL, NULL, &select_timeout));
46 }
47 
48 /// @brief Tests the basic functionality of WatchSocket.
TEST(WatchSocketTest,basics)49 TEST(WatchSocketTest, basics) {
50     WatchSocketPtr watch;
51 
52     /// Verify that we can construct a WatchSocket.
53     ASSERT_NO_THROW(watch.reset(new WatchSocket()));
54     ASSERT_TRUE(watch);
55 
56     /// Verify that post-construction the state the select-fd is valid.
57     int select_fd = watch->getSelectFd();
58     EXPECT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
59 
60     /// Verify that isReady() is false and that a call to select agrees.
61     EXPECT_FALSE(watch->isReady());
62     EXPECT_EQ(0, selectCheck(select_fd));
63 
64     /// Verify that the socket can be marked ready.
65     ASSERT_NO_THROW(watch->markReady());
66 
67     /// Verify that we have exactly one marker waiting to be read.
68     int count = 0;
69     EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
70     EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
71 
72     /// Verify that we can call markReady again without error.
73     ASSERT_NO_THROW(watch->markReady());
74 
75     /// Verify that we STILL have exactly one marker waiting to be read.
76     EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
77     EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
78 
79     /// Verify that isReady() is true and that a call to select agrees.
80     EXPECT_TRUE(watch->isReady());
81     EXPECT_EQ(1, selectCheck(select_fd));
82 
83     /// Verify that the socket can be cleared.
84     ASSERT_NO_THROW(watch->clearReady());
85 
86     /// Verify that isReady() is false and that a call to select agrees.
87     EXPECT_FALSE(watch->isReady());
88     EXPECT_EQ(0, selectCheck(select_fd));
89 }
90 
91 /// @brief Checks behavior when select_fd is closed externally while in the
92 /// "cleared" state.
TEST(WatchSocketTest,closedWhileClear)93 TEST(WatchSocketTest, closedWhileClear) {
94     WatchSocketPtr watch;
95 
96     /// Verify that we can construct a WatchSocket.
97     ASSERT_NO_THROW(watch.reset(new WatchSocket()));
98     ASSERT_TRUE(watch);
99 
100     /// Verify that post-construction the state the select-fd is valid.
101     int select_fd = watch->getSelectFd();
102     ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
103 
104     // Verify that socket does not appear ready.
105     ASSERT_EQ(0, watch->isReady());
106 
107     // Interfere by closing the fd.
108     ASSERT_EQ(0, close(select_fd));
109 
110     // Verify that socket does not appear ready.
111     ASSERT_EQ(0, watch->isReady());
112 
113     // Verify that clear does NOT throw.
114     ASSERT_NO_THROW(watch->clearReady());
115 
116     // Verify that trying to mark it fails.
117     ASSERT_THROW(watch->markReady(), WatchSocketError);
118 
119     // Verify that clear does NOT throw.
120     ASSERT_NO_THROW(watch->clearReady());
121 
122     // Verify that getSelectFd() returns invalid socket.
123     ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
124 }
125 
126 /// @brief Checks behavior when select_fd has closed while in the "ready"
127 /// state.
TEST(WatchSocketTest,closedWhileReady)128 TEST(WatchSocketTest, closedWhileReady) {
129     WatchSocketPtr watch;
130 
131     /// Verify that we can construct a WatchSocket.
132     ASSERT_NO_THROW(watch.reset(new WatchSocket()));
133     ASSERT_TRUE(watch);
134 
135     /// Verify that post-construction the state the select-fd is valid.
136     int select_fd = watch->getSelectFd();
137     ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
138 
139     /// Verify that the socket can be marked ready.
140     ASSERT_NO_THROW(watch->markReady());
141     EXPECT_EQ(1, selectCheck(select_fd));
142     EXPECT_TRUE(watch->isReady());
143 
144     // Interfere by closing the fd.
145     ASSERT_EQ(0, close(select_fd));
146 
147     // Verify that isReady() does not throw.
148     ASSERT_NO_THROW(watch->isReady());
149 
150     // and return false.
151     EXPECT_FALSE(watch->isReady());
152 
153     // Verify that trying to clear it does not throw.
154     ASSERT_NO_THROW(watch->clearReady());
155 
156     // Verify the select_fd fails as socket is invalid/closed.
157     EXPECT_EQ(-1, selectCheck(select_fd));
158 
159     // Verify that subsequent attempts to mark it will fail.
160     ASSERT_THROW(watch->markReady(), WatchSocketError);
161 }
162 
163 /// @brief Checks behavior when select_fd has been marked ready but then
164 /// emptied by an external read.
TEST(WatchSocketTest,emptyReadySelectFd)165 TEST(WatchSocketTest, emptyReadySelectFd) {
166     WatchSocketPtr watch;
167 
168     /// Verify that we can construct a WatchSocket.
169     ASSERT_NO_THROW(watch.reset(new WatchSocket()));
170     ASSERT_TRUE(watch);
171 
172     /// Verify that post-construction the state the select-fd is valid.
173     int select_fd = watch->getSelectFd();
174     ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
175 
176     /// Verify that the socket can be marked ready.
177     ASSERT_NO_THROW(watch->markReady());
178     EXPECT_TRUE(watch->isReady());
179     EXPECT_EQ(1, selectCheck(select_fd));
180 
181     // Interfere by reading the fd. This should empty the read pipe.
182     uint32_t buf = 0;
183     ASSERT_EQ((read (select_fd, &buf, sizeof(buf))), sizeof(buf));
184     ASSERT_EQ(WatchSocket::MARKER, buf);
185 
186     // Really nothing that can be done to protect against this, but let's
187     // make sure we aren't in a weird state.
188     ASSERT_NO_THROW(watch->clearReady());
189 
190     // Verify the select_fd does not fail.
191     EXPECT_FALSE(watch->isReady());
192     EXPECT_EQ(0, selectCheck(select_fd));
193 
194     // Verify that getSelectFd() returns is still good.
195     ASSERT_EQ(select_fd, watch->getSelectFd());
196 }
197 
198 /// @brief Checks behavior when select_fd has been marked ready but then
199 /// contents have been "corrupted" by a partial read.
TEST(WatchSocketTest,badReadOnClear)200 TEST(WatchSocketTest, badReadOnClear) {
201     WatchSocketPtr watch;
202 
203     /// Verify that we can construct a WatchSocket.
204     ASSERT_NO_THROW(watch.reset(new WatchSocket()));
205     ASSERT_TRUE(watch);
206 
207     /// Verify that post-construction the state the select-fd is valid.
208     int select_fd = watch->getSelectFd();
209     ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
210 
211     /// Verify that the socket can be marked ready.
212     ASSERT_NO_THROW(watch->markReady());
213     EXPECT_TRUE(watch->isReady());
214     EXPECT_EQ(1, selectCheck(select_fd));
215 
216     // Interfere by reading the fd. This should empty the read pipe.
217     uint32_t buf = 0;
218     ASSERT_EQ((read (select_fd, &buf, 1)), 1);
219     ASSERT_NE(WatchSocket::MARKER, buf);
220 
221     // Really nothing that can be done to protect against this, but let's
222     // make sure we aren't in a weird state.
223     /// @todo maybe clear should never throw, log only
224     ASSERT_THROW(watch->clearReady(), WatchSocketError);
225 
226     // Verify the select_fd does not evaluate to ready.
227     EXPECT_FALSE(watch->isReady());
228     EXPECT_NE(1, selectCheck(select_fd));
229 
230     // Verify that getSelectFd() returns INVALID.
231     ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
232 
233     // Verify that subsequent attempt to mark it fails.
234     ASSERT_THROW(watch->markReady(), WatchSocketError);
235 }
236 
237 /// @brief Checks if the socket can be explicitly closed.
TEST(WatchSocketTest,explicitClose)238 TEST(WatchSocketTest, explicitClose) {
239     WatchSocketPtr watch;
240 
241     // Create new instance of the socket.
242     ASSERT_NO_THROW(watch.reset(new WatchSocket()));
243     ASSERT_TRUE(watch);
244 
245     // Make sure it has been opened by checking that its descriptor
246     // is valid.
247     EXPECT_NE(watch->getSelectFd(), WatchSocket::SOCKET_NOT_VALID);
248 
249     // Close the socket.
250     std::string error_string;
251     ASSERT_TRUE(watch->closeSocket(error_string));
252 
253     // Make sure that the descriptor is now invalid which indicates
254     // that the socket has been closed.
255     EXPECT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
256     // No errors should be reported.
257     EXPECT_TRUE(error_string.empty());
258     // Not ready too.
259     ASSERT_NO_THROW(watch->isReady());
260     EXPECT_FALSE(watch->isReady());
261 }
262 
263 } // end of anonymous namespace
264