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

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

ogServer is AGPLv3+

Update license header in files.

  • 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_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
189static void og_agent_deliver_pending_cmd(struct og_client *cli)
190{
191        const struct og_cmd *cmd;
192
193        cmd = og_cmd_find(inet_ntoa(cli->addr.sin_addr));
194        if (!cmd)
195                return;
196
197        og_send_request(cmd->method, cmd->type, &cmd->params, cmd->json);
198        cli->last_cmd_id = cmd->id;
199
200        og_cmd_free(cmd);
201}
202
203static void og_agent_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
204{
205        struct og_client *cli;
206        int ret;
207
208        cli = container_of(io, struct og_client, io);
209
210        ret = og_client_recv(cli, events);
211        if (ret <= 0)
212                goto close;
213
214        ev_timer_again(loop, &cli->timer);
215
216        cli->buf_len += ret;
217        if (cli->buf_len >= sizeof(cli->buf)) {
218                syslog(LOG_ERR, "client request from %s:%hu is too long\n",
219                       inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
220                goto close;
221        }
222
223        switch (cli->state) {
224        case OG_AGENT_RECEIVING_HEADER:
225                ret = og_agent_state_recv_hdr_rest(cli);
226                if (ret < 0)
227                        goto close;
228                if (!ret)
229                        return;
230
231                cli->state = OG_AGENT_RECEIVING_PAYLOAD;
232                /* Fall through. */
233        case OG_AGENT_RECEIVING_PAYLOAD:
234                /* Still not enough data to process request. */
235                if (cli->buf_len < cli->msg_len)
236                        return;
237
238                cli->state = OG_AGENT_PROCESSING_RESPONSE;
239                /* fall through. */
240        case OG_AGENT_PROCESSING_RESPONSE:
241                ret = og_agent_state_process_response(cli);
242                if (ret < 0) {
243                        syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
244                               inet_ntoa(cli->addr.sin_addr),
245                               ntohs(cli->addr.sin_port));
246                        goto close;
247                } else if (ret == 0) {
248                        og_agent_deliver_pending_cmd(cli);
249                }
250
251                og_agent_reset_state(cli);
252                break;
253        default:
254                syslog(LOG_ERR, "unknown state, critical internal error\n");
255                goto close;
256        }
257        return;
258close:
259        ev_timer_stop(loop, &cli->timer);
260        og_client_release(loop, cli);
261}
262
263static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
264{
265        struct og_client *cli;
266
267        cli = container_of(timer, struct og_client, timer);
268        if (cli->agent) {
269                ev_timer_again(loop, &cli->timer);
270                return;
271        }
272        syslog(LOG_ERR, "timeout request for client %s:%hu\n",
273               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
274
275        og_client_release(loop, cli);
276}
277
278static void og_agent_send_refresh(struct og_client *cli)
279{
280        struct og_msg_params params;
281        int err;
282
283        params.ips_array[0] = inet_ntoa(cli->addr.sin_addr);
284        params.ips_array_len = 1;
285
286        err = og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, &params, NULL);
287        if (err < 0) {
288                syslog(LOG_ERR, "Can't send refresh to: %s\n",
289                       params.ips_array[0]);
290        } else {
291                syslog(LOG_INFO, "Sent refresh to: %s\n",
292                       params.ips_array[0]);
293        }
294}
295
296/* Shut down connection if there is no complete message after 10 seconds. */
297#define OG_CLIENT_TIMEOUT       10
298
299/* Agent client operation might take longer, shut down after 30 seconds. */
300#define OG_AGENT_CLIENT_TIMEOUT 30
301
302#define OG_TCP_KEEPALIVE_IDLE   60
303#define OG_TCP_KEEPALIVE_INTL   30
304#define OG_TCP_KEEPALIVE_CNT    4
305
306int socket_rest, socket_agent_rest;
307
308void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events)
309{
310        int intl = OG_TCP_KEEPALIVE_INTL, cnt = OG_TCP_KEEPALIVE_CNT;
311        int on = 1, idle = OG_TCP_KEEPALIVE_IDLE;
312        struct sockaddr_in client_addr;
313        socklen_t addrlen = sizeof(client_addr);
314        struct og_client_wol *cli_wol;
315        struct og_client *cli;
316        int client_sd;
317
318        if (events & EV_ERROR)
319                return;
320
321        client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen);
322        if (client_sd < 0) {
323                syslog(LOG_ERR, "cannot accept client connection\n");
324                return;
325        }
326
327        setsockopt(client_sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int));
328        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int));
329        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPINTVL, &intl, sizeof(int));
330        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(int));
331
332        cli_wol = og_client_wol_find(&client_addr.sin_addr);
333        if (cli_wol)
334                og_client_wol_destroy(cli_wol);
335
336        cli = (struct og_client *)calloc(1, sizeof(struct og_client));
337        if (!cli) {
338                close(client_sd);
339                return;
340        }
341        memcpy(&cli->addr, &client_addr, sizeof(client_addr));
342
343        if (io->fd == socket_agent_rest) {
344                cli->agent = true;
345                ev_io_init(&cli->io, og_agent_read_cb, client_sd, EV_READ);
346        } else {
347                ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ);
348        }
349
350        ev_io_start(loop, &cli->io);
351        if (io->fd == socket_agent_rest) {
352                ev_timer_init(&cli->timer, og_client_timer_cb,
353                              OG_AGENT_CLIENT_TIMEOUT, 0.);
354        } else {
355                ev_timer_init(&cli->timer, og_client_timer_cb,
356                              OG_CLIENT_TIMEOUT, 0.);
357        }
358        ev_timer_start(loop, &cli->timer);
359        og_client_add(cli);
360
361        if (io->fd == socket_agent_rest) {
362                og_agent_send_refresh(cli);
363        }
364}
365
366int og_socket_server_init(const char *port)
367{
368        struct sockaddr_in local;
369        int sd, on = 1;
370
371        sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
372        if (sd < 0) {
373                syslog(LOG_ERR, "cannot create main socket\n");
374                return -1;
375        }
376        setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
377
378        local.sin_addr.s_addr = htonl(INADDR_ANY);
379        local.sin_family = AF_INET;
380        local.sin_port = htons(atoi(port));
381
382        if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) {
383                close(sd);
384                syslog(LOG_ERR, "cannot bind socket\n");
385                return -1;
386        }
387
388        listen(sd, 250);
389
390        return sd;
391}
Note: See TracBrowser for help on using the repository browser.