亚洲韩日午夜视频,欧美日韩在线精品一区二区三区,韩国超清无码一区二区三区,亚洲国产成人影院播放,久草新在线,在线看片AV色

您好,歡迎來到思海網絡,我們將竭誠為您提供優質的服務! 誠征網絡推廣 | 網站備案 | 幫助中心 | 軟件下載 | 購買流程 | 付款方式 | 聯系我們 [ 會員登錄/注冊 ]
促銷推廣
客服中心
業務咨詢
有事點擊這里…  531199185
有事點擊這里…  61352289
點擊這里給我發消息  81721488
有事點擊這里…  376585780
有事點擊這里…  872642803
有事點擊這里…  459248018
有事點擊這里…  61352288
有事點擊這里…  380791050
技術支持
有事點擊這里…  714236853
有事點擊這里…  719304487
有事點擊這里…  1208894568
有事點擊這里…  61352289
在線客服
有事點擊這里…  531199185
有事點擊這里…  61352288
有事點擊這里…  983054746
有事點擊這里…  893984210
當前位置:首頁 >> 技術文章 >> 文章瀏覽
技術文章

安全地編寫SQL server擴展存儲過程

添加時間:2013-10-11 17:36:22  添加: 思海網絡 

  SQL server 的擴展存儲過程,其實就是一個普通的 windows DLL,只不過按照某種規則實現了某些函數而已。
  
  近日在寫一個擴展存儲過程時,發現再寫這類動態庫時,還是有一些需要特別注意的地方。之所以會特別注意,是因為DLL運行于SQL server的地址空間,而SQL Server到底是怎么進行線程調度的,卻不是我們能了解的,即便了解也無法控制。
  
  我們寫動態庫一般是自己用,即便給別人用,也很少像SQL Server這樣,一個動態庫很有可能加載多次,并且都是加載到一個進程的地址空間中。我們知道,當一個動態庫加載到進程的地址空間時,DLL所有全局與局部變量初始化且僅初始化一次,以后再次調用 LoadLibrary函數時,僅僅增加其引用計數而已,那么很顯然,假如有一全局 int ,初始化為0,調用一個函數另其自加,此時其值為1,然后再調用LoadLibray,并利用返回的句柄調用輸出函數輸出該值,雖然調用者覺得自己加載后立即輸出,然后該值確實1而不是0。windows是進程獨立的,而在線程方面,假如不注意,上面的情況很可能會程序員帶來麻煩。
  
  介紹一下我的擴展存儲過程,該動態庫導出了三個函數: Init,work,Final,Init讀文件,存儲信息于內存,work簡單的只是向該內存檢索信息,Final回收內存。如上所說,假如不考慮同一進程空間多次加載問題,兩次調用Init將造成無謂的浪費,因為我第一次已經讀進了內存,要是通過堆分配內存,還會造成內存泄露。
  
  我使用的引用計數解決的該問題,代碼很短,直接貼上來:
  
  #include "stdafx.h"
  #include <string>
  
  using namespace std;
  
  extern "C" {
  RETCODE __declspec(dllexport) xp_part_init(SRV_PROC *srvproc);
  RETCODE __declspec(dllexport) xp_part_process(SRV_PROC *srvproc);
  RETCODE __declspec(dllexport) xp_part_finalize(SRV_PROC *srvproc);
  }
  
  #define XP_NOERROR   0
  #define XP_ERROR    1
  
  HINSTANCE hInst = NULL;
  int nRef = 0;
  
  void printError (SRV_PROC *pSrvProc, CHAR* szErrorMsg);
  
  ULONG __GetXpVersion(){ return ODS_VERSION;}
  
  SRVRETCODE xp_part_init(SRV_PROC* pSrvProc){
  typedef bool (*Func)();
  
  if(nRef == 0){
  hInst = ::LoadLibrary("part.dll");
  if(hInst == NULL){
  printError(pSrvProc,"不能加載part.dll");
  return XP_ERROR;
  }
  Func theFunc = (Func)::GetProcAddress(hInst,"Init");
  if(!theFunc()){
  ::FreeLibrary(hInst);
  printError(pSrvProc,"不能獲得分類號與專輯的對應表");
  return XP_ERROR;
  }
  }
  
  ++ nRef;
  return (XP_NOERROR);
  }
  
  SRVRETCODE xp_part_process(SRV_PROC* pSrvProc){
  typedef bool (*Func)(char*);
  
  if(nRef == 0){
  printError(pSrvProc,"函數尚未初始化,請首先調用xp_part_init");
  return XP_ERROR;
  }
  Func theFunc = (Func)::GetProcAddress(hInst,"Get");
  
  BYTE bType;
  ULONG cbMaxLen,cbActualLen;
  BOOL fNull;
  char szInput[256] = {0};
  
  if (srv_paraminfo(pSrvProc, 1, &bType, (ULONG*)&cbMaxLen, (ULONG*)&cbActualLen, (BYTE*)szInput, &fNull) == FAIL){
  printError(pSrvProc,"srv_paraminfo 返回 FAIL");
  return XP_ERROR;
  }
  szInput[cbActualLen] = 0;
  
  string strInput = szInput;
  string strOutput = ";";
  int cur,old = 0;
  while(string::npos != (cur = strInput.find(';',old)) ){
  strncpy(szInput,strInput.c_str() + old,cur - old);
  szInput[cur - old] = 0;
  old = cur + 1;
  theFunc(szInput);
  
  if(string::npos ==strOutput.find((string)";" + szInput))
  strOutput += szInput;
  }
  
  strcpy(szInput,strOutput.c_str());
  if (FAIL == srv_paramsetoutput(pSrvProc, 1, (BYTE*)(szInput + 1), strlen(szInput) - 1,FALSE)){
  printError (pSrvProc, "srv_paramsetoutput 調用失敗");
  return XP_ERROR;
  }
  
  srv_senddone(pSrvProc, (SRV_DONE_COUNT | SRV_DONE_MORE), 0, 0);
  return XP_NOERROR;
  }
  
  SRVRETCODE xp_part_finalize(SRV_PROC* pSrvProc){
  typedef void (*Func)();
  
  if(nRef == 0)
  return XP_NOERROR;
  Func theFunc = (Func)::GetProcAddress(hInst,"Fin");
  
  if((--nRef) == 0){
  theFunc();
  ::FreeLibrary(hInst);
  hInst = NULL;
  }
  return (XP_NOERROR);
  }
  
  我想雖然看上去不是很高明,然而問題應該是解決了的。
  
  還有一點說明,為什么不使用Tls,老實說,我考慮過使用的,因為其實代碼是有一點問題的,假如一個用戶調用xp_part_init,然后另一個用戶也調用xp_part_init,注意我們的存儲過程可是服務器端的,然后第一個用戶調用xp_part_finalize,那么會怎樣,他仍然可以正常使用xp_part_process,這倒無所謂,然而第一個用戶調用兩次xp_part_finalize,就能夠影響第二個用戶了,他的 xp_part_process將返回錯誤。
  
  使用Tls 似乎可以解決這問題,例如再添加一個tls_index變量,調用 TlsSetValue保存用戶私人數據,TlsGetValue檢索私人數據,當xp_part_init時,假如該私人數據為0,執行正常的初始化過程,(即上面的xp_part_init)執行成功后存儲私人數據為1,假如是1,直接返回,xp_part_finalize時,假如私人數據為1,則執行正常的xp_part_finalize,然后設私人數據為0,假如是0,直接返回。
  
  好像想法還是不錯的,這樣隔離了多個用戶,安全性似乎提高了不少,然而事實是不可行的。因為Tls保存的并不是私人數據,而是線程本地變量,我們不能保證一個用戶的多次操作都是用同一個線程執行的,這個由SQL Server自己控制,事實上我在查詢分析器里多次執行的結果顯示,SQL Server內部似乎使用了一個線程池。既然如此,那這種想法也只能作罷。
  
  標題叫 編寫安全的SQL server擴展存儲過程,其實還是很不安全,除非調用者嚴格遵守 Init與Finalize的配對原則,幸運的是,這個是程序員可以控制的,只要遵守這個原則,那就是安全的。

關鍵字:server、存儲過程、數據

分享到:

頂部 】 【 關閉
版權所有:佛山思海電腦網絡有限公司 ©1998-2024 All Rights Reserved.
聯系電話:(0757)22630313、22633833
中華人民共和國增值電信業務經營許可證: 粵B1.B2-20030321 備案號:粵B2-20030321-1
網站公安備案編號:44060602000007 交互式欄目專項備案編號:200303DD003  
察察 工商 網安 舉報有獎  警警  手機打開網站