/* * 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 #include #ifdef WITH_IPV6 #include #endif #include #include #include #include #include #include #include #include #include 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 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 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