1 martin 1.36 //%LICENSE////////////////////////////////////////////////////////////////
|
2 martin 1.37 //
|
3 martin 1.36 // Licensed to The Open Group (TOG) under one or more contributor license
4 // agreements. Refer to the OpenPegasusNOTICE.txt file distributed with
5 // this work for additional information regarding copyright ownership.
6 // Each contributor licenses this file to you under the OpenPegasus Open
7 // Source License; you may not use this file except in compliance with the
8 // License.
|
9 martin 1.37 //
|
10 martin 1.36 // Permission is hereby granted, free of charge, to any person obtaining a
11 // copy of this software and associated documentation files (the "Software"),
12 // to deal in the Software without restriction, including without limitation
13 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 // and/or sell copies of the Software, and to permit persons to whom the
15 // Software is furnished to do so, subject to the following conditions:
|
16 martin 1.37 //
|
17 martin 1.36 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software.
|
19 martin 1.37 //
|
20 martin 1.36 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
21 martin 1.37 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22 martin 1.36 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27 martin 1.37 //
|
28 martin 1.36 //////////////////////////////////////////////////////////////////////////
|
29 mike 1.2 //
30 //%/////////////////////////////////////////////////////////////////////////////
31
32 #include "Socket.h"
|
33 mike 1.23 #include "Network.h"
34 #include <cctype>
35 #include <cstring>
36 #include <Pegasus/Common/Sharable.h>
|
37 thilo.boehm 1.25 #include <Pegasus/Common/Logger.h>
38 #include <Pegasus/Common/System.h>
|
39 kumpf 1.28 #include <Pegasus/Common/Tracer.h>
|
40 kumpf 1.35 #include <Pegasus/Common/Threads.h>
|
41 venkat.puvvada 1.38 #include <Pegasus/Common/Mutex.h>
|
42 mike 1.23
|
43 kumpf 1.28 PEGASUS_NAMESPACE_BEGIN
44
|
45 venkat.puvvada 1.38 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
46 kumpf 1.28 static Uint32 _socketInterfaceRefCount = 0;
|
47 venkat.puvvada 1.38 static Mutex _socketInterfaceRefCountLock;
48 #endif
|
49 kumpf 1.28
50 Boolean Socket::timedConnect(
51 SocketHandle socket,
52 sockaddr* address,
53 int addressLength,
54 Uint32 timeoutMilliseconds)
55 {
56 int connectResult;
|
57 kumpf 1.35 #ifdef PEGASUS_OS_TYPE_WINDOWS
58 connectResult = ::connect(socket, address, addressLength);
59 #else
60 Uint32 maxConnectAttempts = 100;
61 // Retry the connect() until it succeeds or it fails with an error other
|
62 marek 1.39 // than EINTR, EAGAIN (for Linux), or ECONNREFUSED (for HP-UX and z/OS).
|
63 kumpf 1.35 while (((connectResult = ::connect(socket, address, addressLength)) == -1)
64 && (maxConnectAttempts-- > 0)
65 && ((errno == EINTR) || (errno == EAGAIN) ||
|
66 marek 1.39 (errno == ECONNREFUSED)))
|
67 kumpf 1.35 {
68 Threads::sleep(1);
69 }
70 #endif
|
71 kumpf 1.28
72 if (connectResult == 0)
73 {
74 return true;
75 }
|
76 mike 1.2
|
77 kumpf 1.28 if (getSocketError() == PEGASUS_NETWORK_EINPROGRESS)
78 {
79 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
80 "Connection to server in progress. Waiting up to %u milliseconds "
81 "for the socket to become connected.",
82 timeoutMilliseconds));
83
84 fd_set fdwrite;
85 FD_ZERO(&fdwrite);
86 FD_SET(socket, &fdwrite);
87 struct timeval timeoutValue =
88 { timeoutMilliseconds/1000, timeoutMilliseconds%1000*1000 };
89 int selectResult = -1;
|
90 mike 1.2
|
91 venkat.puvvada 1.34 #ifdef PEGASUS_OS_TYPE_WINDOWS
92 PEGASUS_RETRY_SYSTEM_CALL(
93 select(FD_SETSIZE, NULL, &fdwrite, &fdwrite, &timeoutValue),
94 selectResult);
95 #else
|
96 kumpf 1.28 PEGASUS_RETRY_SYSTEM_CALL(
97 select(FD_SETSIZE, NULL, &fdwrite, NULL, &timeoutValue),
98 selectResult);
|
99 venkat.puvvada 1.34 #endif
|
100 kumpf 1.28 if (selectResult == 0)
101 {
|
102 marek 1.32 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1,
|
103 kumpf 1.28 "select() timed out waiting for the socket connection to be "
104 "established.");
105 return false;
106 }
107 else if (selectResult > 0)
108 {
109 int optval;
110 SocketLength optlen = sizeof(int);
111 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen);
112 if (optval == 0)
113 {
114 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
115 "Connection with server established.");
116 return true;
117 }
118 else
119 {
|
120 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
121 kumpf 1.28 "Did not connect, getsockopt() returned optval = %d",
122 optval));
123 return false;
124 }
125 }
126 else
127 {
|
128 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
129 kumpf 1.28 "select() returned error code %d",
130 getSocketError()));
131 return false;
132 }
133 }
|
134 mike 1.2
|
135 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
136 kumpf 1.28 "connect() returned error code %d",
137 getSocketError()));
138 return false;
139 }
|
140 mike 1.2
|
141 mike 1.23 Sint32 Socket::read(SocketHandle socket, void* ptr, Uint32 size)
|
142 mike 1.2 {
143 #ifdef PEGASUS_OS_TYPE_WINDOWS
144 return ::recv(socket, (char*)ptr, size, 0);
145 #else
|
146 mike 1.23 int status;
147 PEGASUS_RETRY_SYSTEM_CALL(::read(socket, (char*)ptr, size), status);
148 return status;
|
149 mday 1.11 #endif
|
150 mike 1.2 }
151
|
152 mike 1.23 Sint32 Socket::write(SocketHandle socket, const void* ptr, Uint32 size)
|
153 mike 1.2 {
154 #ifdef PEGASUS_OS_TYPE_WINDOWS
155 return ::send(socket, (const char*)ptr, size, 0);
156 #else
|
157 mike 1.23 int status;
158 PEGASUS_RETRY_SYSTEM_CALL(::write(socket, (char*)ptr, size), status);
159 return status;
|
160 mday 1.11 #endif
|
161 mike 1.2 }
162
|
163 kumpf 1.26 Sint32 Socket::timedWrite(
164 SocketHandle socket,
165 const void* ptr,
166 Uint32 size,
167 Uint32 socketWriteTimeout)
|
168 marek 1.24 {
169 Sint32 bytesWritten = 0;
170 Sint32 totalBytesWritten = 0;
171 Boolean socketTimedOut = false;
172 int selreturn = 0;
173 while (1)
174 {
175 #ifdef PEGASUS_OS_TYPE_WINDOWS
176 PEGASUS_RETRY_SYSTEM_CALL(
177 ::send(socket, (const char*)ptr, size, 0), bytesWritten);
178 #else
179 PEGASUS_RETRY_SYSTEM_CALL(
180 ::write(socket, (char*)ptr, size), bytesWritten);
181 #endif
|
182 kumpf 1.26 // Some data written this cycle ?
|
183 marek 1.24 // Add it to the total amount of written data.
184 if (bytesWritten > 0)
185 {
186 totalBytesWritten += bytesWritten;
187 socketTimedOut = false;
188 }
189
190 // All data written ? return amount of data written
191 if ((Uint32)bytesWritten == size)
192 {
193 return totalBytesWritten;
194 }
195 // If data has been written partially, we resume writing data
196 // this also accounts for the case of a signal interrupt
197 // (i.e. errno = EINTR)
198 if (bytesWritten > 0)
199 {
200 size -= bytesWritten;
201 ptr = (void *)((char *)ptr + bytesWritten);
202 continue;
203 }
204 marek 1.24 // Something went wrong
205 if (bytesWritten == PEGASUS_SOCKET_ERROR)
206 {
207 // if we already waited for the socket to get ready, bail out
|
208 kumpf 1.26 if (socketTimedOut) return bytesWritten;
|
209 marek 1.24 #ifdef PEGASUS_OS_TYPE_WINDOWS
210 if (WSAGetLastError() == WSAEWOULDBLOCK)
211 #else
212 if (errno == EAGAIN || errno == EWOULDBLOCK)
213 #endif
214 {
215 fd_set fdwrite;
216 // max. timeout seconds waiting for the socket to get ready
217 struct timeval tv = { socketWriteTimeout, 0 };
218 FD_ZERO(&fdwrite);
219 FD_SET(socket, &fdwrite);
220 selreturn = select(FD_SETSIZE, NULL, &fdwrite, NULL, &tv);
221 if (selreturn == 0) socketTimedOut = true; // ran out of time
|
222 kumpf 1.26 continue;
|
223 marek 1.24 }
224 return bytesWritten;
225 }
226 }
227 }
228
|
229 kumpf 1.29 void Socket::close(SocketHandle& socket)
|
230 mike 1.2 {
|
231 kumpf 1.29 if (socket != PEGASUS_INVALID_SOCKET)
|
232 mike 1.23 {
233 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
234 kumpf 1.26 if (!closesocket(socket))
|
235 kumpf 1.29 {
236 socket = PEGASUS_INVALID_SOCKET;
237 }
|
238 mike 1.23 #else
|
239 kumpf 1.26 int status;
240 PEGASUS_RETRY_SYSTEM_CALL(::close(socket), status);
|
241 mike 1.23
|
242 kumpf 1.26 if (status == 0)
|
243 kumpf 1.29 {
244 socket = PEGASUS_INVALID_SOCKET;
245 }
|
246 mike 1.23 #endif
247 }
|
248 mike 1.2 }
249
|
250 mike 1.23 void Socket::disableBlocking(SocketHandle socket)
|
251 mike 1.2 {
252 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
253 kumpf 1.28 unsigned long flag = 1; // Use "flag = 0" to enable blocking
|
254 mike 1.2 ioctlsocket(socket, FIONBIO, &flag);
|
255 carson.hovey 1.31 #elif PEGASUS_OS_VMS
256 int flag=1; // Use "flag = 0" to enable blocking
257 ioctl(socket, FIONBIO, &flag);
|
258 mike 1.2 #else
259 int flags = fcntl(socket, F_GETFL, 0);
|
260 kumpf 1.28 flags |= O_NONBLOCK; // Use "flags &= ~O_NONBLOCK" to enable blocking
|
261 mike 1.2 fcntl(socket, F_SETFL, flags);
262 #endif
263 }
264
265 void Socket::initializeInterface()
266 {
267 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
268 venkat.puvvada 1.38 AutoMutex mtx(_socketInterfaceRefCountLock);
|
269 mike 1.2 if (_socketInterfaceRefCount == 0)
270 {
|
271 david.dillard 1.20 WSADATA tmp;
|
272 mike 1.2
|
273 kumpf 1.30 int err = WSAStartup(0x202, &tmp);
274 if (err != 0)
275 {
276 throw Exception(MessageLoaderParms(
277 "Common.Socket.WSASTARTUP_FAILED.WINDOWS",
278 "WSAStartup failed with error $0.",
279 err));
280 }
|
281 mike 1.2 }
282
283 _socketInterfaceRefCount++;
284 #endif
285 }
286
287 void Socket::uninitializeInterface()
288 {
289 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
290 venkat.puvvada 1.38 AutoMutex mtx(_socketInterfaceRefCountLock);
|
291 mike 1.2 _socketInterfaceRefCount--;
292
293 if (_socketInterfaceRefCount == 0)
|
294 david.dillard 1.20 WSACleanup();
|
295 mike 1.2 #endif
296 }
297
|
298 thilo.boehm 1.25 //------------------------------------------------------------------------------
299 //
300 // _setTCPNoDelay()
301 //
302 //------------------------------------------------------------------------------
303
304 inline void _setTCPNoDelay(SocketHandle socket)
305 {
306 // This function disables "Nagle's Algorithm" also known as "the TCP delay
307 // algorithm", which causes read operations to obtain whatever data is
|
308 kumpf 1.26 // already in the input queue and then wait a little longer to see if
309 // more data arrives. This algorithm optimizes the case in which data is
310 // sent in only one direction but severely impairs performance of round
311 // trip servers. Disabling TCP delay is a standard technique for round
|
312 thilo.boehm 1.25 // trip servers.
313
314 int opt = 1;
315 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));
316 }
|
317 marek 1.41
318 #ifdef PEGASUS_OS_ZOS
|
319 thilo.boehm 1.25 inline void _setInformIfNewTCPIP(SocketHandle socket)
320 {
321 // This function enables the notification of the CIM Server that a new
|
322 kumpf 1.26 // TCPIP transport layer is active. This is needed to be aware of a
|
323 thilo.boehm 1.25 // restart of the transport layer. When this option is in effect,
324 // the accetp(), select(), and read() request will receive an errno=EIO.
325 // Once this happens, the socket should be closed and create a new.
|
326 kumpf 1.26
|
327 thilo.boehm 1.25 int NewTcpipOn = 1;
|
328 kumpf 1.26 setibmsockopt(
329 socket,
330 SOL_SOCKET,
331 SO_EioIfNewTP,
332 (char*)&NewTcpipOn,
333 sizeof(NewTcpipOn));
|
334 marek 1.41 }
335 #else
336 inline void _setInformIfNewTCPIP(SocketHandle)
337 {
338 }
|
339 thilo.boehm 1.25 #endif
340
341
342 SocketHandle Socket::createSocket(int domain, int type, int protocol)
343 {
344 SocketHandle newSocket;
345
|
346 kumpf 1.26 if (domain == AF_UNIX)
|
347 thilo.boehm 1.25 {
|
348 kumpf 1.26 return socket(domain,type,protocol);
|
349 thilo.boehm 1.25 }
350
|
351 marek 1.40 #ifdef PEGASUS_OS_ZOS
|
352 thilo.boehm 1.25 bool sendTcpipMsg = true;
|
353 marek 1.40 #endif
|
354 thilo.boehm 1.25
|
355 kumpf 1.26 while (1)
|
356 thilo.boehm 1.25 {
357 newSocket = socket(domain,type,protocol);
358
|
359 kumpf 1.27 if ((newSocket != PEGASUS_INVALID_SOCKET) ||
360 (getSocketError() != PEGASUS_NETWORK_TRYAGAIN))
361 {
362 break;
363 }
364
365 #ifdef PEGASUS_OS_ZOS
|
366 thilo.boehm 1.25 // The program should wait for transport layer to become ready.
367
|
368 kumpf 1.27 if (sendTcpipMsg)
|
369 thilo.boehm 1.25 {
|
370 kumpf 1.27 Logger::put_l(
371 Logger::STANDARD_LOG, System::CIMSERVER, Logger::INFORMATION,
|
372 kumpf 1.33 MessageLoaderParms(
373 "Common.Socket.WAIT_FOR_TCPIP",
374 "TCP/IP temporary unavailable."));
|
375 kumpf 1.27 sendTcpipMsg = false;
376 }
|
377 thilo.boehm 1.25
|
378 kumpf 1.27 System::sleep(30);
379 #endif
|
380 thilo.boehm 1.25 } // wait for the transport layer become ready.
381
382 // Is the socket in an unrecoverable error ?
383 if (newSocket == PEGASUS_INVALID_SOCKET)
384 {
385 // return immediate
|
386 kumpf 1.26 return PEGASUS_INVALID_SOCKET;
387 }
388 else
389 {
|
390 thilo.boehm 1.25 // set aditional socket options
391 _setTCPNoDelay(newSocket);
392 _setInformIfNewTCPIP(newSocket);
393
|
394 kumpf 1.26 return newSocket;
|
395 thilo.boehm 1.25 }
396 }
397
|
398 mike 1.2 PEGASUS_NAMESPACE_END
|