~ubuntu-branches/ubuntu/trusty/keepalived/trusty

« back to all changes in this revision

Viewing changes to keepalived/check/check_smtp.c

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Wirt
  • Date: 2005-04-29 23:22:40 UTC
  • mfrom: (1.1.1 upstream) (2.1.1 hoary)
  • Revision ID: james.westby@ubuntu.com-20050429232240-a8m3jtpi3cvuyyy2
Tags: 1.1.11-3
Added a warning about sarge kernels to README.Debian and 
the package description 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Soft:        Keepalived is a failover program for the LVS project
 
3
 *              <www.linuxvirtualserver.org>. It monitor & manipulate
 
4
 *              a loadbalanced server pool using multi-layer checks.
 
5
 *
 
6
 * Part:        SMTP CHECK. Check an SMTP-server.
 
7
 *
 
8
 * Version:     $Id: check_smtp.c,v 1.1.11 2005/03/01 01:22:13 acassen Exp $
 
9
 *
 
10
 * Authors:     Jeremy Rumpf, <jrumpf@heavyload.net>
 
11
 *              Alexandre Cassen, <acassen@linux-vs.org>
 
12
 *
 
13
 *              This program is distributed in the hope that it will be useful,
 
14
 *              but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
16
 *              See the GNU General Public License for more details.
 
17
 *
 
18
 *              This program is free software; you can redistribute it and/or
 
19
 *              modify it under the terms of the GNU General Public License
 
20
 *              as published by the Free Software Foundation; either version
 
21
 *              2 of the License, or (at your option) any later version.
 
22
 *
 
23
 * Copyright (C) 2001-2005 Alexandre Cassen, <acassen@linux-vs.org>
 
24
 */
 
25
 
 
26
#include <ctype.h>
 
27
 
 
28
#include "check_smtp.h"
 
29
#include "check_api.h"
 
30
#include "memory.h"
 
31
#include "ipwrapper.h"
 
32
#include "utils.h"
 
33
#include "parser.h"
 
34
#include "daemon.h"
 
35
 
 
36
int smtp_connect_thread(thread *);
 
37
 
 
38
/* module variables */
 
39
static smtp_host *default_host = NULL;
 
40
 
 
41
/*
 
42
 * Used as a callback from free_list() to free all
 
43
 * the list elements in smtp_checker->host before we
 
44
 * free smtp_checker itself.
 
45
 */
 
46
void
 
47
smtp_free_host(void *data)
 
48
{
 
49
        FREE(data);
 
50
}
 
51
 
 
52
/* Used as a callback from the checker api, queue_checker(),
 
53
 * to free up a checker entry and all its associated data.
 
54
 */
 
55
void
 
56
free_smtp_check(void *data)
 
57
{
 
58
        smtp_checker *smtp_chk = CHECKER_DATA(data);
 
59
        free_list(smtp_chk->host);
 
60
        FREE(smtp_chk->helo_name);
 
61
        FREE(smtp_chk);
 
62
        FREE(data);
 
63
}
 
64
 
 
65
/*
 
66
 * Used as a callback from dump_list() to print out all
 
67
 * the list elements in smtp_checker->host.
 
68
 */
 
69
void
 
70
smtp_dump_host(void *data)
 
71
{
 
72
        smtp_host *smtp_hst = data;
 
73
        syslog(LOG_INFO, "   Checked ip = %s", inet_ntop2(smtp_hst->ip));
 
74
        syslog(LOG_INFO, "           port = %d", ntohs(smtp_hst->port));
 
75
        if (smtp_hst->bindto)
 
76
                syslog(LOG_INFO, "           bindto = %s", inet_ntop2(smtp_hst->bindto));
 
77
}
 
78
 
 
79
/* 
 
80
 * Callback for whenever we've been requested to dump our
 
81
 * configuration.
 
82
 */
 
83
void
 
84
dump_smtp_check(void *data)
 
85
{
 
86
        smtp_checker *smtp_chk = CHECKER_DATA(data);
 
87
        syslog(LOG_INFO, "   Keepalive method = SMTP_CHECK");
 
88
        syslog(LOG_INFO, "           helo = %s", smtp_chk->helo_name);
 
89
        syslog(LOG_INFO, "           timeout = %ld", smtp_chk->timeout/TIMER_HZ);
 
90
        syslog(LOG_INFO, "           retry = %d", smtp_chk->retry);
 
91
        syslog(LOG_INFO, "           delay before retry = %ld", smtp_chk->db_retry/TIMER_HZ);
 
92
        dump_list(smtp_chk->host);
 
93
}
 
94
 
 
95
/* Allocates a default host structure */
 
96
smtp_host *
 
97
smtp_alloc_host(void)
 
98
{
 
99
        checker *chk = LIST_TAIL_DATA(checkers_queue);
 
100
        smtp_host *new;
 
101
 
 
102
        /* Allocate the new host data structure */
 
103
        new = (smtp_host *)MALLOC(sizeof(smtp_host));
 
104
 
 
105
        /* 
 
106
         * By default we set the ip to connect to as the same ip as the current real server
 
107
         * in the rs config. This might be overridden later on by a "connect_ip" keyword.
 
108
         */
 
109
        new->ip = CHECKER_RIP(chk);
 
110
        new->port = htons(SMTP_DEFAULT_PORT);
 
111
        return new;
 
112
}
 
113
 
 
114
/* 
 
115
 * Callback for whenever an SMTP_CHECK keyword is encountered
 
116
 * in the config file. 
 
117
 */
 
118
void
 
119
smtp_check_handler(vector strvec)
 
120
{
 
121
        smtp_checker *smtp_chk = (smtp_checker *)MALLOC(sizeof(smtp_checker));
 
122
 
 
123
        /* 
 
124
         * Set something sane for the default HELO banner
 
125
         * May be overridden by a "helo_name" keyword later.
 
126
         */
 
127
        smtp_chk->helo_name = (char *)MALLOC(strlen(SMTP_DEFAULT_HELO) + 1);
 
128
        memcpy(smtp_chk->helo_name, SMTP_DEFAULT_HELO, strlen(SMTP_DEFAULT_HELO) + 1);
 
129
 
 
130
        /* some other sane values */
 
131
        smtp_chk->timeout = 5 * TIMER_HZ;
 
132
        smtp_chk->db_retry = 1 * TIMER_HZ;
 
133
        smtp_chk->retry = 1;
 
134
 
 
135
        /*
 
136
         * Have the checker queue code put our checker into the checkers_queue
 
137
         * list.
 
138
         *
 
139
         * queue_checker(void (*free) (void *), void (*dump) (void *),
 
140
         *               int (*launch) (struct _thread *),
 
141
         *               void *data)
 
142
         */
 
143
        queue_checker(free_smtp_check, dump_smtp_check, smtp_connect_thread,
 
144
                      smtp_chk);
 
145
 
 
146
        /* 
 
147
         * Last, allocate/setup the list that will hold all the per host 
 
148
         * configuration structures. We'll set a "default host", which
 
149
         * is the same ip as the real server. If there are additional "host"
 
150
         * sections in the config, the default will be deleted and overridden.
 
151
         * This must come after queue_checker()!
 
152
         */
 
153
        smtp_chk->host = alloc_list(smtp_free_host, smtp_dump_host);
 
154
        if (!default_host)
 
155
                default_host = smtp_alloc_host();
 
156
        list_add(smtp_chk->host, default_host);
 
157
}
 
158
 
 
159
/* 
 
160
 * Callback for whenever the "host" keyword is encountered
 
161
 * in the config file. 
 
162
 */
 
163
void
 
164
smtp_host_handler(vector strvec)
 
165
{
 
166
        smtp_checker *smtp_chk = CHECKER_GET();
 
167
 
 
168
        /*
 
169
         * If the default host is still allocated, delete it
 
170
         * before we stick user defined hosts in the list.
 
171
         */
 
172
        if (default_host) {
 
173
                list_del(smtp_chk->host, default_host);
 
174
                FREE(default_host);
 
175
                default_host = NULL;
 
176
        }
 
177
 
 
178
        /* add an empty host to the list, smtp_checker->host */
 
179
        list_add(smtp_chk->host, smtp_alloc_host());
 
180
}
 
181
 
 
182
/* "connect_ip" keyword */
 
183
void
 
184
smtp_ip_handler(vector strvec)
 
185
{
 
186
        smtp_checker *smtp_chk = CHECKER_GET();
 
187
        smtp_host *smtp_hst = LIST_TAIL_DATA(smtp_chk->host);
 
188
        inet_ston(VECTOR_SLOT(strvec, 1), &smtp_hst->ip);
 
189
}
 
190
 
 
191
/* "connect_port" keyword */
 
192
void
 
193
smtp_port_handler(vector strvec)
 
194
{
 
195
        smtp_checker *smtp_chk = CHECKER_GET();
 
196
        smtp_host *smtp_hst = LIST_TAIL_DATA(smtp_chk->host);
 
197
        smtp_hst->port = htons(CHECKER_VALUE_INT(strvec)); 
 
198
}
 
199
 
 
200
/* "helo_name" keyword */
 
201
void
 
202
smtp_helo_name_handler(vector strvec)
 
203
{
 
204
        smtp_checker *smtp_chk = CHECKER_GET();
 
205
        smtp_chk->helo_name = CHECKER_VALUE_STRING(strvec);
 
206
}
 
207
 
 
208
/* "connect_timeout" keyword */
 
209
void
 
210
smtp_timeout_handler(vector strvec)
 
211
{
 
212
        smtp_checker *smtp_chk = CHECKER_GET();
 
213
        smtp_chk->timeout = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
 
214
}
 
215
 
 
216
/* "retry" keyword */
 
217
void
 
218
smtp_retry_handler(vector strvec)
 
219
{
 
220
        smtp_checker *smtp_chk = CHECKER_GET();
 
221
        smtp_chk->retry = CHECKER_VALUE_INT(strvec);
 
222
}
 
223
 
 
224
/* "delay_before_retry" keyword */
 
225
void
 
226
smtp_db_retry_handler(vector strvec)
 
227
{
 
228
        smtp_checker *smtp_chk = CHECKER_GET();
 
229
        smtp_chk->db_retry = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
 
230
}
 
231
 
 
232
/* "bindto" keyword */
 
233
void
 
234
smtp_bindto_handler(vector strvec)
 
235
{
 
236
        smtp_checker *smtp_chk = CHECKER_GET();
 
237
        smtp_host *smtp_hst = LIST_TAIL_DATA(smtp_chk->host);
 
238
        inet_ston(VECTOR_SLOT(strvec, 1), &smtp_hst->bindto);
 
239
}
 
240
 
 
241
/* Config callback installer */
 
242
void
 
243
install_smtp_check_keyword(void)
 
244
{
 
245
        /* 
 
246
         * Notify the config log parser that we need to be notified via
 
247
         * callbacks when the following keywords are encountered in the
 
248
         * keepalive.conf file.
 
249
         */
 
250
        install_keyword("SMTP_CHECK", &smtp_check_handler);
 
251
        install_sublevel();
 
252
        install_keyword("helo_name", &smtp_helo_name_handler);
 
253
        install_keyword("connect_timeout", &smtp_timeout_handler);
 
254
        install_keyword("delay_before_retry", &smtp_db_retry_handler);
 
255
        install_keyword("retry", &smtp_retry_handler);
 
256
        install_keyword("host", &smtp_host_handler);
 
257
        install_sublevel();
 
258
        install_keyword("connect_ip", &smtp_ip_handler);
 
259
        install_keyword("connect_port", &smtp_port_handler);
 
260
        install_keyword("bindto", &smtp_bindto_handler);
 
261
        install_sublevel_end();
 
262
        install_sublevel_end();
 
263
}
 
264
 
 
265
/*
 
266
 * Final handler. Determines if we need a retry or not. 
 
267
 * Also has to make a decision if we need to bring the resulting
 
268
 * service down in case of error.
 
269
 */
 
270
int
 
271
smtp_final(thread *thread_obj, int error, const char *format, ...)
 
272
{
 
273
        checker *chk = THREAD_ARG(thread_obj);
 
274
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
275
        char error_buff[512];
 
276
        char smtp_buff[542];
 
277
        va_list varg_list;
 
278
 
 
279
        /* Error or no error we should always have to close the socket */
 
280
        close(thread_obj->u.fd);
 
281
 
 
282
        /* If we're here, an attempt HAS been made already for the current host */
 
283
        smtp_chk->attempts++;
 
284
        
 
285
        if (error) {
 
286
                /* Always syslog the error when the real server is up */
 
287
                if (svr_checker_up(chk->id, chk->rs)) {
 
288
                        if (format != NULL) {
 
289
                                memcpy(error_buff, "SMTP_CHECK ", 11);
 
290
                                va_start(varg_list, format);
 
291
                                vsnprintf(error_buff + 11, 512 - 11, format, varg_list);
 
292
                                va_end(varg_list);
 
293
                                error_buff[512 - 1] = '\0';
 
294
 
 
295
                                syslog(LOG_INFO, error_buff);
 
296
                        } else {
 
297
                                syslog(LOG_INFO, "SMTP_CHECK Unknown error");
 
298
                        }
 
299
                }
 
300
 
 
301
                /*
 
302
                 * If we still have retries left, try this host again by
 
303
                 * scheduling the main thread to check it again after the
 
304
                 * configured backoff delay. Otherwise down the RS.
 
305
                 */
 
306
                if (smtp_chk->attempts < smtp_chk->retry) {
 
307
                        thread_add_timer(thread_obj->master, smtp_connect_thread, chk,
 
308
                                         smtp_chk->db_retry);
 
309
                        return 0;
 
310
                }
 
311
 
 
312
                /*
 
313
                 * No more retries, pull the real server from the virtual server.
 
314
                 * Only smtp_alert if it wasn't previously down. It should
 
315
                 * be noted that smtp_alert makes a copy of the string arguments, so
 
316
                 * we don't have to keep them statically allocated.
 
317
                 */
 
318
                if (svr_checker_up(chk->id, chk->rs)) {
 
319
                        if (format != NULL) {
 
320
                                snprintf(smtp_buff, 542, "=> CHECK failed on service : %s <=",
 
321
                                         error_buff + 11);
 
322
                        } else {
 
323
                                snprintf(smtp_buff, 542, "=> CHECK failed on service <=");
 
324
                        }
 
325
 
 
326
                        smtp_buff[542 - 1] = '\0';
 
327
                        smtp_alert(chk->rs, NULL, NULL, "DOWN", smtp_buff);
 
328
                }
 
329
 
 
330
                update_svr_checker_state(DOWN, chk->id, chk->vs, chk->rs);
 
331
 
 
332
                /* Reset everything back to the first host in the list */
 
333
                smtp_chk->attempts = 0;
 
334
                smtp_chk->host_ctr = 0;
 
335
 
 
336
                /* Reschedule the main thread using the configured delay loop */;
 
337
                thread_add_timer(thread_obj->master, smtp_connect_thread, chk, chk->vs->delay_loop);
 
338
 
 
339
                return 0;
 
340
        }       
 
341
 
 
342
        /*
 
343
         * Ok this host was successful, increment to the next host in the list
 
344
         * and reset the attempts counter. We'll then reschedule the main thread again.
 
345
         * If host_ctr exceeds the number of hosts in the list, http_main_thread will
 
346
         * take note and bring up the real server as well as inject the delay_loop.
 
347
         */
 
348
        smtp_chk->attempts = 0;
 
349
        smtp_chk->host_ctr++;
 
350
 
 
351
        thread_add_timer(thread_obj->master, smtp_connect_thread, chk, 1);
 
352
        return 0;
 
353
}
 
354
 
 
355
/* 
 
356
 * Zeros out the rx/tx buffer
 
357
 */
 
358
void
 
359
smtp_clear_buff(thread *thread_obj)
 
360
{
 
361
        checker *chk = THREAD_ARG(thread_obj);
 
362
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
363
        memset(smtp_chk->buff, 0, SMTP_BUFF_MAX);
 
364
        smtp_chk->buff_ctr = 0;
 
365
}
 
366
 
 
367
/*
 
368
 * One thing to note here is we do a very cheap check for a newline.
 
369
 * We could receive two lines (with two newline characters) in a
 
370
 * single packet, but we don't care. We are only looking at the
 
371
 * SMTP response codes at the beginning anyway.
 
372
 */
 
373
int
 
374
smtp_get_line_cb(thread *thread_obj)
 
375
{
 
376
        checker *chk = THREAD_ARG(thread_obj);
 
377
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
378
        smtp_host *smtp_hst = smtp_chk->host_ptr;
 
379
        int f, r, x;
 
380
 
 
381
        /* Handle read timeout */
 
382
        if (thread_obj->type == THREAD_READ_TIMEOUT) {
 
383
                smtp_final(thread_obj, 1, "Read timeout from server [%s:%d]",
 
384
                           inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
385
                return 0;
 
386
        }
 
387
 
 
388
        /* wrap the buffer, if full, by clearing it */
 
389
        if (SMTP_BUFF_MAX - smtp_chk->buff_ctr <= 0) {
 
390
                syslog(LOG_INFO, "SMTP_CHECK Buffer overflow reading from server [%s:%d]. "
 
391
                       "Increase SMTP_BUFF_MAX in smtp_check.h",
 
392
                       inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
393
                smtp_clear_buff(thread_obj);
 
394
        }
 
395
 
 
396
        /* Set descriptor non blocking */
 
397
        f = fcntl(thread_obj->u.fd, F_GETFL, 0);
 
398
        fcntl(thread_obj->u.fd, F_SETFL, f | O_NONBLOCK);
 
399
 
 
400
        /* read the data */
 
401
        r = read(thread_obj->u.fd, smtp_chk->buff + smtp_chk->buff_ctr,
 
402
                 SMTP_BUFF_MAX - smtp_chk->buff_ctr);
 
403
 
 
404
        if (r == -1 && (errno == EAGAIN || errno == EINTR)) {
 
405
                thread_add_read(thread_obj->master, smtp_get_line_cb, chk,
 
406
                                thread_obj->u.fd, smtp_chk->timeout);
 
407
                fcntl(thread_obj->u.fd, F_SETFL, f);
 
408
                return 0;
 
409
        } else if (r > 0)
 
410
                smtp_chk->buff_ctr += r;
 
411
 
 
412
        /* restore descriptor flags */
 
413
        fcntl(thread_obj->u.fd, F_SETFL, f);
 
414
 
 
415
        /* check if we have a newline, if so, callback */
 
416
        for (x = 0; x < SMTP_BUFF_MAX; x++) {
 
417
                if (smtp_chk->buff[x] == '\n') {
 
418
                        smtp_chk->buff[SMTP_BUFF_MAX - 1] = '\0';
 
419
                        
 
420
                        DBG("SMTP_CHECK [%s:%d] < %s", inet_ntop2(smtp_hst->ip),
 
421
                            ntohs(smtp_hst->port), smtp_chk->buff);
 
422
 
 
423
                        (smtp_chk->buff_cb)(thread_obj);
 
424
 
 
425
                        return 0;
 
426
                }
 
427
        }
 
428
 
 
429
        /*
 
430
         * If the connection was closed or there was
 
431
         * some sort of error, notify smtp_final()
 
432
         */
 
433
        if (r <= 0) {
 
434
                smtp_final(thread_obj, 1, "Read failure from server [%s:%d]",
 
435
                           inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
436
                return 0;
 
437
        }
 
438
 
 
439
        /*
 
440
         * Last case, we haven't read enough data yet
 
441
         * to pull a newline. Schedule ourselves for
 
442
         * another round.
 
443
         */
 
444
        thread_add_read(thread_obj->master, smtp_get_line_cb, chk,
 
445
                        thread_obj->u.fd, smtp_chk->timeout);
 
446
        return 0;
 
447
}
 
448
 
 
449
/* 
 
450
 * Ok a caller has asked us to asyncronously schedule a single line
 
451
 * to be received from the server. They have also passed us a call back
 
452
 * function that we'll call once we have the newline. If something bad
 
453
 * happens, the caller assumes we'll pass the error off to smtp_final(),
 
454
 * which will either down the real server or schedule a retry. The
 
455
 * function smtp_get_line_cb is what does the dirty work since the
 
456
 * sceduler can only accept a single *thread argument.
 
457
 */
 
458
void
 
459
smtp_get_line(thread *thread_obj, int (*callback) (struct _thread *))
 
460
{
 
461
        checker *chk = THREAD_ARG(thread_obj);
 
462
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
463
 
 
464
        /* clear the buffer */
 
465
        smtp_clear_buff(thread_obj);
 
466
 
 
467
        /* set the callback */
 
468
        smtp_chk->buff_cb = callback;
 
469
 
 
470
        /* schedule the I/O with our helper function  */
 
471
        thread_add_read(thread_obj->master, smtp_get_line_cb, chk,
 
472
                thread_obj->u.fd, smtp_chk->timeout);
 
473
        return;
 
474
}
 
475
 
 
476
/*
 
477
 * The scheduler function that puts the data out on the wire.
 
478
 * All our data will fit into one packet, so we only check if
 
479
 * the current write would block or not. If it wants to block,
 
480
 * we'll return to the scheduler and try again later. 
 
481
 */
 
482
int
 
483
smtp_put_line_cb(thread *thread_obj)
 
484
{
 
485
        checker *chk = THREAD_ARG(thread_obj);
 
486
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
487
        smtp_host *smtp_hst = smtp_chk->host_ptr;
 
488
        int f, w;
 
489
 
 
490
 
 
491
        /* Handle read timeout */
 
492
        if (thread_obj->type == THREAD_WRITE_TIMEOUT) {
 
493
                smtp_final(thread_obj, 1, "Write timeout to server [%s:%d]",
 
494
                           inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
495
                return 0;
 
496
        }
 
497
 
 
498
        /* Set descriptor non blocking */
 
499
        f = fcntl(thread_obj->u.fd, F_GETFL, 0);
 
500
        fcntl(thread_obj->u.fd, F_SETFL, f | O_NONBLOCK);
 
501
 
 
502
        /* write the data */
 
503
        w = write(thread_obj->u.fd, smtp_chk->buff, smtp_chk->buff_ctr);
 
504
 
 
505
        if (w == -1 && (errno == EAGAIN || errno == EINTR)) {
 
506
                thread_add_write(thread_obj->master, smtp_put_line_cb, chk,
 
507
                                 thread_obj->u.fd, smtp_chk->timeout);
 
508
                fcntl(thread_obj->u.fd, F_SETFL, f);
 
509
                return 0;
 
510
        }
 
511
 
 
512
        /* restore descriptor flags */
 
513
        fcntl(thread_obj->u.fd, F_SETFL, f);
 
514
 
 
515
        DBG("SMTP_CHECK [%s:%d] > %s", inet_ntop2(smtp_hst->ip),
 
516
            ntohs(smtp_hst->port), smtp_chk->buff);
 
517
 
 
518
        /*
 
519
         * If the connection was closed or there was
 
520
         * some sort of error, notify smtp_final()
 
521
         */
 
522
        if (w <= 0) {
 
523
                smtp_final(thread_obj, 1, "Write failure to server [%s:%d]",
 
524
                           inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
525
                return 0;
 
526
        }
 
527
 
 
528
        /* Execute the callback */
 
529
        (smtp_chk->buff_cb)(thread_obj);
 
530
        return 0;
 
531
}
 
532
 
 
533
/* 
 
534
 * This is the same as smtp_get_line() except that we're sending a
 
535
 * line of data instead of receiving one.
 
536
 */
 
537
void
 
538
smtp_put_line(thread *thread_obj, int (*callback) (struct _thread *))
 
539
{
 
540
        checker *chk = THREAD_ARG(thread_obj);
 
541
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
542
 
 
543
        smtp_chk->buff[SMTP_BUFF_MAX - 1] = '\0';
 
544
        smtp_chk->buff_ctr = strlen(smtp_chk->buff);
 
545
 
 
546
        /* set the callback */
 
547
        smtp_chk->buff_cb = callback;
 
548
 
 
549
        /* schedule the I/O with our helper function  */
 
550
        thread_add_write(thread_obj->master, smtp_put_line_cb, chk,
 
551
                         thread_obj->u.fd, smtp_chk->timeout);
 
552
        return;
 
553
}
 
554
 
 
555
/*
 
556
 * Ok, our goal here is to snag the status code out of the
 
557
 * buffer and return it as an integer. If it's not legible,
 
558
 * return -1.
 
559
 */
 
560
int
 
561
smtp_get_status(thread *thread_obj)
 
562
{
 
563
        checker *chk = THREAD_ARG(thread_obj);
 
564
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
565
        char *buff = smtp_chk->buff;
 
566
 
 
567
        /* First make sure they're all digits */        
 
568
        if (isdigit(buff[0]) && isdigit(buff[1]) &&
 
569
            isdigit(buff[2])) {
 
570
                /* Truncate the string and convert */   
 
571
                buff[3] = '\0';
 
572
                return atoi(buff);
 
573
        }
 
574
 
 
575
        return -1;
 
576
}
 
577
 
 
578
/* 
 
579
 * We have a connected socket and are ready to begin 
 
580
 * the conversation. This function schedules itself to 
 
581
 * be called via callbacks and tracking state in 
 
582
 * smtp_chk->state. Upon first calling, smtp_chk->state 
 
583
 * should be set to SMTP_START.
 
584
 */
 
585
int
 
586
smtp_engine_thread(thread *thread_obj)
 
587
{
 
588
        checker *chk = THREAD_ARG(thread_obj);
 
589
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
590
        smtp_host *smtp_hst = smtp_chk->host_ptr;
 
591
 
 
592
        switch (smtp_chk->state) {
 
593
 
 
594
                /* First step, schedule to receive the greeting banner */
 
595
                case SMTP_START:
 
596
                        /*
 
597
                         * Ok, if smtp_get_line schedules us back, we will
 
598
                         * have data to analyze. Otherwise, smtp_get_line
 
599
                         * will defer directly to smtp_final.
 
600
                         */
 
601
                        smtp_chk->state = SMTP_HAVE_BANNER;
 
602
                        smtp_get_line(thread_obj, smtp_engine_thread);
 
603
                        return 0;
 
604
                        break;
 
605
 
 
606
                /* Second step, analyze banner, send HELO */
 
607
                case SMTP_HAVE_BANNER:
 
608
                        /* Check for "220 some.mailserver.com" in the greeting */
 
609
                        if (smtp_get_status(thread_obj) != 220) {
 
610
                                smtp_final(thread_obj, 1, "Bad greeting banner from server [%s:%d]",
 
611
                                        inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
612
 
 
613
                                return 0;
 
614
                        }
 
615
 
 
616
                        /*
 
617
                         * Schedule to send the HELO, smtp_put_line will
 
618
                         * defer directly to smtp_final on error.
 
619
                         */
 
620
                        smtp_chk->state = SMTP_SENT_HELO;
 
621
                        snprintf(smtp_chk->buff, SMTP_BUFF_MAX, "HELO %s\r\n",
 
622
                                 smtp_chk->helo_name);
 
623
                        smtp_put_line(thread_obj, smtp_engine_thread);
 
624
                        return 0;
 
625
                        break;
 
626
 
 
627
                /* Third step, schedule to read the HELO response */
 
628
                case SMTP_SENT_HELO:
 
629
                        smtp_chk->state = SMTP_RECV_HELO;
 
630
                        smtp_get_line(thread_obj, smtp_engine_thread);
 
631
                        return 0;
 
632
                        break;
 
633
 
 
634
                /* Fourth step, analyze HELO return, send QUIT */
 
635
                case SMTP_RECV_HELO:
 
636
                        /* Check for "250 Please to meet you..." */
 
637
                        if (smtp_get_status(thread_obj) != 250) {
 
638
                                smtp_final(thread_obj, 1, "Bad HELO response from server [%s:%d]",
 
639
                                        inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
640
 
 
641
                                return 0;
 
642
                        }
 
643
 
 
644
                        smtp_chk->state = SMTP_SENT_QUIT;
 
645
                        snprintf(smtp_chk->buff, SMTP_BUFF_MAX, "QUIT\r\n");
 
646
                        smtp_put_line(thread_obj, smtp_engine_thread);
 
647
                        return 0;
 
648
                        break;
 
649
 
 
650
                /* Fifth step, schedule to receive QUIT confirmation */
 
651
                case SMTP_SENT_QUIT:
 
652
                        smtp_chk->state = SMTP_RECV_QUIT;
 
653
                        smtp_get_line(thread_obj, smtp_engine_thread);
 
654
                        return 0;
 
655
                        break;
 
656
 
 
657
                /* Sixth step, wrap up success to smtp_final */
 
658
                case SMTP_RECV_QUIT:
 
659
                        smtp_final(thread_obj, 0, NULL);
 
660
                        return 0;
 
661
                        break;
 
662
        }
 
663
 
 
664
        /* We shouldn't be here */
 
665
        smtp_final(thread_obj, 1, "Unknown smtp engine state encountered");
 
666
        return 0;
 
667
}
 
668
                
 
669
/* 
 
670
 * Second step in the process. Here we'll see if the connection
 
671
 * to the host we're checking was successful or not.
 
672
 */
 
673
int
 
674
smtp_check_thread(thread *thread_obj)
 
675
{
 
676
        checker *chk = THREAD_ARG(thread_obj);
 
677
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
678
        smtp_host *smtp_hst = smtp_chk->host_ptr;
 
679
        int status;
 
680
 
 
681
        status = tcp_socket_state(thread_obj->u.fd, thread_obj, smtp_hst->ip,
 
682
                                  smtp_hst->port, smtp_check_thread);
 
683
        switch (status) {
 
684
                case connect_error:
 
685
                        smtp_final(thread_obj, 1, "Error connecting to server [%s:%d]",
 
686
                                   inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
687
                        return 0;
 
688
                        break;
 
689
 
 
690
                case connect_timeout:
 
691
                        smtp_final(thread_obj, 1, "Connection timeout to server [%s:%d]",
 
692
                                   inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
693
                        return 0;
 
694
                        break;
 
695
 
 
696
                case connect_success:
 
697
                        DBG("SMTP_CHECK Remote SMTP server [%s:%d] connected",
 
698
                            inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
699
 
 
700
                        /* Enter the engine at SMTP_START */
 
701
                        smtp_chk->state = SMTP_START;
 
702
                        smtp_engine_thread(thread_obj);
 
703
                        return 0;
 
704
                        break;
 
705
        }
 
706
 
 
707
        /* we shouldn't be here */              
 
708
        smtp_final(thread_obj, 1, "Unknown connection error to server [%s:%d]",
 
709
                   inet_ntop2(smtp_hst->ip), ntohs(smtp_hst->port));
 
710
        return 0;
 
711
}
 
712
 
 
713
/* 
 
714
 * This is the main thread, where all the action starts.
 
715
 * When the check daemon comes up, it goes down the checkers_queue
 
716
 * and launches a thread for each checker that got registered.
 
717
 * This is the callback/event function for that initial thread.
 
718
 *
 
719
 * It should be noted that we ARE responsible for sceduling 
 
720
 * ourselves to run again. It doesn't have to be right here,
 
721
 * but eventually has to happen.
 
722
 */
 
723
int
 
724
smtp_connect_thread(thread *thread_obj)
 
725
{
 
726
        checker *chk = THREAD_ARG(thread_obj);
 
727
        smtp_checker *smtp_chk = CHECKER_ARG(chk);
 
728
        smtp_host *smtp_hst;
 
729
        enum connect_result status;
 
730
        int sd;
 
731
 
 
732
        /* Let's review our data structures.
 
733
         *
 
734
         * Thread is the structure used by the sceduler
 
735
         * for sceduling many types of events. thread->arg in this
 
736
         * case points to a checker structure. The checker
 
737
         * structure holds data about the vs and rs configurations
 
738
         * as well as the delay loop, etc. Each real server 
 
739
         * defined in the keepalived.conf will more than likely have
 
740
         * a checker structure assigned to it. Each checker structure
 
741
         * has a data element that is meant to hold per checker 
 
742
         * configurations. So thread->arg(checker)->data points to
 
743
         * a smtp_checker structure. In the smtp_checker structure
 
744
         * we hold global configuration data for the smtp check.
 
745
         * Smtp_checker has a list of per host (smtp_host) configuration
 
746
         * data in smtp_checker->host.
 
747
         *
 
748
         * So this whole thing looks like this:
 
749
         * thread->arg(checker)->data(smtp_checker)->host(smtp_host)
 
750
         * 
 
751
         * To make life simple, we'll break the structures out so
 
752
         * that "chk" always points to the current checker structure,
 
753
         * "smtp_chk" points to the current smtp_checker structure, 
 
754
         * and "smtp_hst" points to the current smtp_host structure.
 
755
         */
 
756
 
 
757
        /*
 
758
         * If we're disabled, we'll do nothing at all.
 
759
         * But we still have to register ourselves again so
 
760
         * we don't fall of the face of the earth.
 
761
         */
 
762
        if (!CHECKER_ENABLED(chk)) {
 
763
                thread_add_timer(thread_obj->master, smtp_connect_thread, chk,
 
764
                                 chk->vs->delay_loop);
 
765
                return 0;
 
766
        }
 
767
 
 
768
        /*
 
769
         * Set the internal host pointer to the host that well be 
 
770
         * working on. If it's NULL, we've successfully tested all hosts.
 
771
         * We'll bring the service up (if it's not already), reset the host list,
 
772
         * and insert the delay loop. When we get scheduled again the host list
 
773
         * will be reset and we will continue on checking them one by one.
 
774
         */
 
775
        if ((smtp_chk->host_ptr = list_element(smtp_chk->host, smtp_chk->host_ctr)) == NULL) {
 
776
                if (!svr_checker_up(chk->id, chk->rs)) {
 
777
                        syslog(LOG_INFO, "Remote SMTP server [%s:%d] succeed on service.",
 
778
                               inet_ntop2(CHECKER_RIP(chk)), ntohs(CHECKER_RPORT(chk)));
 
779
 
 
780
                        smtp_alert(chk->rs, NULL, NULL, "UP",
 
781
                                   "=> CHECK succeed on service <=");
 
782
                        update_svr_checker_state(UP, chk->id, chk->vs, chk->rs);
 
783
                }
 
784
 
 
785
                smtp_chk->attempts = 0;
 
786
                smtp_chk->host_ctr = 0;
 
787
                smtp_chk->host_ptr = list_element(smtp_chk->host, 0);
 
788
 
 
789
                thread_add_timer(thread_obj->master, smtp_connect_thread, chk, chk->vs->delay_loop);
 
790
                return 0;
 
791
        }
 
792
 
 
793
        smtp_hst = smtp_chk->host_ptr;
 
794
 
 
795
        /* Create the socket, failling here should be an oddity */
 
796
        if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
 
797
                DBG("SMTP_CHECK connection failed to create socket.");
 
798
                thread_add_timer(thread_obj->master, smtp_connect_thread, chk,
 
799
                                 chk->vs->delay_loop);
 
800
                return 0;
 
801
        }
 
802
 
 
803
        status = tcp_bind_connect(sd, smtp_hst->ip, smtp_hst->port, smtp_hst->bindto);
 
804
 
 
805
        /* handle tcp connection status & register callback the next setp in the process */
 
806
        tcp_connection_state(sd, status, thread_obj, smtp_check_thread, smtp_chk->timeout);
 
807
        return 0;
 
808
}