mirror of
https://github.com/transmission/transmission
synced 2025-03-03 10:15:45 +00:00
add authentication support to daemon & remote
This commit is contained in:
parent
c0e5efbc03
commit
486bb768f0
4 changed files with 97 additions and 14 deletions
|
@ -38,12 +38,18 @@ saveState( tr_handle * h )
|
|||
{
|
||||
tr_benc d;
|
||||
const char * str;
|
||||
char * username = tr_sessionGetRPCUsername( h );
|
||||
char * password = tr_sessionGetRPCPassword( h );
|
||||
char * auth = tr_strdup_printf( "%s:%s", username, password );
|
||||
|
||||
tr_bencInitDict( &d, 12 );
|
||||
tr_bencInitDict( &d, 14 );
|
||||
tr_bencDictAddStr( &d, "download-dir", tr_sessionGetDownloadDir( h ) );
|
||||
tr_bencDictAddInt( &d, "peer-limit", tr_sessionGetPeerLimit( h ) );
|
||||
tr_bencDictAddInt( &d, "pex-allowed", tr_sessionIsPexEnabled( h ) );
|
||||
tr_bencDictAddInt( &d, "port", tr_sessionGetPeerPort( h ) );
|
||||
tr_bencDictAddStr( &d, "auth", auth );
|
||||
tr_bencDictAddInt( &d, "auth-required",
|
||||
tr_sessionIsRPCPasswordEnabled( h ) );
|
||||
tr_bencDictAddInt( &d, "port-forwarding-enabled",
|
||||
tr_sessionIsPortForwardingEnabled( h ) );
|
||||
tr_bencDictAddStr( &d, "rpc-acl", tr_sessionGetRPCACL( h ) );
|
||||
|
@ -67,10 +73,28 @@ saveState( tr_handle * h )
|
|||
tr_bencSaveFile( myConfigFilename, &d );
|
||||
|
||||
tr_bencFree( &d );
|
||||
tr_free( auth );
|
||||
tr_free( password );
|
||||
tr_free( username );
|
||||
}
|
||||
|
||||
static int
|
||||
parseAuth( const char * auth, char ** username, char ** password )
|
||||
{
|
||||
int err = 0;
|
||||
const char * pch = strchr( auth, ':' );
|
||||
if( !pch )
|
||||
err = -1;
|
||||
else {
|
||||
*username = tr_strndup( auth, pch-auth );
|
||||
*password = tr_strdup( pch+1 );
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
session_init( const char * configDir, int rpc_port, const char * rpc_acl )
|
||||
session_init( const char * configDir, int rpc_port,
|
||||
const char * acl, const char * auth, int noauth )
|
||||
{
|
||||
tr_benc state;
|
||||
int have_state;
|
||||
|
@ -84,10 +108,15 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
|
|||
int64_t down_limited = FALSE;
|
||||
int encryption = TR_ENCRYPTION_PREFERRED;
|
||||
char downloadDir[MAX_PATH_LENGTH] = { '\0' };
|
||||
const char * rpc_acl_fallback = TR_DEFAULT_RPC_ACL;
|
||||
const char * acl_fallback = TR_DEFAULT_RPC_ACL;
|
||||
int64_t rpc_port_fallback = TR_DEFAULT_RPC_PORT;
|
||||
int64_t auth_required_fallback = 0;
|
||||
const char * auth_fallback = NULL;
|
||||
tr_ctor * ctor;
|
||||
tr_torrent ** torrents;
|
||||
int auth_required;
|
||||
char * user = NULL;
|
||||
char * pass = NULL;
|
||||
|
||||
if(( have_state = !tr_bencLoadFile( myConfigFilename, &state )))
|
||||
{
|
||||
|
@ -100,7 +129,9 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
|
|||
tr_bencDictFindInt( &state, "pex-allowed", &pex_enabled );
|
||||
tr_bencDictFindInt( &state, "port", &peer_port );
|
||||
tr_bencDictFindInt( &state, "port-forwarding-enabled", &fwd_enabled );
|
||||
tr_bencDictFindStr( &state, "rpc-acl", &rpc_acl_fallback );
|
||||
tr_bencDictFindStr( &state, "rpc-acl", &acl_fallback );
|
||||
tr_bencDictFindStr( &state, "auth", &auth_fallback );
|
||||
tr_bencDictFindInt( &state, "auth-required", &auth_required_fallback );
|
||||
tr_bencDictFindInt( &state, "rpc-port", &rpc_port_fallback );
|
||||
tr_bencDictFindInt( &state, "speed-limit-down", &down_limit );
|
||||
tr_bencDictFindInt( &state, "speed-limit-down-enabled", &down_limited );
|
||||
|
@ -119,8 +150,23 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
|
|||
getcwd( downloadDir, sizeof( downloadDir ) );
|
||||
if( rpc_port < 1 )
|
||||
rpc_port = rpc_port_fallback;
|
||||
if( !rpc_acl || !*rpc_acl )
|
||||
rpc_acl = rpc_acl_fallback;
|
||||
if( !acl || !*acl )
|
||||
acl = acl_fallback;
|
||||
if( !auth || !*auth )
|
||||
auth = auth_fallback;
|
||||
|
||||
if( auth && parseAuth( auth, &user, &pass ) ) {
|
||||
tr_nerr( MY_NAME, "Unable to parse authentication string \"%s\"", auth );
|
||||
abort( );
|
||||
}
|
||||
|
||||
if( noauth ) {
|
||||
/* user has explicitly turned off authentication */
|
||||
user = NULL;
|
||||
pass = NULL;
|
||||
}
|
||||
|
||||
auth_required = user || pass;
|
||||
|
||||
/* start the session */
|
||||
mySession = tr_sessionInitFull( configDir, "daemon", downloadDir,
|
||||
|
@ -132,7 +178,11 @@ session_init( const char * configDir, int rpc_port, const char * rpc_acl )
|
|||
TR_MSG_INF, 0,
|
||||
FALSE, /* is the blocklist enabled? */
|
||||
TR_DEFAULT_PEER_SOCKET_TOS,
|
||||
TRUE, rpc_port, rpc_acl );
|
||||
TRUE, rpc_port, acl,
|
||||
auth_required, user, pass );
|
||||
|
||||
if( auth_required )
|
||||
tr_ninf( MY_NAME, "requiring authentication" );
|
||||
|
||||
/* load the torrents */
|
||||
ctor = tr_ctorNew( mySession );
|
||||
|
@ -156,6 +206,7 @@ daemonUsage( void )
|
|||
" -f --foreground Run in the foreground and log to stderr\n"
|
||||
" -g --config-dir <dir> Where to look for torrents and daemon-config.benc\n"
|
||||
" -h --help Display this message and exit\n"
|
||||
" -t --auth <user>:<pass> Username and password for authentication\n"
|
||||
" -p --port n Port to listen to for requests (Default: "TR_DEFAULT_RPC_PORT_STR")\n"
|
||||
"\n"
|
||||
MY_NAME" is a headless Transmission session\n"
|
||||
|
@ -165,24 +216,31 @@ daemonUsage( void )
|
|||
|
||||
static void
|
||||
readargs( int argc, char ** argv,
|
||||
int * nofork, int * port, char ** acl,
|
||||
char ** configDir )
|
||||
int * nofork, int * port,
|
||||
char ** configDir,
|
||||
char ** acl,
|
||||
char ** auth,
|
||||
int * noauth )
|
||||
{
|
||||
int opt;
|
||||
char optstr[] = "a:fg:hp:";
|
||||
char optstr[] = "a:fg:hnp:t:u:w:";
|
||||
struct option longopts[] = {
|
||||
{ "acl", required_argument, NULL, 'a' },
|
||||
{ "foreground", no_argument, NULL, 'f' },
|
||||
{ "config-dir", required_argument, NULL, 'g' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "noauth", no_argument, NULL, 'n' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "auth", required_argument, NULL, 't' },
|
||||
{ NULL, 0, NULL, '\0' }
|
||||
};
|
||||
while((( opt = getopt_long( argc, argv, optstr, longopts, NULL ))) != -1 ) {
|
||||
switch( opt ) {
|
||||
case 'a': *acl = tr_strdup( optarg ); break;
|
||||
case 'f': *nofork = 1; break;
|
||||
case 'n': *noauth = 1; break;
|
||||
case 'g': *configDir = tr_strdup( optarg ); break;
|
||||
case 't': *auth = tr_strdup( optarg ); break;
|
||||
case 'p': *port = atoi( optarg ); break;
|
||||
default: daemonUsage( ); break;
|
||||
}
|
||||
|
@ -253,9 +311,11 @@ int
|
|||
main( int argc, char ** argv )
|
||||
{
|
||||
int nofork = 0;
|
||||
int noauth = 0;
|
||||
int port = TR_DEFAULT_RPC_PORT;
|
||||
char * configDir = NULL;
|
||||
char * acl = NULL;
|
||||
char * auth = NULL;
|
||||
|
||||
signal( SIGINT, gotsig );
|
||||
signal( SIGQUIT, gotsig );
|
||||
|
@ -263,7 +323,7 @@ main( int argc, char ** argv )
|
|||
signal( SIGPIPE, SIG_IGN );
|
||||
signal( SIGHUP, SIG_IGN );
|
||||
|
||||
readargs( argc, argv, &nofork, &port, &acl, &configDir );
|
||||
readargs( argc, argv, &nofork, &port, &configDir, &acl, &auth, &noauth );
|
||||
if( configDir == NULL )
|
||||
configDir = tr_strdup_printf( "%s-daemon", tr_getDefaultConfigDir() );
|
||||
tr_buildPath( myConfigFilename, sizeof( myConfigFilename ),
|
||||
|
@ -276,7 +336,7 @@ main( int argc, char ** argv )
|
|||
}
|
||||
}
|
||||
|
||||
session_init( configDir, port, acl );
|
||||
session_init( configDir, port, acl, auth, noauth );
|
||||
|
||||
while( !closing )
|
||||
sleep( 1 );
|
||||
|
|
|
@ -65,6 +65,7 @@ showUsage( void )
|
|||
" -s --start all Start all stopped torrents\n"
|
||||
" -S --stop <int> Stop the torrent with the given ID\n"
|
||||
" -S --stop all Stop all running torrents\n"
|
||||
" -t --auth <user>:<pass> Username and password for authentication\n"
|
||||
" -u --upload-limit <int> Max upload rate in KiB/s\n"
|
||||
" -U --upload-unlimited No upload rate limit\n"
|
||||
" -v --verify <id> Verify the torrent's local data\n" );
|
||||
|
@ -86,6 +87,7 @@ numarg( const char * arg )
|
|||
static char * reqs[256]; /* arbitrary max */
|
||||
static int reqCount = 0;
|
||||
static int debug = 0;
|
||||
static char * auth = NULL;
|
||||
|
||||
static char*
|
||||
absolutify( char * buf, size_t len, const char * path )
|
||||
|
@ -114,7 +116,7 @@ static void
|
|||
readargs( int argc, char ** argv )
|
||||
{
|
||||
int opt;
|
||||
char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:u:Uv:";
|
||||
char optstr[] = "a:c:d:DeEf:ghlmMp:r:s:S:t:u:Uv:";
|
||||
|
||||
const struct option longopts[] =
|
||||
{
|
||||
|
@ -134,6 +136,7 @@ readargs( int argc, char ** argv )
|
|||
{ "remove", required_argument, NULL, 'r' },
|
||||
{ "start", required_argument, NULL, 's' },
|
||||
{ "stop", required_argument, NULL, 'S' },
|
||||
{ "auth", required_argument, NULL, 't' },
|
||||
{ "upload-limit", required_argument, NULL, 'u' },
|
||||
{ "upload-unlimited", no_argument, NULL, 'U' },
|
||||
{ "verify", required_argument, NULL, 'v' },
|
||||
|
@ -154,6 +157,9 @@ readargs( int argc, char ** argv )
|
|||
case 'g': debug = 1;
|
||||
addArg = FALSE;
|
||||
break;
|
||||
case 't': auth = tr_strdup( optarg );
|
||||
addArg = FALSE;
|
||||
break;
|
||||
case 'h': showUsage( );
|
||||
addArg = FALSE;
|
||||
break;
|
||||
|
@ -287,7 +293,7 @@ processResponse( const char * host, int port,
|
|||
(int)len, (int)len, (const char*) response );
|
||||
|
||||
if( tr_jsonParse( response, len, &top, NULL ) )
|
||||
tr_nerr( MY_NAME, "Unable to parse response" );
|
||||
tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len, (int)len, (char*)response );
|
||||
else
|
||||
{
|
||||
tr_benc *args, *list;
|
||||
|
@ -346,6 +352,10 @@ processRequests( const char * host, int port,
|
|||
curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
|
||||
curl_easy_setopt( curl, CURLOPT_POST, 1 );
|
||||
curl_easy_setopt( curl, CURLOPT_URL, url );
|
||||
if( auth ) {
|
||||
curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
|
||||
curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
|
||||
}
|
||||
|
||||
for( i=0; i<reqCount; ++i )
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
.Op Fl a Ar (+|-)x.x.x.x[/x],...
|
||||
.Op Fl g Ar directory
|
||||
.Op Fl p Ar port
|
||||
.Op Fl t Ar user:pass
|
||||
.Ek
|
||||
|
||||
.Sh DESCRIPTION
|
||||
|
@ -38,6 +39,12 @@ Run in the foreground and print errors to stderr instead of forking
|
|||
and logging errors with syslog.
|
||||
.It Fl g Fl -config-dir Ar directory
|
||||
Where to look for .torrent and config files on startup.
|
||||
.It Fl t Fl -auth Ar user:pass
|
||||
Requre
|
||||
.Ar username
|
||||
and
|
||||
.Ar password
|
||||
authentication
|
||||
.It Fl h Fl -help
|
||||
Print command-line option descriptions.
|
||||
.It Fl p Fl -port Ar port
|
||||
|
|
|
@ -24,6 +24,7 @@ and
|
|||
.Op Fl m
|
||||
.Op Fl M
|
||||
.Op Fl p Ar port
|
||||
.Op Fl t Ar user:pass
|
||||
.Op Fl q
|
||||
.Oo
|
||||
.Fl r Ar all | Ar id | Ar hash
|
||||
|
@ -83,6 +84,11 @@ Disable automatic port mapping.
|
|||
Attempt to bind to
|
||||
.Ar port
|
||||
for use as a listening port to accept incoming peer connections.
|
||||
.It Fl t Fl -auth Ar user:pass
|
||||
.Ar Username
|
||||
and
|
||||
.Ar password
|
||||
for authentication
|
||||
|
||||
.It Fl r Fl -remove Ar all | id | torrent-hash
|
||||
Remove all torrents, or the torrent matching the specified
|
||||
|
|
Loading…
Reference in a new issue