2008-04-01 16:52:26 +00:00
|
|
|
/*
|
|
|
|
* ircd-ratbox: A slightly useful ircd.
|
|
|
|
* commio.c: Network/file related functions
|
|
|
|
*
|
|
|
|
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
|
|
|
|
* Copyright (C) 1996-2002 Hybrid Development Team
|
|
|
|
* Copyright (C) 2002-2005 ircd-ratbox development team
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
|
|
* USA
|
|
|
|
*
|
|
|
|
*/
|
2016-03-24 00:10:44 +00:00
|
|
|
|
2016-03-06 09:49:27 +00:00
|
|
|
#include <librb_config.h>
|
|
|
|
#include <rb_lib.h>
|
2008-04-01 16:52:26 +00:00
|
|
|
#include <commio-int.h>
|
|
|
|
#include <commio-ssl.h>
|
|
|
|
#include <event-int.h>
|
|
|
|
#ifdef HAVE_SYS_UIO_H
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#endif
|
|
|
|
#define HAVE_SSL 1
|
|
|
|
|
|
|
|
#ifndef MSG_NOSIGNAL
|
|
|
|
#define MSG_NOSIGNAL 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
struct timeout_data
|
|
|
|
{
|
|
|
|
rb_fde_t *F;
|
|
|
|
rb_dlink_node node;
|
|
|
|
time_t timeout;
|
|
|
|
PF *timeout_handler;
|
|
|
|
void *timeout_data;
|
|
|
|
};
|
|
|
|
|
|
|
|
rb_dlink_list *rb_fd_table;
|
|
|
|
static rb_bh *fd_heap;
|
|
|
|
|
|
|
|
static rb_dlink_list timeout_list;
|
|
|
|
static rb_dlink_list closed_list;
|
|
|
|
|
|
|
|
static struct ev_entry *rb_timeout_ev;
|
|
|
|
|
|
|
|
|
|
|
|
static const char *rb_err_str[] = { "Comm OK", "Error during bind()",
|
|
|
|
"Error during DNS lookup", "connect timeout",
|
|
|
|
"Error during connect()",
|
2014-02-23 23:06:19 +00:00
|
|
|
"Comm Error",
|
|
|
|
"Error with SSL"
|
2008-04-01 16:52:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Highest FD and number of open FDs .. */
|
|
|
|
static int number_fd = 0;
|
2008-12-02 23:49:39 +00:00
|
|
|
int rb_maxconnections = 0;
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
static PF rb_connect_timeout;
|
2017-08-23 21:15:05 +00:00
|
|
|
static PF rb_connect_outcome;
|
2008-04-01 16:52:26 +00:00
|
|
|
static void mangle_mapped_sockaddr(struct sockaddr *in);
|
|
|
|
|
|
|
|
#ifndef HAVE_SOCKETPAIR
|
2016-03-20 05:37:50 +00:00
|
|
|
static int rb_inet_socketpair(int d, int type, int protocol, rb_platform_fd_t sv[2]);
|
2008-12-02 23:49:39 +00:00
|
|
|
static int rb_inet_socketpair_udp(rb_fde_t **newF1, rb_fde_t **newF2);
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline rb_fde_t *
|
2016-03-20 05:33:19 +00:00
|
|
|
add_fd(rb_platform_fd_t fd)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_fde_t *F = rb_find_fd(fd);
|
|
|
|
|
|
|
|
/* look up to see if we have it already */
|
|
|
|
if(F != NULL)
|
2008-12-02 23:49:39 +00:00
|
|
|
return F;
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
F = rb_bh_alloc(fd_heap);
|
|
|
|
F->fd = fd;
|
2008-07-29 22:41:27 +00:00
|
|
|
rb_dlinkAdd(F, &F->node, &rb_fd_table[rb_hash_fd(fd)]);
|
2008-12-02 23:49:39 +00:00
|
|
|
return (F);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
remove_fd(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
if(F == NULL || !IsFDOpen(F))
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-07-29 22:41:27 +00:00
|
|
|
rb_dlinkMoveNode(&F->node, &rb_fd_table[rb_hash_fd(F->fd)], &closed_list);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
static void
|
2008-04-01 16:52:26 +00:00
|
|
|
free_fds(void)
|
|
|
|
{
|
|
|
|
rb_fde_t *F;
|
|
|
|
rb_dlink_node *ptr, *next;
|
|
|
|
RB_DLINK_FOREACH_SAFE(ptr, next, closed_list.head)
|
|
|
|
{
|
|
|
|
F = ptr->data;
|
2016-04-28 21:00:54 +00:00
|
|
|
|
|
|
|
number_fd--;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if(F->type & (RB_FD_SOCKET | RB_FD_PIPE))
|
|
|
|
closesocket(F->fd);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
close(F->fd);
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_dlinkDelete(ptr, &closed_list);
|
|
|
|
rb_bh_free(fd_heap, F);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 32bit solaris is kinda slow and stdio only supports fds < 256
|
|
|
|
* so we got to do this crap below.
|
|
|
|
* (BTW Fuck you Sun, I hate your guts and I hope you go bankrupt soon)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined (__SVR4) && defined (__sun)
|
|
|
|
static void
|
|
|
|
rb_fd_hack(int *fd)
|
|
|
|
{
|
|
|
|
int newfd;
|
|
|
|
if(*fd > 256 || *fd < 0)
|
|
|
|
return;
|
|
|
|
if((newfd = fcntl(*fd, F_DUPFD, 256)) != -1)
|
|
|
|
{
|
|
|
|
close(*fd);
|
|
|
|
*fd = newfd;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define rb_fd_hack(fd)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* close_all_connections() can be used *before* the system come up! */
|
|
|
|
|
|
|
|
static void
|
|
|
|
rb_close_all(void)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
#ifndef _WIN32
|
2008-04-01 16:52:26 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* XXX someone tell me why we care about 4 fd's ? */
|
|
|
|
/* XXX btw, fd 3 is used for profiler ! */
|
2008-12-02 23:49:39 +00:00
|
|
|
for(i = 3; i < rb_maxconnections; ++i)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
close(i);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get_sockerr - get the error value from the socket or the current errno
|
|
|
|
*
|
|
|
|
* Get the *real* error from the socket (well try to anyway..).
|
|
|
|
* This may only work when SO_DEBUG is enabled but its worth the
|
|
|
|
* gamble anyway.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rb_get_sockerr(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
int errtmp;
|
|
|
|
int err = 0;
|
|
|
|
rb_socklen_t len = sizeof(err);
|
|
|
|
|
|
|
|
if(!(F->type & RB_FD_SOCKET))
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
rb_get_errno();
|
|
|
|
errtmp = errno;
|
|
|
|
|
|
|
|
#ifdef SO_ERROR
|
2008-12-02 23:49:39 +00:00
|
|
|
if(F != NULL
|
|
|
|
&& !getsockopt(rb_get_fd(F), SOL_SOCKET, SO_ERROR, (char *)&err, (rb_socklen_t *) & len))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(err)
|
|
|
|
errtmp = err;
|
|
|
|
}
|
|
|
|
errno = errtmp;
|
|
|
|
#endif
|
|
|
|
return errtmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_getmaxconnect - return the max number of connections allowed
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rb_getmaxconnect(void)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
return (rb_maxconnections);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_sock_buffers - set send and receive buffers for socket
|
2014-03-03 04:25:47 +00:00
|
|
|
*
|
2008-04-01 16:52:26 +00:00
|
|
|
* inputs - fd file descriptor
|
|
|
|
* - size to set
|
|
|
|
* output - returns true (1) if successful, false (0) otherwise
|
|
|
|
* side effects -
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rb_set_buffers(rb_fde_t *F, int size)
|
|
|
|
{
|
|
|
|
if(F == NULL)
|
|
|
|
return 0;
|
|
|
|
if(setsockopt
|
2008-12-02 23:49:39 +00:00
|
|
|
(F->fd, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof(size))
|
|
|
|
|| setsockopt(F->fd, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof(size)))
|
2008-04-01 16:52:26 +00:00
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-03-03 04:25:47 +00:00
|
|
|
* set_non_blocking - Set the client connection into non-blocking mode.
|
2008-04-01 16:52:26 +00:00
|
|
|
*
|
|
|
|
* inputs - fd to set into non blocking mode
|
|
|
|
* output - 1 if successful 0 if not
|
|
|
|
* side effects - use POSIX compliant non blocking and
|
|
|
|
* be done with it.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rb_set_nb(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
int nonb = 0;
|
|
|
|
int res;
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_platform_fd_t fd;
|
2008-04-01 16:52:26 +00:00
|
|
|
if(F == NULL)
|
|
|
|
return 0;
|
|
|
|
fd = F->fd;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
if((res = rb_setup_fd(F)))
|
|
|
|
return res;
|
|
|
|
#ifdef O_NONBLOCK
|
|
|
|
nonb |= O_NONBLOCK;
|
|
|
|
res = fcntl(fd, F_GETFL, 0);
|
|
|
|
if(-1 == res || fcntl(fd, F_SETFL, res | nonb) == -1)
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
nonb = 1;
|
|
|
|
res = 0;
|
|
|
|
if(ioctl(fd, FIONBIO, (char *)&nonb) == -1)
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_settimeout() - set the socket timeout
|
|
|
|
*
|
|
|
|
* Set the timeout for the fd
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rb_settimeout(rb_fde_t *F, time_t timeout, PF * callback, void *cbdata)
|
|
|
|
{
|
|
|
|
struct timeout_data *td;
|
|
|
|
|
|
|
|
if(F == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lrb_assert(IsFDOpen(F));
|
|
|
|
td = F->timeout;
|
2008-12-02 23:49:39 +00:00
|
|
|
if(callback == NULL) /* user wants to remove */
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(td == NULL)
|
|
|
|
return;
|
|
|
|
rb_dlinkDelete(&td->node, &timeout_list);
|
|
|
|
rb_free(td);
|
|
|
|
F->timeout = NULL;
|
2008-12-02 23:49:39 +00:00
|
|
|
if(rb_dlink_list_length(&timeout_list) == 0)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_event_delete(rb_timeout_ev);
|
|
|
|
rb_timeout_ev = NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(F->timeout == NULL)
|
2008-12-02 23:49:39 +00:00
|
|
|
td = F->timeout = rb_malloc(sizeof(struct timeout_data));
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
td->F = F;
|
|
|
|
td->timeout = rb_current_time() + timeout;
|
|
|
|
td->timeout_handler = callback;
|
|
|
|
td->timeout_data = cbdata;
|
|
|
|
rb_dlinkAdd(td, &td->node, &timeout_list);
|
|
|
|
if(rb_timeout_ev == NULL)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_timeout_ev = rb_event_add("rb_checktimeouts", rb_checktimeouts, NULL, 5);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_checktimeouts() - check the socket timeouts
|
|
|
|
*
|
|
|
|
* All this routine does is call the given callback/cbdata, without closing
|
|
|
|
* down the file descriptor. When close handlers have been implemented,
|
|
|
|
* this will happen.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rb_checktimeouts(void *notused)
|
|
|
|
{
|
|
|
|
rb_dlink_node *ptr, *next;
|
|
|
|
struct timeout_data *td;
|
|
|
|
rb_fde_t *F;
|
|
|
|
PF *hdl;
|
|
|
|
void *data;
|
|
|
|
|
|
|
|
RB_DLINK_FOREACH_SAFE(ptr, next, timeout_list.head)
|
|
|
|
{
|
|
|
|
td = ptr->data;
|
|
|
|
F = td->F;
|
|
|
|
if(F == NULL || !IsFDOpen(F))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(td->timeout < rb_current_time())
|
|
|
|
{
|
|
|
|
hdl = td->timeout_handler;
|
|
|
|
data = td->timeout_data;
|
|
|
|
rb_dlinkDelete(&td->node, &timeout_list);
|
|
|
|
F->timeout = NULL;
|
|
|
|
rb_free(td);
|
|
|
|
hdl(F, data);
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-19 14:11:49 +00:00
|
|
|
static int
|
|
|
|
rb_setsockopt_reuseaddr(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
int opt_one = 1;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = setsockopt(F->fd, SOL_SOCKET, SO_REUSEADDR, &opt_one, sizeof(opt_one));
|
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_reuseaddr: Cannot set SO_REUSEADDR for FD %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
static int
|
|
|
|
rb_setsockopt_sctp(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
int opt_zero = 0;
|
|
|
|
int opt_one = 1;
|
2017-08-23 23:21:02 +00:00
|
|
|
/* workaround for https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/sctp?id=299ee123e19889d511092347f5fc14db0f10e3a6 */
|
|
|
|
char *env_mapped = getenv("SCTP_I_WANT_MAPPED_V4_ADDR");
|
|
|
|
int opt_mapped = env_mapped != NULL ? atoi(env_mapped) : opt_zero;
|
2017-08-19 14:11:49 +00:00
|
|
|
int ret;
|
|
|
|
struct sctp_initmsg initmsg;
|
|
|
|
struct sctp_rtoinfo rtoinfo;
|
|
|
|
struct sctp_paddrparams paddrparams;
|
|
|
|
struct sctp_assocparams assocparams;
|
|
|
|
|
|
|
|
ret = setsockopt(F->fd, SOL_SCTP, SCTP_NODELAY, &opt_one, sizeof(opt_one));
|
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_NODELAY for fd %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-08-23 23:21:02 +00:00
|
|
|
ret = setsockopt(F->fd, SOL_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &opt_mapped, sizeof(opt_mapped));
|
2017-08-19 14:11:49 +00:00
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_sctp: Cannot unset SCTP_I_WANT_MAPPED_V4_ADDR for fd %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure INIT message to specify that we only want one stream */
|
|
|
|
memset(&initmsg, 0, sizeof(initmsg));
|
|
|
|
initmsg.sinit_num_ostreams = 1;
|
|
|
|
initmsg.sinit_max_instreams = 1;
|
|
|
|
|
|
|
|
ret = setsockopt(F->fd, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
|
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_INITMSG for fd %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure RTO values to reduce the maximum timeout */
|
|
|
|
memset(&rtoinfo, 0, sizeof(rtoinfo));
|
|
|
|
rtoinfo.srto_initial = 3000;
|
|
|
|
rtoinfo.srto_min = 1000;
|
|
|
|
rtoinfo.srto_max = 10000;
|
|
|
|
|
|
|
|
ret = setsockopt(F->fd, SOL_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo));
|
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_RTOINFO for fd %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Configure peer address parameters to ensure that we monitor the connection
|
|
|
|
* more often than the default and don't timeout retransmit attempts before
|
|
|
|
* the ping timeout does.
|
|
|
|
*
|
|
|
|
* Each peer address will timeout reachability in about 750s.
|
|
|
|
*/
|
|
|
|
memset(&paddrparams, 0, sizeof(paddrparams));
|
|
|
|
paddrparams.spp_assoc_id = 0;
|
|
|
|
memcpy(&paddrparams.spp_address, &in6addr_any, sizeof(in6addr_any));
|
|
|
|
paddrparams.spp_pathmaxrxt = 50;
|
|
|
|
paddrparams.spp_hbinterval = 5000;
|
|
|
|
paddrparams.spp_flags |= SPP_HB_ENABLE;
|
|
|
|
|
|
|
|
ret = setsockopt(F->fd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &paddrparams, sizeof(paddrparams));
|
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_PEER_ADDR_PARAMS for fd %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure association parameters for retransmit attempts as above */
|
|
|
|
memset(&assocparams, 0, sizeof(assocparams));
|
|
|
|
assocparams.sasoc_assoc_id = 0;
|
|
|
|
assocparams.sasoc_asocmaxrxt = 50;
|
|
|
|
|
|
|
|
ret = setsockopt(F->fd, SOL_SCTP, SCTP_ASSOCINFO, &assocparams, sizeof(assocparams));
|
|
|
|
if (ret) {
|
|
|
|
rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_ASSOCINFO for fd %d: %s",
|
|
|
|
F->fd, strerror(rb_get_sockerr(F)));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_bind(rb_fde_t *F, struct sockaddr *addr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = rb_setsockopt_reuseaddr(F);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = bind(F->fd, addr, GET_SS_LEN(addr));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-22 19:59:53 +00:00
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
static int
|
|
|
|
rb_sctp_bindx_only(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
if (GET_SS_FAMILY(&addrs[i]) == AF_UNSPEC)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = sctp_bindx(F->fd, (struct sockaddr *)&addrs[i], 1, SCTP_BINDX_ADD_ADDR);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-08-19 14:11:49 +00:00
|
|
|
int
|
|
|
|
rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((F->type & RB_FD_SCTP) == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = rb_setsockopt_reuseaddr(F);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-08-22 19:59:53 +00:00
|
|
|
ret = rb_sctp_bindx_only(F, addrs, len);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2017-08-19 14:11:49 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_inet_get_proto(rb_fde_t *F)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2017-08-19 14:11:49 +00:00
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
if (F->type & RB_FD_SCTP)
|
|
|
|
return IPPROTO_SCTP;
|
|
|
|
#endif
|
|
|
|
return IPPROTO_TCP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rb_accept_tryaccept(rb_fde_t *F, void *data) {
|
2008-04-01 16:52:26 +00:00
|
|
|
struct rb_sockaddr_storage st;
|
|
|
|
rb_fde_t *new_F;
|
2017-07-29 20:38:34 +00:00
|
|
|
rb_socklen_t addrlen;
|
2008-04-01 16:52:26 +00:00
|
|
|
int new_fd;
|
|
|
|
|
|
|
|
while(1)
|
|
|
|
{
|
2017-07-29 20:38:34 +00:00
|
|
|
memset(&st, 0, sizeof(st));
|
|
|
|
addrlen = sizeof(st);
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
new_fd = accept(F->fd, (struct sockaddr *)&st, &addrlen);
|
|
|
|
rb_get_errno();
|
|
|
|
if(new_fd < 0)
|
|
|
|
{
|
|
|
|
rb_setselect(F, RB_SELECT_ACCEPT, rb_accept_tryaccept, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_fd_hack(&new_fd);
|
|
|
|
|
2017-08-19 14:11:49 +00:00
|
|
|
new_F = rb_open(new_fd, RB_FD_SOCKET | (F->type & RB_FD_INHERIT_TYPES), "Incoming Connection");
|
2008-04-01 16:52:26 +00:00
|
|
|
|
2008-07-29 22:41:27 +00:00
|
|
|
if(new_F == NULL)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_lib_log
|
2017-08-19 14:11:49 +00:00
|
|
|
("rb_accept: new_F == NULL on incoming connection. Closing new_fd == %d",
|
2008-12-02 23:49:39 +00:00
|
|
|
new_fd);
|
2008-07-29 22:41:27 +00:00
|
|
|
close(new_fd);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(!rb_set_nb(new_F)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_get_errno();
|
|
|
|
rb_lib_log("rb_accept: Couldn't set FD %d non blocking!", new_F->fd);
|
|
|
|
rb_close(new_F);
|
|
|
|
}
|
|
|
|
|
|
|
|
mangle_mapped_sockaddr((struct sockaddr *)&st);
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
if(F->accept->precb != NULL)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
if(!F->accept->precb(new_F, (struct sockaddr *)&st, addrlen, F->accept->data)) /* pre-callback decided to drop it */
|
2008-04-01 16:52:26 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
if(F->type & RB_FD_SSL)
|
|
|
|
{
|
2008-07-29 22:41:27 +00:00
|
|
|
rb_ssl_accept_setup(F, new_F, (struct sockaddr *)&st, addrlen);
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
else
|
2008-12-02 23:49:39 +00:00
|
|
|
#endif /* HAVE_SSL */
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
F->accept->callback(new_F, RB_OK, (struct sockaddr *)&st, addrlen,
|
|
|
|
F->accept->data);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* try to accept a TCP connection */
|
|
|
|
void
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_accept_tcp(rb_fde_t *F, ACPRE * precb, ACCB * callback, void *data)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(F == NULL)
|
|
|
|
return;
|
|
|
|
lrb_assert(callback);
|
|
|
|
|
|
|
|
F->accept = rb_malloc(sizeof(struct acceptdata));
|
|
|
|
F->accept->callback = callback;
|
|
|
|
F->accept->data = data;
|
|
|
|
F->accept->precb = precb;
|
|
|
|
rb_accept_tryaccept(F, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-03-20 05:33:19 +00:00
|
|
|
* void rb_connect_tcp(rb_platform_fd_t fd, struct sockaddr *dest,
|
2016-04-24 16:11:20 +00:00
|
|
|
* struct sockaddr *clocal,
|
2008-04-01 16:52:26 +00:00
|
|
|
* CNCB *callback, void *data, int timeout)
|
|
|
|
* Input: An fd to connect with, a host and port to connect to,
|
2016-04-24 16:11:20 +00:00
|
|
|
* a local sockaddr to connect from (or NULL to use the
|
2008-04-01 16:52:26 +00:00
|
|
|
* default), a callback, the data to pass into the callback, the
|
|
|
|
* address family.
|
|
|
|
* Output: None.
|
|
|
|
* Side-effects: A non-blocking connection to the host is started, and
|
|
|
|
* if necessary, set up for selection. The callback given
|
|
|
|
* may be called now, or it may be called later.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
|
2016-04-24 16:11:20 +00:00
|
|
|
struct sockaddr *clocal, CNCB * callback, void *data, int timeout)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2017-08-23 21:15:05 +00:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (F == NULL)
|
2008-04-01 16:52:26 +00:00
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
lrb_assert(callback);
|
|
|
|
F->connect = rb_malloc(sizeof(struct conndata));
|
|
|
|
F->connect->callback = callback;
|
|
|
|
F->connect->data = data;
|
|
|
|
|
|
|
|
memcpy(&F->connect->hostaddr, dest, sizeof(F->connect->hostaddr));
|
|
|
|
|
|
|
|
/* Note that we're using a passed sockaddr here. This is because
|
|
|
|
* generally you'll be bind()ing to a sockaddr grabbed from
|
|
|
|
* getsockname(), so this makes things easier.
|
|
|
|
* XXX If NULL is passed as local, we should later on bind() to the
|
|
|
|
* virtual host IP, for completeness.
|
|
|
|
* -- adrian
|
|
|
|
*/
|
2016-04-24 16:11:20 +00:00
|
|
|
if((clocal != NULL) && (bind(F->fd, clocal, GET_SS_LEN(clocal)) < 0))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
/* Failure, call the callback with RB_ERR_BIND */
|
|
|
|
rb_connect_callback(F, RB_ERR_BIND);
|
|
|
|
/* ... and quit */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We have a valid IP, so we just call tryconnect */
|
|
|
|
/* Make sure we actually set the timeout here .. */
|
|
|
|
rb_settimeout(F, timeout, rb_connect_timeout, NULL);
|
|
|
|
|
2017-08-23 21:15:05 +00:00
|
|
|
retval = connect(F->fd,
|
|
|
|
(struct sockaddr *)&F->connect->hostaddr,
|
|
|
|
GET_SS_LEN(&F->connect->hostaddr));
|
|
|
|
/* Error? */
|
|
|
|
if (retval < 0) {
|
|
|
|
/*
|
|
|
|
* If we get EISCONN, then we've already connect()ed the socket,
|
|
|
|
* which is a good thing.
|
|
|
|
* -- adrian
|
|
|
|
*/
|
|
|
|
rb_get_errno();
|
|
|
|
if (errno == EISCONN) {
|
|
|
|
rb_connect_callback(F, RB_OK);
|
|
|
|
} else if (rb_ignore_errno(errno)) {
|
|
|
|
/* Ignore error? Reschedule */
|
|
|
|
rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
|
|
|
|
} else {
|
|
|
|
/* Error? Fail with RB_ERR_CONNECT */
|
|
|
|
rb_connect_callback(F, RB_ERR_CONNECT);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If we get here, we've succeeded, so call with RB_OK */
|
|
|
|
rb_connect_callback(F, RB_OK);
|
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
|
2017-08-22 19:59:53 +00:00
|
|
|
void
|
|
|
|
rb_connect_sctp(rb_fde_t *F, struct sockaddr_storage *dest, size_t dest_len,
|
|
|
|
struct sockaddr_storage *clocal, size_t clocal_len,
|
|
|
|
CNCB *callback, void *data, int timeout)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
uint8_t packed_dest[sizeof(struct sockaddr_storage) * dest_len];
|
|
|
|
uint8_t *p = &packed_dest[0];
|
|
|
|
size_t n = 0;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (F == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lrb_assert(callback);
|
|
|
|
F->connect = rb_malloc(sizeof(struct conndata));
|
|
|
|
F->connect->callback = callback;
|
|
|
|
F->connect->data = data;
|
|
|
|
|
|
|
|
if ((F->type & RB_FD_SCTP) == 0) {
|
|
|
|
rb_connect_callback(F, RB_ERR_CONNECT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < dest_len; i++) {
|
|
|
|
if (GET_SS_FAMILY(&dest[i]) == AF_INET6) {
|
|
|
|
memcpy(p, &dest[i], sizeof(struct sockaddr_in6));
|
|
|
|
n++;
|
|
|
|
p += sizeof(struct sockaddr_in6);
|
|
|
|
} else if (GET_SS_FAMILY(&dest[i]) == AF_INET) {
|
|
|
|
memcpy(p, &dest[i], sizeof(struct sockaddr_in));
|
|
|
|
n++;
|
|
|
|
p += sizeof(struct sockaddr_in);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dest_len = n;
|
|
|
|
|
|
|
|
memcpy(&F->connect->hostaddr, &dest[0], sizeof(F->connect->hostaddr));
|
|
|
|
|
|
|
|
if ((clocal_len > 0) && (rb_sctp_bindx_only(F, clocal, clocal_len) < 0)) {
|
|
|
|
/* Failure, call the callback with RB_ERR_BIND */
|
|
|
|
rb_connect_callback(F, RB_ERR_BIND);
|
|
|
|
/* ... and quit */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_settimeout(F, timeout, rb_connect_timeout, NULL);
|
|
|
|
|
|
|
|
retval = sctp_connectx(F->fd, (struct sockaddr *)packed_dest, dest_len, NULL);
|
|
|
|
/* Error? */
|
|
|
|
if (retval < 0) {
|
|
|
|
/*
|
|
|
|
* If we get EISCONN, then we've already connect()ed the socket,
|
|
|
|
* which is a good thing.
|
|
|
|
* -- adrian
|
|
|
|
*/
|
|
|
|
rb_get_errno();
|
|
|
|
if (errno == EISCONN) {
|
|
|
|
rb_connect_callback(F, RB_OK);
|
|
|
|
} else if (rb_ignore_errno(errno)) {
|
|
|
|
/* Ignore error? Reschedule */
|
|
|
|
rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
|
|
|
|
} else {
|
|
|
|
/* Error? Fail with RB_ERR_CONNECT */
|
|
|
|
rb_connect_callback(F, RB_ERR_CONNECT);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If we get here, we've succeeded, so call with RB_OK */
|
|
|
|
rb_connect_callback(F, RB_OK);
|
|
|
|
#else
|
|
|
|
rb_connect_callback(F, RB_ERR_CONNECT);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
/*
|
|
|
|
* rb_connect_callback() - call the callback, and continue with life
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rb_connect_callback(rb_fde_t *F, int status)
|
|
|
|
{
|
|
|
|
CNCB *hdl;
|
|
|
|
void *data;
|
2008-12-02 23:49:39 +00:00
|
|
|
int errtmp = errno; /* save errno as rb_settimeout clobbers it sometimes */
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
/* This check is gross..but probably necessary */
|
|
|
|
if(F == NULL || F->connect == NULL || F->connect->callback == NULL)
|
|
|
|
return;
|
|
|
|
/* Clear the connect flag + handler */
|
|
|
|
hdl = F->connect->callback;
|
|
|
|
data = F->connect->data;
|
|
|
|
F->connect->callback = NULL;
|
|
|
|
|
2008-05-13 18:21:12 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
/* Clear the timeout handler */
|
|
|
|
rb_settimeout(F, 0, NULL, NULL);
|
2008-12-02 23:49:39 +00:00
|
|
|
errno = errtmp;
|
2008-04-01 16:52:26 +00:00
|
|
|
/* Call the handler */
|
|
|
|
hdl(F, status, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_connect_timeout() - this gets called when the socket connection
|
|
|
|
* times out. This *only* can be called once connect() is initially
|
|
|
|
* called ..
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
rb_connect_timeout(rb_fde_t *F, void *notused)
|
|
|
|
{
|
|
|
|
/* error! */
|
|
|
|
rb_connect_callback(F, RB_ERR_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-08-23 21:15:05 +00:00
|
|
|
rb_connect_outcome(rb_fde_t *F, void *notused)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
int retval;
|
2017-08-23 21:15:05 +00:00
|
|
|
int err = 0;
|
|
|
|
socklen_t len = sizeof(err);
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
if(F == NULL || F->connect == NULL || F->connect->callback == NULL)
|
|
|
|
return;
|
2017-08-23 21:15:05 +00:00
|
|
|
retval = getsockopt(F->fd, SOL_SOCKET, SO_ERROR, &err, &len);
|
|
|
|
if (retval < 0) {
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_get_errno();
|
2017-08-23 21:15:05 +00:00
|
|
|
} else if (err != 0) {
|
|
|
|
errno = err;
|
|
|
|
retval = -1;
|
|
|
|
}
|
|
|
|
if (retval < 0) {
|
|
|
|
/* Error? Fail with RB_ERR_CONNECT */
|
|
|
|
rb_connect_callback(F, RB_ERR_CONNECT);
|
2008-04-01 16:52:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-08-23 21:15:05 +00:00
|
|
|
/* If we get here, we've succeeded, so call with RB_OK */
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_connect_callback(F, RB_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_connect_sockaddr(rb_fde_t *F, struct sockaddr *addr, int len)
|
|
|
|
{
|
|
|
|
if(F == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memcpy(addr, &F->connect->hostaddr, len);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_error_str() - return an error string for the given error condition
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
rb_errstr(int error)
|
|
|
|
{
|
|
|
|
if(error < 0 || error >= RB_ERR_MAX)
|
|
|
|
return "Invalid error number!";
|
|
|
|
return rb_err_str[error];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_socketpair(int family, int sock_type, int proto, rb_fde_t **F1, rb_fde_t **F2, const char *note)
|
|
|
|
{
|
2016-03-20 05:38:46 +00:00
|
|
|
rb_platform_fd_t nfd[2];
|
2008-04-01 16:52:26 +00:00
|
|
|
if(number_fd >= rb_maxconnections)
|
|
|
|
{
|
|
|
|
errno = ENFILE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
#ifdef HAVE_SOCKETPAIR
|
2008-04-01 16:52:26 +00:00
|
|
|
if(socketpair(family, sock_type, proto, nfd))
|
|
|
|
#else
|
2008-12-02 23:49:39 +00:00
|
|
|
if(sock_type == SOCK_DGRAM)
|
|
|
|
{
|
|
|
|
return rb_inet_socketpair_udp(F1, F2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(rb_inet_socketpair(AF_INET, sock_type, proto, nfd))
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rb_fd_hack(&nfd[0]);
|
|
|
|
rb_fd_hack(&nfd[1]);
|
|
|
|
|
|
|
|
*F1 = rb_open(nfd[0], RB_FD_SOCKET, note);
|
|
|
|
*F2 = rb_open(nfd[1], RB_FD_SOCKET, note);
|
|
|
|
|
|
|
|
if(*F1 == NULL)
|
|
|
|
{
|
|
|
|
if(*F2 != NULL)
|
|
|
|
rb_close(*F2);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(*F2 == NULL)
|
|
|
|
{
|
|
|
|
rb_close(*F1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the socket non-blocking, and other wonderful bits */
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(!rb_set_nb(*F1)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_lib_log("rb_open: Couldn't set FD %d non blocking: %s", nfd[0], strerror(errno));
|
|
|
|
rb_close(*F1);
|
|
|
|
rb_close(*F2);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(!rb_set_nb(*F2)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_lib_log("rb_open: Couldn't set FD %d non blocking: %s", nfd[1], strerror(errno));
|
|
|
|
rb_close(*F1);
|
|
|
|
rb_close(*F2);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_pipe(rb_fde_t **F1, rb_fde_t **F2, const char *desc)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
#ifndef _WIN32
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_platform_fd_t fd[2];
|
2008-04-01 16:52:26 +00:00
|
|
|
if(number_fd >= rb_maxconnections)
|
|
|
|
{
|
|
|
|
errno = ENFILE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(pipe(fd) == -1)
|
|
|
|
return -1;
|
|
|
|
rb_fd_hack(&fd[0]);
|
|
|
|
rb_fd_hack(&fd[1]);
|
|
|
|
*F1 = rb_open(fd[0], RB_FD_PIPE, desc);
|
|
|
|
*F2 = rb_open(fd[1], RB_FD_PIPE, desc);
|
|
|
|
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(!rb_set_nb(*F1)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_lib_log("rb_open: Couldn't set FD %d non blocking: %s", fd[0], strerror(errno));
|
|
|
|
rb_close(*F1);
|
|
|
|
rb_close(*F2);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(!rb_set_nb(*F2)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_lib_log("rb_open: Couldn't set FD %d non blocking: %s", fd[1], strerror(errno));
|
|
|
|
rb_close(*F1);
|
|
|
|
rb_close(*F2);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
/* Its not a pipe..but its selectable. I'll take dirty hacks
|
|
|
|
* for $500 Alex.
|
|
|
|
*/
|
2008-12-02 23:49:39 +00:00
|
|
|
return rb_socketpair(AF_INET, SOCK_STREAM, 0, F1, F2, desc);
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_socket() - open a socket
|
|
|
|
*
|
|
|
|
* This is a highly highly cut down version of squid's rb_open() which
|
|
|
|
* for the most part emulates socket(), *EXCEPT* it fails if we're about
|
|
|
|
* to run out of file descriptors.
|
|
|
|
*/
|
|
|
|
rb_fde_t *
|
|
|
|
rb_socket(int family, int sock_type, int proto, const char *note)
|
|
|
|
{
|
|
|
|
rb_fde_t *F;
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_platform_fd_t fd;
|
2008-04-01 16:52:26 +00:00
|
|
|
/* First, make sure we aren't going to run out of file descriptors */
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(number_fd >= rb_maxconnections))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
errno = ENFILE;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Next, we try to open the socket. We *should* drop the reserved FD
|
|
|
|
* limit if/when we get an error, but we can deal with that later.
|
|
|
|
* XXX !!! -- adrian
|
|
|
|
*/
|
|
|
|
fd = socket(family, sock_type, proto);
|
|
|
|
rb_fd_hack(&fd);
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(fd < 0))
|
2008-04-01 16:52:26 +00:00
|
|
|
return NULL; /* errno will be passed through, yay.. */
|
|
|
|
|
2014-03-03 04:25:47 +00:00
|
|
|
/*
|
2008-04-01 16:52:26 +00:00
|
|
|
* Make sure we can take both IPv4 and IPv6 connections
|
2017-08-19 14:11:49 +00:00
|
|
|
* on an AF_INET6 SCTP socket, otherwise keep them separate
|
2008-04-01 16:52:26 +00:00
|
|
|
*/
|
|
|
|
if(family == AF_INET6)
|
|
|
|
{
|
2017-08-19 14:11:49 +00:00
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
int v6only = (proto == IPPROTO_SCTP) ? 0 : 1;
|
|
|
|
#else
|
|
|
|
int v6only = 1;
|
|
|
|
#endif
|
|
|
|
if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only)) == -1)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2017-08-19 14:11:49 +00:00
|
|
|
rb_lib_log("rb_socket: Could not set IPV6_V6ONLY option to %d on FD %d: %s",
|
|
|
|
v6only, fd, strerror(errno));
|
2008-04-01 16:52:26 +00:00
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
F = rb_open(fd, RB_FD_SOCKET, note);
|
|
|
|
if(F == NULL)
|
2008-07-29 22:41:27 +00:00
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_lib_log("rb_socket: rb_open returns NULL on FD %d: %s, closing fd", fd,
|
|
|
|
strerror(errno));
|
2008-07-29 22:41:27 +00:00
|
|
|
close(fd);
|
2008-04-01 16:52:26 +00:00
|
|
|
return NULL;
|
2008-07-29 22:41:27 +00:00
|
|
|
}
|
2017-08-19 14:11:49 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_LIBSCTP
|
|
|
|
if (proto == IPPROTO_SCTP) {
|
|
|
|
F->type |= RB_FD_SCTP;
|
|
|
|
|
|
|
|
if (rb_setsockopt_sctp(F)) {
|
|
|
|
rb_lib_log("rb_socket: Could not set SCTP socket options on FD %d: %s",
|
|
|
|
fd, strerror(errno));
|
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
/* Set the socket non-blocking, and other wonderful bits */
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(!rb_set_nb(F)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
rb_lib_log("rb_open: Couldn't set FD %d non blocking: %s", fd, strerror(errno));
|
|
|
|
rb_close(F);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a sockaddr_storage is AF_INET6 but is a mapped IPv4
|
|
|
|
* socket manged the sockaddr.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
mangle_mapped_sockaddr(struct sockaddr *in)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)in;
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
if(in->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr))
|
|
|
|
{
|
|
|
|
struct sockaddr_in in4;
|
|
|
|
memset(&in4, 0, sizeof(struct sockaddr_in));
|
|
|
|
in4.sin_family = AF_INET;
|
|
|
|
in4.sin_port = in6->sin6_port;
|
2008-12-02 23:49:39 +00:00
|
|
|
in4.sin_addr.s_addr = ((uint32_t *)&in6->sin6_addr)[3];
|
2008-04-01 16:52:26 +00:00
|
|
|
memcpy(in, &in4, sizeof(struct sockaddr_in));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_listen() - listen on a port
|
|
|
|
*/
|
|
|
|
int
|
2012-03-17 14:48:25 +00:00
|
|
|
rb_listen(rb_fde_t *F, int backlog, int defer_accept)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2012-03-17 14:16:39 +00:00
|
|
|
int result;
|
|
|
|
|
2017-08-19 14:11:49 +00:00
|
|
|
F->type = RB_FD_SOCKET | RB_FD_LISTEN | (F->type & RB_FD_INHERIT_TYPES);
|
2012-03-17 14:16:39 +00:00
|
|
|
result = listen(F->fd, backlog);
|
|
|
|
|
|
|
|
#ifdef TCP_DEFER_ACCEPT
|
2012-03-17 14:48:25 +00:00
|
|
|
if (defer_accept && !result)
|
2012-03-17 14:16:39 +00:00
|
|
|
{
|
2014-03-02 21:09:08 +00:00
|
|
|
(void)setsockopt(F->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &backlog, sizeof(int));
|
2012-03-17 14:16:39 +00:00
|
|
|
}
|
|
|
|
#endif
|
2012-03-19 23:33:31 +00:00
|
|
|
#ifdef SO_ACCEPTFILTER
|
|
|
|
if (defer_accept && !result)
|
|
|
|
{
|
|
|
|
struct accept_filter_arg afa;
|
|
|
|
|
|
|
|
memset(&afa, '\0', sizeof afa);
|
|
|
|
rb_strlcpy(afa.af_name, "dataready", sizeof afa.af_name);
|
|
|
|
(void)setsockopt(F->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa,
|
|
|
|
sizeof afa);
|
|
|
|
}
|
|
|
|
#endif
|
2012-03-17 14:16:39 +00:00
|
|
|
|
|
|
|
return result;
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_fdlist_init(int closeall, int maxfds, size_t heapsize)
|
|
|
|
{
|
|
|
|
static int initialized = 0;
|
2008-12-02 23:49:39 +00:00
|
|
|
#ifdef _WIN32
|
2008-04-01 16:52:26 +00:00
|
|
|
WSADATA wsaData;
|
|
|
|
int err;
|
|
|
|
int vers = MAKEWORD(2, 0);
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
err = WSAStartup(vers, &wsaData);
|
|
|
|
if(err != 0)
|
|
|
|
{
|
|
|
|
rb_lib_die("WSAStartup failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
if(!initialized)
|
|
|
|
{
|
|
|
|
rb_maxconnections = maxfds;
|
|
|
|
if(closeall)
|
|
|
|
rb_close_all();
|
|
|
|
/* Since we're doing this once .. */
|
|
|
|
initialized = 1;
|
|
|
|
}
|
|
|
|
fd_heap = rb_bh_create(sizeof(rb_fde_t), heapsize, "librb_fd_heap");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Called to open a given filedescriptor */
|
|
|
|
rb_fde_t *
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_open(rb_platform_fd_t fd, uint8_t type, const char *desc)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-07-29 22:41:27 +00:00
|
|
|
rb_fde_t *F;
|
2008-04-01 16:52:26 +00:00
|
|
|
lrb_assert(fd >= 0);
|
|
|
|
|
2008-07-29 22:41:27 +00:00
|
|
|
F = add_fd(fd);
|
|
|
|
|
|
|
|
lrb_assert(!IsFDOpen(F));
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(IsFDOpen(F)))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-07-29 22:41:27 +00:00
|
|
|
const char *fdesc;
|
|
|
|
if(F != NULL && F->desc != NULL)
|
|
|
|
fdesc = F->desc;
|
|
|
|
else
|
|
|
|
fdesc = "NULL";
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_lib_log("Trying to rb_open an already open FD: %d desc: %s", fd, fdesc);
|
2008-04-01 16:52:26 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
F->fd = fd;
|
|
|
|
F->type = type;
|
|
|
|
SetFDOpen(F);
|
|
|
|
|
|
|
|
if(desc != NULL)
|
|
|
|
F->desc = rb_strndup(desc, FD_DESC_SZ);
|
|
|
|
number_fd++;
|
|
|
|
return F;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Called to close a given filedescriptor */
|
|
|
|
void
|
|
|
|
rb_close(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
int type, fd;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
if(F == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fd = F->fd;
|
2008-12-02 23:49:39 +00:00
|
|
|
type = F->type;
|
2008-04-01 16:52:26 +00:00
|
|
|
lrb_assert(IsFDOpen(F));
|
|
|
|
|
|
|
|
lrb_assert(!(type & RB_FD_FILE));
|
2008-06-25 05:28:30 +00:00
|
|
|
if(rb_unlikely(type & RB_FD_FILE))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
lrb_assert(F->read_handler == NULL);
|
|
|
|
lrb_assert(F->write_handler == NULL);
|
|
|
|
}
|
2017-08-19 14:11:49 +00:00
|
|
|
|
|
|
|
if (type & RB_FD_LISTEN) {
|
|
|
|
listen(F->fd, 0);
|
|
|
|
}
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_setselect(F, RB_SELECT_WRITE | RB_SELECT_READ, NULL, NULL);
|
|
|
|
rb_settimeout(F, 0, NULL, NULL);
|
|
|
|
rb_free(F->accept);
|
|
|
|
rb_free(F->connect);
|
|
|
|
rb_free(F->desc);
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
if(type & RB_FD_SSL)
|
|
|
|
{
|
|
|
|
rb_ssl_shutdown(F);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_SSL */
|
|
|
|
if(IsFDOpen(F))
|
|
|
|
{
|
|
|
|
remove_fd(F);
|
|
|
|
ClearFDOpen(F);
|
|
|
|
}
|
2016-04-30 10:15:03 +00:00
|
|
|
|
|
|
|
if(type & RB_FD_LISTEN)
|
|
|
|
shutdown(fd, SHUT_RDWR);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_dump_fd() - dump the list of active filedescriptors
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rb_dump_fd(DUMPCB * cb, void *data)
|
|
|
|
{
|
|
|
|
static const char *empty = "";
|
|
|
|
rb_dlink_node *ptr;
|
|
|
|
rb_dlink_list *bucket;
|
|
|
|
rb_fde_t *F;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for(i = 0; i < RB_FD_HASH_SIZE; i++)
|
|
|
|
{
|
|
|
|
bucket = &rb_fd_table[i];
|
|
|
|
|
|
|
|
if(rb_dlink_list_length(bucket) <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
RB_DLINK_FOREACH(ptr, bucket->head)
|
|
|
|
{
|
|
|
|
F = ptr->data;
|
|
|
|
if(F == NULL || !IsFDOpen(F))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cb(F->fd, F->desc ? F->desc : empty, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rb_note() - set the fd note
|
|
|
|
*
|
|
|
|
* Note: must be careful not to overflow rb_fd_table[fd].desc when
|
|
|
|
* calling.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rb_note(rb_fde_t *F, const char *string)
|
|
|
|
{
|
|
|
|
if(F == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rb_free(F->desc);
|
|
|
|
F->desc = rb_strndup(string, FD_DESC_SZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-07-29 22:41:27 +00:00
|
|
|
rb_set_type(rb_fde_t *F, uint8_t type)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
/* if the caller is calling this, lets assume they have a clue */
|
|
|
|
F->type = type;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-29 22:41:27 +00:00
|
|
|
uint8_t
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_get_type(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
return F->type;
|
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
int
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_fd_ssl(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
if(F == NULL)
|
|
|
|
return 0;
|
|
|
|
if(F->type & RB_FD_SSL)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-20 05:41:01 +00:00
|
|
|
rb_platform_fd_t
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_get_fd(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
if(F == NULL)
|
|
|
|
return -1;
|
2008-12-02 23:49:39 +00:00
|
|
|
return (F->fd);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_fde_t *
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_get_fde(rb_platform_fd_t fd)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
return rb_find_fd(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
rb_read(rb_fde_t *F, void *buf, int count)
|
|
|
|
{
|
2008-04-12 14:43:12 +00:00
|
|
|
ssize_t ret;
|
2008-04-01 16:52:26 +00:00
|
|
|
if(F == NULL)
|
|
|
|
return 0;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2014-03-03 04:25:47 +00:00
|
|
|
/* This needs to be *before* RB_FD_SOCKET otherwise you'll process
|
|
|
|
* an SSL socket as a regular socket
|
2008-04-01 16:52:26 +00:00
|
|
|
*/
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
if(F->type & RB_FD_SSL)
|
|
|
|
{
|
|
|
|
return rb_ssl_read(F, buf, count);
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
if(F->type & RB_FD_SOCKET)
|
|
|
|
{
|
|
|
|
ret = recv(F->fd, buf, count, 0);
|
|
|
|
if(ret < 0)
|
|
|
|
{
|
|
|
|
rb_get_errno();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* default case */
|
|
|
|
return read(F->fd, buf, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
rb_write(rb_fde_t *F, const void *buf, int count)
|
|
|
|
{
|
2008-04-12 14:43:12 +00:00
|
|
|
ssize_t ret;
|
2008-04-01 16:52:26 +00:00
|
|
|
if(F == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
if(F->type & RB_FD_SSL)
|
|
|
|
{
|
|
|
|
return rb_ssl_write(F, buf, count);
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
if(F->type & RB_FD_SOCKET)
|
|
|
|
{
|
|
|
|
ret = send(F->fd, buf, count, MSG_NOSIGNAL);
|
2008-12-02 23:49:39 +00:00
|
|
|
if(ret < 0)
|
|
|
|
{
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_get_errno();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return write(F->fd, buf, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(HAVE_SSL) || defined(WIN32) || !defined(HAVE_WRITEV)
|
|
|
|
static ssize_t
|
|
|
|
rb_fake_writev(rb_fde_t *F, const struct rb_iovec *vp, size_t vpcount)
|
|
|
|
{
|
2008-04-12 14:43:12 +00:00
|
|
|
ssize_t count = 0;
|
2008-04-01 16:52:26 +00:00
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
while(vpcount-- > 0)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-04-12 14:43:12 +00:00
|
|
|
ssize_t written = rb_write(F, vp->iov_base, vp->iov_len);
|
2008-04-01 16:52:26 +00:00
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
if(written <= 0)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(count > 0)
|
|
|
|
return count;
|
|
|
|
else
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
count += written;
|
|
|
|
vp++;
|
|
|
|
}
|
|
|
|
return (count);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(WIN32) || !defined(HAVE_WRITEV)
|
|
|
|
ssize_t
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_writev(rb_fde_t *F, struct rb_iovec * vecount, int count)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
return rb_fake_writev(F, vecount, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
ssize_t
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_writev(rb_fde_t *F, struct rb_iovec * vector, int count)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
if(F == NULL)
|
|
|
|
{
|
2008-04-01 16:52:26 +00:00
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_SSL
|
|
|
|
if(F->type & RB_FD_SSL)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
return rb_fake_writev(F, vector, count);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
#endif /* HAVE_SSL */
|
|
|
|
#ifdef HAVE_SENDMSG
|
|
|
|
if(F->type & RB_FD_SOCKET)
|
|
|
|
{
|
|
|
|
struct msghdr msg;
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.msg_iov = (struct iovec *)vector;
|
|
|
|
msg.msg_iovlen = count;
|
|
|
|
return sendmsg(F->fd, &msg, MSG_NOSIGNAL);
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif /* HAVE_SENDMSG */
|
|
|
|
return writev(F->fd, (struct iovec *)vector, count);
|
|
|
|
|
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
#endif
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
|
2014-03-03 04:25:47 +00:00
|
|
|
/*
|
2008-04-01 16:52:26 +00:00
|
|
|
* From: Thomas Helvey <tomh@inxpress.net>
|
|
|
|
*/
|
|
|
|
static const char *IpQuadTab[] = {
|
|
|
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
|
|
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
|
|
|
|
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
|
|
|
|
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
|
|
|
|
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
|
|
|
|
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
|
|
|
|
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
|
|
|
|
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
|
|
|
|
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
|
|
|
|
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
|
|
|
|
"100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
|
|
|
|
"110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
|
|
|
|
"120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
|
|
|
|
"130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
|
|
|
|
"140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
|
|
|
|
"150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
|
|
|
|
"160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
|
|
|
|
"170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
|
|
|
|
"180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
|
|
|
|
"190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
|
|
|
|
"200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
|
|
|
|
"210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
|
|
|
|
"220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
|
|
|
|
"230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
|
|
|
|
"240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
|
|
|
|
"250", "251", "252", "253", "254", "255"
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inetntoa - in_addr to string
|
|
|
|
* changed name to remove collision possibility and
|
|
|
|
* so behaviour is guaranteed to take a pointer arg.
|
|
|
|
* -avalon 23/11/92
|
|
|
|
* inet_ntoa -- returned the dotted notation of a given
|
|
|
|
* internet number
|
|
|
|
* argv 11/90).
|
|
|
|
* inet_ntoa -- its broken on some Ultrix/Dynix too. -avalon
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
inetntoa(const char *in)
|
|
|
|
{
|
|
|
|
static char buf[16];
|
|
|
|
char *bufptr = buf;
|
2008-12-02 23:49:39 +00:00
|
|
|
const unsigned char *a = (const unsigned char *)in;
|
2008-04-01 16:52:26 +00:00
|
|
|
const char *n;
|
|
|
|
|
|
|
|
n = IpQuadTab[*a++];
|
2008-12-02 23:49:39 +00:00
|
|
|
while(*n)
|
2008-04-01 16:52:26 +00:00
|
|
|
*bufptr++ = *n++;
|
|
|
|
*bufptr++ = '.';
|
|
|
|
n = IpQuadTab[*a++];
|
2008-12-02 23:49:39 +00:00
|
|
|
while(*n)
|
2008-04-01 16:52:26 +00:00
|
|
|
*bufptr++ = *n++;
|
|
|
|
*bufptr++ = '.';
|
|
|
|
n = IpQuadTab[*a++];
|
2008-12-02 23:49:39 +00:00
|
|
|
while(*n)
|
2008-04-01 16:52:26 +00:00
|
|
|
*bufptr++ = *n++;
|
|
|
|
*bufptr++ = '.';
|
|
|
|
n = IpQuadTab[*a];
|
2008-12-02 23:49:39 +00:00
|
|
|
while(*n)
|
2008-04-01 16:52:26 +00:00
|
|
|
*bufptr++ = *n++;
|
|
|
|
*bufptr = '\0';
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* WARNING: Don't even consider trying to compile this on a system where
|
|
|
|
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
|
|
|
|
*/
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
static const char *inet_ntop4(const unsigned char *src, char *dst, unsigned int size);
|
|
|
|
static const char *inet_ntop6(const unsigned char *src, char *dst, unsigned int size);
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
/* const char *
|
|
|
|
* inet_ntop4(src, dst, size)
|
|
|
|
* format an IPv4 address
|
|
|
|
* return:
|
|
|
|
* `dst' (as a const)
|
|
|
|
* notes:
|
|
|
|
* (1) uses no statics
|
|
|
|
* (2) takes a unsigned char* not an in_addr as input
|
|
|
|
* author:
|
|
|
|
* Paul Vixie, 1996.
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
inet_ntop4(const unsigned char *src, char *dst, unsigned int size)
|
|
|
|
{
|
|
|
|
if(size < 16)
|
|
|
|
return NULL;
|
2010-12-15 20:48:26 +00:00
|
|
|
return strcpy(dst, inetntoa((const char *)src));
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* const char *
|
|
|
|
* inet_ntop6(src, dst, size)
|
|
|
|
* convert IPv6 binary address into presentation (printable) format
|
|
|
|
* author:
|
|
|
|
* Paul Vixie, 1996.
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
inet_ntop6(const unsigned char *src, char *dst, unsigned int size)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Note that int32_t and int16_t need only be "at least" large enough
|
|
|
|
* to contain a value of the specified size. On some systems, like
|
|
|
|
* Crays, there is no such thing as an integer variable with 16 bits.
|
|
|
|
* Keep this in mind if you think this function should have been coded
|
|
|
|
* to use pointer overlays. All the world's not a VAX.
|
|
|
|
*/
|
|
|
|
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
int base, len;
|
|
|
|
}
|
|
|
|
best, cur;
|
|
|
|
unsigned int words[IN6ADDRSZ / INT16SZ];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Preprocess:
|
|
|
|
* Copy the input (bytewise) array into a wordwise array.
|
|
|
|
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
|
|
|
*/
|
|
|
|
memset(words, '\0', sizeof words);
|
2008-12-02 23:49:39 +00:00
|
|
|
for(i = 0; i < IN6ADDRSZ; i += 2)
|
2008-04-01 16:52:26 +00:00
|
|
|
words[i / 2] = (src[i] << 8) | src[i + 1];
|
|
|
|
best.base = -1;
|
|
|
|
best.len = 0;
|
|
|
|
cur.base = -1;
|
|
|
|
cur.len = 0;
|
2008-12-02 23:49:39 +00:00
|
|
|
for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(words[i] == 0)
|
|
|
|
{
|
|
|
|
if(cur.base == -1)
|
|
|
|
cur.base = i, cur.len = 1;
|
|
|
|
else
|
|
|
|
cur.len++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(cur.base != -1)
|
|
|
|
{
|
|
|
|
if(best.base == -1 || cur.len > best.len)
|
|
|
|
best = cur;
|
|
|
|
cur.base = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(cur.base != -1)
|
|
|
|
{
|
|
|
|
if(best.base == -1 || cur.len > best.len)
|
|
|
|
best = cur;
|
|
|
|
}
|
|
|
|
if(best.base != -1 && best.len < 2)
|
|
|
|
best.base = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Format the result.
|
|
|
|
*/
|
|
|
|
tp = tmp;
|
2008-12-02 23:49:39 +00:00
|
|
|
for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
/* Are we inside the best run of 0x00's? */
|
|
|
|
if(best.base != -1 && i >= best.base && i < (best.base + best.len))
|
|
|
|
{
|
|
|
|
if(i == best.base)
|
|
|
|
{
|
|
|
|
if(i == 0)
|
|
|
|
*tp++ = '0';
|
|
|
|
*tp++ = ':';
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Are we following an initial run of 0x00s or any real hex? */
|
|
|
|
if(i != 0)
|
|
|
|
*tp++ = ':';
|
|
|
|
/* Is this address an encapsulated IPv4? */
|
|
|
|
if(i == 6 && best.base == 0 &&
|
|
|
|
(best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
|
|
|
|
{
|
|
|
|
if(!inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp)))
|
|
|
|
return (NULL);
|
|
|
|
tp += strlen(tp);
|
|
|
|
break;
|
|
|
|
}
|
2016-02-10 01:25:32 +00:00
|
|
|
tp += sprintf(tp, "%x", words[i]);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
/* Was it a trailing run of 0x00's? */
|
|
|
|
if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
|
|
|
|
*tp++ = ':';
|
|
|
|
*tp++ = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for overflow, copy, and we're done.
|
|
|
|
*/
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
if((unsigned int)(tp - tmp) > size)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
return (NULL);
|
|
|
|
}
|
2010-12-15 20:48:26 +00:00
|
|
|
return memcpy(dst, tmp, tp - tmp);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2019-08-31 15:10:50 +00:00
|
|
|
rb_inet_pton_sock(const char *src, struct sockaddr_storage *dst)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2017-08-23 20:14:29 +00:00
|
|
|
memset(dst, 0, sizeof(*dst));
|
2008-12-02 23:49:39 +00:00
|
|
|
if(rb_inet_pton(AF_INET, src, &((struct sockaddr_in *)dst)->sin_addr))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2016-03-31 08:00:29 +00:00
|
|
|
SET_SS_FAMILY(dst, AF_INET);
|
2016-09-03 13:36:17 +00:00
|
|
|
SET_SS_PORT(dst, 0);
|
2008-04-01 16:52:26 +00:00
|
|
|
SET_SS_LEN(dst, sizeof(struct sockaddr_in));
|
|
|
|
return 1;
|
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
else if(rb_inet_pton(AF_INET6, src, &((struct sockaddr_in6 *)dst)->sin6_addr))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2016-03-31 08:00:29 +00:00
|
|
|
SET_SS_FAMILY(dst, AF_INET6);
|
2016-09-03 13:36:17 +00:00
|
|
|
SET_SS_PORT(dst, 0);
|
2008-04-01 16:52:26 +00:00
|
|
|
SET_SS_LEN(dst, sizeof(struct sockaddr_in6));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
rb_inet_ntop_sock(struct sockaddr *src, char *dst, unsigned int size)
|
|
|
|
{
|
|
|
|
switch (src->sa_family)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
2008-12-02 23:49:39 +00:00
|
|
|
return (rb_inet_ntop(AF_INET, &((struct sockaddr_in *)src)->sin_addr, dst, size));
|
2008-04-01 16:52:26 +00:00
|
|
|
case AF_INET6:
|
2008-12-02 23:49:39 +00:00
|
|
|
return (rb_inet_ntop
|
|
|
|
(AF_INET6, &((struct sockaddr_in6 *)src)->sin6_addr, dst, size));
|
2008-04-01 16:52:26 +00:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* char *
|
|
|
|
* rb_inet_ntop(af, src, dst, size)
|
|
|
|
* convert a network format address to presentation format.
|
|
|
|
* return:
|
|
|
|
* pointer to presentation format address (`dst'), or NULL (see errno).
|
|
|
|
* author:
|
|
|
|
* Paul Vixie, 1996.
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
rb_inet_ntop(int af, const void *src, char *dst, unsigned int size)
|
|
|
|
{
|
|
|
|
switch (af)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
|
|
|
return (inet_ntop4(src, dst, size));
|
|
|
|
case AF_INET6:
|
2008-12-02 23:49:39 +00:00
|
|
|
if(IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src) ||
|
|
|
|
IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src))
|
2008-04-01 16:52:26 +00:00
|
|
|
return (inet_ntop4
|
2008-12-02 23:49:39 +00:00
|
|
|
((const unsigned char *)&((const struct in6_addr *)src)->
|
|
|
|
s6_addr[12], dst, size));
|
2008-04-01 16:52:26 +00:00
|
|
|
else
|
|
|
|
return (inet_ntop6(src, dst, size));
|
|
|
|
default:
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* WARNING: Don't even consider trying to compile this on a system where
|
|
|
|
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* int
|
|
|
|
* rb_inet_pton(af, src, dst)
|
|
|
|
* convert from presentation format (which usually means ASCII printable)
|
|
|
|
* to network format (which is usually some kind of binary format).
|
|
|
|
* return:
|
|
|
|
* 1 if the address was valid for the specified address family
|
|
|
|
* 0 if the address wasn't valid (`dst' is untouched in this case)
|
|
|
|
* -1 if some other error occurred (`dst' is untouched in this case, too)
|
|
|
|
* author:
|
|
|
|
* Paul Vixie, 1996.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* int
|
|
|
|
* inet_pton4(src, dst)
|
|
|
|
* like inet_aton() but without all the hexadecimal and shorthand.
|
|
|
|
* return:
|
|
|
|
* 1 if `src' is a valid dotted quad, else 0.
|
|
|
|
* notice:
|
|
|
|
* does not touch `dst' unless it's returning 1.
|
|
|
|
* author:
|
|
|
|
* Paul Vixie, 1996.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
inet_pton4(const char *src, unsigned char *dst)
|
|
|
|
{
|
|
|
|
int saw_digit, octets, ch;
|
|
|
|
unsigned char tmp[INADDRSZ], *tp;
|
|
|
|
|
|
|
|
saw_digit = 0;
|
|
|
|
octets = 0;
|
|
|
|
*(tp = tmp) = 0;
|
2008-12-02 23:49:39 +00:00
|
|
|
while((ch = *src++) != '\0')
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
if(ch >= '0' && ch <= '9')
|
|
|
|
{
|
|
|
|
unsigned int new = *tp * 10 + (ch - '0');
|
|
|
|
|
|
|
|
if(new > 255)
|
|
|
|
return (0);
|
|
|
|
*tp = new;
|
|
|
|
if(!saw_digit)
|
|
|
|
{
|
|
|
|
if(++octets > 4)
|
|
|
|
return (0);
|
|
|
|
saw_digit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(ch == '.' && saw_digit)
|
|
|
|
{
|
|
|
|
if(octets == 4)
|
|
|
|
return (0);
|
|
|
|
*++tp = 0;
|
|
|
|
saw_digit = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if(octets < 4)
|
|
|
|
return (0);
|
|
|
|
memcpy(dst, tmp, INADDRSZ);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* int
|
|
|
|
* inet_pton6(src, dst)
|
|
|
|
* convert presentation level address to network order binary form.
|
|
|
|
* return:
|
|
|
|
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
|
|
|
|
* notice:
|
|
|
|
* (1) does not touch `dst' unless it's returning 1.
|
|
|
|
* (2) :: in a full address is silently ignored.
|
|
|
|
* credit:
|
|
|
|
* inspired by Mark Andrews.
|
|
|
|
* author:
|
|
|
|
* Paul Vixie, 1996.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
inet_pton6(const char *src, unsigned char *dst)
|
|
|
|
{
|
|
|
|
static const char xdigits[] = "0123456789abcdef";
|
|
|
|
unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
|
|
|
|
const char *curtok;
|
|
|
|
int ch, saw_xdigit;
|
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
tp = memset(tmp, '\0', IN6ADDRSZ);
|
|
|
|
endp = tp + IN6ADDRSZ;
|
|
|
|
colonp = NULL;
|
|
|
|
/* Leading :: requires some special handling. */
|
|
|
|
if(*src == ':')
|
|
|
|
if(*++src != ':')
|
|
|
|
return (0);
|
|
|
|
curtok = src;
|
|
|
|
saw_xdigit = 0;
|
|
|
|
val = 0;
|
2015-04-20 05:55:20 +00:00
|
|
|
while((ch = tolower((unsigned char)*src++)) != '\0')
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
const char *pch;
|
|
|
|
|
|
|
|
pch = strchr(xdigits, ch);
|
|
|
|
if(pch != NULL)
|
|
|
|
{
|
|
|
|
val <<= 4;
|
|
|
|
val |= (pch - xdigits);
|
|
|
|
if(val > 0xffff)
|
|
|
|
return (0);
|
|
|
|
saw_xdigit = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(ch == ':')
|
|
|
|
{
|
|
|
|
curtok = src;
|
|
|
|
if(!saw_xdigit)
|
|
|
|
{
|
|
|
|
if(colonp)
|
|
|
|
return (0);
|
|
|
|
colonp = tp;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if(*src == '\0')
|
|
|
|
{
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if(tp + INT16SZ > endp)
|
|
|
|
return (0);
|
2008-12-02 23:49:39 +00:00
|
|
|
*tp++ = (unsigned char)(val >> 8) & 0xff;
|
|
|
|
*tp++ = (unsigned char)val & 0xff;
|
2008-04-01 16:52:26 +00:00
|
|
|
saw_xdigit = 0;
|
|
|
|
val = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(*src != '\0' && ch == '.')
|
|
|
|
{
|
|
|
|
if(((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0)
|
|
|
|
{
|
|
|
|
tp += INADDRSZ;
|
|
|
|
saw_xdigit = 0;
|
|
|
|
break; /* '\0' was seen by inet_pton4(). */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if(saw_xdigit)
|
|
|
|
{
|
|
|
|
if(tp + INT16SZ > endp)
|
|
|
|
return (0);
|
2008-12-02 23:49:39 +00:00
|
|
|
*tp++ = (unsigned char)(val >> 8) & 0xff;
|
|
|
|
*tp++ = (unsigned char)val & 0xff;
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
if(colonp != NULL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Since some memmove()'s erroneously fail to handle
|
|
|
|
* overlapping regions, we'll do the shift by hand.
|
|
|
|
*/
|
|
|
|
const int n = tp - colonp;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(tp == endp)
|
|
|
|
return (0);
|
2008-12-02 23:49:39 +00:00
|
|
|
for(i = 1; i <= n; i++)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
endp[-i] = colonp[n - i];
|
|
|
|
colonp[n - i] = 0;
|
|
|
|
}
|
|
|
|
tp = endp;
|
|
|
|
}
|
|
|
|
if(tp != endp)
|
|
|
|
return (0);
|
|
|
|
memcpy(dst, tmp, IN6ADDRSZ);
|
|
|
|
return (1);
|
|
|
|
}
|
2017-08-18 12:03:59 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
int
|
|
|
|
rb_inet_pton(int af, const char *src, void *dst)
|
|
|
|
{
|
|
|
|
switch (af)
|
|
|
|
{
|
|
|
|
case AF_INET:
|
|
|
|
return (inet_pton4(src, dst));
|
|
|
|
case AF_INET6:
|
|
|
|
/* Somebody might have passed as an IPv4 address this is sick but it works */
|
|
|
|
if(inet_pton4(src, dst))
|
|
|
|
{
|
|
|
|
char tmp[HOSTIPLEN];
|
2016-02-10 01:25:32 +00:00
|
|
|
sprintf(tmp, "::ffff:%s", src);
|
2008-04-01 16:52:26 +00:00
|
|
|
return (inet_pton6(tmp, dst));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (inet_pton6(src, dst));
|
|
|
|
default:
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef HAVE_SOCKETPAIR
|
2008-12-02 23:49:39 +00:00
|
|
|
|
|
|
|
/* mostly based on perl's emulation of socketpair udp */
|
|
|
|
static int
|
|
|
|
rb_inet_socketpair_udp(rb_fde_t **newF1, rb_fde_t **newF2)
|
|
|
|
{
|
|
|
|
struct sockaddr_in addr[2];
|
|
|
|
rb_socklen_t size = sizeof(struct sockaddr_in);
|
|
|
|
rb_fde_t *F[2];
|
2016-03-20 05:36:35 +00:00
|
|
|
rb_platform_fd_t fd[2];
|
2008-12-02 23:49:39 +00:00
|
|
|
int i, got;
|
|
|
|
unsigned short port;
|
2008-12-22 09:49:01 +00:00
|
|
|
struct timeval wait = { 0, 100000 };
|
|
|
|
int max;
|
|
|
|
fd_set rset;
|
|
|
|
struct sockaddr_in readfrom;
|
|
|
|
unsigned short buf[2];
|
|
|
|
int o_errno;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
|
|
|
|
for(i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
F[i] = rb_socket(AF_INET, SOCK_DGRAM, 0, "udp socketpair");
|
|
|
|
if(F[i] == NULL)
|
|
|
|
goto failed;
|
|
|
|
addr[i].sin_family = AF_INET;
|
|
|
|
addr[i].sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
addr[i].sin_port = 0;
|
|
|
|
if(bind(rb_get_fd(F[i]), (struct sockaddr *)&addr[i], sizeof(struct sockaddr_in)))
|
|
|
|
goto failed;
|
|
|
|
fd[i] = rb_get_fd(F[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
if(getsockname(fd[i], (struct sockaddr *)&addr[i], &size))
|
|
|
|
goto failed;
|
|
|
|
if(size != sizeof(struct sockaddr_in))
|
|
|
|
goto failed;
|
|
|
|
if(connect(fd[!i], (struct sockaddr *)&addr[i], sizeof(struct sockaddr_in)) == -1)
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
port = addr[i].sin_port;
|
|
|
|
got = rb_write(F[i], &port, sizeof(port));
|
|
|
|
if(got != sizeof(port))
|
|
|
|
{
|
|
|
|
if(got == -1)
|
|
|
|
goto failed;
|
|
|
|
goto abort_failed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-22 09:49:01 +00:00
|
|
|
max = fd[1] > fd[0] ? fd[1] : fd[0];
|
2008-12-02 23:49:39 +00:00
|
|
|
FD_ZERO(&rset);
|
|
|
|
FD_SET(fd[0], &rset);
|
|
|
|
FD_SET(fd[1], &rset);
|
|
|
|
got = select(max + 1, &rset, NULL, NULL, &wait);
|
|
|
|
if(got != 2 || !FD_ISSET(fd[0], &rset) || !FD_ISSET(fd[1], &rset))
|
|
|
|
{
|
|
|
|
if(got == -1)
|
|
|
|
goto failed;
|
|
|
|
goto abort_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 2; i++)
|
|
|
|
{
|
|
|
|
#ifdef MSG_DONTWAIT
|
|
|
|
int flag = MSG_DONTWAIT
|
|
|
|
#else
|
|
|
|
int flag = 0;
|
|
|
|
#endif
|
|
|
|
got = recvfrom(rb_get_fd(F[i]), (char *)&buf, sizeof(buf), flag,
|
|
|
|
(struct sockaddr *)&readfrom, &size);
|
|
|
|
if(got == -1)
|
|
|
|
goto failed;
|
|
|
|
if(got != sizeof(port)
|
|
|
|
|| size != sizeof(struct sockaddr_in)
|
|
|
|
|| buf[0] != (unsigned short)addr[!i].sin_port
|
|
|
|
|| readfrom.sin_family != addr[!i].sin_family
|
|
|
|
|| readfrom.sin_addr.s_addr != addr[!i].sin_addr.s_addr
|
|
|
|
|| readfrom.sin_port != addr[!i].sin_port)
|
|
|
|
goto abort_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
*newF1 = F[0];
|
|
|
|
*newF2 = F[1];
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2016-03-20 05:36:35 +00:00
|
|
|
#ifndef ECONNABORTED
|
2008-12-02 23:49:39 +00:00
|
|
|
#define ECONNABORTED WSAECONNABORTED
|
2016-03-20 05:36:35 +00:00
|
|
|
#endif
|
2008-12-02 23:49:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
abort_failed:
|
|
|
|
rb_get_errno();
|
|
|
|
errno = ECONNABORTED;
|
|
|
|
failed:
|
|
|
|
if(errno != ECONNABORTED)
|
|
|
|
rb_get_errno();
|
2008-12-22 09:49:01 +00:00
|
|
|
o_errno = errno;
|
2008-12-02 23:49:39 +00:00
|
|
|
if(F[0] != NULL)
|
|
|
|
rb_close(F[0]);
|
|
|
|
if(F[1] != NULL)
|
|
|
|
rb_close(F[1]);
|
|
|
|
errno = o_errno;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
int
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_inet_socketpair(int family, int type, int protocol, rb_platform_fd_t fd[2])
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
int listener = -1;
|
|
|
|
int connector = -1;
|
|
|
|
int acceptor = -1;
|
|
|
|
struct sockaddr_in listen_addr;
|
|
|
|
struct sockaddr_in connect_addr;
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_socklen_t size;
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
if(protocol || family != AF_INET)
|
|
|
|
{
|
|
|
|
errno = EAFNOSUPPORT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(!fd)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
listener = socket(AF_INET, type, 0);
|
|
|
|
if(listener == -1)
|
|
|
|
return -1;
|
|
|
|
memset(&listen_addr, 0, sizeof(listen_addr));
|
|
|
|
listen_addr.sin_family = AF_INET;
|
|
|
|
listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
listen_addr.sin_port = 0; /* kernel choses port. */
|
2008-12-02 23:49:39 +00:00
|
|
|
if(bind(listener, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) == -1)
|
2008-04-01 16:52:26 +00:00
|
|
|
goto tidy_up_and_fail;
|
|
|
|
if(listen(listener, 1) == -1)
|
|
|
|
goto tidy_up_and_fail;
|
|
|
|
|
|
|
|
connector = socket(AF_INET, type, 0);
|
|
|
|
if(connector == -1)
|
|
|
|
goto tidy_up_and_fail;
|
|
|
|
/* We want to find out the port number to connect to. */
|
|
|
|
size = sizeof(connect_addr);
|
2008-12-02 23:49:39 +00:00
|
|
|
if(getsockname(listener, (struct sockaddr *)&connect_addr, &size) == -1)
|
2008-04-01 16:52:26 +00:00
|
|
|
goto tidy_up_and_fail;
|
|
|
|
if(size != sizeof(connect_addr))
|
|
|
|
goto abort_tidy_up_and_fail;
|
2008-12-02 23:49:39 +00:00
|
|
|
if(connect(connector, (struct sockaddr *)&connect_addr, sizeof(connect_addr)) == -1)
|
2008-04-01 16:52:26 +00:00
|
|
|
goto tidy_up_and_fail;
|
|
|
|
|
|
|
|
size = sizeof(listen_addr);
|
2008-12-02 23:49:39 +00:00
|
|
|
acceptor = accept(listener, (struct sockaddr *)&listen_addr, &size);
|
2008-04-01 16:52:26 +00:00
|
|
|
if(acceptor == -1)
|
|
|
|
goto tidy_up_and_fail;
|
|
|
|
if(size != sizeof(listen_addr))
|
|
|
|
goto abort_tidy_up_and_fail;
|
|
|
|
close(listener);
|
|
|
|
/* Now check we are talking to ourself by matching port and host on the
|
|
|
|
two sockets. */
|
2008-12-02 23:49:39 +00:00
|
|
|
if(getsockname(connector, (struct sockaddr *)&connect_addr, &size) == -1)
|
2008-04-01 16:52:26 +00:00
|
|
|
goto tidy_up_and_fail;
|
|
|
|
if(size != sizeof(connect_addr)
|
|
|
|
|| listen_addr.sin_family != connect_addr.sin_family
|
|
|
|
|| listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
|
|
|
|
|| listen_addr.sin_port != connect_addr.sin_port)
|
|
|
|
{
|
|
|
|
goto abort_tidy_up_and_fail;
|
|
|
|
}
|
|
|
|
fd[0] = connector;
|
|
|
|
fd[1] = acceptor;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
abort_tidy_up_and_fail:
|
|
|
|
errno = EINVAL; /* I hope this is portable and appropriate. */
|
|
|
|
|
|
|
|
tidy_up_and_fail:
|
|
|
|
{
|
|
|
|
int save_errno = errno;
|
|
|
|
if(listener != -1)
|
|
|
|
close(listener);
|
|
|
|
if(connector != -1)
|
|
|
|
close(connector);
|
|
|
|
if(acceptor != -1)
|
|
|
|
close(acceptor);
|
|
|
|
errno = save_errno;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static void (*setselect_handler) (rb_fde_t *, unsigned int, PF *, void *);
|
|
|
|
static int (*select_handler) (long);
|
|
|
|
static int (*setup_fd_handler) (rb_fde_t *);
|
|
|
|
static int (*io_sched_event) (struct ev_entry *, int);
|
|
|
|
static void (*io_unsched_event) (struct ev_entry *);
|
|
|
|
static int (*io_supports_event) (void);
|
|
|
|
static void (*io_init_event) (void);
|
|
|
|
static char iotype[25];
|
|
|
|
|
|
|
|
const char *
|
|
|
|
rb_get_iotype(void)
|
|
|
|
{
|
|
|
|
return iotype;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
rb_unsupported_event(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_kqueue(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_kqueue())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_kqueue;
|
|
|
|
select_handler = rb_select_kqueue;
|
|
|
|
setup_fd_handler = rb_setup_fd_kqueue;
|
|
|
|
io_sched_event = rb_kqueue_sched_event;
|
|
|
|
io_unsched_event = rb_kqueue_unsched_event;
|
|
|
|
io_init_event = rb_kqueue_init_event;
|
|
|
|
io_supports_event = rb_kqueue_supports_event;
|
|
|
|
rb_strlcpy(iotype, "kqueue", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
return -1;
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_epoll(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_epoll())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_epoll;
|
|
|
|
select_handler = rb_select_epoll;
|
|
|
|
setup_fd_handler = rb_setup_fd_epoll;
|
|
|
|
io_sched_event = rb_epoll_sched_event;
|
|
|
|
io_unsched_event = rb_epoll_unsched_event;
|
|
|
|
io_supports_event = rb_epoll_supports_event;
|
|
|
|
io_init_event = rb_epoll_init_event;
|
|
|
|
rb_strlcpy(iotype, "epoll", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_ports(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_ports())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_ports;
|
|
|
|
select_handler = rb_select_ports;
|
2008-12-02 23:49:39 +00:00
|
|
|
setup_fd_handler = rb_setup_fd_ports;
|
2008-12-22 09:49:01 +00:00
|
|
|
io_sched_event = rb_ports_sched_event;
|
|
|
|
io_unsched_event = rb_ports_unsched_event;
|
|
|
|
io_init_event = rb_ports_init_event;
|
|
|
|
io_supports_event = rb_ports_supports_event;
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_strlcpy(iotype, "ports", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_devpoll(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_devpoll())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_devpoll;
|
|
|
|
select_handler = rb_select_devpoll;
|
2008-12-02 23:49:39 +00:00
|
|
|
setup_fd_handler = rb_setup_fd_devpoll;
|
2008-04-01 16:52:26 +00:00
|
|
|
io_sched_event = NULL;
|
|
|
|
io_unsched_event = NULL;
|
|
|
|
io_init_event = NULL;
|
|
|
|
io_supports_event = rb_unsupported_event;
|
|
|
|
rb_strlcpy(iotype, "devpoll", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_sigio(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_sigio())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_sigio;
|
|
|
|
select_handler = rb_select_sigio;
|
2008-12-02 23:49:39 +00:00
|
|
|
setup_fd_handler = rb_setup_fd_sigio;
|
2008-04-01 16:52:26 +00:00
|
|
|
io_sched_event = rb_sigio_sched_event;
|
2008-12-02 23:49:39 +00:00
|
|
|
io_unsched_event = rb_sigio_unsched_event;
|
|
|
|
io_supports_event = rb_sigio_supports_event;
|
|
|
|
io_init_event = rb_sigio_init_event;
|
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_strlcpy(iotype, "sigio", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
static int
|
2008-04-01 16:52:26 +00:00
|
|
|
try_poll(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_poll())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_poll;
|
|
|
|
select_handler = rb_select_poll;
|
2008-12-02 23:49:39 +00:00
|
|
|
setup_fd_handler = rb_setup_fd_poll;
|
2008-04-01 16:52:26 +00:00
|
|
|
io_sched_event = NULL;
|
|
|
|
io_unsched_event = NULL;
|
|
|
|
io_init_event = NULL;
|
|
|
|
io_supports_event = rb_unsupported_event;
|
|
|
|
rb_strlcpy(iotype, "poll", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_win32(void)
|
|
|
|
{
|
|
|
|
if(!rb_init_netio_win32())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_win32;
|
|
|
|
select_handler = rb_select_win32;
|
2008-12-02 23:49:39 +00:00
|
|
|
setup_fd_handler = rb_setup_fd_win32;
|
2008-04-01 16:52:26 +00:00
|
|
|
io_sched_event = NULL;
|
|
|
|
io_unsched_event = NULL;
|
|
|
|
io_init_event = NULL;
|
|
|
|
io_supports_event = rb_unsupported_event;
|
|
|
|
rb_strlcpy(iotype, "win32", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
try_select(void)
|
2008-12-02 23:49:39 +00:00
|
|
|
{
|
2008-04-01 16:52:26 +00:00
|
|
|
if(!rb_init_netio_select())
|
|
|
|
{
|
|
|
|
setselect_handler = rb_setselect_select;
|
|
|
|
select_handler = rb_select_select;
|
2008-12-02 23:49:39 +00:00
|
|
|
setup_fd_handler = rb_setup_fd_select;
|
2008-04-01 16:52:26 +00:00
|
|
|
io_sched_event = NULL;
|
|
|
|
io_unsched_event = NULL;
|
|
|
|
io_init_event = NULL;
|
|
|
|
io_supports_event = rb_unsupported_event;
|
|
|
|
rb_strlcpy(iotype, "select", sizeof(iotype));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_io_sched_event(struct ev_entry *ev, int when)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
if(ev == NULL || io_supports_event == NULL || io_sched_event == NULL
|
|
|
|
|| !io_supports_event())
|
2008-04-01 16:52:26 +00:00
|
|
|
return 0;
|
|
|
|
return io_sched_event(ev, when);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_io_unsched_event(struct ev_entry *ev)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
if(ev == NULL || io_supports_event == NULL || io_unsched_event == NULL
|
|
|
|
|| !io_supports_event())
|
2008-04-01 16:52:26 +00:00
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
io_unsched_event(ev);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
int
|
|
|
|
rb_io_supports_event(void)
|
|
|
|
{
|
|
|
|
if(io_supports_event == NULL)
|
|
|
|
return 0;
|
|
|
|
return io_supports_event();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_io_init_event(void)
|
|
|
|
{
|
|
|
|
io_init_event();
|
|
|
|
rb_event_io_register_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_init_netio(void)
|
|
|
|
{
|
|
|
|
char *ioenv = getenv("LIBRB_USE_IOTYPE");
|
|
|
|
rb_fd_table = rb_malloc(RB_FD_HASH_SIZE * sizeof(rb_dlink_list));
|
|
|
|
rb_init_ssl();
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
if(ioenv != NULL)
|
|
|
|
{
|
|
|
|
if(!strcmp("epoll", ioenv))
|
|
|
|
{
|
|
|
|
if(!try_epoll())
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else if(!strcmp("kqueue", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(!try_kqueue())
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else if(!strcmp("ports", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(!try_ports())
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else if(!strcmp("poll", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(!try_poll())
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else if(!strcmp("devpoll", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(!try_devpoll())
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else if(!strcmp("sigio", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
if(!try_sigio())
|
|
|
|
return;
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else if(!strcmp("select", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
if(!try_select())
|
2008-04-01 16:52:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
if(!strcmp("win32", ioenv))
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
if(!try_win32())
|
2008-04-01 16:52:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!try_kqueue())
|
|
|
|
return;
|
|
|
|
if(!try_epoll())
|
|
|
|
return;
|
|
|
|
if(!try_ports())
|
|
|
|
return;
|
|
|
|
if(!try_devpoll())
|
|
|
|
return;
|
|
|
|
if(!try_sigio())
|
|
|
|
return;
|
|
|
|
if(!try_poll())
|
|
|
|
return;
|
|
|
|
if(!try_win32())
|
|
|
|
return;
|
|
|
|
if(!try_select())
|
|
|
|
return;
|
|
|
|
|
|
|
|
rb_lib_log("rb_init_netio: Could not find any io handlers...giving up");
|
|
|
|
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
void
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_setselect(rb_fde_t *F, unsigned int type, PF * handler, void *client_data)
|
|
|
|
{
|
|
|
|
setselect_handler(F, type, handler, client_data);
|
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
int
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_select(unsigned long timeout)
|
|
|
|
{
|
|
|
|
int ret = select_handler(timeout);
|
|
|
|
free_fds();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
int
|
2008-04-01 16:52:26 +00:00
|
|
|
rb_setup_fd(rb_fde_t *F)
|
|
|
|
{
|
|
|
|
return setup_fd_handler(F);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_ignore_errno(int error)
|
|
|
|
{
|
2008-12-02 23:49:39 +00:00
|
|
|
switch (error)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
#ifdef EINPROGRESS
|
2008-12-02 23:49:39 +00:00
|
|
|
case EINPROGRESS:
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
#if defined EWOULDBLOCK
|
2008-12-02 23:49:39 +00:00
|
|
|
case EWOULDBLOCK:
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
#if defined(EAGAIN) && (EWOULDBLOCK != EAGAIN)
|
2008-12-02 23:49:39 +00:00
|
|
|
case EAGAIN:
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
#ifdef EINTR
|
2008-12-02 23:49:39 +00:00
|
|
|
case EINTR:
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
#ifdef ERESTART
|
2008-12-02 23:49:39 +00:00
|
|
|
case ERESTART:
|
2008-04-01 16:52:26 +00:00
|
|
|
#endif
|
|
|
|
#ifdef ENOBUFS
|
2008-12-02 23:49:39 +00:00
|
|
|
case ENOBUFS:
|
|
|
|
#endif
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
#if defined(HAVE_SENDMSG) && !defined(WIN32)
|
2008-04-01 16:52:26 +00:00
|
|
|
int
|
|
|
|
rb_recv_fd_buf(rb_fde_t *F, void *data, size_t datasize, rb_fde_t **xF, int nfds)
|
|
|
|
{
|
|
|
|
struct msghdr msg;
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
struct iovec iov[1];
|
|
|
|
struct stat st;
|
2008-07-29 22:41:27 +00:00
|
|
|
uint8_t stype = RB_FD_UNKNOWN;
|
2008-04-01 16:52:26 +00:00
|
|
|
const char *desc;
|
2016-03-20 05:33:19 +00:00
|
|
|
rb_platform_fd_t fd, len, x, rfds;
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
int control_len = CMSG_SPACE(sizeof(int) * nfds);
|
|
|
|
|
|
|
|
iov[0].iov_base = data;
|
|
|
|
iov[0].iov_len = datasize;
|
2008-12-02 23:49:39 +00:00
|
|
|
|
2008-04-01 16:52:26 +00:00
|
|
|
msg.msg_name = NULL;
|
|
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_flags = 0;
|
|
|
|
cmsg = alloca(control_len);
|
|
|
|
msg.msg_control = cmsg;
|
|
|
|
msg.msg_controllen = control_len;
|
|
|
|
|
|
|
|
if((len = recvmsg(rb_get_fd(F), &msg, 0)) <= 0)
|
|
|
|
return len;
|
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
if(msg.msg_controllen > 0 && msg.msg_control != NULL
|
|
|
|
&& (cmsg = CMSG_FIRSTHDR(&msg)) != NULL)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
2009-07-09 22:27:01 +00:00
|
|
|
rfds = ((unsigned char *)cmsg + cmsg->cmsg_len - CMSG_DATA(cmsg)) / sizeof(int);
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
for(x = 0; x < nfds && x < rfds; x++)
|
|
|
|
{
|
|
|
|
fd = ((int *)CMSG_DATA(cmsg))[x];
|
|
|
|
stype = RB_FD_UNKNOWN;
|
|
|
|
desc = "remote unknown";
|
|
|
|
if(!fstat(fd, &st))
|
|
|
|
{
|
|
|
|
if(S_ISSOCK(st.st_mode))
|
|
|
|
{
|
|
|
|
stype = RB_FD_SOCKET;
|
|
|
|
desc = "remote socket";
|
|
|
|
}
|
|
|
|
else if(S_ISFIFO(st.st_mode))
|
|
|
|
{
|
|
|
|
stype = RB_FD_PIPE;
|
|
|
|
desc = "remote pipe";
|
|
|
|
}
|
|
|
|
else if(S_ISREG(st.st_mode))
|
|
|
|
{
|
|
|
|
stype = RB_FD_FILE;
|
|
|
|
desc = "remote file";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xF[x] = rb_open(fd, stype, desc);
|
|
|
|
}
|
2008-12-02 23:49:39 +00:00
|
|
|
}
|
|
|
|
else
|
2008-04-01 16:52:26 +00:00
|
|
|
*xF = NULL;
|
2008-12-02 23:49:39 +00:00
|
|
|
return len;
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2008-12-02 23:49:39 +00:00
|
|
|
rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid)
|
2008-04-01 16:52:26 +00:00
|
|
|
{
|
|
|
|
struct msghdr msg;
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
struct iovec iov[1];
|
|
|
|
char empty = '0';
|
|
|
|
|
2015-12-28 03:19:17 +00:00
|
|
|
memset(&msg, 0, sizeof(msg));
|
2008-04-01 16:52:26 +00:00
|
|
|
if(datasize == 0)
|
|
|
|
{
|
|
|
|
iov[0].iov_base = ∅
|
2008-12-02 23:49:39 +00:00
|
|
|
iov[0].iov_len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-04-01 16:52:26 +00:00
|
|
|
iov[0].iov_base = data;
|
|
|
|
iov[0].iov_len = datasize;
|
|
|
|
}
|
|
|
|
msg.msg_iov = iov;
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_name = NULL;
|
|
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_flags = 0;
|
|
|
|
msg.msg_control = NULL;
|
|
|
|
msg.msg_controllen = 0;
|
|
|
|
|
|
|
|
if(count > 0)
|
|
|
|
{
|
2016-03-22 01:52:40 +00:00
|
|
|
size_t ucount = (size_t)count;
|
2008-04-01 16:52:26 +00:00
|
|
|
int len = CMSG_SPACE(sizeof(int) * count);
|
2015-12-28 03:19:17 +00:00
|
|
|
char buf[len];
|
2008-04-01 16:52:26 +00:00
|
|
|
|
|
|
|
msg.msg_control = buf;
|
|
|
|
msg.msg_controllen = len;
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * count);
|
|
|
|
|
2016-03-22 01:52:40 +00:00
|
|
|
for(size_t i = 0; i < ucount; i++)
|
2008-12-02 23:49:39 +00:00
|
|
|
{
|
|
|
|
((int *)CMSG_DATA(cmsg))[i] = rb_get_fd(F[i]);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
|
|
|
msg.msg_controllen = cmsg->cmsg_len;
|
2015-12-28 03:19:17 +00:00
|
|
|
return sendmsg(rb_get_fd(xF), &msg, MSG_NOSIGNAL);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
2015-12-28 03:19:17 +00:00
|
|
|
return sendmsg(rb_get_fd(xF), &msg, MSG_NOSIGNAL);
|
2008-04-01 16:52:26 +00:00
|
|
|
}
|
2016-04-08 08:49:23 +00:00
|
|
|
#else /* defined(HAVE_SENDMSG) && !defined(WIN32) */
|
2008-12-02 23:49:39 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
int
|
|
|
|
rb_recv_fd_buf(rb_fde_t *F, void *data, size_t datasize, rb_fde_t **xF, int nfds)
|
|
|
|
{
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
}
|
2008-04-01 16:52:26 +00:00
|
|
|
|
2008-12-02 23:49:39 +00:00
|
|
|
int
|
|
|
|
rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid)
|
|
|
|
{
|
|
|
|
errno = ENOSYS;
|
|
|
|
return -1;
|
|
|
|
}
|
2016-04-08 08:49:23 +00:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
#endif /* defined(HAVE_SENDMSG) && !defined(WIN32) */
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_ipv4_from_ipv6(const struct sockaddr_in6 *restrict ip6, struct sockaddr_in *restrict ip4)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!memcmp(ip6->sin6_addr.s6_addr, "\x20\x02", 2))
|
|
|
|
{
|
|
|
|
/* 6to4 and similar */
|
|
|
|
memcpy(&ip4->sin_addr, ip6->sin6_addr.s6_addr + 2, 4);
|
|
|
|
}
|
|
|
|
else if (!memcmp(ip6->sin6_addr.s6_addr, "\x20\x01\x00\x00", 4))
|
|
|
|
{
|
|
|
|
/* Teredo */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
((uint8_t *)&ip4->sin_addr)[i] = 0xFF ^
|
|
|
|
ip6->sin6_addr.s6_addr[12 + i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
SET_SS_LEN(ip4, sizeof(struct sockaddr_in));
|
|
|
|
ip4->sin_family = AF_INET;
|
|
|
|
ip4->sin_port = 0;
|
|
|
|
return 1;
|
|
|
|
}
|