socket_requestor_test.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <server_common/socket_request.h>
  16. #include <gtest/gtest.h>
  17. #include <config/tests/fake_session.h>
  18. #include <config/ccsession.h>
  19. #include <exceptions/exceptions.h>
  20. #include <server_common/tests/data_path.h>
  21. #include <cstdlib>
  22. #include <cstddef>
  23. #include <cerrno>
  24. #include <sys/socket.h>
  25. #include <sys/un.h>
  26. #include <boost/foreach.hpp>
  27. #include <boost/scoped_ptr.hpp>
  28. #include <util/io/fd.h>
  29. #include <util/io/fd_share.h>
  30. using namespace isc::data;
  31. using namespace isc::config;
  32. using namespace isc::server_common;
  33. using namespace isc;
  34. namespace {
  35. // Check it throws an exception when it is not initialized
  36. TEST(SocketRequestorAccess, unitialized) {
  37. // Make sure it is not initialized
  38. initTestSocketRequestor(NULL);
  39. EXPECT_THROW(socketRequestor(), InvalidOperation);
  40. }
  41. // It returns whatever it is initialized to
  42. TEST(SocketRequestorAccess, initialized) {
  43. // A concrete implementation that does nothing, just can exist
  44. class DummyRequestor : public SocketRequestor {
  45. public:
  46. DummyRequestor() : SocketRequestor() {}
  47. virtual void releaseSocket(const std::string&) {}
  48. virtual SocketID requestSocket(Protocol, const std::string&, uint16_t,
  49. ShareMode, const std::string&)
  50. {
  51. return (SocketID(0, "")); // Just to silence warnings
  52. }
  53. };
  54. DummyRequestor requestor;
  55. // Make sure it is initialized (the test way, of course)
  56. initTestSocketRequestor(&requestor);
  57. // It returs the same "pointer" as inserted
  58. // The casts are there as the template system seemed to get confused
  59. // without them, the types should be correct even without them, but
  60. // the EXPECT_EQ wanted to use long long int instead of pointers.
  61. EXPECT_EQ(static_cast<const SocketRequestor*>(&requestor),
  62. static_cast<const SocketRequestor*>(&socketRequestor()));
  63. // Just that we don't have an invalid pointer anyway
  64. initTestSocketRequestor(NULL);
  65. }
  66. // This class contains a fake (module)ccsession to emulate answers from Boss
  67. class SocketRequestorTest : public ::testing::Test {
  68. public:
  69. SocketRequestorTest() : session(ElementPtr(new ListElement),
  70. ElementPtr(new ListElement),
  71. ElementPtr(new ListElement)),
  72. specfile(std::string(TEST_DATA_PATH) +
  73. "/spec.spec")
  74. {
  75. session.getMessages()->add(createAnswer());
  76. cc_session.reset(new ModuleCCSession(specfile, session, NULL, NULL,
  77. false, false));
  78. initSocketReqeustor(*cc_session);
  79. }
  80. ~SocketRequestorTest() {
  81. cleanupSocketRequestor();
  82. }
  83. // Do a standard request with some default values
  84. SocketRequestor::SocketID
  85. doRequest() {
  86. return (socketRequestor().requestSocket(SocketRequestor::UDP,
  87. "192.0.2.1", 12345,
  88. SocketRequestor::DONT_SHARE,
  89. "test"));
  90. }
  91. // Creates a valid socket request answer, as it would be sent by
  92. // Boss. 'valid' in terms of format, not values
  93. void
  94. addAnswer(const std::string& token, const std::string& path) {
  95. ElementPtr answer_part = Element::createMap();
  96. answer_part->set("token", Element::create(token));
  97. answer_part->set("path", Element::create(path));
  98. session.getMessages()->add(createAnswer(0, answer_part));
  99. }
  100. // Clears the messages the client sent so far on the fake msgq
  101. // (for easier access to new messages later)
  102. void
  103. clearMsgQueue() {
  104. while (session.getMsgQueue()->size() > 0) {
  105. session.getMsgQueue()->remove(0);
  106. }
  107. }
  108. isc::cc::FakeSession session;
  109. boost::scoped_ptr<ModuleCCSession> cc_session;
  110. const std::string specfile;
  111. };
  112. // helper function to create the request packet as we expect the
  113. // socket requestor to send
  114. ConstElementPtr
  115. createExpectedRequest(const std::string& address,
  116. int port,
  117. const std::string& protocol,
  118. const std::string& share_mode,
  119. const std::string& share_name)
  120. {
  121. // create command arguments
  122. const ElementPtr command_args = Element::createMap();
  123. command_args->set("address", Element::create(address));
  124. command_args->set("port", Element::create(port));
  125. command_args->set("protocol", Element::create(protocol));
  126. command_args->set("share_mode", Element::create(share_mode));
  127. command_args->set("share_name", Element::create(share_name));
  128. // create the envelope
  129. const ElementPtr packet = Element::createList();
  130. packet->add(Element::create("Boss"));
  131. packet->add(Element::create("*"));
  132. packet->add(createCommand("get_socket", command_args));
  133. return (packet);
  134. }
  135. TEST_F(SocketRequestorTest, testSocketRequestMessages) {
  136. // For each request, it will raise CCSessionError, since we don't
  137. // answer here.
  138. // We are only testing the request messages that are sent,
  139. // so for this test that is no problem
  140. clearMsgQueue();
  141. ConstElementPtr expected_request;
  142. expected_request = createExpectedRequest("192.0.2.1", 12345, "UDP",
  143. "NO", "test");
  144. ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
  145. "192.0.2.1", 12345,
  146. SocketRequestor::DONT_SHARE,
  147. "test"),
  148. CCSessionError);
  149. ASSERT_EQ(1, session.getMsgQueue()->size());
  150. ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
  151. clearMsgQueue();
  152. expected_request = createExpectedRequest("192.0.2.2", 1, "TCP",
  153. "ANY", "test2");
  154. ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::TCP,
  155. "192.0.2.2", 1,
  156. SocketRequestor::SHARE_ANY,
  157. "test2"),
  158. CCSessionError);
  159. ASSERT_EQ(1, session.getMsgQueue()->size());
  160. ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
  161. clearMsgQueue();
  162. expected_request = createExpectedRequest("::1", 2, "UDP",
  163. "SAMEAPP", "test3");
  164. ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
  165. "::1", 2,
  166. SocketRequestor::SHARE_SAME,
  167. "test3"),
  168. CCSessionError);
  169. ASSERT_EQ(1, session.getMsgQueue()->size());
  170. ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
  171. }
  172. TEST_F(SocketRequestorTest, invalidParameterForSocketRequest) {
  173. // Bad protocol
  174. EXPECT_THROW(socketRequestor().
  175. requestSocket(static_cast<SocketRequestor::Protocol>(2),
  176. "192.0.2.1", 12345,
  177. SocketRequestor::DONT_SHARE,
  178. "test"),
  179. InvalidParameter);
  180. // Bad share mode
  181. EXPECT_THROW(socketRequestor().
  182. requestSocket(SocketRequestor::UDP,
  183. "192.0.2.1", 12345,
  184. static_cast<SocketRequestor::ShareMode>(3),
  185. "test"),
  186. InvalidParameter);
  187. }
  188. TEST_F(SocketRequestorTest, testBadRequestAnswers) {
  189. // Test various scenarios where the requestor gets back bad answers
  190. // Should raise CCSessionError if there is no answer
  191. ASSERT_THROW(doRequest(), CCSessionError);
  192. // Also if the answer does not match the format
  193. session.getMessages()->add(createAnswer());
  194. ASSERT_THROW(doRequest(), CCSessionError);
  195. // Now a 'real' answer, should fail on socket connect (no such file)
  196. addAnswer("foo", "/does/not/exist");
  197. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  198. // Another failure (domain socket path too long)
  199. addAnswer("foo", std::string(1000, 'x'));
  200. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  201. // Test values around path boundary
  202. struct sockaddr_un sock_un;
  203. const std::string max_len(sizeof(sock_un.sun_path) - 1, 'x');
  204. addAnswer("foo", max_len);
  205. // The failure should NOT contain 'too long'
  206. // (explicitly checking for existance of nonexistence of 'too long',
  207. // as opposed to the actual error, since 'too long' is a value we set).
  208. try {
  209. doRequest();
  210. FAIL() << "doRequest did not throw an exception";
  211. } catch (const SocketRequestor::SocketError& se) {
  212. ASSERT_EQ(std::string::npos, std::string(se.what()).find("too long"));
  213. }
  214. const std::string too_long(sizeof(sock_un.sun_path), 'x');
  215. addAnswer("foo", too_long);
  216. // The failure SHOULD contain 'too long'
  217. try {
  218. doRequest();
  219. FAIL() << "doRequest did not throw an exception";
  220. } catch (const SocketRequestor::SocketError& se) {
  221. ASSERT_NE(std::string::npos, std::string(se.what()).find("too long"));
  222. }
  223. // Send back an error response
  224. session.getMessages()->add(createAnswer(1, "error"));
  225. ASSERT_THROW(doRequest(), CCSessionError);
  226. }
  227. // Helper function to create the release commands as we expect
  228. // them to be sent by the SocketRequestor class
  229. ConstElementPtr
  230. createExpectedRelease(const std::string& token) {
  231. // create command arguments
  232. const ElementPtr command_args = Element::createMap();
  233. command_args->set("token", Element::create(token));
  234. // create the envelope
  235. const ElementPtr packet = Element::createList();
  236. packet->add(Element::create("Boss"));
  237. packet->add(Element::create("*"));
  238. packet->add(createCommand("drop_socket", command_args));
  239. return (packet);
  240. }
  241. TEST_F(SocketRequestorTest, testSocketReleaseMessages) {
  242. ConstElementPtr expected_release;
  243. session.getMessages()->add(createAnswer());
  244. clearMsgQueue();
  245. expected_release = createExpectedRelease("foo");
  246. socketRequestor().releaseSocket("foo");
  247. ASSERT_EQ(1, session.getMsgQueue()->size());
  248. ASSERT_EQ(*expected_release, *(session.getMsgQueue()->get(0)));
  249. session.getMessages()->add(createAnswer());
  250. clearMsgQueue();
  251. expected_release = createExpectedRelease("bar");
  252. socketRequestor().releaseSocket("bar");
  253. ASSERT_EQ(1, session.getMsgQueue()->size());
  254. ASSERT_EQ(*expected_release, *(session.getMsgQueue()->get(0)));
  255. }
  256. TEST_F(SocketRequestorTest, testBadSocketReleaseAnswers) {
  257. // Should fail if there is no answer at all
  258. ASSERT_THROW(socketRequestor().releaseSocket("bar"),
  259. CCSessionError);
  260. // Should also fail if the answer is an error
  261. session.getMessages()->add(createAnswer(1, "error"));
  262. ASSERT_THROW(socketRequestor().releaseSocket("bar"),
  263. SocketRequestor::SocketError);
  264. }
  265. // A helper function to impose a read timeout for the server socket
  266. // in order to avoid deadlock when the client side has a bug and doesn't
  267. // send expected data.
  268. // It returns true when the timeout is set successfully; otherwise false.
  269. bool
  270. setRecvTimo(int s) {
  271. const struct timeval timeo = { 10, 0 }; // 10sec, arbitrary choice
  272. if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)) == 0) {
  273. return (true);
  274. }
  275. if (errno == ENOPROTOOPT) { // deviant OS, give up using it.
  276. return (false);
  277. }
  278. isc_throw(isc::Unexpected, "set RCVTIMEO failed: " << strerror(errno));
  279. }
  280. // Helper test class that creates a randomly named domain socket
  281. // Upon init, it will only reserve the name (and place an empty file in its
  282. // place).
  283. // When run() is called, it creates the socket, forks, and the child will
  284. // listen for a connection, then send all the data passed to run to that
  285. // connection, and then close the socket
  286. class TestSocket {
  287. public:
  288. TestSocket() : fd_(-1) {
  289. path_ = strdup("test_socket.XXXXXX");
  290. // Misuse mkstemp to generate a file name.
  291. const int f = mkstemp(path_);
  292. if (f == -1) {
  293. isc_throw(Unexpected, "mkstemp failed: " << strerror(errno));
  294. }
  295. // Just need the name, so immediately close
  296. close(f);
  297. }
  298. ~TestSocket() {
  299. cleanup();
  300. }
  301. void
  302. cleanup() {
  303. unlink(path_);
  304. if (path_ != NULL) {
  305. free(path_);
  306. path_ = NULL;
  307. }
  308. if (fd_ != -1) {
  309. close(fd_);
  310. fd_ = -1;
  311. }
  312. }
  313. // Returns the path used for the socket
  314. const char* getPath() const {
  315. return (path_);
  316. }
  317. // create socket, fork, and serve if child (child will exit when done).
  318. // If the underlying system doesn't allow to set read timeout, tell the
  319. // caller that via a false return value so that the caller can avoid
  320. // performing tests that could result in a dead lock.
  321. bool run(const std::vector<std::pair<std::string, int> >& data) {
  322. create();
  323. const bool timo_ok = setRecvTimo(fd_);
  324. const int child_pid = fork();
  325. if (child_pid == 0) {
  326. serve(data);
  327. exit(0);
  328. } else {
  329. // parent does not need fd anymore
  330. close(fd_);
  331. fd_ = -1;
  332. }
  333. return (timo_ok);
  334. }
  335. private:
  336. // Actually create the socket and listen on it
  337. void
  338. create() {
  339. fd_ = socket(AF_UNIX, SOCK_STREAM, 0);
  340. if (fd_ == -1) {
  341. isc_throw(Unexpected, "Unable to create socket");
  342. }
  343. struct sockaddr_un socket_address;
  344. socket_address.sun_family = AF_UNIX;
  345. socklen_t len = strlen(path_);
  346. if (len > sizeof(socket_address.sun_path)) {
  347. isc_throw(Unexpected,
  348. "mkstemp() created a filename too long for sun_path");
  349. }
  350. strncpy(socket_address.sun_path, path_, len);
  351. #ifdef HAVE_SA_LEN
  352. socket_address.sun_len = len;
  353. #endif
  354. len += offsetof(struct sockaddr_un, sun_path);
  355. // Remove the random file we created so we can reuse it for
  356. // a domain socket connection. This contains a minor race condition
  357. // but for the purposes of this test it should be small enough
  358. unlink(path_);
  359. if (bind(fd_, (const struct sockaddr*)&socket_address, len) == -1) {
  360. isc_throw(Unexpected,
  361. "unable to bind to test domain socket " << path_ <<
  362. ": " << strerror(errno));
  363. }
  364. if (listen(fd_, 1) == -1) {
  365. isc_throw(Unexpected,
  366. "unable to listen on test domain socket " << path_ <<
  367. ": " << strerror(errno));
  368. }
  369. }
  370. // Accept one connection, then for each value of the vector,
  371. // read the socket token from the connection and match the string
  372. // part of the vector element, and send the integer part of the element
  373. // using send_fd() (prepended by a status code 'ok'). For simplicity
  374. // we assume the tokens are 4 bytes long; if the test case uses a
  375. // different size of token the test will fail.
  376. //
  377. // There are a few specific exceptions;
  378. // when the value is -1, it will send back an error value (signaling
  379. // CREATOR_SOCKET_UNAVAILABLE)
  380. // when the value is -2, it will send a byte signaling CREATOR_SOCKET_OK
  381. // first, and then one byte from some string (i.e. bad data, not using
  382. // send_fd())
  383. //
  384. // NOTE: client_fd could leak on exception. This should be cleaned up.
  385. // See the note about SocketSessionReceiver in socket_request.cc.
  386. void
  387. serve(const std::vector<std::pair<std::string, int> > data) {
  388. const int client_fd = accept(fd_, NULL, NULL);
  389. if (client_fd == -1) {
  390. isc_throw(Unexpected, "Error in accept(): " << strerror(errno));
  391. }
  392. if (!setRecvTimo(client_fd)) {
  393. // In the loop below we do blocking read. To avoid deadlock
  394. // when the parent is buggy we'll skip it unless we can
  395. // set a read timeout on the socket.
  396. return;
  397. }
  398. typedef std::pair<std::string, int> DataPair;
  399. BOOST_FOREACH(DataPair cur_data, data) {
  400. char buf[5];
  401. memset(buf, 0, 5);
  402. if (isc::util::io::read_data(client_fd, buf, 4) != 4) {
  403. isc_throw(Unexpected, "unable to receive socket token");
  404. }
  405. if (cur_data.first != buf) {
  406. isc_throw(Unexpected, "socket token mismatch: expected="
  407. << cur_data.first << ", actual=" << buf);
  408. }
  409. bool result;
  410. if (cur_data.second == -1) {
  411. // send 'CREATOR_SOCKET_UNAVAILABLE'
  412. result = isc::util::io::write_data(client_fd, "0\n", 2);
  413. } else if (cur_data.second == -2) {
  414. // send 'CREATOR_SOCKET_OK' first
  415. result = isc::util::io::write_data(client_fd, "1\n", 2);
  416. if (result) {
  417. if (send(client_fd, "a", 1, 0) != 1) {
  418. result = false;
  419. }
  420. }
  421. } else {
  422. // send 'CREATOR_SOCKET_OK' first
  423. result = isc::util::io::write_data(client_fd, "1\n", 2);
  424. if (result) {
  425. if (isc::util::io::send_fd(client_fd,
  426. cur_data.second) != 0) {
  427. result = false;
  428. }
  429. }
  430. }
  431. if (!result) {
  432. isc_throw(Exception, "Error in send_fd(): " <<
  433. strerror(errno));
  434. }
  435. }
  436. close(client_fd);
  437. }
  438. int fd_;
  439. char* path_;
  440. };
  441. TEST_F(SocketRequestorTest, testSocketPassing) {
  442. TestSocket ts;
  443. std::vector<std::pair<std::string, int> > data;
  444. data.push_back(std::pair<std::string, int>("foo\n", 1));
  445. data.push_back(std::pair<std::string, int>("bar\n", 2));
  446. data.push_back(std::pair<std::string, int>("foo\n", 3));
  447. data.push_back(std::pair<std::string, int>("foo\n", 1));
  448. data.push_back(std::pair<std::string, int>("foo\n", -1));
  449. data.push_back(std::pair<std::string, int>("foo\n", -2));
  450. // run() returns true iff we can specify read timeout so we avoid a
  451. // deadlock. Unless there's a bug the test should succeed even without the
  452. // timeout, but we don't want to make the test hang up in case with an
  453. // unexpected bug, so we'd rather skip most of the tests in that case.
  454. const bool timo_ok = ts.run(data);
  455. SocketRequestor::SocketID socket_id;
  456. if (timo_ok) {
  457. // 1 should be ok
  458. addAnswer("foo", ts.getPath());
  459. socket_id = doRequest();
  460. ASSERT_EQ("foo", socket_id.second);
  461. ASSERT_EQ(0, close(socket_id.first));
  462. // 2 should be ok too
  463. addAnswer("bar", ts.getPath());
  464. socket_id = doRequest();
  465. ASSERT_EQ("bar", socket_id.second);
  466. ASSERT_EQ(0, close(socket_id.first));
  467. // 3 should be ok too (reuse earlier token)
  468. addAnswer("foo", ts.getPath());
  469. socket_id = doRequest();
  470. ASSERT_EQ("foo", socket_id.second);
  471. ASSERT_EQ(0, close(socket_id.first));
  472. }
  473. // Create a second socket server, to test that multiple different
  474. // domains sockets would work as well (even though we don't actually
  475. // use that feature)
  476. TestSocket ts2;
  477. std::vector<std::pair<std::string, int> > data2;
  478. data2.push_back(std::pair<std::string, int>("foo\n", 1));
  479. const bool timo_ok2 = ts2.run(data2);
  480. if (timo_ok2) {
  481. // 1 should be ok
  482. addAnswer("foo", ts2.getPath());
  483. socket_id = doRequest();
  484. ASSERT_EQ("foo", socket_id.second);
  485. ASSERT_EQ(0, close(socket_id.first));
  486. }
  487. if (timo_ok) {
  488. // Now use first socket again
  489. addAnswer("foo", ts.getPath());
  490. socket_id = doRequest();
  491. ASSERT_EQ("foo", socket_id.second);
  492. ASSERT_EQ(0, close(socket_id.first));
  493. // -1 is a "normal" error
  494. addAnswer("foo", ts.getPath());
  495. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  496. // -2 is an unexpected error. After this point it's not guaranteed the
  497. // connection works as intended.
  498. addAnswer("foo", ts.getPath());
  499. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  500. }
  501. // Vector is of first socket is now empty, so the socket should be gone
  502. addAnswer("foo", ts.getPath());
  503. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  504. // Vector is of second socket is now empty too, so the socket should be
  505. // gone
  506. addAnswer("foo", ts2.getPath());
  507. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  508. }
  509. }