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 1730
  * returns: number of bytes written (>=0) on success
1724 1731
  *          <0 on error */
1725 1732
 int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1726
-					char* buf, unsigned len)
1733
+					const char* buf, unsigned len)
1727 1734
 {
1728 1735
 	struct tcp_connection *c;
1729 1736
 	struct ip_addr ip;
... ...
@@ -1841,30 +1848,47 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1841 1848
 			 * pending and nobody else will try to write on it. However
1842 1849
 			 * this might produce out-of-order writes. If this is not
1843 1850
 			 * desired either lock before the write or use 
1844
-			 * _wbufq_insert(...) */
1851
+			 * _wbufq_insert(...)
1852
+			 * NOTE2: _wbufq_insert() is used now (no out-of-order).
1853
+			 */
1845 1854
 #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
1855
+			if (unlikely(c->type==PROTO_TLS)) {
1856
+			/* for TLS the TLS processing and the send must happen
1857
+			   atomically w/ respect to other sends on the same connection
1858
+			   (otherwise reordering might occur which would break TLS) =>
1859
+			   lock. However in this case this send will always be the first
1860
+			   => we can have the send() out of the lock.
1861
+			*/
1862
+				lock_get(&c->write_lock);
1863
+					n = tls_encode(c, &buf, &len);
1864
+				lock_release(&c->write_lock);
1865
+				if (likely(n >= 0 && len))
1866
+					n=tcpconn_1st_send(fd, c, buf, len, dst->send_flags,
1867
+										&response[1], 0);
1868
+				else if (n < 0)
1869
+					response[1] = CONN_ERROR;
1870
+				else
1871
+					/* len == 0 => nothing to send  (but still have to
1872
+					   inform tcp_main about the new connection) */
1873
+					response[1] = CONN_NEW_COMPLETE;
1874
+			} else
1850 1875
 #endif /* USE_TLS */
1851 1876
 				n=tcpconn_1st_send(fd, c, buf, len, dst->send_flags,
1852 1877
 									&response[1], 0);
1853
-			if (unlikely(n<0))
1878
+			if (unlikely(n<0)) /* this will catch CONN_ERROR too */
1854 1879
 				goto conn_wait_error;
1855 1880
 			if (unlikely(response[1]==CONN_EOF)){
1856 1881
 				/* if close-after-send requested, don't bother
1857 1882
 				   sending the fd back to tcp_main, try closing it
1858 1883
 				   immediately (no other tcp_send should use it,
1859 1884
 				   because it is marked as close-after-send before
1860
-				   being added to the hash */
1885
+				   being added to the hash) */
1861 1886
 				goto conn_wait_close;
1862 1887
 			}
1863 1888
 			/* send to tcp_main */
1864 1889
 			response[0]=(long)c;
1865
-			if (unlikely(response[1]!=CONN_NOP &&
1866
-						(send_fd(unix_tcp_sock, response,
1867
-									sizeof(response), fd) <= 0))){
1890
+			if (unlikely(send_fd(unix_tcp_sock, response,
1891
+									sizeof(response), fd) <= 0)){
1868 1892
 				LOG(L_ERR, "BUG: tcp_send %s: %ld for %p"
1869 1893
 							" failed:" " %s (%d)\n",
1870 1894
 							su2a(&dst->to, sizeof(dst->to)),
... ...
@@ -1906,9 +1930,22 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1906 1930
 		/* new connection => send on it directly */
1907 1931
 #ifdef USE_TLS
1908 1932
 		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]);
1933
+			/* for TLS the TLS processing and the send must happen
1934
+			   atomically w/ respect to other sends on the same connection
1935
+			   (otherwise reordering might occur which would break TLS) =>
1936
+			   lock.
1937
+			*/
1938
+			lock_get(&c->write_lock);
1939
+				n = tls_encode(c, &buf, &len);
1940
+				if (likely(n > 0))
1941
+					n = tcpconn_do_send(fd, c, buf, len, dst->send_flags,
1942
+											&response[1], 1);
1943
+				else if (n == 0)
1944
+					/* len == 0 => nothing to send */
1945
+					response[1] = CONN_NOP;
1946
+				else  /* n < 0 */
1947
+					response[1] = CONN_ERROR;
1948
+			lock_release(&c->write_lock);
1912 1949
 		} else
1913 1950
 #endif /* USE_TLS */
1914 1951
 			n = tcpconn_do_send(fd, c, buf, len, dst->send_flags,
... ...
@@ -2013,8 +2050,8 @@ end_no_conn:
2013 2050
  * @param len - data length,
2014 2051
  * @return >=0 on success, -1 on error.
2015 2052
  */
2016
-static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
2017
-							snd_flags_t send_flags)
2053
+static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2054
+								unsigned len, snd_flags_t send_flags)
2018 2055
 {
2019 2056
 	struct tcp_connection *tmp;
2020 2057
 	int fd;
... ...
@@ -2047,7 +2084,19 @@ static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
2047 2084
 #endif /* TCP_CONNECT_WAIT */
2048 2085
 				{
2049 2086
 					do_close_fd=0;
2050
-					if (unlikely(_wbufq_add(c, buf, len)<0)){
2087
+#ifdef USE_TLS
2088
+					if (unlikely(c->type==PROTO_TLS)) {
2089
+						n = tls_encode(c, &buf, &len);
2090
+						if (unlikely(n < 0)) {
2091
+							lock_release(&c->write_lock);
2092
+							response[1] = CONN_ERROR;
2093
+							c->state=S_CONN_BAD;
2094
+							c->timeout=get_ticks_raw(); /* force timeout */
2095
+							goto error;
2096
+						}
2097
+					}
2098
+#endif /* USE_TLS */
2099
+					if (unlikely(len && (_wbufq_add(c, buf, len)<0))){
2051 2100
 						lock_release(&c->write_lock);
2052 2101
 						n=-1;
2053 2102
 						response[1] = CONN_ERROR;
... ...
@@ -2128,8 +2177,22 @@ static int tcpconn_send_put(struct tcp_connection* c, char* buf, unsigned len,
2128 2177
 	
2129 2178
 #ifdef USE_TLS
2130 2179
 		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]);
2180
+			/* for TLS the TLS processing and the send must happen
2181
+			   atomically w/ respect to other sends on the same connection
2182
+			   (otherwise reordering might occur which would break TLS) =>
2183
+			   lock.
2184
+			*/
2185
+			lock_get(&c->write_lock);
2186
+				n = tls_encode(c, &buf, &len);
2187
+				if (likely(n > 0))
2188
+					n = tcpconn_do_send(fd, c, buf, len, send_flags,
2189
+											&response[1], 1);
2190
+				else if (n == 0)
2191
+					/* len == 0 => nothing to send */
2192
+					response[1] = CONN_NOP;
2193
+				else /* n < 0 */
2194
+					response[1] = CONN_ERROR;
2195
+			lock_release(&c->write_lock);
2133 2196
 		} else
2134 2197
 #endif
2135 2198
 			n = tcpconn_do_send(fd, c, buf, len, send_flags, &response[1], 0);
... ...
@@ -2202,7 +2265,7 @@ release_c:
2202 2265
  * @return <0 on error, number of bytes sent on success.
2203 2266
  */
2204 2267
 int tcpconn_send_unsafe(int fd, struct tcp_connection *c,
2205
-						char* buf, unsigned len, snd_flags_t send_flags)
2268
+						const char* buf, unsigned len, snd_flags_t send_flags)
2206 2269
 {
2207 2270
 	int n;
2208 2271
 	long response[2];
... ...
@@ -2257,8 +2320,8 @@ int tcpconn_send_unsafe(int fd, struct tcp_connection *c,
2257 2320
  * @return >=0 on success, < 0 on error && *resp == CON_ERROR.
2258 2321
  *
2259 2322
  */
2260
-int tcpconn_do_send(int fd, struct tcp_connection* c,
2261
-							char* buf, unsigned len,
2323
+static int tcpconn_do_send(int fd, struct tcp_connection* c,
2324
+							const char* buf, unsigned len,
2262 2325
 							snd_flags_t send_flags, long* resp,
2263 2326
 							int locked)
2264 2327
 {
... ...
@@ -2416,25 +2479,26 @@ end:
2416 2479
  * @param buf - data to be sent.
2417 2480
  * @param len - data length.
2418 2481
  * @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).
2482
+ * @param resp - filled with a fd sending cmd. for tcp_main on success. It
2483
+ *                      _must_ be one of the commands listed below:
2421 2484
  *                      CONN_NEW_PENDING_WRITE - new connection, first write
2422 2485
  *                                 was partially successful (or EAGAIN) and
2423 2486
  *                                 was queued (connection should be watched
2424 2487
  *                                 for write and the write queue flushed).
2425 2488
  *                                 The fd should be sent to tcp_main.
2426 2489
  *                      CONN_NEW_COMPLETE - new connection, first write
2427
- *                                 completed successfuly and no data is queued.
2428
- *                                 The fd should be sent to tcp_main.
2490
+ *                                 completed successfully and no data is
2491
+ *                                 queued. The fd should be sent to tcp_main.
2429 2492
  *                      CONN_EOF - no error, but the connection should be
2430 2493
  *                                  closed (e.g. SND_F_CON_CLOSE send flag).
2494
+ *                      CONN_ERROR - error, _must_ return < 0.
2431 2495
  * @param locked - if set assume the connection is already locked (call from
2432 2496
  *                  tls) and do not lock/unlock the connection.
2433 2497
  * @return >=0 on success, < 0 on error (on error *resp is undefined).
2434 2498
  *
2435 2499
  */
2436
-int tcpconn_1st_send(int fd, struct tcp_connection* c,
2437
-							char* buf, unsigned len,
2500
+static int tcpconn_1st_send(int fd, struct tcp_connection* c,
2501
+							const char* buf, unsigned len,
2438 2502
 							snd_flags_t send_flags, long* resp,
2439 2503
 							int locked)
2440 2504
 {
... ...
@@ -2682,8 +2746,10 @@ close_again:
2682 2746
 	if (unlikely(close(fd)<0)){
2683 2747
 		if (errno==EINTR)
2684 2748
 			goto close_again;
2685
-		LOG(L_ERR, "ERROR: tcpconn_put_destroy; close() failed: %s (%d)\n",
2686
-				strerror(errno), errno);
2749
+		LOG(L_ERR, "ERROR: tcpconn_close_main_fd(%p): %s "
2750
+					"close(%d) failed (flags 0x%x): %s (%d)\n", tcpconn,
2751
+					su2a(&tcpconn->rcv.src_su, sizeof(tcpconn->rcv.src_su)),
2752
+					fd, tcpconn->flags, strerror(errno), errno);
2687 2753
 	}
2688 2754
 }
2689 2755
 
... ...
@@ -2737,6 +2803,7 @@ inline static void tcpconn_destroy(struct tcp_connection* tcpconn)
2737 2803
 		}
2738 2804
 		if (likely(!(tcpconn->flags & F_CONN_FD_CLOSED))){
2739 2805
 			tcpconn_close_main_fd(tcpconn);
2806
+			tcpconn->flags|=F_CONN_FD_CLOSED;
2740 2807
 			(*tcp_connections_no)--;
2741 2808
 		}
2742 2809
 		_tcpconn_free(tcpconn); /* destroys also the wbuf_q if still present*/
... ...
@@ -2999,7 +3066,7 @@ rm_con:
2999 3066
  *  returns number of bytes written on success, -1 on error (and sets errno)
3000 3067
  */
3001 3068
 int _tcpconn_write_nb(int fd, struct tcp_connection* c,
3002
-									char* buf, int len)
3069
+									const char* buf, int len)
3003 3070
 {
3004 3071
 	int n;
3005 3072
 	
... ...
@@ -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 99
 
99 100
 #define tls_tcpconn_init(c, s)	tls_hook_call(on_tcpconn_init, 0, (c), (s))
100 101
 #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))
102
+#define tls_encode(c, pbuf, plen) \
103
+	tls_hook_call(encode, -1, (c), (pbuf), (plen))
105 104
 #define tls_close(conn, fd)		tls_hook_call_v(tcpconn_close, (conn), (fd))
106 105
 #define tls_read(c, flags)				tls_hook_call(read, -1, (c), (flags))
107 106