示例#1
0
// 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;
}
示例#2
0
文件: net.cpp 项目: Isaacssv552/ufoai
/**
 * @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;
}
示例#3
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);
  netCloseSocket(net, tnSrv.cliSock);
  tnSrv.cliSock = 0;
  if (!jsiIsConsoleDeviceForced()) jsiSetConsoleDevice(tnSrv.oldConsole, false);
}
示例#4
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);
  netCloseSocket(net, tnSrv.cliSock);
  tnSrv.cliSock = 0;
  jsiSetConsoleDevice( DEFAULT_CONSOLE_DEVICE );
}
示例#5
0
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);
    }
}
示例#6
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);
}
示例#7
0
// 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;
}
示例#8
0
文件: net.cpp 项目: Isaacssv552/ufoai
/**
 * @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();
}
示例#9
0
文件: net.cpp 项目: Isaacssv552/ufoai
/**
 * @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);
}
示例#10
0
// 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;
}
示例#11
0
文件: net.cpp 项目: ufoai/ufoai
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? */
}
示例#12
0
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);
}
示例#13
0
文件: net.cpp 项目: Isaacssv552/ufoai
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;
}
示例#14
0
文件: net.cpp 项目: Isaacssv552/ufoai
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;
}
示例#15
0
void onError(int socket)
{
	printf("onError: socket %i\n", socket);
	netCloseSocket(socket);
}