Comment fix: OS is not excluded from permissive.
[freeciv.git] / common / networking / packets_json.c
blob92ffe062f9cb899a514629429f537daec6024216
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #ifdef FREECIV_JSON_CONNECTION
20 #include "fc_prehdrs.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <limits.h>
27 #ifdef HAVE_ARPA_INET_H
28 #include <arpa/inet.h>
29 #endif
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
32 #endif
34 #include <jansson.h>
36 /* utility */
37 #include "capability.h"
38 #include "fcintl.h"
39 #include "log.h"
40 #include "mem.h"
41 #include "shared.h"
42 #include "support.h"
44 /* commmon */
45 #include "dataio.h"
46 #include "game.h"
47 #include "events.h"
48 #include "map.h"
50 #include "packets_json.h"
52 /*
53 * Valid values are 0, 1 and 2. For 2 you have to set generate_stats
54 * to 1 in generate_packets.py.
56 #define PACKET_SIZE_STATISTICS 0
58 extern const char *const packet_functional_capability;
60 #define SPECHASH_TAG packet_handler
61 #define SPECHASH_ASTR_KEY_TYPE
62 #define SPECHASH_IDATA_TYPE struct packet_handlers *
63 #define SPECHASH_IDATA_FREE (packet_handler_hash_data_free_fn_t) free
64 #include "spechash.h"
66 /**************************************************************************
67 Read and return a packet from the connection 'pc'. The type of the
68 packet is written in 'ptype'. On error, the connection is closed and
69 the function returns NULL.
70 **************************************************************************/
71 void *get_packet_from_connection_json(struct connection *pc,
72 enum packet_type *ptype)
74 int len_read;
75 int whole_packet_len;
76 struct {
77 enum packet_type type;
78 int itype;
79 } utype;
80 struct data_in din;
81 void *data;
82 void *(*receive_handler)(struct connection *);
83 json_error_t error;
84 json_t *pint;
86 if (!pc->used) {
87 return NULL; /* connection was closed, stop reading */
90 if (pc->buffer->ndata < data_type_size(pc->packet_header.length)) {
91 /* Not got enough for a length field yet */
92 return NULL;
95 dio_input_init(&din, pc->buffer->data, pc->buffer->ndata);
96 dio_get_uint16_raw(&din, &len_read);
98 /* The non-compressed case */
99 whole_packet_len = len_read;
101 if ((unsigned)whole_packet_len > pc->buffer->ndata) {
102 return NULL; /* not all data has been read */
106 * At this point the packet is a plain uncompressed one. These have
107 * to have to be at least the header bytes in size.
109 if (whole_packet_len < (data_type_size(pc->packet_header.length))) {
110 log_verbose("The packet stream is corrupt. The connection "
111 "will be closed now.");
112 connection_close(pc, _("decoding error"));
113 return NULL;
116 /* Parse JSON packet. Note that json string has '\0' */
117 pc->json_packet = json_loadb((char*)pc->buffer->data + 2, whole_packet_len - 3, 0, &error);
119 /* Log errors before we scrap the data */
120 if (!pc->json_packet) {
121 log_error("ERROR: Unable to parse packet: %s", pc->buffer->data + 2);
122 log_error("%s", error.text);
125 log_packet_json("Json in: %s", pc->buffer->data + 2);
127 /* Shift remaining data to the front */
128 pc->buffer->ndata -= whole_packet_len;
129 memmove(pc->buffer->data, pc->buffer->data + whole_packet_len, pc->buffer->ndata);
131 if (!pc->json_packet) {
132 return NULL;
135 pint = json_object_get(pc->json_packet, "pid");
137 if (!pint) {
138 log_error("ERROR: Unable to get packet type.");
139 return NULL;
142 json_int_t packet_type = json_integer_value(pint);
143 utype.type = packet_type;
145 if (utype.type < 0
146 || utype.type >= PACKET_LAST
147 || (receive_handler = pc->phs.handlers->receive[utype.type]) == NULL) {
148 log_verbose("Received unsupported packet type %d (%s). The connection "
149 "will be closed now.",
150 utype.type, packet_name(utype.type));
151 connection_close(pc, _("unsupported packet type"));
152 return NULL;
155 log_packet("got packet type=(%s) len=%d from %s",
156 packet_name(utype.type), whole_packet_len,
157 is_server() ? pc->username : "server");
159 *ptype = utype.type;
161 if (pc->incoming_packet_notify) {
162 pc->incoming_packet_notify(pc, utype.type, whole_packet_len);
165 #if PACKET_SIZE_STATISTICS
167 static struct {
168 int counter;
169 int size;
170 } packets_stats[PACKET_LAST];
171 static int packet_counter = 0;
173 int packet_type = utype.itype;
174 int size = whole_packet_len;
176 if (!packet_counter) {
177 int i;
179 for (i = 0; i < PACKET_LAST; i++) {
180 packets_stats[i].counter = 0;
181 packets_stats[i].size = 0;
185 packets_stats[packet_type].counter++;
186 packets_stats[packet_type].size += size;
188 packet_counter++;
189 if (packet_counter % 100 == 0) {
190 int i, sum = 0;
192 log_test("Received packets:");
193 for (i = 0; i < PACKET_LAST; i++) {
194 if (packets_stats[i].counter == 0)
195 continue;
196 sum += packets_stats[i].size;
197 log_test(" [%-25.25s %3d]: %6d packets; %8d bytes total; "
198 "%5d bytes/packet average",
199 packet_name(i), i, packets_stats[i].counter,
200 packets_stats[i].size,
201 packets_stats[i].size / packets_stats[i].counter);
203 log_test("received %d bytes in %d packets;average size "
204 "per packet %d bytes",
205 sum, packet_counter, sum / packet_counter);
208 #endif /* PACKET_SIZE_STATISTICS */
209 data = receive_handler(pc);
210 if (!data) {
211 connection_close(pc, _("incompatible packet contents"));
212 return NULL;
213 } else {
214 return data;
218 #endif /* FREECIV_JSON_CONNECTION */