source: ogServer-Git/src/core.c @ 36f0232

Last change on this file since 36f0232 was 2d68f8a, checked in by OpenGnSys Support Team <soporte-og@…>, 3 years ago

#1061 add timeout to pending scheduled commands

Pending schedule commands can deny ogLive boot of clients due
to filling of pending cmd queue with commands such as "Iniciar Sesión".

For example: Using RemotePC to serve clients that do not boot into
ogLive will fill up the pending command queue with "Iniciar Sesión".

Introduce a safety timeout for pending (scheduled) commands to
avoid this situation.

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/*
2 * Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Affero General Public License as published by the
6 * Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 */
9
10#include "ogAdmServer.h"
11#include "dbi.h"
12#include "utils.h"
13#include "list.h"
14#include "rest.h"
15#include "wol.h"
16#include "client.h"
17#include "json.h"
18#include "schedule.h"
19#include <syslog.h>
20#include <sys/ioctl.h>
21#include <ifaddrs.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <netinet/tcp.h>
25#include <fcntl.h>
26#include <jansson.h>
27#include <time.h>
28
29static void og_client_release(struct ev_loop *loop, struct og_client *cli)
30{
31        list_del(&cli->list);
32        ev_io_stop(loop, &cli->io);
33        close(cli->io.fd);
34        free(cli);
35}
36
37static int og_client_payload_too_large(struct og_client *cli)
38{
39        char buf[] = "HTTP/1.1 413 Payload Too Large\r\n"
40                     "Content-Length: 0\r\n\r\n";
41
42        send(og_client_socket(cli), buf, strlen(buf), 0);
43
44        return -1;
45}
46
47static int og_client_state_recv_hdr_rest(struct og_client *cli)
48{
49        char *ptr;
50
51        ptr = strstr(cli->buf, "\r\n\r\n");
52        if (!ptr)
53                return 0;
54
55        cli->msg_len = ptr - cli->buf + 4;
56
57        ptr = strstr(cli->buf, "Content-Length: ");
58        if (ptr) {
59                sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length);
60                if (cli->content_length < 0)
61                        return -1;
62                cli->msg_len += cli->content_length;
63        }
64
65        ptr = strstr(cli->buf, "Authorization: ");
66        if (ptr)
67                sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token);
68
69        return 1;
70}
71
72static int og_client_recv(struct og_client *cli, int events)
73{
74        struct ev_io *io = &cli->io;
75        int ret;
76
77        if (events & EV_ERROR) {
78                syslog(LOG_ERR, "unexpected error event from client %s:%hu\n",
79                               inet_ntoa(cli->addr.sin_addr),
80                               ntohs(cli->addr.sin_port));
81                return 0;
82        }
83
84        ret = recv(io->fd, cli->buf + cli->buf_len,
85                   sizeof(cli->buf) - cli->buf_len, 0);
86        if (ret <= 0) {
87                if (ret < 0) {
88                        syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n",
89                               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port),
90                               strerror(errno));
91                }
92                return ret;
93        }
94
95        return ret;
96}
97
98static void og_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
99{
100        struct og_client *cli;
101        int ret;
102
103        cli = container_of(io, struct og_client, io);
104
105        ret = og_client_recv(cli, events);
106        if (ret <= 0)
107                goto close;
108
109        ev_timer_again(loop, &cli->timer);
110
111        cli->buf_len += ret;
112        if (cli->buf_len >= sizeof(cli->buf)) {
113                syslog(LOG_ERR, "client request from %s:%hu is too long\n",
114                       inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
115                og_client_payload_too_large(cli);
116                goto close;
117        }
118
119        switch (cli->state) {
120        case OG_CLIENT_RECEIVING_HEADER:
121                ret = og_client_state_recv_hdr_rest(cli);
122                if (ret < 0)
123                        goto close;
124                if (!ret)
125                        return;
126
127                cli->state = OG_CLIENT_RECEIVING_PAYLOAD;
128                /* Fall through. */
129        case OG_CLIENT_RECEIVING_PAYLOAD:
130                /* Still not enough data to process request. */
131                if (cli->buf_len < cli->msg_len)
132                        return;
133
134                cli->state = OG_CLIENT_PROCESSING_REQUEST;
135                /* fall through. */
136        case OG_CLIENT_PROCESSING_REQUEST:
137                ret = og_client_state_process_payload_rest(cli);
138                if (ret < 0) {
139                        syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
140                               inet_ntoa(cli->addr.sin_addr),
141                               ntohs(cli->addr.sin_port));
142                }
143                goto close;
144        default:
145                syslog(LOG_ERR, "unknown state, critical internal error\n");
146                goto close;
147        }
148        return;
149close:
150        ev_timer_stop(loop, &cli->timer);
151        og_client_release(loop, cli);
152}
153
154enum og_agent_state {
155        OG_AGENT_RECEIVING_HEADER       = 0,
156        OG_AGENT_RECEIVING_PAYLOAD,
157        OG_AGENT_PROCESSING_RESPONSE,
158};
159
160static int og_agent_state_recv_hdr_rest(struct og_client *cli)
161{
162        char *ptr;
163
164        ptr = strstr(cli->buf, "\r\n\r\n");
165        if (!ptr)
166                return 0;
167
168        cli->msg_len = ptr - cli->buf + 4;
169
170        ptr = strstr(cli->buf, "Content-Length: ");
171        if (ptr) {
172                sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length);
173                if (cli->content_length < 0)
174                        return -1;
175                cli->msg_len += cli->content_length;
176        }
177
178        return 1;
179}
180
181static void og_agent_reset_state(struct og_client *cli)
182{
183        cli->state = OG_AGENT_RECEIVING_HEADER;
184        cli->buf_len = 0;
185        cli->content_length = 0;
186        memset(cli->buf, 0, sizeof(cli->buf));
187}
188
189#define OG_AGENT_CMD_TIMEOUT 900
190
191static void og_agent_deliver_pending_cmd(struct og_client *cli)
192{
193        struct timeval now, elapsed;
194        const struct og_cmd *cmd;
195
196        cmd = og_cmd_find(inet_ntoa(cli->addr.sin_addr));
197        if (!cmd)
198                return;
199
200        gettimeofday(&now, NULL);
201        timersub(&now, &cmd->tv, &elapsed);
202        if (elapsed.tv_sec >= OG_AGENT_CMD_TIMEOUT) {
203                og_dbi_update_action(cmd->id, false);
204                og_cmd_free(cmd);
205                return;
206        }
207
208        og_send_request(cmd->method, cmd->type, &cmd->params, cmd->json);
209        cli->last_cmd_id = cmd->id;
210
211        og_cmd_free(cmd);
212}
213
214static void og_agent_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
215{
216        struct og_client *cli;
217        int ret;
218
219        cli = container_of(io, struct og_client, io);
220
221        ret = og_client_recv(cli, events);
222        if (ret <= 0)
223                goto close;
224
225        ev_timer_again(loop, &cli->timer);
226
227        cli->buf_len += ret;
228        if (cli->buf_len >= sizeof(cli->buf)) {
229                syslog(LOG_ERR, "client request from %s:%hu is too long\n",
230                       inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
231                goto close;
232        }
233
234        switch (cli->state) {
235        case OG_AGENT_RECEIVING_HEADER:
236                ret = og_agent_state_recv_hdr_rest(cli);
237                if (ret < 0)
238                        goto close;
239                if (!ret)
240                        return;
241
242                cli->state = OG_AGENT_RECEIVING_PAYLOAD;
243                /* Fall through. */
244        case OG_AGENT_RECEIVING_PAYLOAD:
245                /* Still not enough data to process request. */
246                if (cli->buf_len < cli->msg_len)
247                        return;
248
249                cli->state = OG_AGENT_PROCESSING_RESPONSE;
250                /* fall through. */
251        case OG_AGENT_PROCESSING_RESPONSE:
252                ret = og_agent_state_process_response(cli);
253                if (ret < 0) {
254                        syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
255                               inet_ntoa(cli->addr.sin_addr),
256                               ntohs(cli->addr.sin_port));
257                        goto close;
258                } else if (ret == 0) {
259                        og_agent_deliver_pending_cmd(cli);
260                }
261
262                og_agent_reset_state(cli);
263                break;
264        default:
265                syslog(LOG_ERR, "unknown state, critical internal error\n");
266                goto close;
267        }
268        return;
269close:
270        ev_timer_stop(loop, &cli->timer);
271        og_client_release(loop, cli);
272}
273
274static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
275{
276        struct og_client *cli;
277
278        cli = container_of(timer, struct og_client, timer);
279        if (cli->agent) {
280                ev_timer_again(loop, &cli->timer);
281                return;
282        }
283        syslog(LOG_ERR, "timeout request for client %s:%hu\n",
284               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
285
286        og_client_release(loop, cli);
287}
288
289static void og_agent_send_refresh(struct og_client *cli)
290{
291        struct og_msg_params params;
292        int err;
293
294        params.ips_array[0] = inet_ntoa(cli->addr.sin_addr);
295        params.ips_array_len = 1;
296
297        err = og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, &params, NULL);
298        if (err < 0) {
299                syslog(LOG_ERR, "Can't send refresh to: %s\n",
300                       params.ips_array[0]);
301        } else {
302                syslog(LOG_INFO, "Sent refresh to: %s\n",
303                       params.ips_array[0]);
304        }
305}
306
307/* Shut down connection if there is no complete message after 10 seconds. */
308#define OG_CLIENT_TIMEOUT       10
309
310/* Agent client operation might take longer, shut down after 30 seconds. */
311#define OG_AGENT_CLIENT_TIMEOUT 30
312
313#define OG_TCP_KEEPALIVE_IDLE   60
314#define OG_TCP_KEEPALIVE_INTL   30
315#define OG_TCP_KEEPALIVE_CNT    4
316
317int socket_rest, socket_agent_rest;
318
319void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events)
320{
321        int intl = OG_TCP_KEEPALIVE_INTL, cnt = OG_TCP_KEEPALIVE_CNT;
322        int on = 1, idle = OG_TCP_KEEPALIVE_IDLE;
323        struct sockaddr_in client_addr;
324        socklen_t addrlen = sizeof(client_addr);
325        struct og_client_wol *cli_wol;
326        struct og_client *cli;
327        int client_sd;
328
329        if (events & EV_ERROR)
330                return;
331
332        client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen);
333        if (client_sd < 0) {
334                syslog(LOG_ERR, "cannot accept client connection\n");
335                return;
336        }
337
338        setsockopt(client_sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int));
339        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int));
340        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPINTVL, &intl, sizeof(int));
341        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(int));
342
343        cli_wol = og_client_wol_find(&client_addr.sin_addr);
344        if (cli_wol)
345                og_client_wol_destroy(cli_wol);
346
347        cli = (struct og_client *)calloc(1, sizeof(struct og_client));
348        if (!cli) {
349                close(client_sd);
350                return;
351        }
352        memcpy(&cli->addr, &client_addr, sizeof(client_addr));
353
354        if (io->fd == socket_agent_rest) {
355                cli->agent = true;
356                ev_io_init(&cli->io, og_agent_read_cb, client_sd, EV_READ);
357        } else {
358                ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ);
359        }
360
361        ev_io_start(loop, &cli->io);
362        if (io->fd == socket_agent_rest) {
363                ev_timer_init(&cli->timer, og_client_timer_cb,
364                              OG_AGENT_CLIENT_TIMEOUT, 0.);
365        } else {
366                ev_timer_init(&cli->timer, og_client_timer_cb,
367                              OG_CLIENT_TIMEOUT, 0.);
368        }
369        ev_timer_start(loop, &cli->timer);
370        og_client_add(cli);
371
372        if (io->fd == socket_agent_rest) {
373                og_agent_send_refresh(cli);
374        }
375}
376
377int og_socket_server_init(const char *port)
378{
379        struct sockaddr_in local;
380        int sd, on = 1;
381
382        sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
383        if (sd < 0) {
384                syslog(LOG_ERR, "cannot create main socket\n");
385                return -1;
386        }
387        setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
388
389        local.sin_addr.s_addr = htonl(INADDR_ANY);
390        local.sin_family = AF_INET;
391        local.sin_port = htons(atoi(port));
392
393        if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) {
394                close(sd);
395                syslog(LOG_ERR, "cannot bind socket\n");
396                return -1;
397        }
398
399        listen(sd, 250);
400
401        return sd;
402}
Note: See TracBrowser for help on using the repository browser.