source: ogServer-Git/src/core.c @ 8a0a32c

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

#580 remove old keepalive code

Needed by the old socket Hydra that does not exist anymore

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