您的位置:首页 > 数据库 > MySQL

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

2011-06-18 12:15 183 查看
在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个字节。

而在MySQL中st_vio结构中对IP地址就是使用该结构,而不是以往使用的addr_in了,这是为了处理IPv4和IPv6而定义的吧。
看看下面在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 H***E_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 /* H***E_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 H***E_SOCKADDR_IN_SIN_LEN
((struct sockaddr_in *) sa)->sin_len= sa_length;
#endif /* H***E_SOCKADDR_IN_SIN_LEN */
break;

#ifdef H***E_IPV6
case AF_INET6:
sa_length=
sizeof (struct sockaddr_in6);
# ifdef H***E_SOCKADDR_IN6_SIN6_LEN
((struct sockaddr_in6 *) sa)->sin6_len= sa_length;
# endif /* H***E_SOCKADDR_IN6_SIN6_LEN
*/
break;
#endif /* H***E_IPV6 */
}

return getnameinfo(sa,
sa_length,
hostname,
hostname_size,
port,
port_size,
flags);//使用该函数完成调用
}

个人学习笔记,可能写得较乱,哈哈
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐