wmbiff: handle EAGAIN or GNUTLS_E_AGAIN
[dockapps.git] / wmbiff / wmbiff / tlsComm.c
blobf37f3f5efb6048ae59da9e7ff641b7b47a61c02c
1 /* tlsComm.c - primitive routines to aid TLS communication
2 within wmbiff, without rewriting each mailbox access
3 scheme. These functions hide whether the underlying
4 transport is encrypted.
6 Neil Spring (nspring@cs.washington.edu) */
8 /* TODO: handle "* BYE" internally? */
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
13 #include <stdarg.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <assert.h>
22 #ifdef HAVE_GNUTLS_GNUTLS_H
23 #define USE_GNUTLS
24 #include <gnutls/gnutls.h>
25 #include <gnutls/x509.h>
26 #include <sys/stat.h>
27 #endif
28 #ifdef USE_DMALLOC
29 #include <dmalloc.h>
30 #endif
32 #include "tlsComm.h"
34 #include "Client.h" /* debugging messages */
36 /* if non-null, set to a file for certificate verification */
37 extern const char *certificate_filename;
38 /* if set, don't fail when dealing with a bad certificate.
39 (continue to whine, though, as bad certs should be fixed) */
40 extern int SkipCertificateCheck;
41 /* gnutls: specify the priorities to use on the ciphers, key exchange methods,
42 macs and compression methods. */
43 extern const char *tls;
45 /* WARNING: implcitly uses scs to gain access to the mailbox
46 that holds the per-mailbox debug flag. */
47 #define TDM(lvl, args...) DM(scs->pc, lvl, "comm: " args)
49 /* how long to wait for the server to do its thing
50 when we issue it a command (in seconds) */
51 #define EXPECT_TIMEOUT 40
53 /* this is the per-connection state that is maintained for
54 each connection; BIG variables are for ssl (null if not
55 used). */
56 #define BUF_SIZE 1024
57 struct connection_state {
58 int sd;
59 char *name;
60 #ifdef USE_GNUTLS
61 gnutls_session_t tls_state;
62 gnutls_certificate_credentials_t xcred;
63 #else
64 /*@null@ */ void *tls_state;
65 /*@null@ */ void *xcred;
66 #endif
67 char unprocessed[BUF_SIZE];
68 Pop3 pc; /* mailbox handle for debugging messages */
71 /* gotta do our own line buffering, sigh */
72 int getline_from_buffer(char *readbuffer, char *linebuffer,
73 int linebuflen);
74 void handle_gnutls_read_error(int readbytes, struct connection_state *scs);
76 void tlscomm_close(struct connection_state *scs)
78 TDM(DEBUG_INFO, "%s: closing.\n",
79 (scs->name != NULL) ? scs->name : "null");
81 /* not ok to call this more than once */
82 if (scs->tls_state) {
83 #ifdef USE_GNUTLS
84 /* this next line seems capable of hanging... */
85 /* gnutls_bye(scs->tls_state, GNUTLS_SHUT_RDWR); */
86 /* so we'll try just _bye'ing the WR direction, which
87 should send the alert but not wait for a response. */
88 gnutls_bye(scs->tls_state, GNUTLS_SHUT_WR);
89 gnutls_certificate_free_credentials(scs->xcred);
90 gnutls_deinit(scs->tls_state);
91 scs->xcred = NULL;
92 #endif
93 } else {
94 (void) close(scs->sd);
96 scs->sd = -1;
97 scs->tls_state = NULL;
98 scs->xcred = NULL;
99 free(scs->name);
100 scs->name = NULL;
101 free(scs);
104 extern int x_socket(void);
105 extern void ProcessPendingEvents(void);
107 /* this avoids blocking without using non-blocking i/o */
108 static int wait_for_it(int sd, int timeoutseconds)
110 fd_set readfds;
111 struct timeval tv;
112 int ready_descriptors;
113 int maxfd;
114 int xfd;
115 struct timeval time_now;
116 struct timeval time_out;
118 gettimeofday(&time_now, NULL);
119 memcpy(&time_out, &time_now, sizeof(struct timeval));
120 time_out.tv_sec += timeoutseconds;
122 xfd = x_socket();
123 maxfd = max(sd, xfd);
125 do {
126 do {
127 ProcessPendingEvents();
129 gettimeofday(&time_now, NULL);
130 tv.tv_sec = max(time_out.tv_sec - time_now.tv_sec + 1, (time_t) 0); /* sloppy, but bfd */
131 tv.tv_usec = 0;
132 /* select will return if we have X stuff or we have comm stuff on sd */
133 FD_ZERO(&readfds);
134 FD_SET(sd, &readfds);
135 // FD_SET(xfd, &readfds);
136 ready_descriptors = select(maxfd + 1, &readfds, NULL, NULL, &tv);
137 // DMA(DEBUG_INFO,
138 // "select %d/%d returned %d descriptor, %d\n",
139 // sd, timeoutseconds, ready_descriptors, FD_ISSET(sd, &readfds));
141 } while(tv.tv_sec > 0 && (!FD_ISSET(sd, &readfds) || (errno == EINTR && ready_descriptors == -1)));
143 FD_ZERO(&readfds);
144 FD_SET(sd, &readfds);
145 tv.tv_sec = 0; tv.tv_usec = 0;
146 ready_descriptors = select(sd + 1, &readfds, NULL, NULL, &tv);
148 while (ready_descriptors == -1 && errno == EINTR);
149 if (ready_descriptors == 0) {
150 DMA(DEBUG_INFO,
151 "select timed out after %d seconds on socket: %d\n",
152 timeoutseconds, sd);
153 return (0);
154 } else if (ready_descriptors == -1) {
155 DMA(DEBUG_ERROR,
156 "select failed on socket %d: %s\n", sd, strerror(errno));
157 return (0);
159 return (FD_ISSET(sd, &readfds));
162 /* exported for testing */
163 extern int
164 getline_from_buffer(char *readbuffer, char *linebuffer, int linebuflen)
166 char *p, *q;
167 int i;
168 /* find end of line (stopping if linebuflen is too small. */
169 for (p = readbuffer, i = 0;
170 *p != '\n' && *p != '\0' && i < linebuflen - 1; p++, i++);
172 /* gobble \n if it starts the line. */
173 if (*p == '\n') {
174 /* grab the end of line too! and then advance past
175 the newline */
176 i++;
177 p++;
178 } else {
179 /* TODO -- perhaps we should return no line at all
180 here, as it might be incomplete. don't want to
181 break anything though. */
182 DMA(DEBUG_INFO, "expected line doesn't end on its own.\n");
185 if (i != 0) {
186 /* copy a line into the linebuffer */
187 strncpy(linebuffer, readbuffer, (size_t) i);
188 /* sigh, null terminate */
189 linebuffer[i] = '\0';
190 /* shift the rest over; this could be done
191 instead with strcpy... I think. */
192 q = readbuffer;
193 if (*p != '\0') {
194 while (*p != '\0') {
195 *(q++) = *(p++);
198 /* null terminate */
199 *(q++) = *(p++);
200 /* return the length of the line */
202 if (i < 0 || i > linebuflen) {
203 DM((Pop3) NULL, DEBUG_ERROR, "bork bork bork!: %d %d\n", i,
204 linebuflen);
206 return i;
209 /* eat lines, until one starting with prefix is found;
210 this skips 'informational' IMAP responses */
211 /* the correct response to a return value of 0 is almost
212 certainly tlscomm_close(scs): don't _expect() anything
213 unless anything else would represent failure */
215 tlscomm_expect(struct connection_state *scs,
216 const char *prefix, char *linebuf, int buflen)
218 int prefixlen = (int) strlen(prefix);
219 int buffered_bytes = 0;
220 memset(linebuf, 0, buflen);
221 TDM(DEBUG_INFO, "%s: expecting: %s\n", scs->name, prefix);
222 /* if(scs->unprocessed[0]) {
223 TDM(DEBUG_INFO, "%s: buffered: %s\n", scs->name, scs->unprocessed);
224 } */
225 while (scs->unprocessed[0] != '\0'
226 || wait_for_it(scs->sd, EXPECT_TIMEOUT)) {
227 if (scs->unprocessed[buffered_bytes] == '\0') {
228 int thisreadbytes;
229 #ifdef USE_GNUTLS
230 if (scs->tls_state) {
231 /* BUF_SIZE - 1 leaves room for trailing \0 */
232 do {
233 thisreadbytes =
234 gnutls_read(scs->tls_state,
235 &scs->unprocessed[buffered_bytes],
236 BUF_SIZE - 1 - buffered_bytes);
237 } while (thisreadbytes == GNUTLS_E_AGAIN);
238 if (thisreadbytes < 0) {
239 handle_gnutls_read_error(thisreadbytes, scs);
240 return 0;
242 } else
243 #endif
245 do {
246 thisreadbytes =
247 read(scs->sd, &scs->unprocessed[buffered_bytes],
248 BUF_SIZE - 1 - buffered_bytes);
249 } while (thisreadbytes == EAGAIN);
250 if (thisreadbytes < 0) {
251 TDM(DEBUG_ERROR, "%s: error reading: %s\n",
252 scs->name, strerror(errno));
253 return 0;
256 buffered_bytes += thisreadbytes;
257 /* force null termination */
258 scs->unprocessed[buffered_bytes] = '\0';
259 if (buffered_bytes == 0) {
260 return 0; /* bummer */
262 } else {
263 buffered_bytes = strlen(scs->unprocessed);
265 while (buffered_bytes >= prefixlen) {
266 int linebytes;
267 linebytes =
268 getline_from_buffer(scs->unprocessed, linebuf, buflen);
269 if (linebytes == 0) {
270 buffered_bytes = 0;
271 } else {
272 buffered_bytes -= linebytes;
273 if (strncmp(linebuf, prefix, prefixlen) == 0) {
274 TDM(DEBUG_INFO, "%s: got: %*s", scs->name,
275 linebytes, linebuf);
276 return 1; /* got it! */
278 TDM(DEBUG_INFO, "%s: dumped(%d/%d): %.*s", scs->name,
279 linebytes, buffered_bytes, linebytes, linebuf);
283 if (buffered_bytes == -1) {
284 TDM(DEBUG_INFO, "%s: timed out while expecting '%s'\n",
285 scs->name, prefix);
286 } else {
287 TDM(DEBUG_ERROR, "%s: expecting: '%s', saw (%d): %s%s",
288 scs->name, prefix, buffered_bytes, linebuf,
289 /* only print the newline if the linebuf lacks it */
290 (linebuf[strlen(linebuf) - 1] == '\n') ? "\n" : "");
292 return 0; /* wait_for_it failed */
295 int tlscomm_gets(char *buf, int buflen, struct connection_state *scs)
297 return (tlscomm_expect(scs, "", buf, buflen));
300 void tlscomm_printf(struct connection_state *scs, const char *format, ...)
302 va_list args;
303 char buf[1024];
304 int bytes;
305 ssize_t unused __attribute__((unused));
307 if (scs == NULL) {
308 DMA(DEBUG_ERROR, "null connection to tlscomm_printf\n");
309 abort();
311 va_start(args, format);
312 bytes = vsnprintf(buf, 1024, format, args);
313 va_end(args);
315 if (scs->sd != -1) {
316 #ifdef USE_GNUTLS
317 if (scs->tls_state) {
318 int written = gnutls_write(scs->tls_state, buf, bytes);
319 if (written < bytes) {
320 TDM(DEBUG_ERROR,
321 "Error %s prevented writing: %*s\n",
322 gnutls_strerror(written), bytes, buf);
323 return;
325 } else
326 #endif
327 /* Why???? */
328 unused = write(scs->sd, buf, bytes);
329 } else {
330 printf
331 ("warning: tlscomm_printf called with an invalid socket descriptor\n");
332 return;
334 TDM(DEBUG_INFO, "wrote %*s", bytes, buf);
337 /* most of this file only makes sense if using TLS. */
338 #ifdef USE_GNUTLS
339 #include "gnutls-common.h"
341 static void
342 bad_certificate(const struct connection_state *scs, const char *msg)
344 TDM(DEBUG_ERROR, "%s", msg);
345 if (!SkipCertificateCheck) {
346 TDM(DEBUG_ERROR, "to ignore this error, run wmbiff "
347 "with the -skip-certificate-check option\n");
348 exit(1);
352 static void
353 warn_certificate(const struct connection_state *scs, const char *msg)
355 if (!SkipCertificateCheck) {
356 TDM(DEBUG_ERROR, "%s", msg);
357 TDM(DEBUG_ERROR, "to ignore this warning, run wmbiff "
358 "with the -skip-certificate-check option\n");
362 /* a start of a hack at verifying certificates. does not
363 provide any security at all. I'm waiting for either
364 gnutls to make this as easy as it should be, or someone
365 to port Andrew McDonald's gnutls-for-mutt patch.
368 #define CERT_SEP "-----BEGIN"
370 /* this bit is based on read_ca_file() in gnutls */
371 static int tls_compare_certificates(const gnutls_datum_t * peercert)
373 gnutls_datum_t cert;
374 unsigned char *ptr;
375 FILE *fd1;
376 int ret;
377 gnutls_datum_t b64_data;
378 unsigned char *b64_data_data;
379 struct stat filestat;
381 if (stat(certificate_filename, &filestat) == -1)
382 return 0;
384 b64_data.size = filestat.st_size + 1;
385 b64_data_data = (unsigned char *) malloc(b64_data.size);
386 b64_data_data[b64_data.size - 1] = '\0';
387 b64_data.data = b64_data_data;
389 fd1 = fopen(certificate_filename, "r");
390 if (fd1 == NULL) {
391 return 0;
394 b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1);
395 fclose(fd1);
397 do {
398 ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
399 if (ret != 0) {
400 free(b64_data_data);
401 return 0;
404 ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP) + 1;
405 ptr = (unsigned char *) strstr((char *) ptr, CERT_SEP);
407 b64_data.size = b64_data.size - (ptr - b64_data.data);
408 b64_data.data = ptr;
410 if (cert.size == peercert->size) {
411 if (memcmp(cert.data, peercert->data, cert.size) == 0) {
412 /* match found */
413 gnutls_free(cert.data);
414 free(b64_data_data);
415 return 1;
419 gnutls_free(cert.data);
420 } while (ptr != NULL);
422 /* no match found */
423 free(b64_data_data);
424 return 0;
428 static void
429 tls_check_certificate(struct connection_state *scs,
430 const char *remote_hostname)
432 int ret;
433 unsigned int certstat;
434 const gnutls_datum_t *cert_list;
435 unsigned int cert_list_size = 0;
436 gnutls_x509_crt_t cert;
438 if (gnutls_auth_get_type(scs->tls_state) != GNUTLS_CRD_CERTIFICATE) {
439 bad_certificate(scs, "Unable to get certificate from peer.\n");
440 return; /* bad_cert will exit if -skip-certificate-check was not given */
442 ret = gnutls_certificate_verify_peers2(scs->tls_state, &certstat);
444 if (ret < 0) {
445 char errbuf[1024];
447 snprintf(errbuf, 1024, "could not verify certificate: %s (%d).\n",
448 gnutls_strerror(ret), ret);
449 bad_certificate(scs, (ret == GNUTLS_E_NO_CERTIFICATE_FOUND ?
450 "server presented no certificate.\n" :
451 errbuf));
452 return;
453 #ifdef GNUTLS_CERT_CORRUPTED
454 } else if (certstat & GNUTLS_CERT_CORRUPTED) {
455 bad_certificate(scs, "server's certificate is corrupt.\n");
456 #endif
457 } else if (certstat & GNUTLS_CERT_REVOKED) {
458 bad_certificate(scs, "server's certificate has been revoked.\n");
459 } else if (certstat & GNUTLS_CERT_EXPIRED) {
460 bad_certificate(scs, "server's certificate is expired.\n");
461 } else if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM) {
462 warn_certificate(scs, "server's certificate use an insecure algorithm.\n");
463 } else if (certstat & GNUTLS_CERT_INVALID) {
464 if (gnutls_certificate_type_get(scs->tls_state) == GNUTLS_CRT_X509) {
465 /* bad_certificate(scs, "server's certificate is not trusted.\n"
466 "there may be a problem with the certificate stored in your certfile\n"); */
467 } else {
468 bad_certificate(scs,
469 "server's certificate is invalid or not X.509.\n"
470 "there may be a problem with the certificate stored in your certfile\n");
472 #if defined(GNUTLS_CERT_SIGNER_NOT_FOUND)
473 } else if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND) {
474 TDM(DEBUG_INFO, "server's certificate is not signed.\n");
475 TDM(DEBUG_INFO,
476 "to verify that a certificate is trusted, use the certfile option.\n");
477 #endif
479 #if defined(GNUTLS_CERT_NOT_TRUSTED)
480 } else if (certstat & GNUTLS_CERT_NOT_TRUSTED) {
481 TDM(DEBUG_INFO, "server's certificate is not trusted.\n");
482 TDM(DEBUG_INFO,
483 "to verify that a certificate is trusted, use the certfile option.\n");
484 #endif
487 if (gnutls_x509_crt_init(&cert) < 0) {
488 bad_certificate(scs,
489 "Unable to initialize certificate data structure");
493 /* not checking for not-yet-valid certs... this would make sense
494 if we weren't just comparing to stored ones */
495 cert_list =
496 gnutls_certificate_get_peers(scs->tls_state, &cert_list_size);
498 if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) <
499 0) {
500 bad_certificate(scs, "Error processing certificate data");
503 if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) {
504 bad_certificate(scs, "server's certificate has expired.\n");
505 } else if (gnutls_x509_crt_get_activation_time(cert)
506 > time(NULL)) {
507 bad_certificate(scs, "server's certificate is not yet valid.\n");
508 } else {
509 TDM(DEBUG_INFO, "certificate passed time check.\n");
512 if (gnutls_x509_crt_check_hostname(cert, remote_hostname) == 0) {
513 char certificate_hostname[256];
514 size_t buflen = 255;
515 gnutls_x509_crt_get_dn(cert, certificate_hostname, &buflen);
516 /* gnutls_x509_extract_certificate_dn(&cert_list[0], &dn); */
517 TDM(DEBUG_INFO,
518 "server's certificate (%s) does not match its hostname (%s).\n",
519 certificate_hostname, remote_hostname);
520 bad_certificate(scs,
521 "server's certificate does not match its hostname.\n");
522 } else {
523 if ((scs->pc)->debug >= DEBUG_INFO) {
524 char certificate_hostname[256];
525 size_t buflen = 255;
526 gnutls_x509_crt_get_dn(cert, certificate_hostname, &buflen);
527 /* gnutls_x509_extract_certificate_dn(&cert_list[0], &dn); */
528 TDM(DEBUG_INFO,
529 "server's certificate (%s) matched its hostname (%s).\n",
530 certificate_hostname, remote_hostname);
534 if (certificate_filename != NULL &&
535 tls_compare_certificates(&cert_list[0]) == 0) {
536 bad_certificate(scs,
537 "server's certificate was not found in the certificate file.\n");
540 gnutls_x509_crt_deinit(cert);
542 TDM(DEBUG_INFO, "certificate check ok.\n");
543 return;
546 struct connection_state *initialize_gnutls(intptr_t sd, char *name, Pop3 pc,
547 const char *remote_hostname)
549 static int gnutls_initialized;
550 int zok;
551 struct connection_state *scs = malloc(sizeof(struct connection_state));
552 memset(scs, 0, sizeof(struct connection_state)); /* clears the unprocessed buffer */
554 scs->pc = pc;
556 assert(sd >= 0);
558 if (gnutls_initialized == 0) {
559 assert(gnutls_global_init() == 0);
560 gnutls_initialized = 1;
563 assert(gnutls_init(&scs->tls_state, GNUTLS_CLIENT) == 0);
565 const char *err_pos;
566 if (GNUTLS_E_SUCCESS != gnutls_priority_set_direct(scs->tls_state, tls, &err_pos)) {
567 DMA(DEBUG_ERROR,
568 "Unable to set the priorities to use on the ciphers, "
569 "key exchange methods, macs and/or compression methods.\n"
570 "See 'tls' parameter in config file: '%s'.\n",
571 err_pos);
572 exit(1);
575 /* no client private key */
576 if (gnutls_certificate_allocate_credentials(&scs->xcred) < 0) {
577 DMA(DEBUG_ERROR, "gnutls memory error\n");
578 exit(1);
581 /* certfile seems to work. */
582 if (certificate_filename != NULL) {
583 if (!exists(certificate_filename)) {
584 DMA(DEBUG_ERROR,
585 "Certificate file (certfile=) %s not found.\n",
586 certificate_filename);
587 exit(1);
589 zok = gnutls_certificate_set_x509_trust_file(scs->xcred,
590 (char *)
591 certificate_filename,
592 GNUTLS_X509_FMT_PEM);
593 if (zok < 0) {
594 DMA(DEBUG_ERROR,
595 "GNUTLS did not like your certificate file %s (%d).\n",
596 certificate_filename, zok);
597 gnutls_perror(zok);
598 exit(1);
602 gnutls_cred_set(scs->tls_state, GNUTLS_CRD_CERTIFICATE,
603 scs->xcred);
604 gnutls_transport_set_ptr(scs->tls_state,
605 (gnutls_transport_ptr_t) sd);
606 do {
607 zok = gnutls_handshake(scs->tls_state);
609 while (zok == GNUTLS_E_INTERRUPTED || zok == GNUTLS_E_AGAIN);
611 tls_check_certificate(scs, remote_hostname);
614 if (zok < 0) {
615 TDM(DEBUG_ERROR, "%s: Handshake failed\n", name);
616 TDM(DEBUG_ERROR, "%s: This may be a problem in gnutls, "
617 "which is under development\n", name);
618 TDM(DEBUG_ERROR,
619 "%s: This copy of wmbiff was compiled with \n"
620 " gnutls version %s.\n", name, LIBGNUTLS_VERSION);
621 gnutls_perror(zok);
622 if (scs->pc->u.pop_imap.serverPort != 143 /* starttls */ ) {
623 TDM(DEBUG_ERROR,
624 "%s: Please run 'gnutls-cli-debug -p %d %s' to test ssl directly.\n"
625 " That tool provides a lower-level test of gnutls with your server.\n",
626 name, scs->pc->u.pop_imap.serverPort, remote_hostname);
628 gnutls_deinit(scs->tls_state);
629 free(scs);
630 return (NULL);
631 } else {
632 TDM(DEBUG_INFO, "%s: Handshake was completed\n", name);
633 if (scs->pc->debug >= DEBUG_INFO)
634 print_info(scs->tls_state, remote_hostname);
635 scs->sd = sd;
636 scs->name = name;
638 return (scs);
641 /* moved down here, to keep from interrupting the flow with
642 verbose error crap */
643 void handle_gnutls_read_error(int readbytes, struct connection_state *scs)
645 if (gnutls_error_is_fatal(readbytes) == 1) {
646 TDM(DEBUG_ERROR,
647 "%s: Received corrupted data(%d) - server has terminated the connection abnormally\n",
648 scs->name, readbytes);
649 } else {
650 if (readbytes == GNUTLS_E_WARNING_ALERT_RECEIVED
651 || readbytes == GNUTLS_E_FATAL_ALERT_RECEIVED)
652 TDM(DEBUG_ERROR, "* Received alert [%d]\n",
653 gnutls_alert_get(scs->tls_state));
654 if (readbytes == GNUTLS_E_REHANDSHAKE)
655 TDM(DEBUG_ERROR, "* Received HelloRequest message\n");
657 TDM(DEBUG_ERROR,
658 "%s: gnutls error reading: %s\n",
659 scs->name, gnutls_strerror(readbytes));
662 #else
663 /* declare stubs when tls isn't compiled in */
664 struct connection_state *initialize_gnutls(UNUSED(intptr_t sd),
665 UNUSED(char *name),
666 UNUSED(Pop3 pc),
667 UNUSED(const char
668 *remote_hostname))
670 DM(pc, DEBUG_ERROR,
671 "FATAL: tried to initialize ssl when ssl wasn't compiled in.\n");
672 exit(EXIT_FAILURE);
674 #endif
676 /* either way: */
677 struct connection_state *initialize_unencrypted(int sd,
678 /*@only@ */ char *name,
679 Pop3 pc)
681 struct connection_state *ret = malloc(sizeof(struct connection_state));
682 assert(sd >= 0);
683 assert(ret != NULL);
684 memset(ret, 0, sizeof(struct connection_state)); /* clears the unprocessed buffer */
685 ret->sd = sd;
686 ret->name = name;
687 ret->tls_state = NULL;
688 ret->xcred = NULL;
689 ret->pc = pc;
690 return (ret);
693 /* bad seed connections that can't be setup */
694 /*@only@*/
695 struct connection_state *initialize_blacklist( /*@only@ */ char *name)
697 struct connection_state *ret = malloc(sizeof(struct connection_state));
698 assert(ret != NULL);
699 ret->sd = -1;
700 ret->name = name;
701 ret->tls_state = NULL;
702 ret->xcred = NULL;
703 ret->pc = NULL;
704 return (ret);
708 int tlscomm_is_blacklisted(const struct connection_state *scs)
710 return (scs != NULL && scs->sd == -1);
713 /* vim:set ts=4: */
715 * Local Variables:
716 * tab-width: 4
717 * c-indent-level: 4
718 * c-basic-offset: 4
719 * End: