Browse code

tcp: change tls send callback interface

Instead of 2 different tls send callbacks (with a 3rd one needed),
switch to a different model: 1 tls callback that is supposed to
replace the passed buffer with a tls processed version of it.
This simplifies the tls code and more importantly doesn't require
that the tls send code has very detailed knowledge about the tcp
state machine. Some of the saved complexity moved from the tls
module to the tcp code, but at least this way on changes there's
only one place to update.
The tls callbacks for reading and sending are now very different:
while the send callback has become now more of an encoder
callback, the read callback should still perform the tcp read by
itself. While this is not very consistent it does saves unneeded
memory copies.

Andrei Pelinescu-Onciul authored on 23/06/2010 21:17:15
Showing 5 changed files
... ...
@@ -29,20 +29,12 @@
29 29
 
30 30
 #include "tcp_conn.h"
31 31
 
32
-int tcpconn_do_send(int fd, struct tcp_connection* c,
33
-							char* buf, unsigned len,
34
-							snd_flags_t send_flags, long* resp, int locked);
35
-
36
-int tcpconn_1st_send(int fd, struct tcp_connection* c,
37
-							char* buf, unsigned len,
38
-							snd_flags_t send_flags, long* resp, int locked);
39
-
40 32
 int tcpconn_send_unsafe(int fd, struct tcp_connection *c,
41
-						char* buf, unsigned len, snd_flags_t send_flags);
33
+						const char* buf, unsigned len, snd_flags_t send_flags);
42 34
 
43 35
 /* direct non-blocking, unsafe (assumes locked) send on a tcp connection */
44 36
 int _tcpconn_write_nb(int fd, struct tcp_connection* c,
45
-									char* buf, int len);
37
+									const char* buf, int len);
46 38
 
47 39
 
48 40
 #endif /*__tcp_int_send_h*/
... ...
@@ -642,7 +642,7 @@ end:
642 642
 
643 643
 
644 644
 /* unsafe version, call while holding the connection write lock */
645
-inline static int _wbufq_add(struct  tcp_connection* c, char* data, 
645
+inline static int _wbufq_add(struct  tcp_connection* c, const char* data, 
646 646
 							unsigned int size)
647 647
 {
648 648
 	struct tcp_wbuffer_queue* q;
... ...
@@ -661,8 +661,8 @@ inline static int _wbufq_add(struct  tcp_connection* c, char* data,
661 661
 		LOG(L_ERR, "ERROR: wbufq_add(%d bytes): write queue full or timeout "
662 662
 					" (%d, total %d, last write %d s ago)\n",
663 663
 					size, q->queued, *tcp_total_wq,
664
-					TICKS_TO_S(t-q->wr_timeout-
665
-						cfg_get(tcp, tcp_cfg, send_timeout)));
664
+					TICKS_TO_S(t-(q->wr_timeout-
665
+								cfg_get(tcp, tcp_cfg, send_timeout))));
666 666
 		if (q->first && TICKS_LT(q->wr_timeout, t)){
667 667
 			if (unlikely(c->state==S_CONN_CONNECT)){
668 668
 #ifdef USE_DST_BLACKLIST
... ...
@@ -740,7 +740,7 @@ error:
740 740
  * inserts data at the beginning, it ignores the max queue size checks and
741 741
  * the timeout (use sparingly)
742 742
  * Note: it should never be called on a write buffer after wbufq_run() */
743
-inline static int _wbufq_insert(struct  tcp_connection* c, char* data, 
743
+inline static int _wbufq_insert(struct  tcp_connection* c, const char* data, 
744 744
 							unsigned int size)
745 745
 {
746 746
 	struct tcp_wbuffer_queue* q;
... ...
@@ -1714,8 +1714,15 @@ inline static void tcp_fd_cache_add(struct tcp_connection *c, int fd)
1714 1714
 
1715 1715
 inline static int tcpconn_chld_put(struct tcp_connection* tcpconn);
1716 1716
 
1717
-static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
1718
-							snd_flags_t send_flags);
1717
+static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
1718
+							unsigned len, snd_flags_t send_flags);
1719
+static int tcpconn_do_send(int fd, struct tcp_connection* c,
1720
+							const char* buf, unsigned len,
1721
+							snd_flags_t send_flags, long* resp, int locked);
1722
+
1723
+static int tcpconn_1st_send(int fd, struct tcp_connection* c,
1724
+							const char* buf, unsigned len,
1725
+							snd_flags_t send_flags, long* resp, int locked);
1719 1726
 
1720 1727
 /* finds a tcpconn & sends on it
1721 1728
  * uses the dst members to, proto (TCP|TLS) and id and tries to send
... ...
@@ -1723,7 +1730,7 @@ static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
1723 1723
  * returns: number of bytes written (>=0) on success
1724 1724
  *          <0 on error */
1725 1725
 int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1726
-					char* buf, unsigned len)
1726
+					const char* buf, unsigned len)
1727 1727
 {
1728 1728
 	struct tcp_connection *c;
1729 1729
 	struct ip_addr ip;
... ...
@@ -1841,30 +1848,47 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1841 1841
 			 * pending and nobody else will try to write on it. However
1842 1842
 			 * this might produce out-of-order writes. If this is not
1843 1843
 			 * desired either lock before the write or use 
1844
-			 * _wbufq_insert(...) */
1844
+			 * _wbufq_insert(...)
1845
+			 * NOTE2: _wbufq_insert() is used now (no out-of-order).
1846
+			 */
1845 1847
 #ifdef USE_TLS
1846
-			if (unlikely(c->type==PROTO_TLS))
1847
-				n=tls_1st_send(fd, c, buf, len, dst->send_flags,
1848
-									&response[1]);
1849
-			else
1848
+			if (unlikely(c->type==PROTO_TLS)) {
1849
+			/* for TLS the TLS processing and the send must happen
1850
+			   atomically w/ respect to other sends on the same connection
1851
+			   (otherwise reordering might occur which would break TLS) =>
1852
+			   lock. However in this case this send will always be the first
1853
+			   => we can have the send() out of the lock.
1854
+			*/
1855
+				lock_get(&c->write_lock);
1856
+					n = tls_encode(c, &buf, &len);
1857
+				lock_release(&c->write_lock);
1858
+				if (likely(n >= 0 && len))
1859
+					n=tcpconn_1st_send(fd, c, buf, len, dst->send_flags,
1860
+										&response[1], 0);
1861
+				else if (n < 0)
1862
+					response[1] = CONN_ERROR;
1863
+				else
1864
+					/* len == 0 => nothing to send  (but still have to
1865
+					   inform tcp_main about the new connection) */
1866
+					response[1] = CONN_NEW_COMPLETE;
1867
+			} else
1850 1868
 #endif /* USE_TLS */
1851 1869
 				n=tcpconn_1st_send(fd, c, buf, len, dst->send_flags,
1852 1870
 									&response[1], 0);
1853
-			if (unlikely(n<0))
1871
+			if (unlikely(n<0)) /* this will catch CONN_ERROR too */
1854 1872
 				goto conn_wait_error;
1855 1873
 			if (unlikely(response[1]==CONN_EOF)){
1856 1874
 				/* if close-after-send requested, don't bother
1857 1875
 				   sending the fd back to tcp_main, try closing it
1858 1876
 				   immediately (no other tcp_send should use it,
1859 1877
 				   because it is marked as close-after-send before
1860
-				   being added to the hash */
1878
+				   being added to the hash) */
1861 1879
 				goto conn_wait_close;
1862 1880
 			}
1863 1881
 			/* send to tcp_main */
1864 1882
 			response[0]=(long)c;
1865
-			if (unlikely(response[1]!=CONN_NOP &&
1866
-						(send_fd(unix_tcp_sock, response,
1867
-									sizeof(response), fd) <= 0))){
1883
+			if (unlikely(send_fd(unix_tcp_sock, response,
1884
+									sizeof(response), fd) <= 0)){
1868 1885
 				LOG(L_ERR, "BUG: tcp_send %s: %ld for %p"
1869 1886
 							" failed:" " %s (%d)\n",
1870 1887
 							su2a(&dst->to, sizeof(dst->to)),
... ...
@@ -1906,9 +1930,22 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1906 1906
 		/* new connection => send on it directly */
1907 1907
 #ifdef USE_TLS
1908 1908
 		if (unlikely(c->type==PROTO_TLS)) {
1909
-			response[1] = CONN_ERROR; /* in case tls is not loaded */
1910
-			n = tls_do_send(fd, c, buf, len, dst->send_flags,
1911
-							&response[1]);
1909
+			/* for TLS the TLS processing and the send must happen
1910
+			   atomically w/ respect to other sends on the same connection
1911
+			   (otherwise reordering might occur which would break TLS) =>
1912
+			   lock.
1913
+			*/
1914
+			lock_get(&c->write_lock);
1915
+				n = tls_encode(c, &buf, &len);
1916
+				if (likely(n > 0))
1917
+					n = tcpconn_do_send(fd, c, buf, len, dst->send_flags,
1918
+											&response[1], 1);
1919
+				else if (n == 0)
1920
+					/* len == 0 => nothing to send */
1921
+					response[1] = CONN_NOP;
1922
+				else  /* n < 0 */
1923
+					response[1] = CONN_ERROR;
1924
+			lock_release(&c->write_lock);
1912 1925
 		} else
1913 1926
 #endif /* USE_TLS */
1914 1927
 			n = tcpconn_do_send(fd, c, buf, len, dst->send_flags,
... ...
@@ -2013,8 +2050,8 @@ end_no_conn:
2013 2013
  * @param len - data length,
2014 2014
  * @return >=0 on success, -1 on error.
2015 2015
  */
2016
-static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
2017
-							snd_flags_t send_flags)
2016
+static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2017
+								unsigned len, snd_flags_t send_flags)
2018 2018
 {
2019 2019
 	struct tcp_connection *tmp;
2020 2020
 	int fd;
... ...
@@ -2047,7 +2084,19 @@ static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
2047 2047
 #endif /* TCP_CONNECT_WAIT */
2048 2048
 				{
2049 2049
 					do_close_fd=0;
2050
-					if (unlikely(_wbufq_add(c, buf, len)<0)){
2050
+#ifdef USE_TLS
2051
+					if (unlikely(c->type==PROTO_TLS)) {
2052
+						n = tls_encode(c, &buf, &len);
2053
+						if (unlikely(n < 0)) {
2054
+							lock_release(&c->write_lock);
2055
+							response[1] = CONN_ERROR;
2056
+							c->state=S_CONN_BAD;
2057
+							c->timeout=get_ticks_raw(); /* force timeout */
2058
+							goto error;
2059
+						}
2060
+					}
2061
+#endif /* USE_TLS */
2062
+					if (unlikely(len && (_wbufq_add(c, buf, len)<0))){
2051 2063
 						lock_release(&c->write_lock);
2052 2064
 						n=-1;
2053 2065
 						response[1] = CONN_ERROR;
... ...
@@ -2128,8 +2177,22 @@ static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
2128 2128
 	
2129 2129
 #ifdef USE_TLS
2130 2130
 		if (unlikely(c->type==PROTO_TLS)) {
2131
-			response[1] = CONN_ERROR; /* in case tls is not loaded */
2132
-			n = tls_do_send(fd, c, buf, len, send_flags, &response[1]);
2131
+			/* for TLS the TLS processing and the send must happen
2132
+			   atomically w/ respect to other sends on the same connection
2133
+			   (otherwise reordering might occur which would break TLS) =>
2134
+			   lock.
2135
+			*/
2136
+			lock_get(&c->write_lock);
2137
+				n = tls_encode(c, &buf, &len);
2138
+				if (likely(n > 0))
2139
+					n = tcpconn_do_send(fd, c, buf, len, send_flags,
2140
+											&response[1], 1);
2141
+				else if (n == 0)
2142
+					/* len == 0 => nothing to send */
2143
+					response[1] = CONN_NOP;
2144
+				else /* n < 0 */
2145
+					response[1] = CONN_ERROR;
2146
+			lock_release(&c->write_lock);
2133 2147
 		} else
2134 2148
 #endif
2135 2149
 			n = tcpconn_do_send(fd, c, buf, len, send_flags, &response[1], 0);
... ...
@@ -2202,7 +2265,7 @@ release_c:
2202 2202
  * @return <0 on error, number of bytes sent on success.
2203 2203
  */
2204 2204
 int tcpconn_send_unsafe(int fd, struct tcp_connection *c,
2205
-						char* buf, unsigned len, snd_flags_t send_flags)
2205
+						const char* buf, unsigned len, snd_flags_t send_flags)
2206 2206
 {
2207 2207
 	int n;
2208 2208
 	long response[2];
... ...
@@ -2257,8 +2320,8 @@ int tcpconn_send_unsafe(int fd, struct tcp_connection *c,
2257 2257
  * @return >=0 on success, < 0 on error && *resp == CON_ERROR.
2258 2258
  *
2259 2259
  */
2260
-int tcpconn_do_send(int fd, struct tcp_connection* c,
2261
-							char* buf, unsigned len,
2260
+static int tcpconn_do_send(int fd, struct tcp_connection* c,
2261
+							const char* buf, unsigned len,
2262 2262
 							snd_flags_t send_flags, long* resp,
2263 2263
 							int locked)
2264 2264
 {
... ...
@@ -2416,25 +2479,26 @@ end:
2416 2416
  * @param buf - data to be sent.
2417 2417
  * @param len - data length.
2418 2418
  * @param send_flags
2419
- * @param resp - filled with a fd sending cmd. for tcp_main on success:
2420
- *                      CONN_NOP - nothing needs to be done (unused right now).
2419
+ * @param resp - filled with a fd sending cmd. for tcp_main on success. It
2420
+ *                      _must_ be one of the commands listed below:
2421 2421
  *                      CONN_NEW_PENDING_WRITE - new connection, first write
2422 2422
  *                                 was partially successful (or EAGAIN) and
2423 2423
  *                                 was queued (connection should be watched
2424 2424
  *                                 for write and the write queue flushed).
2425 2425
  *                                 The fd should be sent to tcp_main.
2426 2426
  *                      CONN_NEW_COMPLETE - new connection, first write
2427
- *                                 completed successfuly and no data is queued.
2428
- *                                 The fd should be sent to tcp_main.
2427
+ *                                 completed successfully and no data is
2428
+ *                                 queued. The fd should be sent to tcp_main.
2429 2429
  *                      CONN_EOF - no error, but the connection should be
2430 2430
  *                                  closed (e.g. SND_F_CON_CLOSE send flag).
2431
+ *                      CONN_ERROR - error, _must_ return < 0.
2431 2432
  * @param locked - if set assume the connection is already locked (call from
2432 2433
  *                  tls) and do not lock/unlock the connection.
2433 2434
  * @return >=0 on success, < 0 on error (on error *resp is undefined).
2434 2435
  *
2435 2436
  */
2436
-int tcpconn_1st_send(int fd, struct tcp_connection* c,
2437
-							char* buf, unsigned len,
2437
+static int tcpconn_1st_send(int fd, struct tcp_connection* c,
2438
+							const char* buf, unsigned len,
2438 2439
 							snd_flags_t send_flags, long* resp,
2439 2440
 							int locked)
2440 2441
 {
... ...
@@ -2682,8 +2746,10 @@ close_again:
2682 2682
 	if (unlikely(close(fd)<0)){
2683 2683
 		if (errno==EINTR)
2684 2684
 			goto close_again;
2685
-		LOG(L_ERR, "ERROR: tcpconn_put_destroy; close() failed: %s (%d)\n",
2686
-				strerror(errno), errno);
2685
+		LOG(L_ERR, "ERROR: tcpconn_close_main_fd(%p): %s "
2686
+					"close(%d) failed (flags 0x%x): %s (%d)\n", tcpconn,
2687
+					su2a(&tcpconn->rcv.src_su, sizeof(tcpconn->rcv.src_su)),
2688
+					fd, tcpconn->flags, strerror(errno), errno);
2687 2689
 	}
2688 2690
 }
2689 2691
 
... ...
@@ -2737,6 +2803,7 @@ inline static void tcpconn_destroy(struct tcp_connection* tcpconn)
2737 2737
 		}
2738 2738
 		if (likely(!(tcpconn->flags & F_CONN_FD_CLOSED))){
2739 2739
 			tcpconn_close_main_fd(tcpconn);
2740
+			tcpconn->flags|=F_CONN_FD_CLOSED;
2740 2741
 			(*tcp_connections_no)--;
2741 2742
 		}
2742 2743
 		_tcpconn_free(tcpconn); /* destroys also the wbuf_q if still present*/
... ...
@@ -2999,7 +3066,7 @@ rm_con:
2999 2999
  *  returns number of bytes written on success, -1 on error (and sets errno)
3000 3000
  */
3001 3001
 int _tcpconn_write_nb(int fd, struct tcp_connection* c,
3002
-									char* buf, int len)
3002
+									const char* buf, int len)
3003 3003
 {
3004 3004
 	int n;
3005 3005
 	
... ...
@@ -35,7 +35,7 @@
35 35
 /* "public" functions*/
36 36
 
37 37
 int tcp_send(struct dest_info* dst, union sockaddr_union* from,
38
-				char* buf, unsigned len);
38
+				const char* buf, unsigned len);
39 39
 
40 40
 int tcpconn_add_alias(int id, int port, int proto);
41 41
 
... ...
@@ -37,7 +37,7 @@
37 37
 
38 38
 #ifdef TLS_HOOKS
39 39
 
40
-struct tls_hooks tls_hook= {0, 0, 0, 0, 0 ,0 ,0 ,0 ,0 };
40
+struct tls_hooks tls_hook= {0, 0, 0, 0, 0 ,0 ,0};
41 41
 
42 42
 static int tls_hooks_loaded=0;
43 43
 
... ...
@@ -51,15 +51,16 @@
51 51
 
52 52
 
53 53
 struct tls_hooks{
54
+	/* read using tls (should use tcp internal read functions to
55
+	   get the data from the connection) */
54 56
 	int  (*read)(struct tcp_connection* c, int* flags);
55
-	/* send using tls on a tcp connection */
56
-	int (*do_send)(int fd, struct tcp_connection* c, const char* buf,
57
-							unsigned int len, snd_flags_t send_flags,
58
-							long* resp);
59
-	/* 1st send using tls on a new async. tcp connection */
60
-	int (*fst_send)(int fd, struct tcp_connection* c, const char* buf,
61
-							unsigned int len, snd_flags_t send_flags,
62
-							long* resp);
57
+	/* process data for sending. Should replace pbuf & plen with
58
+	   an internal buffer containing the tls records.
59
+	   Should return *plen (if >=0).
60
+	   If it returns < 0 => error (tcp connection will be closed).
61
+	*/
62
+	int (*encode)(struct tcp_connection* c, const char** pbuf,
63
+						unsigned int* plen);
63 64
 	int  (*on_tcpconn_init)(struct tcp_connection *c, int sock);
64 65
 	void (*tcpconn_clean)(struct tcp_connection* c);
65 66
 	void (*tcpconn_close)(struct tcp_connection*c , int fd);
... ...
@@ -98,10 +99,8 @@ extern struct tls_hooks tls_hook;
98 98
 
99 99
 #define tls_tcpconn_init(c, s)	tls_hook_call(on_tcpconn_init, 0, (c), (s))
100 100
 #define tls_tcpconn_clean(c)	tls_hook_call_v(tcpconn_clean, (c))
101
-#define tls_do_send(fd, c, buf, len, send_flags, resp) \
102
-	tls_hook_call(do_send, -1, (fd), (c), (buf), (len), (send_flags), (resp))
103
-#define tls_1st_send(fd, c, buf, len, send_flags, resp) \
104
-	tls_hook_call(fst_send, -1, (fd), (c), (buf), (len), (send_flags), (resp))
101
+#define tls_encode(c, pbuf, plen) \
102
+	tls_hook_call(encode, -1, (c), (pbuf), (plen))
105 103
 #define tls_close(conn, fd)		tls_hook_call_v(tcpconn_close, (conn), (fd))
106 104
 #define tls_read(c, flags)				tls_hook_call(read, -1, (c), (flags))
107 105