r13266@catbus: nickm | 2007-06-05 16:38:08 -0400

Patch from peter palfrader: control interface via unix domain socket


svn:r10504
This commit is contained in:
Nick Mathewson 2007-06-05 20:54:49 +00:00
parent f0345d46f6
commit 210beff55a
10 changed files with 348 additions and 130 deletions

View File

@ -6,6 +6,11 @@ Changes in version 0.2.0.3-alpha - 2007-??-??
- tor-gencert creates all files as readable to the file creator only, and
write-protects the authority identity key.
o Minor feature (controller):
- You can now use the ControlSocket option to tell Tor to listen for
controller connections on Unix domain sockets on systems that support
them. (Patch from Peter Palfrader.)
o Minor bugfixes (dns):
- Fix a crash when DNSPort is set more than once. (Patch from Robert
Hogan.)

View File

@ -240,7 +240,7 @@ AC_SYS_LARGEFILE
AC_CHECK_HEADERS(unistd.h string.h signal.h ctype.h sys/stat.h sys/types.h fcntl.h sys/fcntl.h sys/time.h errno.h assert.h time.h, , AC_MSG_WARN(Some headers were not found, compilation may fail. If compilation succeeds, please send your orconfig.h to the developers so we can fix this warning.))
AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h grp.h)
AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h grp.h sys/un.h)
dnl These headers are not essential

View File

@ -149,6 +149,7 @@ static config_var_t _option_vars[] = {
VAR("ContactInfo", STRING, ContactInfo, NULL),
VAR("ControlListenAddress",LINELIST, ControlListenAddress, NULL),
VAR("ControlPort", UINT, ControlPort, "0"),
VAR("ControlSocket", LINELIST, ControlSocket, NULL),
VAR("CookieAuthentication",BOOL, CookieAuthentication, "0"),
VAR("DataDirectory", STRING, DataDirectory, NULL),
OBSOLETE("DebugLogFile"),
@ -811,6 +812,14 @@ options_act_reversible(or_options_t *old_options, char **msg)
start_daemon();
}
#ifndef HAVE_SYS_UN_H
if (options->ControlSocket) {
*msg = tor_strdup("Unix domain sockets (ControlSocket) not supported"
" on this OS/with this build.");
goto rollback;
}
#endif
if (running_tor) {
/* We need to set the connection limit before we can open the listeners. */
options->_ConnLimit =

View File

@ -14,8 +14,9 @@ const char connection_c_id[] =
#include "or.h"
static connection_t *connection_create_listener(const char *listenaddress,
uint16_t listenport, int type);
static connection_t *connection_create_listener(
struct sockaddr *listensockaddr, int type,
char* address);
static int connection_init_accepted_conn(connection_t *conn,
uint8_t listener_type);
static int connection_handle_listener_read(connection_t *conn, int new_type);
@ -156,7 +157,7 @@ conn_state_to_string(int type, int state)
* Initialize conn's timestamps to now.
*/
connection_t *
connection_new(int type)
connection_new(int type, int sa_family)
{
static uint32_t n_connections_allocated = 1;
connection_t *conn;
@ -194,6 +195,7 @@ connection_new(int type)
conn->conn_array_index = -1; /* also default to 'not used' */
conn->type = type;
conn->sa_family = sa_family;
if (!connection_is_listener(conn)) { /* listeners never use their buf */
conn->inbuf = buf_new();
conn->outbuf = buf_new();
@ -293,6 +295,17 @@ _connection_free(connection_t *conn)
if (!connection_is_listener(conn)) {
buf_free(conn->inbuf);
buf_free(conn->outbuf);
} else {
if (conn->sa_family == AF_UNIX) {
/* For now only control ports can be unix domain sockets
* and listeners at the same time */
tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER);
if (unlink(conn->address) < 0 && errno != ENOENT) {
log_warn(LD_NET, "Could not unlink %s: %s", conn->address,
strerror(errno));
}
}
}
tor_free(conn->address);
@ -621,43 +634,96 @@ connection_expire_held_open(void)
});
}
/** Bind a new non-blocking socket listening to
* <b>listenaddress</b>:<b>listenport</b>, and add this new connection
* (of type <b>type</b>) to the connection array.
/** Create an AF_INET listenaddr struct.
* <b>listenaddress</b> provides the host and optionally the port information
* for the new structure. If no port is provided in <b>listenaddress</b> then
* <b>listenport</b> is used.
*
* If <b>listenaddress</b> includes a port, we bind on that port;
* otherwise, we use listenport.
* If not NULL <b>readable_addrress</b> will contain a copy of the host part of
* <b>listenaddress</b>.
*
* The listenaddr struct has to be freed by the caller.
*/
static connection_t *
connection_create_listener(const char *listenaddress, uint16_t listenport,
int type)
{
struct sockaddr_in listenaddr; /* where to bind */
char *address = NULL;
connection_t *conn;
uint16_t usePort;
static struct sockaddr_in *
create_inet_sockaddr(const char *listenaddress, uint16_t listenport,
char **readable_address) {
struct sockaddr_in *listenaddr = NULL;
uint32_t addr;
int s; /* the socket we're going to make */
#ifndef MS_WINDOWS
int one=1;
#endif
int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
uint16_t usePort = 0;
memset(&listenaddr,0,sizeof(struct sockaddr_in));
if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
if (parse_addr_port(LOG_WARN,
listenaddress, readable_address, &addr, &usePort)<0) {
log_warn(LD_CONFIG,
"Error parsing/resolving ListenAddress %s", listenaddress);
return NULL;
goto err;
}
if (usePort==0)
usePort = listenport;
listenaddr.sin_addr.s_addr = htonl(addr);
listenaddr.sin_family = AF_INET;
listenaddr.sin_port = htons((uint16_t) usePort);
log_notice(LD_NET, "Opening %s on %s:%d",
conn_type_to_string(type), address, usePort);
listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
listenaddr->sin_addr.s_addr = htonl(addr);
listenaddr->sin_family = AF_INET;
listenaddr->sin_port = htons((uint16_t) usePort);
return listenaddr;
err:
tor_free(listenaddr);
return NULL;
}
#ifdef HAVE_SYS_UN_H
/** Create an AF_UNIX listenaddr struct.
* <b>listenaddress</b> provides the path to the unix socket.
*
* Eventually <b>listenaddress</b> will also optionally contain user, group,
* and file permissions for the new socket. But not yet. XXX
* Also, since we do not create the socket here the information doesn't help
* here.
*
* If not NULL <b>readable_addrress</b> will contain a copy of the path part of
* <b>listenaddress</b>.
*
* The listenaddr struct has to be freed by the caller.
*/
static struct sockaddr_un *
create_unix_sockaddr(const char *listenaddress, char **readable_address)
{
struct sockaddr_un *sockaddr = NULL;
sockaddr = tor_malloc_zero(sizeof(struct sockaddr_un));
sockaddr->sun_family = AF_UNIX;
strncpy(sockaddr->sun_path, listenaddress, sizeof(sockaddr->sun_path));
if (readable_address)
*readable_address = tor_strdup(listenaddress);
return sockaddr;
}
#else
static struct sockaddr *
create_unix_sockaddr(const char *listenaddress, char **readable_address)
{
log_fn(LOG_ERR, LD_BUG,
"Unix domain sockets not supported, yet we tried to create one.");
assert(0);
};
#endif /* HAVE_SYS_UN_H */
/** Bind a new non-blocking socket listening to the socket described
* by <b>listensockaddr</b>.
*
* <b>address</b> is only used for logging purposes and to add the information
* to the conn.
*/
static connection_t *
connection_create_listener(struct sockaddr *listensockaddr, int type,
char* address)
{
connection_t *conn;
int s; /* the socket we're going to make */
uint16_t usePort = 0;
int start_reading = 0;
if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
int n_conns = get_n_open_sockets();
@ -668,6 +734,20 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
return NULL;
}
if (listensockaddr->sa_family == AF_INET) {
int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
#ifndef MS_WINDOWS
int one=1;
#endif
if (is_tcp)
start_reading = 1;
usePort = ntohs( (uint16_t)
((struct sockaddr_in *)listensockaddr)->sin_port);
log_notice(LD_NET, "Opening %s on %s:%d",
conn_type_to_string(type), address, usePort);
s = tor_open_socket(PF_INET,
is_tcp ? SOCK_STREAM : SOCK_DGRAM,
is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
@ -684,7 +764,7 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
#endif
if (bind(s,(struct sockaddr *)&listenaddr,sizeof(listenaddr)) < 0) {
if (bind(s,listensockaddr,sizeof(struct sockaddr_in)) < 0) {
const char *helpfulhint = "";
int e = tor_socket_errno(s);
if (ERRNO_IS_EADDRINUSE(e))
@ -703,13 +783,56 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
goto err;
}
}
#ifdef HAVE_SYS_UN_H
} else if (listensockaddr->sa_family == AF_UNIX) {
int len;
start_reading = 1;
/* For now only control ports can be unix domain sockets
* and listeners at the same time */
tor_assert(type == CONN_TYPE_CONTROL_LISTENER);
log_notice(LD_NET, "Opening %s on %s",
conn_type_to_string(type), address);
if (unlink(address) < 0 && errno != ENOENT) {
log_warn(LD_NET, "Could not unlink %s: %s", address,
strerror(errno));
goto err;
}
s = tor_open_socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno));
goto err;
}
len = strlen(((struct sockaddr_un *)listensockaddr)->sun_path) +
sizeof(((struct sockaddr_un *)listensockaddr)->sun_family);
if (bind(s, listensockaddr, len) == -1) {
log_warn(LD_NET,"Bind to %s failed: %s.", address,
tor_socket_strerror(tor_socket_errno(s)));
goto err;
}
if (listen(s,SOMAXCONN) < 0) {
log_warn(LD_NET, "Could not listen on %s: %s", address,
tor_socket_strerror(tor_socket_errno(s)));
tor_close_socket(s);
goto err;
}
#endif /* HAVE_SYS_UN_H */
} else {
log_err(LD_BUG,"Got unexpected address family %d.",
listensockaddr->sa_family);
tor_assert(0);
}
set_socket_nonblocking(s);
conn = connection_new(type);
conn = connection_new(type, listensockaddr->sa_family);
conn->sa_family = listensockaddr->sa_family;
conn->s = s;
conn->address = address;
address = NULL;
conn->address = tor_strdup(address);
conn->port = usePort;
if (connection_add(conn) < 0) { /* no space, forget it */
@ -722,7 +845,7 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
conn_type_to_string(type), usePort);
conn->state = LISTENER_STATE_READY;
if (is_tcp) {
if (start_reading) {
connection_start_reading(conn);
} else {
tor_assert(type == CONN_TYPE_AP_DNS_LISTENER);
@ -732,7 +855,6 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
return conn;
err:
tor_free(address);
return NULL;
}
@ -801,6 +923,9 @@ connection_handle_listener_read(connection_t *conn, int new_type)
set_socket_nonblocking(news);
tor_assert(((struct sockaddr*)addrbuf)->sa_family == conn->sa_family);
if (conn->sa_family == AF_INET) {
if (check_sockaddr_in((struct sockaddr*)addrbuf, remotelen, LOG_INFO)<0) {
log_info(LD_NET,
"accept() returned a strange address; trying getsockname().");
@ -826,7 +951,8 @@ connection_handle_listener_read(connection_t *conn, int new_type)
/* check sockspolicy to see if we should accept it */
if (socks_policy_permits_address(ntohl(remote.sin_addr.s_addr)) == 0) {
tor_inet_ntoa(&remote.sin_addr, tmpbuf, sizeof(tmpbuf));
log_notice(LD_APP,"Denying socks connection from untrusted address %s.",
log_notice(LD_APP,
"Denying socks connection from untrusted address %s.",
tmpbuf);
tor_close_socket(news);
return 0;
@ -843,7 +969,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
}
}
newconn = connection_new(new_type);
newconn = connection_new(new_type, conn->sa_family);
newconn->s = news;
/* remember the remote address */
@ -851,6 +977,22 @@ connection_handle_listener_read(connection_t *conn, int new_type)
newconn->port = ntohs(remote.sin_port);
newconn->address = tor_dup_addr(newconn->addr);
} else if (conn->sa_family == AF_UNIX) {
/* For now only control ports can be unix domain sockets
* and listeners at the same time */
tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER);
newconn = connection_new(new_type, conn->sa_family);
newconn->s = news;
/* remember the remote address -- do we have anything sane to put here? */
newconn->addr = 0;
newconn->port = 1;
newconn->address = tor_strdup(conn->address);
} else {
tor_assert(0);
};
if (connection_add(newconn) < 0) { /* no space, forget it */
connection_free(newconn);
return 0; /* no need to tear down the parent */
@ -1007,7 +1149,8 @@ retry_listeners(int type, config_line_t *cfg,
int port_option, const char *default_addr,
smartlist_t *replaced_conns,
smartlist_t *new_conns,
int never_open_conns)
int never_open_conns,
int sa_family)
{
smartlist_t *launch = smartlist_create(), *conns;
int free_launch_elts = 1;
@ -1016,6 +1159,8 @@ retry_listeners(int type, config_line_t *cfg,
connection_t *conn;
config_line_t *line;
tor_assert(sa_family == AF_INET || sa_family == AF_UNIX);
if (cfg && port_option) {
for (c = cfg; c; c = c->next) {
smartlist_add(launch, c);
@ -1036,7 +1181,9 @@ retry_listeners(int type, config_line_t *cfg,
conns = get_connection_array();
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
if (conn->type != type || conn->marked_for_close)
if (conn->type != type ||
conn->sa_family != sa_family ||
conn->marked_for_close)
continue;
/* Okay, so this is a listener. Is it configured? */
line = NULL;
@ -1044,7 +1191,10 @@ retry_listeners(int type, config_line_t *cfg,
{
char *address=NULL;
uint16_t port;
if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) {
switch (sa_family) {
case AF_INET:
if (!parse_addr_port(LOG_WARN,
wanted->value, &address, NULL, &port)) {
int addr_matches = !strcasecmp(address, conn->address);
tor_free(address);
if (! port)
@ -1054,6 +1204,16 @@ retry_listeners(int type, config_line_t *cfg,
break;
}
}
break;
case AF_UNIX:
if (!strcasecmp(wanted->value, conn->address)) {
line = wanted;
break;
}
break;
default:
tor_assert(0);
}
});
if (! line) {
/* This one isn't configured. Close it. */
@ -1080,8 +1240,31 @@ retry_listeners(int type, config_line_t *cfg,
if (!never_open_conns) {
SMARTLIST_FOREACH(launch, config_line_t *, cfg_line,
{
conn = connection_create_listener(cfg_line->value,
(uint16_t) port_option, type);
char *address = NULL;
struct sockaddr *listensockaddr;
switch (sa_family) {
case AF_INET:
listensockaddr = (struct sockaddr *)
create_inet_sockaddr(cfg_line->value,
(uint16_t) port_option,
&address);
break;
case AF_UNIX:
listensockaddr = (struct sockaddr *)
create_unix_sockaddr(cfg_line->value,
&address);
break;
default:
tor_assert(0);
}
if (listensockaddr) {
conn = connection_create_listener(listensockaddr, type, address);
tor_free(address);
} else
conn = NULL;
if (!conn) {
r = -1;
} else {
@ -1115,32 +1298,45 @@ retry_all_listeners(smartlist_t *replaced_conns,
if (retry_listeners(CONN_TYPE_OR_LISTENER, options->ORListenAddress,
options->ORPort, "0.0.0.0",
replaced_conns, new_conns, options->ClientOnly)<0)
replaced_conns, new_conns, options->ClientOnly,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
options->DirPort, "0.0.0.0",
replaced_conns, new_conns, 0)<0)
replaced_conns, new_conns, 0,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress,
options->SocksPort, "127.0.0.1",
replaced_conns, new_conns, 0)<0)
replaced_conns, new_conns, 0,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
options->TransPort, "127.0.0.1",
replaced_conns, new_conns, 0)<0)
replaced_conns, new_conns, 0,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_AP_NATD_LISTENER, options->NatdListenAddress,
options->NatdPort, "127.0.0.1",
replaced_conns, new_conns, 0)<0)
replaced_conns, new_conns, 0,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress,
options->DNSPort, "127.0.0.1",
replaced_conns, new_conns, 0)<0)
replaced_conns, new_conns, 0,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
options->ControlListenAddress,
options->ControlPort, "127.0.0.1",
replaced_conns, new_conns, 0)<0)
replaced_conns, new_conns, 0,
AF_INET)<0)
return -1;
if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
options->ControlSocket,
options->ControlSocket ? 1 : 0, NULL,
replaced_conns, new_conns, 0,
AF_UNIX)<0)
return -1;
return 0;

View File

@ -1916,7 +1916,7 @@ connection_ap_make_bridge(char *address, uint16_t port,
log_notice(LD_APP,"Making internal anonymized tunnel to %s:%d ...",
safe_str(address),port); /* XXXX020 Downgrade back to info. */
conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
conn->_base.linked = 1; /* so that we can add it safely below. */
/* populate conn->socks_request */
@ -2210,7 +2210,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
}
log_debug(LD_EXIT,"Creating new exit connection.");
n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT, AF_INET));
n_stream->_base.purpose = EXIT_PURPOSE_CONNECT;
n_stream->stream_id = rh.stream_id;
@ -2316,7 +2316,7 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ)
* resolved; but if we didn't store them in a connection like this,
* the housekeeping in dns.c would get way more complicated.)
*/
dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT, AF_INET));
dummy_conn->stream_id = rh.stream_id;
dummy_conn->_base.address = tor_strndup(cell->payload+RELAY_HEADER_SIZE,
rh.length);
@ -2450,7 +2450,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
exitconn->_base.state = EXIT_CONN_STATE_OPEN;
dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
dirconn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
dirconn->_base.addr = 0x7f000001;
dirconn->_base.port = 0;

View File

@ -465,7 +465,7 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
return NULL;
}
conn = TO_OR_CONN(connection_new(CONN_TYPE_OR));
conn = TO_OR_CONN(connection_new(CONN_TYPE_OR, AF_INET));
/* set up conn so it's got all the data we need to remember */
connection_or_init_conn_from_address(conn, addr, port, id_digest, 1);

View File

@ -339,7 +339,7 @@ spawn_cpuworker(void)
tor_free(fdarray);
#endif
conn = connection_new(CONN_TYPE_CPUWORKER);
conn = connection_new(CONN_TYPE_CPUWORKER, AF_UNIX);
set_socket_nonblocking(fd);

View File

@ -490,7 +490,7 @@ directory_initiate_command(const char *address, uint32_t addr,
tor_assert(0);
}
conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR, AF_INET));
/* set up conn so it's got all the data we need to remember */
conn->_base.addr = addr;

View File

@ -110,7 +110,7 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
/* XXXX020 Send a stream event to the controller. */
/* Make a new dummy AP connection, and attach the request to it. */
conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
if (q->type == EVDNS_TYPE_A)
conn->socks_request->command = SOCKS_COMMAND_RESOLVE;

View File

@ -56,6 +56,9 @@
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
@ -791,6 +794,9 @@ typedef struct connection_t {
time_t timestamp_created; /**< When was this connection_t created? */
/* XXXX020 make this ipv6-capable */
int sa_family; /**< Address family of this connection's socket. Usually
* AF_INET, but it can also be AF_UNIX, or in the future
* AF_INET6 */
uint32_t addr; /**< IP of the other side of the connection; used to identify
* routers, along with port. */
uint16_t port; /**< If non-zero, port on the other end
@ -1825,6 +1831,8 @@ typedef struct {
int TransPort;
int NatdPort; /**< Port to listen on for transparent natd connections. */
int ControlPort; /**< Port to listen on for control connections. */
config_line_t * ControlSocket; /**< Unix Domain Socket to listen on
* for control connections. */
int DirPort; /**< Port to listen on for directory connections. */
int DNSPort; /**< Port to listen on for DNS requests. */
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
@ -2347,7 +2355,7 @@ or_options_t *options_new(void);
const char *conn_type_to_string(int type);
const char *conn_state_to_string(int type, int state);
connection_t *connection_new(int type);
connection_t *connection_new(int type, int sa_family);
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
void connection_unregister_events(connection_t *conn);
void connection_free(connection_t *conn);