Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Mike Perry
Tor
Commits
a6dc00fa
Commit
a6dc00fa
authored
Jun 16, 2010
by
Steven Murdoch
Committed by
Nick Mathewson
Sep 30, 2010
Browse files
Start tor-fw-helper in the background, and log whatever it outputs
parent
3eaa9a37
Changes
8
Hide whitespace changes
Inline
Side-by-side
doc/tor.1.txt
View file @
a6dc00fa
...
...
@@ -873,6 +873,18 @@ is non-zero):
specified in ORPort. (Default: 0.0.0.0) This directive can be specified
multiple times to bind to multiple addresses/ports.
**PortForwarding** **0**|**1**::
Attempt to automatically forward the DirPort and ORPort on a NAT router
connecting this Tor server to the Internet. If set, Tor will try both
NAT-PMP (common on Apple routers) and UPnP (common on routers from other
manufacturers). (Default: 0)
**PortForwardingHelper** __filename__|__pathname__::
If PortForwarding is set, use this executable to configure the forwarding.
If set to a filename, the system path will be searched for the executable.
If set to a path, only the specified path will be executed.
(Default: tor-fw-helper)
**PublishServerDescriptor** **0**|**1**|**v1**|**v2**|**v3**|**bridge**|**hidserv**,**...**::
This option is only considered if you have an ORPort defined. You can
choose multiple arguments, separated by commas.
...
...
src/common/util.c
View file @
a6dc00fa
...
...
@@ -14,6 +14,7 @@
#define _GNU_SOURCE
#include
"orconfig.h"
#define UTIL_PRIVATE
#include
"util.h"
#include
"torlog.h"
#undef log
...
...
@@ -2877,3 +2878,400 @@ load_windows_system_library(const TCHAR *library_name)
return
LoadLibrary
(
path
);
}
#endif
/** Format child_state and saved_errno as a hex string placed in hex_errno.
* Called between fork and _exit, so must be signal-handler safe */
void
format_helper_exit_status
(
unsigned
char
child_state
,
int
saved_errno
,
char
*
hex_errno
)
{
/* Convert errno to be unsigned for hex conversion */
unsigned
int
unsigned_errno
;
char
*
cur
;
/* If errno is negative, negate it */
if
(
saved_errno
<
0
)
{
unsigned_errno
=
(
unsigned
int
)
-
saved_errno
;
}
else
{
unsigned_errno
=
(
unsigned
int
)
saved_errno
;
}
/* Convert errno to hex (start before \n) */
cur
=
hex_errno
+
HEX_ERRNO_SIZE
-
2
;
do
{
*
cur
--
=
"0123456789ABCDEF"
[
unsigned_errno
%
16
];
unsigned_errno
/=
16
;
}
while
(
unsigned_errno
!=
0
&&
cur
>=
hex_errno
);
/* Add on the minus side if errno was negative */
if
(
saved_errno
<
0
)
*
cur
--
=
'-'
;
/* Leave a gap */
*
cur
--
=
'/'
;
/* Convert child_state to hex */
do
{
*
cur
--
=
"0123456789ABCDEF"
[
child_state
%
16
];
child_state
/=
16
;
}
while
(
child_state
!=
0
&&
cur
>=
hex_errno
);
}
/* Maximum number of file descriptors, if we cannot get it via sysconf() */
#define DEFAULT_MAX_FD 256
#define CHILD_STATE_INIT 0
#define CHILD_STATE_PIPE 1
#define CHILD_STATE_MAXFD 2
#define CHILD_STATE_FORK 3
#define CHILD_STATE_DUPOUT 4
#define CHILD_STATE_DUPERR 5
#define CHILD_STATE_REDIRECT 6
#define CHILD_STATE_CLOSEFD 7
#define CHILD_STATE_EXEC 8
#define CHILD_STATE_FAILEXEC 9
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
/** Start a program in the background. If <b>filename</b> contains a '/', then
* it will be treated as an absolute or relative path. Otherwise the system
* path will be searched for <b>filename</b>. Returns pid on success, otherwise
* returns -1.
* Some parts of this code are based on the POSIX subprocess module from Python
*/
static
int
tor_spawn_background
(
const
char
*
const
filename
,
int
*
stdout_read
,
int
*
stderr_read
,
const
char
**
argv
)
{
pid_t
pid
;
int
stdout_pipe
[
2
];
int
stderr_pipe
[
2
];
int
fd
,
retval
;
ssize_t
nbytes
;
const
char
*
error_message
=
SPAWN_ERROR_MESSAGE
;
size_t
error_message_length
;
/* Represents where in the process of spawning the program is;
this is used for printing out the error message */
unsigned
char
child_state
=
CHILD_STATE_INIT
;
char
hex_errno
[
HEX_ERRNO_SIZE
];
static
int
max_fd
=
-
1
;
/* We do the strlen here because strlen() is not signal handler safe,
and we are not allowed to use unsafe functions between fork and exec */
error_message_length
=
strlen
(
error_message
);
/* Fill hex_errno with spaces, and a trailing newline */
memset
(
hex_errno
,
' '
,
sizeof
(
hex_errno
)
-
1
);
hex_errno
[
sizeof
(
hex_errno
)
-
1
]
=
'\n'
;
child_state
=
CHILD_STATE_PIPE
;
/* Set up pipe for redirecting stdout and stderr of child */
retval
=
pipe
(
stdout_pipe
);
if
(
-
1
==
retval
)
{
log_err
(
LD_GENERAL
,
"Failed to set up pipe for stdout communication with child process: %s"
,
strerror
(
errno
));
return
-
1
;
}
retval
=
pipe
(
stderr_pipe
);
if
(
-
1
==
retval
)
{
log_err
(
LD_GENERAL
,
"Failed to set up pipe for stderr communication with child process: %s"
,
strerror
(
errno
));
return
-
1
;
}
child_state
=
CHILD_STATE_MAXFD
;
#ifdef _SC_OPEN_MAX
if
(
-
1
!=
max_fd
)
{
max_fd
=
(
int
)
sysconf
(
_SC_OPEN_MAX
);
if
(
max_fd
==
-
1
)
max_fd
=
DEFAULT_MAX_FD
;
log_warn
(
LD_GENERAL
,
"Cannot find maximum file descriptor, assuming %d"
,
max_fd
);
}
#else
max_fd
=
DEFAULT_MAX_FD
;
#endif
child_state
=
CHILD_STATE_FORK
;
pid
=
fork
();
if
(
0
==
pid
)
{
/* In child */
child_state
=
CHILD_STATE_DUPOUT
;
/* Link child stdout to the write end of the pipe */
retval
=
dup2
(
stdout_pipe
[
1
],
STDOUT_FILENO
);
if
(
-
1
==
retval
)
goto
error
;
child_state
=
CHILD_STATE_DUPERR
;
/* Link child stderr to the write end of the pipe */
retval
=
dup2
(
stderr_pipe
[
1
],
STDERR_FILENO
);
if
(
-
1
==
retval
)
goto
error
;
child_state
=
CHILD_STATE_REDIRECT
;
/* Link stdin to /dev/null */
fd
=
open
(
"/dev/null"
,
O_RDONLY
);
if
(
fd
!=
-
1
)
dup2
(
STDIN_FILENO
,
fd
);
else
goto
error
;
child_state
=
CHILD_STATE_CLOSEFD
;
/* Close all other fds, including the read end of the pipe */
/* TODO: use closefrom if available */
for
(
fd
=
STDERR_FILENO
+
1
;
fd
<
max_fd
;
fd
++
)
close
(
fd
);
child_state
=
CHILD_STATE_EXEC
;
/* Call the requested program. We need the cast because
execvp doesn't define argv as const, even though it
does not modify the arguments */
execvp
(
filename
,
(
char
*
const
*
)
argv
);
/* If we got here, the exec or open(/dev/null) failed */
child_state
=
CHILD_STATE_FAILEXEC
;
error:
/* TODO: are we leaking fds from the pipe? */
format_helper_exit_status
(
child_state
,
errno
,
hex_errno
);
/* Write the error message. GCC requires that we check the return
value, but there is nothing we can do if it fails */
nbytes
=
write
(
STDOUT_FILENO
,
error_message
,
error_message_length
);
nbytes
=
write
(
STDOUT_FILENO
,
hex_errno
,
sizeof
(
hex_errno
));
_exit
(
255
);
return
-
1
;
/* Never reached, but avoids compiler warning */
}
/* In parent */
if
(
-
1
==
pid
)
{
log_err
(
LD_GENERAL
,
"Failed to fork child process: %s"
,
strerror
(
errno
));
close
(
stdout_pipe
[
0
]);
close
(
stdout_pipe
[
1
]);
close
(
stderr_pipe
[
0
]);
close
(
stderr_pipe
[
1
]);
return
-
1
;
}
/* Return read end of the pipes to caller, and close write end */
*
stdout_read
=
stdout_pipe
[
0
];
retval
=
close
(
stdout_pipe
[
1
]);
if
(
-
1
==
retval
)
{
log_err
(
LD_GENERAL
,
"Failed to close write end of stdout pipe in parent process: %s"
,
strerror
(
errno
));
/* Do not return -1, because the child is running, so the parent
needs to know about the pid in order to reap it later */
}
*
stderr_read
=
stderr_pipe
[
0
];
retval
=
close
(
stderr_pipe
[
1
]);
if
(
-
1
==
retval
)
{
log_err
(
LD_GENERAL
,
"Failed to close write end of stderr pipe in parent process: %s"
,
strerror
(
errno
));
/* Do not return -1, because the child is running, so the parent
needs to know about the pid in order to reap it later */
}
return
pid
;
}
/** Read from stream, and send lines to log at the specified log level.
* Returns 1 if stream is closed normally, -1 if there is a error reading, and
* 0 otherwise. Handles lines from tor-fw-helper and
* tor_spawn_background() specially.
*/
static
int
log_from_pipe
(
FILE
*
stream
,
int
severity
,
const
char
*
executable
,
int
*
child_status
)
{
char
buf
[
256
];
for
(;;)
{
char
*
retval
;
retval
=
fgets
(
buf
,
sizeof
(
buf
),
stream
);
if
(
NULL
==
retval
)
{
if
(
feof
(
stream
))
{
/* Program has closed stream (probably it exited) */
/* TODO: check error */
fclose
(
stream
);
return
1
;
}
else
{
if
(
EAGAIN
==
errno
)
{
/* Nothing more to read, try again next time */
return
0
;
}
else
{
/* There was a problem, abandon this child process */
fclose
(
stream
);
return
-
1
;
}
}
}
else
{
/* We have some data, log it and keep asking for more */
size_t
len
;
len
=
strlen
(
buf
);
if
(
buf
[
len
-
1
]
==
'\n'
)
{
/* Remove the trailing newline */
buf
[
len
-
1
]
=
'\0'
;
}
else
{
/* No newline; check whether we overflowed the buffer */
if
(
!
feof
(
stream
))
log_err
(
LD_GENERAL
,
"Line from port forwarding helper was truncated: %s"
,
buf
);
/* TODO: What to do with this error? */
}
/* Check if buf starts with SPAWN_ERROR_MESSAGE */
if
(
strstr
(
buf
,
SPAWN_ERROR_MESSAGE
)
==
buf
)
{
/* Parse error message */
int
retval
,
child_state
,
saved_errno
;
retval
=
sscanf
(
buf
,
SPAWN_ERROR_MESSAGE
"%d/%d"
,
&
child_state
,
&
saved_errno
);
if
(
retval
==
2
)
{
log_err
(
LD_GENERAL
,
"Failed to start child process
\"
%s
\"
in state %d: %s"
,
executable
,
child_state
,
strerror
(
saved_errno
));
if
(
child_status
)
*
child_status
=
1
;
}
else
{
/* Failed to parse message from child process, log it as error */
log_err
(
LD_GENERAL
,
"Unexpected message from port forwarding helper
\"
%s
\"
: %s"
,
executable
,
buf
);
}
}
else
{
log_fn
(
severity
,
LD_GENERAL
,
"Port forwarding helper says: %s"
,
buf
);
}
}
}
/* We should never get here */
return
-
1
;
}
void
tor_check_port_forwarding
(
const
char
*
filename
,
int
dir_port
,
int
or_port
,
time_t
now
)
{
/* When fw-helper succeeds, how long do we wait until running it again */
#define TIME_TO_EXEC_FWHELPER_SUCCESS 300
/* When fw-helper fails, how long do we wait until running it again */
#define TIME_TO_EXEC_FWHELPER_FAIL 60
static
int
child_pid
=
-
1
;
static
FILE
*
stdout_read
=
NULL
;
static
FILE
*
stderr_read
=
NULL
;
static
time_t
time_to_run_helper
=
0
;
int
stdout_status
,
stderr_status
,
retval
;
const
char
*
argv
[
10
];
char
s_dirport
[
6
],
s_orport
[
6
];
tor_assert
(
filename
);
/* Set up command line for tor-fw-helper */
snprintf
(
s_dirport
,
sizeof
s_dirport
,
"%d"
,
dir_port
);
snprintf
(
s_orport
,
sizeof
s_orport
,
"%d"
,
or_port
);
/* TODO: Allow different internal and external ports */
argv
[
0
]
=
filename
;
argv
[
1
]
=
"--internal-or-port"
;
argv
[
2
]
=
s_orport
;
argv
[
3
]
=
"--external-or-port"
;
argv
[
4
]
=
s_orport
;
argv
[
5
]
=
"--internal-dir-port"
;
argv
[
6
]
=
s_dirport
;
argv
[
7
]
=
"--external-dir-port"
;
argv
[
8
]
=
s_dirport
;
argv
[
9
]
=
NULL
;
/* Start the child, if it is not already running */
if
(
-
1
==
child_pid
&&
time_to_run_helper
<
now
)
{
int
fd_out
,
fd_err
;
/* Assume tor-fw-helper will succeed, start it later*/
time_to_run_helper
=
now
+
TIME_TO_EXEC_FWHELPER_SUCCESS
;
child_pid
=
tor_spawn_background
(
filename
,
&
fd_out
,
&
fd_err
,
argv
);
if
(
child_pid
<
0
)
{
log_err
(
LD_GENERAL
,
"Failed to start port forwarding helper %s"
,
filename
);
child_pid
=
-
1
;
return
;
}
/* Set stdout/stderr pipes to be non-blocking */
fcntl
(
fd_out
,
F_SETFL
,
O_NONBLOCK
);
fcntl
(
fd_err
,
F_SETFL
,
O_NONBLOCK
);
/* Open the buffered IO streams */
stdout_read
=
fdopen
(
fd_out
,
"r"
);
stderr_read
=
fdopen
(
fd_err
,
"r"
);
log_info
(
LD_GENERAL
,
"Started port forwarding helper (%s) with pid %d"
,
filename
,
child_pid
);
}
/* If child is running, read from its stdout and stderr) */
if
(
child_pid
>
0
)
{
/* Read from stdout/stderr and log result */
retval
=
0
;
stdout_status
=
log_from_pipe
(
stdout_read
,
LOG_INFO
,
filename
,
&
retval
);
stderr_status
=
log_from_pipe
(
stderr_read
,
LOG_ERR
,
filename
,
&
retval
);
if
(
retval
)
{
/* There was a problem in the child process */
time_to_run_helper
=
now
+
TIME_TO_EXEC_FWHELPER_FAIL
;
}
/* Combine the two statuses in order of severity */
if
(
-
1
==
stdout_status
||
-
1
==
stderr_status
)
/* There was a failure */
retval
=
-
1
;
else
if
(
1
==
stdout_status
||
1
==
stderr_status
)
/* stdout or stderr was closed */
retval
=
1
;
else
/* Both are fine */
retval
=
0
;
/* If either pipe indicates a failure, act on it */
if
(
0
!=
retval
)
{
if
(
1
==
retval
)
{
log_info
(
LD_GENERAL
,
"Port forwarding helper terminated"
);
}
else
{
log_err
(
LD_GENERAL
,
"Failed to read from port forwarding helper"
);
}
/* TODO: The child might not actually be finished (maybe it failed or
closed stdout/stderr), so maybe we shouldn't start another? */
child_pid
=
-
1
;
}
}
}
src/common/util.h
View file @
a6dc00fa
...
...
@@ -340,10 +340,25 @@ void start_daemon(void);
void
finish_daemon
(
const
char
*
desired_cwd
);
void
write_pidfile
(
char
*
filename
);
/* Port forwarding */
void
tor_check_port_forwarding
(
const
char
*
filename
,
int
dir_port
,
int
or_port
,
time_t
now
);
#ifdef MS_WINDOWS
HANDLE
load_windows_system_library
(
const
TCHAR
*
library_name
);
#endif
#ifdef UTIL_PRIVATE
/* Prototypes for private functions only used by util.c (and unit tests) */
void
format_helper_exit_status
(
unsigned
char
child_state
,
int
saved_errno
,
char
*
hex_errno
);
/* Space for hex values of child state, a slash, saved_errno (with
leading minus) and newline (no null) */
#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \
1 + sizeof(int) * 2 + 1)
#endif
const
char
*
libor_get_digests
(
void
);
#endif
...
...
src/or/config.c
View file @
a6dc00fa
...
...
@@ -317,6 +317,8 @@ static config_var_t _option_vars[] = {
V
(
PerConnBWRate
,
MEMUNIT
,
"0"
),
V
(
PidFile
,
STRING
,
NULL
),
V
(
TestingTorNetwork
,
BOOL
,
"0"
),
V
(
PortForwarding
,
BOOL
,
"0"
),
V
(
PortForwardingHelper
,
FILENAME
,
"tor-fw-helper"
),
V
(
PreferTunneledDirConns
,
BOOL
,
"1"
),
V
(
ProtocolWarnings
,
BOOL
,
"0"
),
V
(
PublishServerDescriptor
,
CSV
,
"1"
),
...
...
src/or/main.c
View file @
a6dc00fa
...
...
@@ -1026,6 +1026,7 @@ run_scheduled_events(time_t now)
static
time_t
time_to_check_for_expired_networkstatus
=
0
;
static
time_t
time_to_write_stats_files
=
0
;
static
time_t
time_to_write_bridge_stats
=
0
;
static
time_t
time_to_check_port_forwarding
=
0
;
static
int
should_init_bridge_stats
=
1
;
static
time_t
time_to_retry_dns_init
=
0
;
or_options_t
*
options
=
get_options
();
...
...
@@ -1385,6 +1386,17 @@ run_scheduled_events(time_t now)
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
time_to_write_bridge_status_file
=
now
+
BRIDGE_STATUSFILE_INTERVAL
;
}
if
(
time_to_check_port_forwarding
<
now
&&
options
->
PortForwarding
&&
server_mode
(
options
))
{
#define PORT_FORWARDING_CHECK_INTERVAL 5
tor_check_port_forwarding
(
options
->
PortForwardingHelper
,
options
->
DirPort
,
options
->
ORPort
,
now
);
time_to_check_port_forwarding
=
now
+
PORT_FORWARDING_CHECK_INTERVAL
;
}
}
/** Timer: used to invoke second_elapsed_callback() once per second. */
...
...
src/or/or.h
View file @
a6dc00fa
...
...
@@ -2772,6 +2772,10 @@ typedef struct {
* possible. */
int
PreferTunneledDirConns
;
/**< If true, avoid dirservers that don't
* support BEGIN_DIR, when possible. */
int
PortForwarding
;
/**< If true, use NAT-PMP or UPnP to automatically
* forward the DirPort and ORPort on the NAT device */
char
*
PortForwardingHelper
;
/** < Filename or full path of the port
forwarding helper executable */
int
AllowNonRFC953Hostnames
;
/**< If true, we allow connections to hostnames
* with weird characters. */
/** If true, we try resolving hostnames with weird characters. */
...
...
src/test/test_util.c
View file @
a6dc00fa
...
...
@@ -6,6 +6,7 @@
#include
"orconfig.h"
#define CONTROL_PRIVATE
#define MEMPOOL_PRIVATE
#define UTIL_PRIVATE
#include
"or.h"
#include
"config.h"
#include
"control.h"
...
...
@@ -1208,6 +1209,45 @@ test_util_load_win_lib(void *ptr)
}
#endif
static
void
clear_hex_errno
(
char
*
hex_errno
)
{
memset
(
hex_errno
,
' '
,
HEX_ERRNO_SIZE
-
2
);
hex_errno
[
HEX_ERRNO_SIZE
-
1
]
=
'\n'
;
hex_errno
[
HEX_ERRNO_SIZE
]
=
'\0'
;
}
static
void
test_util_exit_status
(
void
*
ptr
)
{
char
hex_errno
[
HEX_ERRNO_SIZE
+
1
];
(
void
)
ptr
;
clear_hex_errno
(
hex_errno
);
format_helper_exit_status
(
0
,
0
,
hex_errno
);
tt_str_op
(
hex_errno
,
==
,
" 0/0
\n
"
);
clear_hex_errno
(
hex_errno
);
format_helper_exit_status
(
0
,
0x7FFFFFFF
,
hex_errno
);
tt_str_op
(
hex_errno
,
==
,
" 0/7FFFFFFF
\n
"
);
clear_hex_errno
(
hex_errno
);
format_helper_exit_status
(
0xFF
,
-
0x80000000
,
hex_errno
);
tt_str_op
(
hex_errno
,
==
,
"FF/-80000000
\n
"
);
clear_hex_errno
(
hex_errno
);
format_helper_exit_status
(
0x7F
,
0
,
hex_errno
);
tt_str_op
(
hex_errno
,
==
,
" 7F/0
\n
"
);
clear_hex_errno
(
hex_errno
);
format_helper_exit_status
(
0x08
,
-
0x242
,
hex_errno
);
tt_str_op
(
hex_errno
,
==
,
" 8/-242
\n
"
);
done:
;
}
#define UTIL_LEGACY(name) \
{ #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
...
...
@@ -1234,6 +1274,7 @@ struct testcase_t util_tests[] = {
#ifdef MS_WINDOWS
UTIL_TEST
(
load_win_lib
,
0
),
#endif
UTIL_TEST
(
exit_status
,
0
),
END_OF_TESTCASES
};
src/tools/tor-fw-helper/tor-fw-helper.c
View file @
a6dc00fa
...
...
@@ -115,7 +115,7 @@ log_commandline_options(int argc, char **argv)
logfile
=
fopen
(
"tor-fw-helper.log"
,
"a"
);
if
(
NULL
==
logfile
)
return
-
1
;
/* Send all commandline arguments to the file */
now
=
time
(
NULL
);
retval
=
fprintf
(
logfile
,
"START: %s
\n
"
,
ctime
(
&
now
));
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment