void tds_send_login_ack(TDSSOCKET * tds, const char *progname) { TDS_UINT ui, version; tds_put_byte(tds, TDS_LOGINACK_TOKEN); tds_put_smallint(tds, 10 + (IS_TDS7_PLUS(tds->conn)? 2 : 1) * strlen(progname)); /* length of message */ if (IS_TDS50(tds->conn)) { tds_put_byte(tds, 5); version = 0x05000000u; } else { tds_put_byte(tds, 1); /* see src/tds/token.c */ if (IS_TDS73_PLUS(tds->conn)) { version = 0x730B0003u; } else if (IS_TDS72_PLUS(tds->conn)) { version = 0x72090002u; } else if (IS_TDS71_PLUS(tds->conn)) { version = tds->conn->tds71rev1 ? 0x07010000u : 0x71000001u; } else { version = (TDS_MAJOR(tds->conn) << 24) | (TDS_MINOR(tds->conn) << 16); } } TDS_PUT_A4BE(&ui, version); tds_put_n(tds, &ui, 4); tds_put_byte(tds, strlen(progname)); /* FIXME ucs2 */ tds_put_string(tds, progname, strlen(progname)); /* server version, always big endian */ TDS_PUT_A4BE(&ui, tds->conn->product_version & 0x7fffffffu); tds_put_n(tds, &ui, 4); }
void pool_mbr_init(TDS_POOL * pool) { TDS_POOL_MEMBER *pmbr; int i; /* allocate room for pool members */ pool->members = (TDS_POOL_MEMBER *) calloc(pool->num_members, sizeof(TDS_POOL_MEMBER)); /* open connections for each member */ for (i = 0; i < pool->num_members; i++) { pmbr = &pool->members[i]; pmbr->state = TDS_IDLE; if (i >= pool->min_open_conn) continue; pmbr->tds = pool_mbr_login(pool); pmbr->last_used_tm = time(NULL); if (!pmbr->tds) { fprintf(stderr, "Could not open initial connection %d\n", i); exit(1); } if (!IS_TDS71_PLUS(pmbr->tds->conn)) { fprintf(stderr, "Current pool implementation does not support protocol versions former than 7.1\n"); exit(1); } } }
/* * pool_find_idle_member * returns the first pool member in TDS_IDLE state */ TDS_POOL_MEMBER * pool_find_idle_member(TDS_POOL * pool) { int i, active_members; TDS_POOL_MEMBER *pmbr; active_members = 0; for (i = 0; i < pool->num_members; i++) { pmbr = &pool->members[i]; if (pmbr->tds) { active_members++; if (pmbr->current_user == NULL) { /* * make sure member wasn't idle more that the timeout * otherwise it'll send the query and close leaving a * hung client */ pmbr->last_used_tm = time(NULL); return pmbr; } } } /* if we have dead connections we can open */ if (active_members < pool->num_members) { pmbr = NULL; for (i = 0; i < pool->num_members; i++) { pmbr = &pool->members[i]; if (!pmbr->tds) { fprintf(stderr, "No open connections left, opening member number %d\n", i); pmbr->tds = pool_mbr_login(pool); if (pmbr->tds && !IS_TDS71_PLUS(pmbr->tds->conn)) { tds_free_socket(pmbr->tds); pmbr->tds = NULL; } pmbr->last_used_tm = time(NULL); break; } } if (pmbr) return pmbr; } fprintf(stderr, "No idle members left, increase MAX_POOL_CONN\n"); return NULL; }
/* * 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; }
bool pool_user_send_login_ack(TDS_POOL * pool, TDS_POOL_USER * puser) { char msg[256]; char block[32]; TDSSOCKET *tds = puser->sock.tds, *mtds = puser->assigned_member->sock.tds; TDSLOGIN *login = puser->login; const char *database; const char *server = mtds->conn->server ? mtds->conn->server : "JDBC"; bool dbname_mismatch, odbc_mismatch; pool->user_logins++; /* copy a bit of information, resize socket with block */ tds->conn->tds_version = mtds->conn->tds_version; tds->conn->product_version = mtds->conn->product_version; memcpy(tds->conn->collation, mtds->conn->collation, sizeof(tds->conn->collation)); tds->conn->tds71rev1 = mtds->conn->tds71rev1; free(tds->conn->product_name); tds->conn->product_name = strdup(mtds->conn->product_name); tds_realloc_socket(tds, mtds->conn->env.block_size); tds->conn->env.block_size = mtds->conn->env.block_size; tds->conn->client_spid = mtds->conn->spid; /* if database is different use USE statement */ database = pool->database; dbname_mismatch = !tds_dstr_isempty(&login->database) && strcasecmp(tds_dstr_cstr(&login->database), database) != 0; odbc_mismatch = (login->option_flag2 & TDS_ODBC_ON) == 0; if (dbname_mismatch || odbc_mismatch) { char *str; int len = 128 + tds_quote_id(mtds, NULL, tds_dstr_cstr(&login->database),-1); TDSRET ret; if ((str = (char *) malloc(len)) == NULL) return false; str[0] = 0; /* swicth to dblib options */ if (odbc_mismatch) strcat(str, "SET ANSI_DEFAULTS OFF\nSET CONCAT_NULL_YIELDS_NULL OFF\n"); if (dbname_mismatch) { strcat(str, "USE "); tds_quote_id(mtds, strchr(str, 0), tds_dstr_cstr(&login->database), -1); } ret = tds_submit_query(mtds, str); free(str); if (TDS_FAILED(ret) || TDS_FAILED(tds_process_simple_query(mtds))) return false; if (dbname_mismatch) database = tds_dstr_cstr(&login->database); else database = mtds->conn->env.database; } // 7.0 // env database // database change message (with server name correct) // env language // language change message // env 0x3 charset ("iso_1") // env 0x5 lcid ("1033") // env 0x6 ("196609" ?? 0x30001) // loginack // env 0x4 packet size // done // // 7.1/7.2/7.3 // env database // database change message (with server name correct) // env 0x7 collation // env language // language change message // loginack // env 0x4 packet size // done tds->out_flag = TDS_REPLY; tds_env_change(tds, TDS_ENV_DATABASE, "master", database); sprintf(msg, "Changed database context to '%s'.", database); tds_send_msg(tds, 5701, 2, 0, msg, server, NULL, 1); if (!login->suppress_language) { tds_env_change(tds, TDS_ENV_LANG, NULL, "us_english"); tds_send_msg(tds, 5703, 1, 0, "Changed language setting to 'us_english'.", server, NULL, 1); } if (IS_TDS71_PLUS(tds->conn)) { tds_put_byte(tds, TDS_ENVCHANGE_TOKEN); tds_put_smallint(tds, 8); tds_put_byte(tds, TDS_ENV_SQLCOLLATION); tds_put_byte(tds, 5); tds_put_n(tds, tds->conn->collation, 5); tds_put_byte(tds, 0); } tds_send_login_ack(tds, mtds->conn->product_name); sprintf(block, "%d", tds->conn->env.block_size); tds_env_change(tds, TDS_ENV_PACKSIZE, block, block); /* tds_send_capabilities_token(tds); */ tds_send_done_token(tds, 0, 0); /* send it! */ tds_flush_packet(tds); tds_free_login(login); puser->login = NULL; return true; }
/* * 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; }