static int tds_connection_put_packet(TDSSOCKET *tds, TDSPACKET *packet) { TDSCONNECTION *conn = tds->conn; if (TDS_UNLIKELY(!packet)) { tds_close_socket(tds); return TDS_FAIL; } tds->out_pos = 0; tds_mutex_lock(&conn->list_mtx); for (;;) { int wait_res; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Write attempt when state is TDS_DEAD"); break; } /* limit packet sending looking at sequence/window */ if (tds->send_seq <= tds->send_wnd) { /* append packet */ tds_append_packet(&conn->send_packets, packet); packet = NULL; } /* network ok ? process network */ if (!conn->in_net_tds) { tds_connection_network(conn, tds, packet ? 0 : 1); if (packet) continue; /* FIXME we are not sure we sent the packet !!! */ break; } /* signal thread processing network to handle our packet */ /* TODO check result */ tds_wakeup_send(&conn->wakeup, 0); /* wait local condition */ wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout); if (wait_res == ETIMEDOUT && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) { tds_mutex_unlock(&conn->list_mtx); tds_close_socket(tds); tds_free_packets(packet); return TDS_FAIL; } } tds_mutex_unlock(&conn->list_mtx); if (TDS_UNLIKELY(packet)) { tds_free_packets(packet); return TDS_FAIL; } if (IS_TDSDEAD(tds)) return TDS_FAIL; return TDS_SUCCESS; }
static void tds_check_cancel(TDSCONNECTION *conn) { TDSSOCKET *tds; int rc; if (!tds_connection_signaled(conn)) return; do { unsigned n = 0; rc = TDS_SUCCESS; tds_mutex_lock(&conn->list_mtx); /* Here we scan all list searching for sessions that should send cancel packets */ for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(tds=conn->sessions[n]) && tds->in_cancel == 1) { /* send cancel */ tds->in_cancel = 2; tds_mutex_unlock(&conn->list_mtx); rc = tds_append_cancel(tds); tds_mutex_lock(&conn->list_mtx); if (rc != TDS_SUCCESS) break; } tds_mutex_unlock(&conn->list_mtx); /* for all failed */ /* this must be done outside loop cause it can alter list */ /* this must be done unlocked cause it can lock again */ if (rc != TDS_SUCCESS) tds_close_socket(tds); } while(rc != TDS_SUCCESS); }
/** * \param tds the famous socket * \param buffer data to send * \param buflen bytes in buffer * \param last 1 if this is the last packet, else 0 * \return length written (>0), <0 on failure */ int tds_goodwrite(TDSSOCKET * tds, const unsigned char *buffer, size_t buflen) { int len; size_t sent = 0; assert(tds && buffer); while (sent < buflen) { /* TODO if send buffer is full we block receive !!! */ len = tds_select(tds, TDSSELWRITE, tds->query_timeout); if (len > 0) { len = tds_socket_write(tds->conn, tds, buffer + sent, buflen - sent); if (len == 0) continue; if (len < 0) return len; sent += len; continue; } /* error */ if (len < 0) { int err = sock_errno; char *errstr; if (TDSSOCK_WOULDBLOCK(err)) /* shouldn't happen, but OK, retry */ continue; errstr = sock_strerror(err); tdsdump_log(TDS_DBG_NETWORK, "select(2) failed: %d (%s)\n", err, errstr); sock_strerror_free(errstr); tds_connection_close(tds->conn); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, err); return -1; } /* timeout */ tdsdump_log(TDS_DBG_NETWORK, "tds_goodwrite(): timed out, asking client\n"); switch (tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: break; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } } return (int) sent; }
/* Note that this always suceeds */ int try_tds_logout(TDSLOGIN * login, TDSSOCKET * tds, int verbose) { if (verbose) { fprintf(stdout, "Entered tds_try_logout()\n"); } tds_close_socket(tds); tds_free_socket(tds); tds_free_login(login); tds_free_context(test_context); test_context = NULL; return TDS_SUCCESS; }
/** * Loops until we have received some characters * return -1 on failure */ int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen) { if (tds == NULL || buf == NULL || buflen < 1) return -1; for (;;) { int len, err; /* FIXME this block writing from other sessions */ len = tds_select(tds, TDSSELREAD, tds->query_timeout); #if !ENABLE_ODBC_MARS if (len > 0 && (len & TDSPOLLURG)) { char buf[32]; READSOCKET(tds->conn->s_signaled, buf, sizeof(buf)); /* send cancel */ if (!tds->in_cancel) tds_put_cancel(tds); continue; } #endif if (len > 0) { len = tds_socket_read(tds->conn, tds, buf, buflen); if (len == 0) continue; return len; } /* error */ if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; err = sock_errno; tds_connection_close(tds->conn); tdserror(tds_get_ctx(tds), tds, TDSEREAD, err); return -1; } /* timeout */ switch (tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: break; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } } }
void pool_free_member(TDS_POOL_MEMBER * pmbr) { if (!IS_TDSDEAD(pmbr->tds)) { tds_close_socket(pmbr->tds); } pmbr->tds = NULL; /* * if he is allocated disconnect the client * otherwise we end up with broken client. */ if (pmbr->current_user) { pool_free_user(pmbr->current_user); pmbr->current_user = NULL; } pmbr->state = TDS_IDLE; }
void pool_free_member(TDS_POOL_MEMBER * pmbr) { TDSSOCKET *tds = pmbr->tds; if (!IS_TDSDEAD(tds)) tds_close_socket(tds); if (tds) tds_free_socket(tds); pmbr->tds = NULL; /* * if he is allocated disconnect the client * otherwise we end up with broken client. */ if (pmbr->current_user) { pmbr->current_user->assigned_member = NULL; pool_free_user(pmbr->current_user); pmbr->current_user = NULL; } memset(pmbr, 0, sizeof(*pmbr)); pmbr->state = TDS_IDLE; }
/* * 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); } } }
/** * Read in one 'packet' from the server. This is a wrapped outer packet of * the protocol (they bundle result packets into chunks and wrap them at * what appears to be 512 bytes regardless of how that breaks internal packet * up. (tetherow\@nol.org) * @return bytes read or -1 on failure */ int tds_read_packet(TDSSOCKET * tds) { #if ENABLE_ODBC_MARS TDSCONNECTION *conn = tds->conn; tds_mutex_lock(&conn->list_mtx); for (;;) { int wait_res; TDSPACKET **p_packet; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD\n"); break; } /* if there is a packet for me return it */ for (p_packet = &conn->packets; *p_packet; p_packet = &(*p_packet)->next) if ((*p_packet)->sid == tds->sid) break; if (*p_packet) { size_t hdr_size; /* remove our packet from list */ TDSPACKET *packet = *p_packet; *p_packet = packet->next; tds_packet_cache_add(conn, tds->recv_packet); tds_mutex_unlock(&conn->list_mtx); packet->next = NULL; tds->recv_packet = packet; hdr_size = packet->buf[0] == TDS72_SMP ? sizeof(TDS72_SMP_HEADER) : 0; tds->in_buf = packet->buf + hdr_size; tds->in_len = packet->len - hdr_size; tds->in_pos = 8; tds->in_flag = tds->in_buf[0]; /* send acknowledge if needed */ if (tds->recv_seq + 2 >= tds->recv_wnd) tds_update_recv_wnd(tds, tds->recv_seq + 4); return tds->in_len; } /* network ok ? process network */ if (!conn->in_net_tds) { tds_connection_network(conn, tds, 0); continue; } /* wait local condition */ wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout); if (wait_res == ETIMEDOUT && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) { tds_mutex_unlock(&conn->list_mtx); tds_close_socket(tds); return -1; } } tds_mutex_unlock(&conn->list_mtx); return -1; #else /* !ENABLE_ODBC_MARS */ unsigned char *pkt = tds->in_buf, *p, *end; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD"); return -1; } tds->in_len = 0; tds->in_pos = 0; for (p = pkt, end = p+8; p < end;) { int len = tds_connection_read(tds, p, end - p); if (len <= 0) { tds_close_socket(tds); return -1; } p += len; if (p - pkt >= 4) { unsigned pktlen = TDS_GET_A2BE(pkt+2); /* packet must at least contains header */ if (TDS_UNLIKELY(pktlen < 8)) { tds_close_socket(tds); return -1; } if (TDS_UNLIKELY(pktlen > tds->recv_packet->capacity)) { TDSPACKET *packet = tds_realloc_packet(tds->recv_packet, pktlen); if (TDS_UNLIKELY(!packet)) { tds_close_socket(tds); return -1; } tds->recv_packet = packet; pkt = packet->buf; p = pkt + (p-tds->in_buf); tds->in_buf = pkt; } end = pkt + pktlen; } } /* set the received packet type flag */ tds->in_flag = pkt[0]; /* Set the length and pos (not sure what pos is used for now */ tds->in_len = p - pkt; tds->in_pos = 8; tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len); return tds->in_len; #endif /* !ENABLE_ODBC_MARS */ }
static void tds_connection_network(TDSCONNECTION *conn, TDSSOCKET *tds, int send) { assert(!conn->in_net_tds); conn->in_net_tds = tds; tds_mutex_unlock(&conn->list_mtx); for (;;) { /* wait packets or update */ int rc = tds_select(tds, conn->send_packets ? TDSSELREAD|TDSSELWRITE : TDSSELREAD, tds->query_timeout); if (rc < 0) { /* FIXME better error report */ tds_connection_close(conn); break; } /* change notify */ /* TODO async */ if (!rc) { /* timeout */ tdsdump_log(TDS_DBG_INFO1, "timeout\n"); switch (rc = tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: continue; default: case TDS_INT_CANCEL: tds_close_socket(tds); } break; } /* * we must write first to catch write errors as * write errors, not as read */ /* something to send */ if (conn->send_packets && (rc & POLLOUT) != 0) { TDSSOCKET *s; short sid = tds_packet_write(conn); if (sid == tds->sid) break; /* return to caller */ tds_mutex_lock(&conn->list_mtx); if (sid >= 0 && sid < conn->num_sessions) { s = conn->sessions[sid]; if (TDSSOCKET_VALID(s)) tds_cond_signal(&s->packet_cond); } tds_mutex_unlock(&conn->list_mtx); /* avoid using a possible closed connection */ continue; } /* received */ if (rc & POLLIN) { TDSPACKET *packet; TDSSOCKET *s; /* try to read a packet */ tds_packet_read(conn, tds); packet = conn->recv_packet; if (!packet || conn->recv_pos < packet->len) continue; conn->recv_packet = NULL; conn->recv_pos = 0; tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", packet->buf, packet->len); tds_mutex_lock(&conn->list_mtx); if (packet->sid >= 0 && packet->sid < conn->num_sessions) { s = conn->sessions[packet->sid]; if (TDSSOCKET_VALID(s)) { /* append to correct session */ if (packet->buf[0] == TDS72_SMP && packet->buf[1] != TDS_SMP_DATA) tds_packet_cache_add(conn, packet); else tds_append_packet(&conn->packets, packet); packet = NULL; /* notify */ tds_cond_signal(&s->packet_cond); } } tds_mutex_unlock(&conn->list_mtx); tds_free_packets(packet); /* if we are receiving return the packet */ if (!send) break; } } tds_mutex_lock(&conn->list_mtx); conn->in_net_tds = NULL; }
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; }
/** * Read in one 'packet' from the server. This is a wrapped outer packet of * the protocol (they bundle result packets into chunks and wrap them at * what appears to be 512 bytes regardless of how that breaks internal packet * up. (tetherow\@nol.org) * @return bytes read or -1 on failure */ int tds_read_packet(TDSSOCKET * tds) { unsigned char header[8]; int len; int x = 0, have, need; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD"); return -1; } /* * Read in the packet header. We use this to figure out our packet * length */ /* * Cast to int are needed because some compiler seem to convert * len to unsigned (as FreeBSD 4.5 one) */ if ((len = goodread(tds, header, sizeof(header))) < (int) sizeof(header)) { /* GW ADDED */ if (len < 0) { tds_client_msg(tds->tds_ctx, tds, 20004, 6, 0, 0, "Read from SQL server failed."); tds_close_socket(tds); tds->in_len = 0; tds->in_pos = 0; return -1; } /* GW ADDED */ /* * Not sure if this is the best way to do the error * handling here but this is the way it is currently * being done. */ tds->in_len = 0; tds->in_pos = 0; tds->last_packet = 1; if (tds->state != TDS_IDLE && len == 0) { tds_close_socket(tds); } return -1; } tdsdump_dump_buf(TDS_DBG_NETWORK, "Received header", header, sizeof(header)); #if 0 /* * Note: * this was done by Gregg, I don't think its the real solution (it breaks * under 5.0, but I haven't gotten a result big enough to test this yet. */ if (IS_TDS42(tds)) { if (header[0] != 0x04 && header[0] != 0x0f) { tdsdump_log(TDS_DBG_ERROR, "Invalid packet header %d\n", header[0]); /* * Not sure if this is the best way to do the error * handling here but this is the way it is currently * being done. */ tds->in_len = 0; tds->in_pos = 0; tds->last_packet = 1; return (-1); } } #endif /* Convert our packet length from network to host byte order */ len = ((((unsigned int) header[2]) << 8) | header[3]) - 8; need = len; /* * If this packet size is the largest we have gotten allocate * space for it */ if ((unsigned int)len > tds->in_buf_max) { unsigned char *p; if (!tds->in_buf) { p = (unsigned char *) malloc(len); } else { p = (unsigned char *) realloc(tds->in_buf, len); } if (!p) return -1; /* FIXME should close socket too */ tds->in_buf = p; /* Set the new maximum packet size */ tds->in_buf_max = len; } /* Clean out the in_buf so we don't use old stuff by mistake */ memset(tds->in_buf, 0, tds->in_buf_max); /* Now get exactly how many bytes the server told us to get */ have = 0; while (need > 0) { if ((x = goodread(tds, tds->in_buf + have, need)) < 1) { /* * Not sure if this is the best way to do the error * handling here but this is the way it is currently * being done. */ tds->in_len = 0; tds->in_pos = 0; tds->last_packet = 1; /* FIXME should this be "if (x == 0)" ? */ if (len == 0) { tds_close_socket(tds); } return (-1); } have += x; need -= x; } if (x < 1) { /* * Not sure if this is the best way to do the error handling * here but this is the way it is currently being done. */ tds->in_len = 0; tds->in_pos = 0; tds->last_packet = 1; /* return 0 if header found but no payload */ return len ? -1 : 0; } /* Set the last packet flag */ if (header[1]) { tds->last_packet = 1; } else { tds->last_packet = 0; } /* set the received packet type flag */ tds->in_flag = header[0]; /* Set the length and pos (not sure what pos is used for now */ tds->in_len = have; tds->in_pos = 0; tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len); return (tds->in_len); }
/** * Loops until we have received buflen characters * return -1 on failure */ static int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen, unsigned char unfinished) { double start, global_start; int got = 0; int canceled = 0; if (buf == NULL || buflen < 1 || IS_TDSDEAD(tds)) return 0; global_start = start = GetTimeMark(); while (buflen > 0) { int len; double now; if (IS_TDSDEAD(tds)) return -1; if ((len = tds_select(tds, TDSSELREAD, tds->query_timeout, global_start)) > 0) { len = READSOCKET(tds->s, buf + got, buflen); if (len < 0 && TDSSOCK_WOULDBLOCK(sock_errno)) continue; /* detect connection close */ if (len <= 0) { tds_close_socket(tds); if (len == 0) { tds_client_msg(tds->tds_ctx, tds, 20011, 6, 0, 0, "EOF in the socket."); } else { tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); } return -1; } } else if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; tds_close_socket(tds); tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout > 0 && now - start >= tds->query_timeout) { int timeout_action = TDS_INT_CONTINUE; if (canceled) return got ? got : -1; if (tds->query_timeout_func && tds->query_timeout) timeout_action = (*tds->query_timeout_func) (tds->query_timeout_param, (int)(now - global_start)); switch (timeout_action) { case TDS_INT_EXIT: exit(EXIT_FAILURE); break; case TDS_INT_CANCEL: tds_send_cancel(tds); canceled = 1; /* fall through to wait while cancelling happens */ case TDS_INT_CONTINUE: start = now; default: break; } } } buflen -= len; got += len; if (unfinished && got) return got; } return got; }
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; }