source: ogServer-Git/src/core.c @ 608709f

Last change on this file since 608709f was 37e91b2, checked in by OpenGnSys Support Team <soporte-og@…>, 4 years ago

#971 rename sources folder to src

Use the same folder as in ogClient.

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