• Mysql 源码阅读(二)登录连接调试


    简介

    在上篇中我们将源码在本地顺利的跑了起来,接下来我们需要连接我们的数据库,为后面的工作做一定的准备

    使用docker容器尝试连接

    我们首先使用docker容器去启动连接我们上篇文章中成功运行起来的MySQL服务器,但没能如愿,没有连接成功

    下面的错误,以前我们也是经常见到了

    PS C:\Users\lw> docker run -ti mysql mysql -h 192.168.1.4 -P 3306 -u root -p
    Enter password:
    ERROR 1130 (HY000): Host 'DESKTOP-8U69O9P' is not allowed to connect to this MySQL server
    
    • 1
    • 2
    • 3

    我们使用: is not allowed 去全局搜索下代码,找到下面的代码,通过打上断点,也走到了这步里面

    成功的找到了代码入口

    bool acl_check_host(const char *host, const char *ip)
    {
      mysql_mutex_lock(&acl_cache->lock);
      if (allow_all_hosts)
      {
        
        mysql_mutex_unlock(&acl_cache->lock);
        return 0;
      }
    
      if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
          (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
      {
        mysql_mutex_unlock(&acl_cache->lock);
        return 0;                                   // Found host
      }
      for (ACL_HOST_AND_IP *acl= acl_wild_hosts->begin();
           acl != acl_wild_hosts->end(); ++acl)
      {
        if (acl->compare_hostname(host, ip))
        {
          mysql_mutex_unlock(&acl_cache->lock);
          return 0;                                 // Host ok
        }
      }
      mysql_mutex_unlock(&acl_cache->lock);
      if (ip != NULL)
      {
        /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
        Host_errors errors;
        errors.m_host_acl= 1;
        inc_host_errors(ip, &errors);
      }
      return 1;                                     // Host is not allowed
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    通过调试,我们发现acl_wild_hosts里面没有值导致的,导致ip==null,从而host没有匹配上,不允许连接

    我们在当前文件中没有搜索到acl_wild_hosts,只能在整个解决方案中进行搜索,我们定位到下面一段代码:

    static void init_check_host(void)
    {
      DBUG_ENTER("init_check_host");
      if (acl_wild_hosts != NULL)
        acl_wild_hosts->clear();
      else
        acl_wild_hosts=
          new Prealloced_array<ACL_HOST_AND_IP, ACL_PREALLOC_SIZE>(key_memory_acl_mem);
    
      size_t acl_users_size= acl_users ? acl_users->size() : 0;
    
      (void) my_hash_init(&acl_check_hosts,system_charset_info,
                          acl_users_size, 0, 0,
                          (my_hash_get_key) check_get_key, 0, 0,
                          key_memory_acl_mem);
      if (acl_users_size && !allow_all_hosts)
      {
        for (ACL_USER *acl_user= acl_users->begin();
             acl_user != acl_users->end(); ++acl_user)
        {
          if (acl_user->host.has_wildcard())
          {                                         // Has wildcard
            ACL_HOST_AND_IP *acl= NULL;
            for (acl= acl_wild_hosts->begin(); acl != acl_wild_hosts->end(); ++acl)
            {                                       // Check if host already exists
              if (!my_strcasecmp(system_charset_info,
                                 acl_user->host.get_host(), acl->get_host()))
                break;                              // already stored
            }
            if (acl == acl_wild_hosts->end())       // If new
              acl_wild_hosts->push_back(acl_user->host);
          }
          else if (!my_hash_search(&acl_check_hosts,(uchar*)
                                   acl_user->host.get_host(),
                                   acl_user->host.get_host_len()))
          {
            if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
            {                                       // End of memory
              allow_all_hosts=1;                    // Should never happen
              DBUG_VOID_RETURN;
            }
          }
        }
      }
      acl_wild_hosts->shrink_to_fit();
      freeze_size(&acl_check_hosts.array);
      DBUG_VOID_RETURN;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    从名称和功能上看出,这个就是acl_check_hosts的初始化相关代码

    其中acl_users通过断点调试,有三个用户:

    • root:我们的经常用到的初始用户
    • mysql.session: 这个还真没见过
    • mysql.sys: 这个也没有见过

    这个函数看下来,大致有两个地方会导致acl_check_hosts的增加:

    (void) my_hash_init(&acl_check_hosts,system_charset_info,
                          acl_users_size, 0, 0,
                          (my_hash_get_key) check_get_key, 0, 0,
                          key_memory_acl_mem);
    
    • 1
    • 2
    • 3
    • 4

    上面这个是初始化,从里面的参数没有找到什么新增的

    下面一段代码是明显新增的,但调试中,代码一直走不进去这段逻辑

    if (acl == acl_wild_hosts->end())       // If new
              acl_wild_hosts->push_back(acl_user->host);
    
    • 1
    • 2

    而走不下去的第一步就是

    if (acl_user->host.has_wildcard())
    
    bool has_wildcard()
      {
        return (strchr(hostname,wild_many) ||
                strchr(hostname,wild_one)  || ip_mask );
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    strchr是一个字符串搜索函数:该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。

    其中的wild_many是我们熟悉的%,而wild_one是"_",而hostname是localhost,所以没有匹配上

    都走不下去,那我们只能看看acl_user的相关代码了

    很幸运,在当前文件中,我们找到了acl_users的初始化函数:

    static my_bool acl_load(THD *thd, TABLE_LIST *tables)
    
    // 函数代码太长了,先粘贴一个关键的
    acl_users->push_back(user);
    
    • 1
    • 2
    • 3
    • 4

    而host的初始化在这步:

    user.host.update_hostname(get_field(&global_acl_memory,
                                          table->field[table_schema->host_idx()]));
    
    • 1
    • 2

    看着是从表中读取一些数据,有点头大,搞不动了,看不懂(超过认知负载了,我们继续挖下去也挖不动了,只能先往上跳一跳)

    使用mysql workbench进行连接

    为了避免一直卡住不动了,我们还是直接本地安装一个mysql workbench,然后使用localhost的域名和root用户进行连接

    如果忘记了初始密码,想要再初始化一次,可能会遇到下面的错误:

    2022-06-25T00:28:59.791538Z 0 [ERROR] --initialize specified but the data directory has files in it. Aborting.
    
    • 1

    我们把源码根目录下的: release/sql/data 文件夹删除即可

    初次链接的使用需要重置密码,重置完成后,就链接上了

    然后我们执行下面的脚本,赋予root其他ip的连接权限

    SET SQL_SAFE_UPDATES = 0;
    use mysql;
    update user set host='%' where user='root';
    flush privileges;
    
    • 1
    • 2
    • 3
    • 4

    在我们运行上面的SQL的时候,用户的初始化和host检测的初始化,都重新执行一遍

    再次使用mysql容器进行连接

    然后我们再次运行命令进行连接

    docker run -ti mysql mysql -h 192.168.1.4 -P 3306 -u root -p
    
    • 1

    通过调试,我们发现现在是直接:allow_all_hosts 直接允许访问了

    bool acl_check_host(const char *host, const char *ip)
    {
      mysql_mutex_lock(&acl_cache->lock);
      if (allow_all_hosts)
      {
        
        mysql_mutex_unlock(&acl_cache->lock);
        return 0;
      }
      ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过查看代码是在这里设置的,但也看不是太懂,留待日后,今天看的也有点晕了

    static void init_check_host(void)
    {
    ......
      if (acl_users_size && !allow_all_hosts)
      {
          {
            if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
            {                                       // End of memory
              // 这里直接设置了
              allow_all_hosts=1;                    // Should never happen
              DBUG_VOID_RETURN;
            }
          }
        }
      }
    ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    然后我们就成功访问了

    总结

    今天吧登录认证的代码走了一遍,对MySQL的连接认证有了一个初步的认知,但还是有很多的细节不清楚

    使用docker容器连接成功后,我们一直debug下去,发现了tcp连接的监听处理函数:

    extern "C" void *handle_connection(void *arg)
    {
        // 连接准备,今天的host检测和用户认证都是在这个函数里面进行的
        if (thd_prepare_connection(thd))
          handler_manager->inc_aborted_connects();
        else
        {
          while (thd_connection_alive(thd))
          {
    	// 看名字就知道是具体命令的处理函数,嘿嘿,找到了下篇文章的函数入口
            if (do_command(thd))
              break;
          }
          end_connection(thd);
        }
        close_connection(thd, 0, false, false);
        ······
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参考链接

  • 相关阅读:
    nohup命令后台启动jar包
    【考研数学】二. 一元函数积分学
    python面试题(36~50)
    信号与线性系统分析(吴大正,郭宝龙)(信号的分类)
    记录一次 添加脚本的记录+改错记录
    牛客在线编程101-91 反转字符串
    【API要返回一棵树的结构】数据库表结构是平铺的数据,但是api要实现树状结构展示。api实现一棵树的结构,如何实现呢,递归?如何递归呢
    Zotero(1)---文献管理软件Zotero安装教程
    【AGC】云托管新建站点时间过长的问题排查方法
    【C++题解】1632. 需要几辆车
  • 原文地址:https://blog.csdn.net/github_35735591/article/details/125455276