void tds_wakeup_close(TDSPOLLWAKEUP *wakeup) { if (!TDS_IS_SOCKET_INVALID(wakeup->s_signal)) CLOSESOCKET(wakeup->s_signal); if (!TDS_IS_SOCKET_INVALID(wakeup->s_signaled)) CLOSESOCKET(wakeup->s_signaled); }
/* build a listening socket to connect to */ static int init_fake_server(int ip_port) { struct sockaddr_in sin; TDS_SYS_SOCKET s; int err; memset(&sin, 0, sizeof(sin)); sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons((short) ip_port); sin.sin_family = AF_INET; if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) { perror("socket"); exit(1); } if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); CLOSESOCKET(s); return 1; } listen(s, 5); err = tds_thread_create(&fake_thread, fake_thread_proc, int2ptr(s)); if (err != 0) { perror("tds_thread_create"); exit(1); } return 0; }
/* accept a socket and read data as much as you can */ static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg) { TDS_SYS_SOCKET s = ptr2int(arg), sock; socklen_t len; char buf[128]; struct sockaddr_in sin; struct pollfd fd; memset(&sin, 0, sizeof(sin)); len = sizeof(sin); fd.fd = s; fd.events = POLLIN; fd.revents = 0; if (poll(&fd, 1, 30000) <= 0) { perror("poll"); exit(1); } if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) { perror("accept"); exit(1); } tds_mutex_lock(&mtx); fake_sock = sock; tds_mutex_unlock(&mtx); CLOSESOCKET(s); for (;;) { int len; fd.fd = sock; fd.events = POLLIN; fd.revents = 0; if (poll(&fd, 1, 30000) <= 0) { perror("poll"); exit(1); } /* just read and discard */ len = READSOCKET(sock, buf, sizeof(buf)); if (len == 0) break; if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) break; } return NULL; }
void tds_connection_close(TDSCONNECTION *conn) { unsigned n = 0; if (!TDS_IS_SOCKET_INVALID(conn->s)) { /* TODO check error ?? how to return it ?? */ CLOSESOCKET(conn->s); conn->s = INVALID_SOCKET; } tds_mutex_lock(&conn->list_mtx); for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(conn->sessions[n])) tds_set_state(conn->sessions[n], TDS_DEAD); tds_mutex_unlock(&conn->list_mtx); }
static void pool_socket_init(TDS_POOL * pool) { struct sockaddr_in sin; TDS_SYS_SOCKET s, event_pair[2]; int socktrue = 1; /* FIXME -- read the interfaces file and bind accordingly */ sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(pool->port); sin.sin_family = AF_INET; if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) { perror("socket"); exit(1); } tds_socket_set_nonblocking(s); /* don't keep addr in use from [email protected] */ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &socktrue, sizeof(socktrue)); fprintf(stderr, "Listening on port %d\n", pool->port); if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } listen(s, 5); pool->listen_fd = s; if (socketpair(AF_UNIX, SOCK_STREAM, 0, event_pair) < 0) { perror("socketpair"); exit(1); } tds_socket_set_nonblocking(event_pair[0]); tds_socket_set_nonblocking(event_pair[1]); pool->event_fd = event_pair[1]; pool->wakeup_fd = event_pair[0]; }
int tds_socketpair(int domain, int type, int protocol, int sv[2]) { struct sockaddr_in sa, sa2; SOCKLEN_T addrlen; TDS_SYS_SOCKET s; if (!sv) return -1; /* create a listener */ s = socket(AF_INET, type, 0); if (TDS_IS_SOCKET_INVALID(s)) return -1; sv[1] = INVALID_SOCKET; sv[0] = socket(AF_INET, type, 0); if (TDS_IS_SOCKET_INVALID(sv[0])) goto Cleanup; /* bind to a random port */ sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = 0; if (bind(s, (struct sockaddr*) &sa, sizeof(sa)) < 0) goto Cleanup; if (listen(s, 1) < 0) goto Cleanup; /* connect to kernel choosen port */ addrlen = sizeof(sa); if (tds_getsockname(s, (struct sockaddr*) &sa, &addrlen) < 0) goto Cleanup; if (connect(sv[0], (struct sockaddr*) &sa, sizeof(sa)) < 0) goto Cleanup; addrlen = sizeof(sa2); sv[1] = tds_accept(s, (struct sockaddr*) &sa2, &addrlen); if (TDS_IS_SOCKET_INVALID(sv[1])) goto Cleanup; /* check proper connection */ addrlen = sizeof(sa); if (tds_getsockname(sv[0], (struct sockaddr*) &sa, &addrlen) < 0) goto Cleanup; addrlen = sizeof(sa2); if (tds_getpeername(sv[1], (struct sockaddr*) &sa2, &addrlen) < 0) goto Cleanup; if (sa.sin_family != sa2.sin_family || sa.sin_port != sa2.sin_port || sa.sin_addr.s_addr != sa2.sin_addr.s_addr) goto Cleanup; CLOSESOCKET(s); return 0; Cleanup: CLOSESOCKET(s); CLOSESOCKET(sv[0]); CLOSESOCKET(sv[1]); return -1; }
/* * pool_user_create * accepts a client connection and adds it to the users list and returns it */ TDS_POOL_USER * pool_user_create(TDS_POOL * pool, TDS_SYS_SOCKET s) { TDS_POOL_USER *puser; TDS_SYS_SOCKET fd; TDSSOCKET *tds; LOGIN_EVENT *ev; tdsdump_log(TDS_DBG_NETWORK, "accepting connection\n"); if (TDS_IS_SOCKET_INVALID(fd = tds_accept(s, NULL, NULL))) { char *errstr = sock_strerror(errno); tdsdump_log(TDS_DBG_ERROR, "error calling assert :%s\n", errstr); sock_strerror_free(errstr); return NULL; } if (tds_socket_set_nonblocking(fd) != 0) { CLOSESOCKET(fd); return NULL; } puser = pool_user_find_new(pool); if (!puser) { CLOSESOCKET(fd); return NULL; } tds = tds_alloc_socket(pool->ctx, BLOCKSIZ); if (!tds) { CLOSESOCKET(fd); return NULL; } ev = (LOGIN_EVENT *) calloc(1, sizeof(*ev)); if (!ev || TDS_FAILED(tds_iconv_open(tds->conn, "UTF-8", 0))) { free(ev); tds_free_socket(tds); CLOSESOCKET(fd); return NULL; } /* FIX ME - little endian emulation should be config file driven */ tds->conn->emul_little_endian = 1; tds_set_s(tds, fd); tds->state = TDS_IDLE; tds->out_flag = TDS_LOGIN; puser->sock.tds = tds; puser->user_state = TDS_SRV_QUERY; puser->sock.poll_recv = false; puser->sock.poll_send = false; /* launch login asyncronously */ ev->puser = puser; ev->pool = pool; if (tds_thread_create_detached(login_proc, ev) != 0) { pool_free_user(pool, puser); fprintf(stderr, "error creating thread\n"); return NULL; } return puser; }
/** * 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. */ int tds_select(TDSSOCKET * tds, unsigned tds_sel, int timeout_seconds) { int rc, seconds; unsigned int poll_seconds; assert(tds != NULL); assert(timeout_seconds >= 0); /* * 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_get_ctx(tds) && tds_get_ctx(tds)->int_handler)? 1 : timeout_seconds; for (seconds = timeout_seconds; timeout_seconds == 0 || seconds > 0; seconds -= poll_seconds) { struct pollfd fds[2]; int timeout = poll_seconds ? poll_seconds * 1000 : -1; if (TDS_IS_SOCKET_INVALID(tds_get_s(tds))) return -1; if ((tds_sel & TDSSELREAD) != 0 && tds->conn->tls_session && tds_ssl_pending(tds->conn)) return POLLIN; fds[0].fd = tds_get_s(tds); fds[0].events = tds_sel; fds[0].revents = 0; fds[1].fd = tds->conn->s_signaled; fds[1].events = POLLIN; fds[1].revents = 0; rc = poll(fds, 2, timeout); if (rc > 0 ) { if (fds[0].revents & POLLERR) return -1; rc = fds[0].revents; if (fds[1].revents) { #if ENABLE_ODBC_MARS tds_check_cancel(tds->conn); #endif rc |= TDSPOLLURG; } return rc; } if (rc < 0) { char *errstr; switch (sock_errno) { case TDSSOCK_EINTR: /* FIXME this should be global maximun, not loop one */ seconds += poll_seconds; break; /* let interrupt handler be called */ default: /* documented: EFAULT, EBADF, EINVAL */ errstr = sock_strerror(sock_errno); tdsdump_log(TDS_DBG_ERROR, "error: poll(2) returned %d, \"%s\"\n", sock_errno, errstr); sock_strerror_free(errstr); return rc; } } assert(rc == 0 || (rc < 0 && sock_errno == TDSSOCK_EINTR)); if (tds_get_ctx(tds) && tds_get_ctx(tds)->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_get_ctx(tds)->int_handler) (tds_get_parent(tds)); switch (timeout_action) { case TDS_INT_CONTINUE: /* keep waiting */ continue; case TDS_INT_CANCEL: /* abort the current command batch */ /* FIXME tell tds_goodread() not to call tdserror() */ return 0; default: tdsdump_log(TDS_DBG_NETWORK, "tds_select: invalid interupt handler return code: %d\n", timeout_action); return -1; } } /* * We can reach here if no interrupt handler was installed and we either timed out or got EINTR. * We cannot be polling, so we are about to drop out of the loop. */ assert(poll_seconds == timeout_seconds); } return 0; }
static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg) { TDS_SYS_SOCKET s = ptr2int(arg), server_sock; socklen_t sock_len; int len; char buf[128]; struct sockaddr_in sin; fd_set fds_read, fds_write, fds_error; TDS_SYS_SOCKET max_fd = 0; memset(&sin, 0, sizeof(sin)); sock_len = sizeof(sin); alarm(30); fprintf(stderr, "waiting connect...\n"); if ((fake_sock = tds_accept(s, (struct sockaddr *) &sin, &sock_len)) < 0) { perror("accept"); exit(1); } CLOSESOCKET(s); if (TDS_IS_SOCKET_INVALID(server_sock = socket(remote_addr.sa.sa_family, SOCK_STREAM, 0))) { perror("socket"); exit(1); } fprintf(stderr, "connecting to server...\n"); if (remote_addr.sa.sa_family == AF_INET) { fprintf(stderr, "connecting to %x:%d\n", remote_addr.sin.sin_addr.s_addr, ntohs(remote_addr.sin.sin_port)); } if (connect(server_sock, &remote_addr.sa, remote_addr_len)) { perror("connect"); exit(1); } alarm(0); if (fake_sock > max_fd) max_fd = fake_sock; if (server_sock > max_fd) max_fd = server_sock; for (;;) { int res; FD_ZERO(&fds_read); FD_SET(fake_sock, &fds_read); FD_SET(server_sock, &fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error); FD_SET(fake_sock, &fds_error); FD_SET(server_sock, &fds_error); alarm(30); res = select(max_fd + 1, &fds_read, &fds_write, &fds_error, NULL); alarm(0); if (res < 0) { if (sock_errno == TDSSOCK_EINTR) continue; perror("select"); exit(1); } if (FD_ISSET(fake_sock, &fds_error) || FD_ISSET(server_sock, &fds_error)) { fprintf(stderr, "error in select\n"); exit(1); } /* just read and forward */ if (FD_ISSET(fake_sock, &fds_read)) { if (flow != sending) { tds_mutex_lock(&mtx); ++round_trips; tds_mutex_unlock(&mtx); } flow = sending; len = READSOCKET(fake_sock, buf, sizeof(buf)); if (len == 0) { fprintf(stderr, "client connection closed\n"); break; } if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) { fprintf(stderr, "read client error %d\n", sock_errno); break; } count_insert(buf, len); write_all(server_sock, buf, len); } if (FD_ISSET(server_sock, &fds_read)) { if (flow != receiving) { tds_mutex_lock(&mtx); ++round_trips; tds_mutex_unlock(&mtx); } flow = receiving; len = READSOCKET(server_sock, buf, sizeof(buf)); if (len == 0) { fprintf(stderr, "server connection closed\n"); break; } if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) { fprintf(stderr, "read server error %d\n", sock_errno); break; } write_all(fake_sock, buf, len); } } CLOSESOCKET(fake_sock); CLOSESOCKET(server_sock); return NULL; }
/** * Set state of TDS connection, with logging and checking. * \param tds state information for the socket and the TDS protocol * \param state the new state of the connection, cf. TDS_STATE. * \return the new state, which might not be \a state. */ TDS_STATE tds_set_state(TDSSOCKET * tds, TDS_STATE state) { const TDS_STATE prior_state = tds->state; static const char state_names[][10] = { "IDLE", "QUERYING", "PENDING", "READING", "DEAD" }; assert(state < TDS_VECTOR_SIZE(state_names)); assert(tds->state < TDS_VECTOR_SIZE(state_names)); if (state == tds->state) return state; switch(state) { /* transition to READING are valid only from PENDING */ case TDS_PENDING: if (tds->state != TDS_READING && tds->state != TDS_QUERYING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } tds->state = state; break; case TDS_READING: if (tds->state != TDS_PENDING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } tds->state = state; break; case TDS_IDLE: if (tds->state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } case TDS_DEAD: tds->state = state; break; case TDS_QUERYING: CHECK_TDS_EXTRA(tds); if (tds->state == TDS_DEAD) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0); break; } else if (tds->state != TDS_IDLE) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSERPND, 0); break; } /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(tds, tds->cur_cursor); tds->cur_cursor = NULL; tds->internal_sp_called = 0; tds->state = state; break; default: assert(0); break; } tdsdump_log(TDS_DBG_ERROR, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]); CHECK_TDS_EXTRA(tds); return tds->state; }
/** * Set state of TDS connection, with logging and checking. * \param tds state information for the socket and the TDS protocol * \param state the new state of the connection, cf. TDS_STATE. * \return the new state, which might not be \a state. */ TDS_STATE tds_set_state(TDSSOCKET * tds, TDS_STATE state) { TDS_STATE prior_state; static const char state_names[][8] = { "IDLE", "WRITING", "SENDING", "PENDING", "READING", "DEAD" }; assert(state < TDS_VECTOR_SIZE(state_names)); assert(tds->state < TDS_VECTOR_SIZE(state_names)); prior_state = tds->state; if (state == prior_state) return state; switch(state) { case TDS_PENDING: if (prior_state == TDS_READING || prior_state == TDS_WRITING) { tds->state = TDS_PENDING; tds_mutex_unlock(&tds->wire_mtx); break; } tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; case TDS_READING: /* transition to READING are valid only from PENDING */ if (tds_mutex_trylock(&tds->wire_mtx)) return tds->state; if (tds->state != TDS_PENDING) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } tds->state = state; break; case TDS_SENDING: if (prior_state != TDS_READING && prior_state != TDS_WRITING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } if (tds->state == TDS_READING) { /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(&tds->cur_cursor); tds_release_cur_dyn(tds); tds->current_op = TDS_OP_NONE; } tds_mutex_unlock(&tds->wire_mtx); tds->state = state; break; case TDS_IDLE: if (prior_state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } case TDS_DEAD: if (prior_state == TDS_READING || prior_state == TDS_WRITING) tds_mutex_unlock(&tds->wire_mtx); tds->state = state; break; case TDS_WRITING: CHECK_TDS_EXTRA(tds); if (tds_mutex_trylock(&tds->wire_mtx)) return tds->state; if (tds->state == TDS_DEAD) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0); break; } else if (tds->state != TDS_IDLE && tds->state != TDS_SENDING) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSERPND, 0); break; } if (tds->state == TDS_IDLE) { /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(&tds->cur_cursor); tds_release_cur_dyn(tds); tds->current_op = TDS_OP_NONE; } tds->state = state; break; default: assert(0); break; } state = tds->state; tdsdump_log(TDS_DBG_ERROR, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]); CHECK_TDS_EXTRA(tds); return state; }
static int tds_goodwrite(TDSSOCKET * tds, const unsigned char *buffer, int len, unsigned char last) { double start, now; const unsigned char *p = buffer; int rc; assert(tds && buffer); if (TDS_IS_SOCKET_INVALID(tds->s)) return -1; while (p - buffer < len) { start = GetTimeMark(); now = start; if ((rc = tds_select(tds, TDSSELWRITE, tds->query_timeout, start)) > 0) { int err; size_t remaining = len - (p - buffer); #ifdef USE_MSGMORE ssize_t nput = send(tds->s, p, remaining, last ? MSG_NOSIGNAL : MSG_NOSIGNAL|MSG_MORE); /* In case the kernel does not support MSG_MORE, try again without it */ if (nput < 0 && errno == EINVAL && !last) nput = send(tds->s, p, remaining, MSG_NOSIGNAL); #elif defined(__APPLE__) && defined(SO_NOSIGPIPE) ssize_t nput = send(tds->s, p, remaining, 0); #else ssize_t nput = WRITESOCKET(tds->s, p, remaining); #endif if (nput > 0) { p += nput; continue; } err = sock_errno; if (0 == nput || TDSSOCK_WOULDBLOCK(err) || err == TDSSOCK_EINTR) continue; assert(nput < 0); tdsdump_log(TDS_DBG_NETWORK, "send(2) failed: %d (%s)\n", err, strerror(err)); tds_report_error(tds->tds_ctx, tds, err, 20017, "Write to SQL Server failed"); tds_close_socket(tds); return -1; } else if (rc < 0) { int err = sock_errno; if (TDSSOCK_WOULDBLOCK(err)) /* shouldn't happen, but OK, retry */ continue; tdsdump_log(TDS_DBG_NETWORK, "select(2) failed: %d (%s)\n", err, strerror(err)); tds_report_error(tds->tds_ctx, tds, err, 20005, "select/send finished with error"); tds_close_socket(tds); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout && (now - start) >= tds->query_timeout) { tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout"); tds_close_socket(tds); return -1; } tdsdump_log(TDS_DBG_NETWORK, "tds_goodwrite(): timed out, asking client\n"); switch (rc = tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout")) { case TDS_INT_CONTINUE: continue; case TDS_INT_TIMEOUT: /* * "Cancel the operation ... but leave the dbproc in working condition." * We must try to send the cancel packet, else we have to abandon the dbproc. * If it can't be done, a harder error e.g. ECONNRESET will bubble up. */ tds_send_cancel(tds); continue; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } assert(0); /* not reached */ } assert(0); /* not reached */ } #ifdef USE_CORK /* force packet flush */ if (last) { int opt; opt = 0; setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt)); opt = 1; setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt)); } #endif return len; }
int tds_open_socket(TDSSOCKET * tds, const char *ip_addr, unsigned int port, int timeout) { struct sockaddr_in sin; #if !defined(DOS32X) unsigned long ioctl_blocking = 1; time_t start; int retval; #endif int len; int x_errno; char ip[20]; 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.\n", tds_inet_ntoa_r(sin.sin_addr, ip, sizeof(ip)), ntohs(sin.sin_port)); if (TDS_IS_SOCKET_INVALID(tds->s = socket(AF_INET, SOCK_STREAM, 0))) { tds_report_error(tds->tds_ctx, tds, sock_errno, 20008, "Unable to open socket"); 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, "src/tds/login.c: tds_connect: %s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)) >= 0) { perror(message); free(message); } tds_client_msg(tds->tds_ctx, tds, 20009, 9, 0, 0, "Server is unavailable or does not exist."); tds_free_socket(tds); return TDS_FAIL; } #else /* Jeff's hack *** START OF NEW CODE *** */ if (!timeout) /* I don't think anybody complains... */ timeout = 90000; start = time(NULL); /* enable no-blocking mode */ ioctl_blocking = 1; if (IOCTLSOCKET(tds->s, FIONBIO, &ioctl_blocking) < 0) { tds_close_socket(tds); return TDS_FAIL; } retval = connect(tds->s, (struct sockaddr *) &sin, sizeof(sin)); x_errno = sock_errno; if (retval < 0 && x_errno == TDSSOCK_EINPROGRESS) retval = 0; /* if retval < 0 (error) fall through */ if (tds_select(tds, TDSSELWRITE|TDSSELERR, timeout, (double)start) <= 0) { tds_close_socket(tds); tds_client_msg(tds->tds_ctx, tds, 20015, 6, 0, 0, "SQL Server connection timed out."); return TDS_FAIL; } #endif /* END OF NEW CODE */ if (retval < 0) { tds_report_error(tds->tds_ctx, tds, x_errno, 20009, "Server is unavailable or does not exist"); tds_close_socket(tds); return TDS_FAIL; } return TDS_SUCCEED; }
TDSERRNO tds_open_socket(TDSSOCKET *tds, struct addrinfo *addr, unsigned int port, int timeout, int *p_oserr) { TDSCONNECTION *conn = tds->conn; int len; TDSERRNO tds_error; *p_oserr = 0; conn->s = socket(addr->ai_family, SOCK_STREAM, 0); if (TDS_IS_SOCKET_INVALID(conn->s)) { char *errstr = sock_strerror(*p_oserr = sock_errno); tdsdump_log(TDS_DBG_ERROR, "socket creation error: %s\n", errstr); sock_strerror_free(errstr); return TDSESOCK; } tds->state = TDS_IDLE; #ifdef SO_KEEPALIVE len = 1; setsockopt(conn->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len)); #endif #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) len = 40; setsockopt(conn->s, SOL_TCP, TCP_KEEPIDLE, (const void *) &len, sizeof(len)); len = 2; setsockopt(conn->s, SOL_TCP, TCP_KEEPINTVL, (const void *) &len, sizeof(len)); #endif #if defined(__APPLE__) && defined(SO_NOSIGPIPE) len = 1; if (setsockopt(conn->s, SOL_SOCKET, SO_NOSIGPIPE, (const void *) &len, sizeof(len))) { *p_oserr = sock_errno; tds_connection_close(conn); return TDSESOCK; } #endif len = 1; #if defined(USE_NODELAY) setsockopt(conn->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len)); #elif defined(USE_CORK) if (setsockopt(conn->s, SOL_TCP, TCP_CORK, (const void *) &len, sizeof(len)) < 0) setsockopt(conn->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len)); #else #error One should be defined #endif while ((tds_error = tds_connect_socket(tds, addr, port, timeout, p_oserr)) != TDSEOK) { addr = addr->ai_next; if (!addr) { tds_connection_close(conn); tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() failed\n"); return tds_error; } } tdsdump_log(TDS_DBG_INFO2, "tds_open_socket() succeeded\n"); return TDSEOK; }
static TDSERRNO tds_connect_socket(TDSSOCKET *tds, struct addrinfo *addr, unsigned int port, int timeout, int *p_oserr) { SOCKLEN_T optlen; TDSCONNECTION *conn = tds->conn; char ipaddr[128]; int retval, len; tds_addrinfo_set_port(addr, port); tds_addrinfo2str(addr, ipaddr, sizeof(ipaddr)); if (TDS_IS_SOCKET_INVALID(conn->s)) return TDSECONN; *p_oserr = 0; tdsdump_log(TDS_DBG_INFO1, "Connecting to %s port %d (TDS version %d.%d)\n", ipaddr, port, TDS_MAJOR(conn), TDS_MINOR(conn)); #ifdef DOS32X /* the other connection doesn't work on WATTCP32 */ if (connect(conn->s, addr->ai_addr, addr->ai_addrlen) < 0) { *p_oserr = sock_errno; tdsdump_log(TDS_DBG_ERROR, "tds_open_socket(): %s:%d", ipaddr, port); return TDSECONN; } #else if (!timeout) { /* A timeout of zero means wait forever; 90,000 seconds will feel like forever. */ timeout = 90000; } if ((*p_oserr = tds_socket_set_nonblocking(conn->s)) != 0) { tds_connection_close(conn); return TDSEUSCT; /* close enough: "Unable to set communications timer" */ } retval = connect(conn->s, addr->ai_addr, addr->ai_addrlen); if (retval == 0) { tdsdump_log(TDS_DBG_INFO2, "connection established\n"); } else { int err = *p_oserr = sock_errno; char *errstr = sock_strerror(err); tdsdump_log(TDS_DBG_ERROR, "tds_open_socket: connect(2) returned \"%s\"\n", errstr); sock_strerror_free(errstr); #if DEBUGGING_CONNECTING_PROBLEM if (err != ECONNREFUSED && err != ENETUNREACH && err != TDSSOCK_EINPROGRESS) { tdsdump_dump_buf(TDS_DBG_ERROR, "Contents of sockaddr_in", addr->ai_addr, addr->ai_addrlen); tdsdump_log(TDS_DBG_ERROR, " sockaddr_in:\t" "%s = %x\n" "\t\t\t%s = %x\n" "\t\t\t%s = %s\n" , "sin_family", addr->ai_family , "port", port , "address", ipaddr ); } #endif if (err != TDSSOCK_EINPROGRESS) return TDSECONN; *p_oserr = TDSSOCK_ETIMEDOUT; if (tds_select(tds, TDSSELWRITE|TDSSELERR, timeout) == 0) return TDSECONN; } #endif /* not DOS32X */ /* check socket error */ optlen = sizeof(len); len = 0; if (tds_getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (char *) &len, &optlen) != 0) { char *errstr = sock_strerror(*p_oserr = sock_errno); tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) failed: %s\n", errstr); sock_strerror_free(errstr); return TDSECONN; } if (len != 0) { char *errstr = sock_strerror(*p_oserr = len); tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) reported: %s\n", errstr); sock_strerror_free(errstr); return TDSECONN; } return TDSEOK; }
int main(int argc, char **argv) { SQLLEN sql_nts = SQL_NTS; const char *query; SQLINTEGER id = 0; char string[64]; TDS_SYS_SOCKET last_socket; int port; const int num_inserts = 20; int is_freetds; #ifdef _WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif if (tds_mutex_init(&mtx)) return 1; odbc_mark_sockets_opened(); odbc_connect(); /* * this does not work if server is not connected with socket * (ie ms driver connected locally) */ last_socket = odbc_find_last_socket(); if (TDS_IS_SOCKET_INVALID(last_socket)) { fprintf(stderr, "Error finding last socket opened\n"); return 1; } remote_addr_len = sizeof(remote_addr); if (tds_getpeername(last_socket, &remote_addr.sa, &remote_addr_len)) { fprintf(stderr, "Unable to get remote address\n"); return 1; } is_freetds = odbc_driver_is_freetds(); odbc_disconnect(); /* init fake server, behave like a proxy */ for (port = 12340; port < 12350; ++port) if (!init_fake_server(port)) break; if (port == 12350) { fprintf(stderr, "Cannot bind to a port\n"); return 1; } printf("Fake server bound at port %d\n", port); /* override connections */ if (is_freetds) { setenv("TDSHOST", "127.0.0.1", 1); sprintf(string, "%d", port); setenv("TDSPORT", string, 1); odbc_connect(); } else { char tmp[2048]; SQLSMALLINT len; CHKAllocEnv(&odbc_env, "S"); CHKAllocConnect(&odbc_conn, "S"); sprintf(tmp, "DRIVER={SQL Server};SERVER=127.0.0.1,%d;UID=%s;PWD=%s;DATABASE=%s;Network=DBMSSOCN;", port, odbc_user, odbc_password, odbc_database); printf("connection string: %s\n", tmp); CHKDriverConnect(NULL, T(tmp), SQL_NTS, (SQLTCHAR *) tmp, sizeof(tmp)/sizeof(SQLTCHAR), &len, SQL_DRIVER_NOPROMPT, "SI"); CHKAllocStmt(&odbc_stmt, "S"); } /* real test */ odbc_command("CREATE TABLE #test(i int, c varchar(40))"); odbc_reset_statement(); /* do not take into account connection statistics */ tds_mutex_lock(&mtx); round_trips = 0; inserts = 0; tds_mutex_unlock(&mtx); query = "insert into #test values (?, ?)"; CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI"); CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI"); CHKPrepare(T(query), SQL_NTS, "SI"); tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); for (id = 0; id < num_inserts; id++) { sprintf(string, "This is a test (%d)", (int) id); CHKExecute("SI"); CHKFreeStmt(SQL_CLOSE, "S"); } tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); odbc_reset_statement(); tds_mutex_lock(&mtx); if (inserts > 1 || round_trips > (unsigned) (num_inserts * 2 + 6)) { fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts); tds_mutex_unlock(&mtx); return 1; } printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); #ifdef ENABLE_DEVELOPING /* check for SQL_RESET_PARAMS */ tds_mutex_lock(&mtx); round_trips = 0; inserts = 0; tds_mutex_unlock(&mtx); CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI"); CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI"); CHKPrepare((SQLCHAR *) query, SQL_NTS, "SI"); tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); for (id = 0; id < num_inserts; id++) { CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(id), 0, &id, 0, &sql_nts, "SI"); CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, sizeof(string), 0, string, 0, &sql_nts, "SI"); sprintf(string, "This is a test (%d)", (int) id); CHKExecute("SI"); CHKFreeStmt(SQL_RESET_PARAMS, "S"); } tds_mutex_lock(&mtx); printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); odbc_reset_statement(); tds_mutex_lock(&mtx); if (inserts > 1 || round_trips > num_inserts * 2 + 6) { fprintf(stderr, "Too much round trips (%u) or insert (%u) !!!\n", round_trips, inserts); tds_mutex_unlock(&mtx); return 1; } printf("%u round trips %u inserts\n", round_trips, inserts); tds_mutex_unlock(&mtx); #endif odbc_disconnect(); alarm(10); tds_thread_join(fake_thread, NULL); return 0; }
void tds_check_tds_extra(const TDSSOCKET * tds) { const int invalid_state = 0; int i; TDSDYNAMIC *cur_dyn = NULL; TDSCURSOR *cur_cursor = NULL; assert(tds); /* test state and connection */ switch (tds->state) { case TDS_DEAD: case TDS_WRITING: case TDS_SENDING: case TDS_PENDING: case TDS_IDLE: case TDS_READING: break; default: assert(invalid_state); } assert(tds->conn); #if ENABLE_ODBC_MARS if (tds->state != TDS_DEAD) assert(!TDS_IS_SOCKET_INVALID(tds_get_s(tds))); #else assert(tds->state == TDS_DEAD || !TDS_IS_SOCKET_INVALID(tds_get_s(tds))); assert(tds->state != TDS_DEAD || TDS_IS_SOCKET_INVALID(tds_get_s(tds))); #endif /* test env */ tds_check_env_extra(&tds->conn->env); /* test buffers and positions */ tds_check_packet_extra(tds->send_packet); tds_check_packet_extra(tds->recv_packet); #if ENABLE_ODBC_MARS if (tds->conn->send_packets) assert(tds->conn->send_pos <= tds->conn->send_packets->len); if (tds->conn->recv_packet) assert(tds->conn->recv_pos <= tds->conn->recv_packet->len); #endif assert(tds->in_pos <= tds->in_len); assert(tds->in_len <= tds->recv_packet->capacity); /* TODO remove blocksize from env and use out_len ?? */ /* assert(tds->out_pos <= tds->out_len); */ /* assert(tds->out_len == 0 || tds->out_buf != NULL); */ assert(tds->send_packet->capacity >= tds->out_buf_max + TDS_ADDITIONAL_SPACE); assert(tds->out_buf >= tds->send_packet->buf); assert(tds->out_buf + tds->out_buf_max + TDS_ADDITIONAL_SPACE <= tds->send_packet->buf + tds->send_packet->capacity); assert(tds->out_pos <= tds->out_buf_max); assert(tds->in_buf == tds->recv_packet->buf || tds->in_buf == tds->recv_packet->buf + 16); assert(tds->recv_packet->capacity > 0); /* test res_info */ if (tds->res_info) tds_check_resultinfo_extra(tds->res_info); /* test num_comp_info, comp_info */ assert(tds->num_comp_info >= 0); for (i = 0; i < tds->num_comp_info; ++i) { assert(tds->comp_info); tds_check_resultinfo_extra(tds->comp_info[i]); } /* param_info */ if (tds->param_info) tds_check_resultinfo_extra(tds->param_info); /* test cursors */ for (cur_cursor = tds->conn->cursors; cur_cursor != NULL; cur_cursor = cur_cursor->next) tds_check_cursor_extra(cur_cursor); /* test dynamics */ for (cur_dyn = tds->conn->dyns; cur_dyn != NULL; cur_dyn = cur_dyn->next) tds_check_dynamic_extra(cur_dyn); /* test tds_ctx */ tds_check_context_extra(tds_get_ctx(tds)); /* TODO test char_conv_count, char_convs */ /* we can't have compute and no results */ assert(tds->num_comp_info == 0 || tds->res_info != NULL); /* we can't have normal and parameters results */ /* TODO too strict ?? */ /* assert(tds->param_info == NULL || tds->res_info == NULL); */ }
void tds_check_tds_extra(const TDSSOCKET * tds) { const int invalid_state = 0; int found, i; int result_found = 0; TDSDYNAMIC *cur_dyn = NULL; TDSCURSOR *cur_cursor = NULL; assert(tds); /* teset state and connection */ switch (tds->state) { case TDS_DEAD: case TDS_QUERYING: case TDS_PENDING: case TDS_IDLE: case TDS_READING: break; default: assert(invalid_state); } assert(tds->state == TDS_DEAD || !TDS_IS_SOCKET_INVALID(tds_get_s(tds))); assert(tds->state != TDS_DEAD || TDS_IS_SOCKET_INVALID(tds_get_s(tds))); /* test env */ tds_check_env_extra(&tds->env); /* test buffers and positions */ assert(tds->in_pos <= tds->in_len && tds->in_len <= tds->in_buf_max); /* TODO remove blocksize from env and use out_len ?? */ /* assert(tds->out_pos <= tds->out_len); */ /* assert(tds->out_len == 0 || tds->out_buf != NULL); */ assert(tds->out_pos <= tds->env.block_size); assert(tds->env.block_size == 0 || tds->out_buf != NULL); assert(tds->in_buf_max == 0 || tds->in_buf != NULL); /* test res_info */ if (tds->res_info) { tds_check_resultinfo_extra(tds->res_info); if (tds->current_results == tds->res_info) result_found = 1; } /* test num_comp_info, comp_info */ assert(tds->num_comp_info >= 0); for (i = 0; i < tds->num_comp_info; ++i) { assert(tds->comp_info); tds_check_resultinfo_extra(tds->comp_info[i]); if (tds->current_results == tds->comp_info[i]) result_found = 1; } /* param_info */ if (tds->param_info) { tds_check_resultinfo_extra(tds->param_info); if (tds->current_results == tds->param_info) result_found = 1; } /* test cursors */ found = 0; for (cur_cursor = tds->cursors; cur_cursor != NULL; cur_cursor = cur_cursor->next) { tds_check_cursor_extra(cur_cursor); if (tds->current_results == cur_cursor->res_info) result_found = 1; if (cur_cursor == tds->cur_cursor) found = 1; } assert(found || tds->cur_cursor == NULL); /* test num_dyms, cur_dyn, dyns */ found = 0; for (cur_dyn = tds->dyns; cur_dyn != NULL; cur_dyn = cur_dyn->next) { if (cur_dyn == tds->cur_dyn) found = 1; tds_check_dynamic_extra(cur_dyn); if (tds->current_results == cur_dyn->res_info) result_found = 1; } assert(found || tds->cur_dyn == NULL); /* test tds_ctx */ tds_check_context_extra(tds_get_ctx(tds)); /* TODO test char_conv_count, char_convs */ /* current_results should be one of res_info, comp_info, param_info or dynamic */ /* * TODO this test was here to check that current_results was an alias * but with cursor and reference counting current_results can point * to a cursor result available on upper layer * Perhaps we should free results on deallocate and enable * assert again? */ /* assert(result_found || tds->current_results == NULL); */ /* we can't have compute and no results */ assert(tds->num_comp_info == 0 || tds->res_info != NULL); /* we can't have normal and parameters results */ /* TODO too strict ?? */ /* assert(tds->param_info == NULL || tds->res_info == NULL); */ }
/* * pool_main_loop * Accept new connections from clients, and handle all input from clients and * pool members. */ static void pool_main_loop(TDS_POOL * pool) { TDS_POOL_USER *puser; TDS_POOL_MEMBER *pmbr; struct sockaddr_in sin; int s, maxfd, i; fd_set rfds; int socktrue = 1; /* FIXME -- read the interfaces file and bind accordingly */ sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(pool->port); sin.sin_family = AF_INET; if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) { perror("socket"); exit(1); } /* don't keep addr in use from [email protected] */ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &socktrue, sizeof(socktrue)); fprintf(stderr, "Listening on port %d\n", pool->port); if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } listen(s, 5); FD_ZERO(&rfds); FD_SET(s, &rfds); maxfd = s; while (!term) { /* fprintf(stderr, "waiting for a connect\n"); */ /* FIXME check return value */ select(maxfd + 1, &rfds, NULL, NULL, NULL); if (term) break; /* process the sockets */ if (FD_ISSET(s, &rfds)) { pool_user_create(pool, s, &sin); } pool_process_users(pool, &rfds); pool_process_members(pool, &rfds); /* back from members */ if (waiters) { pool_schedule_waiters(pool); } FD_ZERO(&rfds); /* add the listening socket to the read list */ FD_SET(s, &rfds); maxfd = s; /* add the user sockets to the read list */ for (i = 0; i < pool->max_users; i++) { puser = (TDS_POOL_USER *) & pool->users[i]; /* skip dead connections */ if (!IS_TDSDEAD(puser->tds)) { if (tds_get_s(puser->tds) > maxfd) maxfd = tds_get_s(puser->tds); FD_SET(tds_get_s(puser->tds), &rfds); } } /* add the pool member sockets to the read list */ for (i = 0; i < pool->num_members; i++) { pmbr = (TDS_POOL_MEMBER *) & pool->members[i]; if (!IS_TDSDEAD(pmbr->tds)) { if (tds_get_s(pmbr->tds) > maxfd) maxfd = tds_get_s(pmbr->tds); FD_SET(tds_get_s(pmbr->tds), &rfds); } } } /* while !term */ CLOSESOCKET(s); for (i = 0; i < pool->max_users; i++) { puser = (TDS_POOL_USER *) & pool->users[i]; if (!IS_TDSDEAD(puser->tds)) { fprintf(stderr, "Closing user %d\n", i); tds_close_socket(puser->tds); } } for (i = 0; i < pool->num_members; i++) { pmbr = (TDS_POOL_MEMBER *) & pool->members[i]; if (!IS_TDSDEAD(pmbr->tds)) { fprintf(stderr, "Closing member %d\n", i); tds_close_socket(pmbr->tds); } } }
TDSERRNO tds_open_socket(TDSSOCKET *tds, struct addrinfo *addr, unsigned int port, int timeout, int *p_oserr) { ioctl_nonblocking_t ioctl_nonblocking; SOCKLEN_T optlen; TDSCONNECTION *conn = tds->conn; char ipaddr[128]; int retval, len; TDSERRNO tds_error = TDSECONN; *p_oserr = 0; tds_addrinfo_set_port(addr, port); tds_addrinfo2str(addr, ipaddr, sizeof(ipaddr)); tdsdump_log(TDS_DBG_INFO1, "Connecting to %s port %d (TDS version %d.%d)\n", ipaddr, port, TDS_MAJOR(conn), TDS_MINOR(conn)); conn->s = socket(addr->ai_family, SOCK_STREAM, 0); if (TDS_IS_SOCKET_INVALID(conn->s)) { char *errstr = sock_strerror(*p_oserr = sock_errno); tdsdump_log(TDS_DBG_ERROR, "socket creation error: %s\n", errstr); sock_strerror_free(errstr); return TDSESOCK; } tds->state = TDS_IDLE; #ifdef SO_KEEPALIVE len = 1; setsockopt(conn->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len)); #endif #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) len = 40; setsockopt(conn->s, SOL_TCP, TCP_KEEPIDLE, (const void *) &len, sizeof(len)); len = 2; setsockopt(conn->s, SOL_TCP, TCP_KEEPINTVL, (const void *) &len, sizeof(len)); #endif #if defined(__APPLE__) && defined(SO_NOSIGPIPE) len = 1; if (setsockopt(conn->s, SOL_SOCKET, SO_NOSIGPIPE, (const void *) &len, sizeof(len))) { *p_oserr = sock_errno; tds_connection_close(conn); return TDSESOCK; } #endif len = 1; #if defined(USE_NODELAY) setsockopt(conn->s, SOL_TCP, TCP_NODELAY, (const void *) &len, sizeof(len)); #elif defined(USE_CORK) if (setsockopt(conn->s, SOL_TCP, TCP_CORK, (const void *) &len, sizeof(len)) < 0) setsockopt(conn->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(conn->s, addr->ai_addr, addr->ai_addrlen) < 0) { char *message; *p_oserr = sock_errno; if (asprintf(&message, "tds_open_socket(): %s:%d", ipaddr, port) >= 0) { perror(message); free(message); } tds_connection_close(conn); return TDSECONN; } #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(conn->s, FIONBIO, &ioctl_nonblocking) < 0) { *p_oserr = sock_errno; tds_connection_close(conn); return TDSEUSCT; /* close enough: "Unable to set communications timer" */ } retval = connect(conn->s, addr->ai_addr, addr->ai_addrlen); if (retval == 0) { tdsdump_log(TDS_DBG_INFO2, "connection established\n"); } else { int err = *p_oserr = sock_errno; char *errstr = sock_strerror(err); tdsdump_log(TDS_DBG_ERROR, "tds_open_socket: connect(2) returned \"%s\"\n", errstr); sock_strerror_free(errstr); #if DEBUGGING_CONNECTING_PROBLEM if (err != ECONNREFUSED && err != ENETUNREACH && err != TDSSOCK_EINPROGRESS) { tdsdump_dump_buf(TDS_DBG_ERROR, "Contents of sockaddr_in", addr->ai_addr, addr->ai_addrlen); tdsdump_log(TDS_DBG_ERROR, " sockaddr_in:\t" "%s = %x\n" "\t\t\t%s = %x\n" "\t\t\t%s = %s\n" , "sin_family", addr->ai_family , "port", port , "address", ipaddr ); } #endif if (err != TDSSOCK_EINPROGRESS) goto not_available; *p_oserr = TDSSOCK_ETIMEDOUT; if (tds_select(tds, TDSSELWRITE|TDSSELERR, timeout) == 0) { tds_error = TDSECONN; goto not_available; } } #endif /* not DOS32X */ /* check socket error */ optlen = sizeof(len); len = 0; if (tds_getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (char *) &len, &optlen) != 0) { char *errstr = sock_strerror(*p_oserr = sock_errno); tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) failed: %s\n", errstr); sock_strerror_free(errstr); goto not_available; } if (len != 0) { char *errstr = sock_strerror(*p_oserr = len); tdsdump_log(TDS_DBG_ERROR, "getsockopt(2) reported: %s\n", errstr); sock_strerror_free(errstr); goto not_available; } tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() succeeded\n"); return TDSEOK; not_available: tds_connection_close(conn); tdsdump_log(TDS_DBG_ERROR, "tds_open_socket() failed\n"); return tds_error; }