📄 socket.c
字号:
/*------- * Module: socket.c * * Description: This module contains functions for low level socket * operations (connecting/reading/writing to the backend) * * Classes: SocketClass (Functions prefix: "SOCK_") * * API functions: none * * Comments: See "notice.txt" for copyright and license information. *------- */#include <libpq-fe.h>#include <openssl/ssl.h>#include "socket.h"#include "loadlib.h"#include "connection.h"#ifndef WIN32#include <stdlib.h>#include <string.h> /* for memset */#ifdef TIME_WITH_SYS_TIME#include <sys/time.h>#include <time.h>#else#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#else#include <time.h>#endif /* HAVE_SYS_TIME_H */#endif /* TIME_WITH__SYS_TIME */#endif /* WIN32 */extern GLOBAL_VALUES globals;static void SOCK_set_error(SocketClass *s, int _no, const char *_msg){ int gerrno = SOCK_ERRNO; s->errornumber = _no; if (NULL != s->_errormsg_) free(s->_errormsg_); if (NULL != _msg) s->_errormsg_ = strdup(_msg); else s->_errormsg_ = NULL; mylog("(%d)%s ERRNO=%d\n", _no, _msg, gerrno);}voidSOCK_clear_error(SocketClass *self){ self->errornumber = 0; if (NULL != self->_errormsg_) free(self->_errormsg_); self->_errormsg_ = NULL;}SocketClass *SOCK_Constructor(const ConnectionClass *conn){ SocketClass *rv; rv = (SocketClass *) malloc(sizeof(SocketClass)); if (rv != NULL) { rv->socket = (SOCKETFD) -1; rv->via_libpq = FALSE; rv->ssl = NULL; rv->pqconn = NULL; rv->pversion = 0; rv->reslen = 0; rv->buffer_filled_in = 0; rv->buffer_filled_out = 0; rv->buffer_read_in = 0; if (conn) rv->buffer_size = conn->connInfo.drivers.socket_buffersize; else rv->buffer_size = globals.socket_buffersize; rv->buffer_in = (UCHAR *) malloc(rv->buffer_size); if (!rv->buffer_in) { free(rv); return NULL; } rv->buffer_out = (UCHAR *) malloc(rv->buffer_size); if (!rv->buffer_out) { free(rv->buffer_in); free(rv); return NULL; } rv->_errormsg_ = NULL; rv->errornumber = 0; rv->reverse = FALSE; } return rv;}voidSOCK_Destructor(SocketClass *self){ mylog("SOCK_Destructor\n"); if (!self) return; if (self->socket != (SOCKETFD) -1) { if (self->pqconn) { if (self->via_libpq) { PQfinish(self->pqconn); /* UnloadDelayLoadedDLLs(NULL != self->ssl); */ } self->via_libpq = FALSE; self->pqconn = NULL; self->ssl = NULL; } else { SOCK_put_char(self, 'X'); if (PG_PROTOCOL_74 == self->pversion) SOCK_put_int(self, 4, 4); SOCK_flush_output(self); closesocket(self->socket); } } if (self->buffer_in) free(self->buffer_in); if (self->buffer_out) free(self->buffer_out); if (self->_errormsg_) free(self->_errormsg_); free(self);}#if defined(_MSC_VER) && (_MSC_VER < 1300)static freeaddrinfo_func freeaddrinfo_ptr = NULL;static getaddrinfo_func getaddrinfo_ptr = NULL;static getnameinfo_func getnameinfo_ptr = NULL;static HMODULE ws2_hnd = NULL;#elsestatic freeaddrinfo_func freeaddrinfo_ptr = freeaddrinfo;static getaddrinfo_func getaddrinfo_ptr = getaddrinfo;static getnameinfo_func getnameinfo_ptr = getnameinfo;#endif /* _MSC_VER */static BOOL format_sockerr(char *errmsg, size_t buflen, int errnum, const char *cmd, const char *host, int portno){ BOOL ret = FALSE;#ifdef WIN32 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errnum, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), errmsg, (DWORD)buflen, NULL)) ret = TRUE;#else#if defined(POSIX_MULTITHREAD_SUPPORT) && defined(HAVE_STRERROR_R)#ifdef STRERROR_R_INT if (0 == strerror_r(errnum, errmsg, buflen)) ret = TRUE;#else const char *pchar; pchar = (const char *) strerror_r(errnum, errmsg, buflen); if (NULL != pchar) { if (pchar != errmsg) strncpy(errmsg, pchar, buflen); ret = TRUE; }#endif /* STRERROR_R_INT */#else strncpy(errmsg, strerror(errnum), buflen); ret = TRUE;#endif /* POSIX_MULTITHREAD_SUPPORT */#endif /* WIN32 */ if (ret) { size_t tlen = strlen(errmsg); errmsg += tlen; buflen -= tlen; snprintf(errmsg, buflen, " [%s:%d]", host, portno); } else snprintf(errmsg, buflen, "%s failed for [%s:%d] ", cmd, host, portno); return ret;} charSOCK_connect_to(SocketClass *self, unsigned short port, char *hostname, long timeout){ struct addrinfo rest, *addrs = NULL, *curadr = NULL; int family = 0; char retval = 0; int gerrno; if (self->socket != (SOCKETFD) -1) { SOCK_set_error(self, SOCKET_ALREADY_CONNECTED, "Socket is already connected"); return 0; }#if defined(_MSC_VER) && (_MSC_VER < 1300) if (ws2_hnd == NULL) ws2_hnd = GetModuleHandle("ws2_32.dll"); if (freeaddrinfo_ptr == NULL) freeaddrinfo_ptr = (freeaddrinfo_func)GetProcAddress(ws2_hnd, "freeaddrinfo"); if (getaddrinfo_ptr == NULL) getaddrinfo_ptr = (getaddrinfo_func)GetProcAddress(ws2_hnd, "getaddrinfo"); if (getnameinfo_ptr == NULL) getnameinfo_ptr = (getnameinfo_func)GetProcAddress(ws2_hnd, "getnameinfo"); #endif /* * Hostname lookup. */ if (hostname && hostname[0]#ifndef WIN32 && '/' != hostname[0]#endif /* WIN32 */ ) { char portstr[16]; int ret; memset(&rest, 0, sizeof(rest)); rest.ai_socktype = SOCK_STREAM; rest.ai_family = AF_UNSPEC; snprintf(portstr, sizeof(portstr), "%d", port); if (inet_addr(hostname) != INADDR_NONE) rest.ai_flags |= AI_NUMERICHOST; ret = getaddrinfo_ptr(hostname, portstr, &rest, &addrs); if (ret || !addrs) { SOCK_set_error(self, SOCKET_HOST_NOT_FOUND, "Could not resolve hostname."); if (addrs) freeaddrinfo_ptr(addrs); return 0; } curadr = addrs; } else#ifdef HAVE_UNIX_SOCKETS { struct sockaddr_un *un = (struct sockaddr_un *) &(self->sadr_area); family = un->sun_family = AF_UNIX; /* passing NULL or '' means pg default "/tmp" */ UNIXSOCK_PATH(un, port, hostname); self->sadr_len = UNIXSOCK_LEN(un); }#else { SOCK_set_error(self, SOCKET_HOST_NOT_FOUND, "Hostname isn't specified."); return 0; }#endif /* HAVE_UNIX_SOCKETS */retry: if (curadr) family = curadr->ai_family; self->socket = socket(family, SOCK_STREAM, 0); if (self->socket == (SOCKETFD) -1) { SOCK_set_error(self, SOCKET_COULD_NOT_CREATE_SOCKET, "Could not create Socket."); return 0; }#ifdef TCP_NODELAY if (family != AF_UNIX) { int i; socklen_t len; i = 1; len = sizeof(i); if (setsockopt(self->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, len) < 0) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not set socket to NODELAY."); closesocket(self->socket); self->socket = (SOCKETFD) -1; return 0; } }#endif /* TCP_NODELAY */#ifdef WIN32 { long ioctlsocket_ret = 1; /* Returns non-0 on failure, while fcntl() returns -1 on failure */ ioctlsocket(self->socket, FIONBIO, &ioctlsocket_ret); }#else fcntl(self->socket, F_SETFL, O_NONBLOCK);#endif if (curadr) { struct sockaddr *in = (struct sockaddr *) &(self->sadr_area); memset((char *) in, 0, sizeof(self->sadr_area)); memcpy(in, curadr->ai_addr, curadr->ai_addrlen); self->sadr_len = (int) curadr->ai_addrlen; } if (connect(self->socket, (struct sockaddr *) &(self->sadr_area), self->sadr_len) < 0) { int ret, optval; fd_set fds, except_fds; struct timeval tm; socklen_t optlen = sizeof(optval); time_t t_now, t_finish = 0; BOOL tm_exp = FALSE; gerrno = SOCK_ERRNO; switch (gerrno) { case 0: case EINPROGRESS: case EINTR: case EWOULDBLOCK: break; default: SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect to remote socket immedaitely"); goto cleanup; } if (timeout > 0) { t_now = time(NULL); t_finish = t_now + timeout; tm.tv_sec = timeout; tm.tv_usec = 0; } do { FD_ZERO(&fds); FD_ZERO(&except_fds); FD_SET(self->socket, &fds); FD_SET(self->socket, &except_fds); ret = select((int) self->socket + 1, NULL, &fds, &except_fds, timeout > 0 ? &tm : NULL); gerrno = SOCK_ERRNO; if (0 < ret) break; else if (0 == ret) tm_exp = TRUE; else if (EINTR != gerrno) break; else if (timeout > 0) { if (t_now = time(NULL), t_now >= t_finish) tm_exp = TRUE; else { tm.tv_sec = (long) (t_finish - t_now); tm.tv_usec = 0; } } } while (!tm_exp); if (tm_exp) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect .. timeout occured."); goto cleanup; } else if (0 > ret) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect .. select error occured."); mylog("select error ret=%d ERROR=%d\n", ret, gerrno); goto cleanup; } if (getsockopt(self->socket, SOL_SOCKET, SO_ERROR, (char *) &optval, &optlen) == -1) { SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, "Could not connect .. getsockopt error."); } else if (optval != 0) { char errmsg[256], host[64]; host[0] = '\0'; getnameinfo_ptr((struct sockaddr *) &(self->sadr_area), self->sadr_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); /* snprintf(errmsg, sizeof(errmsg), "connect getsockopt val %d addr=%s\n", optval, host); */ format_sockerr(errmsg, sizeof(errmsg), optval, "connect", host, port); mylog(errmsg); SOCK_set_error(self, SOCKET_COULD_NOT_CONNECT, errmsg); } else retval = 1; } else retval = 1;cleanup: if (0 == retval) { if (self->socket >= 0) { closesocket(self->socket); self->socket = (SOCKETFD) -1; } if (curadr && curadr->ai_next) { curadr = curadr->ai_next; goto retry; } } else SOCK_set_error(self, 0, NULL); if (addrs) freeaddrinfo_ptr(addrs); return retval;}/* * To handle EWOULDBLOCK etc (mainly for libpq non-blocking connection). */#define MAX_RETRY_COUNT 30static int SOCK_wait_for_ready(SocketClass *sock, BOOL output, int retry_count){ int ret, gerrno; fd_set fds, except_fds; struct timeval tm; BOOL no_timeout = (0 != retry_count && (retry_count < 0 || (!sock->ssl))); do { FD_ZERO(&fds); FD_ZERO(&except_fds); FD_SET(sock->socket, &fds); FD_SET(sock->socket, &except_fds); if (!no_timeout) { tm.tv_sec = retry_count; tm.tv_usec = 0; } ret = select((int)sock->socket + 1, output ? NULL : &fds, output ? &fds : NULL, &except_fds, no_timeout ? NULL : &tm); gerrno = SOCK_ERRNO; } while (ret < 0 && EINTR == gerrno); if (retry_count < 0) retry_count *= -1; if (0 == ret && retry_count > MAX_RETRY_COUNT) { ret = -1; SOCK_set_error(sock, output ? SOCKET_WRITE_TIMEOUT : SOCKET_READ_TIMEOUT, "SOCK_wait_for_ready timeout");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -