| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * $Id$ |
| */ |
| |
| |
| #include <xercesc/util/NetAccessors/WinSock/BinHTTPURLInputStream.hpp> |
| #include <windows.h> |
| |
| #ifdef WITH_IPV6 |
| #include <ws2tcpip.h> |
| #endif |
| |
| #include <cstdio> |
| #include <string.h> |
| |
| #include <xercesc/util/PlatformUtils.hpp> |
| #include <xercesc/util/XMLNetAccessor.hpp> |
| #include <xercesc/util/XMLString.hpp> |
| #include <xercesc/util/XMLExceptMsgs.hpp> |
| #include <xercesc/util/Janitor.hpp> |
| #include <xercesc/util/XMLUniDefs.hpp> |
| #include <xercesc/util/Mutexes.hpp> |
| |
| XERCES_CPP_NAMESPACE_BEGIN |
| |
| typedef u_short (WSAAPI * LPFN_HTONS)(u_short hostshort); |
| typedef SOCKET (WSAAPI * LPFN_SOCKET)(int af, int type, int protocol); |
| typedef int (WSAAPI * LPFN_CONNECT)(SOCKET s, const struct sockaddr* name, int namelen); |
| typedef int (WSAAPI * LPFN_SEND)(SOCKET s, const char* buf, int len, int flags); |
| typedef int (WSAAPI * LPFN_RECV)(SOCKET s, char* buf, int len, int flags); |
| typedef int (WSAAPI * LPFN_SHUTDOWN)(SOCKET s, int how); |
| typedef int (WSAAPI * LPFN_CLOSESOCKET)(SOCKET s); |
| typedef int (WSAAPI * LPFN_WSACLEANUP)(); |
| typedef int (WSAAPI * LPFN_WSASTARTUP)(WORD wVersionRequested, LPWSADATA lpWSAData); |
| #ifdef WITH_IPV6 |
| typedef int (WSAAPI * LPFN_GETADDRINFO)(const char* nodename, const char * servname, const struct addrinfo * hints, struct addrinfo ** res); |
| typedef void (WSAAPI * LPFN_FREEADDRINFO)(struct addrinfo * ai); |
| #else |
| typedef struct hostent *(WSAAPI * LPFN_GETHOSTBYNAME)(const char* name); |
| typedef struct hostent *(WSAAPI * LPFN_GETHOSTBYADDR)(const char* addr, int len, int type); |
| typedef unsigned long (WSAAPI * LPFN_INET_ADDR)(const char* cp); |
| #endif |
| |
| static HMODULE gWinsockLib = NULL; |
| static LPFN_HTONS gWShtons = NULL; |
| static LPFN_SOCKET gWSsocket = NULL; |
| static LPFN_CONNECT gWSconnect = NULL; |
| static LPFN_SEND gWSsend = NULL; |
| static LPFN_RECV gWSrecv = NULL; |
| static LPFN_SHUTDOWN gWSshutdown = NULL; |
| static LPFN_CLOSESOCKET gWSclosesocket = NULL; |
| static LPFN_WSACLEANUP gWSACleanup = NULL; |
| #ifdef WITH_IPV6 |
| static LPFN_GETADDRINFO gWSgetaddrinfo = NULL; |
| static LPFN_FREEADDRINFO gWSfreeaddrinfo = NULL; |
| #else |
| static LPFN_GETHOSTBYNAME gWSgethostbyname = NULL; |
| static LPFN_GETHOSTBYADDR gWSgethostbyaddr = NULL; |
| static LPFN_INET_ADDR gWSinet_addr = NULL; |
| #endif |
| |
| static u_short wrap_htons(u_short hostshort) |
| { |
| return (*gWShtons)(hostshort); |
| } |
| |
| static SOCKET wrap_socket(int af,int type,int protocol) |
| { |
| return (*gWSsocket)(af,type,protocol); |
| } |
| |
| static int wrap_connect(SOCKET s,const struct sockaddr* name,int namelen) |
| { |
| return (*gWSconnect)(s,name,namelen); |
| } |
| |
| static int wrap_send(SOCKET s,const char* buf,int len,int flags) |
| { |
| return (*gWSsend)(s,buf,len,flags); |
| } |
| |
| static int wrap_recv(SOCKET s,char* buf,int len,int flags) |
| { |
| return (*gWSrecv)(s,buf,len,flags); |
| } |
| |
| static int wrap_shutdown(SOCKET s,int how) |
| { |
| return (*gWSshutdown)(s,how); |
| } |
| |
| static int wrap_closesocket(SOCKET socket) |
| { |
| return (*gWSclosesocket)(socket); |
| } |
| |
| #ifdef WITH_IPV6 |
| static int wrap_getaddrinfo(const char* nodename,const char* servname,const struct addrinfo* hints,struct addrinfo** res) |
| { |
| return (*gWSgetaddrinfo)(nodename,servname,hints,res); |
| } |
| |
| static void wrap_freeaddrinfo(struct addrinfo* ai) |
| { |
| (*gWSfreeaddrinfo)(ai); |
| } |
| #else |
| static struct hostent* wrap_gethostbyname(const char* name) |
| { |
| return (*gWSgethostbyname)(name); |
| } |
| |
| static struct hostent* wrap_gethostbyaddr(const char* addr,int len,int type) |
| { |
| return (*gWSgethostbyaddr)(addr,len,type); |
| } |
| |
| static unsigned long wrap_inet_addr(const char* cp) |
| { |
| return (*gWSinet_addr)(cp); |
| } |
| |
| #endif |
| |
| |
| class SocketJanitor |
| { |
| public: |
| // ----------------------------------------------------------------------- |
| // Constructors and Destructor |
| // ----------------------------------------------------------------------- |
| SocketJanitor(SOCKET* toDelete) : fData(toDelete) {} |
| ~SocketJanitor() { reset(); } |
| |
| SOCKET* get() const { return fData; } |
| SOCKET* release() { SOCKET* p = fData; fData = 0; return p; } |
| |
| void reset(SOCKET* p = 0) |
| { |
| if(fData) { |
| wrap_shutdown(*fData, SD_BOTH); |
| wrap_closesocket(*fData); |
| } |
| fData = p; |
| } |
| bool isDataNull() { return (fData == 0); } |
| |
| private : |
| // ----------------------------------------------------------------------- |
| // Unimplemented constructors and operators |
| // ----------------------------------------------------------------------- |
| SocketJanitor(); |
| SocketJanitor(const SocketJanitor&); |
| SocketJanitor& operator=(const SocketJanitor&); |
| |
| // ----------------------------------------------------------------------- |
| // Private data members |
| // |
| // fData |
| // This is the pointer to the socket that must be closed when |
| // this object is destroyed. |
| // ----------------------------------------------------------------------- |
| SOCKET* fData; |
| }; |
| |
| bool BinHTTPURLInputStream::fInitialized = false; |
| |
| void BinHTTPURLInputStream::Initialize(MemoryManager* const manager) |
| { |
| // |
| // Initialize the WinSock library here. |
| // |
| WORD wVersionRequested; |
| WSADATA wsaData; |
| |
| LPFN_WSASTARTUP startup = NULL; |
| if(gWinsockLib == NULL) |
| { |
| #ifdef WITH_IPV6 |
| gWinsockLib = LoadLibraryA("WS2_32"); |
| #else |
| gWinsockLib = LoadLibraryA("WSOCK32"); |
| #endif |
| if(gWinsockLib == NULL) |
| { |
| ThrowXMLwithMemMgr(NetAccessorException, XMLExcepts::NetAcc_InitFailed, manager); |
| } |
| else |
| { |
| startup = (LPFN_WSASTARTUP) GetProcAddress(gWinsockLib,"WSAStartup"); |
| gWSACleanup = (LPFN_WSACLEANUP) GetProcAddress(gWinsockLib,"WSACleanup"); |
| gWShtons = (LPFN_HTONS) GetProcAddress(gWinsockLib,"htons"); |
| gWSsocket = (LPFN_SOCKET) GetProcAddress(gWinsockLib,"socket"); |
| gWSconnect = (LPFN_CONNECT) GetProcAddress(gWinsockLib,"connect"); |
| gWSsend = (LPFN_SEND) GetProcAddress(gWinsockLib,"send"); |
| gWSrecv = (LPFN_RECV) GetProcAddress(gWinsockLib,"recv"); |
| gWSshutdown = (LPFN_SHUTDOWN) GetProcAddress(gWinsockLib,"shutdown"); |
| gWSclosesocket = (LPFN_CLOSESOCKET) GetProcAddress(gWinsockLib,"closesocket"); |
| #ifdef WITH_IPV6 |
| gWSgetaddrinfo = (LPFN_GETADDRINFO) GetProcAddress(gWinsockLib,"getaddrinfo"); |
| gWSfreeaddrinfo = (LPFN_FREEADDRINFO) GetProcAddress(gWinsockLib,"freeaddrinfo"); |
| #else |
| gWSgethostbyname = (LPFN_GETHOSTBYNAME) GetProcAddress(gWinsockLib,"gethostbyname"); |
| gWSgethostbyaddr = (LPFN_GETHOSTBYADDR) GetProcAddress(gWinsockLib,"gethostbyaddr"); |
| gWSinet_addr = (LPFN_INET_ADDR) GetProcAddress(gWinsockLib,"inet_addr"); |
| #endif |
| |
| if(startup == NULL |
| || gWSACleanup == NULL |
| || gWShtons == NULL |
| || gWSsocket == NULL |
| || gWSconnect == NULL |
| || gWSsend == NULL |
| || gWSrecv == NULL |
| || gWSshutdown == NULL |
| || gWSclosesocket == NULL |
| #ifdef WITH_IPV6 |
| || gWSgetaddrinfo == NULL |
| || gWSfreeaddrinfo == NULL |
| #else |
| || gWSgethostbyname == NULL |
| || gWSgethostbyaddr == NULL |
| || gWSinet_addr == NULL |
| #endif |
| ) |
| { |
| gWSACleanup = NULL; |
| Cleanup(); |
| ThrowXMLwithMemMgr(NetAccessorException, XMLExcepts::NetAcc_InitFailed, manager); |
| } |
| } |
| } |
| |
| wVersionRequested = MAKEWORD( 2, 2 ); |
| |
| int err = (*startup)(wVersionRequested, &wsaData); |
| if (err != 0) |
| { |
| // Call WSAGetLastError() to get the last error. |
| ThrowXMLwithMemMgr(NetAccessorException, XMLExcepts::NetAcc_InitFailed, manager); |
| } |
| fInitialized = true; |
| } |
| |
| void BinHTTPURLInputStream::Cleanup() |
| { |
| XMLMutexLock lock(XMLPlatformUtils::fgAtomicMutex); |
| |
| if(fInitialized) |
| { |
| if(gWSACleanup) (*gWSACleanup)(); |
| gWSACleanup = NULL; |
| FreeLibrary(gWinsockLib); |
| gWinsockLib = NULL; |
| gWShtons = NULL; |
| gWSsocket = NULL; |
| gWSconnect = NULL; |
| gWSsend = NULL; |
| gWSrecv = NULL; |
| gWSshutdown = NULL; |
| gWSclosesocket = NULL; |
| #ifdef WITH_IPV6 |
| gWSgetaddrinfo = NULL; |
| gWSfreeaddrinfo = NULL; |
| #else |
| gWSgethostbyname = NULL; |
| gWSgethostbyaddr = NULL; |
| gWSinet_addr = NULL; |
| #endif |
| |
| fInitialized = false; |
| } |
| } |
| |
| BinHTTPURLInputStream::BinHTTPURLInputStream(const XMLURL& urlSource, const XMLNetHTTPInfo* httpInfo /*=0*/) |
| : BinHTTPInputStreamCommon(urlSource.getMemoryManager()) |
| , fSocketHandle(0) |
| { |
| MemoryManager *memoryManager = urlSource.getMemoryManager(); |
| |
| // Check if we need to load the winsock library. While locking the |
| // mutex every time may be somewhat slow, we don't care in this |
| // particular case since the next operation will most likely be |
| // the network access which is a lot slower. |
| // |
| { |
| XMLMutexLock lock(XMLPlatformUtils::fgAtomicMutex); |
| |
| if (!fInitialized) |
| Initialize(memoryManager); |
| } |
| |
| // |
| // Pull all of the parts of the URL out of th urlSource object, and transcode them |
| // and transcode them back to ASCII. |
| // |
| const XMLCh* hostName = urlSource.getHost(); |
| |
| if (hostName == 0) |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::File_CouldNotOpenFile, |
| urlSource.getURLText(), |
| memoryManager); |
| |
| char* hostNameAsCharStar = XMLString::transcode(hostName, memoryManager); |
| ArrayJanitor<char> janHostNameAsCharStar(hostNameAsCharStar, memoryManager); |
| |
| XMLURL url(urlSource); |
| int redirectCount = 0; |
| SocketJanitor janSock(0); |
| |
| do { |
| // |
| // Set up a socket. |
| // |
| |
| #ifdef WITH_IPV6 |
| struct addrinfo hints, *res, *ai; |
| |
| CharBuffer portBuffer(10, memoryManager); |
| portBuffer.appendDecimalNumber(url.getPortNum()); |
| |
| memset(&hints, 0, sizeof(struct addrinfo)); |
| hints.ai_family = PF_UNSPEC; |
| hints.ai_socktype = SOCK_STREAM; |
| int n = wrap_getaddrinfo(hostNameAsCharStar,portBuffer.getRawBuffer(),&hints, &res); |
| if(n != 0) |
| { |
| hints.ai_flags = AI_NUMERICHOST; |
| n = wrap_getaddrinfo(hostNameAsCharStar,(const char*)tempbuf,&hints, &res); |
| if(n != 0) |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::NetAcc_TargetResolution, hostName, memoryManager); |
| } |
| janSock.reset(); |
| for (ai = res; ai != NULL; ai = ai->ai_next) { |
| // Open a socket with the correct address family for this address. |
| fSocketHandle = wrap_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
| if (fSocketHandle == INVALID_SOCKET) |
| continue; |
| janSock.reset(&fSocketHandle); |
| if (wrap_connect(fSocketHandle, ai->ai_addr, (int)ai->ai_addrlen) == SOCKET_ERROR) |
| { |
| wrap_freeaddrinfo(res); |
| // Call WSAGetLastError() to get the error number. |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_ConnSocket, url.getURLText(), memoryManager); |
| } |
| break; |
| } |
| wrap_freeaddrinfo(res); |
| if (fSocketHandle == INVALID_SOCKET) |
| { |
| // Call WSAGetLastError() to get the error number. |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_CreateSocket, url.getURLText(), memoryManager); |
| } |
| #else |
| struct hostent* hostEntPtr = 0; |
| struct sockaddr_in sa; |
| |
| |
| if ((hostEntPtr = wrap_gethostbyname(hostNameAsCharStar)) == NULL) |
| { |
| unsigned long numAddress = wrap_inet_addr(hostNameAsCharStar); |
| if (numAddress == INADDR_NONE) |
| { |
| // Call WSAGetLastError() to get the error number. |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_TargetResolution, hostName, memoryManager); |
| } |
| if ((hostEntPtr = |
| wrap_gethostbyaddr((const char *) &numAddress, |
| sizeof(unsigned long), AF_INET)) == NULL) |
| { |
| // Call WSAGetLastError() to get the error number. |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_TargetResolution, hostName, memoryManager); |
| } |
| } |
| |
| memcpy((void *) &sa.sin_addr, |
| (const void *) hostEntPtr->h_addr, hostEntPtr->h_length); |
| sa.sin_family = hostEntPtr->h_addrtype; |
| sa.sin_port = wrap_htons((unsigned short)url.getPortNum()); |
| |
| janSock.reset(); |
| fSocketHandle = wrap_socket(hostEntPtr->h_addrtype, SOCK_STREAM, 0); |
| if (fSocketHandle == INVALID_SOCKET) |
| { |
| // Call WSAGetLastError() to get the error number. |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_CreateSocket, url.getURLText(), memoryManager); |
| } |
| janSock.reset(&fSocketHandle); |
| |
| if (wrap_connect(fSocketHandle, (struct sockaddr *) &sa, sizeof(sa)) == SOCKET_ERROR) |
| { |
| // Call WSAGetLastError() to get the error number. |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::NetAcc_ConnSocket, url.getURLText(), memoryManager); |
| } |
| |
| #endif |
| |
| int status = sendRequest(url, httpInfo); |
| |
| if(status == 200) { |
| // HTTP 200 OK response means we're done. |
| // We're done |
| break; |
| } |
| // a 3xx response means there was an HTTP redirect |
| else if(status >= 300 && status <= 307) { |
| redirectCount++; |
| |
| XMLCh *newURLString = findHeader("Location"); |
| ArrayJanitor<XMLCh> janNewURLString(newURLString, memoryManager); |
| |
| if(newURLString == 0 || *newURLString == 0) { |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::File_CouldNotOpenFile, url.getURLText(), memoryManager); |
| } |
| |
| XMLURL newURL(memoryManager); |
| newURL.setURL(url, newURLString); |
| if(newURL.getProtocol() != XMLURL::HTTP) { |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::File_CouldNotOpenFile, newURL.getURLText(), memoryManager); |
| } |
| |
| url = newURL; |
| hostName = newURL.getHost(); |
| |
| if (hostName == 0) |
| ThrowXMLwithMemMgr1(NetAccessorException, |
| XMLExcepts::File_CouldNotOpenFile, |
| newURL.getURLText(), |
| memoryManager); |
| |
| janHostNameAsCharStar.release(); |
| hostNameAsCharStar = XMLString::transcode(hostName, memoryManager); |
| janHostNameAsCharStar.reset(hostNameAsCharStar, memoryManager); |
| } |
| else { |
| // Most likely a 404 Not Found error. |
| ThrowXMLwithMemMgr1(NetAccessorException, XMLExcepts::File_CouldNotOpenFile, url.getURLText(), memoryManager); |
| } |
| } while(redirectCount < 6); |
| |
| janSock.release(); |
| } |
| |
| BinHTTPURLInputStream::~BinHTTPURLInputStream() |
| { |
| wrap_shutdown(fSocketHandle, SD_BOTH); |
| wrap_closesocket(fSocketHandle); |
| } |
| |
| bool BinHTTPURLInputStream::send(const char *buf, XMLSize_t len) |
| { |
| XMLSize_t done = 0; |
| int ret; |
| |
| while(done < len) { |
| ret = wrap_send(fSocketHandle, buf + done, (int)(len - done), 0); |
| if(ret == SOCKET_ERROR) return false; |
| done += ret; |
| } |
| |
| return true; |
| } |
| |
| int BinHTTPURLInputStream::receive(char *buf, XMLSize_t len) |
| { |
| int iLen = wrap_recv(fSocketHandle, buf, (int)len, 0); |
| if (iLen == SOCKET_ERROR) return -1; |
| return iLen; |
| } |
| |
| XERCES_CPP_NAMESPACE_END |