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. {
  73. initSocketRequestor(session);
  74. }
  75. ~SocketRequestorTest() {
  76. cleanupSocketRequestor();
  77. }
  78. // Do a standard request with some default values
  79. SocketRequestor::SocketID
  80. doRequest() {
  81. return (socketRequestor().requestSocket(SocketRequestor::UDP,
  82. "192.0.2.1", 12345,
  83. SocketRequestor::DONT_SHARE,
  84. "test"));
  85. }
  86. // Creates a valid socket request answer, as it would be sent by
  87. // Boss. 'valid' in terms of format, not values
  88. void
  89. addAnswer(const std::string& token, const std::string& path) {
  90. ElementPtr answer_part = Element::createMap();
  91. answer_part->set("token", Element::create(token));
  92. answer_part->set("path", Element::create(path));
  93. session.getMessages()->add(createAnswer(0, answer_part));
  94. }
  95. // Clears the messages the client sent so far on the fake msgq
  96. // (for easier access to new messages later)
  97. void
  98. clearMsgQueue() {
  99. while (session.getMsgQueue()->size() > 0) {
  100. session.getMsgQueue()->remove(0);
  101. }
  102. }
  103. isc::cc::FakeSession session;
  104. const std::string specfile;
  105. };
  106. // helper function to create the request packet as we expect the
  107. // socket requestor to send
  108. ConstElementPtr
  109. createExpectedRequest(const std::string& address,
  110. int port,
  111. const std::string& protocol,
  112. const std::string& share_mode,
  113. const std::string& share_name)
  114. {
  115. // create command arguments
  116. const ElementPtr command_args = Element::createMap();
  117. command_args->set("address", Element::create(address));
  118. command_args->set("port", Element::create(port));
  119. command_args->set("protocol", Element::create(protocol));
  120. command_args->set("share_mode", Element::create(share_mode));
  121. command_args->set("share_name", Element::create(share_name));
  122. // create the envelope
  123. const ElementPtr packet = Element::createList();
  124. packet->add(Element::create("Boss"));
  125. packet->add(Element::create("*"));
  126. packet->add(createCommand("get_socket", command_args));
  127. return (packet);
  128. }
  129. TEST_F(SocketRequestorTest, testSocketRequestMessages) {
  130. // For each request, it will raise CCSessionError, since we don't
  131. // answer here.
  132. // We are only testing the request messages that are sent,
  133. // so for this test that is no problem
  134. clearMsgQueue();
  135. ConstElementPtr expected_request;
  136. expected_request = createExpectedRequest("192.0.2.1", 12345, "UDP",
  137. "NO", "test");
  138. ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
  139. "192.0.2.1", 12345,
  140. SocketRequestor::DONT_SHARE,
  141. "test"),
  142. CCSessionError);
  143. ASSERT_EQ(1, session.getMsgQueue()->size());
  144. ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
  145. clearMsgQueue();
  146. expected_request = createExpectedRequest("192.0.2.2", 1, "TCP",
  147. "ANY", "test2");
  148. ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::TCP,
  149. "192.0.2.2", 1,
  150. SocketRequestor::SHARE_ANY,
  151. "test2"),
  152. CCSessionError);
  153. ASSERT_EQ(1, session.getMsgQueue()->size());
  154. ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
  155. clearMsgQueue();
  156. expected_request = createExpectedRequest("::1", 2, "UDP",
  157. "SAMEAPP", "test3");
  158. ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
  159. "::1", 2,
  160. SocketRequestor::SHARE_SAME,
  161. "test3"),
  162. CCSessionError);
  163. ASSERT_EQ(1, session.getMsgQueue()->size());
  164. ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
  165. }
  166. TEST_F(SocketRequestorTest, invalidParameterForSocketRequest) {
  167. // Bad protocol
  168. EXPECT_THROW(socketRequestor().
  169. requestSocket(static_cast<SocketRequestor::Protocol>(2),
  170. "192.0.2.1", 12345,
  171. SocketRequestor::DONT_SHARE,
  172. "test"),
  173. InvalidParameter);
  174. // Bad share mode
  175. EXPECT_THROW(socketRequestor().
  176. requestSocket(SocketRequestor::UDP,
  177. "192.0.2.1", 12345,
  178. static_cast<SocketRequestor::ShareMode>(3),
  179. "test"),
  180. InvalidParameter);
  181. }
  182. TEST_F(SocketRequestorTest, testBadRequestAnswers) {
  183. // Test various scenarios where the requestor gets back bad answers
  184. // Should raise CCSessionError if there is no answer
  185. ASSERT_THROW(doRequest(), CCSessionError);
  186. // Also if the answer does not match the format
  187. session.getMessages()->add(createAnswer());
  188. ASSERT_THROW(doRequest(), CCSessionError);
  189. // Now a 'real' answer, should fail on socket connect (no such file)
  190. addAnswer("foo", "/does/not/exist");
  191. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  192. // Another failure (domain socket path too long)
  193. addAnswer("foo", std::string(1000, 'x'));
  194. ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
  195. // Test values around path boundary
  196. struct sockaddr_un sock_un;
  197. const std::string max_len(sizeof(sock_un.sun_path) - 1, 'x');
  198. addAnswer("foo", max_len);
  199. // The failure should NOT contain 'too long'
  200. // (explicitly checking for existance of nonexistence of 'too long',
  201. // as opposed to the actual error, since 'too long' is a value we set).
  202. try {
  203. doRequest();
  204. FAIL() << "doRequest did not throw an exception";
  205. } catch (const SocketRequestor::SocketError& se) {
  206. ASSERT_EQ(std::string::npos, std::string(se.what()).find("too long"));
  207. }
  208. const std::string too_long(sizeof(sock_un.sun_path), 'x');
  209. addAnswer("foo", too_long);
  210. // The failure SHOULD contain 'too long'
  211. try {
  212. doRequest();
  213. FAIL() << "doRequest did not throw an exception";
  214. } catch (const SocketRequestor::SocketError& se) {
  215. ASSERT_NE(std::string::npos, std::string(se.what()).find("too long"));
  216. }
  217. // Send back an error response
  218. // A generic one first
  219. session.getMessages()->add(createAnswer(1, "error"));
  220. ASSERT_THROW(doRequest(), CCSessionError);
  221. // Now some with specific exceptions
  222. session.getMessages()->add(createAnswer(2, "error"));
  223. ASSERT_THROW(doRequest(), SocketRequestor::SocketAllocateError);
  224. session.getMessages()->add(createAnswer(3, "error"));
  225. ASSERT_THROW(doRequest(), SocketRequestor::ShareError);
  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. }