ucspi-ssl  0.12.7
ucspi-ssl
ssl_io.c
Go to the documentation of this file.
1 #include <unistd.h>
2 #include <sys/types.h>
3 #include <sys/socket.h>
4 #include "iopause.h"
5 #include "buffer.h"
6 #include "taia.h"
7 #include "ucspissl.h"
8 #include "error.h"
9 
10 static int leftstatus = 0;
11 static char leftbuf[16 * 1024];
12 static int leftlen;
13 static int leftpos;
14 
15 static int rightstatus = 0;
16 static char rightbuf[16 * 1024];
17 static int rightlen;
18 static int rightpos;
19 
20 int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) {
21  struct taia now;
22  struct taia deadline;
23  iopause_fd x[4];
24  int xlen;
25  iopause_fd *io0;
26  iopause_fd *ioleft;
27  iopause_fd *io1;
28  iopause_fd *ioright;
29  int r;
30  int rc = 0;
31  int rfd;
32  int wfd;
33 
34  rfd = SSL_get_fd(ssl); /* XXX */
35  if (rfd == -1) {
36  close(fdleft); close(fdright);
37  return -1;
38  }
39  wfd = SSL_get_fd(ssl); /* XXX */
40  if (wfd == -1) {
41  close(fdleft); close(fdright);
42  return -1;
43  }
44 
45  for (;;) {
46  xlen = 0;
47 
48  if (leftstatus == -1 && rightstatus == -1)
49  goto DONE;
50 
51  io0 = 0;
52  if (leftstatus == 0 && rightstatus != 1) {
53  io0 = &x[xlen++];
54  io0->fd = rfd;
55  io0->events = IOPAUSE_READ;
56  }
57 
58  ioleft = 0;
59  if (leftstatus == 1) {
60  ioleft = &x[xlen++];
61  ioleft->fd = fdleft;
62  ioleft->events = IOPAUSE_WRITE;
63  }
64 
65  ioright = 0;
66  if (rightstatus == 0) {
67  ioright = &x[xlen++];
68  ioright->fd = fdright;
69  ioright->events = IOPAUSE_READ;
70  }
71 
72  io1 = 0;
73  if (rightstatus == 1) {
74  io1 = &x[xlen++];
75  io1->fd = wfd;
76  io1->events = IOPAUSE_WRITE;
77  }
78 
79  if (taia_now(&now) == -1) {
80  errno = ETIMEDOUT;
81  rc = -1;
82  goto BOMB;
83  }
84  taia_uint(&deadline,timeout);
85  taia_add(&deadline,&now,&deadline);
86  iopause(x,xlen,&deadline,&now);
87 
88  for (r = 0; r < xlen; ++r)
89  if (x[r].revents) goto EVENTS;
90 
91  if (io0 && !ssl_pending(ssl)) {
92  close(fdleft);
93  leftstatus = -1;
94  continue;
95  }
96  errno = ETIMEDOUT;
97  rc = -1;
98  goto BOMB;
99 
100 
101 EVENTS:
102  if (io0 && io0->revents) {
103  r = SSL_read(ssl,leftbuf,sizeof(leftbuf));
104  ssl_errno = SSL_get_error(ssl,r);
105  switch (ssl_errno) {
106  case SSL_ERROR_NONE:
107  leftstatus = 1;
108  leftpos = 0;
109  leftlen = r;
110  break;
111  case SSL_ERROR_WANT_READ:
112  case SSL_ERROR_WANT_WRITE:
113  case SSL_ERROR_WANT_X509_LOOKUP:
114  break;
115  case SSL_ERROR_ZERO_RETURN:
116  if (rightstatus == -1) goto DONE;
117  close(fdleft);
118  leftstatus = -1;
119  break;
120  case SSL_ERROR_SYSCALL:
121  if (errno == EAGAIN || errno == EINTR) break;
122  close(fdleft);
123  leftstatus = -1;
124  if (!errno) break;
125  /* premature close */
126  if (errno == ECONNRESET && rightstatus == -1) goto DONE;
127  goto BOMB;
128  case SSL_ERROR_SSL:
129 /* Continuing after a received SSL error given the socket is still
130  * potentially active might be a noble cause, but is impracticle.
131  * We consider an SSL_ERROR_SSL as application failure; not TLS
132  * and close the connection gracefully.
133  * if (errno == EAGAIN || errno == EINTR) break;
134  * if (!errno) break;
135  */
136  goto DONE;
137  default:
138  close(fdleft);
139  leftstatus = -1;
140  if (rightstatus == 1) break;
141  if (ssl_shutdown_pending(ssl)) goto DONE;
142  goto BOMB;
143  }
144  }
145 
146  if (ioleft && ioleft->revents) {
147  r = buffer_unixwrite(fdleft,leftbuf + leftpos,leftlen - leftpos);
148  if (r == -1) {
149  if (errno == EINTR || errno == EWOULDBLOCK) {
150  /* retry */
151  }
152  else if (errno == EPIPE || errno == EAGAIN) {
153  if (rightstatus == -1) goto DONE;
154  close(fdleft);
155  leftstatus = -1;
156  } else {
157  rc = -1;
158  goto BOMB;
159  }
160  }
161  else {
162  leftpos += r;
163  if (leftpos == leftlen) {
164  leftstatus = 0;
165  if ((r = ssl_pending(ssl))) {
166  if (r > sizeof(leftbuf)) r = sizeof(leftbuf);
167  r = SSL_read(ssl,leftbuf,r);
168  ssl_errno = SSL_get_error(ssl,r);
169  switch (ssl_errno) {
170  case SSL_ERROR_NONE:
171  leftstatus = 1;
172  leftpos = 0;
173  leftlen = r;
174  break;
175  case SSL_ERROR_WANT_READ:
176  case SSL_ERROR_WANT_WRITE:
177  case SSL_ERROR_WANT_X509_LOOKUP:
178  break;
179  case SSL_ERROR_ZERO_RETURN:
180  if (rightstatus == -1) goto DONE;
181  close(fdleft);
182  leftstatus = -1;
183  break;
184  default:
185  rc = -1;
186  goto BOMB;
187  }
188  }
189  }
190  }
191  }
192 
193  if (ioright && ioright->revents) {
194  r = buffer_unixread(fdright,rightbuf,sizeof(rightbuf));
195  if (r == -1) {
196  if (errno == EINTR || errno == EWOULDBLOCK) {
197  /* retry */
198  } else {
199  rc = -1;
200  goto BOMB; /* errno == EAGAIN => unrecoverable */
201  }
202  }
203  else if (r == 0) {
204  close(fdright);
205  rightstatus = -1;
206  if (ssl_shutdown(ssl)) goto DONE;
207  if (leftstatus == -1) goto DONE;
208  }
209  else {
210  rightstatus = 1;
211  rightpos = 0;
212  rightlen = r;
213  }
214  }
215 
216  if (io1 && io1->revents) {
217  r = SSL_write(ssl,rightbuf + rightpos,rightlen - rightpos);
218  ssl_errno = SSL_get_error(ssl,r);
219  switch (ssl_errno) {
220  case SSL_ERROR_NONE:
221  rightpos += r;
222  if (rightpos == rightlen) rightstatus = 0;
223  break;
224  case SSL_ERROR_WANT_READ:
225  case SSL_ERROR_WANT_WRITE:
226  case SSL_ERROR_WANT_X509_LOOKUP:
227  break;
228  case SSL_ERROR_ZERO_RETURN:
229  close(fdright);
230  rightstatus = -1;
231  if (leftstatus == -1) goto DONE;
232  if (ssl_shutdown(ssl)) goto DONE;
233  break;
234  case SSL_ERROR_SYSCALL:
235  if (errno == EAGAIN || errno == EINTR) break;
236  if (errno == EPIPE) {
237  close(fdright);
238  rightstatus = -1;
239  if (leftstatus == -1) goto DONE;
240  if (ssl_shutdown(ssl)) goto DONE;
241  break;
242  }
243  default:
244  rc = -1;
245  goto BOMB;
246  }
247  }
248  }
249 
250 
251 BOMB:
252  r = errno;
253  if (leftstatus != -1) close(fdleft);
254  if (rightstatus != -1) close(fdright);
255  if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl);
256  if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl);
257  shutdown(wfd,2);
258  errno = r;
259  return rc;
260 
261 
262 DONE:
263  if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl);
264  if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl);
265  shutdown(wfd,2);
266  if (leftstatus != -1) close(fdleft);
267  if (rightstatus != -1) close(fdright);
268  return 0;
269 }
int ssl_io(SSL *ssl, int fdleft, int fdright, unsigned int timeout)
Definition: ssl_io.c:20
unsigned long timeout
Definition: sslhandle.c:66
int ssl_errno
Definition: ucspissl.c:3
Header file to be used with sqmail; previously called ssl.h. (name clash)
#define ssl_pending(ssl)
Definition: ucspissl.h:65
#define ssl_shutdown_sent(ssl)
Definition: ucspissl.h:68
#define ssl_shutdown(ssl)
Definition: ucspissl.h:66
#define ssl_shutdown_pending(ssl)
Definition: ucspissl.h:67