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 1863
 			/* for TLS the TLS processing and the send must happen
1857 1864
 			   atomically w/ respect to other sends on the same connection
1858 1865
 			   (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.
1866
+			   lock. However in this case this send will always be the first.
1867
+			   We can have the send() outside the lock only if this is the
1868
+			   first and only send (tls_encode is not called again), or
1869
+			   this is the last send for a tls_encode() loop and all the
1870
+			   previous ones did return CONN_NEW_COMPLETE or CONN_EOF.
1861 1871
 			*/
1872
+				response[1] = CONN_NOP;
1873
+				t_buf = buf;
1874
+				t_len = len;
1862 1875
 				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;
1876
+redo_tls_encode:
1877
+					t_send_flags = dst->send_flags;
1878
+					n = tls_encode(c, &t_buf, &t_len, &rest_buf, &rest_len,
1879
+									&t_send_flags);
1880
+					/* There are 4 cases:
1881
+					   1. entire buffer consumed from the first try
1882
+					     (rest_len == rest_buf == 0)
1883
+					   2. rest_buf & first call
1884
+					   3. rest_buf & not first call
1885
+						  3a. CONN_NEW_COMPLETE or CONN_EOF
1886
+						  3b. CONN_NEW_PENDING_WRITE
1887
+					   4. entire buffer consumed, but not first call
1888
+					       4a. CONN_NEW_COMPLETE or CONN_EOF
1889
+						   4b. CONN_NEW_PENDING_WRITE
1890
+						We misuse response[1] == CONN_NOP to test for the
1891
+						first call.
1892
+					*/
1893
+					if (unlikely(n < 0)) {
1894
+						lock_release(&c->write_lock);
1895
+						goto conn_wait_error;
1896
+					}
1897
+					if (likely(rest_len == 0)) {
1898
+						/* 1 or 4*: CONN_NEW_COMPLETE, CONN_EOF,  CONN_NOP
1899
+						    or CONN_NEW_PENDING_WRITE (*rest_len == 0) */
1900
+						if (likely(response[1] != CONN_NEW_PENDING_WRITE)) {
1901
+							/* 1 or 4a => it's safe to do the send outside the
1902
+							   lock (it will either send directly or
1903
+							   wbufq_insert())
1904
+							*/
1905
+							lock_release(&c->write_lock);
1906
+							if (likely(t_len != 0)) {
1907
+								n=tcpconn_1st_send(fd, c, t_buf, t_len,
1908
+													t_send_flags,
1909
+													&response[1], 0);
1910
+							} else { /* t_len == 0 */
1911
+								if (response[1] == CONN_NOP) {
1912
+									/* nothing to send (e.g  parallel send
1913
+									   tls_encode queues some data and then
1914
+									   WANT_READ => this tls_encode will queue
1915
+									   the cleartext too and will have nothing
1916
+									   to send right now) and initial send =>
1917
+									   behave as if the send was successful
1918
+									   (but never return EOF here) */
1919
+									response[1] = CONN_NEW_COMPLETE;
1920
+								}
1921
+							}
1922
+							/* exit */
1923
+						} else {
1924
+							/* CONN_NEW_PENDING_WRITE:  4b: it was a
1925
+							   repeated tls_encode() (or otherwise we would
1926
+							   have here CONN_NOP) => add to the queue */
1927
+							if (unlikely(t_len &&
1928
+											_wbufq_add(c, t_buf, t_len) < 0)) {
1929
+								response[1] = CONN_ERROR;
1930
+								n = -1;
1931
+							}
1932
+							lock_release(&c->write_lock);
1933
+							/* exit (no send) */
1934
+						}
1935
+					} else {  /* rest_len != 0 */
1936
+						/* 2 or 3*: if tls_encode hasn't finished, we have to
1937
+						   call tcpconn_1st_send() under lock (otherwise if it
1938
+						   returns CONN_NEW_PENDING_WRITE, there is no way
1939
+						   to find the right place to add the new queued
1940
+						   data from the 2nd tls_encode()) */
1941
+						if (likely((response[1] == CONN_NOP /*2*/ ||
1942
+									response[1] == CONN_NEW_COMPLETE /*3a*/ ||
1943
+									response[1] == CONN_EOF /*3a*/) && t_len))
1944
+							n = tcpconn_1st_send(fd, c, t_buf, t_len,
1945
+													t_send_flags,
1946
+													&response[1], 1);
1947
+						else if (unlikely(t_len &&
1948
+											_wbufq_add(c, t_buf, t_len) < 0)) {
1949
+							/*3b: CONN_NEW_PENDING_WRITE*/
1950
+							response[1] = CONN_ERROR;
1951
+							n = -1;
1952
+						}
1953
+						if (likely(n >= 0)) {
1954
+							/* if t_len == 0 => nothing was sent => previous
1955
+							   response will be kept */
1956
+							t_buf = rest_buf;
1957
+							t_len = rest_len;
1958
+							goto redo_tls_encode;
1959
+						} else {
1960
+							lock_release(&c->write_lock);
1961
+							/* error exit */
1962
+						}
1963
+					}
1874 1964
 			} else
1875 1965
 #endif /* USE_TLS */
1876 1966
 				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 2025
 			   (otherwise reordering might occur which would break TLS) =>
1936 2026
 			   lock.
1937 2027
 			*/
2028
+			response[1] = CONN_NOP;
2029
+			t_buf = buf;
2030
+			t_len = len;
1938 2031
 			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;
2032
+				do {
2033
+					t_send_flags = dst->send_flags;
2034
+					n = tls_encode(c, &t_buf, &t_len, &rest_buf, &rest_len,
2035
+									&t_send_flags);
2036
+					if (likely(n > 0)) {
2037
+						n = tcpconn_do_send(fd, c, t_buf, t_len, t_send_flags,
2038
+												&resp, 1);
2039
+						if (likely(response[1] != CONN_QUEUED_WRITE ||
2040
+									resp == CONN_ERROR))
2041
+							/* don't overwrite a previous CONN_QUEUED_WRITE
2042
+							   unless error */
2043
+							response[1] = resp;
2044
+					} else  if (unlikely(n < 0)) {
2045
+						response[1] = CONN_ERROR;
2046
+						break;
2047
+					}
2048
+					/* else do nothing for n (t_len) == 0, keep
2049
+					   the last reponse */
2050
+					t_buf = rest_buf;
2051
+					t_len = rest_len;
2052
+				} while(unlikely(rest_len && n > 0));
1948 2053
 			lock_release(&c->write_lock);
1949 2054
 		} else
1950 2055
 #endif /* USE_TLS */
... ...
@@ -2058,6 +2163,13 @@ static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2058 2163
 	long response[2];
2059 2164
 	int n;
2060 2165
 	int do_close_fd;
2166
+#ifdef USE_TLS
2167
+	const char* rest_buf;
2168
+	const char* t_buf;
2169
+	unsigned rest_len, t_len;
2170
+	long resp;
2171
+	snd_flags_t t_send_flags;
2172
+#endif /* USE_TLS */
2061 2173
 #ifdef TCP_FD_CACHE
2062 2174
 	struct fd_cache_entry* fd_cache_e;
2063 2175
 	int use_fd_cache;
... ...
@@ -2086,24 +2198,35 @@ static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2086 2198
 					do_close_fd=0;
2087 2199
 #ifdef USE_TLS
2088 2200
 					if (unlikely(c->type==PROTO_TLS)) {
2089
-						n = tls_encode(c, &buf, &len);
2090
-						if (unlikely(n < 0)) {
2201
+						t_buf = buf;
2202
+						t_len = len;
2203
+						do {
2204
+							t_send_flags = send_flags;
2205
+							n = tls_encode(c, &t_buf, &t_len,
2206
+											&rest_buf, &rest_len,
2207
+											&t_send_flags);
2208
+							if (unlikely((n < 0) || (t_len &&
2209
+									 (_wbufq_add(c, t_buf, t_len) < 0)))) {
2210
+								lock_release(&c->write_lock);
2211
+								n=-1;
2212
+								response[1] = CONN_ERROR;
2213
+								c->state=S_CONN_BAD;
2214
+								c->timeout=get_ticks_raw(); /* force timeout */
2215
+								goto error;
2216
+							}
2217
+							t_buf = rest_buf;
2218
+							t_len = rest_len;
2219
+						} while(unlikely(rest_len && n > 0));
2220
+					} else
2221
+#endif /* USE_TLS */
2222
+						if (unlikely(len && (_wbufq_add(c, buf, len)<0))){
2091 2223
 							lock_release(&c->write_lock);
2224
+							n=-1;
2092 2225
 							response[1] = CONN_ERROR;
2093 2226
 							c->state=S_CONN_BAD;
2094 2227
 							c->timeout=get_ticks_raw(); /* force timeout */
2095 2228
 							goto error;
2096 2229
 						}
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 2230
 					n=len;
2108 2231
 					lock_release(&c->write_lock);
2109 2232
 					goto release_c;
... ...
@@ -2182,16 +2305,31 @@ static int tcpconn_send_put(struct tcp_connection* c, const char* buf,
2182 2305
 			   (otherwise reordering might occur which would break TLS) =>
2183 2306
 			   lock.
2184 2307
 			*/
2308
+			response[1] = CONN_NOP;
2309
+			t_buf = buf;
2310
+			t_len = len;
2185 2311
 			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;
2312
+				do {
2313
+					t_send_flags = send_flags;
2314
+					n = tls_encode(c, &t_buf, &t_len, &rest_buf, &rest_len,
2315
+									&t_send_flags);
2316
+					if (likely(n > 0)) {
2317
+						n = tcpconn_do_send(fd, c, t_buf, t_len, t_send_flags,
2318
+												&resp, 1);
2319
+						if (likely(response[1] != CONN_QUEUED_WRITE ||
2320
+									resp == CONN_ERROR))
2321
+							/* don't overwrite a previous CONN_QUEUED_WRITE
2322
+							   unless error */
2323
+							response[1] = resp;
2324
+					} else if (unlikely(n < 0)) {
2325
+						response[1] = CONN_ERROR;
2326
+						break;
2327
+					}
2328
+					/* else do nothing for n (t_len) == 0, keep
2329
+					   the last reponse */
2330
+					t_buf = rest_buf;
2331
+					t_len = rest_len;
2332
+				} while(unlikely(rest_len && n > 0));
2195 2333
 			lock_release(&c->write_lock);
2196 2334
 		} else
2197 2335
 #endif
... ...
@@ -2511,7 +2649,8 @@ static int tcpconn_1st_send(int fd, struct tcp_connection* c,
2511 2649
 				" (%d/%d bytes written)\n", c, n, len);
2512 2650
 			if (unlikely(n<0)) n=0;
2513 2651
 			else{
2514
-				TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2652
+				if (likely(c->state == S_CONN_CONNECT))
2653
+					TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2515 2654
 				c->state=S_CONN_OK; /* partial write => connect()
2516 2655
 												ended */
2517 2656
 			}
... ...
@@ -2565,7 +2704,8 @@ static int tcpconn_1st_send(int fd, struct tcp_connection* c,
2565 2704
 		goto error;
2566 2705
 	}
2567 2706
 	LOG(L_INFO, "quick connect for %p\n", c);
2568
-	TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2707
+	if (likely(c->state == S_CONN_CONNECT))
2708
+		TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
2569 2709
 	if (unlikely(send_flags.f & SND_F_CON_CLOSE)){
2570 2710
 		/* close after write =>  EOF => close immediately */
2571 2711
 		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 108
 
100 109
 #define tls_tcpconn_init(c, s)	tls_hook_call(on_tcpconn_init, 0, (c), (s))
101 110
 #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))
111
+#define tls_encode(c, pbuf, plen, rbuf, rlen, sflags) \
112
+	tls_hook_call(encode, -1, (c), (pbuf), (plen), (rbuf), (rlen), (sflags))
104 113
 #define tls_close(conn, fd)		tls_hook_call_v(tcpconn_close, (conn), (fd))
105 114
 #define tls_read(c, flags)				tls_hook_call(read, -1, (c), (flags))
106 115