1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// net_wins.c 21 22#include "quakedef.h" 23#include "winquake.h" 24 25extern cvar_t hostname; 26 27#define MAXHOSTNAMELEN 256 28 29static int net_acceptsocket = -1; // socket for fielding new connections 30static int net_controlsocket; 31static int net_broadcastsocket = 0; 32static struct qsockaddr broadcastaddr; 33 34static unsigned long myAddr; 35 36qboolean winsock_lib_initialized; 37 38int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); 39int (PASCAL FAR *pWSACleanup)(void); 40int (PASCAL FAR *pWSAGetLastError)(void); 41SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); 42int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); 43int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, 44 const char FAR * optval, int optlen); 45int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, 46 struct sockaddr FAR *from, int FAR * fromlen); 47int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, 48 const struct sockaddr FAR *to, int tolen); 49int (PASCAL FAR *pclosesocket)(SOCKET s); 50int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); 51struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); 52struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, 53 int len, int type); 54int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, 55 int FAR * namelen); 56 57#include "net_wins.h" 58 59int winsock_initialized = 0; 60WSADATA winsockdata; 61 62//============================================================================= 63 64static double blocktime; 65 66BOOL PASCAL FAR BlockingHook(void) 67{ 68 MSG msg; 69 BOOL ret; 70 71 if ((Sys_FloatTime() - blocktime) > 2.0) 72 { 73 WSACancelBlockingCall(); 74 return FALSE; 75 } 76 77 /* get the next message, if any */ 78 ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); 79 80 /* if we got one, process it */ 81 if (ret) { 82 TranslateMessage(&msg); 83 DispatchMessage(&msg); 84 } 85 86 /* TRUE if we got a message */ 87 return ret; 88} 89 90 91void WINS_GetLocalAddress() 92{ 93 struct hostent *local = NULL; 94 char buff[MAXHOSTNAMELEN]; 95 unsigned long addr; 96 97 if (myAddr != INADDR_ANY) 98 return; 99 100 if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) 101 return; 102 103 blocktime = Sys_FloatTime(); 104 WSASetBlockingHook(BlockingHook); 105 local = pgethostbyname(buff); 106 WSAUnhookBlockingHook(); 107 if (local == NULL) 108 return; 109 110 myAddr = *(int *)local->h_addr_list[0]; 111 112 addr = ntohl(myAddr); 113 sprintf(my_tcpip_address, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); 114} 115 116 117int WINS_Init (void) 118{ 119 int i; 120 char buff[MAXHOSTNAMELEN]; 121 char *p; 122 int r; 123 WORD wVersionRequested; 124 HINSTANCE hInst; 125 126// initialize the Winsock function vectors (we do this instead of statically linking 127// so we can run on Win 3.1, where there isn't necessarily Winsock) 128 hInst = LoadLibrary("wsock32.dll"); 129 130 if (hInst == NULL) 131 { 132 Con_SafePrintf ("Failed to load winsock.dll\n"); 133 winsock_lib_initialized = false; 134 return -1; 135 } 136 137 winsock_lib_initialized = true; 138 139 pWSAStartup = (void *)GetProcAddress(hInst, "WSAStartup"); 140 pWSACleanup = (void *)GetProcAddress(hInst, "WSACleanup"); 141 pWSAGetLastError = (void *)GetProcAddress(hInst, "WSAGetLastError"); 142 psocket = (void *)GetProcAddress(hInst, "socket"); 143 pioctlsocket = (void *)GetProcAddress(hInst, "ioctlsocket"); 144 psetsockopt = (void *)GetProcAddress(hInst, "setsockopt"); 145 precvfrom = (void *)GetProcAddress(hInst, "recvfrom"); 146 psendto = (void *)GetProcAddress(hInst, "sendto"); 147 pclosesocket = (void *)GetProcAddress(hInst, "closesocket"); 148 pgethostname = (void *)GetProcAddress(hInst, "gethostname"); 149 pgethostbyname = (void *)GetProcAddress(hInst, "gethostbyname"); 150 pgethostbyaddr = (void *)GetProcAddress(hInst, "gethostbyaddr"); 151 pgetsockname = (void *)GetProcAddress(hInst, "getsockname"); 152 153 if (!pWSAStartup || !pWSACleanup || !pWSAGetLastError || 154 !psocket || !pioctlsocket || !psetsockopt || 155 !precvfrom || !psendto || !pclosesocket || 156 !pgethostname || !pgethostbyname || !pgethostbyaddr || 157 !pgetsockname) 158 { 159 Con_SafePrintf ("Couldn't GetProcAddress from winsock.dll\n"); 160 return -1; 161 } 162 163 if (COM_CheckParm ("-noudp")) 164 return -1; 165 166 if (winsock_initialized == 0) 167 { 168 wVersionRequested = MAKEWORD(1, 1); 169 170 r = pWSAStartup (MAKEWORD(1, 1), &winsockdata); 171 172 if (r) 173 { 174 Con_SafePrintf ("Winsock initialization failed.\n"); 175 return -1; 176 } 177 } 178 winsock_initialized++; 179 180 // determine my name 181 if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) 182 { 183 Con_DPrintf ("Winsock TCP/IP Initialization failed.\n"); 184 if (--winsock_initialized == 0) 185 pWSACleanup (); 186 return -1; 187 } 188 189 // if the quake hostname isn't set, set it to the machine name 190 if (Q_strcmp(hostname.string, "UNNAMED") == 0) 191 { 192 // see if it's a text IP address (well, close enough) 193 for (p = buff; *p; p++) 194 if ((*p < '0' || *p > '9') && *p != '.') 195 break; 196 197 // if it is a real name, strip off the domain; we only want the host 198 if (*p) 199 { 200 for (i = 0; i < 15; i++) 201 if (buff[i] == '.') 202 break; 203 buff[i] = 0; 204 } 205 Cvar_Set ("hostname", buff); 206 } 207 208 i = COM_CheckParm ("-ip"); 209 if (i) 210 { 211 if (i < com_argc-1) 212 { 213 myAddr = inet_addr(com_argv[i+1]); 214 if (myAddr == INADDR_NONE) 215 Sys_Error ("%s is not a valid IP address", com_argv[i+1]); 216 strcpy(my_tcpip_address, com_argv[i+1]); 217 } 218 else 219 { 220 Sys_Error ("NET_Init: you must specify an IP address after -ip"); 221 } 222 } 223 else 224 { 225 myAddr = INADDR_ANY; 226 strcpy(my_tcpip_address, "INADDR_ANY"); 227 } 228 229 if ((net_controlsocket = WINS_OpenSocket (0)) == -1) 230 { 231 Con_Printf("WINS_Init: Unable to open control socket\n"); 232 if (--winsock_initialized == 0) 233 pWSACleanup (); 234 return -1; 235 } 236 237 ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; 238 ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; 239 ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons((unsigned short)net_hostport); 240 241 Con_Printf("Winsock TCP/IP Initialized\n"); 242 tcpipAvailable = true; 243 244 return net_controlsocket; 245} 246 247//============================================================================= 248 249void WINS_Shutdown (void) 250{ 251 WINS_Listen (false); 252 WINS_CloseSocket (net_controlsocket); 253 if (--winsock_initialized == 0) 254 pWSACleanup (); 255} 256 257//============================================================================= 258 259void WINS_Listen (qboolean state) 260{ 261 // enable listening 262 if (state) 263 { 264 if (net_acceptsocket != -1) 265 return; 266 WINS_GetLocalAddress(); 267 if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1) 268 Sys_Error ("WINS_Listen: Unable to open accept socket\n"); 269 return; 270 } 271 272 // disable listening 273 if (net_acceptsocket == -1) 274 return; 275 WINS_CloseSocket (net_acceptsocket); 276 net_acceptsocket = -1; 277} 278 279//============================================================================= 280 281int WINS_OpenSocket (int port) 282{ 283 int newsocket; 284 struct sockaddr_in address; 285 u_long _true = 1; 286 287 if ((newsocket = psocket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 288 return -1; 289 290 if (pioctlsocket (newsocket, FIONBIO, &_true) == -1) 291 goto ErrorReturn; 292 293 address.sin_family = AF_INET; 294 address.sin_addr.s_addr = myAddr; 295 address.sin_port = htons((unsigned short)port); 296 if( bind (newsocket, (void *)&address, sizeof(address)) == 0) 297 return newsocket; 298 299 Sys_Error ("Unable to bind to %s", WINS_AddrToString((struct qsockaddr *)&address)); 300ErrorReturn: 301 pclosesocket (newsocket); 302 return -1; 303} 304 305//============================================================================= 306 307int WINS_CloseSocket (int socket) 308{ 309 if (socket == net_broadcastsocket) 310 net_broadcastsocket = 0; 311 return pclosesocket (socket); 312} 313 314 315//============================================================================= 316/* 317============ 318PartialIPAddress 319 320this lets you type only as much of the net address as required, using 321the local network components to fill in the rest 322============ 323*/ 324static int PartialIPAddress (char *in, struct qsockaddr *hostaddr) 325{ 326 char buff[256]; 327 char *b; 328 int addr; 329 int num; 330 int mask; 331 int run; 332 int port; 333 334 buff[0] = '.'; 335 b = buff; 336 strcpy(buff+1, in); 337 if (buff[1] == '.') 338 b++; 339 340 addr = 0; 341 mask=-1; 342 while (*b == '.') 343 { 344 b++; 345 num = 0; 346 run = 0; 347 while (!( *b < '0' || *b > '9')) 348 { 349 num = num*10 + *b++ - '0'; 350 if (++run > 3) 351 return -1; 352 } 353 if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) 354 return -1; 355 if (num < 0 || num > 255) 356 return -1; 357 mask<<=8; 358 addr = (addr<<8) + num; 359 } 360 361 if (*b++ == ':') 362 port = Q_atoi(b); 363 else 364 port = net_hostport; 365 366 hostaddr->sa_family = AF_INET; 367 ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); 368 ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); 369 370 return 0; 371} 372//============================================================================= 373 374int WINS_Connect (int socket, struct qsockaddr *addr) 375{ 376 return 0; 377} 378 379//============================================================================= 380 381int WINS_CheckNewConnections (void) 382{ 383 char buf[4096]; 384 385 if (net_acceptsocket == -1) 386 return -1; 387 388 if (precvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) > 0) 389 { 390 return net_acceptsocket; 391 } 392 return -1; 393} 394 395//============================================================================= 396 397int WINS_Read (int socket, byte *buf, int len, struct qsockaddr *addr) 398{ 399 int addrlen = sizeof (struct qsockaddr); 400 int ret; 401 402 ret = precvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); 403 if (ret == -1) 404 { 405 int errno = pWSAGetLastError(); 406 407 if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED) 408 return 0; 409 410 } 411 return ret; 412} 413 414//============================================================================= 415 416int WINS_MakeSocketBroadcastCapable (int socket) 417{ 418 int i = 1; 419 420 // make this socket broadcast capable 421 if (psetsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) 422 return -1; 423 net_broadcastsocket = socket; 424 425 return 0; 426} 427 428//============================================================================= 429 430int WINS_Broadcast (int socket, byte *buf, int len) 431{ 432 int ret; 433 434 if (socket != net_broadcastsocket) 435 { 436 if (net_broadcastsocket != 0) 437 Sys_Error("Attempted to use multiple broadcasts sockets\n"); 438 WINS_GetLocalAddress(); 439 ret = WINS_MakeSocketBroadcastCapable (socket); 440 if (ret == -1) 441 { 442 Con_Printf("Unable to make socket broadcast capable\n"); 443 return ret; 444 } 445 } 446 447 return WINS_Write (socket, buf, len, &broadcastaddr); 448} 449 450//============================================================================= 451 452int WINS_Write (int socket, byte *buf, int len, struct qsockaddr *addr) 453{ 454 int ret; 455 456 ret = psendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); 457 if (ret == -1) 458 if (pWSAGetLastError() == WSAEWOULDBLOCK) 459 return 0; 460 461 return ret; 462} 463 464//============================================================================= 465 466char *WINS_AddrToString (struct qsockaddr *addr) 467{ 468 static char buffer[22]; 469 int haddr; 470 471 haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); 472 sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); 473 return buffer; 474} 475 476//============================================================================= 477 478int WINS_StringToAddr (char *string, struct qsockaddr *addr) 479{ 480 int ha1, ha2, ha3, ha4, hp; 481 int ipaddr; 482 483 sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); 484 ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; 485 486 addr->sa_family = AF_INET; 487 ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); 488 ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp); 489 return 0; 490} 491 492//============================================================================= 493 494int WINS_GetSocketAddr (int socket, struct qsockaddr *addr) 495{ 496 int addrlen = sizeof(struct qsockaddr); 497 unsigned int a; 498 499 Q_memset(addr, 0, sizeof(struct qsockaddr)); 500 pgetsockname(socket, (struct sockaddr *)addr, &addrlen); 501 a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; 502 if (a == 0 || a == inet_addr("127.0.0.1")) 503 ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; 504 505 return 0; 506} 507 508//============================================================================= 509 510int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name) 511{ 512 struct hostent *hostentry; 513 514 hostentry = pgethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); 515 if (hostentry) 516 { 517 Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); 518 return 0; 519 } 520 521 Q_strcpy (name, WINS_AddrToString (addr)); 522 return 0; 523} 524 525//============================================================================= 526 527int WINS_GetAddrFromName(char *name, struct qsockaddr *addr) 528{ 529 struct hostent *hostentry; 530 531 if (name[0] >= '0' && name[0] <= '9') 532 return PartialIPAddress (name, addr); 533 534 hostentry = pgethostbyname (name); 535 if (!hostentry) 536 return -1; 537 538 addr->sa_family = AF_INET; 539 ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); 540 ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; 541 542 return 0; 543} 544 545//============================================================================= 546 547int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) 548{ 549 if (addr1->sa_family != addr2->sa_family) 550 return -1; 551 552 if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) 553 return -1; 554 555 if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) 556 return 1; 557 558 return 0; 559} 560 561//============================================================================= 562 563int WINS_GetSocketPort (struct qsockaddr *addr) 564{ 565 return ntohs(((struct sockaddr_in *)addr)->sin_port); 566} 567 568 569int WINS_SetSocketPort (struct qsockaddr *addr, int port) 570{ 571 ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port); 572 return 0; 573} 574 575//============================================================================= 576