PageRenderTime 26ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/ClassMaster2014/barahon/Cultivation_9_UnixSource/minorGems/network/linux/SocketLinux.cpp

https://gitlab.com/garheade/linux_camp
C++ | 443 lines | 199 code | 119 blank | 125 comment | 31 complexity | 3a10507d3ab4fc9e78252fb7aae596a1 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * Modification History
  3. *
  4. * 2001-January-9 Jason Rohrer
  5. * Created.
  6. *
  7. * 2001-January-10 Jason Rohrer
  8. * Fixed a bug in the receive code (in use of timed_read).
  9. *
  10. * 2001-January-15 Jason Rohrer
  11. * Added a safeguard against receives that exceed the
  12. * network buffer size.
  13. *
  14. * 2001-January-28 Jason Rohrer
  15. * Changed to comply with new initSocketFramework Socket interface.
  16. * Added a close() call to the destructor.
  17. *
  18. * 2001-January-29 Jason Rohrer
  19. * Fixed compile bug by including unistd.h for close call.
  20. *
  21. * 2001-November-13 Jason Rohrer
  22. * Changed to use ::send function instead of __send function.
  23. * Changed timeout parameter to signed, since -1 is a possible argument.
  24. *
  25. * 2001-December-12 Jason Rohrer
  26. * Changed the include order to make BSD compatible.
  27. *
  28. * 2002-August-2 Jason Rohrer
  29. * Changed to ignore SIGPIPE.
  30. * Added functon for getting remote host address.
  31. * Added fix for linux-BSD differences.
  32. *
  33. * 2002-November-15 Jason Rohrer
  34. * Fixed a security hole when getting the remote host address.
  35. *
  36. * 2003-February-3 Jason Rohrer
  37. * Added a function for getting the local host address from a socket.
  38. *
  39. * 2003-February-21 Jason Rohrer
  40. * Fixed a BSD compatibility bug.
  41. *
  42. * 2004-January-4 Jason Rohrer
  43. * Added use of network function locks.
  44. *
  45. * 2004-March-23 Jason Rohrer
  46. * Removed timeout error message.
  47. *
  48. * 2004-December13 Jason Rohrer
  49. * Added a breakConnection function.
  50. *
  51. * 2005-July-5 Jason Rohrer
  52. * Added port number when getting address of remote host.
  53. *
  54. * 2006-May-28 Jason Rohrer
  55. * Changed timeout behavior slightly to support emulation of non-blocking mode.
  56. * Added support for non-blocking sends.
  57. *
  58. * 2006-June-5 Jason Rohrer
  59. * Fixed error checking for non-blocking sends.
  60. *
  61. * 2006-June-23 Jason Rohrer
  62. * Changed implementation of non-blocking sends to work on Mac.
  63. *
  64. * 2008-September-30 Jason Rohrer
  65. * Added support for non-blocking connect.
  66. * Fixed close-detection in non-blocking read.
  67. *
  68. * 2009-April-3 Jason Rohrer
  69. * OpenBSD support.
  70. *
  71. * 2010-January-26 Jason Rohrer
  72. * Fixed socklen_t on later versions of MacOSX.
  73. *
  74. * 2010-January-26 Jason Rohrer
  75. * Added support for disabling Nagle algorithm.
  76. */
  77. #include "minorGems/network/Socket.h"
  78. #include "minorGems/network/NetworkFunctionLocks.h"
  79. #include <sys/time.h>
  80. #include <sys/types.h>
  81. #include <sys/socket.h>
  82. #include <sys/errno.h>
  83. #include <netinet/in.h>
  84. #include <netinet/tcp.h>
  85. #include <arpa/inet.h>
  86. #include <netdb.h>
  87. #include <stdio.h>
  88. #include <fcntl.h>
  89. #include <time.h>
  90. #include <string.h>
  91. #include <unistd.h>
  92. #include <signal.h>
  93. #include <errno.h>
  94. // BSD does not define socklen_t
  95. #ifdef BSD
  96. #ifndef IPHONE
  97. #ifndef __OpenBSD__
  98. #ifndef _SOCKLEN_T // later versions of MacOS define it and mark it
  99. typedef int socklen_t;
  100. #endif
  101. #endif
  102. #endif
  103. #endif
  104. // prototypes
  105. int timed_read( int inSock, unsigned char *inBuf,
  106. int inLen, long inMilliseconds );
  107. /**
  108. * Linux-specific implementation of the Socket class member functions.
  109. *
  110. * May also be compatible with other POSIX-like systems.
  111. *
  112. * To compile:
  113. * no special options needed
  114. */
  115. char Socket::sInitialized = false;
  116. int Socket::initSocketFramework() {
  117. // ignore SIGPIPE, which occurs on send whenever the receiver
  118. // has closed the socket
  119. signal(SIGPIPE, SIG_IGN);
  120. sInitialized = true;
  121. return 0;
  122. }
  123. Socket::~Socket() {
  124. int *socketIDptr = (int *)( mNativeObjectPointer );
  125. int socketID = socketIDptr[0];
  126. if( !mIsConnectionBroken ) {
  127. shutdown( socketID, SHUT_RDWR );
  128. mIsConnectionBroken = true;
  129. }
  130. close( socketID );
  131. delete [] socketIDptr;
  132. }
  133. int Socket::isConnected() {
  134. if( mConnected ) {
  135. return 1;
  136. }
  137. int *socketIDptr = (int *)( mNativeObjectPointer );
  138. int socketID = socketIDptr[0];
  139. int ret;
  140. fd_set fsr;
  141. struct timeval tv;
  142. int val;
  143. socklen_t len;
  144. FD_ZERO( &fsr );
  145. FD_SET( socketID, &fsr );
  146. // check if connection event waiting right now
  147. // timeout of 0
  148. tv.tv_sec = 0;
  149. tv.tv_usec = 0;
  150. ret = select( socketID + 1, NULL, &fsr, NULL, &tv );
  151. if( ret==0 ) {
  152. // timeout
  153. return 0;
  154. }
  155. // no timeout
  156. // error?
  157. len = 4;
  158. ret = getsockopt( socketID, SOL_SOCKET, SO_ERROR, &val, &len );
  159. if( ret < 0 ) {
  160. // error
  161. return -1;
  162. }
  163. if( val != 0 ) {
  164. // error
  165. return -1;
  166. }
  167. // success
  168. mConnected = true;
  169. return 1;
  170. }
  171. void Socket::setNoDelay( int inValue ) {
  172. int *socketIDptr = (int *)( mNativeObjectPointer );
  173. int socketID = socketIDptr[0];
  174. int flag = inValue;
  175. setsockopt( socketID,
  176. IPPROTO_TCP,
  177. TCP_NODELAY,
  178. (char *) &flag,
  179. sizeof(int) );
  180. }
  181. int Socket::send( unsigned char *inBuffer, int inNumBytes,
  182. char inAllowedToBlock,
  183. char inAllowDelay ) {
  184. int *socketIDptr = (int *)( mNativeObjectPointer );
  185. int socketID = socketIDptr[0];
  186. if( inAllowedToBlock ) {
  187. if( ! inAllowDelay ) {
  188. // turn nodelay on
  189. setNoDelay( 1 );
  190. }
  191. int returnVal = ::send( socketID, inBuffer, inNumBytes, 0 );
  192. if( ! inAllowDelay ) {
  193. // turn nodelay back off
  194. setNoDelay( 0 );
  195. }
  196. return returnVal;
  197. }
  198. else {
  199. // set to non-blocking mode
  200. int result = fcntl( socketID, F_SETFL, O_NONBLOCK );
  201. if( result < 0 ) {
  202. return result;
  203. }
  204. if( ! inAllowDelay ) {
  205. // turn nodelay on
  206. setNoDelay( 1 );
  207. }
  208. int returnValue = ::send( socketID, inBuffer, inNumBytes,
  209. // no flags
  210. 0 );
  211. if( ! inAllowDelay ) {
  212. // turn nodelay back off
  213. setNoDelay( 0 );
  214. }
  215. // back into blocking mode
  216. result = fcntl( socketID, F_SETFL, 0 );
  217. if( result < 0 ) {
  218. return result;
  219. }
  220. if( returnValue == -1 && errno == EAGAIN ) {
  221. return -2;
  222. }
  223. else {
  224. return returnValue;
  225. }
  226. }
  227. }
  228. int Socket::receive( unsigned char *inBuffer, int inNumBytes,
  229. long inTimeout ) {
  230. int *socketIDptr = (int *)( mNativeObjectPointer );
  231. int socketID = socketIDptr[0];
  232. if( inTimeout == -1 ) {
  233. // use MSG_WAITALL flag here to block until inNumBytes has arrived
  234. return recv( socketID, inBuffer, inNumBytes, MSG_WAITALL );
  235. }
  236. else {
  237. return timed_read( socketID, inBuffer, inNumBytes, inTimeout );
  238. }
  239. }
  240. void Socket::breakConnection() {
  241. int *socketIDptr = (int *)( mNativeObjectPointer );
  242. int socketID = socketIDptr[0];
  243. if( !mIsConnectionBroken ) {
  244. shutdown( socketID, SHUT_RDWR );
  245. mIsConnectionBroken = true;
  246. }
  247. close( socketID );
  248. }
  249. HostAddress *Socket::getRemoteHostAddress() {
  250. int *socketIDptr = (int *)( mNativeObjectPointer );
  251. int socketID = socketIDptr[0];
  252. // adapted from Unix Socket FAQ
  253. socklen_t len;
  254. struct sockaddr_in sin;
  255. len = sizeof sin;
  256. int error = getpeername( socketID, (struct sockaddr *) &sin, &len );
  257. if( error ) {
  258. return NULL;
  259. }
  260. // this is potentially insecure, since a fake DNS name might be returned
  261. // we should use the IP address only
  262. //
  263. // struct hostent *host = gethostbyaddr( (char *) &sin.sin_addr,
  264. // sizeof sin.sin_addr,
  265. // AF_INET );
  266. NetworkFunctionLocks::mInet_ntoaLock.lock();
  267. // returned string is statically allocated, copy it
  268. char *ipAddress = stringDuplicate( inet_ntoa( sin.sin_addr ) );
  269. NetworkFunctionLocks::mInet_ntoaLock.unlock();
  270. int port = ntohs( sin.sin_port );
  271. return new HostAddress( ipAddress, port );
  272. }
  273. HostAddress *Socket::getLocalHostAddress() {
  274. int *socketIDptr = (int *)( mNativeObjectPointer );
  275. int socketID = socketIDptr[0];
  276. // adapted from GTK-gnutalla code, and elsewhere
  277. struct sockaddr_in addr;
  278. socklen_t len = sizeof( struct sockaddr_in );
  279. int result = getsockname( socketID, (struct sockaddr*)( &addr ), &len );
  280. if( result == -1 ) {
  281. return NULL;
  282. }
  283. else {
  284. char *stringAddress = inet_ntoa( addr.sin_addr );
  285. return new HostAddress( stringDuplicate( stringAddress ),
  286. 0 );
  287. }
  288. }
  289. /* timed_read adapted from gnut, by Josh Pieper */
  290. /* Josh Pieper, (c) 2000 */
  291. /* This file is distributed under the GPL, see file COPYING for details */
  292. // exactly like the real read, except that it returns -2
  293. // if no data was read before the timeout occurred...
  294. int timed_read( int inSock, unsigned char *inBuf,
  295. int inLen, long inMilliseconds ) {
  296. fd_set fsr;
  297. struct timeval tv;
  298. int ret;
  299. //ret = fcntl( inSock, F_SETFL, O_NONBLOCK );
  300. FD_ZERO( &fsr );
  301. FD_SET( inSock, &fsr );
  302. tv.tv_sec = inMilliseconds / 1000;
  303. int remainder = inMilliseconds % 1000;
  304. tv.tv_usec = remainder * 1000;
  305. ret = select( inSock + 1, &fsr, NULL, NULL, &tv );
  306. if( ret==0 ) {
  307. // printf( "Timed out waiting for data on socket receive.\n" );
  308. return -2;
  309. }
  310. if( ret<0 ) {
  311. printf( "Selecting socket during receive failed.\n" );
  312. return ret;
  313. }
  314. // do not use MSG_WAITALL flag here, since we just want to return
  315. // data that is available
  316. ret = recv( inSock, inBuf, inLen, 0 );
  317. if( ret == 0 ) {
  318. // select came back as 1, but no data there
  319. // connection closed on remote end
  320. return -1;
  321. }
  322. //fcntl( inSock, F_SETFL, 0 );
  323. return ret;
  324. }