Skip to content

Instantly share code, notes, and snippets.

@alexey-gamov
Created July 28, 2013 06:34
Show Gist options
  • Save alexey-gamov/6097660 to your computer and use it in GitHub Desktop.
Save alexey-gamov/6097660 to your computer and use it in GitHub Desktop.
Модуль для отправки сообщений в локальной сети. Сервер не требуется, т.к. сообщение отправляется в глобальном пакете. Для настройки защиты в блоке const нужно настроить переменные salt (соль) и pass (пароль)..
(*
LAN module by XProger
Security addon by atomos.pro
version: 0.2b
*)
unit Net;
interface
const
salt = 'hackme';
pass = 85;
{ Инициализация сети - Если в результате получаем false, значит произошли ошибки }
function NET_Init: Boolean;
{ Высвобождение ресурсов под сеть }
procedure NET_Free;
{ Очистка буфера записи -т.е. сначала нужно очистить буфер, затем записать в него
данные при помощи NET_Write и послать их через NET_Send
после отправки буфер не очищается, т.е. можно ещё раз
вызвать NET_Write и послать этот же буфер кому-то другому }
procedure NET_Clear;
{ Инициализирует сокет на указанном порту, если значене порта выставленно в 0, то выбирается
любой свободный порт. Возвращает идентификатор сокета. Сокет всего один, т.к. для большинства задач больше и не нужно ;) }
function NET_InitSocket(Port: WORD): Integer;
{ Записывает Count байт из Buf (указатель на что-либо) в буфер исходящих данных }
function NET_Write(Buf: Pointer; Count: Integer): Boolean;
{ Отправляет данные содержащиеся в исходящем буфере на отправку по указанному IP адресу и Port.
В случае если IP = nil посылается широковещательный пакет (только локальные сети)
APL (Anti Packet Lost) указывает, нужна ли гарантированная доставка для этих данных
которая не работает для широковещательных рассылок :) }
function NET_Send(IP: PChar; Port: WORD; APL: Boolean): Integer;
{ Вытащить Count байт из входящего буфера пакетов
Count желательно указывать максимально большим, т.к. не прочитанные из пакета данные будут утеряны
В IP и Port записываются IP адрес и порт компьютера приславшего пакет
Результат - кол-во полученных байт, также записывается в RecvBytes
Рекомендуется производить проверку пришедших данных как можно чаще (например, раз в тик игрового цикла)
вызывать эту функцию нужно до тех пор, пока RecvBytes (или результат) превышают 0 байт :) }
function NET_Recv(Buf: Pointer; Count: Integer; var IP: PChar; var Port: Integer; var RecvBytes: Integer): Integer;
implementation
uses
Windows, WinSock, StrUtils;
const
MaxBufLen = 65507;
type
TByteArray = array [0..1024] of Byte;
PByteArray = ^TByteArray;
var
NET_Ready : Boolean = False; // инициализированна ли сеть
NET_Buf : PByteArray; // сам буфер-накопитель
NET_BufLen : Integer; // текущая длинна буфера
NET_Socket : Integer = -1;
NET_trys : Byte = 4;
regseek : Integer = 0;
var
NET_tmpBuf : PByteArray;
function Encrypt(s: String; code: Boolean): String;
var
i, Delta, Res: Integer;
begin
Result := '';
for i := 1 to Length(s) do
begin
Delta := ((i xor pass) mod 256);
if not code then
begin
Res := Ord(s[i]) - Delta - 32;
if Res < 32 then Res := Res + 256;
end
else Res := ((Ord(s[i]) + Delta) mod 256) + 32;
Result := Result + chr(Res);
end;
end;
procedure NET_Free;
begin
if NET_Ready then
begin
if NET_Socket > 0 then CloseSocket(NET_Socket);
NET_Socket := -1;
FreeMem(NET_Buf);
FreeMem(NET_tmpBuf);
NET_Buf := nil;
NET_tmpBuf := nil;
WSACleanup;
NET_Ready := False;
end;
end;
procedure NET_Clear;
begin
NET_BufLen := 1;
end;
function NET_Init: Boolean;
var
winsock_version : WORD;
winsock_data : WsaData;
error : Integer;
begin
Result := False;
NET_Free;
NET_Ready := False;
winsock_version := MAKEWORD(1, 1);
error := WSAStartup(winsock_version, winsock_data);
if error <> 0 then Exit;
NET_Ready := True;
NET_Clear;
NET_Socket := -1;
if NET_Buf = nil then GetMem(NET_Buf, MaxBufLen);
if NET_tmpBuf = nil then GetMem(NET_tmpBuf, MaxBufLen);
Result := True;
end;
function NET_InitSocket(Port: WORD): Integer;
var
sock : Integer;
flag : Integer;
i : Integer;
address : SockAddr_in;
begin
Result := 0;
i := 1;
flag := 1;
if NET_Socket > 0 then CloseSocket(NET_Socket);
// создаем UDP socket
sock := socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if sock = -1 then Exit;
// устанавливаем параметр nonblocking для socket, что означает, что если на
// входе нет данных, то метод чтения не будет ждать их появления
if IoctlSocket(sock, FIONBIO, flag) = -1 then Exit;
// настраиваем socket так, чтобы была возможность посылать и принимать broadcast
// сообщения, то есть, сообщения, направленные всем сетевым клиентам в текущей
// локальной сети
SetSockOpt(sock, SOL_SOCKET, SO_BROADCAST, PChar(@i), SizeOf(i));
address.sin_addr.S_addr := INADDR_ANY;
address.sin_port := htons(Port);
address.sin_family := AF_INET;
// "прикрепляем" socket к порту
if bind(sock, address, sizeof(address)) = -1 then
begin
CloseSocket(sock);
Exit;
end;
NET_Socket := sock;
Result := sock;
end;
function NET_Write(Buf: Pointer; Count: Integer): Boolean;
var
i : Integer;
begin
Result := False;
// добавим соли в buf
Buf := Pointer(PChar(Encrypt(PChar(Buf) + salt, False)));
Count := Length(PChar(Buf));
if (not NET_Ready) or (NET_Socket <= 0) then Exit;
if Count <= 0 then Exit;
if NET_BufLen + Count < MaxBufLen then
begin
for i := Net_BufLen to Net_BufLen + Count - 1 do
NET_Buf[i] := PByteArray(buf)[i - Net_BufLen];
NET_BufLen := NET_BufLen + Count;
Result := True;
end;
end;
function NET_Recv(Buf: Pointer; Count: Integer; var IP: PChar; var Port: Integer; var RecvBytes: Integer): Integer;
var
from : sockaddr_in;
i : Integer;
s : String;
begin
Result := 0;
if (not NET_Ready) or (NET_Socket <= 0) then Exit;
if (Count <= 0) or (Count > MaxBufLen) then Exit;
i := SizeOf(from);
Result := recvfrom(NET_Socket, NET_tmpBuf[0], Count, 0, from, i);
if Result <= 0 then
begin
Result := -1;
RecvBytes := Result;
Exit;
end;
dec(Result);
if Result > 0 then
begin
IP := inet_ntoa(from.sin_addr);
Port := ntohs(from.sin_port);
if NET_tmpBuf[0] = 0 then
begin
// запишем полученное в строку s и расшифруем
SetString(s, PAnsiChar(@NET_tmpBuf[1]), Result);
s := Encrypt(s, True);
// проверим на содержание соли и удалим её вконце
if RightStr(s, Length(salt)) <> salt then Result := 0
else begin
Move(s[1], PByteArray(buf)[0], Length(s) - Length(salt));
Result := Result - Length(salt);
end;
end;
end;
RecvBytes := Result;
end;
function NET_Send(IP: PChar; Port: WORD; APL: Boolean): Integer;
var
address : sockaddr_in;
begin
Result := 0;
if (not NET_Ready) or (NET_Socket <= 0) then Exit;
address.sin_family := AF_INET;
address.sin_port := htons(Port);
if IP <> nil then address.sin_addr.S_addr := inet_addr(IP)
else address.sin_addr.S_addr := INADDR_BROADCAST;
FillChar(address.sin_zero, SizeOf(address.sin_zero), 0);
if IP = nil then
begin
NET_Buf[0] := 0;
Result := SendTo(NET_Socket, NET_Buf[0], NET_BufLen, 0, address, SizeOf(address));
end;
end;
end.
unit usage;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Net;
type
TForm1 = class(TForm)
Timer1: TTimer;
Memo1: TMemo;
procedure TForm1.FormCreate(Sender: TObject);
procedure TForm1.FormDestroy(Sender: TObject);
procedure TForm1.Timer1Timer(Sender: TObject);
end;
var
Form1: TForm1;
Message: String;
implementation
{$R *.dfm}
{ функция отправки сообщения }
function SendMessage(Edit: String): String;
begin
Message := Edit;
NET_Clear;
NET_Write(@Message[1], Length(Message));
NET_Send(nil, 21666, False);
end;
{ инициализация чата}
procedure TForm1.FormCreate(Sender: TObject);
begin
if not NET_Init then
begin
MessageBox(Handle, 'Невозможно инициализировать сетевой протокол', 'Ошибка', MB_ICONHAND);
Halt;
end;
NET_InitSocket(21666);
SendMessage('hello chat!');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
NET_Free;
end;
{ получение сообщения }
procedure TForm1.Timer1Timer(Sender: TObject);
var
buf: array [0..255] of Char;
Port, recv, i: Integer;
IP: PChar;
Message: String;
begin
// получим сообщение через рекурсивную фукцию
while NET_Recv(@buf, 255, IP, Port, recv) > 0 do
begin
Message := Copy(buf, 1, recv);
Memo1.Items.Add(Message);
end;
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment