diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj
index e646e1b68b..de1f84bf82 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj
+++ b/Builds/VisualStudio2012/RippleD.vcxproj
@@ -301,6 +301,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -998,12 +1004,6 @@
true
true
-
- true
- true
- true
- true
-
true
@@ -1423,6 +1423,7 @@
+
@@ -1563,7 +1564,6 @@
-
diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters
index 71ad15054e..91219499c1 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters
@@ -160,9 +160,6 @@
{c69b07a2-44e5-4b06-99a9-81f5d137ea15}
-
- {f0308442-e7af-44d4-a76a-c91fbf685e10}
-
@@ -885,8 +882,8 @@
[1] Ripple\ripple_app\main
-
- [1] Ripple\ripple_net\protocol
+
+ [1] Ripple\ripple_app\network
@@ -1676,8 +1673,8 @@
[2] Build
-
- [1] Ripple\ripple_net\protocol
+
+ [1] Ripple\ripple_app\network
diff --git a/Notes/VFALCO_TODO.txt b/Notes/VFALCO_TODO.txt
index 7acb0598a8..331207b8b3 100644
--- a/Notes/VFALCO_TODO.txt
+++ b/Notes/VFALCO_TODO.txt
@@ -7,6 +7,7 @@ REMINDER: KEEP CHANGE LOG UP TO DATE
Vinnie's List: Changes day to day, descending priority
(Items marked '*' can be handled by others.)
+- Emergency implement PROXY protcol
- Get rid of boost::filesystem
- Deeply create directories specified in config settings
- Finish unit tests and code for Validators
diff --git a/modules/ripple_app/network/ripple_ProxyHandshake.cpp b/modules/ripple_app/network/ripple_ProxyHandshake.cpp
new file mode 100644
index 0000000000..c3dba646ee
--- /dev/null
+++ b/modules/ripple_app/network/ripple_ProxyHandshake.cpp
@@ -0,0 +1,371 @@
+//------------------------------------------------------------------------------
+/*
+ Copyright (c) 2011-2013, OpenCoin, Inc.
+*/
+//==============================================================================
+
+ProxyHandshake::ProxyHandshake (bool expectHandshake)
+ : m_status (expectHandshake ? statusHandshake : statusNone)
+ , m_gotCR (false)
+{
+ m_buffer.preallocateBytes (maxVersion1Bytes);
+}
+
+ProxyHandshake::~ProxyHandshake ()
+{
+}
+
+std::size_t ProxyHandshake::feed (void const* inputBuffer, size_t inputBytes)
+{
+ std::size_t bytesConsumed = 0;
+
+ char const* p = static_cast (inputBuffer);
+
+ if (m_status == statusHandshake)
+ {
+ if (! m_gotCR)
+ {
+ while (inputBytes > 0 && m_buffer.length () < maxVersion1Bytes - 1)
+ {
+ beast_wchar c = *p++;
+ ++bytesConsumed;
+ --inputBytes;
+ m_buffer += c;
+
+ if (c == '\r')
+ {
+ m_gotCR = true;
+ break;
+ }
+ else if (c == '\n')
+ {
+ m_status = statusFailed;
+ }
+ }
+
+ if (m_buffer.length () > maxVersion1Bytes - 1)
+ {
+ m_status = statusFailed;
+ }
+ }
+ }
+
+ if (m_status == statusHandshake)
+ {
+ if (inputBytes > 0 && m_gotCR)
+ {
+ bassert (m_buffer.length () < maxVersion1Bytes);
+
+ char const lf ('\n');
+
+ if (*p == lf)
+ {
+ ++bytesConsumed;
+ --inputBytes;
+ m_buffer += lf;
+
+ parseLine ();
+ }
+ else
+ {
+ m_status = statusFailed;
+ }
+ }
+ }
+
+ return bytesConsumed;
+}
+
+void ProxyHandshake::parseLine ()
+{
+ Version1 p;
+
+ bool success = p.parse (m_buffer.getCharPointer (), m_buffer.length ());
+
+ if (success)
+ {
+ m_endpoints = p.endpoints;
+ m_status = statusOk;
+ }
+ else
+ {
+ m_status = statusFailed;
+ }
+}
+
+int ProxyHandshake::indexOfFirstNonNumber (String const& input)
+{
+ bassert (input.length () > 0);
+
+ int i = 0;
+ for (; i < input.length (); ++i)
+ {
+ if (! CharacterFunctions::isDigit (input [i]))
+ break;
+ }
+
+ return i;
+}
+
+bool ProxyHandshake::chop (String const& what, String& input)
+{
+ if (input.startsWith (what))
+ {
+ input = input.substring (what.length ());
+
+ return true;
+ }
+
+ return false;
+}
+
+bool ProxyHandshake::chopUInt (int* value, int limit, String& input)
+{
+ if (input.length () <= 0)
+ return false;
+
+ String const s = input.substring (0, indexOfFirstNonNumber (input));
+
+ if (s.length () <= 0)
+ return false;
+
+ int const n = s.getIntValue ();
+
+ // Leading zeroes disallowed as per spec, to prevent confusion with octal
+ if (String (n) != s)
+ return false;
+
+ if (n < 0 || n > limit)
+ return false;
+
+ input = input.substring (s.length ());
+
+ *value = n;
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+
+/*
+
+steps:
+
+Proxy protocol lets us filter attackers by learning the source ip and port
+
+1. Determine if we should use the proxy on a connection
+ - Port just for proxy protocol connections
+ - Filter on source IPs
+
+2. Read a line from the connection to get the proxy information
+
+3. Parse the line (human or binary?)
+
+4. Code Interface to retrieve proxy information (ip/port) on connection
+
+*/
+
+ProxyHandshake::Version1::Version1 ()
+{
+}
+
+bool ProxyHandshake::IPv4::Addr::chop (String& input)
+{
+ if (!ProxyHandshake::chopUInt (&a, 255, input))
+ return false;
+
+ if (!ProxyHandshake::chop (".", input))
+ return false;
+
+ if (!ProxyHandshake::chopUInt (&b, 255, input))
+ return false;
+
+ if (!ProxyHandshake::chop (".", input))
+ return false;
+
+ if (!ProxyHandshake::chopUInt (&c, 255, input))
+ return false;
+
+ if (!ProxyHandshake::chop (".", input))
+ return false;
+
+ if (!ProxyHandshake::chopUInt (&d, 255, input))
+ return false;
+
+ return true;
+}
+
+bool ProxyHandshake::Version1::parse (void const* headerData, size_t headerBytes)
+{
+ String input (static_cast (headerData), headerBytes);
+
+ if (input.length () < 2)
+ return false;
+
+ if (! input.endsWith ("\r\n"))
+ return false;
+
+ input = input.dropLastCharacters (2);
+
+ if (! ProxyHandshake::chop ("PROXY ", input))
+ return false;
+
+ if (ProxyHandshake::chop ("UNKNOWN", input))
+ {
+ endpoints.proto = protoUnknown;
+
+ input = "";
+ }
+ else
+ {
+ if (ProxyHandshake::chop ("TCP4 ", input))
+ {
+ endpoints.proto = protoTcp4;
+
+ if (! endpoints.ipv4.sourceAddr.chop (input))
+ return false;
+
+ if (! ProxyHandshake::chop (" ", input))
+ return false;
+
+ if (! endpoints.ipv4.destAddr.chop (input))
+ return false;
+
+ if (! ProxyHandshake::chop (" ", input))
+ return false;
+
+ if (! ProxyHandshake::chopUInt (&endpoints.ipv4.sourcePort, 65535, input))
+ return false;
+
+ if (! ProxyHandshake::chop (" ", input))
+ return false;
+
+ if (! ProxyHandshake::chopUInt (&endpoints.ipv4.destPort, 65535, input))
+ return false;
+ }
+ else if (ProxyHandshake::chop ("TCP6 ", input))
+ {
+ endpoints.proto = protoTcp6;
+
+ //bassertfalse;
+
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Can't have anything extra between the last port number and the CRLF
+ if (input.length () > 0)
+ return false;
+
+ return true;
+}
+
+//------------------------------------------------------------------------------
+
+class ProxyHandshakeTests : public UnitTest
+{
+public:
+ ProxyHandshakeTests () : UnitTest ("ProxyHandshake", "ripple", runManual)
+ {
+ }
+
+ static std::string goodIpv4 ()
+ {
+ return "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"; // 56 chars
+ }
+
+ static std::string goodIpv6 ()
+ {
+ return "PROXY TCP6 fffffffffffffffffffffffffffffffffffffff.fffffffffffffffffffffffffffffffffffffff 65535 65535\r\n";
+ //1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123 4 (104 chars)
+ }
+
+ static std::string goodUnknown ()
+ {
+ return "PROXY UNKNOWN\r\n";
+ }
+
+ static std::string goodUnknownBig ()
+ {
+ return "PROXY UNKNOWN fffffffffffffffffffffffffffffffffffffff.fffffffffffffffffffffffffffffffffffffff 65535 65535\r\n";
+ //1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 7 (107 chars)
+ }
+
+ void testHandshake (std::string const& s, bool shouldSucceed)
+ {
+ if (s.size () > 1)
+ {
+ ProxyHandshake h (true);
+
+ expect (h.getStatus () == ProxyHandshake::statusHandshake);
+
+ for (int i = 0; i < s.size () && h.getStatus () == ProxyHandshake::statusHandshake ; ++i)
+ {
+ std::size_t const bytesConsumed = h.feed (& s[i], 1);
+
+ if (i != s.size () - 1)
+ expect (h.getStatus () == ProxyHandshake::statusHandshake);
+
+ expect (bytesConsumed == 1);
+ }
+
+ if (shouldSucceed)
+ {
+ expect (h.getStatus () == ProxyHandshake::statusOk);
+ }
+ else
+ {
+ expect (h.getStatus () == ProxyHandshake::statusFailed);
+ }
+ }
+ else
+ {
+ bassertfalse;
+ }
+ }
+
+ void testVersion1String (std::string const& s, bool shouldSucceed)
+ {
+ ProxyHandshake::Version1 p;
+
+ if (shouldSucceed)
+ {
+ expect (p.parse (s.c_str (), s.size ()));
+ }
+ else
+ {
+ unexpected (p.parse (s.c_str (), s.size ()));
+ }
+
+ for (int i = 1; i < s.size () - 1; ++i)
+ {
+ String const partial = String (s).dropLastCharacters (i);
+ std::string ss (partial.toStdString ());
+
+ expect (! p.parse (ss.c_str (), ss.size ()));
+ }
+
+ testHandshake (s, shouldSucceed);
+ }
+
+ void testVersion1 ()
+ {
+ beginTestCase ("version1");
+
+ testVersion1String (goodIpv4 (), true);
+ testVersion1String (goodIpv6 (), false);
+ testVersion1String (goodUnknown (), true);
+ testVersion1String (goodUnknownBig (), true);
+ }
+
+ void runTest ()
+ {
+ testVersion1 ();
+ }
+};
+
+static ProxyHandshakeTests proxyHandshakeTests;
diff --git a/modules/ripple_app/network/ripple_ProxyHandshake.h b/modules/ripple_app/network/ripple_ProxyHandshake.h
new file mode 100644
index 0000000000..d9bd6efa3c
--- /dev/null
+++ b/modules/ripple_app/network/ripple_ProxyHandshake.h
@@ -0,0 +1,151 @@
+//------------------------------------------------------------------------------
+/*
+ Copyright (c) 2011-2013, OpenCoin, Inc.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_PROXYYHANDSHAKE_H_INCLUDED
+#define RIPPLE_PROXYYHANDSHAKE_H_INCLUDED
+
+/** PROXY protocol handshake state machine.
+
+ The PROXY Protocol:
+ http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
+*/
+class ProxyHandshake
+{
+public:
+ /** Status of the handshake state machine. */
+ enum Status
+ {
+ statusNone, // No handshake expected
+ statusHandshake, // Handshake in progress
+ statusFailed, // Handshake failed
+ statusOk, // Handshake succeeded
+ };
+
+ enum Proto
+ {
+ protoTcp4,
+ protoTcp6,
+ protoUnknown
+ };
+
+ /** PROXY information for IPv4 families. */
+ struct IPv4
+ {
+ struct Addr
+ {
+ int a;
+ int b;
+ int c;
+ int d;
+
+ bool chop (String& input);
+ };
+
+ Addr sourceAddr;
+ Addr destAddr;
+ int sourcePort;
+ int destPort;
+ };
+
+ /** PROXY information for IPv6 families. */
+ struct IPv6
+ {
+ struct Addr
+ {
+ int a;
+ int b;
+ int c;
+ int d;
+ };
+
+ Addr sourceAddr;
+ Addr destAddr;
+ int sourcePort;
+ int destPort;
+ };
+
+ /** Fully decoded PROXY information. */
+ struct Endpoints
+ {
+ Endpoints ()
+ : proto (protoUnknown)
+ {
+ }
+
+ Proto proto;
+ IPv4 ipv4; // valid if proto == protoTcp4
+ IPv6 ipv6; // valid if proto == protoTcp6;
+ };
+
+ //--------------------------------------------------------------------------
+
+ /** Parser for PROXY version 1. */
+ struct Version1
+ {
+ enum
+ {
+ // Maximum input buffer size needed, including a null
+ // terminator, as per the PROXY protocol specification.
+ maxBufferBytes = 108
+ };
+
+ Endpoints endpoints;
+
+ Version1 ();
+
+ /** Parse the header.
+ @param rawHeader a pointer to the header data
+ @return `true` If it was parsed successfully.
+ */
+ bool parse (void const* headerData, size_t headerBytes);
+ };
+
+ //--------------------------------------------------------------------------
+
+ /** Create the handshake state.
+ If a handshake is expected, then it is required.
+ @param wantHandshake `false` to skip handshaking.
+ */
+ explicit ProxyHandshake (bool expectHandshake = false);
+
+ ~ProxyHandshake ();
+
+ inline Status getStatus () const noexcept
+ {
+ return m_status;
+ }
+
+ inline Endpoints const& getEndpoints () const noexcept
+ {
+ return m_endpoints;
+ };
+
+ /** Feed the handshaking state engine.
+ @return The number of bytes consumed in the input buffer.
+ */
+ std::size_t feed (void const* inputBuffer, std::size_t inputBytes);
+
+ // Utility functions used by parsers
+ static int indexOfFirstNonNumber (String const& input);
+ static bool chop (String const& what, String& input);
+ static bool chopUInt (int* value, int limit, String& input);
+
+private:
+ void parseLine ();
+
+private:
+ enum
+ {
+ maxVersion1Bytes = 107 // including crlf, not including null term
+ };
+
+ Status m_status;
+ String m_buffer;
+ bool m_gotCR;
+ Endpoints m_endpoints;
+};
+
+#endif
diff --git a/modules/ripple_app/ripple_app.cpp b/modules/ripple_app/ripple_app.cpp
index 9e24a0e63b..469bccd3bd 100644
--- a/modules/ripple_app/ripple_app.cpp
+++ b/modules/ripple_app/ripple_app.cpp
@@ -239,6 +239,8 @@ static const uint64 tenTo17m1 = tenTo17 - 1;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
+#include "network/ripple_ProxyHandshake.h" // private?
+
//------------------------------------------------------------------------------
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 1
@@ -326,6 +328,7 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
#include "tx/Transactor.cpp"
#include "network/WSConnection.cpp"
#include "network/WSDoor.cpp"
+#include "network/ripple_ProxyHandshake.cpp"
#endif
diff --git a/modules/ripple_net/ripple_net.cpp b/modules/ripple_net/ripple_net.cpp
index f8694664b6..a2ed5d8037 100644
--- a/modules/ripple_net/ripple_net.cpp
+++ b/modules/ripple_net/ripple_net.cpp
@@ -25,6 +25,4 @@ namespace ripple
#include "basics/ripple_RPCServer.cpp"
#include "basics/ripple_SNTPClient.cpp"
-#include "protocol/ripple_ProxyProtocol.cpp"
-
}
diff --git a/modules/ripple_net/ripple_net.h b/modules/ripple_net/ripple_net.h
index d5bfdb6723..2452be7f9d 100644
--- a/modules/ripple_net/ripple_net.h
+++ b/modules/ripple_net/ripple_net.h
@@ -32,8 +32,6 @@ namespace ripple
#include "basics/ripple_RPCServer.h"
#include "basics/ripple_SNTPClient.h"
-#include "protocol/ripple_ProxyProtocol.h"
-
}
#endif