static void nlPrepareClose(NLsocket socket) { if(nlIsValidSocket(socket) != NL_TRUE) return; struct Unlocker { NLsocket socket; ~Unlocker() { nlUnlockSocket(socket, NL_BOTH); } Unlocker(NLsocket s) : socket(s) {} }; if(nlLockSocket(socket, NL_BOTH) == NL_FALSE) { return; } Unlocker unlocker(socket); // code copied&modified from sock_Close // The advantage we have here is that we don't lock the whole socket array. // nlClose is doing this, so if we would hang in nlClose, we block the // *whole* HawkNL system (or at least actions like opening new sockets etc.)! nl_socket_t *sock = nlSockets[socket]; struct ip_mreq mreq; if(sock->type == NL_UDP_MULTICAST) { /* leave the multicast group */ mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)&sock->addressout)->sin_addr.s_addr; mreq.imr_interface.s_addr = INADDR_ANY; //bindaddress; (void)setsockopt((SOCKET)sock->realsocket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, (int)sizeof(mreq)); } if(sock->type == NL_RELIABLE_PACKETS) { /* check for unsent data */ if(sock->sendlen > 0) { int tries = 200; /* 200 * 50 ms = up to a 10 second delay to allow data to be sent */ while(tries-- > 0 && sock->sendlen > 0) { SDL_Delay(50); } } // oh just f**k it sock->sendlen = 0; } if((sock->type == NL_RELIABLE_PACKETS || sock->type == NL_RELIABLE) && sock->listen == NL_FALSE) { struct linger l = {1, 10}; (void)setsockopt((SOCKET)sock->realsocket, SOL_SOCKET, SO_LINGER, (const char *)&l, (int)sizeof(l)); } (void)closesocket((SOCKET)sock->realsocket); }
NL_EXP NLboolean NL_APIENTRY nlGroupDeleteSocket(NLint group, NLsocket socket) { NLint realgroup = group - NL_FIRST_GROUP; NLint i; nl_group_t *pgroup = NULL; if(groups == NULL) { nlSetError(NL_NO_NETWORK); return NL_FALSE; } if(realgroup < 0) { nlSetError(NL_INVALID_GROUP); return NL_FALSE; } /* delete the socket from the group */ if(nlMutexLock(&grouplock) == NL_FALSE) { return NL_FALSE; } pgroup = groups[realgroup]; for(i=0;i<pgroup->numsockets;i++) { /* check for match */ if(pgroup->sockets[i] == socket) break; } if(i == pgroup->numsockets) { /* did not find the socket */ (void)nlMutexUnlock(&grouplock); nlSetError(NL_SOCKET_NOT_FOUND); return NL_FALSE; } /* now pgroup[i] points to the socket to delete */ /* shift all other sockets down to close the gap */ i++; for(;i<pgroup->maxsockets;i++) { pgroup->sockets[i - 1] = pgroup->sockets[i]; /* check for end of list */ if(pgroup->sockets[i] == -1) break; } pgroup->numsockets--; if(pgroup->fdset != NULL) { /* make sure the socket is valid */ if(nlIsValidSocket(socket) == NL_TRUE) { SOCKET realsock; realsock = (SOCKET)nlSockets[socket]->realsocket; FD_CLR(realsock, pgroup->fdset); } else { /* the socket was already closed */ /* free the fdset so that it can be rebuilt */ free(pgroup->fdset); pgroup->fdset = NULL; (void)nlMutexUnlock(&grouplock); nlSetError(NL_INVALID_SOCKET); return NL_FALSE; } } if(nlMutexUnlock(&grouplock) == NL_FALSE) { return NL_FALSE; } return NL_TRUE; }
NL_EXP NLboolean NL_APIENTRY nlGroupAddSocket(NLint group, NLsocket socket) { NLint realgroup = group - NL_FIRST_GROUP; NLint i; nl_group_t *pgroup = NULL; if(groups == NULL) { nlSetError(NL_NO_NETWORK); return NL_FALSE; } if(realgroup < 0) { nlSetError(NL_INVALID_GROUP); return NL_FALSE; } /* add the socket to the group */ if(nlMutexLock(&grouplock) == NL_FALSE) { return NL_FALSE; } pgroup = groups[realgroup]; /* allocate more sockets as needed */ if(pgroup->numsockets == pgroup->maxsockets) { NLint oldmax = pgroup->maxsockets; NLint j; NLsocket *newsockets; if(oldmax == NL_MAX_GROUP_SOCKETS) { (void)nlMutexUnlock(&grouplock); nlSetError(NL_OUT_OF_GROUP_SOCKETS); return NL_FALSE; } pgroup->maxsockets *= 2; if(pgroup->maxsockets > NL_MAX_GROUP_SOCKETS) { pgroup->maxsockets = NL_MAX_GROUP_SOCKETS; } if((newsockets = (NLsocket *)realloc(pgroup->sockets, pgroup->maxsockets * sizeof(NLsocket *))) == NULL) { pgroup->maxsockets = oldmax; (void)nlMutexUnlock(&grouplock); nlSetError(NL_OUT_OF_MEMORY); return NL_FALSE; } /* set the new sockets to -1 */ for(j=oldmax;j<pgroup->maxsockets;j++) { newsockets[j] = -1; } pgroup->sockets = newsockets; } for(i=0;i<pgroup->maxsockets;i++) { if(pgroup->sockets[i] == -1) { pgroup->sockets[i] = socket; if(pgroup->fdset != NULL) { SOCKET realsock; /* make sure the socket is valid */ if(nlIsValidSocket(socket) == NL_FALSE) { (void)nlMutexUnlock(&grouplock); nlSetError(NL_INVALID_SOCKET); return NL_FALSE; } realsock = (SOCKET)nlSockets[socket]->realsocket; FD_SET(realsock, pgroup->fdset); if(pgroup->highest < realsock + 1) { pgroup->highest = realsock + 1; } } break; } } if(i == pgroup->maxsockets) { (void)nlMutexUnlock(&grouplock); nlSetError(NL_OUT_OF_GROUP_SOCKETS); return NL_FALSE; } pgroup->numsockets++; if(nlMutexUnlock(&grouplock) == NL_FALSE) { return NL_FALSE; } return NL_TRUE; }
NLint loopback_PollGroup(NLint group, NLenum name, NLsocket *sockets, NLint number, NLint timeout) { NLint count = 0; NLint numsockets = NL_MAX_GROUP_SOCKETS; NLsocket temp[NL_MAX_GROUP_SOCKETS]; nlGroupGetSockets(group, (NLint *)&temp, &numsockets); if(numsockets < 0) { /* any error is set by nlGroupGetSockets */ return NL_INVALID; } if(numsockets == 0) { return 0; } switch(name) { case NL_READ_STATUS: { NLint i = 0; while(numsockets-- > 0) { /* check for a packet */ nl_socket_t *sock; if(nlIsValidSocket(temp[i]) != NL_TRUE) { nlSetError(NL_INVALID_SOCKET); return NL_INVALID; } sock = nlSockets[temp[i]]; if(sock->inlen[sock->nextinused] > 0) { *sockets = temp[i]; sockets++; count++; if(count > number) { nlSetError(NL_BUFFER_SIZE); return NL_INVALID; } } i++; } } break; case NL_WRITE_STATUS: { NLint i = 0; while(numsockets-- > 0) { nl_socket_t *sock; if(nlIsValidSocket(temp[i]) != NL_TRUE) { nlSetError(NL_INVALID_SOCKET); return NL_INVALID; } sock = nlSockets[temp[i]]; /* check for a free packet if reliable and connected */ if((sock->type == NL_RELIABLE || sock->type == NL_RELIABLE_PACKETS) && (sock->connecting == NL_TRUE || sock->connected == NL_TRUE)) { nl_socket_t *othersock = nlSockets[sock->consock]; if(othersock->nextinfree == NL_INVALID) { continue; } } /* add the socket to the list */ *sockets = temp[i]; sockets++; count++; if(count > number) { nlSetError(NL_BUFFER_SIZE); return NL_INVALID; } i++; } } break; default: nlSetError(NL_INVALID_ENUM); return NL_INVALID; } return count; }
NLint loopback_Write(NLsocket socket, NLvoid *buffer, NLint nbytes) { nl_socket_t *sock = nlSockets[socket]; nl_socket_t *othersock; NLint s[NL_MAX_GROUP_SOCKETS]; NLint number = NL_MAX_GROUP_SOCKETS; NLint i; NLint count; switch (sock->type) { case NL_RELIABLE: case NL_UNRELIABLE: case NL_RELIABLE_PACKETS: default: { if(sock->connected == NL_TRUE) { /* check for broken connection */ if(sock->consock == NL_INVALID) { nlSetError(NL_CON_TERM); return NL_INVALID; } count = loopback_WritePacket(sock->consock, buffer, nbytes, sock->localport); } else if(sock->connecting == NL_TRUE) { nlSetError(NL_CON_PENDING); return NL_INVALID; } count = nbytes; nlGroupGetSockets(loopgroup, s, &number); for(i=0;i<number;i++) { if(nlIsValidSocket(s[i]) == NL_TRUE) { othersock = nlSockets[s[i]]; if(sock->remoteport == othersock->localport && othersock->connected == NL_FALSE && sock->type == othersock->type) { (void)loopback_WritePacket(s[i], buffer, nbytes, sock->localport); } } } } break; case NL_BROADCAST: { count = nbytes; nlGroupGetSockets(loopgroup, s, &number); for(i=0;i<number;i++) { if(nlIsValidSocket(s[i]) == NL_TRUE) { othersock = nlSockets[s[i]]; if(sock->localport == othersock->localport && sock->type == othersock->type) { (void)loopback_WritePacket(s[i], buffer, nbytes, sock->localport); } } } } } return count; }
// modified sock_Write of sock.c from HawkNL // returns true if socket is connected and data could be send static bool nlUpdateState(NLsocket socket) { if(nlIsValidSocket(socket) != NL_TRUE) return false; struct Unlocker { NLsocket socket; ~Unlocker() { nlUnlockSocket(socket, NL_BOTH); } Unlocker(NLsocket s) : socket(s) {} }; if(nlLockSocket(socket, NL_BOTH) == NL_FALSE) { return false; } Unlocker unlocker(socket); nl_socket_t *sock = nlSockets[socket]; NLint count = 0; if((sock->type == NL_RELIABLE) || (sock->type == NL_RELIABLE_PACKETS)) /* TCP */ { if(sock->connecting == NL_TRUE) { fd_set fdset; struct timeval t = {0,0}; int serrval = -1; socklen_t serrsize = (socklen_t)sizeof(serrval); FD_ZERO(&fdset); FD_SET((SOCKET)sock->realsocket, &fdset); if(select(sock->realsocket + 1, NULL, &fdset, NULL, &t) == 1) { /* Check the socket status */ (void)getsockopt( sock->realsocket, SOL_SOCKET, SO_ERROR, (char *)&serrval, &serrsize ); if(serrval != 0) { if(serrval == ECONNREFUSED) { nlSetError(NL_CON_REFUSED); } else if(serrval == EINPROGRESS || serrval == EWOULDBLOCK) { nlSetError(NL_CON_PENDING); } return false; } /* the connect has completed */ sock->connected = NL_TRUE; sock->connecting = NL_FALSE; } else { /* check for a failed connect */ FD_ZERO(&fdset); FD_SET((SOCKET)sock->realsocket, &fdset); if(select(sock->realsocket + 1, NULL, NULL, &fdset, &t) == 1) { nlSetError(NL_CON_REFUSED); } else { nlSetError(NL_CON_PENDING); } return false; } } /* check for reliable packets */ if(sock->type == NL_RELIABLE_PACKETS) { return true; } return true; } else /* unconnected UDP */ { /* check for a non-blocking connection pending */ if(sock->connecting == NL_TRUE) { nlSetError(NL_CON_PENDING); return false; } /* check for a connection error */ if(sock->conerror == NL_TRUE) { nlSetError(NL_CON_REFUSED); return false; } if(sock->type == NL_BROADCAST) { ((struct sockaddr_in *)&sock->addressin)->sin_addr.s_addr = INADDR_BROADCAST; } if(sock->type == NL_UDP_MULTICAST) { return true; } else if(sock->connected == NL_TRUE) { return true; } else { return true; } } if(count == SOCKET_ERROR) { return false; } return false; }