source: ogServer-Git/src/core.c @ facd0d5

Last change on this file since facd0d5 was 64bbc0c, checked in by OpenGnSys Support Team <soporte-og@…>, 3 years ago

#1043 fix timeout refresh

as described by man(3) ev, to make it work with ev_timer_again()
otherwise timer might not ever expire.

  • Property mode set to 100644
File size: 9.2 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_timer_stop(loop, &cli->timer);
33        ev_io_stop(loop, &cli->io);
34        close(cli->io.fd);
35        free(cli);
36}
37
38static int og_client_payload_too_large(struct og_client *cli)
39{
40        char buf[] = "HTTP/1.1 413 Payload Too Large\r\n"
41                     "Content-Length: 0\r\n\r\n";
42
43        send(og_client_socket(cli), buf, strlen(buf), 0);
44
45        return -1;
46}
47
48static int og_client_state_recv_hdr_rest(struct og_client *cli)
49{
50        char *ptr;
51
52        ptr = strstr(cli->buf, "\r\n\r\n");
53        if (!ptr)
54                return 0;
55
56        cli->msg_len = ptr - cli->buf + 4;
57
58        ptr = strstr(cli->buf, "Content-Length: ");
59        if (ptr) {
60                sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length);
61                if (cli->content_length < 0)
62                        return -1;
63                cli->msg_len += cli->content_length;
64        }
65
66        ptr = strstr(cli->buf, "Authorization: ");
67        if (ptr)
68                sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token);
69
70        return 1;
71}
72
73static int og_client_recv(struct og_client *cli, int events)
74{
75        struct ev_io *io = &cli->io;
76        int ret;
77
78        if (events & EV_ERROR) {
79                syslog(LOG_ERR, "unexpected error event from client %s:%hu\n",
80                               inet_ntoa(cli->addr.sin_addr),
81                               ntohs(cli->addr.sin_port));
82                return 0;
83        }
84
85        ret = recv(io->fd, cli->buf + cli->buf_len,
86                   sizeof(cli->buf) - cli->buf_len, 0);
87        if (ret <= 0) {
88                if (ret < 0) {
89                        syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n",
90                               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port),
91                               strerror(errno));
92                }
93                return ret;
94        }
95
96        return ret;
97}
98
99static void og_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
100{
101        struct og_client *cli;
102        int ret;
103
104        cli = container_of(io, struct og_client, io);
105
106        ret = og_client_recv(cli, events);
107        if (ret <= 0)
108                goto close;
109
110        ev_timer_again(loop, &cli->timer);
111
112        cli->buf_len += ret;
113        if (cli->buf_len >= sizeof(cli->buf)) {
114                syslog(LOG_ERR, "client request from %s:%hu is too long\n",
115                       inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
116                og_client_payload_too_large(cli);
117                goto close;
118        }
119
120        switch (cli->state) {
121        case OG_CLIENT_RECEIVING_HEADER:
122                ret = og_client_state_recv_hdr_rest(cli);
123                if (ret < 0)
124                        goto close;
125                if (!ret)
126                        return;
127
128                cli->state = OG_CLIENT_RECEIVING_PAYLOAD;
129                /* Fall through. */
130        case OG_CLIENT_RECEIVING_PAYLOAD:
131                /* Still not enough data to process request. */
132                if (cli->buf_len < cli->msg_len)
133                        return;
134
135                cli->state = OG_CLIENT_PROCESSING_REQUEST;
136                /* fall through. */
137        case OG_CLIENT_PROCESSING_REQUEST:
138                ret = og_client_state_process_payload_rest(cli);
139                if (ret < 0) {
140                        syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
141                               inet_ntoa(cli->addr.sin_addr),
142                               ntohs(cli->addr.sin_port));
143                }
144                goto close;
145        default:
146                syslog(LOG_ERR, "unknown state, critical internal error\n");
147                goto close;
148        }
149        return;
150close:
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                        goto close;
255                } else if (ret == 0) {
256                        og_agent_deliver_pending_cmd(cli);
257                }
258
259                og_agent_reset_state(cli);
260                break;
261        default:
262                syslog(LOG_ERR, "unknown state, critical internal error\n");
263                goto close;
264        }
265        return;
266close:
267        og_client_release(loop, cli);
268}
269
270static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
271{
272        struct og_client *cli;
273
274        cli = container_of(timer, struct og_client, timer);
275        if (cli->agent) {
276                ev_timer_again(loop, &cli->timer);
277                return;
278        }
279        syslog(LOG_ERR, "timeout request for client %s:%hu\n",
280               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
281
282        og_client_release(loop, cli);
283}
284
285static void og_agent_send_refresh(struct og_client *cli)
286{
287        struct og_msg_params params;
288        int err;
289
290        params.ips_array[0] = inet_ntoa(cli->addr.sin_addr);
291        params.ips_array_len = 1;
292
293        err = og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, &params, NULL);
294        if (err < 0) {
295                syslog(LOG_ERR, "Can't send refresh to: %s\n",
296                       params.ips_array[0]);
297        } else {
298                syslog(LOG_INFO, "Sent refresh to: %s\n",
299                       params.ips_array[0]);
300        }
301}
302
303/* Shut down connection if there is no complete message after 10 seconds. */
304#define OG_CLIENT_TIMEOUT       10.
305
306/* Agent client operation might take longer, shut down after 30 seconds. */
307#define OG_AGENT_CLIENT_TIMEOUT 30.
308
309#define OG_TCP_KEEPALIVE_IDLE   60
310#define OG_TCP_KEEPALIVE_INTL   30
311#define OG_TCP_KEEPALIVE_CNT    4
312
313int socket_rest, socket_agent_rest;
314
315void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events)
316{
317        int intl = OG_TCP_KEEPALIVE_INTL, cnt = OG_TCP_KEEPALIVE_CNT;
318        int on = 1, idle = OG_TCP_KEEPALIVE_IDLE;
319        struct sockaddr_in client_addr;
320        socklen_t addrlen = sizeof(client_addr);
321        struct og_client_wol *cli_wol;
322        struct og_client *cli;
323        int client_sd;
324
325        if (events & EV_ERROR)
326                return;
327
328        client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen);
329        if (client_sd < 0) {
330                syslog(LOG_ERR, "cannot accept client connection\n");
331                return;
332        }
333
334        setsockopt(client_sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int));
335        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int));
336        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPINTVL, &intl, sizeof(int));
337        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(int));
338
339        cli_wol = og_client_wol_find(&client_addr.sin_addr);
340        if (cli_wol)
341                og_client_wol_destroy(cli_wol);
342
343        cli = (struct og_client *)calloc(1, sizeof(struct og_client));
344        if (!cli) {
345                close(client_sd);
346                return;
347        }
348        memcpy(&cli->addr, &client_addr, sizeof(client_addr));
349
350        if (io->fd == socket_agent_rest) {
351                cli->agent = true;
352                ev_io_init(&cli->io, og_agent_read_cb, client_sd, EV_READ);
353        } else {
354                ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ);
355        }
356
357        ev_io_start(loop, &cli->io);
358        ev_init(&cli->timer, og_client_timer_cb);
359        if (io->fd == socket_agent_rest)
360                cli->timer.repeat = OG_AGENT_CLIENT_TIMEOUT;
361        else
362                cli->timer.repeat = OG_CLIENT_TIMEOUT;
363
364        ev_timer_again(loop, &cli->timer);
365        og_client_add(cli);
366
367        if (io->fd == socket_agent_rest) {
368                og_agent_send_refresh(cli);
369        }
370}
371
372int og_socket_server_init(const char *port)
373{
374        struct sockaddr_in local;
375        int sd, on = 1;
376
377        sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
378        if (sd < 0) {
379                syslog(LOG_ERR, "cannot create main socket\n");
380                return -1;
381        }
382        setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
383
384        local.sin_addr.s_addr = htonl(INADDR_ANY);
385        local.sin_family = AF_INET;
386        local.sin_port = htons(atoi(port));
387
388        if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) {
389                close(sd);
390                syslog(LOG_ERR, "cannot bind socket\n");
391                return -1;
392        }
393
394        listen(sd, 250);
395
396        return sd;
397}
Note: See TracBrowser for help on using the repository browser.