View Javadoc
1   /*
2    * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.transport.sshd.agent;
11  
12  import java.io.IOException;
13  import java.text.MessageFormat;
14  import java.util.Objects;
15  
16  import org.apache.sshd.agent.SshAgentConstants;
17  import org.apache.sshd.common.SshException;
18  import org.apache.sshd.common.util.buffer.BufferUtils;
19  import org.eclipse.jgit.internal.transport.sshd.SshdText;
20  
21  /**
22   * Provides some utility methods for implementing {@link Connector}s.
23   *
24   * @since 6.0
25   */
26  public abstract class AbstractConnector implements Connector {
27  
28  	// A somewhat sane lower bound for the maximum reply length
29  	private static final int MIN_REPLY_LENGTH = 8 * 1024;
30  
31  	/**
32  	 * Default maximum reply length. 256kB is the OpenSSH limit.
33  	 */
34  	protected static final int DEFAULT_MAX_REPLY_LENGTH = 256 * 1024;
35  
36  	private final int maxReplyLength;
37  
38  	/**
39  	 * Creates a new instance using the {@link #DEFAULT_MAX_REPLY_LENGTH}.
40  	 */
41  	protected AbstractConnector() {
42  		this(DEFAULT_MAX_REPLY_LENGTH);
43  	}
44  
45  	/**
46  	 * Creates a new instance.
47  	 *
48  	 * @param maxReplyLength
49  	 *            maximum number of payload bytes we're ready to accept
50  	 */
51  	protected AbstractConnector(int maxReplyLength) {
52  		if (maxReplyLength < MIN_REPLY_LENGTH) {
53  			throw new IllegalArgumentException(
54  					"Maximum payload length too small"); //$NON-NLS-1$
55  		}
56  		this.maxReplyLength = maxReplyLength;
57  	}
58  
59  	/**
60  	 * Retrieves the maximum message length this {@link AbstractConnector} is
61  	 * configured for.
62  	 *
63  	 * @return the maximum message length
64  	 */
65  	protected int getMaximumMessageLength() {
66  		return this.maxReplyLength;
67  	}
68  
69  	/**
70  	 * Prepares a message for sending by inserting the command and message
71  	 * length.
72  	 *
73  	 * @param command
74  	 *            SSH agent command the request is for
75  	 * @param message
76  	 *            about to be sent, including the 5 spare bytes at the front
77  	 * @throws IllegalArgumentException
78  	 *             if {@code message} has less than 5 bytes
79  	 */
80  	protected void prepareMessage(byte command, byte[] message)
81  			throws IllegalArgumentException {
82  		Objects.requireNonNull(message);
83  		if (message.length < 5) {
84  			// No translation; internal error
85  			throw new IllegalArgumentException("Message buffer for " //$NON-NLS-1$
86  					+ SshAgentConstants.getCommandMessageName(command)
87  					+ " must have at least 5 bytes; have only " //$NON-NLS-1$
88  					+ message.length);
89  		}
90  		BufferUtils.putUInt(message.length - 4, message);
91  		message[4] = command;
92  	}
93  
94  	/**
95  	 * Checks the received length of a reply.
96  	 *
97  	 * @param command
98  	 *            SSH agent command the reply is for
99  	 * @param length
100 	 *            length as received: number of payload bytes
101 	 * @return the length as an {@code int}
102 	 * @throws IOException
103 	 *             if the length is invalid
104 	 */
105 	protected int toLength(byte command, byte[] length)
106 			throws IOException {
107 		long l = BufferUtils.getUInt(length);
108 		if (l <= 0 || l > maxReplyLength - 4) {
109 			throw new SshException(MessageFormat.format(
110 					SshdText.get().sshAgentReplyLengthError,
111 					Long.toString(l),
112 					SshAgentConstants.getCommandMessageName(command)));
113 		}
114 		return (int) l;
115 	}
116 }