Kart-Public/tools/SRB2Updater/MSClient.cs
2014-03-15 13:11:35 -04:00

121 lines
4.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace SRB2Updater
{
class MSClient
{
private Socket socket;
/// <summary>
/// Constructs an MS client object.
/// </summary>
public MSClient()
{
socket = new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.IP);
}
/// <summary>
/// Gets list of servers reported by the MS.
/// </summary>
/// <param name="strAddress">Hostname or IP address of MS.</param>
/// <param name="unPort">Port of MS.</param>
/// <returns>List of running servers.</returns>
public List<MSServerEntry> GetServerList(string strAddress, ushort unPort)
{
// Resolve the address if necessary.
IPHostEntry iphe = Dns.GetHostEntry(strAddress);
int iIPIndex = 0;
while (iIPIndex < iphe.AddressList.Length &&
iphe.AddressList[iIPIndex].AddressFamily != socket.AddressFamily)
{
iIPIndex++;
}
// No addresses from our family?
if (iIPIndex >= iphe.AddressList.Length)
throw new SocketException((int)SocketError.HostNotFound);
socket.Connect(iphe.AddressList[iIPIndex], unPort);
// Send a request for the short server list.
byte[] byPacket = new byte[12];
BinaryWriter bw = new BinaryWriter(new MemoryStream(byPacket));
bw.Write((uint)0); // Unused
bw.Write((uint)IPAddress.HostToNetworkOrder(205)); // GET_SHORT_SERVER_MSG
bw.Write((uint)0); // Length of tail.
socket.Send(byPacket);
List<MSServerEntry> listServers = new List<MSServerEntry>();
// Don't sit and wait for ever.
socket.ReceiveTimeout = 10000;
// Keep reading packets. We break if we receive the sentinel end-packet.
byte[] byServer = new byte[12 + 80];
while (true)
{
int iLen = socket.Receive(byServer);
BinaryReader br = new BinaryReader(new MemoryStream(byServer));
// Ignore the first eight bytes.
br.ReadInt64();
// Is that the list finished?
int iTailLen = IPAddress.NetworkToHostOrder(br.ReadInt32());
if (iTailLen == 0)
break;
else if (iTailLen != iLen - 12)
{
// MS is in a bad mood.
//throw new Exception("Bad packet.");
break;
}
// Otherwise, add the server to the list.
MSServerEntry msse = new MSServerEntry();
br.ReadBytes(16); // Skip.
msse.strAddress = Encoding.ASCII.GetString(br.ReadBytes(16));
string str = Encoding.ASCII.GetString(br.ReadBytes(8));
int iPos = str.IndexOf("\0");
if (iPos >= 0)
str = str.Remove(iPos);
msse.unPort = Convert.ToUInt16(str);
msse.strName = Encoding.ASCII.GetString(br.ReadBytes(32));
iPos = msse.strName.IndexOf("\0");
if (iPos >= 0)
msse.strName = msse.strName.Remove(iPos);
msse.strVersion = Encoding.ASCII.GetString(br.ReadBytes(8));
iPos = msse.strVersion.IndexOf("\0");
if (iPos >= 0)
msse.strVersion = msse.strVersion.Remove(iPos);
listServers.Add(msse);
}
return listServers;
}
}
public struct MSServerEntry
{
public string strAddress;
public ushort unPort;
public string strName;
public string strVersion;
public bool bPermanent;
}
}