📄 net.c
字号:
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Brian Bruns * Copyright (C) 2004, 2005, 2006, 2007 Ziglio Frediano * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */#if HAVE_CONFIG_H#include <config.h>#endif /* HAVE_CONFIG_H */#include <stdarg.h>#include <stdio.h>#if TIME_WITH_SYS_TIME# if HAVE_SYS_TIME_H# include <sys/time.h># endif# include <time.h>#else# if HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#if HAVE_SYS_TYPES_H#include <sys/types.h>#endif /* HAVE_SYS_TYPES_H */#if HAVE_ERRNO_H#include <errno.h>#endif /* HAVE_ERRNO_H */#if HAVE_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#if HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#if HAVE_STRING_H#include <string.h>#endif /* HAVE_STRING_H */#if HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif /* HAVE_SYS_SOCKET_H */#if HAVE_NETINET_IN_H#include <netinet/in.h>#endif /* HAVE_NETINET_IN_H */#if HAVE_NETINET_TCP_H#include <netinet/tcp.h>#endif /* HAVE_NETINET_TCP_H */#if HAVE_ARPA_INET_H#include <arpa/inet.h>#endif /* HAVE_ARPA_INET_H */#if HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif /* HAVE_SYS_IOCTL_H */#if HAVE_SELECT_H#include <sys/select.h>#endif /* HAVE_SELECT_H */#if HAVE_POLL_H#include <poll.h>#endif /* HAVE_POLL_H */#include "tds.h"#include "tdsstring.h"#include "replacements.h"#include <signal.h>#include <assert.h>#ifdef HAVE_GNUTLS#include <gnutls/gnutls.h>#elif defined(HAVE_OPENSSL)#include <openssl/ssl.h>#endif#ifdef DMALLOC#include <dmalloc.h>#endifTDS_RCSID(var, "$Id: net.c,v 1.71.2.1 2008/01/12 00:21:39 freddy77 Exp $");#undef USE_POLL#if defined(HAVE_POLL_H) && defined(HAVE_POLL)# define USE_POLL 1# define TDSSELREAD POLLIN# define TDSSELWRITE POLLOUT/* error is always returned */# define TDSSELERR 0#else# define USE_POLL 0# define TDSSELREAD 1# define TDSSELWRITE 2# define TDSSELERR 4#endifstatic int tds_select(TDSSOCKET * tds, unsigned tds_sel, int timeout_seconds);/** * \addtogroup network * @{ */#ifdef WIN32int_tds_socket_init(void){ WSADATA wsadata; return WSAStartup(MAKEWORD(1, 1), &wsadata);}void_tds_socket_done(void){ WSACleanup();}#endif#if !defined(SOL_TCP) && defined(IPPROTO_TCP)#define SOL_TCP IPPROTO_TCP#endif/* Optimize the way we send packets */#undef USE_MSGMORE#undef USE_CORK#undef USE_NODELAY/* On Linux 2.4.x we can use MSG_MORE */#if defined(__linux__) && defined(MSG_MORE)#define USE_MSGMORE 1/* On early Linux use TCP_CORK if available */#elif defined(__linux__) && defined(TCP_CORK)#define USE_CORK 1/* On *BSD try to use TCP_CORK *//* * NOPUSH flag do not behave in the same way * cf ML "FreeBSD 5.0 performance problems with TCP_NOPUSH" */#elif (defined(__FreeBSD__) || defined(__GNU_FreeBSD__) || defined(__OpenBSD__)) && defined(TCP_CORK)#define USE_CORK 1/* otherwise use NODELAY */#elif defined(TCP_NODELAY) && defined(SOL_TCP)#define USE_NODELAY 1/* under VMS we have to define TCP_NODELAY */#elif defined(__VMS)#define TCP_NODELAY 1#define USE_NODELAY 1#endif#if !defined(WIN32)typedef unsigned int ioctl_nonblocking_t;#elsetypedef u_long ioctl_nonblocking_t;#endifinttds_open_socket(TDSSOCKET * tds, const char *ip_addr, unsigned int port, int timeout){ struct sockaddr_in sin;#if !defined(DOS32X) ioctl_nonblocking_t ioctl_nonblocking; int retval;#endif int len; int tds_error = TDSECONN; char ip[20];#if defined(DOS32X) || defined(WIN32) int optlen;#else socklen_t optlen;#endif memset(&sin, 0, sizeof(sin)); sin.sin_addr.s_addr = inet_addr(ip_addr); if (sin.sin_addr.s_addr == INADDR_NONE) { tdsdump_log(TDS_DBG_ERROR, "inet_addr() failed, IP = %s\n", ip_addr); return TDS_FAIL; } sin.sin_family = AF_INET; sin.sin_port = htons(port); tdsdump_log(TDS_DBG_INFO1, "Connecting to %s port %d (TDS version %d.%d)\n", tds_inet_ntoa_r(sin.sin_addr, ip, sizeof(ip)), ntohs(sin.sin_port), tds->major_version, tds->minor_version); if (TDS_IS_SOCKET_INVALID(tds->s = socket(AF_INET, SOCK_STREAM, 0))) { tdserror(tds->tds_ctx, tds, TDSESOCK, 0); tdsdump_log(TDS_DBG_ERROR, "socket creation error: %s\n", strerror(sock_errno)); return TDS_FAIL; }#ifdef SO_KEEPALIVE len = 1; setsockopt(tds->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len));#endif len = 1;#if defined(USE_NODELAY) || defined(USE_MSGMORE) setsockopt(tds->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len));#elif defined(USE_CORK) if (setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &len, sizeof(len)) < 0) setsockopt(tds->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len));#else#error One should be defined#endif#ifdef DOS32X /* the other connection doesn't work on WATTCP32 */ if (connect(tds->s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { char *message; if (asprintf(&message, "tds_open_socket(): %s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)) >= 0) { perror(message); free(message); } tds_close_socket(tds); tdserror(tds->tds_ctx, tds, TDSECONN, 0); return TDS_FAIL; }#else if (!timeout) { /* A timeout of zero means wait forever; 90,000 seconds will feel like forever. */ timeout = 90000; } /* enable non-blocking mode */ ioctl_nonblocking = 1; if (IOCTLSOCKET(tds->s, FIONBIO, &ioctl_nonblocking) < 0) { tds_close_socket(tds); return TDS_FAIL; } retval = connect(tds->s, (struct sockaddr *) &sin, sizeof(sin)); if (retval == 0) { tdsdump_log(TDS_DBG_INFO2, "connection established\n"); } else { tdsdump_log(TDS_DBG_ERROR, "tds_open_socket: connect(2) returned \"%s\"\n", strerror(sock_errno));#if DEBUGGING_CONNECTING_PROBLEM if (sock_errno != ECONNREFUSED && sock_errno != ENETUNREACH && sock_errno != EINPROGRESS) { tdsdump_dump_buf(TDS_DBG_ERROR, "Contents of sockaddr_in", &sin, sizeof(sin)); tdsdump_log(TDS_DBG_ERROR, " sockaddr_in:\t" "%s = %x\n" "\t\t\t%s = %x\n" "\t\t\t%s = %x\n" "\t\t\t%s = '%s'\n" , "sin_family", sin.sin_family , "sin_port", sin.sin_port , "sin_addr.s_addr", sin.sin_addr.s_addr , "(param ip_addr)", ip_addr ); }#endif if (sock_errno != TDSSOCK_EINPROGRESS) goto not_available; if (tds_select(tds, TDSSELWRITE|TDSSELERR, timeout) <= 0) { tds_error = TDSESOCK; goto not_available; } }#endif /* check socket error */ optlen = sizeof(len); len = 0; if (getsockopt(tds->s, SOL_SOCKET, SO_ERROR, (char *) &len, &optlen) != 0) { tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) failed: %s\n", strerror(sock_errno)); goto not_available; } if (len != 0) { tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) reported: %s\n", strerror(len)); goto not_available; } tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() succeeded\n"); return TDS_SUCCEED; not_available: tds_close_socket(tds); tdserror(tds->tds_ctx, tds, tds_error, sock_errno); tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() failed\n"); return TDS_FAIL;}inttds_close_socket(TDSSOCKET * tds){ int rc = -1; if (!IS_TDSDEAD(tds)) { rc = CLOSESOCKET(tds->s); tds->s = INVALID_SOCKET; tds_set_state(tds, TDS_DEAD); if (-1 == rc) tdserror(tds->tds_ctx, tds, TDSECLOS, sock_errno); } return rc;}/** * Select on a socket until it's available or the timeout expires. * Meanwhile, call the interrupt function. * \return >0 ready descriptors * 0 timeout * <0 error (cf. errno). Caller should close socket and return failure. * This function does not call tdserror or close the socket because it can't know the context in which it's being called. */static inttds_select(TDSSOCKET * tds, unsigned tds_sel, int timeout_seconds){ int rc, seconds; unsigned int poll_seconds;#if !USE_POLL fd_set fds[3]; fd_set *readfds = NULL, *writefds = NULL, *exceptfds = NULL;#endif assert(tds != NULL); assert(timeout_seconds >= 0);#if !USE_POLL#if !defined(WIN32) && defined(FD_SETSIZE) if (tds->s >= FD_SETSIZE) { sock_errno = EINVAL; return -1; }#endif if ((tds_sel & TDSSELREAD) != 0) { FD_ZERO(&fds[0]); readfds = &fds[0]; } if ((tds_sel & TDSSELWRITE) != 0) { FD_ZERO(&fds[1]); writefds = &fds[1]; } if ((tds_sel & TDSSELERR) != 0) { FD_ZERO(&fds[2]); exceptfds = &fds[2]; }#endif /* * The select loop. * If an interrupt handler is installed, we iterate once per second, * else we try once, timing out after timeout_seconds (0 == never). * If select(2) is interrupted by a signal (e.g. press ^C in sqsh), we timeout. * (The application can retry if desired by installing a signal handler.) * * We do not measure current time against end time, to avoid being tricked by ntpd(8) or similar. * Instead, we just count down. * * We exit on the first of these events: * 1. a descriptor is ready. (return to caller) * 2. select(2) returns an important error. (return to caller) * A timeout of zero says "wait forever". We do that by passing a NULL timeval pointer to select(2). */ poll_seconds = (tds->tds_ctx && tds->tds_ctx->int_handler)? 1 : timeout_seconds; for (seconds = timeout_seconds; timeout_seconds == 0 || seconds > 0; seconds -= poll_seconds) {#if USE_POLL struct pollfd fd; int timeout = poll_seconds ? poll_seconds * 1000 : -1; fd.fd = tds->s; fd.events = tds_sel; fd.revents = 0; rc = poll(&fd, 1, timeout);#else struct timeval tv, *ptv = poll_seconds? &tv : NULL; tv.tv_sec = poll_seconds; tv.tv_usec = 0; if (readfds) FD_SET(tds->s, readfds); if (writefds) FD_SET(tds->s, writefds); if (exceptfds) FD_SET(tds->s, exceptfds); rc = select(tds->s + 1, readfds, writefds, exceptfds, ptv); #endif if (rc > 0 ) { return rc; } if (rc < 0) { switch (sock_errno) { case TDSSOCK_EINTR: break; /* let interrupt handler be called */ default: /* documented: EFAULT, EBADF, EINVAL */ tdsdump_log(TDS_DBG_ERROR, "error: select(2) returned 0x%x, \"%s\"\n", sock_errno, strerror(sock_errno)); return rc; } } assert(rc == 0 || (rc < 0 && sock_errno == TDSSOCK_EINTR)); if (tds->tds_ctx && tds->tds_ctx->int_handler) { /* interrupt handler installed */ /* * "If hndlintr() returns INT_CANCEL, DB-Library sends an attention token [TDS_BUFSTAT_ATTN] * to the server. This causes the server to discontinue command processing. * The server may send additional results that have already been computed. * When control returns to the mainline code, the mainline code should do * one of the following: * - Flush the results using dbcancel * - Process the results normally" */ int timeout_action = (*tds->tds_ctx->int_handler) (tds->parent);#if 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -