LCOV - code coverage report
Current view: top level - src/tds - read.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 65 71 91.5 %
Date: 2025-01-18 11:50:39 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004  Brian Bruns
       3             :  * Copyright (C) 2005-2014  Frediano Ziglio
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU Library General Public
       7             :  * License as published by the Free Software Foundation; either
       8             :  * version 2 of the License, or (at your option) any later version.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * Library General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU Library General Public
      16             :  * License along with this library; if not, write to the
      17             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      18             :  * Boston, MA 02111-1307, USA.
      19             :  */
      20             : 
      21             : /**
      22             :  * \file
      23             :  * \brief Grab data from TDS packets
      24             :  */
      25             : 
      26             : #include <config.h>
      27             : 
      28             : #if HAVE_ERRNO_H
      29             : #include <errno.h>
      30             : #endif /* HAVE_ERRNO_H */
      31             : 
      32             : #if HAVE_STDLIB_H
      33             : #include <stdlib.h>
      34             : #endif /* HAVE_STDLIB_H */
      35             : 
      36             : #if HAVE_STRING_H
      37             : #include <string.h>
      38             : #endif /* HAVE_STRING_H */
      39             : 
      40             : #if HAVE_UNISTD_H
      41             : #include <unistd.h>
      42             : #endif /* HAVE_UNISTD_H */
      43             : 
      44             : #include <assert.h>
      45             : 
      46             : #include <freetds/tds.h>
      47             : #include <freetds/iconv.h>
      48             : #include <freetds/bytes.h>
      49             : #include <freetds/stream.h>
      50             : #include <freetds/utils/string.h>
      51             : #include <freetds/checks.h>
      52             : 
      53             : static size_t read_and_convert(TDSSOCKET * tds, TDSICONV * char_conv,
      54             :                                size_t * wire_size, char *outbuf, size_t outbytesleft);
      55             : 
      56             : /**
      57             :  * \ingroup libtds
      58             :  * \defgroup network Network functions
      59             :  * Functions for reading or writing from network.
      60             :  */
      61             : 
      62             : /**
      63             :  * \addtogroup network
      64             :  * @{ 
      65             :  */
      66             : 
      67             : /**
      68             :  * Return a single byte from the input buffer
      69             :  * \tds
      70             :  */
      71             : unsigned char
      72     1298604 : tds_get_byte(TDSSOCKET * tds)
      73             : {
      74     2661293 :         while (tds->in_pos >= tds->in_len) {
      75       64131 :                 if (tds_read_packet(tds) < 0)
      76             :                         return 0;
      77             :         }
      78     1298558 :         return tds->in_buf[tds->in_pos++];
      79             : }
      80             : 
      81             : /**
      82             :  * Unget will always work as long as you don't call it twice in a row.  It
      83             :  * it may work if you call it multiple times as long as you don't backup
      84             :  * over the beginning of network packet boundary which can occur anywhere in
      85             :  * the token stream.
      86             :  * \tds
      87             :  */
      88             : void
      89       50236 : tds_unget_byte(TDSSOCKET * tds)
      90             : {
      91             :         /* this is a one trick pony...don't call it twice */
      92       50236 :         tds->in_pos--;
      93       50236 : }
      94             : 
      95             : /**
      96             :  * Reads a byte from the TDS stream without removing it
      97             :  * \tds
      98             :  */
      99             : unsigned char
     100       36542 : tds_peek(TDSSOCKET * tds)
     101             : {
     102       36542 :         unsigned char result = tds_get_byte(tds);
     103       36542 :         if (tds->in_pos > 0)
     104       36542 :                 --tds->in_pos;
     105       36542 :         return result;
     106             : }
     107             : 
     108             : /**
     109             :  * Get an uint16 from the server.
     110             :  */
     111             : TDS_USMALLINT
     112      802636 : tds_get_usmallint(TDSSOCKET * tds)
     113             : {
     114             :         TDS_USMALLINT bytes;
     115             : 
     116      802636 :         tds_get_n(tds, &bytes, 2);
     117      802636 :         return (TDS_USMALLINT) TDS_GET_A2LE(&bytes);
     118             : }
     119             : 
     120             : /**
     121             :  * Get an uint32 from the server.
     122             :  * \tds
     123             :  */
     124             : TDS_UINT
     125      201698 : tds_get_uint(TDSSOCKET * tds)
     126             : {
     127             :         TDS_UINT bytes;
     128             : 
     129      201698 :         tds_get_n(tds, &bytes, 4);
     130      201698 :         return TDS_GET_A4LE(&bytes);
     131             : }
     132             : 
     133             : /**
     134             :  * Get an uint64 from the server.
     135             :  * \tds
     136             :  */
     137             : TDS_UINT8
     138       33476 : tds_get_uint8(TDSSOCKET * tds)
     139             : {
     140             :         TDS_UINT h;
     141             :         TDS_UINT l;
     142             :         TDS_UINT bytes[2];
     143             : 
     144       33476 :         tds_get_n(tds, bytes, 8);
     145       33476 :         l = TDS_GET_A4LE(bytes);
     146       33476 :         h = TDS_GET_A4LE(bytes+1);
     147       33476 :         return (((TDS_UINT8) h) << 32) | l;
     148             : }
     149             : 
     150             : /**
     151             :  * Fetch a string from the wire.
     152             :  * Output string is NOT null terminated.
     153             :  * If TDS version is 7 or 8 read unicode string and convert it.
     154             :  * This function should be use to read server default encoding strings like 
     155             :  * columns name, table names, etc, not for data (use tds_get_char_data instead)
     156             :  * @return bytes written to \a dest
     157             :  * @param tds  connection information
     158             :  * @param string_len length of string to read from wire 
     159             :  *        (in server characters, bytes for tds4-tds5, ucs2 for tds7+)
     160             :  * @param dest destination buffer, if NULL string is read and discarded
     161             :  * @param dest_size destination buffer size, in bytes
     162             :  */
     163             : size_t
     164      147598 : tds_get_string(TDSSOCKET * tds, size_t string_len, char *dest, size_t dest_size)
     165             : {
     166      147598 :         size_t wire_bytes = string_len;
     167      147598 :         unsigned conv = client2server_chardata;
     168             : 
     169      147598 :         if (IS_TDS7_PLUS(tds->conn)) {
     170       97165 :                 wire_bytes *= 2u;
     171       97165 :                 conv = client2ucs2;
     172             :         }
     173             : 
     174      147598 :         if (dest == NULL) {
     175          22 :                 tds_get_n(tds, NULL, wire_bytes);
     176          22 :                 return string_len;
     177             :         }
     178             : 
     179      147576 :         return read_and_convert(tds, tds->conn->char_convs[conv], &wire_bytes, dest, dest_size);
     180             : }
     181             : 
     182             : /**
     183             :  * Fetch character data the wire.
     184             :  * Output is NOT null terminated.
     185             :  * If \a char_conv is not NULL, convert data accordingly.
     186             :  * \param tds         state information for the socket and the TDS protocol
     187             :  * \param row_buffer  destination buffer in current_row. Can't be NULL
     188             :  * \param wire_size   size to read from wire (in bytes)
     189             :  * \param curcol      column information
     190             :  * \return TDS_SUCCESS or TDS_FAIL (probably memory error on text data)
     191             :  */
     192             : TDSRET
     193      381965 : tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol)
     194             : {
     195             :         size_t in_left;
     196             : 
     197      381965 :         assert(curcol->char_conv);
     198             : 
     199             :         /*
     200             :          * row_buffer is a column buffer, allocated when the column's metadata are processed
     201             :          * and reused for each row.
     202             :          */
     203             : 
     204             :         /* silly case, empty string */
     205      381965 :         if (wire_size == 0) {
     206          66 :                 curcol->column_cur_size = 0;
     207          66 :                 return TDS_SUCCESS;
     208             :         }
     209             : 
     210      381899 :         in_left = curcol->column_size;
     211      381899 :         curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, row_buffer, in_left);
     212      381899 :         if (TDS_UNLIKELY(wire_size > 0)) {
     213           0 :                 tds_get_n(tds, NULL, wire_size);
     214           0 :                 tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n",
     215             :                                                  (unsigned int) wire_size, curcol->column_cur_size);
     216             :                 return TDS_FAIL;
     217             :         }
     218             :         return TDS_SUCCESS;
     219             : }
     220             : 
     221             : /**
     222             :  * Get N bytes from the buffer and return them in the already allocated space  
     223             :  * given to us.  We ASSUME that the person calling this function has done the  
     224             :  * bounds checking for us since they know how many bytes they want here.
     225             :  * dest of NULL means we just want to eat the bytes.   (tetherow@nol.org)
     226             :  */
     227             : bool
     228     2488530 : tds_get_n(TDSSOCKET * tds, void *dest, size_t need)
     229             : {
     230             :         for (;;) {
     231     2494644 :                 unsigned int have = tds->in_len - tds->in_pos;
     232             : 
     233     2494644 :                 if (need <= have)
     234             :                         break;
     235             :                 /* We need more than is in the buffer, copy what is there */
     236        6114 :                 if (dest != NULL) {
     237        6113 :                         memcpy((char *) dest, tds->in_buf + tds->in_pos, have);
     238        6113 :                         dest = (char *) dest + have;
     239             :                 }
     240        6114 :                 need -= have;
     241        6114 :                 if (TDS_UNLIKELY((tds->in_buf[1] & TDS_STATUS_EOM) != 0
     242             :                                  || tds_read_packet(tds) < 0)) {
     243           0 :                         tds_close_socket(tds); /* evidently out of sync */
     244           0 :                         return false;
     245             :                 }
     246             :         }
     247     2488530 :         if (need > 0) {
     248             :                 /* get the remainder if there is any */
     249     2134302 :                 if (dest != NULL) {
     250     2098300 :                         memcpy((char *) dest, tds->in_buf + tds->in_pos, need);
     251             :                 }
     252     2134302 :                 tds->in_pos += need;
     253             :         }
     254             :         return true;
     255             : }
     256             : 
     257             : /**
     258             :  * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary
     259             :  * is not aligned with the character boundary.  In that event, it will return an error, and
     260             :  * some number of bytes (less than a character) will remain in the tail end of temp[].  They are
     261             :  * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read.
     262             :  * \tds
     263             :  * \param char_conv conversion structure
     264             :  * \param[out] wire_size size readed from wire
     265             :  * \param outbuf buffer to write to
     266             :  * \param outbytesleft buffer length
     267             :  * \return bytes readed
     268             :  */
     269             : static size_t
     270      529475 : read_and_convert(TDSSOCKET * tds, TDSICONV * char_conv, size_t * wire_size, char *outbuf,
     271             :                  size_t outbytesleft)
     272             : {
     273             :         int res;
     274             :         TDSDATAINSTREAM r;
     275             :         TDSSTATICOUTSTREAM w;
     276             : 
     277      529475 :         tds_datain_stream_init(&r, tds, *wire_size);
     278      529475 :         tds_staticout_stream_init(&w, outbuf, outbytesleft);
     279             : 
     280      529475 :         res = tds_convert_stream(tds, char_conv, to_client, &r.stream, &w.stream);
     281      529475 :         *wire_size = r.wire_size;
     282      529475 :         return (char *) w.stream.buffer - outbuf;
     283             : }
     284             : 
     285             : /**
     286             :  * Reads a string from wire and put in a DSTR.
     287             :  * On error we read the bytes from the wire anyway.
     288             :  * \tds
     289             :  * \param[out] s output string
     290             :  * \param[in] len string length (in characters)
     291             :  * \return string or NULL on error
     292             :  */
     293             : DSTR*
     294       84110 : tds_dstr_get(TDSSOCKET * tds, DSTR * s, size_t len)
     295             : {
     296             :         size_t out_len;
     297             : 
     298       84110 :         CHECK_TDS_EXTRA(tds);
     299             : 
     300             :         /* assure sufficient space for every conversion */
     301       84110 :         if (TDS_UNLIKELY(!tds_dstr_alloc(s, len * 4))) {
     302           0 :                 tds_get_n(tds, NULL, len);
     303           0 :                 return NULL;
     304             :         }
     305             : 
     306      168220 :         out_len = tds_get_string(tds, len, tds_dstr_buf(s), len * 4);
     307       84110 :         tds_dstr_setlen(s, out_len);
     308       84110 :         return s;
     309             : }
     310             : 
     311             : /** @} */

Generated by: LCOV version 1.13