View Javadoc
1   package org.metricshub.ipmi.core.coding.sol;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * IPMI Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 Verax Systems, MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import org.metricshub.ipmi.core.coding.PayloadCoder;
26  import org.metricshub.ipmi.core.coding.commands.IpmiVersion;
27  import org.metricshub.ipmi.core.coding.commands.ResponseData;
28  import org.metricshub.ipmi.core.coding.payload.IpmiPayload;
29  import org.metricshub.ipmi.core.coding.payload.lan.IPMIException;
30  import org.metricshub.ipmi.core.coding.payload.sol.SolAckState;
31  import org.metricshub.ipmi.core.coding.payload.sol.SolInboundMessage;
32  import org.metricshub.ipmi.core.coding.payload.sol.SolInboundStatusField;
33  import org.metricshub.ipmi.core.coding.payload.sol.SolOperation;
34  import org.metricshub.ipmi.core.coding.payload.sol.SolOutboundMessage;
35  import org.metricshub.ipmi.core.coding.payload.sol.SolOutboundOperationField;
36  import org.metricshub.ipmi.core.coding.protocol.AuthenticationType;
37  import org.metricshub.ipmi.core.coding.protocol.IpmiMessage;
38  import org.metricshub.ipmi.core.coding.protocol.PayloadType;
39  import org.metricshub.ipmi.core.coding.security.CipherSuite;
40  import org.metricshub.ipmi.core.common.TypeConverter;
41  
42  import java.security.InvalidKeyException;
43  import java.security.NoSuchAlgorithmException;
44  import java.util.Arrays;
45  import java.util.HashSet;
46  import java.util.Set;
47  
48  /**
49   * Implementation of {@link PayloadCoder} for payload type {@link PayloadType#Sol}.
50   * This coder is used during exchanging SOL messages between BMC and application.
51   */
52  public class SolCoder extends PayloadCoder {
53  
54      private final byte[] message;
55      private final byte ackNackSequenceNumber;
56      private final byte acceptedCharacters;
57      private final SolAckState ackState;
58      private final Set<SolOperation> operations;
59      private boolean acknowledgeOnly;
60  
61  
62      /**
63       * Creates new {@link SolCoder}. Use this constructor if you want to send character data, operations and ACK/NACK in single packet.
64       *
65       * @param message
66       *          data to send as byte array
67       * @param ackNackSequenceNumber
68       *          sequence number of packet that is ACKd/NACKd
69       * @param acceptedCharacters
70       *          number of characters accepted from the ACKd/NACKd packet
71       * @param ackState
72       *          actual acknowledge state - {@link SolAckState#ACK} or {@link SolAckState#NACK}
73       * @param operations
74       *          set of operations to invoke on remote serial port
75       * @param cipherSuite
76       *          {@link CipherSuite} containing authentication, confidentiality and integrity algorithms for this session
77       */
78      public SolCoder(byte[] message, byte ackNackSequenceNumber, byte acceptedCharacters, SolAckState ackState,
79                      Set<SolOperation> operations, CipherSuite cipherSuite) {
80          super(IpmiVersion.V20, cipherSuite, AuthenticationType.RMCPPlus);
81          this.message = message;
82          this.ackNackSequenceNumber = ackNackSequenceNumber;
83          this.acceptedCharacters = acceptedCharacters;
84          this.ackState = ackState;
85          this.operations = operations;
86      }
87  
88      /**
89       * Creates new {@link SolCoder}. Use this constructor for character data only packets (no ACK/NACK and no operations will be sent).
90       *
91       * @param message
92       *          data to send as byte array
93       * @param cipherSuite
94       *          {@link CipherSuite} containing authentication, confidentiality and integrity algorithms for this session.
95       */
96      public SolCoder(byte[] message, CipherSuite cipherSuite) {
97          this(message, (byte) 0, (byte) 0, SolAckState.ACK, new HashSet<SolOperation>(), cipherSuite);
98      }
99  
100     /**
101      * Creates new {@link SolCoder}. Use this constructor for packets that should only invoke operations on remote serial port.
102      *
103      * @param operations
104      *          set of operations to invoke on remote serial port.
105      * @param cipherSuite
106      *          {@link CipherSuite} containing authentication, confidentiality and integrity algorithms for this session
107      */
108     public SolCoder(Set<SolOperation> operations, CipherSuite cipherSuite) {
109         this(new byte[] { '\0' }, (byte) 0, (byte) 0, SolAckState.ACK, operations, cipherSuite);
110     }
111 
112     /**
113      * Creates new {@link SolCoder}. Use this constructor for ACK/NACK packet, that only sends ACK/NACK for specific remote message.
114      *
115      * @param ackNackSequenceNumber
116      *           sequence number of packet that is ACKd/NACKd
117      * @param acceptedCharacters
118      *          number of characters accepted from the ACKd/NACKd packet
119      * @param ackState
120      *          actual acknowledge state - {@link SolAckState#ACK} or {@link SolAckState#NACK}
121      * @param cipherSuite
122      *          {@link CipherSuite} containing authentication, confidentiality and integrity algorithms for this session.
123      */
124     public SolCoder(byte ackNackSequenceNumber, byte acceptedCharacters, SolAckState ackState, CipherSuite cipherSuite) {
125         this(null, ackNackSequenceNumber, acceptedCharacters, ackState, new HashSet<SolOperation>(), cipherSuite);
126 
127         this.acknowledgeOnly = true;
128     }
129 
130     @Override
131     public PayloadType getSupportedPayloadType() {
132         return PayloadType.Sol;
133     }
134 
135     @Override
136     protected IpmiPayload preparePayload(int sequenceNumber) throws NoSuchAlgorithmException, InvalidKeyException {
137         byte actualSequenceNumber = acknowledgeOnly ? 0 : TypeConverter.intToByte(sequenceNumber);
138 
139         SolOutboundMessage request = new SolOutboundMessage(actualSequenceNumber, ackNackSequenceNumber, acceptedCharacters,
140                 new SolOutboundOperationField(ackState, operations));
141 
142         if (message != null && message.length > 0) {
143             request.setData(message);
144         }
145 
146         return request;
147     }
148 
149     @Override
150     public ResponseData getResponseData(IpmiMessage message) throws IPMIException, NoSuchAlgorithmException, InvalidKeyException {
151         final SolInboundMessage payload = (SolInboundMessage) message.getPayload();
152         SolInboundStatusField statusField = payload.getStatusField();
153 
154         return new SolResponseData(payload.getAckNackSequenceNumber(), statusField.getAckState(), statusField.getStatuses(), payload.getAcceptedCharacterCount());
155     }
156 
157     @Override
158     public boolean equals(Object o) {
159         if (this == o) return true;
160         if (o == null || getClass() != o.getClass()) return false;
161 
162         SolCoder solCoder = (SolCoder) o;
163 
164         if (ackNackSequenceNumber != solCoder.ackNackSequenceNumber) return false;
165         if (acceptedCharacters != solCoder.acceptedCharacters) return false;
166         if (acknowledgeOnly != solCoder.acknowledgeOnly) return false;
167         if (!Arrays.equals(message, solCoder.message)) return false;
168         if (ackState != solCoder.ackState) return false;
169         return operations != null ? operations.equals(solCoder.operations) : solCoder.operations == null;
170     }
171 
172     @Override
173     public int hashCode() {
174         int result = Arrays.hashCode(message);
175         result = 31 * result + (int) ackNackSequenceNumber;
176         result = 31 * result + (int) acceptedCharacters;
177         result = 31 * result + (ackState != null ? ackState.hashCode() : 0);
178         result = 31 * result + (operations != null ? operations.hashCode() : 0);
179         result = 31 * result + (acknowledgeOnly ? 1 : 0);
180         return result;
181     }
182 }