summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/TunnelUtils.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/protocol/http/TunnelUtils.h302
1 files changed, 302 insertions, 0 deletions
diff --git a/netwerk/protocol/http/TunnelUtils.h b/netwerk/protocol/http/TunnelUtils.h
new file mode 100644
index 0000000000..834d0888c3
--- /dev/null
+++ b/netwerk/protocol/http/TunnelUtils.h
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_TLSFilterTransaction_h
+#define mozilla_net_TLSFilterTransaction_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAHttpTransaction.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsINamed.h"
+#include "nsISocketTransport.h"
+#include "nsITimer.h"
+#include "NullHttpTransaction.h"
+#include "mozilla/TimeStamp.h"
+#include "prio.h"
+
+// a TLSFilterTransaction wraps another nsAHttpTransaction but
+// applies a encode/decode filter of TLS onto the ReadSegments
+// and WriteSegments data. It is not used for basic https://
+// but it is used for supplemental TLS tunnels - such as those
+// needed by CONNECT tunnels in HTTP/2 or even CONNECT tunnels when
+// the underlying proxy connection is already running TLS
+//
+// HTTP/2 CONNECT tunnels cannot use pushed IO layers because of
+// the multiplexing involved on the base stream. i.e. the base stream
+// once it is decrypted may have parts that are encrypted with a
+// variety of keys, or none at all
+
+/* ************************************************************************
+The input path of http over a spdy CONNECT tunnel once it is established as a
+stream
+
+note the "real http transaction" can be either a http/1 transaction or another
+spdy session inside the tunnel.
+
+ nsHttpConnection::OnInputStreamReady (real socket)
+ nsHttpConnection::OnSocketReadable()
+ SpdySession::WriteSegment()
+ SpdyStream::WriteSegment (tunnel stream)
+ SpdyConnectTransaction::WriteSegment
+ SpdyStream::OnWriteSegment(tunnel stream)
+ SpdySession::OnWriteSegment()
+ SpdySession::NetworkRead()
+ nsHttpConnection::OnWriteSegment (real socket)
+ realSocketIn->Read() return data from network
+
+now pop the stack back up to SpdyConnectTransaction::WriteSegment, the data
+that has been read is stored mInputData
+
+ SpdyConnectTransaction.mTunneledConn::OnInputStreamReady(mTunnelStreamIn)
+ SpdyConnectTransaction.mTunneledConn::OnSocketReadable()
+ TLSFilterTransaction::WriteSegment()
+ nsHttpTransaction::WriteSegment(real http transaction)
+ TLSFilterTransaction::OnWriteSegment() removes tls on way back up stack
+ SpdyConnectTransaction.mTunneledConn::OnWriteSegment()
+ // gets data from mInputData
+ SpdyConnectTransaction.mTunneledConn.mTunnelStreamIn->Read()
+
+The output path works similarly:
+ nsHttpConnection::OnOutputStreamReady (real socket)
+ nsHttpConnection::OnSocketWritable()
+ SpdySession::ReadSegments (locates tunnel)
+ SpdyStream::ReadSegments (tunnel stream)
+ SpdyConnectTransaction::ReadSegments()
+ SpdyConnectTransaction.mTunneledConn::OnOutputStreamReady (tunnel connection)
+ SpdyConnectTransaction.mTunneledConn::OnSocketWritable (tunnel connection)
+ TLSFilterTransaction::ReadSegment()
+ nsHttpTransaction::ReadSegment (real http transaction generates plaintext on
+ way down)
+ TLSFilterTransaction::OnReadSegment (BUF and LEN gets encrypted here on way
+ down)
+ SpdyConnectTransaction.mTunneledConn::OnReadSegment (BUF and LEN)
+ (tunnel connection)
+ SpdyConnectTransaction.mTunneledConn.mTunnelStreamOut->Write(BUF, LEN) ..
+ get stored in mOutputData
+
+Now pop the stack back up to SpdyConnectTransaction::ReadSegment(), where it has
+the encrypted text available in mOutputData
+
+ SpdyStream->OnReadSegment(BUF,LEN) from mOutputData. Tunnel stream
+ SpdySession->OnReadSegment() // encrypted data gets put in a data frame
+ nsHttpConnection->OnReadSegment()
+ realSocketOut->write() writes data to network
+
+**************************************************************************/
+
+struct PRSocketOptionData;
+
+namespace mozilla {
+namespace net {
+
+class nsHttpRequestHead;
+class NullHttpTransaction;
+class TLSFilterTransaction;
+
+class NudgeTunnelCallback : public nsISupports {
+ public:
+ virtual nsresult OnTunnelNudged(TLSFilterTransaction*) = 0;
+};
+
+#define NS_DECL_NUDGETUNNELCALLBACK \
+ nsresult OnTunnelNudged(TLSFilterTransaction*) override;
+
+class TLSFilterTransaction final : public nsAHttpTransaction,
+ public nsAHttpSegmentReader,
+ public nsAHttpSegmentWriter,
+ public nsITimerCallback,
+ public nsINamed {
+ ~TLSFilterTransaction();
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSAHTTPTRANSACTION
+ NS_DECL_NSAHTTPSEGMENTREADER
+ NS_DECL_NSAHTTPSEGMENTWRITER
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ TLSFilterTransaction(nsAHttpTransaction* aWrappedTransaction,
+ const char* tlsHost, int32_t tlsPort,
+ nsAHttpSegmentReader* reader,
+ nsAHttpSegmentWriter* writer);
+
+ const nsAHttpTransaction* Transaction() const { return mTransaction.get(); }
+ [[nodiscard]] nsresult CommitToSegmentSize(uint32_t size,
+ bool forceCommitment) override;
+ [[nodiscard]] nsresult GetTransactionSecurityInfo(nsISupports**) override;
+ [[nodiscard]] nsresult NudgeTunnel(NudgeTunnelCallback* callback);
+ [[nodiscard]] nsresult SetProxiedTransaction(
+ nsAHttpTransaction* aTrans,
+ nsAHttpTransaction* aSpdyConnectTransaction = nullptr);
+ void newIODriver(nsIAsyncInputStream* aSocketIn,
+ nsIAsyncOutputStream* aSocketOut,
+ nsIAsyncInputStream** outSocketIn,
+ nsIAsyncOutputStream** outSocketOut);
+
+ // nsAHttpTransaction overloads
+ bool IsNullTransaction() override;
+ NullHttpTransaction* QueryNullTransaction() override;
+ nsHttpTransaction* QueryHttpTransaction() override;
+ SpdyConnectTransaction* QuerySpdyConnectTransaction() override;
+ [[nodiscard]] nsresult WriteSegmentsAgain(nsAHttpSegmentWriter* writer,
+ uint32_t count,
+ uint32_t* countWritten,
+ bool* again) override;
+
+ bool HasDataToRecv();
+
+ private:
+ [[nodiscard]] nsresult StartTimerCallback();
+ void Cleanup();
+ int32_t FilterOutput(const char* aBuf, int32_t aAmount);
+ int32_t FilterInput(char* aBuf, int32_t aAmount);
+
+ static PRStatus GetPeerName(PRFileDesc* fd, PRNetAddr* addr);
+ static PRStatus GetSocketOption(PRFileDesc* fd, PRSocketOptionData* data);
+ static PRStatus SetSocketOption(PRFileDesc* fd,
+ const PRSocketOptionData* data);
+ static int32_t FilterWrite(PRFileDesc* fd, const void* buf, int32_t amount);
+ static int32_t FilterRead(PRFileDesc* fd, void* buf, int32_t amount);
+ static int32_t FilterSend(PRFileDesc* fd, const void* buf, int32_t amount,
+ int flags, PRIntervalTime timeout);
+ static int32_t FilterRecv(PRFileDesc* fd, void* buf, int32_t amount,
+ int flags, PRIntervalTime timeout);
+ static PRStatus FilterClose(PRFileDesc* fd);
+
+ private:
+ RefPtr<nsAHttpTransaction> mTransaction;
+ nsWeakPtr mWeakTrans; // SpdyConnectTransaction *
+ nsCOMPtr<nsISupports> mSecInfo;
+ nsCOMPtr<nsITimer> mTimer;
+ RefPtr<NudgeTunnelCallback> mNudgeCallback;
+
+ // buffered network output, after encryption
+ UniquePtr<char[]> mEncryptedText;
+ uint32_t mEncryptedTextUsed;
+ uint32_t mEncryptedTextSize;
+
+ PRFileDesc* mFD;
+ nsAHttpSegmentReader* mSegmentReader;
+ nsAHttpSegmentWriter* mSegmentWriter;
+
+ nsresult mFilterReadCode;
+ int32_t mFilterReadAmount;
+ // Set only when we are calling PR_Write from inside OnReadSegment. Prevents
+ // calling back to OnReadSegment from inside FilterOutput.
+ bool mInOnReadSegment;
+ bool mForce;
+ nsresult mReadSegmentReturnValue;
+ // Before Close() is called this is NS_ERROR_UNEXPECTED, in Close() we either
+ // take the reason, if it is a failure, or we change to
+ // NS_ERROR_BASE_STREAM_CLOSE. This is returned when Write/ReadSegments is
+ // called after Close, when we don't have mTransaction any more.
+ nsresult mCloseReason;
+ uint32_t mNudgeCounter;
+};
+
+class SocketTransportShim;
+class InputStreamShim;
+class OutputStreamShim;
+class nsHttpConnection;
+
+class SpdyConnectTransaction final : public NullHttpTransaction {
+ public:
+ SpdyConnectTransaction(nsHttpConnectionInfo* ci,
+ nsIInterfaceRequestor* callbacks, uint32_t caps,
+ nsHttpTransaction* trans, nsAHttpConnection* session,
+ bool isWebsocket);
+ ~SpdyConnectTransaction();
+
+ SpdyConnectTransaction* QuerySpdyConnectTransaction() override {
+ return this;
+ }
+
+ // A transaction is forced into plaintext when it is intended to be used as a
+ // CONNECT tunnel but the setup fails. The plaintext only carries the CONNECT
+ // error.
+ void ForcePlainText();
+ // True if we successfully map stream to a nsHttpConnection. Currently we skip
+ // 1xx response only.
+ bool MapStreamToHttpConnection(nsISocketTransport* aTransport,
+ nsHttpConnectionInfo* aConnInfo,
+ const nsACString& aFlat407Headers,
+ int32_t aHttpResponseCode);
+
+ [[nodiscard]] nsresult ReadSegments(nsAHttpSegmentReader* reader,
+ uint32_t count,
+ uint32_t* countRead) final;
+ [[nodiscard]] nsresult WriteSegments(nsAHttpSegmentWriter* writer,
+ uint32_t count,
+ uint32_t* countWritten) final;
+ nsHttpRequestHead* RequestHead() final;
+ void Close(nsresult reason) final;
+
+ // ConnectedReadyForInput() tests whether the spdy connect transaction is
+ // attached to an nsHttpConnection that can properly deal with flow control,
+ // etc..
+ bool ConnectedReadyForInput();
+
+ bool IsWebsocket() { return mIsWebsocket; }
+ void SetConnRefTaken();
+
+ private:
+ friend class SocketTransportShim;
+ friend class InputStreamShim;
+ friend class OutputStreamShim;
+
+ [[nodiscard]] nsresult Flush(uint32_t count, uint32_t* countRead);
+ void CreateShimError(nsresult code);
+
+ nsCString mConnectString;
+ uint32_t mConnectStringOffset;
+
+ nsAHttpConnection* mSession;
+ nsAHttpSegmentReader* mSegmentReader;
+
+ UniquePtr<char[]> mInputData;
+ uint32_t mInputDataSize;
+ uint32_t mInputDataUsed;
+ uint32_t mInputDataOffset;
+
+ UniquePtr<char[]> mOutputData;
+ uint32_t mOutputDataSize;
+ uint32_t mOutputDataUsed;
+ uint32_t mOutputDataOffset;
+
+ bool mForcePlainText;
+ TimeStamp mTimestampSyn;
+
+ // mTunneledConn, mTunnelTransport, mTunnelStreamIn, mTunnelStreamOut
+ // are the connectors to the "real" http connection. They are created
+ // together when the tunnel setup is complete and a static reference is held
+ // for the lifetime of the tunnel.
+ RefPtr<nsHttpConnection> mTunneledConn;
+ RefPtr<SocketTransportShim> mTunnelTransport;
+ RefPtr<InputStreamShim> mTunnelStreamIn;
+ RefPtr<OutputStreamShim> mTunnelStreamOut;
+ RefPtr<nsHttpTransaction> mDrivingTransaction;
+
+ // This is all for websocket support
+ bool mIsWebsocket;
+ bool mConnRefTaken;
+ nsCOMPtr<nsIAsyncOutputStream> mInputShimPipe;
+ nsCOMPtr<nsIAsyncInputStream> mOutputShimPipe;
+ nsresult WriteDataToBuffer(nsAHttpSegmentWriter* writer, uint32_t count,
+ uint32_t* countWritten);
+ [[nodiscard]] nsresult WebsocketWriteSegments(nsAHttpSegmentWriter* writer,
+ uint32_t count,
+ uint32_t* countWritten);
+
+ bool mCreateShimErrorCalled;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TLSFilterTransaction_h