Changeset View
Changeset View
Standalone View
Standalone View
sbin/ggate/ggated/ggated.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <libutil.h> | #include <libutil.h> | ||||
#include <paths.h> | #include <paths.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <stdbool.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <syslog.h> | #include <syslog.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "ggate.h" | #include "ggate.h" | ||||
#define GGATED_EXPORT_FILE "/etc/gg.exports" | #define GGATED_EXPORT_FILE "/etc/gg.exports" | ||||
struct ggd_connection { | struct ggd_connection { | ||||
int c_version; | |||||
off_t c_mediasize; | off_t c_mediasize; | ||||
unsigned c_sectorsize; | unsigned c_sectorsize; | ||||
unsigned c_flags; /* flags (RO/RW) */ | unsigned c_flags; /* flags (RO/RW) */ | ||||
int c_diskfd; | int c_diskfd; | ||||
int c_sendfd; | int c_sendfd; | ||||
int c_recvfd; | int c_recvfd; | ||||
time_t c_birthtime; | time_t c_birthtime; | ||||
char *c_path; | char *c_path; | ||||
Show All 31 Lines | |||||
static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports); | static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports); | ||||
static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections); | static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections); | ||||
static void *recv_thread(void *arg); | static void *recv_thread(void *arg); | ||||
static void *disk_thread(void *arg); | static void *disk_thread(void *arg); | ||||
static void *send_thread(void *arg); | static void *send_thread(void *arg); | ||||
static void connection_remove(struct ggd_connection *conn); | |||||
static void | static void | ||||
usage(void) | usage(void) | ||||
{ | { | ||||
fprintf(stderr, "usage: %s [-nv] [-a address] [-F pidfile] [-p port] " | fprintf(stderr, "usage: %s [-nv] [-a address] [-F pidfile] [-p port] " | ||||
"[-R rcvbuf] [-S sndbuf] [exports file]\n", getprogname()); | "[-R rcvbuf] [-S sndbuf] [exports file]\n", getprogname()); | ||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 288 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
connection_cleanups(void) | connection_cleanups(void) | ||||
{ | { | ||||
struct ggd_connection *conn, *tconn; | struct ggd_connection *conn, *tconn; | ||||
time_t now; | time_t now; | ||||
time(&now); | time(&now); | ||||
LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { | LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { | ||||
if (now - conn->c_birthtime > 10) { | if (now - conn->c_birthtime > 10) | ||||
LIST_REMOVE(conn, c_next); | connection_remove(conn); | ||||
g_gate_log(LOG_NOTICE, | |||||
"Connection from %s [%s] removed.", | |||||
ip2str(conn->c_srcip), conn->c_path); | |||||
close(conn->c_diskfd); | |||||
close(conn->c_sendfd); | |||||
close(conn->c_recvfd); | |||||
free(conn->c_path); | |||||
free(conn); | |||||
} | } | ||||
} | } | ||||
} | |||||
static struct ggd_connection * | static struct ggd_connection * | ||||
connection_find(struct g_gate_cinit *cinit) | connection_find(struct g_gate_cinit *cinit) | ||||
{ | { | ||||
struct ggd_connection *conn; | struct ggd_connection *conn; | ||||
LIST_FOREACH(conn, &connections, c_next) { | LIST_FOREACH(conn, &connections, c_next) { | ||||
if (conn->c_token == cinit->gc_token) | if (conn->c_token == cinit->gc_token) | ||||
break; | break; | ||||
} | } | ||||
return (conn); | return (conn); | ||||
} | } | ||||
static struct ggd_connection * | static struct ggd_connection * | ||||
connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) | connection_new(int ver, struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) | ||||
{ | { | ||||
struct ggd_connection *conn; | struct ggd_connection *conn; | ||||
in_addr_t ip; | in_addr_t ip; | ||||
/* | /* | ||||
* First, look for old connections. | * First, look for old connections. | ||||
* We probably should do it every X seconds, but what for? | * We probably should do it every X seconds, but what for? | ||||
* It is only dangerous if an attacker wants to overload connections | * It is only dangerous if an attacker wants to overload connections | ||||
* queue, so here is a good place to do the cleanups. | * queue, so here is a good place to do the cleanups. | ||||
*/ | */ | ||||
connection_cleanups(); | connection_cleanups(); | ||||
conn = malloc(sizeof(*conn)); | conn = malloc(sizeof(*conn)); | ||||
if (conn == NULL) | if (conn == NULL) | ||||
return (NULL); | return (NULL); | ||||
conn->c_version = ver; | |||||
conn->c_path = strdup(cinit->gc_path); | conn->c_path = strdup(cinit->gc_path); | ||||
if (conn->c_path == NULL) { | if (conn->c_path == NULL) { | ||||
free(conn); | free(conn); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
conn->c_token = cinit->gc_token; | conn->c_token = cinit->gc_token; | ||||
ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); | ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); | ||||
conn->c_srcip = ip; | conn->c_srcip = ip; | ||||
conn->c_diskfd = conn->c_sendfd = conn->c_recvfd = -1; | conn->c_diskfd = conn->c_sendfd = conn->c_recvfd = -1; | ||||
if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) | |||||
conn->c_sendfd = sfd; | |||||
else | |||||
conn->c_recvfd = sfd; | |||||
conn->c_mediasize = 0; | conn->c_mediasize = 0; | ||||
conn->c_sectorsize = 0; | conn->c_sectorsize = 0; | ||||
time(&conn->c_birthtime); | time(&conn->c_birthtime); | ||||
conn->c_flags = cinit->gc_flags; | conn->c_flags = cinit->gc_flags; | ||||
if (conn->c_version == GGATE_VERSION1) { | |||||
conn->c_sendfd = sfd; | |||||
conn->c_recvfd = sfd; | |||||
} else if (conn->c_version == GGATE_VERSION0) { | |||||
if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) | |||||
conn->c_sendfd = sfd; | |||||
else | |||||
conn->c_recvfd = sfd; | |||||
LIST_INSERT_HEAD(&connections, conn, c_next); | LIST_INSERT_HEAD(&connections, conn, c_next); | ||||
} | |||||
g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), | g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), | ||||
conn->c_path); | conn->c_path); | ||||
return (conn); | return (conn); | ||||
} | } | ||||
static int | static int | ||||
connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit, | connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit, | ||||
struct sockaddr *s, int sfd) | struct sockaddr *s, int sfd) | ||||
Show All 26 Lines | |||||
/* | /* | ||||
* Remove one socket from the given connection or the whole | * Remove one socket from the given connection or the whole | ||||
* connection if sfd == -1. | * connection if sfd == -1. | ||||
*/ | */ | ||||
static void | static void | ||||
connection_remove(struct ggd_connection *conn) | connection_remove(struct ggd_connection *conn) | ||||
{ | { | ||||
if (conn->c_version == GGATE_VERSION0) | |||||
LIST_REMOVE(conn, c_next); | LIST_REMOVE(conn, c_next); | ||||
g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", | g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", | ||||
ip2str(conn->c_srcip), conn->c_path); | ip2str(conn->c_srcip), conn->c_path); | ||||
if (conn->c_diskfd != -1) | if (conn->c_diskfd != -1) | ||||
close(conn->c_diskfd); | close(conn->c_diskfd); | ||||
if (conn->c_sendfd != -1) | if (conn->c_sendfd != -1) | ||||
close(conn->c_sendfd); | close(conn->c_sendfd); | ||||
if (conn->c_recvfd != -1) | if (conn->c_recvfd != -1 && conn->c_sendfd != conn->c_recvfd) | ||||
close(conn->c_recvfd); | close(conn->c_recvfd); | ||||
free(conn->c_path); | free(conn->c_path); | ||||
free(conn); | free(conn); | ||||
} | } | ||||
static int | static int | ||||
connection_ready(struct ggd_connection *conn) | connection_ready(struct ggd_connection *conn) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 303 Lines • ▼ Show 20 Lines | |||||
log_connection(struct sockaddr *from) | log_connection(struct sockaddr *from) | ||||
{ | { | ||||
in_addr_t ip; | in_addr_t ip; | ||||
ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr); | ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr); | ||||
g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); | g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); | ||||
} | } | ||||
static int | /* | ||||
* handshake() returns true if sfd is closed; otherwise false so that caller | |||||
markj: "handshake() returns true..." | |||||
* has to close the sfd. | |||||
*/ | |||||
static bool | |||||
handshake(struct sockaddr *from, int sfd) | handshake(struct sockaddr *from, int sfd) | ||||
{ | { | ||||
struct g_gate_version ver; | struct g_gate_version ver; | ||||
struct g_gate_cinit cinit; | struct g_gate_cinit cinit; | ||||
struct g_gate_sinit sinit; | struct g_gate_sinit sinit; | ||||
struct ggd_connection *conn; | struct ggd_connection *conn; | ||||
struct ggd_export *ex; | struct ggd_export *ex; | ||||
ssize_t data; | ssize_t data; | ||||
log_connection(from); | log_connection(from); | ||||
/* | /* | ||||
* Phase 1: Version verification. | * Phase 1: Version verification. | ||||
*/ | */ | ||||
g_gate_log(LOG_DEBUG, "Receiving version packet."); | g_gate_log(LOG_DEBUG, "Receiving version packet."); | ||||
data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL); | data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL); | ||||
g_gate_swap2h_version(&ver); | g_gate_swap2h_version(&ver); | ||||
if (data != sizeof(ver)) { | if (data != sizeof(ver)) { | ||||
g_gate_log(LOG_WARNING, "Malformed version packet."); | g_gate_log(LOG_WARNING, "Malformed version packet."); | ||||
return (0); | return (false); | ||||
} | } | ||||
g_gate_log(LOG_DEBUG, "Version packet received."); | g_gate_log(LOG_DEBUG, "Version packet received."); | ||||
if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) { | if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) { | ||||
g_gate_log(LOG_WARNING, "Invalid magic field."); | g_gate_log(LOG_WARNING, "Invalid magic field."); | ||||
return (0); | return (false); | ||||
} | } | ||||
if (ver.gv_version != GGATE_VERSION) { | switch (ver.gv_version) { | ||||
case GGATE_VERSION0: | |||||
case GGATE_VERSION1: | |||||
g_gate_log(LOG_DEBUG, "Received version %" PRIu16, | |||||
ver.gv_version); | |||||
break; | |||||
default: | |||||
g_gate_log(LOG_WARNING, "Version %u is not supported.", | g_gate_log(LOG_WARNING, "Version %u is not supported.", | ||||
ver.gv_version); | ver.gv_version); | ||||
return (0); | return (false); | ||||
} | } | ||||
ver.gv_error = 0; | ver.gv_error = 0; | ||||
g_gate_swap2n_version(&ver); | g_gate_swap2n_version(&ver); | ||||
data = g_gate_send(sfd, &ver, sizeof(ver), 0); | data = g_gate_send(sfd, &ver, sizeof(ver), 0); | ||||
g_gate_swap2h_version(&ver); | g_gate_swap2h_version(&ver); | ||||
if (data == -1) { | if (data == -1) { | ||||
sendfail(sfd, errno, "Error while sending version packet: %s.", | sendfail(sfd, errno, "Error while sending version packet: %s.", | ||||
strerror(errno)); | strerror(errno)); | ||||
return (0); | return (false); | ||||
} | } | ||||
/* | /* | ||||
* Phase 2: Request verification. | * Phase 2: Request verification. | ||||
*/ | */ | ||||
g_gate_log(LOG_DEBUG, "Receiving initial packet."); | g_gate_log(LOG_DEBUG, "Receiving initial packet."); | ||||
data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); | data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); | ||||
g_gate_swap2h_cinit(&cinit); | g_gate_swap2h_cinit(&cinit); | ||||
if (data != sizeof(cinit)) { | if (data != sizeof(cinit)) { | ||||
g_gate_log(LOG_WARNING, "Malformed initial packet."); | g_gate_log(LOG_WARNING, "Malformed initial packet."); | ||||
return (0); | return (0); | ||||
} | } | ||||
g_gate_log(LOG_DEBUG, "Initial packet received."); | g_gate_log(LOG_DEBUG, "Initial packet received."); | ||||
switch (ver.gv_version) { | |||||
case GGATE_VERSION0: | |||||
conn = connection_find(&cinit); | conn = connection_find(&cinit); | ||||
break; | |||||
case GGATE_VERSION1: | |||||
conn = NULL; | |||||
break; | |||||
default: | |||||
g_gate_log(LOG_WARNING, "Version %u is not supported.", | |||||
ver.gv_version); | |||||
return (false); | |||||
} | |||||
if (conn != NULL) { | if (conn != NULL) { | ||||
/* | /* | ||||
* Connection should already exists. | * Connection should already exists. | ||||
*/ | */ | ||||
g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).", | g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).", | ||||
(unsigned long)conn->c_token); | (unsigned long)conn->c_token); | ||||
if (connection_add(conn, &cinit, from, sfd) == -1) { | if (ver.gv_version != GGATE_VERSION0 || | ||||
connection_add(conn, &cinit, from, sfd) == -1) { | |||||
Done Inline ActionsHow can we have gv_version != GGATE_VERSION0 here? If it is equal to GGATE_VERSION1 then conn == NULL. markj: How can we have `gv_version != GGATE_VERSION0` here? If it is equal to GGATE_VERSION1 then… | |||||
connection_remove(conn); | connection_remove(conn); | ||||
return (0); | return (false); | ||||
} | } | ||||
} else { | } else { | ||||
/* | /* | ||||
* New connection, allocate space. | * New connection, allocate space. | ||||
*/ | */ | ||||
conn = connection_new(&cinit, from, sfd); | conn = connection_new(ver.gv_version, &cinit, from, sfd); | ||||
if (conn == NULL) { | if (conn == NULL) { | ||||
sendfail(sfd, ENOMEM, | sendfail(sfd, ENOMEM, | ||||
"Cannot allocate new connection."); | "Cannot allocate new connection."); | ||||
return (0); | return (false); | ||||
} | } | ||||
g_gate_log(LOG_DEBUG, "New connection created (token=%lu).", | g_gate_log(LOG_DEBUG, "New connection created (token=%lu).", | ||||
(unsigned long)conn->c_token); | (unsigned long)conn->c_token); | ||||
} | } | ||||
ex = exports_find(from, &cinit, conn); | ex = exports_find(from, &cinit, conn); | ||||
if (ex == NULL) { | if (ex == NULL) { | ||||
sendfail(sfd, errno, NULL); | sendfail(sfd, errno, NULL); | ||||
connection_remove(conn); | connection_remove(conn); | ||||
return (0); | return (false); | ||||
} | } | ||||
if (conn->c_mediasize == 0) { | if (conn->c_mediasize == 0) { | ||||
conn->c_mediasize = g_gate_mediasize(conn->c_diskfd); | conn->c_mediasize = g_gate_mediasize(conn->c_diskfd); | ||||
conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd); | conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd); | ||||
} | } | ||||
sinit.gs_mediasize = conn->c_mediasize; | sinit.gs_mediasize = conn->c_mediasize; | ||||
sinit.gs_sectorsize = conn->c_sectorsize; | sinit.gs_sectorsize = conn->c_sectorsize; | ||||
sinit.gs_error = 0; | sinit.gs_error = 0; | ||||
g_gate_log(LOG_DEBUG, "Sending initial packet."); | g_gate_log(LOG_DEBUG, "Sending initial packet."); | ||||
g_gate_swap2n_sinit(&sinit); | g_gate_swap2n_sinit(&sinit); | ||||
data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); | data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); | ||||
g_gate_swap2h_sinit(&sinit); | g_gate_swap2h_sinit(&sinit); | ||||
if (data == -1) { | if (data == -1) { | ||||
sendfail(sfd, errno, "Error while sending initial packet: %s.", | sendfail(sfd, errno, "Error while sending initial packet: %s.", | ||||
strerror(errno)); | strerror(errno)); | ||||
return (0); | return (false); | ||||
} | } | ||||
switch (conn->c_version) { | |||||
case GGATE_VERSION0: | |||||
if (connection_ready(conn)) { | if (connection_ready(conn)) { | ||||
connection_launch(conn); | connection_launch(conn); | ||||
connection_remove(conn); | connection_remove(conn); | ||||
} | } | ||||
return (1); | return (true); | ||||
case GGATE_VERSION1: | |||||
connection_launch(conn); | |||||
connection_remove(conn); | |||||
return (true); | |||||
default: | |||||
return (false); | |||||
} | |||||
} | } | ||||
Not Done Inline ActionsIsn't this statement unreachable? markj: Isn't this statement unreachable? | |||||
Done Inline ActionsYes, that's the case because of another switch statement returning earlier above. What's the best practice in FreeBSD? ota_j.email.ne.jp: Yes, that's the case because of another switch statement returning earlier above.
What's the… | |||||
static void | static void | ||||
huphandler(int sig __unused) | huphandler(int sig __unused) | ||||
{ | { | ||||
got_sighup = 1; | got_sighup = 1; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 118 Lines • Show Last 20 Lines |
"handshake() returns true..."