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