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 
61     void expectAddrInfo()
62     {
63         EXPECT_CALL(
64             sysMock,
65             getaddrinfo(StrEq(host), StrEq(port),
66                         AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
67                               Field(&addrinfo::ai_family, AF_UNSPEC),
68                               Field(&addrinfo::ai_socktype, SOCK_STREAM)),
69                         NotNull()))
70             .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0)));
71         EXPECT_CALL(sysMock, freeaddrinfo(&addr));
72     }
73 
74     void expectConnection()
75     {
76         EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
77             .WillOnce(Return(connFd));
78         EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
79         EXPECT_CALL(sysMock,
80                     connect(connFd, reinterpret_cast<struct sockaddr*>(&sa),
81                             sizeof(sa)))
82             .WillOnce(Return(0));
83     }
84 
85     internal::InternalSysMock sysMock;
86     ipmiblob::BlobInterfaceMock blobMock;
87     ProgressMock progMock;
88 
89     const std::string host = "::1"s;
90     const std::string port = "622"s;
91 
92     struct sockaddr_in6 sa;
93     struct addrinfo addr;
94 
95     static constexpr std::uint16_t session = 0xbeef;
96     const std::string filePath = "/asdf"s;
97     static constexpr int inFd = 5;
98     static constexpr int connFd = 7;
99     static constexpr size_t fakeFileSize = 128;
100     static constexpr size_t chunkSize = 16;
101 
102     NetDataHandler handler;
103 };
104 
105 TEST_F(NetHandleTest, openFileFail)
106 {
107     EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
108         .WillOnce(SetErrnoAndReturn(EACCES, -1));
109 
110     EXPECT_FALSE(handler.sendContents(filePath, session));
111 }
112 
113 TEST_F(NetHandleTest, getSizeFail)
114 {
115     EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _))
116         .WillOnce(Return(inFd));
117     EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0));
118     EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))).WillOnce(Return(0));
119 
120     EXPECT_FALSE(handler.sendContents(filePath, session));
121 }
122 
123 TEST_F(NetHandleTest, getaddrinfoFail)
124 {
125     expectOpenFile();
126 
127     EXPECT_CALL(sysMock,
128                 getaddrinfo(StrEq(host), StrEq(port),
129                             AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
130                                   Field(&addrinfo::ai_family, AF_UNSPEC),
131                                   Field(&addrinfo::ai_socktype, SOCK_STREAM)),
132                             NotNull()))
133         .WillOnce(Return(EAI_ADDRFAMILY));
134 
135     EXPECT_FALSE(handler.sendContents(filePath, session));
136 }
137 
138 TEST_F(NetHandleTest, connectFail)
139 {
140     expectOpenFile();
141     expectAddrInfo();
142 
143     EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
144         .WillOnce(Return(connFd));
145     EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
146     EXPECT_CALL(
147         sysMock,
148         connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)))
149         .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1));
150 
151     EXPECT_FALSE(handler.sendContents(filePath, session));
152 }
153 
154 TEST_F(NetHandleTest, sendfileFail)
155 {
156     expectOpenFile();
157     expectAddrInfo();
158     expectConnection();
159 
160     EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _))
161         .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1));
162 
163     EXPECT_FALSE(handler.sendContents(filePath, session));
164 }
165 
166 TEST_F(NetHandleTest, successOneChunk)
167 {
168     expectOpenFile();
169     expectAddrInfo();
170     expectConnection();
171 
172     {
173         InSequence seq;
174 
175         EXPECT_CALL(sysMock,
176                     sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize)))
177             .WillOnce(
178                 DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize)));
179         EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
180                                       Gt(fakeFileSize)))
181             .WillOnce(Return(0));
182     }
183 
184     struct ipmi_flash::ExtChunkHdr chunk;
185     chunk.length = fakeFileSize;
186     std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
187     std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
188     EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes)));
189 
190     EXPECT_CALL(progMock, updateProgress(fakeFileSize));
191 
192     EXPECT_TRUE(handler.sendContents(filePath, session));
193 }
194 
195 TEST_F(NetHandleTest, successMultiChunk)
196 {
197     expectOpenFile();
198     expectAddrInfo();
199     expectConnection();
200 
201     struct ipmi_flash::ExtChunkHdr chunk;
202     chunk.length = chunkSize;
203     std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
204     std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
205 
206     {
207         InSequence seq;
208 
209         for (std::uint32_t offset = 0; offset < fakeFileSize;
210              offset += chunkSize)
211         {
212             EXPECT_CALL(sysMock,
213                         sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize)))
214                 .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize),
215                                 Return(chunkSize)));
216 
217             EXPECT_CALL(blobMock,
218                         writeBytes(session, offset, ContainerEq(chunkBytes)));
219             EXPECT_CALL(progMock, updateProgress(chunkSize));
220         }
221         EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
222                                       Gt(chunkSize)))
223             .WillOnce(Return(0));
224     }
225 
226     EXPECT_TRUE(handler.sendContents(filePath, session));
227 }
228 
229 } // namespace
230 } // namespace host_tool
231