1 // SockUtils.cpp - Some basic socket utility functions.
2 // (C) 2002-2004 Royce Mitchell III
3 // This file is under the BSD & LGPL licenses
8 # ifndef SD_SEND // defined in winsock2.h, but not winsock.h
11 # define snprintf _snprintf
13 # pragma comment ( lib, "ws2_32.lib" )
17 # include "string.h" // memset
18 # include <netdb.h> // hostent
19 # include <arpa/inet.h> //inet_addr
20 # include <sys/time.h>
21 # define SD_SEND SHUT_WR //bah thou shalt name thy defines the same
23 # error unrecognized target
25 //// Constants /////////////////////////////////////////////////////////
26 const int kBufferSize
= 1024;
27 // creates broadcast address
28 SockAddrIn::SockAddrIn()
30 memset ( this, 0, sizeof(sockaddr_in
) );
33 SockAddrIn::SockAddrIn ( const char* szAddr
, u_short iPort
)
35 memset ( this, 0, sizeof(sockaddr_in
) );
37 sin_addr
.s_addr
= suLookupAddress(szAddr
);
38 sin_port
= htons(iPort
);
40 SockAddrIn::SockAddrIn ( in_addr_t iAddr
, u_short iPort
)
42 memset ( this, 0, sizeof(sockaddr_in
) );
44 sin_addr
.s_addr
= iAddr
;
45 sin_port
= htons(iPort
);
51 if ( WSAStartup ( MAKEWORD(2,0), &wsaData
) )
53 if ( wsaData
.wVersion
!= MAKEWORD(2,0) )
60 // nothing special required here
63 # error unrecognized target
66 //// suTcpSocket ////////////////////////////////////////////////
67 // Creates a TCP socket.
70 SOCKET so
= socket ( AF_INET
, SOCK_STREAM
, 0 );
71 #if defined(_DEBUG) && defined(WIN32)
72 if ( so
== INVALID_SOCKET
&& WSANOTINITIALISED
== WSAGetLastError() )
73 MessageBox ( NULL
, "You forgot to call suStartup()!", "SockUtils", MB_OK
|MB_ICONEXCLAMATION
);
77 //// suUdpSocket ////////////////////////////////////////////////
78 // Creates a UDP socket. Compensates for new "functionality" introduced
79 // in Win2K with regards to select() calls
80 // MS Transport Provider IOCTL to control
81 // reporting PORT_UNREACHABLE messages
82 // on UDP sockets via recv/WSARecv/etc.
83 // Path TRUE in input buffer to enable (default if supported),
85 #ifndef SIO_UDP_CONNRESET
86 #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
87 #endif//SIO_UDP_CONNRESET
90 SOCKET so
= socket ( AF_INET
, SOCK_DGRAM
, 0 );
91 #if defined(_DEBUG) && defined(WIN32)
92 if ( so
== INVALID_SOCKET
&& WSANOTINITIALISED
== WSAGetLastError() )
93 MessageBox ( NULL
, "You forgot to call suStartup()!", "SockUtils", MB_OK
|MB_ICONEXCLAMATION
);
96 // for Windows 2000, disable new behavior...
97 // see: http://www-pc.uni-regensburg.de/systemsw/W2KPRO/UPDATE/POSTSP1/Q263823.htm
98 // this code is innocuous on other win32 platforms
99 DWORD dwBytesReturned
= 0;
100 BOOL bNewBehavior
= FALSE
;
101 // disable new Win2K behavior using
102 // IOCTL: SIO_UDP_CONNRESET
103 // we don't care about return value :)
104 WSAIoctl(so
, SIO_UDP_CONNRESET
,
105 &bNewBehavior
, sizeof(bNewBehavior
),
106 NULL
, 0, &dwBytesReturned
,
111 //// suShutdownConnection ////////////////////////////////////////////////
112 // Gracefully shuts the connection sd down. Returns true if it was able
113 // to shut it down nicely, false if we had to "slam" it shut.
114 // (either way, the socket does get closed)
115 bool suShutdownConnection(SOCKET sd
)
117 if ( sd
== INVALID_SOCKET
)
119 // Disallow any further data sends. This will tell the other side
120 // that we want to go away now. If we skip this step, we don't
121 // shut the connection down nicely.
122 if (shutdown(sd
, SD_SEND
) == SOCKET_ERROR
)
127 // Receive any extra data still sitting on the socket. After all
128 // data is received, this call will block until the remote host
129 // acknowledges the TCP control packet sent by the shutdown above.
130 // Then we'll get a 0 back from recv, signalling that the remote
131 // host has closed its side of the connection.
132 char acReadBuffer
[kBufferSize
];
135 int nNewBytes
= recv(sd
, acReadBuffer
, kBufferSize
, 0);
136 if (nNewBytes
== SOCKET_ERROR
)
141 else if (nNewBytes
!= 0)
143 // FYI, received (nNewBytes) unexpected bytes during shutdown.
152 if (closesocket(sd
) == SOCKET_ERROR
)
158 //// suLookupAddress ////////////////////////////////////////////////
159 // Basically converts a name address to an ip address
160 in_addr_t
suLookupAddress ( const char* pcHost
)
162 in_addr_t nRemoteAddr
= inet_addr(pcHost
);
163 if ( nRemoteAddr
== INADDR_NONE
)
165 // pcHost isn't a dotted IP, so resolve it through DNS
166 hostent
* pHE
= gethostbyname(pcHost
);
169 #if defined(_DEBUG) && defined(WIN32)
170 if ( WSANOTINITIALISED
== WSAGetLastError() )
171 MessageBox ( NULL
, "You forgot to call suStartup()!", "SockUtils", MB_OK
|MB_ICONEXCLAMATION
);
175 nRemoteAddr
= *((in_addr_t
*)pHE
->h_addr_list
[0]);
179 bool suConnect ( SOCKET so
, in_addr_t iAddress
, u_short iPort
)
181 SockAddrIn
sinRemote ( iAddress
, iPort
);
182 if ( SOCKET_ERROR
== connect(so
,sinRemote
,sizeof(sinRemote
)) )
184 #if defined(_DEBUG) && defined(WIN32)
185 if ( WSANOTINITIALISED
== WSAGetLastError() )
186 MessageBox ( NULL
, "You forgot to call suStartup()!", "SockUtils", MB_OK
|MB_ICONEXCLAMATION
);
192 bool suConnect ( SOCKET so
, const char* szAddress
, u_short iPort
)
194 return suConnect ( so
, suLookupAddress(szAddress
), iPort
);
196 //// suEstablishConnection ////////////////////////////////////////////////
197 // creates a socket of the specified type, connects to the ip address/port
198 // requested, and returns the SOCKET created
199 SOCKET
suEstablishConnection ( in_addr_t iAddress
, u_short iPort
, int type
)
202 if ( type
!= SOCK_STREAM
&& type
!= SOCK_DGRAM
)
203 return INVALID_SOCKET
;
204 SOCKET so
= socket(AF_INET
, type
, 0);
205 if ( so
== INVALID_SOCKET
)
207 if ( !suConnect(so
, iAddress
, iPort
) )
210 return INVALID_SOCKET
;
214 //// suEstablishConnection ////////////////////////////////////////////////
215 // creates a socket of the specified type, connects to the address/port
216 // requested, and returns the SOCKET created
217 SOCKET
suEstablishConnection ( const char* szAddress
, u_short iPort
, int type
)
219 return suEstablishConnection ( suLookupAddress ( szAddress
), iPort
, type
);
221 //// suBroadcast ////////////////////////////////////////////////
222 // takes a previously created broadcast-enabled UDP socket, and broadcasts
223 // a message on the local network
224 bool suBroadcast ( SOCKET so
, u_short port
, const char* buf
, int len
/* = -1 */ )
227 len
= (int)strlen(buf
);
229 SockAddrIn
to ( INADDR_BROADCAST
, port
);
230 #else // some strange MS OS's don't broadcast to localhost...
231 SockAddrIn
to ( "127.0.0.1", port
);
232 if ( SOCKET_ERROR
== sendto ( so
, buf
, len
, 0, to
, sizeof(to
) ) )
234 to
.sin_addr
.s_addr
= INADDR_BROADCAST
;
236 if ( SOCKET_ERROR
== sendto ( so
, buf
, len
, 0, to
, sizeof(to
) ) )
240 //// suRecv ////////////////////////////////////////////////
241 // retrieves data sent to our TCP socket. If no data, waits for
242 // a period of timeout ms.
243 // returns bytes received
244 // -1 == SOCKET_ERROR
245 // -2 == timed out waiting for data
246 int suRecv ( SOCKET so
, char* buf
, int buflen
, int timeout
)
251 FD_ZERO(&rread
); // clear the fd_set
252 FD_SET(so
,&rread
); // indicate which socket(s) we want to check
253 memset((char *)&to
,0,sizeof(to
)); // clear the timeval struct
254 to
.tv_sec
= timeout
; // timeout select after (timeout) seconds
255 // select returns > 0 if there is an event on the socket
256 res
= select((int)so
+1, &rread
, (fd_set
*)0, (fd_set
*)0, &to
);
258 return -1; // socket error
259 // there was an event on the socket
260 if ( (res
>0) && (FD_ISSET(so
,&rread
)) )
261 return recv ( so
, buf
, buflen
, 0 );
264 //// suRecvFrom ////////////////////////////////////////////////
265 // retrieves data sent to our UDP socket. If no data, waits for
266 // a period of timeout ms.
267 // returns bytes received
268 // returns bytes received
269 // -1 == SOCKET_ERROR
270 // -2 == timed out waiting for data
271 int suRecvFrom ( SOCKET so
, char* buf
, int buflen
, int timeout
, sockaddr_in
* from
, socklen_t
* fromlen
)
276 FD_ZERO(&rread
); // clear the fd_set
277 FD_SET(so
,&rread
); // indicate which socket(s) we want to check
278 memset((char *)&to
,0,sizeof(to
)); // clear the timeval struct
279 to
.tv_sec
= timeout
; // timeout select after (timeout) seconds
280 // select returns > 0 if there is an event on the socket
281 res
= select((int)so
+1, &rread
, (fd_set
*)0, (fd_set
*)0, &to
);
283 return -1; // socket error
284 // there was an event on the socket
285 if ( (res
>0) && (FD_ISSET(so
,&rread
)) )
286 return recvfrom ( so
, buf
, buflen
, 0, (sockaddr
*)from
, fromlen
);
287 return -2; // timeout
289 //// suBind ////////////////////////////////////////////////
290 // binds a UDP socket to an interface & port to receive
292 bool suBind ( SOCKET so
, in_addr_t iInterfaceAddress
, u_short iListenPort
, bool bReuseAddr
/* = false */ )
294 SockAddrIn
sinInterface ( iInterfaceAddress
, iListenPort
);
297 int optval
= -1; // true
298 if ( SOCKET_ERROR
== setsockopt ( so
, SOL_SOCKET
, SO_REUSEADDR
, (const char*)&optval
, sizeof(optval
) ) )
300 #if defined(_DEBUG) && defined(WIN32)
301 if ( WSANOTINITIALISED
== WSAGetLastError() )
302 MessageBox ( NULL
, "You forgot to call suStartup()!", "SockUtils", MB_OK
|MB_ICONEXCLAMATION
);
307 if ( SOCKET_ERROR
== bind(so
, sinInterface
, sizeof(sinInterface
)) )
310 if ( err
!= EADDRINUSE
)
312 #if defined(_DEBUG) && defined(WIN32)
313 if ( WSANOTINITIALISED
== WSAGetLastError() )
314 MessageBox ( NULL
, "You forgot to call suStartup()!", "SockUtils", MB_OK
|MB_ICONEXCLAMATION
);
319 //// suBind ////////////////////////////////////////////////
320 // binds a UDP socket to an interface & port to receive
322 bool suBind ( SOCKET so
, const char* szInterfaceAddress
, u_short iListenPort
, bool bReuseAddr
/* = false */ )
324 in_addr_t iInterfaceAddr
= inet_addr(szInterfaceAddress
);
325 if ( iInterfaceAddr
== INADDR_NONE
)
327 return suBind ( so
, iInterfaceAddr
, iListenPort
, bReuseAddr
);
329 //// suEnableBroadcast ////////////////////////////////////////////////
330 // in order to send broadcast messages on a UDP socket, this function
331 // must be called first
332 bool suEnableBroadcast ( SOCKET so
, bool bEnable
/* = true */ )
334 int optval
= bEnable
? -1 : 0;
335 if ( SOCKET_ERROR
== setsockopt ( so
, SOL_SOCKET
, SO_BROADCAST
, (const char*)&optval
, sizeof(optval
) ) )
339 //// suErrDesc ////////////////////////////////////////////////
340 // returns text description of error code
341 const char* suErrDesc ( int err
)
343 static char errbuf
[256];
347 #define X(E) case E: return #E;
348 X(WSAEINTR
) X(WSAEBADF
)
349 X(WSAEACCES
) X(WSAEFAULT
)
350 X(WSAEINVAL
) X(WSAEMFILE
)
351 X(WSAEWOULDBLOCK
) X(WSAEINPROGRESS
)
352 X(WSAEALREADY
) X(WSAENOTSOCK
)
353 X(WSAEDESTADDRREQ
) X(WSAEMSGSIZE
)
354 X(WSAEPROTOTYPE
) X(WSAENOPROTOOPT
)
355 X(WSAEPROTONOSUPPORT
) X(WSAESOCKTNOSUPPORT
)
356 X(WSAEOPNOTSUPP
) X(WSAEPFNOSUPPORT
)
357 X(WSAEAFNOSUPPORT
) X(WSAEADDRINUSE
)
358 X(WSAEADDRNOTAVAIL
) X(WSAENETDOWN
)
359 X(WSAENETUNREACH
) X(WSAENETRESET
)
360 X(WSAECONNABORTED
) X(WSAECONNRESET
)
361 X(WSAENOBUFS
) X(WSAEISCONN
)
362 X(WSAENOTCONN
) X(WSAESHUTDOWN
)
363 X(WSAETOOMANYREFS
) X(WSAETIMEDOUT
)
364 X(WSAECONNREFUSED
) X(WSAELOOP
)
365 X(WSAENAMETOOLONG
) X(WSAEHOSTDOWN
)
366 X(WSAEHOSTUNREACH
) X(WSAENOTEMPTY
)
367 X(WSAEPROCLIM
) X(WSAEUSERS
)
368 X(WSAEDQUOT
) X(WSAESTALE
)
369 X(WSAEREMOTE
) X(WSASYSNOTREADY
)
370 X(WSAVERNOTSUPPORTED
) X(WSANOTINITIALISED
)
371 X(WSAEDISCON
) X(WSAENOMORE
)
372 X(WSAECANCELLED
) X(WSAEINVALIDPROCTABLE
)
373 X(WSAEINVALIDPROVIDER
) X(WSAEPROVIDERFAILEDINIT
)
374 X(WSASYSCALLFAILURE
) X(WSASERVICE_NOT_FOUND
)
375 X(WSATYPE_NOT_FOUND
) X(WSA_E_NO_MORE
)
376 X(WSA_E_CANCELLED
) X(WSAEREFUSED
)
379 snprintf ( errbuf
, sizeof(errbuf
), "Unknown socket error (%lu)", err
);
380 errbuf
[sizeof(errbuf
)-1] = '\0';
386 # error unrecognized target
389 #if defined(UNICODE) || defined(_UNICODE)
390 in_addr_t
suLookupAddress ( const wchar_t* pcHost
)
392 int len
= wcslen(pcHost
);
393 char* p
= new char[len
+1];
394 wcstombs ( p
, pcHost
, len
);
396 in_addr_t rc
= suLookupAddress ( p
);
400 bool suBroadcast ( SOCKET so
, u_short port
, const wchar_t* buf
, int len
/* = -1 */ )
402 char* p
= new char[len
+1];
403 wcstombs ( p
, buf
, len
);
405 bool rc
= suBroadcast ( so
, port
, p
, len
);
409 int suRecv ( SOCKET so
, wchar_t* buf
, int buflen
, int timeout
)
411 char* p
= new char[buflen
+1];
412 int rc
= suRecv ( so
, p
, buflen
, timeout
);
414 mbstowcs ( buf
, p
, buflen
);
418 int suRecvFrom ( SOCKET so
, wchar_t* buf
, int buflen
, int timeout
, sockaddr_in
* from
, int* fromlen
)
420 char* p
= new char[buflen
+1];
421 int rc
= suRecvFrom ( so
, p
, buflen
, timeout
, from
, fromlen
);
423 mbs2wcs ( buf
, p
, buflen
);
427 bool suBind ( SOCKET so
, const wchar_t* szInterfaceAddress
, u_short iListenPort
, bool bReuseAddr
/* = false */ )
429 int len
= wcslen(szInterfaceAddress
);
430 char* p
= new char[len
+1];
431 wcstombs ( p
, szInterfaceAddress
, len
);
433 bool rc
= suBind ( so
, p
, iListenPort
, bReuseAddr
);
439 suBufferedRecvSocket::suBufferedRecvSocket ( SOCKET so
)
440 : suSocket ( so
), _off(0), _len(0)
444 int suBufferedRecvSocket::recvUntil ( std::string
& buf
, char until
, int timeout
)
448 else if ( _off
> (sizeof(_buf
)>>1) )
450 memmove ( _buf
, &_buf
[_off
], _len
);
453 char* poff
= &_buf
[_off
];
456 char* p
= (char*)memchr ( poff
, until
, _len
);
457 if ( p
/*&& p < &poff[_len]*/ )
459 int ret_len
= p
-poff
+1;
460 buf
.resize ( ret_len
);
461 memmove ( &buf
[0], poff
, ret_len
);
466 int rc
= suRecv ( *this, &poff
[_len
], sizeof(_buf
)-_len
-_off
, timeout
);
473 memmove ( &buf
[0], &_buf
[_off
], rc
);
482 void suBufferedRecvSocket::recvPending()
486 else if ( _off
> (sizeof(_buf
)>>1) )
488 memmove ( _buf
, &_buf
[_off
], _len
);
491 char* poff
= &_buf
[_off
];
492 while ( sizeof(_buf
)-_len
-_off
)
494 int rc
= suRecv ( *this, &poff
[_len
], sizeof(_buf
)-_len
-_off
, 1 );
501 bool suBufferedRecvSocket::recvInStr ( char c
)
503 return NULL
!= memchr ( &_buf
[_off
], c
, _len
);