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 */
46 #define CNET_LINEMODE 0x40
53 /* connection information */
60 char *in_buf
, *out_buf
;
64 cnet_handler_t
*handler
;
68 static cnet_socket_t
*socks
= NULL
;
69 static struct pollfd
*pollfds
= NULL
;
70 static int *pollsids
= NULL
;
71 static int nsocks
= 0;
72 static int npollfds
= 0;
75 /*** private helper methods ***/
76 static int cnet_grow_sockets (void)
79 newsocks
= (nsocks
/ 3) + 16;
80 socks
= realloc (socks
, (nsocks
+newsocks
) * sizeof(*socks
));
81 pollfds
= realloc (pollfds
, (nsocks
+newsocks
) * sizeof(*pollfds
));
82 pollsids
= realloc (pollsids
, (nsocks
+newsocks
) * sizeof(*pollsids
));
83 memset(socks
+nsocks
, '\0', newsocks
*sizeof(*socks
));
84 memset (pollfds
+newsocks
, '\0', sizeof(*pollfds
));
85 for (i
= 0; i
< newsocks
; i
++) {
86 socks
[nsocks
+i
].fd
= -1;
87 socks
[nsocks
+i
].flags
= CNET_AVAIL
;
94 /* returns fd. NOT sid */
95 static int cnet_bind (const char *host
, int port
)
100 struct addrinfo hints
, *res
= NULL
;
102 fd
= socket (AF_INET
, SOCK_STREAM
, 0);
103 if (!host
|| 0 > fd
) return fd
;
105 memset (&hints
, '\0', sizeof(hints
));
106 hints
.ai_family
= PF_UNSPEC
;
107 if (cnet_ip_type(host
)) hints
.ai_flags
= AI_NUMERICHOST
;
108 if (port
) snprintf (strport
, 6, "%d", port
);
109 if (getaddrinfo (host
, (port
? strport
: NULL
), &hints
, &res
)) return -1;
111 salen
= res
->ai_addrlen
;
113 if (-1 == bind (fd
, sa
, sizeof(*sa
))) {
122 /* create a sid / configure fd */
123 static int cnet_register (int fd
, int sockflags
, int fdflags
)
129 if (0 == nsocks
) cnet_grow_sockets();
130 for (sid
= 0; sid
< nsocks
; sid
++)
131 if (socks
[sid
].flags
& CNET_AVAIL
) break;
132 if (sid
== nsocks
) return -1;
135 sock
->flags
= sockflags
;
136 sock
->poll
= npollfds
;
138 /* configure the fd */
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 (npollfds
< nsocks
) {
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
)
196 char buf
[1024], *beg
, *end
;
200 if (-1 == (len
= read(sock
->fd
, buf
, 1024))) return cnet_close(sid
);
201 sock
->in_buf
= sock
->in_len
? realloc(sock
->in_buf
, sock
->in_len
+len
+1) : calloc(1, len
+1);
202 memcpy (sock
->in_buf
+ sock
->in_len
, buf
, len
);
204 if (1024 > len
) break;
206 sock
->in_buf
[sock
->in_len
] = '\0';
208 if (0 == (sock
->flags
& CNET_LINEMODE
)) {
209 sock
->handler
->on_read (sid
, sock
->data
, sock
->in_buf
, sock
->in_len
);
217 for (end
= sock
->in_buf
; NULL
!= (beg
= strsep(&end
, "\r\n"));) {
218 if (NULL
== end
) break;
219 if ('\0' == *beg
) continue;
220 sock
->handler
->on_read (sid
, sock
->data
, beg
, end
-beg
);
229 sock
->in_len
-= (beg
- sock
->in_buf
);
230 memmove (sock
->in_buf
, beg
, sock
->in_len
);
231 sock
->in_buf
= realloc(sock
->in_buf
, sock
->in_len
);
236 static int cnet_on_eof (int sid
, cnet_socket_t
*sock
, int err
)
238 if (sock
->handler
->on_eof
) sock
->handler
->on_eof (sid
, sock
->data
, err
);
239 return cnet_close(sid
);
243 /*** public functions ***/
244 int cnet_listen (const char *host
, int port
)
248 if (-1 == (fd
= cnet_bind (host
, port
))) return -1;
249 if (-1 == listen (fd
, 2)) {
253 return cnet_register (fd
, CNET_SERVER
, POLLIN
|POLLERR
|POLLHUP
|POLLNVAL
);
256 int cnet_connect (const char *rhost
, int rport
, const char *lhost
, int lport
)
261 struct addrinfo hints
, *res
= NULL
;
263 memset (&hints
, '\0', sizeof(hints
));
264 hints
.ai_family
= PF_UNSPEC
;
265 if (-1 == (fd
= cnet_bind (lhost
, lport
))) return -1;
267 /* if we have lhost we need to get hints for connect */
269 getsockname (fd
, sa
, NULL
);
270 hints
.ai_family
= sa
->sa_family
;
273 snprintf (port
, 6, "%d", rport
);
274 if (getaddrinfo (rhost
, port
, &hints
, &res
)) goto cleanup
;
276 salen
= res
->ai_addrlen
;
278 ret
= connect (fd
, sa
, sizeof(*sa
));
279 if (-1 == ret
&& EINPROGRESS
!= errno
) goto cleanup
;
280 return cnet_register (fd
, CNET_CLIENT
|CNET_CONNECT
, POLLIN
|POLLOUT
|POLLERR
|POLLHUP
|POLLNVAL
);
284 if (res
) freeaddrinfo (res
);
288 int cnet_close (int sid
)
292 if (NULL
== (sock
= cnet_fetch(sid
))) return -1;
293 if (0 > sock
->fd
) return -1;
295 /* remove socket from pollfds is wise.... */
297 if (sock
->poll
< npollfds
) {
298 memcpy (&pollfds
[sock
->poll
], &pollfds
[npollfds
], sizeof(*pollfds
));
299 pollsids
[sock
->poll
] = pollsids
[npollfds
];
300 socks
[pollsids
[sock
->poll
]].poll
= sock
->poll
;
302 memset (&pollfds
[npollfds
], '\0', sizeof(*pollfds
));
305 if (sock
->handler
->on_close
) sock
->handler
->on_close (sid
, sock
->data
);
308 if (sock
->out_len
) free (sock
->out_buf
);
309 if (sock
->in_len
) free (sock
->in_buf
);
310 memset (sock
, '\0', sizeof(*sock
));
312 sock
->flags
= CNET_AVAIL
;
316 int cnet_select (int timeout
)
318 static int active
= 0;
322 if (active
) return 0;
325 ret
= n
= poll (pollfds
, npollfds
, timeout
);
326 if (-1 == ret
) return -1;
327 if (0 == ret
) n
= npollfds
;
329 for (i
= 0; n
&& i
< npollfds
; i
++) {
333 if (!sock
->handler
|| !p
->revents
) continue;
335 if (p
->revents
& (POLLERR
|POLLHUP
|POLLNVAL
)) {
336 cnet_on_eof (sid
, sock
, 0);
342 if (p
->revents
& POLLIN
) {
343 if (sock
->flags
& CNET_SERVER
) cnet_on_newclient (sid
, sock
);
344 else cnet_on_readable (sid
, sock
);
346 if (p
->revents
& POLLOUT
) {
347 p
->events
&= ~(POLLOUT
);
348 if (sock
->flags
& CNET_CONNECT
) {
349 cnet_on_connect (sid
, sock
);
350 socks
->flags
&= ~CNET_CONNECT
;
352 if (sock
->flags
& CNET_BLOCKED
) {
353 sock
->flags
&= ~CNET_BLOCKED
;
354 cnet_write (sid
, NULL
, 0);
363 /* grow sockets if we must */
364 if (npollfds
> nsocks
- (nsocks
/ 3)) cnet_grow_sockets();
369 /* pass NULL to get the current handler returned */
370 /* you can't 'unset' a handler, defeats the purpose of an open socket */
371 cnet_handler_t
*cnet_handler (int sid
, cnet_handler_t
*handler
)
374 if (NULL
== (sock
= cnet_fetch(sid
))) return NULL
;
375 if (handler
) sock
->handler
= handler
;
376 return sock
->handler
;
379 void *cnet_conndata (int sid
, void *conn_data
)
382 if (NULL
== (sock
= cnet_fetch(sid
))) return NULL
;
383 if (conn_data
) sock
->data
= conn_data
;
387 int cnet_ip_type (const char *ip
)
389 struct in_addr in_buf
;
390 struct in6_addr in6_buf
;
391 if (0 < inet_pton(AF_INET
, ip
, &in_buf
)) return 4;
392 if (0 < inet_pton(AF_INET6
, ip
, &in6_buf
)) return 6;
396 /* is this a valid & connected sid ? */
397 int cnet_valid (int sid
)
399 return cnet_fetch(sid
) ? 1 : 0;
402 int cnet_linemode (int sid
, int toggle
)
405 if (NULL
== (sock
= cnet_fetch(sid
))) return 0;
406 if (toggle
) sock
->flags
|= CNET_LINEMODE
;
407 else sock
->flags
&= ~(CNET_LINEMODE
);
411 int cnet_write (int sid
, const void *data
, int len
)
413 int i
, written
= 0, ret
= 0;
415 if (NULL
== (sock
= cnet_fetch(sid
))) return -1;
416 if (0 >= len
&& !sock
->out_len
) return 0;
417 if (sock
->flags
& (CNET_BLOCKED
|CNET_CONNECT
)) goto buffer
;
420 if (sock
->out_len
!= (written
= write(sock
->fd
, sock
->out_buf
, sock
->out_len
))) goto buffer
;
421 free (sock
->out_buf
);
424 if (len
== (ret
= write(sock
->fd
, data
, len
))) return ret
+written
;
427 pollfds
[sock
->poll
].events
|= POLLOUT
;
428 sock
->flags
|= CNET_BLOCKED
;
430 if (sock
->out_len
) memmove(sock
->out_buf
, sock
->out_buf
+ written
, sock
->out_len
- written
);
431 sock
->out_buf
= realloc(sock
->out_buf
, (sock
->out_len
- written
) + (len
- ret
));
432 memcpy(sock
->out_buf
+(sock
->out_len
-written
), data
, len
);
433 sock
->out_len
= (sock
->out_len
- written
) + (len
- ret
);
434 return written
+ ret
;
437 int cnprintf (int sid
, const char *format
, ...)
443 va_start (args
, format
);
444 if (-1 == (len
= vasprintf (&data
, format
, args
))) return -1;
446 ret
= cnet_write (sid
, data
, len
);