소스 검색

Merge branch 'ser_core_cvs'

* ser_core_cvs:
  sctp: direct blacklist support
  Support for MESSAGE and OPTIONS method types are added to the parser.
  log/dbg: level is not limited anymore
  config: tcp_async alias for tcp_buf_write
  tcp: async write timeout fixes
  tcp: blacklist at tcp level if possible
  blst: another blacklist dest. function version
  cfg_size macro renamed to cfg_sizeof to avoid a conflict with one of the

Conflicts:

	cfg.y

The first conflict in BREAK definition, the file coming from cvs contained
RETURN_R_F, while the file coming from git contained BREAK_R_F, I resolved it
with BREAK_R_F.

The second conflict was in the definition in LOG_TOK which was resolved with
the latest version from cvs.
Jan Janak 16 년 전
부모
커밋
7a0489e835
12개의 변경된 파일311개의 추가작업 그리고 44개의 파일을 삭제
  1. 2 1
      NEWS
  2. 1 1
      cfg.lex
  3. 2 1
      cfg.y
  4. 55 21
      dprint.h
  5. 35 0
      dst_blacklist.c
  6. 14 1
      dst_blacklist.h
  7. 2 1
      parser/msg_parser.h
  8. 2 0
      parser/parse_fline.c
  9. 2 0
      parser/parse_fline.h
  10. 30 1
      sctp_server.c
  11. 141 15
      tcp_main.c
  12. 25 2
      tcp_read.c

+ 2 - 1
NEWS

@@ -293,9 +293,10 @@ new config variables:
      will be cached inside the process calling tcp_send (performance increase
      for sending over tcp at the cost of slightly slower connection closing and
      extra FDs kept open)
-  tcp_buf_write = yes | no (default no) - if enabled all the tcp  writes that 
+  tcp_async = yes | no (default no) - if enabled all the tcp  writes that 
      would block / wait for connect to finish, will be queued and attempted
      latter (see also tcp_conn_wq_max and tcp_wq_max).
+  tcp_buf_write = synonim for tcp_async
   tcp_conn_wq_max = bytes (default 32 K) - maximum bytes queued for write 
      allowed per connection. Attempting to queue more bytes would result
      in an error and in the connection being closed (too slow). If 

+ 1 - 1
cfg.lex

@@ -328,7 +328,7 @@ TCP_MAX_CONNECTIONS	"tcp_max_connections"
 TCP_SOURCE_IPV4		"tcp_source_ipv4"
 TCP_SOURCE_IPV6		"tcp_source_ipv6"
 TCP_OPT_FD_CACHE	"tcp_fd_cache"
-TCP_OPT_BUF_WRITE	"tcp_buf_write"
+TCP_OPT_BUF_WRITE	"tcp_buf_write"|"tcp_async"
 TCP_OPT_CONN_WQ_MAX	"tcp_conn_wq_max"
 TCP_OPT_WQ_MAX		"tcp_wq_max"
 TCP_OPT_DEFER_ACCEPT "tcp_defer_accept"

+ 2 - 1
cfg.y

@@ -2463,7 +2463,8 @@ cmd:
 	| RETURN NUMBER			{$$=mk_action(DROP_T, 2, NUMBER_ST, (void*)$2, NUMBER_ST, (void*)RETURN_R_F);}
 	| RETURN RETCODE		{$$=mk_action(DROP_T, 2, RETCODE_ST, 0, NUMBER_ST, (void*)RETURN_R_F);}
 	| BREAK				{$$=mk_action(DROP_T, 2, NUMBER_ST, 0, NUMBER_ST, (void*)BREAK_R_F); }
-	| LOG_TOK LPAREN STRING RPAREN	{$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)4, STRING_ST, $3); }
+	| LOG_TOK LPAREN STRING RPAREN	{$$=mk_action(LOG_T, 2, NUMBER_ST,
+										(void*)(L_DBG+1), STRING_ST, $3); }
 	| LOG_TOK LPAREN NUMBER COMMA STRING RPAREN	{$$=mk_action(LOG_T, 2, NUMBER_ST, (void*)$3, STRING_ST, $5); }
 	| LOG_TOK error 		{ $$=0; yyerror("missing '(' or ')' ?"); }
 	| LOG_TOK LPAREN error RPAREN	{ $$=0; yyerror("bad log argument"); }

+ 55 - 21
dprint.h

@@ -32,6 +32,7 @@
 #include <syslog.h>
 #include <stdio.h> /* stderr, fprintf() */
 
+#include "compiler_opt.h"
 #include "cfg_core.h"
 
 
@@ -146,19 +147,36 @@ int log_facility_fixup(void *handle, str *name, void **val);
 #	ifdef __SUNPRO_C
 #		define LOG_(level, prefix, fmt, ...) \
 			do { \
-				if (cfg_get(core, core_cfg, debug) >= (level) && \
-						DPRINT_NON_CRIT) { \
+				if (unlikely(cfg_get(core, core_cfg, debug) >= (level) && \
+						DPRINT_NON_CRIT)) { \
 					DPRINT_CRIT_ENTER; \
-					assert(((level) >= L_ALERT) && ((level) <= L_DBG)); \
-					if (log_stderr) { \
-						fprintf(stderr, "%2d(%d) %s: %s" fmt, \
-								process_no, my_pid(), LOG_LEVEL2NAME(level),\
-								(prefix), __VA_ARGS__); \
+					if (likely(((level) >= L_ALERT) && ((level) <= L_DBG))){ \
+						if (unlikely(log_stderr)) { \
+							fprintf(stderr, "%2d(%d) %s: %s" fmt, \
+									process_no, my_pid(), \
+									LOG_LEVEL2NAME(level), (prefix), \
+									__VA_ARGS__); \
+						} else { \
+							syslog(LOG2SYSLOG_LEVEL(level) | \
+									cfg_get(core, core_cfg, log_facility),\
+									"%s: %s" fmt, LOG_LEVEL2NAME(level),\
+									(prefix), __VA_ARGS__); \
+						} \
 					} else { \
-						syslog(LOG2SYSLOG_LEVEL(level) | \
-									cfg_get(core, core_cfg, log_facility), \
-								"%s: %s" fmt, LOG_LEVEL2NAME(level),\
-								(prefix), __VA_ARGS__); \
+						if (log_stderr) { \
+							fprintf(stderr, "%2d(%d) %s" fmt, \
+									process_no, my_pid(), \
+									(prefix),  __VA_ARGS__); \
+						} else { \
+							if ((level)<L_ALERT) \
+								syslog(LOG2SYSLOG_LEVEL(L_ALERT) | \
+										cfg_get(core, core_cfg, log_facility),\
+										"%s" fmt, (prefix), __VA_ARGS__); \
+							else \
+								syslog(LOG2SYSLOG_LEVEL(L_DBG) | \
+										cfg_get(core, core_cfg, log_facility),\
+										"%s" fmt, (prefix), __VA_ARGS__); \
+						} \
 					} \
 					DPRINT_CRIT_EXIT; \
 				} \
@@ -166,22 +184,38 @@ int log_facility_fixup(void *handle, str *name, void **val);
 			
 #		define LOG(level, fmt, ...)  LOG_((level), LOC_INFO, fmt, __VA_ARGS__)
 
-#	else
+#	else /* ! __SUNPRO_C */
 #		define LOG_(level, prefix, fmt, args...) \
 			do { \
 				if (cfg_get(core, core_cfg, debug) >= (level) && \
 						DPRINT_NON_CRIT) { \
 					DPRINT_CRIT_ENTER; \
-					assert(((level) >= L_ALERT) && ((level) <= L_DBG)); \
-					if (log_stderr) { \
-						fprintf(stderr, "%2d(%d) %s: %s" fmt, \
-								process_no, my_pid(), LOG_LEVEL2NAME(level),\
-								(prefix), ## args); \
-					} else { \
-						syslog(LOG2SYSLOG_LEVEL(level) |\
+					if (likely(((level) >= L_ALERT) && ((level) <= L_DBG))){ \
+						if (unlikely(log_stderr)) { \
+							fprintf(stderr, "%2d(%d) %s: %s" fmt, \
+									process_no, my_pid(), \
+									LOG_LEVEL2NAME(level),(prefix), ## args);\
+						} else { \
+							syslog(LOG2SYSLOG_LEVEL(level) |\
 									cfg_get(core, core_cfg, log_facility), \
-						 		"%s: %s" fmt, LOG_LEVEL2NAME(level),\
-								(prefix), ## args); \
+									"%s: %s" fmt, LOG_LEVEL2NAME(level),\
+									(prefix), ## args); \
+						} \
+					} else { \
+						if (log_stderr) { \
+							fprintf(stderr, "%2d(%d) %s" fmt, \
+										process_no, my_pid(), \
+										(prefix), ## args); \
+						} else { \
+							if ((level)<L_ALERT) \
+								syslog(LOG2SYSLOG_LEVEL(L_ALERT) | \
+										cfg_get(core, core_cfg, log_facility),\
+										"%s" fmt, (prefix), ## args); \
+							else \
+								syslog(LOG2SYSLOG_LEVEL(L_DBG) | \
+										cfg_get(core, core_cfg, log_facility),\
+										"%s" fmt, (prefix), ## args); \
+						} \
 					} \
 					DPRINT_CRIT_EXIT; \
 				} \

+ 35 - 0
dst_blacklist.c

@@ -34,6 +34,7 @@
  *  2007-07-30  added dst_blacklist_del() and dst_blacklist_add_to()  (andrei)
  *  2007-07-30  dst blacklist measurements added (Gergo)
  *  2008-02-11  dns_blacklist_init cfg parameter is introduced (Miklos)
+ *  2009-02-26  added dst_blacklist_su* variant (andrei)
  */
 
 
@@ -785,6 +786,14 @@ inline static int dst_is_blacklisted_ip(unsigned char proto,
 
 
 
+/** add dst to the blacklist, specifying the timeout.
+ * @param err_flags - reason (bitmap)
+ * @param si - destination (protocol, ip and port)
+ * @param msg - sip message that triggered the blacklisting (can be 0 if 
+ *               not known)
+ * @param timeout - timeout in ticks
+ * @return 0 on success, -1 on error
+ */
 int dst_blacklist_add_to(unsigned char err_flags,  struct dest_info* si,
 						struct sip_msg* msg, ticks_t timeout)
 {
@@ -802,6 +811,32 @@ int dst_blacklist_add_to(unsigned char err_flags,  struct dest_info* si,
 
 
 
+/** add dst to the blacklist, specifying the timeout.
+ * (like @function dst_blacklist_add_to)= above, but uses 
+ * (proto, sockaddr_union) instead of struct dest_info)
+ */
+int dst_blacklist_su_to(unsigned char err_flags, unsigned char proto,
+							union sockaddr_union* dst,
+							struct sip_msg* msg, ticks_t timeout)
+{
+	struct ip_addr ip;
+#ifdef DST_BLACKLIST_HOOKS
+	struct dest_info si;
+	
+	init_dest_info(&si);
+	si.to=*dst;
+	si.proto=proto;
+	if (unlikely (blacklist_run_hooks(&blst_add_cb, &si, &err_flags, msg) ==
+					DST_BLACKLIST_DENY))
+		return 0;
+#endif
+	su2ip_addr(&ip, dst);
+	return dst_blacklist_add_ip(err_flags, proto, &ip,
+								su_getport(dst), timeout);
+}
+
+
+
 int dst_is_blacklisted(struct dest_info* si, struct sip_msg* msg)
 {
 	int ires;

+ 14 - 1
dst_blacklist.h

@@ -88,12 +88,25 @@ void destroy_dst_blacklist();
 /* like dst_blacklist_add, but the timeout can be also set */
 int dst_blacklist_add_to(unsigned char err_flags, struct dest_info* si,
 						struct sip_msg* msg, ticks_t timeout);
+/* like above, but using a differnt way of passing the target */
+int dst_blacklist_su_to(unsigned char err_flags, unsigned char proto,
+							union sockaddr_union* dst,
+							struct sip_msg* msg, ticks_t timeout);
 
-/* adds a dst to the blacklist with default timeout */
+/** adds a dst to the blacklist with default timeout.
+ * @see dst_blacklist_add_to for more details.
+ */
 #define dst_blacklist_add(err_flags, si, msg) \
 	dst_blacklist_add_to((err_flags), (si), (msg), \
 		S_TO_TICKS(cfg_get(core, core_cfg, blst_timeout)))
 
+/** adds a dst to the blacklist with default timeout.
+ * @see dst_blacklist_su_to for more details.
+ */
+#define dst_blacklist_su(err_flags, proto, dst, msg) \
+	dst_blacklist_su_to((err_flags), (proto), (dst), (msg), \
+		S_TO_TICKS(cfg_get(core, core_cfg, blst_timeout)))
+
 int dst_is_blacklisted(struct dest_info* si, struct sip_msg* msg);
 /* delete an entry from the blacklist */
 int dst_blacklist_del(struct dest_info* si, struct sip_msg* msg);

+ 2 - 1
parser/msg_parser.h

@@ -79,7 +79,8 @@
 /* number methods as power of two to allow bitmap matching */
 enum request_method { METHOD_UNDEF=0, METHOD_INVITE=1, METHOD_CANCEL=2, METHOD_ACK=4,
 	METHOD_BYE=8, METHOD_INFO=16, METHOD_REGISTER=32, METHOD_SUBSCRIBE=64,
-        METHOD_NOTIFY=128, METHOD_OTHER=256 };
+	METHOD_NOTIFY=128, METHOD_MESSAGE=256, METHOD_OPTIONS=512,
+	METHOD_OTHER=1024 };
 
 #define FL_FORCE_RPORT 1   /* force rport */
 #define FL_FORCE_ACTIVE 2  /* force active SDP */

+ 2 - 0
parser/parse_fline.c

@@ -123,6 +123,8 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start * fl)
 	else IFISMETHOD( REGISTER, 'R')
 	else IFISMETHOD( SUBSCRIBE, 'S')
 	else IFISMETHOD( NOTIFY, 'N')
+	else IFISMETHOD( MESSAGE, 'M')
+	else IFISMETHOD( OPTIONS, 'O')
 	/* if you want to add another method XXX, include METHOD_XXX in
            H-file (this is the value which you will take later in
            processing and define XXX_LEN as length of method name;

+ 2 - 0
parser/parse_fline.h

@@ -58,6 +58,8 @@
 #define REGISTER_LEN 8
 #define SUBSCRIBE_LEN 9
 #define NOTIFY_LEN 6
+#define MESSAGE_LEN 7
+#define OPTIONS_LEN 7
 
 struct msg_start {
 	int type;					/* Type of the Message - Request/Response */

+ 30 - 1
sctp_server.c

@@ -22,6 +22,7 @@
  * History:
  * --------
  *  2008-08-07  initial version (andrei)
+ *  2009-02-27  blacklist support (andrei)
  */
 
 #ifdef USE_SCTP
@@ -49,6 +50,9 @@
 #include "mem/mem.h"
 #include "ip_addr.h"
 #include "cfg/cfg_struct.h"
+#ifdef USE_DST_BLACKLIST
+#include "dst_blacklist.h"
+#endif /* USE_DST_BLACKLIST */
 
 
 
@@ -643,6 +647,16 @@ static int sctp_handle_send_failed(struct socket_info* si,
 		
 		ret=sctp_msg_send_raw(&dst, data, data_len, &sinfo);
 	}
+#ifdef USE_DST_BLACKLIST
+	 else if (cfg_get(core, core_cfg, use_dst_blacklist) &&
+					sctp_options.sctp_send_retries) {
+		/* blacklist only if send_retries is on, if off we blacklist
+		   from SCTP_ASSOC_CHANGE: SCTP_COMM_LOST/SCTP_CANT_STR_ASSOC
+		   which is better (because we can tell connect errors from send
+		   errors and we blacklist a failed dst only once) */
+		dst_blacklist_su(BLST_ERR_SEND, PROTO_SCTP, su, 0);
+	}
+#endif /* USE_DST_BLACKLIST */
 	
 	return (ret>0)?0:ret;
 }
@@ -665,7 +679,7 @@ static int sctp_handle_notification(struct socket_info* si,
 						text " too short (%d bytes instead of %d bytes)\n", \
 						su2a((from_su), sizeof(*(from_su))), \
 						(bind_addr)->name.len, (bind_addr)->name.s, \
-						(bind_addr)->port_no, (length), (val)); \
+						(bind_addr)->port_no, (int)(length), (int)(val)); \
 			goto error; \
 		}
 
@@ -735,6 +749,21 @@ static int sctp_handle_notification(struct socket_info* si,
 					snp->sn_assoc_change.sac_outbound_streams,
 					snp->sn_assoc_change.sac_inbound_streams
 					);
+#ifdef USE_DST_BLACKLIST
+			/* blacklist only if send_retries is turned off (if on we don't 
+			   know here if we did retry or we are at the first error) */
+			if (cfg_get(core, core_cfg, use_dst_blacklist) &&
+					(sctp_options.sctp_send_retries==0)){
+				switch(snp->sn_assoc_change.sac_state) {
+					case SCTP_CANT_STR_ASSOC:
+						dst_blacklist_su(BLST_ERR_CONNECT, PROTO_SCTP, su, 0);
+						break;
+					case SCTP_COMM_LOST:
+						dst_blacklist_su(BLST_ERR_SEND, PROTO_SCTP, su, 0);
+						break;
+				}
+			}
+#endif /* USE_DST_BLACKLIST */
 			break;
 #ifdef SCTP_ADAPTION_INDICATION
 		case SCTP_ADAPTION_INDICATION:

+ 141 - 15
tcp_main.c

@@ -97,6 +97,7 @@
  *               POLLHUP (andrei)
  *              on write error check if there's still data in the socket 
  *               read buffer and process it first (andrei)
+ *  2009-02-26  direct blacklist support (andrei)
  */
 
 
@@ -161,7 +162,10 @@
 #else
 #include "tls_hooks_init.h"
 #include "tls_hooks.h"
-#endif
+#endif /* CORE_TLS*/
+#ifdef USE_DST_BLACKLIST
+#include "dst_blacklist.h"
+#endif /* USE_DST_BLACKLIST */
 
 #include "tcp_info.h"
 #include "tcp_options.h"
@@ -481,7 +485,8 @@ error:
  * if BLOCKING_USE_SELECT and HAVE_SELECT are defined it will internally
  * use select() instead of poll (bad if fd > FD_SET_SIZE, poll is preferred)
  */
-static int tcp_blocking_connect(int fd, const struct sockaddr *servaddr,
+static int tcp_blocking_connect(int fd, int type,
+								const struct sockaddr *servaddr,
 								socklen_t addrlen)
 {
 	int n;
@@ -511,6 +516,19 @@ again:
 			else goto error_timeout;
 		}
 		if (errno!=EINPROGRESS && errno!=EALREADY){
+#ifdef USE_DST_BLACKLIST
+			if (cfg_get(core, core_cfg, use_dst_blacklist))
+				switch(errno){
+					case ECONNREFUSED:
+					case ENETUNREACH:
+					case ETIMEDOUT:
+					case ECONNRESET:
+					case EHOSTUNREACH:
+						dst_blacklist_su(BLST_ERR_CONNECT, type,
+										 (union sockaddr_union*)servaddr, 0);
+						break;
+				}
+#endif /* USE_DST_BLACKLIST */
 			LOG(L_ERR, "ERROR: tcp_blocking_connect %s: (%d) %s\n",
 					su2a((union sockaddr_union*)servaddr, addrlen),
 					errno, strerror(errno));
@@ -574,6 +592,11 @@ again:
 	}
 error_timeout:
 	/* timeout */
+#ifdef USE_DST_BLACKLIST
+	if (cfg_get(core, core_cfg, use_dst_blacklist))
+		dst_blacklist_su(BLST_ERR_CONNECT, type,
+							(union sockaddr_union*)servaddr, 0);
+#endif /* USE_DST_BLACKLIST */
 	LOG(L_ERR, "ERROR: tcp_blocking_connect %s: timeout %d s elapsed "
 				"from %d s\n", su2a((union sockaddr_union*)servaddr, addrlen),
 				elapsed, tcp_connect_timeout);
@@ -619,6 +642,15 @@ inline static int _wbufq_add(struct  tcp_connection* c, char* data,
 					" (%d, total %d, last write %d s ago)\n",
 					size, q->queued, *tcp_total_wq,
 					TICKS_TO_S(t-q->wr_timeout-tcp_options.tcp_wq_timeout));
+#ifdef USE_DST_BLACKLIST
+		if (q->first && TICKS_LT(q->wr_timeout, t) &&
+				cfg_get(core, core_cfg, use_dst_blacklist)){
+			ERR("blacklisting, state=%d\n", c->state);
+			dst_blacklist_su((c->state==S_CONN_CONNECT)?  BLST_ERR_CONNECT:
+									BLST_ERR_SEND,
+								c->rcv.proto, &c->rcv.src_su, 0);
+		}
+#endif /* USE_DST_BLACKLIST */
 		goto error;
 	}
 	
@@ -682,7 +714,7 @@ inline static int _wbufq_insert(struct  tcp_connection* c, char* data,
 		return _wbufq_add(c, data, size);
 	
 	if (unlikely((*tcp_total_wq+size)>tcp_options.tcp_wq_max)){
-		LOG(L_ERR, "ERROR: wbufq_insert(%d bytes): write queue full or timeout"
+		LOG(L_ERR, "ERROR: wbufq_insert(%d bytes): write queue full"
 					" (%d, total %d, last write %d s ago)\n",
 					size, q->queued, *tcp_total_wq,
 					TICKS_TO_S(get_ticks_raw()-q->wr_timeout-
@@ -784,11 +816,24 @@ inline static int wbufq_run(int fd, struct tcp_connection* c, int* empty)
 				break;
 			}
 			q->wr_timeout=t+tcp_options.tcp_wq_timeout;
-			c->state=S_CONN_OK;
 		}else{
 			if (n<0){
 				/* EINTR is handled inside _tcpconn_write_nb */
 				if (!(errno==EAGAIN || errno==EWOULDBLOCK)){
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						switch(errno){
+							case ENETUNREACH:
+							case ECONNRESET:
+							/*case EHOSTUNREACH: -- not posix */
+								dst_blacklist_su((c->state==S_CONN_CONNECT)?
+														BLST_ERR_CONNECT:
+														BLST_ERR_SEND,
+														c->rcv.proto,
+														&c->rcv.src_su, 0);
+								break;
+						}
+#endif /* USE_DST_BLACKLIST */
 					ret=-1;
 					LOG(L_ERR, "ERROR: wbuf_runq: %s [%d]\n",
 						strerror(errno), errno);
@@ -994,6 +1039,19 @@ again:
 			if (likely(errno==EINPROGRESS))
 				*state=S_CONN_CONNECT;
 			else if (errno!=EALREADY){
+#ifdef USE_DST_BLACKLIST
+				if (cfg_get(core, core_cfg, use_dst_blacklist))
+					switch(errno){
+						case ECONNREFUSED:
+						case ENETUNREACH:
+						case ETIMEDOUT:
+						case ECONNRESET:
+						case EHOSTUNREACH:
+							dst_blacklist_su(BLST_ERR_CONNECT, type, server,
+												0);
+							break;
+				}
+#endif /* USE_DST_BLACKLIST */
 				LOG(L_ERR, "ERROR: tcp_do_connect: connect %s: (%d) %s\n",
 							su2a(server, sizeof(*server)),
 							errno, strerror(errno));
@@ -1002,7 +1060,8 @@ again:
 		}
 	}else{
 #endif /* TCP_BUF_WRITE */
-		if (tcp_blocking_connect(s, &server->s, sockaddru_len(*server))<0){
+		if (tcp_blocking_connect(s, type, &server->s,
+									sockaddru_len(*server))<0){
 			LOG(L_ERR, "ERROR: tcp_do_connect: tcp_blocking_connect %s"
 						" failed\n", su2a(server, sizeof(*server)));
 			goto error;
@@ -1656,6 +1715,8 @@ no_id:
 				/* do connect and if src ip or port changed, update the 
 				 * aliases */
 				if (unlikely((fd=tcpconn_finish_connect(c, from))<0)){
+					/* tcpconn_finish_connect will automatically blacklist
+					   on error => no need to do it here */
 					LOG(L_ERR, "ERROR: tcp_send %s: tcpconn_finish_connect(%p)"
 							" failed\n", su2a(&dst->to, sizeof(dst->to)),
 								c);
@@ -1703,6 +1764,18 @@ no_id:
 						n=len;
 						goto end;
 					}
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						switch(errno){
+							case ENETUNREACH:
+							case ECONNRESET:
+							/*case EHOSTUNREACH: -- not posix */
+								/* if first write failed it's most likely a
+								   connect error */
+								dst_blacklist_add( BLST_ERR_CONNECT, dst, 0);
+								break;
+						}
+#endif /* USE_DST_BLACKLIST */
 					/* error: destroy it directly */
 					LOG(L_ERR, "ERROR: tcp_send %s: connect & send "
 										" for %p failed:" " %s (%d)\n",
@@ -1899,6 +1972,20 @@ send_it:
 			lock_release(&c->write_lock);
 		}
 #endif /* TCP_BUF_WRITE */
+#ifdef USE_DST_BLACKLIST
+		if (cfg_get(core, core_cfg, use_dst_blacklist))
+			switch(errno){
+				case ENETUNREACH:
+				case ECONNRESET:
+				/*case EHOSTUNREACH: -- not posix */
+					dst_blacklist_su((c->state==S_CONN_CONNECT)?
+											BLST_ERR_CONNECT:
+											BLST_ERR_SEND,
+										c->rcv.proto,
+										&c->rcv.src_su, 0);
+					break;
+			}
+#endif /* USE_DST_BLACKLIST */
 		LOG(L_ERR, "ERROR: tcp_send: failed to send on %p (%s:%d->%s): %s (%d)"
 					"\n", c, ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port,
 					su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)),
@@ -2574,11 +2661,19 @@ inline static int handle_tcp_child(struct tcp_child* tcp_c, int fd_i)
 #ifdef TCP_BUF_WRITE
 			if (unlikely(tcp_options.tcp_buf_write && 
 							_wbufq_non_empty(tcpconn) )){
-				if (unlikely(TICKS_LE(t, tcpconn->wbuf_q.wr_timeout))){
+				if (unlikely(TICKS_GE(t, tcpconn->wbuf_q.wr_timeout))){
 					DBG("handle_tcp_child: wr. timeout on CONN_RELEASE for %p "
 							"refcnt= %d\n", tcpconn,
 							atomic_get(&tcpconn->refcnt));
 					/* timeout */
+#ifdef USE_DST_BLACKLIST
+					if (cfg_get(core, core_cfg, use_dst_blacklist))
+						dst_blacklist_su((tcpconn->state==S_CONN_CONNECT)?
+													BLST_ERR_CONNECT:
+													BLST_ERR_SEND,
+													tcpconn->rcv.proto,
+													&tcpconn->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
 					if (unlikely(tcpconn->flags & F_CONN_WRITE_W)){
 						io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING);
 						tcpconn->flags&=~F_CONN_WRITE_W;
@@ -2677,6 +2772,9 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
 	int fd;
 	int flags;
 	ticks_t t;
+#ifdef TCP_BUF_WRITE
+	ticks_t nxt_timeout;
+#endif /* TCP_BUF_WRITE */
 	
 	ret=-1;
 	if (unlikely(p->unix_sock<=0)){
@@ -2893,30 +2991,49 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
 				tcpconn_put_destroy(tcpconn);
 				break;
 			}
-			tcpconn->state=S_CONN_OK;
 			(*tcp_connections_no)++;
 			tcpconn->s=fd;
 			/* update the timeout*/
 			t=get_ticks_raw();
 			tcpconn->timeout=t+tcp_con_lifetime;
-			/* activate the timer (already properly init. in tcpconn_new())
-			 * no need for reinit */
-			local_timer_add(&tcp_main_ltimer, &tcpconn->timer, 
-								tcp_con_lifetime, t);
-			tcpconn->flags|=F_CONN_MAIN_TIMER|F_CONN_READ_W|F_CONN_WANTS_RD;
+			nxt_timeout=tcp_con_lifetime;
 			if (unlikely(cmd==CONN_NEW_COMPLETE)){
+				tcpconn->state=S_CONN_OK;
 				/* check if needs to be watched for write */
 				lock_get(&tcpconn->write_lock);
 					/* if queue non empty watch it for write */
 					flags=(_wbufq_empty(tcpconn)-1)&POLLOUT;
 				lock_release(&tcpconn->write_lock);
-				tcpconn->flags|=(!(flags&POLLOUT)-1)&
-									(F_CONN_WRITE_W|F_CONN_WANTS_WR);
+				if (flags){
+					if (TICKS_LT(tcpconn->wbuf_q.wr_timeout, tcpconn->timeout)
+							&& TICKS_LT(t, tcpconn->wbuf_q.wr_timeout))
+						nxt_timeout=tcpconn->wbuf_q.wr_timeout-t;
+					tcpconn->flags|=F_CONN_WRITE_W|F_CONN_WANTS_WR;
+				}
+				/* activate the timer (already properly init. in 
+				   tcpconn_new())  no need for reinit */
+				local_timer_add(&tcp_main_ltimer, &tcpconn->timer, nxt_timeout,
+									t);
+				tcpconn->flags|=F_CONN_MAIN_TIMER|F_CONN_READ_W| 
+								F_CONN_WANTS_RD;
 			}else{
 				/* CONN_NEW_PENDING_WRITE */
+				/* we don't know if we successfully sent anything, but
+				   for sure we haven't sent all what we wanted, so consider
+				   the connection in "connecting" state */
+				tcpconn->state=S_CONN_CONNECT;
 				/* no need to check, we have something queued for write */
 				flags=POLLOUT;
-				tcpconn->flags|=(F_CONN_WRITE_W|F_CONN_WANTS_WR);
+				if (TICKS_LT(tcpconn->wbuf_q.wr_timeout, tcpconn->timeout)
+						&& TICKS_LT(t, tcpconn->wbuf_q.wr_timeout))
+					nxt_timeout=tcpconn->wbuf_q.wr_timeout-t;
+				/* activate the timer (already properly init. in 
+				   tcpconn_new())  no need for reinit */
+				local_timer_add(&tcp_main_ltimer, &tcpconn->timer, nxt_timeout,
+									t);
+				tcpconn->flags|=F_CONN_MAIN_TIMER|F_CONN_READ_W| 
+								F_CONN_WANTS_RD |
+								F_CONN_WRITE_W|F_CONN_WANTS_WR;
 			}
 			flags|=POLLIN;
 			if (unlikely(
@@ -3351,6 +3468,15 @@ static ticks_t tcpconn_main_timeout(ticks_t t, struct timer_ln* tl, void* data)
 		else
 			return (ticks_t)(c->timeout - t);
 	}
+#ifdef USE_DST_BLACKLIST
+	/* if time out due to write, add it to the blacklist */
+	if (tcp_options.tcp_buf_write && _wbufq_non_empty(c) &&
+			TICKS_GE(t, c->wbuf_q.wr_timeout) &&
+			cfg_get(core, core_cfg, use_dst_blacklist))
+		dst_blacklist_su((c->state==S_CONN_CONNECT)?  BLST_ERR_CONNECT:
+										BLST_ERR_SEND,
+								c->rcv.proto, &c->rcv.src_su, 0);
+#endif /* USE_DST_BLACKLIST */
 #else /* ! TCP_BUF_WRITE */
 	if (TICKS_LT(t, c->timeout)){
 		/* timeout extended, exit */

+ 25 - 2
tcp_read.c

@@ -40,6 +40,7 @@
  * 2007-11-26  improved tcp timers: switched to local_timer (andrei)
  * 2008-02-04  optimizations: handle POLLRDHUP (if supported), detect short
  *              reads (sock. buffer empty) (andrei)
+ * 2009-02-26  direct blacklist support (andrei)
  */
 
 #ifdef USE_TCP
@@ -72,7 +73,10 @@
 #include "tls/tls_server.h"
 #else
 #include "tls_hooks.h"
-#endif
+#endif /* CORE_TLS */
+#ifdef USE_DST_BLACKLIST
+#include "dst_blacklist.h"
+#endif /* USE_DST_BLACKLIST */
 
 #define HANDLE_IO_INLINE
 #include "io_wait.h"
@@ -142,6 +146,19 @@ again:
 				bytes_read=0; /* nothing has been read */
 			}else if (errno == EINTR) goto again;
 			else{
+#ifdef USE_DST_BLACKLIST
+				if (cfg_get(core, core_cfg, use_dst_blacklist))
+					switch(errno){
+						case ECONNRESET:
+						case ETIMEDOUT:
+							dst_blacklist_su((c->state==S_CONN_CONNECT)?
+													BLST_ERR_CONNECT:
+													BLST_ERR_SEND,
+													c->rcv.proto,
+													&c->rcv.src_su, 0);
+							break;
+					}
+#endif /* USE_DST_BLACKLIST */
 				LOG(L_ERR, "ERROR: tcp_read: error reading: %s (%d)\n",
 							strerror(errno), errno);
 				r->error=TCP_READ_ERROR;
@@ -152,10 +169,16 @@ again:
 			c->state=S_CONN_EOF;
 			*flags|=RD_CONN_EOF;
 			DBG("tcp_read: EOF on %p, FD %d\n", c, fd);
+		}else{
+			if (unlikely(c->state==S_CONN_CONNECT))
+				c->state=S_CONN_OK;
 		}
 		/* short read */
 		*flags|=RD_CONN_SHORT_READ;
-	} /* else normal full read */
+	}else{ /* else normal full read */
+		if (unlikely(c->state==S_CONN_CONNECT))
+			c->state=S_CONN_OK;
+	}
 #ifdef EXTRA_DEBUG
 	DBG("tcp_read: read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos);
 #endif