pass socket_t to register function
[cnet.git] / cnet.c
blob2eb708bff83a928466ed557348dcd7ed9832fda3
1 /* Copyright (c) 2007 Zachery Hostens <zacheryph@gmail.com>
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
21 /* this define is for gnu/linux retardation! */
22 #define _GNU_SOURCE
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <netdb.h>
26 #include <poll.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33 #include <arpa/inet.h>
34 #include <netinet/in.h>
35 #include <sys/socket.h>
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include "cnet.h"
40 #define CNET_CLIENT 0x01
41 #define CNET_SERVER 0x02
42 #define CNET_AVAIL 0x04
43 #define CNET_DELETED 0x08
44 #define CNET_CONNECT 0x10 /* socket is connecting */
45 #define CNET_BLOCKED 0x20 /* write's will block */
47 typedef struct {
48 int fd;
49 int flags;
51 /* connection information */
52 char *lhost;
53 int lport;
54 char *rhost;
55 int rport;
57 /* buffer data */
58 char *buf;
59 int len;
61 /* client data */
62 cnet_handler_t *handler;
63 void *data;
64 } cnet_socket_t;
66 static cnet_socket_t *socks = NULL;
67 static int nsocks = 0;
68 static int navailsocks = 0;
69 static struct pollfd *pollfds = NULL;
70 static int *pollsids = NULL;
71 static int npollfds;
74 /*** private helper methods ***/
75 static int cnet_grow_sockets (void)
77 int i, newsocks;
78 newsocks = (nsocks / 3) + 16;
79 printf ("growing socks: %d total: %d\n", newsocks, nsocks+newsocks);
80 socks = realloc (socks, (nsocks+newsocks) * sizeof(*socks));
81 memset(socks+nsocks, '\0', newsocks*sizeof(*socks));
82 for (i = 0; i < newsocks; i++) {
83 socks[nsocks+i].fd = -1;
84 socks[nsocks+i].flags = CNET_AVAIL;
86 nsocks += newsocks;
87 navailsocks += newsocks;
88 return newsocks;
91 static int cnet_new (void)
93 int sid;
95 if (0 == nsocks) cnet_grow_sockets();
96 for (sid = 0; sid < nsocks; sid++)
97 if (socks[sid].flags & CNET_AVAIL) break;
99 if (sid == nsocks) return -1;
101 navailsocks--;
102 return sid;
105 static int cnet_set_nonblock (int sid)
107 int flags;
108 if (-1 == (flags = fcntl (socks[sid].fd, F_GETFL, 0))) return -1;
109 flags |= O_NONBLOCK;
110 fcntl (socks[sid].fd, F_SETFL, flags);
111 return 0;
114 /* returns fd. NOT sid */
115 static int cnet_bind (const char *host, int port)
117 int salen, fd;
118 char strport[6];
119 struct sockaddr *sa;
120 struct addrinfo hints, *res = NULL;
122 memset (&hints, '\0', sizeof(hints));
123 hints.ai_family = PF_UNSPEC;
124 if (cnet_ip_type(host)) hints.ai_flags = AI_NUMERICHOST;
125 if (port) snprintf (strport, 6, "%d", port);
126 if (getaddrinfo (host, (port ? strport : NULL), &hints, &res)) return -1;
127 sa = res->ai_addr;
128 salen = res->ai_addrlen;
130 if (-1 == (fd = socket (AF_INET, SOCK_STREAM, 0))) goto cleanup;
131 if (-1 == bind (fd, sa, sizeof(*sa))) goto cleanup;
132 freeaddrinfo (res);
133 return fd;
135 cleanup:
136 freeaddrinfo (res);
137 return -1;
140 static int cnet_register (int sid, cnet_socket_t *sock, int flags)
142 pollfds = realloc (pollfds, (npollfds+1) * sizeof(*pollfds));
143 pollsids = realloc (pollsids, (npollfds+1) * sizeof(*pollsids));
144 memset (pollfds+npollfds, '\0', sizeof(*pollfds));
145 pollfds[npollfds].fd = sock->fd;
146 pollfds[npollfds].events = flags;
147 pollsids[npollfds] = sid;
148 npollfds++;
150 return 0;
153 /* fetch the cnet_socket_t related to a sid if avail and not deleted */
154 static cnet_socket_t *cnet_fetch (int sid)
156 if (sid >= nsocks) return NULL;
157 if (socks[sid].flags & (CNET_AVAIL | CNET_DELETED)) return NULL;
158 return &socks[sid];
162 /*** connection handlers ***/
163 static int cnet_on_connect (int sid, cnet_socket_t *sock)
165 sock->flags &= ~(CNET_CONNECT);
166 if (sock->handler->on_connect) sock->handler->on_connect (sid, sock->data);
167 return 0;
170 static int cnet_on_newclient (int sid, cnet_socket_t *sock)
172 int fd, newsid;
173 char host[40], serv[6];
174 socklen_t salen;
175 cnet_socket_t *newsock;
176 struct sockaddr sa;
177 salen = sizeof(sa);
178 memset (&sa, '\0', salen);
180 fd = accept (sock->fd, &sa, &salen);
181 if (0 > fd) return -1;
182 newsid = cnet_new ();
183 newsock = &socks[newsid];
184 newsock->fd = fd;
185 newsock->flags = CNET_CLIENT;
186 cnet_register (newsid, newsock, POLLIN|POLLERR|POLLHUP|POLLNVAL);
188 getnameinfo (&sa, salen, host, 40, serv, 6, NI_NUMERICHOST|NI_NUMERICSERV);
189 newsock->rhost = strdup (host);
190 newsock->rport = atoi (serv);
192 return sock->handler->on_newclient (sid, sock->data, newsid, newsock->rhost, newsock->rport);
195 static int cnet_on_readable (int sid, cnet_socket_t *sock)
197 char *buf;
198 int len, ret;
199 buf = calloc (1, 1024);
200 if (-1 == (len = read(sock->fd, buf, 1023))) return cnet_close(sid);
201 ret = sock->handler->on_read (sid, sock->data, buf, len);
202 free (buf);
203 return ret;
206 static int cnet_on_eof (int sid, cnet_socket_t *sock, int err)
208 if (sock->handler->on_eof) sock->handler->on_eof (sid, sock->data, err);
209 return cnet_close(sid);
213 /*** public functions ***/
214 int cnet_listen (const char *host, int port)
216 int sid;
217 cnet_socket_t *sock;
219 if (-1 == (sid = cnet_new ())) return -1;
220 sock = &socks[sid];
221 if (-1 == (sock->fd = cnet_bind (host, port))) return -1;
222 cnet_set_nonblock (sid);
223 if (-1 == listen (sock->fd, 2)) goto cleanup;
224 sock->flags = CNET_SERVER;
225 cnet_register (sid, sock, POLLIN|POLLERR|POLLHUP|POLLNVAL);
226 return sid;
228 cleanup:
229 cnet_close (sid);
230 return -1;
233 int cnet_connect (const char *rhost, int rport, const char *lhost, int lport)
235 int salen, sid, ret;
236 char port[6];
237 cnet_socket_t *sock;
238 struct sockaddr *sa;
239 struct addrinfo hints, *res = NULL;
241 if (-1 == (sid = cnet_new ())) return -1;
242 sock = &socks[sid];
243 memset (&hints, '\0', sizeof(hints));
244 hints.ai_family = PF_UNSPEC;
246 if (!lhost) {
247 if (-1 == (sock->fd = socket (AF_INET, SOCK_STREAM, 0))) return -1;
249 else {
250 if (-1 == (sock->fd = cnet_bind (lhost, lport))) return -1;
251 /* now we need to get the socket family to hint the connect() */
252 getsockname (sock->fd, sa, NULL);
253 hints.ai_family = sa->sa_family;
255 cnet_set_nonblock (sid);
257 snprintf (port, 6, "%d", rport);
258 if (getaddrinfo (rhost, port, &hints, &res)) goto cleanup;
259 sa = res->ai_addr;
260 salen = res->ai_addrlen;
262 ret = connect (sock->fd, sa, sizeof(*sa));
263 if (-1 == ret && EINPROGRESS != errno) goto cleanup;
264 cnet_register (sid, sock, POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL);
265 sock->flags = CNET_CLIENT|CNET_CONNECT;
266 return sid;
268 cleanup:
269 cnet_close (sid);
270 if (res) freeaddrinfo (res);
271 return -1;
274 int cnet_close (int sid)
276 int i;
277 cnet_socket_t *sock;
278 if (NULL == (sock = cnet_fetch(sid))) return -1;
279 if (0 > sock->fd) return -1;
281 /* remove socket from pollfds is wise.... */
282 for (i = 0; i < npollfds; i++)
283 if (sock->fd == pollfds[i].fd) break;
284 npollfds--;
285 if (i < npollfds) {
286 memcpy (&pollfds[i], &pollfds[npollfds], sizeof(*pollfds));
287 pollsids[i] = pollsids[npollfds];
289 pollfds = realloc (pollfds, npollfds * sizeof(*pollfds));
290 pollsids = realloc (pollsids, npollfds * sizeof(*pollsids));
293 close (sock->fd);
294 if (sock->handler->on_close) sock->handler->on_close (sid, sock->data);
295 free (sock->lhost);
296 free (sock->rhost);
297 if (sock->len) free (sock->buf);
298 memset (sock, '\0', sizeof(*sock));
299 sock->fd = -1;
300 sock->flags = CNET_AVAIL;
301 navailsocks++;
302 return 0;
305 int cnet_select (int timeout)
307 static int active = 0;
308 int i, n, ret, sid;
309 struct pollfd *p;
310 cnet_socket_t *sock;
311 if (active) return 0;
312 active++;
314 ret = n = poll (pollfds, npollfds, timeout);
315 if (-1 == ret) return -1;
316 if (0 == ret) n = npollfds;
318 for (i = 0; n && i < npollfds; i++) {
319 p = &pollfds[i];
320 sid = pollsids[i];
321 sock = &socks[sid];
322 if (!sock->handler || !p->revents) continue;
324 if (p->revents & (POLLERR|POLLHUP|POLLNVAL)) {
325 cnet_on_eof (sid, sock, 0);
326 i--;
327 n--;
328 continue;
331 if (p->revents & POLLIN) {
332 if (sock->flags & CNET_SERVER) cnet_on_newclient (sid, sock);
333 else cnet_on_readable (sid, sock);
335 if (p->revents & POLLOUT) {
336 p->events &= ~(POLLOUT);
337 if (sock->flags & CNET_CONNECT) {
338 cnet_on_connect (sid, sock);
339 socks->flags &= ~CNET_BLOCKED;
341 if (sock->flags & CNET_BLOCKED) cnet_write (sid, NULL, 0);
344 n--;
345 if (!n) break;
347 active--;
349 /* grow sockets if we must */
350 if (navailsocks < (nsocks / 3)) cnet_grow_sockets();
352 return ret;
355 /* pass NULL to get the current handler returned */
356 /* you can't 'unset' a handler, defeats the purpose of an open socket */
357 cnet_handler_t *cnet_handler (int sid, cnet_handler_t *handler)
359 cnet_socket_t *sock;
360 if (NULL == (sock = cnet_fetch(sid))) return NULL;
361 if (handler) sock->handler = handler;
362 return sock->handler;
365 void *cnet_conndata (int sid, void *conn_data)
367 cnet_socket_t *sock;
368 if (NULL == (sock = cnet_fetch(sid))) return NULL;
369 if (conn_data) sock->data = conn_data;
370 return sock->data;
373 int cnet_ip_type (const char *ip)
375 struct in_addr in_buf;
376 struct in6_addr in6_buf;
377 if (0 < inet_pton(AF_INET, ip, &in_buf)) return 4;
378 if (0 < inet_pton(AF_INET6, ip, &in6_buf)) return 6;
379 return 0;
382 /* is this a valid & connected sid ? */
383 int cnet_valid (int sid)
385 return cnet_fetch(sid) ? 1 : 0;
388 int cnet_write (int sid, const char *data, int len)
390 int i;
391 cnet_socket_t *sock;
392 if (NULL == (sock = cnet_fetch(sid))) return -1;
393 if (0 >= len && !sock->len) return 0;
395 /* check if there is data that still needs to be written */
396 if (len > 0) {
397 sock->buf = sock->len ? realloc(sock->buf, sock->len + len) : calloc(1, len);
398 memcpy (sock->buf + sock->len, data, len);
399 sock->len += len;
402 if (sock->flags & (CNET_BLOCKED|CNET_CONNECT)) return 0;
403 len = write (sock->fd, sock->buf, sock->len);
404 if (0 > len && errno != EAGAIN) return cnet_on_eof (sid, sock, errno);
406 for (i = 0; i < npollfds; i++)
407 if (sock->fd == pollfds[i].fd) break;
409 if (len == sock->len) {
410 free (sock->buf);
411 sock->len = 0;
412 pollfds[i].events &= ~POLLOUT;
414 else {
415 memmove (sock->buf, sock->buf+len, sock->len-len);
416 sock->len -= len;
417 sock->buf = realloc (sock->buf, sock->len);
419 /* we need to set block since we still have data */
420 sock->flags |= CNET_BLOCKED;
421 pollfds[i].events |= POLLOUT;
423 return len;
426 int cnprintf (int sid, const char *format, ...)
428 va_list args;
429 char *data;
430 int len, ret;
432 va_start (args, format);
433 if (-1 == (len = vasprintf (&data, format, args))) return -1;
434 va_end (args);
435 ret = cnet_write (sid, data, len);
436 free (data);
437 return ret;