source: ogServer-Git/src/core.c

Last change on this file was 1379ee9, checked in by OpenGnSys Support Team <soporte-og@…>, 2 years ago

#1043 check for WoL pending confirmation logic only for client

REST API request does not need to perform a list lookup on the wol list.

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