From 8e0fe1920eee959ad14225ad2327793371be05d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Bj=C3=B6rklund?= Date: Tue, 18 Feb 2020 14:08:35 +0100 Subject: [PATCH] Use hash for ControlPath instead of full variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated socket path can be too long, if your FQDN is very long... Typical error message from ssh client: unix_listener: path "/tmp/ssh-fqduawon/master-USER@HOST:PORT.qfCZ51OAZgTzVLbg" too long for Unix domain socket Use a hashed version instead, to keep within the socket file path limit. This requires OpenSSH_6.7p1, or later. Change-Id: Ia4bb9ae8aac6c4ee31d5a458f917f3753f40001b Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/255632 Reviewed-by: Mike Frysinger Reviewed-by: David Pursehouse Tested-by: Anders Björklund --- git_command.py | 36 +++++++++++++++++++++++++++++++++++- tests/test_git_command.py | 27 +++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/git_command.py b/git_command.py index a2782151..1cb8f1aa 100644 --- a/git_command.py +++ b/git_command.py @@ -16,6 +16,7 @@ from __future__ import print_function import os +import re import sys import subprocess import tempfile @@ -47,6 +48,35 @@ LAST_CWD = None _ssh_proxy_path = None _ssh_sock_path = None _ssh_clients = [] +_ssh_version = None + + +def _run_ssh_version(): + """run ssh -V to display the version number""" + return subprocess.check_output(['ssh', '-V'], stderr=subprocess.STDOUT).decode() + + +def _parse_ssh_version(ver_str=None): + """parse a ssh version string into a tuple""" + if ver_str is None: + ver_str = _run_ssh_version() + m = re.match(r'^OpenSSH_([0-9.]+)(p[0-9]+)?\s', ver_str) + if m: + return tuple(int(x) for x in m.group(1).split('.')) + else: + return () + + +def ssh_version(): + """return ssh version as a tuple""" + global _ssh_version + if _ssh_version is None: + try: + _ssh_version = _parse_ssh_version() + except subprocess.CalledProcessError: + print('fatal: unable to detect ssh version', file=sys.stderr) + sys.exit(1) + return _ssh_version def ssh_sock(create=True): @@ -57,9 +87,13 @@ def ssh_sock(create=True): tmp_dir = '/tmp' if not os.path.exists(tmp_dir): tmp_dir = tempfile.gettempdir() + if ssh_version() < (6, 7): + tokens = '%r@%h:%p' + else: + tokens = '%C' # hash of %l%h%p%r _ssh_sock_path = os.path.join( tempfile.mkdtemp('', 'ssh-', tmp_dir), - 'master-%r@%h:%p') + 'master-' + tokens) return _ssh_sock_path diff --git a/tests/test_git_command.py b/tests/test_git_command.py index c2d3f1df..2c22b250 100644 --- a/tests/test_git_command.py +++ b/tests/test_git_command.py @@ -30,6 +30,33 @@ import git_command import wrapper +class SSHUnitTest(unittest.TestCase): + """Tests the ssh functions.""" + + def test_ssh_version(self): + """Check ssh_version() handling.""" + ver = git_command._parse_ssh_version('Unknown\n') + self.assertEqual(ver, ()) + ver = git_command._parse_ssh_version('OpenSSH_1.0\n') + self.assertEqual(ver, (1, 0)) + ver = git_command._parse_ssh_version('OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13, OpenSSL 1.0.1f 6 Jan 2014\n') + self.assertEqual(ver, (6, 6, 1)) + ver = git_command._parse_ssh_version('OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017\n') + self.assertEqual(ver, (7, 6)) + + def test_ssh_sock(self): + """Check ssh_sock() function.""" + with mock.patch('tempfile.mkdtemp', return_value='/tmp/foo'): + # old ssh version uses port + with mock.patch('git_command.ssh_version', return_value=(6, 6)): + self.assertTrue(git_command.ssh_sock().endswith('%p')) + git_command._ssh_sock_path = None + # new ssh version uses hash + with mock.patch('git_command.ssh_version', return_value=(6, 7)): + self.assertTrue(git_command.ssh_sock().endswith('%C')) + git_command._ssh_sock_path = None + + class GitCallUnitTest(unittest.TestCase): """Tests the _GitCall class (via git_command.git)."""