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