aboutsummaryrefslogtreecommitdiffstats
path: root/erts/epmd/src/epmd_srv.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/epmd/src/epmd_srv.c')
-rw-r--r--erts/epmd/src/epmd_srv.c227
1 files changed, 154 insertions, 73 deletions
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index 26e42adb19..e1bac99ef9 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -4,16 +4,17 @@
*
* Copyright Ericsson AB 1998-2013. All Rights Reserved.
*
- * The contents of this file are subject to the Erlang Public License,
- * Version 1.1, (the "License"); you may not use this file except in
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*
* %CopyrightEnd%
*/
@@ -75,6 +76,7 @@ static time_t current_time(EpmdVars*);
static Connection *conn_init(EpmdVars*);
static int conn_open(EpmdVars*,int);
+static int conn_local_peer_check(EpmdVars*, int);
static int conn_close_fd(EpmdVars*,int);
static void node_init(EpmdVars*);
@@ -205,10 +207,11 @@ void run(EpmdVars *g)
{
struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS];
int listensock[MAX_LISTEN_SOCKETS];
- int num_sockets;
+ int num_sockets = 0;
int i;
int opt;
unsigned short sport = g->port;
+ int bound = 0;
node_init(g);
g->conn = conn_init(g);
@@ -251,64 +254,82 @@ void run(EpmdVars *g)
if (g->addresses != NULL && /* String contains non-separator characters if: */
g->addresses[strspn(g->addresses," ,")] != '\000')
{
- char *tmp;
- char *token;
- int loopback_ok = 0;
+ char *tmp = NULL;
+ char *token = NULL;
+
+ /* Always listen on the loopback. */
+ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport);
+ num_sockets++;
+#if defined(EPMD6)
+ SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport);
+ num_sockets++;
+#endif
- if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL)
+ if ((tmp = strdup(g->addresses)) == NULL)
{
dbg_perror(g,"cannot allocate memory");
epmd_cleanup_exit(g,1);
}
- strcpy(tmp,g->addresses);
- for(token = strtok(tmp,", "), num_sockets = 0;
+ for(token = strtok(tmp,", ");
token != NULL;
- token = strtok(NULL,", "), num_sockets++)
+ token = strtok(NULL,", "))
{
- struct EPMD_IN_ADDR addr;
-#ifdef HAVE_INET_PTON
- int ret;
+ struct in_addr addr;
+#if defined(EPMD6)
+ struct in6_addr addr6;
+ struct sockaddr_storage *sa = &iserv_addr[num_sockets];
- if ((ret = inet_pton(FAMILY,token,&addr)) == -1)
+ if (inet_pton(AF_INET6,token,&addr6) == 1)
{
- dbg_perror(g,"cannot convert IP address to network format");
- epmd_cleanup_exit(g,1);
+ SET_ADDR6(iserv_addr[num_sockets],addr6,sport);
+ }
+ else if (inet_pton(AF_INET,token,&addr) == 1)
+ {
+ SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
+ }
+ else
+#else
+ if ((addr.s_addr = inet_addr(token)) != INADDR_NONE)
+ {
+ SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport);
}
- else if (ret == 0)
-#elif !defined(EPMD6)
- if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE)
+ else
#endif
{
dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token);
epmd_cleanup_exit(g,1);
}
+#if defined(EPMD6)
+ if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6))
+ continue;
+
+ if (sa->ss_family == AF_INET)
+#endif
if (IS_ADDR_LOOPBACK(addr))
- loopback_ok = 1;
+ continue;
- if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1)
+ num_sockets++;
+
+ if (num_sockets >= MAX_LISTEN_SOCKETS)
{
dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses",
MAX_LISTEN_SOCKETS);
epmd_cleanup_exit(g,1);
}
-
- SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport);
}
free(tmp);
-
- if (!loopback_ok)
- {
- SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport);
- num_sockets++;
- }
}
else
{
- SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport);
- num_sockets = 1;
+ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport);
+ num_sockets++;
+#if defined(EPMD6)
+ SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport);
+ num_sockets++;
+#endif
}
#ifdef HAVE_SYSTEMD_DAEMON
}
@@ -339,13 +360,39 @@ void run(EpmdVars *g)
#endif /* HAVE_SYSTEMD_DAEMON */
for (i = 0; i < num_sockets; i++)
{
- if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0)
+ struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i];
+#if defined(EPMD6)
+ size_t salen = (sa->sa_family == AF_INET6 ?
+ sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in));
+#else
+ size_t salen = sizeof(struct sockaddr_in);
+#endif
+
+ if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0)
{
- dbg_perror(g,"error opening stream socket");
+ switch (errno) {
+ case EAFNOSUPPORT:
+ case EPROTONOSUPPORT:
+ continue;
+ default:
+ dbg_perror(g,"error opening stream socket");
+ epmd_cleanup_exit(g,1);
+ }
+ }
+ g->listenfd[bound++] = listensock[i];
+
+#if HAVE_DECL_IPV6_V6ONLY
+ opt = 1;
+ if (sa->sa_family == AF_INET6 &&
+ setsockopt(listensock[i],IPPROTO_IPV6,IPV6_V6ONLY,&opt,
+ sizeof(opt)) <0)
+ {
+ dbg_perror(g,"can't set IPv6 only socket option");
epmd_cleanup_exit(g,1);
}
- g->listenfd[i] = listensock[i];
-
+#endif
+
/*
* Note that we must not enable the SO_REUSEADDR on Windows,
* because addresses will be reused even if they are still in use.
@@ -377,8 +424,7 @@ void run(EpmdVars *g)
dbg_perror(g,"failed to set non-blocking mode of listening socket %d",
listensock[i]);
- if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i],
- sizeof(iserv_addr[i])) < 0)
+ if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], salen) < 0)
{
if (errno == EADDRINUSE)
{
@@ -399,6 +445,11 @@ void run(EpmdVars *g)
}
select_fd_set(g, listensock[i]);
}
+ if (bound == 0) {
+ dbg_perror(g,"unable to bind any address");
+ epmd_cleanup_exit(g,1);
+ }
+ num_sockets = bound;
#ifdef HAVE_SYSTEMD_DAEMON
}
sd_notifyf(0, "READY=1\n"
@@ -443,8 +494,8 @@ void run(EpmdVars *g)
}
for (i = 0; i < num_sockets; i++)
- if (FD_ISSET(listensock[i],&read_mask)) {
- if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) {
+ if (FD_ISSET(g->listenfd[i],&read_mask)) {
+ if (do_accept(g, g->listenfd[i]) && g->active_conn < g->max_conn) {
/*
* The accept() succeeded, and we have at least one file
* descriptor still free, which means that another accept()
@@ -699,11 +750,9 @@ static void do_request(g, fd, s, buf, bsize)
put_int16(node->creation, wbuf+2);
}
- if (g->delay_write) /* Test of busy server */
- sleep(g->delay_write);
-
if (reply(g, fd, wbuf, 4) != 4)
{
+ node_unreg(g, name);
dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"",
name);
return;
@@ -1006,15 +1055,6 @@ static int conn_open(EpmdVars *g,int fd)
for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
- struct sockaddr_in si;
- struct sockaddr_in di;
-#ifdef HAVE_SOCKLEN_T
- socklen_t st;
-#else
- int st;
-#endif
- st = sizeof(si);
-
g->active_conn++;
s = &g->conn[i];
@@ -1025,20 +1065,7 @@ static int conn_open(EpmdVars *g,int fd)
s->open = EPMD_TRUE;
s->keep = EPMD_FALSE;
- /* Determine if connection is from localhost */
- if (getpeername(s->fd,(struct sockaddr*) &si,&st) ||
- st < sizeof(si)) {
- /* Failure to get peername is regarded as non local host */
- s->local_peer = EPMD_FALSE;
- } else {
- /* Only 127.x.x.x and connections from the host's IP address
- allowed, no false positives */
- s->local_peer =
- (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
- 0x7F000000U) ||
- (getsockname(s->fd,(struct sockaddr*) &di,&st) ?
- EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr));
- }
+ s->local_peer = conn_local_peer_check(g, s->fd);
dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
"Non-local peer connected");
@@ -1046,7 +1073,7 @@ static int conn_open(EpmdVars *g,int fd)
s->got = 0;
s->mod_time = current_time(g); /* Note activity */
- s->buf = (char *)malloc(INBUF_SIZE);
+ s->buf = malloc(INBUF_SIZE);
if (s->buf == NULL) {
dbg_printf(g,0,"epmd: Insufficient memory");
@@ -1064,6 +1091,60 @@ static int conn_open(EpmdVars *g,int fd)
return EPMD_FALSE;
}
+static int conn_local_peer_check(EpmdVars *g, int fd)
+{
+ struct EPMD_SOCKADDR_IN si;
+ struct EPMD_SOCKADDR_IN di;
+
+ struct sockaddr_in *si4 = (struct sockaddr_in *)&si;
+ struct sockaddr_in *di4 = (struct sockaddr_in *)&di;
+
+#if defined(EPMD6)
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si;
+ struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di;
+#endif
+
+#ifdef HAVE_SOCKLEN_T
+ socklen_t st;
+#else
+ int st;
+#endif
+
+ st = sizeof(si);
+
+ /* Determine if connection is from localhost */
+ if (getpeername(fd,(struct sockaddr*) &si,&st) ||
+ st > sizeof(si)) {
+ /* Failure to get peername is regarded as non local host */
+ return EPMD_FALSE;
+ }
+
+ /* Only 127.x.x.x and connections from the host's IP address
+ allowed, no false positives */
+#if defined(EPMD6)
+ if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr)))
+ return EPMD_TRUE;
+
+ if (si.ss_family == AF_INET)
+#endif
+ if ((((unsigned) ntohl(si4->sin_addr.s_addr)) & 0xFF000000U) ==
+ 0x7F000000U)
+ return EPMD_TRUE;
+
+ if (getsockname(fd,(struct sockaddr*) &di,&st))
+ return EPMD_FALSE;
+
+#if defined(EPMD6)
+ if (si.ss_family == AF_INET6)
+ return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr));
+ if (si.ss_family == AF_INET)
+#endif
+ return si4->sin_addr.s_addr == di4->sin_addr.s_addr;
+#if defined(EPMD6)
+ return EPMD_FALSE;
+#endif
+}
+
static int conn_close_fd(EpmdVars *g,int fd)
{
int i;