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 Boolean connectionAlreadyRefused = false;
61 Uint32 maxConnectAttempts = 100;
62 // Retry the connect() until it succeeds or it fails with an error other
63 // than EINTR, EAGAIN (for Linux), or the first ECONNREFUSED (for HP-UX).
64 while (((connectResult = ::connect(socket, address, addressLength)) == -1)
65 && (maxConnectAttempts-- > 0)
66 && ((errno == EINTR) || (errno == EAGAIN) ||
67 ((errno == ECONNREFUSED) && !connectionAlreadyRefused)))
68 {
69 if (errno == ECONNREFUSED)
70 {
71 connectionAlreadyRefused = true;
72 }
73 Threads::sleep(1);
74 }
75 #endif
|
76 kumpf 1.28
77 if (connectResult == 0)
78 {
79 return true;
80 }
|
81 mike 1.2
|
82 kumpf 1.28 if (getSocketError() == PEGASUS_NETWORK_EINPROGRESS)
83 {
84 PEG_TRACE((TRC_HTTP, Tracer::LEVEL4,
85 "Connection to server in progress. Waiting up to %u milliseconds "
86 "for the socket to become connected.",
87 timeoutMilliseconds));
88
89 fd_set fdwrite;
90 FD_ZERO(&fdwrite);
91 FD_SET(socket, &fdwrite);
92 struct timeval timeoutValue =
93 { timeoutMilliseconds/1000, timeoutMilliseconds%1000*1000 };
94 int selectResult = -1;
|
95 mike 1.2
|
96 venkat.puvvada 1.34 #ifdef PEGASUS_OS_TYPE_WINDOWS
97 PEGASUS_RETRY_SYSTEM_CALL(
98 select(FD_SETSIZE, NULL, &fdwrite, &fdwrite, &timeoutValue),
99 selectResult);
100 #else
|
101 kumpf 1.28 PEGASUS_RETRY_SYSTEM_CALL(
102 select(FD_SETSIZE, NULL, &fdwrite, NULL, &timeoutValue),
103 selectResult);
|
104 venkat.puvvada 1.34 #endif
|
105 kumpf 1.28 if (selectResult == 0)
106 {
|
107 marek 1.32 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1,
|
108 kumpf 1.28 "select() timed out waiting for the socket connection to be "
109 "established.");
110 return false;
111 }
112 else if (selectResult > 0)
113 {
114 int optval;
115 SocketLength optlen = sizeof(int);
116 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen);
117 if (optval == 0)
118 {
119 PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4,
120 "Connection with server established.");
121 return true;
122 }
123 else
124 {
|
125 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
126 kumpf 1.28 "Did not connect, getsockopt() returned optval = %d",
127 optval));
128 return false;
129 }
130 }
131 else
132 {
|
133 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
134 kumpf 1.28 "select() returned error code %d",
135 getSocketError()));
136 return false;
137 }
138 }
|
139 mike 1.2
|
140 marek 1.32 PEG_TRACE((TRC_HTTP, Tracer::LEVEL1,
|
141 kumpf 1.28 "connect() returned error code %d",
142 getSocketError()));
143 return false;
144 }
|
145 mike 1.2
|
146 mike 1.23 Sint32 Socket::read(SocketHandle socket, void* ptr, Uint32 size)
|
147 mike 1.2 {
148 #ifdef PEGASUS_OS_TYPE_WINDOWS
149 return ::recv(socket, (char*)ptr, size, 0);
150 #else
|
151 mike 1.23 int status;
152 PEGASUS_RETRY_SYSTEM_CALL(::read(socket, (char*)ptr, size), status);
153 return status;
|
154 mday 1.11 #endif
|
155 mike 1.2 }
156
|
157 mike 1.23 Sint32 Socket::write(SocketHandle socket, const void* ptr, Uint32 size)
|
158 mike 1.2 {
159 #ifdef PEGASUS_OS_TYPE_WINDOWS
160 return ::send(socket, (const char*)ptr, size, 0);
161 #else
|
162 mike 1.23 int status;
163 PEGASUS_RETRY_SYSTEM_CALL(::write(socket, (char*)ptr, size), status);
164 return status;
|
165 mday 1.11 #endif
|
166 mike 1.2 }
167
|
168 kumpf 1.26 Sint32 Socket::timedWrite(
169 SocketHandle socket,
170 const void* ptr,
171 Uint32 size,
172 Uint32 socketWriteTimeout)
|
173 marek 1.24 {
174 Sint32 bytesWritten = 0;
175 Sint32 totalBytesWritten = 0;
176 Boolean socketTimedOut = false;
177 int selreturn = 0;
178 while (1)
179 {
180 #ifdef PEGASUS_OS_TYPE_WINDOWS
181 PEGASUS_RETRY_SYSTEM_CALL(
182 ::send(socket, (const char*)ptr, size, 0), bytesWritten);
183 #else
184 PEGASUS_RETRY_SYSTEM_CALL(
185 ::write(socket, (char*)ptr, size), bytesWritten);
186 #endif
|
187 kumpf 1.26 // Some data written this cycle ?
|
188 marek 1.24 // Add it to the total amount of written data.
189 if (bytesWritten > 0)
190 {
191 totalBytesWritten += bytesWritten;
192 socketTimedOut = false;
193 }
194
195 // All data written ? return amount of data written
196 if ((Uint32)bytesWritten == size)
197 {
198 return totalBytesWritten;
199 }
200 // If data has been written partially, we resume writing data
201 // this also accounts for the case of a signal interrupt
202 // (i.e. errno = EINTR)
203 if (bytesWritten > 0)
204 {
205 size -= bytesWritten;
206 ptr = (void *)((char *)ptr + bytesWritten);
207 continue;
208 }
209 marek 1.24 // Something went wrong
210 if (bytesWritten == PEGASUS_SOCKET_ERROR)
211 {
212 // if we already waited for the socket to get ready, bail out
|
213 kumpf 1.26 if (socketTimedOut) return bytesWritten;
|
214 marek 1.24 #ifdef PEGASUS_OS_TYPE_WINDOWS
215 if (WSAGetLastError() == WSAEWOULDBLOCK)
216 #else
217 if (errno == EAGAIN || errno == EWOULDBLOCK)
218 #endif
219 {
220 fd_set fdwrite;
221 // max. timeout seconds waiting for the socket to get ready
222 struct timeval tv = { socketWriteTimeout, 0 };
223 FD_ZERO(&fdwrite);
224 FD_SET(socket, &fdwrite);
225 selreturn = select(FD_SETSIZE, NULL, &fdwrite, NULL, &tv);
226 if (selreturn == 0) socketTimedOut = true; // ran out of time
|
227 kumpf 1.26 continue;
|
228 marek 1.24 }
229 return bytesWritten;
230 }
231 }
232 }
233
|
234 kumpf 1.29 void Socket::close(SocketHandle& socket)
|
235 mike 1.2 {
|
236 kumpf 1.29 if (socket != PEGASUS_INVALID_SOCKET)
|
237 mike 1.23 {
238 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
239 kumpf 1.26 if (!closesocket(socket))
|
240 kumpf 1.29 {
241 socket = PEGASUS_INVALID_SOCKET;
242 }
|
243 mike 1.23 #else
|
244 kumpf 1.26 int status;
245 PEGASUS_RETRY_SYSTEM_CALL(::close(socket), status);
|
246 mike 1.23
|
247 kumpf 1.26 if (status == 0)
|
248 kumpf 1.29 {
249 socket = PEGASUS_INVALID_SOCKET;
250 }
|
251 mike 1.23 #endif
252 }
|
253 mike 1.2 }
254
|
255 mike 1.23 void Socket::disableBlocking(SocketHandle socket)
|
256 mike 1.2 {
257 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
258 kumpf 1.28 unsigned long flag = 1; // Use "flag = 0" to enable blocking
|
259 mike 1.2 ioctlsocket(socket, FIONBIO, &flag);
|
260 carson.hovey 1.31 #elif PEGASUS_OS_VMS
261 int flag=1; // Use "flag = 0" to enable blocking
262 ioctl(socket, FIONBIO, &flag);
|
263 mike 1.2 #else
264 int flags = fcntl(socket, F_GETFL, 0);
|
265 kumpf 1.28 flags |= O_NONBLOCK; // Use "flags &= ~O_NONBLOCK" to enable blocking
|
266 mike 1.2 fcntl(socket, F_SETFL, flags);
267 #endif
268 }
269
270 void Socket::initializeInterface()
271 {
272 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
273 venkat.puvvada 1.38 AutoMutex mtx(_socketInterfaceRefCountLock);
|
274 mike 1.2 if (_socketInterfaceRefCount == 0)
275 {
|
276 david.dillard 1.20 WSADATA tmp;
|
277 mike 1.2
|
278 kumpf 1.30 int err = WSAStartup(0x202, &tmp);
279 if (err != 0)
280 {
281 throw Exception(MessageLoaderParms(
282 "Common.Socket.WSASTARTUP_FAILED.WINDOWS",
283 "WSAStartup failed with error $0.",
284 err));
285 }
|
286 mike 1.2 }
287
288 _socketInterfaceRefCount++;
289 #endif
290 }
291
292 void Socket::uninitializeInterface()
293 {
294 #ifdef PEGASUS_OS_TYPE_WINDOWS
|
295 venkat.puvvada 1.38 AutoMutex mtx(_socketInterfaceRefCountLock);
|
296 mike 1.2 _socketInterfaceRefCount--;
297
298 if (_socketInterfaceRefCount == 0)
|
299 david.dillard 1.20 WSACleanup();
|
300 mike 1.2 #endif
301 }
302
|
303 thilo.boehm 1.25 //------------------------------------------------------------------------------
304 //
305 // _setTCPNoDelay()
306 //
307 //------------------------------------------------------------------------------
308
309 inline void _setTCPNoDelay(SocketHandle socket)
310 {
311 // This function disables "Nagle's Algorithm" also known as "the TCP delay
312 // algorithm", which causes read operations to obtain whatever data is
|
313 kumpf 1.26 // already in the input queue and then wait a little longer to see if
314 // more data arrives. This algorithm optimizes the case in which data is
315 // sent in only one direction but severely impairs performance of round
316 // trip servers. Disabling TCP delay is a standard technique for round
|
317 thilo.boehm 1.25 // trip servers.
318
319 int opt = 1;
320 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));
321 }
322 //------------------------------------------------------------------------------
323 //
324 // _setInformIfNewTCPIP()
325 //
326 //------------------------------------------------------------------------------
327 inline void _setInformIfNewTCPIP(SocketHandle socket)
328 {
329 #ifdef PEGASUS_OS_ZOS
330 // This function enables the notification of the CIM Server that a new
|
331 kumpf 1.26 // TCPIP transport layer is active. This is needed to be aware of a
|
332 thilo.boehm 1.25 // restart of the transport layer. When this option is in effect,
333 // the accetp(), select(), and read() request will receive an errno=EIO.
334 // Once this happens, the socket should be closed and create a new.
|
335 kumpf 1.26
|
336 thilo.boehm 1.25 int NewTcpipOn = 1;
|
337 kumpf 1.26 setibmsockopt(
338 socket,
339 SOL_SOCKET,
340 SO_EioIfNewTP,
341 (char*)&NewTcpipOn,
342 sizeof(NewTcpipOn));
|
343 thilo.boehm 1.25 #endif
344 }
345
346
347 SocketHandle Socket::createSocket(int domain, int type, int protocol)
348 {
349 SocketHandle newSocket;
350
|
351 kumpf 1.26 if (domain == AF_UNIX)
|
352 thilo.boehm 1.25 {
|
353 kumpf 1.26 return socket(domain,type,protocol);
|
354 thilo.boehm 1.25 }
355
356 bool sendTcpipMsg = true;
357
|
358 kumpf 1.26 while (1)
|
359 thilo.boehm 1.25 {
360 newSocket = socket(domain,type,protocol);
361
|
362 kumpf 1.27 if ((newSocket != PEGASUS_INVALID_SOCKET) ||
363 (getSocketError() != PEGASUS_NETWORK_TRYAGAIN))
364 {
365 break;
366 }
367
368 #ifdef PEGASUS_OS_ZOS
|
369 thilo.boehm 1.25 // The program should wait for transport layer to become ready.
370
|
371 kumpf 1.27 if (sendTcpipMsg)
|
372 thilo.boehm 1.25 {
|
373 kumpf 1.27 Logger::put_l(
374 Logger::STANDARD_LOG, System::CIMSERVER, Logger::INFORMATION,
|
375 kumpf 1.33 MessageLoaderParms(
376 "Common.Socket.WAIT_FOR_TCPIP",
377 "TCP/IP temporary unavailable."));
|
378 kumpf 1.27 sendTcpipMsg = false;
379 }
|
380 thilo.boehm 1.25
|
381 kumpf 1.27 System::sleep(30);
382 #endif
|
383 thilo.boehm 1.25 } // wait for the transport layer become ready.
384
385 // Is the socket in an unrecoverable error ?
386 if (newSocket == PEGASUS_INVALID_SOCKET)
387 {
388 // return immediate
|
389 kumpf 1.26 return PEGASUS_INVALID_SOCKET;
390 }
391 else
392 {
|
393 thilo.boehm 1.25 // set aditional socket options
394 _setTCPNoDelay(newSocket);
395 _setInformIfNewTCPIP(newSocket);
396
|
397 kumpf 1.26 return newSocket;
|
398 thilo.boehm 1.25 }
399 }
400
|
401 mike 1.2 PEGASUS_NAMESPACE_END
|