1 #include "data.hpp"
2 #include "internal_sys_mock.hpp"
3 #include "net.hpp"
4 #include "progress_mock.hpp"
5 
6 #include <ipmiblob/test/blob_interface_mock.hpp>
7 
8 #include <cstring>
9 
10 #include <gtest/gtest.h>
11 
12 namespace host_tool
13 {
14 namespace
15 {
16 
17 using namespace std::literals;
18 
19 using ::testing::_;
20 using ::testing::AllOf;
21 using ::testing::ContainerEq;
22 using ::testing::DoAll;
23 using ::testing::Field;
24 using ::testing::Gt;
25 using ::testing::InSequence;
26 using ::testing::NotNull;
27 using ::testing::Pointee;
28 using ::testing::Return;
29 using ::testing::SetArgPointee;
30 using ::testing::SetErrnoAndReturn;
31 using ::testing::StrEq;
32 
33 class NetHandleTest : public ::testing::Test
34 {
35   protected:
36     NetHandleTest() : handler(&blobMock, &progMock, host, port, &sysMock)
37     {
38         sa.sin6_family = AF_INET6;
39         sa.sin6_port = htons(622);
40         sa.sin6_flowinfo = 0;
41         sa.sin6_addr = in6addr_loopback; // ::1
42         sa.sin6_scope_id = 0;
43 
44         addr.ai_family = AF_INET6;
45         addr.ai_socktype = SOCK_STREAM;
46         addr.ai_addr = reinterpret_cast<struct sockaddr*>(&sa);
47         addr.ai_addrlen = sizeof(sa);
48         addr.ai_protocol = 0;
49         addr.ai_next = nullptr;
50     }
51 
52     void expectOpenFile()
53     {
54         EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
55             .WillOnce(Return(inFd));
56         EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0));
57         EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str())))
58             .WillOnce(Return(fakeFileSize));
59 
60         EXPECT_CALL(progMock, start(fakeFileSize));
61     }
62 
63     void expectAddrInfo()
64     {
65         EXPECT_CALL(
66             sysMock,
67             getaddrinfo(StrEq(host), StrEq(port),
68                         AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
69                               Field(&addrinfo::ai_family, AF_UNSPEC),
70                               Field(&addrinfo::ai_socktype, SOCK_STREAM)),
71                         NotNull()))
72             .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0)));
73         EXPECT_CALL(sysMock, freeaddrinfo(&addr));
74     }
75 
76     void expectConnection()
77     {
78         EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
79             .WillOnce(Return(connFd));
80         EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
81         EXPECT_CALL(sysMock,
82                     connect(connFd, reinterpret_cast<struct sockaddr*>(&sa),
83                             sizeof(sa)))
84             .WillOnce(Return(0));
85     }
86 
87     internal::InternalSysMock sysMock;
88     ipmiblob::BlobInterfaceMock blobMock;
89     ProgressMock progMock;
90 
91     const std::string host = "::1"s;
92     const std::string port = "622"s;
93 
94     struct sockaddr_in6 sa;
95     struct addrinfo addr;
96 
97     static constexpr std::uint16_t session = 0xbeef;
98     const std::string filePath = "/asdf"s;
99     static constexpr int inFd = 5;
100     static constexpr int connFd = 7;
101     static constexpr size_t fakeFileSize = 128;
102     static constexpr size_t chunkSize = 16;
103 
104     NetDataHandler handler;
105 };
106 
107 TEST_F(NetHandleTest, openFileFail)
108 {
109     EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
110         .WillOnce(SetErrnoAndReturn(EACCES, -1));
111 
112     EXPECT_FALSE(handler.sendContents(filePath, session));
113 }
114 
115 TEST_F(NetHandleTest, getSizeFail)
116 {
117     EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
118         .WillOnce(Return(inFd));
119     EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0));
120     EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))).WillOnce(Return(0));
121 
122     EXPECT_FALSE(handler.sendContents(filePath, session));
123 }
124 
125 TEST_F(NetHandleTest, getaddrinfoFail)
126 {
127     expectOpenFile();
128 
129     EXPECT_CALL(sysMock,
130                 getaddrinfo(StrEq(host), StrEq(port),
131                             AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
132                                   Field(&addrinfo::ai_family, AF_UNSPEC),
133                                   Field(&addrinfo::ai_socktype, SOCK_STREAM)),
134                             NotNull()))
135         .WillOnce(Return(EAI_ADDRFAMILY));
136 
137     EXPECT_FALSE(handler.sendContents(filePath, session));
138 }
139 
140 TEST_F(NetHandleTest, connectFail)
141 {
142     expectOpenFile();
143     expectAddrInfo();
144 
145     EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
146         .WillOnce(Return(connFd));
147     EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
148     EXPECT_CALL(
149         sysMock,
150         connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)))
151         .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1));
152 
153     EXPECT_FALSE(handler.sendContents(filePath, session));
154 }
155 
156 TEST_F(NetHandleTest, sendfileFail)
157 {
158     expectOpenFile();
159     expectAddrInfo();
160     expectConnection();
161 
162     EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _))
163         .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1));
164 
165     EXPECT_FALSE(handler.sendContents(filePath, session));
166 }
167 
168 TEST_F(NetHandleTest, successOneChunk)
169 {
170     expectOpenFile();
171     expectAddrInfo();
172     expectConnection();
173 
174     {
175         InSequence seq;
176 
177         EXPECT_CALL(sysMock,
178                     sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize)))
179             .WillOnce(
180                 DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize)));
181         EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
182                                       Gt(fakeFileSize)))
183             .WillOnce(Return(0));
184     }
185 
186     struct ipmi_flash::ExtChunkHdr chunk;
187     chunk.length = fakeFileSize;
188     std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
189     std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
190     EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes)));
191 
192     EXPECT_CALL(progMock, updateProgress(fakeFileSize));
193 
194     EXPECT_TRUE(handler.sendContents(filePath, session));
195 }
196 
197 TEST_F(NetHandleTest, successMultiChunk)
198 {
199     expectOpenFile();
200     expectAddrInfo();
201     expectConnection();
202 
203     struct ipmi_flash::ExtChunkHdr chunk;
204     chunk.length = chunkSize;
205     std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
206     std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
207 
208     {
209         InSequence seq;
210 
211         for (std::uint32_t offset = 0; offset < fakeFileSize;
212              offset += chunkSize)
213         {
214             EXPECT_CALL(sysMock,
215                         sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize)))
216                 .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize),
217                                 Return(chunkSize)));
218 
219             EXPECT_CALL(blobMock,
220                         writeBytes(session, offset, ContainerEq(chunkBytes)));
221             EXPECT_CALL(progMock, updateProgress(chunkSize));
222         }
223         EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
224                                       Gt(chunkSize)))
225             .WillOnce(Return(0));
226     }
227 
228     EXPECT_TRUE(handler.sendContents(filePath, session));
229 }
230 
231 } // namespace
232 } // namespace host_tool
233