1 /***********************************************************************************************************************************
2 Server Test Harness
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <arpa/inet.h>
7 #include <netinet/in.h>
8 #include <string.h>
9 #include <sys/socket.h>
10 #include <unistd.h>
11
12 #include <openssl/conf.h>
13 #include <openssl/ssl.h>
14
15 #include "common/crypto/common.h"
16 #include "common/error.h"
17 #include "common/io/socket/common.h"
18 #include "common/io/socket/session.h"
19 #include "common/io/tls/session.h"
20 #include "common/log.h"
21 #include "common/type/buffer.h"
22 #include "common/type/json.h"
23 #include "common/wait.h"
24
25 #include "common/harnessDebug.h"
26 #include "common/harnessServer.h"
27 #include "common/harnessTest.h"
28
29 /***********************************************************************************************************************************
30 Command enum
31 ***********************************************************************************************************************************/
32 typedef enum
33 {
34 hrnServerCmdAbort,
35 hrnServerCmdAccept,
36 hrnServerCmdClose,
37 hrnServerCmdDone,
38 hrnServerCmdExpect,
39 hrnServerCmdReply,
40 hrnServerCmdSleep,
41 } HrnServerCmd;
42
43 /***********************************************************************************************************************************
44 Constants
45 ***********************************************************************************************************************************/
46 #define HRN_SERVER_HOST "tls.test.pgbackrest.org"
47 #define HRN_SERVER_FAKE_CERT_PATH "/etc/fake-cert"
48 #define HRN_SERVER_FAKE_KEY_FILE HRN_SERVER_FAKE_CERT_PATH "/pgbackrest-test.key"
49 #define HRN_SERVER_FAKE_CERT_FILE HRN_SERVER_FAKE_CERT_PATH "/pgbackrest-test.crt"
50
51 /***********************************************************************************************************************************
52 Send commands to the server
53 ***********************************************************************************************************************************/
54 static void
hrnServerScriptCommand(IoWrite * write,HrnServerCmd cmd,const Variant * data)55 hrnServerScriptCommand(IoWrite *write, HrnServerCmd cmd, const Variant *data)
56 {
57 FUNCTION_HARNESS_BEGIN();
58 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
59 FUNCTION_HARNESS_PARAM(ENUM, cmd);
60 FUNCTION_HARNESS_PARAM(VARIANT, data);
61 FUNCTION_HARNESS_END();
62
63 ASSERT(write != NULL);
64
65 ioWriteStrLine(write, jsonFromUInt(cmd));
66 ioWriteStrLine(write, jsonFromVar(data));
67 ioWriteFlush(write);
68
69 FUNCTION_HARNESS_RETURN_VOID();
70 }
71
72 /**********************************************************************************************************************************/
hrnServerScriptBegin(IoWrite * write)73 IoWrite *hrnServerScriptBegin(IoWrite *write)
74 {
75 FUNCTION_HARNESS_BEGIN();
76 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
77 FUNCTION_HARNESS_END();
78
79 ASSERT(write != NULL);
80
81 FUNCTION_HARNESS_RETURN(IO_WRITE, write);
82 }
83
hrnServerScriptEnd(IoWrite * write)84 void hrnServerScriptEnd(IoWrite *write)
85 {
86 FUNCTION_HARNESS_BEGIN();
87 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
88 FUNCTION_HARNESS_END();
89
90 ASSERT(write != NULL);
91
92 hrnServerScriptCommand(write, hrnServerCmdDone, NULL);
93
94 FUNCTION_HARNESS_RETURN_VOID();
95 }
96
97 /**********************************************************************************************************************************/
98 void
hrnServerScriptAbort(IoWrite * write)99 hrnServerScriptAbort(IoWrite *write)
100 {
101 FUNCTION_HARNESS_BEGIN();
102 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
103 FUNCTION_HARNESS_END();
104
105 hrnServerScriptCommand(write, hrnServerCmdAbort, NULL);
106
107 FUNCTION_HARNESS_RETURN_VOID();
108 }
109
110 void
hrnServerScriptAccept(IoWrite * write)111 hrnServerScriptAccept(IoWrite *write)
112 {
113 FUNCTION_HARNESS_BEGIN();
114 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
115 FUNCTION_HARNESS_END();
116
117 hrnServerScriptCommand(write, hrnServerCmdAccept, NULL);
118
119 FUNCTION_HARNESS_RETURN_VOID();
120 }
121
122 void
hrnServerScriptClose(IoWrite * write)123 hrnServerScriptClose(IoWrite *write)
124 {
125 FUNCTION_HARNESS_BEGIN();
126 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
127 FUNCTION_HARNESS_END();
128
129 hrnServerScriptCommand(write, hrnServerCmdClose, NULL);
130
131 FUNCTION_HARNESS_RETURN_VOID();
132 }
133
134 void
hrnServerScriptExpect(IoWrite * write,const String * data)135 hrnServerScriptExpect(IoWrite *write, const String *data)
136 {
137 FUNCTION_HARNESS_BEGIN();
138 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
139 FUNCTION_HARNESS_PARAM(STRING, data);
140 FUNCTION_HARNESS_END();
141
142 ASSERT(data != NULL);
143
144 hrnServerScriptCommand(write, hrnServerCmdExpect, VARSTR(data));
145
146 FUNCTION_HARNESS_RETURN_VOID();
147 }
148
149 void
hrnServerScriptExpectZ(IoWrite * write,const char * data)150 hrnServerScriptExpectZ(IoWrite *write, const char *data)
151 {
152 FUNCTION_HARNESS_BEGIN();
153 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
154 FUNCTION_HARNESS_PARAM(STRINGZ, data);
155 FUNCTION_HARNESS_END();
156
157 ASSERT(data != NULL);
158
159 hrnServerScriptCommand(write, hrnServerCmdExpect, VARSTRZ(data));
160
161 FUNCTION_HARNESS_RETURN_VOID();
162 }
163
164 void
hrnServerScriptReply(IoWrite * write,const String * data)165 hrnServerScriptReply(IoWrite *write, const String *data)
166 {
167 FUNCTION_HARNESS_BEGIN();
168 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
169 FUNCTION_HARNESS_PARAM(STRING, data);
170 FUNCTION_HARNESS_END();
171
172 ASSERT(data != NULL);
173
174 hrnServerScriptCommand(write, hrnServerCmdReply, VARSTR(data));
175
176 FUNCTION_HARNESS_RETURN_VOID();
177 }
178
179 void
hrnServerScriptReplyZ(IoWrite * write,const char * data)180 hrnServerScriptReplyZ(IoWrite *write, const char *data)
181 {
182 FUNCTION_HARNESS_BEGIN();
183 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
184 FUNCTION_HARNESS_PARAM(STRINGZ, data);
185 FUNCTION_HARNESS_END();
186
187 ASSERT(data != NULL);
188
189 hrnServerScriptCommand(write, hrnServerCmdReply, VARSTRZ(data));
190
191 FUNCTION_HARNESS_RETURN_VOID();
192 }
193
194 void
hrnServerScriptSleep(IoWrite * write,TimeMSec sleepMs)195 hrnServerScriptSleep(IoWrite *write, TimeMSec sleepMs)
196 {
197 FUNCTION_HARNESS_BEGIN();
198 FUNCTION_HARNESS_PARAM(IO_WRITE, write);
199 FUNCTION_HARNESS_PARAM(TIME_MSEC, sleepMs);
200 FUNCTION_HARNESS_END();
201
202 ASSERT(sleepMs > 0);
203
204 hrnServerScriptCommand(write, hrnServerCmdSleep, VARUINT64(sleepMs));
205
206 FUNCTION_HARNESS_RETURN_VOID();
207 }
208
209 /**********************************************************************************************************************************/
hrnServerRun(IoRead * read,HrnServerProtocol protocol,HrnServerRunParam param)210 void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam param)
211 {
212 FUNCTION_HARNESS_BEGIN();
213 FUNCTION_HARNESS_PARAM(IO_READ, read);
214 FUNCTION_HARNESS_PARAM(ENUM, protocol);
215 FUNCTION_HARNESS_PARAM(UINT, param.port);
216 FUNCTION_HARNESS_PARAM(STRING, param.certificate);
217 FUNCTION_HARNESS_PARAM(STRING, param.key);
218 FUNCTION_HARNESS_END();
219
220 ASSERT(read != NULL);
221
222 // Set port to index 0 if not specified
223 if (param.port == 0)
224 param.port = hrnServerPort(0);
225
226 // Add test hosts
227 if (testContainer())
228 {
229 if (system("echo \"127.0.0.1 " HRN_SERVER_HOST "\" | sudo tee -a /etc/hosts > /dev/null") != 0)
230 THROW(AssertError, "unable to add test host to /etc/hosts");
231 }
232
233 // Initialize ssl and create a context
234 SSL_CTX *serverContext = NULL;
235
236 if (protocol == hrnServerProtocolTls)
237 {
238 ASSERT((param.certificate != NULL && param.key != NULL) || (param.certificate == NULL && param.key == NULL));
239
240 // If certificate and key are not set then use defaults
241 if (param.certificate == NULL)
242 {
243 // If running in a container use the installed certificate
244 if (testContainer())
245 {
246 param.certificate = strNewZ(HRN_SERVER_FAKE_CERT_FILE);
247 param.key = strNewZ(HRN_SERVER_FAKE_KEY_FILE);
248 }
249 // Else use a certificate from the test path -- tests will need to disable verify
250 else
251 {
252 param.certificate = strNewFmt("%s/" HRN_SERVER_CERT_PREFIX ".crt", hrnPathRepo());
253 param.key = strNewFmt("%s/" HRN_SERVER_CERT_PREFIX ".key", hrnPathRepo());
254 }
255 }
256
257 // Initialize TLS
258 cryptoInit();
259
260 const SSL_METHOD *method = SSLv23_method();
261 cryptoError(method == NULL, "unable to load TLS method");
262
263 serverContext = SSL_CTX_new(method);
264 cryptoError(serverContext == NULL, "unable to create TLS context");
265
266 // Configure the context by setting key and cert
267 cryptoError(
268 SSL_CTX_use_certificate_file(serverContext, strZ(param.certificate), SSL_FILETYPE_PEM) <= 0,
269 "unable to load server certificate");
270 cryptoError(
271 SSL_CTX_use_PrivateKey_file(serverContext, strZ(param.key), SSL_FILETYPE_PEM) <= 0,
272 "unable to load server private key");
273 }
274
275 // Create the socket
276 int serverSocket;
277
278 struct sockaddr_in address;
279
280 address.sin_family = AF_INET;
281 address.sin_port = htons((uint16_t)param.port);
282 address.sin_addr.s_addr = htonl(INADDR_ANY);
283
284 if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
285 THROW_SYS_ERROR(AssertError, "unable to create socket");
286
287 // Set the address as reusable so we can bind again in the same process for testing
288 int reuseAddr = 1;
289 setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
290
291 // Bind the address. It might take a bit to bind if another process was recently using it so retry a few times.
292 Wait *wait = waitNew(2000);
293 int result;
294
295 do
296 {
297 result = bind(serverSocket, (struct sockaddr *)&address, sizeof(address));
298 }
299 while (result < 0 && waitMore(wait));
300
301 if (result < 0)
302 THROW_SYS_ERROR(AssertError, "unable to bind socket");
303
304 // Listen for client connections
305 if (listen(serverSocket, 1) < 0)
306 THROW_SYS_ERROR(AssertError, "unable to listen on socket");
307
308 // Loop until no more commands
309 IoSession *serverSession = NULL;
310 bool done = false;
311
312 do
313 {
314 HrnServerCmd cmd = jsonToUInt(ioReadLine(read));
315 const Variant *data = jsonToVar(ioReadLine(read));
316
317 switch (cmd)
318 {
319 case hrnServerCmdAbort:
320 {
321 // Only makes since to abort in TLS, otherwise it is just a close
322 ASSERT(protocol == hrnServerProtocolTls);
323
324 ioSessionFree(serverSession);
325 serverSession = NULL;
326
327 break;
328 }
329
330 case hrnServerCmdAccept:
331 {
332 // Accept the socket connection
333 struct sockaddr_in addr;
334 unsigned int len = sizeof(addr);
335
336 int testClientSocket = accept(serverSocket, (struct sockaddr *)&addr, &len);
337
338 if (testClientSocket < 0)
339 THROW_SYS_ERROR(AssertError, "unable to accept socket");
340
341 // Create socket session
342 sckOptionSet(testClientSocket);
343 serverSession = sckSessionNew(ioSessionRoleServer, testClientSocket, STRDEF("localhost"), param.port, 5000);
344
345 // Start TLS if requested
346 if (protocol == hrnServerProtocolTls)
347 {
348 SSL *testClientSSL = SSL_new(serverContext);
349 serverSession = tlsSessionNew(testClientSSL, serverSession, 5000);
350 }
351
352 break;
353 }
354
355 case hrnServerCmdClose:
356 {
357 if (serverSession == NULL)
358 THROW(AssertError, "session is already closed");
359
360 ioSessionClose(serverSession);
361 ioSessionFree(serverSession);
362 serverSession = NULL;
363
364 break;
365 }
366
367 case hrnServerCmdDone:
368 done = true;
369 break;
370
371 case hrnServerCmdExpect:
372 {
373 const String *expected = varStr(data);
374
375 // Read as much as possible
376 Buffer *buffer = bufNew(strSize(expected));
377
378 TRY_BEGIN()
379 {
380 ioRead(ioSessionIoRead(serverSession), buffer);
381 }
382 CATCH(FileReadError)
383 {
384 // If nothing was read then throw the original error
385 if (bufEmpty(buffer))
386 THROW_FMT(AssertError, "server expected '%s' but got EOF", strZ(expected));
387 }
388 TRY_END();
389
390 // Treat any ? characters as wildcards so variable elements (e.g. auth hashes) can be ignored
391 String *actual = strNewBuf(buffer);
392
393 for (unsigned int actualIdx = 0; actualIdx < strSize(actual); actualIdx++)
394 {
395 if (strZ(expected)[actualIdx] == '?')
396 ((char *)strZ(actual))[actualIdx] = '?';
397 }
398
399 // Error if actual does not match expected
400 if (!strEq(actual, expected))
401 THROW_FMT(AssertError, "server expected '%s' but got '%s'", strZ(expected), strZ(actual));
402
403 break;
404 }
405
406 case hrnServerCmdReply:
407 ioWrite(ioSessionIoWrite(serverSession), BUFSTR(varStr(data)));
408 ioWriteFlush(ioSessionIoWrite(serverSession));
409 break;
410
411 case hrnServerCmdSleep:
412 sleepMSec(varUInt64Force(data));
413 break;
414 }
415 }
416 while (!done);
417
418 // Free TLS context
419 if (protocol == hrnServerProtocolTls)
420 SSL_CTX_free(serverContext);
421
422 FUNCTION_HARNESS_RETURN_VOID();
423 }
424
425 /**********************************************************************************************************************************/
hrnServerHost(void)426 const String *hrnServerHost(void)
427 {
428 return strNewZ(testContainer() ? HRN_SERVER_HOST : "127.0.0.1");
429 }
430
431 /**********************************************************************************************************************************/
hrnServerPort(unsigned int portIdx)432 unsigned int hrnServerPort(unsigned int portIdx)
433 {
434 ASSERT(portIdx < HRN_SERVER_PORT_MAX);
435
436 return 44443 + (HRN_SERVER_PORT_MAX * testIdx()) + portIdx;
437 }
438