otl rlogon(...)连接失败的问题分析

某个客户现场部署的数据库是Oracle的,历史遗留原因,Oracle密码在配置文件里是明文的,客户要求修改为密文,修改后交由客户验证,客户反馈修改密码后,终端上报审计信息插入Oracle时,模块崩溃了,按理说密码即使不对或者解析问题也不会崩溃

然后我们看下代码流程:
主线版本如果set之后是需要调用connect()方法去校验下密码是否能成功连接odbc

	HOTLDBMgr::ins().set(m_config.mstr_username,m_config.mstr_userpasswd,m_config.mstr_dsn);
	int i_con = -1;
	while (--i_try_connect_times && i_con != 0)
	{
		i_con = HOTLDBMgr::ins().connect();
		if (i_con != 0)
		{
			HEnvironment::Sleep(6000);
		}
	}

包含Oracle的分支代码只set(),没有connect()校验,至于为什么没有去connect(),历史遗留代码不太清楚,先不管这个

	HString strIsDec;
	ini.read_string(L"Common", L"OraclePasswdBase64", strIsDec);
	if (strIsDec.is_equal(L"true"))
	{
		string strDecPw = HBase64::decode(str_password_oracle.get_ice_str());
		str_password_oracle.make_by_ice_str(strDecPw);
	}

	HOTLDBMgrOracle::ins().set(str_username_oracle,str_password_oracle,str_dsn_oracle);

看下具体流程

//1 终端上报审计信息 server去入库
HOTLStreamOracle* p_stream =  HOTLDBMgrOracle::ins().do_exec_sql(strSql,UNISERVER_DB_OP_BUFFER);

//2 do_exec_sql里面调用了 get_connect()
oracle::otl_connect* p_connect = get_connect(ui_id , str_sql);

//3 get_connect里面调用了get_new_connect()
oracle::otl_connect* p_connect = get_new_connect();

//4 get_new_connect里面调用了 rlogon()
oracle::otl_connect* mp_connect = 0;
HString str_login = get_login_str();
mp_connect = new oracle::otl_connect;
mp_connect->rlogon(str_login.get_str_direct().c_str()); // connect to ODBC

//5 根据堆栈和1的调用 可以知道当密码校验失败后返回的是空指针 这里的逻辑是失败后(密码校验失败,连接断开等情况) 
//保存这些sql内容到文件中等后面重新尝试插入(重启或者定时执行) 
//但是发现在失败后保存sql的逻辑里面又使用了空指针p_stream这种操作 *p_stream << 1;导致的崩溃
if(p_stream != NULL)
{
	//执行入库	
	...		
}
else
{
	//失败保存记录
	b_need_save = true;
}

if(b_need_save)
{		
		//保存记录 
		...
		
		if (b_need_notify)
		{
			*p_stream << 1;
			//insert_fail_record.begin() << 1; 应该使用这个
		}
		else
		{
			*p_stream << 0;
			//insert_fail_record.begin() << 0;应该使用这个
		}
		insert_fail_record.end();
}

以上,可能是复制代码忘记改了,估计测试也没有进入到这个分支过,才遗留了这么个问题

但是密码为什么校验失败了,我们解密出来的密码事实上是正确的,这才是我们要关注的地方

//查看日志文件发现了这样的报错
[1][2023-03-22][11:01:29][T3909064448][HOTLDBMgrOracle.h   ][2095][W]connect error Msg:ORA-12154: TNS:could not resolve the connect identifier specified
, STM:, State:, Var: ,take time 2 ms

//然后我们查看对应行数的代码,发现上面的第4点 
HString str_login = get_login_str();
//get_login_str()中有这样的一行代码
str_login = mstr_user + L"/" + mstr_pass + L"@" + str_server_info;

这里是把账号密码用这种形式拼接起来调用rlogon()去连接odbc的 user/password@DSN
再结合客户设置的密码中包含’@‘符号 能猜出个大概了 可能是rlogon()里面处理的时候会根据这些符号进行切割
设置的密码中含有’@',导致切割出来的字符串有误 从而密码校验失败

rlogon()这个封装在最底层的接口是otl提供的,查看官网接口文档
https://otl.sourceforge.net/otl3_connect_class.htm
我们发现了这一段话
在这里插入图片描述
rlogon()接口非常的通用,并且提供的格式就是上述所说的 user/password@DSN ,并且如果密码中含有’@'符号,需要使用这样格式userid/pass\\@word@DSN才行

接着我们查看otl中rlogon()的实现来作为验证
不需要看实现细节,也能看出来根据’/’ '@'符号去切割的,当然官网也提供了更多的重载的接口(rlogon),并非一定要使用这种格式的接口,但是这种接口是更通用的,对于连接不同类型的数据库来说

至此,问题已经清晰。底层封装了rlogon()接口,用户密码DSN必须使用特定格式否则otl内部无法成功解析而导致连接odbc失败。密码中可以携带’@‘,但是前面需要加上’\\’

  OTL_NODISCARD int rlogon(const char *connect_str, const int
#ifndef OTL_ODBC_MYSQL
                                          auto_commit
#endif
             ) {
    char username[256];
    char passwd[256];
    char tnsname[1024];
    char *tnsname_ptr = nullptr;
    char *c = OTL_CCAST(char *, connect_str);
    char *username_ptr = username;
    char *passwd_ptr = passwd;
    char temp_connect_str[512];

    if (extern_lda) {
      extern_lda = false;
      henv = OTL_SQL_NULL_HANDLE_VAL;
      hdbc = OTL_SQL_NULL_HANDLE_VAL;
    }
    memset(username, 0, sizeof(username));
    memset(passwd, 0, sizeof(passwd));
    memset(tnsname, 0, sizeof(tnsname));

    char *c1 = OTL_CCAST(char *, connect_str);
    int oracle_format = 0;
    char prev_c = ' ';
    while (*c1) {
      if (*c1 == '@' && prev_c != '\\') {
        oracle_format = 1;
        break;
      }
      prev_c = *c1;
      ++c1;
    }

    if (oracle_format) {
      while (*c && *c != '/' && (OTL_SCAST(unsigned, username_ptr - username) <
                                 sizeof(username) - 1)) {
        *username_ptr = *c;
        ++c;
        ++username_ptr;
      }
      *username_ptr = 0;

      if (*c == '/')
        ++c;
      prev_c = ' ';
      while (*c && !(*c == '@' && prev_c != '\\') &&
             (OTL_SCAST(unsigned, passwd_ptr - passwd) < sizeof(passwd) - 1)) {
        if (prev_c == '\\')
          --passwd_ptr;
        *passwd_ptr = *c;
        prev_c = *c;
        ++c;
        ++passwd_ptr;
      }
      *passwd_ptr = 0;

      if (*c == '@') {
        ++c;
        tnsname_ptr = tnsname;
        while (*c && (OTL_SCAST(unsigned, tnsname_ptr - tnsname) <
                      sizeof(tnsname) - 1)) {
          *tnsname_ptr = *c;
          ++c;
          ++tnsname_ptr;
        }
        *tnsname_ptr = 0;
      }
    } else {
      c1 = OTL_CCAST(char *, connect_str);
      char *c2 = temp_connect_str;
      while (*c1 && (OTL_SCAST(unsigned, c2 - temp_connect_str) <
                     sizeof(temp_connect_str) - 1)) {
        *c2 = otl_to_upper(*c1);
        ++c1;
        ++c2;
      }
      *c2 = 0;
      c1 = temp_connect_str;
      char entry_name[256];
      char entry_value[256];
      while (*c1 && (OTL_SCAST(unsigned, c1 - temp_connect_str) <
                     sizeof(temp_connect_str) - 1)) {
        c2 = entry_name;
        while (*c1 && *c1 != '=' &&
               (OTL_SCAST(unsigned, c1 - temp_connect_str) <
                sizeof(temp_connect_str) - 1)) {
          *c2 = *c1;
          ++c1;
          ++c2;
        }
        *c2 = 0;
#if defined(_MSC_VER) && (_MSC_VER == 1600)
        entry_name[c2 - entry_name] = 0;
#endif
        if (*c1)
          ++c1;
        c2 = entry_value;
        prev_c = ' ';
        while (*c1 && *c1 != ';' && (OTL_SCAST(unsigned, c2 - entry_value) <
                                     sizeof(entry_value) - 1)) {
          if (prev_c == '\\')
            --c2;
          *c2 = *c1;
          prev_c = *c1;
          ++c1;
          ++c2;
        }
        *c2 = 0;
#if defined(_MSC_VER) && (_MSC_VER == 1600)
        entry_value[c2 - entry_value] = 0;
#endif
        if (*c1)
          ++c1;
        if (strcmp(entry_name, "DSN") == 0)
          OTL_STRCPY_S(tnsname, sizeof(tnsname), entry_value);
        if (strcmp(entry_name, "UID") == 0)
          OTL_STRCPY_S(username, sizeof(username), entry_value);
        if (strcmp(entry_name, "PWD") == 0)
          OTL_STRCPY_S(passwd, sizeof(passwd), entry_value);
      }
    }
#ifndef OTL_ODBC_MYSQL
    OTL_TRACE_RLOGON_ODBC(0x1, "otl_connect", "rlogon", tnsname, username,
                          passwd, auto_commit)
#else
    OTL_TRACE_RLOGON_ODBC(0x1, "otl_connect", "rlogon", tnsname, username,
                          passwd, 0)
#endif
    if (henv == OTL_SQL_NULL_HANDLE_VAL || hdbc == OTL_SQL_NULL_HANDLE_VAL) {
#if (ODBCVER >= 0x0300)
      status = SQLAllocHandle(SQL_HANDLE_ENV, OTL_SQL_NULL_HANDLE_VAL, &henv);
#else
      status = SQLAllocEnv(&henv);
#endif
      if (status != SQL_SUCCESS && status != SQL_SUCCESS_WITH_INFO)
        return 0;

#if (ODBCVER >= 0x0300)
      status = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
                             OTL_RCAST(void *, SQL_OV_ODBC3), SQL_NTS);
      if (status != SQL_SUCCESS && status != SQL_SUCCESS_WITH_INFO)
        return 0;
#endif

#if (ODBCVER >= 0x0300)
      status = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
#else
      status = SQLAllocConnect(henv, &hdbc);
#endif
      if (status != SQL_SUCCESS && status != SQL_SUCCESS_WITH_INFO)
        return 0;
    } else
      status = SQL_SUCCESS;

#ifndef OTL_ODBC_MYSQL
#if (ODBCVER >= 0x0300)
    if (auto_commit)
      status = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT,
                                 OTL_RCAST(SQLPOINTER, SQL_AUTOCOMMIT_ON),
                                 SQL_IS_POINTER);
    else
      status = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT,
#if defined(OTL_ANSI_CPP_11_NULLPTR_SUPPORT)
                                 nullptr,
#else
                                 OTL_RCAST(SQLPOINTER, SQL_AUTOCOMMIT_OFF),
#endif
                                 SQL_IS_POINTER);
#else
    if (auto_commit)
      status = SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, 1);
    else
      status = SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, 0);
#endif
    if (status != SQL_SUCCESS && status != SQL_SUCCESS_WITH_INFO)
      return 0;
#endif
#if (ODBCVER >= 0x0300)
    if (timeout > 0)
      status =
          SQLSetConnectAttr(hdbc, SQL_ATTR_LOGIN_TIMEOUT,
                            OTL_RCAST(void *, OTL_SCAST(size_t, timeout)), 0);
#else
    if (timeout > 0)
      status = SQLSetConnectOption(hdbc, 
                                   SQL_LOGIN_TIMEOUT, 
                                   OTL_SCAST(size_t,timeout));
#endif
    if (status != SQL_SUCCESS && status != SQL_SUCCESS_WITH_INFO)
      return 0;

#if defined(OTL_DB2_CLI)
    status = SQLSetConnectAttr(hdbc, SQL_ATTR_LONGDATA_COMPAT,
                               OTL_RCAST(SQLPOINTER, SQL_LD_COMPAT_YES),
                               SQL_IS_INTEGER);
    if (status != SQL_SUCCESS && status != SQL_SUCCESS_WITH_INFO)
      return 0;
#endif

#if defined(OTL_ENABLE_MSSQL_MARS)
#if !defined(OTL_DB2_CLI) && (ODBCVER >= 0x0300)
    status = SQLSetConnectAttr(hdbc, OTL_SQL_COPT_SS_MARS_ENABLED,
                               OTL_RCAST(SQLPOINTER, OTL_SQL_MARS_ENABLED_YES),
                               SQL_IS_UINTEGER);
    if (status != SQL_SUCCESS_WITH_INFO && status != SQL_SUCCESS)
      return 0;
#endif
#endif

    if (oracle_format) {
#if defined(OTL_ODBC_zOS)
      if (tnsname[0] == 0 && username[0] == 0 && passwd[0] == 0) {
        status = SQLConnect(hdbc, 0L, SQL_NTS, 0L, SQL_NTS, 0L, SQL_NTS);
        logoff_commit = false;
      } else
        status = SQLConnect(hdbc, OTL_RCAST(unsigned char *, tnsname), SQL_NTS,
                            OTL_RCAST(unsigned char *, username), SQL_NTS,
                            OTL_RCAST(unsigned char *, passwd), SQL_NTS);
#else

#if defined(UNICODE) || defined(_UNICODE)
      {
        SQLWCHAR *temp_tnsname = new SQLWCHAR[strlen(tnsname) + 1];
        SQLWCHAR *temp_username = new SQLWCHAR[strlen(username) + 1];
        SQLWCHAR *temp_passwd = new SQLWCHAR[strlen(passwd) + 1];
        otl_convert_char_to_SQLWCHAR_2(temp_tnsname,
                                       OTL_RCAST(unsigned char *, tnsname));
        otl_convert_char_to_SQLWCHAR_2(temp_username,
                                       OTL_RCAST(unsigned char *, username));
        otl_convert_char_to_SQLWCHAR_2(temp_passwd,
                                       OTL_RCAST(unsigned char *, passwd));
        status = SQLConnect(hdbc, temp_tnsname, SQL_NTS, temp_username, SQL_NTS,
                            temp_passwd, SQL_NTS);
        delete[] temp_tnsname;
        delete[] temp_username;
        delete[] temp_passwd;
      }
#else
      status = SQLConnect(hdbc, OTL_RCAST(unsigned char *, tnsname), SQL_NTS,
                          OTL_RCAST(unsigned char *, username), SQL_NTS,
                          OTL_RCAST(unsigned char *, passwd), SQL_NTS);
#endif

#endif
    } else {
      char *tc2 = temp_connect_str;
      const char *tc1 = connect_str;
      prev_c = ' ';
      while (*tc1 && (OTL_SCAST(unsigned, tc2 - temp_connect_str) <
                      sizeof(temp_connect_str) - 1)) {
        if (*tc1 == '@' && prev_c == '\\')
          --tc2;
        *tc2 = *tc1;
        prev_c = *tc1;
        ++tc1;
        ++tc2;
      }
      *tc2 = 0;
#if defined(_MSC_VER) && (_MSC_VER == 1600)
      temp_connect_str[tc2 - temp_connect_str] = 0;
#endif
      SQLSMALLINT out_len = 0;
#if (defined(UNICODE) || defined(_UNICODE))
      {
        size_t len = strlen(temp_connect_str);
        SQLWCHAR *temp_connect_str2 = new SQLWCHAR[len + 1];
        SQLWCHAR out_str[2048];
        otl_convert_char_to_SQLWCHAR_2(
            temp_connect_str2, OTL_RCAST(unsigned char *, temp_connect_str));
        status = SQLDriverConnect(
            hdbc, nullptr, temp_connect_str2, OTL_SCAST(short, len), out_str,
            OTL_SCAST(OTL_SQLSMALLINT, sizeof(out_str) / sizeof(SQLWCHAR)),
            &out_len, SQL_DRIVER_NOPROMPT);
        delete[] temp_connect_str2;
      }
#else
      SQLCHAR out_str[2048];
      status = SQLDriverConnect(
          hdbc, nullptr,
          OTL_RCAST(SQLCHAR *, OTL_CCAST(char *, temp_connect_str)),
          OTL_SCAST(short, strlen(temp_connect_str)), out_str,
          OTL_SCAST(SQLSMALLINT, sizeof(out_str)), &out_len,
          SQL_DRIVER_NOPROMPT);
#endif
    }

    if (status == SQL_SUCCESS_WITH_INFO || status == SQL_SUCCESS)
      return 1;
    else
      return 0;
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值