1 /*
2 * Copyright (c) 2012 Hypertriton, Inc. <http://hypertriton.com/>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 /*
27 * Network access interface.
28 */
29
30 #include <agar/core/core.h>
31
32 const char *agNetAddrFamilyNames[] = {
33 "none",
34 "local",
35 "inet4",
36 "inet6"
37 };
38 const char *agNetSocketTypeNames[] = {
39 "none",
40 "stream",
41 "dgram",
42 "raw",
43 "rdm",
44 "seqpacket"
45 };
46
47 const AG_NetOps *agNetOps = NULL;
48
49 /* Create a new, unconnected socket. */
50 AG_NetSocket *
AG_NetSocketNew(enum ag_net_addr_family af,enum ag_net_socket_type type,int proto)51 AG_NetSocketNew(enum ag_net_addr_family af, enum ag_net_socket_type type,
52 int proto)
53 {
54 AG_NetSocket *ns;
55
56 if ((ns = TryMalloc(sizeof(AG_NetSocket))) == NULL) {
57 return (NULL);
58 }
59 ns->family = af;
60 ns->type = type;
61 ns->proto = proto;
62 ns->flags = 0;
63 ns->poll = 0;
64 ns->addrLocal = NULL;
65 ns->addrRemote = NULL;
66 ns->fd = -1;
67 ns->listenBacklog = 10;
68 ns->p = NULL;
69
70 if (agNetOps->initSocket != NULL &&
71 agNetOps->initSocket(ns) == -1) {
72 free(ns);
73 return (NULL);
74 }
75 AG_MutexInitRecursive(&ns->lock);
76 return (ns);
77 }
78
79 void
AG_NetSocketFree(AG_NetSocket * ns)80 AG_NetSocketFree(AG_NetSocket *ns)
81 {
82 if (agNetOps->destroySocket != NULL) {
83 agNetOps->destroySocket(ns);
84 }
85 if (ns->addrLocal != NULL) { AG_NetAddrFree(ns->addrLocal); }
86 if (ns->addrRemote != NULL) { AG_NetAddrFree(ns->addrRemote); }
87 AG_MutexDestroy(&ns->lock);
88 free(ns);
89 }
90
91 /* Allocate a new socket set. */
92 void
AG_NetSocketSetInit(AG_NetSocketSet * nss)93 AG_NetSocketSetInit(AG_NetSocketSet *nss)
94 {
95 TAILQ_INIT(nss);
96 }
97
98 /* Clear a network socket set. */
99 void
AG_NetSocketSetClear(AG_NetSocketSet * nss)100 AG_NetSocketSetClear(AG_NetSocketSet *nss)
101 {
102 AG_NetSocket *ns, *nsNext;
103
104 for (ns = TAILQ_FIRST(nss);
105 ns != TAILQ_END(nss);
106 ns = nsNext) {
107 nsNext = TAILQ_NEXT(ns, sockets);
108 AG_NetSocketFree(ns);
109 }
110 TAILQ_INIT(nss);
111 }
112
113 /* Allocate a new network address list. */
114 AG_NetAddrList *
AG_NetAddrListNew(void)115 AG_NetAddrListNew(void)
116 {
117 AG_NetAddrList *nal;
118
119 if ((nal = TryMalloc(sizeof(AG_NetAddrList))) == NULL) {
120 return (NULL);
121 }
122 TAILQ_INIT(nal);
123 return (nal);
124 }
125
126 /* Clear a network address list. */
127 void
AG_NetAddrListClear(AG_NetAddrList * nal)128 AG_NetAddrListClear(AG_NetAddrList *nal)
129 {
130 AG_NetAddr *addr, *addrNext;
131
132 for (addr = TAILQ_FIRST(nal);
133 addr != TAILQ_END(nal);
134 addr = addrNext) {
135 addrNext = TAILQ_NEXT(addr, addrs);
136 AG_NetAddrFree(addr);
137 }
138 TAILQ_INIT(nal);
139 }
140
141 /* Free a network address list. */
142 void
AG_NetAddrListFree(AG_NetAddrList * nal)143 AG_NetAddrListFree(AG_NetAddrList *nal)
144 {
145 AG_NetAddrListClear(nal);
146 free(nal);
147 }
148
149 /* Allocate a new network address. */
150 AG_NetAddr *
AG_NetAddrNew(void)151 AG_NetAddrNew(void)
152 {
153 AG_NetAddr *addr;
154
155 if ((addr = TryMalloc(sizeof(AG_NetAddr))) == NULL) {
156 return (NULL);
157 }
158 addr->family = AG_NET_AF_NONE;
159 addr->port = 0;
160 addr->sNum = NULL;
161 addr->sName = NULL;
162 return (addr);
163 }
164
165 void
AG_NetAddrFree(AG_NetAddr * na)166 AG_NetAddrFree(AG_NetAddr *na)
167 {
168 if (na->family == AG_NET_LOCAL) {
169 Free(na->na_local.path);
170 }
171 Free(na->sNum);
172 Free(na->sName);
173 free(na);
174 }
175
176 /* Duplicate a network address. */
177 AG_NetAddr *
AG_NetAddrDup(const AG_NetAddr * na)178 AG_NetAddrDup(const AG_NetAddr *na)
179 {
180 AG_NetAddr *naDup;
181
182 if ((naDup = AG_NetAddrNew()) == NULL) {
183 return (NULL);
184 }
185 naDup->family = na->family;
186 naDup->port = na->port;
187 switch (na->family) {
188 case AG_NET_LOCAL:
189 if (na->na_local.path != NULL) {
190 naDup->na_local.path = TryStrdup(na->na_local.path);
191 if (naDup->na_local.path == NULL)
192 goto fail;
193 } else {
194 naDup->na_local.path = NULL;
195 }
196 break;
197 case AG_NET_INET4:
198 naDup->na_inet4.addr = na->na_inet4.addr;
199 break;
200 case AG_NET_INET6:
201 memcpy(naDup->na_inet6.addr, na->na_inet6.addr, sizeof(naDup->na_inet6.addr));
202 break;
203 }
204 if (na->sNum != NULL) { naDup->sNum = TryStrdup(na->sNum); }
205 if (na->sName != NULL) { naDup->sName = TryStrdup(na->sName); }
206 return (naDup);
207 fail:
208 free(naDup);
209 return (NULL);
210 }
211
212 /* Compare two network addresses. */
213 int
AG_NetAddrCompare(const AG_NetAddr * a,const AG_NetAddr * b)214 AG_NetAddrCompare(const AG_NetAddr *a, const AG_NetAddr *b)
215 {
216 int diff;
217
218 if (a->family != b->family) {
219 return (b->family - a->family);
220 }
221 if (a->port != b->port) {
222 return (b->port - a->port);
223 }
224 switch (a->family) {
225 case AG_NET_LOCAL:
226 return strcmp(b->na_local.path, a->na_local.path);
227 case AG_NET_INET4:
228 diff = (b->na_inet4.addr - a->na_inet4.addr);
229 if (diff != 0) { return (diff); }
230 case AG_NET_INET6:
231 diff = (b->na_inet6.addr - a->na_inet6.addr);
232 if (diff != 0) { return (diff); }
233 }
234 return (0);
235 }
236
237 /* Test if the given address represents "any" address. */
238 int
AG_NetAddrIsAny(const AG_NetAddr * na)239 AG_NetAddrIsAny(const AG_NetAddr *na)
240 {
241 int i;
242
243 switch (na->family) {
244 case AG_NET_INET4:
245 return (na->na_inet4.addr == 0x00000000);
246 case AG_NET_INET6:
247 for (i = 0; i < sizeof(na->na_inet6.addr); i++) {
248 if (na->na_inet6.addr[i] != 0)
249 return (0);
250 }
251 return (1);
252 default:
253 return (0);
254 }
255 }
256
257 /*
258 * Return a numerical representation of the given network address.
259 * The string is allocated and freed internally with the AG_NetAddr.
260 */
261 const char *
AG_NetAddrNumerical(AG_NetAddr * na)262 AG_NetAddrNumerical(AG_NetAddr *na)
263 {
264 return agNetOps->getAddrNumerical(na);
265 }
266
267 /* Resolve the specified hostname and port name/number. */
268 AG_NetAddrList *
AG_NetResolve(const char * host,const char * port,Uint flags)269 AG_NetResolve(const char *host, const char *port, Uint flags)
270 {
271 AG_NetAddrList *nal;
272
273 if ((nal = AG_NetAddrListNew()) == NULL) {
274 return (NULL);
275 }
276 if (agNetOps->resolve(nal, host, port, flags) == -1) {
277 goto fail;
278 }
279 return (nal);
280 fail:
281 AG_NetAddrListFree(nal);
282 return (NULL);
283 }
284
285 /* Return the list of addresses associated with local network interfaces. */
286 AG_NetAddrList *
AG_NetGetIfConfig(void)287 AG_NetGetIfConfig(void)
288 {
289 AG_NetAddrList *nal;
290
291 if ((nal = AG_NetAddrListNew()) == NULL) {
292 return (NULL);
293 }
294 if (agNetOps->getIfConfig(nal) == -1) {
295 goto fail;
296 }
297 return (nal);
298 fail:
299 AG_NetAddrListFree(nal);
300 return (NULL);
301 }
302
303 /* Establish a connection to one of the network addresses specified. */
304 int
AG_NetConnect(AG_NetSocket * ns,const AG_NetAddrList * nal)305 AG_NetConnect(AG_NetSocket *ns, const AG_NetAddrList *nal)
306 {
307 AG_NetAddr *na;
308
309 AG_MutexLock(&ns->lock);
310
311 if (ns->flags & AG_NET_SOCKET_BOUND) {
312 AG_SetError(_("Socket is already bound"));
313 goto fail;
314 }
315 if (ns->flags & AG_NET_SOCKET_CONNECTED) {
316 AG_SetError(_("Socket is already connected"));
317 goto fail;
318 }
319 TAILQ_FOREACH(na, nal, addrs) {
320 if (agNetOps->connect(ns, na) == 0)
321 break;
322 }
323 if (na == NULL) {
324 AG_SetError(_("Connection failed"));
325 goto fail;
326 }
327 ns->flags |= AG_NET_SOCKET_CONNECTED;
328 ns->addrRemote = AG_NetAddrDup(na);
329
330 AG_MutexUnlock(&ns->lock);
331 return (0);
332 fail:
333 AG_MutexUnlock(&ns->lock);
334 return (-1);
335 }
336
337 /*
338 * Assign a local address to a socket. If the address happens to be
339 * of AG_NET_LOCAL family, a Unix socket is created in the filesystem.
340 */
341 int
AG_NetBind(AG_NetSocket * ns,const AG_NetAddr * na)342 AG_NetBind(AG_NetSocket *ns, const AG_NetAddr *na)
343 {
344 AG_MutexLock(&ns->lock);
345
346 if (ns->flags & AG_NET_SOCKET_CONNECTED) {
347 AG_SetError(_("Socket is already connected"));
348 goto fail;
349 }
350 if (ns->flags & AG_NET_SOCKET_BOUND) {
351 AG_SetError(_("Socket is already bound"));
352 goto fail;
353 }
354 if (agNetOps->bind(ns, na) != 0) {
355 goto fail;
356 }
357 ns->flags |= AG_NET_SOCKET_BOUND;
358 ns->addrLocal = AG_NetAddrDup(na);
359
360 AG_MutexUnlock(&ns->lock);
361 return (0);
362 fail:
363 AG_MutexUnlock(&ns->lock);
364 return (-1);
365 }
366
367 /* Retrieve a socket option. */
368 int
AG_NetGetOption(AG_NetSocket * ns,enum ag_net_socket_option opt,void * arg)369 AG_NetGetOption(AG_NetSocket *ns, enum ag_net_socket_option opt, void *arg)
370 {
371 int rv;
372
373 AG_MutexLock(&ns->lock);
374 rv = agNetOps->getOption(ns, opt, arg);
375 AG_MutexUnlock(&ns->lock);
376 return (rv);
377 }
378
379 /* Retrieve a socket option (integer). */
380 int
AG_NetGetOptionInt(AG_NetSocket * ns,enum ag_net_socket_option opt,int * arg)381 AG_NetGetOptionInt(AG_NetSocket *ns, enum ag_net_socket_option opt, int *arg)
382 {
383 int rv;
384
385 AG_MutexLock(&ns->lock);
386 rv = agNetOps->getOption(ns, opt, arg);
387 AG_MutexUnlock(&ns->lock);
388 return (rv);
389 }
390
391 /* Set a socket option. */
392 int
AG_NetSetOption(AG_NetSocket * ns,enum ag_net_socket_option opt,const void * arg)393 AG_NetSetOption(AG_NetSocket *ns, enum ag_net_socket_option opt, const void *arg)
394 {
395 int rv;
396
397 AG_MutexLock(&ns->lock);
398 rv = agNetOps->setOption(ns, opt, arg);
399 AG_MutexUnlock(&ns->lock);
400 return (rv);
401 }
402
403 /* Set a socket option (integer). */
404 int
AG_NetSetOptionInt(AG_NetSocket * ns,enum ag_net_socket_option opt,int val)405 AG_NetSetOptionInt(AG_NetSocket *ns, enum ag_net_socket_option opt, int val)
406 {
407 int rv;
408
409 AG_MutexLock(&ns->lock);
410 rv = agNetOps->setOption(ns, opt, &val);
411 AG_MutexUnlock(&ns->lock);
412 return (rv);
413 }
414
415 /* Poll a set of sockets for read/write/exception events. */
416 int
AG_NetPoll(AG_NetSocketSet * nsInput,AG_NetSocketSet * nsRead,AG_NetSocketSet * nsWrite,AG_NetSocketSet * nsExcept,Uint32 timeout)417 AG_NetPoll(AG_NetSocketSet *nsInput, AG_NetSocketSet *nsRead,
418 AG_NetSocketSet *nsWrite, AG_NetSocketSet *nsExcept, Uint32 timeout)
419 {
420 return agNetOps->poll(nsInput, nsRead, nsWrite, nsExcept, timeout);
421 }
422
423 /* Accept a connection on a bound socket. */
424 AG_NetSocket *
AG_NetAccept(AG_NetSocket * ns)425 AG_NetAccept(AG_NetSocket *ns)
426 {
427 AG_NetSocket *nsNew;
428
429 AG_MutexLock(&ns->lock);
430 nsNew = agNetOps->accept(ns);
431 AG_MutexUnlock(&ns->lock);
432 return (nsNew);
433 }
434
435 /* Read data from a socket. */
436 int
AG_NetRead(AG_NetSocket * ns,void * p,size_t size,size_t * nRead)437 AG_NetRead(AG_NetSocket *ns, void *p, size_t size, size_t *nRead)
438 {
439 int rv;
440
441 AG_MutexLock(&ns->lock);
442 rv = agNetOps->read(ns, p, size, nRead);
443 AG_MutexUnlock(&ns->lock);
444 return (rv);
445 }
446
447 /* Write data to a socket. */
448 int
AG_NetWrite(AG_NetSocket * ns,const void * p,size_t size,size_t * nWrote)449 AG_NetWrite(AG_NetSocket *ns, const void *p, size_t size, size_t *nWrote)
450 {
451 int rv;
452
453 AG_MutexLock(&ns->lock);
454 rv = agNetOps->write(ns, p, size, nWrote);
455 AG_MutexUnlock(&ns->lock);
456 return (rv);
457 }
458
459 /* Close a connection on a socket. */
460 void
AG_NetClose(AG_NetSocket * ns)461 AG_NetClose(AG_NetSocket *ns)
462 {
463 AG_MutexLock(&ns->lock);
464
465 if ((ns->flags & AG_NET_SOCKET_CONNECTED) == 0) {
466 goto out;
467 }
468 agNetOps->close(ns);
469
470 if (ns->addrLocal != NULL) {
471 AG_NetAddrFree(ns->addrLocal);
472 ns->addrLocal = NULL;
473 }
474 if (ns->addrRemote != NULL) {
475 AG_NetAddrFree(ns->addrRemote);
476 ns->addrRemote = NULL;
477 }
478 ns->flags &= ~(AG_NET_SOCKET_CONNECTED);
479 out:
480 AG_MutexUnlock(&ns->lock);
481 }
482
483 int
AG_InitNetworkSubsystem(const AG_NetOps * ops)484 AG_InitNetworkSubsystem(const AG_NetOps *ops)
485 {
486 if (agNetOps == ops) {
487 return (0);
488 }
489 if (agNetOps != NULL && agNetOps->destroy != NULL) {
490 AG_DestroyNetworkSubsystem();
491 }
492 agNetOps = ops;
493 return (ops->init != NULL) ? ops->init() : 0;
494 }
495
496 void
AG_DestroyNetworkSubsystem(void)497 AG_DestroyNetworkSubsystem(void)
498 {
499 if (agNetOps != NULL && agNetOps->destroy != NULL) {
500 agNetOps->destroy();
501 }
502 agNetOps = NULL;
503 }
504