亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

高級進程間通信之傳送文件描述符

2024-06-28 13:27:47
字體:
來源:轉載
供稿:網友
高級進程間通信之傳送文件描述符

在進程間傳送打開的文件描述符的能力是非常有用的,可以用它對客戶進程/服務器進程應用進行不同的設計。它使一個進程(一般是服務器進程)能夠處理為打開一個文件所要求的一切操作(具體如將網絡名翻譯為網絡地址、撥號調制解調器、協商文件鎖等)以及向調用進程送回一描述符,該描述符可被用于以后的所有I/O函數。涉及打開文件或設備的所有細節對客戶進程而言都是隱藏的。

下面進一步說明從一個進程向另一個進程“傳送一打開的文件描述符”的含義?;貞沨ttp://www.CUOXin.com/nufangrensheng/p/3498509.html中的圖3-2,其中顯示了兩個進程,它們打開了同一文件。雖然它們共享同一v節點表,但每個進程都有它自己的文件表項。

當一個進程向另一個進程傳送一打開的文件描述符時,我們想要發送進程和接收進程共享同一文件表項。圖17-8顯示了所希望的安排。

未命名

                                   圖17-8 從頂部進程傳送一個打開的文件至底部進程

在技術上,發送進程實際上向接收進程傳送一個指向一打開文件表項的指針。該指針被分配存放在接收進程的第一個可用描述符項中。(注意,不要造成錯覺,以為發送進程和接收進程中的描述符編號是相同的,通常它們是不同的。)兩個進程共享同一打開文件表項,在這一點上與fork之后,父、子進程共享打開文件表項的情況完全相同(參考http://www.CUOXin.com/nufangrensheng/p/3509492.html中圖8-1所示)。

當發送進程將描述符傳送給接收進程后,通常它關閉該描述符。發送進程關閉該描述符并不造成關閉該文件或設備,其原因是該描述符對應的文件仍被視為由接收者進程打開(即使接收進程尚未接收到該描述符)。

下面定義三個函數以發送和接收文件描述符。本節將會給出對于STREAMS和套接字的這三個函數的不同實現代碼。

#include "apue.h"int send_fd(int fd, int fd_to_send);int send_err(int fd, int status, const char *errmsg);兩個函數返回值:若成功則返回0,出錯則返回-1int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t));返回值:若成功則返回文件描述符,出錯則返回負值

當一個進程(通常是服務器進程)希望將一個描述符傳送給另一個進程時,它調用send_fd或send_err。等待接收描述符的進程(客戶進程)調用recv_fd。

send_fd經由fd代表的STREAMS管道或UNIX域套接字發送描述符fd_to_send。

send_err函數用fd發送errmsg以及后隨的status字節。status的值應在-1到-255之間。

客戶進程調用recv_fd接收一描述符。如果一切正常(發送者調用了send_fd),則作為函數值返回非負描述符。否則,返回值是由send_err發送的status(-1到-255之間的一個值)。另外,如果服務器進程發送了一條出錯消息,則客戶進程調用它自己的userfunc處理該消息。userfunc的第一個參數是常量STDERR_FILENO,然后是指向出錯消息的指針及其長度。userfunc函數的返回值是已寫的字節數或負的出錯編號值??蛻暨M程常將userfunc指定為通常的write函數。

我們實現了用于這三個函數的我們自己指定的協議。為發送一描述符,send_fd先發送兩個0字節,然后是實際描述符。為了發送一條出錯消息,send_err發送errmsg,然后是1個0字節,最后是status字節的絕對值(1-255)。recv_fd讀s管道(可以實現為STREAMS管道或UNIX域套接字的雙向通信管道)中所有字節直至null字符。null字符之前的所有字符都傳送給調用者的userfunc。recv_fd讀到的下一個字節是status字節。若status字節為0,那么一個描述符已傳送過來,否則表示沒有描述符可接收。

send_err函數在將出錯消息寫到STREAMS管道后,即調用send_fd函數。如程序清單17-11所示。

程序清單17-11 send_err函數

#include "apue.h"/** Used when we had planned to send an fd using send_fd(),* but encountered an error instead. We send the error back* using the send_fd()/recv_fd() PRotocol.*/intsend_err(int fd, int errcode, const char *msg){    int    n;        if((n = strlen(msg)) > 0)        if(writen(fd, msg, n) != n)    /* send the error message */            return(-1);    if(errcode >= 0)        errcode = -1;    /* must be negtive */    if(send_fd(fd, errcode) < 0)        return(-1);            return(0);}

1、經由基于STREAMS的管道傳送文件描述符

文件描述符用兩個ioctl命令經由STREAMS管道交換,這兩個命令是:I_SENDFD和I_RECVFD。為了發送一個描述符,將ioctl的第三個參數設置為實際描述符。

程序清單17-12 STREAMS管道的send_fd函數

#include "apue.h"#include <stropts.h>/** Pass a file descriptor to another process.* If fd < 0, then -fd is sent back instead as the error status.*/int send_fd(int fd, int fd_to_send){    char     buf[2];        /* send_fd()/recv_fd() 2-byte protocol */    buf[0] = 0;        /* null bytes flag to recv_fd() */    if(fd_to_send < 0)    {        buf[1] = -fd_to_send;    /* nonzero status means error */        if(buf[1] == 0)            buf[1] = 1;    /* -256, etc. would screw up protocol */     }    else    {        buf[1] = 0;    /* zero status means OK */    }        if(write(fd, buf, 2) != 2)        return(-1);    if(fd_to_send >= 0)        if(ioctl(fd, I_SENDFD, fd_to_send) < 0)            return(-1);    return(0);}

當接收一個描述符時,ioctl的第三個參數是一指向strrecvfd結構的指針。

struct strrecvfd {    int      fd;     /* new descriptor */    uid_t    uid;    /* effective user ID of sender */    gid_t    gid;    /* effective group ID of sender */    char     fill[8];};

recv_fd讀STREAMS管道直到接收到雙字節協議的第一個字節(null字節)。當發出I_RECVFD ioctl命令時,位于流首讀隊列中的下一條消息應當是一個描述符,它是由I_SENDFD發來的,或者是一條出錯消息。

程序清單17-13 STREAMS管道的recv_fd函數

#include "apue.h"#include <stropts.h>/** Receive a file descirpor from another process ( a server ).* In addition, any data received from the server is passed* to (*userfunc)(STDERR_FILENO, buf, nbytes). We have a * 2-byte protocol for receiving the fd from send_fd(). */intrecv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t)){    itn                  newfd,    nread, flag, status;    char                *ptr;    char                 buf[MAXLINE];    struct strbuf        dat;    struct strrecvfd     recvfd;        status = -1;    for(;;)    {        dat.buf = buf;        dat.maxlen = MAXLINE;        flag = 0;        if(getmsg(fd, NULL, &dat, &flag) < 0)            err_sys("getmsg error");        nread = dat.len;        if(nread == 0)        {            err_ret("connection closed by server");            return(-1);        }        /*        * See if this is the final data with null & status.        * Null must be next to last byte of buffer, status        * byte is last byte. Zero status means there must         * be a file descriptor to receive.        */        for(ptr = buf; ptr < &buf[nread]; )        {            if(*ptr++ == 0)            {                if(ptr != &buf[nread - 1])                    err_dump("message format error");                status = *ptr & 0xFF;    /* prevent sign extension */                if(status == 0)                {                    if(ioctl(fd, I_RECVFD, &recvfd) < 0)                        return(-1);                    newfd = recvfd.fd; /* new descriptor */                }                else                {                    newfd = -status;                }                nread -= 2;            }        }        if(nread > 0)            if((*userfunc(STDERR_FILENO, buf, nread) != nread))                return(-1);        if(status >= 0)    /* final data has arrived */            return(newfd);    /* descriptor, or -status */    }        }

2、經由UNIX域套接字傳送文件描述符

為了用UNIX域套接字交換文件描述符,調用sendmsg(2)和recvmsg(2)函數(http://www.CUOXin.com/nufangrensheng/p/3567376.html)。這兩個函數的參數中都有一個指向msghdr結構的指針,該結構包含了所有有關收發內容的信息。該結構的定義大致如下:

struct msghdr {    void            *msg_name;         /* optional address */    socklen_t        msg_namelen;      /* address size in bytes */    struct iovec    *msg_iov;          /* array of I/O buffers */    int              msg_iovlen;       /* number of elements in array */    void            *msg_control;      /* ancillary data */    socklen_t        msg_controllen;   /* number of ancillary bytes */    int              msg_flags;        /* flags for received message */};

其中,頭兩個元素通常用于在網絡連接上發送數據報文,在這里,目的地址可以由每個數據報文指定。下面兩個元素使我們可以指定由多個緩沖區構成的數組(散布讀和聚集寫),這與對readv和writev函數(http://www.CUOXin.com/nufangrensheng/p/3559304.html)的說明一樣。msg_flags字段包含了說明所接收到消息的標志,這些標志摘要示于表16-9中(http://www.CUOXin.com/nufangrensheng/p/3567376.html)。

有兩個參數用來處理控制信息的傳送和接收:msg_control字段指向cmsghdr(控制信息首部)結構,msg_contrllen字段包含控制信息的字節數。

struct cmsghdr {    socklen_t        cmsg_len;      /* data byte count, including header */    int              cmsg_level;    /* originating protocol */    int              cmsg_type;     /* protocol-specific type */    /* followed by the actual control message data */};

為了發送文件描述符,將cmsg_len設置為cmsghdr結構的長度加一個整型(描述符)的長度,cmsg_level字段設置為SOL_SOCKET,cmsg_type字段設置為SCM_RIGHTS,用以指明我們在傳送訪問權。(SCM指的是套接字級控制信息,socket_level cnotrol message。)訪問權僅能通過UNIX域套接字傳送。描述符緊隨cmsg_type字段之后存放,用CMSG_DATA宏獲得該整型量的指針。

三個宏用于訪問控制數據,一個宏用于幫助計算smsg_len所使用的值。

#include <sys/socket.h>unsigned char *CMSG_DATA(struct cmsghdr *cp);返回值:指向與cmsghdr結構相關聯的數據的指針struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mp);返回值:指向與msghdr結構相關聯的第一個cmsghdr結構的指針,若無這樣的結構則返回NULLstruct cmsghdr *CMSG_NXTHDR(struct msghdr *mp, struct cmsghdr *cp);返回值:指向與msghdr結構相關聯的下一個cmsghdr結構的指針,該msghdr結構給出了當前cmsghdr結構,若當前cmsghdr結構已是最后一個則返回NULLunsigned int CMSG_LEN(unsigned int nbytes);返回值:為nbytes大小的數據對象分配的長度

Single UNIX規范定義了前三個宏,但沒有定義CMSG_LEN。

GMSG_LEN宏返回為存放長度為nbytes的數據對象(控制數據)所需的字節數。它先將nbytes加上cmsghdr結構(控制數據頭部)的長度,然后按處理機體系結構的對齊要求進行調整,最后再向上取整。

程序清單17-14 UNIX域套接字的send_fd函數

#include "apue.h"#include <sys/socket.h>/* size of control buffer to send/recv one file descriptor */#define    CONTROLLEN    CMSG_LEN(sizeof(int))static struct cmsghdr    *cmptr = NULL;    /* malloc'ed first time *//** Pass a file descriptor to another process.* If fd < 0, then -fd is sent back instead as the error status.*/int send_fd(int fd, int fd_to_send){    struct iovec     iov[1];    struct msghdr    msg;    char             buf[2];    /* send_fd()/recv_fd() 2-byte protocol */    iov[0].iov_base     = buf;    iov[0].iov_len      = 2;    msg.msg_iov         = iov;    msg.msg_iovlen      = 1;    msg.msg_name        = NULL;    msg.msg_namelen     = 0;        if(fd_to_send < 0)    {        msg.msg_control    = NULL;        msg.msg_controllen = 0;        buf[1] = -fd_to_send;    /* nonzero status means error */        if(buf[1] == 0)            buf[1] = 1;    /* -256, etc. would screw up protocol */            }    else    {        if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)            return(-1);        cmptr->cmsg_level      = SOL_SOCKET;        cmptr->cmsg_type       = SCM_RIGHTS;        cmptr->cmsg_len        = CONTROLLEN;        msg.msg_control        = cmptr;        msg.msg_controllen     = CONTROLLEN;        *(int *)CMSG_DATA(cmptr) = fd_to_send;    /* the fd to pass */        buf[1] = 0;    /* zero status means ok */    }    buf[0] = 0;    /* null byte flag to recv_fd() */    if(sendmsg(fd, &msg, 0) != 2)        return(-1);    return(0);}

在sendmsg調用中,發送雙字節協議數據(null和status字節)和描述符。

為了接收文件描述符,我們為cmsghdr結構和描述符分配足夠大的空間,將msg_control指向該存儲空間,然后調用recvmsg。我們使用MSG_LEN宏計算所需空間的總量。

我們從UNIX域套接字讀入,直至讀到null字節,它位于最后的status字節之前。null字節之前是一條來自發送者的出錯消息。

程序清單17-15 UNIX域套接字的recv_fd函數

#include "apue.h"#include <sys/socket.h>    /* struct msghdr *//* size of control buffer to send/recv one file descriptor */#define CONTOLLEN    CMSG_LEN(sizeof(int))static struct cmsghdr    *cmptr = NULL;    /* malloc'ed first time *//** Receive a file descriptor from a server process. Also, any data* received is passed to (*userfunc)(STDERR_FILENO, buf, nbytes).* We have a 2-byte protocol for receiving the fd from send_fd().*/intrecv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t)){    int              newfd,    nr, status;    char            *ptr;    char             buf[MAXLINE];    struct iovec     iov[1];    struct msghdr    msg;    status = -1;    for(;;)    {        iov[0].iov_base   = buf;        iov[0].iov_len    = sizeof(buf);        msg.msg_iov       = iov;            msg.msg_iovlen    = 1;        msg.msg_name      = NULL;        msg.msg_namelen   = 0;                if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)            return(-1);        msg.msg_control        = cmptr;        msg.msg_controllen     = CONTROLLEN;        if((nr = recvmsg(fd, &msg, 0)) < 0)        {            err_sys("recvmsg error");            }        else if(nr == 0)        {            err_ret("connection close by server");            return(-1);            }        /*        * See if this is the final data with null & status. Null        * is next to last byte to buffer; status byte is last byte.        * Zero status means there is a file descriptor to receive.         */        for(ptr = buf; ptr < &buf[nr];)        {            if(*ptr++ == 0)            {                if(ptr != &buf[nr - 1])                {                    err_dump("message format error");                }                status = *ptr & 0xFF;    /* prevent sign extension */                if(status == 0)                {                    if(msg.msg_controllen != CONTROLLEN)                    {                        err_dump("status = 0 but no fd");                    }                    newfd = *(int *)CMSG_DATA(cmptr);                }                else                {                    newfd = -status;                }                nr -= 2;            }        }        if(nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)            return(-1);        if(status >= 0)    /* final data has arrived */            return(newfd);    /* descriptor, or -status */    }}

注意,該程序總是準備接收一描述符(在每次調用recvmsg之前,設置msg_control和msg_controllen),但是僅當在返回時,msg_controllen非0,才確實接收到一描述符。

在傳送文件描述符方面,UNIX域套接字和STREAMS管道之間的一個區別是,用STREAMS管道時我們得到發送進程的身份。

FreeBSD 5.2.1和linux 2.4.22支持在UNIX域套接字上發送憑證,但實現方式不同。

在FreeBSD,將憑證作為cmsgcred結構傳送。

#define    CMGROUP_MAX    16struct cmsgcred {    pid_t    cmcred_pid;                     /* sender's process ID */    uid_t    cmcred_uid;                     /* sender's real UID */    uid_t    cmcred_euid;                    /* sender's effective UID */    gid_t    cmcred_gid;                     /* sender's read GID */    short    cmcred_ngroups;                 /* number of groups */    gid_t    cmcred_groups[CMGROUP_MAX];     /* groups */};

當傳送憑證時,僅需為cmsgcred結構保留存儲空間。內核將填充該結構以防止應用程序偽裝成具有另一種身份。

在Linux中,將憑證作為ucred結構傳送。

struct ucred {    uint32_t    pid;    /* sender's process ID */    uint32_t    uid;    /* sender's user ID */    uint32_t    gid;    /* sender's group ID */};

不同于FreeBSD的是,Linux要求在傳送前先將結構初始化。內核將確保應用程序使用對應于調用程序的值,或具有適當的權限使用其他值。

程序清單17-16 在UNIX域套接字上發送憑證

#include "apue.h"#include <sys/socket.h>#if define(SCM_CRED)    /* BSD interface */#define CREDSTRUCT    cmsgcred#define SCM_CREDTYPE    SCM_CREDS#elif define(SCM_CREDENTIALS)    /* Linux interface */#define CREDSTRUCT    ucred#define    SCM_CREDTYPE    SCM_CREDENTIALS#else#error passing credentials is unsupported!#endif/* size of control buffer to send/recv one file descriptor */#define    CONTROLLEN    CMSG_LEN(sizeof(int))static struct cmsghdr    *cmptr = NULL;    /* malloc'ed first time *//** Pass a file descriptor to another process.* If fd < 0, then -fd is sent back instead as the error status.*/int send_fd(int fd, int fd_to_send){    struct CREDSTRUCT    *credp;    struct cmsghdr        *cmp;    struct iovec        iov[1];    struct msghdr        msg;    char            buf[2];    /* send_fd()/recv_fd() 2-byte protocol */    iov[0].iov_base     = buf;    iov[0].iov_len     = 2;    msg.msg_iov     = iov;    msg.msg_iovlen     = 1;    msg.msg_name     = NULL;    msg.msg_namelen     = 0;    msg.msg_flags = 0;    if(fd_to_send < 0)    {        msg.msg_control    = NULL;        msg.msg_controllen = 0;        buf[1] = -fd_to_send;    /* nonzero status means error */        if(buf[1] == 0)            buf[1] = 1;    /* -256, etc. would screw up protocol */            }    else    {        if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)            return(-1);        msg.msg_control        = cmptr;        msg.msg_controllen    = CONTROLLEN;        cmp = cmptr;        cmp->cmsg_level        = SOL_SOCKET;        cmp->cmsg_type        = SCM_RIGHTS;        cmp->cmsg_len        = RIGHTSLEN;        *(int *)CMSG_DATA(cmp) = fd_to_send;    /* the fd to pass */                cmp = CMSG_NXTHDR(&msg, cmp);        cmp->cmsg_level =SOL_SOCKET;        cmp->cmsg_type    = SCM_CREDTYPE;        cmp->cmsg_len    = CREADSLEN;        credp = (struct CREADSTRUCT *)CMSG_DATA(cmp);#if defined(SCM_CREDENTIALS)        credp->uid = geteuid();        credp->gid = getegid();        credp->pid = getpid();#endif        buf[1] = 0;    /* zero status means ok */    }    buf[0] = 0;    /* null byte flag to recv_fd() */    if(sendmsg(fd, &msg, 0) != 2)        return(-1);    return(0);}

注意,只是Linux上才需要初始化憑證結構。

程序清單17-17 在UNIX域套接字上接收憑證

#include "apue.h"#include <sys/socket.h>    /* struct msghdr */**************************************#if define(SCM_CRED)    /* BSD interface */#define CREDSTRUCT    cmsgcred#define CR_UID        cmcred_uid#define CREDOPT        LOCAL_PEERCRED#define SCM_CREDTYPE    SCM_CREDS#elif define(SCM_CREDENTIALS)    /* Linux interface */#define CREDSTRUCT    ucred#define    CR_UID        uid#define    CREDOPT        SO_PASSCRED#define    SCM_CREDTYPE    SCM_CREDENTIALS#else#error passing credentials is unsupported!#endif/* size of control buffer to send/recv one file descriptor */#define RIGHTSLEN    CMSG_LEN(sizeof(int))#define CREDSLEN    CMSG_LEN(sizeof(struct CREDSTRUCT))#define    CONTROLLEN    (RIGHTSLEN + CREDSLEN)static struct cmsghdr    *cmptr = NULL;    /* malloc'ed first time *//** Receive a file descriptor from a server process. Also, any data* received is passed to (*userfunc)(STDERR_FILENO, buf, nbytes).* We have a 2-byte protocol for receiving the fd from send_fd().*/intrecv_fd(int fd, uid_t *uidptr, ssize_t (*userfunc)(int, const void *, size_t)){    struct cmsghdr        *cmp;        struct CREDSTRUCT     *credp;    int                    newfd,    nr, status;    char                  *ptr;    char                   buf[MAXLINE];    struct iovec           iov[1];    struct msghdr          msg;    const int              on = 1;    status = -1;    newfd = -1;    if(setsockopt(fd, SOL_SOCKET, CREDOPT, &on, sizeof(int)) < 0)    {        err_ret("setsockopt failed");        return(-1);    }    for(;;)    {        iov[0].iov_base   = buf;        iov[0].iov_len    = sizeof(buf);        msg.msg_iov       = iov;            msg.msg_iovlen    = 1;        msg.msg_name      = NULL;        msg.msg_namelen   = 0;                if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)            return(-1);        msg.msg_control        = cmptr;        msg.msg_controllen     = CONTROLLEN;        if((nr = recvmsg(fd, &msg, 0)) < 0)        {            err_sys("recvmsg error");            }        else if(nr == 0)        {            err_ret("connection close by server");            return(-1);            }        /*        * See if this is the final data with null & status. Null        * is next to last byte to buffer; status byte is last byte.        * Zero status means there is a file descriptor to receive.         */        for(ptr = buf; ptr < &buf[nr];)        {            if(*ptr++ == 0)            {                if(ptr != &buf[nr - 1])                {                    err_dump("message format error");                }                status = *ptr & 0xFF;    /* prevent sign extension */                if(status == 0)                {                    if(msg.msg_controllen != CONTROLLEN)                    {                        err_dump("status = 0 but no fd");                    }                                        /* process the control data */                    for(cmp = CMSG_FIRSTHDR(&msg); cmp != NULL; cmp = CMSG_NXTHDR(&msg, cmp))                    {                        if(cmp->cmsg_level != SOL_SOCKET)                            continue;                        switch(cmp->cmsg_type)                        {                            case SCM_RIGHTS:                                newfd = *(int *)CMSG_DATA(cmptr);                                    break;                            case SCM_CREDTYPE:                                credp = (struct CREDSTRUCT *)CMSG_DATA(cmp);                                *uidptr = credp->CR_UID;                                                        }                    }                }                else                {                    newfd = -status;                }                nr -= 2;            }        }        if(nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)            return(-1);        if(status >= 0)    /* final data has arrived */            return(newfd);    /* descriptor, or -status */    }}

本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美在线x视频| 成人精品一区二区三区电影黑人| 欧美视频免费在线| 欧美成人亚洲成人日韩成人| 狠狠色狠狠色综合日日小说| 国产手机视频精品| 91在线观看免费| 91精品啪aⅴ在线观看国产| 日韩有码视频在线| 91网站免费看| 韩国欧美亚洲国产| 久久精品久久久久| 久久亚洲欧美日韩精品专区| 日韩一中文字幕| 亚洲欧美福利视频| 欧美电影免费观看高清| 自拍偷拍免费精品| 欧美日产国产成人免费图片| 色综合视频一区中文字幕| 亚洲欧美中文字幕在线一区| 92看片淫黄大片欧美看国产片| 亚洲国产精品va在线看黑人动漫| 久久久999精品免费| 国产精品高潮呻吟视频| 亚洲最大福利网站| 欧美一级大胆视频| 成人福利视频网| www国产精品com| 久久99国产精品自在自在app| 亚洲第一级黄色片| 在线亚洲欧美视频| 亚洲人成电影网| 日韩精品中文字| 欧美孕妇孕交黑巨大网站| 欧美做爰性生交视频| 一区二区在线视频| 欧美在线精品免播放器视频| 国产精品 欧美在线| 亚洲一区二区中文字幕| 不卡av在线播放| 亚洲香蕉av在线一区二区三区| 亚洲三级 欧美三级| 日韩电影中文 亚洲精品乱码| 国产精品96久久久久久又黄又硬| 免费av一区二区| 国产成人短视频| 91免费电影网站| 欧美成人中文字幕| 一区二区三区回区在观看免费视频| 亚洲国产成人精品女人久久久| 亚洲国产成人av在线| 91欧美精品午夜性色福利在线| 自拍偷拍亚洲精品| 2019中文字幕免费视频| 国产精品v片在线观看不卡| 成人欧美一区二区三区在线| 亚洲精品按摩视频| 久久99青青精品免费观看| 一本色道久久综合狠狠躁篇的优点| 成人黄色中文字幕| 亚洲第一区中文99精品| 不卡毛片在线看| 日韩欧中文字幕| 国产69精品久久久| 欧美在线播放视频| 亚洲直播在线一区| 欧美精品第一页在线播放| 国产精品久久久久久av福利| 精品爽片免费看久久| 狠狠综合久久av一区二区小说| 亚洲性猛交xxxxwww| 精品无码久久久久久国产| 国产成人精品在线| 国产欧美一区二区三区久久| 欧美日韩视频在线| 日韩中文字幕免费| 91日本视频在线| 中文字幕精品视频| 6080yy精品一区二区三区| 在线电影av不卡网址| 国产精品日韩在线播放| 亚州精品天堂中文字幕| 欧美二区在线播放| 欧美日韩国产综合新一区| 久久久久久久久久久国产| 欧美午夜宅男影院在线观看| 国产精品免费久久久久影院| 国产精品视频精品视频| 亚洲精品久久久一区二区三区| 精品亚洲va在线va天堂资源站| 日韩av黄色在线观看| 91欧美视频网站| 欧美成人午夜激情| 久久成人这里只有精品| 国产午夜精品全部视频播放| 91青草视频久久| 亚洲欧美日韩一区在线| 九九热99久久久国产盗摄| 久久视频国产精品免费视频在线| 亚洲精品99999| 国产成+人+综合+亚洲欧美丁香花| 欧美激情在线一区| www.日韩.com| 国产精品69久久| 欧美日韩国产成人在线观看| 欧美贵妇videos办公室| 亚洲乱码一区av黑人高潮| 久久久精品2019中文字幕神马| 日韩欧美国产骚| 亚洲精品自产拍| 日韩精品在线看| 亚洲欧洲偷拍精品| 亚洲国产精品久久久久秋霞不卡| 中文字幕欧美精品在线| 亚洲国产精品女人久久久| 91亚洲va在线va天堂va国| 国产精品视频在线播放| 91影视免费在线观看| 中国china体内裑精亚洲片| 91人成网站www| 不卡中文字幕av| 欧美制服第一页| 少妇高潮久久久久久潘金莲| 国产精品久久久久久久久借妻| 日本人成精品视频在线| 欧美午夜激情视频| 日本免费在线精品| 成人激情视频小说免费下载| 国产精品一区二区电影| 久久精品最新地址| 在线成人免费网站| 青青草一区二区| 国产日韩欧美成人| 日韩欧美亚洲成人| 精品国产区一区二区三区在线观看| 国产在线精品成人一区二区三区| 久久香蕉国产线看观看网| 国产ts一区二区| 精品视频—区二区三区免费| 欧美三级欧美成人高清www| 欧美在线激情视频| 亚洲视频在线视频| 国产成人精品免费视频| 亚洲国产精品大全| 成人福利在线视频| 国产亚洲精品久久久久动| 国产成人aa精品一区在线播放| 欧美老少做受xxxx高潮| 精品中文字幕视频| 亚洲视频综合网| 欧美专区中文字幕| 色综合久久88色综合天天看泰| 欧美高清视频一区二区| 91精品视频免费看| 亚洲成人免费在线视频| 国产精品久久久久久久久免费| 中日韩美女免费视频网站在线观看| 久久久免费观看| 5278欧美一区二区三区| 97成人精品视频在线观看| 久久免费视频在线| 狠狠久久亚洲欧美专区| 国产日韩欧美在线观看| 国产精品尤物福利片在线观看|