/src/PcapPlusPlus/Packet++/header/FtpLayer.h
Line | Count | Source (jump to first uncovered line) |
1 | | #ifndef PACKETPP_FTP_LAYER |
2 | | #define PACKETPP_FTP_LAYER |
3 | | |
4 | | #include "SingleCommandTextProtocol.h" |
5 | | |
6 | | /// @file |
7 | | |
8 | | /** |
9 | | * \namespace pcpp |
10 | | * \brief The main namespace for the PcapPlusPlus lib |
11 | | */ |
12 | | namespace pcpp |
13 | | { |
14 | | |
15 | | /** |
16 | | * Class for general FTP message |
17 | | */ |
18 | | class FtpLayer : public SingleCommandTextProtocol |
19 | | { |
20 | | protected: |
21 | 824 | FtpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : SingleCommandTextProtocol(data, dataLen, prevLayer, packet) { m_Protocol = FTP; }; |
22 | 0 | FtpLayer(const std::string &command, const std::string &option) : SingleCommandTextProtocol(command, option) { m_Protocol = FTP; }; |
23 | | |
24 | | public: |
25 | | |
26 | | /** |
27 | | * A static method that checks whether the port is considered as FTP |
28 | | * @param[in] port The port number to be checked |
29 | | */ |
30 | 170k | static bool isFtpPort(uint16_t port) { return port == 21; } |
31 | | |
32 | | // overridden methods |
33 | | |
34 | | /// FTP is the always last so does nothing for this layer |
35 | 824 | void parseNextLayer() {} |
36 | | |
37 | | /** |
38 | | * @return Get the size of the layer |
39 | | */ |
40 | 0 | size_t getHeaderLen() const { return m_DataLen; } |
41 | | |
42 | | /// Does nothing for this layer |
43 | 0 | void computeCalculateFields() {} |
44 | | |
45 | | /** |
46 | | * @return The OSI layer level of FTP (Application Layer). |
47 | | */ |
48 | 824 | OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; } |
49 | | |
50 | | }; |
51 | | |
52 | | /** |
53 | | * Class for representing the request messages of FTP Layer |
54 | | */ |
55 | | class FtpRequestLayer : public FtpLayer |
56 | | { |
57 | | public: |
58 | | |
59 | | /** |
60 | | * Enum for FTP command codes |
61 | | */ |
62 | | enum class FtpCommand : int |
63 | | { |
64 | | /// Unknown command |
65 | | UNK, |
66 | | /// Abort an active file transfer. |
67 | | ABOR = ('A') | ('B' << 8) | ('O' << 16) | ('R' << 24), |
68 | | /// Account information. |
69 | | ACCT = ('A') | ('C' << 8) | ('C' << 16) | ('T' << 24), |
70 | | /// Authentication/Security Data |
71 | | ADAT = ('A') | ('D' << 8) | ('A' << 16) | ('T' << 24), |
72 | | /// Allocate sufficient disk space to receive a file. |
73 | | ALLO = ('A') | ('L' << 8) | ('L' << 16) | ('O' << 24), |
74 | | /// Append (with create) |
75 | | APPE = ('A') | ('P' << 8) | ('P' << 16) | ('E' << 24), |
76 | | /// Authentication/Security Mechanism |
77 | | AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24), |
78 | | /// Get the available space |
79 | | AVBL = ('A') | ('V' << 8) | ('B' << 16) | ('L' << 24), |
80 | | /// Clear Command Channel |
81 | | CCC = ('C') | ('C' << 8) | ('C' << 16), |
82 | | /// Change to Parent Directory. |
83 | | CDUP = ('C') | ('D' << 8) | ('U' << 16) | ('P' << 24), |
84 | | /// Confidentiality Protection Command |
85 | | CONF = ('C') | ('O' << 8) | ('N' << 16) | ('F' << 24), |
86 | | /// Client / Server Identification |
87 | | CSID = ('C') | ('S' << 8) | ('I' << 16) | ('D' << 24), |
88 | | /// Change working directory. |
89 | | CWD = ('C') | ('W' << 8) | ('D' << 16), |
90 | | /// Delete file. |
91 | | DELE = ('D') | ('E' << 8) | ('L' << 16) | ('E' << 24), |
92 | | /// Get the directory size |
93 | | DSIZ = ('D') | ('S' << 8) | ('I' << 16) | ('Z' << 24), |
94 | | /// Privacy Protected Channel |
95 | | ENC = ('E') | ('N' << 8) | ('C' << 16), |
96 | | /// Specifies an extended address and port to which the server should connect. |
97 | | EPRT = ('E') | ('P' << 8) | ('R' << 16) | ('T' << 24), |
98 | | /// Enter extended passive mode. |
99 | | EPSV = ('E') | ('P' << 8) | ('S' << 16) | ('V' << 24), |
100 | | /// Get the feature list implemented by the server. |
101 | | FEAT = ('F') | ('E' << 8) | ('A' << 16) | ('T' << 24), |
102 | | /// Returns usage documentation on a command if specified, else a general help document is returned. |
103 | | HELP = ('H') | ('E' << 8) | ('L' << 16) | ('P' << 24), |
104 | | /// Identify desired virtual host on server, by name. |
105 | | HOST = ('H') | ('O' << 8) | ('S' << 16) | ('T' << 24), |
106 | | /// Language Negotiation |
107 | | LANG = ('L') | ('A' << 8) | ('N' << 16) | ('G' << 24), |
108 | | /// Returns information of a file or directory if specified, else information of the current working directory is returned. |
109 | | LIST = ('L') | ('I' << 8) | ('S' << 16) | ('T' << 24), |
110 | | /// Specifies a long address and port to which the server should connect. |
111 | | LPRT = ('L') | ('P' << 8) | ('R' << 16) | ('T' << 24), |
112 | | /// Enter long passive mode. |
113 | | LPSV = ('L') | ('P' << 8) | ('S' << 16) | ('V' << 24), |
114 | | /// Return the last-modified time of a specified file. |
115 | | MDTM = ('M') | ('D' << 8) | ('T' << 16) | ('M' << 24), |
116 | | /// Modify the creation time of a file. |
117 | | MFCT = ('M') | ('F' << 8) | ('C' << 16) | ('T' << 24), |
118 | | /// Modify fact (the last modification time, creation time, UNIX group/owner/mode of a file). |
119 | | MFF = ('M') | ('F' << 8) | ('F' << 16), |
120 | | /// Modify the last modification time of a file. |
121 | | MFMT = ('M') | ('F' << 8) | ('M' << 16) | ('T' << 24), |
122 | | /// Integrity Protected Command |
123 | | MIC = ('M') | ('I' << 8) | ('C' << 16), |
124 | | /// Make directory. |
125 | | MKD = ('M') | ('K' << 8) | ('D' << 16), |
126 | | /// Lists the contents of a directory in a standardized machine-readable format. |
127 | | MLSD = ('M') | ('L' << 8) | ('S' << 16) | ('D' << 24), |
128 | | /// Provides data about exactly the object named on its command line in a standardized machine-readable format. |
129 | | MLST = ('M') | ('L' << 8) | ('S' << 16) | ('T' << 24), |
130 | | /// Sets the transfer mode (Stream, Block, or Compressed). |
131 | | MODE = ('M') | ('O' << 8) | ('D' << 16) | ('E' << 24), |
132 | | /// Returns a list of file names in a specified directory. |
133 | | NLST = ('N') | ('L' << 8) | ('S' << 16) | ('T' << 24), |
134 | | /// No operation (dummy packet; used mostly on keepalives). |
135 | | NOOP = ('N') | ('O' << 8) | ('O' << 16) | ('P' << 24), |
136 | | /// Select options for a feature (for example OPTS UTF8 ON). |
137 | | OPTS = ('O') | ('P' << 8) | ('T' << 16) | ('S' << 24), |
138 | | /// Authentication password. |
139 | | PASS = ('P') | ('A' << 8) | ('S' << 16) | ('S' << 24), |
140 | | /// Enter passive mode. |
141 | | PASV = ('P') | ('A' << 8) | ('S' << 16) | ('V' << 24), |
142 | | /// Protection Buffer Size |
143 | | PBSZ = ('P') | ('B' << 8) | ('S' << 16) | ('Z' << 24), |
144 | | /// Specifies an address and port to which the server should connect. |
145 | | PORT = ('P') | ('O' << 8) | ('R' << 16) | ('T' << 24), |
146 | | /// Data Channel Protection Level. |
147 | | PROT = ('P') | ('R' << 8) | ('O' << 16) | ('T' << 24), |
148 | | /// Print working directory. Returns the current directory of the host. |
149 | | PWD = ('P') | ('W' << 8) | ('D' << 16), |
150 | | /// Disconnect. |
151 | | QUIT = ('Q') | ('U' << 8) | ('I' << 16) | ('T' << 24), |
152 | | /// Re initializes the connection. |
153 | | REIN = ('R') | ('E' << 8) | ('I' << 16) | ('N' << 24), |
154 | | /// Restart transfer from the specified point. |
155 | | REST = ('R') | ('E' << 8) | ('S' << 16) | ('T' << 24), |
156 | | /// Retrieve a copy of the file |
157 | | RETR = ('R') | ('E' << 8) | ('T' << 16) | ('R' << 24), |
158 | | /// Remove a directory. |
159 | | RMD = ('R') | ('M' << 8) | ('D' << 16), |
160 | | /// Remove a directory tree |
161 | | RMDA = ('R') | ('M' << 8) | ('D' << 16) | ('A' << 24), |
162 | | /// Rename from. |
163 | | RNFR = ('R') | ('N' << 8) | ('F' << 16) | ('R' << 24), |
164 | | /// Rename to. |
165 | | RNTO = ('R') | ('N' << 8) | ('T' << 16) | ('O' << 24), |
166 | | /// Sends site specific commands to remote server (like SITE IDLE 60 or SITE UMASK 002). Inspect SITE HELP output for complete list of supported commands. |
167 | | SITE = ('S') | ('I' << 8) | ('T' << 16) | ('E' << 24), |
168 | | /// Return the size of a file. |
169 | | SIZE = ('S') | ('I' << 8) | ('Z' << 16) | ('E' << 24), |
170 | | /// Mount file structure. |
171 | | SMNT = ('S') | ('M' << 8) | ('N' << 16) | ('T' << 24), |
172 | | /// Use single port passive mode (only one TCP port number for both control connections and passive-mode data connections) |
173 | | SPSV = ('S') | ('P' << 8) | ('S' << 16) | ('V' << 24), |
174 | | /// Returns information on the server status, including the status of the current connection |
175 | | STAT = ('S') | ('T' << 8) | ('A' << 16) | ('T' << 24), |
176 | | /// Accept the data and to store the data as a file at the server site |
177 | | STOR = ('S') | ('T' << 8) | ('O' << 16) | ('R' << 24), |
178 | | /// Store file uniquely. |
179 | | STOU = ('S') | ('T' << 8) | ('O' << 16) | ('U' << 24), |
180 | | /// Set file transfer structure. |
181 | | STRU = ('S') | ('T' << 8) | ('R' << 16) | ('U' << 24), |
182 | | /// Return system type. |
183 | | SYST = ('S') | ('Y' << 8) | ('S' << 16) | ('T' << 24), |
184 | | /// Get a thumbnail of a remote image file |
185 | | THMB = ('T') | ('H' << 8) | ('M' << 16) | ('B' << 24), |
186 | | /// Sets the transfer mode (ASCII/Binary). |
187 | | TYPE = ('T') | ('Y' << 8) | ('P' << 16) | ('E' << 24), |
188 | | /// Authentication username. |
189 | | USER = ('U') | ('S' << 8) | ('E' << 16) | ('R' << 24), |
190 | | /// Change to the parent of the current working directory |
191 | | XCUP = ('X') | ('C' << 8) | ('U' << 16) | ('P' << 24), |
192 | | /// Make a directory |
193 | | XMKD = ('X') | ('M' << 8) | ('K' << 16) | ('D' << 24), |
194 | | /// Print the current working directory |
195 | | XPWD = ('X') | ('P' << 8) | ('W' << 16) | ('D' << 24), |
196 | | /// |
197 | | XRCP = ('X') | ('R' << 8) | ('C' << 16) | ('P' << 24), |
198 | | /// Remove the directory |
199 | | XRMD = ('X') | ('R' << 8) | ('M' << 16) | ('D' << 24), |
200 | | /// |
201 | | XRSQ = ('X') | ('R' << 8) | ('S' << 16) | ('Q' << 24), |
202 | | /// Send, mail if cannot |
203 | | XSEM = ('X') | ('S' << 8) | ('E' << 16) | ('M' << 24), |
204 | | /// Send to terminal |
205 | | XSEN = ('X') | ('S' << 8) | ('E' << 16) | ('N' << 24) |
206 | | }; |
207 | | |
208 | | /** A constructor that creates the layer from an existing packet raw data |
209 | | * @param[in] data A pointer to the raw data |
210 | | * @param[in] dataLen Size of the data in bytes |
211 | | * @param[in] prevLayer A pointer to the previous layer |
212 | | * @param[in] packet A pointer to the Packet instance where layer will be stored in |
213 | | */ |
214 | 235 | FtpRequestLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : FtpLayer(data, dataLen, prevLayer, packet) {}; |
215 | | |
216 | | /** |
217 | | * A constructor that creates layer with provided input values |
218 | | * @param[in] command FTP command |
219 | | * @param[in] option Argument of the command |
220 | | */ |
221 | 0 | explicit FtpRequestLayer(const FtpCommand &command, const std::string &option = "") : FtpLayer(getCommandAsString(command), option) {}; |
222 | | |
223 | | /** |
224 | | * Set the command of request message |
225 | | * @param[in] code Value to set command |
226 | | * @return True if the operation is successful, false otherwise |
227 | | */ |
228 | | bool setCommand(FtpCommand code); |
229 | | |
230 | | /** |
231 | | * Get the command of request message |
232 | | * @return FtpCommand Value of the command |
233 | | */ |
234 | | FtpCommand getCommand() const; |
235 | | |
236 | | /** |
237 | | * Get the command of request message as string |
238 | | * @return std::string Value of the command as string |
239 | | */ |
240 | | std::string getCommandString() const; |
241 | | |
242 | | /** |
243 | | * Set the command argument of request message |
244 | | * @param[in] value Value to set command argument |
245 | | * @return True if the operation is successful, false otherwise |
246 | | */ |
247 | | bool setCommandOption(const std::string &value); |
248 | | |
249 | | /** |
250 | | * Get the command argument of request message |
251 | | * @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not |
252 | | * @return std::string Value of command argument |
253 | | */ |
254 | | std::string getCommandOption(bool removeEscapeCharacters = true) const; |
255 | | |
256 | | /** |
257 | | * Convert the command info to readable string |
258 | | * @param[in] code Command code to convert |
259 | | * @return std::string Returns the command info as readable string |
260 | | */ |
261 | | static std::string getCommandInfo(FtpCommand code); |
262 | | |
263 | | /** |
264 | | * Convert the command to readable string |
265 | | * @param[in] code Command code to convert |
266 | | * @return std::string Returns the command as readable string |
267 | | */ |
268 | | static std::string getCommandAsString(FtpCommand code); |
269 | | |
270 | | // overridden methods |
271 | | |
272 | | /** |
273 | | * @return Returns the protocol info as readable string |
274 | | */ |
275 | | std::string toString() const; |
276 | | }; |
277 | | |
278 | | /** |
279 | | * Class for representing the response messages of FTP Layer |
280 | | */ |
281 | | class FtpResponseLayer : public FtpLayer |
282 | | { |
283 | | public: |
284 | | |
285 | | /** |
286 | | * Enum for FTP response codes |
287 | | */ |
288 | | enum class FtpStatusCode : int |
289 | | { |
290 | | /// Unknown status code |
291 | | UNKNOWN, |
292 | | /// Restart marker reply |
293 | | RESTART_MARKER = 110, |
294 | | /// Service ready in nnn minutes |
295 | | SERVICE_READY_IN_MIN = 120, |
296 | | /// Data connection already open; transfer starting |
297 | | DATA_ALREADY_OPEN_START_TRANSFER = 125, |
298 | | /// File status okay; about to open data connection |
299 | | FILE_OK = 150, |
300 | | /// Command okay |
301 | | COMMAND_OK = 200, |
302 | | /// Command not implemented, superfluous at this site |
303 | | COMMAND_NOT_IMPLEMENTED_SUPERFLUOUS = 202, |
304 | | /// System status, or system help reply |
305 | | SYSTEM_STATUS = 211, |
306 | | /// Directory status |
307 | | DIR_STATUS = 212, |
308 | | /// File status |
309 | | FILE_STATUS = 213, |
310 | | /// Help message |
311 | | HELP_MESSAGE = 214, |
312 | | /// NAME system type |
313 | | NAME_SYSTEM_TYPE = 215, |
314 | | /// Service ready for new user |
315 | | SERVICE_READY_FOR_USER = 220, |
316 | | /// Service closing control connection |
317 | | SERVICE_CLOSING_CONTROL = 221, |
318 | | /// Data connection open; no transfer in progress |
319 | | DATA_OPEN_NO_TRANSFER = 225, |
320 | | /// Closing data connection |
321 | | CLOSING_DATA = 226, |
322 | | /// Entering Passive Mode |
323 | | ENTERING_PASSIVE = 227, |
324 | | /// Entering Extended Passive Mode |
325 | | ENTERING_EXTENDED_PASSIVE = 229, |
326 | | /// User logged in, proceed |
327 | | USER_LOG_IN_PROCEED = 230, |
328 | | /// User logged in, authorized by security data exchange |
329 | | USER_LOG_IN_AUTHORIZED = 232, |
330 | | /// Security data exchange complete |
331 | | SEC_DATA_EXCHANGE_COMPLETE = 234, |
332 | | /// Security data exchange completed successfully |
333 | | SEC_DATA_EXCHANGE_COMPLETE_SUCCESS = 235, |
334 | | /// Requested file action okay, completed |
335 | | REQ_FILE_OK_COMPLETE = 250, |
336 | | /// PATHNAME created |
337 | | PATHNAME_CREATED = 257, |
338 | | /// User name okay, need password |
339 | | USER_OK_NEED_PASSWORD = 331, |
340 | | /// Need account for login |
341 | | NEED_ACCOUNT = 332, |
342 | | /// Requested security mechanism is ok |
343 | | REQ_SEC_MECHANISM_OK = 334, |
344 | | /// Security data is acceptable, more is required |
345 | | SEC_IS_ACCEPTABLE = 335, |
346 | | /// Username okay, need password. Challenge is ... |
347 | | USER_OK_NEED_PASS_CHALLENGE = 336, |
348 | | /// Requested file action pending further information |
349 | | FILE_PENDING_ACTION = 350, |
350 | | /// Service not available, closing control connection |
351 | | SERVICE_NOT_AVAILABLE = 421, |
352 | | /// Can't open data connection |
353 | | CANT_OPEN_DATA_CONNECTION = 425, |
354 | | /// Connection closed; transfer aborted |
355 | | CONNECTION_CLOSED = 426, |
356 | | /// Need some unavailable resource to process security |
357 | | NEED_UNAVAILABLE_RESOURCE_TO_SEC = 431, |
358 | | /// Requested file action not taken |
359 | | REQ_FILE_ACTION_NOT_TAKEN = 450, |
360 | | /// Requested action aborted: local error in processing |
361 | | REQ_ACTION_ABORTED = 451, |
362 | | /// Requested action not taken. Insufficient storage space in system |
363 | | REQ_ACTION_NOT_TAKEN = 452, |
364 | | /// Syntax error, command unrecognized |
365 | | SYNTAX_ERROR_COMMAND_UNRECOGNIZED = 500, |
366 | | /// Syntax error in parameters or arguments |
367 | | SYNTAX_ERROR_PARAMETER_OR_ARGUMENT = 501, |
368 | | /// Command not implemented |
369 | | COMMAND_NOT_IMPLEMENTED = 502, |
370 | | /// Bad sequence of commands |
371 | | BAD_SEQUENCE_COMMANDS = 503, |
372 | | /// Command not implemented for that parameter |
373 | | COMMAND_NOT_IMPLEMENTED_FOR_PARAMETER = 504, |
374 | | /// Network protocol not supported |
375 | | NETWORK_PROTOCOL_NOT_SUPPORTED = 522, |
376 | | /// Not logged in |
377 | | NOT_LOGGED_IN = 530, |
378 | | /// Need account for storing files |
379 | | NEED_ACCOUNT_FOR_STORE_FILE = 532, |
380 | | /// Command protection level denied for policy reasons |
381 | | COMMAND_PROTECTION_DENIED = 533, |
382 | | /// Request denied for policy reasons |
383 | | REQUEST_DENIED = 534, |
384 | | /// Failed security check (hash, sequence, etc) |
385 | | FAILED_SEC_CHECK = 535, |
386 | | /// Requested PROT level not supported by mechanism |
387 | | REQ_PROT_LEVEL_NOT_SUPPORTED = 536, |
388 | | /// Command protection level not supported by security mechanism |
389 | | COMMAND_PROTECTION_LEVEL_NOT_SUPPORTED = 537, |
390 | | /// Requested action not taken: File unavailable |
391 | | FILE_UNAVAILABLE = 550, |
392 | | /// Requested action aborted: page type unknown |
393 | | PAGE_TYPE_UNKNOWN = 551, |
394 | | /// Requested file action aborted: Exceeded storage allocation |
395 | | EXCEED_STORAGE_ALLOCATION = 552, |
396 | | /// Requested action not taken: File name not allowed |
397 | | FILENAME_NOT_ALLOWED = 553, |
398 | | /// Integrity protected reply |
399 | | INTEGRITY_PROTECTED = 631, |
400 | | /// Confidentiality and integrity protected reply |
401 | | CONFIDENTIALITY_AND_INTEGRITY_PROTECTED = 632, |
402 | | /// Confidentiality protected reply |
403 | | CONFIDENTIALITY_PROTECTED = 633 |
404 | | }; |
405 | | |
406 | | /** A constructor that creates the layer from an existing packet raw data |
407 | | * @param[in] data A pointer to the raw data |
408 | | * @param[in] dataLen Size of the data in bytes |
409 | | * @param[in] prevLayer A pointer to the previous layer |
410 | | * @param[in] packet A pointer to the Packet instance where layer will be stored in |
411 | | */ |
412 | 589 | FtpResponseLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet) : FtpLayer(data, dataLen, prevLayer, packet) {}; |
413 | | |
414 | | /** |
415 | | * A constructor that creates layer with provided input values |
416 | | * @param[in] code Status code |
417 | | * @param[in] option Argument of the status code |
418 | | */ |
419 | 0 | explicit FtpResponseLayer(const FtpStatusCode &code, const std::string &option = "") : FtpLayer(std::to_string(int(code)), option) {}; |
420 | | |
421 | | /** |
422 | | * Set the status code of response message |
423 | | * @param[in] code Value to set status code |
424 | | * @return True if the operation is successful, false otherwise |
425 | | */ |
426 | | bool setStatusCode(FtpStatusCode code); |
427 | | |
428 | | /** |
429 | | * Get the status code of response message |
430 | | * @return FtpStatusCode Value of the status code |
431 | | */ |
432 | | FtpStatusCode getStatusCode() const; |
433 | | |
434 | | /** |
435 | | * Get the status code of response message as string |
436 | | * @return std::string Value of the status code as string |
437 | | */ |
438 | | std::string getStatusCodeString() const; |
439 | | |
440 | | /** |
441 | | * Set the argument of response message |
442 | | * @param[in] value Value to set argument |
443 | | * @return True if the operation is successful, false otherwise |
444 | | */ |
445 | | bool setStatusOption(const std::string &value); |
446 | | |
447 | | /** |
448 | | * Get the argument of response message |
449 | | * @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not |
450 | | * @return std::string Value of argument |
451 | | */ |
452 | | std::string getStatusOption(bool removeEscapeCharacters = true) const; |
453 | | |
454 | | /** |
455 | | * Convert the status code to readable string |
456 | | * @param[in] code Status code to convert |
457 | | * @return std::string Returns the status info as readable string |
458 | | */ |
459 | | static std::string getStatusCodeAsString(FtpStatusCode code); |
460 | | |
461 | | // overridden methods |
462 | | |
463 | | /** |
464 | | * @return Returns the protocol info as readable string |
465 | | */ |
466 | | std::string toString() const; |
467 | | }; |
468 | | } // namespace pcpp |
469 | | |
470 | | #endif /* PACKETPP_FTP_LAYER */ |