/** * Get N bytes from the buffer and return them in the already allocated space * given to us. We ASSUME that the person calling this function has done the * bounds checking for us since they know how many bytes they want here. * dest of NULL means we just want to eat the bytes. ([email protected]) */ void * tds_get_n(TDSSOCKET * tds, void *dest, int need) { assert(need >= 0); for (;;) { int have = tds->in_len - tds->in_pos; if (need <= have) break; /* We need more than is in the buffer, copy what is there */ if (dest != NULL) { memcpy((char *) dest, tds->in_buf + tds->in_pos, have); dest = (char *) dest + have; } need -= have; if (tds_read_packet(tds) < 0) return NULL; } if (need > 0) { /* get the remainder if there is any */ if (dest != NULL) { memcpy((char *) dest, tds->in_buf + tds->in_pos, need); } tds->in_pos += need; } return dest; }
static SSL_RET tds_pull_func_login(SSL_PULL_ARGS) { TDSSOCKET *tds = (TDSSOCKET *) SSL_PTR; int have; tdsdump_log(TDS_DBG_INFO1, "in tds_pull_func_login\n"); /* here we are initializing (crypted inside TDS packets) */ /* if we have some data send it */ /* here MARS is not already initialized so test is correct */ /* TODO test even after initializing ?? */ if (tds->out_pos > 8) tds_flush_packet(tds); for(;;) { have = tds->in_len - tds->in_pos; tdsdump_log(TDS_DBG_INFO1, "have %d\n", have); assert(have >= 0); if (have > 0) break; tdsdump_log(TDS_DBG_INFO1, "before read\n"); if (tds_read_packet(tds) < 0) return -1; tdsdump_log(TDS_DBG_INFO1, "after read\n"); } if (len > have) len = have; tdsdump_log(TDS_DBG_INFO1, "read %lu bytes\n", (unsigned long int) len); memcpy(data, tds->in_buf + tds->in_pos, len); tds->in_pos += len; return len; }
/* ** Return a single byte from the input buffer */ unsigned char tds_get_byte(TDSSOCKET * tds) { while (tds->in_pos >= tds->in_len) { if (tds_read_packet(tds) < 0) return 0; } return tds->in_buf[tds->in_pos++]; }
/* * if a dead connection on the client side left this member in a questionable * state, let's bring in a correct one * We are not sure what the client did so we must try to clean as much as * possible. * Use pool_free_member if the state is really broken. */ void pool_reset_member(TDS_POOL_MEMBER * pmbr) { // FIXME not wait for server !!! asyncronous TDSSOCKET *tds = pmbr->tds; if (pmbr->current_user) { pmbr->current_user->assigned_member = NULL; pool_free_user(pmbr->current_user); pmbr->current_user = NULL; } /* cancel whatever pending */ tds->state = TDS_IDLE; tds_init_write_buf(tds); tds->out_flag = TDS_CANCEL; tds_flush_packet(tds); tds->state = TDS_PENDING; if (tds_read_packet(tds) < 0) { pool_free_member(pmbr); return; } if (IS_TDS71_PLUS(tds->conn)) { /* this 0x9 final reset the state from mssql 2000 */ tds_init_write_buf(tds); tds->out_flag = TDS_QUERY; tds_write_packet(tds, 0x9); tds->state = TDS_PENDING; if (tds_read_packet(tds) < 0) { pool_free_member(pmbr); return; } } pmbr->state = TDS_IDLE; }
/* ** Return a single byte from the input buffer */ unsigned char tds_get_byte(TDSSOCKET * tds) { int rc; if (tds->in_pos >= tds->in_len) { do { if (IS_TDSDEAD(tds) || (rc = tds_read_packet(tds)) < 0) return 0; } while (!rc); } return tds->in_buf[tds->in_pos++]; }
/* * pool_user_login * Reads clients login packet and forges a login acknowledgement sequence */ static bool pool_user_login(TDS_POOL * pool, TDS_POOL_USER * puser) { TDSSOCKET *tds; TDSLOGIN *login; tds = puser->sock.tds; while (tds->in_len <= tds->in_pos) if (tds_read_packet(tds) < 0) return false; tdsdump_log(TDS_DBG_NETWORK, "got packet type %d\n", tds->in_flag); if (tds->in_flag == TDS71_PRELOGIN) { if (!tds->conn->tds_version) tds->conn->tds_version = 0x701; tds->out_flag = TDS_REPLY; // TODO proper one !! // TODO detect TDS version here ?? tds_put_n(tds, "\x00\x00\x1a\x00\x06" /* version */ "\x01\x00\x20\x00\x01" /* encryption */ "\x02\x00\x21\x00\x01" /* instance ?? */ "\x03\x00\x22\x00\x00" /* process id ?? */ "\x04\x00\x22\x00\x01" /* MARS */ "\xff" "\x0a\x00\x06\x40\x00\x00" "\x02" "\x01" "" "\x00", 0x23); tds_flush_packet(tds); /* read another packet */ tds->in_pos = tds->in_len; while (tds->in_len <= tds->in_pos) if (tds_read_packet(tds) < 0) return false; } puser->login = login = tds_alloc_login(1); if (tds->in_flag == TDS_LOGIN) { if (!tds->conn->tds_version) tds->conn->tds_version = 0x500; tds_read_login(tds, login); } else if (tds->in_flag == TDS7_LOGIN) { if (!tds->conn->tds_version) tds->conn->tds_version = 0x700; if (!tds7_read_login(tds, login)) return false; } else { return false; } /* check we support version required */ // TODO function to check it if (!IS_TDS71_PLUS(login)) return false; tds->in_len = tds->in_pos = 0; dump_login(login); if (strcmp(tds_dstr_cstr(&login->user_name), pool->user) != 0 || strcmp(tds_dstr_cstr(&login->password), pool->password) != 0) /* TODO send nack before exiting */ return false; return true; }
/** * Read a query, and return it as an ASCII string with a \0 terminator. This * should work for TDS4, TDS5, and TDS7+ connections. Also, it converts RPC * calls into stored procedure queries, and it skips CANCEL packets. The query * string is returned in a static buffer which is overwritten each time this * function is called. * \param tds The socket to read from. * \return A query string if successful, or NULL if we either can't read from * the socket or we read something that we can't handle. */ char *tds_get_generic_query(TDSSOCKET * tds) { int token, byte; int len, more, i, j; for (;;) { /* * Read a new packet. We must explicitly read it, * instead of letting functions such as tds_get_byte() * to read it for us, so that we can examine the packet * type via tds->in_flag. */ if (tds_read_packet(tds) < 0) { return NULL; } /* Queries can arrive in a couple different formats. */ switch (tds->in_flag) { case TDS_RPC: /* TODO */ case TDS_NORMAL: /* TDS5 query packet */ /* get the token */ token = tds_get_byte(tds); switch (token) { case TDS_LANGUAGE_TOKEN: /* SQL query */ len = tds_get_int(tds); /* query size +1 */ assert(len >= 1); /* TODO handle */ tds_get_byte(tds); /* has args, ignored TODO */ if (len > query_buflen) { query_buflen = len; query = (char *) realloc(query, query_buflen); } --len; tds_get_n(tds, query, len); query[len] = 0; return query; case TDS_DBRPC_TOKEN: /* RPC call -- make it look like a query */ /* skip the overall length */ (void)tds_get_smallint(tds); /* get the length of the stored procedure's name */ len = tds_get_byte(tds) + 1;/* sproc name size +1 */ if (len > query_buflen) { query_buflen = len; query = (char *) realloc(query, query_buflen); } /* * Read the chars of the name. Skip NUL * bytes, as a cheap way to convert * Unicode to ASCII. (For TDS7+, the * name is sent in Unicode.) */ for (i = j = 0; i < len - 1; i++) { byte = tds_get_byte(tds); if (byte != '\0') query[j++] = byte; } query[j] = '\0'; /* TODO: WE DON'T HANDLE PARAMETERS YET! */ /* eat the rest of the packet */ while (!tds_lastpacket(tds) && tds_read_packet(tds) > 0) { } return query; default: /* unexpected token */ /* eat the rest of the packet */ while (!tds_lastpacket(tds) && tds_read_packet(tds) > 0) { } return NULL; } break; case TDS_QUERY: /* TDS4 and TDS7+ fill the whole packet with a query */ len = 0; for (;;) { const char *src; /* If buffer needs to grow, then grow */ more = tds->in_len - tds->in_pos; src = (char *) (tds->in_buf + tds->in_pos); if ((size_t)(len + more + 1) > query_buflen) { query_buflen = len + more + 1024u; query_buflen -= query_buflen % 1024u; query = (char *)realloc(query, query_buflen); } /* * Pull new data into the query buffer. * Ignore NUL bytes -- this is a cheap way * to convert Unicode to Latin-1/ASCII. */ while (--more >= 0) { query[len] = *src++; if (query[len] != '\0') len++; } /* if more then read it */ if (tds_lastpacket(tds)) break; if (tds_read_packet(tds) < 0) return NULL; } /* add a NUL to mark the end */ query[len] = '\0'; return query; case TDS_CANCEL: /* * ignore cancel requests -- if we're waiting * for the next query then it's obviously too * late to cancel the previous query. */ /* TODO it's not too late -- freddy77 */ break; default: /* not a query packet */ return NULL; } } }