-
Notifications
You must be signed in to change notification settings - Fork 5
/
NamedPipes_SMB.pas
253 lines (208 loc) · 6.11 KB
/
NamedPipes_SMB.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// This PoC does not handle exceptions, consider handling exception if used it in production.
program NamedPipes;
uses Winapi.Windows,
System.SysUtils,
System.Classes;
const PIPE_NAME = 'NamedPipeExample';
SERVER_MACHINE_NAME = '.'; // `.` = Local Machine
var SERVER_LISTENING_EVENT : THandle;
Type
TCommand = (
cmdPing,
cmdPong,
cmdExit
);
TServer = class(TThread)
protected
{@M}
procedure Execute(); override;
end;
TClient = class(TThread)
protected
{@M}
procedure Execute(); override;
end;
(* Local *)
{ _.PIPE_WriteInteger
Write to named pipe a signed integer (4 bytes), since in our example, named pipe has
a buffer of 2 bytes, we must split our signed integer to two words }
procedure PIPE_WriteInteger(const hPipe : THandle; const AValue : Integer);
var wLow, wHigh : Word;
ABytesWritten : Cardinal;
begin
wLow := Word(AValue and $FFFF);
wHigh := Word(AValue shr 16);
///
WriteFile(hPipe, wLow, SizeOf(Word), ABytesWritten, nil);
WriteFile(hPipe, wHigh, SizeOf(Word), ABytesWritten, nil);
end;
{ _.PIPE_ReadInteger
Reconstruct signed integer from two words }
function PIPE_ReadInteger(const hPipe : THandle) : Integer;
var wLow, wHigh : Word;
dwBytesRead : Cardinal;
begin
result := -1;
///
ReadFile(hPipe, wLow, SizeOf(Word), dwBytesRead, nil);
ReadFile(hPipe, wHigh, SizeOf(Word), dwBytesRead, nil);
///
result := wLow or (wHigh shl 16);
end;
{ _.PIPE_WriteLine
Write to NamedPipe and append a CRLF to signify end of buffer }
procedure PIPE_WriteLine(const hPipe : THandle; AMessage : String);
var ABytesWritten : Cardinal;
i : Cardinal;
begin
AMessage := Trim(AMessage) + #13#10;
///
for I := 1 to Length(AMessage) do begin
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile?WT_mc_id=SEC-MVP-5005282
if not WriteFile(
hPipe,
AMessage[I],
SizeOf(WideChar),
ABytesWritten,
nil
) then
break;
end;
end;
{ _.PIPE_ReadLine
Read NamedPipe Buffer until CRLF is reached }
function PIPE_ReadLine(const hPipe : THandle) : String;
var ABuffer : WideChar;
dwBytesRead : Cardinal;
CR : Boolean;
LF : Boolean;
begin
result := '';
///
CR := False;
LF := False;
while True do begin
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile?WT_mc_id=SEC-MVP-5005282
if not ReadFile(hPipe, ABuffer, SizeOf(ABuffer), dwBytesRead, nil) then
break;
case ABuffer of
#13 : CR := True;
#10 : LF := True;
end;
if CR and LF then
break;
///
result := result + ABuffer;
end;
end;
(* TServer *)
{ TServer.Execute }
procedure TServer.Execute();
var hPipe : THandle;
begin
hPipe := INVALID_HANDLE_VALUE;
try
// https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew?WT_mc_id=SEC-MVP-5005282
hPipe := CreateNamedPipeW(
PWideChar(Format('\\.\pipe\%s', [PIPE_NAME])),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
1,
SizeOf(WideChar),
SizeOf(WideChar),
NMPWAIT_USE_DEFAULT_WAIT,
nil
);
if hPipe = INVALID_HANDLE_VALUE then
Exit();
SetEvent(SERVER_LISTENING_EVENT); // Signal we are listening for named pipe client
while (not Terminated) do begin
// https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe?WT_mc_id=SEC-MVP-5005282
if not ConnectNamedPipe(hPipe, nil) then
continue;
try
while (not Terminated) do begin
case TCommand(PIPE_ReadInteger(hPipe)) of
cmdPing : PIPE_WriteLine(hPIpe, Format('Pong: %d', [GetTickCount()]));
else begin
WriteLn('Bye!');
break;
end;
end;
end;
WriteLn(PIPE_ReadLine(hPipe));
finally
// https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-disconnectnamedpipe?WT_mc_id=SEC-MVP-5005282
DisconnectNamedPipe(hPipe);
end;
end;
finally
if hPipe <> INVALID_HANDLE_VALUE then
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle?WT_mc_id=SEC-MVP-5005282
CloseHandle(hPipe);
///
ExitThread(0);
end;
end;
(* TClient *)
{ TClient.Execute
An alternative to CreateFileW + WriteFile would be to use:
- https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-callnamedpipew?WT_mc_id=SEC-MVP-5005282
}
procedure TClient.Execute();
var hPipe : THandle;
begin
hPipe := INVALID_HANDLE_VALUE;
try
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew?WT_mc_id=SEC-MVP-5005282
hPipe := CreateFileW(
PWideChar(Format('\\%s\pipe\%s', [
SERVER_MACHINE_NAME,
PIPE_NAME
])),
GENERIC_READ or GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
0,
0
);
if hPipe = INVALID_HANDLE_VALUE then
Exit();
PIPE_WriteInteger(hPipe, Integer(TCommand.cmdPing));
WriteLn(PIPE_ReadLine(hPipe));
PIPE_WriteInteger(hPipe, Integer(TCommand.cmdExit));
finally
if hPipe <> INVALID_HANDLE_VALUE then
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle?WT_mc_id=SEC-MVP-5005282
CloseHandle(hPipe);
///
ExitThread(0);
end;
end;
(* _.EntryPoint *)
var Server : TServer;
Client : TClient;
begin
AllocConsole();
///
// Create a event to signal when named pipe server is successfully listening for
// Namedpipe clients.
// When event is signaled, we can start our named pipe client thread.
SERVER_LISTENING_EVENT := CreateEvent(nil, False, False, nil);
if SERVER_LISTENING_EVENT = 0 then
Exit();
try
// Launch NamedPipe Server
Server := TServer.Create();
///
WaitForSingleObject(SERVER_LISTENING_EVENT, INFINITE);
finally
CloseHandle(SERVER_LISTENING_EVENT);
end;
// Launch NamedPipe Client
Client := TClient.Create();
// Wait for Threads end
Client.WaitFor();
Server.WaitFor();
end.