LCOV - code coverage report
Current view: top level - src/tds - util.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 116 135 85.9 %
Date: 2026-11-07 23:58:45 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Brian Bruns
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU Library General Public
       6             :  * License as published by the Free Software Foundation; either
       7             :  * version 2 of the License, or (at your option) any later version.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             :  * Library General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU Library General Public
      15             :  * License along with this library; if not, write to the
      16             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      17             :  * Boston, MA 02111-1307, USA.
      18             :  */
      19             : 
      20             : #include <config.h>
      21             : 
      22             : #include <stdarg.h>
      23             : 
      24             : #include <freetds/time.h>
      25             : 
      26             : #include <assert.h>
      27             : #include <ctype.h>
      28             : #include <limits.h>
      29             : #include <stdio.h>
      30             : 
      31             : #if HAVE_STDLIB_H
      32             : #include <stdlib.h>
      33             : #endif /* HAVE_STDLIB_H */
      34             : 
      35             : #if HAVE_STRING_H
      36             : #include <string.h>
      37             : #endif /* HAVE_STRING_H */
      38             : 
      39             : #if HAVE_UNISTD_H
      40             : #include <unistd.h>
      41             : #endif /* HAVE_UNISTD_H */
      42             : 
      43             : #ifdef _WIN32
      44             : #include <process.h>
      45             : #endif
      46             : 
      47             : #include <freetds/tds.h>
      48             : #include <freetds/tds/checks.h>
      49             : #include <freetds/thread.h>
      50             : 
      51             : /**
      52             :  * Set state of TDS connection, with logging and checking.
      53             :  * \param tds     state information for the socket and the TDS protocol
      54             :  * \param state   the new state of the connection, cf. TDS_STATE.
      55             :  * \return        the new state, which might not be \a state.
      56             :  */
      57             : TDS_STATE
      58     1514258 : tds_set_state(TDSSOCKET * tds, TDS_STATE state)
      59             : {
      60             :         TDS_STATE prior_state;
      61             :         static const char state_names[][8] = {
      62             :                 "IDLE",
      63             :                 "WRITING",
      64             :                 "SENDING",
      65             :                 "PENDING",
      66             :                 "READING",
      67             :                 "DEAD"
      68             :         };
      69     1514258 :         assert(state < TDS_VECTOR_SIZE(state_names));
      70     1514258 :         assert(tds->state < TDS_VECTOR_SIZE(state_names));
      71             : 
      72     1514258 :         prior_state = tds->state;
      73     1514258 :         if (state == prior_state)
      74             :                 return state;
      75             : 
      76     1503220 :         switch (state) {
      77      645473 :         case TDS_PENDING:
      78      645473 :                 if (prior_state == TDS_READING || prior_state == TDS_WRITING) {
      79      566449 :                         tds->state = TDS_PENDING;
      80      566449 :                         tds_mutex_unlock(&tds->wire_mtx);
      81      566449 :                         break;
      82             :                 }
      83       79024 :                 tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n",
      84             :                                                 state_names[prior_state], state_names[state]);
      85             :                 break;
      86      566388 :         case TDS_READING:
      87             :                 /* transition to READING are valid only from PENDING */
      88      566388 :                 if (tds_mutex_trylock(&tds->wire_mtx))
      89          10 :                         return tds->state;
      90      566378 :                 if (tds->state != TDS_PENDING) {
      91           9 :                         tds_mutex_unlock(&tds->wire_mtx);
      92           9 :                         tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", 
      93             :                                                         state_names[prior_state], state_names[state]);
      94             :                         break;
      95             :                 }
      96      566369 :                 tds->state = state;
      97      566369 :                 break;
      98       64442 :         case TDS_SENDING:
      99       64442 :                 if (prior_state != TDS_READING && prior_state != TDS_WRITING) {
     100           0 :                         tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n",
     101             :                                 state_names[prior_state], state_names[state]);
     102             :                         break;
     103             :                 }
     104       64442 :                 if (tds->state == TDS_READING) {
     105             :                         /* TODO check this code, copied from tds_submit_prepare */
     106         756 :                         tds_free_all_results(tds);
     107         756 :                         tds->rows_affected = TDS_NO_COUNT;
     108         756 :                         tds_release_cursor(&tds->cur_cursor);
     109         756 :                         tds_release_cur_dyn(tds);
     110         756 :                         tds->current_op = TDS_OP_NONE;
     111             :                 }
     112             : 
     113       64442 :                 tds_mutex_unlock(&tds->wire_mtx);
     114       64442 :                 tds->state = state;
     115       64442 :                 break;
     116       78920 :         case TDS_IDLE:
     117       78920 :                 if (prior_state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) {
     118          20 :                         tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n",
     119             :                                 state_names[prior_state], state_names[state]);
     120             :                         break;
     121             :                 }
     122             :         case TDS_DEAD:
     123       83343 :                 if (prior_state == TDS_READING || prior_state == TDS_WRITING)
     124       79014 :                         tds_mutex_unlock(&tds->wire_mtx);
     125       83343 :                 tds->state = state;
     126             : 
     127             :                 /* invalid, code should have either close or aborted all freezes */
     128       83343 :                 if (TDS_UNLIKELY(tds->frozen)) {
     129             :                         TDSFREEZE freeze;
     130             : 
     131           0 :                         tds->frozen = 1;
     132           0 :                         freeze.tds = tds;
     133           0 :                         freeze.pkt = tds->frozen_packets;
     134           0 :                         freeze.pkt_pos = 8;
     135           0 :                         freeze.size_len = 0;
     136           0 :                         tds_freeze_abort(&freeze);
     137             : 
     138           0 :                         tds_connection_close(tds->conn);
     139             :                 }
     140             :                 break;
     141      143554 :         case TDS_WRITING:
     142      143554 :                 CHECK_TDS_EXTRA(tds);
     143             : 
     144      143554 :                 if (tds_mutex_trylock(&tds->wire_mtx))
     145           0 :                         return tds->state;
     146             : 
     147      143554 :                 if (tds->state == TDS_DEAD) {
     148           2 :                         tds_mutex_unlock(&tds->wire_mtx);
     149           2 :                         tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", 
     150             :                                                         state_names[prior_state], state_names[state]);
     151           2 :                         tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0);
     152           2 :                         break;
     153      143552 :                 } else if (tds->state != TDS_IDLE && tds->state != TDS_SENDING) {
     154          16 :                         tds_mutex_unlock(&tds->wire_mtx);
     155          16 :                         tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", 
     156             :                                                         state_names[prior_state], state_names[state]);
     157          16 :                         tdserror(tds_get_ctx(tds), tds, TDSERPND, 0);
     158          16 :                         break;
     159             :                 }
     160             : 
     161      143536 :                 if (tds->state == TDS_IDLE) {
     162             :                         /* TODO check this code, copied from tds_submit_prepare */
     163       79094 :                         tds_free_all_results(tds);
     164       79094 :                         tds->rows_affected = TDS_NO_COUNT;
     165       79094 :                         tds_release_cursor(&tds->cur_cursor);
     166       79094 :                         tds_release_cur_dyn(tds);
     167       79094 :                         tds->current_op = TDS_OP_NONE;
     168             :                 }
     169             : 
     170      143536 :                 tds->state = state;
     171      143536 :                 break;
     172             :         default:
     173           0 :                 assert(0);
     174             :                 break;
     175             :         }
     176             : 
     177     1503210 :         state = tds->state;
     178             : 
     179     1503210 :         tdsdump_log(TDS_DBG_INFO1, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]);
     180     1503210 :         CHECK_TDS_EXTRA(tds);
     181             : 
     182     1503210 :         return state;
     183             : }
     184             : 
     185             : 
     186             : void
     187        3676 : tds_swap_bytes(void *buf, size_t bytes)
     188             : {
     189             :         unsigned char tmp, *begin, *last;
     190             : 
     191        3676 :         begin = (unsigned char *) buf;
     192        3676 :         last  = begin + bytes;
     193             : 
     194       22944 :         while (begin < --last) {
     195       15592 :                 tmp      = *last;
     196       15592 :                 *last    = *begin;
     197       15592 :                 *begin++ = tmp;
     198             :         }
     199        3676 : }
     200             : 
     201             : unsigned int
     202        7938 : tds_gettime_ms(void)
     203             : {
     204             : #ifdef _WIN32
     205             :         /* note: static analysis warns that this will loop every 49 days,
     206             :          * however elsewhere we take care to use this return value in a way
     207             :          * that allows for unsigned wraparound */
     208             :         return GetTickCount();
     209             : #elif defined(HAVE_GETHRTIME)
     210             :         return (unsigned int) (gethrtime() / 1000000u);
     211             : #elif defined(HAVE_CLOCK_GETTIME) && defined(TDS_GETTIMEMILLI_CONST)
     212             :         struct timespec ts;
     213        7938 :         clock_gettime(TDS_GETTIMEMILLI_CONST, &ts);
     214        7938 :         return (unsigned int) (ts.tv_sec * 1000u + ts.tv_nsec / 1000000u);
     215             : #elif defined(HAVE_GETTIMEOFDAY)
     216             :         struct timeval tv;
     217             :         gettimeofday(&tv, NULL);
     218             :         return (unsigned int) (tv.tv_sec * 1000u + tv.tv_usec / 1000u);
     219             : #else
     220             : #error How to implement tds_gettime_ms ??
     221             : #endif
     222             : }
     223             : 
     224             : /*
     225             :  * Call the client library's error handler
     226             :  */
     227             : #define EXINFO         1
     228             : #define EXUSER         2
     229             : #define EXNONFATAL     3
     230             : #define EXCONVERSION   4
     231             : #define EXSERVER       5
     232             : #define EXTIME         6
     233             : #define EXPROGRAM      7
     234             : #define EXRESOURCE     8
     235             : #define EXCOMM         9
     236             : #define EXFATAL       10
     237             : #define EXCONSISTENCY 11
     238             : 
     239             : typedef struct tds_error_message
     240             : {
     241             :         TDSERRNO msgno;
     242             :         TDS_TINYINT severity;
     243             :         const char *msgtext;
     244             : } TDS_ERROR_MESSAGE;
     245             : 
     246             : static const TDS_ERROR_MESSAGE tds_error_messages[] = 
     247             :         { { TDSEICONVIU,     EXCONVERSION,      "Buffer exhausted converting characters from client into server's character set" }
     248             :         , { TDSEICONVAVAIL,  EXCONVERSION,      "Character set conversion is not available between client character set '%.*s' and "
     249             :                                                 "server character set '%.*s'" }
     250             :         , { TDSEICONVO,      EXCONVERSION,      "Error converting characters into server's character set. Some character(s) could "
     251             :                                                 "not be converted" }
     252             :         , { TDSEICONVI,      EXCONVERSION,      "Some character(s) could not be converted into client's character set.  "
     253             :                                                 "Unconverted bytes were changed to question marks ('?')" }
     254             :         , { TDSEICONV2BIG,   EXCONVERSION,      "Some character(s) could not be converted into client's character set" }
     255             :         , { TDSEICONVPARTIAL, EXCONVERSION,     "Illegal byte sequence encountered, data ends with an incomplete "
     256             :                                                 "multibyte sequence" }
     257             :         , { TDSEPORTINSTANCE,      EXUSER,      "Both port and instance specified" }
     258             :         , { TDSEVERDOWN,           EXINFO,      "TDS version downgraded to 7.1!" }
     259             :         , { TDSEMEM,            EXRESOURCE,     "Unable to allocate sufficient memory" }
     260             :         , { TDSEPWD,               EXUSER,      "Login incorrect" }
     261             :         , { TDSERPND,           EXPROGRAM,      "Attempt to initiate a new TDS server operation with results pending" }
     262             :         , { TDSEBTOK,              EXCOMM,      "Bad token from the server: Datastream processing out of sync" }
     263             :         , { TDSECAP,               EXCOMM,      "DB-Library capabilities not accepted by the Server" }
     264             :         , { TDSECAPTYP,            EXCOMM,      "Unexpected capability type in CAPABILITY datastream" }
     265             :         , { TDSECLOS,              EXCOMM,      "Error in closing network connection" }
     266             :         , { TDSECONN,              EXCOMM,      "Unable to connect: TDS server is unavailable or does not exist" }
     267             :         , { TDSEEUNR,              EXCOMM,      "Unsolicited event notification received" }
     268             :         , { TDSEFCON,              EXCOMM,      "TDS server connection failed" }
     269             :         , { TDSENEG,               EXCOMM,      "Negotiated login attempt failed" }
     270             :         , { TDSEOOB,               EXCOMM,      "Error in sending out-of-band data to the server" }
     271             :         , { TDSEREAD,              EXCOMM,      "Read from the server failed" }
     272             :         , { TDSETIME,              EXTIME,      "TDS server connection timed out" }
     273             :         , { TDSESEOF,              EXCOMM,      "Unexpected EOF from the server" }
     274             :         , { TDSEINTF,              EXUSER,      "Server name not found in configuration files." }
     275             :         , { TDSESOCK,              EXCOMM,      "Unable to open socket" }
     276             :         , { TDSESYNC,              EXCOMM,      "Read attempted while out of synchronization with TDS server" }
     277             :         , { TDSEUHST,              EXUSER,      "Unknown host machine name." }
     278             :         , { TDSEUMSG,              EXCOMM,      "Unknown message-id in MSG datastream" }
     279             :         , { TDSEUSCT,              EXCOMM,      "Unable to set communications timer" }
     280             :         , { TDSEUTDS,              EXCOMM,      "Unrecognized TDS version received from the server" }
     281             :         , { TDSEWRIT,              EXCOMM,      "Write to the server failed" }
     282             :         , { TDSECONF,              EXUSER,      "Local configuration error.  "
     283             :                                                 "Check TDSDUMPCONFIG log for details." }
     284             :         /* last, with msgno == TDSEOK */
     285             :         , { TDSEOK,              EXCONSISTENCY, "unrecognized msgno" }
     286             :         };
     287             : 
     288             : static const TDS_ERROR_MESSAGE *tds_find_error(int msgno);
     289             : static int tds_report_error(const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, const TDS_ERROR_MESSAGE * err,
     290             :                             int msgno, int errnum, char *message);
     291             : 
     292             : static const char *
     293           0 : retname(int retcode)
     294             : {
     295           0 :         switch(retcode) {
     296             :         case TDS_INT_CONTINUE:
     297             :                 return "TDS_INT_CONTINUE";
     298           0 :         case TDS_INT_CANCEL:
     299           0 :                 return "TDS_INT_CANCEL";
     300           0 :         case TDS_INT_TIMEOUT:
     301           0 :                 return "TDS_INT_TIMEOUT";
     302             :         }
     303           0 :         assert(0);
     304             :         return "nonesuch";
     305             : }
     306             : 
     307             : /**
     308             :  * \brief Call the client library's error handler (for library-generated errors only)
     309             :  *
     310             :  * The client library error handler may return: 
     311             :  * TDS_INT_CANCEL -- Return TDS_FAIL to the calling function.  For TDSETIME, closes the connection first. 
     312             :  * TDS_INT_CONTINUE -- For TDSETIME only, retry the network read/write operation. Else invalid.
     313             :  * TDS_INT_TIMEOUT -- For TDSETIME only, send a TDSCANCEL packet. Else invalid.
     314             :  *
     315             :  * These are Sybase semantics, but they serve all purposes.  
     316             :  * The application tells the library to quit, fail, retry, or attempt to cancel.  In the event of a network timeout, 
     317             :  * a failed operation necessarily means the connection becomes unusable, because no cancellation dialog was 
     318             :  * concluded with the server.  
     319             :  *
     320             :  * It is the client library's duty to call the error handler installed by the application, if any, and to interpret the
     321             :  * installed handler's return code.  It may return to this function one of the above codes only.  This function will not 
     322             :  * check the return code because there's nothing that can be done here except abort.  It is merely passed to the 
     323             :  * calling function, which will (we hope) DTRT.  
     324             :  *
     325             :  * \param tds_ctx       points to a TDSCONTEXT structure
     326             :  * \param tds           the connection structure, may be NULL if not connected
     327             :  * \param msgno         an enumerated libtds msgno, cf. tds.h
     328             :  * \param errnum        the OS errno, if it matters, else zero
     329             :  * 
     330             :  * \returns client library function's return code
     331             :  */
     332             : int
     333       41378 : tdserror(const TDSCONTEXT *tds_ctx, TDSSOCKET *tds, int msgno, int errnum)
     334             : {
     335       41378 :         return tds_report_error(tds_ctx, tds, tds_find_error(msgno), msgno, errnum, NULL);
     336             : }
     337             : 
     338             : /**
     339             :  * \brief Call the client library's error handler for a message with placeholders.
     340             :  *
     341             :  * Same as tdserror() but the error message is used as a format for the additional arguments.
     342             :  * If the message cannot be formatted, the handler receives the message with the placeholders.
     343             :  *
     344             :  * \param tds_ctx       points to a TDSCONTEXT structure
     345             :  * \param tds           the connection structure, may be NULL if not connected
     346             :  * \param msgno         an enumerated libtds msgno, cf. tds.h
     347             :  * \param errnum        the OS errno, if it matters, else zero
     348             :  * \returns same as tdserror()
     349             :  */
     350             : int
     351          10 : tdserror_args(const TDSCONTEXT *tds_ctx, TDSSOCKET *tds, int msgno, int errnum, ...)
     352             : {
     353          10 :         const TDS_ERROR_MESSAGE *err = tds_find_error(msgno);
     354          10 :         char *message = NULL;
     355             :         va_list ap;
     356             :         int rc;
     357             : 
     358          10 :         va_start(ap, errnum);
     359          10 :         if (vasprintf(&message, err->msgtext, ap) < 0)
     360           0 :                 message = NULL;
     361          10 :         va_end(ap);
     362             : 
     363          10 :         rc = tds_report_error(tds_ctx, tds, err, msgno, errnum, message);
     364          10 :         free(message);
     365          10 :         return rc;
     366             : }
     367             : 
     368             : /** Look up an error message, returns a generic one if not found. */
     369             : static const TDS_ERROR_MESSAGE *
     370             : tds_find_error(int msgno)
     371             : {
     372             :         const TDS_ERROR_MESSAGE *err;
     373             : 
     374       65512 :         for (err = tds_error_messages; err->msgno != TDSEOK; ++err) {
     375       65512 :                 if (err->msgno == msgno)
     376             :                         break;
     377             :         }
     378             :         return err;
     379             : }
     380             : 
     381             : /**
     382             :  * Call the client library's error handler.
     383             :  * \param message allocated text of the message, still owned by the caller, NULL to use the text in \a err
     384             :  */
     385             : static int
     386       41388 : tds_report_error(const TDSCONTEXT *tds_ctx, TDSSOCKET *tds, const TDS_ERROR_MESSAGE *err, int msgno, int errnum, char *message)
     387             : {
     388             : #if 0
     389             :         static const char int_exit_text[] = "FreeTDS: libtds: exiting because client error handler returned %d for msgno %d\n";
     390             :         static const char int_invalid_text[] = "%s (%d) received from client library error handler for nontimeout for error %d."
     391             :                 "  Treating as INT_EXIT\n";
     392             : #endif
     393             :         TDSMESSAGE msg;
     394       41388 :         int rc = TDS_INT_CANCEL;
     395             : 
     396       41388 :         tdsdump_log(TDS_DBG_FUNC, "tdserror(%p, %p, %d, %d)\n", tds_ctx, tds, msgno, errnum);
     397             : 
     398       41388 :         CHECK_CONTEXT_EXTRA(tds_ctx);
     399             : 
     400       41388 :         if (tds)
     401       41388 :                 CHECK_TDS_EXTRA(tds);
     402             : 
     403       41388 :         if (tds_ctx && tds_ctx->err_handler) {
     404       41334 :                 memset(&msg, 0, sizeof(TDSMESSAGE));
     405       41334 :                 msg.msgno = msgno;
     406       41334 :                 msg.severity = err->severity;
     407       41334 :                 msg.state = -1;
     408       41334 :                 msg.server = "OpenClient";
     409       41334 :                 msg.line_number = -1;
     410       41334 :                 msg.message = message ? message : (TDS_CHAR *) err->msgtext;
     411       41334 :                 msg.message_allocated = (message != NULL);
     412       41334 :                 msg.sql_state = tds_alloc_client_sqlstate(msgno);
     413             : 
     414       41334 :                 msg.oserr = errnum;
     415             : 
     416             :                 /*
     417             :                  * Call client library handler.
     418             :                  * The client library must return a valid code.  It is not checked again here.
     419             :                  */
     420       41334 :                 rc = tds_ctx->err_handler(tds_ctx, tds, &msg);
     421       41334 :                 tdsdump_log(TDS_DBG_FUNC, "tdserror: client library returned %s(%d)\n", retname(rc), rc);
     422             : 
     423       41334 :                 TDS_ZERO_FREE(msg.sql_state);
     424             :         } else {
     425             :                 static const char msg[] = "tdserror: client library not called because either "
     426             :                                           "tds_ctx (%p) or tds_ctx->err_handler is NULL\n";
     427          54 :                 tdsdump_log(TDS_DBG_ERROR, msg, tds_ctx);
     428             :         }
     429             : 
     430             :   
     431       41388 :         assert(msgno == TDSETIME || rc != TDS_INT_TIMEOUT);   /* client library should prevent */
     432       41388 :         assert(msgno == TDSETIME || rc != TDS_INT_CONTINUE);  /* client library should prevent */
     433             : 
     434       41388 :         if (msgno != TDSETIME && rc != TDS_INT_CANCEL) {
     435           0 :                 tdsdump_log(TDS_DBG_SEVERE, "exit: %s(%d) valid only for TDSETIME\n", retname(rc), rc);
     436             :                 rc = TDS_INT_CANCEL;
     437             :         }
     438             : 
     439       41388 :         if (rc == TDS_INT_TIMEOUT) {
     440         196 :                 tds_send_cancel(tds);
     441         196 :                 rc = TDS_INT_CONTINUE;
     442             :         } 
     443             : 
     444       41388 :         tdsdump_log(TDS_DBG_FUNC, "tdserror: returning %s(%d)\n", retname(rc), rc);
     445             :   
     446       41388 :         return rc;
     447             : }
     448             : 

Generated by: LCOV version 1.13