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 /* returns fd. NOT sid */
92 static int cnet_bind (const char *host
, int port
)
97 struct addrinfo hints
, *res
= NULL
;
99 fd
= socket (AF_INET
, SOCK_STREAM
, 0);
100 if (!host
|| 0 > fd
) return fd
;
102 memset (&hints
, '\0', sizeof(hints
));
103 hints
.ai_family
= PF_UNSPEC
;
104 if (cnet_ip_type(host
)) hints
.ai_flags
= AI_NUMERICHOST
;
105 if (port
) snprintf (strport
, 6, "%d", port
);
106 if (getaddrinfo (host
, (port
? strport
: NULL
), &hints
, &res
)) return -1;
108 salen
= res
->ai_addrlen
;
110 if (-1 == bind (fd
, sa
, sizeof(*sa
))) {
119 /* create a sid / configure fd */
120 static int cnet_register (int fd
, int sockflags
, int fdflags
)
126 if (0 == nsocks
) cnet_grow_sockets();
127 for (sid
= 0; sid
< nsocks
; sid
++)
128 if (socks
[sid
].flags
& CNET_AVAIL
) break;
129 if (sid
== nsocks
) return -1;
133 sock
->flags
= sockflags
;
135 /* configure the fd */
136 pollfds
= realloc (pollfds
, (npollfds
+1) * sizeof(*pollfds
));
137 pollsids
= realloc (pollsids
, (npollfds
+1) * sizeof(*pollsids
));
138 memset (pollfds
+npollfds
, '\0', sizeof(*pollfds
));
139 pollfds
[npollfds
].fd
= sock
->fd
;
140 pollfds
[npollfds
].events
= fdflags
;
141 pollsids
[npollfds
] = sid
;
144 /* set nonblocking */
145 if (-1 == (flags
= fcntl (sock
->fd
, F_GETFL
, 0))) return -1;
147 fcntl (sock
->fd
, F_SETFL
, flags
);
152 /* fetch the cnet_socket_t related to a sid if avail and not deleted */
153 static cnet_socket_t
*cnet_fetch (int sid
)
155 if (sid
>= nsocks
) return NULL
;
156 if (socks
[sid
].flags
& (CNET_AVAIL
| CNET_DELETED
)) return NULL
;
161 /*** connection handlers ***/
162 static int cnet_on_connect (int sid
, cnet_socket_t
*sock
)
164 sock
->flags
&= ~(CNET_CONNECT
);
165 if (sock
->handler
->on_connect
) sock
->handler
->on_connect (sid
, sock
->data
);
169 static int cnet_on_newclient (int sid
, cnet_socket_t
*sock
)
171 int fd
, newsid
, accepted
= 0;
172 char host
[40], serv
[6];
174 cnet_socket_t
*newsock
;
177 memset (&sa
, '\0', salen
);
179 while (navailsocks
) {
180 fd
= accept (sock
->fd
, &sa
, &salen
);
183 newsid
= cnet_register (fd
, CNET_CLIENT
, POLLIN
|POLLERR
|POLLHUP
|POLLNVAL
);
184 newsock
= &socks
[newsid
];
186 getnameinfo (&sa
, salen
, host
, 40, serv
, 6, NI_NUMERICHOST
|NI_NUMERICSERV
);
187 newsock
->rhost
= strdup (host
);
188 newsock
->rport
= atoi (serv
);
189 sock
->handler
->on_newclient (sid
, sock
->data
, newsid
, newsock
->rhost
, newsock
->rport
);
194 static int cnet_on_readable (int sid
, cnet_socket_t
*sock
)
198 buf
= calloc (1, 1024);
199 if (-1 == (len
= read(sock
->fd
, buf
, 1023))) return cnet_close(sid
);
200 ret
= sock
->handler
->on_read (sid
, sock
->data
, buf
, len
);
205 static int cnet_on_eof (int sid
, cnet_socket_t
*sock
, int err
)
207 if (sock
->handler
->on_eof
) sock
->handler
->on_eof (sid
, sock
->data
, err
);
208 return cnet_close(sid
);
212 /*** public functions ***/
213 int cnet_listen (const char *host
, int port
)
217 if (-1 == (fd
= cnet_bind (host
, port
))) return -1;
218 if (-1 == listen (fd
, 2)) {
222 return cnet_register (fd
, CNET_SERVER
, POLLIN
|POLLERR
|POLLHUP
|POLLNVAL
);
225 int cnet_connect (const char *rhost
, int rport
, const char *lhost
, int lport
)
230 struct addrinfo hints
, *res
= NULL
;
232 memset (&hints
, '\0', sizeof(hints
));
233 hints
.ai_family
= PF_UNSPEC
;
234 if (-1 == (fd
= cnet_bind (lhost
, lport
))) return -1;
236 /* if we have lhost we need to get hints for connect */
238 getsockname (fd
, sa
, NULL
);
239 hints
.ai_family
= sa
->sa_family
;
242 snprintf (port
, 6, "%d", rport
);
243 if (getaddrinfo (rhost
, port
, &hints
, &res
)) goto cleanup
;
245 salen
= res
->ai_addrlen
;
247 ret
= connect (fd
, sa
, sizeof(*sa
));
248 if (-1 == ret
&& EINPROGRESS
!= errno
) goto cleanup
;
249 return cnet_register (fd
, CNET_CLIENT
|CNET_CONNECT
, POLLIN
|POLLOUT
|POLLERR
|POLLHUP
|POLLNVAL
);
253 if (res
) freeaddrinfo (res
);
257 int cnet_close (int sid
)
261 if (NULL
== (sock
= cnet_fetch(sid
))) return -1;
262 if (0 > sock
->fd
) return -1;
264 /* remove socket from pollfds is wise.... */
265 for (i
= 0; i
< npollfds
; i
++)
266 if (sock
->fd
== pollfds
[i
].fd
) break;
269 memcpy (&pollfds
[i
], &pollfds
[npollfds
], sizeof(*pollfds
));
270 pollsids
[i
] = pollsids
[npollfds
];
272 pollfds
= realloc (pollfds
, npollfds
* sizeof(*pollfds
));
273 pollsids
= realloc (pollsids
, npollfds
* sizeof(*pollsids
));
277 if (sock
->handler
->on_close
) sock
->handler
->on_close (sid
, sock
->data
);
280 if (sock
->len
) free (sock
->buf
);
281 memset (sock
, '\0', sizeof(*sock
));
283 sock
->flags
= CNET_AVAIL
;
288 int cnet_select (int timeout
)
290 static int active
= 0;
294 if (active
) return 0;
297 ret
= n
= poll (pollfds
, npollfds
, timeout
);
298 if (-1 == ret
) return -1;
299 if (0 == ret
) n
= npollfds
;
301 for (i
= 0; n
&& i
< npollfds
; i
++) {
305 if (!sock
->handler
|| !p
->revents
) continue;
307 if (p
->revents
& (POLLERR
|POLLHUP
|POLLNVAL
)) {
308 cnet_on_eof (sid
, sock
, 0);
314 if (p
->revents
& POLLIN
) {
315 if (sock
->flags
& CNET_SERVER
) cnet_on_newclient (sid
, sock
);
316 else cnet_on_readable (sid
, sock
);
318 if (p
->revents
& POLLOUT
) {
319 p
->events
&= ~(POLLOUT
);
320 if (sock
->flags
& CNET_CONNECT
) {
321 cnet_on_connect (sid
, sock
);
322 socks
->flags
&= ~CNET_BLOCKED
;
324 if (sock
->flags
& CNET_BLOCKED
) cnet_write (sid
, NULL
, 0);
332 /* grow sockets if we must */
333 if (navailsocks
< (nsocks
/ 3)) cnet_grow_sockets();
338 /* pass NULL to get the current handler returned */
339 /* you can't 'unset' a handler, defeats the purpose of an open socket */
340 cnet_handler_t
*cnet_handler (int sid
, cnet_handler_t
*handler
)
343 if (NULL
== (sock
= cnet_fetch(sid
))) return NULL
;
344 if (handler
) sock
->handler
= handler
;
345 return sock
->handler
;
348 void *cnet_conndata (int sid
, void *conn_data
)
351 if (NULL
== (sock
= cnet_fetch(sid
))) return NULL
;
352 if (conn_data
) sock
->data
= conn_data
;
356 int cnet_ip_type (const char *ip
)
358 struct in_addr in_buf
;
359 struct in6_addr in6_buf
;
360 if (0 < inet_pton(AF_INET
, ip
, &in_buf
)) return 4;
361 if (0 < inet_pton(AF_INET6
, ip
, &in6_buf
)) return 6;
365 /* is this a valid & connected sid ? */
366 int cnet_valid (int sid
)
368 return cnet_fetch(sid
) ? 1 : 0;
371 int cnet_write (int sid
, const char *data
, int len
)
375 if (NULL
== (sock
= cnet_fetch(sid
))) return -1;
376 if (0 >= len
&& !sock
->len
) return 0;
378 /* check if there is data that still needs to be written */
380 sock
->buf
= sock
->len
? realloc(sock
->buf
, sock
->len
+ len
) : calloc(1, len
);
381 memcpy (sock
->buf
+ sock
->len
, data
, len
);
385 if (sock
->flags
& (CNET_BLOCKED
|CNET_CONNECT
)) return 0;
386 len
= write (sock
->fd
, sock
->buf
, sock
->len
);
387 if (0 > len
&& errno
!= EAGAIN
) return cnet_on_eof (sid
, sock
, errno
);
389 for (i
= 0; i
< npollfds
; i
++)
390 if (sock
->fd
== pollfds
[i
].fd
) break;
392 if (len
== sock
->len
) {
395 pollfds
[i
].events
&= ~POLLOUT
;
398 memmove (sock
->buf
, sock
->buf
+len
, sock
->len
-len
);
400 sock
->buf
= realloc (sock
->buf
, sock
->len
);
402 /* we need to set block since we still have data */
403 sock
->flags
|= CNET_BLOCKED
;
404 pollfds
[i
].events
|= POLLOUT
;
409 int cnprintf (int sid
, const char *format
, ...)
415 va_start (args
, format
);
416 if (-1 == (len
= vasprintf (&data
, format
, args
))) return -1;
418 ret
= cnet_write (sid
, data
, len
);