wininet內部實現探索

wininet內部的探索


這裏源代碼都是reatos,有些是WIN2K的代碼。


1:HINTERNET Handles


對外的定義winhttp.h 裏面


這個就是我用的到句柄


typedef LPVOID HINTERNET;
typedef HINTERNET * LPHINTERNET;


這裏就是一個指針,具體指向什麼數據結構就看他的上下文了。(上下文可以看成是哪個函數調用的他)


MSDN說了一句很重要的話
Note that handle values are recycled quickly; therefore, if a handle is closed and a new handle is generated immediately, there is a good chance that the new handle has the same value as the handle just closed. 


注意釋放 HINTERNET句柄很可能會導致一些意想不到的問題。(後面寫一個專門測試例子來驗證)




2:InternetOpen 看看他做了什麼,畢竟這個所有的根函數


MSDN解析:Initializes an application's use of the WinINet functions.
貌似說初始化wininet的函數。
我覺得是在一個進程裏面建立起一個數據結構(我覺得這個不是系統的數據結構,而是一個進程的內部的數據結構)。


看一下在這個函數做了什麼?
HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
    LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
{
    appinfo_t *lpwai = NULL;


    lpwai = alloc_object(NULL, &APPINFOVtbl, sizeof(appinfo_t));
    if (!lpwai) {
        SetLastError(ERROR_OUTOFMEMORY);
        return NULL;
    }


    lpwai->hdr.htype = WH_HINIT;
    lpwai->hdr.dwFlags = dwFlags;
    lpwai->accessType = dwAccessType;
    lpwai->proxyUsername = NULL;
    lpwai->proxyPassword = NULL;


    lpwai->agent = heap_strdupW(lpszAgent);
    if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
        INTERNET_ConfigureProxy( lpwai );
    else
        lpwai->proxy = heap_strdupW(lpszProxy);
    lpwai->proxyBypass = heap_strdupW(lpszProxyBypass);


    TRACE("returning %p\n", lpwai);


    return lpwai->hdr.hInternet;
//這裏在alloc_object做的。
//這裏看是返回的索引,但我實際檢測的時候,返回數組的地址,而不是索引,其實2種相同。
}




這個函數做了分配對應的INTERNET根的數據結構,同時把參數分配內存保存堆上。


我在vc6 測試InternetOpen.


void TestInternetOpen()
{
HINTERNET hInternetRoot = InternetOpen("fishclient",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
printf("%u\n",(UINT_PTR)hInternetRoot);
//InternetCloseHandle(hInternetRoot);
}


第一次創建值都是固定,如果你沒有釋放的話,下次創建的值就偏移Int位置,看上面的函數,貌似return lpwai->hdr.hInternet;
返回的索引,但根據TestInternetOpen是不對的,後來我自己下了win2000的源代碼,看了一下返回是內部數據結構的假的地址(類似索引),所以reactos反彙編確實不完全安裝微軟的源代碼走的,而是通過自己理解再重寫一遍,可以說提煉微軟意思,然後用少量代碼寫出來(因爲一些功能,reactos沒有實現)。感謝這些前輩給我留下這麼好的東西。
win2k 的InternetOpenA的代碼
I
NTERNETAPI
HINTERNET
WINAPI
InternetOpenA(
    IN LPCSTR lpszAgent,
    IN DWORD dwAccessType,
    IN LPCSTR lpszProxy OPTIONAL,
    IN LPCSTR lpszProxyBypass OPTIONAL,
    IN DWORD dwFlags
    )


/*++


Routine Description:


    Opens a root Internet handle from which all HINTERNET objects are derived


Arguments:


    lpszAgent       - name of the application making the request (arbitrary
                      identifying string). Used in "User-Agent" header when
                      communicating with HTTP servers, if the application does
                      not add a User-Agent header of its own


    dwAccessType    - type of access required. Can be


                        INTERNET_OPEN_TYPE_PRECONFIG
                            - Gets the configuration from the registry


                        INTERNET_OPEN_TYPE_DIRECT
                            - Requests are made directly to the nominated server


                        INTERNET_OPEN_TYPE_PROXY
                            - Requests are made via the nominated proxy


                        INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY
                            - Like Pre-Config, but prevents JavaScript, INS
                                and other auto-proxy types from being used.


    lpszProxy       - if INTERNET_OPEN_TYPE_PROXY, a list of proxy servers to
                      use


    lpszProxyBypass - if INTERNET_OPEN_TYPE_PROXY, a list of servers which we
                      will communicate with directly


    dwFlags         - flags to control the operation of this API or potentially
                      all APIs called on the handle generated by this API.
                      Currently supported are:


                        INTERNET_FLAG_ASYNC
                            - if specified then all subsequent API calls made
                              against the handle returned from this API, or
                              handles descended from the handle returned by
                              this API, have the opportunity to complete
                              asynchronously, depending on other factors
                              relevant at the time the API is called


Return Value:


    HINTERNET
        Success - handle of Internet object


        Failure - NULL. For more information, call GetLastError()


--*/


{
    PERF_INIT();


    DEBUG_ENTER_API((DBG_API,
                     Handle,
                     "InternetOpenA",
                     "%q, %s (%d), %q, %q, %#x",
                     lpszAgent,
                     InternetMapOpenType(dwAccessType),
                     dwAccessType,
                     lpszProxy,
                     lpszProxyBypass,
                     dwFlags
                     ));


    DWORD error;
    HINTERNET hInternet = NULL;


    if (!GlobalDataInitialized) {
        error = GlobalDataInitialize();
        if (error != ERROR_SUCCESS) {
            goto quit;
        }
    }


    //
    // we are doing GetUserName here instead of in DLL_PROCESS_ATTACH
    // As every caller of wininet has to do this first, we ensure
    // that the username is initialized when they get to actually doing
    // any real operation
    //


    GetWininetUserName();


    //
    // validate parameters
    //


    if (!
         (
              (dwAccessType == INTERNET_OPEN_TYPE_DIRECT)
           || (dwAccessType == INTERNET_OPEN_TYPE_PROXY)
           || (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
           || (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY)
           || (
                (dwAccessType == INTERNET_OPEN_TYPE_PROXY)
                &&
                    (
                       !ARGUMENT_PRESENT(lpszProxy)
                    || (*lpszProxy == '\0')


                    )
              )
           || (dwFlags & ~INTERNET_FLAGS_MASK)
         )
       )
    {
        error = ERROR_INVALID_PARAMETER;
        goto quit;
    }




    GlobalHaveInternetOpened = TRUE;


    //
    // Initalize an auto proxy dll if needed,
    //  as long as the caller is allowing us free rein to do this
    //  by calling us with INTERNET_OPEN_TYPE_PRECONFIG.
    //


    //if ( dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG )
    //{
    //    if ( ! InitalizeAutoConfigDllIfNeeded() )
    //  {
    //      error = GetLastError();
    //
    //      INET_ASSERT(error != ERROR_SUCCESS);
    //
    //      goto quit;
    //  }
    //
    //




    INTERNET_HANDLE_OBJECT * lpInternet;


    lpInternet = new INTERNET_HANDLE_OBJECT(lpszAgent,
                                            dwAccessType,
                                            (LPSTR)lpszProxy,
                                            (LPSTR)lpszProxyBypass,
                                            dwFlags
                                            );
    if (lpInternet == NULL) {
        error = ERROR_NOT_ENOUGH_MEMORY;
        goto quit;
    }
    error = lpInternet->GetStatus();
    if (error == ERROR_SUCCESS) {
        hInternet = (HINTERNET)lpInternet;


        //
        // success - don't return the object address, return the pseudo-handle
        // value we generated
        //


        hInternet = ((HANDLE_OBJECT *)hInternet)->GetPseudoHandle();//這裏強制轉發基類指針調用函數。(微軟這個時候已經用的C++來寫了)


        //
        // start async support now if required. If we can't start it, we'll get
        // another chance the next time we create an async request
        //


        if (dwFlags & INTERNET_FLAG_ASYNC) {
            InitializeAsyncSupport();
        }
    } else {


        //
        // hack fix to stop InternetIndicateStatus (called from the handle
        // object destructor) blowing up if there is no handle object in the
        // thread info block. We can't call back anyway
        //


        LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();


        if (lpThreadInfo) {


            //
            // BUGBUG - incorrect handle value
            //


            _InternetSetObjectHandle(lpThreadInfo, lpInternet, lpInternet);
        }


        //
        // we failed during initialization. Kill the handle using Dereference()
        // (in order to stop the debug version complaining about the reference
        // count not being 0. Invalidate for same reason)
        //


        lpInternet->Invalidate();
        lpInternet->Dereference();


        INET_ASSERT(hInternet == NULL);


    }


quit:


    if (error != ERROR_SUCCESS) {


        DEBUG_ERROR(API, error);


        SetLastError(error);
    }


    DEBUG_LEAVE_API(hInternet);


    return hInternet;
}


---------------------------------------------------------------------------------------------


//我們看到這個數據結構所以我們一定要知道保存了,不過其實大概也能夠猜到一些東西
typedef struct
{
    object_header_t hdr;
    LPWSTR  agent;
    LPWSTR  proxy;
    LPWSTR  proxyBypass;
    LPWSTR  proxyUsername;
    LPWSTR  proxyPassword;
    DWORD   accessType;
} appinfo_t;




typedef struct _object_header_t object_header_t;


struct _object_header_t
{
    WH_TYPE htype;
    const object_vtbl_t *vtbl;
    HINTERNET hInternet;
    BOOL valid_handle;
    DWORD  dwFlags;
    DWORD_PTR dwContext;
    DWORD  dwError;
    ULONG  ErrorMask;
    DWORD  dwInternalFlags;
    LONG   refs;
    INTERNET_STATUS_CALLBACK lpfnStatusCB;
    struct list entry;
    struct list children;
};
可以看出來這個就是鏈表所有的數據都保存在這裏了。
這個結構會一些inertnet通用函數調用的,進行設置。


因爲reactos 的 wininet的數據結構用的c來寫,win2k用c++ 寫。
但是意思表達一個意思,可能我覺得用c來寫更能清晰,感覺的reacos代碼非常好,有些技巧有必要自己慢慢的挖掘出來。




看一下指針


typedef struct {
    void (*Destroy)(object_header_t*);
    void (*CloseConnection)(object_header_t*);
    DWORD (*QueryOption)(object_header_t*,DWORD,void*,DWORD*,BOOL);
    DWORD (*SetOption)(object_header_t*,DWORD,void*,DWORD);
    DWORD (*ReadFile)(object_header_t*,void*,DWORD,DWORD*);
    DWORD (*ReadFileExA)(object_header_t*,INTERNET_BUFFERSA*,DWORD,DWORD_PTR);
    DWORD (*ReadFileExW)(object_header_t*,INTERNET_BUFFERSW*,DWORD,DWORD_PTR);
    DWORD (*WriteFile)(object_header_t*,const void*,DWORD,DWORD*);
    DWORD (*QueryDataAvailable)(object_header_t*,DWORD*,DWORD,DWORD_PTR);
    DWORD (*FindNextFileW)(object_header_t*,void*);
} object_vtbl_t;








BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
{
    object_header_t *obj;
    
    TRACE("%p\n", hInternet);


    obj = get_handle_object( hInternet );
    if (!obj) {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }


    invalidate_handle(obj);
    WININET_Release(obj);


    return TRUE;
}


//關閉就比較明瞭,到引用爲0就是放內部結構


總結:
inertOpen 確實跟MSDN說作用是一個意思
Initializes an application's use of the WinINet functions.


其實就是初始化一個重要的數據結構而已,然後插入到鏈表裏面去。
其實HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
    LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
參數已經說了這一切了;。
保存的客戶端的名字,保存接受類型,保存代理。===這些都是關係到後面的請求怎麼去請求的問題,比喻代理如果有了,那麼socket連接的時候必須連接到代理的服務器去。所有這些信息保存內部數據結構。然後通過 HINTERNET 傳遞出去,帶調用不同的函數。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章