modify logging to simplify i18n
[voxelands-alt.git] / src / lib / net_udp.c
blobd098bc7526e555d0117d5ad601135ee420f4d15a
1 /************************************************************************
2 * net_udp.c
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
20 #include "common.h"
21 #include "file.h"
22 #include "list.h"
23 #define _NET_LOCAL
24 #include "net.h"
26 #include <string.h>
27 #ifndef WIN32
28 #include <unistd.h>
29 #include <errno.h>
30 #endif
32 /* create a new udp client connection */
33 net_connection_t *udp_client_connect(char* host, char* port)
35 net_connection_t *n = net_connection();
36 n->type = NETTYPE_UDP;
37 n->host = strdup(host);
38 n->port = strdup(port);
40 udp_client_reconnect(n);
42 return n;
45 /* reconnect a close udp connection */
46 int udp_client_reconnect(net_connection_t *n)
48 int max_retries = config_get_int("net.max_retries");
49 if (!max_retries)
50 max_retries = 5;
51 if (n->state == NETSTATE_OPEN || n->tries > max_retries)
52 return 2;
54 n->tries++;
56 if (n->state == NETSTATE_UNUSED) {
57 int f = config_get_int("net.ipv4");
58 int s = config_get_int("net.ipv6");
59 memset(&n->hints, 0, sizeof(n->hints));
60 if (s == f) {
61 n->hints.ai_family = AF_UNSPEC;
62 }else if (s) {
63 n->hints.ai_family = AF_INET6;
64 }else if (f) {
65 n->hints.ai_family = AF_INET;
67 n->hints.ai_socktype = SOCK_DGRAM;
69 /* resolve hostname */
70 if (getaddrinfo(n->host, n->port, &n->hints, &n->addr)) {
71 vlprintf(CN_ERROR, "Unable to resolve host '%s'",n->host);
72 return 1;
75 n->state = NETSTATE_CLOSED;
78 /* open socket */
79 if ((n->fd = socket(n->addr->ai_family, n->addr->ai_socktype, n->addr->ai_protocol)) == -1) {
80 vlprintf(CN_ERROR, "Unable to reconnect to host %s",n->host);
81 return 1;
84 /* connect to server */
85 if (connect(n->fd, n->addr->ai_addr, n->addr->ai_addrlen)) {
86 shutdown(n->fd,2);
87 vlprintf(CN_ERROR, "Unable to reconnect to host %s",n->host);
88 return 1;
91 n->state = NETSTATE_OPEN;
92 n->tries = 0;
94 return 0;
97 /* create a new udp server connection */
98 net_connection_t *udp_host_connect(char* host, char* port)
100 net_connection_t *n = net_connection();
101 if (!host)
102 host = "*";
104 n->type = NETTYPE_UDP_HOST;
105 n->tries = 0;
106 n->host = strdup(host);
107 n->port = strdup(port);
108 n->peers = array_create(ARRAY_TYPE_PTR);
110 if (udp_host_reconnect(n)) {
111 net_close(n);
112 return NULL;
115 return n;
118 /* start listening for udp clients */
119 int udp_host_reconnect(net_connection_t *n)
121 int max_retries = config_get_int("net.max_retries");
122 if (!max_retries)
123 max_retries = 5;
124 if (n->state == NETSTATE_OPEN || n->tries > max_retries)
125 return 2;
127 n->tries++;
129 if (n->state == NETSTATE_UNUSED) {
130 char* nhost = NULL;
131 int f = config_get_int("net.ipv4");
132 int s = config_get_int("net.ipv6");
134 memset(&n->hints, 0, sizeof(n->hints));
135 if (s == f) {
136 n->hints.ai_family = AF_UNSPEC;
137 }else if (s) {
138 n->hints.ai_family = AF_INET6;
139 }else if (f) {
140 n->hints.ai_family = AF_INET;
142 n->hints.ai_socktype = SOCK_DGRAM;
143 n->hints.ai_flags = AI_PASSIVE;
145 if (strcmp(n->host,"*"))
146 nhost = n->host;
148 /* resolve hostname */
149 if (getaddrinfo(nhost, n->port, &n->hints, &n->addr)) {
150 vlprintf(CN_ERROR, "Unable to resolve host '%s'",n->host);
151 return 1;
154 n->state = NETSTATE_CLOSED;
157 /* open socket */
158 if ((n->fd = socket(n->hints.ai_family, n->hints.ai_socktype, n->hints.ai_protocol)) == -1) {
159 vlprintf(CN_ERROR, "Unable to open port %u",n->port);
160 return 1;
162 /* bind to the socket */
163 if ((bind(n->fd, n->addr->ai_addr,sizeof(*n->addr->ai_addr))) < 0) {
164 vlprintf(CN_ERROR, "Unable to bind port %u",n->port);
165 return 1;
168 n->state = NETSTATE_OPEN;
169 n->tries = 0;
171 return 0;
174 /* write data to a udp connection */
175 int udp_write(net_connection_t *n, void *buff, unsigned int size)
177 int r = 0;
178 if (n->state == NETSTATE_OPEN) {
180 if (n->type == NETTYPE_UDP_HOST) {
181 r = (int)sendto(n->fd, buff, size, 0, (struct sockaddr*)&n->remote_addr, n->remote_addr_len);
182 }else{
183 r = (int)write(n->fd,buff,size);
185 if (r < 1) {
186 vlprintf(CN_ERROR, "failed to write to connection %d (%d)",n->fd,errno);
187 if (n->type != NETTYPE_UDP_HOST)
188 shutdown(n->fd,2);
189 n->state = NETSTATE_CLOSED;
192 return r;
195 static int udp_process(net_connection_t *n)
197 char buff[2048];
198 int l;
200 errno = 0;
201 if (n->type == NETTYPE_UDP_HOST) {
202 l = recvfrom(n->fd, buff, 2048, 0, (struct sockaddr *)&n->remote_addr, &n->remote_addr_len);
203 }else{
204 l = (int)recv(n->fd,buff,2048,0);
207 if (l < 1) {
208 #ifndef WIN32
209 if (errno != EAGAIN && errno != EWOULDBLOCK) {
210 if (n->type != NETTYPE_UDP_HOST)
211 shutdown(n->fd,2);
212 n->state = NETSTATE_CLOSED;
214 #else
215 if (n->type != NETTYPE_UDP_HOST)
216 shutdown(n->fd,2);
217 n->state = NETSTATE_CLOSED;
218 #endif
219 }else{
220 file_t *f = file_create(NULL,NULL);
221 file_write(f,buff,l);
222 n->buff.udp.unsorted = list_push(&n->buff.udp.unsorted,f);
225 /* TODO: sort the unsorted packets */
227 return 0;
230 /* discards the current packet buffer, and returns a pointer to the next packet buffer */
231 file_t *udp_next(net_connection_t *n)
233 file_t *f;
234 if (n->state != NETSTATE_OPEN)
235 return NULL;
237 udp_process(n);
239 if (!n->buff.udp.sorted)
240 return NULL;
242 f = list_pull(&n->buff.udp.sorted);
243 if (f)
244 file_free(f);
246 return n->buff.udp.sorted;
249 /* gets a pointer to the current packet buffer */
250 file_t *udp_current(net_connection_t *n)
252 if (n->state != NETSTATE_OPEN)
253 return NULL;
255 udp_process(n);
257 return n->buff.udp.sorted;
260 /* determine if data is pending on a udp connection */
261 int udp_pending(net_connection_t *n)
263 if (n->state != NETSTATE_OPEN)
264 return 0;
266 udp_process(n);
267 return 0;
270 /* read data from a udp connection */
271 int udp_read(net_connection_t *n, void *buff, unsigned int size)
273 file_t *b;
274 int l;
276 b = udp_current(n);
277 if (!b)
278 return 0;
280 l = file_read(b,buff,size);
281 if (l > 0)
282 return l;
284 b = udp_next(n);
285 if (!b)
286 return 0;
288 return file_read(b,buff,size);
291 /* read a line from a udp connection */
292 int udp_readline(net_connection_t *n, void *buff, unsigned int size)
294 file_t *b;
295 int l;
297 b = udp_current(n);
298 if (!b)
299 return 0;
301 l = file_readline(b,buff,size);
302 if (l > 0)
303 return l;
305 b = udp_next(n);
306 if (!b)
307 return 0;
309 return file_readline(b,buff,size);
312 /* broadcast data to all peers of a udp host connection */
313 int udp_broadcast(net_connection_t *n, void *buff, unsigned int size)
315 int i;
316 net_connection_t **p;
317 if (!n || n->state != NETSTATE_OPEN || !n->peers || !n->peers->length)
318 return 0;
320 p = n->peers->data;
321 for (i=0; i<n->peers->length; i++) {
322 if (!p[i])
323 continue;
324 udp_write(p[i],buff,size);
327 return i;
330 /* accept new connections to a udp host */
331 int udp_accept(net_connection_t *n)
333 net_connection_t *c;
334 struct sockaddr_in *s4[2];
335 struct sockaddr_in6 *s6[2];
336 char buff[2048];
337 int l;
338 array_t *a = net_select(0,0,n);
339 if (!a)
340 return -1;
342 if (!a->length) {
343 array_free(a,1);
344 return -1;
347 c = net_connection();
348 c->type = n->type;
349 c->state = NETSTATE_OPEN;
350 c->fd = n->fd;
351 c->buff.udp.unsorted = NULL;
352 c->buff.udp.sorted = NULL;
354 l = recvfrom(n->fd, buff, 2048, 0, (struct sockaddr *)&c->remote_addr, &c->remote_addr_len);
356 if (l > -1) {
357 int i;
358 net_connection_t **p = n->peers->data;
359 file_t *f = file_create(NULL,NULL);
360 file_write(f,buff,l);
361 c->buff.udp.unsorted = list_push(&c->buff.udp.unsorted,f);
362 for (i=0; i<n->peers->length; i++) {
363 if (p[i] && c->remote_addr.ss_family == p[i]->remote_addr.ss_family) {
364 if (c->remote_addr.ss_family == AF_INET) {
365 s4[0] = (struct sockaddr_in*)&c->remote_addr;
366 s4[1] = (struct sockaddr_in*)&p[i]->remote_addr;
367 if (s4[0]->sin_addr.s_addr == s4[1]->sin_addr.s_addr) {
368 c->fd = -1;
369 net_close(c);
370 return i;
372 }else if (c->remote_addr.ss_family == AF_INET6) {
373 s6[0] = (struct sockaddr_in6*)&c->remote_addr;
374 s6[1] = (struct sockaddr_in6*)&p[i]->remote_addr;
375 if (!memcmp(s6[0]->sin6_addr.s6_addr,s6[1]->sin6_addr.s6_addr,16)) {
376 c->fd = -1;
377 net_close(c);
378 return i;
383 for (i=0; i<n->peers->length; i++) {
384 if (!p[i]) {
385 p[i] = c;
386 return i;
389 array_push_ptr(n->peers,c);
390 return n->peers->length-1;
393 c->fd = -1;
394 net_close(c);
395 return -1;