素直になれない人のwindows SSL
クロス開発をすると、問題になるのがライブラリです。
今回は、SSLを取り上げます。
めんどくさいwindowsのSSL
SSLをwindowsで使おうとすると、結構めんどくさいことがあります。
windowsのcrypt32 secure32とかの使い方が複雑すぎて人間が理解できないぐらいになっているのです。
作った奴は何を考えてこんな設計にしたのか、1ヶ月ぐらい問い詰めたいところです。
で、これではあんまりだということで、 wininetという素晴らしいライブラリがありますが、
こちらはどちらかというと抽象化されすぎているように思えて、小回りがきかないところがあります。
それに、よく使われる手法だからいまさらやっても面白くありませんw
linuxだとOpenSSL一択でしょう。
OSディフォルトで入っていたりして大変便利です。
windowsでも、OpenSSLのwindowsポーティングはありますが、導入はメンドイものです。
やっぱりVisualStudio大好きっことしては、VisualStudioプロジェクトとしてビルドできないとイヤです。
そうおもって、OpenSSLをハックして、VisualStudioでちゃんとコンパイルできるようにしたのですが、
実行してみると、謎のエラーが発生してクラッシュしてしまいました。
なんか結構奥まった所で、 void* ポインタにキャストしまくっているいう謎の処理で変な領域に読み書きして落ちているのでたちが悪い、これを直すのは大変だ!!ということで、あきらめました。
crypt32 secure32をなんとか使って、windowsでもSSLを実装してみよう!!と思いました。
webclient.c
資料としては、microsoftのサンプルソース webclient.c があります。
(元リンクがないんで、googleで見つけたこれとかですかね。 http://www.ualberta.ca/dept/aict/uts/software/openbsd/ports/4.6/i386/obj/pubcookie-3.3.3/pubcookie-3.3.3/src/Win32/WebClient.c )
それを紐解いて、 windowsとlinux(openssl)の両方で動作するソケットルーチンを作ってみました。
超長いですが、こんなかんじです。
ソース
//ヘッダー // XLSocket.h: XLSocket クラスのインターフェイス // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_XLSOCKET_H__137F6EB5_32A1_46CD_9CA7_EC6E9C6A6E6A__INCLUDED_) #define AFX_XLSOCKET_H__137F6EB5_32A1_46CD_9CA7_EC6E9C6A6E6A__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #if _MSC_VER #include <Winsock2.h> #include <mswsock.h> #define SECURITY_WIN32 #include <wincrypt.h> #include <wintrust.h> #include <schannel.h> #include <Security.h> #include <sspi.h> #else #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/ioctl.h> typedef int SOCKET; typedef struct sockaddr_in SOCKADDR_IN; typedef struct sockaddr SOCKADDR; typedef struct hostent HOSTENT; typedef struct in_addr IN_ADDR; const int INVALID_SOCKET = -1; const int SOCKET_ERROR = -1; #define WSAGetLastError() (errno) #define closesocket(p) close(p) #define ioctlsocket(p,a,b) ioctl((p),(a),(b)) #include <openssl/crypto.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/rand.h> #endif class XLSocket { public: /** * XLSocket: ソケット作成 * * @return */ XLSocket(); virtual ~XLSocket(); /** * XLSocket: 作成済みソケットの割り当て. * * @param inSocket * @return */ XLSocket(SOCKET inSocket); /** * Create: ソケットの作成 * * @param type ソケットの種類 SOCK_STREAM : TCP * SOCK_DGRAM : UDP * @return void */ void CreateLow( int type , int protocol ,int timeout = 60 ); void CreateTCP(int timeout = 60 ); void CreateUDP(int timeout = 60 ); void CreateSSL(int timeout = 60 ); /** * Close: ソケットを閉じる * * @return void */ void Close(); /** * ioctlsocket: * * @param inCmd * @param ioArgp * @return int */ int IoctlSocket(long inCmd , unsigned long * ioArgp); int SetSockopt( int level, int optname, const char * optval, int optlen ); int SetSockopt( int level, int optname, int optval ); int GetSockopt( int level, int optname, char * optval, int * optlen ); int GetSockopt( int level, int optname, int * optval ); void Connect(const std::string &inHost , int inPort); void Connect(const SOCKADDR_IN * inSai); void Connect(const SOCKADDR * inSai); void Bind(unsigned long inBindIP , int inPort); void Bind(const std::string& inHost , int inPort); void Bind(const SOCKADDR_IN * inSai); void Bind(const SOCKADDR * inSa); void Listen(int inBacklog = SOMAXCONN); XLSocket* Accept(); void Shutdown(); int Send( const char* inBuffer ,unsigned int inBufferLen ); int Recv( char* outBuffer ,unsigned int inBufferLen ); int SendTo(const char* inBuffer ,unsigned int inBufferLen , int inFlags ,struct sockaddr_in *senderinfo,int fromlen); int RecvFrom(char* outBuffer ,unsigned int inBufferLen , int inFlags,struct sockaddr_in *senderinfo,int* fromlen); SOCKADDR_IN ToSockAddrIn(const std::string &inHost , int inPort, int sin_family = AF_INET) ; SOCKADDR_IN ToSockAddrIn(unsigned long ip , int inPort, int sin_family = AF_INET) ; SOCKADDR ToSockAddr(const std::string &inHost , int inPort, int sin_family = AF_INET) ; SOCKADDR ToSockAddr(unsigned long ip , int inPort, int sin_family = AF_INET) ; int getErrorCode() const; std::string getErrorMessage(int errorcode) const; std::string getErrorMessage() const; static std::map<std::string,std::string> getIPAddressMap() ; SOCKET getSocketHandle() const { return this->Socket; } protected: /** * ErrorToMesage: ソケットエラーを文字列化 * * @param inErrorCode * @return string */ std::string ErrorToMesage(unsigned long inErrorCode) const; /** * Socket: ソケットハンドル */ SOCKET Socket; /** * Connected: 接続しているかどうか? */ bool Connected; bool UseSSL; #if _MSC_VER CredHandle Creds; CtxtHandle Context; #else //OpenSSL SSL_CTX* SSLContext; SSL* SSLhandle; #endif }; #endif // !defined(AFX_XLSOCKET_H__137F6EB5_32A1_46CD_9CA7_EC6E9C6A6E6A__INCLUDED_) //cpp //安いだけじゃないソケット!! // XLSocket.cpp: XLSocket クラスのインプリメンテーション // ////////////////////////////////////////////////////////////////////// #if _MSC_VER #pragma comment(lib, "ws2_32.lib") #endif #include "common.h" #include <string> #include <assert.h> #include <time.h> #include "XLException.h" #include "XLSocket.h" #if _MSC_VER //IPアドレスを取得するのに使う #include <iptypes.h> #include <iphlpapi.h> #pragma comment(lib, "IPHLPAPI.lib") #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "Crypt32.lib") #pragma comment(lib, "Secur32.lib") static void GetNewClientCredentials( CredHandle *phCreds, CtxtHandle *phContext) { CredHandle hCreds; SecPkgContext_IssuerListInfoEx IssuerListInfo; PCCERT_CHAIN_CONTEXT pChainContext; CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara; PCCERT_CONTEXT pCertContext; TimeStamp tsExpiry; SECURITY_STATUS Status; // // Read list of trusted issuers from schannel. // Status = QueryContextAttributes(phContext, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo); if(Status != SEC_E_OK) { // printf("Error 0x%x querying issuer list info\n", Status); return; } // // Enumerate the client certificates. // ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara)); FindByIssuerPara.cbSize = sizeof(FindByIssuerPara); FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH; FindByIssuerPara.dwKeySpec = 0; FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers; FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers; pChainContext = NULL; while(TRUE) { // Find a certificate chain. pChainContext = CertFindChainInStore(NULL, X509_ASN_ENCODING, 0, CERT_CHAIN_FIND_BY_ISSUER, &FindByIssuerPara, pChainContext); if(pChainContext == NULL) { // printf("Error 0x%x finding cert chain\n", GetLastError()); break; } // printf("\ncertificate chain found\n"); // Get pointer to leaf certificate context. pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; // Create schannel credential. SCHANNEL_CRED SchannelCred; SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; SchannelCred.cCreds = 1; SchannelCred.paCred = &pCertContext; Status = AcquireCredentialsHandleA( NULL, // Name of principal UNISP_NAME_A, // Name of package SECPKG_CRED_OUTBOUND, // Flags indicating use NULL, // Pointer to logon ID &SchannelCred, // Package specific data NULL, // Pointer to GetKey() func NULL, // Value to pass to GetKey() &hCreds, // (out) Cred Handle &tsExpiry); // (out) Lifetime (optional) if(Status != SEC_E_OK) { // printf("**** Error 0x%x returned by AcquireCredentialsHandle\n", Status); continue; } // printf("\nnew schannel credential created\n"); // Destroy the old credentials. FreeCredentialsHandle(phCreds); *phCreds = hCreds; // // As you can see, this sample code maintains a single credential // handle, replacing it as necessary. This is a little unusual. // // Many applications maintain a global credential handle that's // anonymous (that is, it doesn't contain a client certificate), // which is used to connect to all servers. If a particular server // should require client authentication, then a new credential // is created for use when connecting to that server. The global // anonymous credential is retained for future connections to // other servers. // // Maintaining a single anonymous credential that's used whenever // possible is most efficient, since creating new credentials all // the time is rather expensive. // break; } } static SECURITY_STATUS ClientHandshakeLoop( SOCKET Socket, // in PCredHandle phCreds, // in CtxtHandle * phContext, // in, out BOOL fDoInitialRead, // in SecBuffer * pExtraData) // out { SecBufferDesc InBuffer; SecBuffer InBuffers[2]; SecBufferDesc OutBuffer; SecBuffer OutBuffers[1]; DWORD dwSSPIFlags; DWORD dwSSPIOutFlags; TimeStamp tsExpiry; SECURITY_STATUS scRet; DWORD cbData; PUCHAR IoBuffer; DWORD cbIoBuffer; BOOL fDoRead; const DWORD IO_BUFFER_SIZE = 0x10000; dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; // // Allocate data buffer. // IoBuffer = (PUCHAR)LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE); if(IoBuffer == NULL) { // printf("**** Out of memory (1)\n"); return SEC_E_INTERNAL_ERROR; } cbIoBuffer = 0; fDoRead = fDoInitialRead; // // Loop until the handshake is finished or an error occurs. // scRet = SEC_I_CONTINUE_NEEDED; while(scRet == SEC_I_CONTINUE_NEEDED || scRet == SEC_E_INCOMPLETE_MESSAGE || scRet == SEC_I_INCOMPLETE_CREDENTIALS) { // // Read data from server. // if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { if(fDoRead) { cbData = recv(Socket, (char*) (IoBuffer + cbIoBuffer), IO_BUFFER_SIZE - cbIoBuffer, 0); if(cbData == SOCKET_ERROR) { // printf("**** Error %d reading data from server\n", WSAGetLastError()); scRet = SEC_E_INTERNAL_ERROR; break; } else if(cbData == 0) { // printf("**** Server unexpectedly disconnected\n"); scRet = SEC_E_INTERNAL_ERROR; break; } // printf("%d bytes of handshake data received\n", cbData); cbIoBuffer += cbData; } else { fDoRead = TRUE; } } // // Set up the input buffers. Buffer 0 is used to pass in data // received from the server. Schannel will consume some or all // of this. Leftover data (if any) will be placed in buffer 1 and // given a buffer type of SECBUFFER_EXTRA. // InBuffers[0].pvBuffer = IoBuffer; InBuffers[0].cbBuffer = cbIoBuffer; InBuffers[0].BufferType = SECBUFFER_TOKEN; InBuffers[1].pvBuffer = NULL; InBuffers[1].cbBuffer = 0; InBuffers[1].BufferType = SECBUFFER_EMPTY; InBuffer.cBuffers = 2; InBuffer.pBuffers = InBuffers; InBuffer.ulVersion = SECBUFFER_VERSION; // // Set up the output buffers. These are initialized to NULL // so as to make it less likely we'll attempt to free random // garbage later. // OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType= SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; // // Call InitializeSecurityContext. // scRet = InitializeSecurityContextA(phCreds, phContext, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, &InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); // // If InitializeSecurityContext was successful (or if the error was // one of the special extended ones), send the contends of the output // buffer to the server. // if(scRet == SEC_E_OK || scRet == SEC_I_CONTINUE_NEEDED || FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) { if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { cbData = send(Socket, (char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 0); if(cbData == SOCKET_ERROR || cbData == 0) { // printf("**** Error %d sending data to server (2)\n", // WSAGetLastError()); FreeContextBuffer(OutBuffers[0].pvBuffer); DeleteSecurityContext(phContext); return SEC_E_INTERNAL_ERROR; } // printf("%d bytes of handshake data sent\n", cbData); // Free output buffer. FreeContextBuffer(OutBuffers[0].pvBuffer); OutBuffers[0].pvBuffer = NULL; } } // // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, // then we need to read more data from the server and try again. // if(scRet == SEC_E_INCOMPLETE_MESSAGE) { continue; } // // If InitializeSecurityContext returned SEC_E_OK, then the // handshake completed successfully. // if(scRet == SEC_E_OK) { // // If the "extra" buffer contains data, this is encrypted application // protocol layer stuff. It needs to be saved. The application layer // will later decrypt it with DecryptMessage. // // printf("Handshake was successful\n"); if(InBuffers[1].BufferType == SECBUFFER_EXTRA) { pExtraData->pvBuffer = LocalAlloc(LMEM_FIXED, InBuffers[1].cbBuffer); if(pExtraData->pvBuffer == NULL) { // printf("**** Out of memory (2)\n"); return SEC_E_INTERNAL_ERROR; } MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer); pExtraData->cbBuffer = InBuffers[1].cbBuffer; pExtraData->BufferType = SECBUFFER_TOKEN; // printf("%d bytes of app data was bundled with handshake data\n", // pExtraData->cbBuffer); } else { pExtraData->pvBuffer = NULL; pExtraData->cbBuffer = 0; pExtraData->BufferType = SECBUFFER_EMPTY; } // // Bail out to quit // break; } // // Check for fatal error. // if(FAILED(scRet)) { // printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet); break; } // // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS, // then the server just requested client authentication. // if(scRet == SEC_I_INCOMPLETE_CREDENTIALS) { // // Busted. The server has requested client authentication and // the credential we supplied didn't contain a client certificate. // // // This function will read the list of trusted certificate // authorities ("issuers") that was received from the server // and attempt to find a suitable client certificate that // was issued by one of these. If this function is successful, // then we will connect using the new certificate. Otherwise, // we will attempt to connect anonymously (using our current // credentials). // GetNewClientCredentials(phCreds, phContext); // Go around again. fDoRead = FALSE; scRet = SEC_I_CONTINUE_NEEDED; continue; } // // Copy any leftover data from the "extra" buffer, and go around // again. // if ( InBuffers[1].BufferType == SECBUFFER_EXTRA ) { MoveMemory(IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer); cbIoBuffer = InBuffers[1].cbBuffer; } else { cbIoBuffer = 0; } } // Delete the security context in the case of a fatal error. if(FAILED(scRet)) { DeleteSecurityContext(phContext); } LocalFree(IoBuffer); return scRet; } static SECURITY_STATUS PerformClientHandshake( SOCKET Socket, // in PCredHandle phCreds, // in const char* pszServerName, // in CtxtHandle * phContext, // out SecBuffer * pExtraData) // out { SecBufferDesc OutBuffer; SecBuffer OutBuffers[1]; DWORD dwSSPIFlags; DWORD dwSSPIOutFlags; TimeStamp tsExpiry; SECURITY_STATUS scRet; DWORD cbData; dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; // // Initiate a ClientHello message and generate a token. // OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; scRet = InitializeSecurityContextA( phCreds, NULL, (char*)pszServerName, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, phContext, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); if(scRet != SEC_I_CONTINUE_NEEDED) { // printf("**** Error %d returned by InitializeSecurityContext (1)\n", scRet); return scRet; } // Send response to server if there is one. if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { cbData = send(Socket, (char*)(OutBuffers[0].pvBuffer), OutBuffers[0].cbBuffer, 0); if(cbData == SOCKET_ERROR || cbData == 0) { // printf("**** Error %d sending data to server (1)\n", WSAGetLastError()); FreeContextBuffer(OutBuffers[0].pvBuffer); DeleteSecurityContext(phContext); return SEC_E_INTERNAL_ERROR; } // printf("%d bytes of handshake data sent\n", cbData); // Free output buffer. FreeContextBuffer(OutBuffers[0].pvBuffer); OutBuffers[0].pvBuffer = NULL; } return ClientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData); } static LONG DisconnectFromServer( SOCKET Socket, PCredHandle phCreds, CtxtHandle * phContext) { DWORD dwType; PBYTE pbMessage; DWORD cbMessage; DWORD cbData; SecBufferDesc OutBuffer; SecBuffer OutBuffers[1]; DWORD dwSSPIFlags; DWORD dwSSPIOutFlags; TimeStamp tsExpiry; DWORD Status; // // Notify schannel that we are about to close the connection. // dwType = SCHANNEL_SHUTDOWN; OutBuffers[0].pvBuffer = &dwType; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = sizeof(dwType); OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; Status = ApplyControlToken(phContext, &OutBuffer); if(FAILED(Status)) { // printf("**** Error 0x%x returned by ApplyControlToken\n", Status); goto cleanup; } // // Build an SSL close notify message. // dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; OutBuffers[0].pvBuffer = NULL; OutBuffers[0].BufferType = SECBUFFER_TOKEN; OutBuffers[0].cbBuffer = 0; OutBuffer.cBuffers = 1; OutBuffer.pBuffers = OutBuffers; OutBuffer.ulVersion = SECBUFFER_VERSION; Status = InitializeSecurityContextA( phCreds, phContext, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, NULL, 0, phContext, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); if(FAILED(Status)) { // printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status); goto cleanup; } pbMessage = (PBYTE)OutBuffers[0].pvBuffer; cbMessage = OutBuffers[0].cbBuffer; // // Send the close notify message to the server. // if(pbMessage != NULL && cbMessage != 0) { cbData = send(Socket, (const char*)pbMessage, cbMessage, 0); if(cbData == SOCKET_ERROR || cbData == 0) { Status = WSAGetLastError(); // printf("**** Error %d sending close notify\n", Status); goto cleanup; } // printf("Sending Close Notify\n"); // printf("%d bytes of handshake data sent\n", cbData); // Free output buffer. FreeContextBuffer(pbMessage); } cleanup: // Free the security context. DeleteSecurityContext(phContext); return Status; } #endif ////////////////////////////////////////////////////////////////////// // 構築/消滅 ////////////////////////////////////////////////////////////////////// XLSocket::XLSocket() { this->Socket = INVALID_SOCKET; this->Connected = false; //SSL this->UseSSL = false; #if _MSC_VER #else this->SSLContext = NULL; this->SSLhandle = NULL; #endif } XLSocket::~XLSocket() { if (this->Connected) { Shutdown(); } if (this->Socket != INVALID_SOCKET) { Close(); } } XLSocket::XLSocket(SOCKET inSocket) { this->Socket = inSocket; this->Connected = true; this->UseSSL = false; #if _MSC_VER #else this->SSLhandle = NULL; this->SSLContext = NULL; #endif } void XLSocket::CreateLow( int type , int protocol ,int timeout) { Close(); //ソケットを開く this->Socket = ::socket(AF_INET,type,protocol); if (this->Socket == INVALID_SOCKET) { throw XLException( ErrorToMesage( WSAGetLastError() ) ); } #if _MSC_VER int32_t _timeout = timeout * 1000; setsockopt(this->Socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&_timeout, sizeof(_timeout)); setsockopt(this->Socket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&_timeout, sizeof(_timeout)); #else struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = 0; setsockopt(this->Socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(this->Socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); #endif } void XLSocket::CreateTCP(int timeout ) { CreateLow(SOCK_STREAM , 0,timeout); } void XLSocket::CreateUDP(int timeout ) { CreateLow(SOCK_DGRAM , 0,timeout); } void XLSocket::CreateSSL(int timeout ) { CreateLow(SOCK_STREAM , 0,timeout); this->UseSSL = true; } void XLSocket::Close() { if (this->Socket == INVALID_SOCKET) { return ; } if (this->UseSSL) { #if _MSC_VER DisconnectFromServer(this->Socket,&this->Creds,&this->Context); #else if (this->SSLhandle) { // SSL_shutdown (this->SSLhandle); SSL_free (this->SSLhandle); } if (this->SSLContext) { SSL_CTX_free (this->SSLContext); } #endif } ::closesocket(this->Socket); this->Socket = INVALID_SOCKET; } int XLSocket::IoctlSocket(long inCmd , unsigned long * ioArgp) { assert(this->Socket != INVALID_SOCKET); int ret = ::ioctlsocket(this->Socket , inCmd , ioArgp); if (ret == SOCKET_ERROR ) { throw XLException( ErrorToMesage( WSAGetLastError() ) ); } return ret; } int XLSocket::SetSockopt( int level, int optname, const char * optval, int optlen ) { assert(this->Socket != INVALID_SOCKET); return ::setsockopt(this->Socket , level , optname , optval , optlen); } int XLSocket::SetSockopt( int level, int optname, int optval ) { assert(this->Socket != INVALID_SOCKET); return ::setsockopt(this->Socket , level , optname , (char *)&optval , sizeof(int)); } int XLSocket::GetSockopt( int level, int optname, char * optval, int * optlen ) { assert(this->Socket != INVALID_SOCKET); return ::getsockopt(this->Socket , level , optname , optval , optlen); } int XLSocket::GetSockopt( int level, int optname, int * optval ) { assert(this->Socket != INVALID_SOCKET); char buffer[20]={0}; int optlen = sizeof(buffer)-1; int r = ::getsockopt(this->Socket , level , optname , buffer , &optlen); *optval = atoi(buffer); return r; } std::map<std::string,std::string> XLSocket::getIPAddressMap() //static { std::map<std::string,std::string> interfaces; #if _MSC_VER IP_ADAPTER_INFO adapterInfo[256]; ULONG ulOutBufLen = sizeof(adapterInfo); DWORD ret = GetAdaptersInfo(adapterInfo, &ulOutBufLen); if (ret != NO_ERROR) { // DEBUGLOG("IPアドレス一覧が取得できませんでした"); return interfaces; } for (const IP_ADAPTER_INFO* pAdapter = adapterInfo; pAdapter; pAdapter = pAdapter->Next) { if (strcmp(pAdapter->IpAddressList.IpAddress.String , "0.0.0.0") == 0 ) {//動いていないものはボツ // DEBUGLOG("IPインターフェース " << pAdapter->Description << " は動作していないので無視します"); continue; } if (strcmp(pAdapter->GatewayList.IpAddress.String , "0.0.0.0") == 0 ) {//動いているけど、 gatewayが設定されていないものは無視 // DEBUGLOG("IPインターフェース " << pAdapter->Description << " の、IPアドレス " << pAdapter->IpAddressList.IpAddress.String << " は、動作しているが gatewayが設定されていないので無視します。"); continue; } interfaces[pAdapter->Description] = pAdapter->IpAddressList.IpAddress.String; // DEBUGLOG("IPインターフェース " << pAdapter->Description << " の、IPアドレス " << pAdapter->IpAddressList.IpAddress.String << " を発見しました。"); } #else //http://www.geekpage.jp/programming/linux-network/book/04/4-10.php より int socket = ::socket(AF_INET, SOCK_DGRAM, 0); if (!socket) { // DEBUGLOG("IPアドレス一覧が取得できませんでした"); return interfaces; } struct ifconf ifc; //何個あるか取得する. ifc.ifc_len = 0; ifc.ifc_ifcu.ifcu_buf = NULL; ioctl(socket, SIOCGIFCONF, &ifc); //個数分のメモリ確保 ifc.ifc_ifcu.ifcu_buf = (void *)malloc(ifc.ifc_len); //実際の数 ioctl(socket, SIOCGIFCONF, &ifc); unsigned int nifs = ifc.ifc_len / sizeof(struct ifreq); struct ifreq * ifr = (struct ifreq *)ifc.ifc_ifcu.ifcu_buf; for (unsigned int i=0; i<nifs; i++) { if (strcmp(ifr[i].ifr_name,"lo")==0) { // DEBUGLOG("IPインターフェース " << ifr[i].ifr_name << " はローカルループバックなので無視します"); } else { interfaces[ifr[i].ifr_name] = inet_ntoa(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr); // DEBUGLOG("IPインターフェース " << ifr[i].ifr_name << " の、IPアドレス " << interfaces[ifr[i].ifr_name] << " を発見しました。"); } } free(ifc.ifc_ifcu.ifcu_buf); close(socket); #endif return interfaces; } std::string XLSocket::ErrorToMesage(unsigned long inErrorCode) const { #if _MSC_VER switch(inErrorCode) { case WSAEINTR: return std::string(" 関数呼び出しが中断された。\nWinSock 1.1 のブロッキング関数呼び出しが、WSACancelBlockingCall() によって中断された。"); case WSAEBADF: return std::string("無効なファイルハンドル。\nソケット関数に渡されたファイルハンドル(ソケットハンドル)が不正である。(WSAENOTSOCKと同義で用いられる)"); case WSAEACCES: return std::string("アクセスが拒否された。\nconnect():ブロードキャストアドレスで接続しようとしたソケットの SO_BROADCASTオプション が有効になっていない。\nsendto():ブロードキャストアドレスに送信しようとしたソケットの SO_BROADCASTオプションが有効になっていない。\naccept():受付しようとした接続要求がすでにタイムアウトになっている、もしくは取り消されている。"); case WSAEFAULT: return std::string("無効なバッファアドレス\n関数に渡されたバッファを指すポインタが無効なアドレス空間を指している。またはバッファのサイズが小さすぎる。"); case WSAEINVAL: return std::string("無効な引数が渡された。\nlisten()を呼び出さずにaccept()を呼び出した。\nバインド済みのソケットに対してbind()しようとした。\nlisten()で待機中のソケットでconnect()しようとした。\n関数に渡されたパラメータに無効な値が含まれている。"); case WSAEMFILE: return std::string("使用中のソケットの数が多すぎる。\n同時に利用できるソケット数の最大に達しており、新たにソケットを作成できない。"); // case WSAEWOULDBLOCK: return std::string("操作はブロッキングされる。\nノンブロッキングモードでソケット関数が呼び出されため、関数は即座にリターンしたが要求された操作は完了していない。"); case WSAEINPROGRESS: return std::string("すでにブロッキング手続きが実行されている。\nブロッキング関数が実行中であるか、サービスプロバイダがコールバック関数を処理中である。WinSockでは1つのプロセスで同時に複数のブロッキング操作をすることはできないため、前回呼び出した操作が完全に終了するまで次の操作を開始することはできない。"); case WSAEALREADY: return std::string("要求された操作は既に実行中、または実行済み。\nキャンセルしようとした非同期操作が既にキャンセルされている。\nconnect()を呼び出したが、既に前回の呼び出しによって接続処理中である。"); case WSAENOTSOCK: return std::string("指定されたソケットが無効である。\nソケットハンドルとして無効な値がソケット関数に渡された。"); case WSAEMSGSIZE: return std::string("メッセージサイズが大きすぎる。\n送信、または受信しようとしたメッセージが、基盤となるプロトコルのサポートしている最大メッセージサイズを超えている。ioctlsocket()でバッファサイズを増やすことで回避可能なこともある。"); case WSAEPROTOTYPE: return std::string("ソケットは要求されたプロトコルに適合していない。\nsocket()を呼び出すときに指定したプロトコルタイプがソケットタイプと適合していない。メッセージ型プロトコルでストリームソケットを作成しようとした場合など。"); case WSAENOPROTOOPT: return std::string("不正なプロトコルオプション。\n指定したソケットオプションまたはioctlsocket()での操作が、そのプロトコルではサポートされてない。\nストリームソケットに SO_BROADCAST を指定しようとした。"); case WSAEPROTONOSUPPORT: return std::string("プロトコルがサポートされていない。\nサポートされていないプロトコルを指定した、またはプロトコルのサポートしていない操作を実行しようとした。"); case WSAESOCKTNOSUPPORT: return std::string("指定されたソケットタイプはサポートされていない。"); case WSAEOPNOTSUPP: return std::string("要求された操作はサポートされていない。\nバンド外データを送信/受信しようとしたソケットでそれがサポートされていない。\n非接続型(UDPなど)ソケットでaccept()を呼び出した。"); case WSAEPFNOSUPPORT: return std::string("プロトコルファミリがサポートされていない。"); case WSAEAFNOSUPPORT: return std::string("アドレスファミリがサポートされていない。"); case WSAEADDRINUSE: return std::string("アドレスは既に使用中である。\nbind()しようとしたアドレスは、既にほかのソケットで使われている。同じローカルアドレスを複数のソケットで使うためには、SO_REUSEADDRソケットオプションをTRUEにする必要がある。"); case WSAEADDRNOTAVAIL: return std::string("無効なネットワークアドレス。\nソケット関数に渡されたネットワークアドレスに無効な部分がある。"); case WSAENETDOWN: return std::string("ネットワークがダウンしている。\nネットワークシステムが何らかの障害を起こし、機能停止しているため要求された操作が実行できない。"); case WSAENETUNREACH: return std::string("指定されたネットワークホストに到達できない。\nWSAEHOSTUNREACHと同義で使われている。"); case WSAENETRESET: return std::string("ネットワーク接続が破棄された。\nキープアライブを行っている接続で、何らかの障害のためタイムアウトが検出され接続が閉じられた。"); case WSAECONNABORTED: return std::string("ネットワーク接続が破棄された。"); case WSAECONNRESET: return std::string("ネットワーク接続が相手によって破棄された。"); case WSAENOBUFS: return std::string("バッファが不足している。\nメモリ不足のためWinSockが操作に必要なバッファを確保することができない。または、バッファを必要とする関数呼び出しで、小さすぎるバッファが渡された。"); case WSAEISCONN: return std::string("ソケットは既に接続されている。\n接続中のソケットに対してconnect()、listen()、bind()等の操作を行おうとした。\n接続中のソケットでsendto()を実行しようとした。"); case WSAENOTCONN: return std::string("ソケットは接続されていない。\n接続されていないソケットでsend()、getpeername()等の接続されたソケットを必要とする操作を実行しようとした。"); case WSAESHUTDOWN: return std::string("ソケットはシャットダウンされている。\n既にシャットダウンされて送信/受信が停止されているソケットで、送信または受信の操作を実行しようとした。"); case WSAETOOMANYREFS: return std::string("参照の数が多すぎる。"); case WSAETIMEDOUT: return std::string("接続要求がタイムアウトした。\nconnect()を呼び出して接続を試みたが、(相手の応答がない等で)処理がタイムアウトになり接続要求がキャンセルされた。"); case WSAECONNREFUSED: return std::string("接続が拒否された。"); case WSAELOOP: return std::string("ループ。"); case WSAENAMETOOLONG: return std::string("名前が長すぎる。"); case WSAEHOSTDOWN: return std::string("ホストがダウンしている。\nWSAETIMEDOUTと同義。"); case WSAEHOSTUNREACH: return std::string("ホストへの経路がない。\nネットワークの構造上到達できないホストに対して操作を実行しようとした。またはアドレスが不正である。"); case WSAENOTEMPTY: return std::string("ディレクトリが空ではない。"); case WSAEPROCLIM: return std::string("プロセスの数が多すぎる。\nWSAStartup():既にWinSockが管理できる最大プロセス数に達しており処理が実行できない。"); case WSAEUSERS: return std::string("ユーザーの数が多すぎる。"); case WSAEDQUOT: return std::string("ディスククォータ。"); case WSAESTALE: return std::string("実行しようとした操作は廃止されている。"); case WSAEREMOTE: return std::string("リモート。"); case WSASYSNOTREADY: return std::string("ネットワークサブシステムが利用できない。\nWSAStartup():ネットワークサブシステムが利用できない、または正しくセットアップされていないため機能していない。"); case WSAVERNOTSUPPORTED: return std::string("Winsock.dllのバージョンが範囲外である。\nWSAStartup():要求したWinSockバージョンは、現在の実装ではサポートされていない。"); case WSANOTINITIALISED: return std::string("WinSockシステムが初期化されていない。\nWinSock関数を実行しようとしたが、WSAStartup()が正常に実行されていないため機能しない。"); case WSAEDISCON: return std::string("シャットダウン処理中。\n接続が相手の処理によって切断されようとしている。"); case WSAHOST_NOT_FOUND: return std::string("ホストが見つからない。\nDNSなどの名前解決サービスが指定されたホストを見つけられなかった。プロトコルやサービスのクエリー操作においても返される。"); case WSATRY_AGAIN: return std::string("指定されたホストが見つからない、またはサービスの異常。"); case WSANO_RECOVERY: return std::string("回復不能なエラーが発生した。\nデータベース検索の場合は、名前解決サービスが使用できないことを意味する。"); case WSANO_DATA: return std::string("要求されたタイプのデータレコードが見つからない。"); } #endif return std::string() + "不明のエラーメッセージ " + num2str( inErrorCode); } SOCKADDR_IN XLSocket::ToSockAddrIn(const std::string &inHost , int inPort, int sin_family) { SOCKADDR_IN sai = {0}; //IP? unsigned long connectIP = inet_addr( inHost.c_str() ); if(connectIP==INADDR_NONE) { //名前のようなので解決してみる. HOSTENT * hostEnt; hostEnt=gethostbyname(inHost.c_str() ); if(!hostEnt) { throw XLException("ホスト名を解決できません" + ErrorToMesage(WSAGetLastError() ) ); } sai.sin_addr=*((IN_ADDR*)*hostEnt->h_addr_list); } else { #if _MSC_VER sai.sin_addr.S_un.S_addr = connectIP; #else sai.sin_addr.s_addr = connectIP; #endif } sai.sin_port=htons((unsigned short)inPort); sai.sin_family=sin_family; return sai; } SOCKADDR_IN XLSocket::ToSockAddrIn(unsigned long ip , int inPort, int sin_family) { SOCKADDR_IN sai = {0}; sai.sin_family = sin_family; sai.sin_addr.s_addr = htonl(ip); sai.sin_port = htons((unsigned short)inPort); return sai; } SOCKADDR XLSocket::ToSockAddr(const std::string &inHost , int inPort, int sin_family) { SOCKADDR_IN sai = ToSockAddrIn(inHost,inPort,sin_family); return *((SOCKADDR*)(&sai)); } SOCKADDR XLSocket::ToSockAddr(unsigned long ip , int inPort, int sin_family) { SOCKADDR_IN sai = ToSockAddrIn(ip,inPort,sin_family); return *((SOCKADDR*)(&sai)); } void XLSocket::Connect(const std::string &inHost , int inPort ) { assert(this->Socket != INVALID_SOCKET); assert(this->Connected == false); SOCKADDR_IN sai = ToSockAddrIn(inHost,inPort); //接続を試みる. const int ret = connect(Socket,(SOCKADDR *)&sai,sizeof(SOCKADDR_IN)); if( ret == SOCKET_ERROR ) { throw XLException("接続に失敗しました" + ErrorToMesage( WSAGetLastError() ) ); } this->Connected = true; if (this->UseSSL) { #if _MSC_VER TimeStamp tsExpiry; SECURITY_STATUS ss; ss = AcquireCredentialsHandleA( NULL, // Name of principal UNISP_NAME_A, // Name of package SECPKG_CRED_OUTBOUND, // Flags indicating use NULL, // Pointer to logon ID NULL, // Package specific data NULL, // Pointer to GetKey() func NULL, // Value to pass to GetKey() &this->Creds, // (out) Cred Handle &tsExpiry); // (out) Lifetime (optional) if(ss != SEC_E_OK) { throw XLException("AcquireCredentialsHandleAに失敗しました" + num2str(ss) ); } // サーバーとのハンドシェイク SecBuffer extraData; ss = PerformClientHandshake(Socket, &this->Creds,inHost.c_str(), &this->Context, &extraData); if(ss != SEC_E_OK) { throw XLException("ClientHandshakeLoopに失敗しました" + num2str(ss) ); } #else // Register the error strings for libcrypto & libssl //SSL_load_error_strings (); // Register the available ciphers and digests SSL_library_init (); // New context saying we are a client, and using SSL 2 or 3 this->SSLContext = SSL_CTX_new (SSLv23_client_method ()); if (this->SSLContext == NULL) { throw XLException("SSL_CTX_newに失敗しました"); } // Create an SSL struct for the connection this->SSLhandle = SSL_new (this->SSLContext); if (this->SSLhandle == NULL) { throw XLException("SSL_newに失敗しました"); } // Connect the SSL struct to our connection if (!SSL_set_fd (this->SSLhandle, this->Socket)) { throw XLException("SSL_set_fdに失敗しました"); } // Initiate SSL handshake int r; r=SSL_connect (this->SSLhandle); if (r != 1) { throw XLException("SSL_connectに失敗しました"); } #endif } } void XLSocket::Bind(unsigned long inBindIP , int inPort) { assert(this->Socket != INVALID_SOCKET); assert(this->Connected == false); SOCKADDR sa = ToSockAddr(inBindIP,inPort); this->Bind( &sa ); } void XLSocket::Bind(const std::string& inHost , int inPort) { assert(this->Socket != INVALID_SOCKET); assert(this->Connected == false); SOCKADDR sa = ToSockAddr(inHost,inPort); this->Bind( &sa ); } void XLSocket::Bind(const SOCKADDR_IN * inSai) { assert(this->Socket != INVALID_SOCKET); assert(this->Connected == false); Bind((const SOCKADDR*)inSai); } void XLSocket::Bind(const SOCKADDR * inSa) { assert(this->Socket != INVALID_SOCKET); assert(this->Connected == false); int Opt = 1; this->SetSockopt(SOL_SOCKET, SO_REUSEADDR, (const char*)&Opt , sizeof(int) ); int ret = ::bind( this->Socket ,inSa , sizeof(SOCKADDR) ); if (ret < 0) { throw XLException("bind に失敗しました" + ErrorToMesage( WSAGetLastError() ) ); } this->Connected = true; } void XLSocket::Listen(int inBacklog ) { int ret = ::listen( this->Socket , inBacklog ); if (ret < 0) { throw XLException("listen に失敗しました" + ErrorToMesage( WSAGetLastError() ) ); } } XLSocket* XLSocket::Accept() { assert(Socket != INVALID_SOCKET); SOCKET newSock = ::accept( this->Socket , (struct sockaddr*) NULL , NULL); if (newSock <= 0 ) { throw XLException("accept に失敗しました" + ErrorToMesage( WSAGetLastError() ) ); } if (this->UseSSL) { #if _MSC_VER #else SSL_accept(this->SSLhandle); #endif } //新規に作成して返す return new XLSocket(newSock); } void XLSocket::Shutdown() { if ( this->Socket == INVALID_SOCKET) return ; if ( this->Connected == false) return ; ::shutdown( this->Socket , 1 ); //この時間になってもつないでいる場合は、即切る. time_t TimeOutTime = time(NULL) + 10; //10秒待つ. //保留中のすべてのデータを受け取る. int ret; char buffer[1024]; while(1) { ret = ::recv( this->Socket , buffer , 1024 , 0 ); if ( ret <= 0 || ret == SOCKET_ERROR) break; //タイムアウトチェック if ( TimeOutTime < time(NULL) ) { break; } } //データをこれ以上受信しないことを相手に伝える. ::shutdown( this->Socket , 2); this->Connected = false; } int XLSocket::Send( const char* inBuffer ,unsigned int inBufferLen ) { unsigned int sended = 0; if (this->UseSSL) { #if _MSC_VER SecPkgContext_StreamSizes Sizes; SECURITY_STATUS ss; ss = QueryContextAttributes(&this->Context, SECPKG_ATTR_STREAM_SIZES, &Sizes); if(ss != SEC_E_OK) { throw XLException("QueryContextAttributesに失敗しました" + num2str(ss) ); } auto cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer; std::vector<char> buffer(cbIoBufferLength); char * pbIoBuffer = &buffer[0]; char * pbMessage = pbIoBuffer + Sizes.cbHeader; while(1) { //メッセージの最大値があるのでそれを超える場合は分割する(めんどくさい) const unsigned int nowsize = min(inBufferLen - sended,Sizes.cbMaximumMessage); memcpy(pbMessage, inBuffer+sended,nowsize); SecBuffer Buffers[4]; Buffers[0].pvBuffer = pbIoBuffer; Buffers[0].cbBuffer = Sizes.cbHeader; Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; Buffers[1].pvBuffer = pbMessage; Buffers[1].cbBuffer = nowsize; Buffers[1].BufferType = SECBUFFER_DATA; Buffers[2].pvBuffer = pbMessage + inBufferLen; Buffers[2].cbBuffer = Sizes.cbTrailer; Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; Buffers[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc messageB; messageB.ulVersion = SECBUFFER_VERSION; messageB.cBuffers = 4; messageB.pBuffers = Buffers; ss = EncryptMessage(&this->Context, 0, &messageB, 0); if(ss != SEC_E_OK) { throw XLException("EncryptMessageに失敗しました" + num2str(ss) ); } //暗号化ヘッダーなどのサイズをすべて含めた送信するバイト数 const unsigned int allsize = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer; unsigned int currentSended = 0; while(1) { int ret = ::send( this->Socket , pbIoBuffer + currentSended, allsize - currentSended, 0); if (ret < 0) { return ret; } currentSended += ret; if (currentSended >= allsize) break; } //送信できたサイズは本文だけのサイズに丸める sended += nowsize; if (sended >= inBufferLen) { return (int)sended; } } #else while(1) { int ret = SSL_write( this->SSLhandle ,(void*) (inBuffer + sended), inBufferLen - sended); if (ret < 0) { return ret; } sended += ret; // printf("SSL_write %d\n",ret); if (sended >= inBufferLen) return (int)sended; } #endif } else { while(1) { int ret = ::send( this->Socket , inBuffer + sended, inBufferLen - sended, 0); if (ret < 0) { return ret; } sended += ret; if (sended >= inBufferLen) return (int)sended; } } return (int)sended; } int XLSocket::Recv( char* outBuffer ,unsigned int inBufferLen ) { unsigned int recved = 0; if (this->UseSSL) { #if _MSC_VER SecPkgContext_StreamSizes Sizes; SECURITY_STATUS ss; ss = QueryContextAttributes(&this->Context, SECPKG_ATTR_STREAM_SIZES, &Sizes); if(ss != SEC_E_OK) { throw XLException("QueryContextAttributesに失敗しました" + num2str(ss) ); } auto cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer; std::vector<char> buffer(cbIoBufferLength); char * pbIoBuffer = &buffer[0]; char * pbMessage = pbIoBuffer + Sizes.cbHeader; int cbIoBuffer = 0; while(TRUE) { if(0 == cbIoBuffer || ss == SEC_E_INCOMPLETE_MESSAGE) { int ret = recv(this->Socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0); if (ret < 0) { return ret; } if (ret == 0) { break; } cbIoBuffer += ret; } // // Attempt to decrypt the received data. // SecBuffer Buffers[4]; Buffers[0].pvBuffer = pbIoBuffer; Buffers[0].cbBuffer = cbIoBuffer; Buffers[0].BufferType = SECBUFFER_DATA; Buffers[1].BufferType = SECBUFFER_EMPTY; Buffers[2].BufferType = SECBUFFER_EMPTY; Buffers[3].BufferType = SECBUFFER_EMPTY; SecBufferDesc Message; Message.ulVersion = SECBUFFER_VERSION; Message.cBuffers = 4; Message.pBuffers = Buffers; ss = DecryptMessage(&this->Context, &Message, 0, NULL); if(ss == SEC_E_INCOMPLETE_MESSAGE) { // The input buffer contains only a fragment of an // encrypted record. Loop around and read some more // data. continue; } // Server signalled end of session if(ss == SEC_I_CONTEXT_EXPIRED) break; if( ss != SEC_E_OK && ss != SEC_I_RENEGOTIATE && ss != SEC_I_CONTEXT_EXPIRED) { throw XLException("DecryptMessageに失敗 " ); } // Locate data and (optional) extra buffers. SecBuffer* pDataBuffer = NULL; SecBuffer* pExtraBuffer = NULL; for(int i = 1; i < 4; i++) { if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA) { pDataBuffer = &Buffers[i]; } if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA) { pExtraBuffer = &Buffers[i]; } } if(pDataBuffer) { if (inBufferLen < recved + pDataBuffer->cbBuffer ) {//受信バッファー以上にデータが来たので丸める memcpy(outBuffer+recved , pDataBuffer->pvBuffer , inBufferLen - recved ); return inBufferLen; } memcpy(outBuffer+recved , pDataBuffer->pvBuffer , pDataBuffer->cbBuffer); recved += pDataBuffer->cbBuffer; } // Move any "extra" data to the input buffer. if(pExtraBuffer) { MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); cbIoBuffer = pExtraBuffer->cbBuffer; } else { cbIoBuffer = 0; } if(ss == SEC_I_RENEGOTIATE) { // The server wants to perform another handshake // sequence. SecBuffer extraData; ss = ClientHandshakeLoop(this->Socket, &this->Creds, &this->Context, FALSE, &extraData); if(ss != SEC_E_OK) { throw XLException("ClientHandshakeLoopに失敗 " ); } // Move any "extra" data to the input buffer. if(extraData.pvBuffer) { MoveMemory(pbIoBuffer, extraData.pvBuffer, extraData.cbBuffer); cbIoBuffer = extraData.cbBuffer; } } } #else while(1) { int ret = SSL_read( this->SSLhandle ,(void*) (outBuffer + recved), inBufferLen - recved); // printf("SSL_read %d\n",ret); if (ret < 0) { return ret; } if (ret == 0) { break; } recved += ret; if (recved >= inBufferLen) return recved; } #endif } else { while(1) { int ret = ::recv( this->Socket , outBuffer + recved , inBufferLen - recved, 0); if (ret < 0) { return ret; } if (ret == 0) { break; } recved += ret; if (recved >= inBufferLen) break; } } return recved; } int XLSocket::SendTo(const char* inBuffer ,unsigned int inBufferLen , int inFlags ,struct sockaddr_in *senderinfo,int fromlen) { int ret = ::sendto(this->Socket,inBuffer,inBufferLen,inFlags,(struct sockaddr *)senderinfo, fromlen); if (ret < 0) { return ret; } return ret; } int XLSocket::RecvFrom(char* outBuffer ,unsigned int inBufferLen , int inFlags,struct sockaddr_in *senderinfo,int* fromlen ) { *fromlen = sizeof(struct sockaddr_in); int ret = ::recvfrom(this->Socket,outBuffer,inBufferLen,inFlags,(struct sockaddr *)senderinfo, fromlen ); if (ret < 0) { return ret; } return ret; } int XLSocket::getErrorCode() const { return WSAGetLastError(); } std::string XLSocket::getErrorMessage(int errorcode) const { return ErrorToMesage( errorcode ); } std::string XLSocket::getErrorMessage() const { return ErrorToMesage( WSAGetLastError() ); }
一応、ちゃんと動作します。
しかし、この分けわかんなさは異常ですね。
windowsとlinuxでSSL使いたいけど、
windowsでOpenSSLとか嫌だし、wininetとかも嫌だ!!と素直になれない人は使うといいんじゃないんでしょうか。