MySQL源码中处理客户端不同地址族的源码

ws2def.h文件中定义的结构:

typedef struct sockaddr_storage {

    ADDRESS_FAMILY ss_family;      // address family

 

    CHAR __ss_pad1[_SS_PAD1SIZE];  // 6 byte pad, this is to make

                                   //   implementation specific pad up to

                                   //   alignment field that follows explicit

                                   //   in the data structure

    __int64 __ss_align;            // Field to force desired structure

    CHAR __ss_pad2[_SS_PAD2SIZE];  // 112 byte pad to achieve desired size;

                                   //   _SS_MAXSIZE value minus size of

                                   //   ss_family, __ss_pad1, and

                                   //   __ss_align fields is 112

} SOCKADDR_STORAGE_LH, *PSOCKADDR_STORAGE_LH, FAR *LPSOCKADDR_STORAGE_LH;

微软说明:

The SOCKADDR_STORAGE structure stores socket address information. Since the SOCKADDR_STORAGE structure is sufficiently large to store address information for IPv4, IPv6, or other address families, its use promotes protocol-family and protocol-version independence and simplifies cross-platform development. Use the SOCKADDR_STORAGEstructure in place of the sockaddr structure.可用用来存储不同协议族的地址结构,在跨平台的环境中,用来代替sockaddr结构。sockaddr不能用于夸平台结构中吗?不会吧。

其中:

#define _SS_MAXSIZE 128                 // Maximum size

#define _SS_ALIGNSIZE (sizeof(__int64)) // Desired alignment

 

#if(_WIN32_WINNT >= 0x0600)

#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof(USHORT))

#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof(USHORT) + _SS_PAD1SIZE + _SS_ALIGNSIZE))

#else

#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof (short))

#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (short) + _SS_PAD1SIZE /

                                                    + _SS_ALIGNSIZE))

#endif //(_WIN32_WINNT >= 0x0600)

可以看出,整个sockaddr_storage结构大小为128个字节。

 

而在MySQLst_vio结构中对IP地址就是使用该结构,而不是以往使用的addr_in了,这是为了处理IPv4IPv6而定义的吧。

看看下面在MySQL中获取客户端的处理流程吧

通过客户端套接字à获取客户端sockaddr->获取客户端IP和端口

调用函数getpeername->getnameinfo

源码如下:

/**

  Return IP address and port of a VIO client socket.

 

  The function returns an IPv4 address if IPv6 support is disabled.

 

  The function returns an IPv4 address if the client socket is associated

  with an IPv4-compatible or IPv4-mapped IPv6 address. Otherwise, the native

  IPv6 address is returned.

*/

 

my_bool vio_peer_addr(Vio *vio, char *ip_buffer, uint16 *port,

                      size_t ip_buffer_size)

{

  DBUG_ENTER("vio_peer_addr");

  DBUG_PRINT("enter", ("Client socked fd: %d", (int) vio->sd));

 

  if (vio->localhost)//是本机的话,当然简单了

  {

    /*

      Initialize vio->remote and vio->addLen. Set vio->remote to IPv4 loopback

      address.

    */

    struct in_addr *ip4= &((struct sockaddr_in *) &(vio->remote))->sin_addr;

 

    vio->remote.ss_family= AF_INET;

    vio->addrLen= sizeof (struct sockaddr_in);

 

    ip4->s_addr= htonl(INADDR_LOOPBACK);

 

    /* Initialize ip_buffer and port. */

 

    strmov(ip_buffer, "127.0.0.1");

    *port= 0;

  }

  else

  {

    int err_code;

    char port_buffer[NI_MAXSERV];

 

    struct sockaddr_storage addr_storage;

    struct sockaddr *addr= (struct sockaddr *) &addr_storage;

    size_socket addr_length= sizeof (addr_storage);

 

    /* Get sockaddr by socked fd. */

 

    err_code= getpeername(vio->sd, addr, &addr_length);

 

    if (err_code)

    {

      DBUG_PRINT("exit", ("getpeername() gave error: %d", socket_errno));

      DBUG_RETURN(TRUE);

    }

 

    /* Normalize IP address. */

 

    vio_get_normalized_ip(addr, addr_length,

                          (struct sockaddr *) &vio->remote, &vio->addrLen);

 

    /* Get IP address & port number. */

 

    err_code= vio_getnameinfo((struct sockaddr *) &vio->remote,

                              ip_buffer, ip_buffer_size,

                              port_buffer, NI_MAXSERV,

                              NI_NUMERICHOST | NI_NUMERICSERV);

 

    if (err_code)

    {

      DBUG_PRINT("exit", ("getnameinfo() gave error: %s",

                          gai_strerror(err_code)));

      DBUG_RETURN(TRUE);

    }

 

    *port= (uint16) strtol(port_buffer, NULL, 10);

  }

 

  DBUG_PRINT("exit", ("Client IP address: %s; port: %d",

                      (const char *) ip_buffer,

                      (int) *port));

  DBUG_RETURN(FALSE);

}

 

针对不同协议族进行处理:

 

/**

  Convert a sock-address (AF_INET or AF_INET6) into the "normalized" form,

  which is the IPv4 form for IPv4-mapped or IPv4-compatible IPv6 addresses.

 

  @note Background: when IPv4 and IPv6 are used simultaneously, IPv4

  addresses may be written in a form of IPv4-mapped or IPv4-compatible IPv6

  addresses. That means, one address (a.b.c.d) can be written in three forms:

    - IPv4: a.b.c.d;

    - IPv4-compatible IPv6: ::a.b.c.d;

    - IPv4-mapped IPv4: ::ffff:a.b.c.d;

 

  Having three forms of one address makes it a little difficult to compare

  addresses with each other (the IPv4-compatible IPv6-address of foo.bar

  will be different from the IPv4-mapped IPv6-address of foo.bar).

 

  @note This function can be made public when it's needed.

 

  @param src        [in] source IP address (AF_INET or AF_INET6).

  @param src_length [in] length of the src.

  @param dst        [out] a buffer to store normalized IP address

                          (sockaddr_storage).

  @param dst_length [out] actual length of the normalized IP address.

*/

static void vio_get_normalized_ip(const struct sockaddr *src,

                                  int src_length,

                                  struct sockaddr *dst,

                                  int *dst_length)

{

  switch (src->sa_family) {

  case AF_INET:

    memcpy(dst, src, src_length);

    *dst_length= src_length;

    break;

 

#ifdef HAVE_IPV6

  case AF_INET6:

  {

    const struct sockaddr_in6 *src_addr6= (const struct sockaddr_in6 *) src;

    const struct in6_addr *src_ip6= &(src_addr6->sin6_addr);

    const uint32 *src_ip6_int32= (uint32 *) src_ip6->s6_addr;

 

    if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6))

    {

      struct sockaddr_in *dst_ip4= (struct sockaddr_in *) dst;

 

      /*判断该地址是否是从IPv4转换而来的,如果是,则转换成IPv4地址

        This is an IPv4-mapped or IPv4-compatible IPv6 address. It should

        be converted to the IPv4 form.

      */

 

      *dst_length= sizeof (struct sockaddr_in);

 

      memset(dst_ip4, 0, *dst_length);

      dst_ip4->sin_family= AF_INET;

      dst_ip4->sin_port= src_addr6->sin6_port;

 

      /*

        In an IPv4 mapped or compatible address, the last 32 bits represent

        the IPv4 address. The byte orders for IPv6 and IPv4 addresses are

        the same, so a simple copy is possible.

      */

      dst_ip4->sin_addr.s_addr= src_ip6_int32[3];

    }

    else

    {

      /* This is a "native" IPv6 address. */

 

      memcpy(dst, src, src_length);

      *dst_length= src_length;

    }

 

    break;

  }

#endif /* HAVE_IPV6 */

  }

}

IPv6地址结构:

typedef struct sockaddr_in6 {

    ADDRESS_FAMILY sin6_family; // AF_INET6.

    USHORT sin6_port;           // Transport level port number.

    ULONG  sin6_flowinfo;       // IPv6 flow information.

    IN6_ADDR sin6_addr;         // IPv6 address.

    union {

        ULONG sin6_scope_id;     // Set of interfaces for a scope.

        SCOPE_ID sin6_scope_struct;

    };

} SOCKADDR_IN6_LH, *PSOCKADDR_IN6_LH, FAR *LPSOCKADDR_IN6_LH;

 

 

下面就到了返回套接字地址的IP和端口了,MySQL函数调用如下:

/**

  This is a wrapper for the system getnameinfo(), because different OS

  differ in the getnameinfo() implementation:

    - Solaris 10 requires that the 2nd argument (salen) must match the

      actual size of the struct sockaddr_storage passed to it;

    - Mac OS X has sockaddr_in::sin_len and sockaddr_in6::sin6_len and

      requires them to be filled.

*/

 

int vio_getnameinfo(const struct sockaddr *sa,

                    char *hostname, size_t hostname_size,

                    char *port, size_t port_size,

                    int flags)

{

  int sa_length= 0;

 

  switch (sa->sa_family) {

  case AF_INET:

    sa_length= sizeof (struct sockaddr_in);

#ifdef HAVE_SOCKADDR_IN_SIN_LEN

    ((struct sockaddr_in *) sa)->sin_len= sa_length;

#endif /* HAVE_SOCKADDR_IN_SIN_LEN */

    break;

 

#ifdef HAVE_IPV6

  case AF_INET6:

    sa_length= sizeof (struct sockaddr_in6);

# ifdef HAVE_SOCKADDR_IN6_SIN6_LEN

    ((struct sockaddr_in6 *) sa)->sin6_len= sa_length;

# endif /* HAVE_SOCKADDR_IN6_SIN6_LEN */

    break;

#endif /* HAVE_IPV6 */

  }

 

  return getnameinfo(sa, sa_length,

                     hostname, hostname_size,

                     port, port_size,

                     flags);//使用该函数完成调用

}

 

 个人学习笔记,可能写得较乱,哈哈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值