36 #include <SDL_thread.h>
38 #include "../ports/system.h"
40 #include "../shared/scopedmutex.h"
42 #define MAX_STREAMS 56
43 #define MAX_DATAGRAM_SOCKETS 7
46 # include <winsock2.h>
47 # include <ws2tcpip.h>
53 # define netError WSAGetLastError()
54 # define netStringError netStringErrorWin
55 # define netCloseSocket closesocket
56 # define gai_strerrorA netStringErrorWin
60 # include <sys/ioctl.h>
61 # include <sys/select.h>
62 # include <sys/types.h>
63 # include <sys/socket.h>
64 # include <sys/time.h>
66 # include <arpa/inet.h>
67 # include <netinet/in.h>
70 # define INVALID_SOCKET (-1)
71 # define netError errno
72 # define netStringError strerror
73 # define netCloseSocket close
74 # define ioctlsocket ioctl
78 #define NI_NUMERICHOST 0
79 #define NI_NUMERICSERV 0
82 #define AI_NUMERICHOST 0
83 #define INADDR_BROADCAST 0
91 #ifndef AI_NUMERICSERV
92 #define AI_NUMERICSERV 0
95 #define AI_ADDRCONFIG 0
102 #define NET_MULTICAST_IP6 "ff04::696f:7175:616b:6533"
104 #define dbuffer_len(dbuf) (dbuf ? (dbuf)->length() : 0)
161 static const char* netStringErrorWin (
int code)
164 case WSAEINTR:
return "WSAEINTR";
165 case WSAEBADF:
return "WSAEBADF";
166 case WSAEACCES:
return "WSAEACCES";
167 case WSAEDISCON:
return "WSAEDISCON";
168 case WSAEFAULT:
return "WSAEFAULT";
169 case WSAEINVAL:
return "WSAEINVAL";
170 case WSAEMFILE:
return "WSAEMFILE";
171 case WSAEWOULDBLOCK:
return "WSAEWOULDBLOCK";
172 case WSAEINPROGRESS:
return "WSAEINPROGRESS";
173 case WSAEALREADY:
return "WSAEALREADY";
174 case WSAENOTSOCK:
return "WSAENOTSOCK";
175 case WSAEDESTADDRREQ:
return "WSAEDESTADDRREQ";
176 case WSAEMSGSIZE:
return "WSAEMSGSIZE";
177 case WSAEPROTOTYPE:
return "WSAEPROTOTYPE";
178 case WSAENOPROTOOPT:
return "WSAENOPROTOOPT";
179 case WSAEPROTONOSUPPORT:
return "WSAEPROTONOSUPPORT";
180 case WSAESOCKTNOSUPPORT:
return "WSAESOCKTNOSUPPORT";
181 case WSAEOPNOTSUPP:
return "WSAEOPNOTSUPP";
182 case WSAEPFNOSUPPORT:
return "WSAEPFNOSUPPORT";
183 case WSAEAFNOSUPPORT:
return "WSAEAFNOSUPPORT";
184 case WSAEADDRINUSE:
return "WSAEADDRINUSE";
185 case WSAEADDRNOTAVAIL:
return "WSAEADDRNOTAVAIL";
186 case WSAENETDOWN:
return "WSAENETDOWN";
187 case WSAENETUNREACH:
return "WSAENETUNREACH";
188 case WSAENETRESET:
return "WSAENETRESET";
189 case WSAEHOSTDOWN:
return "WSAEHOSTDOWN";
190 case WSAEHOSTUNREACH:
return "WSAEHOSTUNREACH";
191 case WSAECONNABORTED:
return "WSWSAECONNABORTEDAEINTR";
192 case WSAECONNRESET:
return "WSAECONNRESET";
193 case WSAENOBUFS:
return "WSAENOBUFS";
194 case WSAEISCONN:
return "WSAEISCONN";
195 case WSAENOTCONN:
return "WSAENOTCONN";
196 case WSAESHUTDOWN:
return "WSAESHUTDOWN";
197 case WSAETOOMANYREFS:
return "WSAETOOMANYREFS";
198 case WSAETIMEDOUT:
return "WSAETIMEDOUT";
199 case WSAECONNREFUSED:
return "WSAECONNREFUSED";
200 case WSAELOOP:
return "WSAELOOP";
201 case WSAENAMETOOLONG:
return "WSAENAMETOOLONG";
202 case WSASYSNOTREADY:
return "WSASYSNOTREADY";
203 case WSAVERNOTSUPPORTED:
return "WSAVERNOTSUPPORTED";
204 case WSANOTINITIALISED:
return "WSANOTINITIALISED";
205 case WSAHOST_NOT_FOUND:
return "WSAHOST_NOT_FOUND";
206 case WSATRY_AGAIN:
return "WSATRY_AGAIN";
207 case WSANO_RECOVERY:
return "WSANO_RECOVERY";
208 case WSANO_DATA:
return "WSANO_DATA";
209 default:
return "NO ERROR";
227 static int start = 0;
230 const int pos = (
i + start) % MAX_STREAMS;
231 if (streams[pos] ==
nullptr) {
232 start = (pos + 1) % MAX_STREAMS;
245 static int start = 0;
248 const int pos = (
i + start) % MAX_DATAGRAM_SOCKETS;
249 if (datagram_sockets[pos] ==
nullptr) {
250 start = (pos + 1) % MAX_DATAGRAM_SOCKETS;
290 if (streams[
i] ==
nullptr)
298 Com_Printf(
"%i/%i streams opened\n", cnt, MAX_STREAMS);
307 Com_Printf(
"\n----- network initialization -------\n");
311 if (WSAStartup(MAKEWORD(2, 0), &winsockdata) != 0)
320 streams[
i] =
nullptr;
322 datagram_sockets[
i] =
nullptr;
325 signal(SIGPIPE, SIG_IGN);
357 Com_Printf(
"The outbound buffer for this socket (%d) is not empty\n", s->
socket);
359 Com_Printf(
"The inbound buffer for this socket (%d) is not empty\n", s->
socket);
367 streams[s->
index] =
nullptr;
388 }
else if (s->
func) {
392 if (s !=
nullptr && s->
onclose !=
nullptr)
400 Com_Printf(
"Too many streams open, rejecting inbound connection\n");
429 fd_set write_fds_out;
431 memcpy(&read_fds_out, &
read_fds,
sizeof(read_fds_out));
432 memcpy(&write_fds_out, &
write_fds,
sizeof(write_fds_out));
439 tv.tv_sec = timeout / 1000;
440 tv.tv_usec = 1000 * (timeout % 1000);
442 if (read_fds_out.fd_count == 0) {
447 ready = select(
maxfd, &read_fds_out, &write_fds_out,
nullptr, &tv);
488 if (FD_ISSET(s->
socket, &write_fds_out)) {
504 len = send(s->
socket, buf, len, 0);
518 if (FD_ISSET(s->
socket, &read_fds_out)) {
520 const int len = recv(s->
socket, buf,
sizeof(buf), 0);
550 if (FD_ISSET(s->
socket, &write_fds_out)) {
568 if (FD_ISSET(s->
socket, &read_fds_out)) {
571 socklen_t addrlen =
sizeof(addrbuf);
572 const int len = recvfrom(s->
socket, buf,
sizeof(buf), 0, (
struct sockaddr* )addrbuf, &addrlen);
576 s->
func(s, buf, len, (
struct sockaddr* )addrbuf);
587 Com_Printf(
"ioctl FIONBIO failed: %s\n", strerror(errno));
595 SOCKET sock =
socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
606 if (connect(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
609 if (err != WSAEWOULDBLOCK) {
611 if (err != EINPROGRESS) {
623 s->
family = addr->ai_family;
646 struct addrinfo* res;
647 struct addrinfo hints;
651 hints.ai_socktype = SOCK_STREAM;
654 hints.ai_family = AF_INET;
656 const int rc = getaddrinfo(node, service, &hints, &res);
658 Com_Printf(
"Failed to resolve host %s:%s: %s\n", node, service, gai_strerror(rc));
664 Com_Printf(
"Failed to connect to host %s:%s, too many streams open\n", node, service);
689 if (server_index == -1 || client_index == -1 || server_index == client_index) {
690 Com_Printf(
"Failed to connect to loopback server, too many streams open\n");
754 return dbuf->
get(data, len);
787 const int size =
sizeof(tmp);
802 return s ? s->
data :
nullptr;
861 static char node[64];
878 return "loopback connection";
882 if (getpeername(s->
socket, (
struct sockaddr* )buf, &addrlen) != 0)
887 const int rc = getnameinfo((
struct sockaddr* )buf, addrlen, node,
sizeof(node), service,
sizeof(service),
888 NI_NUMERICHOST | NI_NUMERICSERV);
890 Com_Printf(
"Failed to convert sockaddr to string: %s\n", gai_strerror(rc));
896 node[
sizeof(node) - 1] =
'\0';
897 service[
sizeof(service) - 1] =
'\0';
917 SOCKET sock =
socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
931 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (
char*) &t,
sizeof(t)) != 0) {
933 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &t,
sizeof(t)) != 0) {
940 if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
946 if (listen(sock, SOMAXCONN) != 0) {
962 struct addrinfo* res;
963 struct addrinfo hints;
967 hints.ai_socktype = SOCK_STREAM;
970 hints.ai_family = AF_INET;
972 const int rc = getaddrinfo(node, service, &hints, &res);
974 Com_Printf(
"Failed to resolve host %s:%s: %s\n", node ? node :
"*", service, gai_strerror(rc));
996 Com_Printf(
"SV_Start: Server is still running - call SV_Stop before\n");
1008 Com_Printf(
"Failed to start server on %s:%s\n", node ? node :
"*", service);
1045 Com_Printf(
"Too many datagram sockets open\n");
1049 SOCKET sock =
socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
1060 int socketOptTrue = 1;
1061 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (
char*) &socketOptTrue,
sizeof(socketOptTrue)) != 0) {
1067 if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (
char*) &socketOptTrue,
sizeof(socketOptTrue)) != 0) {
1073 if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) {
1083 s->
family = addr->ai_family;
1084 s->
addrlen = addr->ai_addrlen;
1090 datagram_sockets[
index] = s;
1104 struct addrinfo* res;
1105 struct addrinfo hints;
1107 if (!service || !func)
1112 hints.ai_socktype = SOCK_DGRAM;
1115 hints.ai_family = AF_INET;
1117 const int rc = getaddrinfo(node, service, &hints, &res);
1120 Com_Printf(
"Failed to resolve host %s:%s: %s\n", node ? node :
"*", service, gai_strerror(rc));
1137 if (!s || len <= 0 || !buf || !to)
1143 memcpy(dgram->
msg, buf, len);
1144 memcpy(dgram->
addr, to, len);
1146 dgram->
next =
nullptr;
1161 if (s->
family == AF_INET) {
1162 struct sockaddr_in addr;
1163 addr.sin_family = AF_INET;
1164 addr.sin_port = htons(port);
1165 addr.sin_addr.s_addr = INADDR_BROADCAST;
1167 }
else if (s->
family == AF_INET6) {
1168 struct sockaddr_in addr;
1169 addr.sin_family = AF_INET6;
1170 addr.sin_port = htons(port);
1171 addr.sin_addr.s_addr = INADDR_BROADCAST;
1199 datagram_sockets[s->
index] =
nullptr;
1214 const int rc = getnameinfo(addr, s->
addrlen, node, nodelen, service, servicelen,
1215 NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM);
1217 Com_Printf(
"Failed to convert sockaddr to string: %s\n", gai_strerror(rc));
1225 char* service = inet_ntoa(((
struct sockaddr_in*)addr->ai_addr)->sin_addr);
1232 if (addrinfo ==
nullptr) {
1237 freeaddrinfo(addrinfo);
int NET_StreamDequeue(struct net_stream *s, char *data, int len)
static SOCKET server_socket
void NET_StreamFinished(struct net_stream *s)
Call NET_StreamFinished to mark the stream as uninteresting, but to finish sending any data in the bu...
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
void NET_SockaddrToStrings(struct datagram_socket *s, struct sockaddr *addr, char *node, size_t nodelen, char *service, size_t servicelen)
Convert sockaddr to string.
static struct net_stream * NET_StreamNew(int index)
struct net_stream * NET_Connect(const char *node, const char *service, stream_onclose_func *onclose)
Try to connect to a given host on a given port.
stream_callback_func * func
struct datagram_socket * NET_DatagramSocketNew(const char *node, const char *service, datagram_callback_func *func)
Opens a datagram socket (UDP)
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
static void NET_ShowStreams_f(void)
void NET_Wait(int timeout)
bool SV_Start(const char *node, const char *service, stream_callback_func *func)
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
struct net_stream * NET_ConnectToLoopBack(stream_onclose_func *onclose)
struct net_stream * loopback_peer
static struct datagram_socket * NET_DatagramSocketDoNew(const struct addrinfo *addr)
static stream_callback_func * server_func
#define dbuffer_len(dbuf)
void NET_StreamFree(struct net_stream *s)
Call NET_StreamFree to dump the whole thing right now.
void Com_Printf(const char *const fmt,...)
const char * NET_StreamToString(struct net_stream *s)
Returns the numerical representation of a net_stream.
stream_onclose_func * onclose
static int NET_DoStartServer(const struct addrinfo *addr)
static int NET_DatagramFindFreeSocket(void)
static int NET_StreamGetFree(void)
static struct net_stream * streams[MAX_STREAMS]
struct datagram ** queue_tail
#define MAX_DATAGRAM_SOCKETS
static void NET_StreamClose(struct net_stream *s)
void * NET_StreamGetData(struct net_stream *s)
void Com_Error(int code, const char *fmt,...)
void NET_DatagramSend(struct datagram_socket *s, const char *buf, int len, struct sockaddr *to)
void Sys_Sleep(int milliseconds)
Calls the win32 sleep function.
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
void add(const char *, size_t)
static int server_addrlen
void stream_callback_func(struct net_stream *s)
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
void NET_StreamEnqueue(struct net_stream *s, const char *data, int len)
Enqueue a network message into a stream.
SharedPtr< dbuffer > dbufferptr
size_t extract(char *, size_t)
Read and delete data from a dbuffer.
static struct net_stream * NET_DoConnect(const char *node, const char *service, const struct addrinfo *addr, int i, stream_onclose_func *onclose)
void stream_onclose_func()
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
void datagram_callback_func(struct datagram_socket *s, const char *buf, int len, struct sockaddr *from)
static bool loopback_ready
static struct datagram_socket * datagram_sockets[MAX_DATAGRAM_SOCKETS]
#define Mem_PoolAllocTypeN(type, n, pool)
void NET_StreamSetData(struct net_stream *s, void *data)
static struct addrinfo * NET_GetAddrinfoForNode(const char *node, const char *service)
static int NET_StreamPeek(struct net_stream *s, char *data, int len)
Returns the length of the waiting inbound buffer.
size_t get(char *, size_t) const
Read data from a dbuffer.
memPool_t * com_networkPool
QGL_EXTERN GLuint GLchar GLuint * len
void NET_DatagramBroadcast(struct datagram_socket *s, const char *buf, int len, int port)
void NET_DatagramSocketClose(struct datagram_socket *s)
static void do_accept(SOCKET sock)
size_t remove(size_t)
Deletes data from a dbuffer.
definitions common between client and server, but not game lib
bool NET_StreamIsLoopback(struct net_stream *s)
dbuffer * NET_ReadMsg(struct net_stream *s)
Reads messages from the network channel and adds them to the dbuffer where you can use the NET_Read* ...
GLsizei const GLvoid * data
const char * NET_StreamPeerToName(struct net_stream *s, char *dst, int len, bool appendPort)
#define Mem_PoolAllocType(type, pool)
static SDL_mutex * netMutex
void NET_StreamSetCallback(struct net_stream *s, stream_callback_func *func)
datagram_callback_func * func
static bool NET_SocketSetNonBlocking(SOCKET socketNum)
QGL_EXTERN int GLboolean GLfloat * v
static bool server_running
static void NET_AddrinfoToString(const struct addrinfo *addr, char *buf, size_t bufLength)
bool NET_ResolvNode(const char *node, char *buf, size_t bufLength)
static int NET_StreamGetLength(struct net_stream *s)