source: ogServer-Git/src/core.c @ 927d42b

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

#1043 add WOL_SENT state

WOL_SENT tells that WakeOnLan? was sent to computer, after 60 seconds,
if computer does not boot, this state is released.

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