## Transport Protocols

Transport protocols are used to transform the data passing over a transport. RCF uses transport protocols to provide authentication, encryption and compression for remote calls.

RCF currently supports NTLM, Kerberos, Negotiate and SSL transport protocols. NTLM, Kerberos and Negotiate are only supported on Windows platforms, while SSL is supported on all platforms.

In addition, RCF also supports zlib-based compression of remote calls.

Transport protocols are assigned to a client connection by calling `ClientStub::setTransportProtocol()`:

    RcfClient<I_HelloWorld> client(( RCF::TcpEndpoint(port) ));
    client.getClientStub().setTransportProtocol(RCF::Tp_Ntlm);

From within the server session of a client connection, you can call `RcfSession::getTransportProtocol()` to determine the transport prococol the client is using:

    RCF::RcfSession & session = RCF::getCurrentRcfSession();
    RCF::TransportProtocol tp = session.getTransportProtocol();

### NTLM

To configure NTLM on a client connection:

    RcfClient<I_HelloWorld> client(( RCF::TcpEndpoint(port) ));
    client.getClientStub().setTransportProtocol(RCF::Tp_Ntlm);
    client.Print("Hello World");

On the server-side, you can determine the Windows username of the client, and impersonate them:

    std::string clientUsername = session.getClientUsername();

    RCF::SspiImpersonator impersonator(session);
    // We are now impersonating the client, until impersonator goes out of scope.
    // ...

### Kerberos

To configure Kerberos on a client connection:

    client.getClientStub().setTransportProtocol(RCF::Tp_Kerberos);
    client.getClientStub().setKerberosSpn("Domain\\ServerAccount");
    client.Print("Hello World");

Notice that the client needs to call `ClientStub::setKerberosSpn()` to specify the username it expects the server to be running under. This is known as the SPN (Service Principal Name) of the server, and is used by Kerberos to implement mutual authentication. If the server is not running under this account, the connection will fail.

On the server-side, you can determine the Windows username of the client, and impersonate them:

    std::string clientUsername = session.getClientUsername();

    RCF::SspiImpersonator impersonator(session);
    // We are now impersonating the client, until impersonator goes out of scope.
    // ...

### Negotiate

Negotiate is a negotiation protocol between the NTLM and Kerberos protocols. It will resolve to Kerberos if possible, and otherwise fall back to NTLM.

To configure Negotiate on a client connection:

    client.getClientStub().setTransportProtocol(RCF::Tp_Negotiate);
    client.getClientStub().setKerberosSpn("Domain\\ServerAccount");
    client.Print("Hello World");

As with the Kerberos transport protocol, you need to supply a SPN for the server.

On the server-side, you can determine the Windows username of the client, and impersonate them:

    std::string clientUsername = session.getClientUsername();

    RCF::SspiImpersonator impersonator(session);
    // We are now impersonating the client, until impersonator goes out of scope.
    // ...

### SSL

RCF offers two SSL transport protocol implementations. One is based on the cross-platform OpenSSL library, and the other is based on the Windows-only Schannel package.

OpenSSL support is only available if `RCF_USE_OPENSSL` has been defined. Schannel support is only available in Windows builds.

If you define `RCF_USE_OPENSSL` in a Windows build, RCF will use OpenSSL rather than Schannel. Should you want to use Schannel, despite defining `RCF_USE_OPENSSL`, you can set the SSL implementation for individual servers and clients using the `RcfServer::setSslImplementation()` and `ClientStub::setSslImplementation()` functions, or for the whole RCF runtime using the `RCF::setSslImplementation()` function.

The SSL protocol uses certificates to authenticate servers to clients (and optionally clients to servers). Certificate and certificate validation functionality is handled differently by the two SSL transport protocol implemenations, as described below.

#### Schannel

To configure a server to accept SSL connections, you need to provide a server certificate. RCF provides the `RCF::PfxCertificate` class, to load certificates from .pfx and .p12 files:

    RCF::CertificatePtr serverCertPtr( new RCF::PfxCertificate(
        "C:\\serverCert.p12",
        "password",
        "CertificateName") );

    server.setCertificate(serverCertPtr);

RCF also provides the `RCF::StoreCertificate` class, to load certificates from Windows certificate stores:

    RCF::CertificatePtr serverCertPtr( new RCF::StoreCertificate(
        RCF::Cl_LocalMachine,
        RCF::Cs_My,
        "CertificateName") );

    server.setCertificate(serverCertPtr);

On the client, you need to provide a means of validating the certificate presented by the server. There are several ways of doing this. You can let the Schannel package apply its own internal validation logic, which will defer to the locally installed certificate authorities:

    client.getClientStub().setTransportProtocol(RCF::Tp_Ssl);
    client.getClientStub().setEnableSchannelCertificateValidation("CertificateName");
    client.Print("Hello World");

You can also provide a particular certificate authority yourself, which will be used to validate the server certificate:

    RCF::CertificatePtr caCertPtr( new RCF::PfxCertificate(
        "C:\\clientCaCertificate.p12",
        "password",
        "CaCertificatename"));

    client.getClientStub().setCaCertificate(caCertPtr);

You can also write your own custom certificate validation logic:

    bool schannelValidateCert(RCF::Certificate * pCert)
    {
        RCF::Win32Certificate * pWin32Cert = static_cast<RCF::Win32Certificate *>(pCert);
        if (pWin32Cert)
        {
            RCF::tstring certName = pWin32Cert->getCertificateName();
            RCF::tstring issuerName = pWin32Cert->getIssuerName();

            PCCERT_CONTEXT pContext = pWin32Cert->getWin32Context();

            // Custom code to inspect and validate certificate.
            // ...
        }

        // Return true if the certificate is considered valid. Otherwise, return false,
        // or throw an exception.
        return true;
    }

    client.getClientStub().setCertificateValidationCallback(&schannelValidateCert);

RCF clients can also be configured to present a certificate to the server:

    RCF::CertificatePtr clientCertPtr( new RCF::PfxCertificate(
        "C:\\clientCert.p12",
        "password",
        "CertificateName") );

    client.getClientStub().setCertificate(clientCertPtr);

Server-side certificate validation is done in the same way as client-side certificate validation, but using the `RcfServer::setSchannelDefaultCertificateValidation()`, `RcfServer::setSchannelCertificateValidationCb()`, and `RcfServer::setSslCaCertificate()` functions.

#### OpenSSL

When using the OpenSSL-based SSL transport protocol, certificates need to be loaded from .pem files, using the `RCF::PemCertificate` class. Here is an example of providing a server certificate:

    RCF::CertificatePtr serverCertPtr( new RCF::PemCertificate(
        "C:\\serverCert.pem",
        "password") );

    server.setCertificate(serverCertPtr);

The client can validate the server certificate in two ways. It can provide a certificate authority certificate:

    RCF::CertificatePtr caCertPtr( new RCF::PemCertificate(
        "C:\\clientCaCertificate.pem",
        "password"));

    client.getClientStub().setCaCertificate(caCertPtr);

, or it can provide custom validation logic in a callback function:

    bool opensslValidateCert(RCF::Certificate * pCert)
    {
        RCF::X509Certificate * pX509Cert = static_cast<RCF::X509Certificate *>(pCert);

        if (pX509Cert)
        {
            std::string certName = pX509Cert->getCertificateName();
            std::string issuerName = pX509Cert->getIssuerName();

            X509 * pX509 = pX509Cert->getX509();

            // Custom code to inspect and validate certificate.
            // ...
        }

        // Return true if valid, false if not.
        return true;
    }

    client.getClientStub().setCertificateValidationCallback(&opensslValidateCert);

The client can also provide a certificate of its own, to present to the server:

    RCF::CertificatePtr clientCertPtr( new RCF::PemCertificate(
        "C:\\clientCert.pem",
        "password") );

    client.getClientStub().setCertificate(clientCertPtr);

Server-side certificate validation is done in the same way as client-side certificate validation, but using the `RcfServer::setOpenSslCertificateValidationCb()`, and `RcfServer::setSslCaCertificate()` functions.

### Compression

RCF supports compression of remote calls. To build RCF with support for compression, define `RCF_USE_ZLIB` (see Building).

Compression is configured independently of other transport protocols, using `ClientStub::setEnableCompression()`:

    RcfClient<I_HelloWorld> client(( RCF::TcpEndpoint(port) ));
    client.getClientStub().setEnableCompression(true);
    client.Print("Hello World");

RCF applies the compression stage before the transport protocol stage. For example, if you configure compression and NTLM on the same connection:

    RcfClient<I_HelloWorld> client(( RCF::TcpEndpoint(port) ));
    client.getClientStub().setTransportProtocol(RCF::Tp_Ntlm);
    client.getClientStub().setEnableCompression(true);
    client.Print("Hello World");

, the remote call data will be compressed first, before being encrypted and sent across the wire.