~ubuntu-branches/ubuntu/precise/primrose/precise

« back to all changes in this revision

Viewing changes to minorGems/network/win32/SocketWin32.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Paul Wise
  • Date: 2009-04-06 19:26:56 UTC
  • Revision ID: james.westby@ubuntu.com-20090406192656-cri7503gebyvfl8t
Tags: upstream-5+dfsg1
ImportĀ upstreamĀ versionĀ 5+dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Modification History
 
3
 *
 
4
 * 2001-January-28              Jason Rohrer
 
5
 * Created.  
 
6
 *
 
7
 * 2001-February-4              Jason Rohrer
 
8
 * Fixed receive so that it waits for all requested bytes to arrive.
 
9
 *
 
10
 * 2001-March-4         Jason Rohrer
 
11
 * Replaced include of <winbase.h> and <windef.h> with <windows.h> 
 
12
 * to fix compile bugs encountered with newer windows compilers.
 
13
 *
 
14
 * 2001-May-12   Jason Rohrer
 
15
 * Fixed a bug in socket receive error checking. 
 
16
 *
 
17
 * 2001-November-13             Jason Rohrer
 
18
 * Changed timeout parameter to signed, since -1 is a possible argument. 
 
19
 *
 
20
 * 2002-April-15    Jason Rohrer
 
21
 * Removed call to WSAGetLastError, since it seems to pick up errors from
 
22
 * non-socket calls.  For example, if sys/stat.h stat() is called on a file
 
23
 * that does not exist, WSAGetLastError returns 2, which is not even a
 
24
 * winsock error code.  We should probably report this bug, huh?
 
25
 *
 
26
 * 2002-August-2    Jason Rohrer
 
27
 * Added functon for getting remote host address, but no implementation.
 
28
 *
 
29
 * 2002-August-5    Jason Rohrer
 
30
 * Added implementation of getRemoteHostAddress().
 
31
 *
 
32
 * 2002-September-8    Jason Rohrer
 
33
 * Fixed a major looping bug with broken sockets.
 
34
 *
 
35
 * 2002-November-15   Jason Rohrer
 
36
 * Fixed a security hole when getting the remote host address.
 
37
 *
 
38
 * 2003-February-4   Jason Rohrer
 
39
 * Added a function for getting the local host address from a socket.
 
40
 * Still need to test the win32 version of this.
 
41
 *
 
42
 * 2003-February-5   Jason Rohrer
 
43
 * Fixed a bug in call to gethostname.  Removed unused variable.
 
44
 *
 
45
 * 2004-January-4   Jason Rohrer
 
46
 * Added use of network function locks.
 
47
 *
 
48
 * 2004-January-11   Jason Rohrer
 
49
 * Fixed a bug in handling of timeout return value.
 
50
 *
 
51
 * 2004-March-23   Jason Rohrer
 
52
 * Removed timeout error message.
 
53
 *
 
54
 * 2004-December-13   Jason Rohrer
 
55
 * Added a breakConnection function.
 
56
 *
 
57
 * 2004-December-24   Jason Rohrer
 
58
 * Fixed bug in close call.
 
59
 *
 
60
 * 2005-July-5   Jason Rohrer
 
61
 * Added port number when getting address of remote host.
 
62
 *
 
63
 * 2006-May-28   Jason Rohrer
 
64
 * Changed timeout behavior slightly to support emulation of non-blocking mode.
 
65
 *
 
66
 * 2006-June-5   Jason Rohrer
 
67
 * Added support for non-blocking sends.
 
68
 *
 
69
 * 2008-September-30   Jason Rohrer
 
70
 * Added support for non-blocking connect.
 
71
 * Fixed close-detection in non-blocking read.
 
72
 */
 
73
 
 
74
 
 
75
 
 
76
#include "minorGems/network/Socket.h"
 
77
#include "minorGems/network/NetworkFunctionLocks.h"
 
78
 
 
79
#include <winsock.h>
 
80
#include <windows.h>
 
81
 
 
82
#include <stdio.h>
 
83
#include <time.h>
 
84
#include <string.h>
 
85
 
 
86
// prototypes
 
87
int timed_read( int inSock, unsigned char *inBuf, 
 
88
        int inLen, long inMilliseconds );
 
89
        
 
90
 
 
91
/**
 
92
 * Windows-specific implementation of the Socket class member functions.
 
93
 *
 
94
 */
 
95
 
 
96
 
 
97
 
 
98
// Win32 does not define socklen_t
 
99
typedef int socklen_t;
 
100
 
 
101
 
 
102
 
 
103
char Socket::sInitialized = false;
 
104
 
 
105
 
 
106
 
 
107
int Socket::initSocketFramework() {
 
108
        WORD wVersionRequested;
 
109
        WSADATA wsaData;
 
110
        
 
111
        int err; 
 
112
        wVersionRequested = MAKEWORD( 1, 0 ); 
 
113
        err = WSAStartup( wVersionRequested, &wsaData );
 
114
        
 
115
        if ( err != 0 ) {
 
116
        // no usable DLL found  
 
117
        printf( "WinSock DLL version 1.0 or higher not found.\n" );  
 
118
        
 
119
                return -1;
 
120
                }
 
121
    
 
122
    sInitialized = true;
 
123
        return 0;
 
124
        }
 
125
                
 
126
 
 
127
 
 
128
 
 
129
Socket::~Socket() {
 
130
        int *socketIDptr = (int *)( mNativeObjectPointer );
 
131
        int socketID = socketIDptr[0];
 
132
 
 
133
    if( !mIsConnectionBroken ) {
 
134
        
 
135
        // 2 specifies shutting down both sends and receives
 
136
        shutdown( socketID, 2 );
 
137
        mIsConnectionBroken = true;
 
138
        }
 
139
    
 
140
        closesocket( socketID );
 
141
        
 
142
        delete [] socketIDptr;
 
143
        }
 
144
 
 
145
 
 
146
 
 
147
int Socket::isConnected() {
 
148
    
 
149
    if( mConnected ) {
 
150
        return 1;
 
151
        }
 
152
    
 
153
    int *socketIDptr = (int *)( mNativeObjectPointer );
 
154
        int socketID = socketIDptr[0];
 
155
 
 
156
    int ret;
 
157
        fd_set fsr;
 
158
        struct timeval tv;
 
159
        int val;
 
160
    socklen_t len;
 
161
 
 
162
        FD_ZERO( &fsr );
 
163
        FD_SET( socketID, &fsr );
 
164
 
 
165
    // check if connection event waiting right now
 
166
    // timeout of 0
 
167
    tv.tv_sec = 0;
 
168
        tv.tv_usec = 0;    
 
169
 
 
170
        ret = select( socketID + 1, NULL, &fsr, NULL, &tv );
 
171
 
 
172
        if( ret==0 ) {
 
173
                // timeout
 
174
                return 0;
 
175
        }
 
176
 
 
177
    // no timeout
 
178
    // error?
 
179
 
 
180
        len = 4;
 
181
        ret = getsockopt( socketID, SOL_SOCKET, SO_ERROR, (char*)( &val ), &len );
 
182
        
 
183
        if( ret < 0 ) {
 
184
                // error
 
185
        return -1;
 
186
        }
 
187
 
 
188
        if( val != 0 ) {
 
189
        // error
 
190
                return -1;
 
191
        }
 
192
        
 
193
    // success
 
194
    mConnected = true;
 
195
    
 
196
    return 1;
 
197
    }
 
198
 
 
199
 
 
200
 
 
201
int Socket::send( unsigned char *inBuffer, int inNumBytes,
 
202
                  char inAllowedToBlock ) {
 
203
        
 
204
        int *socketIDptr = (int *)( mNativeObjectPointer );
 
205
        int socketID = socketIDptr[0];
 
206
 
 
207
    if( inAllowedToBlock ) {
 
208
    
 
209
        return ::send( socketID, (char*)inBuffer, inNumBytes, 0 );
 
210
        }
 
211
    else {
 
212
        // 1 for non-blocking, 0 for blocking
 
213
        u_long socketMode = 1;
 
214
        ioctlsocket( socketID, FIONBIO, &socketMode );
 
215
 
 
216
        int result = ::send( socketID, (char*)inBuffer, inNumBytes, 0 );
 
217
 
 
218
        // set back to blocking
 
219
        socketMode = 0;
 
220
        ioctlsocket( socketID, FIONBIO, &socketMode );
 
221
        
 
222
        
 
223
        if( result == -1 &&
 
224
            WSAGetLastError() == WSAEWOULDBLOCK ) {
 
225
 
 
226
            return -2;
 
227
            }
 
228
        else {
 
229
            return result;
 
230
            }
 
231
        }
 
232
    }
 
233
                
 
234
                
 
235
                
 
236
int Socket::receive( unsigned char *inBuffer, int inNumBytes,
 
237
        long inTimeout ) {
 
238
        
 
239
        int *socketIDptr = (int *)( mNativeObjectPointer );
 
240
        int socketID = socketIDptr[0];
 
241
        
 
242
        int numReceived = 0;
 
243
        
 
244
        char error = false;
 
245
        char errorReturnValue = -1;
 
246
 
 
247
 
 
248
    char stopLooping = false;
 
249
    
 
250
    
 
251
        // for win32, we can't specify MSG_WAITALL
 
252
        // so we have too loop until the entire message is received,
 
253
        // as long as there is no error.
 
254
 
 
255
    // note that if a timeout is set, we use the stopLooping flag
 
256
    // to return only the available data (we do not emulate MSG_WAITALL)
 
257
    
 
258
        while( numReceived < inNumBytes &&
 
259
           !error &&
 
260
           !stopLooping ) {
 
261
                   
 
262
                // the number of bytes left to receive
 
263
                int numRemaining = inNumBytes - numReceived;
 
264
                
 
265
                // pointer to the spot in the buffer where the
 
266
                // remaining bytes should be stored
 
267
                unsigned char *remainingBuffer = &( inBuffer[ numReceived ] );
 
268
                
 
269
                int numReceivedIn;
 
270
                
 
271
                if( inTimeout == -1 ) {         
 
272
                        numReceivedIn = 
 
273
                                recv( socketID, (char*)remainingBuffer, numRemaining, 0 );
 
274
                        }
 
275
                else {          
 
276
                        numReceivedIn = 
 
277
                                timed_read( socketID, remainingBuffer,
 
278
                            numRemaining, inTimeout );
 
279
 
 
280
            // stop looping after one timed read
 
281
            stopLooping = true;
 
282
                        }
 
283
                        
 
284
                        
 
285
                if( numReceivedIn > 0 ) {
 
286
                        numReceived += numReceivedIn;
 
287
                        }
 
288
        else {
 
289
            error = true;
 
290
 
 
291
            if( numReceivedIn == 0 ) {
 
292
                // the socket was gracefully closed
 
293
                errorReturnValue = -1;
 
294
                }
 
295
            else if( numReceivedIn == SOCKET_ERROR ) {
 
296
                // socket error
 
297
                errorReturnValue = -1;
 
298
                }
 
299
            else if( numReceivedIn == -2 ) {
 
300
                // timeout
 
301
                errorReturnValue = -2;
 
302
                }
 
303
            else {
 
304
                printf( "Unexpected return value from socket receive: %d.\n",
 
305
                        numReceivedIn );
 
306
                errorReturnValue = -1;
 
307
                }
 
308
            
 
309
            }
 
310
                        
 
311
                }
 
312
 
 
313
    if( error ) {
 
314
        return errorReturnValue;
 
315
        }
 
316
    else {
 
317
        return numReceived;
 
318
        }
 
319
        }
 
320
 
 
321
 
 
322
 
 
323
void Socket::breakConnection() {
 
324
        int *socketIDptr = (int *)( mNativeObjectPointer );
 
325
        int socketID = socketIDptr[0];
 
326
 
 
327
    if( !mIsConnectionBroken ) {
 
328
 
 
329
        shutdown( socketID, 2 );
 
330
        mIsConnectionBroken = true;
 
331
        }
 
332
    
 
333
        closesocket( socketID );
 
334
    }
 
335
 
 
336
 
 
337
 
 
338
HostAddress *Socket::getRemoteHostAddress() {
 
339
 
 
340
    int *socketIDptr = (int *)( mNativeObjectPointer );
 
341
        int socketID = socketIDptr[0];
 
342
 
 
343
    // adapted from Unix Socket FAQ
 
344
    
 
345
    socklen_t len;
 
346
    struct sockaddr_in sin;
 
347
    
 
348
    len = sizeof sin;
 
349
    int error = getpeername( socketID, (struct sockaddr *) &sin, &len );
 
350
 
 
351
    if( error ) {
 
352
        return NULL;
 
353
        }
 
354
 
 
355
    // this is potentially insecure, since a fake DNS name might be returned
 
356
    // we should use the IP address only
 
357
    //
 
358
    // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr,
 
359
    //                                       sizeof sin.sin_addr,
 
360
    //                                       AF_INET );
 
361
 
 
362
 
 
363
    NetworkFunctionLocks::mInet_ntoaLock.lock();
 
364
    // returned string is statically allocated, copy it
 
365
    char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) );
 
366
    NetworkFunctionLocks::mInet_ntoaLock.unlock();
 
367
 
 
368
    int port = ntohs( sin.sin_port );
 
369
    
 
370
    return new HostAddress( ipAddress, port );    
 
371
    }
 
372
 
 
373
 
 
374
 
 
375
HostAddress *Socket::getLocalHostAddress() {
 
376
    int *socketIDptr = (int *)( mNativeObjectPointer );
 
377
        int socketID = socketIDptr[0];
 
378
 
 
379
    // adapted from GTK-gnutalla code, and elsewhere
 
380
 
 
381
    struct sockaddr_in addr;
 
382
        int len = sizeof( struct sockaddr_in );
 
383
 
 
384
    int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len );
 
385
 
 
386
    if( result == -1 ) {
 
387
        return NULL;
 
388
        }
 
389
    else {
 
390
 
 
391
        char *stringAddress = inet_ntoa( addr.sin_addr );
 
392
 
 
393
        return new HostAddress( stringDuplicate( stringAddress ),
 
394
                                0 );
 
395
        }
 
396
    
 
397
    }
 
398
 
 
399
 
 
400
 
 
401
/* timed_read adapted from gnut, by Josh Pieper */
 
402
/* Josh Pieper, (c) 2000 */
 
403
/* This file is distributed under the GPL, see file COPYING for details */
 
404
 
 
405
// exactly like the real read, except that it returns -2
 
406
// if no data was read before the timeout occurred...
 
407
int timed_read( int inSock, unsigned char *inBuf, 
 
408
        int inLen, long inMilliseconds ) {
 
409
        fd_set fsr;
 
410
        struct timeval tv;
 
411
        int ret;
 
412
        
 
413
        FD_ZERO( &fsr );
 
414
        FD_SET( inSock, &fsr );
 
415
 
 
416
        tv.tv_sec = inMilliseconds / 1000;
 
417
        int remainder = inMilliseconds % 1000;
 
418
        tv.tv_usec = remainder * 1000;
 
419
        
 
420
        ret = select( inSock + 1, &fsr, NULL, NULL, &tv );
 
421
        
 
422
        if( ret==0 ) {
 
423
                // printf( "Timed out waiting for data on socket receive.\n" );
 
424
                return -2;
 
425
                }
 
426
        
 
427
        if( ret<0 ) {
 
428
                printf( "Selecting socket during receive failed.\n" );
 
429
                return ret;
 
430
                }
 
431
        
 
432
        ret = recv( inSock, (char*)inBuf, inLen, 0 );
 
433
        
 
434
 
 
435
    if( ret == 0  ) {
 
436
        // select came back as 1, but no data there
 
437
        // connection closed on remote end
 
438
        return -1;
 
439
        }
 
440
 
 
441
        return ret;
 
442
        }