Add outgoing SCTP connect support
This commit is contained in:
parent
c6ad9b0c5f
commit
6003ce763c
7 changed files with 174 additions and 41 deletions
|
@ -210,12 +210,14 @@ struct server_conf
|
||||||
#define SERVER_AUTOCONN 0x0020
|
#define SERVER_AUTOCONN 0x0020
|
||||||
#define SERVER_SSL 0x0040
|
#define SERVER_SSL 0x0040
|
||||||
#define SERVER_NO_EXPORT 0x0080
|
#define SERVER_NO_EXPORT 0x0080
|
||||||
|
#define SERVER_SCTP 0x0100
|
||||||
|
|
||||||
#define ServerConfIllegal(x) ((x)->flags & SERVER_ILLEGAL)
|
#define ServerConfIllegal(x) ((x)->flags & SERVER_ILLEGAL)
|
||||||
#define ServerConfEncrypted(x) ((x)->flags & SERVER_ENCRYPTED)
|
#define ServerConfEncrypted(x) ((x)->flags & SERVER_ENCRYPTED)
|
||||||
#define ServerConfCompressed(x) ((x)->flags & SERVER_COMPRESSED)
|
#define ServerConfCompressed(x) ((x)->flags & SERVER_COMPRESSED)
|
||||||
#define ServerConfTb(x) ((x)->flags & SERVER_TB)
|
#define ServerConfTb(x) ((x)->flags & SERVER_TB)
|
||||||
#define ServerConfAutoconn(x) ((x)->flags & SERVER_AUTOCONN)
|
#define ServerConfAutoconn(x) ((x)->flags & SERVER_AUTOCONN)
|
||||||
|
#define ServerConfSCTP(x) ((x)->flags & SERVER_SCTP)
|
||||||
#define ServerConfSSL(x) ((x)->flags & SERVER_SSL)
|
#define ServerConfSSL(x) ((x)->flags & SERVER_SSL)
|
||||||
#define ServerConfNoExport(x) ((x)->flags & SERVER_NO_EXPORT)
|
#define ServerConfNoExport(x) ((x)->flags & SERVER_NO_EXPORT)
|
||||||
|
|
||||||
|
|
|
@ -361,6 +361,7 @@ static struct mode_table connect_table[] = {
|
||||||
{ "compressed", SERVER_COMPRESSED },
|
{ "compressed", SERVER_COMPRESSED },
|
||||||
{ "encrypted", SERVER_ENCRYPTED },
|
{ "encrypted", SERVER_ENCRYPTED },
|
||||||
{ "topicburst", SERVER_TB },
|
{ "topicburst", SERVER_TB },
|
||||||
|
{ "sctp", SERVER_SCTP },
|
||||||
{ "ssl", SERVER_SSL },
|
{ "ssl", SERVER_SSL },
|
||||||
{ "no-export", SERVER_NO_EXPORT },
|
{ "no-export", SERVER_NO_EXPORT },
|
||||||
{ NULL, 0 },
|
{ NULL, 0 },
|
||||||
|
|
|
@ -1031,8 +1031,8 @@ int
|
||||||
serv_connect(struct server_conf *server_p, struct Client *by)
|
serv_connect(struct server_conf *server_p, struct Client *by)
|
||||||
{
|
{
|
||||||
struct Client *client_p;
|
struct Client *client_p;
|
||||||
struct rb_sockaddr_storage sa_connect;
|
struct sockaddr_storage sa_connect[2];
|
||||||
struct rb_sockaddr_storage sa_bind;
|
struct sockaddr_storage sa_bind[ARRAY_SIZE(sa_connect)];
|
||||||
char note[HOSTLEN + 10];
|
char note[HOSTLEN + 10];
|
||||||
rb_fde_t *F;
|
rb_fde_t *F;
|
||||||
|
|
||||||
|
@ -1040,8 +1040,10 @@ serv_connect(struct server_conf *server_p, struct Client *by)
|
||||||
if(server_p == NULL)
|
if(server_p == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
SET_SS_FAMILY(&sa_connect, AF_UNSPEC);
|
for (int i = 0; i < ARRAY_SIZE(sa_connect); i++) {
|
||||||
SET_SS_FAMILY(&sa_bind, AF_UNSPEC);
|
SET_SS_FAMILY(&sa_connect[i], AF_UNSPEC);
|
||||||
|
SET_SS_FAMILY(&sa_bind[i], AF_UNSPEC);
|
||||||
|
}
|
||||||
|
|
||||||
if(server_p->aftype == AF_UNSPEC
|
if(server_p->aftype == AF_UNSPEC
|
||||||
&& GET_SS_FAMILY(&server_p->connect4) == AF_INET
|
&& GET_SS_FAMILY(&server_p->connect4) == AF_INET
|
||||||
|
@ -1049,30 +1051,48 @@ serv_connect(struct server_conf *server_p, struct Client *by)
|
||||||
{
|
{
|
||||||
if(rand() % 2 == 0)
|
if(rand() % 2 == 0)
|
||||||
{
|
{
|
||||||
sa_connect = server_p->connect4;
|
sa_connect[0] = server_p->connect4;
|
||||||
sa_bind = server_p->bind4;
|
sa_connect[1] = server_p->connect6;
|
||||||
|
sa_bind[0] = server_p->bind4;
|
||||||
|
sa_bind[1] = server_p->bind6;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sa_connect = server_p->connect6;
|
sa_connect[0] = server_p->connect6;
|
||||||
sa_bind = server_p->bind6;
|
sa_connect[1] = server_p->connect4;
|
||||||
|
sa_bind[0] = server_p->bind6;
|
||||||
|
sa_bind[1] = server_p->bind4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(server_p->aftype == AF_INET || GET_SS_FAMILY(&server_p->connect4) == AF_INET)
|
else if(server_p->aftype == AF_INET || GET_SS_FAMILY(&server_p->connect4) == AF_INET)
|
||||||
{
|
{
|
||||||
sa_connect = server_p->connect4;
|
sa_connect[0] = server_p->connect4;
|
||||||
sa_bind = server_p->bind4;
|
sa_bind[0] = server_p->bind4;
|
||||||
}
|
}
|
||||||
else if(server_p->aftype == AF_INET6 || GET_SS_FAMILY(&server_p->connect6) == AF_INET6)
|
else if(server_p->aftype == AF_INET6 || GET_SS_FAMILY(&server_p->connect6) == AF_INET6)
|
||||||
{
|
{
|
||||||
sa_connect = server_p->connect6;
|
sa_connect[0] = server_p->connect6;
|
||||||
sa_bind = server_p->bind6;
|
sa_bind[0] = server_p->bind6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* log */
|
/* log */
|
||||||
|
#ifdef HAVE_LIBSCTP
|
||||||
|
if (ServerConfSCTP(server_p) && GET_SS_FAMILY(&sa_connect[1]) != AF_UNSPEC) {
|
||||||
|
char buf2[HOSTLEN + 1];
|
||||||
|
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
rb_inet_ntop_sock((struct sockaddr *)&sa_connect, buf, sizeof(buf));
|
buf2[0] = 0;
|
||||||
|
rb_inet_ntop_sock((struct sockaddr *)&sa_connect[0], buf, sizeof(buf));
|
||||||
|
rb_inet_ntop_sock((struct sockaddr *)&sa_connect[1], buf2, sizeof(buf2));
|
||||||
|
ilog(L_SERVER, "Connect to *[%s] @%s&%s", server_p->name, buf, buf2);
|
||||||
|
} else {
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
buf[0] = 0;
|
||||||
|
rb_inet_ntop_sock((struct sockaddr *)&sa_connect[0], buf, sizeof(buf));
|
||||||
ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf);
|
ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure this server isn't already connected
|
* Make sure this server isn't already connected
|
||||||
|
@ -1099,13 +1119,17 @@ serv_connect(struct server_conf *server_p, struct Client *by)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a socket for the server connection */
|
/* create a socket for the server connection */
|
||||||
if(GET_SS_FAMILY(&sa_connect) == AF_UNSPEC)
|
if(GET_SS_FAMILY(&sa_connect[0]) == AF_UNSPEC) {
|
||||||
{
|
|
||||||
ilog_error("unspecified socket address family");
|
ilog_error("unspecified socket address family");
|
||||||
return 0;
|
return 0;
|
||||||
|
#ifdef HAVE_LIBSCTP
|
||||||
|
} else if (ServerConfSCTP(server_p)) {
|
||||||
|
if ((F = rb_socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP, NULL)) == NULL) {
|
||||||
|
ilog_error("opening a stream socket");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if((F = rb_socket(GET_SS_FAMILY(&sa_connect), SOCK_STREAM, 0, NULL)) == NULL)
|
#endif
|
||||||
{
|
} else if ((F = rb_socket(GET_SS_FAMILY(&sa_connect[0]), SOCK_STREAM, IPPROTO_TCP, NULL)) == NULL) {
|
||||||
ilog_error("opening a stream socket");
|
ilog_error("opening a stream socket");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1126,7 +1150,8 @@ serv_connect(struct server_conf *server_p, struct Client *by)
|
||||||
rb_strlcpy(client_p->sockhost, buf, sizeof(client_p->sockhost));
|
rb_strlcpy(client_p->sockhost, buf, sizeof(client_p->sockhost));
|
||||||
client_p->localClient->F = F;
|
client_p->localClient->F = F;
|
||||||
/* shove the port number into the sockaddr */
|
/* shove the port number into the sockaddr */
|
||||||
SET_SS_PORT(&sa_connect, htons(server_p->port));
|
SET_SS_PORT(&sa_connect[0], htons(server_p->port));
|
||||||
|
SET_SS_PORT(&sa_connect[1], htons(server_p->port));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the initial server evilness, ripped straight from
|
* Set up the initial server evilness, ripped straight from
|
||||||
|
@ -1161,19 +1186,31 @@ serv_connect(struct server_conf *server_p, struct Client *by)
|
||||||
SetConnecting(client_p);
|
SetConnecting(client_p);
|
||||||
rb_dlinkAddTail(client_p, &client_p->node, &global_client_list);
|
rb_dlinkAddTail(client_p, &client_p->node, &global_client_list);
|
||||||
|
|
||||||
if(GET_SS_FAMILY(&sa_bind) == AF_UNSPEC)
|
for (int i = 0; i < ARRAY_SIZE(sa_connect); i++) {
|
||||||
{
|
if (GET_SS_FAMILY(&sa_bind[i]) == AF_UNSPEC) {
|
||||||
if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind4))
|
if (GET_SS_FAMILY(&sa_connect[i]) == GET_SS_FAMILY(&ServerInfo.bind4))
|
||||||
sa_bind = ServerInfo.bind4;
|
sa_bind[i] = ServerInfo.bind4;
|
||||||
if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind6))
|
if (GET_SS_FAMILY(&sa_connect[i]) == GET_SS_FAMILY(&ServerInfo.bind6))
|
||||||
sa_bind = ServerInfo.bind6;
|
sa_bind[i] = ServerInfo.bind6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_connect_tcp(client_p->localClient->F,
|
#ifdef HAVE_LIBSCTP
|
||||||
(struct sockaddr *)&sa_connect,
|
if (ServerConfSCTP(server_p)) {
|
||||||
GET_SS_FAMILY(&sa_bind) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind,
|
rb_connect_sctp(client_p->localClient->F,
|
||||||
|
sa_connect, ARRAY_SIZE(sa_connect), sa_bind, ARRAY_SIZE(sa_bind),
|
||||||
ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
|
ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
|
||||||
client_p, ConfigFileEntry.connect_timeout);
|
client_p, ConfigFileEntry.connect_timeout);
|
||||||
|
} else {
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
rb_connect_tcp(client_p->localClient->F,
|
||||||
|
(struct sockaddr *)&sa_connect[0],
|
||||||
|
GET_SS_FAMILY(&sa_bind[0]) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind[0],
|
||||||
|
ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback,
|
||||||
|
client_p, ConfigFileEntry.connect_timeout);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ void rb_settimeout(rb_fde_t *, time_t, PF *, void *);
|
||||||
void rb_checktimeouts(void *);
|
void rb_checktimeouts(void *);
|
||||||
void rb_connect_tcp(rb_fde_t *, struct sockaddr *, struct sockaddr *, CNCB *, void *, int);
|
void rb_connect_tcp(rb_fde_t *, struct sockaddr *, struct sockaddr *, CNCB *, void *, int);
|
||||||
void rb_connect_tcp_ssl(rb_fde_t *, struct sockaddr *, struct sockaddr *, CNCB *, void *, int);
|
void rb_connect_tcp_ssl(rb_fde_t *, struct sockaddr *, struct sockaddr *, CNCB *, void *, int);
|
||||||
|
void rb_connect_sctp(rb_fde_t *, struct sockaddr_storage *connect_addrs, size_t connect_len, struct sockaddr_storage *bind_addrs, size_t bind_len, CNCB *, void *, int);
|
||||||
int rb_connect_sockaddr(rb_fde_t *, struct sockaddr *addr, int len);
|
int rb_connect_sockaddr(rb_fde_t *, struct sockaddr *addr, int len);
|
||||||
|
|
||||||
const char *rb_errstr(int status);
|
const char *rb_errstr(int status);
|
||||||
|
|
|
@ -459,6 +459,25 @@ rb_bind(rb_fde_t *F, struct sockaddr *addr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
|
rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
|
||||||
{
|
{
|
||||||
|
@ -472,14 +491,9 @@ rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
ret = rb_sctp_bindx_only(F, addrs, len);
|
||||||
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
|
@ -647,6 +661,81 @@ rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
|
||||||
rb_connect_callback(F, RB_OK);
|
rb_connect_callback(F, RB_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rb_connect_callback() - call the callback, and continue with life
|
* rb_connect_callback() - call the callback, and continue with life
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,6 +18,7 @@ rb_close
|
||||||
rb_connect_sockaddr
|
rb_connect_sockaddr
|
||||||
rb_connect_tcp
|
rb_connect_tcp
|
||||||
rb_connect_tcp_ssl
|
rb_connect_tcp_ssl
|
||||||
|
rb_connect_sctp
|
||||||
rb_count_rb_linebuf_memory
|
rb_count_rb_linebuf_memory
|
||||||
rb_crypt
|
rb_crypt
|
||||||
rb_ctime
|
rb_ctime
|
||||||
|
|
|
@ -315,7 +315,7 @@ stats_hash(struct Client *source_p)
|
||||||
static void
|
static void
|
||||||
stats_connect(struct Client *source_p)
|
stats_connect(struct Client *source_p)
|
||||||
{
|
{
|
||||||
static char buf[5];
|
static char buf[BUFSIZE];
|
||||||
struct server_conf *server_p;
|
struct server_conf *server_p;
|
||||||
char *s;
|
char *s;
|
||||||
rb_dlink_node *ptr;
|
rb_dlink_node *ptr;
|
||||||
|
@ -342,6 +342,8 @@ stats_connect(struct Client *source_p)
|
||||||
{
|
{
|
||||||
if(ServerConfAutoconn(server_p))
|
if(ServerConfAutoconn(server_p))
|
||||||
*s++ = 'A';
|
*s++ = 'A';
|
||||||
|
if(ServerConfSCTP(server_p))
|
||||||
|
*s++ = 'M';
|
||||||
if(ServerConfSSL(server_p))
|
if(ServerConfSSL(server_p))
|
||||||
*s++ = 'S';
|
*s++ = 'S';
|
||||||
if(ServerConfTb(server_p))
|
if(ServerConfTb(server_p))
|
||||||
|
|
Loading…
Reference in a new issue