精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

語言中的數據安全傳遞機制

開發 前端
在不同的語言中,都有數據安全傳遞的場景,一是為了減少顯式傳遞的麻煩,比如 logId 在處理請求代碼中的傳遞,二是為了保持數據的安全,比如多個線程/請求間數據不能互相影響。

在寫代碼時,我們經常需要把一些數據從一個地方傳遞到另一個地方,或者在某個地方定義一個數據,在另一個地方使用,我們通常使用的方式是顯式傳遞或全局變量。但是在某些場景中,顯式傳遞會變得非常麻煩,比如處理請求時打印日志時帶上 logid,而全局變量在多線程環境中會存在問題。為了解決這些問題,提供更好的開發方式,開發者探索了一些其他的數據安全傳遞的機制,本文介紹這方面的內容。

C/C++ 線程局部存儲(TLS)

全局變量是最簡單的數據傳遞方式,因為它對所有代碼可見,在單線程中,每次操作對后面操作來說都是可見,在多線程中,需要額外的鎖機制。而線程局部存儲讓全局變量看起來是對所有線程可見,但實際上是對單個線程可見,它對單個線程來說保持了全局的效果,方便數據傳遞,對多線程來保持了數據的安全,不會導致線程間的數據互相修改,因為這個數據只存在某個線程的自己分配的內存中。下面是一個 C++ 中使用線程局部存儲的例子。

#include <iostream>
#include <thread>

thread_localint number = 0;

void worker() {
std::cout << "number: " << ++number << std::endl;
}

int main() {
    std::thread t1(worker);

    t1.join();

    std::thread t2(worker);

    t2.join();

    return0;
}

上面的代碼輸出如下:

number: 1
number: 1

上面代碼的執行過程是:

  1. 創建線程 1,執行 ++number,這時 number 的值是 1,然后線程 1 退出。
  2. 創建線程 2,執行 ++number,這時 number 的值仍然是 1,然后線程 2 退出。 可以看到多個線程對同一個變量進行修改,但是是互相獨立的,因為實際上這個變量的內存是存在單獨的線程中的。

接下來再看一下 C 語言中線程局部存儲的例子。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

pthread_key_t key;

void print() {
int *number = (int *)pthread_getspecific(key);
printf("print: %p, %d \n", pthread_self(), *number);
}

void * worker(void *arg) {
    int number = rand();
    printf("worker: %p, %d\n",  pthread_self(), number);
    if (pthread_setspecific(key, &number) != 0) {
        perror("pthread_setspecific failed");
        returnNULL;
    }
    print();
    returnNULL;
}

int main() {
    pthread_t threads[2];

    if (pthread_key_create(&key, NULL) != 0) {
        perror("pthread_key_create failed");
        return1;
    }

    for (int i = 0; i < 2; i++) {
        if (pthread_create(&threads[i], NULL, worker, NULL) != 0) {
            perror("pthread_create failed");
            return1;
        }
    }

    for (int i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }

    pthread_key_delete(key);

    return0;
}

上面代碼輸出如下:

worker: 0x16ceef000, 16807
print: 0x16ceef000, 16807 
worker: 0x16cf7b000, 282475249
print: 0x16cf7b000, 282475249

C 語言中通過 pthread_key_create、pthread_setspecific 和 pthread_getspecific 三個 API 實現。

  1. pthread_key_create 創建一個 key,這個 key 在所有的線程中共享使用。
  2. pthread_setspecific 通過 key 設置線程中單獨的數據,這對其他線程是不可見的,因為它實際上是執行 pthread_setspecific 時把數據存在調用線程的內存空間中。
  3. pthread_getspecific 則是根據 key 從線程內存空間中獲取 pthread_setspecific 設置的數據。
  4. 如果有多個數據,則創建多個 key。

接著分析線程庫是如何實現線程局部存儲能力的,不同架構的實現不一樣這里的 x86_64。

下面是表示線程的數據結構。

typedef struct {
void *self;  /* 指向 pthread */
} tcbhead_t;

// 表示一個線程的數據結構
struct pthread
{
union
  {  
    tcbhead_t header;
  }
pid_t tid;
int flags;
// 線程 TLS 相關的數據結構
struct pthread_key_data
  {
    uintptr_t seq;
    void *data;
  } specific_1stblock[PTHREAD_KEY_2NDLEVEL_SIZE];
// 和 specific_1stblock 類似
struct pthread_key_data *specific[PTHREAD_KEY_1STLEVEL_SIZE];
}

下面是創建線程的代碼。

int __pthread_create_2_1 (pthread_t *newthread, constpthread_attr_t *attr,
        void *(*start_routine) (void *), void *arg)
{
void *stackaddr = NULL;
size_t stacksize = 0;
struct pthread *pd = NULL;
// 分配一個 pthread 結構體
int err = allocate_stack (iattr, &pd, &stackaddr, &stacksize);

// header 是一個 tcbhead_t 結構體
#if TLS_TCB_AT_TP
/* Reference to the TCB itself.  */
  pd->header.self = pd;

/* Self-reference for TLS.  */
  pd->header.tcb = pd;
#endif

// 設置返回值
  *newthread = (pthread_t) pd;

  retval = create_thread (pd, iattr, &stopped_start, stackaddr,
       stacksize, &thread_ran);

return retval;
}

下面是 create_thread 的代碼。

static int create_thread (struct pthread *pd, const struct pthread_attr *attr,
     bool *stopped_start, void *stackaddr,
     size_t stacksize, bool *thread_ran)
{
// CLONE_SETTLS 設置 tls 相關
constint clone_flags = (CLONE_SETTLS | ...);

// x86_64 下實現如下
// # define TLS_DEFINE_INIT_TP(tp, pd) void *tp = (pd)
  TLS_DEFINE_INIT_TP (tp, pd);
// 
struct clone_args args =
    {
      .flags = clone_flags,
      // ...
      // 設置 FS 寄存器指向線程結構體
      .tls = (uintptr_t) tp,
    };
// 創建線程
int ret = __clone_internal (&args, &start_thread, pd);

return0;
}

創建線程的過程中,線程庫調用 clone 系統調用時設置了 FS 寄存器指向 pthread 結構體,這是 tls 實現的基礎。

接著看使用 tls 的代碼。下面是 pthread_key_create 時創建的 key 相關的數據結構。

struct pthread_key_struct
{
  // 偶數代表空虛的項
  uintptr_t seq;
  // 析構函數
  void (*destr) (void *);
};

// 全局數據結構,用于 pthread_key_create
struct pthread_key_struct __pthread_keys[PTHREAD_KEYS_MAX];

下面是 pthread_key_create 的代碼。

int ___pthread_key_create (pthread_key_t *key, void (*destr) (void *))
{
// 遍歷找到可用的 slot
for (size_t cnt = 0; cnt < PTHREAD_KEYS_MAX; ++cnt)
  {
    uintptr_t seq = __pthread_keys[cnt].seq;
    // 空閑且可用, 設置該 slot 的 seq 為 偶數 + 1,即奇數,表示已占用,成功則返回 0,否則繼續找下一個
    if (KEY_UNUSED (seq) && KEY_USABLE (seq) && ! atomic_compare_and_exchange_bool_acq (&__pthread_keys[cnt].seq, seq + 1, seq))
    {
      // 設置”析構函數“
      __pthread_keys[cnt].destr = destr;
      // 返回 key 給調用者
      *key = cnt;
      return0;
    }
  }
return EAGAIN;
}

pthread_key_create 的實現很簡單,就是從一個全局數據結構數組中分配一個可用項,然后返回對應的索引,所有的線程通過這個索引在線程本身的數據結構 pthread 中存儲數據,從而達到數據只對某個線程可見的效果,即局部存儲。

接著看 pthread_setspecific 是如何設置數據的。

int ___pthread_setspecific (pthread_key_t key, constvoid *value)
{
struct pthread *self;
unsignedint idx1st;
unsignedint idx2nd;
struct pthread_key_data *level2;
unsignedint seq;
/*
    // 從 FS 寄存器獲取當前線程對應的 pthread 結構體
    #  define THREAD_SELF \
      ({ struct pthread *__self;            \
         asm ("mov %%fs:%c1,%0" : "=r" (__self)          \
        : "i" (offsetof (struct pthread, header.self)));         \
         __self;})
    # endif
  */
// 獲取當前線程對應的數據結構
  self = THREAD_SELF;

/* Special case access to the first 2nd-level block.  This is the
     usual case.  */
if (__glibc_likely (key < PTHREAD_KEY_2NDLEVEL_SIZE))
    {
      // 從當前線程數據結構的某個字段中獲取對應的項
      level2 = &self->specific_1stblock[key];
    }
else
    {
      // ...
    }

// 記錄線程內可見的數據
  level2->seq = seq;
  level2->data = (void *) value;

return0;
}

pthread_setspecific 第一部通過 FS 寄存器獲取了當前調用線程的數據結構,然后在該數據結構中保存對應的數據即可。pthread_getspecific 的實現也和 pthread_setspecific 相似,代碼如下。

void * ___pthread_getspecific (pthread_key_t key)
{
struct pthread_key_data *data;
// 從當前線程結構體獲取對應 key 的數據
if (__glibc_likely (key < PTHREAD_KEY_2NDLEVEL_SIZE))
    data = &THREAD_SELF->specific_1stblock[key];
else
    {
     // ...
    }
// 返回對應的數據
return data->data;
}

Go 協程局部存儲

Go 本身沒有提供協程局部存儲,Go 官方建議通過 context 傳遞需要的數據,這樣就形成了每個函數都需要顯式傳遞一個 context 變量的習慣。民間有一些自己實現了協程存儲的庫:

  1. https://github.com/modern-go/gls。
  2. https://chai2010.cn/advanced-go-programming-book/ch3-asm/ch3-08-goroutine-id.html)。

大致的實現原理如下。

package main

import (
"fmt"
"runtime/debug"
"strconv"
"strings"
"sync"
)

var gls struct {
 m map[int64]map[any]any
 sync.Mutex
}

func init() {
 gls.m = make(map[int64]map[any]any)
}

// 獲取當前的協程 ID
func currentGID() int64 {
 stack := strings.TrimPrefix(string(debug.Stack()), "goroutine")
 idField := strings.Fields(stack)[0]
 id, err := strconv.ParseInt(idField, 10, 64)
if err != nil {
panic(fmt.Errorf("can not get goroutine id: %v", err))
 }
return id
}

func tls() map[any]any {
 gls.Lock()
defer gls.Unlock()

 goid := currentGID()
if m, _ := gls.m[goid]; m != nil {
return m
 }

 gls.m[goid] = make(map[any]any)
return gls.m[goid]
}

func Get(key any) any {
return tls()[key]
}

func Set(key any, v any) {
 tls()[key] = v
}

func worker(idx int) {
// 獲取協程局部存儲
 fmt.Printf("idx=%d, gid=%d, number=%d\n", idx, currentGID(), Get("number"))
}

func main() {
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
  wg.Add(1)
gofunc(idx int) {
   defer wg.Done()
   defer worker(idx)
      // 設置協程局部存儲
   Set("number", idx)
  }(i)
 }
 wg.Wait()
}

協程局部存儲的實現原理是在一個全局數據結構中,以協程 ID 為 key,存儲的數據為值,這樣在協程內的任何地方都可以獲取到其他地方設置的值的。

Node.js 代碼間數據傳遞

Node.js 的數據傳遞場景,除了同步情況下函數間傳遞,還有異步時,在回調里使用其他地方設置的數據,比如處理一個請求的過程中,每個同步/異步函數打印日志時都帶上 logid。

async_hooks 是 Node.js 中用于實現代碼間數據傳遞的機制,尤其用于追蹤異步操作,它的實現有點復雜。下面簡單介紹其內容。async_hooks 有三個核心概念:

  1. async_id:對象對應的唯一 id。
  2. trigger_async_id:創建對象時,對應的父對象的 async_id,比如在 A 對象的回調中創建 B 對象,則 B 對象的 trigger_async_id 是 A 對象的 async_id。
  3. execution_async_resource:當作正在執行的對象,比如執行 setTimeout 回調時,當作正在執行的對象是 setTimeout 創建的 Timeout 對象。

啟動時 async_id 是 1,trigger_async_id 是 0,execution_async_resource 是空對象。下面以 setTimeout 為例看一下 async_hooks 的流程。

function setTimeout(callback, after, arg1, arg2, arg3) {
const timeout = new Timeout(callback, after, args, false, true);
return timeout;
}

function Timeout(callback, after, args, isRepeat, isRefed) {
  initAsyncResource(this, 'Timeout');
}

function initAsyncResource(resource, type) {
// 獲取新的 async_id
const asyncId = resource[async_id_symbol] = newAsyncId();
// 在哪個對象回調中創建了該 Timeout 對象,獲取其 async_id,默認 1
const triggerAsyncId = resource[trigger_async_id_symbol] = getDefaultTriggerAsyncId();
// 是否設置了init鉤子,是則觸發回調
if (initHooksExist())
    emitInit(asyncId, type, triggerAsyncId, resource);
}

執行 setTimeout 時,Node.js 會創建一個 Timeout 對象,并設置 async_hooks 的相關 id 到 Timeout 對象中。然后觸發init鉤子。

function emitInit(asyncId, type, triggerAsyncId, resource) {
  emitInitNative(asyncId, type, triggerAsyncId, resource);
}

當定時器到期時,會設置當前的執行資源為 Timeout 對象,async_id 為 Timeout async_id,trigger_async_id 為 Timeout 的 trigger_async_id,然后執行 before 鉤子。

function emitBeforeScript(asyncId, triggerAsyncId, resource) {
  pushAsyncContext(asyncId, triggerAsyncId, resource);

  if (hasHooks(kBefore))
    emitBeforeNative(asyncId);
}

執行 before 鉤子時我們就可以通過相關 API 獲取當前執行的 async_id 或資源,即 Timeout 對象的信息。當執行完 before 鉤子后,接著執行定時器的回調。然后執行 after 鉤子。這就是 Node.js 中,異步資源的整個生命周期和 async_hooks 鉤子的關系。但是 Promise 的情況稍微不同,因為 Promise 是 V8 實現的,但是原理相似,Node.js 通過設置 V8 的 Promise 鉤子感知 Promise 的生命周期,比如創建,before、after、settled 等,從而執行 async_hooks 定義的鉤子即可。Node.js 大部份的異步場景都是類似的方式實現的。

async_hooks 可以讓我們知道資源間的關系已經生命周期,在這個基礎上 AsyncLocalStorage 基于 async_hooks 實現了資源間數據的傳遞。接下來看看 AsyncLocalStorage 的實現。

const storageList = [];
const storageHook = createHook({
  init(asyncId, type, triggerAsyncId, resource) {
    // resource 為新創建的對象
    // currentResource 為創建該對象的對象
    const currentResource = executionAsyncResource();
    
    for (let i = 0; i < storageList.length; ++i) {
      storageList[i]._propagate(resource, currentResource, type);
    }
  },
});

class AsyncLocalStorage {

constructor(options = {}) {
    this.kResourceStore = Symbol('kResourceStore');
    this._enable();
  }

  _enable() {
    // 開啟,感知每個創建的資源
    if (!this.enabled) {
      this.enabled = true;
      ArrayPrototypePush(storageList, this);
      storageHook.enable();
    }
  }

// Propagate the context from a parent resource to a child one
  _propagate(resource, triggerResource, type) {
    // 新創建對象指向創建該新對象的對象的數據,比如 A 創建 B,則 B 指向 A 的數據。
    const store = triggerResource[this.kResourceStore];
    if (this.enabled) {
      resource[this.kResourceStore] = store;
    }
  }

  enterWith(store) {
    this._enable();
    // 設置當前執行對象指向新的數據
    const resource = executionAsyncResource();
    resource[this.kResourceStore] = store;
  }

  run(store, callback, ...args) {
    this._enable();
    // 當前對象
    const resource = executionAsyncResource();
    const oldStore = resource[this.kResourceStore];
    // 設置當前對象指向新的數據
    resource[this.kResourceStore] = store;
    // 執行函數,執行完后恢復指向的數據
    try {
      return ReflectApply(callback, null, args);
    } finally {
      resource[this.kResourceStore] = oldStore;
    }
  }

  getStore() {
    if (this.enabled) {
      // 獲取當前對象指向的數據
      const resource = executionAsyncResource();
      return resource[this.kResourceStore];
    }
    returnthis.#defaultValue;
  }
}

AsyncLocalStorage 通過 enterWith 或 run 函數修改當前執行對象指向的數據,然后通過 async_hooks 的 init 鉤子函數感知在當前執行對象執行過程中創建的新對象,并把數據指針復制到新創建的對象中,從而實現了數據的傳遞。比如在對象 A 執行過程中創建了 B,然后 B 執行過程中又創建了 C,則 C 也指向了 A 的數據,從而實現在后續的執行對象的回調時都能直接獲取到前面代碼設置的數據。下面看一些例子。

  • 同步代碼。
const { executionAsyncResource, AsyncLocalStorage } = require('async_hooks');

const als = new AsyncLocalStorage();

function run() {
  // 默認執行對象
  console.log(als.getStore());
}
// 設置默認執行對象的數據
als.enterWith({number: 1});
run();
  • 基于回調的異步代碼。
const { AsyncLocalStorage } = require('async_hooks');

const als = new AsyncLocalStorage();

function run() {
// 默認執行對象
console.log(als.getStore());
// 創建 Timeout 對象,繼承了默認對象的數據({number: 1})
  setTimeout(function() {
    // 執行對象為 Timeout,拿到數據 {number: 1}
    console.log(als.getStore());
  }, 100);
}
// 設置默認執行對象的數據
als.enterWith({number: 1});
run();
  • 基于 Promise 的異步代碼。
const { AsyncLocalStorage } = require('async_hooks');

const als = new AsyncLocalStorage();

function run() {
// 創建 Promise 對象,繼承了默認對象的數據({number: 1})
returnnewPromise((resolve) => {
    // 創建 Timeout 對象,繼承了默認對象的數據({number: 1})
    setTimeout(resolve, 1000);
  })
}

// 設置默認執行對象的數據
als.enterWith({number: 1});

run().then(function() {
// 執行對象為 Promise,獲取 Promise 從默認對象繼承來的數據 ({number: 1})
console.log(als.getStore());
});

(asyncfunction() {
await run();
// 執行對象為 Promise,獲取 Promise 從默認對象繼承來的數據 ({number: 1})
console.log(als.getStore());
})();

在比較新的 Node.js 版本中,已經使用了 V8 的 SetContinuationPreservedEmbedderData 和 GetContinuationPreservedEmbedderData API 實現數據的傳遞,該 API 使用例子如下。

void SetContext(const FunctionCallbackInfo<Value> &info) {
  Isolate *isolate = info.GetIsolate();
  Local<Value> value = info[0].As<Object>();
  isolate->GetCurrentContext()->SetContinuationPreservedEmbedderData(value);
}

void GetContext(const FunctionCallbackInfo<Value> &info) {
  Isolate *isolate = info.GetIsolate();
  info.GetReturnValue().Set(
      isolate->GetCurrentContext()->GetContinuationPreservedEmbedderData());
}

JS 層使用如下。

const { getContext, setContext }= require('./build/Release/addon');
// 設置數據
setContext({"value": 1});
// 執行時會同步記錄上面設置的數據并傳遞下去
Promise.resolve().then(() => {
// {"value": 1} 可以一直傳下去
console.log(getContext())
Promise.resolve().then(() => {
    // {"value": 1} 可以一直傳下去
    console.log(getContext())
  })
});
// 修改數據不會影響上面數據的傳遞
setContext({"value": 2});

不過 Node.j 中的邏輯比這個復雜,后續再單獨介紹。

總結

在不同的語言中,都有數據安全傳遞的場景,一是為了減少顯式傳遞的麻煩,比如 logId 在處理請求代碼中的傳遞,二是為了保持數據的安全,比如多個線程/請求間數據不能互相影響。但是不同語言的解決方式不太一樣,C/C++ 中的場景是線程級的數據傳遞和隔離,這個是通過底層 C 庫和操作系統提供的能力實現的。Go 中則是協程級的,需要通過獲取協程 ID,然后以協程 ID 為 key 進行數據的存取。Node.js 本身是單線程的,需要實現的是數據在同步代碼和異步操作間的安全傳遞,這個需要感知操作對象的生命周期,并在操作對象間把數據傳遞下去,從而保證在每個操作對象的上下文都能獲取到前面設置的數據。

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2011-08-01 10:36:01

2021-01-07 14:17:31

Springboot數據安全加密

2022-10-28 18:36:18

2011-03-29 10:41:51

Java線程安全

2022-07-19 12:25:29

Go

2009-11-26 13:12:01

2021-01-29 08:19:50

HTTPS安全傳輸

2016-10-10 23:00:18

2024-05-08 08:16:11

2013-03-21 09:32:31

文件傳輸安全文件傳輸

2017-05-05 14:26:43

2024-06-13 08:41:41

2020-09-26 22:04:32

數據安全傳輸HTTPSHTTP 協議

2010-06-10 18:51:52

UML語言

2023-09-07 10:50:22

2024-04-07 11:33:02

Go逃逸分析

2021-07-15 23:18:48

Go語言并發

2009-07-08 12:43:59

Scala ServlScala語言

2023-12-21 07:09:32

Go語言任務

2025-04-02 05:23:00

GoChannel數據
點贊
收藏

51CTO技術棧公眾號

国产福利小视频| 久久久久无码精品国产sm果冻| 99在线播放| www.久久久久久久久| 欧美在线观看一区二区三区| 亚洲午夜精品久久久久久高潮| 91精品亚洲一区在线观看| 亚洲成av人片一区二区三区| 亚洲精美视频| 男人天堂网在线视频| 久久婷婷一区| 欧美丰满少妇xxxxx做受| 精品人妻少妇嫩草av无码| 亚洲在线资源| 色婷婷综合中文久久一本| 佐佐木明希av| 99青草视频在线播放视| 成人黄色网址在线观看| 国产日韩在线视频| 一级片中文字幕| 888久久久| 亚洲天堂网在线观看| 深夜视频在线观看| 免费视频成人| 亚洲地区一二三色| 在线免费观看成人| 噜噜噜在线观看播放视频| 国产成人精品亚洲日本在线桃色 | 国产一区91精品张津瑜| 日本伊人精品一区二区三区介绍| 亚洲国产成人精品综合99| 日韩www.| 一区二区国产精品视频| 草草影院第一页| 在线视频亚洲欧美中文| 欧美一区中文字幕| 少妇一级淫免费播放| 香蕉视频亚洲一级| 欧美日韩国产丝袜另类| 免费看毛片的网址| 丝袜综合欧美| 亚洲精品成人天堂一二三| 中文字幕在线中文字幕日亚韩一区| 国产一二三在线观看| 91丨九色丨蝌蚪丨老版| 国产福利一区二区三区在线观看| 99视频国产精品免费观看a | 成人免费福利在线| 中文字幕观看视频| 日本 国产 欧美色综合| 国产成人精品视频在线| 亚洲熟妇无码乱子av电影| 一级成人国产| 欧美一区二三区| 成人午夜淫片100集| 国产精品一页| 日本精品性网站在线观看| 日韩视频在线观看一区| 久久久久欧美精品| 国产成人精品优优av| 一二三区免费视频| 日韩精品视频网站| 国产免费一区二区三区在线观看 | 丝袜美腿小色网| 欧美在线高清| 久久久久久久久电影| 国产在线精品观看| 午夜在线一区二区| 国产成人一区三区| 亚洲一区中文字幕永久在线| 麻豆专区一区二区三区四区五区| 国产欧美精品一区二区| 国产a级免费视频| 成人午夜在线免费| 久久免费一区| 在线观看a视频| 亚洲同性同志一二三专区| www.18av.com| 亚洲欧洲美洲av| 欧美人与禽zozo性伦| 九色91porny| 国产日韩三级| 尤物tv国产一区| 欧美三级日本三级| 国产一级久久| 国产精品网红直播| 国产婷婷在线视频| 99久久精品99国产精品| 四虎一区二区| 女人天堂av在线播放| 欧美日韩亚洲91| 亚洲精品手机在线观看| jizz国产精品| 中文字幕日韩欧美| 久久精品这里只有精品| 久久av在线| 亚洲r级在线观看| 亚洲色图21p| 亚洲欧美综合色| 欧美变态另类刺激| 99综合久久| 亚洲欧美日韩一区二区三区在线| 肉色超薄丝袜脚交69xx图片| 亚洲伦理一区| 成人午夜两性视频| 青青草视频免费在线观看| 亚洲欧洲国产日本综合| 日本毛片在线免费观看| 国产高清日韩| 国产一区二区日韩精品欧美精品| 免费网站观看www在线观| 三级一区在线视频先锋| www.久久久| av资源网在线观看| 欧美日韩一区二区免费在线观看 | av图片在线观看| 国产乱妇无码大片在线观看| 欧美日韩一区二| 国产盗摄精品一区二区酒店| 4438x亚洲最大成人网| 97伦伦午夜电影理伦片| 在线欧美亚洲| 亚洲综合第一页| 最新国产在线观看| 色成年激情久久综合| 色婷婷精品久久二区二区密 | 日本www在线视频| 免费观看在线一区二区三区| 中文字幕亚洲欧美| 中文字幕在线天堂| 91麻豆免费在线观看| 国产xxxx振车| 日韩一区二区三区在线看| 色天天综合狠狠色| 日韩欧美国产另类| 国产亚洲欧美在线| 激情网站五月天| 色爱av综合网| 久久久噜噜噜久久| 成人久久久精品国产乱码一区二区 | 成人免费黄色网| 成人av一区| 色八戒一区二区三区| 日韩av在线看免费观看| 国产情侣久久| 久久综合福利| 亚洲黄色免费av| 亚洲毛片在线看| 日韩人妻精品中文字幕| 91视频国产观看| 日本免费不卡一区二区| 亲子伦视频一区二区三区| 97在线观看免费| 日本黄色免费视频| 午夜亚洲福利老司机| av无码一区二区三区| 一本色道精品久久一区二区三区| 精品日本一区二区三区| 天堂中文在线播放| 亚洲三级黄色在线观看| 波多野结衣电车痴汉| 中文一区二区在线观看| 日韩av在线中文| 雨宫琴音一区二区三区| 动漫精品视频| 亚洲精品mv| 视频直播国产精品| 99riav国产| 亚洲第一激情av| 亚洲最大成人网站| 奇米精品一区二区三区四区| 亚洲在线色站| 日韩高清一区| 欧美一区二区三区免费观看| 粉嫩一区二区三区国产精品| 欧美日韩视频第一区| 五月婷婷一区二区| 2022国产精品视频| 黄色永久免费网站| 国产精品videosex极品| 精品欧美国产| 国产精品99久久久久久董美香| 日韩网站免费观看| 高清一区二区三区四区| 日本韩国一区二区三区| 久久精品一区二区三区四区五区| 国产成人综合在线播放| 97国产精东麻豆人妻电影| 色135综合网| 高清视频在线观看一区| 欧美一区国产| 欧美乱妇高清无乱码| 手机亚洲第一页| 67194成人在线观看| 亚洲一区欧美在线| 1024亚洲合集| 好吊一区二区三区视频| 国产在线视频精品一区| 六月丁香婷婷激情| 欧美在线免费| 日韩精品久久久毛片一区二区| 亚洲日本va| 国产精品美女网站| 91美女主播在线视频| 中文字幕在线国产精品| 人妻妺妺窝人体色www聚色窝| 欧美图区在线视频| 可以在线观看av的网站| 亚洲女同ⅹxx女同tv| 国产免费一区二区三区网站免费| 国产精品影视在线| 一级片视频免费观看| 亚洲久久成人| www.69av| 999国产精品| 日韩精品av一区二区三区| 久久国产精品免费精品3p| 91香蕉嫩草影院入口| 懂色aⅴ精品一区二区三区| 97视频在线观看免费| 女同一区二区免费aⅴ| 伦伦影院午夜日韩欧美限制| 国产在线资源| 亚洲免费视频一区二区| 天堂在线视频观看| 日韩亚洲欧美一区| 91福利免费视频| 欧美最猛黑人xxxxx猛交| 精品人妻一区二区三区免费看| 亚洲国产日日夜夜| 久久久无码精品亚洲国产| 亚洲欧美在线视频| 五月天婷婷丁香网| 欧美激情一二三区| 香蕉视频久久久| 国产亚洲欧美日韩在线一区| 中文字幕人妻一区二区| 91麻豆精品在线观看| 黄色在线观看av| 91最新地址在线播放| xfplay5566色资源网站| 成人免费视频app| 国产日韩视频一区| 成人综合在线观看| 国产综合内射日韩久| 国产精品1024| 风韵丰满熟妇啪啪区老熟熟女| 国产成人激情av| 极品白嫩少妇无套内谢| 成人精品国产免费网站| 国产xxxxxxxxx| 91丝袜呻吟高潮美腿白嫩在线观看| 亚洲av成人无码一二三在线观看| 99视频精品在线| 人人妻人人澡人人爽人人精品| 久久亚洲捆绑美女| 2019男人天堂| 亚洲三级视频在线观看| 欧美日韩综合一区二区| 亚洲黄色小说网站| 中文字幕一区二区三区手机版| 无码av免费一区二区三区试看| 国产精品va无码一区二区三区| 在线影视一区二区三区| 中文字幕人妻色偷偷久久| 欧美日韩一本到| av中文字幕免费在线观看| 精品国产免费一区二区三区香蕉| 免费av网站在线播放| 日韩精品在线免费观看| caoporn国产精品免费视频| 美女999久久久精品视频| missav|免费高清av在线看| 欧美一级电影久久| 日韩中文视频| 7777奇米亚洲综合久久| 欧美aaaaa级| 亚洲精品人成| 亚洲性感美女99在线| 99免费视频观看| 国产呦萝稀缺另类资源| 水蜜桃av无码| 国产精品视频你懂的| 国产在线综合网| 欧洲色大大久久| 午夜免费福利视频| 亚洲欧美在线免费观看| av网站网址在线观看| 91精品国产99| 粉嫩一区二区三区在线观看| 精品免费视频123区| 久久一区二区三区电影| www.99热这里只有精品| 日本亚洲三级在线| 中文字幕亚洲日本| 国产拍揄自揄精品视频麻豆| 麻豆chinese极品少妇| 久久性天堂网| 天美星空大象mv在线观看视频| 国产中文字幕精品| 黄色正能量网站| 樱桃国产成人精品视频| 日韩精品在线一区二区三区| 精品精品欲导航| 中文字幕日本在线| 97碰在线观看| 日韩免费成人| 一区二区三区四区五区精品| 国产日韩精品视频一区二区三区 | 亚洲一级一级97网| 色呦呦在线播放| 国产免费观看久久黄| 丝袜美腿一区二区三区动态图| 一本色道久久99精品综合| 国产日韩综合| 日本久久久久久久久久| 一区在线播放视频| 色老头在线视频| 亚洲精品av在线| 亚洲精品天堂| 国产美女精品视频| 精品国产精品国产偷麻豆| 国产原创popny丨九色 | 99视频在线播放| 日韩精品dvd| 欧美亚洲日本在线观看| www激情久久| 日韩精品手机在线| 欧美大片免费久久精品三p | 1024亚洲合集| 中文字幕 自拍偷拍| 亚洲社区在线观看| 成人影院入口| 精品一卡二卡三卡四卡日本乱码| 欧美日韩蜜桃| 韩国三级在线看| 一二三四社区欧美黄| 国产又粗又猛又黄| 日韩一区视频在线| 久久久免费人体| 亚洲欧洲日夜超级视频| 肉丝袜脚交视频一区二区| 久操视频免费看| 色婷婷av一区二区三区gif | 久久精品电影网站| 成人综合网站| 婷婷视频在线播放| 国产一区二区久久| 五月天丁香激情| 欧美草草影院在线视频| 日本理论片午伦夜理片在线观看| 99国产高清| 亚洲人成免费| 国产美女喷水视频| 色婷婷精品久久二区二区蜜臂av| 飘雪影院手机免费高清版在线观看| 欧美在线免费观看| 美女久久久久| 日日干夜夜操s8| 亚洲欧美日韩国产一区二区三区| 国产麻豆91视频| 欧美黑人一级爽快片淫片高清| 国产精品99久久免费观看| 黄色免费视频大全| 久久亚洲欧美国产精品乐播| 91丨九色丨海角社区| 最新91在线视频| 亚洲精品aⅴ| 欧美 丝袜 自拍 制服 另类| 久久久久久久久久久久久久久99| 午夜一区二区三区四区| 久久精品国产久精国产一老狼| 精品久久亚洲| 欧美日韩在线一| 国产精品午夜在线| 国产福利小视频| 欧美一级高清免费播放| 日韩电影免费在线观看| 天美一区二区三区| 亚洲成av人影院在线观看网| 国产视频在线看| 成人啪啪免费看| 99精品国产在热久久婷婷| 国产视频不卡在线| 日韩视频在线你懂得| 性xxxxfreexxxxx欧美丶| 自拍偷拍亚洲色图欧美| 波多野结衣精品在线| 最新中文字幕免费| 欧美激情精品久久久| 夜夜躁狠狠躁日日躁2021日韩| 亚洲免费黄色网| 午夜国产不卡在线观看视频| 成人高清在线| 国产精品中出一区二区三区| 人人精品人人爱| 国产成人亚洲欧洲在线| 日韩一级黄色av| 国产午夜一区| 青青草精品在线|