diff -rNu keepalived-1.1.13/doc/man/man5/keepalived.conf.5 keepalived-1.1.13-extcheck/doc/man/man5/keepalived.conf.5
--- keepalived-1.1.13/doc/man/man5/keepalived.conf.5	2005-02-07 10:24:59.000000000 +0900
+++ keepalived-1.1.13-extcheck/doc/man/man5/keepalived.conf.5	2007-07-06 17:19:38.000000000 +0900
@@ -346,7 +346,7 @@
            notify_down <STRING>|<QUOTED-STRING> 
    
            # pick one healthchecker
-           # HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK
+           # HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|FTP_CHECK|DNS_CHECK|SSL_HELLO|MISC_CHECK
    
            # HTTP and SSL healthcheckers
            HTTP_GET|SSL_GET 
@@ -431,6 +431,40 @@
                #     weight to 253)
                misc_dynamic
            }
+           
+           #FTP healthchecker
+           FTP_CHECK
+           {
+               connect_port <PORT>
+               connect_timeout <INTEGER>
+               bindto <IP ADDRESS>
+               retry <INTEGER>
+               delay_before_retry <INTEGER>
+           }
+
+           #DNS healthchecker
+           DNS_CHECK
+           {
+               port <PORT>
+               timeout <INTEGER>
+               retry <INTEGER>
+               # DNS request record type
+               # A|NS|SOA... (see RFC1035
+               # the default of A
+               type <STRING>|<QUOTED-STRING>
+               name <STRING>|<QUOTED-STRING>
+           }
+
+           #SSL HELLO healthchecker
+           SSL_HELLO
+           {
+               connect_port <PORT>
+               connect_timeout <INTEGER>
+               retry <INTEGER>
+               delay_before_retry <INTEGER>
+               common_name <STRING>|<QUOTED-STRING>
+           }
+
        } # realserver defn
     } # virtual service
 
diff -rNu keepalived-1.1.13/keepalived/check/Makefile.in keepalived-1.1.13-extcheck/keepalived/check/Makefile.in
--- keepalived-1.1.13/keepalived/check/Makefile.in	2006-03-09 22:19:59.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/check/Makefile.in	2007-07-06 17:19:38.000000000 +0900
@@ -14,7 +14,8 @@
 
 OBJS = 	check_daemon.o check_data.o check_parser.o \
 	check_api.o check_tcp.o check_http.o check_ssl.o \
-	check_smtp.o check_misc.o ipwrapper.o ipvswrapper.o
+	check_smtp.o check_misc.o ipwrapper.o ipvswrapper.o \
+	check_ssl_hello.o check_ftp.o check_dns.o
 
 ifeq ($(KERNEL),_KRNL_2_2_)
   OBJS += ipfwwrapper.o
@@ -68,3 +69,13 @@
   ../../lib/utils.h ../../lib/notify.h
 ipvswrapper.o: ipvswrapper.c ../include/ipvswrapper.h ../../lib/utils.h \
   ../../lib/memory.h
+check_ssl_hello.o: check_ssl_hello.c ../include/check_ssl_hello.h ../include/check_api.h \
+  ../../lib/memory.h ../include/ipwrapper.h ../include/layer4.h \
+  ../../lib/utils.h ../../lib/parser.h ../include/smtp.h
+check_ftp.o: check_ftp.c ../include/check_ftp.h ../include/check_api.h \
+  ../../lib/memory.h ../include/ipwrapper.h ../include/smtp.h \
+  ../../lib/utils.h ../../lib/notify.h ../../lib/parser.h ../include/daemon.h
+check_dns.o: check_dns.c ../include/check_dns.h ../include/check_api.h \
+  ../../lib/memory.h ../include/ipwrapper.h ../include/smtp.h \
+  ../../lib/utils.h ../../lib/parser.h
+
diff -rNu keepalived-1.1.13/keepalived/check/check_api.c keepalived-1.1.13-extcheck/keepalived/check/check_api.c
--- keepalived-1.1.13/keepalived/check/check_api.c	2006-10-11 18:40:17.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/check/check_api.c	2007-07-06 17:19:38.000000000 +0900
@@ -35,6 +35,9 @@
 #include "check_tcp.h"
 #include "check_http.h"
 #include "check_ssl.h"
+#include "check_ssl_hello.h"
+#include "check_ftp.h"
+#include "check_dns.h"
 
 /* Global vars */
 static checker_id_t ncheckers = 0;
@@ -171,4 +174,7 @@
 	install_tcp_check_keyword();
 	install_http_check_keyword();
 	install_ssl_check_keyword();
+	install_ssl_hello_keyword();
+	install_ftp_check_keyword();
+	install_dns_check_keyword();
 }
diff -rNu keepalived-1.1.13/keepalived/check/check_dns.c keepalived-1.1.13-extcheck/keepalived/check/check_dns.c
--- keepalived-1.1.13/keepalived/check/check_dns.c	1970-01-01 09:00:00.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/check/check_dns.c	2007-07-06 17:19:38.000000000 +0900
@@ -0,0 +1,388 @@
+#include <strings.h>
+#include <arpa/inet.h>
+#include "check_dns.h"
+#include "scheduler.h"
+#include "check_api.h"
+#include "memory.h"
+#include "ipwrapper.h"
+#include "utils.h"
+#include "parser.h"
+#include "smtp.h"
+
+int dns_send_thread(thread *thread_obj);
+int dns_recv_thread(thread *thread_obj);
+
+char *RRTYPE[]={"A","NS","MD","MF","CNAME","SOA","MB","MG","MR",
+                "NULL","WKS","PTR","HINFO","MINFO","MX","TXT",NULL};
+	
+void 
+dns_log(thread *thread_obj, int level, const char *format, ...)
+{
+	checker *chk = THREAD_ARG(thread_obj);
+	dns_checker *dns = CHECKER_ARG(chk);
+  char buff[1024];
+	va_list varg_list;
+  if(dns->log_level >= level){
+		va_start(varg_list, format);
+		vsnprintf(buff, 1024, format, varg_list);
+		va_end(varg_list);
+		syslog(LOG_INFO,"DNS_CHECK[%s:%d] %s",inet_ntop2(CHECKER_RIP(chk)), ntohs(dns->port),buff);
+	}
+}
+
+int
+dns_final(thread *thread_obj, int error)
+{
+	checker *chk = THREAD_ARG(thread_obj);
+	dns_checker *dns = CHECKER_ARG(chk);
+
+	close(thread_obj->u.fd);
+	dns_log(thread_obj, 7, "final error=%d attempts=%d retry=%d", error, dns->attempts, dns->retry);
+	if(error){
+    if(svr_checker_up(chk->id, chk->rs)){
+			if(dns->attempts < dns->retry) {
+				dns->attempts++;
+				thread_add_timer(thread_obj->master, dns_send_thread, chk, chk->vs->delay_loop);
+				return 0;
+			}
+			update_svr_checker_state(DOWN, chk->id, chk->vs, chk->rs);
+			smtp_alert(chk->rs, NULL, NULL, "DOWN", "=> DNS CHECK failed on service <=");
+		}
+	}else{
+    if(!svr_checker_up(chk->id, chk->rs)){
+		  smtp_alert(chk->rs, NULL, NULL, "UP", "=> DNS CHECK succeed on service <=");
+		  update_svr_checker_state(UP, chk->id, chk->vs, chk->rs);
+		}
+	}
+	dns->attempts = 0;
+	thread_add_timer(thread_obj->master, dns_send_thread, chk, chk->vs->delay_loop);
+	return(0);
+}
+
+void
+dns_dump(void *data)
+{
+	dns_checker *dns = CHECKER_DATA(data);
+	syslog(LOG_INFO, "  Port    = %d", ntohs(dns->port));
+	syslog(LOG_INFO, "  Timeout = %d", dns->timeout/TIMER_HZ);
+	syslog(LOG_INFO, "  Retry   = %d", dns->retry);
+	syslog(LOG_INFO, "  Type    = %s", dns->type);
+	syslog(LOG_INFO, "  Name    = %s", dns->name);
+}
+
+int
+dns_recv_thread(thread *thread_obj)
+{
+  size_t r;
+  socklen_t len;
+  struct sockaddr_in addr;
+	checker *chk = THREAD_ARG(thread_obj);
+	dns_checker *dns = CHECKER_ARG(chk);
+	dns_header s_header;
+	dns_header r_header;
+	
+	dns_log(thread_obj, 7, "recv_thread");
+  if(thread_obj->type == THREAD_READ_TIMEOUT){
+	  dns_log(thread_obj, 1, "read timeout");
+		dns_final(thread_obj,1);
+		return(0);
+	}
+	
+  len = sizeof(addr);
+  r = recvfrom(thread_obj->u.fd, dns->read, dns->size, MSG_DONTWAIT, (struct sockaddr *)&addr, &len);
+  if(r == -1 && (errno == EAGAIN || errno == EINTR)) {
+    thread_add_read(thread_obj->master, dns_recv_thread, chk, thread_obj->u.fd, dns->timeout);
+    return(0);
+  }
+
+  if(r <= 0){
+    /*----- error -----*/
+    dns_log(thread_obj, 0,"socket read error!");
+    dns_final(thread_obj,1);
+    return(0);
+  }
+
+  dns_log(thread_obj,7,"read size=%d",r);
+	if(addr.sin_addr.s_addr != CHECKER_RIP(chk) || addr.sin_port != dns->port){
+    dns_log(thread_obj, 1, "ignore message [%s:%d]", inet_ntop2(addr.sin_addr.s_addr), ntohs(addr.sin_port));
+    thread_add_read(thread_obj->master, dns_recv_thread, chk, thread_obj->u.fd, dns->timeout);
+		return(0);
+	}
+
+	if(r < sizeof(r_header)){
+		dns->read += r;
+		dns->size -= r;
+    thread_add_read(thread_obj->master, dns_recv_thread, chk, thread_obj->u.fd, dns->timeout);
+    return(0);
+	}
+	
+	memcpy(&s_header, dns->send, sizeof(s_header));
+	s_header.id      = ntohs(s_header.id);
+	s_header.flags   = ntohs(s_header.flags);
+	s_header.qdcount = ntohs(s_header.qdcount);
+	s_header.ancount = ntohs(s_header.ancount);
+	s_header.nscount = ntohs(s_header.nscount);
+	s_header.arcount = ntohs(s_header.arcount);
+	
+	memcpy(&r_header, dns->recv, sizeof(r_header));
+	r_header.id      = ntohs(r_header.id);
+	r_header.flags   = ntohs(r_header.flags);
+	r_header.qdcount = ntohs(r_header.qdcount);
+	r_header.ancount = ntohs(r_header.ancount);
+	r_header.nscount = ntohs(r_header.nscount);
+	r_header.arcount = ntohs(r_header.arcount);
+
+	/* ID CHECK */
+  if(s_header.id != r_header.id){
+    dns_log(thread_obj, 1, "ID is different! (%04x != %04x)", s_header.id, r_header.id);
+    thread_add_read(thread_obj->master, dns_recv_thread, chk, thread_obj->u.fd, dns->timeout);
+    return(0);
+	}
+
+	/* QR CHECK */
+	if(!DNS_QR(r_header.flags)){
+		dns_log(thread_obj, 1, "dns query receive?");
+    thread_add_read(thread_obj->master, dns_recv_thread, chk, thread_obj->u.fd, dns->timeout);
+		return(0);
+	}
+	
+	/* success */	
+  dns_final(thread_obj,0);
+	return 0;
+}
+
+int
+dns_send_query(thread *thread_obj)
+{
+	size_t w;
+	struct sockaddr_in addr;
+	checker *chk = THREAD_ARG(thread_obj);
+	dns_checker *dns = CHECKER_ARG(chk);
+	
+	addr.sin_family      = AF_INET;
+	addr.sin_addr.s_addr = CHECKER_RIP(chk);
+	addr.sin_port        = dns->port;
+
+	dns_log(thread_obj, 7, "send_request");
+  if(thread_obj->type == THREAD_WRITE_TIMEOUT){
+	  dns_log(thread_obj, 0, "write timeout");
+		dns_final(thread_obj,1);
+		return(0);
+	}
+
+	w = sendto(thread_obj->u.fd, dns->read, dns->size, MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr));
+  if(w == -1 && (errno == EAGAIN || errno == EINTR)) {
+		thread_add_write(thread_obj->master, dns_send_query, chk, thread_obj->u.fd, dns->timeout);
+    return(0);
+  }
+
+  if(w <= 0){
+    /*----- error -----*/
+    dns_log(thread_obj, 0,"socket write error!");
+	  dns_final(thread_obj,1);
+    return(0);
+	}
+
+	if(w == dns->size){
+    dns->read = dns->recv;
+		dns->size = sizeof(dns->recv);
+		thread_add_read(thread_obj->master, dns_recv_thread, chk, thread_obj->u.fd, dns->timeout);
+  }else{
+	  dns->read += w;
+		dns->size -= w;
+		thread_add_write(thread_obj->master, dns_send_query, chk, thread_obj->u.fd, dns->timeout);
+	}
+
+	return(0);
+}
+
+int dns_make_query(thread *thread_obj)
+{
+	int i,s;
+  char buff[256];
+	char *n = buff;
+  checker *chk = THREAD_ARG(thread_obj);
+  dns_checker *dns = CHECKER_ARG(chk);
+  uint8_t *p = dns->send;
+	
+  uint16_t id       = random();
+  uint16_t flags    = 0;
+  uint16_t flag_qr  = 0;
+  uint16_t flag_op  = 0;
+  uint16_t flag_aa  = 0;
+  uint16_t flag_tc  = 0;
+  uint16_t flag_rd  = 1;
+  uint16_t flag_ra  = 0;
+  uint16_t flag_z   = 0;
+  uint16_t flag_rc  = 0;
+  uint16_t qdcount  = 1;
+  uint16_t ancount  = 0;
+  uint16_t nscount  = 0;
+  uint16_t arcount  = 0;
+  uint16_t rr_type  = 1; /* A  */
+  uint16_t rr_class = 1; /* IN */
+
+	if(dns->type){
+  	for(i=0;RRTYPE[i];i++){
+			if(!strcasecmp(dns->type,RRTYPE[i])){
+				rr_type=i+1;
+				break;
+			}
+		}
+	}	
+
+  /*----- ID -----*/
+  *(p++) = ((id >> 8) & 0xff);
+  *(p++) = ((id >> 0) & 0xff);
+
+  /*----- FLAG -----*/
+	flags |= ((flag_qr & 0x0001) << 15);
+	flags |= ((flag_op & 0x000F) << 11);
+	flags |= ((flag_aa & 0x0001) << 10);
+	flags |= ((flag_tc & 0x0001) <<  9);
+	flags |= ((flag_rd & 0x0001) <<  8);
+	flags |= ((flag_ra & 0x0001) <<  7);
+	flags |= ((flag_z  & 0x0007) <<  4);
+	flags |= ((flag_rc & 0x000f) <<  0);
+  *(p++) = ((flags >> 8) & 0xff);
+  *(p++) = ((flags >> 0) & 0xff);
+
+  /*----- COUNT -----*/
+  *(p++) = ((qdcount >> 8) & 0xff);
+  *(p++) = ((qdcount >> 0) & 0xff);
+  *(p++) = ((ancount >> 8) & 0xff);
+  *(p++) = ((ancount >> 0) & 0xff);
+  *(p++) = ((nscount >> 8) & 0xff);
+  *(p++) = ((nscount >> 0) & 0xff);
+  *(p++) = ((arcount >> 8) & 0xff);
+  *(p++) = ((arcount >> 0) & 0xff);
+
+	/*----- RR(Name) -----*/
+	strcpy(n, dns->name);
+	for(n=strtok(n,".");n;n=strtok(NULL,".")){
+		s = strlen(n);
+		*(p++) = s;
+		memcpy(p,n,s);
+		p += s;
+	}
+  *(p++)=0;
+
+	/*----- RR(Type) -----*/
+  *(p++) = ((rr_type >> 8) & 0xff);
+  *(p++) = ((rr_type >> 0) & 0xff);
+
+	/*----- RR(Class) -----*/
+  *(p++) = ((rr_class >> 8) & 0xff);
+  *(p++) = ((rr_class >> 0) & 0xff);
+
+  dns->size = p - dns->send;
+  dns->read = dns -> send;
+  return(0);
+}
+
+int
+dns_send_thread(thread *thread_obj)
+{
+	int fd;
+	checker *chk = THREAD_ARG(thread_obj);
+	dns_checker *dns = CHECKER_ARG(chk);
+	
+	if(!dns->port){
+		dns->port = CHECKER_RPORT(chk);
+	}
+	if(!CHECKER_ENABLED(chk)){
+		thread_add_timer(thread_obj->master, dns_send_thread, chk, chk->vs->delay_loop);
+		return 0;
+	}
+	dns_log(thread_obj, 7, "send_thread");
+	if((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+		dns_log(thread_obj, 0, "can't create socket");
+		return 0;
+	}
+	dns_make_query(thread_obj);
+	thread_add_write(thread_obj->master, dns_send_query, chk, fd, dns->timeout);
+	return 0;
+}
+
+void
+dns_free(void *data)
+{
+	dns_checker *dns = CHECKER_DATA(data);
+	FREE(dns);
+	FREE(data);
+}
+
+void 
+dns_check_handler(vector strvec)
+{
+	dns_checker *dns = (dns_checker *)MALLOC(sizeof(dns_checker));
+	if(!dns){
+		syslog(LOG_INFO, "DNS_CHECK: out of memory!");
+		fprintf(stderr,  "DNS_CHECK: out of memory!\n");
+	}else{
+		dns->port     = htons(53);
+		dns->retry    = 3;
+		dns->timeout  = 5 * TIMER_HZ;
+		dns->attempts = 0;
+		dns->type     = DNS_DEFAULT_TYPE;
+		dns->name     = DNS_DEFAULT_NAME;
+		queue_checker(dns_free, dns_dump, dns_send_thread, dns);
+	}
+}
+
+void 
+dns_port_handler(vector strvec)
+{
+	dns_checker *dns = CHECKER_GET();
+	dns->port = htons(CHECKER_VALUE_INT(strvec));
+}
+
+void 
+dns_timeout_handler(vector strvec)
+{
+	dns_checker *dns = CHECKER_GET();
+	dns->timeout = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+void 
+dns_retry_handler(vector strvec)
+{
+	dns_checker *dns = CHECKER_GET();
+	dns->retry = CHECKER_VALUE_INT(strvec);
+}
+
+void 
+dns_type_handler(vector strvec)
+{
+	dns_checker *dns = CHECKER_GET();
+	dns->type = CHECKER_VALUE_STRING(strvec);
+}
+
+void 
+dns_name_handler(vector strvec)
+{
+	dns_checker *dns = CHECKER_GET();
+	dns->name = CHECKER_VALUE_STRING(strvec);
+}
+
+void 
+dns_log_level_handler(vector strvec)
+{
+	dns_checker *dns = CHECKER_GET();
+	dns->log_level = CHECKER_VALUE_INT(strvec);
+}
+
+void
+install_dns_check_keyword(void)
+{
+	install_keyword("DNS_CHECK", &dns_check_handler);
+	install_sublevel();
+	install_keyword("port",      &dns_port_handler);
+	install_keyword("timeout",   &dns_timeout_handler);
+	install_keyword("retry",     &dns_retry_handler);
+	install_keyword("type",      &dns_type_handler);
+	install_keyword("name",      &dns_name_handler);
+	install_keyword("log_level", &dns_log_level_handler);
+	install_sublevel_end();
+}
+
diff -rNu keepalived-1.1.13/keepalived/check/check_ftp.c keepalived-1.1.13-extcheck/keepalived/check/check_ftp.c
--- keepalived-1.1.13/keepalived/check/check_ftp.c	1970-01-01 09:00:00.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/check/check_ftp.c	2007-07-06 17:19:38.000000000 +0900
@@ -0,0 +1,386 @@
+#include "check_ftp.h"
+#include "check_api.h"
+#include "memory.h"
+#include "ipwrapper.h"
+#include "utils.h"
+#include "parser.h"
+#include "daemon.h"
+#include "smtp.h"
+
+int ftp_connect_thread(thread *);
+
+void 
+ftp_log(thread *t_obj, int level, const char *format, ...)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+  char info[FTP_INFO_MAX];
+	va_list varg_list;
+  if(ftp->log_level >= level){
+		va_start(varg_list, format);
+		vsnprintf(info, FTP_INFO_MAX, format, varg_list);
+		va_end(varg_list);
+		syslog(LOG_INFO, "FTP_CHECK[%s:%d] %s", inet_ntop2(CHECKER_RIP(chk)), ntohs(ftp->port), info);
+	}
+}
+
+void
+ftp_clear_buff(thread *t_obj)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+	memset(ftp->buff, 0, FTP_BUFF_MAX);
+	ftp->buff_ctr = 0;
+}
+
+int
+ftp_final(thread *t_obj, int error)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+
+	int timeout = chk->vs->delay_loop;
+	close(t_obj->u.fd);
+	if(error){
+		if(ftp->attempts < ftp->retry){
+			ftp->attempts++;
+			timeout=ftp->db_retry;
+		}else{
+			if(svr_checker_up(chk->id, chk->rs)){
+				smtp_alert(chk->rs, NULL, NULL, "DOWN", "=> FTP CHECK failed on service <=");
+				update_svr_checker_state(DOWN, chk->id, chk->vs, chk->rs);
+			}
+		}
+	}else{
+		ftp->attempts = 0;
+		if(!svr_checker_up(chk->id, chk->rs)){
+			smtp_alert(chk->rs, NULL, NULL, "UP", "=> FTP CHECK succeed on service <=");
+			update_svr_checker_state(UP, chk->id, chk->vs, chk->rs);
+		}
+	}
+	thread_add_timer(t_obj->master, ftp_connect_thread, chk, timeout);
+	return 0;
+}
+
+int
+ftp_get_line_cb(thread *t_obj)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+	int i, f, r;
+
+	if(t_obj->type == THREAD_READ_TIMEOUT){
+		ftp_log(t_obj, 3, "Read timeout from server");
+		ftp_final(t_obj, 1);
+		return 0;
+	}
+
+	f = fcntl(t_obj->u.fd, F_GETFL, 0);
+	fcntl(t_obj->u.fd, F_SETFL, f | O_NONBLOCK);
+	r = read(t_obj->u.fd, ftp->buff + ftp->buff_ctr, FTP_BUFF_MAX - ftp->buff_ctr);
+	fcntl(t_obj->u.fd, F_SETFL, f);
+
+	if(r == -1 && (errno == EAGAIN || errno == EINTR)){
+		thread_add_read(t_obj->master, ftp_get_line_cb, chk, t_obj->u.fd, ftp->timeout);
+		return 0;
+	}
+
+	if(r <= 0){
+		ftp_log(t_obj, 3, "Read failure");
+		ftp_final(t_obj, 1);
+		return 0;
+	}
+
+	for(i=0;i<r;i++){
+		if(FTP_BUFF_MAX <= ftp->buff_ctr){
+			ftp_log(t_obj, 0, "ftp buffer over fllow");
+			ftp_final(t_obj, 1);
+			return(0);
+		}
+		if(ftp->buff[ftp->buff_ctr] == '\n'){
+			ftp->buff[ftp->buff_ctr] = '\0';
+			(ftp->buff_cb)(t_obj);
+			return 0;
+		}
+		(ftp->buff_ctr)++;
+	}
+	thread_add_read(t_obj->master, ftp_get_line_cb, chk, t_obj->u.fd, ftp->timeout);
+	return 0;
+}
+
+void
+ftp_get_line(thread *t_obj, int (*callback) (struct _thread *))
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+
+	ftp_clear_buff(t_obj);
+	ftp->buff_cb = callback;
+	thread_add_read(t_obj->master, ftp_get_line_cb, chk, t_obj->u.fd, ftp->timeout);
+	return;
+}
+
+int
+ftp_put_line_cb(thread *t_obj)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+	int f, w;
+
+	if(t_obj->type == THREAD_WRITE_TIMEOUT) {
+		ftp_log(t_obj, 3, "Write timeout");
+		ftp_final(t_obj, 1);
+		return 0;
+	}
+
+	f = fcntl(t_obj->u.fd, F_GETFL, 0);
+	fcntl(t_obj->u.fd, F_SETFL, f | O_NONBLOCK);
+	w = write(t_obj->u.fd, ftp->buff, ftp->buff_ctr);
+	fcntl(t_obj->u.fd, F_SETFL, f);
+
+	if(w == -1 && (errno == EAGAIN || errno == EINTR)) {
+		thread_add_write(t_obj->master, ftp_put_line_cb, chk, t_obj->u.fd, ftp->timeout);
+		return 0;
+	}
+
+	if(w <= 0) {
+		ftp_log(t_obj, 3, "Write failure");
+		ftp_final(t_obj, 1);
+		return 0;
+	}
+
+	if(ftp->buff_ctr -= w){
+		thread_add_write(t_obj->master, ftp_put_line_cb, chk, t_obj->u.fd, ftp->timeout);
+	}else{
+		(ftp->buff_cb)(t_obj);
+	}
+	return 0;
+}
+
+void
+ftp_put_line(thread *t_obj, int (*callback)(struct _thread *))
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+
+	ftp->buff[FTP_BUFF_MAX - 1] = '\0';
+	ftp->buff_ctr = strlen(ftp->buff);
+	ftp->buff_cb = callback;
+	thread_add_write(t_obj->master, ftp_put_line_cb, chk, t_obj->u.fd, ftp->timeout);
+	return;
+}
+
+int
+ftp_get_status(thread *t_obj)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+	char *buff = ftp->buff;
+	if(isdigit(buff[0]) && isdigit(buff[1]) && isdigit(buff[2])){
+		buff[3] = '\0';
+		return atoi(buff);
+	}
+	return(-1);
+}
+
+int
+ftp_engine_thread(thread *t_obj)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+
+	switch (ftp->state) {
+		case FTP_START:
+			ftp->state = FTP_HAVE_BANNER;
+			ftp_get_line(t_obj, ftp_engine_thread);
+			return 0;
+			break;
+			
+		case FTP_HAVE_BANNER:
+			if(ftp_get_status(t_obj) != 220) {
+				ftp_log(t_obj, 0, "Bad greeting banner");
+				ftp_final(t_obj, 1);
+				return 0;
+			}
+			ftp->state = FTP_SENT_NOOP;
+			snprintf(ftp->buff, FTP_BUFF_MAX, "NOOP\r\n");
+			ftp_put_line(t_obj, ftp_engine_thread);
+			return 0;
+			break;
+			
+		case FTP_SENT_NOOP:
+			ftp->state = FTP_RECV_NOOP;
+			ftp_get_line(t_obj, ftp_engine_thread);
+			return 0;
+			break;
+			
+		case FTP_RECV_NOOP:
+			if (ftp_get_status(t_obj) != 200) {
+				ftp_log(t_obj, 0, "Bad NOOP response");
+				ftp_final(t_obj, 1);
+				return 0;
+			}
+			ftp->state = FTP_SENT_QUIT;
+			snprintf(ftp->buff, FTP_BUFF_MAX, "QUIT\r\n");
+			ftp_put_line(t_obj, ftp_engine_thread);
+			return 0;
+			break;
+			
+		case FTP_SENT_QUIT:
+			ftp->state = FTP_RECV_QUIT;
+			ftp_get_line(t_obj, ftp_engine_thread);
+			return 0;
+			break;
+			
+		case FTP_RECV_QUIT:
+			ftp_final(t_obj, 0);
+			return 0;
+			break;
+	}
+	ftp_log(t_obj, 0, "unknown state=%d", ftp->state);
+	ftp_final(t_obj, 1);
+	return 0;
+}
+		
+int
+ftp_check_thread(thread *t_obj)
+{
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+	int status;
+
+	status = tcp_socket_state(t_obj->u.fd, t_obj, CHECKER_RIP(chk), ftp->port, ftp_check_thread);
+	switch (status) {
+		case connect_error:
+			ftp_log(t_obj, 3, "Error connecting");
+			ftp_final(t_obj, 1);
+			return 0;
+			break;
+
+		case connect_timeout:
+			ftp_log(t_obj, 3, "Connection timeout");
+			ftp_final(t_obj, 1);
+			return 0;
+			break;
+
+		case connect_success:
+			ftp->state = FTP_START;
+			ftp_engine_thread(t_obj);
+			return 0;
+			break;
+	}
+	ftp_log(t_obj, 0, "Unknown connection error");
+	ftp_final(t_obj, 1);
+	return 0;
+}
+
+int
+ftp_connect_thread(thread *t_obj)
+{
+	int fd;
+	checker     *chk = THREAD_ARG(t_obj);
+	ftp_checker *ftp = CHECKER_ARG(chk);
+	enum connect_result status;
+
+	if(!CHECKER_ENABLED(chk)) {
+		thread_add_timer(t_obj->master, ftp_connect_thread, chk, chk->vs->delay_loop);
+		return 0;
+	}
+	if((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		ftp_log(t_obj, 0, "can't create socket");
+		thread_add_timer(t_obj->master, ftp_connect_thread, chk, chk->vs->delay_loop);
+		return 0;
+	}
+	status = tcp_bind_connect(fd, CHECKER_RIP(chk), ftp->port, ftp->bindto);
+	tcp_connection_state(fd, status, t_obj, ftp_check_thread, ftp->timeout);
+	return 0;
+}
+
+void
+ftp_port_handler(vector strvec)
+{
+	ftp_checker *ftp = CHECKER_GET();
+	ftp->port = htons(CHECKER_VALUE_INT(strvec)); 
+}
+
+void
+ftp_timeout_handler(vector strvec)
+{
+	ftp_checker *ftp = CHECKER_GET();
+	ftp->timeout = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+void
+ftp_retry_handler(vector strvec)
+{
+	ftp_checker *ftp = CHECKER_GET();
+	ftp->retry = CHECKER_VALUE_INT(strvec);
+}
+
+void
+ftp_db_retry_handler(vector strvec)
+{
+	ftp_checker *ftp = CHECKER_GET();
+	ftp->db_retry = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+void
+ftp_bindto_handler(vector strvec)
+{
+	ftp_checker *ftp = CHECKER_GET();
+	inet_ston(VECTOR_SLOT(strvec, 1), &ftp->bindto);
+}
+
+void
+ftp_log_level_handler(vector strvec)
+{
+	ftp_checker *ftp = CHECKER_GET();
+	ftp->log_level = CHECKER_VALUE_INT(strvec);
+}
+
+void
+free_ftp_check(void *data)
+{
+	ftp_checker *ftp = CHECKER_DATA(data);
+	FREE(ftp);
+	FREE(data);
+}
+
+void
+dump_ftp_check(void *data)
+{
+	ftp_checker *ftp = CHECKER_DATA(data);
+	syslog(LOG_INFO, "   Keepalive method = FTP_CHECK");
+	syslog(LOG_INFO, "           timeout = %ld", ftp->timeout/TIMER_HZ);
+	syslog(LOG_INFO, "           retry = %d", ftp->retry);
+	syslog(LOG_INFO, "           delay before retry = %ld", ftp->db_retry/TIMER_HZ);
+}
+
+void
+ftp_check_handler(vector strvec)
+{
+	ftp_checker *ftp = (ftp_checker *)MALLOC(sizeof(ftp_checker));
+	ftp->port      = htons(21);
+	ftp->bindto    = 0;
+	ftp->timeout   = 5 * TIMER_HZ;
+	ftp->db_retry  = 3 * TIMER_HZ;
+	ftp->retry     = 3;
+	ftp->attempts  = 0;
+	ftp->log_level = 0;
+	queue_checker(free_ftp_check, dump_ftp_check, ftp_connect_thread ,ftp);
+}
+
+void
+install_ftp_check_keyword(void)
+{
+	install_keyword("FTP_CHECK",          &ftp_check_handler);
+	install_sublevel();
+	install_keyword("connect_port",       &ftp_port_handler);
+	install_keyword("bindto",             &ftp_bindto_handler);
+	install_keyword("connect_timeout",    &ftp_timeout_handler);
+	install_keyword("delay_before_retry", &ftp_db_retry_handler);
+	install_keyword("retry",              &ftp_retry_handler);
+	install_keyword("log_level",          &ftp_log_level_handler);
+	install_sublevel_end();
+}
+
diff -rNu keepalived-1.1.13/keepalived/check/check_ssl_hello.c keepalived-1.1.13-extcheck/keepalived/check/check_ssl_hello.c
--- keepalived-1.1.13/keepalived/check/check_ssl_hello.c	1970-01-01 09:00:00.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/check/check_ssl_hello.c	2007-07-11 21:34:14.000000000 +0900
@@ -0,0 +1,730 @@
+#include <stdio.h>
+#include <arpa/inet.h>
+#include "check_ssl_hello.h"
+#include "scheduler.h"
+#include "check_api.h"
+#include "memory.h"
+#include "ipwrapper.h"
+#include "layer4.h"
+#include "utils.h"
+#include "parser.h"
+#include "smtp.h"
+
+int ssl_hello_engine_thread(thread *);
+int ssl_hello_check_thread(thread *);
+int ssl_hello_connect_thread(thread *);
+
+void 
+ssl_hello_log(thread *thread_obj, int level, const char *format, ...)
+{
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	char buff[1024];
+	va_list varg_list;
+	if(ssl->log_level >= level){
+		va_start(varg_list, format);
+		vsnprintf(buff, 1024, format, varg_list);
+		va_end(varg_list);
+		syslog(LOG_INFO,"SSL_HELLO[%s:%d] %s",inet_ntop2(CHECKER_RIP(chk)), ntohs(ssl->port),buff);
+	}
+}
+
+int 
+ssl_hello_asn1_get_length(uint8_t *b, int *o)
+{
+	int len, i;
+	if(!(b[*o] & 0x80)){
+		len = b[(*o)++];
+	}
+	else{
+		int length_bytes = b[(*o)++]&0x7f;
+		len = 0;
+		for(i=0;i<length_bytes;i++){
+			len <<= 8;
+			len += b[(*o)++];
+		}
+	}
+	return(len);
+}
+
+int 
+ssl_hello_asn1_get_oid(uint8_t *b, int l)
+{
+	int dn_type = 0;
+	if(l == 3 && *(b++) == 0x55 && *(b++) == 0x04)
+		dn_type = *(b++);
+	return(dn_type);
+}
+
+char *
+ssl_hello_asn1_get_cn(uint8_t *p, int l){
+	int o_type = 0;
+	int o_len  = 0;
+	int offset = 0;
+	int pcflag = 0;
+	int oid = 0;
+	char *s = NULL;
+	while(offset<l){
+		o_type = p[offset++];
+		o_len  = ssl_hello_asn1_get_length(p,&offset);
+		pcflag = (o_type & 0x20);
+		switch(o_type){
+		case 0x06:
+			oid = ssl_hello_asn1_get_oid(p + offset, o_len);
+			break;
+		case 0x13:
+		case 0x14:
+		case 0x16:
+			if(oid == 3){
+				s = MALLOC(o_len+1);
+				memcpy(s,p+offset,o_len);
+				s[o_len] = 0;
+				return(s);
+			}
+			break;
+		}
+		if(pcflag){
+			s = ssl_hello_asn1_get_cn(p+offset,o_len);
+			if(s){
+				return(s);
+			}
+		}
+		offset += o_len;
+	}
+	return(0);
+}
+
+uint8_t *
+ssl_hello_asn1_next(uint8_t *p, uint8_t *l){
+	int o_type = 0;
+	int o_len  = 0;
+	int offset = 0;
+	if(!p){
+		return(0);
+	}
+	o_type = p[offset++];
+	o_len  = ssl_hello_asn1_get_length(p,&offset);
+	p += offset;
+	if(p < l){
+		return(p);
+	}
+	return(0);
+}
+
+uint8_t *
+ssl_hello_asn1_skip(uint8_t *p, uint8_t *l){
+	int o_type = 0;
+	int o_len  = 0;
+	int offset = 0;
+	if(!p){
+		return(0);
+	}
+	o_type = p[offset++];
+	o_len  = ssl_hello_asn1_get_length(p,&offset);
+	p += offset + o_len;
+	if(p < l){
+		return(p);
+	}
+	return(0);
+}
+
+char *
+ssl_hello_get_cn(uint8_t *cert, unsigned short len1){
+	int o_type = 0;
+	int o_len  = 0;
+	int offset = 0;
+	uint8_t *b = cert + 6;
+	uint8_t *p = b;
+	int len2 = cert[0] * 65536 + cert[1] * 256 + cert[2];
+	int len3 = cert[3] * 65536 + cert[4] * 256 + cert[5];
+	if(len1 != len2 + 3){
+		return(0);
+	}
+	/*----- X509 -----*/
+	p = ssl_hello_asn1_next(p, b + len3);
+	p = ssl_hello_asn1_next(p, b + len3);
+	p = ssl_hello_asn1_skip(p, b + len3); /* Version			*/
+	p = ssl_hello_asn1_skip(p, b + len3); /* SerialNumber */
+	p = ssl_hello_asn1_skip(p, b + len3); /* Signature		*/
+	p = ssl_hello_asn1_skip(p, b + len3); /* Issuer				*/
+	p = ssl_hello_asn1_skip(p, b + len3); /* Validity			*/
+	/* Subject */
+	if(!p){
+		return(0);
+	}
+	o_type = p[offset++];
+	o_len  = ssl_hello_asn1_get_length(p,&offset);
+	return(ssl_hello_asn1_get_cn(p + offset, o_len));
+}
+
+ssl_hello_record *
+ssl_hello_create_record(void)
+{
+	ssl_hello_record *record = (ssl_hello_record *)MALLOC(sizeof(ssl_hello_record));
+	if(record){
+		record->Type = 0;
+		record->Size = 0;
+		record->Read = NULL;
+		record->Data = NULL;
+	}
+	return(record); 
+}
+
+void
+ssl_hello_clear_record(ssl_hello_record *record)
+{
+	if(record){
+		if(record->Data){
+			FREE(record->Data);
+		}
+		memset(record, 0, sizeof(ssl_hello_record));
+	}
+}
+
+void
+ssl_hello_free_record(ssl_hello_record *record)
+{
+	ssl_hello_clear_record(record);
+	FREE(record);
+}
+
+int
+ssl_hello_final(thread *thread_obj, int error)
+{
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+
+	close(thread_obj->u.fd);
+	ssl_hello_clear_record(ssl->send_record);
+	ssl_hello_clear_record(ssl->recv_record);
+	ssl_hello_log(thread_obj, 7, "final error=%d check=%d attempts=%d", error, ssl->check_flag, ssl->attempts);
+	if(error || !ssl->check_flag){
+		if(svr_checker_up(chk->id, chk->rs)){
+			if(ssl->attempts < ssl->retry) {
+				ssl->attempts++;
+				thread_add_timer(thread_obj->master, ssl_hello_connect_thread, chk, ssl->db_retry);
+				return 0;
+			}
+			smtp_alert(chk->rs, NULL, NULL, "UP", "=> SSL HELLO succeed on service <=");
+			update_svr_checker_state(DOWN, chk->id, chk->vs, chk->rs);
+		}
+	}else{
+		if(!svr_checker_up(chk->id, chk->rs)){
+			smtp_alert(chk->rs, NULL, NULL, "DOWN", "=> SSL HELLO failed on service <=");
+			update_svr_checker_state(UP,   chk->id, chk->vs, chk->rs);
+		}
+	}
+	ssl->attempts = 0;
+	ssl->check_flag = 0;
+	thread_add_timer(thread_obj->master, ssl_hello_connect_thread, chk, chk->vs->delay_loop);
+	return(0);
+}
+
+int
+ssl_hello_get_handshake(ssl_hello_record *r, ssl_hello_handshake *h){
+	if(r->Read >= r->Data + r->Size) return(0);
+	h->Type = *(r->Read);
+	r->Read++;
+	if(r->Read >= r->Data + r->Size) return(-1);
+	h->Size = *(r->Read) * 65536;
+	r->Read++;
+	if(r->Read >= r->Data + r->Size) return(-2);
+	h->Size += *(r->Read) * 256;
+	r->Read++;
+	if(r->Read >= r->Data + r->Size) return(-3);
+	h->Size += *(r->Read);
+	r->Read++;
+	if(!h->Size){
+		h->Data = NULL;
+	}else{
+		h->Data  = r->Read;
+		r->Read += h->Size;
+		if(r->Read > r->Data + r->Size) return(-4);
+	}
+	return(1);
+}
+
+void
+ssl_hello_dump(void *data)
+{
+	ssl_hello_checker *ssl = CHECKER_DATA(data);
+	syslog(LOG_INFO, "  Connection port    = %d", ntohs(ssl->port));
+	syslog(LOG_INFO, "  Connection timeout = %d", ssl->timeout/TIMER_HZ);
+	syslog(LOG_INFO, "  Retry              = %d", ssl->retry);
+	syslog(LOG_INFO, "  delay_before_retry = %d", ssl->db_retry);
+	if(ssl->common_name){
+		syslog(LOG_INFO, "  common_name        = %s", ssl->common_name);
+	}
+}
+
+int
+ssl_hello_send_thread(thread *thread_obj)
+{
+	int f;
+	int w;
+	int l;
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	ssl_hello_record *send_record = ssl->send_record;
+
+	if(thread_obj->type == THREAD_WRITE_TIMEOUT){
+		ssl_hello_log(thread_obj, 0, "write timeout"); 
+		ssl_hello_final(thread_obj,1);
+		return(0);
+	}
+
+	f = fcntl(thread_obj->u.fd, F_GETFL, 0);
+	fcntl(thread_obj->u.fd, F_SETFL, f | O_NONBLOCK);
+
+	l = send_record->Size - (send_record->Read - send_record->Data);
+	w = write(thread_obj->u.fd, send_record->Read, l);
+
+	if(w == -1 && (errno == EAGAIN || errno == EINTR)) {
+		thread_add_write(thread_obj->master, ssl_hello_send_thread, chk, thread_obj->u.fd, ssl->timeout);
+		fcntl(thread_obj->u.fd, F_SETFL, f);
+		return(0);
+	}
+
+	if(w <= 0){
+		/*----- error -----*/
+		ssl_hello_log(thread_obj, 0,"socket write error!");
+		fcntl(thread_obj->u.fd, F_SETFL, f);
+		ssl_hello_final(thread_obj,1);
+		return(0);
+	}
+
+	ssl->send_record->Read += w;
+	if(w != l){
+		thread_add_write(thread_obj->master, ssl_hello_send_thread, chk, thread_obj->u.fd, ssl->timeout);
+		fcntl(thread_obj->u.fd, F_SETFL, f);
+		return(0);
+	}
+
+	/*----- send complate -----*/
+	if(send_record->Type == 0x15){
+		fcntl(thread_obj->u.fd, F_SETFL, f);
+		ssl_hello_final(thread_obj,0);
+	}else{
+		ssl_hello_engine_thread(thread_obj);
+		fcntl(thread_obj->u.fd, F_SETFL, f);
+		ssl_hello_clear_record(send_record);
+	}
+
+	return(0);
+}
+
+int
+send_client_hello(thread *thread_obj)
+{
+	int i;
+	int len;
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	ssl_hello_record *send_record = ssl->send_record;
+
+	unsigned char *p;
+	unsigned char *record_len;
+	unsigned char *handshake_len;
+	unsigned char *ciphers_len;
+	unsigned long Time=(unsigned long)time(NULL);
+
+	ssl_hello_log(thread_obj, 7, "send client hello"); 
+	send_record->Type = 0x16;   /* SSL_HANDSHAKE */
+	send_record->Vers = 0x0300; /* SSLv3         */
+
+	send_record->Data = MALLOC(512);
+	send_record->Read = send_record->Data;
+	p = send_record->Data;
+
+	*(p++) = send_record->Type;
+	*(p++) = ((send_record->Vers >> 8)&0xff);
+	*(p++) = ((send_record->Vers     )&0xff);
+	record_len = p;
+	p += 2;
+
+	*(p++) = 0x01; /* CLIENT_HELLO */
+	handshake_len = p;
+	p += 3;
+
+	/* client version */
+	*(p++) = ((send_record->Vers) >> 8 & 0xff);
+	*(p++) = ((send_record->Vers)      & 0xff);
+
+	/* Random */
+	*(p++) = (unsigned char)((Time>>24)&0xff);
+	*(p++) = (unsigned char)((Time>>16)&0xff);
+	*(p++) = (unsigned char)((Time>> 8)&0xff);
+	*(p++) = (unsigned char)((Time    )&0xff);
+	p+=28;
+
+	/* Session ID */
+	*(p++)=0;
+
+	/* Ciphers */
+	ciphers_len = p;
+	p+=2;
+	for(i=1;i<28;i++){
+		*(p++) = (unsigned char)((i >> 8)&0xff);
+		*(p++) = (unsigned char)((i     )&0xff);
+	}
+	len = (p - ciphers_len) - 2;
+	*(ciphers_len + 0) = (unsigned char)((len >> 8)&0xff);
+	*(ciphers_len + 1) = (unsigned char)((len     )&0xff);
+
+	/* COMPRESSION */
+	*(p++)=1;
+	*(p++)=0; /* Add NULL method */
+
+	/*----- handshake length -----*/
+	len = (p - handshake_len) - 3;
+	*(handshake_len + 0) = (unsigned char)((len >>16)&0xff);
+	*(handshake_len + 1) = (unsigned char)((len >> 8)&0xff);
+	*(handshake_len + 2) = (unsigned char)((len     )&0xff);
+
+	/*----- record length -----*/
+	len = (p - record_len) - 2;
+	*(record_len + 0) = (unsigned char)((len >> 8)&0xff);
+	*(record_len + 1) = (unsigned char)((len     )&0xff);
+
+	/*----- DataSize -----*/
+	send_record->Size=(p - send_record->Data);
+
+	/*----- Send -----*/
+	thread_add_write(thread_obj->master, ssl_hello_send_thread, chk, thread_obj->u.fd, ssl->timeout);
+	return(0);
+}
+
+int
+send_alart(thread *thread_obj)
+{
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	ssl_hello_record *send_record = ssl->send_record;
+
+	ssl_hello_log(thread_obj, 7, "send alart"); 
+	send_record->Type = 0x15;   /* SSL_ALART */
+	send_record->Vers = 0x0300; /* SSLv3     */
+	send_record->Size = 7;
+	send_record->Data = MALLOC(send_record->Size);
+	send_record->Read = send_record->Data;
+	unsigned char *p  = send_record->Data;
+
+	*(p++) = send_record->Type;
+	*(p++) = ((send_record->Vers >> 8)&0xff);
+	*(p++) = ((send_record->Vers     )&0xff);
+	*(p++) = 0;
+	*(p++) = 2;
+	*(p++) = 0x02; /* ALART_TYPE_FATAL  */
+	*(p++) = 0x28; /* HANDSHAKE_FAILURE */
+
+	thread_add_write(thread_obj->master, ssl_hello_send_thread, chk, thread_obj->u.fd, ssl->timeout);
+	return(0);
+}
+
+int
+ssl_hello_recv_check(thread *thread_obj)
+{
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	ssl_hello_record *recv_record = ssl->recv_record;
+	if(!recv_record){
+		return(0);
+	}
+	if(!recv_record->Size){
+		return(0);
+	}
+	if(!recv_record->Data || !recv_record->Read){
+		return(0);
+	}
+	return(recv_record->Read - recv_record->Data == recv_record->Size);
+}
+
+int
+ssl_hello_recv_thread(thread *thread_obj)
+{
+	char d;
+	int f,r;
+	int count;
+	int error;
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	ssl_hello_record *recv_record = ssl->recv_record;
+
+	if(thread_obj->type == THREAD_READ_TIMEOUT){
+		ssl_hello_log(thread_obj, 1, "Read Timeout!"); 
+		ssl_hello_final(thread_obj,1);
+		return(0);
+	}
+
+	count = (recv_record->Read - recv_record->Data);
+	f = fcntl(thread_obj->u.fd, F_GETFL, 0);
+	fcntl(thread_obj->u.fd, F_SETFL, f | O_NONBLOCK);
+
+	if(!recv_record->Data){
+		r=read(thread_obj->u.fd,&d,1);
+	}else{
+		r=read(thread_obj->u.fd, recv_record->Read, recv_record->Size - count);
+	}
+	error = errno;
+	fcntl(thread_obj->u.fd, F_SETFL, f);
+
+	if(r == -1 && (error == EAGAIN || error == EINTR)) {
+		thread_add_read(thread_obj->master, ssl_hello_recv_thread, chk, thread_obj->u.fd, ssl->timeout);
+		return 0;
+	}
+
+	if(r <= 0){
+		ssl_hello_log(thread_obj, 1, "Read Error!"); 
+		ssl_hello_final(thread_obj,1);
+		return(0);
+	}
+
+	ssl_hello_log(thread_obj, 7, "ssl_hello_recv_thread: recv size=%d",r); 
+	recv_record->Read += r;
+	if(recv_record->Data){
+		if(ssl_hello_recv_check(thread_obj)){
+			ssl_hello_engine_thread(thread_obj);
+			return(0);
+		}
+	}else{
+		switch(count){
+		case 0:
+			recv_record->Type = d;
+			break;
+		case 1:
+			recv_record->Vers = d * 256;
+			break;
+		case 2:
+			recv_record->Vers += d;
+			break;
+		case 3:
+			recv_record->Size = d * 256;
+			break;
+		case 4:
+			recv_record->Size += d;
+			ssl_hello_log(thread_obj, 7, "ssl_hello_recv_thread: RecordSize=%d", recv_record->Size);
+			recv_record->Data = MALLOC(recv_record->Size);
+			recv_record->Read = recv_record->Data;
+			break;
+		}
+	}
+	thread_add_read(thread_obj->master, ssl_hello_recv_thread, chk, thread_obj->u.fd, ssl->timeout);
+	return(0);
+}
+
+int 
+ssl_hello_cn_check(thread *thread_obj, ssl_hello_handshake *handshake, char *common_name)
+{
+	int  r=0;
+	char *cn;
+
+	if(!common_name){
+		r = 1;
+	}else{
+		cn = ssl_hello_get_cn(handshake->Data, handshake->Size);
+		if(cn){
+			r=(strcmp(cn, common_name)==0);
+			if(r){
+				ssl_hello_log(thread_obj, 7, "cn_check OK ('%s'=='%s')", cn, common_name);
+			}else{
+				ssl_hello_log(thread_obj, 7, "cn_check NG ('%s'!='%s')", cn, common_name);
+			}
+			FREE(cn);
+		}
+	}  
+	return(r);
+}
+
+int 
+ssl_hello_engine_thread(thread *thread_obj)
+{
+	int r;
+	unsigned short oldsize;
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+	ssl_hello_handshake handshake;
+
+	if(ssl_hello_recv_check(thread_obj)){
+		ssl->recv_record->Read = ssl->recv_record->Data;
+		while(r=ssl_hello_get_handshake(ssl->recv_record,&handshake)){
+			if(r < 0){
+				ssl_hello_log(thread_obj, 7, "engine_thread: err=%d htype=%d hsize=%d", r,handshake.Type, handshake.Size);
+				if(r > -4){
+					ssl_hello_final(thread_obj,1);
+				}else{
+					oldsize = ssl->recv_record->Size;
+					ssl->recv_record->Size = ssl->recv_record->Read - ssl->recv_record->Data;
+					ssl->recv_record->Read = ssl->recv_record->Data;
+					ssl->recv_record->Data = MALLOC(ssl->recv_record->Size);
+					if(ssl->recv_record->Data == NULL){
+						ssl_hello_log(thread_obj, 0, "engine_thread: out of memory");
+						ssl_hello_final(thread_obj,1);
+					}else{
+						memcpy(ssl->recv_record->Data, ssl->recv_record->Read, oldsize);
+						FREE(ssl->recv_record->Read);
+						ssl->recv_record->Read = ssl->recv_record->Data + oldsize;
+						ssl_hello_log(thread_obj, 7, "engine_thread: recv(%d)", ssl->recv_record->Size - (ssl->recv_record->Read - ssl->recv_record->Data));
+						thread_add_read(thread_obj->master, ssl_hello_recv_thread, chk, thread_obj->u.fd, ssl->timeout);
+					}
+				}
+				return(0);
+			}else{
+				switch(handshake.Type){
+				case 0x02:  /* SERVER_HELLO */
+				  ssl_hello_log(thread_obj, 7, "engine_thread HandshakeType = SERVER_HELLO");
+					break;
+				case 0x0b:  /* SERVER_CERTIFICATE */
+				  ssl_hello_log(thread_obj, 7, "engine_thread HandshakeType = SERVER_CERTIFICATE");
+					ssl->check_flag = ssl_hello_cn_check(thread_obj, &handshake, ssl->common_name);
+					break;
+				case 0x0d:  /* CERTIFICATE_REQUEST */
+				  ssl_hello_log(thread_obj, 7, "engine_thread HandshakeType = CERTIFICATE_REQUEST");
+					break;
+				case 0x0e:  /* SERVER_HELLO_DONE */
+				  ssl_hello_log(thread_obj, 7, "engine_thread HandshakeType = SERVER_HELLO_DONE");
+					send_alart(thread_obj);
+					return(0);
+				default:
+				  ssl_hello_log(thread_obj, 7, "engine_thread HandshakeType = 0x%02x",handshake.Type);
+					break;
+				}
+			}
+		}
+	}
+	ssl_hello_clear_record(ssl->recv_record);
+	thread_add_read(thread_obj->master, ssl_hello_recv_thread, chk, thread_obj->u.fd, ssl->timeout);
+	return(0);
+}
+
+int
+ssl_hello_check_thread(thread *thread_obj)
+{
+	int status;
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+
+	ssl_hello_log(thread_obj, 7, "check_thread");
+	status = tcp_socket_state(thread_obj->u.fd, thread_obj, CHECKER_RIP(chk), ssl->port, ssl_hello_check_thread);
+	if(status==connect_success){ 
+		send_client_hello(thread_obj);
+	}else{
+		ssl_hello_log(thread_obj, 1, "Connect Error!!");
+		ssl_hello_final(thread_obj,1);
+	}
+	return 0;
+}
+
+int
+ssl_hello_connect_thread(thread *thread_obj)
+{
+	int fd;
+	int status;
+	checker *chk = THREAD_ARG(thread_obj);
+	ssl_hello_checker *ssl = CHECKER_ARG(chk);
+
+	if(!ssl->port){
+		ssl->port = CHECKER_RPORT(chk);
+	}
+
+	if(!CHECKER_ENABLED(chk)){
+		thread_add_timer(thread_obj->master, ssl_hello_connect_thread, chk, chk->vs->delay_loop);
+		return 0;
+	}
+
+	ssl_hello_log(thread_obj, 7, "connect_thread");
+	if((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		ssl_hello_log(thread_obj, 0, "can't create socket");
+		return 0;
+	}
+	status = tcp_connect(fd, CHECKER_RIP(chk), ssl->port);
+	tcp_connection_state(fd, status, thread_obj, ssl_hello_check_thread, ssl->timeout);
+	return 0;
+}
+
+void
+ssl_hello_free(void *data)
+{
+	ssl_hello_checker *ssl = CHECKER_DATA(data);
+	ssl_hello_free_record(ssl->send_record);
+	ssl_hello_free_record(ssl->recv_record);
+	FREE(ssl);
+	FREE(data);
+}
+
+void 
+ssl_hello_check_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = (ssl_hello_checker *)MALLOC(sizeof(ssl_hello_checker));
+	if(!ssl){
+		syslog(LOG_INFO, "SSL_HELLO: out of memory!");
+		fprintf(stderr,  "SSL_HELLO: out of memory!\n");
+	}else{
+		ssl->port        = htons(443);
+		ssl->retry       = 3;
+		ssl->timeout     = 5 * TIMER_HZ;
+		ssl->db_retry    = 3 * TIMER_HZ;
+		ssl->attempts    = 0;
+		ssl->log_level   = 0;
+		ssl->common_name = NULL;
+		ssl->send_record = ssl_hello_create_record();
+		ssl->recv_record = ssl_hello_create_record();
+		queue_checker(ssl_hello_free, ssl_hello_dump, ssl_hello_connect_thread, ssl);
+	}
+}
+
+void 
+ssl_hello_port_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = CHECKER_GET();
+	ssl->port = htons(CHECKER_VALUE_INT(strvec));
+}
+
+void 
+ssl_hello_timeout_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = CHECKER_GET();
+	ssl->timeout = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+void 
+ssl_hello_retry_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = CHECKER_GET();
+	ssl->retry = CHECKER_VALUE_INT(strvec);
+}
+
+void 
+ssl_hello_db_retry_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = CHECKER_GET();
+	ssl->db_retry = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+void 
+ssl_hello_common_name_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = CHECKER_GET();
+	ssl->common_name = CHECKER_VALUE_STRING(strvec);
+}
+
+void 
+ssl_hello_log_level_handler(vector strvec)
+{
+	ssl_hello_checker *ssl = CHECKER_GET();
+	ssl->log_level = CHECKER_VALUE_INT(strvec);
+}
+
+void
+install_ssl_hello_keyword(void)
+{
+	install_keyword("SSL_HELLO",          &ssl_hello_check_handler);
+	install_sublevel();
+	install_keyword("connect_port",       &ssl_hello_port_handler);
+	install_keyword("connect_timeout",    &ssl_hello_timeout_handler);
+	install_keyword("common_name",        &ssl_hello_common_name_handler);
+	install_keyword("retry",              &ssl_hello_retry_handler);
+	install_keyword("delay_before_retry", &ssl_hello_db_retry_handler);
+	install_keyword("log_level",          &ssl_hello_log_level_handler);
+	install_sublevel_end();
+}
+
diff -rNu keepalived-1.1.13/keepalived/include/check_dns.h keepalived-1.1.13-extcheck/keepalived/include/check_dns.h
--- keepalived-1.1.13/keepalived/include/check_dns.h	1970-01-01 09:00:00.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/include/check_dns.h	2007-07-06 17:19:38.000000000 +0900
@@ -0,0 +1,40 @@
+#ifndef _CHECK_DNS_CHECK_H
+#define _CHECK_DNS_CHECK_H
+
+#define DNS_DEFAULT_TYPE ""
+#define DNS_DEFAULT_NAME ""
+#define DNS_BUFFER_SIZE 768
+#define DNS_QR(flags) ((flags >> 15) & 0x0001)
+#define DNS_OP(flags) ((flags >> 11) & 0x000F)
+#define DNS_AA(flags) ((flags >> 10) & 0x0001)
+#define DNS_TC(flags) ((flags >>  9) & 0x0001)
+#define DNS_RD(flags) ((flags >>  8) & 0x0001)
+#define DNS_RA(flags) ((flags >>  7) & 0x0001)
+#define DNS_Z(flags)  ((flags >>  4) & 0x0007)
+#define DNS_RC(flags) ((flags >>  0) & 0x000F)
+
+typedef struct _dns_header {
+  uint16_t id;
+	uint16_t flags;
+	uint16_t qdcount;
+	uint16_t ancount;
+	uint16_t nscount;
+	uint16_t arcount;
+} dns_header;
+
+typedef struct _dns_checker {
+	uint16_t port;
+	int      timeout;
+	int      retry;
+	int      log_level;
+	int      attempts;
+	char    *type;
+	char    *name;
+	size_t   size;
+	uint8_t *read;
+	uint8_t  send[DNS_BUFFER_SIZE];
+	uint8_t  recv[DNS_BUFFER_SIZE];
+} dns_checker;
+
+extern void install_dns_check_keyword(void);
+#endif
diff -rNu keepalived-1.1.13/keepalived/include/check_ftp.h keepalived-1.1.13-extcheck/keepalived/include/check_ftp.h
--- keepalived-1.1.13/keepalived/include/check_ftp.h	1970-01-01 09:00:00.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/include/check_ftp.h	2007-07-06 17:19:38.000000000 +0900
@@ -0,0 +1,32 @@
+#ifndef _CHECK_FTP_H
+#define _CHECK_FTP_H
+#include "check_data.h"
+#include "scheduler.h"
+
+#define FTP_START        1 
+#define FTP_HAVE_BANNER  2
+#define FTP_SENT_NOOP    3
+#define FTP_RECV_NOOP    4
+#define FTP_SENT_QUIT    5
+#define FTP_RECV_QUIT    6
+#define FTP_DEFAULT_PORT 21
+#define FTP_BUFF_MAX     512
+#define FTP_INFO_MAX     512
+
+typedef struct _ftp_checker {
+	uint16_t port;
+  uint32_t bindto;
+	long     timeout;
+	long     db_retry;
+	int      retry;
+	int      attempts;
+	char     buff[FTP_BUFF_MAX];
+	int      buff_ctr;
+	int      (*buff_cb)(struct _thread *);
+	int      state;
+	int      log_level;
+} ftp_checker;
+
+extern void install_ftp_check_keyword(void);
+
+#endif
diff -rNu keepalived-1.1.13/keepalived/include/check_ssl_hello.h keepalived-1.1.13-extcheck/keepalived/include/check_ssl_hello.h
--- keepalived-1.1.13/keepalived/include/check_ssl_hello.h	1970-01-01 09:00:00.000000000 +0900
+++ keepalived-1.1.13-extcheck/keepalived/include/check_ssl_hello.h	2007-07-06 17:19:38.000000000 +0900
@@ -0,0 +1,33 @@
+#ifndef _CHECK_SSL_HELLO_H
+#define _CHECK_SSL_HELLO_H
+
+typedef struct _ssl_hello_record {
+	unsigned char    Type;
+	unsigned short   Vers;
+	unsigned short   Size;
+	unsigned char   *Read;
+	unsigned char   *Data;
+} ssl_hello_record;
+
+typedef struct _ssl_hello_handshake {
+	unsigned char   Type;
+	unsigned short  Size;
+	unsigned char  *Data;
+} ssl_hello_handshake;
+
+typedef struct _ssl_hello_checker {
+	uint16_t          port;
+	int               timeout;
+	int               retry;
+	int               db_retry;
+	int               log_level;
+	int               attempts;
+	int               check_flag;
+	char             *common_name;
+	ssl_hello_record *send_record;
+	ssl_hello_record *recv_record;
+} ssl_hello_checker;
+
+extern void install_ssl_hello_keyword(void);
+
+#endif
