]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-template.c
detect: remove unused arg from generic list inspect
[people/ms/suricata.git] / src / app-layer-template.c
CommitLineData
455eab37 1/* Copyright (C) 2015-2020 Open Information Security Foundation
c1b92126
JI
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
e878dd22
JI
18/*
19 * TODO: Update \author in this file and app-layer-template.h.
20 * TODO: Implement your app-layer logic with unit tests.
21 * TODO: Remove SCLogNotice statements or convert to debug.
22 */
23
c1b92126 24/**
e878dd22
JI
25 * \file
26 *
27 * \author FirstName LastName <yourname@domain>
28 *
29 * Template application layer detector and parser for learning and
c1b92126
JI
30 * template pruposes.
31 *
32 * This template implements a simple application layer for something
33 * like the echo protocol running on port 7.
34 */
35
36#include "suricata-common.h"
37#include "stream.h"
9697a09d 38#include "conf.h"
c1b92126
JI
39#include "app-layer-detect-proto.h"
40#include "app-layer-parser.h"
c1b92126
JI
41#include "app-layer-template.h"
42
a013cece 43#include "util-unittest.h"
7476399f 44#include "util-validate.h"
a013cece 45
c1b92126
JI
46/* The default port to probe for echo traffic if not provided in the
47 * configuration file. */
48#define TEMPLATE_DEFAULT_PORT "7"
49
a013cece 50/* The minimum size for a message. For some protocols this might
c1b92126
JI
51 * be the size of a header. */
52#define TEMPLATE_MIN_FRAME_LEN 1
53
a013cece 54/* Enum of app-layer events for the protocol. Normally you might
c1b92126 55 * have events for errors in parsing data, like unexpected data being
a013cece 56 * received. For template we'll make something up, and log an app-layer
c1b92126
JI
57 * level alert if an empty message is received.
58 *
59 * Example rule:
60 *
3da79610 61 * alert template any any -> any any (msg:"SURICATA Template empty message"; \
c1b92126
JI
62 * app-layer-event:template.empty_message; sid:X; rev:Y;)
63 */
64enum {
65 TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE,
66};
67
68SCEnumCharMap template_decoder_event_table[] = {
69 {"EMPTY_MESSAGE", TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE},
08e4908d
PC
70
71 // event table must be NULL-terminated
72 { NULL, -1 },
c1b92126
JI
73};
74
a013cece 75static TemplateTransaction *TemplateTxAlloc(TemplateState *state)
c1b92126
JI
76{
77 TemplateTransaction *tx = SCCalloc(1, sizeof(TemplateTransaction));
78 if (unlikely(tx == NULL)) {
79 return NULL;
80 }
81
82 /* Increment the transaction ID on the state each time one is
83 * allocated. */
a013cece 84 tx->tx_id = state->transaction_max++;
c1b92126 85
a013cece 86 TAILQ_INSERT_TAIL(&state->tx_list, tx, next);
c1b92126
JI
87
88 return tx;
89}
90
a013cece 91static void TemplateTxFree(void *txv)
c1b92126 92{
a013cece 93 TemplateTransaction *tx = txv;
c1b92126 94
a013cece
VJ
95 if (tx->request_buffer != NULL) {
96 SCFree(tx->request_buffer);
c1b92126
JI
97 }
98
a013cece
VJ
99 if (tx->response_buffer != NULL) {
100 SCFree(tx->response_buffer);
c1b92126
JI
101 }
102
a013cece 103 AppLayerDecoderEventsFreeEvents(&tx->decoder_events);
c1b92126
JI
104
105 SCFree(tx);
106}
107
547d6c2d 108static void *TemplateStateAlloc(void *orig_state, AppProto proto_orig)
c1b92126
JI
109{
110 SCLogNotice("Allocating template state.");
111 TemplateState *state = SCCalloc(1, sizeof(TemplateState));
112 if (unlikely(state == NULL)) {
113 return NULL;
114 }
115 TAILQ_INIT(&state->tx_list);
116 return state;
117}
118
119static void TemplateStateFree(void *state)
120{
121 TemplateState *template_state = state;
122 TemplateTransaction *tx;
123 SCLogNotice("Freeing template state.");
124 while ((tx = TAILQ_FIRST(&template_state->tx_list)) != NULL) {
125 TAILQ_REMOVE(&template_state->tx_list, tx, next);
126 TemplateTxFree(tx);
127 }
128 SCFree(template_state);
129}
130
131/**
132 * \brief Callback from the application layer to have a transaction freed.
133 *
134 * \param state a void pointer to the TemplateState object.
135 * \param tx_id the transaction ID to free.
136 */
a013cece 137static void TemplateStateTxFree(void *statev, uint64_t tx_id)
c1b92126 138{
a013cece 139 TemplateState *state = statev;
c1b92126
JI
140 TemplateTransaction *tx = NULL, *ttx;
141
142 SCLogNotice("Freeing transaction %"PRIu64, tx_id);
143
a013cece 144 TAILQ_FOREACH_SAFE(tx, &state->tx_list, next, ttx) {
c1b92126
JI
145
146 /* Continue if this is not the transaction we are looking
147 * for. */
148 if (tx->tx_id != tx_id) {
149 continue;
150 }
151
152 /* Remove and free the transaction. */
a013cece 153 TAILQ_REMOVE(&state->tx_list, tx, next);
c1b92126
JI
154 TemplateTxFree(tx);
155 return;
156 }
157
158 SCLogNotice("Transaction %"PRIu64" not found.", tx_id);
159}
160
161static int TemplateStateGetEventInfo(const char *event_name, int *event_id,
162 AppLayerEventType *event_type)
163{
164 *event_id = SCMapEnumNameToValue(event_name, template_decoder_event_table);
165 if (*event_id == -1) {
166 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
167 "template enum map table.", event_name);
168 /* This should be treated as fatal. */
169 return -1;
170 }
171
172 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
173
174 return 0;
175}
176
50e23ba9
JL
177static int TemplateStateGetEventInfoById(int event_id, const char **event_name,
178 AppLayerEventType *event_type)
179{
180 *event_name = SCMapEnumValueToName(event_id, template_decoder_event_table);
181 if (*event_name == NULL) {
182 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%d\" not present in "
183 "template enum map table.", event_id);
184 /* This should be treated as fatal. */
185 return -1;
186 }
187
188 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
189
190 return 0;
191}
192
d568e7fa 193static AppLayerDecoderEvents *TemplateGetEvents(void *tx)
c1b92126 194{
d568e7fa 195 return ((TemplateTransaction *)tx)->decoder_events;
c1b92126
JI
196}
197
c1b92126 198/**
5ff50773 199 * \brief Probe the input to server to see if it looks like template.
c1b92126 200 *
5ff50773
PA
201 * \retval ALPROTO_TEMPLATE if it looks like template,
202 * ALPROTO_FAILED, if it is clearly not ALPROTO_TEMPLATE,
203 * otherwise ALPROTO_UNKNOWN.
c1b92126 204 */
5ff50773 205static AppProto TemplateProbingParserTs(Flow *f, uint8_t direction,
579cc9f0 206 const uint8_t *input, uint32_t input_len, uint8_t *rdir)
5ff50773
PA
207{
208 /* Very simple test - if there is input, this is template. */
209 if (input_len >= TEMPLATE_MIN_FRAME_LEN) {
210 SCLogNotice("Detected as ALPROTO_TEMPLATE.");
211 return ALPROTO_TEMPLATE;
212 }
213
214 SCLogNotice("Protocol not detected as ALPROTO_TEMPLATE.");
215 return ALPROTO_UNKNOWN;
216}
217
218/**
219 * \brief Probe the input to client to see if it looks like template.
220 * TemplateProbingParserTs can be used instead if the protocol
221 * is symmetric.
222 *
223 * \retval ALPROTO_TEMPLATE if it looks like template,
224 * ALPROTO_FAILED, if it is clearly not ALPROTO_TEMPLATE,
225 * otherwise ALPROTO_UNKNOWN.
226 */
227static AppProto TemplateProbingParserTc(Flow *f, uint8_t direction,
579cc9f0 228 const uint8_t *input, uint32_t input_len, uint8_t *rdir)
c1b92126 229{
a013cece 230 /* Very simple test - if there is input, this is template. */
c1b92126
JI
231 if (input_len >= TEMPLATE_MIN_FRAME_LEN) {
232 SCLogNotice("Detected as ALPROTO_TEMPLATE.");
233 return ALPROTO_TEMPLATE;
234 }
235
236 SCLogNotice("Protocol not detected as ALPROTO_TEMPLATE.");
237 return ALPROTO_UNKNOWN;
238}
239
44d3f264 240static AppLayerResult TemplateParseRequest(Flow *f, void *statev,
579cc9f0 241 AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len,
7bc3c3ac 242 void *local_data, const uint8_t flags)
c1b92126 243{
a013cece 244 TemplateState *state = statev;
c1b92126 245
a013cece 246 SCLogNotice("Parsing template request: len=%"PRIu32, input_len);
c1b92126 247
7476399f 248 if (input == NULL) {
4f73943d 249 if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS)) {
7476399f
JI
250 /* This is a signal that the stream is done. Do any
251 * cleanup if needed. Usually nothing is required here. */
252 SCReturnStruct(APP_LAYER_OK);
253 } else if (flags & STREAM_GAP) {
254 /* This is a signal that there has been a gap in the
255 * stream. This only needs to be handled if gaps were
256 * enabled during protocol registration. The input_len
257 * contains the size of the gap. */
258 SCReturnStruct(APP_LAYER_OK);
259 }
260 /* This should not happen. If input is NULL, one of the above should be
261 * true. */
262 DEBUG_VALIDATE_BUG_ON(true);
263 SCReturnStruct(APP_LAYER_ERROR);
c1b92126
JI
264 }
265
266 /* Normally you would parse out data here and store it in the
267 * transaction object, but as this is echo, we'll just record the
268 * request data. */
269
270 /* Also, if this protocol may have a "protocol data unit" span
271 * multiple chunks of data, which is always a possibility with
272 * TCP, you may need to do some buffering here.
273 *
274 * For the sake of simplicity, buffering is left out here, but
275 * even for an echo protocol we may want to buffer until a new
276 * line is seen, assuming its text based.
277 */
278
279 /* Allocate a transaction.
280 *
281 * But note that if a "protocol data unit" is not received in one
282 * chunk of data, and the buffering is done on the transaction, we
283 * may need to look for the transaction that this newly recieved
284 * data belongs to.
285 */
a013cece 286 TemplateTransaction *tx = TemplateTxAlloc(state);
c1b92126
JI
287 if (unlikely(tx == NULL)) {
288 SCLogNotice("Failed to allocate new Template tx.");
289 goto end;
290 }
291 SCLogNotice("Allocated Template tx %"PRIu64".", tx->tx_id);
a013cece 292
c1b92126
JI
293 /* Make a copy of the request. */
294 tx->request_buffer = SCCalloc(1, input_len);
295 if (unlikely(tx->request_buffer == NULL)) {
296 goto end;
297 }
298 memcpy(tx->request_buffer, input, input_len);
299 tx->request_buffer_len = input_len;
300
301 /* Here we check for an empty message and create an app-layer
302 * event. */
303 if ((input_len == 1 && tx->request_buffer[0] == '\n') ||
304 (input_len == 2 && tx->request_buffer[0] == '\r')) {
305 SCLogNotice("Creating event for empty message.");
306 AppLayerDecoderEventsSetEventRaw(&tx->decoder_events,
307 TEMPLATE_DECODER_EVENT_EMPTY_MESSAGE);
c1b92126
JI
308 }
309
a013cece 310end:
44d3f264 311 SCReturnStruct(APP_LAYER_OK);
c1b92126
JI
312}
313
44d3f264 314static AppLayerResult TemplateParseResponse(Flow *f, void *statev, AppLayerParserState *pstate,
579cc9f0 315 const uint8_t *input, uint32_t input_len, void *local_data,
7bc3c3ac 316 const uint8_t flags)
c1b92126 317{
a013cece
VJ
318 TemplateState *state = statev;
319 TemplateTransaction *tx = NULL, *ttx;
c1b92126
JI
320
321 SCLogNotice("Parsing Template response.");
322
323 /* Likely connection closed, we can just return here. */
324 if ((input == NULL || input_len == 0) &&
4f73943d 325 AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC)) {
44d3f264 326 SCReturnStruct(APP_LAYER_OK);
c1b92126
JI
327 }
328
329 /* Probably don't want to create a transaction in this case
330 * either. */
331 if (input == NULL || input_len == 0) {
44d3f264 332 SCReturnStruct(APP_LAYER_OK);
c1b92126
JI
333 }
334
335 /* Look up the existing transaction for this response. In the case
336 * of echo, it will be the most recent transaction on the
337 * TemplateState object. */
338
339 /* We should just grab the last transaction, but this is to
340 * illustrate how you might traverse the transaction list to find
341 * the transaction associated with this response. */
a013cece 342 TAILQ_FOREACH(ttx, &state->tx_list, next) {
c1b92126
JI
343 tx = ttx;
344 }
a013cece 345
c1b92126 346 if (tx == NULL) {
a013cece
VJ
347 SCLogNotice("Failed to find transaction for response on state %p.",
348 state);
c1b92126
JI
349 goto end;
350 }
351
a013cece
VJ
352 SCLogNotice("Found transaction %"PRIu64" for response on state %p.",
353 tx->tx_id, state);
c1b92126
JI
354
355 /* If the protocol requires multiple chunks of data to complete, you may
356 * run into the case where you have existing response data.
357 *
358 * In this case, we just log that there is existing data and free it. But
359 * you might want to realloc the buffer and append the data.
360 */
361 if (tx->response_buffer != NULL) {
362 SCLogNotice("WARNING: Transaction already has response data, "
363 "existing data will be overwritten.");
364 SCFree(tx->response_buffer);
365 }
366
367 /* Make a copy of the response. */
368 tx->response_buffer = SCCalloc(1, input_len);
369 if (unlikely(tx->response_buffer == NULL)) {
370 goto end;
371 }
372 memcpy(tx->response_buffer, input, input_len);
373 tx->response_buffer_len = input_len;
374
375 /* Set the response_done flag for transaction state checking in
376 * TemplateGetStateProgress(). */
377 tx->response_done = 1;
378
379end:
44d3f264 380 SCReturnStruct(APP_LAYER_OK);
c1b92126
JI
381}
382
a013cece 383static uint64_t TemplateGetTxCnt(void *statev)
c1b92126 384{
a013cece
VJ
385 const TemplateState *state = statev;
386 SCLogNotice("Current tx count is %"PRIu64".", state->transaction_max);
387 return state->transaction_max;
c1b92126
JI
388}
389
a013cece 390static void *TemplateGetTx(void *statev, uint64_t tx_id)
c1b92126 391{
a013cece 392 TemplateState *state = statev;
c1b92126
JI
393 TemplateTransaction *tx;
394
395 SCLogNotice("Requested tx ID %"PRIu64".", tx_id);
396
a013cece 397 TAILQ_FOREACH(tx, &state->tx_list, next) {
c1b92126
JI
398 if (tx->tx_id == tx_id) {
399 SCLogNotice("Transaction %"PRIu64" found, returning tx object %p.",
400 tx_id, tx);
401 return tx;
402 }
403 }
404
405 SCLogNotice("Transaction ID %"PRIu64" not found.", tx_id);
406 return NULL;
407}
408
409/**
410 * \brief Called by the application layer.
411 *
412 * In most cases 1 can be returned here.
413 */
414static int TemplateGetAlstateProgressCompletionStatus(uint8_t direction) {
415 return 1;
416}
417
418/**
419 * \brief Return the state of a transaction in a given direction.
420 *
421 * In the case of the echo protocol, the existence of a transaction
422 * means that the request is done. However, some protocols that may
423 * need multiple chunks of data to complete the request may need more
424 * than just the existence of a transaction for the request to be
425 * considered complete.
426 *
427 * For the response to be considered done, the response for a request
428 * needs to be seen. The response_done flag is set on response for
429 * checking here.
430 */
a013cece 431static int TemplateGetStateProgress(void *txv, uint8_t direction)
c1b92126 432{
a013cece 433 TemplateTransaction *tx = txv;
c1b92126
JI
434
435 SCLogNotice("Transaction progress requested for tx ID %"PRIu64
a013cece 436 ", direction=0x%02x", tx->tx_id, direction);
c1b92126 437
a013cece 438 if (direction & STREAM_TOCLIENT && tx->response_done) {
c1b92126
JI
439 return 1;
440 }
441 else if (direction & STREAM_TOSERVER) {
a013cece 442 /* For the template, just the existence of the transaction means the
c1b92126
JI
443 * request is done. */
444 return 1;
445 }
446
447 return 0;
448}
449
455eab37
VJ
450/**
451 * \brief retrieve the tx data used for logging, config, detection
452 */
453static AppLayerTxData *TemplateGetTxData(void *vtx)
454{
455 TemplateTransaction *tx = vtx;
456 return &tx->tx_data;
457}
458
c1b92126 459/**
a013cece 460 * \brief retrieve the detection engine per tx state
c1b92126
JI
461 */
462static DetectEngineState *TemplateGetTxDetectState(void *vtx)
463{
464 TemplateTransaction *tx = vtx;
465 return tx->de_state;
466}
467
468/**
a013cece 469 * \brief get the detection engine per tx state
c1b92126 470 */
7548944b 471static int TemplateSetTxDetectState(void *vtx,
c1b92126
JI
472 DetectEngineState *s)
473{
474 TemplateTransaction *tx = vtx;
475 tx->de_state = s;
476 return 0;
477}
478
479void RegisterTemplateParsers(void)
480{
ab1200fb 481 const char *proto_name = "template";
c1b92126 482
9697a09d
JI
483 /* TEMPLATE_START_REMOVE */
484 if (ConfGetNode("app-layer.protocols.template") == NULL) {
485 return;
486 }
487 /* TEMPLATE_END_REMOVE */
c1b92126
JI
488 /* Check if Template TCP detection is enabled. If it does not exist in
489 * the configuration file then it will be enabled by default. */
490 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
491
492 SCLogNotice("Template TCP protocol detection enabled.");
493
494 AppLayerProtoDetectRegisterProtocol(ALPROTO_TEMPLATE, proto_name);
495
496 if (RunmodeIsUnittests()) {
497
498 SCLogNotice("Unittest mode, registeringd default configuration.");
499 AppLayerProtoDetectPPRegister(IPPROTO_TCP, TEMPLATE_DEFAULT_PORT,
500 ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER,
5ff50773 501 TemplateProbingParserTs, TemplateProbingParserTc);
c1b92126
JI
502
503 }
504 else {
505
506 if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
507 proto_name, ALPROTO_TEMPLATE, 0, TEMPLATE_MIN_FRAME_LEN,
5ff50773 508 TemplateProbingParserTs, TemplateProbingParserTc)) {
a013cece 509 SCLogNotice("No template app-layer configuration, enabling echo"
c1b92126
JI
510 " detection TCP detection on port %s.",
511 TEMPLATE_DEFAULT_PORT);
512 AppLayerProtoDetectPPRegister(IPPROTO_TCP,
513 TEMPLATE_DEFAULT_PORT, ALPROTO_TEMPLATE, 0,
514 TEMPLATE_MIN_FRAME_LEN, STREAM_TOSERVER,
5ff50773 515 TemplateProbingParserTs, TemplateProbingParserTc);
c1b92126
JI
516 }
517
518 }
519
520 }
521
522 else {
523 SCLogNotice("Protocol detecter and parser disabled for Template.");
524 return;
525 }
526
a013cece 527 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
c1b92126
JI
528
529 SCLogNotice("Registering Template protocol parser.");
530
531 /* Register functions for state allocation and freeing. A
532 * state is allocated for every new Template flow. */
533 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE,
534 TemplateStateAlloc, TemplateStateFree);
535
536 /* Register request parser for parsing frame from server to client. */
537 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE,
538 STREAM_TOSERVER, TemplateParseRequest);
539
540 /* Register response parser for parsing frames from server to client. */
9697a09d
JI
541 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TEMPLATE,
542 STREAM_TOCLIENT, TemplateParseResponse);
c1b92126
JI
543
544 /* Register a function to be called by the application layer
545 * when a transaction is to be freed. */
546 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
547 TemplateStateTxFree);
548
549 /* Register a function to return the current transaction count. */
9697a09d
JI
550 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TEMPLATE,
551 TemplateGetTxCnt);
c1b92126
JI
552
553 /* Transaction handling. */
c4b918b6
MK
554 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_TEMPLATE,
555 TemplateGetAlstateProgressCompletionStatus);
c1b92126
JI
556 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP,
557 ALPROTO_TEMPLATE, TemplateGetStateProgress);
558 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TEMPLATE,
559 TemplateGetTx);
455eab37
VJ
560 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
561 TemplateGetTxData);
c1b92126 562
c1b92126
JI
563 /* What is this being registered for? */
564 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_TEMPLATE,
7548944b 565 TemplateGetTxDetectState, TemplateSetTxDetectState);
c1b92126
JI
566
567 AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TEMPLATE,
568 TemplateStateGetEventInfo);
50e23ba9
JL
569 AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_TEMPLATE,
570 TemplateStateGetEventInfoById);
c1b92126
JI
571 AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_TEMPLATE,
572 TemplateGetEvents);
7476399f
JI
573
574 /* Leave this is if you parser can handle gaps, otherwise
575 * remove. */
576 AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_TEMPLATE,
577 APP_LAYER_PARSER_OPT_ACCEPT_GAPS);
c1b92126
JI
578 }
579 else {
580 SCLogNotice("Template protocol parsing disabled.");
581 }
582
583#ifdef UNITTESTS
584 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_TEMPLATE,
585 TemplateParserRegisterTests);
586#endif
587}
588
589#ifdef UNITTESTS
590#endif
591
592void TemplateParserRegisterTests(void)
593{
594#ifdef UNITTESTS
595#endif
596}