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
21 /* this define is for gnu/linux retardation! */
33 #include <arpa/inet.h>
34 #include <netinet/in.h>
35 #include <sys/socket.h>
36 #include <sys/types.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 */
51 /* connection information */
62 cnet_handler_t
*handler
;
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
;
74 /*** private helper methods ***/
75 static int cnet_grow_sockets (void)
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
;
87 navailsocks
+= newsocks
;
91 static int cnet_new (void)
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;
105 static int cnet_set_nonblock (int sid
)
108 if (-1 == (flags
= fcntl (socks
[sid
].fd
, F_GETFL
, 0))) return -1;
110 fcntl (socks
[sid
].fd
, F_SETFL
, flags
);
114 /* returns fd. NOT sid */
115 static int cnet_bind (const char *host
, int port
)
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;
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
;
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
;
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
;
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
);
170 static int cnet_on_newclient (int sid
, cnet_socket_t
*sock
)
173 char host
[40], serv
[6];
175 cnet_socket_t
*newsock
;
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
];
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
)
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
);
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
)
219 if (-1 == (sid
= cnet_new ())) return -1;
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
);
233 int cnet_connect (const char *rhost
, int rport
, const char *lhost
, int lport
)
239 struct addrinfo hints
, *res
= NULL
;
241 if (-1 == (sid
= cnet_new ())) return -1;
243 memset (&hints
, '\0', sizeof(hints
));
244 hints
.ai_family
= PF_UNSPEC
;
247 if (-1 == (sock
->fd
= socket (AF_INET
, SOCK_STREAM
, 0))) return -1;
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
;
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
;
270 if (res
) freeaddrinfo (res
);
274 int cnet_close (int sid
)
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;
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
));
294 if (sock
->handler
->on_close
) sock
->handler
->on_close (sid
, sock
->data
);
297 if (sock
->len
) free (sock
->buf
);
298 memset (sock
, '\0', sizeof(*sock
));
300 sock
->flags
= CNET_AVAIL
;
305 int cnet_select (int timeout
)
307 static int active
= 0;
311 if (active
) return 0;
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
++) {
322 if (!sock
->handler
|| !p
->revents
) continue;
324 if (p
->revents
& (POLLERR
|POLLHUP
|POLLNVAL
)) {
325 cnet_on_eof (sid
, sock
, 0);
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);
349 /* grow sockets if we must */
350 if (navailsocks
< (nsocks
/ 3)) cnet_grow_sockets();
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
)
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
)
368 if (NULL
== (sock
= cnet_fetch(sid
))) return NULL
;
369 if (conn_data
) sock
->data
= conn_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;
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
)
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 */
397 sock
->buf
= sock
->len
? realloc(sock
->buf
, sock
->len
+ len
) : calloc(1, len
);
398 memcpy (sock
->buf
+ sock
->len
, data
, 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
) {
412 pollfds
[i
].events
&= ~POLLOUT
;
415 memmove (sock
->buf
, sock
->buf
+len
, 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
;
426 int cnprintf (int sid
, const char *format
, ...)
432 va_start (args
, format
);
433 if (-1 == (len
= vasprintf (&data
, format
, args
))) return -1;
435 ret
= cnet_write (sid
, data
, len
);