1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 /*
21  * XSEC
22  *
23  * XSECSOAPRequestorSimple := (Very) Basic implementation of a SOAP
24  *                         HTTP wrapper for testing the client code.
25  *
26  *
27  * $Id: XSECSOAPRequestorSimpleUnix.cpp 1820685 2018-01-09 17:48:51Z scantor $
28  *
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <netdb.h>
41 #include <errno.h>
42 
43 #include <string>
44 #include <sstream>
45 
46 #include <xsec/utils/XSECSOAPRequestorSimple.hpp>
47 #include <xsec/utils/XSECSafeBuffer.hpp>
48 #include <xsec/framework/XSECError.hpp>
49 
50 #ifdef XSEC_XKMS_ENABLED
51 
52 #include "../../utils/XSECAutoPtr.hpp"
53 
54 #include <xercesc/dom/DOM.hpp>
55 #include <xercesc/util/XMLNetAccessor.hpp>
56 #include <xercesc/util/XMLString.hpp>
57 #include <xercesc/util/XMLExceptMsgs.hpp>
58 #include <xercesc/util/Janitor.hpp>
59 #include <xercesc/util/XMLUniDefs.hpp>
60 
61 XERCES_CPP_NAMESPACE_USE
62 using std::string;
63 using std::ostringstream;
64 
65 // --------------------------------------------------------------------------------
66 //           Platform specific constructor
67 // --------------------------------------------------------------------------------
68 
69 
XSECSOAPRequestorSimple(const XMLCh * uri)70 XSECSOAPRequestorSimple::XSECSOAPRequestorSimple(const XMLCh * uri) : m_uri(uri), m_envelopeType(ENVELOPE_NONE) {
71 
72 
73 }
74 
75 // --------------------------------------------------------------------------------
76 //           Interface
77 // --------------------------------------------------------------------------------
78 
79 
doRequest(DOMDocument * request)80 DOMDocument * XSECSOAPRequestorSimple::doRequest(DOMDocument * request) {
81 
82 
83     char* content = wrapAndSerialise(request);
84 
85     // First we need to serialise
86 
87     //
88     // Pull all of the parts of the URL out of th m_uri object, and transcode them
89     //   and transcode them back to ASCII.
90     //
91     const XMLCh*        hostName = m_uri.getHost();
92     XSECAutoPtrChar     hostNameAsCharStar(hostName);
93 
94     const XMLCh*        path = m_uri.getPath();
95     XSECAutoPtrChar     pathAsCharStar(path);
96 
97     const XMLCh*        fragment = m_uri.getFragment();
98     XSECAutoPtrChar     fragmentAsCharStar(fragment);
99 
100     const XMLCh*        query = m_uri.getQueryString();
101     XSECAutoPtrChar     queryAsCharStar(query);
102 
103     unsigned short      portNumber = (unsigned short) m_uri.getPort();
104 
105     // If no number is set, go with port 80
106     if (portNumber == USHRT_MAX)
107         portNumber = 80;
108 
109     //
110     // Set up a socket.
111     //
112     struct hostent*     hostEntPtr = 0;
113     struct sockaddr_in  sa;
114 
115 
116     if ((hostEntPtr = gethostbyname(hostNameAsCharStar.get())) == NULL)
117     {
118         unsigned long  numAddress = inet_addr(hostNameAsCharStar.get());
119         if (numAddress == 0)
120         {
121             ThrowXML(NetAccessorException,
122                      XMLExcepts::NetAcc_TargetResolution);
123         }
124         if ((hostEntPtr =
125                 gethostbyaddr((char *) &numAddress,
126                               sizeof(unsigned long), AF_INET)) == NULL)
127         {
128             ThrowXML(NetAccessorException,
129                      XMLExcepts::NetAcc_TargetResolution);
130         }
131     }
132 
133     memcpy((void *) &sa.sin_addr,
134            (const void *) hostEntPtr->h_addr, hostEntPtr->h_length);
135     sa.sin_family = hostEntPtr->h_addrtype;
136     sa.sin_port = htons(portNumber);
137 
138     int s = socket(hostEntPtr->h_addrtype, SOCK_STREAM, 0);
139     if (s < 0)
140     {
141         throw XSECException(XSECException::HTTPURIInputStreamError,
142                             "Error creating socket");
143     }
144 
145     if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
146     {
147         throw XSECException(XSECException::HTTPURIInputStreamError,
148                             "Error connecting to end server");
149     }
150 
151     // The port is open and ready to go.
152     // Build up the http GET command to send to the server.
153     // To do:  We should really support http 1.1.  This implementation
154     //         is weak.
155 
156     ostringstream outBuffer;
157 
158     outBuffer << "POST " << pathAsCharStar.get();
159 
160     if (queryAsCharStar.get() != 0)
161     {
162         // Tack on a ? before the fragment
163         outBuffer << '?' << queryAsCharStar.get();
164     }
165 
166     if (fragmentAsCharStar.get() != 0)
167     {
168         outBuffer << fragmentAsCharStar.get();
169     }
170 
171     outBuffer << "HTTP/1.0\r\n"
172         << "Content-Type: text/xml; charset=utf-8\r\n";
173 
174     outBuffer << "Host: " << hostNameAsCharStar.get();
175     if (portNumber != 80)
176     {
177         outBuffer << ':' << portNumber;
178     }
179     outBuffer << "\r\n";
180 
181     outBuffer << "Content-Length: " << strlen(content) << "\r\n"
182         << "SOAPAction: \"\"\r\n"
183         << "\r\n";
184 
185     // Now the content
186     outBuffer << content;
187 
188     // Send the http request
189     string ostr = outBuffer.str();
190     size_t lent = ostr.length();
191     int  aLent = 0;
192     if ((aLent = write(s, (void *) ostr.c_str(), lent)) != lent)
193     {
194         throw XSECException(XSECException::HTTPURIInputStreamError,
195                             "Error writing to socket");
196     }
197 
198     char inBuffer[4000];
199     char* inBufferEnd;
200     char* inBufferPos;
201 
202     //
203     // get the response, check the http header for errors from the server.
204     //
205     aLent = read(s, (void *)inBuffer, sizeof(inBuffer)-1);
206     if (aLent <= 0)
207     {
208         throw XSECException(XSECException::HTTPURIInputStreamError,
209                             "Error reported reading socket");
210     }
211 
212     inBufferEnd = inBuffer+aLent;
213     *inBufferEnd = 0;
214 
215     // Find the break between the returned http header and any data.
216     //  (Delimited by a blank line)
217     // Hang on to any data for use by the first read from this BinHTTPURLInputStream.
218     //
219     bool headerRead = false;
220     do {
221         inBufferPos = strstr(inBuffer, "\r\n\r\n");
222         if (inBufferPos != 0) {
223             inBufferPos += 4;
224             *(inBufferPos-2) = 0;
225             headerRead = true;
226         }
227         else {
228             inBufferPos = strstr(inBuffer, "\n\n");
229             if (inBufferPos != 0) {
230                 inBufferPos += 2;
231                 *(inBufferPos-1) = 0;
232                 headerRead = true;
233             }
234             else {
235                 //
236                 // Header is not yet read, do another recv() to get more data...
237                 aLent = read(s,
238                              inBufferEnd,
239                              (sizeof(inBuffer) - 1) - (inBufferEnd - inBuffer));
240                 if (aLent <= 0) {
241                     throw XSECException(XSECException::HTTPURIInputStreamError,
242                                         "Error reported reading socket");
243                 }
244                 inBufferEnd = inBufferEnd + aLent;
245                 *inBufferEnd = 0;
246             }
247         }
248     } while(headerRead == false);
249 
250     // Make sure the header includes an HTTP 200 OK response.
251     //
252     char *p = strstr(inBuffer, "HTTP");
253     if (p == 0)
254     {
255         throw XSECException(XSECException::HTTPURIInputStreamError,
256                             "Error reported reading socket");
257     }
258 
259     p = strchr(p, ' ');
260     if (p == 0)
261     {
262         throw XSECException(XSECException::HTTPURIInputStreamError,
263                             "Error reported reading socket");
264     }
265 
266     int httpResponse = atoi(p);
267 
268     if (httpResponse == 302 || httpResponse == 301) {
269         //Once grows, should use a switch
270         char redirectBuf[256];
271         int q;
272 
273         // Find the "Location:" string
274         p = strstr(p, "Location:");
275         if (p == 0)
276         {
277             throw XSECException(XSECException::HTTPURIInputStreamError,
278                             "Error reported reading socket");
279         }
280         p = strchr(p, ' ');
281         if (p == 0)
282         {
283             throw XSECException(XSECException::HTTPURIInputStreamError,
284                             "Error reported reading socket");
285         }
286 
287         // Now read
288         p++;
289         for (q=0; q < 255 && p[q] != '\r' && p[q] !='\n'; ++q)
290             redirectBuf[q] = p[q];
291 
292         redirectBuf[q] = '\0';
293 
294         // Try to find this location
295         XMLCh * recString = XMLString::transcode(redirectBuf);
296 
297         XSECSOAPRequestorSimple recurse(recString);
298         XSEC_RELEASE_XMLCH(recString);
299         return recurse.doRequest(request);
300 
301     }
302 
303     else if (httpResponse != 200)
304     {
305         // Most likely a 404 Not Found error.
306         //   Should recognize and handle the forwarding responses.
307         //
308         char * q = strstr(p, "\n");
309         if (q == NULL)
310             q = strstr(p, "\r");
311         if (q != NULL)
312             *q = '\0';
313         safeBuffer sb;
314         sb.sbStrcpyIn("SOAPRequestorSimple HTTP Error : ");
315         if (strlen(p) < 256)
316             sb.sbStrcatIn(p);
317         throw XSECException(XSECException::HTTPURIInputStreamError, sb.rawCharBuffer());
318 
319     }
320 
321     /* Now find out how long the return is */
322 
323     p = strstr(inBuffer, "Content-Length:");
324     int responseLength;
325 
326     if (p == NULL) {
327         // Need to work it out from the amount of data returned
328         responseLength = -1;
329     }
330     else {
331 
332         p = strchr(p, ' ');
333         p++;
334 
335         responseLength = atoi(p);
336     }
337 
338     safeBuffer responseBuffer;
339     lent = inBufferEnd - inBufferPos;
340     responseBuffer.sbMemcpyIn(inBufferPos, lent);
341 
342     while (responseLength == -1 || lent < responseLength) {
343         aLent = read(s, (void *)inBuffer, sizeof(inBuffer)-1);
344         if (aLent > 0) {
345             responseBuffer.sbMemcpyIn(lent, inBuffer, aLent);
346             lent += aLent;
347         }
348         else {
349             responseLength = 0;
350         }
351     }
352 
353     return parseAndUnwrap(responseBuffer.rawCharBuffer(), lent);
354 }
355 
356 #endif /* XSEC_XKMS_ENABLED */
357