Browse code

tcp: support for tls partial encoding

The tls_encode() callback supports now partial encoding. It can
encode only part of the input buffer (e.g. tls partial write
enabled and not enough space in the internal buffers for all the
tls records) and ask to be called back later with the remaining
un-encoded part.
The send flags are also passed to it, so that it can modify them
if needed (e.g. reset SND_F_CON_CLOSE on partial tls write or when
reading some data is needed before continuing the tls write).

Andrei Pelinescu-Onciul authored on 05/07/2010 12:44:14
Showing 2 changed files
... ...
@@ -1740,6 +1740,13 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1740 1740
 	int n;
1741 1741
 	int do_close_fd;
1742 1742
 	ticks_t con_lifetime;
1743
+#ifdef USE_TLS
1744
+	const char* rest_buf;
1745
+	const char* t_buf;
1746
+	unsigned rest_len, t_len;
1747
+	long resp;
1748
+	snd_flags_t t_send_flags;
1749
+#endif /* USE_TLS */
1743 1750
 	
1744 1751
 	do_close_fd=1; /* close the fd on exit */
1745 1752
 	port=su_getport(&dst->to);
... ...
@@ -1856,21 +1863,104 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1856 1856
 			/* for TLS the TLS processing and the send must happen
1857 1857
 			   atomically w/ respect to other sends on the same connection
1858 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.
1859
+			   lock. However in this case this send will always be the first.
1860
+			   We can have the send() outside the lock only if this is the
1861
+			   first and only send (tls_encode is not called again), or
1862
+			   this is the last send for a tls_encode() loop and all the
1863
+			   previous ones did return CONN_NEW_COMPLETE or CONN_EOF.
1861 1864
 			*/
1865
+				response[1] = CONN_NOP;
1866
+				t_buf = buf;
1867
+				t_len = len;
1862 1868
 				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;
1869
+redo_tls_encode:
1870
+					t_send_flags = dst->send_flags;
1871
+					n = tls_encode(c, &t_buf, &t_len, &rest_buf, &rest_len,
1872
+									&t_send_flags);
1873
+					/* There are 4 cases:
1874
+					   1. entire buffer consumed from the first try
1875
+					     (rest_len == rest_buf == 0)
1876
+					   2. rest_buf & first call
1877
+					   3. rest_buf & not first call
1878
+						  3a. CONN_NEW_COMPLETE or CONN_EOF
1879
+						  3b. CONN_NEW_PENDING_WRITE
1880
+					   4. entire buffer consumed, but not first call
1881
+					       4a. CONN_NEW_COMPLETE or CONN_EOF
1882
+						   4b. CONN_NEW_PENDING_WRITE
1883
+						We misuse response[1] == CONN_NOP to test for the
1884
+						first call.
1885
+					*/
1886
+					if (unlikely(n < 0)) {
1887
+						lock_release(&c->write_lock);
1888
+						goto conn_wait_error;
1889
+					}
1890
+					if (likely(rest_len == 0)) {
1891
+						/* 1 or 4*: CONN_NEW_COMPLETE, CONN_EOF,  CONN_NOP
1892
+						    or CONN_NEW_PENDING_WRITE (*rest_len == 0) */
1893
+						if (likely(response[1] != CONN_NEW_PENDING_WRITE)) {
1894
+							/* 1 or 4a => it's safe to do the send outside the
1895
+							   lock (it will either send directly or
1896
+							   wbufq_insert())
1897
+							*/
1898
+							lock_release(&c->write_lock);
1899
+							if (likely(t_len != 0)) {
1900
+								n=tcpconn_1st_send(fd, c, t_buf, t_len,
1901
+													t_send_flags,
1902
+													&response[1], 0);
1903
+							} else { /* t_len == 0 */
1904
+								if (response[1] == CONN_NOP) {
1905
+									/* nothing to send (e.g  parallel send
1906
+									   tls_encode queues some data and then
1907
+									   WANT_READ => this tls_encode will queue
1908
+									   the cleartext too and will have nothing
1909
+									   to send right now) and initial send =>
1910
+									   behave as if the send was successful
1911
+									   (but never return EOF here) */
1912
+									response[1] = CONN_NEW_COMPLETE;
1913
+								}
1914
+							}
1915
+							/* exit */
1916
+						} else {
1917
+							/* CONN_NEW_PENDING_WRITE:  4b: it was a
1918
+							   repeated tls_encode() (or otherwise we would
1919
+							   have here CONN_NOP) => add to the queue */
1920
+							if (unlikely(t_len &&
1921
+											_wbufq_add(c, t_buf, t_len) < 0)) {
1922
+								response[1] = CONN_ERROR;
1923
+								n = -1;
1924
+							}
1925
+							lock_release(&c->write_lock);
1926
+							/* exit (no send) */
1927
+						}
1928
+					} else {  /* rest_len != 0 */
1929
+						/* 2 or 3*: if tls_encode hasn't finished, we have to
1930
+						   call tcpconn_1st_send() under lock (otherwise if it
1931
+						   returns CONN_NEW_PENDING_WRITE, there is no way
1932
+						   to find the right place to add the new queued
1933
+						   data from the 2nd tls_encode()) */
1934
+						if (likely((response[1] == CONN_NOP /*2*/ ||
1935
+									response[1] == CONN_NEW_COMPLETE /*3a*/ ||
1936
+									response[1] == CONN_EOF /*3a*/) && t_len))
1937
+							n = tcpconn_1st_send(fd, c, t_buf, t_len,
1938
+													t_send_flags,
1939
+													&response[1], 1);
1940
+						else if (unlikely(t_len &&
1941
+											_wbufq_add(c, t_buf, t_len) < 0)) {
1942
+							/*3b: CONN_NEW_PENDING_WRITE*/
1943
+							response[1] = CONN_ERROR;
1944
+							n = -1;
1945
+						}
1946
+						if (likely(n >= 0)) {
1947
+							/* if t_len == 0 => nothing was sent => previous
1948
+							   response will be kept */
1949
+							t_buf = rest_buf;
1950
+							t_len = rest_len;
1951
+							goto redo_tls_encode;
1952
+						} else {
1953
+							lock_release(&c->write_lock);
1954
+							/* error exit */
1955
+						}
1956
+					}
1874 1957
 			} else
1875 1958
 #endif /* USE_TLS */
1876 1959
 				n=tcpconn_1st_send(fd, c, buf, len, dst->send_flags,
... ...
@@ -1935,16 +2025,31 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
1935 1935
 			   (otherwise reordering might occur which would break TLS) =>
1936 1936
 			   lock.
1937 1937
 			*/
1938
+			response[1] = CONN_NOP;
1939
+			t_buf = buf;
1940
+			t_len = len;
1938 1941
 			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;
1942
+				do {
1943
+					t_send_flags = dst->send_flags;
1944
+					n = tls_encode(c, &t_buf, &t_len, &rest_buf, &rest_len,
1945
+									&t_send_flags);
1946
+					if (likely(n > 0)) {
1947
+						n = tcpconn_do_send(fd, c, t_buf, t_len, t_send_flags,
1948
+												&resp, 1);
1949
+						if (likely(response[1] != CONN_QUEUED_WRITE ||
1950
+									resp == CONN_ERROR))
1951
+							/* don't overwrite a previous CONN_QUEUED_WRITE
1952
+							   unless error */
1953
+							response[1] = resp;
1954
+					} else  if (unlikely(n < 0)) {
1955
+						response[1] = CONN_ERROR;
1956
+						break;
1957
+					}
1958
+					/* else do nothing for n (t_len) == 0, keep
1959
+					   the last reponse */
1960
+					t_buf = rest_buf;
1961
+					t_len = rest_len;
1962
+				} while(unlikely(rest_len && n > 0));
1948 1963
 			lock_release(&c->write_lock);
1949 1964
 		} else
1950 1965
 #endif /* USE_TLS */
... ...
@@ -2058,6 +2163,13 @@ static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2058 2058
 	long response[2];
2059 2059
 	int n;
2060 2060
 	int do_close_fd;
2061
+#ifdef USE_TLS
2062
+	const char* rest_buf;
2063
+	const char* t_buf;
2064
+	unsigned rest_len, t_len;
2065
+	long resp;
2066
+	snd_flags_t t_send_flags;
2067
+#endif /* USE_TLS */
2061 2068
 #ifdef TCP_FD_CACHE
2062 2069
 	struct fd_cache_entry* fd_cache_e;
2063 2070
 	int use_fd_cache;
... ...
@@ -2086,24 +2198,35 @@ static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2086 2086
 					do_close_fd=0;
2087 2087
 #ifdef USE_TLS
2088 2088
 					if (unlikely(c->type==PROTO_TLS)) {
2089
-						n = tls_encode(c, &buf, &len);
2090
-						if (unlikely(n < 0)) {
2089
+						t_buf = buf;
2090
+						t_len = len;
2091
+						do {
2092
+							t_send_flags = send_flags;
2093
+							n = tls_encode(c, &t_buf, &t_len,
2094
+											&rest_buf, &rest_len,
2095
+											&t_send_flags);
2096
+							if (unlikely((n < 0) || (t_len &&
2097
+									 (_wbufq_add(c, t_buf, t_len) < 0)))) {
2098
+								lock_release(&c->write_lock);
2099
+								n=-1;
2100
+								response[1] = CONN_ERROR;
2101
+								c->state=S_CONN_BAD;
2102
+								c->timeout=get_ticks_raw(); /* force timeout */
2103
+								goto error;
2104
+							}
2105
+							t_buf = rest_buf;
2106
+							t_len = rest_len;
2107
+						} while(unlikely(rest_len && n > 0));
2108
+					} else
2109
+#endif /* USE_TLS */
2110
+						if (unlikely(len && (_wbufq_add(c, buf, len)<0))){
2091 2111
 							lock_release(&c->write_lock);
2112
+							n=-1;
2092 2113
 							response[1] = CONN_ERROR;
2093 2114
 							c->state=S_CONN_BAD;
2094 2115
 							c->timeout=get_ticks_raw(); /* force timeout */
2095 2116
 							goto error;
2096 2117
 						}
2097
-					}
2098
-#endif /* USE_TLS */
2099
-					if (unlikely(len && (_wbufq_add(c, buf, len)<0))){
2100
-						lock_release(&c->write_lock);
2101
-						n=-1;
2102
-						response[1] = CONN_ERROR;
2103
-						c->state=S_CONN_BAD;
2104
-						c->timeout=get_ticks_raw(); /* force timeout */
2105
-						goto error;
2106
-					}
2107 2118
 					n=len;
2108 2119
 					lock_release(&c->write_lock);
2109 2120
 					goto release_c;
... ...
@@ -2182,16 +2305,31 @@ static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2182 2182
 			   (otherwise reordering might occur which would break TLS) =>
2183 2183
 			   lock.
2184 2184
 			*/
2185
+			response[1] = CONN_NOP;
2186
+			t_buf = buf;
2187
+			t_len = len;
2185 2188
 			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;
2189
+				do {
2190
+					t_send_flags = send_flags;
2191
+					n = tls_encode(c, &t_buf, &t_len, &rest_buf, &rest_len,
2192
+									&t_send_flags);
2193
+					if (likely(n > 0)) {
2194
+						n = tcpconn_do_send(fd, c, t_buf, t_len, t_send_flags,
2195
+												&resp, 1);
2196
+						if (likely(response[1] != CONN_QUEUED_WRITE ||
2197
+									resp == CONN_ERROR))
2198
+							/* don't overwrite a previous CONN_QUEUED_WRITE
2199
+							   unless error */
2200
+							response[1] = resp;
2201
+					} else if (unlikely(n < 0)) {
2202
+						response[1] = CONN_ERROR;
2203
+						break;
2204
+					}
2205
+					/* else do nothing for n (t_len) == 0, keep
2206
+					   the last reponse */
2207
+					t_buf = rest_buf;
2208
+					t_len = rest_len;
2209
+				} while(unlikely(rest_len && n > 0));
2195 2210
 			lock_release(&c->write_lock);
2196 2211
 		} else
2197 2212
 #endif
... ...
@@ -2511,7 +2649,8 @@ static int tcpconn_1st_send(int fd, struct tcp_connection* c,
2511 2511
 				" (%d/%d bytes written)\n", c, n, len);
2512 2512
 			if (unlikely(n<0)) n=0;
2513 2513
 			else{
2514
-				TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2514
+				if (likely(c->state == S_CONN_CONNECT))
2515
+					TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2515 2516
 				c->state=S_CONN_OK; /* partial write => connect()
2516 2517
 												ended */
2517 2518
 			}
... ...
@@ -2565,7 +2704,8 @@ static int tcpconn_1st_send(int fd, struct tcp_connection* c,
2565 2565
 		goto error;
2566 2566
 	}
2567 2567
 	LOG(L_INFO, "quick connect for %p\n", c);
2568
-	TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2568
+	if (likely(c->state == S_CONN_CONNECT))
2569
+		TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2569 2570
 	if (unlikely(send_flags.f & SND_F_CON_CLOSE)){
2570 2571
 		/* close after write =>  EOF => close immediately */
2571 2572
 		c->state=S_CONN_BAD;
... ...
@@ -55,12 +55,21 @@ struct tls_hooks{
55 55
 	   get the data from the connection) */
56 56
 	int  (*read)(struct tcp_connection* c, int* flags);
57 57
 	/* process data for sending. Should replace pbuf & plen with
58
-	   an internal buffer containing the tls records.
58
+	   an internal buffer containing the tls records. If it was not able
59
+	   to process the whole pbuf, it should set (rest_buf, rest_len) to
60
+	   the remaining unprocessed part, else they must be set to 0.
61
+	   send_flags are passed as a pointer and they can also be changed
62
+	   (e.g. reset a FORCE_CLOSE flag if there is internal queued data
63
+	    waiting to be written).
64
+	   If rest_len or rest_buf are not 0 the call will be repeated after the
65
+	   contents of pbuf is sent, with (rest_buf, rest_len) as input.
59 66
 	   Should return *plen (if >=0).
60 67
 	   If it returns < 0 => error (tcp connection will be closed).
61 68
 	*/
62
-	int (*encode)(struct tcp_connection* c, const char** pbuf,
63
-						unsigned int* plen);
69
+	int (*encode)(struct tcp_connection* c,
70
+					const char** pbuf, unsigned int* plen,
71
+					const char** rest_buf, unsigned int* rest_len,
72
+					snd_flags_t* send_flags);
64 73
 	int  (*on_tcpconn_init)(struct tcp_connection *c, int sock);
65 74
 	void (*tcpconn_clean)(struct tcp_connection* c);
66 75
 	void (*tcpconn_close)(struct tcp_connection*c , int fd);
... ...
@@ -99,8 +108,8 @@ extern struct tls_hooks tls_hook;
99 99
 
100 100
 #define tls_tcpconn_init(c, s)	tls_hook_call(on_tcpconn_init, 0, (c), (s))
101 101
 #define tls_tcpconn_clean(c)	tls_hook_call_v(tcpconn_clean, (c))
102
-#define tls_encode(c, pbuf, plen) \
103
-	tls_hook_call(encode, -1, (c), (pbuf), (plen))
102
+#define tls_encode(c, pbuf, plen, rbuf, rlen, sflags) \
103
+	tls_hook_call(encode, -1, (c), (pbuf), (plen), (rbuf), (rlen), (sflags))
104 104
 #define tls_close(conn, fd)		tls_hook_call_v(tcpconn_close, (conn), (fd))
105 105
 #define tls_read(c, flags)				tls_hook_call(read, -1, (c), (flags))
106 106