Kart-Public/tools/SRB2Launcher/mserv.c

401 lines
7.9 KiB
C

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// MSERV SDK
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Adapted by Oogaland.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
//
// DESCRIPTION:
// Commands used to communicate with the master server
//
//-----------------------------------------------------------------------------
#ifdef WIN32
#include <windows.h> // socket(),...
#else
#include <unistd.h>
#endif
#include "launcher.h"
#include "mserv.h"
#include "i_tcp.h"
// ================================ DEFINITIONS ===============================
#define PACKET_SIZE 1024
#define MS_NO_ERROR 0
#define MS_SOCKET_ERROR -201
#define MS_CONNECT_ERROR -203
#define MS_WRITE_ERROR -210
#define MS_READ_ERROR -211
#define MS_CLOSE_ERROR -212
#define MS_GETHOSTBYNAME_ERROR -220
#define MS_GETHOSTNAME_ERROR -221
#define MS_TIMEOUT_ERROR -231
// see master server code for the values
#define GET_SERVER_MSG 200
#define HEADER_SIZE ((long)sizeof(long)*3)
#define HEADER_MSG_POS 0
#define IP_MSG_POS 16
#define PORT_MSG_POS 32
#define HOSTNAME_MSG_POS 40
#ifndef SOCKET
#define SOCKET int
#endif
typedef struct {
long id;
long type;
long length;
char buffer[PACKET_SIZE];
} msg_t;
// win32 or djgpp
#if defined( WIN32) || defined( __DJGPP__ )
#define ioctl ioctlsocket
#define close closesocket
#endif
#if defined( WIN32) || defined( __OS2__)
// it seems windows doesn't define that... maybe some other OS? OS/2
int inet_aton(char *hostname, struct in_addr *addr)
{
return ( (addr->s_addr=inet_addr(hostname)) != INADDR_NONE );
}
#endif
enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE;
static SOCKET socket_fd = -1; // TCP/IP socket
static struct sockaddr_in addr;
static struct timeval select_timeout;
static fd_set wset;
int MS_Connect(char *ip_addr, char *str_port, int async);
static int MS_Read(msg_t *msg);
static int MS_Write(msg_t *msg);
static int MS_GetIP(char *);
void ExtractServerInfo(char *serverout, struct SERVERLIST *serverlist);
void CloseConnection(void)
{
if(socket_fd > 0) close(socket_fd);
socket_fd = -1;
}
/*
** MS_GetIP()
*/
static int MS_GetIP(char *hostname)
{
struct hostent *host_ent;
if (!inet_aton(hostname, &addr.sin_addr)) {
//TODO: only when we are connected to Internet, or use a non bloking call
host_ent = gethostbyname(hostname);
if (host_ent==NULL)
return MS_GETHOSTBYNAME_ERROR;
memcpy(&addr.sin_addr, host_ent->h_addr_list[0], sizeof(struct in_addr));
}
return 0;
}
/*
** MS_Connect()
*/
int MS_Connect(char *ip_addr, char *str_port, int async)
{
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
I_InitTcpDriver(); // this is done only if not already done
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return MS_SOCKET_ERROR;
if (MS_GetIP(ip_addr)==MS_GETHOSTBYNAME_ERROR)
return MS_GETHOSTBYNAME_ERROR;
addr.sin_port = htons((u_short)atoi(str_port));
if (async) // do asynchronous connection
{
int res = 1;
ioctl(socket_fd, FIONBIO, &res);
res = connect(socket_fd, (struct sockaddr *) &addr, sizeof(addr));
if (res < 0)
{
// humm, on win32 it doesn't work with EINPROGRESS (stupid windows)
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
con_state = MSCS_FAILED;
CloseConnection();
return MS_CONNECT_ERROR;
}
}
con_state = MSCS_WAITING;
FD_ZERO(&wset);
FD_SET(socket_fd, &wset);
select_timeout.tv_sec = 0, select_timeout.tv_usec = 0;
}
else
{
if (connect(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
return MS_CONNECT_ERROR;
}
return 0;
}
/*
* MS_Write():
*/
static int MS_Write(msg_t *msg)
{
int len;
if (msg->length < 0)
msg->length = strlen(msg->buffer);
len = msg->length+HEADER_SIZE;
//msg->id = htonl(msg->id);
msg->type = htonl(msg->type);
msg->length = htonl(msg->length);
if (send(socket_fd, (char*)msg, len, 0) != len)
return MS_WRITE_ERROR;
return 0;
}
/*
* MS_Read():
*/
static int MS_Read(msg_t *msg)
{
if (recv(socket_fd, (char*)msg, HEADER_SIZE, 0) != HEADER_SIZE)
return MS_READ_ERROR;
msg->type = ntohl(msg->type);
msg->length = ntohl(msg->length);
if (!msg->length) //Hurdler: fix a bug in Windows 2000
return 0;
if (recv(socket_fd, (char*)msg->buffer, msg->length, 0) != msg->length)
return MS_READ_ERROR;
return 0;
}
/***************************************************************************/
/* GetServerListEx */
EXPORT int __stdcall GetServerListEx(char *host, char *str_port, struct SERVERLIST serverlist[], short max_servers)
{
msg_t msg;
int count = 0;
/* Attempt to connect to list server. */
MS_Connect(host, str_port, 0);
/* Poll the list server. If it fails, depart with an error code of -1. */
msg.type = GET_SERVER_MSG;
msg.length = 0;
if (MS_Write(&msg) < 0)
return -1;
/* Get a description of each server in turn. */
/* What we get is exactly the same as the output to the console when using LISTSERV. */
while (MS_Read(&msg) >= 0)
{
if(msg.length == 0 || count >= max_servers)
{
CloseConnection();
return count;
}
ExtractServerInfo(msg.buffer, &serverlist[count]);
count++;
}
CloseConnection();
return -2;
}
/* GetServerList */
/* Warning: Large kludge follows! This function is only included for backwards-compatibility. */
/* Use GetServerListVB or GetServerListEx instead. */
EXPORT int __stdcall GetServerList(char *host, char *str_port,
struct SERVERLIST *serverlist1,struct SERVERLIST *serverlist2,struct SERVERLIST *serverlist3,
struct SERVERLIST *serverlist4,struct SERVERLIST *serverlist5,struct SERVERLIST *serverlist6,
struct SERVERLIST *serverlist7,struct SERVERLIST *serverlist8,struct SERVERLIST *serverlist9,
struct SERVERLIST *serverlist10,struct SERVERLIST *serverlist11,struct SERVERLIST *serverlist12,
struct SERVERLIST *serverlist13,struct SERVERLIST *serverlist14,struct SERVERLIST *serverlist15,
struct SERVERLIST *serverlist16)
{
msg_t msg;
int count = 0;
struct SERVERLIST *serverlist[16];
/* Attempt to connect to list server. */
MS_Connect(host, str_port, 0);
/* Poll the list server. If it fails, bomb with an error code of -1. */
msg.type = GET_SERVER_MSG;
msg.length = 0;
if (MS_Write(&msg) < 0)
return -1;
serverlist[0] = serverlist1;
serverlist[1] = serverlist2;
serverlist[2] = serverlist3;
serverlist[3] = serverlist4;
serverlist[4] = serverlist5;
serverlist[5] = serverlist6;
serverlist[6] = serverlist7;
serverlist[7] = serverlist8;
serverlist[8] = serverlist9;
serverlist[9] = serverlist10;
serverlist[10] = serverlist11;
serverlist[11] = serverlist12;
serverlist[12] = serverlist13;
serverlist[13] = serverlist14;
serverlist[14] = serverlist15;
serverlist[15] = serverlist16;
while (MS_Read(&msg) >= 0 && count < 16)
{
if(msg.length == 0 || count >= 16)
{
CloseConnection();
return count;
}
ExtractServerInfo(msg.buffer, serverlist[count]);
count++;
}
CloseConnection();
return -2;
}
void ExtractServerInfo(char *serverout, struct SERVERLIST *serverlist)
{
char *lines[5];
int i;
i=0;
lines[0] = strtok(serverout, "\r\n");
for(i=1; i<5; i++)
{
lines[i] = strtok(NULL, "\r\n");
}
strcpy(serverlist->ip, strstr(lines[0], ": ")+2);
strcpy(serverlist->port, strstr(lines[1], ": ")+2);
strcpy(serverlist->name, strstr(lines[2], ": ")+2);
strcpy(serverlist->version, strstr(lines[3], ": ")+2);
strcpy(serverlist->perm, strstr(lines[4], ": ")+2);
}