blob: 9a670a665dca2a4084a5b8c888b9c49983cf45e1 [file] [log] [blame]
/**
* 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.
*/
/*
* XSEC
*
* XSECBinHTTPURIInputStream := Re-implementation of Xerces BinHTTPInputStream
* Allows us to modify and create an input
* stream that follows re-directs which is
* necessary to fully support XML-DSIG interop
* tests
*
* Author(s): Berin Lautenbach
*
* $Id$
*
* $Log$
* Revision 1.8 2005/02/03 13:55:08 milan
* Apache licence fix.
*
* Revision 1.7 2004/02/08 10:50:22 blautenb
* Update to Apache 2.0 license
*
* Revision 1.6 2003/09/11 11:29:12 blautenb
* Fix Xerces namespace usage in *NIX build
*
* Revision 1.5 2003/07/05 10:30:37 blautenb
* Copyright update
*
* Revision 1.4 2003/05/19 12:31:00 blautenb
* Cleaned up constants so can compile under INTEL compiler
*
* Revision 1.3 2003/03/23 09:49:49 blautenb
* Silly mistype in ==
*
* Revision 1.2 2003/03/15 22:41:46 blautenb
* Add 301 (permanently moved) support
*
* Revision 1.1 2003/02/12 11:21:03 blautenb
* UNIX generic URI resolver
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <limits.h>
#include <xsec/framework/XSECError.hpp>
#include <xsec/utils/unixutils/XSECBinHTTPURIInputStream.hpp>
#include "../../utils/XSECAutoPtr.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>
XERCES_CPP_NAMESPACE_USE
int XSECBinHTTPURIInputStream::getSocketHandle(const XMLUri& urlSource) {
//
// 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();
XSECAutoPtrChar hostNameAsCharStar(hostName);
const XMLCh* path = urlSource.getPath();
XSECAutoPtrChar pathAsCharStar(path);
const XMLCh* fragment = urlSource.getFragment();
XSECAutoPtrChar fragmentAsCharStar(fragment);
const XMLCh* query = urlSource.getQueryString();
XSECAutoPtrChar queryAsCharStar(query);
unsigned short portNumber = (unsigned short) urlSource.getPort();
if (portNumber == USHRT_MAX)
portNumber = 80;
//
// Set up a socket.
//
struct hostent* hostEntPtr = 0;
struct sockaddr_in sa;
if ((hostEntPtr = gethostbyname(hostNameAsCharStar.get())) == NULL)
{
unsigned long numAddress = inet_addr(hostNameAsCharStar.get());
if (numAddress == 0)
{
ThrowXML(NetAccessorException,
XMLExcepts::NetAcc_TargetResolution);
}
if ((hostEntPtr =
gethostbyaddr((char *) &numAddress,
sizeof(unsigned long), AF_INET)) == NULL)
{
ThrowXML(NetAccessorException,
XMLExcepts::NetAcc_TargetResolution);
}
}
memcpy((void *) &sa.sin_addr,
(const void *) hostEntPtr->h_addr, hostEntPtr->h_length);
sa.sin_family = hostEntPtr->h_addrtype;
sa.sin_port = htons(portNumber);
int s = socket(hostEntPtr->h_addrtype, SOCK_STREAM, 0);
if (s < 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error creating socket");
}
if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error connecting to end server");
}
// The port is open and ready to go.
// Build up the http GET command to send to the server.
// To do: We should really support http 1.1. This implementation
// is weak.
strcpy(fBuffer, "GET ");
strcat(fBuffer, pathAsCharStar.get());
if (queryAsCharStar.get() != 0)
{
size_t n = strlen(fBuffer);
fBuffer[n] = XERCES_CPP_NAMESPACE_QUALIFIER chQuestion;
fBuffer[n+1] = XERCES_CPP_NAMESPACE_QUALIFIER chNull;
strcat(fBuffer, queryAsCharStar.get());
}
if (fragmentAsCharStar.get() != 0)
{
strcat(fBuffer, fragmentAsCharStar.get());
}
strcat(fBuffer, " HTTP/1.0\r\n");
strcat(fBuffer, "Host: ");
strcat(fBuffer, hostNameAsCharStar.get());
if (portNumber != 80)
{
int i = strlen(fBuffer);
sprintf(fBuffer+i, ":%d", portNumber);
// _itoa(portNumber, fBuffer+i, 10);
}
strcat(fBuffer, "\r\n\r\n");
// Send the http request
int lent = strlen(fBuffer);
int aLent = 0;
if ((aLent = write(s, (void *) fBuffer, lent)) != lent)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error writing to socket");
}
//
// get the response, check the http header for errors from the server.
//
aLent = read(s, (void *)fBuffer, sizeof(fBuffer)-1);
if (aLent <= 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error reported reading socket");
}
fBufferEnd = fBuffer+aLent;
*fBufferEnd = 0;
// Find the break between the returned http header and any data.
// (Delimited by a blank line)
// Hang on to any data for use by the first read from this BinHTTPURLInputStream.
//
fBufferPos = strstr(fBuffer, "\r\n\r\n");
if (fBufferPos != 0)
{
fBufferPos += 4;
*(fBufferPos-2) = 0;
}
else
{
fBufferPos = strstr(fBuffer, "\n\n");
if (fBufferPos != 0)
{
fBufferPos += 2;
*(fBufferPos-1) = 0;
}
else
fBufferPos = fBufferEnd;
}
// Make sure the header includes an HTTP 200 OK response.
//
char *p = strstr(fBuffer, "HTTP");
if (p == 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error reported reading socket");
}
p = strchr(p, ' ');
if (p == 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error reported reading socket");
}
int httpResponse = atoi(p);
if (httpResponse == 302 || httpResponse == 301) {
//Once grows, should use a switch
char redirectBuf[256];
int q;
// Find the "Location:" string
p = strstr(p, "Location:");
if (p == 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error reported reading socket");
}
p = strchr(p, ' ');
if (p == 0)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error reported reading socket");
}
// Now read
p++;
for (q=0; q < 255 && p[q] != '\r' && p[q] !='\n'; ++q)
redirectBuf[q] = p[q];
redirectBuf[q] = '\0';
// Try to find this location
XSECAutoPtrXMLCh redirectBufTrans(redirectBuf);
return getSocketHandle(XMLUri(redirectBufTrans.get()));
}
else if (httpResponse != 200)
{
// Most likely a 404 Not Found error.
// Should recognize and handle the forwarding responses.
//
throw XSECException(XSECException::HTTPURIInputStreamError,
"Unknown HTTP Response");
}
return s;
}
XSECBinHTTPURIInputStream::XSECBinHTTPURIInputStream(const XMLUri& urlSource)
: fSocket(0)
, fBytesProcessed(0)
{
fSocket = getSocketHandle(urlSource);
}
XSECBinHTTPURIInputStream::~XSECBinHTTPURIInputStream()
{
shutdown(fSocket, 2);
close(fSocket);
}
xsecsize_t XSECBinHTTPURIInputStream::readBytes(XMLByte* const toFill
, const xsecsize_t maxToRead)
{
xsecsize_t len = fBufferEnd - fBufferPos;
if (len > 0)
{
// If there's any data left over in the buffer into which we first
// read from the server (to get the http header), return that.
if (len > maxToRead)
len = maxToRead;
memcpy(toFill, fBufferPos, len);
fBufferPos += len;
}
else
{
// There was no data in the local buffer.
// Read some from the socket, straight into our caller's buffer.
//
len = read(fSocket, (void *) toFill, maxToRead);
if (len == UINT_MAX)
{
throw XSECException(XSECException::HTTPURIInputStreamError,
"Error reading from Socket");
}
}
fBytesProcessed += len;
return len;
}
#ifdef XSEC_XERCES_INPUTSTREAM_HAS_CONTENTTYPE
const XMLCh* XSECBinHTTPURIInputStream::getContentType() const {
return NULL;
}
#endif