/src/kea/src/lib/testutils/unix_control_client.cc
Line | Count | Source |
1 | | // Copyright (C) 2015-2026 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 | | |
7 | | #include <config.h> |
8 | | |
9 | | #include <gtest/gtest.h> |
10 | | #include <testutils/unix_control_client.h> |
11 | | #include <util/ready_check.h> |
12 | | #include <unistd.h> |
13 | | #include <sys/socket.h> |
14 | | #include <sys/un.h> |
15 | | #include <errno.h> |
16 | | #include <string.h> |
17 | | |
18 | | namespace isc { |
19 | | namespace dhcp { |
20 | | namespace test { |
21 | | |
22 | 4 | UnixControlClient::UnixControlClient() { |
23 | 4 | socket_fd_ = -1; |
24 | 4 | } |
25 | | |
26 | 0 | UnixControlClient::~UnixControlClient() { |
27 | 0 | disconnectFromServer(); |
28 | 0 | } |
29 | | |
30 | | /// @brief Closes the Control Channel socket |
31 | 6.84k | void UnixControlClient::disconnectFromServer() { |
32 | 6.84k | if (socket_fd_ >= 0) { |
33 | 6.84k | static_cast<void>(close(socket_fd_)); |
34 | 6.84k | socket_fd_ = -1; |
35 | 6.84k | } |
36 | 6.84k | } |
37 | | |
38 | 6.84k | bool UnixControlClient::connectToServer(const std::string& socket_path) { |
39 | | // Create UNIX socket |
40 | 6.84k | socket_fd_ = socket(AF_UNIX, SOCK_STREAM, 0); |
41 | 6.84k | if (socket_fd_ < 0) { |
42 | 0 | const char* errmsg = strerror(errno); |
43 | 0 | ADD_FAILURE() << "Failed to open unix stream socket: " << errmsg; |
44 | 0 | return (false); |
45 | 0 | } |
46 | | |
47 | 6.84k | struct sockaddr_un srv_addr; |
48 | 6.84k | if (socket_path.size() > sizeof(srv_addr.sun_path) - 1) { |
49 | 0 | ADD_FAILURE() << "Socket path specified (" << socket_path |
50 | 0 | << ") is larger than " << (sizeof(srv_addr.sun_path) - 1) |
51 | 0 | << " allowed."; |
52 | 0 | disconnectFromServer(); |
53 | 0 | return (false); |
54 | 0 | } |
55 | | |
56 | | // Prepare socket address |
57 | 6.84k | memset(&srv_addr, 0, sizeof(srv_addr)); |
58 | 6.84k | srv_addr.sun_family = AF_UNIX; |
59 | 6.84k | strncpy(srv_addr.sun_path, socket_path.c_str(), |
60 | 6.84k | sizeof(srv_addr.sun_path) - 1); |
61 | 6.84k | socklen_t len = sizeof(srv_addr); |
62 | | |
63 | | // Connect to the specified UNIX socket |
64 | 6.84k | int status = connect(socket_fd_, reinterpret_cast<struct sockaddr*>(&srv_addr), len); |
65 | 6.84k | if (status == -1) { |
66 | 0 | const char* errmsg = strerror(errno); |
67 | 0 | ADD_FAILURE() << "Failed to connect unix socket: fd=" << socket_fd_ |
68 | 0 | << ", path=" << socket_path << " : " << errmsg; |
69 | 0 | disconnectFromServer(); |
70 | 0 | return (false); |
71 | 0 | } |
72 | | |
73 | 6.84k | return (true); |
74 | 6.84k | } |
75 | | |
76 | 6.84k | bool UnixControlClient::sendCommand(const std::string& command) { |
77 | 6.84k | if (socket_fd_ < 0) { |
78 | 0 | ADD_FAILURE() << "send command with closed socket"; |
79 | 0 | return (false); |
80 | 0 | } |
81 | | // Send command |
82 | 6.84k | int bytes_sent = send(socket_fd_, command.c_str(), command.length(), 0); |
83 | 6.84k | if (bytes_sent < static_cast<int>(command.length())) { |
84 | 96 | const char* errmsg = strerror(errno); |
85 | 96 | ADD_FAILURE() << "Failed to send " << command.length() |
86 | 96 | << " bytes, send() returned " << bytes_sent |
87 | 96 | << " : " << errmsg; |
88 | 96 | return (false); |
89 | 96 | } |
90 | | |
91 | 6.74k | return (true); |
92 | 6.84k | } |
93 | | |
94 | | bool UnixControlClient::getResponse(std::string& response, |
95 | 6.84k | const unsigned int timeout_sec) { |
96 | | // Receive response |
97 | 6.84k | char buf[65536]; |
98 | 6.84k | memset(buf, 0, sizeof(buf)); |
99 | 6.84k | switch (selectCheck(timeout_sec)) { |
100 | 0 | case -1: { |
101 | 0 | const char* errmsg = strerror(errno); |
102 | 0 | ADD_FAILURE() << "getResponse - select failed: " << errmsg; |
103 | 0 | return (false); |
104 | 0 | } |
105 | 973 | case 0: |
106 | 973 | return (false); |
107 | | |
108 | 5.86k | default: |
109 | 5.86k | break; |
110 | 6.84k | } |
111 | | |
112 | 5.86k | int bytes_rcvd = recv(socket_fd_, buf, sizeof(buf), 0); |
113 | 5.86k | if (bytes_rcvd < 0) { |
114 | 0 | const char* errmsg = strerror(errno); |
115 | 0 | ADD_FAILURE() << "Failed to receive a response. recv() returned " |
116 | 0 | << bytes_rcvd << " : " << errmsg; |
117 | 0 | return (false); |
118 | 0 | } |
119 | | |
120 | | // Convert the response to a string |
121 | 5.86k | response = std::string(buf, bytes_rcvd); |
122 | 5.86k | return (true); |
123 | 5.86k | } |
124 | | |
125 | 6.84k | int UnixControlClient::selectCheck(const unsigned int timeout_sec) { |
126 | 6.84k | if (socket_fd_ < 0) { |
127 | 0 | ADD_FAILURE() << "select check with closed socket"; |
128 | 0 | return (-1); |
129 | 0 | } |
130 | 6.84k | if (socket_fd_ >= FD_SETSIZE) { |
131 | 0 | ADD_FAILURE() << "select check with out of bound socket"; |
132 | 0 | return (-1); |
133 | 0 | } |
134 | | |
135 | 6.84k | return (util::selectCheck(socket_fd_, timeout_sec)); |
136 | 6.84k | } |
137 | | |
138 | | } |
139 | | } |
140 | | } |