1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.net.tftp;
19
20 import java.net.DatagramPacket;
21 import java.net.InetAddress;
22 import java.nio.charset.Charset;
23 import java.nio.charset.StandardCharsets;
24 import java.util.HashMap;
25 import java.util.Locale;
26 import java.util.Map;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public abstract class TFTPRequestPacket extends TFTPPacket {
46
47
48
49 static final String[] modeStrings = { "netascii", "octet" };
50
51
52
53
54 private static final byte[] modeBytes[] = { { (byte) 'n', (byte) 'e', (byte) 't', (byte) 'a', (byte) 's', (byte) 'c', (byte) 'i', (byte) 'i', 0 },
55 { (byte) 'o', (byte) 'c', (byte) 't', (byte) 'e', (byte) 't', 0 } };
56
57
58 private final int mode;
59
60
61 private final String fileName;
62
63
64 private final Map<String, String> options = new HashMap<>();
65
66
67
68
69
70
71
72
73
74
75 TFTPRequestPacket(final InetAddress destination, final int port, final int type, final String fileName, final int mode) {
76 super(type, destination, port);
77
78 this.fileName = fileName;
79 this.mode = mode;
80 }
81
82
83
84
85
86
87
88
89
90 TFTPRequestPacket(final int type, final DatagramPacket datagram) throws TFTPPacketException {
91 super(type, datagram.getAddress(), datagram.getPort());
92
93 final byte[] data = datagram.getData();
94
95 if (getType() != data[1]) {
96 throw new TFTPPacketException("TFTP operator code does not match type.");
97 }
98
99 final StringBuilder buffer = new StringBuilder();
100
101 int index = 2;
102 final int length = datagram.getLength();
103
104 while (index < length && data[index] != 0) {
105 buffer.append((char) data[index]);
106 ++index;
107 }
108
109 this.fileName = buffer.toString();
110
111 if (index >= length) {
112 throw new TFTPPacketException("Bad file name and mode format.");
113 }
114
115 buffer.setLength(0);
116 ++index;
117 while (index < length && data[index] != 0) {
118 buffer.append((char) data[index]);
119 ++index;
120 }
121
122 final String modeString = buffer.toString().toLowerCase(Locale.ENGLISH);
123 final int modeStringsLength = modeStrings.length;
124
125 int mode = 0;
126 int modeIndex;
127 for (modeIndex = 0; modeIndex < modeStringsLength; modeIndex++) {
128 if (modeString.equals(modeStrings[modeIndex])) {
129 mode = modeIndex;
130 break;
131 }
132 }
133
134 this.mode = mode;
135
136 if (modeIndex >= modeStringsLength) {
137 throw new TFTPPacketException("Unrecognized TFTP transfer mode: " + modeString);
138
139
140
141 }
142
143 ++index;
144 while (index < length) {
145 int start = index;
146 for (; data[index] != 0; ++index) {
147 if (index >= length) {
148 throw new TFTPPacketException("Invalid option format");
149 }
150 }
151 final String option = new String(data, start, index - start, StandardCharsets.US_ASCII);
152 ++index;
153 start = index;
154 for (; data[index] != 0; ++index) {
155 if (index >= length) {
156 throw new TFTPPacketException("Invalid option format");
157 }
158 }
159 final String octets = new String(data, start, index - start, StandardCharsets.US_ASCII);
160 this.options.put(option, octets);
161 ++index;
162 }
163 }
164
165
166
167
168
169
170 public final String getFilename() {
171 return fileName;
172 }
173
174
175
176
177
178
179 public final int getMode() {
180 return mode;
181 }
182
183
184
185
186
187
188
189
190 public final Map<String, String> getOptions() {
191 return options;
192 }
193
194
195
196
197
198
199
200
201 @Override
202 public final DatagramPacket newDatagram() {
203 final int fileLength;
204 final int modeLength;
205 final byte[] data;
206
207 fileLength = fileName.length();
208 modeLength = modeBytes[mode].length;
209
210 int optionsLength = 0;
211 for (final Map.Entry<String, String> entry : options.entrySet()) {
212 optionsLength += entry.getKey().length() + 1 + entry.getValue().length() + 1;
213 }
214 data = new byte[fileLength + modeLength + 3 + optionsLength];
215 data[0] = 0;
216 data[1] = (byte) type;
217 System.arraycopy(fileName.getBytes(Charset.defaultCharset()), 0, data, 2, fileLength);
218 data[fileLength + 2] = 0;
219 System.arraycopy(modeBytes[mode], 0, data, fileLength + 3, modeLength);
220
221 if (optionsLength > 0) {
222 handleOptions(data, fileLength, modeLength);
223 }
224
225 return new DatagramPacket(data, data.length, address, port);
226 }
227
228
229
230
231
232
233
234
235
236 @Override
237 final DatagramPacket newDatagram(final DatagramPacket datagram, final byte[] data) {
238 final int fileLength;
239 final int modeLength;
240
241 fileLength = fileName.length();
242 modeLength = modeBytes[mode].length;
243
244 data[0] = 0;
245 data[1] = (byte) type;
246 System.arraycopy(fileName.getBytes(Charset.defaultCharset()), 0, data, 2, fileLength);
247 data[fileLength + 2] = 0;
248 System.arraycopy(modeBytes[mode], 0, data, fileLength + 3, modeLength);
249
250 handleOptions(data, fileLength, modeLength);
251
252 datagram.setAddress(address);
253 datagram.setPort(port);
254 datagram.setData(data);
255 datagram.setLength(fileLength + modeLength + 3);
256
257 return datagram;
258 }
259
260 private void handleOptions(final byte[] data, final int fileLength, final int modeLength) {
261 int index = fileLength + modeLength + 2;
262 for (final Map.Entry<String, String> entry : options.entrySet()) {
263 data[index] = 0;
264 final String key = entry.getKey();
265 final String value = entry.getValue();
266
267 System.arraycopy(key.getBytes(StandardCharsets.US_ASCII), 0, data, ++index, key.length());
268 index += key.length();
269 data[index++] = 0;
270
271 System.arraycopy(value.getBytes(StandardCharsets.US_ASCII), 0, data, index, value.length());
272 index += value.length();
273 }
274 }
275 }