Mercurial > code
comparison nsock.c @ 168:a89ff602b30f
Initial import of nsock.c nsock.h
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 22 Aug 2013 17:40:54 +0200 |
parents | |
children | 29531c2f8213 |
comparison
equal
deleted
inserted
replaced
167:1167cd06b475 | 168:a89ff602b30f |
---|---|
1 /* | |
2 * nsock.c -- portable BSD sockets wrapper | |
3 * | |
4 * Copyright (c) 2013 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 # include <stdio.h> | |
20 # include <stdlib.h> | |
21 | |
22 #if !defined(_WIN32) | |
23 # include <errno.h> | |
24 # include <string.h> | |
25 #endif | |
26 | |
27 #include "nsock.h" | |
28 | |
29 #define TAILQ_HEAD(name, type) \ | |
30 struct name { \ | |
31 struct type *tqh_first; /* first element */ \ | |
32 struct type **tqh_last; /* addr of last next element */ \ | |
33 size_t noclients; \ | |
34 } | |
35 | |
36 #define TAILQ_ENTRY(type) \ | |
37 struct { \ | |
38 struct type *tqe_next; /* next element */ \ | |
39 struct type **tqe_prev; /* address of previous next element */ \ | |
40 } | |
41 | |
42 #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) | |
43 | |
44 #define TAILQ_FIRST(head) ((head)->tqh_first) | |
45 | |
46 #define TAILQ_FOREACH(var, head, field) \ | |
47 for ((var) = TAILQ_FIRST((head)); \ | |
48 (var); \ | |
49 (var) = TAILQ_NEXT((var), field)) | |
50 | |
51 #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ | |
52 for ((var) = TAILQ_FIRST((head)); \ | |
53 (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ | |
54 (var) = (tvar)) | |
55 | |
56 #define TAILQ_INIT(head) do { \ | |
57 TAILQ_FIRST((head)) = NULL; \ | |
58 (head)->tqh_last = &TAILQ_FIRST((head)); \ | |
59 } while (0) | |
60 | |
61 #define TAILQ_INSERT_HEAD(head, elm, field) do { \ | |
62 if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ | |
63 TAILQ_FIRST((head))->field.tqe_prev = \ | |
64 &TAILQ_NEXT((elm), field); \ | |
65 else \ | |
66 (head)->tqh_last = &TAILQ_NEXT((elm), field); \ | |
67 TAILQ_FIRST((head)) = (elm); \ | |
68 (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ | |
69 (head)->noclients ++; \ | |
70 } while (0) | |
71 | |
72 #define TAILQ_INSERT_TAIL(head, elm, field) do { \ | |
73 TAILQ_NEXT((elm), field) = NULL; \ | |
74 (elm)->field.tqe_prev = (head)->tqh_last; \ | |
75 *(head)->tqh_last = (elm); \ | |
76 (head)->tqh_last = &TAILQ_NEXT((elm), field); \ | |
77 (head)->noclients ++; \ | |
78 } while (0) | |
79 | |
80 #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) | |
81 | |
82 #define TAILQ_REMOVE(head, elm, field) do { \ | |
83 if ((TAILQ_NEXT((elm), field)) != NULL) \ | |
84 TAILQ_NEXT((elm), field)->field.tqe_prev = \ | |
85 (elm)->field.tqe_prev; \ | |
86 else { \ | |
87 (head)->tqh_last = (elm)->field.tqe_prev; \ | |
88 } \ | |
89 *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ | |
90 (head)->noclients --; \ | |
91 } while (0) | |
92 | |
93 struct nsock { | |
94 nsock_socket_t fd; | |
95 char error[128]; | |
96 }; | |
97 | |
98 struct nsock_ep { | |
99 struct sockaddr_storage addr; | |
100 socklen_t addrlen; | |
101 char error[128]; | |
102 }; | |
103 | |
104 struct nsock_listener { | |
105 const struct nsock *sock; | |
106 char error[128]; | |
107 TAILQ_HEAD(, nsock_clt) head; | |
108 }; | |
109 | |
110 struct nsock_clt { | |
111 const struct nsock *sock; | |
112 TAILQ_ENTRY(nsock_clt) link; | |
113 }; | |
114 | |
115 /* -------------------------------------------------------- | |
116 * Private helpers | |
117 * -------------------------------------------------------- */ | |
118 | |
119 static void | |
120 nsock_set_error(char *buffer, size_t bufsize) | |
121 { | |
122 memset(buffer, 0, bufsize); | |
123 | |
124 #if defined(_WIN32) | |
125 LPSTR str; | |
126 | |
127 FormatMessageA( | |
128 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | |
129 NULL, | |
130 WSAGetLastError(), | |
131 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
132 (LPSTR)&str, 0, NULL); | |
133 | |
134 if (str) { | |
135 strncpy(buffer, str, bufsize); | |
136 LocalFree(str); | |
137 } | |
138 #else | |
139 strncpy(buffer, strerror(errno), bufsize); | |
140 #endif | |
141 } | |
142 | |
143 static void | |
144 nsock_set_errno(char *buffer, size_t bufsize, int no) | |
145 { | |
146 memset(buffer, 0, bufsize); | |
147 strncpy(buffer, strerror(no), bufsize - 1); | |
148 } | |
149 | |
150 static struct nsock_clt * | |
151 nsock_clt_new(const struct nsock *ns) | |
152 { | |
153 struct nsock_clt *clt; | |
154 | |
155 if ((clt = malloc(sizeof (struct nsock_clt))) == NULL) | |
156 return NULL; | |
157 | |
158 clt->sock = ns; | |
159 | |
160 return clt; | |
161 } | |
162 | |
163 /* -------------------------------------------------------- | |
164 * Sockets functions | |
165 * -------------------------------------------------------- */ | |
166 | |
167 void | |
168 nsock_init(void) | |
169 { | |
170 #if defined(_WIN32) | |
171 WSADATA wsa; | |
172 WSAStartup(MAKEWORD(2, 2), &wsa); | |
173 #endif | |
174 } | |
175 | |
176 struct nsock * | |
177 nsock_create(int domain, int type, int protocol) | |
178 { | |
179 struct nsock *ns; | |
180 | |
181 if ((ns = malloc(sizeof (struct nsock))) == NULL) | |
182 return NULL; | |
183 | |
184 ns->fd = socket(domain, type, protocol); | |
185 | |
186 if (ns->fd == INVALID_SOCKET) | |
187 nsock_set_error(ns->error, sizeof (ns->error)); | |
188 | |
189 return ns; | |
190 } | |
191 | |
192 const char * | |
193 nsock_error(struct nsock *ns) | |
194 { | |
195 if (ns == NULL) | |
196 return strerror(ENOMEM); | |
197 | |
198 return ns->error; | |
199 } | |
200 | |
201 int | |
202 nsock_bind(struct nsock *ns, const struct nsock_ep *ep) | |
203 { | |
204 int ret; | |
205 | |
206 ret = bind(ns->fd, (const struct sockaddr *)&ep->addr, ep->addrlen); | |
207 | |
208 if (ret == SOCKET_ERROR) | |
209 nsock_set_error(ns->error, sizeof (ns->error)); | |
210 | |
211 return ret == SOCKET_ERROR ? -1 : 0; | |
212 } | |
213 | |
214 int | |
215 nsock_listen(struct nsock *ns, int max) | |
216 { | |
217 int ret; | |
218 | |
219 ret = listen(ns->fd, max); | |
220 | |
221 if (ret == SOCKET_ERROR) | |
222 nsock_set_error(ns->error, sizeof (ns->error)); | |
223 | |
224 return ret == SOCKET_ERROR ? -1 : 0; | |
225 } | |
226 | |
227 int | |
228 nsock_accept(struct nsock *ns, struct nsock **client, struct nsock_ep **clientinfo) | |
229 { | |
230 struct sockaddr_storage *st = NULL; | |
231 socklen_t *len = NULL; | |
232 int ret; | |
233 | |
234 if ((*client = malloc(sizeof (struct nsock))) == NULL) { | |
235 nsock_set_errno(ns->error, sizeof (ns->error), ENOMEM); | |
236 return -1; | |
237 } | |
238 | |
239 if (clientinfo != NULL) { | |
240 if ((*clientinfo = malloc(sizeof (struct nsock_ep))) == NULL) { | |
241 free(client); | |
242 nsock_set_errno(ns->error, sizeof (ns->error), ENOMEM); | |
243 return -1; | |
244 } | |
245 | |
246 st = &(*clientinfo)->addr; | |
247 len = &(*clientinfo)->addrlen; | |
248 | |
249 /* Set the addrlen to sockaddr_storage first */ | |
250 *len = sizeof (struct sockaddr_storage); | |
251 } | |
252 | |
253 /* Prepare client */ | |
254 memset((*client)->error, 0, sizeof ((*client)->error)); | |
255 (*client)->fd = accept(ns->fd, (struct sockaddr *)st, len); | |
256 | |
257 if ((*client)->fd == INVALID_SOCKET) { | |
258 nsock_set_error(ns->error, sizeof (ns->error)); | |
259 | |
260 /* free clients and set to NULL so user will not use it */ | |
261 free(*client); | |
262 *client = NULL; | |
263 | |
264 if (clientinfo != NULL) { | |
265 free(*clientinfo); | |
266 *clientinfo = NULL; | |
267 } | |
268 | |
269 ret = -1; | |
270 } else | |
271 ret = 0; | |
272 | |
273 return ret; | |
274 } | |
275 | |
276 int | |
277 nsock_connect(struct nsock *ns, const struct nsock_ep *ep) | |
278 { | |
279 int ret; | |
280 | |
281 ret = connect(ns->fd, (const struct sockaddr *)&ep->addr, ep->addrlen); | |
282 | |
283 if (ret == SOCKET_ERROR) | |
284 nsock_set_error(ns->error, sizeof (ns->error)); | |
285 | |
286 return ret == SOCKET_ERROR ? -1 : 0; | |
287 } | |
288 | |
289 int | |
290 nsock_set(struct nsock *ns, int level, int name, const void *arg, unsigned arglen) | |
291 { | |
292 int ret; | |
293 | |
294 ret = setsockopt(ns->fd, level, name, (nsock_carg_t)arg, arglen); | |
295 | |
296 if (ret == SOCKET_ERROR) | |
297 nsock_set_error(ns->error, sizeof (ns->error)); | |
298 | |
299 return ret == SOCKET_ERROR ? -1 : 0; | |
300 } | |
301 | |
302 long | |
303 nsock_recv(struct nsock *ns, void *data, size_t datasz, int flags) | |
304 { | |
305 long nbread; | |
306 | |
307 nbread = recv(ns->fd, data, datasz, flags); | |
308 | |
309 if (nbread == -1) | |
310 nsock_set_error(ns->error, sizeof (ns->error)); | |
311 | |
312 return nbread; | |
313 } | |
314 | |
315 long | |
316 nsock_recvfrom(struct nsock *ns, struct nsock_ep *ep, void *data, size_t datasz, int flags) | |
317 { | |
318 struct sockaddr_storage *st = NULL; | |
319 socklen_t *len = NULL; | |
320 long nbread; | |
321 | |
322 if (ep != NULL) { | |
323 st = &ep->addr; | |
324 len = &ep->addrlen; | |
325 | |
326 /* Set the addrlen to sockaddr_storage first */ | |
327 *len = sizeof (struct sockaddr_storage); | |
328 } | |
329 | |
330 nbread = recvfrom(ns->fd, data, datasz, flags, | |
331 (struct sockaddr *)st, len); | |
332 | |
333 if (nbread == SOCKET_ERROR) | |
334 nsock_set_error(ns->error, sizeof (ns->error)); | |
335 | |
336 return nbread; | |
337 } | |
338 | |
339 long | |
340 nsock_send(struct nsock *ns, const void *data, size_t datasz, int flags) | |
341 { | |
342 long nbsent; | |
343 | |
344 nbsent = send(ns->fd, data, datasz, flags); | |
345 | |
346 if (nbsent == -1) | |
347 nsock_set_error(ns->error, sizeof (ns->error)); | |
348 | |
349 return nbsent; | |
350 } | |
351 | |
352 long | |
353 nsock_sendto(struct nsock *ns, const struct nsock_ep *ep, const void *data, size_t datasz, int flags) | |
354 { | |
355 long nbsent; | |
356 | |
357 nbsent = sendto(ns->fd, data, datasz, flags, | |
358 (const struct sockaddr *)&ep->addr, ep->addrlen); | |
359 | |
360 if (nbsent == SOCKET_ERROR) | |
361 nsock_set_error(ns->error, sizeof (ns->error)); | |
362 | |
363 return nbsent; | |
364 } | |
365 | |
366 void | |
367 nsock_close(struct nsock *ns) | |
368 { | |
369 closesocket(ns->fd); | |
370 } | |
371 | |
372 void | |
373 nsock_free(struct nsock *ns) | |
374 { | |
375 free(ns); | |
376 } | |
377 | |
378 void | |
379 nsock_finish(void) | |
380 { | |
381 #if defined(_WIN32) | |
382 WSACleanup(); | |
383 #endif | |
384 } | |
385 | |
386 /* -------------------------------------------------------- | |
387 * End point functions | |
388 * -------------------------------------------------------- */ | |
389 | |
390 struct nsock_ep * | |
391 nsock_ep_create(void) | |
392 { | |
393 struct nsock_ep *ep; | |
394 | |
395 if ((ep = calloc(1, sizeof (struct nsock_ep))) == NULL) | |
396 return NULL; | |
397 | |
398 return ep; | |
399 } | |
400 | |
401 const char * | |
402 nsock_ep_error(struct nsock_ep *ep) | |
403 { | |
404 if (ep == NULL) | |
405 return strerror(ENOMEM); | |
406 | |
407 return ep->error; | |
408 } | |
409 | |
410 int | |
411 nsock_ep_bind_ip(struct nsock_ep *ep, const char *iface, unsigned port, int family) | |
412 { | |
413 if (family == AF_INET6) { | |
414 struct sockaddr_in6 *ptr = (struct sockaddr_in6 *)&ep->addr; | |
415 | |
416 memset(ptr, 0, sizeof (struct sockaddr_in6)); | |
417 ptr->sin6_family = AF_INET6; | |
418 ptr->sin6_port = htons(port); | |
419 | |
420 if (iface == NULL || strcmp(iface, "*") == 0) | |
421 ptr->sin6_addr = in6addr_any; | |
422 else if (inet_pton(AF_INET6, iface, &ptr->sin6_addr) <= 0) { | |
423 nsock_set_error(ep->error, sizeof (ep->error)); | |
424 return -1; | |
425 } | |
426 | |
427 ep->addrlen = sizeof (struct sockaddr_in6); | |
428 } else { | |
429 struct sockaddr_in *ptr = (struct sockaddr_in *)&ep->addr; | |
430 | |
431 memset(ptr, 0, sizeof (struct sockaddr_in)); | |
432 ptr->sin_family = AF_INET; | |
433 ptr->sin_port = htons(port); | |
434 | |
435 if (iface == NULL || strcmp(iface, "*") == 0) | |
436 ptr->sin_addr.s_addr = INADDR_ANY; | |
437 else if (inet_pton(AF_INET, iface, &ptr->sin_addr) <= 0) { | |
438 nsock_set_error(ep->error, sizeof (ep->error)); | |
439 return -1; | |
440 } | |
441 | |
442 ep->addrlen = sizeof (struct sockaddr_in); | |
443 } | |
444 | |
445 return 0; | |
446 } | |
447 | |
448 int | |
449 nsock_ep_connect_ip(struct nsock_ep *ep, const char *host, unsigned port, int family) | |
450 { | |
451 struct addrinfo hints, *res; | |
452 char portstr[32]; | |
453 int error; | |
454 | |
455 memset(&hints, 0, sizeof (hints)); | |
456 hints.ai_family = family; | |
457 hints.ai_socktype = SOCK_STREAM; | |
458 | |
459 memset(portstr, 0, sizeof (portstr)); | |
460 sprintf(portstr, "%u", port); | |
461 | |
462 error = getaddrinfo(host, portstr, &hints, &res); | |
463 if (error) { | |
464 memset(ep->error, 0, sizeof (ep->error)); | |
465 strncpy(ep->error, gai_strerrorA(error), sizeof (ep->error) - 1); | |
466 return -1; | |
467 } | |
468 | |
469 memcpy(&ep->addr, res->ai_addr, res->ai_addrlen); | |
470 ep->addrlen = res->ai_addrlen; | |
471 | |
472 freeaddrinfo(res); | |
473 | |
474 return 0; | |
475 } | |
476 | |
477 #if !defined(_WIN32) | |
478 | |
479 void | |
480 nsock_ep_unix(struct nsock_ep *ep, const char *path) | |
481 { | |
482 struct sockaddr_un *ptr= (struct sockaddr_un *)&ep->addr; | |
483 | |
484 /* Path */ | |
485 memset(ptr, 0, sizeof (struct sockaddr_un)); | |
486 strncpy(ptr->sun_path, path, sizeof (ptr->sun_path) - 1); | |
487 ptr->sun_family = AF_UNIX; | |
488 | |
489 /* Len is computed with SUN_LEN */ | |
490 ep->addrlen = SUN_LEN(ptr); | |
491 } | |
492 | |
493 #endif | |
494 | |
495 struct sockaddr * | |
496 nsock_ep_getaddr(struct nsock_ep *ep) | |
497 { | |
498 return (struct sockaddr *)&ep->addr; | |
499 } | |
500 | |
501 socklen_t | |
502 nsock_ep_getaddrlen(const struct nsock_ep *ep) | |
503 { | |
504 return ep->addrlen; | |
505 } | |
506 | |
507 void | |
508 nsock_ep_free(struct nsock_ep *ep) | |
509 { | |
510 free(ep); | |
511 } | |
512 | |
513 /* -------------------------------------------------------- | |
514 * listener functions | |
515 * -------------------------------------------------------- */ | |
516 | |
517 struct nsock_listener * | |
518 nsock_lst_create(const struct nsock *ns) | |
519 { | |
520 struct nsock_listener *ls; | |
521 | |
522 if ((ls = malloc(sizeof (struct nsock_listener))) == NULL) | |
523 return NULL; | |
524 | |
525 ls->sock = ns; | |
526 TAILQ_INIT(&ls->head); | |
527 | |
528 return ls; | |
529 } | |
530 | |
531 const char * | |
532 nsock_lst_error(struct nsock_listener *ls) | |
533 { | |
534 if (ls == NULL) | |
535 return strerror(ENOMEM); | |
536 | |
537 return ls->error; | |
538 } | |
539 | |
540 int | |
541 nsock_lst_push(struct nsock_listener *ls, struct nsock *ns) | |
542 { | |
543 struct nsock_clt *clt; | |
544 | |
545 if ((clt = nsock_clt_new(ns)) == NULL) | |
546 return -1; | |
547 | |
548 TAILQ_INSERT_HEAD(&ls->head, clt, link); | |
549 | |
550 return 0; | |
551 } | |
552 | |
553 int | |
554 nsock_lst_append(struct nsock_listener *ls, struct nsock *ns) | |
555 { | |
556 struct nsock_clt *clt; | |
557 | |
558 if ((clt = nsock_clt_new(ns)) == NULL) | |
559 return -1; | |
560 | |
561 TAILQ_INSERT_TAIL(&ls->head, clt, link); | |
562 | |
563 return 0; | |
564 } | |
565 | |
566 size_t | |
567 nsock_lst_count(const struct nsock_listener *ls) | |
568 { | |
569 return ls->head.noclients; | |
570 } | |
571 | |
572 void | |
573 nsock_lst_remove(struct nsock_listener *ls, const struct nsock *ns) | |
574 { | |
575 struct nsock_clt *clt, *tmp; | |
576 | |
577 TAILQ_FOREACH_SAFE(clt, &ls->head, link, tmp) { | |
578 if (clt->sock == ns) { | |
579 TAILQ_REMOVE(&ls->head, clt, link); | |
580 free(clt); | |
581 break; | |
582 } | |
583 } | |
584 } | |
585 | |
586 struct nsock * | |
587 nsock_lst_select(struct nsock_listener *ls, long sec, long usec) | |
588 { | |
589 fd_set fds; | |
590 struct timeval maxwait, *towait; | |
591 int error; | |
592 int fdmax; | |
593 struct nsock_clt *clt; | |
594 | |
595 fdmax = TAILQ_FIRST(&ls->head)->sock->fd; | |
596 | |
597 FD_ZERO(&fds); | |
598 TAILQ_FOREACH(clt, &ls->head, link) { | |
599 FD_SET(clt->sock->fd, &fds); | |
600 if ((int)clt->sock->fd > fdmax) | |
601 fdmax = clt->sock->fd; | |
602 } | |
603 | |
604 maxwait.tv_sec = sec; | |
605 maxwait.tv_usec = usec; | |
606 | |
607 // Set to NULL for infinite timeout. | |
608 towait = (sec == 0 && usec == 0) ? NULL : &maxwait; | |
609 error = select(fdmax + 1, &fds, NULL, NULL, towait); | |
610 | |
611 TAILQ_FOREACH(clt, &ls->head, link) | |
612 if (FD_ISSET(clt->sock->fd, &fds)) | |
613 return (struct nsock *)clt->sock; | |
614 | |
615 return NULL; | |
616 } | |
617 | |
618 void | |
619 nsock_lst_free(struct nsock_listener *ls) | |
620 { | |
621 struct nsock_clt *clt, *tmp; | |
622 | |
623 TAILQ_FOREACH_SAFE(clt, &ls->head, link, tmp) | |
624 free(clt); | |
625 | |
626 free(ls); | |
627 } |