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, getaddrinfoFail)
114 {
115     expectOpenFile();
116 
117     EXPECT_CALL(sysMock,
118                 getaddrinfo(StrEq(host), StrEq(port),
119                             AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST),
120                                   Field(&addrinfo::ai_family, AF_UNSPEC),
121                                   Field(&addrinfo::ai_socktype, SOCK_STREAM)),
122                             NotNull()))
123         .WillOnce(Return(EAI_ADDRFAMILY));
124 
125     EXPECT_FALSE(handler.sendContents(filePath, session));
126 }
127 
128 TEST_F(NetHandleTest, connectFail)
129 {
130     expectOpenFile();
131     expectAddrInfo();
132 
133     EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0))
134         .WillOnce(Return(connFd));
135     EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0));
136     EXPECT_CALL(
137         sysMock,
138         connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)))
139         .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1));
140 
141     EXPECT_FALSE(handler.sendContents(filePath, session));
142 }
143 
144 TEST_F(NetHandleTest, sendfileFail)
145 {
146     expectOpenFile();
147     expectAddrInfo();
148     expectConnection();
149 
150     EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _))
151         .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1));
152 
153     EXPECT_FALSE(handler.sendContents(filePath, session));
154 }
155 
156 TEST_F(NetHandleTest, successOneChunk)
157 {
158     expectOpenFile();
159     expectAddrInfo();
160     expectConnection();
161 
162     {
163         InSequence seq;
164 
165         EXPECT_CALL(sysMock,
166                     sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize)))
167             .WillOnce(
168                 DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize)));
169         EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
170                                       Gt(fakeFileSize)))
171             .WillOnce(Return(0));
172     }
173 
174     struct ipmi_flash::ExtChunkHdr chunk;
175     chunk.length = fakeFileSize;
176     std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
177     std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
178     EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes)));
179 
180     EXPECT_CALL(progMock, updateProgress(fakeFileSize));
181 
182     EXPECT_TRUE(handler.sendContents(filePath, session));
183 }
184 
185 TEST_F(NetHandleTest, successMultiChunk)
186 {
187     expectOpenFile();
188     expectAddrInfo();
189     expectConnection();
190 
191     struct ipmi_flash::ExtChunkHdr chunk;
192     chunk.length = chunkSize;
193     std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
194     std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
195 
196     {
197         InSequence seq;
198 
199         for (std::uint32_t offset = 0; offset < fakeFileSize;
200              offset += chunkSize)
201         {
202             EXPECT_CALL(sysMock,
203                         sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize)))
204                 .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize),
205                                 Return(chunkSize)));
206 
207             EXPECT_CALL(blobMock,
208                         writeBytes(session, offset, ContainerEq(chunkBytes)));
209             EXPECT_CALL(progMock, updateProgress(chunkSize));
210         }
211         EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize),
212                                       Gt(chunkSize)))
213             .WillOnce(Return(0));
214     }
215 
216     EXPECT_TRUE(handler.sendContents(filePath, session));
217 }
218 
219 } // namespace
220 } // namespace host_tool
221