1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.value; |
7 | |
8 | import java.io.BufferedInputStream; |
9 | import java.io.BufferedOutputStream; |
10 | import java.io.DataInputStream; |
11 | import java.io.DataOutputStream; |
12 | import java.io.IOException; |
13 | import java.io.Reader; |
14 | import java.math.BigDecimal; |
15 | import java.net.InetAddress; |
16 | import java.net.Socket; |
17 | import java.sql.ResultSet; |
18 | import java.sql.ResultSetMetaData; |
19 | import java.sql.SQLException; |
20 | import java.sql.Timestamp; |
21 | |
22 | import org.h2.api.ErrorCode; |
23 | import org.h2.engine.Constants; |
24 | import org.h2.engine.SessionInterface; |
25 | import org.h2.message.DbException; |
26 | import org.h2.mvstore.DataUtils; |
27 | import org.h2.security.SHA256; |
28 | import org.h2.store.Data; |
29 | import org.h2.store.DataReader; |
30 | import org.h2.tools.SimpleResultSet; |
31 | import org.h2.util.DateTimeUtils; |
32 | import org.h2.util.IOUtils; |
33 | import org.h2.util.JdbcUtils; |
34 | import org.h2.util.MathUtils; |
35 | import org.h2.util.NetUtils; |
36 | import org.h2.util.StringUtils; |
37 | import org.h2.util.Utils; |
38 | |
39 | /** |
40 | * The transfer class is used to send and receive Value objects. |
41 | * It is used on both the client side, and on the server side. |
42 | */ |
43 | public class Transfer { |
44 | |
45 | private static final int BUFFER_SIZE = 64 * 1024; |
46 | private static final int LOB_MAGIC = 0x1234; |
47 | private static final int LOB_MAC_SALT_LENGTH = 16; |
48 | |
49 | private Socket socket; |
50 | private DataInputStream in; |
51 | private DataOutputStream out; |
52 | private SessionInterface session; |
53 | private boolean ssl; |
54 | private int version; |
55 | private byte[] lobMacSalt; |
56 | |
57 | /** |
58 | * Create a new transfer object for the specified session. |
59 | * |
60 | * @param session the session |
61 | */ |
62 | public Transfer(SessionInterface session) { |
63 | this.session = session; |
64 | } |
65 | |
66 | /** |
67 | * Set the socket this object uses. |
68 | * |
69 | * @param s the socket |
70 | */ |
71 | public void setSocket(Socket s) { |
72 | socket = s; |
73 | } |
74 | |
75 | /** |
76 | * Initialize the transfer object. This method will try to open an input and |
77 | * output stream. |
78 | */ |
79 | public synchronized void init() throws IOException { |
80 | if (socket != null) { |
81 | in = new DataInputStream( |
82 | new BufferedInputStream( |
83 | socket.getInputStream(), Transfer.BUFFER_SIZE)); |
84 | out = new DataOutputStream( |
85 | new BufferedOutputStream( |
86 | socket.getOutputStream(), Transfer.BUFFER_SIZE)); |
87 | } |
88 | } |
89 | |
90 | /** |
91 | * Write pending changes. |
92 | */ |
93 | public void flush() throws IOException { |
94 | out.flush(); |
95 | } |
96 | |
97 | /** |
98 | * Write a boolean. |
99 | * |
100 | * @param x the value |
101 | * @return itself |
102 | */ |
103 | public Transfer writeBoolean(boolean x) throws IOException { |
104 | out.writeByte((byte) (x ? 1 : 0)); |
105 | return this; |
106 | } |
107 | |
108 | /** |
109 | * Read a boolean. |
110 | * |
111 | * @return the value |
112 | */ |
113 | public boolean readBoolean() throws IOException { |
114 | return in.readByte() == 1; |
115 | } |
116 | |
117 | /** |
118 | * Write a byte. |
119 | * |
120 | * @param x the value |
121 | * @return itself |
122 | */ |
123 | private Transfer writeByte(byte x) throws IOException { |
124 | out.writeByte(x); |
125 | return this; |
126 | } |
127 | |
128 | /** |
129 | * Read a byte. |
130 | * |
131 | * @return the value |
132 | */ |
133 | private byte readByte() throws IOException { |
134 | return in.readByte(); |
135 | } |
136 | |
137 | /** |
138 | * Write an int. |
139 | * |
140 | * @param x the value |
141 | * @return itself |
142 | */ |
143 | public Transfer writeInt(int x) throws IOException { |
144 | out.writeInt(x); |
145 | return this; |
146 | } |
147 | |
148 | /** |
149 | * Read an int. |
150 | * |
151 | * @return the value |
152 | */ |
153 | public int readInt() throws IOException { |
154 | return in.readInt(); |
155 | } |
156 | |
157 | /** |
158 | * Write a long. |
159 | * |
160 | * @param x the value |
161 | * @return itself |
162 | */ |
163 | public Transfer writeLong(long x) throws IOException { |
164 | out.writeLong(x); |
165 | return this; |
166 | } |
167 | |
168 | /** |
169 | * Read a long. |
170 | * |
171 | * @return the value |
172 | */ |
173 | public long readLong() throws IOException { |
174 | return in.readLong(); |
175 | } |
176 | |
177 | /** |
178 | * Write a double. |
179 | * |
180 | * @param i the value |
181 | * @return itself |
182 | */ |
183 | private Transfer writeDouble(double i) throws IOException { |
184 | out.writeDouble(i); |
185 | return this; |
186 | } |
187 | |
188 | /** |
189 | * Write a float. |
190 | * |
191 | * @param i the value |
192 | * @return itself |
193 | */ |
194 | private Transfer writeFloat(float i) throws IOException { |
195 | out.writeFloat(i); |
196 | return this; |
197 | } |
198 | |
199 | /** |
200 | * Read a double. |
201 | * |
202 | * @return the value |
203 | */ |
204 | private double readDouble() throws IOException { |
205 | return in.readDouble(); |
206 | } |
207 | |
208 | /** |
209 | * Read a float. |
210 | * |
211 | * @return the value |
212 | */ |
213 | private float readFloat() throws IOException { |
214 | return in.readFloat(); |
215 | } |
216 | |
217 | /** |
218 | * Write a string. The maximum string length is Integer.MAX_VALUE. |
219 | * |
220 | * @param s the value |
221 | * @return itself |
222 | */ |
223 | public Transfer writeString(String s) throws IOException { |
224 | if (s == null) { |
225 | out.writeInt(-1); |
226 | } else { |
227 | int len = s.length(); |
228 | out.writeInt(len); |
229 | for (int i = 0; i < len; i++) { |
230 | out.writeChar(s.charAt(i)); |
231 | } |
232 | } |
233 | return this; |
234 | } |
235 | |
236 | /** |
237 | * Read a string. |
238 | * |
239 | * @return the value |
240 | */ |
241 | public String readString() throws IOException { |
242 | int len = in.readInt(); |
243 | if (len == -1) { |
244 | return null; |
245 | } |
246 | StringBuilder buff = new StringBuilder(len); |
247 | for (int i = 0; i < len; i++) { |
248 | buff.append(in.readChar()); |
249 | } |
250 | String s = buff.toString(); |
251 | s = StringUtils.cache(s); |
252 | return s; |
253 | } |
254 | |
255 | /** |
256 | * Write a byte array. |
257 | * |
258 | * @param data the value |
259 | * @return itself |
260 | */ |
261 | public Transfer writeBytes(byte[] data) throws IOException { |
262 | if (data == null) { |
263 | writeInt(-1); |
264 | } else { |
265 | writeInt(data.length); |
266 | out.write(data); |
267 | } |
268 | return this; |
269 | } |
270 | |
271 | /** |
272 | * Write a number of bytes. |
273 | * |
274 | * @param buff the value |
275 | * @param off the offset |
276 | * @param len the length |
277 | * @return itself |
278 | */ |
279 | public Transfer writeBytes(byte[] buff, int off, int len) throws IOException { |
280 | out.write(buff, off, len); |
281 | return this; |
282 | } |
283 | |
284 | /** |
285 | * Read a byte array. |
286 | * |
287 | * @return the value |
288 | */ |
289 | public byte[] readBytes() throws IOException { |
290 | int len = readInt(); |
291 | if (len == -1) { |
292 | return null; |
293 | } |
294 | byte[] b = DataUtils.newBytes(len); |
295 | in.readFully(b); |
296 | return b; |
297 | } |
298 | |
299 | /** |
300 | * Read a number of bytes. |
301 | * |
302 | * @param buff the target buffer |
303 | * @param off the offset |
304 | * @param len the number of bytes to read |
305 | */ |
306 | public void readBytes(byte[] buff, int off, int len) throws IOException { |
307 | in.readFully(buff, off, len); |
308 | } |
309 | |
310 | /** |
311 | * Close the transfer object and the socket. |
312 | */ |
313 | public synchronized void close() { |
314 | if (socket != null) { |
315 | try { |
316 | if (out != null) { |
317 | out.flush(); |
318 | } |
319 | if (socket != null) { |
320 | socket.close(); |
321 | } |
322 | } catch (IOException e) { |
323 | DbException.traceThrowable(e); |
324 | } finally { |
325 | socket = null; |
326 | } |
327 | } |
328 | } |
329 | |
330 | /** |
331 | * Write a value. |
332 | * |
333 | * @param v the value |
334 | */ |
335 | public void writeValue(Value v) throws IOException { |
336 | int type = v.getType(); |
337 | writeInt(type); |
338 | switch (type) { |
339 | case Value.NULL: |
340 | break; |
341 | case Value.BYTES: |
342 | case Value.JAVA_OBJECT: |
343 | writeBytes(v.getBytesNoCopy()); |
344 | break; |
345 | case Value.UUID: { |
346 | ValueUuid uuid = (ValueUuid) v; |
347 | writeLong(uuid.getHigh()); |
348 | writeLong(uuid.getLow()); |
349 | break; |
350 | } |
351 | case Value.BOOLEAN: |
352 | writeBoolean(v.getBoolean().booleanValue()); |
353 | break; |
354 | case Value.BYTE: |
355 | writeByte(v.getByte()); |
356 | break; |
357 | case Value.TIME: |
358 | if (version >= Constants.TCP_PROTOCOL_VERSION_9) { |
359 | writeLong(((ValueTime) v).getNanos()); |
360 | } else if (version >= Constants.TCP_PROTOCOL_VERSION_7) { |
361 | writeLong(DateTimeUtils.getTimeLocalWithoutDst(v.getTime())); |
362 | } else { |
363 | writeLong(v.getTime().getTime()); |
364 | } |
365 | break; |
366 | case Value.DATE: |
367 | if (version >= Constants.TCP_PROTOCOL_VERSION_9) { |
368 | writeLong(((ValueDate) v).getDateValue()); |
369 | } else if (version >= Constants.TCP_PROTOCOL_VERSION_7) { |
370 | writeLong(DateTimeUtils.getTimeLocalWithoutDst(v.getDate())); |
371 | } else { |
372 | writeLong(v.getDate().getTime()); |
373 | } |
374 | break; |
375 | case Value.TIMESTAMP: { |
376 | if (version >= Constants.TCP_PROTOCOL_VERSION_9) { |
377 | ValueTimestamp ts = (ValueTimestamp) v; |
378 | writeLong(ts.getDateValue()); |
379 | writeLong(ts.getTimeNanos()); |
380 | } else if (version >= Constants.TCP_PROTOCOL_VERSION_7) { |
381 | Timestamp ts = v.getTimestamp(); |
382 | writeLong(DateTimeUtils.getTimeLocalWithoutDst(ts)); |
383 | writeInt(ts.getNanos() % 1000000); |
384 | } else { |
385 | Timestamp ts = v.getTimestamp(); |
386 | writeLong(ts.getTime()); |
387 | writeInt(ts.getNanos() % 1000000); |
388 | } |
389 | break; |
390 | } |
391 | case Value.DECIMAL: |
392 | writeString(v.getString()); |
393 | break; |
394 | case Value.DOUBLE: |
395 | writeDouble(v.getDouble()); |
396 | break; |
397 | case Value.FLOAT: |
398 | writeFloat(v.getFloat()); |
399 | break; |
400 | case Value.INT: |
401 | writeInt(v.getInt()); |
402 | break; |
403 | case Value.LONG: |
404 | writeLong(v.getLong()); |
405 | break; |
406 | case Value.SHORT: |
407 | writeInt(v.getShort()); |
408 | break; |
409 | case Value.STRING: |
410 | case Value.STRING_IGNORECASE: |
411 | case Value.STRING_FIXED: |
412 | writeString(v.getString()); |
413 | break; |
414 | case Value.BLOB: { |
415 | if (version >= Constants.TCP_PROTOCOL_VERSION_11) { |
416 | if (v instanceof ValueLobDb) { |
417 | ValueLobDb lob = (ValueLobDb) v; |
418 | if (lob.isStored()) { |
419 | writeLong(-1); |
420 | writeInt(lob.getTableId()); |
421 | writeLong(lob.getLobId()); |
422 | if (version >= Constants.TCP_PROTOCOL_VERSION_12) { |
423 | writeBytes(calculateLobMac(lob.getLobId())); |
424 | } |
425 | writeLong(lob.getPrecision()); |
426 | break; |
427 | } |
428 | } |
429 | } |
430 | long length = v.getPrecision(); |
431 | if (length < 0) { |
432 | throw DbException.get( |
433 | ErrorCode.CONNECTION_BROKEN_1, "length=" + length); |
434 | } |
435 | writeLong(length); |
436 | long written = IOUtils.copyAndCloseInput(v.getInputStream(), out); |
437 | if (written != length) { |
438 | throw DbException.get( |
439 | ErrorCode.CONNECTION_BROKEN_1, "length:" + length + " written:" + written); |
440 | } |
441 | writeInt(LOB_MAGIC); |
442 | break; |
443 | } |
444 | case Value.CLOB: { |
445 | if (version >= Constants.TCP_PROTOCOL_VERSION_11) { |
446 | if (v instanceof ValueLobDb) { |
447 | ValueLobDb lob = (ValueLobDb) v; |
448 | if (lob.isStored()) { |
449 | writeLong(-1); |
450 | writeInt(lob.getTableId()); |
451 | writeLong(lob.getLobId()); |
452 | if (version >= Constants.TCP_PROTOCOL_VERSION_12) { |
453 | writeBytes(calculateLobMac(lob.getLobId())); |
454 | } |
455 | writeLong(lob.getPrecision()); |
456 | break; |
457 | } |
458 | } |
459 | } |
460 | long length = v.getPrecision(); |
461 | if (length < 0) { |
462 | throw DbException.get( |
463 | ErrorCode.CONNECTION_BROKEN_1, "length=" + length); |
464 | } |
465 | writeLong(length); |
466 | Reader reader = v.getReader(); |
467 | Data.copyString(reader, out); |
468 | writeInt(LOB_MAGIC); |
469 | break; |
470 | } |
471 | case Value.ARRAY: { |
472 | ValueArray va = (ValueArray) v; |
473 | Value[] list = va.getList(); |
474 | int len = list.length; |
475 | Class<?> componentType = va.getComponentType(); |
476 | if (componentType == Object.class) { |
477 | writeInt(len); |
478 | } else { |
479 | writeInt(-(len + 1)); |
480 | writeString(componentType.getName()); |
481 | } |
482 | for (Value value : list) { |
483 | writeValue(value); |
484 | } |
485 | break; |
486 | } |
487 | case Value.RESULT_SET: { |
488 | try { |
489 | ResultSet rs = ((ValueResultSet) v).getResultSet(); |
490 | rs.beforeFirst(); |
491 | ResultSetMetaData meta = rs.getMetaData(); |
492 | int columnCount = meta.getColumnCount(); |
493 | writeInt(columnCount); |
494 | for (int i = 0; i < columnCount; i++) { |
495 | writeString(meta.getColumnName(i + 1)); |
496 | writeInt(meta.getColumnType(i + 1)); |
497 | writeInt(meta.getPrecision(i + 1)); |
498 | writeInt(meta.getScale(i + 1)); |
499 | } |
500 | while (rs.next()) { |
501 | writeBoolean(true); |
502 | for (int i = 0; i < columnCount; i++) { |
503 | int t = DataType.getValueTypeFromResultSet(meta, i + 1); |
504 | Value val = DataType.readValue(session, rs, i + 1, t); |
505 | writeValue(val); |
506 | } |
507 | } |
508 | writeBoolean(false); |
509 | rs.beforeFirst(); |
510 | } catch (SQLException e) { |
511 | throw DbException.convertToIOException(e); |
512 | } |
513 | break; |
514 | } |
515 | case Value.GEOMETRY: |
516 | if (version >= Constants.TCP_PROTOCOL_VERSION_14) { |
517 | writeBytes(v.getBytesNoCopy()); |
518 | } else { |
519 | writeString(v.getString()); |
520 | } |
521 | break; |
522 | default: |
523 | throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "type=" + type); |
524 | } |
525 | } |
526 | |
527 | /** |
528 | * Read a value. |
529 | * |
530 | * @return the value |
531 | */ |
532 | public Value readValue() throws IOException { |
533 | int type = readInt(); |
534 | switch(type) { |
535 | case Value.NULL: |
536 | return ValueNull.INSTANCE; |
537 | case Value.BYTES: |
538 | return ValueBytes.getNoCopy(readBytes()); |
539 | case Value.UUID: |
540 | return ValueUuid.get(readLong(), readLong()); |
541 | case Value.JAVA_OBJECT: |
542 | return ValueJavaObject.getNoCopy(null, readBytes(), session.getDataHandler()); |
543 | case Value.BOOLEAN: |
544 | return ValueBoolean.get(readBoolean()); |
545 | case Value.BYTE: |
546 | return ValueByte.get(readByte()); |
547 | case Value.DATE: |
548 | if (version >= Constants.TCP_PROTOCOL_VERSION_9) { |
549 | return ValueDate.fromDateValue(readLong()); |
550 | } else if (version >= Constants.TCP_PROTOCOL_VERSION_7) { |
551 | return ValueDate.fromMillis(DateTimeUtils.getTimeUTCWithoutDst(readLong())); |
552 | } |
553 | return ValueDate.fromMillis(readLong()); |
554 | case Value.TIME: |
555 | if (version >= Constants.TCP_PROTOCOL_VERSION_9) { |
556 | return ValueTime.fromNanos(readLong()); |
557 | } else if (version >= Constants.TCP_PROTOCOL_VERSION_7) { |
558 | return ValueTime.fromMillis(DateTimeUtils.getTimeUTCWithoutDst(readLong())); |
559 | } |
560 | return ValueTime.fromMillis(readLong()); |
561 | case Value.TIMESTAMP: { |
562 | if (version >= Constants.TCP_PROTOCOL_VERSION_9) { |
563 | return ValueTimestamp.fromDateValueAndNanos( |
564 | readLong(), readLong()); |
565 | } else if (version >= Constants.TCP_PROTOCOL_VERSION_7) { |
566 | return ValueTimestamp.fromMillisNanos( |
567 | DateTimeUtils.getTimeUTCWithoutDst(readLong()), |
568 | readInt() % 1000000); |
569 | } |
570 | return ValueTimestamp.fromMillisNanos(readLong(), |
571 | readInt() % 1000000); |
572 | } |
573 | case Value.DECIMAL: |
574 | return ValueDecimal.get(new BigDecimal(readString())); |
575 | case Value.DOUBLE: |
576 | return ValueDouble.get(readDouble()); |
577 | case Value.FLOAT: |
578 | return ValueFloat.get(readFloat()); |
579 | case Value.INT: |
580 | return ValueInt.get(readInt()); |
581 | case Value.LONG: |
582 | return ValueLong.get(readLong()); |
583 | case Value.SHORT: |
584 | return ValueShort.get((short) readInt()); |
585 | case Value.STRING: |
586 | return ValueString.get(readString()); |
587 | case Value.STRING_IGNORECASE: |
588 | return ValueStringIgnoreCase.get(readString()); |
589 | case Value.STRING_FIXED: |
590 | return ValueStringFixed.get(readString()); |
591 | case Value.BLOB: { |
592 | long length = readLong(); |
593 | if (version >= Constants.TCP_PROTOCOL_VERSION_11) { |
594 | if (length == -1) { |
595 | int tableId = readInt(); |
596 | long id = readLong(); |
597 | byte[] hmac; |
598 | if (version >= Constants.TCP_PROTOCOL_VERSION_12) { |
599 | hmac = readBytes(); |
600 | } else { |
601 | hmac = null; |
602 | } |
603 | long precision = readLong(); |
604 | return ValueLobDb.create( |
605 | Value.BLOB, session.getDataHandler(), tableId, id, hmac, precision); |
606 | } |
607 | int len = (int) length; |
608 | byte[] small = new byte[len]; |
609 | IOUtils.readFully(in, small, len); |
610 | int magic = readInt(); |
611 | if (magic != LOB_MAGIC) { |
612 | throw DbException.get( |
613 | ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic); |
614 | } |
615 | return ValueLobDb.createSmallLob(Value.BLOB, small, length); |
616 | } |
617 | Value v = session.getDataHandler().getLobStorage().createBlob(in, length); |
618 | int magic = readInt(); |
619 | if (magic != LOB_MAGIC) { |
620 | throw DbException.get( |
621 | ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic); |
622 | } |
623 | return v; |
624 | } |
625 | case Value.CLOB: { |
626 | long length = readLong(); |
627 | if (version >= Constants.TCP_PROTOCOL_VERSION_11) { |
628 | if (length == -1) { |
629 | int tableId = readInt(); |
630 | long id = readLong(); |
631 | byte[] hmac; |
632 | if (version >= Constants.TCP_PROTOCOL_VERSION_12) { |
633 | hmac = readBytes(); |
634 | } else { |
635 | hmac = null; |
636 | } |
637 | long precision = readLong(); |
638 | return ValueLobDb.create( |
639 | Value.CLOB, session.getDataHandler(), tableId, id, hmac, precision); |
640 | } |
641 | DataReader reader = new DataReader(in); |
642 | int len = (int) length; |
643 | char[] buff = new char[len]; |
644 | IOUtils.readFully(reader, buff, len); |
645 | int magic = readInt(); |
646 | if (magic != LOB_MAGIC) { |
647 | throw DbException.get( |
648 | ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic); |
649 | } |
650 | byte[] small = new String(buff).getBytes(Constants.UTF8); |
651 | return ValueLobDb.createSmallLob(Value.CLOB, small, length); |
652 | } |
653 | Value v = session.getDataHandler().getLobStorage(). |
654 | createClob(new DataReader(in), length); |
655 | int magic = readInt(); |
656 | if (magic != LOB_MAGIC) { |
657 | throw DbException.get( |
658 | ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic); |
659 | } |
660 | return v; |
661 | } |
662 | case Value.ARRAY: { |
663 | int len = readInt(); |
664 | Class<?> componentType = Object.class; |
665 | if (len < 0) { |
666 | len = -(len + 1); |
667 | componentType = JdbcUtils.loadUserClass(readString()); |
668 | } |
669 | Value[] list = new Value[len]; |
670 | for (int i = 0; i < len; i++) { |
671 | list[i] = readValue(); |
672 | } |
673 | return ValueArray.get(componentType, list); |
674 | } |
675 | case Value.RESULT_SET: { |
676 | SimpleResultSet rs = new SimpleResultSet(); |
677 | rs.setAutoClose(false); |
678 | int columns = readInt(); |
679 | for (int i = 0; i < columns; i++) { |
680 | rs.addColumn(readString(), readInt(), readInt(), readInt()); |
681 | } |
682 | while (true) { |
683 | if (!readBoolean()) { |
684 | break; |
685 | } |
686 | Object[] o = new Object[columns]; |
687 | for (int i = 0; i < columns; i++) { |
688 | o[i] = readValue().getObject(); |
689 | } |
690 | rs.addRow(o); |
691 | } |
692 | return ValueResultSet.get(rs); |
693 | } |
694 | case Value.GEOMETRY: |
695 | if (version >= Constants.TCP_PROTOCOL_VERSION_14) { |
696 | return ValueGeometry.get(readBytes()); |
697 | } |
698 | return ValueGeometry.get(readString()); |
699 | default: |
700 | throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "type=" + type); |
701 | } |
702 | } |
703 | |
704 | /** |
705 | * Get the socket. |
706 | * |
707 | * @return the socket |
708 | */ |
709 | public Socket getSocket() { |
710 | return socket; |
711 | } |
712 | |
713 | /** |
714 | * Set the session. |
715 | * |
716 | * @param session the session |
717 | */ |
718 | public void setSession(SessionInterface session) { |
719 | this.session = session; |
720 | } |
721 | |
722 | /** |
723 | * Enable or disable SSL. |
724 | * |
725 | * @param ssl the new value |
726 | */ |
727 | public void setSSL(boolean ssl) { |
728 | this.ssl = ssl; |
729 | } |
730 | |
731 | /** |
732 | * Open a new new connection to the same address and port as this one. |
733 | * |
734 | * @return the new transfer object |
735 | */ |
736 | public Transfer openNewConnection() throws IOException { |
737 | InetAddress address = socket.getInetAddress(); |
738 | int port = socket.getPort(); |
739 | Socket s2 = NetUtils.createSocket(address, port, ssl); |
740 | Transfer trans = new Transfer(null); |
741 | trans.setSocket(s2); |
742 | trans.setSSL(ssl); |
743 | return trans; |
744 | } |
745 | |
746 | public void setVersion(int version) { |
747 | this.version = version; |
748 | } |
749 | |
750 | public synchronized boolean isClosed() { |
751 | return socket == null || socket.isClosed(); |
752 | } |
753 | |
754 | /** |
755 | * Verify the HMAC. |
756 | * |
757 | * @param hmac the message authentication code |
758 | * @param lobId the lobId |
759 | * @throws DbException if the HMAC does not match |
760 | */ |
761 | public void verifyLobMac(byte[] hmac, long lobId) { |
762 | byte[] result = calculateLobMac(lobId); |
763 | if (!Utils.compareSecure(hmac, result)) { |
764 | throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, |
765 | "Invalid lob hmac; possibly the connection was re-opened internally"); |
766 | } |
767 | } |
768 | |
769 | private byte[] calculateLobMac(long lobId) { |
770 | if (lobMacSalt == null) { |
771 | lobMacSalt = MathUtils.secureRandomBytes(LOB_MAC_SALT_LENGTH); |
772 | } |
773 | byte[] data = new byte[8]; |
774 | Utils.writeLong(data, 0, lobId); |
775 | byte[] hmacData = SHA256.getHashWithSalt(data, lobMacSalt); |
776 | return hmacData; |
777 | } |
778 | |
779 | } |