1 |
/* Socket routines. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
/* Define this to trace function calls: */ |
11 |
/* #define TRACE_CALLS */ |
12 |
|
13 |
/* Define this to log warnings on exceeding buffer size: */ |
14 |
/* #define WARN_ON_BUFSIZE */ |
15 |
|
16 |
/* Define this to disable swritemap(). For IRC Services, we disable this |
17 |
* to avoid depending on the presence of munmap(), since we don't use |
18 |
* swritemap() anyway. */ |
19 |
#define DISABLE_SWRITEMAP |
20 |
|
21 |
#include "services.h" |
22 |
#include <fcntl.h> |
23 |
#include <sys/socket.h> |
24 |
#include <netdb.h> |
25 |
#include <netinet/in.h> |
26 |
#include <arpa/inet.h> |
27 |
|
28 |
#ifndef DISABLE_SWRITEMAP |
29 |
# include <sys/mman.h> /* for munmap() */ |
30 |
#endif |
31 |
|
32 |
/*************************************************************************/ |
33 |
|
34 |
/* For function call tracing: */ |
35 |
|
36 |
#ifdef TRACE_CALLS |
37 |
|
38 |
static int trace_depth = -1; |
39 |
|
40 |
#define ENTER(fmt,...) \ |
41 |
trace_depth++; \ |
42 |
log_debug(1, "sockets: TRACE: %*s%s(" fmt ")", \ |
43 |
trace_depth*2, "", __FUNCTION__ , ## __VA_ARGS__) |
44 |
#define ENTER_WITH(retfmt,fmt,...) \ |
45 |
const char *__retfmt = (retfmt); \ |
46 |
ENTER(fmt , ## __VA_ARGS__) |
47 |
#define RETURN do { \ |
48 |
log_debug(1, "sockets: TRACE: %*s%s() -> void", \ |
49 |
trace_depth*2, "", __FUNCTION__); \ |
50 |
trace_depth--; \ |
51 |
return; \ |
52 |
} while (0) |
53 |
#define RETURN_WITH(val) do { \ |
54 |
typeof(val) __tmp = (val); \ |
55 |
if (debug) { \ |
56 |
char buf[64]; \ |
57 |
snprintf(buf, sizeof(buf), __retfmt, __tmp); \ |
58 |
log_debug(1, "sockets: TRACE: %*s%s() -> %s", \ |
59 |
trace_depth*2, "", __FUNCTION__, buf); \ |
60 |
} \ |
61 |
trace_depth--; \ |
62 |
return __tmp; \ |
63 |
} while (0) |
64 |
|
65 |
#else /* !TRACE_CALLS */ |
66 |
|
67 |
#define ENTER(fmt,...) /* nothing */ |
68 |
#define ENTER_WITH(retfmt,fmt,...) /* nothing */ |
69 |
#define RETURN return |
70 |
#define RETURN_WITH(val) return(val) |
71 |
|
72 |
#endif /* TRACE_CALLS */ |
73 |
|
74 |
/*************************************************************************/ |
75 |
|
76 |
/* Socket data structure */ |
77 |
|
78 |
struct socket_ |
79 |
{ |
80 |
Socket *next, *prev; |
81 |
int fd; /* Socket's file descriptor */ |
82 |
struct sockaddr_in remote; /* Remote address */ |
83 |
int flags; /* Status flags (SF_*) */ |
84 |
|
85 |
SocketCallback cb_connect; /* Connect callback */ |
86 |
SocketCallback cb_disconn; /* Disconnect callback */ |
87 |
SocketCallback cb_accept; /* Accept callback */ |
88 |
SocketCallback cb_read; /* Data-available callback */ |
89 |
SocketCallback cb_readline; /* Line-available callback */ |
90 |
SocketCallback cb_trigger; /* Write trigger callback */ |
91 |
|
92 |
int write_timeout; /* Write timeout, in seconds (0 = none) */ |
93 |
time_t last_write_time; /* Last time data was successfully sent */ |
94 |
|
95 |
/* Usage of pointers: |
96 |
* - Xbuf is the buffer base pointer |
97 |
* - Xptr is the address of the next character to store |
98 |
* - Xend is the address of the next character to retrieve |
99 |
* - Xtop is the address of the last byte of the buffer + 1 |
100 |
* Xend-Xptr (mod Xbufsize) gives the number of bytes in the buffer. |
101 |
*/ |
102 |
char *rbuf, *rptr, *rend, *rtop; /* Read buffer and pointers */ |
103 |
char *wbuf, *wptr, *wend, *wtop; /* Write buffer and pointers */ |
104 |
|
105 |
/* List of mapped memory areas to send, earliest first */ |
106 |
struct wmapinfo |
107 |
{ |
108 |
struct wmapinfo *next; |
109 |
int32 wait; /* # of bytes to write from wbuf first */ |
110 |
const char *map; /* Start of mapped area, or trigger data */ |
111 |
int32 maplen; /* Length of mapped area (0 = write trigger) */ |
112 |
int32 pos; /* Current position */ |
113 |
} *writemap, *writemap_tail; |
114 |
|
115 |
uint64 total_read; /* Total number of bytes read */ |
116 |
uint64 total_written; /* Total number of bytes written */ |
117 |
}; |
118 |
|
119 |
#define SF_SELFCREATED 0x0001 /* We created this socket ourselves */ |
120 |
#define SF_BLOCKING 0x0002 /* Writes are blocking */ |
121 |
#define SF_LISTENER 0x0004 /* Socket is a listener socket */ |
122 |
#define SF_CONNECTING 0x0008 /* Socket is busy connecting */ |
123 |
#define SF_CONNECTED 0x0010 /* Socket has connected */ |
124 |
#define SF_MUTE 0x0020 /* Socket is muted */ |
125 |
#define SF_UNMUTED 0x0040 /* Socket was just unmuted */ |
126 |
#define SF_CALLBACK 0x0080 /* Currently calling callbacks */ |
127 |
#define SF_WTRIGGER 0x0100 /* A write trigger has been reached */ |
128 |
#define SF_WARNED 0x0200 /* Warned about hitting buffer limit */ |
129 |
#define SF_DISCONNECT 0x0400 /* Disconnect when writebuf empty */ |
130 |
#define SF_DISCONN_REQ 0x0800 /* Disconnect has been requested */ |
131 |
#define SF_DISCONNECTING 0x1000 /* Socket is in the middle of disconnecting */ |
132 |
#define SF_BROKEN 0x2000 /* Connection was broken while in a callback */ |
133 |
#define SF_DELETEME 0x4000 /* Delete socket when convenient */ |
134 |
|
135 |
/* Used when calling do_disconn() due to SF_DISCONNECT */ |
136 |
#define DISCONN_RESUME_FLAG 0x100 |
137 |
#define DISCONN_LOCAL_RESUME (void *)((int)DISCONN_LOCAL|DISCONN_RESUME_FLAG) |
138 |
#define DISCONN_REMOTE_RESUME (void *)((int)DISCONN_REMOTE|DISCONN_RESUME_FLAG) |
139 |
|
140 |
/* Size of read/write buffers */ |
141 |
#define read_buffer_size(s) ((s)->rtop - (s)->rbuf) |
142 |
#define write_buffer_size(s) ((s)->wtop - (s)->wbuf) |
143 |
|
144 |
/* Does the socket still have more data that needs to be written? */ |
145 |
#define MORE_TO_WRITE(s) (write_buffer_len(s) > 0 || ((s)->writemap)) |
146 |
|
147 |
/*************************************************************************/ |
148 |
|
149 |
/* List of all sockets (even unopened ones) */ |
150 |
static Socket *allsockets = NULL; |
151 |
|
152 |
/* Array of all opened sockets (indexed by FD), dynamically allocated */ |
153 |
static Socket **sockets = NULL; |
154 |
|
155 |
/* Highest FD number in use plus 1; also length of sockets[] array */ |
156 |
static int max_fd; |
157 |
|
158 |
/* Set of all connected socket FDs */ |
159 |
static fd_set sock_fds; |
160 |
|
161 |
/* Set of all FDs that need data written (or are connecting) */ |
162 |
static fd_set write_fds; |
163 |
|
164 |
/* Total memory used by socket buffers */ |
165 |
static uint32 total_bufsize; |
166 |
|
167 |
/* Per-connection and total buffer size limits */ |
168 |
static uint32 bufsize_limit = 0, total_bufsize_limit = 0; |
169 |
|
170 |
/* Global read timeout, in milliseconds */ |
171 |
static int read_timeout = -1; |
172 |
|
173 |
/*************************************************************************/ |
174 |
|
175 |
/* Internal routine declarations (definitions at bottom of file) */ |
176 |
|
177 |
static int do_callback(Socket * s, SocketCallback cb, void *param); |
178 |
static void do_accept(Socket * s); |
179 |
static int fill_read_buffer(Socket * s); |
180 |
static int flush_write_buffer(Socket * s); |
181 |
static uint32 resize_how_much(const Socket * s, uint32 current_size, |
182 |
int *errp); |
183 |
static int resize_rbuf(Socket * s, uint32 size); |
184 |
static int resize_wbuf(Socket * s, uint32 size); |
185 |
static int resize_buf(char **p_buf, char **p_ptr, char **p_end, char **p_top, |
186 |
uint32 newsize); |
187 |
static int reclaim_buffer_space_one(Socket * s); |
188 |
static int reclaim_buffer_space(void); |
189 |
static void next_wmap(Socket * s); |
190 |
static int buffered_write(Socket * s, const char *buf, int len); |
191 |
static int do_disconn(Socket * s, void *code); |
192 |
static void sock_closefd(Socket * s); |
193 |
|
194 |
/*************************************************************************/ |
195 |
/*************************** Global routines *****************************/ |
196 |
/*************************************************************************/ |
197 |
|
198 |
/* Set the per-connection and total buffer size limits (zero means no |
199 |
* limit). Always successful; both values are silently rounded down to a |
200 |
* multiple of SOCK_MIN_BUFSIZE (if a value is less than SOCK_MIN_BUFSIZE it |
201 |
* is rounded up). Both values default to zero, i.e. unlimited. |
202 |
*/ |
203 |
|
204 |
void |
205 |
sock_set_buflimits(uint32 per_conn, uint32 total) |
206 |
{ |
207 |
if (per_conn > 0) |
208 |
{ |
209 |
per_conn = per_conn / SOCK_MIN_BUFSIZE * SOCK_MIN_BUFSIZE; |
210 |
if (!per_conn) |
211 |
per_conn = SOCK_MIN_BUFSIZE; |
212 |
} |
213 |
if (total > 0) |
214 |
{ |
215 |
total = total / SOCK_MIN_BUFSIZE * SOCK_MIN_BUFSIZE; |
216 |
if (!total) |
217 |
total = SOCK_MIN_BUFSIZE; |
218 |
} |
219 |
bufsize_limit = per_conn; |
220 |
total_bufsize_limit = total; |
221 |
} |
222 |
|
223 |
/*************************************************************************/ |
224 |
|
225 |
/* Set the global read timeout. A value of -1 (default) means no timeout. */ |
226 |
|
227 |
void |
228 |
sock_set_rto(int msec) |
229 |
{ |
230 |
read_timeout = msec; |
231 |
} |
232 |
|
233 |
/*************************************************************************/ |
234 |
|
235 |
/* Create and return a new socket. Returns NULL if unsuccessful (i.e. no |
236 |
* more space for buffers). |
237 |
*/ |
238 |
|
239 |
Socket * |
240 |
sock_new(void) |
241 |
{ |
242 |
Socket *s; |
243 |
|
244 |
ENTER_WITH("%p", ""); |
245 |
if (total_bufsize_limit) |
246 |
{ |
247 |
while (total_bufsize + SOCK_MIN_BUFSIZE * 2 > total_bufsize_limit) |
248 |
{ |
249 |
if (!reclaim_buffer_space()) |
250 |
{ |
251 |
log("sockets: sock_new(): out of buffer space! current=%lu," |
252 |
" limit=%lu", (unsigned long) total_bufsize, |
253 |
(unsigned long) total_bufsize_limit); |
254 |
RETURN_WITH(NULL); |
255 |
} |
256 |
} |
257 |
} |
258 |
|
259 |
s = malloc(sizeof(*s)); |
260 |
if (!s) |
261 |
{ |
262 |
RETURN_WITH(NULL); |
263 |
} |
264 |
s->rbuf = malloc(SOCK_MIN_BUFSIZE); |
265 |
if (!s->rbuf) |
266 |
{ |
267 |
int errno_save = errno; |
268 |
free(s); |
269 |
errno = errno_save; |
270 |
RETURN_WITH(NULL); |
271 |
} |
272 |
s->wbuf = malloc(SOCK_MIN_BUFSIZE); |
273 |
if (!s->wbuf) |
274 |
{ |
275 |
int errno_save = errno; |
276 |
free(s->rbuf); |
277 |
free(s); |
278 |
errno = errno_save; |
279 |
RETURN_WITH(NULL); |
280 |
} |
281 |
|
282 |
s->fd = -1; |
283 |
memset(&s->remote, 0, sizeof(s->remote)); |
284 |
s->flags = 0; |
285 |
s->cb_connect = NULL; |
286 |
s->cb_disconn = NULL; |
287 |
s->cb_accept = NULL; |
288 |
s->cb_read = NULL; |
289 |
s->cb_readline = NULL; |
290 |
s->cb_trigger = NULL; |
291 |
s->write_timeout = 0; |
292 |
s->last_write_time = time(NULL); |
293 |
s->rptr = s->rbuf; |
294 |
s->rend = s->rbuf; |
295 |
s->rtop = s->rbuf + SOCK_MIN_BUFSIZE; |
296 |
s->wptr = s->wbuf; |
297 |
s->wend = s->wbuf; |
298 |
s->wtop = s->wbuf + SOCK_MIN_BUFSIZE; |
299 |
s->writemap = NULL; |
300 |
s->writemap_tail = NULL; |
301 |
s->total_read = 0; |
302 |
s->total_written = 0; |
303 |
LIST_INSERT(s, allsockets); |
304 |
total_bufsize += read_buffer_size(s) + write_buffer_size(s); |
305 |
RETURN_WITH(s); |
306 |
} |
307 |
|
308 |
/*************************************************************************/ |
309 |
|
310 |
/* Free a socket, first disconnecting/closing it if necessary. */ |
311 |
|
312 |
void |
313 |
sock_free(Socket * s) |
314 |
{ |
315 |
ENTER("%p", s); |
316 |
if (!s) |
317 |
{ |
318 |
log("sockets: sock_free() with NULL socket!"); |
319 |
errno = EINVAL; |
320 |
RETURN; |
321 |
} |
322 |
if (s->flags & (SF_CONNECTING | SF_CONNECTED)) |
323 |
{ |
324 |
s->flags |= SF_DELETEME; |
325 |
do_disconn(s, DISCONN_LOCAL); |
326 |
/* do_disconn() will call us again at the appropriate time */ |
327 |
RETURN; |
328 |
} |
329 |
else if (s->flags & SF_LISTENER) |
330 |
{ |
331 |
close_listener(s); |
332 |
} |
333 |
LIST_REMOVE(s, allsockets); |
334 |
total_bufsize -= read_buffer_size(s) + write_buffer_size(s); |
335 |
free(s->rbuf); |
336 |
free(s->wbuf); |
337 |
free(s); |
338 |
/* If this was the last socket, free the sockets[] array too */ |
339 |
if (!allsockets) |
340 |
{ |
341 |
free(sockets); |
342 |
sockets = NULL; |
343 |
} |
344 |
RETURN; |
345 |
} |
346 |
|
347 |
/*************************************************************************/ |
348 |
|
349 |
/* Set a callback on a socket. */ |
350 |
|
351 |
void |
352 |
sock_setcb(Socket * s, SocketCallbackID which, SocketCallback func) |
353 |
{ |
354 |
ENTER("%p,%d,%p", s, which, func); |
355 |
if (!s) |
356 |
{ |
357 |
log("sockets: sock_setcb() with NULL socket!"); |
358 |
errno = EINVAL; |
359 |
RETURN; |
360 |
} |
361 |
switch (which) |
362 |
{ |
363 |
case SCB_CONNECT: |
364 |
s->cb_connect = func; |
365 |
break; |
366 |
case SCB_DISCONNECT: |
367 |
s->cb_disconn = func; |
368 |
break; |
369 |
case SCB_ACCEPT: |
370 |
s->cb_accept = func; |
371 |
break; |
372 |
case SCB_READ: |
373 |
s->cb_read = func; |
374 |
break; |
375 |
case SCB_READLINE: |
376 |
s->cb_readline = func; |
377 |
break; |
378 |
case SCB_TRIGGER: |
379 |
s->cb_trigger = func; |
380 |
break; |
381 |
default: |
382 |
log("sockets: sock_setcb(): invalid callback ID %d", which); |
383 |
break; |
384 |
} |
385 |
RETURN; |
386 |
} |
387 |
|
388 |
/*************************************************************************/ |
389 |
|
390 |
/* Return whether the given socket is currently connected. */ |
391 |
|
392 |
int |
393 |
sock_isconn(const Socket * s) |
394 |
{ |
395 |
ENTER_WITH("%d", "%p", s); |
396 |
if (!s) |
397 |
{ |
398 |
log("sockets: sock_isconn() with NULL socket!"); |
399 |
errno = EINVAL; |
400 |
RETURN_WITH(0); |
401 |
} |
402 |
RETURN_WITH(s->flags & SF_CONNECTED ? 1 : 0); |
403 |
} |
404 |
|
405 |
/*************************************************************************/ |
406 |
|
407 |
/* Retrieve address of remote end of socket. Functions the same way as |
408 |
* getpeername() (initialize *lenptr to sizeof(sa), address returned in sa, |
409 |
* non-truncated length of address returned in *lenptr). Returns -1 with |
410 |
* errno == EINVAL if a NULL pointer is passed or the given socket is not |
411 |
* connected. |
412 |
*/ |
413 |
|
414 |
int |
415 |
sock_remote(const Socket * s, struct sockaddr *sa, int *lenptr) |
416 |
{ |
417 |
ENTER_WITH("%d", "%p,%p,%p", s, sa, lenptr); |
418 |
if (!s || !sa || !lenptr || !(s->flags & SF_CONNECTED)) |
419 |
{ |
420 |
if (!s || !sa || !lenptr) |
421 |
{ |
422 |
log("sockets: sock_remote() with NULL %s!", |
423 |
!s ? "socket" : !sa ? "sockaddr" : "lenptr"); |
424 |
} |
425 |
errno = EINVAL; |
426 |
RETURN_WITH(-1); |
427 |
} |
428 |
if (sizeof(s->remote) <= *lenptr) |
429 |
memcpy(sa, &s->remote, sizeof(s->remote)); |
430 |
else |
431 |
memcpy(sa, &s->remote, *lenptr); |
432 |
*lenptr = sizeof(s->remote); |
433 |
RETURN_WITH(0); |
434 |
} |
435 |
|
436 |
/*************************************************************************/ |
437 |
|
438 |
/* Set whether socket writes should block (blocking != 0) or not |
439 |
* (blocking == 0). |
440 |
*/ |
441 |
|
442 |
void |
443 |
sock_set_blocking(Socket * s, int blocking) |
444 |
{ |
445 |
ENTER("%p,%d", s, blocking); |
446 |
if (!s) |
447 |
{ |
448 |
log("sockets: sock_set_blocking() with NULL socket!"); |
449 |
errno = EINVAL; |
450 |
RETURN; |
451 |
} |
452 |
if (blocking) |
453 |
s->flags |= SF_BLOCKING; |
454 |
else |
455 |
s->flags &= ~SF_BLOCKING; |
456 |
RETURN; |
457 |
} |
458 |
|
459 |
/*************************************************************************/ |
460 |
|
461 |
/* Return whether socket writes are blocking (return value > 0) or not |
462 |
* (return value == 0). Returns -1 if socket is invalid. |
463 |
*/ |
464 |
|
465 |
int |
466 |
sock_get_blocking(const Socket * s) |
467 |
{ |
468 |
ENTER_WITH("%d", "%p", s); |
469 |
if (!s) |
470 |
{ |
471 |
log("sockets: sock_get_blocking() with NULL socket!"); |
472 |
errno = EINVAL; |
473 |
RETURN_WITH(-1); |
474 |
} |
475 |
RETURN_WITH(s->flags & SF_BLOCKING); |
476 |
} |
477 |
|
478 |
/*************************************************************************/ |
479 |
|
480 |
/* Set (or clear, if seconds==0) the write timeout for a socket. */ |
481 |
|
482 |
void |
483 |
sock_set_wto(Socket * s, int seconds) |
484 |
{ |
485 |
ENTER("%p,%d", s, seconds); |
486 |
if (!s || seconds < 0) |
487 |
{ |
488 |
log("sockets: sock_set_wto() with %s!", |
489 |
!s ? "NULL socket" : "negative timeout"); |
490 |
errno = EINVAL; |
491 |
RETURN; |
492 |
} |
493 |
s->write_timeout = seconds; |
494 |
RETURN; |
495 |
} |
496 |
|
497 |
/*************************************************************************/ |
498 |
|
499 |
/* Mute a socket. When a socket is muted, no arriving data will be |
500 |
* acknowledged (except to the extent that the operating system |
501 |
* automatically acknowledges and buffers the data), and the read and |
502 |
* accept callbacks will not be called. However, a socket in the |
503 |
* process of conenction will still call the connect or disconnect |
504 |
* callbacks when it completes or fails. |
505 |
*/ |
506 |
|
507 |
void |
508 |
sock_mute(Socket * s) |
509 |
{ |
510 |
ENTER("%p", s); |
511 |
if (!s) |
512 |
{ |
513 |
log("sockets: sock_mute() with NULL socket!"); |
514 |
RETURN; |
515 |
} |
516 |
if (!(s->flags & SF_MUTE)) |
517 |
{ |
518 |
if (s->fd >= 0) |
519 |
FD_CLR(s->fd, &sock_fds); |
520 |
s->flags |= SF_MUTE; |
521 |
} |
522 |
RETURN; |
523 |
} |
524 |
|
525 |
/*************************************************************************/ |
526 |
|
527 |
/* Unmute a socket. If any data is waiting, it will be acknowledged and |
528 |
* the appropriate callback (read or accept) called during the next call to |
529 |
* check_sockets(). |
530 |
*/ |
531 |
|
532 |
void |
533 |
sock_unmute(Socket * s) |
534 |
{ |
535 |
ENTER("%p", s); |
536 |
if (!s) |
537 |
{ |
538 |
log("sockets: sock_unmute() with NULL socket!"); |
539 |
RETURN; |
540 |
} |
541 |
if (s->flags & SF_MUTE) |
542 |
{ |
543 |
if (s->fd >= 0) |
544 |
FD_SET(s->fd, &sock_fds); |
545 |
s->flags &= ~SF_MUTE; |
546 |
s->flags |= SF_UNMUTED; |
547 |
} |
548 |
RETURN; |
549 |
} |
550 |
|
551 |
/*************************************************************************/ |
552 |
|
553 |
/* Return amount of data in read buffer. Assumes socket is valid. */ |
554 |
|
555 |
inline uint32 |
556 |
read_buffer_len(const Socket * s) |
557 |
{ |
558 |
if (s->rend >= s->rptr) |
559 |
return s->rend - s->rptr; |
560 |
else |
561 |
return (s->rend + read_buffer_size(s)) - s->rptr; |
562 |
} |
563 |
|
564 |
|
565 |
/*************************************************************************/ |
566 |
|
567 |
/* Return amount of data in write buffer. Assumes socket is valid. */ |
568 |
|
569 |
inline uint32 |
570 |
write_buffer_len(const Socket * s) |
571 |
{ |
572 |
if (s->wend >= s->wptr) |
573 |
return s->wend - s->wptr; |
574 |
else |
575 |
return (s->wend + write_buffer_size(s)) - s->wptr; |
576 |
} |
577 |
|
578 |
/*************************************************************************/ |
579 |
|
580 |
/* Return total number of bytes received and sent on this socket in |
581 |
* *read_ret and *writekb_ret respectively. Sent data count does not |
582 |
* include buffered but unsent data. Returns 0 on success, -1 on error |
583 |
* (invalid socket). |
584 |
*/ |
585 |
|
586 |
int |
587 |
sock_rwstat(const Socket * s, uint64 * read_ret, uint64 * written_ret) |
588 |
{ |
589 |
ENTER_WITH("%u", "%p,%p,%p", s, read_ret, written_ret); |
590 |
if (!s) |
591 |
{ |
592 |
log("sockets: sock_rwstat() with NULL socket!"); |
593 |
errno = EINVAL; |
594 |
RETURN_WITH(-1); |
595 |
} |
596 |
if (read_ret) |
597 |
*read_ret = s->total_read; |
598 |
if (written_ret) |
599 |
*written_ret = s->total_written; |
600 |
RETURN_WITH(0); |
601 |
} |
602 |
|
603 |
/*************************************************************************/ |
604 |
|
605 |
/* Return the larger of (1) the ratio of the given socket's total buffer |
606 |
* size (read and write buffers combined) to bufsize_limit and (2) the |
607 |
* ratio of the amount of memory used by all sockets' buffers to |
608 |
* total_bufsize_limit, as a percentage rounded up to the next integer. |
609 |
* Ratios (1) and (2) are zero if bufsize_limit or total_bufsize_limit, |
610 |
* respectively, are set to zero (unlimited). |
611 |
* |
612 |
* If any of `socksize_ret', `totalsize_ret', `ratio1_ret', and |
613 |
* `ratio2_ret' are non-NULL, they are set respectively to the given |
614 |
* socket's total buffer size, the amount of memory used by all sockets' |
615 |
* buffers, ratio (1) as a percentage, and ratio (2) as a percentage. |
616 |
* |
617 |
* If `s' is NULL, ratio (1) is set to zero, and *socksize_ret is not |
618 |
* modified. |
619 |
*/ |
620 |
|
621 |
int |
622 |
sock_bufstat(const Socket * s, uint32 * socksize_ret, |
623 |
uint32 * totalsize_ret, int *ratio1_ret, int *ratio2_ret) |
624 |
{ |
625 |
int ratio1 = 0, ratio2 = 0; |
626 |
|
627 |
ENTER_WITH("%d", "%p,%p,%p,%p,%p", s, socksize_ret, totalsize_ret, |
628 |
ratio1_ret, ratio2_ret); |
629 |
if (bufsize_limit && s) |
630 |
{ |
631 |
uint32 size = read_buffer_size(s) + write_buffer_size(s); |
632 |
if (bufsize_limit <= 0x7FFFFFFF / 100) |
633 |
ratio1 = (size * 100 + bufsize_limit - 1) / bufsize_limit; |
634 |
else |
635 |
ratio1 = (size + bufsize_limit / 100 - 1) / (bufsize_limit / 100); |
636 |
} |
637 |
if (total_bufsize_limit) |
638 |
{ |
639 |
if (bufsize_limit <= 0x7FFFFFFF / 100) |
640 |
ratio2 = (total_bufsize * 100 + total_bufsize_limit - 1) |
641 |
/ total_bufsize_limit; |
642 |
else |
643 |
ratio2 = (total_bufsize + total_bufsize_limit / 100 - 1) |
644 |
/ (total_bufsize_limit / 100); |
645 |
} |
646 |
if (socksize_ret && s) |
647 |
*socksize_ret = read_buffer_size(s) + write_buffer_size(s); |
648 |
if (totalsize_ret) |
649 |
*totalsize_ret = total_bufsize; |
650 |
if (ratio1_ret) |
651 |
*ratio1_ret = ratio1; |
652 |
if (ratio2_ret) |
653 |
*ratio2_ret = ratio2; |
654 |
if (ratio1 > ratio2) |
655 |
RETURN_WITH(ratio1); |
656 |
else |
657 |
RETURN_WITH(ratio2); |
658 |
} |
659 |
|
660 |
/*************************************************************************/ |
661 |
/*************************************************************************/ |
662 |
|
663 |
/* Check all sockets for activity, and call callbacks as necessary. |
664 |
* Returns after activity has been detected on at least one socket. |
665 |
*/ |
666 |
|
667 |
void |
668 |
check_sockets(void) |
669 |
{ |
670 |
fd_set rfds, wfds; |
671 |
int i; |
672 |
int32 res; |
673 |
struct timeval tv; |
674 |
time_t now = time(NULL); |
675 |
Socket *s; |
676 |
|
677 |
ENTER(""); |
678 |
rfds = sock_fds; |
679 |
wfds = write_fds; |
680 |
if (read_timeout < 0) |
681 |
{ |
682 |
tv.tv_sec = -1; // flag: use a NULL tv parameter to select() |
683 |
tv.tv_usec = 0; |
684 |
} |
685 |
else |
686 |
{ |
687 |
tv.tv_sec = read_timeout / 1000; |
688 |
tv.tv_usec = (read_timeout % 1000) * 1000; |
689 |
} |
690 |
for (i = 0; i < max_fd; i++) |
691 |
{ |
692 |
s = sockets[i]; |
693 |
if (s && s->write_timeout && MORE_TO_WRITE(s)) |
694 |
{ |
695 |
int tleft = s->write_timeout - (now - s->last_write_time); |
696 |
if (tleft < 0) |
697 |
tleft = 0; |
698 |
if (tv.tv_sec < 0 || tleft <= tv.tv_sec) |
699 |
{ |
700 |
tv.tv_sec = tleft; |
701 |
tv.tv_usec = 0; |
702 |
} |
703 |
} |
704 |
} |
705 |
enable_signals(); |
706 |
do |
707 |
{ |
708 |
struct timeval thistv = tv; |
709 |
res = select(max_fd, &rfds, &wfds, NULL, tv.tv_sec < 0 ? NULL : &thistv); |
710 |
} |
711 |
while (res < 0 && errno == EINTR); |
712 |
disable_signals(); |
713 |
log_debug(3, "sockets: select returned %d", res); |
714 |
if (res < 0) |
715 |
{ |
716 |
log_perror("sockets: select()"); |
717 |
RETURN; |
718 |
} |
719 |
|
720 |
for (i = 0; i < max_fd; i++) |
721 |
{ |
722 |
s = sockets[i]; |
723 |
if (!s) |
724 |
continue; |
725 |
if (s->fd != i) |
726 |
{ |
727 |
log("sockets: BUG: sockets[%d]->fd = %d (should be equal)," |
728 |
" clearing socket from table", i, s->fd); |
729 |
sockets[i] = NULL; |
730 |
continue; |
731 |
} |
732 |
|
733 |
if (FD_ISSET(i, &wfds)) |
734 |
{ |
735 |
log_debug(3, "sockets: write ready on fd %d", i); |
736 |
if (!s) |
737 |
{ |
738 |
log("sockets: BUG: got write-ready on fd %d but no socket" |
739 |
" for it!", i); |
740 |
continue; |
741 |
} |
742 |
else if (s->flags & SF_CONNECTING) |
743 |
{ |
744 |
/* Connection established (or failed) */ |
745 |
int val; |
746 |
socklen_t vallen; |
747 |
vallen = sizeof(val); |
748 |
log_debug(2, "sockets: connect on fd %d returned", i); |
749 |
if (getsockopt(i, SOL_SOCKET, SO_ERROR, &val, &vallen) < 0) |
750 |
{ |
751 |
log_perror("sockets: getsockopt(SO_ERROR) for connect (%d" |
752 |
" -> %s:%u)", i, inet_ntoa(s->remote.sin_addr), |
753 |
htons(s->remote.sin_port)); |
754 |
do_disconn(s, DISCONN_CONNFAIL); |
755 |
continue; |
756 |
} |
757 |
if (val != 0) |
758 |
{ |
759 |
errno = val; |
760 |
log_perror("sockets: connect(%d -> %s:%u)", i, |
761 |
inet_ntoa(s->remote.sin_addr), |
762 |
htons(s->remote.sin_port)); |
763 |
do_disconn(s, DISCONN_CONNFAIL); |
764 |
continue; |
765 |
} |
766 |
else |
767 |
{ |
768 |
if (!do_callback(s, s->cb_connect, 0)) |
769 |
continue; |
770 |
} |
771 |
s->flags &= ~SF_CONNECTING; |
772 |
s->flags |= SF_CONNECTED; |
773 |
FD_CLR(i, &write_fds); |
774 |
if (flush_write_buffer(s) < 0) /* we may have pending data */ |
775 |
continue; /* socket is not valid any more */ |
776 |
if (!(s->flags & SF_MUTE)) |
777 |
FD_SET(i, &sock_fds); |
778 |
} |
779 |
else if (!(s->flags & SF_CONNECTED)) |
780 |
{ |
781 |
log("sockets: BUG: got write-ready on fd %d but socket not" |
782 |
" connected!", i); |
783 |
} |
784 |
else |
785 |
{ |
786 |
if (flush_write_buffer(s) < 0) |
787 |
continue; /* socket is not valid any more */ |
788 |
} |
789 |
} /* set in write fds */ |
790 |
|
791 |
while (s->flags & SF_WTRIGGER) |
792 |
{ |
793 |
/* Write trigger reached */ |
794 |
void *userdata = (void *) s->writemap->map; |
795 |
next_wmap(s); |
796 |
s->flags &= ~SF_WTRIGGER; |
797 |
do_callback(s, s->cb_trigger, userdata); |
798 |
} |
799 |
|
800 |
if (FD_ISSET(i, &rfds) || (s && (s->flags & SF_UNMUTED))) |
801 |
{ |
802 |
log_debug(3, "sockets: %s on fd %d", |
803 |
FD_ISSET(i, &rfds) ? "read ready" : "unmute", i); |
804 |
if (!s) |
805 |
{ |
806 |
log("sockets: BUG: got data on fd %d but no socket for it!", i); |
807 |
FD_CLR(i, &sock_fds); |
808 |
continue; |
809 |
} |
810 |
if (s->flags & SF_MUTE) |
811 |
{ |
812 |
/* Socket was muted by a previous socket's callback */ |
813 |
log_debug(3, "sockets: socket %d has been muted", i); |
814 |
continue; |
815 |
} |
816 |
s->flags &= ~SF_UNMUTED; |
817 |
if (FD_ISSET(i, &rfds)) |
818 |
{ |
819 |
if (s->flags & SF_LISTENER) |
820 |
{ |
821 |
/* Connection arrived */ |
822 |
do_accept(s); |
823 |
continue; |
824 |
} |
825 |
else if (!(s->flags & SF_CONNECTED)) |
826 |
{ |
827 |
log("sockets: BUG: got data on fd %d but not connected!", i); |
828 |
FD_CLR(i, &sock_fds); |
829 |
continue; |
830 |
} |
831 |
/* Normal read */ |
832 |
if (read_buffer_len(s) >= read_buffer_size(s) - 1) |
833 |
{ |
834 |
/* Buffer is full, try to expand it */ |
835 |
int newsize = 0; |
836 |
if (read_buffer_size(s) < SOCK_MIN_BUFSIZE) |
837 |
newsize = SOCK_MIN_BUFSIZE; |
838 |
else if (read_buffer_size(s) + SOCK_MIN_BUFSIZE < bufsize_limit) |
839 |
newsize = read_buffer_size(s) + SOCK_MIN_BUFSIZE; |
840 |
if (newsize > 0) |
841 |
resize_rbuf(s, newsize); |
842 |
} |
843 |
res = fill_read_buffer(s); |
844 |
if (res < 0) |
845 |
{ |
846 |
/* Connection was closed (or some other error occurred) */ |
847 |
if (res < 0) |
848 |
log_perror_debug(1, "sockets: recv(%d)", i); |
849 |
do_disconn(s, DISCONN_REMOTE); |
850 |
continue; |
851 |
} |
852 |
else if (res == 0) |
853 |
{ |
854 |
/* Read buffer is full (NOTE: this causes busy waiting |
855 |
* until space is made available) */ |
856 |
} |
857 |
} |
858 |
else |
859 |
{ |
860 |
res = read_buffer_len(s); |
861 |
} |
862 |
if (res > 0) |
863 |
{ |
864 |
uint32 left = read_buffer_len(s), newleft; |
865 |
if (left == 0) |
866 |
{ |
867 |
log("sockets: BUG: 0 bytes avail after successful read!"); |
868 |
continue; |
869 |
} |
870 |
/* Call read callback(s) in a loop until no more data is |
871 |
* left or neither callback takes any data, or the socket is |
872 |
* disconnected */ |
873 |
newleft = left; |
874 |
do |
875 |
{ |
876 |
if (!do_callback(s, s->cb_read, (void *) (long) newleft)) |
877 |
goto disconnected; |
878 |
if (s->flags & SF_MUTE) |
879 |
break; |
880 |
newleft = read_buffer_len(s); |
881 |
if (s->cb_readline) |
882 |
{ |
883 |
char *newline; |
884 |
if (s->rend > s->rptr) |
885 |
{ |
886 |
newline = memchr(s->rptr, '\n', newleft); |
887 |
} |
888 |
else |
889 |
{ |
890 |
newline = memchr(s->rptr, '\n', s->rtop - s->rptr); |
891 |
if (!newline) |
892 |
newline = memchr(s->rbuf, '\n', s->rend - s->rbuf); |
893 |
} |
894 |
if (newline) |
895 |
{ |
896 |
if (!do_callback(s, s->cb_readline, (void *) (long) newleft)) |
897 |
goto disconnected; |
898 |
if (s->flags & SF_MUTE) |
899 |
break; |
900 |
newleft = read_buffer_len(s); |
901 |
} |
902 |
} |
903 |
} |
904 |
while (newleft != left && (left = newleft) != 0); |
905 |
reclaim_buffer_space_one(s); |
906 |
} |
907 |
} /* socket ready for reading or unmuted */ |
908 |
|
909 |
if (s->write_timeout |
910 |
&& MORE_TO_WRITE(s) && s->last_write_time + s->write_timeout <= now) |
911 |
{ |
912 |
if (s->flags & SF_DISCONNECT) |
913 |
do_disconn(s, DISCONN_LOCAL_RESUME); |
914 |
else |
915 |
do_disconn(s, DISCONN_REMOTE); |
916 |
} |
917 |
|
918 |
disconnected: |
919 |
/* at least one statement is required after a label */ ; |
920 |
|
921 |
} /* for all sockets */ |
922 |
|
923 |
RETURN; |
924 |
} /* check_sockets() */ |
925 |
|
926 |
/*************************************************************************/ |
927 |
/*************************************************************************/ |
928 |
|
929 |
/* Initiate a connection to the given host and port. If an error occurs, |
930 |
* returns -1, else returns 0. The connection is not necessarily complete |
931 |
* even if 0 is returned, and may later fail; use the SCB_CONNECT and |
932 |
* SCB_DISCONNECT callbacks. If this function fails due to inability to |
933 |
* resolve a hostname, errno will be set to the negative of h_errno; pass |
934 |
* the negative of this value to hstrerror() to get an appropriate error |
935 |
* message. |
936 |
* |
937 |
* lhost/lport specify the local side of the connection. If they are not |
938 |
* given (lhost==NULL, lport==0), then they are left to be set by the OS. |
939 |
* |
940 |
* If either host or lhost is not a valid IP address and the gethostbyname() |
941 |
* function is available, this function may block while the hostname is |
942 |
* being resolved. |
943 |
* |
944 |
* This function may be called from a socket's disconnect callback to |
945 |
* establish a new connection using the same socket. It may not be called, |
946 |
* however, if the socket is being freed with sock_free(). |
947 |
*/ |
948 |
|
949 |
int |
950 |
conn(Socket * s, const char *host, int port, const char *lhost, int lport) |
951 |
{ |
952 |
#if HAVE_GETHOSTBYNAME |
953 |
struct hostent *hp; |
954 |
#endif |
955 |
uint8 *addr; |
956 |
struct sockaddr_in sa, lsa; |
957 |
int fd, i; |
958 |
|
959 |
ENTER_WITH("%d", "%p,[%s],%d,[%s],%d", s, host ? host : "(null)", port, |
960 |
lhost ? lhost : "(null)", lport); |
961 |
if (!s || !host || port <= 0 || port > 65535) |
962 |
{ |
963 |
if (port <= 0 || port > 65535) |
964 |
log("sockets: conn() with bad port number (%d)!", port); |
965 |
else |
966 |
log("sockets: conn() with NULL %s!", !s ? "socket" : "hostname"); |
967 |
errno = EINVAL; |
968 |
RETURN_WITH(-1); |
969 |
} |
970 |
if (s->flags & SF_DELETEME) |
971 |
{ |
972 |
log("sockets: conn() called on a freeing socket (%p)", s); |
973 |
errno = EPERM; |
974 |
return -1; |
975 |
} |
976 |
memset(&lsa, 0, sizeof(lsa)); |
977 |
lsa.sin_family = AF_INET; |
978 |
if (lhost) |
979 |
{ |
980 |
if ((addr = pack_ip(lhost)) != 0) |
981 |
memcpy((char *) &lsa.sin_addr, addr, 4); |
982 |
#if HAVE_GETHOSTBYNAME |
983 |
else if ((hp = gethostbyname(lhost)) != NULL) |
984 |
memcpy((char *) &lsa.sin_addr, hp->h_addr, hp->h_length); |
985 |
#endif |
986 |
else |
987 |
lhost = NULL; |
988 |
} |
989 |
if (lport) |
990 |
lsa.sin_port = htons(lport); |
991 |
|
992 |
memset(&sa, 0, sizeof(sa)); |
993 |
if ((addr = pack_ip(host)) != 0) |
994 |
{ |
995 |
memcpy((char *) &sa.sin_addr, addr, 4); |
996 |
sa.sin_family = AF_INET; |
997 |
} |
998 |
#if HAVE_GETHOSTBYNAME |
999 |
else if ((hp = gethostbyname(host)) != NULL) |
1000 |
{ |
1001 |
memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length); |
1002 |
sa.sin_family = hp->h_addrtype; |
1003 |
} |
1004 |
else |
1005 |
{ |
1006 |
errno = -h_errno; |
1007 |
RETURN_WITH(-1); |
1008 |
} |
1009 |
#else |
1010 |
else |
1011 |
{ |
1012 |
log("sockets: conn(): `%s' is not a valid IP address", host); |
1013 |
errno = EINVAL; |
1014 |
RETURN_WITH(-1); |
1015 |
} |
1016 |
#endif |
1017 |
sa.sin_port = htons((uint16) port); |
1018 |
|
1019 |
if ((fd = socket(sa.sin_family, SOCK_STREAM, 0)) < 0) |
1020 |
RETURN_WITH(-1); |
1021 |
|
1022 |
if (fcntl(fd, F_SETFL, O_NDELAY) < 0) |
1023 |
{ |
1024 |
int errno_save = errno; |
1025 |
close(fd); |
1026 |
errno = errno_save; |
1027 |
RETURN_WITH(-1); |
1028 |
} |
1029 |
|
1030 |
if ((lhost || lport) && bind(fd, (struct sockaddr *) &lsa, sizeof(sa)) < 0) |
1031 |
{ |
1032 |
int errno_save = errno; |
1033 |
close(fd); |
1034 |
errno = errno_save; |
1035 |
RETURN_WITH(-1); |
1036 |
} |
1037 |
|
1038 |
if ((i = connect(fd, (struct sockaddr *) &sa, sizeof(sa))) < 0 |
1039 |
&& errno != EINPROGRESS) |
1040 |
{ |
1041 |
int errno_save = errno; |
1042 |
close(fd); |
1043 |
errno = errno_save; |
1044 |
RETURN_WITH(-1); |
1045 |
} |
1046 |
|
1047 |
if (max_fd < fd + 1) |
1048 |
{ |
1049 |
int j; |
1050 |
void *new_sockets = realloc(sockets, (fd + 1) * sizeof(*sockets)); |
1051 |
if (!new_sockets) |
1052 |
{ |
1053 |
int errno_save = errno; |
1054 |
close(fd); |
1055 |
errno = errno_save; |
1056 |
RETURN_WITH(-1); |
1057 |
} |
1058 |
sockets = new_sockets; |
1059 |
for (j = max_fd; j < fd; j++) |
1060 |
sockets[j] = NULL; |
1061 |
max_fd = fd + 1; |
1062 |
} |
1063 |
sockets[fd] = s; |
1064 |
s->remote = sa; |
1065 |
s->fd = fd; |
1066 |
s->flags &= ~(SF_CONNECTING | SF_CONNECTED); |
1067 |
if (i == 0) |
1068 |
{ |
1069 |
s->flags |= SF_CONNECTED; |
1070 |
if (!(s->flags & SF_MUTE)) |
1071 |
FD_SET(fd, &sock_fds); |
1072 |
do_callback(s, s->cb_connect, 0); |
1073 |
} |
1074 |
else |
1075 |
{ |
1076 |
s->flags |= SF_CONNECTING; |
1077 |
FD_SET(fd, &write_fds); |
1078 |
} |
1079 |
RETURN_WITH(0); |
1080 |
} |
1081 |
|
1082 |
/*************************************************************************/ |
1083 |
|
1084 |
/* Disconnect a socket. Returns 0 on success, -1 on error (s == NULL or |
1085 |
* listener socket). Calling this routine on an already-disconnected |
1086 |
* socket returns success without doing anything. Note that the socket may |
1087 |
* not be disconnected immediately; callers who intend to reuse the socket |
1088 |
* MUST wait until the disconnect callback is called before doing so. |
1089 |
*/ |
1090 |
|
1091 |
int |
1092 |
disconn(Socket * s) |
1093 |
{ |
1094 |
ENTER_WITH("%d", "%p", s); |
1095 |
if (!s) |
1096 |
{ |
1097 |
log("sockets: disconn() with NULL socket!"); |
1098 |
errno = EINVAL; |
1099 |
RETURN_WITH(-1); |
1100 |
} |
1101 |
else if (s->flags & SF_LISTENER) |
1102 |
{ |
1103 |
log("sockets: disconn() with listener socket!"); |
1104 |
errno = EINVAL; |
1105 |
RETURN_WITH(-1); |
1106 |
} |
1107 |
RETURN_WITH(do_disconn(s, DISCONN_LOCAL)); |
1108 |
} |
1109 |
|
1110 |
/*************************************************************************/ |
1111 |
|
1112 |
/* Open a listener socket on the given host and port; returns 0 on success |
1113 |
* (the socket is set up and listening), -1 on error. If `host' has |
1114 |
* multiple addresses, only the first one is used; if `host' is NULL, all |
1115 |
* addresses are bound to. As with conn(), a negative errno value |
1116 |
* indicates a failure to resolve the hostname `host'. `backlog' is the |
1117 |
* backlog limit for incoming connections, and is passed directly to the |
1118 |
* listen() system call. |
1119 |
* |
1120 |
* Note that if the SCB_ACCEPT callback is not set, any connections to the |
1121 |
* socket will be dropped immediately. |
1122 |
* |
1123 |
* If host is not a valid IP address and the gethostbyname() function is |
1124 |
* available, this function may block while the hostname is being resolved. |
1125 |
*/ |
1126 |
|
1127 |
int |
1128 |
open_listener(Socket * s, const char *host, int port, int backlog) |
1129 |
{ |
1130 |
#if HAVE_GETHOSTBYNAME |
1131 |
struct hostent *hp; |
1132 |
#endif |
1133 |
uint8 *addr; |
1134 |
struct sockaddr_in sa; |
1135 |
int fd, i; |
1136 |
|
1137 |
ENTER_WITH("%d", "%p,[%s],%d,%d", s, host ? host : "(null)", port, backlog); |
1138 |
if (!s || port <= 0 || port > 65535 || backlog < 1) |
1139 |
{ |
1140 |
if (port <= 0 || port > 65535) |
1141 |
log("sockets: open_listener() with bad port number (%d)!", port); |
1142 |
else if (backlog < 1) |
1143 |
log("sockets: open_listener() with bad backlog (%d)!", backlog); |
1144 |
else |
1145 |
log("sockets: open_listener() with NULL socket!"); |
1146 |
errno = EINVAL; |
1147 |
RETURN_WITH(-1); |
1148 |
} |
1149 |
memset(&sa, 0, sizeof(sa)); |
1150 |
if (host) |
1151 |
{ |
1152 |
if ((addr = pack_ip(host)) != 0) |
1153 |
{ |
1154 |
memcpy((char *) &sa.sin_addr, addr, 4); |
1155 |
sa.sin_family = AF_INET; |
1156 |
} |
1157 |
#if HAVE_GETHOSTBYNAME |
1158 |
else if ((hp = gethostbyname(host)) != NULL) |
1159 |
{ |
1160 |
memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length); |
1161 |
sa.sin_family = hp->h_addrtype; |
1162 |
} |
1163 |
else |
1164 |
{ |
1165 |
errno = -h_errno; |
1166 |
RETURN_WITH(-1); |
1167 |
} |
1168 |
#else |
1169 |
else |
1170 |
{ |
1171 |
log("sockets: open_listener(): `%s' is not a valid IP address", host); |
1172 |
errno = EINVAL; |
1173 |
RETURN_WITH(-1); |
1174 |
} |
1175 |
#endif |
1176 |
} |
1177 |
else |
1178 |
{ /* !host */ |
1179 |
sa.sin_family = AF_INET; |
1180 |
sa.sin_addr.s_addr = INADDR_ANY; |
1181 |
} |
1182 |
sa.sin_port = htons((uint16) port); |
1183 |
|
1184 |
if ((fd = socket(sa.sin_family, SOCK_STREAM, 0)) < 0) |
1185 |
RETURN_WITH(-1); |
1186 |
|
1187 |
i = 1; |
1188 |
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) |
1189 |
{ |
1190 |
log_perror("sockets: open_listener(): setsockopt(%d, SO_REUSEADDR," |
1191 |
" 1) failed", fd); |
1192 |
} |
1193 |
|
1194 |
if (fcntl(fd, F_SETFL, O_NDELAY) < 0) |
1195 |
{ |
1196 |
int errno_save = errno; |
1197 |
close(fd); |
1198 |
errno = errno_save; |
1199 |
RETURN_WITH(-1); |
1200 |
} |
1201 |
|
1202 |
if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) |
1203 |
{ |
1204 |
int errno_save = errno; |
1205 |
close(fd); |
1206 |
errno = errno_save; |
1207 |
RETURN_WITH(-1); |
1208 |
} |
1209 |
|
1210 |
if (listen(fd, backlog) < 0) |
1211 |
{ |
1212 |
int errno_save = errno; |
1213 |
close(fd); |
1214 |
errno = errno_save; |
1215 |
RETURN_WITH(-1); |
1216 |
} |
1217 |
|
1218 |
if (max_fd < fd + 1) |
1219 |
{ |
1220 |
int j; |
1221 |
void *new_sockets = realloc(sockets, (fd + 1) * sizeof(*sockets)); |
1222 |
if (!new_sockets) |
1223 |
{ |
1224 |
int errno_save = errno; |
1225 |
close(fd); |
1226 |
errno = errno_save; |
1227 |
RETURN_WITH(-1); |
1228 |
} |
1229 |
sockets = new_sockets; |
1230 |
for (j = max_fd; j < fd; j++) |
1231 |
sockets[j] = NULL; |
1232 |
max_fd = fd + 1; |
1233 |
} |
1234 |
sockets[fd] = s; |
1235 |
if (!(s->flags & SF_MUTE)) |
1236 |
FD_SET(fd, &sock_fds); |
1237 |
s->fd = fd; |
1238 |
s->flags |= SF_LISTENER; |
1239 |
|
1240 |
/* Listener sockets don't need read/write buffers */ |
1241 |
free(s->rbuf); |
1242 |
free(s->wbuf); |
1243 |
s->rbuf = s->rptr = s->rend = s->rtop = NULL; |
1244 |
s->wbuf = s->wptr = s->wend = s->wtop = NULL; |
1245 |
|
1246 |
RETURN_WITH(0); |
1247 |
} |
1248 |
|
1249 |
/*************************************************************************/ |
1250 |
|
1251 |
/* Close a listener socket. */ |
1252 |
|
1253 |
int |
1254 |
close_listener(Socket * s) |
1255 |
{ |
1256 |
ENTER_WITH("%d", "%p", s); |
1257 |
if (s == NULL || !(s->flags & SF_LISTENER)) |
1258 |
{ |
1259 |
if (s) |
1260 |
log("sockets: close_listener() with non-listener socket (%d)!", s->fd); |
1261 |
else |
1262 |
log("sockets: close_listener() with NULL socket!"); |
1263 |
errno = EINVAL; |
1264 |
RETURN_WITH(-1); |
1265 |
} |
1266 |
sock_closefd(s); |
1267 |
s->flags &= ~SF_LISTENER; |
1268 |
RETURN_WITH(0); |
1269 |
} |
1270 |
|
1271 |
/*************************************************************************/ |
1272 |
|
1273 |
/* Read raw data from a socket, like read(). Returns number of bytes read, |
1274 |
* or -1 on error. Only reads from the buffer (does not attempt to fetch |
1275 |
* more data from the connection). |
1276 |
*/ |
1277 |
|
1278 |
int32 |
1279 |
sread(Socket * s, char *buf, int32 len) |
1280 |
{ |
1281 |
int32 nread = 0; |
1282 |
|
1283 |
ENTER_WITH("%d", "%p,%p,%d", s, buf, len); |
1284 |
if (!s || !buf || len <= 0) |
1285 |
{ |
1286 |
log("sockets: sread() with %s!", |
1287 |
!s ? "NULL socket" : !buf ? "NULL buffer" : "len <= 0"); |
1288 |
errno = EINVAL; |
1289 |
RETURN_WITH(-1); |
1290 |
} |
1291 |
if (s->rend < s->rptr) |
1292 |
{ |
1293 |
/* Buffer data wraps around */ |
1294 |
if (s->rptr + len <= s->rtop) |
1295 |
{ |
1296 |
/* Only need to read from end of buffer */ |
1297 |
memcpy(buf, s->rptr, len); |
1298 |
s->rptr += len; |
1299 |
if (s->rptr >= s->rtop) |
1300 |
s->rptr -= read_buffer_size(s); |
1301 |
RETURN_WITH(len); |
1302 |
} |
1303 |
else |
1304 |
{ |
1305 |
/* Need to read from both end and beginning */ |
1306 |
nread = s->rtop - s->rptr; |
1307 |
memcpy(buf, s->rptr, nread); |
1308 |
s->rptr = s->rbuf; |
1309 |
len -= nread; |
1310 |
/* Continue below */ |
1311 |
} |
1312 |
} |
1313 |
/* Read data from beginning of buffer */ |
1314 |
if (s->rptr < s->rend) |
1315 |
{ |
1316 |
if (len > s->rend - s->rptr) |
1317 |
len = s->rend - s->rptr; |
1318 |
memcpy(buf + nread, s->rptr, len); |
1319 |
s->rptr += len; |
1320 |
nread += len; |
1321 |
} |
1322 |
/* Return number of bytes read */ |
1323 |
RETURN_WITH(nread); |
1324 |
} |
1325 |
|
1326 |
/*************************************************************************/ |
1327 |
|
1328 |
/* Write raw data to a socket, like write(). Returns number of bytes |
1329 |
* written, or -1 on error. |
1330 |
*/ |
1331 |
|
1332 |
int32 |
1333 |
swrite(Socket * s, const char *buf, int32 len) |
1334 |
{ |
1335 |
ENTER_WITH("%d", "%p,%p,%d", s, buf, len); |
1336 |
if (!s || !buf || len < 0) |
1337 |
{ |
1338 |
log("sockets: swrite() with %s!", |
1339 |
!s ? "NULL socket" : !buf ? "NULL buffer" : "len < 0"); |
1340 |
errno = EINVAL; |
1341 |
RETURN_WITH(-1); |
1342 |
} |
1343 |
RETURN_WITH(buffered_write(s, buf, len)); |
1344 |
} |
1345 |
|
1346 |
/*************************************************************************/ |
1347 |
|
1348 |
/* Write data from an mmap()ed area to a socket. The area will be |
1349 |
* automatically unmapped when the write completes. |
1350 |
*/ |
1351 |
|
1352 |
int32 |
1353 |
swritemap(Socket * s, const char *buf, int32 len) |
1354 |
{ |
1355 |
#ifndef DISABLE_SWRITEMAP |
1356 |
struct wmapinfo *info; |
1357 |
|
1358 |
ENTER_WITH("%d", "%p,%p,%d", s, buf, len); |
1359 |
if (!s || !buf || len < 0) |
1360 |
{ |
1361 |
log("sockets: swritemap() with %s!", |
1362 |
!s ? "NULL socket" : !buf ? "NULL buffer" : "len < 0"); |
1363 |
errno = EINVAL; |
1364 |
RETURN_WITH(-1); |
1365 |
} |
1366 |
if (!len) |
1367 |
RETURN_WITH(0); |
1368 |
info = malloc(sizeof(*info)); |
1369 |
if (!info) |
1370 |
RETURN_WITH(-1); |
1371 |
info->next = NULL; |
1372 |
info->wait = write_buffer_len(s); |
1373 |
info->map = buf; |
1374 |
info->maplen = len; |
1375 |
info->pos = 0; |
1376 |
if (s->writemap) |
1377 |
{ |
1378 |
if (!s->writemap_tail) |
1379 |
{ |
1380 |
log("sockets: BUG: socket %d: writemap non-NULL but writemap_tail" |
1381 |
" NULL", s->fd); |
1382 |
s->writemap_tail = s->writemap; |
1383 |
while (s->writemap_tail->next) |
1384 |
s->writemap_tail = s->writemap_tail->next; |
1385 |
} |
1386 |
s->writemap_tail->next = info; |
1387 |
} |
1388 |
else |
1389 |
{ |
1390 |
if (s->writemap_tail) |
1391 |
{ |
1392 |
log("sockets: BUG: socket %d: writemap NULL but writemap_tail" |
1393 |
" non-NULL", s->fd); |
1394 |
} |
1395 |
s->writemap = info; |
1396 |
} |
1397 |
s->writemap_tail = info; |
1398 |
flush_write_buffer(s); /* to ensure FD is set in write_fds if needed */ |
1399 |
RETURN_WITH(len); |
1400 |
#else /* DISABLE_SWRITEMAP */ |
1401 |
errno = ENOSYS; |
1402 |
RETURN_WITH(-1); |
1403 |
#endif |
1404 |
} |
1405 |
|
1406 |
/*************************************************************************/ |
1407 |
|
1408 |
/* Add a write trigger to the socket. The `data' parameter will be passed |
1409 |
* to the callback when the trigger is reached. Returns 0 on success, -1 |
1410 |
* on failure. |
1411 |
*/ |
1412 |
|
1413 |
int |
1414 |
swrite_trigger(Socket * s, void *data) |
1415 |
{ |
1416 |
struct wmapinfo *info; |
1417 |
|
1418 |
ENTER_WITH("%d", "%p,%p", s, data); |
1419 |
if (!s) |
1420 |
{ |
1421 |
log("sockets: swrite_trigger() with NULL socket!"); |
1422 |
errno = EINVAL; |
1423 |
RETURN_WITH(-1); |
1424 |
} |
1425 |
info = malloc(sizeof(*info)); |
1426 |
if (!info) |
1427 |
RETURN_WITH(-1); |
1428 |
info->next = NULL; |
1429 |
info->wait = write_buffer_len(s); |
1430 |
info->map = (const char *) data; |
1431 |
info->maplen = 0; |
1432 |
info->pos = 0; |
1433 |
if (s->writemap) |
1434 |
{ |
1435 |
if (!s->writemap_tail) |
1436 |
{ |
1437 |
log("sockets: BUG: socket %d: writemap non-NULL but writemap_tail" |
1438 |
" NULL", s->fd); |
1439 |
s->writemap_tail = s->writemap; |
1440 |
while (s->writemap_tail->next) |
1441 |
s->writemap_tail = s->writemap_tail->next; |
1442 |
} |
1443 |
s->writemap_tail->next = info; |
1444 |
} |
1445 |
else |
1446 |
{ |
1447 |
if (s->writemap_tail) |
1448 |
{ |
1449 |
log("sockets: BUG: socket %d: writemap NULL but writemap_tail" |
1450 |
" non-NULL", s->fd); |
1451 |
} |
1452 |
s->writemap = info; |
1453 |
} |
1454 |
s->writemap_tail = info; |
1455 |
flush_write_buffer(s); /* to ensure FD is set in write_fds if needed */ |
1456 |
RETURN_WITH(0); |
1457 |
} |
1458 |
|
1459 |
/*************************************************************************/ |
1460 |
/*************************************************************************/ |
1461 |
|
1462 |
/* Read a character from a socket, like fgetc(). Returns EOF if no data |
1463 |
* is available in the socket buffer. Assumes the socket is valid. |
1464 |
*/ |
1465 |
|
1466 |
int |
1467 |
sgetc(Socket * s) |
1468 |
{ |
1469 |
int c; |
1470 |
|
1471 |
ENTER_WITH("%d", "%p", s); |
1472 |
/* No paranoia check here, to save time */ |
1473 |
if (s->rptr == s->rend) |
1474 |
RETURN_WITH(EOF); |
1475 |
c = *s->rptr++; |
1476 |
if (s->rptr >= s->rtop) |
1477 |
s->rptr -= read_buffer_size(s); |
1478 |
RETURN_WITH(c); |
1479 |
} |
1480 |
|
1481 |
/*************************************************************************/ |
1482 |
|
1483 |
/* Read a line from a socket, like fgets(). If not enough buffered data |
1484 |
* is available to fill a complete line, or another error occurs, returns |
1485 |
* NULL. |
1486 |
*/ |
1487 |
|
1488 |
char * |
1489 |
sgets(char *buf, int32 len, Socket * s) |
1490 |
{ |
1491 |
char *ptr = s->rptr, *eol; |
1492 |
int32 to_top = s->rtop - ptr; /* used for efficiency */ |
1493 |
|
1494 |
ENTER_WITH("%p", "%p,%d,%p", buf, len, s); |
1495 |
if (!s || !buf || len <= 0) |
1496 |
{ |
1497 |
log("sockets: sgets[2]() with %s!", |
1498 |
!s ? "NULL socket" : !buf ? "NULL buffer" : "len <= 0"); |
1499 |
RETURN_WITH(NULL); |
1500 |
} |
1501 |
|
1502 |
/* Find end of line */ |
1503 |
if (s->rend > s->rptr) |
1504 |
{ |
1505 |
eol = memchr(s->rptr, '\n', s->rend - s->rptr); |
1506 |
} |
1507 |
else |
1508 |
{ |
1509 |
eol = memchr(s->rptr, '\n', to_top); |
1510 |
if (!eol) |
1511 |
eol = memchr(s->rbuf, '\n', s->rend - s->rbuf); |
1512 |
} |
1513 |
if (!eol) |
1514 |
RETURN_WITH(NULL); |
1515 |
eol++; /* Point 1 byte after \n */ |
1516 |
|
1517 |
/* Set rptr now; old value is in ptr variable */ |
1518 |
s->rptr = eol; |
1519 |
if (s->rptr >= s->rtop) /* >rtop is impossible, but just in case */ |
1520 |
s->rptr = s->rbuf; |
1521 |
|
1522 |
/* Note: The greatest possible value for eol is s->rend, so as long as |
1523 |
* we ensure that rend doesn't wrap around and reach rptr (i.e. always |
1524 |
* leave at least 1 byte in the buffer unused), we can never have |
1525 |
* eol == ptr here. */ |
1526 |
|
1527 |
/* Trim eol to <len bytes */ |
1528 |
if (eol > ptr) |
1529 |
{ |
1530 |
if (eol - ptr >= len) |
1531 |
eol = ptr + len - 1; |
1532 |
} |
1533 |
else |
1534 |
{ |
1535 |
if (to_top >= len - 1) /* we don't mind eol == rtop */ |
1536 |
eol = ptr + len - 1; |
1537 |
else if (to_top + (eol - s->rbuf) >= len) |
1538 |
eol = s->rbuf + (len - 1 - to_top); |
1539 |
} |
1540 |
|
1541 |
/* Actually copy to buffer and return */ |
1542 |
if (eol > ptr) |
1543 |
{ |
1544 |
memcpy(buf, ptr, eol - ptr); |
1545 |
buf[eol - ptr] = 0; |
1546 |
} |
1547 |
else |
1548 |
{ |
1549 |
memcpy(buf, ptr, to_top); |
1550 |
memcpy(buf + to_top, s->rbuf, eol - s->rbuf); |
1551 |
buf[to_top + (eol - s->rbuf)] = 0; |
1552 |
} |
1553 |
RETURN_WITH(buf); |
1554 |
} |
1555 |
|
1556 |
/*************************************************************************/ |
1557 |
|
1558 |
/* Reads a line of text from a socket, and strips newline and carriage |
1559 |
* return characters from the end of the line. |
1560 |
*/ |
1561 |
|
1562 |
char * |
1563 |
sgets2(char *buf, int32 len, Socket * s) |
1564 |
{ |
1565 |
char *str; |
1566 |
|
1567 |
ENTER_WITH("%p", "%p,%d,%p", buf, len, s); |
1568 |
str = sgets(buf, len, s); |
1569 |
if (!str) |
1570 |
RETURN_WITH(str); |
1571 |
str = buf + strlen(buf) - 1; |
1572 |
if (*str == '\n') |
1573 |
*str-- = 0; |
1574 |
if (*str == '\r') |
1575 |
*str = 0; |
1576 |
RETURN_WITH(buf); |
1577 |
} |
1578 |
|
1579 |
/*************************************************************************/ |
1580 |
|
1581 |
/* Write a string to a socket, like fputs(). Returns the number of bytes |
1582 |
* written. |
1583 |
*/ |
1584 |
|
1585 |
int |
1586 |
sputs(const char *str, Socket * s) |
1587 |
{ |
1588 |
ENTER_WITH("%d", "[%s],%p", str ? str : "(null)", s); |
1589 |
if (!str || !s) |
1590 |
{ |
1591 |
log("sockets: sputs() with %s!", !s ? "NULL socket" : "NULL string"); |
1592 |
errno = EINVAL; |
1593 |
RETURN_WITH(-1); |
1594 |
} |
1595 |
RETURN_WITH(buffered_write(s, str, strlen(str))); |
1596 |
} |
1597 |
|
1598 |
/*************************************************************************/ |
1599 |
|
1600 |
/* Write to a socket a la [v]printf(). Returns the number of bytes written; |
1601 |
* in no case will more than 65535 bytes be written (if the output would be |
1602 |
* be longer than this, it will be truncated). |
1603 |
*/ |
1604 |
|
1605 |
int |
1606 |
sockprintf(Socket * s, const char *fmt, ...) |
1607 |
{ |
1608 |
va_list args; |
1609 |
int ret; |
1610 |
|
1611 |
ENTER_WITH("%d", "%p,[%s],...", s, fmt ? fmt : "(null)"); |
1612 |
va_start(args, fmt); |
1613 |
ret = vsockprintf(s, fmt, args); |
1614 |
va_end(args); |
1615 |
RETURN_WITH(ret); |
1616 |
} |
1617 |
|
1618 |
int |
1619 |
vsockprintf(Socket * s, const char *fmt, va_list args) |
1620 |
{ |
1621 |
char buf[65536]; |
1622 |
|
1623 |
ENTER_WITH("%d", "%p,[%s],%p", s, fmt ? fmt : "(null)", args); |
1624 |
if (!s || !fmt) |
1625 |
{ |
1626 |
log("sockets: [v]sockprintf() with %s!", |
1627 |
!s ? "NULL socket" : "NULL format string"); |
1628 |
errno = EINVAL; |
1629 |
RETURN_WITH(-1); |
1630 |
} |
1631 |
RETURN_WITH(buffered_write(s, buf, vsnprintf(buf, sizeof(buf), fmt, args))); |
1632 |
} |
1633 |
|
1634 |
/*************************************************************************/ |
1635 |
/************************** Internal routines ****************************/ |
1636 |
/*************************************************************************/ |
1637 |
|
1638 |
/* Call a callback on the given socket. Handles setting and clearing |
1639 |
* SF_CALLBACK, and checking for remote disconnects after the callback |
1640 |
* completes. Returns 0 if the socket has been disconnected, 1 otherwise. |
1641 |
* If 0 is returned, the socket should be considered no longer valid; if it |
1642 |
* was an automatically-created socket, the socket itself will have been |
1643 |
* freed. |
1644 |
* |
1645 |
* `s' or `cb' may be NULL, in which case this routine does nothing and |
1646 |
* returns 1. |
1647 |
*/ |
1648 |
|
1649 |
static int |
1650 |
do_callback(Socket * s, SocketCallback cb, void *param) |
1651 |
{ |
1652 |
ENTER_WITH("%d", "%p,%p,%p", s, cb, param); |
1653 |
if (s && cb) |
1654 |
{ |
1655 |
/* Check whether the SF_CALLBACK flag was set; if it is, then this |
1656 |
* is a nested callback, so don't clear the flag or disconnect the |
1657 |
* socket afterwards. */ |
1658 |
int was_set = s->flags & SF_CALLBACK; |
1659 |
|
1660 |
if (!was_set) |
1661 |
s->flags |= SF_CALLBACK; |
1662 |
log_debug(3, "sockets: callback(%d,%p,%p)", s->fd, cb, param); |
1663 |
cb(s, param); |
1664 |
log_debug(3, "sockets: ...cbret(%d,%p,%p)", s->fd, cb, param); |
1665 |
if (!was_set) |
1666 |
{ |
1667 |
s->flags &= ~SF_CALLBACK; |
1668 |
if (s->flags & SF_BROKEN) |
1669 |
{ |
1670 |
log_debug(3, "sockets: ...broken"); |
1671 |
do_disconn(s, DISCONN_REMOTE_RESUME); |
1672 |
RETURN_WITH(0); |
1673 |
} |
1674 |
if (s->flags & SF_DELETEME) |
1675 |
{ |
1676 |
log_debug(3, "sockets: ...deleteme"); |
1677 |
sock_free(s); |
1678 |
RETURN_WITH(0); |
1679 |
} |
1680 |
if ((s->flags & SF_DISCONNECT) || s->fd < 0) |
1681 |
{ |
1682 |
log_debug(3, "sockets: ...disconnect"); |
1683 |
RETURN_WITH(0); |
1684 |
} |
1685 |
log_debug(3, "sockets: ...normalret"); |
1686 |
} |
1687 |
else |
1688 |
{ /* SF_CALLBACK was already set */ |
1689 |
log_debug(3, "sockets: ...nestedret"); |
1690 |
} |
1691 |
} |
1692 |
RETURN_WITH(1); |
1693 |
} |
1694 |
|
1695 |
/*************************************************************************/ |
1696 |
|
1697 |
/* Accept a connection on the given socket. Called from check_sockets(). */ |
1698 |
|
1699 |
static void |
1700 |
do_accept(Socket * s) |
1701 |
{ |
1702 |
int i; |
1703 |
struct sockaddr_in sin; |
1704 |
socklen_t sin_len = sizeof(sin); |
1705 |
int newfd; |
1706 |
|
1707 |
ENTER("%p", s); |
1708 |
newfd = accept(s->fd, (struct sockaddr *) &sin, &sin_len); |
1709 |
if (newfd < 0) |
1710 |
{ |
1711 |
if (errno != ECONNRESET) |
1712 |
log_perror("sockets: accept(%d)", s->fd); |
1713 |
} |
1714 |
else if (!s->cb_accept) |
1715 |
{ |
1716 |
/* No accept callback, so just throw the connection away */ |
1717 |
close(newfd); |
1718 |
} |
1719 |
else if (fcntl(newfd, F_SETFL, O_NDELAY) < 0) |
1720 |
{ |
1721 |
log_perror("sockets: fcntl(NDELAY) on accept(%d)", s->fd); |
1722 |
close(newfd); |
1723 |
} |
1724 |
else |
1725 |
{ |
1726 |
Socket *news = sock_new(); |
1727 |
if (!news) |
1728 |
{ |
1729 |
log("sockets: accept(%d): Unable to create socket structure" |
1730 |
" (out of buffer space?)", s->fd); |
1731 |
} |
1732 |
else |
1733 |
{ |
1734 |
news->fd = newfd; |
1735 |
news->flags |= SF_SELFCREATED | SF_CONNECTED; |
1736 |
memcpy(&news->remote, &sin, sin_len); |
1737 |
for (i = newfd; i < max_fd; i++); |
1738 |
if (max_fd < newfd + 1) |
1739 |
{ |
1740 |
void *new_sockets = realloc(sockets, (newfd + 1) * sizeof(*sockets)); |
1741 |
if (!new_sockets) |
1742 |
{ |
1743 |
sock_free(news); |
1744 |
close(newfd); |
1745 |
RETURN; |
1746 |
} |
1747 |
sockets = new_sockets; |
1748 |
for (i = max_fd; i < newfd; i++) |
1749 |
sockets[i] = NULL; |
1750 |
max_fd = newfd + 1; |
1751 |
} |
1752 |
sockets[newfd] = news; |
1753 |
if (!(news->flags & SF_MUTE)) |
1754 |
FD_SET(newfd, &sock_fds); |
1755 |
do_callback(s, s->cb_accept, news); |
1756 |
} |
1757 |
} |
1758 |
RETURN; |
1759 |
} |
1760 |
|
1761 |
/*************************************************************************/ |
1762 |
|
1763 |
/* Fill up the read buffer of a socket with any data that may have arrived. |
1764 |
* Returns the number of bytes read (nonzero), or -1 on error; errno is set |
1765 |
* by recv() calls but is otherwise preserved. |
1766 |
*/ |
1767 |
|
1768 |
static int |
1769 |
fill_read_buffer(Socket * s) |
1770 |
{ |
1771 |
int nread = 0; |
1772 |
int errno_save = errno; |
1773 |
|
1774 |
ENTER_WITH("%d", "%p", s); |
1775 |
if (s->fd < 0) |
1776 |
{ |
1777 |
errno = EBADF; |
1778 |
RETURN_WITH(-1); |
1779 |
} |
1780 |
for (;;) |
1781 |
{ |
1782 |
int maxread, res; |
1783 |
if (read_buffer_len(s) >= read_buffer_size(s) - 1) |
1784 |
{ |
1785 |
/* Read buffer is full; try to expand the buffer. */ |
1786 |
int over; |
1787 |
int32 more = resize_how_much(s, read_buffer_size(s), &over); |
1788 |
if (more) |
1789 |
{ |
1790 |
if (!resize_rbuf(s, read_buffer_size(s) + more)) |
1791 |
{ |
1792 |
log("sockets: attempt to expand socket %d read buffer" |
1793 |
" failed (out of memory?)", s->fd); |
1794 |
errno_save = EAGAIN; |
1795 |
if (nread == 0) |
1796 |
nread = -1; |
1797 |
break; |
1798 |
} |
1799 |
} |
1800 |
else |
1801 |
{ |
1802 |
#ifdef WARN_ON_BUFSIZE |
1803 |
if (!(s->flags & SF_WARNED)) |
1804 |
{ |
1805 |
log("sockets: socket %d exceeded %s buffer size limit" |
1806 |
" (%d)", s->fd, over ? "per-connection" : "total", |
1807 |
over ? bufsize_limit : total_bufsize_limit); |
1808 |
s->flags |= SF_WARNED; |
1809 |
} |
1810 |
#endif |
1811 |
errno_save = EAGAIN; |
1812 |
if (nread == 0) |
1813 |
nread = -1; |
1814 |
break; |
1815 |
} |
1816 |
} |
1817 |
if (s->rend < s->rptr) /* wrapped around? */ |
1818 |
maxread = (s->rptr - 1) - s->rend; |
1819 |
else if (s->rptr == s->rbuf) |
1820 |
maxread = s->rtop - s->rend - 1; |
1821 |
else |
1822 |
maxread = s->rtop - s->rend; |
1823 |
do |
1824 |
{ |
1825 |
errno = 0; |
1826 |
res = recv(s->fd, s->rend, maxread, 0); |
1827 |
if (res <= 0 && errno == 0) |
1828 |
errno = ECONNRESET; /* make a guess */ |
1829 |
} |
1830 |
while (res <= 0 && errno == EINTR); |
1831 |
errno_save = errno; |
1832 |
log_debug(3, "sockets: fill_read_buffer wanted %d, got %d", |
1833 |
maxread, nread); |
1834 |
if (res <= 0) |
1835 |
{ |
1836 |
if (nread == 0) |
1837 |
nread = -1; |
1838 |
break; |
1839 |
} |
1840 |
nread += res; |
1841 |
s->total_read += res; |
1842 |
s->rend += res; |
1843 |
if (s->rend == s->rtop) |
1844 |
s->rend = s->rbuf; |
1845 |
} |
1846 |
if (nread == 0) |
1847 |
{ |
1848 |
nread = -1; |
1849 |
errno = ENOBUFS; |
1850 |
} |
1851 |
else |
1852 |
{ |
1853 |
errno = errno_save; |
1854 |
} |
1855 |
RETURN_WITH(nread); |
1856 |
} |
1857 |
|
1858 |
/*************************************************************************/ |
1859 |
|
1860 |
/* Try and write up to one chunk of data from the buffer to the socket. |
1861 |
* Return -1 on error, -2 if a socket waiting for disconnection was closed, |
1862 |
* or else how many bytes were written. (Note that a return value of zero |
1863 |
* should not be considered an error.) |
1864 |
*/ |
1865 |
|
1866 |
static int |
1867 |
flush_write_buffer(Socket * s) |
1868 |
{ |
1869 |
int32 maxwrite, nwritten; |
1870 |
|
1871 |
ENTER_WITH("%d", "%p", s); |
1872 |
if (s->fd < 0) |
1873 |
{ |
1874 |
errno = EBADF; |
1875 |
RETURN_WITH(-1); |
1876 |
} |
1877 |
if (!sock_isconn(s)) /* not yet connected */ |
1878 |
RETURN_WITH(0); |
1879 |
if (s->flags & SF_WTRIGGER) |
1880 |
{ |
1881 |
/* Write trigger is still pending on this socket, don't do anything */ |
1882 |
nwritten = 0; |
1883 |
goto check_result; |
1884 |
} |
1885 |
/* "while" instead of "if" to handle the case of: |
1886 |
* s->writemap->wait == 0 |
1887 |
* s->writemap->pos == s->writemap->maplen (should be impossible here) |
1888 |
* s->writemap->next->wait == 0 |
1889 |
*/ |
1890 |
while (s->writemap && s->writemap->wait <= 0) |
1891 |
{ |
1892 |
/* Write data from the map instead of the buffer */ |
1893 |
if (!s->writemap->maplen) |
1894 |
{ |
1895 |
/* It's a trigger, stop here. We don't skip to the next wmap |
1896 |
* yet in case we're called again before the trigger callback |
1897 |
* is called. */ |
1898 |
s->flags |= SF_WTRIGGER; |
1899 |
nwritten = 0; /* return value */ |
1900 |
goto check_result; |
1901 |
} |
1902 |
maxwrite = s->writemap->maplen - s->writemap->pos; |
1903 |
if (maxwrite > 0) |
1904 |
{ |
1905 |
nwritten = send(s->fd, s->writemap->map + s->writemap->pos, |
1906 |
maxwrite, 0); |
1907 |
log_debug(3, "sockets: flush_write_buffer/map wanted %d, got %d", |
1908 |
maxwrite, nwritten); |
1909 |
if (nwritten > 0) |
1910 |
{ |
1911 |
s->writemap->pos += nwritten; |
1912 |
if (s->writemap->pos >= s->writemap->maplen) |
1913 |
next_wmap(s); |
1914 |
} |
1915 |
goto check_result; |
1916 |
} |
1917 |
else |
1918 |
{ |
1919 |
next_wmap(s); |
1920 |
} |
1921 |
} |
1922 |
nwritten = 0; |
1923 |
if (s->wend != s->wptr) |
1924 |
{ |
1925 |
if (s->wptr > s->wend) /* wrapped around? */ |
1926 |
maxwrite = s->wtop - s->wptr; |
1927 |
else |
1928 |
maxwrite = s->wend - s->wptr; |
1929 |
if (s->writemap && maxwrite > s->writemap->wait) |
1930 |
{ |
1931 |
maxwrite = s->writemap->wait; |
1932 |
if (maxwrite <= 0) |
1933 |
{ |
1934 |
/* paranoia: this is impossible from the while() above */ |
1935 |
log("sockets: BUG: flush_write_buffer(%d): writemap wait 0" |
1936 |
" after while", s->fd); |
1937 |
do_disconn(s, DISCONN_REMOTE); |
1938 |
RETURN_WITH(-1); |
1939 |
} |
1940 |
} |
1941 |
nwritten = send(s->fd, s->wptr, maxwrite, 0); |
1942 |
log_debug(3, "sockets: flush_write_buffer wanted %d, got %d", |
1943 |
maxwrite, nwritten); |
1944 |
if (nwritten > 0) |
1945 |
{ |
1946 |
struct wmapinfo *info; |
1947 |
for (info = s->writemap; info; info = info->next) |
1948 |
{ |
1949 |
info->wait -= nwritten; |
1950 |
} |
1951 |
s->wptr += nwritten; |
1952 |
if (s->wptr >= s->wtop) |
1953 |
s->wptr = s->wbuf; |
1954 |
} |
1955 |
check_result: |
1956 |
if (nwritten < 0 && errno != EAGAIN && errno != EINTR) |
1957 |
{ |
1958 |
int errno_save = errno; |
1959 |
if (errno != ECONNRESET && errno != EPIPE) |
1960 |
log_perror("sockets: flush_write_buffer(%d)", s->fd); |
1961 |
do_disconn(s, DISCONN_REMOTE); |
1962 |
errno = errno_save; |
1963 |
RETURN_WITH(-1); |
1964 |
} |
1965 |
if (nwritten > 0) |
1966 |
{ |
1967 |
s->last_write_time = time(NULL); |
1968 |
s->flags &= ~SF_WARNED; |
1969 |
s->total_written += nwritten; |
1970 |
} |
1971 |
else |
1972 |
{ |
1973 |
nwritten = 0; /* return value */ |
1974 |
} |
1975 |
} |
1976 |
if ((s->flags & SF_CALLBACK) || MORE_TO_WRITE(s)) |
1977 |
{ |
1978 |
FD_SET(s->fd, &write_fds); |
1979 |
} |
1980 |
else |
1981 |
{ |
1982 |
FD_CLR(s->fd, &write_fds); |
1983 |
if (s->flags & SF_DISCONNECT) |
1984 |
{ |
1985 |
s->flags &= ~SF_DISCONNECT; |
1986 |
do_disconn(s, DISCONN_LOCAL_RESUME); |
1987 |
RETURN_WITH(-2); |
1988 |
} |
1989 |
else |
1990 |
{ |
1991 |
reclaim_buffer_space_one(s); |
1992 |
} |
1993 |
} |
1994 |
RETURN_WITH(nwritten); |
1995 |
} |
1996 |
|
1997 |
/*************************************************************************/ |
1998 |
|
1999 |
/* Return the amount by which a socket's read or write buffer should be |
2000 |
* expanded. Return zero if the per-connection or total network buffer |
2001 |
* size limits have been reached and the buffer cannot be expanded; in |
2002 |
* this case, set *errp to 1 if the per-connection buffer size limit was |
2003 |
* exceeded, 0 if the total buffer size limit was exceeded. |
2004 |
*/ |
2005 |
|
2006 |
static uint32 |
2007 |
resize_how_much(const Socket * s, uint32 current_size, int *errp) |
2008 |
{ |
2009 |
uint32 socktotal = read_buffer_size(s) + write_buffer_size(s); |
2010 |
int32 more; |
2011 |
int over = 0, over_total = 0; |
2012 |
|
2013 |
/* Check current size against limits */ |
2014 |
if (bufsize_limit && socktotal >= bufsize_limit) |
2015 |
over = 1; |
2016 |
if (total_bufsize_limit && total_bufsize >= total_bufsize_limit) |
2017 |
over_total = 1; |
2018 |
if (over || over_total) |
2019 |
{ |
2020 |
if (over) |
2021 |
*errp = 1; |
2022 |
else |
2023 |
*errp = 0; |
2024 |
return 0; |
2025 |
} |
2026 |
|
2027 |
/* Expand by 10%, rounded up to a multiple of SOCK_MIN_BUFSIZE */ |
2028 |
more = current_size / 10; |
2029 |
more = (more + SOCK_MIN_BUFSIZE - 1) / SOCK_MIN_BUFSIZE * SOCK_MIN_BUFSIZE; |
2030 |
|
2031 |
/* Make sure new size doesn't exceed limits */ |
2032 |
if (bufsize_limit && socktotal + more >= bufsize_limit) |
2033 |
more = bufsize_limit - socktotal; |
2034 |
if (total_bufsize_limit && total_bufsize + more >= total_bufsize_limit) |
2035 |
more = total_bufsize_limit - total_bufsize; |
2036 |
if (more < 0) |
2037 |
{ |
2038 |
more = 0; |
2039 |
/* This shouldn't be possible; assume it's the total buffer size */ |
2040 |
*errp = 0; |
2041 |
} |
2042 |
|
2043 |
/* Return it */ |
2044 |
return more; |
2045 |
} |
2046 |
|
2047 |
|
2048 |
/* Resize a socket's read or write buffer. */ |
2049 |
|
2050 |
static int |
2051 |
resize_rbuf(Socket * s, uint32 size) |
2052 |
{ |
2053 |
ENTER("%p,%u", s, size); |
2054 |
if (size <= read_buffer_len(s)) |
2055 |
{ |
2056 |
log("sockets: BUG: resize_rbuf(%d): size (%d) <= rlen (%d)" |
2057 |
" (cursize %ld)", s->fd, size, read_buffer_len(s), |
2058 |
(long) (s->rtop - s->rbuf)); |
2059 |
RETURN_WITH(0); |
2060 |
} |
2061 |
RETURN_WITH(resize_buf(&s->rbuf, &s->rptr, &s->rend, &s->rtop, size)); |
2062 |
} |
2063 |
|
2064 |
|
2065 |
static int |
2066 |
resize_wbuf(Socket * s, uint32 size) |
2067 |
{ |
2068 |
ENTER("%p,%u", s, size); |
2069 |
if (size <= write_buffer_len(s)) |
2070 |
{ |
2071 |
log("sockets: BUG: resize_wbuf(%d): size (%d) <= wlen (%d)" |
2072 |
" (cursize %ld)", s->fd, size, write_buffer_len(s), |
2073 |
(long) (s->wtop - s->wbuf)); |
2074 |
RETURN_WITH(0); |
2075 |
} |
2076 |
RETURN_WITH(resize_buf(&s->wbuf, &s->wptr, &s->wend, &s->wtop, size)); |
2077 |
} |
2078 |
|
2079 |
/* Routine that does the actual resizing. Assumes that newsize >= current |
2080 |
* size. Returns nonzero on success, zero on failure (out of memory). */ |
2081 |
static int |
2082 |
resize_buf(char **p_buf, char **p_ptr, char **p_end, char **p_top, |
2083 |
uint32 newsize) |
2084 |
{ |
2085 |
uint32 size = *p_top - *p_buf; |
2086 |
char *newbuf; |
2087 |
uint32 len = 0; |
2088 |
|
2089 |
ENTER_WITH("%d", "%p,%p,%p,%p,%u", p_buf, p_ptr, p_end, p_top, newsize); |
2090 |
if (newsize <= size) |
2091 |
RETURN_WITH(1); |
2092 |
newbuf = malloc(newsize); |
2093 |
if (!newbuf) |
2094 |
RETURN_WITH(0); |
2095 |
total_bufsize -= size; |
2096 |
total_bufsize += newsize; |
2097 |
/* Copy old data to new buffer, if any */ |
2098 |
if (*p_end < *p_ptr) |
2099 |
{ |
2100 |
len = *p_top - *p_ptr; |
2101 |
memcpy(newbuf, *p_ptr, len); |
2102 |
*p_ptr = *p_buf; |
2103 |
} |
2104 |
if (*p_end > *p_ptr) |
2105 |
{ |
2106 |
memcpy(newbuf + len, *p_ptr, *p_end - *p_ptr); |
2107 |
len += *p_end - *p_ptr; |
2108 |
} |
2109 |
free(*p_buf); |
2110 |
*p_buf = newbuf; |
2111 |
*p_ptr = newbuf; |
2112 |
*p_end = newbuf + len; |
2113 |
*p_top = newbuf + newsize; |
2114 |
RETURN_WITH(1); |
2115 |
} |
2116 |
|
2117 |
/*************************************************************************/ |
2118 |
|
2119 |
/* Try to reclaim unused buffer space. Return 1 if some buffer space was |
2120 |
* freed, 0 if not. |
2121 |
*/ |
2122 |
|
2123 |
static int |
2124 |
reclaim_buffer_space_one(Socket * s) |
2125 |
{ |
2126 |
uint32 rlen = read_buffer_len(s), wlen = write_buffer_len(s); |
2127 |
int retval = 0; |
2128 |
|
2129 |
ENTER_WITH("%d", "%p", s); |
2130 |
if (read_buffer_size(s) > SOCK_MIN_BUFSIZE |
2131 |
&& rlen < read_buffer_size(s) - SOCK_MIN_BUFSIZE) |
2132 |
{ |
2133 |
if (rlen < SOCK_MIN_BUFSIZE) |
2134 |
{ |
2135 |
rlen = SOCK_MIN_BUFSIZE; |
2136 |
} |
2137 |
else |
2138 |
{ |
2139 |
/* Round up to the next multiple of SOCK_MIN_BUFSIZE, leaving |
2140 |
* at least one byte available */ |
2141 |
rlen += SOCK_MIN_BUFSIZE; |
2142 |
rlen /= SOCK_MIN_BUFSIZE; |
2143 |
rlen *= SOCK_MIN_BUFSIZE; |
2144 |
} |
2145 |
resize_rbuf(s, rlen); |
2146 |
retval = 1; |
2147 |
} |
2148 |
if (write_buffer_size(s) > SOCK_MIN_BUFSIZE |
2149 |
&& wlen < write_buffer_size(s) - SOCK_MIN_BUFSIZE) |
2150 |
{ |
2151 |
if (wlen < SOCK_MIN_BUFSIZE) |
2152 |
{ |
2153 |
wlen = SOCK_MIN_BUFSIZE; |
2154 |
} |
2155 |
else |
2156 |
{ |
2157 |
wlen += SOCK_MIN_BUFSIZE; |
2158 |
wlen /= SOCK_MIN_BUFSIZE; |
2159 |
wlen *= SOCK_MIN_BUFSIZE; |
2160 |
} |
2161 |
resize_wbuf(s, wlen); |
2162 |
retval = 1; |
2163 |
} |
2164 |
RETURN_WITH(retval); |
2165 |
} |
2166 |
|
2167 |
|
2168 |
static int |
2169 |
reclaim_buffer_space(void) |
2170 |
{ |
2171 |
Socket *s; |
2172 |
int retval = 0; |
2173 |
|
2174 |
ENTER_WITH("%d", ""); |
2175 |
LIST_FOREACH(s, allsockets) |
2176 |
{ |
2177 |
retval |= reclaim_buffer_space_one(s); |
2178 |
} |
2179 |
RETURN_WITH(retval); |
2180 |
} |
2181 |
|
2182 |
/*************************************************************************/ |
2183 |
|
2184 |
/* Free the first map on the socket's writemap list. */ |
2185 |
|
2186 |
static void |
2187 |
next_wmap(Socket * s) |
2188 |
{ |
2189 |
struct wmapinfo *next; |
2190 |
|
2191 |
ENTER("%p", s); |
2192 |
if (!s || !s->writemap) |
2193 |
{ |
2194 |
log("sockets: BUG: next_wmap() with NULL %s!", s ? "writemap" : "socket"); |
2195 |
RETURN; |
2196 |
} |
2197 |
if (s->writemap->map) |
2198 |
{ |
2199 |
#ifdef DISABLE_SWRITEMAP |
2200 |
log("sockets: BUG: DISABLE_SWRITEMAP but non-NULL map %p for socket" |
2201 |
" %p", s->writemap->map, s); |
2202 |
#else |
2203 |
munmap((void *) s->writemap->map, s->writemap->maplen); |
2204 |
#endif |
2205 |
} |
2206 |
next = s->writemap->next; |
2207 |
free(s->writemap); |
2208 |
if (next) |
2209 |
{ |
2210 |
s->writemap = next; |
2211 |
} |
2212 |
else |
2213 |
{ |
2214 |
s->writemap = s->writemap_tail = NULL; |
2215 |
} |
2216 |
RETURN; |
2217 |
} |
2218 |
|
2219 |
/*************************************************************************/ |
2220 |
|
2221 |
/* Write data to a socket with buffering. */ |
2222 |
|
2223 |
static int |
2224 |
buffered_write(Socket * s, const char *buf, int len) |
2225 |
{ |
2226 |
int nwritten, left = len; |
2227 |
int errno_save = errno; |
2228 |
|
2229 |
ENTER_WITH("%d", "%p,%p,%d", s, buf, len); |
2230 |
if (s->fd < 0) |
2231 |
{ |
2232 |
errno = EBADF; |
2233 |
RETURN_WITH(-1); |
2234 |
} |
2235 |
|
2236 |
/* Reset the write timeout if the buffer is currently empty and we're |
2237 |
* not busy writing a mapped buffer. */ |
2238 |
if (!MORE_TO_WRITE(s)) |
2239 |
s->last_write_time = time(NULL); |
2240 |
|
2241 |
while (left > 0) |
2242 |
{ |
2243 |
|
2244 |
/* Fill up to the current buffer size. */ |
2245 |
if (write_buffer_len(s) < write_buffer_size(s) - 1) |
2246 |
{ |
2247 |
int maxwrite; |
2248 |
/* If buffer is empty, reset pointers to beginning for efficiency */ |
2249 |
if (write_buffer_len(s) == 0) |
2250 |
s->wptr = s->wend = s->wbuf; |
2251 |
if (s->wptr == s->wbuf) |
2252 |
{ |
2253 |
/* Buffer not wrapped */ |
2254 |
maxwrite = s->wtop - s->wend - 1; |
2255 |
} |
2256 |
else |
2257 |
{ |
2258 |
/* Buffer is wrapped. If this write would reach to or past |
2259 |
* the end of the buffer, write it first and reset the end |
2260 |
* pointer to the beginning of the buffer. */ |
2261 |
if (s->wend + left >= s->wtop && s->wptr <= s->wend) |
2262 |
{ |
2263 |
nwritten = s->wtop - s->wend; |
2264 |
memcpy(s->wend, buf, nwritten); |
2265 |
buf += nwritten; |
2266 |
left -= nwritten; |
2267 |
s->wend = s->wbuf; |
2268 |
} |
2269 |
/* Now we can copy a single chunk to wend. */ |
2270 |
if (s->wptr > s->wend) |
2271 |
maxwrite = s->wptr - s->wend - 1; |
2272 |
else |
2273 |
maxwrite = left; /* guaranteed to fit from above code */ |
2274 |
} |
2275 |
if (left > maxwrite) |
2276 |
nwritten = maxwrite; |
2277 |
else |
2278 |
nwritten = left; |
2279 |
if (nwritten) |
2280 |
{ |
2281 |
memcpy(s->wend, buf, nwritten); |
2282 |
buf += nwritten; |
2283 |
left -= nwritten; |
2284 |
s->wend += nwritten; |
2285 |
} |
2286 |
} |
2287 |
|
2288 |
/* Now write to the socket as much as we can. */ |
2289 |
if (flush_write_buffer(s) < 0) |
2290 |
RETURN_WITH(len - left); /* socket is not valid any more */ |
2291 |
errno_save = errno; |
2292 |
if (write_buffer_len(s) >= write_buffer_size(s) - 1) |
2293 |
{ |
2294 |
/* Write failed on full buffer; try to expand the buffer. */ |
2295 |
int over; |
2296 |
uint32 more = resize_how_much(s, write_buffer_size(s), &over); |
2297 |
if (more) |
2298 |
{ |
2299 |
if (!resize_wbuf(s, write_buffer_size(s) + more)) |
2300 |
{ |
2301 |
/* It would be proper to block here, but if we ran out |
2302 |
* of memory the program's probably about to abort |
2303 |
* anyway, so just error out */ |
2304 |
log("sockets: attempt to expand socket %d read buffer" |
2305 |
" failed (out of memory?)", s->fd); |
2306 |
errno_save = EAGAIN; |
2307 |
break; |
2308 |
} |
2309 |
} |
2310 |
else |
2311 |
{ |
2312 |
if ((s->flags & SF_BLOCKING) && !(s->flags & SF_WTRIGGER)) |
2313 |
{ |
2314 |
fd_set fds; |
2315 |
FD_ZERO(&fds); |
2316 |
FD_SET(s->fd, &fds); |
2317 |
if (select(s->fd + 1, NULL, &fds, NULL, NULL) < 0) |
2318 |
{ |
2319 |
log("sockets: waiting on blocking socket %d: %s", |
2320 |
s->fd, strerror(errno)); |
2321 |
break; |
2322 |
} |
2323 |
continue; /* don't expand the buffer, since it's at max */ |
2324 |
} |
2325 |
else |
2326 |
{ |
2327 |
#ifdef WARN_ON_BUFSIZE |
2328 |
if (!(s->flags & SF_WARNED)) |
2329 |
{ |
2330 |
log("sockets: socket %d exceeded %s buffer size" |
2331 |
" limit (%d)", s->fd, |
2332 |
over ? "per-connection" : "total", |
2333 |
over ? bufsize_limit : total_bufsize_limit); |
2334 |
s->flags |= SF_WARNED; |
2335 |
} |
2336 |
#endif |
2337 |
errno_save = EAGAIN; |
2338 |
break; |
2339 |
} |
2340 |
} |
2341 |
} |
2342 |
|
2343 |
} /* while (left > 0) */ |
2344 |
|
2345 |
errno = errno_save; |
2346 |
RETURN_WITH(len - left); |
2347 |
} |
2348 |
|
2349 |
/*************************************************************************/ |
2350 |
|
2351 |
/* Internal version of disconn(), used to pass a specific code to the |
2352 |
* disconnect callback. If code == DISCONN_LOCAL, attempt to first write |
2353 |
* out any data left in the write buffer, and delay disconnection if we |
2354 |
* can't. |
2355 |
*/ |
2356 |
|
2357 |
static int |
2358 |
do_disconn(Socket * s, void *code) |
2359 |
{ |
2360 |
int errno_save = errno; /* for passing to the callback */ |
2361 |
|
2362 |
ENTER_WITH("%d", "%p,%p", s, code); |
2363 |
if (s == NULL || (s->flags & SF_LISTENER)) |
2364 |
{ |
2365 |
if (s) |
2366 |
log("sockets: BUG: do_disconn(%d) with listener socket (%d)!", |
2367 |
(int) (long) code, s->fd); |
2368 |
else |
2369 |
log("sockets: BUG: do_disconn(%d) with NULL socket!", |
2370 |
(int) (long) code); |
2371 |
errno = EINVAL; |
2372 |
RETURN_WITH(-1); |
2373 |
} |
2374 |
if ((s->flags & SF_DISCONNECTING) && !((int) code & DISCONN_RESUME_FLAG)) |
2375 |
RETURN_WITH(0); |
2376 |
if ((s->flags & SF_DISCONN_REQ) && code == DISCONN_LOCAL) |
2377 |
RETURN_WITH(0); |
2378 |
if (!(s->flags & (SF_CONNECTING | SF_CONNECTED))) |
2379 |
RETURN_WITH(0); |
2380 |
s->flags |= SF_DISCONN_REQ; |
2381 |
FD_CLR(s->fd, &sock_fds); |
2382 |
if (code == DISCONN_LOCAL && MORE_TO_WRITE(s)) |
2383 |
{ |
2384 |
/* Write out any buffered data */ |
2385 |
if (flush_write_buffer(s) >= 0 && MORE_TO_WRITE(s)) |
2386 |
{ |
2387 |
/* Some data is still buffered; request disconnect after it |
2388 |
* goes out */ |
2389 |
s->flags |= SF_DISCONNECT; |
2390 |
/* It's not technically disconnected yet, but it will (should) |
2391 |
* succeed eventually */ |
2392 |
RETURN_WITH(0); |
2393 |
} |
2394 |
} |
2395 |
s->flags |= SF_DISCONNECTING; |
2396 |
if (code == DISCONN_REMOTE && (s->flags & SF_CALLBACK)) |
2397 |
{ |
2398 |
/* Socket was closed while a callback was doing something; wait |
2399 |
* until the callback finishes to actually close the socket */ |
2400 |
s->flags |= SF_BROKEN; |
2401 |
RETURN_WITH(0); |
2402 |
} |
2403 |
shutdown(s->fd, 2); |
2404 |
sock_closefd(s); |
2405 |
while (s->writemap) |
2406 |
next_wmap(s); |
2407 |
s->writemap_tail = NULL; |
2408 |
if (s->cb_disconn) |
2409 |
{ |
2410 |
/* The disconnect callback doesn't need to check for disconnection, |
2411 |
* so we just call it directly */ |
2412 |
errno = errno_save; |
2413 |
s->cb_disconn(s, (void *) ((int) code & ~DISCONN_RESUME_FLAG)); |
2414 |
} |
2415 |
s->flags &= ~SF_DISCONNECTING; |
2416 |
if (s->fd >= 0) |
2417 |
{ |
2418 |
/* The socket was reconnected */ |
2419 |
return 0; |
2420 |
} |
2421 |
s->flags &= ~(SF_CONNECTING | SF_CONNECTED); |
2422 |
if (s->flags & (SF_SELFCREATED | SF_DELETEME)) |
2423 |
{ |
2424 |
if (s->flags & SF_CALLBACK) |
2425 |
s->flags |= SF_DELETEME; |
2426 |
else |
2427 |
sock_free(s); |
2428 |
} |
2429 |
else |
2430 |
{ |
2431 |
reclaim_buffer_space_one(s); |
2432 |
} |
2433 |
RETURN_WITH(0); |
2434 |
} |
2435 |
|
2436 |
/*************************************************************************/ |
2437 |
|
2438 |
/* Close a socket's file descriptor, and clear it from all associated |
2439 |
* structures (s->fd, sockets[], sock_fds, write_fds). |
2440 |
*/ |
2441 |
|
2442 |
static void |
2443 |
sock_closefd(Socket * s) |
2444 |
{ |
2445 |
ENTER("%p", s); |
2446 |
/* FIXME: apparently it's possible to come here twice for the same |
2447 |
* connection; under what circumstances can that happen? */ |
2448 |
if (s->fd >= 0) |
2449 |
{ |
2450 |
close(s->fd); |
2451 |
FD_CLR(s->fd, &sock_fds); |
2452 |
FD_CLR(s->fd, &write_fds); |
2453 |
sockets[s->fd] = NULL; |
2454 |
s->fd = -1; |
2455 |
} |
2456 |
RETURN; |
2457 |
} |
2458 |
|
2459 |
/*************************************************************************/ |
2460 |
|
2461 |
/* |
2462 |
* Local variables: |
2463 |
* c-file-style: "stroustrup" |
2464 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
2465 |
* indent-tabs-mode: nil |
2466 |
* End: |
2467 |
* |
2468 |
* vim: expandtab shiftwidth=4: |
2469 |
*/ |