NCBI C++ ToolKit
net.c
Go to the documentation of this file.
00001 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
00002  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Brian Bruns
00003  * Copyright (C) 2004, 2005  Ziglio Frediano
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public
00016  * License along with this library; if not, write to the
00017  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018  * Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #if HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif /* HAVE_CONFIG_H */
00024 
00025 #if TIME_WITH_SYS_TIME
00026 # if HAVE_SYS_TIME_H
00027 #  include <sys/time.h>
00028 # endif
00029 # include <time.h>
00030 #else
00031 # if HAVE_SYS_TIME_H
00032 #  include <sys/time.h>
00033 # else
00034 #  include <time.h>
00035 # endif
00036 #endif
00037 
00038 #ifdef NCBI_OS_MSWIN
00039 #  include <windows.h>
00040 #endif
00041 
00042 #if HAVE_SYS_TYPES_H
00043 #include <sys/types.h>
00044 #endif /* HAVE_SYS_TYPES_H */
00045 
00046 #if HAVE_ERRNO_H
00047 #include <errno.h>
00048 #endif /* HAVE_ERRNO_H */
00049 
00050 #include <stdio.h>
00051 
00052 #if HAVE_UNISTD_H
00053 #include <unistd.h>
00054 #endif /* HAVE_UNISTD_H */
00055 
00056 #if HAVE_STDLIB_H
00057 #include <stdlib.h>
00058 #endif /* HAVE_STDLIB_H */
00059 
00060 #if HAVE_STRING_H
00061 #include <string.h>
00062 #endif /* HAVE_STRING_H */
00063 
00064 #if HAVE_SYS_SOCKET_H
00065 #include <sys/socket.h>
00066 #endif /* HAVE_SYS_SOCKET_H */
00067 
00068 #if HAVE_NETINET_IN_H
00069 #include <netinet/in.h>
00070 #endif /* HAVE_NETINET_IN_H */
00071 
00072 #if HAVE_NETINET_TCP_H
00073 #include <netinet/tcp.h>
00074 #endif /* HAVE_NETINET_TCP_H */
00075 
00076 #if HAVE_ARPA_INET_H
00077 #include <arpa/inet.h>
00078 #endif /* HAVE_ARPA_INET_H */
00079 
00080 #if HAVE_SYS_IOCTL_H
00081 #include <sys/ioctl.h>
00082 #endif /* HAVE_SYS_IOCTL_H */
00083 
00084 #if HAVE_SELECT_H
00085 #include <sys/select.h>
00086 #endif /* HAVE_SELECT_H */
00087 
00088 #include "tds.h"
00089 #include "tdsstring.h"
00090 #include "replacements.h"
00091 
00092 #include <signal.h>
00093 #include <assert.h>
00094 
00095 #ifdef NCBI_FTDS_ALLOW_TDS_80
00096 #ifdef HAVE_GNUTLS
00097 #include <gnutls/gnutls.h>
00098 #elif defined(HAVE_OPENSSL)
00099 #include <openssl/ssl.h>
00100 #endif
00101 #endif
00102 
00103 #ifdef DMALLOC
00104 #include <dmalloc.h>
00105 #endif
00106 
00107 TDS_RCSID(var, "$Id: net.c 53287 2012-03-06 18:21:30Z ivanovp $");
00108 
00109 /**
00110  * \addtogroup network
00111  * \@{
00112  */
00113 
00114 #if !defined(SOL_TCP) && defined(IPPROTO_TCP)
00115 #define SOL_TCP IPPROTO_TCP
00116 #endif
00117 
00118 /* Optimize the way we send packets */
00119 #undef USE_MSGMORE
00120 #undef USE_CORK
00121 #undef USE_NODELAY
00122 /* On Linux 2.4.x we can use MSG_MORE */
00123 #if defined(__linux__) && defined(MSG_MORE)
00124 #define USE_MSGMORE 1
00125 /* On early Linux use TCP_CORK if available */
00126 #elif defined(__linux__) && defined(TCP_CORK)
00127 #define USE_CORK 1
00128 /* On *BSD try to use TCP_CORK */
00129 /*
00130  * NOPUSH flag do not behave in the same way
00131  * cf ML "FreeBSD 5.0 performance problems with TCP_NOPUSH"
00132  */
00133 #elif (defined(__FreeBSD__) || defined(__GNU_FreeBSD__) || defined(__OpenBSD__)) && defined(TCP_CORK)
00134 #define USE_CORK 1
00135 /* otherwise use NODELAY */
00136 #elif defined(TCP_NODELAY) && defined(SOL_TCP)
00137 #define USE_NODELAY 1
00138 /* under VMS we have to define TCP_NODELAY */
00139 #elif defined(__VMS)
00140 #define TCP_NODELAY 1
00141 #define USE_NODELAY 1
00142 #endif
00143 
00144 
00145 #define NCBI_INCLUDE_STRERROR_C
00146 #include "ncbi_strerror.c"
00147 
00148 
00149 static void
00150 tds_report_error(const TDSCONTEXT* tds_ctx, TDSSOCKET* tds, int x_errno, int msgno, const char* msg)
00151 {
00152     const char* str_err = s_StrError(x_errno);
00153     char err_msg[4096];
00154 
00155     snprintf(err_msg, sizeof(err_msg), "%s (%i, %s).", msg, x_errno, str_err);
00156     tds_client_msg(tds_ctx, tds, msgno, 6, 0, 0, err_msg);
00157 
00158     UTIL_ReleaseBuffer(str_err);
00159 }
00160 
00161 
00162 int
00163 tds_open_socket(TDSSOCKET * tds, const char *ip_addr, unsigned int port, int timeout)
00164 {
00165     struct sockaddr_in sin;
00166     fd_set wfds, efds;
00167 #if !defined(DOS32X)
00168     unsigned long ioctl_blocking = 1;
00169     time_t start, now;
00170     struct timeval selecttimeout;
00171     int retval;
00172 #endif
00173     int len;
00174     int x_errno;
00175     char ip[20];
00176 #if defined(DOS32X) || defined(WIN32)
00177     int optlen;
00178 #else
00179     socklen_t optlen;
00180 #endif
00181 
00182     sin.sin_addr.s_addr = inet_addr(ip_addr);
00183     if (sin.sin_addr.s_addr == INADDR_NONE) {
00184         tdsdump_log(TDS_DBG_ERROR, "inet_addr() failed, IP = %s\n", ip_addr);
00185         return TDS_FAIL;
00186     }
00187 
00188     sin.sin_family = AF_INET;
00189     sin.sin_port = htons(port);
00190 
00191     tdsdump_log(TDS_DBG_INFO1, "Connecting to %s port %d.\n", tds_inet_ntoa_r(sin.sin_addr, ip, sizeof(ip)), ntohs(sin.sin_port));
00192 
00193     if (TDS_IS_SOCKET_INVALID(tds->s = socket(AF_INET, SOCK_STREAM, 0))) {
00194         tds_report_error(tds->tds_ctx, tds, sock_errno, 20008, "Unable to open socket");
00195         return TDS_FAIL;
00196     }
00197 
00198 #ifdef SO_KEEPALIVE
00199     len = 1;
00200     setsockopt(tds->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len));
00201 #endif
00202 
00203     len = 1;
00204 #if defined(USE_NODELAY) || defined(USE_MSGMORE)
00205     setsockopt(tds->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len));
00206 #elif defined(USE_CORK)
00207     if (setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &len, sizeof(len)) < 0)
00208         setsockopt(tds->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len));
00209 #else
00210 #error One should be defined
00211 #endif
00212 
00213 #ifdef  DOS32X          /* the other connection doesn't work  on WATTCP32 */
00214     if (connect(tds->s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
00215         char *message;
00216 
00217         if (asprintf(&message, "src/tds/login.c: tds_connect: %s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)) >= 0) {
00218             perror(message);
00219             free(message);
00220         }
00221         tds_client_msg(tds->tds_ctx, tds, 20009, 9, 0, 0, "Server is unavailable or does not exist.");
00222         tds_free_socket(tds);
00223         return TDS_FAIL;
00224     }
00225 #else
00226     /* Jeff's hack *** START OF NEW CODE *** */
00227     if (!timeout)
00228         /* I don't think anybody complains... */
00229         timeout = 90000;
00230 
00231     start = time(NULL);
00232 
00233     /* enable no-blocking mode */
00234     ioctl_blocking = 1;
00235     if (IOCTLSOCKET(tds->s, FIONBIO, &ioctl_blocking) < 0) {
00236         tds_close_socket(tds);
00237         return TDS_FAIL;
00238     }
00239 
00240     retval = connect(tds->s, (struct sockaddr *) &sin, sizeof(sin));
00241     x_errno = sock_errno;
00242     if (retval < 0 && x_errno == TDSSOCK_EINPROGRESS)
00243         retval = 0;
00244     /* if retval < 0 (error) fall through */
00245 
00246     /* Select on writeability for connect_timeout */
00247     now = start;
00248     while ((retval == 0) && ((now - start) < timeout)) {
00249         FD_ZERO(&wfds);
00250         FD_SET(tds->s, &wfds);
00251         FD_ZERO(&efds);
00252         FD_SET(tds->s, &efds);
00253         selecttimeout.tv_sec = 1;
00254         selecttimeout.tv_usec = 0;
00255         retval = select(tds->s + 1, NULL, &wfds, &efds, &selecttimeout);
00256         x_errno = sock_errno;
00257         /* on interrupt ignore */
00258         if (retval < 0 && x_errno == TDSSOCK_EINTR)
00259             retval = 0;
00260         now = time(NULL);
00261     }
00262 
00263     if ((now - start) >= timeout) {
00264         tds_close_socket(tds);
00265         tds_client_msg(tds->tds_ctx, tds, 20015, 6, 0, 0, "SQL Server connection timed out.");
00266         return TDS_FAIL;
00267     }
00268 #endif
00269     /* END OF NEW CODE */
00270 
00271     /* check socket error */
00272     if (retval > 0  &&  FD_ISSET(tds->s, &efds)) {
00273         optlen = sizeof(x_errno);
00274         if (getsockopt(tds->s, SOL_SOCKET, SO_ERROR, (char *) &x_errno, &optlen) != 0) {
00275             tds_report_error(tds->tds_ctx, tds, sock_errno, 20007, "Error in getsockopt");
00276             tds_close_socket(tds);
00277             return TDS_FAIL;
00278         }
00279         retval = -1;
00280     }
00281     if (retval < 0) {
00282         tds_report_error(tds->tds_ctx, tds, x_errno, 20009, "Server is unavailable or does not exist");
00283         tds_close_socket(tds);
00284         return TDS_FAIL;
00285     }
00286 
00287     return TDS_SUCCEED;
00288 }
00289 
00290 int
00291 tds_close_socket(TDSSOCKET * tds)
00292 {
00293     int rc = -1;
00294 
00295     if (!IS_TDSDEAD(tds)) {
00296         rc = CLOSESOCKET(tds->s);
00297         tds->s = INVALID_SOCKET;
00298         tds_set_state(tds, TDS_DEAD);
00299     }
00300     return rc;
00301 }
00302 
00303 /* Function from CStopWatch */
00304 static double
00305 GetTimeMark(void)
00306 {
00307 #if defined(NCBI_OS_MSWIN)
00308     /* For Win32, we use QueryPerformanceCounter() */
00309 
00310     LARGE_INTEGER bigint;
00311     static double freq;
00312     static int first = 1;
00313 
00314     if ( first ) {
00315         LARGE_INTEGER nfreq;
00316         QueryPerformanceFrequency(&nfreq);
00317         freq  = (double)nfreq.QuadPart;
00318         first = 0;
00319     }
00320 
00321     if ( !QueryPerformanceCounter(&bigint) ) {
00322         return 0.0;
00323     }
00324     return (double)bigint.QuadPart / freq;
00325 
00326 #else
00327     /* For Unixes, we use gettimeofday() */
00328 
00329     struct timeval time;
00330     if ( gettimeofday (&time, 0) ) {
00331         return 0.0;
00332     }
00333     return (double)time.tv_sec + (double)time.tv_usec / 1e6;
00334 #endif
00335 }
00336 
00337 /**
00338  * Loops until we have received buflen characters
00339  * return -1 on failure
00340  */
00341 static int
00342 tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen, unsigned char unfinished)
00343 {
00344     double start, global_start;
00345     int got = 0;
00346     fd_set rfds, efds;
00347     struct timeval tv;
00348     int canceled = 0;
00349     int retval, x_errno;
00350 #if defined(DOS32X) || defined(WIN32)
00351     int optlen;
00352 #else
00353     socklen_t optlen;
00354 #endif
00355 
00356     if (buf == NULL || buflen < 1 || IS_TDSDEAD(tds))
00357         return 0;
00358 
00359     global_start = start = GetTimeMark();
00360 
00361     while (buflen > 0) {
00362         int len;
00363         double now;
00364 
00365         if (IS_TDSDEAD(tds))
00366             return -1;
00367 
00368         FD_ZERO(&rfds);
00369         FD_SET(tds->s, &rfds);
00370         FD_ZERO(&efds);
00371         FD_SET(tds->s, &efds);
00372 
00373         /* tv structure is modified inside select() on Linux, so we need to
00374            reinitialize it every time. */
00375         tv.tv_usec = 0;
00376         tv.tv_sec = 1;
00377 
00378         retval = select(tds->s + 1, &rfds, NULL, &efds, &tv);
00379         x_errno = sock_errno;
00380         len = 0;
00381         if (retval > 0) {
00382             if (FD_ISSET(tds->s, &efds)) {
00383                 optlen = sizeof(x_errno);
00384                 if (getsockopt(tds->s, SOL_SOCKET, SO_ERROR, (char *) &x_errno, &optlen) != 0) {
00385                     tds_report_error(tds->tds_ctx, tds, sock_errno, 20016, "Error in getsockopt");
00386                     tds_close_socket(tds);
00387                     return -1;
00388                 }
00389                 retval = -1;
00390             }
00391             else if (FD_ISSET(tds->s, &rfds)) {
00392 #ifndef MSG_NOSIGNAL
00393                 retval = READSOCKET(tds->s, buf + got, buflen);
00394 #else
00395                 retval = recv(tds->s, buf + got, buflen, MSG_NOSIGNAL);
00396 #endif
00397                 /* detect connection close */
00398                 if (retval == 0) {
00399                     tds_client_msg(tds->tds_ctx, tds, 20011, 6, 0, 0, "EOF in the socket.");
00400                     tds_close_socket(tds);
00401                     return -1;
00402                 }
00403                 len = retval;
00404             }
00405         }
00406 
00407         if (retval < 0) {
00408             switch(x_errno) {
00409             case TDSSOCK_EINTR:
00410             case EAGAIN:
00411             case TDSSOCK_EINPROGRESS:
00412                 break;
00413             default:
00414                 tds_report_error(tds->tds_ctx, tds, x_errno, 20012, "select/recv finished with error");
00415                 return -1;
00416             }
00417         }
00418 
00419         buflen -= len;
00420         got += len;
00421 
00422         now = GetTimeMark();
00423         if (tds->query_timeout > 0 && now - start >= tds->query_timeout) {
00424 
00425             int timeout_action = TDS_INT_CONTINUE;
00426 
00427             if (canceled)
00428                 return got;
00429 
00430             if (tds->query_timeout_func && tds->query_timeout)
00431                 timeout_action = (*tds->query_timeout_func) (tds->query_timeout_param, (int)(now - global_start));
00432 
00433             switch (timeout_action) {
00434             case TDS_INT_EXIT:
00435                 exit(EXIT_FAILURE);
00436                 break;
00437             case TDS_INT_CANCEL:
00438                 tds_send_cancel(tds);
00439                 canceled = 1;
00440                 /* fall through to wait while cancelling happens */
00441             case TDS_INT_CONTINUE:
00442                 start = now;
00443             default:
00444                 break;
00445             }
00446         }
00447 
00448         if (unfinished && got)
00449             return got;
00450     }
00451     return got;
00452 }
00453 
00454 static int
00455 goodread(TDSSOCKET * tds, unsigned char *buf, int buflen)
00456 {
00457 #ifdef NCBI_FTDS_ALLOW_TDS_80
00458 #ifdef HAVE_GNUTLS
00459     if (tds->tls_session)
00460         return gnutls_record_recv(tds->tls_session, buf, buflen);
00461 #elif defined(HAVE_OPENSSL)
00462     if (tds->tls_session)
00463         return SSL_read((SSL*) tds->tls_session, buf, buflen);
00464 #endif
00465 #endif
00466     return tds_goodread(tds, buf, buflen, 0);
00467 }
00468 
00469 /**
00470  * Read in one 'packet' from the server.  This is a wrapped outer packet of
00471  * the protocol (they bundle result packets into chunks and wrap them at
00472  * what appears to be 512 bytes regardless of how that breaks internal packet
00473  * up.   (tetherow\@nol.org)
00474  * @return bytes read or -1 on failure
00475  */
00476 int
00477 tds_read_packet(TDSSOCKET * tds)
00478 {
00479     unsigned char header[8];
00480     int len;
00481     int x = 0, have, need;
00482 
00483     if (IS_TDSDEAD(tds)) {
00484         tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD");
00485         return -1;
00486     }
00487 
00488     /*
00489      * Read in the packet header.  We use this to figure out our packet
00490      * length
00491      */
00492 
00493     /*
00494      * Cast to int are needed because some compiler seem to convert
00495      * len to unsigned (as FreeBSD 4.5 one)
00496      */
00497     if ((len = goodread(tds, header, sizeof(header))) < (int) sizeof(header)) {
00498         /* GW ADDED */
00499         if (len < 0) {
00500             tds_client_msg(tds->tds_ctx, tds, 20004, 6, 0, 0, "Read from SQL server failed.");
00501             tds_close_socket(tds);
00502             tds->in_len = 0;
00503             tds->in_pos = 0;
00504             return -1;
00505         }
00506 
00507         /* GW ADDED */
00508         /*
00509          * Not sure if this is the best way to do the error
00510          * handling here but this is the way it is currently
00511          * being done.
00512          */
00513 
00514         tds->in_len = 0;
00515         tds->in_pos = 0;
00516         tds->last_packet = 1;
00517         if (tds->state != TDS_IDLE && len == 0) {
00518             tds_close_socket(tds);
00519         }
00520         return -1;
00521     }
00522     tdsdump_dump_buf(TDS_DBG_NETWORK, "Received header", header, sizeof(header));
00523 
00524 #if 0
00525     /*
00526      * Note:
00527      * this was done by Gregg, I don't think its the real solution (it breaks
00528      * under 5.0, but I haven't gotten a result big enough to test this yet.
00529      */
00530     if (IS_TDS42(tds)) {
00531         if (header[0] != 0x04 && header[0] != 0x0f) {
00532             tdsdump_log(TDS_DBG_ERROR, "Invalid packet header %d\n", header[0]);
00533             /*
00534              * Not sure if this is the best way to do the error
00535              * handling here but this is the way it is currently
00536              * being done.
00537              */
00538             tds->in_len = 0;
00539             tds->in_pos = 0;
00540             tds->last_packet = 1;
00541             return (-1);
00542         }
00543     }
00544 #endif
00545 
00546     /* Convert our packet length from network to host byte order */
00547     len = ((((unsigned int) header[2]) << 8) | header[3]) - 8;
00548     need = len;
00549 
00550     /*
00551      * If this packet size is the largest we have gotten allocate
00552      * space for it
00553      */
00554     if ((unsigned int)len > tds->in_buf_max) {
00555         unsigned char *p;
00556 
00557         if (!tds->in_buf) {
00558             p = (unsigned char *) malloc(len);
00559         } else {
00560             p = (unsigned char *) realloc(tds->in_buf, len);
00561         }
00562         if (!p)
00563             return -1;  /* FIXME should close socket too */
00564         tds->in_buf = p;
00565         /* Set the new maximum packet size */
00566         tds->in_buf_max = len;
00567     }
00568 
00569     /* Clean out the in_buf so we don't use old stuff by mistake */
00570     memset(tds->in_buf, 0, tds->in_buf_max);
00571 
00572     /* Now get exactly how many bytes the server told us to get */
00573     have = 0;
00574     while (need > 0) {
00575         if ((x = goodread(tds, tds->in_buf + have, need)) < 1) {
00576             /*
00577              * Not sure if this is the best way to do the error
00578              * handling here but this is the way it is currently
00579              * being done.
00580              */
00581             tds->in_len = 0;
00582             tds->in_pos = 0;
00583             tds->last_packet = 1;
00584             /* FIXME should this be "if (x == 0)" ? */
00585             if (len == 0) {
00586                 tds_close_socket(tds);
00587             }
00588             return (-1);
00589         }
00590         have += x;
00591         need -= x;
00592     }
00593     if (x < 1) {
00594         /*
00595          * Not sure if this is the best way to do the error handling
00596          * here but this is the way it is currently being done.
00597          */
00598         tds->in_len = 0;
00599         tds->in_pos = 0;
00600         tds->last_packet = 1;
00601         /* return 0 if header found but no payload */
00602         return len ? -1 : 0;
00603     }
00604 
00605     /* Set the last packet flag */
00606     if (header[1]) {
00607         tds->last_packet = 1;
00608     } else {
00609         tds->last_packet = 0;
00610     }
00611 
00612     /* set the received packet type flag */
00613     tds->in_flag = header[0];
00614 
00615     /* Set the length and pos (not sure what pos is used for now */
00616     tds->in_len = have;
00617     tds->in_pos = 0;
00618     tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len);
00619 
00620     return (tds->in_len);
00621 }
00622 
00623 /* goodwrite function adapted from patch by freddy77 */
00624 static int
00625 tds_goodwrite(TDSSOCKET * tds, const unsigned char *p, int len, unsigned char last)
00626 {
00627     int retval = 0;
00628     double start, now;
00629     fd_set wfds, efds;
00630     struct timeval tv;
00631     int left = len;
00632     int x_errno;
00633 #if defined(DOS32X) || defined(WIN32)
00634     int optlen;
00635 #else
00636     socklen_t optlen;
00637 #endif
00638 
00639     while (left > 0) {
00640         if (IS_TDSDEAD(tds))
00641             return -1;
00642 
00643         FD_ZERO(&wfds);
00644         FD_SET(tds->s, &wfds);
00645         FD_ZERO(&efds);
00646         FD_SET(tds->s, &efds);
00647 
00648         /* tv structure is modified inside select() on Linux, so we need to
00649            reinitialize it every time. */
00650         tv.tv_usec = 0;
00651         tv.tv_sec = 1;
00652 
00653         start = GetTimeMark();
00654         now = start;
00655 
00656         retval = select(tds->s + 1, NULL, &wfds, &efds, &tv);
00657         x_errno = sock_errno;
00658         if (retval > 0) {
00659             if (FD_ISSET(tds->s, &efds)) {
00660                 optlen = sizeof(x_errno);
00661                 if (getsockopt(tds->s, SOL_SOCKET, SO_ERROR, (char *) &x_errno, &optlen) != 0) {
00662                     tds_report_error(tds->tds_ctx, tds, sock_errno, 20001, "Error in getsockopt");
00663                     tds_close_socket(tds);
00664                     return -1;
00665                 }
00666                 retval = -1;
00667             }
00668             else if (FD_ISSET(tds->s, &wfds)) {
00669 #ifdef USE_MSGMORE
00670                 retval = send(tds->s, p, left, last ? MSG_NOSIGNAL : MSG_NOSIGNAL|MSG_MORE);
00671 #elif !defined(MSG_NOSIGNAL)
00672                 retval = WRITESOCKET(tds->s, p, left);
00673 #else
00674                 retval = send(tds->s, p, left, MSG_NOSIGNAL);
00675 #endif
00676                 x_errno = sock_errno;
00677                 /* detect connection close */
00678                 if (retval <= 0) {
00679                     if (!retval || (x_errno != TDSSOCK_EINTR
00680                                     &&  x_errno != EAGAIN
00681                                     &&  x_errno != TDSSOCK_EINPROGRESS))
00682                     {
00683                         tds_report_error(tds->tds_ctx, tds, x_errno, 20017, "Write to SQL Server failed");
00684                         tds->in_pos = 0;
00685                         tds->in_len = 0;
00686                         tds_close_socket(tds);
00687                         return -1;
00688                     }
00689                     retval = 0;
00690                 }
00691             }
00692         }
00693 
00694         if (retval < 0) {
00695             switch(x_errno) {
00696             case TDSSOCK_EINTR:
00697             case EAGAIN:
00698             case TDSSOCK_EINPROGRESS:
00699                 break;
00700             default:
00701                 tds_report_error(tds->tds_ctx, tds, x_errno, 20005, "select/send finished with error");
00702                 return -1;
00703             }
00704         }
00705 
00706         left -= retval;
00707         p += retval;
00708 
00709         now = GetTimeMark();
00710         if (tds->query_timeout  &&  (now - start) >= tds->query_timeout) {
00711             tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout");
00712             tds_close_socket(tds);
00713             return -1;
00714         }
00715     }
00716 
00717 #ifdef USE_CORK
00718     /* force packet flush */
00719     if (last) {
00720         int opt;
00721         opt = 0;
00722         setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt));
00723         opt = 1;
00724         setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt));
00725     }
00726 #endif
00727 
00728     return len;
00729 }
00730 
00731 int
00732 tds_write_packet(TDSSOCKET * tds, unsigned char final)
00733 {
00734     int sent;
00735     unsigned int left = 0;
00736 
00737 #if !defined(WIN32) && !defined(MSG_NOSIGNAL) && !defined(DOS32X)
00738     void (*oldsig) (int);
00739 #endif
00740 
00741 #if TDS_ADDITIONAL_SPACE != 0
00742     if (tds->out_pos > tds->env.block_size) {
00743         left = tds->out_pos - tds->env.block_size;
00744         tds->out_pos = tds->env.block_size;
00745     }
00746 #endif
00747 
00748     tds->out_buf[0] = tds->out_flag;
00749     tds->out_buf[1] = final;
00750     tds->out_buf[2] = (tds->out_pos) / 256u;
00751     tds->out_buf[3] = (tds->out_pos) % 256u;
00752     if (IS_TDS7_PLUS(tds) && !tds->connection)
00753         tds->out_buf[6] = 0x01;
00754 
00755     tdsdump_dump_buf(TDS_DBG_NETWORK, "Sending packet", tds->out_buf, tds->out_pos);
00756 
00757 #if !defined(WIN32) && !defined(MSG_NOSIGNAL) && !defined(DOS32X)
00758     oldsig = signal(SIGPIPE, SIG_IGN);
00759     if (oldsig == SIG_ERR) {
00760         tdsdump_log(TDS_DBG_WARN, "TDS: Warning: Couldn't set SIGPIPE signal to be ignored\n");
00761     }
00762 #endif
00763 
00764 #ifdef NCBI_FTDS_ALLOW_TDS_80
00765 #ifdef HAVE_GNUTLS
00766     if (tds->tls_session)
00767         sent = gnutls_record_send(tds->tls_session, tds->out_buf, tds->out_pos);
00768     else
00769 #elif defined(HAVE_OPENSSL)
00770     if (tds->tls_session)
00771         sent = SSL_write((SSL*) tds->tls_session, tds->out_buf, tds->out_pos);
00772     else
00773 #endif
00774 #endif
00775         sent = tds_goodwrite(tds, tds->out_buf, tds->out_pos, final);
00776 
00777 #if !defined(WIN32) && !defined(MSG_NOSIGNAL) && !defined(DOS32X)
00778     if (signal(SIGPIPE, oldsig) == SIG_ERR) {
00779         tdsdump_log(TDS_DBG_WARN, "TDS: Warning: Couldn't reset SIGPIPE signal to previous value\n");
00780     }
00781 #endif
00782 
00783 #if TDS_ADDITIONAL_SPACE != 0
00784     memcpy(tds->out_buf + 8, tds->out_buf + tds->env.block_size, left);
00785 #endif
00786     tds->out_pos = left + 8;
00787 
00788     /* GW added in check for write() returning <0 and SIGPIPE checking */
00789     return sent <= 0 ? TDS_FAIL : TDS_SUCCEED;
00790 }
00791 
00792 /**
00793  * Get port of given instance
00794  * @return port number or 0 if error
00795  */
00796 int
00797 tds7_get_instance_port(const char *ip_addr, const char *instance)
00798 {
00799     int num_try;
00800     struct sockaddr_in sin;
00801     unsigned long ioctl_blocking = 1;
00802     struct timeval selecttimeout;
00803     fd_set fds;
00804     int retval;
00805     TDS_SYS_SOCKET s;
00806     char msg[1024];
00807     size_t msg_len;
00808     int port = 0;
00809 
00810     sin.sin_addr.s_addr = inet_addr(ip_addr);
00811     if (sin.sin_addr.s_addr == INADDR_NONE) {
00812         tdsdump_log(TDS_DBG_ERROR, "inet_addr() failed, IP = %s\n", ip_addr);
00813         return 0;
00814     }
00815 
00816     sin.sin_family = AF_INET;
00817     sin.sin_port = htons(1434);
00818 
00819     /* create an UDP socket */
00820     if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_DGRAM, 0))) {
00821         tdsdump_log(TDS_DBG_ERROR, "socket creation error: %s\n", strerror(sock_errno));
00822         return 0;
00823     }
00824 
00825     /*
00826      * on cluster environment is possible that reply packet came from
00827      * different IP so do not filter by ip with connect
00828      */
00829 
00830     ioctl_blocking = 1;
00831     if (IOCTLSOCKET(s, FIONBIO, &ioctl_blocking) < 0) {
00832         CLOSESOCKET(s);
00833         return 0;
00834     }
00835 
00836     /* TODO is there a way to see if server reply with an ICMP (port not available) ?? */
00837 
00838     /* try to get port */
00839     for (num_try = 0; num_try < 16; ++num_try) {
00840         /* request instance information */
00841         msg[0] = 4;
00842         tds_strlcpy(msg + 1, instance, sizeof(msg) - 1);
00843         sendto(s, msg, strlen(msg) + 1, 0, (struct sockaddr *) &sin, sizeof(sin));
00844 
00845         FD_ZERO(&fds);
00846         FD_SET(s, &fds);
00847         selecttimeout.tv_sec = 1;
00848         selecttimeout.tv_usec = 0;
00849         retval = select(s + 1, &fds, NULL, NULL, &selecttimeout);
00850         tdsdump_log(TDS_DBG_INFO1, "select: retval %d err %d\n", retval, sock_errno);
00851         /* on interrupt ignore */
00852         if (retval == 0 || (retval < 0 && sock_errno == TDSSOCK_EINTR))
00853             continue;
00854         if (retval < 0)
00855             break;
00856 
00857         /* TODO pass also connection and set instance/servername ?? */
00858 
00859         /* got data, read and parse */
00860         if ((msg_len = recv(s, msg, sizeof(msg) - 1, 0)) > 3 && msg[0] == 5) {
00861             char *p;
00862             long l = 0;
00863             int instance_ok = 0, port_ok = 0;
00864 
00865             /* assure null terminated */
00866             msg[msg_len] = 0;
00867             tdsdump_dump_buf(TDS_DBG_INFO1, "instance info", msg, msg_len);
00868 
00869             /*
00870              * parse message and check instance name and port
00871              * we don't check servername cause it can be very
00872              * different from client one
00873              */
00874             p = msg + 3;
00875             for (;;) {
00876                 char *name, *value;
00877 
00878                 name = p;
00879                 p = strchr(p, ';');
00880                 if (!p)
00881                     break;
00882                 *p++ = 0;
00883 
00884                 value = p;
00885                 p = strchr(p, ';');
00886                 if (!p)
00887                     break;
00888                 *p++ = 0;
00889 
00890                 if (strcasecmp(name, "InstanceName") == 0) {
00891                     if (strcasecmp(value, instance) != 0)
00892                         break;
00893                     instance_ok = 1;
00894                 } else if (strcasecmp(name, "tcp") == 0) {
00895                     l = strtol(value, &p, 10);
00896                     if (l > 0 && l <= 0xffff && *p == 0)
00897                         port_ok = 1;
00898                 }
00899             }
00900             if (port_ok && instance_ok) {
00901                 port = l;
00902                 break;
00903             }
00904         }
00905     }
00906     CLOSESOCKET(s);
00907     return port;
00908 }
00909 
00910 #ifdef NCBI_FTDS_ALLOW_TDS_80
00911 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
00912 
00913 #ifdef HAVE_GNUTLS
00914 static ssize_t
00915 tds_pull_func(gnutls_transport_ptr ptr, void* data, size_t len)
00916 {
00917     TDSSOCKET *tds = (TDSSOCKET *) ptr;
00918 #else
00919 static int
00920 tds_ssl_read(BIO *b, char* data, int len)
00921 {
00922     TDSSOCKET *tds = (TDSSOCKET *) b->ptr;
00923 #endif
00924 
00925     int have;
00926 
00927     tdsdump_log(TDS_DBG_INFO1, "in tds_pull_func\n");
00928     
00929     /* if we have some data send it */
00930     if (tds->out_pos > 8)
00931         tds_flush_packet(tds);
00932 
00933     if (tds->tls_session) {
00934         /* read directly from socket */
00935         return tds_goodread(tds, data, len, 1);
00936     }
00937 
00938     for(;;) {
00939         have = tds->in_len - tds->in_pos;
00940         tdsdump_log(TDS_DBG_INFO1, "have %d\n", have);
00941         assert(have >= 0);
00942         if (have > 0)
00943             break;
00944         tdsdump_log(TDS_DBG_INFO1, "before read\n");
00945         if (tds_read_packet(tds) < 0)
00946             return -1;
00947         tdsdump_log(TDS_DBG_INFO1, "after read\n");
00948     }
00949     if (len > have)
00950         len = have;
00951     tdsdump_log(TDS_DBG_INFO1, "read %d bytes\n", len);
00952     memcpy(data, tds->in_buf + tds->in_pos, len);
00953     tds->in_pos += len;
00954     return len;
00955 }
00956 
00957 #ifdef HAVE_GNUTLS
00958 static ssize_t
00959 tds_push_func(gnutls_transport_ptr ptr, const void* data, size_t len)
00960 {
00961     TDSSOCKET *tds = (TDSSOCKET *) ptr;
00962 #else
00963 static int
00964 tds_ssl_write(BIO *b, const char* data, int len)
00965 {
00966     TDSSOCKET *tds = (TDSSOCKET *) b->ptr;
00967 #endif
00968     tdsdump_log(TDS_DBG_INFO1, "in tds_push_func\n");
00969 
00970     if (tds->tls_session) {
00971         /* write to socket directly */
00972         return tds_goodwrite(tds, data, len, 1);
00973     }
00974     tds_put_n(tds, data, len);
00975     return len;
00976 }
00977 
00978 #ifdef HAVE_GNUTLS
00979 
00980 static void
00981 tds_tls_log( int level, const char* s)
00982 {
00983     tdsdump_log(TDS_DBG_INFO1, "GNUTLS: level %d:\n  %s", level, s);
00984 }
00985 
00986 static int tls_initialized = 0;
00987 
00988 #ifdef TDS_ATTRIBUTE_DESTRUCTOR
00989 static void __attribute__((destructor))
00990 tds_tls_deinit(void)
00991 {
00992     if (tls_initialized)
00993         gnutls_global_deinit();
00994 }
00995 #endif
00996 
00997 int
00998 tds_ssl_init(TDSSOCKET *tds)
00999 {
01000     gnutls_session session;
01001     gnutls_certificate_credentials xcred;
01002 
01003     static const int kx_priority[] = {
01004         GNUTLS_KX_RSA_EXPORT,
01005         GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA,
01006         0
01007     };
01008     static const int cipher_priority[] = {
01009         GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC,
01010         GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR_128,
01011 #if 0
01012         GNUTLS_CIPHER_ARCFOUR_40,
01013         GNUTLS_CIPHER_DES_CBC,
01014 #endif
01015         0
01016     };
01017     static const int comp_priority[] = { GNUTLS_COMP_NULL, 0 };
01018     static const int mac_priority[] = {
01019         GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0
01020     };
01021     int ret;
01022     const char *tls_msg;
01023 
01024     xcred = NULL;
01025     session = NULL; 
01026     tls_msg = "initializing tls";
01027 
01028     /* FIXME place somewhere else, deinit at end */
01029     ret = 0;
01030     if (!tls_initialized)
01031         ret = gnutls_global_init();
01032     if (ret == 0) {
01033         tls_initialized = 1;
01034 
01035         gnutls_global_set_log_level(11);
01036         gnutls_global_set_log_function(tds_tls_log);
01037         tls_msg = "allocating credentials";
01038         ret = gnutls_certificate_allocate_credentials(&xcred);
01039     }
01040 
01041     if (ret == 0) {
01042         /* Initialize TLS session */
01043         tls_msg = "initializing session";
01044         ret = gnutls_init(&session, GNUTLS_CLIENT);
01045     }
01046     
01047     if (ret == 0) {
01048         gnutls_transport_set_ptr(session, tds);
01049         gnutls_transport_set_pull_function(session, tds_pull_func);
01050         gnutls_transport_set_push_function(session, tds_push_func);
01051 
01052         /* NOTE: there functions return int however they cannot fail */
01053 
01054         /* use default priorities... */
01055         gnutls_set_default_priority(session);
01056 
01057         /* ... but overwrite some */
01058         gnutls_cipher_set_priority(session, cipher_priority);
01059         gnutls_compression_set_priority(session, comp_priority);
01060         gnutls_kx_set_priority(session, kx_priority);
01061         gnutls_mac_set_priority(session, mac_priority);
01062         
01063         /* put the anonymous credentials to the current session */
01064         tls_msg = "setting credential";
01065         ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
01066     }
01067     
01068     if (ret == 0) {
01069         /* Perform the TLS handshake */
01070         tls_msg = "handshake";
01071         ret = gnutls_handshake (session);
01072     }
01073 
01074     if (ret != 0) {
01075         if (session)
01076             gnutls_deinit(session);
01077         if (xcred)
01078             gnutls_certificate_free_credentials(xcred);
01079         tdsdump_log(TDS_DBG_ERROR, "%s failed: %s\n", tls_msg, gnutls_strerror (ret));
01080         return TDS_FAIL;
01081     }
01082 
01083     tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n");
01084     tds->tls_session = session;
01085     tds->tls_credentials = xcred;
01086 
01087     return TDS_SUCCEED;
01088 }
01089 
01090 void
01091 tds_ssl_deinit(TDSSOCKET *tds)
01092 {
01093     if (tds->tls_session) {
01094         gnutls_deinit(tds->tls_session);
01095         tds->tls_session = NULL;
01096     }
01097     if (tds->tls_credentials) {
01098         gnutls_certificate_free_credentials(tds->tls_credentials);
01099         tds->tls_credentials = NULL;
01100     }
01101 }
01102 
01103 #else
01104 static long
01105 tds_ssl_ctrl(BIO *b, int cmd, long num, void *ptr)
01106 {
01107     TDSSOCKET *tds = (TDSSOCKET *) b->ptr;
01108 
01109     switch (cmd) {
01110     case BIO_CTRL_FLUSH:
01111         if (tds->out_pos > 8)
01112             tds_flush_packet(tds);
01113         return 1;
01114     }
01115     return 0;
01116 }
01117 
01118 static int
01119 tds_ssl_free(BIO *a)
01120 {
01121     /* nothing to do but required */
01122     return 1;
01123 }
01124 
01125 static BIO_METHOD tds_method =
01126 {
01127     BIO_TYPE_MEM,
01128     "tds",
01129     tds_ssl_write,
01130     tds_ssl_read,
01131     NULL,
01132     NULL,
01133     tds_ssl_ctrl,
01134     NULL,
01135     tds_ssl_free,
01136     NULL,
01137 };
01138 
01139 static SSL_CTX *ssl_ctx;
01140 
01141 static int
01142 tds_init_openssl(void)
01143 {
01144     SSL_METHOD *meth;
01145 
01146     SSL_library_init ();
01147     meth = TLSv1_client_method ();
01148     if (meth == NULL)
01149         return 1;
01150     ssl_ctx = SSL_CTX_new (meth);
01151     if (ssl_ctx == NULL)
01152         return 1;
01153     return 0;
01154 }
01155 
01156 #ifdef TDS_ATTRIBUTE_DESTRUCTOR
01157 static void __attribute__((destructor))
01158 tds_tls_deinit(void)
01159 {
01160     if (ssl_ctx)
01161         SSL_CTX_free (ssl_ctx);
01162 }
01163 #endif
01164 
01165 int
01166 tds_ssl_init(TDSSOCKET *tds)
01167 {
01168 #define OPENSSL_CIPHERS \
01169     SSL3_TXT_RSA_DES_64_CBC_SHA " " \
01170     TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_SHA " " \
01171     TLS1_TXT_RSA_EXPORT1024_WITH_DES_CBC_SHA " " \
01172     SSL3_TXT_RSA_RC4_40_MD5 " " \
01173     SSL3_TXT_RSA_RC2_40_MD5 " " \
01174     SSL3_TXT_EDH_DSS_DES_64_CBC_SHA " " \
01175     TLS1_TXT_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA
01176 
01177     SSL *con;
01178     BIO *b;
01179 
01180     int ret;
01181     const char *tls_msg;
01182 
01183     con = NULL;
01184     b = NULL;
01185     tls_msg = "initializing tls";
01186 
01187     /* FIXME place somewhere else, deinit at end */
01188     ret = 0;
01189     if (!ssl_ctx)
01190         ret = tds_init_openssl();
01191 
01192     if (ret == 0) {
01193         /* Initialize TLS session */
01194         tls_msg = "initializing session";
01195         con = SSL_new(ssl_ctx);
01196     }
01197     
01198     if (con) {
01199         tls_msg = "creating bio";
01200         b = BIO_new(&tds_method);
01201     }
01202 
01203     ret = 0;
01204     if (b) {
01205         b->shutdown=1;
01206         b->init=1;
01207         b->num= -1;
01208         b->ptr = tds;
01209         SSL_set_bio(con, b, b);
01210 
01211         /* use priorities... */
01212         SSL_set_cipher_list(con, OPENSSL_CIPHERS);
01213 
01214         /* Perform the TLS handshake */
01215         tls_msg = "handshake";
01216         SSL_set_connect_state(con);
01217         ret = SSL_connect(con) != 1 || con->state != SSL_ST_OK;
01218     }
01219 
01220     if (ret != 0) {
01221         if (con) {
01222             SSL_shutdown(con);
01223             SSL_free(con);
01224         }
01225         tdsdump_log(TDS_DBG_ERROR, "%s failed\n", tls_msg);
01226         return TDS_FAIL;
01227     }
01228 
01229     tdsdump_log(TDS_DBG_INFO1, "handshake succeeded!!\n");
01230     tds->tls_session = con;
01231     tds->tls_credentials = NULL;
01232 
01233     return TDS_SUCCEED;
01234 }
01235 
01236 void
01237 tds_ssl_deinit(TDSSOCKET *tds)
01238 {
01239     if (tds->tls_session) {
01240         /* NOTE do not call SSL_shutdown here */
01241         SSL_free(tds->tls_session);
01242         tds->tls_session = NULL;
01243     }
01244 }
01245 #endif
01246 
01247 #endif
01248 #endif
01249 /** \@} */
01250 
Modified on Wed May 23 12:54:55 2012 by modify_doxy.py rev. 337098