// Terminate the telnet console void telnetStop(JsNetwork *net) { printf("tnSrv: stopped sock=%d\n", tnSrv.sock); if (tnSrv.cliSock != 0) netCloseSocket(net, tnSrv.cliSock); tnSrv.cliSock = 0; if (tnSrv.sock != 0) netCloseSocket(net, tnSrv.sock); tnSrv.sock = 0; }
/** * @sa NET_DatagramSocketNew */ static struct datagram_socket* NET_DatagramSocketDoNew (const struct addrinfo* addr) { SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); int t = 1; const int index = NET_DatagramFindFreeSocket(); if (index == -1) { Com_Printf("Too many datagram sockets open\n"); return nullptr; } if (sock == INVALID_SOCKET) { Com_Printf("Failed to create socket: %s\n", netStringError(netError)); return nullptr; } if (!NET_SocketSetNonBlocking(sock)) { netCloseSocket(sock); return nullptr; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &t, sizeof(t)) != 0) { Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringError(netError)); netCloseSocket(sock); return nullptr; } if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof(t)) != 0) { Com_Printf("Failed to set SO_BROADCAST on socket: %s\n", netStringError(netError)); netCloseSocket(sock); return nullptr; } if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) { Com_Printf("Failed to bind socket: %s\n", netStringError(netError)); netCloseSocket(sock); return nullptr; } maxfd = std::max(sock + 1, maxfd); FD_SET(sock, &read_fds); datagram_socket* const s = Mem_PoolAllocType(datagram_socket, com_networkPool); s->family = addr->ai_family; s->addrlen = addr->ai_addrlen; s->socket = sock; s->index = index; s->queue = nullptr; s->queue_tail = &s->queue; s->func = nullptr; datagram_sockets[index] = s; return s; }
// Close the connection and release the console device void telnetRelease(JsNetwork *net) { if (!(tnSrv.sock && tnSrv.cliSock)) return; printf("tnSrv: released console from sock %d\n", tnSrv.cliSock); netCloseSocket(net, tnSrv.cliSock); tnSrv.cliSock = 0; if (!jsiIsConsoleDeviceForced()) jsiSetConsoleDevice(tnSrv.oldConsole, false); }
// Close the connection and release the console device void telnetRelease(JsNetwork *net) { if (!(tnSrv.sock && tnSrv.cliSock)) return; printf("tnSrv: released console from sock %d\n", tnSrv.cliSock); netCloseSocket(net, tnSrv.cliSock); tnSrv.cliSock = 0; jsiSetConsoleDevice( DEFAULT_CONSOLE_DEVICE ); }
void _socketConnectionKill(JsNetwork *net, JsVar *connection) { if (!net || networkState != NETWORKSTATE_ONLINE) return; int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt>=0) { netCloseSocket(net, sckt); jsvObjectSetChild(connection,HTTP_NAME_SOCKET,0); } }
// Close the connection and release the console device void telnetRelease(JsNetwork *net) { if (!(tnSrv.sock && tnSrv.cliSock)) return; printf("tnSrv: released console from sock %d\n", tnSrv.cliSock-1); netCloseSocket(net, tnSrv.cliSock-1); tnSrv.cliSock = 0; IOEventFlags console = jsiGetConsoleDevice(); // only switch away from telnet if the current console is TELNET, this allows the current // console to be set to something else while connected via telnet and then not have it // switched again when disconnecting from telnet if (console == EV_TELNET && !jsiIsConsoleDeviceForced()) jsiSetConsoleDevice(tnSrv.oldConsole, false); }
// Attempt to accept a connection, returns true if it did something bool telnetAccept(JsNetwork *net) { // we're gonna do a single accept per idle iteration for now if (tnSrv.sock == 0) return false; int sock = netAccept(net, tnSrv.sock); if (sock < 0) return false; // nothing // if we already have a client, then disconnect it if (tnSrv.cliSock != 0) { netCloseSocket(net, tnSrv.cliSock); } jsiSetConsoleDevice(EV_TELNET); tnSrv.cliSock = sock; printf("tnSrv: accepted console on sock=%d\n", sock); return true; }
/** * @sa NET_StreamFinished * @sa NET_StreamNew */ static void NET_StreamClose (struct net_stream* s) { if (!s || s->closed) return; if (s->socket != INVALID_SOCKET) { if (dbuffer_len(s->outbound)) Com_Printf("The outbound buffer for this socket (%d) is not empty\n", s->socket); else if (dbuffer_len(s->inbound)) Com_Printf("The inbound buffer for this socket (%d) is not empty\n", s->socket); FD_CLR(s->socket, &read_fds); FD_CLR(s->socket, &write_fds); netCloseSocket(s->socket); s->socket = INVALID_SOCKET; } if (s->index >= 0) streams[s->index] = nullptr; if (s->loopback_peer) { /* Detach the peer, so that it won't send us anything more */ s->loopback_peer->outbound = dbufferptr(); s->loopback_peer->loopback_peer = nullptr; } s->closed = true; Com_DPrintf(DEBUG_SERVER, "Close stream at index: %i\n", s->index); s->outbound = dbufferptr(); s->socket = INVALID_SOCKET; /* Note that this is potentially invalid after the callback returns */ if (s->finished) { s->inbound = dbufferptr(); if (s->onclose != nullptr) s->onclose(); Mem_Free(s); s = nullptr; } else if (s->func) { s->func(s); } if (s != nullptr && s->onclose != nullptr) s->onclose(); }
/** * @sa NET_DatagramSocketNew * @sa NET_DatagramSocketDoNew */ void NET_DatagramSocketClose (struct datagram_socket* s) { if (!s) return; FD_CLR(s->socket, &read_fds); FD_CLR(s->socket, &write_fds); netCloseSocket(s->socket); while (s->queue) { struct datagram* dgram = s->queue; s->queue = dgram->next; Mem_Free(dgram->msg); Mem_Free(dgram->addr); Mem_Free(dgram); } datagram_sockets[s->index] = nullptr; Mem_Free(s); }
// Attempt to accept a connection, returns true if it did something bool telnetAccept(JsNetwork *net) { // we're gonna do a single accept per idle iteration for now if (tnSrv.sock == 0) return false; int sock = netAccept(net, tnSrv.sock); if (sock < 0) return false; // nothing // if we already have a client, then disconnect it if (tnSrv.cliSock != 0) { netCloseSocket(net, tnSrv.cliSock); } // if the console is not already telnet, then change it IOEventFlags console = jsiGetConsoleDevice(); if (console != EV_TELNET) { tnSrv.oldConsole = console; if (!jsiIsConsoleDeviceForced()) jsiSetConsoleDevice(EV_TELNET, false); } tnSrv.cliSock = sock; printf("tnSrv: accepted console on sock=%d\n", sock); return true; }
static void do_accept (SOCKET sock) { const int index = NET_StreamGetFree(); if (index == -1) { Com_Printf("Too many streams open, rejecting inbound connection\n"); netCloseSocket(sock); return; } struct net_stream* s = NET_StreamNew(index); s->socket = sock; s->inbound = dbufferptr(new dbuffer(4096)); s->outbound = dbufferptr(new dbuffer(4096)); s->family = server_family; s->addrlen = server_addrlen; s->func = server_func; maxfd = std::max(sock + 1, maxfd); FD_SET(sock, &read_fds); server_func(s); /** @todo close stream? */ }
int main(int argc, char *argv[]) { int i, j; int thisSocket, thatSocket; int *confPort; unsigned int tmpSeconds; char *confConfig, *confEngine, *confPassword; char magicCookie[] = "TELLUTELLUTELLUTELLUTELLUTELLU"; size_t s; uid_t *thisUid; gid_t *thisGid; /* * * Initialize default values. * */ pMainMainInfo = &mainMainInfo; pMainThreadInfo = &mainThreadInfo; memset(pMainMainInfo, 0, sizeof(mainMainInfo)); memset(pMainThreadInfo, 0, sizeof(mainThreadInfo)); /* * * Read command line and parse configuration file. * */ cmdRead(argv, argc); if((confConfig = configFetch("config_file", &i)) != NULL) { if(configRead(confConfig) != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to read configuration file"); } } else { if(configRead(CONFIG_DEFAULT_FILE) != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to read configuration file"); } } cmdRead(argv, argc); nodeInitNames(); /* * * Initialize thread pool. * */ if((threadPool = malloc(sizeof(struct threadInfo) * THREAD_TELSKIND)) == NULL) { warningMessage(ERROR_FATAL, "Error occurred while trying to allocate memory for thread pool"); } memset(threadPool, 0, sizeof(struct threadInfo) * THREAD_TELSKIND); /* * * Initialize configurable subroutines. * */ pMainThreadInfo->threadReady = 1; for(j = 0; j < THREAD_TELSKIND; j++) { threadPool[j].threadReady = 1; threadPool[j].magicCookie = magicCookie; threadPool[j].pMainInfo = pMainMainInfo; if((confEngine = configFetch("storage_engine", &i)) != NULL) { if(strncasecmp(confEngine, "plain", strlen(confEngine)) == 0) { threadPool[j].dbInfo.connect = plainConnect; threadPool[j].dbInfo.disconnect = plainDisconnect; threadPool[j].dbInfo.escape = plainEscape; threadPool[j].dbInfo.push = plainPush; threadPool[j].dbInfo.pull = plainPull; threadPool[j].dbInfo.round = plainRound; threadPool[j].dbInfo.free = plainFree; threadPool[j].dbInfo.expire = plainExpire; threadPool[j].dbInfo.cookie = plainCookie; threadPool[j].dbInfo.insert = plainInsert; threadPool[j].dbInfo.login = plainLogin; threadPool[j].dbInfo.logout = plainLogout; threadPool[j].dbInfo.session = plainSession; threadPool[j].dbInfo.permission = plainPermission; pMainThreadInfo->dbInfo.connect = plainConnect; pMainThreadInfo->dbInfo.disconnect = plainDisconnect; pMainThreadInfo->dbInfo.escape = plainEscape; pMainThreadInfo->dbInfo.push = plainPush; pMainThreadInfo->dbInfo.pull = plainPull; pMainThreadInfo->dbInfo.round = plainRound; pMainThreadInfo->dbInfo.free = plainFree; pMainThreadInfo->dbInfo.expire = plainExpire; pMainThreadInfo->dbInfo.cookie = plainCookie; pMainThreadInfo->dbInfo.insert = plainInsert; pMainThreadInfo->dbInfo.login = plainLogin; pMainThreadInfo->dbInfo.logout = plainLogout; pMainThreadInfo->dbInfo.session = plainSession; pMainThreadInfo->dbInfo.permission = plainPermission; continue; } } threadPool[j].dbInfo.connect = mysqlConnect; threadPool[j].dbInfo.disconnect = mysqlDisconnect; threadPool[j].dbInfo.escape = mysqlEscape; threadPool[j].dbInfo.push = mysqlPush; threadPool[j].dbInfo.pull = mysqlPull; threadPool[j].dbInfo.round = mysqlRound; threadPool[j].dbInfo.free = mysqlFree; threadPool[j].dbInfo.expire = mysqlExpire; threadPool[j].dbInfo.cookie = mysqlCookie; threadPool[j].dbInfo.insert = mysqlInsert; threadPool[j].dbInfo.login = mysqlLogin; threadPool[j].dbInfo.logout = mysqlLogout; threadPool[j].dbInfo.session = mysqlSession; threadPool[j].dbInfo.permission = mysqlPermission; pMainThreadInfo->dbInfo.connect = mysqlConnect; pMainThreadInfo->dbInfo.disconnect = mysqlDisconnect; pMainThreadInfo->dbInfo.escape = mysqlEscape; pMainThreadInfo->dbInfo.push = mysqlPush; pMainThreadInfo->dbInfo.pull = mysqlPull; pMainThreadInfo->dbInfo.round = mysqlRound; pMainThreadInfo->dbInfo.free = mysqlFree; pMainThreadInfo->dbInfo.expire = mysqlExpire; pMainThreadInfo->dbInfo.cookie = mysqlCookie; pMainThreadInfo->dbInfo.insert = mysqlInsert; pMainThreadInfo->dbInfo.login = mysqlLogin; pMainThreadInfo->dbInfo.logout = mysqlLogout; pMainThreadInfo->dbInfo.session = mysqlSession; pMainThreadInfo->dbInfo.permission = mysqlPermission; } /* * * Initialize magick cookie. * */ if((confPassword = configFetch("agent_password", &i)) != NULL) { s = strlen(confPassword); if(s > DATA_COOKIE_SIZE) { s = DATA_COOKIE_SIZE; } strncpy(magicCookie, confPassword, s); } /* * * Initialize main thread. * */ configSetUmask(0077); if(configSetLocale(CONFIG_DEFAULT_LOCALE) != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to set default locale"); } if(configChangeRoot(CONFIG_DEFAULT_ROOT) != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to change root directory"); } if(configCloseInput() != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to close standard input"); } if(configDaemonize() != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to daemonize process"); } threadStack(THREAD_TELSKIND); beginProcess(pMainThreadInfo); /* * * Initialize timer. * */ if(timerInit(TIMER_RESOLUTION_STATUS, 0, timerStatThreads) != 0) { warningMessage(ERROR_FATAL, "Error occurred while trying to initialize timer"); } /* * * Initialize worker threads. * */ pMainMainInfo->allRunning = 0; pMainMainInfo->theEnd = 0; pMainMainInfo->threadEnd = 0; for(i = 0; i < THREAD_TELSKIND; i++) { if(threadInit(&threadPool[i], workerThread, &threadPool[i]) != 0) { warningMessage(ERROR_FATAL, "Error occurred while trying to initialize worker thread"); } j = 0; while(threadPool[i].threadReady != 0) { timerWait(&tmpSeconds, 0, THREAD_AGAIN); if(j == 10000 || j == 20000 || j == 30000 || j == 40000 || j == 50000) { warningMessage(ERROR_SLIGHT, "Waiting for worker threads to start taking too long, still waiting"); } else if(j >= 60000) { warningMessage(ERROR_FATAL, "Waiting for threads to start taking too long"); } j++; } } /* * * Create socket to listen. * */ if((confPort = configFetch("listen_port", &i)) != NULL) { thisSocket = netCreateListenSocket(*confPort); } else { thisSocket = netCreateListenSocket(CONFIG_DEFAULT_PORT); } /* * * Create process id, shm segment, switch user and group id's. * */ pidCreate(); shmCreate(DAEMON_TELSKIND, THREAD_TELSKIND); if((thisUid = configFetch("user_id", &i)) != NULL) { if(*thisUid != -1) { if(uidSwitch(*thisUid) != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to change user id"); } } } if((thisGid = configFetch("group_id", &i)) != NULL) { if(*thisGid != -1) { if(gidSwitch(*thisGid) != 0) { warningMessage(ERROR_SLIGHT, "Error occurred while trying to change group id"); } } } /* * * Serve connected clients. * */ startProcess(THREAD_TELSKIND); pMainMainInfo->allRunning++; pMainMainInfo->rushThreadCounter = 0; while(pMainMainInfo->theEnd == 0) { thatSocket = netWaitConnection(thisSocket, pMainThreadInfo); if(pMainMainInfo->theEnd != 0) { break; } mainLoop: for(i = 0; i < THREAD_TELSKIND; i++) { if(threadPool[i].threadReady == 0) { threadPool[i].threadReady++; threadPool[i].threadSocket = thatSocket; shmUpdate(i, DAEMON_TELSKIND); if(threadWake(&threadPool[i]) != 0) { netCloseSocket(threadPool[i].threadSocket); threadPool[i].threadReady = 0; threadPool[i].threadSocket = 0; warningMessage(ERROR_SLIGHT, "Error occurred while trying to start worker thread"); } pMainMainInfo->rushThreadCounter = 0; break; } } if(i == THREAD_TELSKIND) { if(pMainMainInfo->rushThreadCounter > 1000) { pMainMainInfo->rushThreadCounter = 0; warningMessage(ERROR_SLIGHT, "No free worker thread available, dropping agent"); netCloseSocket(thatSocket); } else { pMainMainInfo->rushThreadCounter++; if(pMainMainInfo->rushThreadCounter < 2) { warningMessage(ERROR_SLIGHT, "No free worker thread available, it may be necessary to enlarge the thread pool"); } timerWait(&tmpSeconds, 0, THREAD_AGAIN); goto mainLoop; } } } /* * * Free allocated resources and terminate program. * */ netCloseSocket(thisSocket); pMainMainInfo->threadEnd++; if(timerInit(TIMER_RESOLUTION_THREADSTOP, 0, timerStopThreads) == 0) { pMainMainInfo->timerThreadAccess = 1; for(i = 0; i < THREAD_TELSKIND; i++) { threadWake(&threadPool[i]); } pMainMainInfo->timerThreadAccess = 0; } else { for(i = 0; i < THREAD_TELSKIND; i++) { threadKill(&threadPool[i]); } } timesProcess(pMainThreadInfo); pidRemove(); shmRemove(DAEMON_TELSKIND); free(threadPool); exitProcess(0); exit(0); }
static int NET_DoStartServer (const struct addrinfo* addr) { SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); int t = 1; if (sock == INVALID_SOCKET) { Com_Printf("Failed to create socket: %s\n", netStringError(netError)); return INVALID_SOCKET; } if (!NET_SocketSetNonBlocking(sock)) { netCloseSocket(sock); return INVALID_SOCKET; } #ifdef _WIN32 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &t, sizeof(t)) != 0) { #else if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(t)) != 0) { #endif Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringError(netError)); netCloseSocket(sock); return INVALID_SOCKET; } if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) { Com_Printf("Failed to bind socket: %s\n", netStringError(netError)); netCloseSocket(sock); return INVALID_SOCKET; } if (listen(sock, SOMAXCONN) != 0) { Com_Printf("Failed to listen on socket: %s\n", netStringError(netError)); netCloseSocket(sock); return INVALID_SOCKET; } maxfd = std::max(sock + 1, maxfd); FD_SET(sock, &read_fds); server_family = addr->ai_family; server_addrlen = addr->ai_addrlen; return sock; } static struct addrinfo* NET_GetAddrinfoForNode (const char* node, const char* service) { struct addrinfo* res; struct addrinfo hints; int rc; OBJZERO(hints); hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; /* force ipv4 */ if (net_ipv4->integer) hints.ai_family = AF_INET; rc = getaddrinfo(node, service, &hints, &res); if (rc != 0) { Com_Printf("Failed to resolve host %s:%s: %s\n", node ? node : "*", service, gai_strerror(rc)); return nullptr; } return res; } /** * @sa NET_DoStartServer * @param[in] node The node to start the server with * @param[in] service If this is nullptr we are in single player mode * @param[in] func The server callback function to read the packets * @sa SV_ReadPacket * @sa server_func * @sa SV_Stop */ bool SV_Start (const char* node, const char* service, stream_callback_func* func) { if (!func) return false; if (server_running) { Com_Printf("SV_Start: Server is still running - call SV_Stop before\n"); return false; } if (service) { struct addrinfo* res = NET_GetAddrinfoForNode(node, service); server_socket = NET_DoStartServer(res); if (server_socket == INVALID_SOCKET) { Com_Printf("Failed to start server on %s:%s\n", node ? node : "*", service); } else { server_running = true; server_func = func; } freeaddrinfo(res); } else { /* Loopback server only */ server_running = true; server_func = func; } return server_running; } /** * @sa SV_Start */ void SV_Stop (void) { server_running = false; server_func = nullptr; if (server_socket != INVALID_SOCKET) { FD_CLR(server_socket, &read_fds); netCloseSocket(server_socket); } server_socket = INVALID_SOCKET; }
static struct net_stream* NET_DoConnect (const char* node, const char* service, const struct addrinfo* addr, int i, stream_onclose_func* onclose) { struct net_stream* s; SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (sock == INVALID_SOCKET) { Com_Printf("Failed to create socket: %s\n", netStringError(netError)); return nullptr; } if (!NET_SocketSetNonBlocking(sock)) { netCloseSocket(sock); return nullptr; } if (connect(sock, addr->ai_addr, addr->ai_addrlen) != 0) { const int err = netError; #ifdef _WIN32 if (err != WSAEWOULDBLOCK) { #else if (err != EINPROGRESS) { #endif Com_Printf("Failed to start connection to %s:%s: %s\n", node, service, netStringError(err)); netCloseSocket(sock); return nullptr; } } s = NET_StreamNew(i); s->socket = sock; s->inbound = dbufferptr(new dbuffer(4096)); s->outbound = dbufferptr(new dbuffer(4096)); s->family = addr->ai_family; s->addrlen = addr->ai_addrlen; s->onclose = onclose; maxfd = std::max(sock + 1, maxfd); FD_SET(sock, &read_fds); return s; } /** * @brief Try to connect to a given host on a given port * @param[in] node The host to connect to * @param[in] service The port to connect to * @param[in] onclose The callback that is called on closing the returned stream. This is useful if * you hold the pointer for the returned stream anywhere else and would like to get notified once * this pointer is invalid. * @sa NET_DoConnect * @sa NET_ConnectToLoopBack * @todo What about a timeout */ struct net_stream* NET_Connect (const char* node, const char* service, stream_onclose_func* onclose) { struct addrinfo* res; struct addrinfo hints; int rc; struct net_stream* s = nullptr; int index; OBJZERO(hints); hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; hints.ai_socktype = SOCK_STREAM; /* force ipv4 */ if (net_ipv4->integer) hints.ai_family = AF_INET; rc = getaddrinfo(node, service, &hints, &res); if (rc != 0) { Com_Printf("Failed to resolve host %s:%s: %s\n", node, service, gai_strerror(rc)); return nullptr; } index = NET_StreamGetFree(); if (index == -1) { Com_Printf("Failed to connect to host %s:%s, too many streams open\n", node, service); freeaddrinfo(res); return nullptr; } s = NET_DoConnect(node, service, res, index, onclose); freeaddrinfo(res); return s; } /** * @param[in] onclose The callback that is called on closing the returned stream. This is useful if * you hold the pointer for the returned stream anywhere else and would like to get notified once * this pointer is invalid. * @sa NET_Connect */ struct net_stream* NET_ConnectToLoopBack (stream_onclose_func* onclose) { struct net_stream* client, *server; int server_index, client_index; if (!server_running) return nullptr; server_index = NET_StreamGetFree(); client_index = NET_StreamGetFree(); if (server_index == -1 || client_index == -1 || server_index == client_index) { Com_Printf("Failed to connect to loopback server, too many streams open\n"); return nullptr; } client = NET_StreamNew(client_index); client->loopback = true; client->inbound = dbufferptr(new dbuffer(4096)); client->outbound = dbufferptr(new dbuffer(4096)); client->onclose = onclose; server = NET_StreamNew(server_index); server->loopback = true; server->inbound = client->outbound; server->outbound = client->inbound; server->func = server_func; server->onclose = nullptr; client->loopback_peer = server; server->loopback_peer = client; server_func(server); return client; } /** * @brief Enqueue a network message into a stream * @sa NET_StreamDequeue */ void NET_StreamEnqueue (struct net_stream* s, const char* data, int len) { if (len <= 0 || !s || s->closed || s->finished) return; if (s->outbound) { const ScopedMutex scopedMutex(netMutex); s->outbound->add(data, len); } /* on linux, socket is int, and INVALID_SOCKET -1 * on windows it is unsigned and INVALID_SOCKET (~0) * Let's hope that checking for INVALID_SOCKET is good enough for linux. */ //if (s->socket >= 0) if (s->socket != INVALID_SOCKET) FD_SET(s->socket, &write_fds); if (s->loopback_peer) { loopback_ready = true; s->loopback_peer->ready = true; } } /** * @brief Returns the length of the waiting inbound buffer */ static int NET_StreamPeek (struct net_stream* s, char* data, int len) { if (len <= 0 || !s) return 0; dbufferptr& dbuf = s->inbound; if ((s->closed || s->finished) && dbuffer_len(dbuf) == 0) return 0; return dbuf->get(data, len); } /** * @sa NET_StreamEnqueue */ int NET_StreamDequeue (struct net_stream* s, char* data, int len) { if (len <= 0 || !s || s->finished) return 0; return s->inbound->extract(data, len); } /** * @brief Reads messages from the network channel and adds them to the dbuffer * where you can use the NET_Read* functions to get the values in the correct * order * @sa NET_StreamDequeue */ dbuffer* NET_ReadMsg (struct net_stream* s) { unsigned int v; const ScopedMutex scopedMutex(netMutex); if (NET_StreamPeek(s, (char*)&v, 4) < 4) return nullptr; int len = LittleLong(v); if (NET_StreamGetLength(s) < (4 + len)) return nullptr; char tmp[256]; const int size = sizeof(tmp); NET_StreamDequeue(s, tmp, 4); dbuffer* buf = new dbuffer(); while (len > 0) { const int x = NET_StreamDequeue(s, tmp, std::min(len, size)); buf->add(tmp, x); len -= x; } return buf; }
void onError(int socket) { printf("onError: socket %i\n", socket); netCloseSocket(socket); }