1 //
2 // Copyright 1998 CDS Networks, Inc., Medford Oregon
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 1. Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // 3. All advertising materials mentioning features or use of this software
14 // must display the following acknowledgement:
15 // This product includes software developed by CDS Networks, Inc.
16 // 4. The name of CDS Networks, Inc. may not be used to endorse or promote
17 // products derived from this software without specific prior
18 // written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND
21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 // ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE
24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 // SUCH DAMAGE.
31 //
32
33
34 package com.internetcds.jdbc.tds;
35
36 import java.io.*;
37 import java.net.*;
38 import com.internetcds.util.HexDump;
39 import com.internetcds.util.Logger;
40 import java.sql.Timestamp;
41
42 /***
43 * Handle the communications for a Tds instance.
44 *
45 * @version $Id: TdsComm.java,v 1.1 2003/04/29 18:07:53 sinisa Exp $
46 * @author Craig Spannring
47 * @author Igor Petrovski
48 */
49 public class TdsComm implements TdsDefinitions
50 {
51 public static final String cvsVersion = "$Id: TdsComm.java,v 1.1 2003/04/29 18:07:53 sinisa Exp $";
52 //Dusan
53 static int z = 0;
54
55 static final int headerLength = 8;
56
57 //
58 // The following constants are the packet types.
59 //
60 // They are the first databayte in the packet and
61 // define the type of data in that packet.
62 public static final byte QUERY = 1;
63 public static final byte LOGON = 2;
64 public static final byte PROC = 3;
65 public static final byte REPLY = 4;
66 public static final byte CANCEL = 6;
67 public static final byte LOGON70 = 16; // Added 2000-06-05
68
69
70 // The minimum packet length that a TDS implementation can support
71 // is 512 bytes. This implementation will not support packets longer
72 // than 512. This will simplify the connection negotiation.
73 //
74 // XXX Some future release of this driver should be modified to
75 // negotiate longer packet sizes with the DB server.
76 private static final int maxPacketLength = 4096;
77
78 // in and out are the sockets used for all communication with the
79 // server.
80 private DataOutputStream out = null;
81 private DataInputStream in = null;
82
83
84 // outBuffer is used to construct the physical packets that will
85 // be sent to the database server.
86 byte outBuffer[];
87
88 // nextOutBufferIndex is an index into outBuffer where the next
89 // byte of data will be stored while constructing a packet.
90 int nextOutBufferIndex = 0;
91
92 // The type of the TDS packet that is being constructed
93 // in outBuffer.
94 int packetType = 0;
95
96
97 // Place to store the incoming data from the DB server.
98 byte inBuffer[];
99
100 // index of next byte that will be 'read' from inBuffer
101 int inBufferIndex = 0;
102
103 // Total Number of bytes stored in inBuffer. (The number includes bytes
104 // that have been 'read' as well as bytes that still need to be 'read'.
105 int inBufferLen = 0;
106
107 // Track how many packets we have sent and received
108 int packetsSent = 0;
109 int packetsReceived = 0;
110
111 // For debuging purposes it would be nice to uniquely identify each Tds
112 // stream. id will be a unique value for each instance of this class.
113 static int id = 0;
114
115 // Added 2000-06-07. Used to control TDS version-specific behavior.
116
117 private int tdsVer = TDS42;
118
119 public TdsComm(Socket sock, int tdsVer_)
120 throws java.io.IOException
121 {
122 out = new DataOutputStream(sock.getOutputStream());
123 in = new DataInputStream(sock.getInputStream());
124 outBuffer = new byte[4096];
125 inBuffer = new byte[4096];
126 // Added 2000-06-07
127 tdsVer = tdsVer_;
128
129 id++;
130 }
131
132 public void close()
133 {
134 // nop for now.
135 }
136
137
138 /***
139 * start a TDS packet.
140 *
141 * <br>
142 * This method should be called to start a logical TDS packet.
143 *
144 * @param type Type of the packet. Can be QUERY, LOGON, PROC,
145 * REPLY, or CANCEL.
146 */
147 public synchronized void startPacket(int type)
148 {
149 // Only one thread at a time can be building an outboudn packet.
150 // This is primarily a concern with building cancel packets.
151 while(someThreadIsBuildingPacket())
152 {
153 try
154 {
155 wait();
156 }
157 catch (java.lang.InterruptedException e)
158 {
159 // nop
160 }
161 }
162
163 packetType = type;
164 nextOutBufferIndex = headerLength;
165 }
166
167 /***
168 * Is some thread currently building a logical TDS packet?
169 *
170 * @return true iff a packet is being built.
171 */
172 public boolean someThreadIsBuildingPacket()
173 {
174 return packetType!=0;
175 }
176
177
178 /***
179 * append a byte onto the end of the logical TDS packet.
180 *
181 * <p>
182 * Append a byte onto the end of the logical TDS packet. When a
183 * physical packet is full send it to the server.
184 *
185 * @param b byte to add to the TDS packet
186 */
187 public void appendByte(byte b)
188 throws java.io.IOException
189 {
190 if (nextOutBufferIndex == maxPacketLength)
191 {
192 // If we have a full physical packet then ship it out to the
193 // network.
194 sendPhysicalPacket(false);
195 nextOutBufferIndex = headerLength;
196 }
197
198 storeByte(nextOutBufferIndex, b);
199 nextOutBufferIndex++;
200
201
202 } // appendByte()
203
204
205 /***
206 * append an array of bytes onto the end of the logical TDS packet.
207 *
208 * @param b bytes to add to the TDS packet
209 */
210 public void appendBytes(byte[] b)
211 throws java.io.IOException
212 {
213 appendBytes(b, b.length, (byte)0);
214 } // appendBytes()
215
216
217
218 /***
219 * append an array of bytes onto the end of the logical TDS packet.
220 *
221 * @param b bytes to add to the TDS packet
222 * @param len maximum number of bytes to transmit
223 * @param pad fill with this byte until len is reached
224 */
225 public void appendBytes(byte[] b, int len, byte pad)
226 throws java.io.IOException
227 {
228 int i = 0;
229 for (; i<b.length && i<len; i++)
230 {
231 appendByte(b[i]);
232 }
233 for (; i<len; i++)
234 {
235 appendByte(pad);
236 }
237 }
238
239
240 /***
241 * append a short int onto the end of the logical TDS packet.
242 * <p>
243 * @param s short int to add to the TDS packet
244 */
245 public void appendShort(short s)
246 throws java.io.IOException
247 {
248 appendByte((byte)((s>>8)&0xff));
249 appendByte((byte)((s>>0)&0xff));
250 }
251
252 /***
253 * Appends a short int onto the end of the logical TDS packet.
254 * <p>
255 * @param s short int to add to the TDS packet
256 */
257 public void appendTdsShort(short s)
258 throws java.io.IOException
259 {
260 appendByte((byte)((s>>0)&0xff));
261 appendByte((byte)((s>>8)&0xff));
262 }
263
264
265 /***
266 * append a Double onto the end of the logical TDS packet.
267 * <p>
268 * Append the Double value onto the end of the TDS packet as a
269 * SYBFLT8.
270 *
271 * @param value Double to add to the TDS packet
272 */
273 public void appendFlt8(Double value)
274 throws java.io.IOException
275 {
276 long l = Double.doubleToLongBits(value.doubleValue());
277
278 appendByte((byte)((l>>0)&0xff));
279 appendByte((byte)((l>>8)&0xff));
280 appendByte((byte)((l>>16)&0xff));
281 appendByte((byte)((l>>24)&0xff));
282 appendByte((byte)((l>>32)&0xff));
283 appendByte((byte)((l>>40)&0xff));
284 appendByte((byte)((l>>48)&0xff));
285 appendByte((byte)((l>>56)&0xff));
286 }
287
288 public void appendInt(int i)
289 throws java.io.IOException
290 {
291 appendByte((byte)((i>>24)&0xff));
292 appendByte((byte)((i>>16)&0xff));
293 appendByte((byte)((i>>8)&0xff));
294 appendByte((byte)((i>>0)&0xff));
295 }
296
297 public void appendTdsInt(int i)
298 throws java.io.IOException
299 {
300 appendByte((byte)((i>>0)&0xff));
301 appendByte((byte)((i>>8)&0xff));
302 appendByte((byte)((i>>16)&0xff));
303 appendByte((byte)((i>>24)&0xff));
304 }
305
306
307 public void appendInt64(long i)
308 throws java.io.IOException
309 {
310 appendByte((byte)((i>>56)&0xff));
311 appendByte((byte)((i>>48)&0xff));
312 appendByte((byte)((i>>40)&0xff));
313 appendByte((byte)((i>>32)&0xff));
314 appendByte((byte)((i>>24)&0xff));
315 appendByte((byte)((i>>16)&0xff));
316 appendByte((byte)((i>>8)&0xff));
317 appendByte((byte)((i>>0)&0xff));
318 }
319
320 /***
321 * Appends the 16-bit characters from the caller's string, without
322 * narrowing the characters.
323 *
324 * Sybase let's the client decide what byte order to use but it \
325 * appears that SQLServer 7.0 little-endian byte order.
326 *
327 * Added 2000-06-05
328 */
329 public void appendChars(String s) throws java.io.IOException {
330
331 for (int i = 0; i < s.length(); ++i) {
332 int c = s.charAt(i);
333 byte b1 = (byte)(c & 0xFF);
334 byte b2 = (byte)((c >> 8) & 0xFF);
335 appendByte(b1);
336 appendByte(b2);
337 }
338 }
339
340 /*
341 * Stefan Bodewig 2000-06-21
342 *
343 * removed appendString() to keep the encoding to and from the
344 * server charset in on place - i.e. Tds.
345 *
346 * It had to be Tds as we need to specify the length for the
347 * String as well, sometimes before we send the actual data,
348 * sometimes after we've sent them.
349 *
350 * If we need to know the length beforehand in Tds, we'd have to
351 * convert the data twice, once to get the length and once to send
352 * them.
353 */
354 // public void appendString(
355 // String s,
356 // int length,
357 // byte pad)
358 // throws java.io.IOException
359 // {
360 // int i;
361 // byte dst[];
362 //
363 //
364 // dst = encoder.getBytes(s.substring(0, (length<=s.length() ? length
365 // : s.length())));
366 //
367 // for(i=0; i<dst.length; i++)
368 // {
369 // appendByte(dst[i]);
370 // }
371 //
372 // for(; i<length; i++)
373 // {
374 // appendByte(pad);
375 // }
376 // }
377
378
379 /***
380 * Send the logical packet.
381 * <p>
382 * Send the logical packet the has been constructed. */
383 public synchronized void sendPacket()
384 throws java.io.IOException
385 {
386 sendPhysicalPacket(true);
387 nextOutBufferIndex = 0;
388 packetType = 0;
389 notify();
390 }
391
392
393 /***
394 * store a byte of data at a particular location in the outBuffer.
395 *
396 * @param index position in outBuffer to store data
397 * @param value value to store in the outBuffer.
398 */
399 private void storeByte(
400 int index,
401 byte value)
402 {
403 outBuffer[index] = value;
404
405 }
406
407 /***
408 * store a short integer of data at a particular location in the outBuffer.
409 *
410 * @param index position in outBuffer to store data
411 * @param value value to store in the outBuffer.
412 */
413 private void storeShort(
414 int index,
415 short s)
416 {
417 outBuffer[index] = (byte)((s>>8) & 0xff);
418 outBuffer[index+1] = (byte)((s>>0) & 0xff);
419 }
420
421
422 /***
423 * send the data in the outBuffer.
424 * <p>
425 * Fill in the TDS packet header data and send the data in outBuffer
426 * to the DB server.
427 *
428 * @param isLastSegment is this the last physical packet that makes
429 * up the physical packet?
430 */
431 private void sendPhysicalPacket(boolean isLastSegment)
432 throws java.io.IOException
433 {
434 if (nextOutBufferIndex>headerLength
435 || packetType == CANCEL)
436 {
437 // packet type
438 storeByte(0, (byte)(packetType & 0xff));
439 storeByte(1, isLastSegment ? (byte)1 : (byte)0);
440 storeShort(2, (short)nextOutBufferIndex);
441 storeByte(4, (byte)0);
442 storeByte(5, (byte)0);
443 storeByte(6, (byte)(tdsVer == TDS70 ? 1 : 0));
444 storeByte(7, (byte)0);
445 //Dusan
446 z++;
447 /* if (z==7) {
448
449 storeByte(18, "1".getBytes()[0]);
450 storeByte(20, (byte)0);
451 storeByte(21, "#".getBytes()[0]);
452 storeByte(22, "1".getBytes()[0]);
453 storeByte(23, "(".getBytes()[0]);
454 storeByte(24, "@".getBytes()[0]);
455 storeByte(25, "P".getBytes()[0]);
456 storeByte(26, "1".getBytes()[0]);
457 storeByte(31, (byte)0);
458
459 }
460 */
461 out.write(outBuffer, 0, nextOutBufferIndex);
462 packetsSent++;
463 for(int j=0; j < outBuffer.length; j++)
464 storeByte(j, (byte)0);
465
466
467 if (Logger.isActive())
468 {
469 String dump = HexDump.hexDump(outBuffer, nextOutBufferIndex);
470 String t = (new Timestamp(
471 System.currentTimeMillis())).toString();
472 Logger.println("Instance " + id + " @ " + t
473 + " sent packet #" + packetsSent + "\n" + dump);
474 }
475 }
476 }
477
478 /***
479 * peek at the next byte of data.
480 * <p>
481 * This returns the next byte of data that would be returned
482 * by getByte(), but does not actually consume the data.
483 *
484 * <b>Note-</b> We can't synchronize this method (or most of the other
485 * methods in this class) because of the way cancels are handled.
486 * If a thread is waiting for a response from the server the
487 * cancelController class must be able to call sendPacket() to
488 * cancel the request.
489 *
490 * @return The next byte of data that will be returned by getByte()
491 *
492 * @exception com.internetcds.jdbc.tds.TdsException
493 * @exception java.io.IOException
494 */
495 public byte peek()
496 throws com.internetcds.jdbc.tds.TdsException,
497 java.io.IOException
498 {
499
500 // XXX RACE CONDITION- It is possible that two threads
501 // could be modifying inBuffer at the same time. We need
502 // to synchronized based on inBuffer itself.
503 byte result = getByte();
504 backup();
505 return result;
506 }
507
508
509 /***
510 * put the most recently read byte of data back in the inBuffer.
511 * <p>
512 * This function effectivly ungets the last byte read.
513 * It is guaranteed to be able to unget the last byte read.
514 * Trying to unget multiple bytes is not recomended and will
515 * only work so long as all the bytes were in the same
516 * physical TDS network packet.
517 *
518 * @author Craig Spannring
519 */
520 public void backup()
521 {
522 inBufferIndex--;
523
524 // make sure we have fallen of the beginning of the buffer
525 // throw an array out of bounds error if we've gone back too far.
526 byte b = inBuffer[inBufferIndex];
527 }
528
529
530 /***
531 * read a byte of data from the DB server.
532 * <p>
533 * This will return the next byte of data from the DB server.
534 * <p>
535 * <B>Warning</B> If there is not data available this method
536 * will block.
537 */
538 public byte getByte()
539 throws com.internetcds.jdbc.tds.TdsException,
540 java.io.IOException
541 {
542 byte result;
543
544 if (inBufferIndex >= inBufferLen)
545 {
546 // out of data, read another physical packet.
547 getPhysicalPacket();
548 }
549
550 result = inBuffer[inBufferIndex++];
551 return result;
552 }
553
554 public byte[] getBytes(int len)
555 throws com.internetcds.jdbc.tds.TdsException,
556 java.io.IOException
557 {
558 byte result[] = new byte[len];
559 int i;
560
561 for(i=0; i<len; i++)
562 {
563 result[i] = getByte();
564 }
565
566 return result;
567 }
568
569 /***
570 * Reads bytes or characters (depending on TDS version) and constructs a
571 * string with them.
572 *
573 * Sybase will let the client choose byte ordering, but SQLServer 7.0
574 * wants little endian only. In the interest of simplicity, just use
575 * little endian regardless of the type of server.
576 *
577 * Added 2000-06-05.
578 */
579 public String getString(int len)
580 throws com.internetcds.jdbc.tds.TdsException,
581 java.io.IOException
582 {
583 if (tdsVer == TDS70) {
584 char[] chars = new char[len];
585 for (int i = 0; i < len; ++i) {
586 int lo = getByte() & 0xFF;
587 int hi = getByte() & 0xFF;
588 chars[i] = (char)(lo | (hi << 8));
589 }
590 return new String(chars);
591 }
592 else
593 return new String(getBytes(len));
594 }
595
596 public void skip(int i)
597 throws com.internetcds.jdbc.tds.TdsException,
598 java.io.IOException
599 {
600 for(; i>0; i--)
601 {
602 getByte();
603 }
604 } // skip()
605
606 public int getNetShort()
607 throws TdsException, java.io.IOException
608 {
609 byte tmp[] = new byte[2];
610 tmp[0] = getByte();
611 tmp[1] = getByte();
612 return ntohs(tmp, 0);
613 }
614
615 public int getTdsShort()
616 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException
617 {
618 int lo = ((int)getByte() & 0xff);
619 int hi = ((int)getByte() & 0xff) << 8;
620 return lo | hi;
621 }
622
623 public int getTdsInt()
624 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException
625 {
626 int result;
627
628 int b1 = ((int)getByte() & 0xff);
629 int b2 = ((int)getByte() & 0xff) << 8;
630 int b3 = ((int)getByte() & 0xff) << 16;
631 int b4 = ((int)getByte() & 0xff) << 24;
632
633 result = b4 | b3 | b2 | b1;
634
635 return result;
636 }
637
638 public long getTdsInt64()
639 throws com.internetcds.jdbc.tds.TdsException, java.io.IOException
640 {
641 long b1 = ((long)getByte() & 0xff);
642 long b2 = ((long)getByte() & 0xff) << 8;
643 long b3 = ((long)getByte() & 0xff) << 16;
644 long b4 = ((long)getByte() & 0xff) << 24;
645 long b5 = ((long)getByte() & 0xff) << 32;
646 long b6 = ((long)getByte() & 0xff) << 40;
647 long b7 = ((long)getByte() & 0xff) << 48;
648 long b8 = ((long)getByte() & 0xff) << 56;
649 return b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8;
650 }
651
652
653 /***
654 * convert two bytes in a byte array into a Java short integer.
655 *
656 * @param buf array of data
657 * @param offset index into the buf array where the short integer
658 * is stored.
659 */
660 private static int ntohs(byte buf[], int offset)
661 {
662 int lo = ((int)buf[offset+1] & 0xff);
663 int hi = (((int)buf[offset] & 0xff) << 8);
664
665 return hi | lo; // return an int since we really want an _unsigned_
666 }
667
668 /***
669 * Read a physical packet.
670 * <p>
671 * <B>Warning</B> This method will block until it gets all of the input.
672 */
673 private void getPhysicalPacket()
674 throws TdsException, java.io.IOException
675 {
676 byte tmpBuf[] = new byte[8];
677
678
679 // read the header
680 for (int nread = 0; nread < 8; )
681 {
682 nread += in.read(tmpBuf, nread, 8 - nread);
683 }
684 if (Logger.isActive())
685 {
686 String dump = com.internetcds.util.HexDump.hexDump(tmpBuf, 8);
687 String t = (new Timestamp(
688 System.currentTimeMillis())).toString();
689
690 Logger.println("Instance " + id + " @ " + t
691 + " recevied header #" + (packetsReceived+1)
692 + "\n" + dump);
693 }
694 byte packetType = tmpBuf[0];
695 if (packetType!=LOGON
696 && packetType!=QUERY
697 && packetType!=REPLY)
698 {
699 throw new TdsUnknownPacketType(packetType, tmpBuf);
700 }
701 // figure out how many bytes are remaining in this packet.
702 int len = ntohs(tmpBuf, 2) - 8;
703 // Added 2000-06-05
704 if (len >= inBuffer.length)
705 {
706 inBuffer = new byte[len];
707 }
708 if (len < 0)
709 {
710 throw new TdsException("Confused by a length of " + len);
711 }
712
713 // now get the data
714 for (int nread = 0; nread < len; )
715 {
716 //Dusan1
717
718 nread += in.read(inBuffer, nread, len - nread);
719 }
720 packetsReceived++;
721
722
723 // adjust the bookkeeping info about the incoming buffer
724 inBufferLen = len;
725 inBufferIndex = 0;
726
727 if (Logger.isActive())
728 {
729 String dump = com.internetcds.util.HexDump.hexDump(inBuffer, len);
730 String t = (new Timestamp(
731 System.currentTimeMillis())).toString();
732
733 Logger.println("Instance " + id + " @ " + t
734 + " recevied data #" + (packetsReceived)
735 + "\n" + dump);
736 }
737 }
738 }
739
This page was automatically generated by Maven