/* * pool_process_members * check the fd_set for members returning data to the client, lookup the * client holding this member and forward the results. */ int pool_process_members(TDS_POOL * pool, fd_set * fds) { TDS_POOL_MEMBER *pmbr; TDS_POOL_USER *puser; TDSSOCKET *tds; int i, age, ret; int cnt = 0; time_t time_now; for (i = 0; i < pool->num_members; i++) { pmbr = &pool->members[i]; tds = pmbr->tds; if (!tds) { assert(pmbr->state == TDS_IDLE); continue; /* dead connection */ } time_now = time(NULL); if (FD_ISSET(tds_get_s(tds), fds)) { pmbr->last_used_tm = time_now; cnt++; if (pool_packet_read(tds)) continue; if (tds->in_len == 0) { fprintf(stderr, "Uh oh! member %d disconnected\n", i); /* mark as dead */ pool_free_member(pmbr); } else if (tds->in_len < 0) { fprintf(stderr, "Uh oh! member %d disconnected\n", i); perror("read"); pool_free_member(pmbr); } else { tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from server:", tds->in_buf, tds->in_len); /* fprintf(stderr, "read %d bytes from member %d\n", tds->in_len, i); */ puser = pmbr->current_user; if (puser) { tdsdump_log(TDS_DBG_INFO1, "writing it sock %d\n", tds_get_s(puser->tds)); /* cf. net.c for better technique. */ /* FIXME handle partial write, stop read on member */ ret = pool_write_all(tds_get_s(puser->tds), tds->in_buf, tds->in_len); if (ret < 0) { /* couldn't write, ditch the user */ fprintf(stdout, "member %d received error while writing\n",i); pool_free_member(pmbr); } } } } age = time_now - pmbr->last_used_tm; if (age > pool->max_member_age && i >= pool->min_open_conn && !pmbr->current_user) { fprintf(stderr, "member %d is %d seconds old...closing\n", i, age); pool_free_member(pmbr); } } return cnt; }
/* * pool_user_read * checks the packet type of data coming from the client and allocates a * pool member if necessary. */ static bool pool_user_read(TDS_POOL * pool, TDS_POOL_USER * puser) { TDSSOCKET *tds = puser->sock.tds; TDS_POOL_MEMBER *pmbr = NULL; for (;;) { if (pool_packet_read(tds)) break; if (tds->in_len == 0) { tdsdump_log(TDS_DBG_INFO1, "user disconnected\n"); pool_free_user(pool, puser); return false; } else { TDS_UCHAR in_flag = tds->in_buf[0]; tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from client:", tds->in_buf, tds->in_len); switch (in_flag) { case TDS_QUERY: case TDS_NORMAL: case TDS_RPC: case TDS_BULK: case TDS_CANCEL: case TDS7_TRANS: if (!pool_write_data(&puser->sock, &puser->assigned_member->sock)) { pool_reset_member(pool, puser->assigned_member); return false; } pmbr = puser->assigned_member; break; default: tdsdump_log(TDS_DBG_ERROR, "Unrecognized packet type, closing user\n"); pool_free_user(pool, puser); return false; } } } if (pmbr && !pmbr->sock.poll_send) tds_socket_flush(tds_get_s(pmbr->sock.tds)); return true; }
/* * For UTF-8 and similar, tds_iconv() may encounter a partial sequence when the chunk boundary * is not aligned with the character boundary. In that event, it will return an error, and * some number of bytes (less than a character) will remain in the tail end of temp[]. They are * moved to the beginning, ptemp is adjusted to point just behind them, and the next chunk is read. */ static int read_and_convert(TDSSOCKET * tds, const TDSICONV * char_conv, size_t * wire_size, char **outbuf, size_t * outbytesleft) { TEMP_INIT(256); /* * temp (above) is the "preconversion" buffer, the place where the UCS-2 data * are parked before converting them to ASCII. It has to have a size, * and there's no advantage to allocating dynamically. * This also avoids any memory allocation error. */ const char *bufp; size_t bufleft = 0; const size_t max_output = *outbytesleft; /* cast away const for message suppression sub-structure */ TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress; memset(suppress, 0, sizeof(char_conv->suppress)); for (bufp = temp; *wire_size > 0 && *outbytesleft > 0; bufp = temp + bufleft) { assert(bufp >= temp); /* read a chunk of data */ bufleft = TEMP_SIZE - bufleft; if (bufleft > *wire_size) bufleft = *wire_size; tds_get_n(tds, (char *) bufp, (int)bufleft); *wire_size -= bufleft; bufleft += bufp - temp; /* Convert chunk and write to dest. */ bufp = temp; /* always convert from start of buffer */ suppress->einval = *wire_size > 0; /* EINVAL matters only on the last chunk. */ if ((size_t)-1 == tds_iconv(tds, char_conv, to_client, &bufp, &bufleft, outbuf, outbytesleft)) { tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: tds_iconv returned errno %d\n", errno); if (errno != EILSEQ) { tdsdump_log(TDS_DBG_NETWORK, "Error: read_and_convert: " "Gave up converting %u bytes due to error %d.\n", (unsigned int) bufleft, errno); tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", bufp, bufleft); } if (bufp == temp) { /* tds_iconv did not convert anything, avoid infinite loop */ tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: draining remaining %u bytes.\n", (unsigned int) *wire_size); tds_get_n(tds, NULL, (int)(*wire_size)); /* perhaps we should read unconverted data into outbuf? */ *wire_size = 0; break; } if (bufleft) { memmove(temp, bufp, bufleft); } } } assert(*wire_size == 0 || *outbytesleft == 0); TEMP_FREE; return (int)(max_output - *outbytesleft); }
/** * 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; }
/* read partial packet */ static void tds_packet_read(TDSCONNECTION *conn, TDSSOCKET *tds) { TDSPACKET *packet = conn->recv_packet; int len; /* allocate some space to read data */ if (!packet) { conn->recv_packet = packet = tds_get_packet(conn, MAX(conn->env.block_size + sizeof(TDS72_SMP_HEADER), 512)); if (!packet) goto Memory_Error; TDS_MARK_UNDEFINED(packet->buf, packet->capacity); conn->recv_pos = 0; packet->len = 8; } assert(conn->recv_pos < packet->len && packet->len <= packet->capacity); len = tds_connection_read(tds, packet->buf + conn->recv_pos, packet->len - conn->recv_pos); if (len < 0) goto Severe_Error; conn->recv_pos += len; assert(conn->recv_pos <= packet->len && packet->len <= packet->capacity); /* handle SMP */ if (conn->recv_pos > 0 && packet->buf[0] == TDS72_SMP) { TDS72_SMP_HEADER mars_header; short sid; TDSSOCKET *tds; TDS_UINT size; if (conn->recv_pos < 16) { packet->len = 16; return; } memcpy(&mars_header, packet->buf, sizeof(mars_header)); tdsdump_dump_buf(TDS_DBG_HEADER, "Received MARS header", &mars_header, sizeof(mars_header)); sid = TDS_GET_A2LE(&mars_header.sid); /* FIXME this is done even by caller !! */ tds = NULL; tds_mutex_lock(&conn->list_mtx); if (sid >= 0 && sid < conn->num_sessions) tds = conn->sessions[sid]; tds_mutex_unlock(&conn->list_mtx); packet->sid = sid; if (tds == BUSY_SOCKET) { if (mars_header.type != TDS_SMP_FIN) { tdsdump_log(TDS_DBG_ERROR, "Received MARS with no session (%d)\n", sid); goto Severe_Error; } /* check if was just a "zombie" socket */ tds_mutex_lock(&conn->list_mtx); conn->sessions[sid] = NULL; tds_mutex_unlock(&conn->list_mtx); /* reset packet to initial state to reuse it */ packet->len = 8; conn->recv_pos = 0; return; } if (!tds) { /* server sent a unknown packet, close connection */ goto Severe_Error; } tds->send_wnd = TDS_GET_A4LE(&mars_header.wnd); size = TDS_GET_A4LE(&mars_header.size); if (mars_header.type == TDS_SMP_ACK) { if (size != sizeof(mars_header)) goto Severe_Error; } else if (mars_header.type == TDS_SMP_DATA) { if (size < 0x18 || size > 0xffffu + sizeof(mars_header)) goto Severe_Error; /* avoid recursive SMP */ if (conn->recv_pos > 16 && packet->buf[16] == TDS72_SMP) goto Severe_Error; /* TODO is possible to put 2 TDS packet inside a single DATA ?? */ if (conn->recv_pos >= 20 && TDS_GET_A2BE(&packet->buf[18]) != size - 16) goto Severe_Error; tds->recv_seq = TDS_GET_A4LE(&mars_header.seq); /* * does not sent ACK here cause this would lead to memory waste * if session is not able to handle all that packets */ } else if (mars_header.type == TDS_SMP_FIN) { if (size != sizeof(mars_header)) goto Severe_Error; /* this socket shold now not start another session */ // tds_set_state(tds, TDS_DEAD); // tds->sid = -1; } else goto Severe_Error; if (mars_header.type != TDS_SMP_DATA) return; if (packet->len < size) { packet = tds_realloc_packet(packet, size); if (!packet) goto Memory_Error; conn->recv_packet = packet; } packet->len = size; return; } assert(conn->recv_pos <= packet->len && packet->len <= packet->capacity); /* normal packet */ if (conn->recv_pos >= 8) { len = TDS_GET_A2BE(&packet->buf[2]); if (len < 8) goto Severe_Error; if (packet->len < len) { packet = tds_realloc_packet(packet, len); if (!packet) goto Memory_Error; conn->recv_packet = packet; } packet->len = len; } return; Memory_Error: Severe_Error: tds_connection_close(conn); tds_free_packets(packet); conn->recv_packet = NULL; }
static TDSRET tds_gss_continue(TDSSOCKET * tds, struct tds_gss_auth *auth, gss_buffer_desc *token_ptr) { gss_buffer_desc send_tok; OM_uint32 maj_stat, min_stat = 0; OM_uint32 ret_flags; int gssapi_flags; const char *msg = "???"; gss_OID pmech = GSS_C_NULL_OID; auth->last_stat = GSS_S_COMPLETE; send_tok.value = NULL; send_tok.length = 0; /* * Perform the context-establishement loop. * * On each pass through the loop, token_ptr points to the token * to send to the server (or GSS_C_NO_BUFFER on the first pass). * Every generated token is stored in send_tok which is then * transmitted to the server; every received token is stored in * recv_tok, which token_ptr is then set to, to be processed by * the next call to gss_init_sec_context. * * GSS-API guarantees that send_tok's length will be non-zero * if and only if the server is expecting another token from us, * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if * and only if the server has another token to send us. */ /* * We always want to ask for the mutual, replay, and integ flags. * We may ask for delegation based on config in the tds.conf and other conf files. */ gssapi_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG; if (tds->login->gssapi_use_delegation) gssapi_flags |= GSS_C_DELEG_FLAG; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &auth->gss_context, auth->target_name, GSS_C_NULL_OID, gssapi_flags, 0, NULL, /* no channel bindings */ token_ptr, &pmech, &send_tok, &ret_flags, NULL); /* ignore time_rec */ tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: actual mechanism at 0x%p\n", pmech); if (pmech && pmech->elements) { tdsdump_dump_buf(TDS_DBG_NETWORK, "actual mechanism", pmech->elements, pmech->length); } auth->last_stat = maj_stat; switch (maj_stat) { case GSS_S_COMPLETE: msg = "GSS_S_COMPLETE: gss_init_sec_context completed successfully."; break; case GSS_S_CONTINUE_NEEDED: msg = "GSS_S_CONTINUE_NEEDED: gss_init_sec_context() routine must be called again."; break; case GSS_S_FAILURE: msg = "GSS_S_FAILURE: The routine failed for reasons that are not defined at the GSS level."; tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: min_stat %ld \"%s\"\n", (long) min_stat, error_message(min_stat)); break; case GSS_S_BAD_BINDINGS: msg = "GSS_S_BAD_BINDINGS: The channel bindings are not valid."; break; case GSS_S_BAD_MECH: msg = "GSS_S_BAD_MECH: The request security mechanism is not supported."; break; case GSS_S_BAD_NAME: msg = "GSS_S_BAD_NAME: The target_name parameter is not valid."; break; case GSS_S_BAD_SIG: msg = "GSS_S_BAD_SIG: The input token contains an incorrect integrity check value."; break; case GSS_S_CREDENTIALS_EXPIRED: msg = "GSS_S_CREDENTIALS_EXPIRED: The supplied credentials are no longer valid."; break; case GSS_S_DEFECTIVE_CREDENTIAL: msg = "GSS_S_DEFECTIVE_CREDENTIAL: Consistency checks performed on the credential failed."; break; case GSS_S_DEFECTIVE_TOKEN: msg = "GSS_S_DEFECTIVE_TOKEN: Consistency checks performed on the input token failed."; break; case GSS_S_DUPLICATE_TOKEN: msg = "GSS_S_DUPLICATE_TOKEN: The token is a duplicate of a token that has already been processed."; break; case GSS_S_NO_CONTEXT: msg = "GSS_S_NO_CONTEXT: The context handle provided by the caller does not refer to a valid security context."; break; case GSS_S_NO_CRED: msg = "GSS_S_NO_CRED: The supplied credential handle does not refer to a valid credential, the supplied credential is not"; break; case GSS_S_OLD_TOKEN: msg = "GSS_S_OLD_TOKEN: The token is too old to be checked for duplication against previous tokens which have already been processed."; break; } if (GSS_ERROR(maj_stat)) { gss_release_buffer(&min_stat, &send_tok); tdsdump_log(TDS_DBG_NETWORK, "gss_init_sec_context: %s\n", msg); return TDS_FAIL; } auth->tds_auth.packet = (TDS_UCHAR *) send_tok.value; auth->tds_auth.packet_len = send_tok.length; return TDS_SUCCESS; }
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; }
/** * 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); }
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; }