test: Dispatch called service
[libisds.git] / test / simline / server.c
blob15a5dfab620dfdcff7a3909bf3063fd830e07f1c
1 #ifndef _POSIX_SOURCE
2 #define _POSIX_SOURCE /* For getaddrinfo(3) */
3 #endif
5 #ifndef _BSD_SOURCE
6 #define _BSD_SOURCE /* For NI_MAXHOST */
7 #endif
9 #include "../test-tools.h"
10 #include "http.h"
11 #include "server.h"
12 #include "service.h"
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netdb.h>
19 #include <string.h>
20 #include <signal.h>
21 #include <unistd.h>
22 #include <wait.h>
24 const char *server_error = NULL;
26 static const char *as_path_hotp = "/as/processLogin?type=hotp&uri=";
27 static const char *as_path_sendsms = "/as/processLogin?type=totp&sendSms=true&uri=";
28 static const char *as_path_dontsendsms = "/as/processLogin?type=totp&uri=";
29 static const char *as_path_logout = "/as/processLogout?uri=";
30 static const char *ws_path = "/apps/DS/dz";
32 static const char *authorization_cookie_name = "IPCZ-X-COOKIE";
33 static char *authorization_cookie_value = NULL;
35 /* Save pointer to static error message if not yet set */
36 void set_server_error(const char *message) {
37 if (server_error == NULL) {
38 server_error = message;
43 /* Creates listening TCP socket on localhost.
44 * Returns the socket descriptor or -1. */
45 int listen_on_socket(void) {
46 int retval;
47 struct addrinfo hints;
48 struct addrinfo *addresses, *address;
49 int fd;
51 memset(&hints, 0, sizeof(hints));
52 hints.ai_family = AF_UNSPEC;
53 hints.ai_socktype = SOCK_STREAM;
54 retval = getaddrinfo("localhost", NULL, &hints, &addresses);
55 if (retval) {
56 set_server_error("Could not resolve `localhost'");
57 return -1;
60 for (address = addresses; address != NULL; address = address->ai_next) {
61 fd = socket(address->ai_family, address->ai_socktype,
62 address->ai_protocol);
63 if (fd == -1) continue;
65 retval = bind(fd, address->ai_addr, address->ai_addrlen);
66 if (retval != 0) continue;
68 retval = listen(fd, 0);
69 if (retval == 0) {
70 freeaddrinfo(addresses);
71 return fd;
75 freeaddrinfo(addresses);
76 set_server_error("Could not start listening on TCP/localhost");
77 return -1;
81 /* Format socket address as printable string.
82 * @return allocated string or NULL in case of error. */
83 char *socket2address(int socket) {
84 struct sockaddr_storage storage;
85 socklen_t length = (socklen_t) sizeof(storage);
86 char host[NI_MAXHOST];
87 char service[NI_MAXSERV];
88 char *address = NULL;
90 if (-1 == getsockname(socket, (struct sockaddr *)&storage, &length)) {
91 set_server_error("Could not get address of server socket");
92 return NULL;
95 if (0 != getnameinfo((struct sockaddr *)&storage, length,
96 host, sizeof(host), service, sizeof(service),
97 NI_NUMERICHOST|NI_NUMERICSERV)) {
98 set_server_error("Could not resolve address of server socket");
99 return NULL;
102 if (-1 == test_asprintf(&address,
103 (strchr(host, ':') == NULL) ? "%s:%s" : "[%s]:%s",
104 host, service)) {
105 set_server_error("Could not format server address");
106 return NULL;
109 return address;
113 /* Process ISDS WS ping */
114 static void do_ws(int client_socket, const struct http_request *request) {
115 if (request->method != HTTP_METHOD_POST) {
116 http_send_response_400(client_socket,
117 "Regular ISDS web service request must be POST");
118 return;
121 soap(client_socket, request->body, request->body_length);
125 /* Do the server protocol.
126 * @server_socket is listening TCP socket of the server
127 * @server_arguments is pointer to structure:
128 * Never returns. Terminates by exit(). */
129 void server_basic_authentication(int server_socket,
130 const void *server_arguments) {
131 int client_socket;
132 const struct arguments_basic_authentication *arguments =
133 (const struct arguments_basic_authentication *) server_arguments;
134 struct http_request *request = NULL;
135 http_error error;
137 if (arguments == NULL) {
138 close(server_socket);
139 exit(EXIT_FAILURE);
142 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
143 fprintf(stderr, "Connection accepted\n");
144 error = http_read_request(client_socket, &request);
145 if (error) {
146 fprintf(stderr, "Error while reading request\n");
147 if (error == HTTP_ERROR_CLIENT)
148 http_send_response_400(client_socket, "Error in request");
149 else
150 http_send_response_500(client_socket,
151 "Error while reading request");
152 close(client_socket);
153 continue;
156 if (request->method == HTTP_METHOD_POST) {
157 /* Only POST requests are used in Basic authentication mode */
158 if (arguments->username != NULL) {
159 if (http_client_authenticates(request)) {
160 switch(http_authenticate_basic(request,
161 arguments->username, arguments->password)) {
162 case HTTP_ERROR_SUCCESS:
163 do_ws(client_socket, request);
164 break;
165 case HTTP_ERROR_CLIENT:
166 if (arguments->isds_deviations)
167 http_send_response_401_basic(client_socket);
168 else
169 http_send_response_403(client_socket);
170 break;
171 default:
172 http_send_response_500(client_socket,
173 "Server error while verifying Basic "
174 "authentication");
176 } else {
177 http_send_response_401_basic(client_socket);
179 } else {
180 do_ws(client_socket, request);
182 } else {
183 /* HTTP method unsupported per ISDS specification */
184 http_send_response_400(client_socket,
185 "Only POST method is allowed");
187 http_request_free(&request);
190 close(client_socket);
193 close(server_socket);
194 exit(EXIT_SUCCESS);
198 /* Process first phase of TOTP request */
199 static void do_as_sendsms(int client_socket, const struct http_request *request,
200 const struct arguments_otp_authentication *arguments) {
201 if (arguments == NULL) {
202 http_send_response_500(client_socket,
203 "Third argument of do_as_sendsms() is NULL");
204 return;
207 if (request->method != HTTP_METHOD_POST) {
208 http_send_response_400(client_socket,
209 "First phase TOTP request must be POST");
210 return;
213 if (!http_client_authenticates(request)) {
214 http_send_response_401_otp(client_socket,
215 "totpsendsms",
216 "authentication.error.userIsNotAuthenticated",
217 "Client did not send any authentication header");
218 return;
221 switch(http_authenticate_basic(request,
222 arguments->username, arguments->password)) {
223 case HTTP_ERROR_SUCCESS: {
224 /* Find final URI */
225 char *uri = strstr(request->uri, "&uri=");
226 if (uri == NULL) {
227 http_send_response_400(client_socket,
228 "Missing uri parameter in Request URI");
229 return;
231 uri += 5;
232 /* Build new location for second OTP phase */
233 char *location = NULL;
234 if (-1 == test_asprintf(&location, "%s%s", as_path_dontsendsms, uri)) {
235 http_send_response_500(client_socket,
236 "Could not build new localtion for "
237 "second OTP phase");
238 return;
240 char *terminator = strchr(uri, '&');
241 if (NULL != terminator)
242 location[strlen(as_path_dontsendsms) + (uri - terminator)] = '\0';
243 http_send_response_302_totp(client_socket,
244 "authentication.info.totpSended",
245 "=?UTF-8?B?SmVkbm9yw6F6b3bDvSBrw7NkIG9kZXNsw6FuLg==?=",
246 location);
247 free(location);
249 break;
250 case HTTP_ERROR_CLIENT:
251 if (arguments->isds_deviations)
252 http_send_response_401_otp(client_socket,
253 "totpsendsms",
254 "authentication.error.userIsNotAuthenticated",
255 " Retry: Bad user name or password in first OTP phase.\r\n"
256 " This is very long header\r\n"
257 " which should span to more lines.\r\n"
258 " Surrounding LWS are meaning-less. ");
259 else
260 http_send_response_403(client_socket);
261 break;
262 default:
263 http_send_response_500(client_socket,
264 "Could not verify Basic authentication");
269 /* Return static string representation of HTTP OTP authentication method.
270 * Or NULL in case of error. */
271 static const char *auth_otp_method2string(enum auth_otp_method method) {
272 switch (method) {
273 case AUTH_OTP_HMAC: return "hotp";
274 case AUTH_OTP_TIME: return "totp";
275 default: return NULL;
280 /* Process second phase of OTP request */
281 static void do_as_phase_two(int client_socket, const struct http_request *request,
282 const struct arguments_otp_authentication *arguments) {
283 if (arguments == NULL) {
284 http_send_response_500(client_socket,
285 "Third argument of do_as_phase_two() is NULL");
286 return;
289 if (request->method != HTTP_METHOD_POST) {
290 http_send_response_400(client_socket,
291 "Second phase OTP request must be POST");
292 return;
295 if (!http_client_authenticates(request)) {
296 http_send_response_401_otp(client_socket,
297 auth_otp_method2string(arguments->method),
298 "authentication.error.userIsNotAuthenticated",
299 "Client did not send any authentication header");
300 return;
303 switch(http_authenticate_otp(request,
304 arguments->username, arguments->password, arguments->otp)) {
305 case HTTP_ERROR_SUCCESS: {
306 /* Find final URI */
307 char *uri = strstr(request->uri, "&uri=");
308 if (uri == NULL) {
309 http_send_response_400(client_socket,
310 "Missing uri parameter in Request URI");
311 return;
313 uri += 5;
314 /* Build new location for final request */
315 char *location = NULL;
316 if (NULL == (location = strdup(uri))) {
317 http_send_response_500(client_socket,
318 "Could not build new location for final request");
319 return;
321 char *terminator = strchr(location, '&');
322 if (NULL != terminator)
323 *terminator = '\0';
324 /* Generate pseudo-random cookie value. This is to prevent
325 * client from reusing the cookie accidentally. We use the
326 * same seed to get reproducible tests. */
327 if (-1 == test_asprintf(&authorization_cookie_value, "%d",
328 rand())) {
329 http_send_response_500(client_socket,
330 "Could not generate cookie value");
331 free(location);
332 return;
334 /* XXX: Add Path parameter to cookie, otherwise
335 * different paths will not match.
336 * FIXME: Domain argument does not work with cURL. Report a bug. */
337 http_send_response_302_cookie(client_socket,
338 authorization_cookie_name,
339 authorization_cookie_value,
340 /*http_find_host(request)*/NULL,
341 /*NULL*/"/",
342 location);
343 free(location);
345 break;
346 case HTTP_ERROR_CLIENT:
347 if (arguments->isds_deviations)
348 http_send_response_401_otp(client_socket,
349 auth_otp_method2string(arguments->method),
350 "authentication.error.userIsNotAuthenticated",
351 " Retry: Bad user name or password in second OTP phase.\r\n"
352 " This is very long header\r\n"
353 " which should span to more lines.\r\n"
354 " Surrounding LWS are meaning-less. ");
355 else
356 http_send_response_403(client_socket);
357 break;
358 default:
359 http_send_response_500(client_socket,
360 "Could not verify OTP authentication");
365 /* Process OTP session cookie invalidation request */
366 static void do_as_logout(int client_socket, const struct http_request *request,
367 const struct arguments_otp_authentication *arguments) {
368 if (arguments == NULL) {
369 http_send_response_500(client_socket,
370 "Third argument of do_as_logout() is NULL");
371 return;
374 if (request->method != HTTP_METHOD_GET) {
375 http_send_response_400(client_socket,
376 "OTP cookie invalidation request must be GET");
377 return;
380 const char *received_cookie =
381 http_find_cookie(request, authorization_cookie_name);
383 if (authorization_cookie_value == NULL || received_cookie == NULL ||
384 strcmp(authorization_cookie_value, received_cookie)) {
385 http_send_response_403(client_socket);
386 return;
389 /* XXX: Add Path parameter to cookie, otherwise
390 * different paths will not match.
391 * FIXME: Domain argument does not work with cURL. Report a bug. */
392 http_send_response_200_cookie(client_socket,
393 authorization_cookie_name,
395 /*http_find_host(request)*/ NULL,
396 /*NULL*/"/",
397 NULL, 0, NULL);
401 /* Process ISDS WS ping authorized by cookie */
402 static void do_ws_with_cookie(int client_socket, const struct http_request *request,
403 const struct arguments_otp_authentication *arguments) {
404 const char *received_cookie =
405 http_find_cookie(request, authorization_cookie_name);
407 if (authorization_cookie_value != NULL && received_cookie != NULL &&
408 !strcmp(authorization_cookie_value, received_cookie))
409 do_ws(client_socket, request);
410 else
411 http_send_response_403(client_socket);
415 /* Do the server protocol with OTP authentication.
416 * @server_socket is listening TCP socket of the server
417 * @server_arguments is pointer to structure arguments_otp_authentication. It
418 * selects OTP method to enable.
419 * Never returns. Terminates by exit(). */
420 void server_otp_authentication(int server_socket,
421 const void *server_arguments) {
422 int client_socket;
423 const struct arguments_otp_authentication *arguments =
424 (const struct arguments_otp_authentication *) server_arguments;
425 struct http_request *request = NULL;
426 http_error error;
428 if (arguments == NULL) {
429 close(server_socket);
430 exit(EXIT_FAILURE);
433 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
434 fprintf(stderr, "Connection accepted\n");
435 error = http_read_request(client_socket, &request);
436 if (error) {
437 fprintf(stderr, "Error while reading request\n");
438 if (error == HTTP_ERROR_CLIENT)
439 http_send_response_400(client_socket, "Error in request");
440 else
441 http_send_response_500(client_socket, "Could not read request");
442 close(client_socket);
443 continue;
446 if (arguments->username != NULL) {
447 if (arguments->method == AUTH_OTP_HMAC &&
448 !strncmp(request->uri, as_path_hotp, strlen(as_path_hotp))) {
449 do_as_phase_two(client_socket, request, arguments);
450 } else if (arguments->method == AUTH_OTP_TIME &&
451 !strncmp(request->uri, as_path_sendsms,
452 strlen(as_path_sendsms))) {
453 do_as_sendsms(client_socket, request, arguments);
454 } else if (arguments->method == AUTH_OTP_TIME &&
455 !strncmp(request->uri, as_path_dontsendsms,
456 strlen(as_path_dontsendsms))) {
457 do_as_phase_two(client_socket, request, arguments);
458 } else if (!strncmp(request->uri, as_path_logout,
459 strlen(as_path_logout))) {
460 do_as_logout(client_socket, request, arguments);
461 } else if (!strcmp(request->uri, ws_path)) {
462 do_ws_with_cookie(client_socket, request, arguments);
463 } else {
464 http_send_response_400(client_socket,
465 "Unknown path for TOTP authenticating service");
467 } else {
468 if (!strcmp(request->uri, ws_path)) {
469 do_ws(client_socket, request);
470 } else {
471 http_send_response_400(client_socket,
472 "Unknown path for TOTP authenticating service");
475 http_request_free(&request);
478 close(client_socket);
481 close(server_socket);
482 free(authorization_cookie_value);
483 exit(EXIT_SUCCESS);
487 /* Implementation of server that is out of order.
488 * It always sends back SOAP Fault with HTTP error 503.
489 * @server_socket is listening TCP socket of the server
490 * @server_arguments is ununsed pointer
491 * Never returns. Terminates by exit(). */
492 void server_out_of_order(int server_socket,
493 const void *server_arguments) {
494 int client_socket;
495 struct http_request *request = NULL;
496 http_error error;
497 const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
498 const char *fault = "<?xml version='1.0' encoding='UTF-8'?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode xsi:type=\"xsd:string\">Probíhá plánovaná údržba</faultcode><faultstring xsi:type=\"xsd:string\">Omlouváme se všem uživatelům datových schránek za dočasné omezení přístupu do systému datových schránek z důvodu plánované údržby systému. Děkujeme za pochopení.</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>";
500 while (0 <= (client_socket = accept(server_socket, NULL, NULL))) {
501 fprintf(stderr, "Connection accepted\n");
502 error = http_read_request(client_socket, &request);
503 if (error) {
504 fprintf(stderr, "Error while reading request\n");
505 if (error == HTTP_ERROR_CLIENT)
506 http_send_response_400(client_socket, "Error in request");
507 close(client_socket);
508 continue;
511 http_send_response_503(client_socket, fault, strlen(fault),
512 soap_mime_type);
513 http_request_free(&request);
515 close(client_socket);
518 close(server_socket);
519 exit(EXIT_SUCCESS);
523 /* Start sever in separate process.
524 * @server_process is PID of forked server
525 * @server_address is automatically allocated TCP address of listening server
526 * @username sets required user name server has to require. Set NULL to
527 * disable HTTP authentication.
528 * @password sets required password server has to require
529 * socket.
530 * @isds_deviations is flag to set conformance level. If false, server is
531 * compliant to standards (HTTP, SOAP) if not conflicts with ISDS
532 * specification. Otherwise server mimics real ISDS implementation as much
533 * as possible.
534 * @return -1 in case of error. */
535 int start_server(pid_t *server_process, char **server_address,
536 void (*server_implementation)(int, const void *),
537 const void *server_arguments) {
538 int server_socket;
540 if (server_address == NULL) {
541 set_server_error("start_server(): Got invalid server_address pointer");
542 return -1;
544 *server_address = NULL;
546 if (server_process == NULL) {
547 set_server_error("start_server(): Got invalid server_process pointer");
548 return -1;
551 server_socket = listen_on_socket();
552 if (server_socket == -1) {
553 set_server_error("Could not create listening socket");
554 return -1;
557 *server_address = socket2address(server_socket);
558 if (*server_address == NULL) {
559 close(server_socket);
560 set_server_error("Could not format address of listening address");
561 return -1;
564 *server_process = fork();
565 if (*server_process == -1) {
566 close(server_socket);
567 set_server_error("Server could not been forked");
568 return -1;
571 if (*server_process == 0) {
572 server_implementation(server_socket, server_arguments);
573 /* Does not return */
576 return 0;
580 /* Kill the server process.
581 * Return -1 in case of error. */
582 int stop_server(pid_t server_process) {
583 if (server_process <= 0) {
584 set_server_error("Invalid server PID to kill");
585 return -1;
587 if (-1 == kill(server_process, SIGTERM)) {
588 set_server_error("Could not terminate server");
589 return -1;
591 if (-1 == waitpid(server_process, NULL, 0)) {
592 set_server_error("Could not wait for server termination");
593 return -1;
595 return 0;