diff --git a/include/s_newconf.h b/include/s_newconf.h index 391d396b..4792b005 100644 --- a/include/s_newconf.h +++ b/include/s_newconf.h @@ -210,12 +210,14 @@ struct server_conf #define SERVER_AUTOCONN 0x0020 #define SERVER_SSL 0x0040 #define SERVER_NO_EXPORT 0x0080 +#define SERVER_SCTP 0x0100 #define ServerConfIllegal(x) ((x)->flags & SERVER_ILLEGAL) #define ServerConfEncrypted(x) ((x)->flags & SERVER_ENCRYPTED) #define ServerConfCompressed(x) ((x)->flags & SERVER_COMPRESSED) #define ServerConfTb(x) ((x)->flags & SERVER_TB) #define ServerConfAutoconn(x) ((x)->flags & SERVER_AUTOCONN) +#define ServerConfSCTP(x) ((x)->flags & SERVER_SCTP) #define ServerConfSSL(x) ((x)->flags & SERVER_SSL) #define ServerConfNoExport(x) ((x)->flags & SERVER_NO_EXPORT) diff --git a/ircd/newconf.c b/ircd/newconf.c index c9b0a56a..bdb7f261 100644 --- a/ircd/newconf.c +++ b/ircd/newconf.c @@ -361,6 +361,7 @@ static struct mode_table connect_table[] = { { "compressed", SERVER_COMPRESSED }, { "encrypted", SERVER_ENCRYPTED }, { "topicburst", SERVER_TB }, + { "sctp", SERVER_SCTP }, { "ssl", SERVER_SSL }, { "no-export", SERVER_NO_EXPORT }, { NULL, 0 }, diff --git a/ircd/s_serv.c b/ircd/s_serv.c index 316f9cfd..928688a9 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -1031,8 +1031,8 @@ int serv_connect(struct server_conf *server_p, struct Client *by) { struct Client *client_p; - struct rb_sockaddr_storage sa_connect; - struct rb_sockaddr_storage sa_bind; + struct sockaddr_storage sa_connect[2]; + struct sockaddr_storage sa_bind[ARRAY_SIZE(sa_connect)]; char note[HOSTLEN + 10]; rb_fde_t *F; @@ -1040,8 +1040,10 @@ serv_connect(struct server_conf *server_p, struct Client *by) if(server_p == NULL) return 0; - SET_SS_FAMILY(&sa_connect, AF_UNSPEC); - SET_SS_FAMILY(&sa_bind, AF_UNSPEC); + for (int i = 0; i < ARRAY_SIZE(sa_connect); i++) { + SET_SS_FAMILY(&sa_connect[i], AF_UNSPEC); + SET_SS_FAMILY(&sa_bind[i], AF_UNSPEC); + } if(server_p->aftype == AF_UNSPEC && 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) { - sa_connect = server_p->connect4; - sa_bind = server_p->bind4; + sa_connect[0] = server_p->connect4; + sa_connect[1] = server_p->connect6; + sa_bind[0] = server_p->bind4; + sa_bind[1] = server_p->bind6; } else { - sa_connect = server_p->connect6; - sa_bind = server_p->bind6; + sa_connect[0] = server_p->connect6; + 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) { - sa_connect = server_p->connect4; - sa_bind = server_p->bind4; + sa_connect[0] = server_p->connect4; + sa_bind[0] = server_p->bind4; } else if(server_p->aftype == AF_INET6 || GET_SS_FAMILY(&server_p->connect6) == AF_INET6) { - sa_connect = server_p->connect6; - sa_bind = server_p->bind6; + sa_connect[0] = server_p->connect6; + sa_bind[0] = server_p->bind6; } /* log */ - buf[0] = 0; - rb_inet_ntop_sock((struct sockaddr *)&sa_connect, buf, sizeof(buf)); - ilog(L_SERVER, "Connect to *[%s] @%s", server_p->name, buf); +#ifdef HAVE_LIBSCTP + if (ServerConfSCTP(server_p) && GET_SS_FAMILY(&sa_connect[1]) != AF_UNSPEC) { + char buf2[HOSTLEN + 1]; + + buf[0] = 0; + 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); + } /* * 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 */ - if(GET_SS_FAMILY(&sa_connect) == AF_UNSPEC) - { + if(GET_SS_FAMILY(&sa_connect[0]) == AF_UNSPEC) { ilog_error("unspecified socket address family"); return 0; - } - else if((F = rb_socket(GET_SS_FAMILY(&sa_connect), SOCK_STREAM, 0, NULL)) == NULL) - { +#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; + } +#endif + } else if ((F = rb_socket(GET_SS_FAMILY(&sa_connect[0]), SOCK_STREAM, IPPROTO_TCP, NULL)) == NULL) { ilog_error("opening a stream socket"); 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)); client_p->localClient->F = F; /* 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 @@ -1161,19 +1186,31 @@ serv_connect(struct server_conf *server_p, struct Client *by) SetConnecting(client_p); rb_dlinkAddTail(client_p, &client_p->node, &global_client_list); - if(GET_SS_FAMILY(&sa_bind) == AF_UNSPEC) - { - if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind4)) - sa_bind = ServerInfo.bind4; - if(GET_SS_FAMILY(&sa_connect) == GET_SS_FAMILY(&ServerInfo.bind6)) - sa_bind = ServerInfo.bind6; + 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[i]) == GET_SS_FAMILY(&ServerInfo.bind4)) + sa_bind[i] = ServerInfo.bind4; + if (GET_SS_FAMILY(&sa_connect[i]) == GET_SS_FAMILY(&ServerInfo.bind6)) + sa_bind[i] = ServerInfo.bind6; + } } - rb_connect_tcp(client_p->localClient->F, - (struct sockaddr *)&sa_connect, - GET_SS_FAMILY(&sa_bind) == AF_UNSPEC ? NULL : (struct sockaddr *)&sa_bind, - ServerConfSSL(server_p) ? serv_connect_ssl_callback : serv_connect_callback, - client_p, ConfigFileEntry.connect_timeout); +#ifdef HAVE_LIBSCTP + if (ServerConfSCTP(server_p)) { + 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, + 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; } diff --git a/librb/include/rb_commio.h b/librb/include/rb_commio.h index bbaabbf5..88e948b6 100644 --- a/librb/include/rb_commio.h +++ b/librb/include/rb_commio.h @@ -126,6 +126,7 @@ void rb_settimeout(rb_fde_t *, time_t, PF *, void *); void rb_checktimeouts(void *); 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_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); const char *rb_errstr(int status); diff --git a/librb/src/commio.c b/librb/src/commio.c index dd2ba8c7..a994d5bc 100644 --- a/librb/src/commio.c +++ b/librb/src/commio.c @@ -459,6 +459,25 @@ rb_bind(rb_fde_t *F, struct sockaddr *addr) 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 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) return 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; - } + ret = rb_sctp_bindx_only(F, addrs, len); + if (ret) + return ret; return 0; #else @@ -647,6 +661,81 @@ rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest, 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 */ diff --git a/librb/src/export-syms.txt b/librb/src/export-syms.txt index 358d9db5..0c479b6d 100644 --- a/librb/src/export-syms.txt +++ b/librb/src/export-syms.txt @@ -18,6 +18,7 @@ rb_close rb_connect_sockaddr rb_connect_tcp rb_connect_tcp_ssl +rb_connect_sctp rb_count_rb_linebuf_memory rb_crypt rb_ctime diff --git a/modules/m_stats.c b/modules/m_stats.c index 56f542e0..673d2212 100644 --- a/modules/m_stats.c +++ b/modules/m_stats.c @@ -315,7 +315,7 @@ stats_hash(struct Client *source_p) static void stats_connect(struct Client *source_p) { - static char buf[5]; + static char buf[BUFSIZE]; struct server_conf *server_p; char *s; rb_dlink_node *ptr; @@ -342,6 +342,8 @@ stats_connect(struct Client *source_p) { if(ServerConfAutoconn(server_p)) *s++ = 'A'; + if(ServerConfSCTP(server_p)) + *s++ = 'M'; if(ServerConfSSL(server_p)) *s++ = 'S'; if(ServerConfTb(server_p))