1 // Module: Log4CPLUS
2 // File: socket-win32.cxx
3 // Created: 4/2003
4 // Author: Tad E. Smith
5 //
6 //
7 // Copyright 2003-2010 Tad E. Smith
8 //
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 //
13 // http://www.apache.org/licenses/LICENSE-2.0
14 //
15 // Unless required by applicable law or agreed to in writing, software
16 // distributed under the License is distributed on an "AS IS" BASIS,
17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 // See the License for the specific language governing permissions and
19 // limitations under the License.
20
21 #include "dcmtk/oflog/config.h"
22 #if defined (DCMTK_LOG4CPLUS_USE_WINSOCK)
23
24 #include <cassert>
25 #include <cerrno>
26 #include "dcmtk/ofstd/ofvector.h"
27 #include <cstring>
28 #include "dcmtk/oflog/internal/socket.h"
29 #include "dcmtk/oflog/helpers/loglog.h"
30 #include "dcmtk/oflog/thread/threads.h"
31
32
33 /////////////////////////////////////////////////////////////////////////////
34 // file LOCAL Classes
35 /////////////////////////////////////////////////////////////////////////////
36
37 namespace
38 {
39
40 enum WSInitStates
41 {
42 WS_UNINITIALIZED,
43 WS_INITIALIZING,
44 WS_INITIALIZED
45 };
46
47
48 static WSADATA wsa;
49 static LONG volatile winsock_state = WS_UNINITIALIZED;
50
51
DoInterlockedCompareExchange(LPLONG p,LONG comp,LONG set)52 static LONG DoInterlockedCompareExchange(LPLONG p, LONG comp, LONG set)
53 {
54 LONG ret;
55 #ifdef HAVE_OLD_INTERLOCKEDCOMPAREEXCHANGE
56 void *tmp = ::InterlockedCompareExchange(OFreinterpret_cast(void **, p),
57 OFreinterpret_cast(void *, comp), OFreinterpret_cast(void *, set));
58 ret = OFreinterpret_cast(LONG, tmp);
59 #else
60 ret = ::InterlockedCompareExchange(p, comp, set);
61 #endif
62 return ret;
63 }
64
65
66 static
67 void
init_winsock_worker()68 init_winsock_worker ()
69 {
70 dcmtk::log4cplus::helpers::LogLog * loglog
71 = dcmtk::log4cplus::helpers::LogLog::getLogLog ();
72
73 // Try to change the state to WS_INITIALIZING.
74 LONG val = DoInterlockedCompareExchange (
75 OFconst_cast(LPLONG, &winsock_state), WS_INITIALIZING, WS_UNINITIALIZED);
76 switch (val)
77 {
78 case WS_UNINITIALIZED:
79 {
80 int ret = WSAStartup (MAKEWORD (2, 2), &wsa);
81 if (ret != 0)
82 {
83 // Revert the state back to WS_UNINITIALIZED to unblock other
84 // threads and let them throw exception.
85 val = DoInterlockedCompareExchange (
86 OFconst_cast(LPLONG, &winsock_state), WS_UNINITIALIZED,
87 WS_INITIALIZING);
88 assert (val == WS_INITIALIZING);
89 loglog->error (DCMTK_LOG4CPLUS_TEXT ("Could not initialize WinSock."),
90 true);
91 }
92
93 // WinSock is initialized, change the state to WS_INITIALIZED.
94 val = DoInterlockedCompareExchange (
95 OFconst_cast(LPLONG, &winsock_state), WS_INITIALIZED,
96 WS_INITIALIZING);
97 assert (val == WS_INITIALIZING);
98 return;
99 }
100
101 case WS_INITIALIZING:
102 // Wait for state change.
103 while (true)
104 {
105 switch (winsock_state)
106 {
107 case WS_INITIALIZED:
108 return;
109
110 case WS_INITIALIZING:
111 dcmtk::log4cplus::thread::yield ();
112 continue;
113
114 default:
115 assert (0);
116 loglog->error (DCMTK_LOG4CPLUS_TEXT ("Unknown WinSock state."), true);
117 }
118 }
119
120 case WS_INITIALIZED:
121 // WinSock is already initialized.
122 return;
123
124 default:
125 assert (0);
126 loglog->error (DCMTK_LOG4CPLUS_TEXT ("Unknown WinSock state."), true);
127 }
128 }
129
130
131 static
132 void
init_winsock()133 init_winsock ()
134 {
135 // Quick check first to avoid the expensive interlocked compare
136 // and exchange.
137 if (winsock_state == WS_INITIALIZED)
138 return;
139 else
140 init_winsock_worker ();
141 }
142
143
144 struct WinSockInitializer
145 {
~WinSockInitializer__anon7380fa3c0111::WinSockInitializer146 ~WinSockInitializer ()
147 {
148 if (winsock_state == WS_INITIALIZED)
149 WSACleanup ();
150 }
151
152 static WinSockInitializer winSockInitializer;
153 };
154
155
156 WinSockInitializer WinSockInitializer::winSockInitializer;
157
158
159 } // namespace
160
161
162 namespace dcmtk {
163 namespace log4cplus { namespace helpers {
164
165
166 /////////////////////////////////////////////////////////////////////////////
167 // Global Methods
168 /////////////////////////////////////////////////////////////////////////////
169
170 SOCKET_TYPE
openSocket(unsigned short port,SocketState & state)171 openSocket(unsigned short port, SocketState& state)
172 {
173 struct sockaddr_in server;
174
175 init_winsock ();
176
177 SOCKET sock = WSASocket (AF_INET, SOCK_STREAM, AF_UNSPEC, 0, 0
178 #if defined (WSA_FLAG_NO_HANDLE_INHERIT)
179 , WSA_FLAG_NO_HANDLE_INHERIT
180 #else
181 , 0
182 #endif
183 );
184
185 if (sock == INVALID_OS_SOCKET_VALUE)
186 goto error;
187
188 server.sin_family = AF_INET;
189 server.sin_addr.s_addr = htonl(INADDR_ANY);
190 server.sin_port = htons(port);
191
192 if (bind(sock, OFreinterpret_cast(struct sockaddr*, &server), OFstatic_cast(int, sizeof(server)))
193 != 0)
194 goto error;
195
196 if (::listen(sock, 10) != 0)
197 goto error;
198
199 state = ok;
200 return to_log4cplus_socket (sock);
201
202 error:
203 int eno = WSAGetLastError ();
204
205 if (sock != INVALID_OS_SOCKET_VALUE)
206 ::closesocket (sock);
207
208 set_last_socket_error (eno);
209 return INVALID_SOCKET_VALUE;
210 }
211
212
213 SOCKET_TYPE
connectSocket(const tstring & hostn,unsigned short port,bool udp,SocketState & state)214 connectSocket(const tstring& hostn, unsigned short port, bool udp, SocketState& state)
215 {
216 struct hostent * hp;
217 struct sockaddr_in insock;
218 int retval;
219
220 init_winsock ();
221
222 SOCKET sock = WSASocket (AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM),
223 AF_UNSPEC, 0, 0
224 #if defined (WSA_FLAG_NO_HANDLE_INHERIT)
225 , WSA_FLAG_NO_HANDLE_INHERIT
226 #else
227 , 0
228 #endif
229 );
230 if (sock == INVALID_OS_SOCKET_VALUE)
231 goto error;
232
233 hp = ::gethostbyname( DCMTK_LOG4CPLUS_TSTRING_TO_STRING(hostn).c_str() );
234 if (hp == 0 || hp->h_addrtype != AF_INET)
235 {
236 insock.sin_family = AF_INET;
237 INT insock_size = OFstatic_cast(int, sizeof (insock));
238 #if defined (DCMTK_OFLOG_UNICODE)
239 INT ret = WSAStringToAddress (OFconst_cast(LPTSTR, hostn.c_str ()),
240 AF_INET, 0, OFreinterpret_cast(struct sockaddr *, &insock),
241 &insock_size);
242 #else
243 INT ret = WSAStringToAddressA (OFconst_cast(LPSTR, hostn.c_str ()),
244 AF_INET, 0, OFreinterpret_cast(struct sockaddr *, &insock),
245 &insock_size);
246 #endif
247 if (ret == SOCKET_ERROR || insock_size != sizeof (insock))
248 {
249 state = bad_address;
250 goto error;
251 }
252 }
253 else
254 memcpy (&insock.sin_addr, hp->h_addr_list[0],
255 sizeof (insock.sin_addr));
256
257 insock.sin_port = htons(port);
258 insock.sin_family = AF_INET;
259
260 while( (retval = ::connect(sock, OFreinterpret_cast(struct sockaddr*, &insock), OFstatic_cast(int, sizeof(insock)))) == -1
261 && (WSAGetLastError() == WSAEINTR))
262 ;
263 if (retval == SOCKET_ERROR)
264 goto error;
265
266 state = ok;
267 return to_log4cplus_socket (sock);
268
269 error:
270 int eno = WSAGetLastError ();
271
272 if (sock != INVALID_OS_SOCKET_VALUE)
273 ::closesocket (sock);
274
275 set_last_socket_error (eno);
276 return INVALID_SOCKET_VALUE;
277 }
278
279
280 SOCKET_TYPE
acceptSocket(SOCKET_TYPE sock,SocketState & state)281 acceptSocket(SOCKET_TYPE sock, SocketState & state)
282 {
283 init_winsock ();
284
285 SOCKET connected_socket = ::accept (to_os_socket (sock), NULL, NULL);
286
287 if (connected_socket != INVALID_OS_SOCKET_VALUE)
288 state = ok;
289
290 return to_log4cplus_socket (connected_socket);
291 }
292
293
294
295 int
closeSocket(SOCKET_TYPE sock)296 closeSocket(SOCKET_TYPE sock)
297 {
298 return ::closesocket (to_os_socket (sock));
299 }
300
301
302
303 long
read(SOCKET_TYPE sock,SocketBuffer & buffer)304 read(SOCKET_TYPE sock, SocketBuffer& buffer)
305 {
306 long res, read = 0;
307
308 do
309 {
310 res = ::recv(to_os_socket (sock),
311 buffer.getBuffer() + read,
312 OFstatic_cast(int, buffer.getMaxSize() - read),
313 0);
314 if (res == SOCKET_ERROR)
315 {
316 set_last_socket_error (WSAGetLastError ());
317 return res;
318 }
319 read += res;
320 }
321 while (read < OFstatic_cast(long, buffer.getMaxSize()));
322
323 return read;
324 }
325
326
327
328 long
write(SOCKET_TYPE sock,const SocketBuffer & buffer)329 write(SOCKET_TYPE sock, const SocketBuffer& buffer)
330 {
331 long ret = ::send (to_os_socket (sock), buffer.getBuffer(),
332 OFstatic_cast(int, buffer.getSize()), 0);
333 if (ret == SOCKET_ERROR)
334 set_last_socket_error (WSAGetLastError ());
335 return ret;
336 }
337
338
339 long
write(SOCKET_TYPE sock,const STD_NAMESPACE string & buffer)340 write(SOCKET_TYPE sock, const STD_NAMESPACE string & buffer)
341 {
342 long ret = ::send (to_os_socket (sock), buffer.c_str (),
343 OFstatic_cast(int, buffer.size ()), 0);
344 if (ret == SOCKET_ERROR)
345 set_last_socket_error (WSAGetLastError ());
346 return ret;
347 }
348
349
350 tstring
getHostname(bool fqdn)351 getHostname (bool fqdn)
352 {
353 char const * hostname = "unknown";
354 int ret;
355 OFVector<char> hn (1024, 0);
356
357 while (true)
358 {
359 ret = ::gethostname (&hn[0], OFstatic_cast(int, hn.size ()) - 1);
360 if (ret == 0)
361 {
362 hostname = &hn[0];
363 break;
364 }
365 else if (ret != 0 && WSAGetLastError () == WSAEFAULT)
366 // Out buffer was too short. Retry with buffer twice the size.
367 hn.resize (hn.size () * 2, 0);
368 else
369 break;
370 }
371
372 if (ret != 0 || (ret == 0 && ! fqdn))
373 return DCMTK_LOG4CPLUS_STRING_TO_TSTRING (hostname);
374
375 struct ::hostent * hp = ::gethostbyname (hostname);
376 if (hp)
377 hostname = hp->h_name;
378
379 return DCMTK_LOG4CPLUS_STRING_TO_TSTRING (hostname);
380 }
381
382
383 int
setTCPNoDelay(SOCKET_TYPE sock,bool val)384 setTCPNoDelay (SOCKET_TYPE sock, bool val)
385 {
386 int result;
387 int enabled = OFstatic_cast(int, val);
388 if ((result = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
389 OFreinterpret_cast(char*, &enabled),OFstatic_cast(int, sizeof(enabled)))) != 0)
390 {
391 int eno = WSAGetLastError ();
392 set_last_socket_error (eno);
393 }
394
395 return result;
396 }
397
398
399 } } // namespace log4cplus { namespace helpers {
400 } // end namespace dcmtk
401
402 #endif // DCMTK_LOG4CPLUS_USE_WINSOCK
403