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

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

#998 disable incremental image API

This API is not supported by ogClient yet and it uses the obsolete socket hydra API.

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