Browse Source

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 năm trước cách đây
mục cha
commit
7a0489e835
12 tập tin đã thay đổi với 311 bổ sung44 xóa
  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