diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index cdc662a3..50cb8bf5 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -95,6 +95,15 @@ namespace client version *= 10; version += (ch - '0'); } + else if (ch == '.') + { + // no-op: skip periods + } + else + { + // if version contains other characters, return error + return -1; + } } return version; } @@ -131,7 +140,10 @@ namespace client if (!strcmp (m_Buffer, SAM_HANDSHAKE)) { int minVer = 0, maxVer = 0; - // try to find MIN and MAX, 3.0 if not found + bool verErr = false; + // try to find MIN and MAX, MAX_SAM_VERSION if not found, + // since the highest possible version must be returned + // given the constraints if (separator) { separator++; @@ -143,14 +155,35 @@ namespace client if (!minVerStr.empty ()) minVer = ExtractVersion (minVerStr); } + // if parsing error or impossible version constraints + if (minVer == -1 || maxVer == -1 || (minVer && maxVer && minVer > maxVer)) + verErr = true; // version negotiation - if (maxVer && maxVer <= MAX_SAM_VERSION) - m_Version = maxVer; - else if (minVer && minVer >= MIN_SAM_VERSION && minVer <= MAX_SAM_VERSION) - m_Version = minVer; - else if (!maxVer && !minVer) - m_Version = MIN_SAM_VERSION; - else + else if (maxVer && minVer) // if both constraints provided + { + if (maxVer < MIN_SAM_VERSION || minVer > MAX_SAM_VERSION) + verErr = true; + else + m_Version = std::min(maxVer, MAX_SAM_VERSION); + } + else if (maxVer) // if only max provided + { + if (maxVer < MIN_SAM_VERSION) + verErr = true; + else + m_Version = std::min(maxVer, MAX_SAM_VERSION); + } + else if (minVer) // if only min provided + { + if (minVer > MAX_SAM_VERSION) + verErr = true; + else + m_Version = MAX_SAM_VERSION; + } + else // if neither min nor max is provided + m_Version = MAX_SAM_VERSION; + + if (verErr) { LogPrint (eLogError, "SAM: Handshake version mismatch ", minVer, " ", maxVer); SendMessageReply (SAM_HANDSHAKE_NOVERSION, true); diff --git a/tests/test-sam-version.sh b/tests/test-sam-version.sh new file mode 100644 index 00000000..c1f40a10 --- /dev/null +++ b/tests/test-sam-version.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# This script tests the version negotiation in the SAM handshake. + +# Inputs and expected outputs +IN=() +EXP=() + +IN+=("MIN=3.1 MAX=3.3") +EXP+=("OK VERSION=3.3") + +IN+=("MAX=3.3") +EXP+=("OK VERSION=3.3") + +IN+=("MAX=3.4") +EXP+=("OK VERSION=3.3") + +IN+=("MIN=3.0") +EXP+=("OK VERSION=3.3") + +IN+=("MIN=3.1") +EXP+=("OK VERSION=3.3") + +IN+=("MIN=2.9") +EXP+=("OK VERSION=3.3") + +IN+=("") +EXP+=("OK VERSION=3.3") + +IN+=("MIN=3.3 MAX=3.1") +EXP+=("NOVERSION") + +IN+=("MIN=3.5 MAX=2.7") +EXP+=("NOVERSION") + +IN+=("MIN=2.7 MAX=3.5") +EXP+=("OK VERSION=3.3") + +IN+=("MIN=afddab3vsfdsg1df MAX=dsaaffdb3ggfgfbgf1bssbf") +EXP+=("NOVERSION") + +IN+=("MIN=31 MAX=31") +EXP+=("OK VERSION=3.1") + + +for i in $(seq 0 $((${#IN[@]} - 1))); do + printf "HELLO VERSION ${IN[$i]} - " + + # Observed output + OBS=$(printf "HELLO VERSION ${IN[$i]}\n" | nc -q 0 127.0.0.1 7656) + + if [ "$OBS" = "HELLO REPLY RESULT=${EXP[$i]}" ]; then + printf "OK\n" + else + printf "received $OBS\n" + fi +done + +