1 #define TCP_BODY
2
3 #include "basictcp.h"
4
5 #include <sys/socket.h>
6 #include <unistd.h>
7 #include <arpa/inet.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <stdarg.h>
11 #include "postal.h"
12 #include "userlist.h"
13 #include "address.h"
14 #include "logit.h"
15 #include "results.h"
16
17 #ifdef USE_GNUTLS
18 int base_tcp::m_init_dh_params = 0;
19 gnutls_dh_params_t base_tcp::m_dh_params;
20 #endif
21
base_tcp(int fd,Logit * log,Logit * debug,results * res,int ssl)22 base_tcp::base_tcp(int fd, Logit *log, Logit *debug, results *res
23 #ifdef USE_SSL
24 , int ssl
25 #endif
26 ) :
27 #ifdef USE_SSL
28 m_canTLS(false),
29 m_useTLS(ssl),
30 #endif
31 m_sock(fd)
32 , m_start(0)
33 , m_end(0)
34 , m_open(true)
35 , m_log(log)
36 , m_debug(debug)
37 , m_res(res)
38 #ifdef USE_SSL
39 #ifdef USE_OPENSSL
40 , m_sslMeth(NULL)
41 , m_sslCtx(NULL)
42 , m_ssl(NULL)
43 #else
44 , m_gnutls_session(NULL)
45 #endif
46 , m_isTLS(false)
47 #endif
48 {
49 m_poll.fd = m_sock;
50 #ifdef USE_SSL
51 if(m_useTLS)
52 {
53 #ifdef USE_OPENSSL
54 //don't seem to need this SSL_library_init();
55 SSLeay_add_ssl_algorithms();
56 SSL_load_error_strings();
57 #endif
58 }
59 #endif
60 }
61
~base_tcp()62 base_tcp::~base_tcp()
63 {
64 }
65
66 #ifdef USE_SSL
67
68 #ifdef USE_GNUTLS
69 #define DH_BITS 1024
70
m_initialize_tls_session()71 void base_tcp::m_initialize_tls_session()
72 {
73 gnutls_init(&m_gnutls_session, GNUTLS_SERVER);
74
75 /* avoid calling all the priority functions, since the defaults
76 * are adequate.
77 */
78 gnutls_set_default_priority(m_gnutls_session);
79 // Need to enable anonymous specifically
80 gnutls_priority_set_direct(m_gnutls_session, "NORMAL:+ANON-DH", NULL);
81
82 gnutls_credentials_set(m_gnutls_session, GNUTLS_CRD_ANON, m_anoncred);
83
84 gnutls_dh_set_prime_bits(m_gnutls_session, DH_BITS);
85 }
86
m_generate_dh_params()87 void base_tcp::m_generate_dh_params()
88 {
89 /* Generate Diffie Hellman parameters - for use with DHE
90 * kx algorithms. These should be discarded and regenerated
91 * once a day, once a week or once a month. Depending on the
92 * security requirements.
93 */
94 gnutls_dh_params_init(&m_dh_params);
95 gnutls_dh_params_generate2(m_dh_params, DH_BITS);
96 }
97 #endif // USE_GNUTLS
98
ConnectTLS()99 int base_tcp::ConnectTLS()
100 {
101 #ifdef USE_OPENSSL
102 m_sslMeth = NULL;
103 m_sslCtx = NULL;
104 m_ssl = NULL;
105 #ifndef OPENSSL_NO_SSL3
106 m_sslMeth = SSLv3_client_method();
107 #else
108 m_sslMeth = SSLv23_client_method();
109 #endif
110 if(m_sslMeth == NULL)
111 {
112 #ifndef OPENSSL_NO_SSL3
113 fprintf(stderr, "Can't get SSLv3_client_method.\n");
114 #else
115 fprintf(stderr, "Can't get SSLv23_client_method.\n");
116 #endif
117 return 2;
118 }
119 m_sslCtx = SSL_CTX_new(m_sslMeth);
120 if(m_sslCtx == NULL)
121 {
122 fprintf(stderr, "Can't SSL_CTX_new\n");
123 return 2;
124 }
125 if((m_ssl = SSL_new(m_sslCtx)) == NULL)
126 {
127 fprintf(stderr, "Can't SSL_new\n");
128 SSL_CTX_free(m_sslCtx);
129 return 2;
130 }
131 SSL_set_fd(m_ssl, m_sock);
132 if(-1 == SSL_connect(m_ssl))
133 {
134 fprintf(stderr, "Can't SSL_CONNECT\n");
135 SSL_free(m_ssl);
136 SSL_CTX_free(m_sslCtx);
137 return 1;
138 }
139 m_isTLS = true;
140
141 // debugging code that may be useful to have around in a commented-out state.
142 #if 0
143 /* Following two steps are optional and not required for
144 data exchange to be successful. */
145
146 /* Get the cipher - opt */
147
148 printf ("SSL connection using %s\n", SSL_get_cipher(m_ssl));
149
150 /* Get server's certificate (note: beware of dynamic allocation) - opt */
151
152 X509 *server_cert;
153 server_cert = SSL_get_peer_certificate(m_ssl);
154 if(!server_cert)
155 {
156 fprintf(stderr, "Can't SSL_get_peer_certificate\n");
157 return 2;
158 }
159 printf ("Server certificate:\n");
160
161 char *str = X509_NAME_oneline(X509_get_subject_name(server_cert),0,0);
162 if(!str)
163 {
164 fprintf(stderr, "Can't X509_NAME_oneline\n");
165 return 2;
166 }
167 printf ("\t subject: %s\n", str);
168 Free (str);
169 str = X509_NAME_oneline (X509_get_issuer_name(server_cert),0,0);
170 if(!str)
171 {
172 fprintf(stderr, "Can't X509_get_issuer_name\n");
173 return 2;
174 }
175 printf ("\t issuer: %s\n", str);
176 Free (str);
177
178 /* We could do all sorts of certificate verification stuff here before
179 deallocating the certificate. */
180
181 X509_free(server_cert);
182 #endif // 0
183 #else
184
185 gnutls_anon_allocate_server_credentials(&m_anoncred);
186 m_initialize_tls_session();
187
188 if(!m_init_dh_params)
189 {
190 m_init_dh_params = 1;
191 m_generate_dh_params();
192 }
193
194 gnutls_anon_set_server_dh_params(m_anoncred, m_dh_params);
195
196 gnutls_transport_set_ptr(m_gnutls_session, (gnutls_transport_ptr_t)m_sock);
197 int rc = gnutls_handshake(m_gnutls_session);
198 if(rc < 0)
199 {
200 gnutls_deinit(m_gnutls_session);
201 return 2;
202 }
203 m_isTLS = 1;
204
205 /* request client certificate if any.
206 */
207 gnutls_certificate_server_set_request(m_gnutls_session, GNUTLS_CERT_REQUEST);
208
209 #endif // USE_OPENSSL
210 return 0;
211 }
212 #endif // USE_SSL
213
disconnect()214 int base_tcp::disconnect()
215 {
216 if(m_open)
217 {
218 #ifdef USE_SSL
219 if(m_isTLS)
220 {
221 #ifdef USE_OPENSSL
222 SSL_shutdown(m_ssl);
223 close(m_sock);
224 SSL_free(m_ssl);
225 SSL_CTX_free(m_sslCtx);
226 m_isTLS = false;
227 #else
228 #endif
229 }
230 else
231 #endif
232 {
233 close(m_sock);
234 }
235 }
236 m_open = false;
237 return 0;
238 }
239
printf(CPCCHAR fmt,...)240 ERROR_TYPE base_tcp::printf(CPCCHAR fmt, ...)
241 {
242 va_list argp;
243 va_start(argp, fmt);
244 char buf[1024];
245 int len = vsnprintf(buf, sizeof(buf), fmt, argp);
246 if(len > (int)sizeof(buf))
247 len = sizeof(buf);
248 return sendData(buf, len);
249 }
250
sendData(CPCCHAR buf,int size)251 ERROR_TYPE base_tcp::sendData(CPCCHAR buf, int size)
252 {
253 if(!m_open)
254 return eCorrupt;
255 int sent = 0;
256 m_poll.events = POLLOUT | POLLERR | POLLHUP;
257 int rc;
258 while(sent != size)
259 {
260 rc = poll(&m_poll, 1, 60000);
261 if(rc == 0)
262 {
263 fprintf(stderr, "Server timed out on write.\n");
264 return eTimeout;
265 }
266 if(rc < 0)
267 {
268 fprintf(stderr, "Poll error.\n");
269 return eSocket;
270 }
271 #ifdef USE_SSL
272 if(m_isTLS)
273 {
274 #ifdef USE_OPENSSL
275 rc = SSL_write(m_ssl, &buf[sent], size - sent);
276 #else
277 rc = gnutls_record_send(m_gnutls_session, &buf[sent], size - sent);
278 #endif
279 }
280 else
281 #endif
282 {
283 rc = write(m_sock, &buf[sent], size - sent);
284 }
285 if(rc < 1)
286 {
287 // fprintf(stderr, "Can't write to socket.\n");
288 return eSocket;
289 }
290 if(m_debug)
291 m_debug->Write(buf, rc);
292 sent += rc;
293 }
294 sentData(size);
295 return eNoError;
296 }
297
readLine(char * buf,int bufSize,bool stripCR,int timeout)298 int base_tcp::readLine(char *buf, int bufSize, bool stripCR, int timeout)
299 {
300 if(!m_open)
301 return eCorrupt;
302 int ind = 0;
303 if(m_start < m_end)
304 {
305 do
306 {
307 buf[ind] = m_buf[m_start];
308 ind++;
309 m_start++;
310 }
311 while(m_start < m_end && m_buf[m_start - 1] != '\n' && ind < bufSize);
312 }
313 if(ind == bufSize || (ind > 0 && buf[ind - 1] == '\n') )
314 {
315 receivedData(ind);
316 if(m_debug)
317 m_debug->Write(buf, ind);
318 if(ind < bufSize)
319 {
320 ind--;
321 buf[ind] = '\0';
322 if(stripCR && buf[ind - 1] == '\r')
323 {
324 ind--;
325 buf[ind] = '\0';
326 }
327 }
328 return ind;
329 }
330 // buffer is empty
331 m_start = 0;
332 m_end = 0;
333
334 time_t now = time(NULL);
335 m_poll.events = POLLIN | POLLERR | POLLHUP;
336 while(1)
337 {
338 int tmo = timeout - (time(NULL) - now);
339 int rc;
340 if(tmo < 0 || (rc = poll(&m_poll, 1, tmo * 1000)) == 0)
341 {
342 return eTimeout;
343 }
344 if(rc < 0)
345 {
346 fprintf(stderr, "Poll error.\n");
347 return eCorrupt;
348 }
349 #ifdef USE_SSL
350 if(m_isTLS)
351 {
352 #ifdef USE_OPENSSL
353 rc = SSL_read(m_ssl, m_buf, sizeof(m_buf));
354 #else
355 rc = gnutls_record_recv(m_gnutls_session, m_buf, sizeof(m_buf));
356 #endif
357 }
358 else
359 #endif
360 {
361 rc = read(m_sock, m_buf, sizeof(m_buf));
362 }
363 if(rc < 0)
364 return eSocket;
365 m_end = rc;
366 do
367 {
368 buf[ind] = m_buf[m_start];
369 ind++;
370 m_start++;
371 } while(m_start < m_end && m_buf[m_start - 1] != '\n' && ind < bufSize);
372
373 if(ind == bufSize || (ind > 0 && buf[ind - 1] == '\n') )
374 {
375 receivedData(ind);
376 if(m_debug)
377 m_debug->Write(buf, ind);
378 if(ind < bufSize)
379 {
380 ind--;
381 buf[ind] = '\0';
382 if(stripCR && buf[ind - 1] == '\r')
383 {
384 ind--;
385 buf[ind] = '\0';
386 }
387 }
388 return ind;
389 }
390 if(m_start == m_end)
391 {
392 m_start = 0;
393 m_end = 0;
394 }
395 }
396 return 0; // never reached
397 }
398
sentData(int)399 void base_tcp::sentData(int)
400 {
401 }
402
receivedData(int bytes)403 void base_tcp::receivedData(int bytes)
404 {
405 m_res->dataBytes(bytes);
406 }
407
408