rpcfwd.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2014-1/
  • rpc/
  • rpcfwd.cc
  • View
  • Commits
  • Open Download .zip Download (9 KB)
/*
 * Copyright 1995, 2006 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

# include <stdhdrs.h>

# include <strbuf.h>
# include <strdict.h>
# include <error.h>
# include <ticket.h>
# include <md5.h>
# include <keepalive.h>
# include <debug.h>

# include <rpc.h>
# include <rpcfwd.h>
# include <rpcdebug.h>
# include <rpcdispatch.h>
# include <rpcservice.h>

# include <p4tags.h>

/*
 * RpcForward -- connect two RPCs together
 */

/*
 * s2cCompress1() - process compression request from server to client
 * c2sCompress2() - process compression ack from client to server
 *
 * s2cFlush1() - dispatch from client if pipe through client now full
 * c2sFlsuh2() - note pipe through client emptying
 *
 * s2cForward() - forward message wholesale from server to client
 * c2sForward() - forward message wholesale from client to server
 */

void s2cCompress1( Rpc *rpc, Error *e ) { rpc->GetForwarder()->Compress1( e ); }
void c2sCompress2( Rpc *rpc, Error *e ) { rpc->GetForwarder()->Compress2( e ); }
void s2cFlush1( Rpc *rpc, Error *e ) { rpc->GetForwarder()->Flush1( e ); }
void c2sFlush2( Rpc *rpc, Error *e ) { rpc->GetForwarder()->Flush2( e ); }
void s2cForward( Rpc *rpc, Error *e ) { rpc->GetForwarder()->ForwardS2C(); }
void c2sForward( Rpc *rpc, Error *e ) { rpc->GetForwarder()->ForwardC2S(); }
void s2cCrypto( Rpc *rpc, Error *e ) { rpc->GetForwarder()->CryptoS2C( e ); }
void c2sCrypto( Rpc *rpc, Error *e ) { rpc->GetForwarder()->CryptoC2S( e ); }

const RpcDispatch s2cDispatch[] = {
	P4Tag::p_compress1,	RpcCallback( s2cCompress1 ),
	P4Tag::p_flush1,	RpcCallback( s2cFlush1 ),
	P4Tag::p_protocol,	RpcCallback( s2cForward ),
	P4Tag::c_Crypto,	RpcCallback( s2cCrypto ),
	P4Tag::p_funcHandler,	RpcCallback( s2cForward ),
	0, 0
} ;

const RpcDispatch c2sDispatch[] = {
	P4Tag::p_compress2,	RpcCallback( c2sCompress2 ),
	P4Tag::p_flush2,	RpcCallback( c2sFlush2 ),
	P4Tag::p_protocol,	RpcCallback( c2sForward ),
	"crypto",		RpcCallback( c2sCrypto ),
	P4Tag::p_funcHandler,	RpcCallback( c2sForward ),
	0, 0
} ;

/*
 * RpcForward -- connect two RPCs together
 */

RpcForward::RpcForward( Rpc *client, Rpc *server )
{
	this->client = client;
	this->server = server;

	c2sDispatcher = new RpcDispatcher;
	s2cDispatcher = new RpcDispatcher;

	c2sDispatcher->Add( rpcServices );
	s2cDispatcher->Add( rpcServices );
	c2sDispatcher->Add( c2sDispatch );
	s2cDispatcher->Add( s2cDispatch );

	client->SetForwarder( this );
	server->SetForwarder( this );

	duplexCount = 0;
	himarkadjustment = 50;
}

RpcForward::~RpcForward()
{
	delete c2sDispatcher;
	delete s2cDispatcher;
}

void
RpcForward::Dispatch()
{
	// Dispatch until released

	server->Dispatch( Rpc::DfComplete, s2cDispatcher );
}

void
RpcForward::DispatchOne()
{
	// Dispatch once
	server->DispatchOne( s2cDispatcher );
}

void
RpcForward::ClientDispatchOne()
{
	// Dispatch once
	client->DispatchOne( c2sDispatcher );
}

void
RpcForward::ForwardNoInvoke( Rpc *src, Rpc *dst, StrRef &func )
{
	int i;
	StrRef var, val;

	// copy unnamed args, then named vars

	for( i = 0; i < src->GetArgc(); ++i )
	    dst->SetVar( StrRef::Null(), *src->GetArgi(i) );

	for( i = 0; src->GetVar( i, var, val ) && var != P4Tag::v_func; ++i )
	    dst->SetVar( var, val );

	// last is "func"
	func = val;
}

void
RpcForward::Forward( Rpc *src, Rpc *dst )
{
	int i;
	StrRef var, val;

	// copy unnamed args, then named vars

	for( i = 0; i < src->GetArgc(); ++i )
	    dst->SetVar( StrRef::Null(), *src->GetArgi(i) );

	for( i = 0; src->GetVar( i, var, val ) && var != P4Tag::v_func; ++i )
	    dst->SetVar( var, val );

	// last is "func"

	dst->Invoke( val.Text() );
}

void
RpcForward::InvokeDict( StrDict *dict, Rpc *dst )
{
	int i;
	StrRef var, val;

	for( i = 0; dict->GetVar( i, var, val ) && var != P4Tag::v_func; ++i )
	    dst->SetVar( var, val );

	dst->Invoke( val.Text() );
}

void
RpcForward::ForwardExcept( Rpc *src, Rpc *dst, const StrPtr &except )
{
	int i;
	StrRef var, val;

	// copy unnamed args, then named vars

	for( i = 0; i < src->GetArgc(); ++i )
	    dst->SetVar( StrRef::Null(), *src->GetArgi(i) );

	for( i = 0; src->GetVar( i, var, val ) && var != P4Tag::v_func; ++i )
	    if( var != except )
		dst->SetVar( var, val );

	// last is "func"

	dst->Invoke( val.Text() );
}

void
RpcForward::Compress1( Error *e )
{
	server->GotRecvCompressed( e );
	Forward( server, client );
	client->GotSendCompressed( e );
}

void
RpcForward::Compress2( Error *e )
{
	client->GotRecvCompressed( e );
	Forward( client, server );
	server->GotSendCompressed( e );
}

void
RpcForward::Flush1( Error *e )
{
	StrRef himarkVar( P4Tag::v_himark );

	StrPtr *fseq = server->GetVar( P4Tag::v_fseq );
	StrPtr *himark = server->GetVar( himarkVar );

	if( fseq ) duplexCount += fseq->Atoi();

	// nb. Our high mark is gets adjusted to avoid lockstep network delays

	if( himark )
	{
	    // Note:  The himark comes from the server,  so
	    // we adjust it down when calculating if the pipe through
	    // the client is now full.  However this intermediate
	    // process may have a (rsh:) pipe connection rather than
	    // a socket,  in that case its size is limited by 2K
	    // buffers,  adjust himark to compensate.

	    int himark2 = himark->Atoi();

	    if( client->IsSingle() && himark2 > 4096 )
	        himark2 = 4096;

	    if( himark2 > 0 && himarkadjustment < 100 )
	    {
		int newhimark = himarkadjustment * himark2 / 100;

		// no less than 1k (unless zero - zero is fine and handled above)
		if( newhimark < 1024 )
		    newhimark = 1024;

		himark2 = newhimark;
	    }

	    if( client->recvBuffering == 0 )
	        client->recvBuffering = client->GetRecvBuffering();
	    if( client->recvBuffering > 0 && client->recvBuffering < himark2 )
	        himark2 = client->recvBuffering;

	    client->SetVar( himarkVar, StrNum( himark2 ) );

	    ForwardExcept( server, client, himarkVar );

	    KeepAlive *k = client->GetKeepAlive();

	    while( duplexCount > himark2 )
	    {
		if( client->Dropped() )
		    if( !k || !k->IsAlive() || client->ReadErrors() )
			break;
		client->DispatchOne( c2sDispatcher );
	    }
	}
	else
	    Forward( server, client );
}

void
RpcForward::Flush2( Error *e )
{
	Forward( client, server );

	StrPtr *fseq = client->GetVar( P4Tag::v_fseq );

	if( fseq ) duplexCount -= fseq->Atoi();
}

void
RpcForward::SetCrypto( StrPtr *svr, StrPtr *tfile )
{
	crypto.Set( svr, tfile );
}

void
RpcForward::CryptoC2S( Error * )
{
	crypto.C2S( client, server );

	Forward( client, server );
}

void
RpcForward::CryptoS2C( Error * )
{
	Forward( server, client );

	crypto.S2C( server );
}

/*
 * RpcCrypto -- handle intermediate service authentication
 */

RpcCrypto::RpcCrypto()
    : attackCount( 0 )
{
}

RpcCrypto::~RpcCrypto()
{
}

void
RpcCrypto::Set( StrPtr *svr, StrPtr *tfile )
{
	if( svr )
	    svrname = *svr;

	if( tfile )
	    ticketFile = *tfile;
}

void
RpcCrypto::S2C( Rpc *server )
{
	cryptoToken.Set( server->GetVar( P4Tag::v_token ) );

	StrPtr *addr = server->GetVar( P4Tag::v_serverAddress );

	if( addr )
	{
	    serverID.Set( addr );

	    // lookup ticket from svrname user
	    if( svrname.Length() )
	    {
		Ticket t( &ticketFile );
		const char *c = t.GetTicket( *addr, svrname );

		if( c )
		    ticket.Set( c );
	    }
	}
}

void
RpcCrypto::C2S( Rpc *client, Rpc *server )
{
	// Set intermediate authentication stuff...

	MD5	md5;
	StrPtr *daddr;
	StrPtr *addr;
	int	clevel = 0;

	StrRef	daddrref( P4Tag::v_daddr );
	StrRef	caddrref( P4Tag::v_caddr );

	daddr = client->GetVar( P4Tag::v_ipaddr );

	if( !daddr )
	    daddr = client->GetVar( daddrref );
	else
	    DEBUGPRINTF( DEBUG_SVR_INFO, "client daddr %s", daddr->Text() );

	while( client->GetVar( daddrref, clevel ) )
	    ++clevel;

	addr = client->GetPeerAddress( 0 );

	if( clevel )
	{
	    // nested proxy ( not closest to client )
	    daddr = client->GetVar( daddrref, clevel - 1 );
	    if( addr )
		server->SetVar( caddrref, clevel, *addr );
	}
	else
	{
	    // first level proxy

	    if( client->GetVar( caddrref ) )
	    {
		DEBUGPRINT( DEBUG_SVR_WARN,
			"client sent caddr to forwarder - ATTACK");
		attackCount++;
		md5.Update( caddrref );
		server->SetVar( P4Tag::v_attack, caddrref );
	    }

	    if( addr )
		server->SetVar( caddrref, *addr );
	}

	if( daddr && ( addr = client->GetAddress( RAF_PORT ) ) )
	{
	    if( *addr != *daddr )
	    {
		attackCount++;
		md5.Update( *addr );
		server->SetVar( P4Tag::v_attack, clevel, *addr );
		DEBUGPRINTF( DEBUG_SVR_WARN,
			"forwarder to unknown %s vs %s - ATTACK",
			addr->Text(), daddr->Text() );
	    }
	    else
		DEBUGPRINTF( DEBUG_SVR_INFO,
			"forwarder to unknown %s vs %s",
			addr->Text(), daddr->Text() );
	}

	if( addr = server->GetPeerAddress( RAF_PORT ) )
	{
	    StrBuf phash;

	    if( svrname.Length() > 0 )
	    {
		md5.Update( svrname );
		server->SetVar( P4Tag::v_svrname, clevel, svrname );
	    }
	    if( ticket.Length() > 0 )
		md5.Update( ticket );
	    md5.Update( cryptoToken );

	    md5.Update( *addr );
	    server->SetVar( daddrref, clevel, *addr );
	    md5.Final( phash );
	    server->SetVar( P4Tag::v_dhash, clevel, phash );
	}
}
# Change User Description Committed
#1 15902 Matt Attaway A second renaming that I will not obliterate as a badge of shame
//guest/perforce_software/p4/2014_1/rpc/rpcfwd.cc
#1 15901 Matt Attaway Clean up code to fit modern Workshop naming standards
//guest/perforce_software/p4/2014.1/rpc/rpcfwd.cc
#1 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
//guest/perforce_software/p4/rpc/rpcfwd.cc
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code