-
Notifications
You must be signed in to change notification settings - Fork 25
/
read_write.c
149 lines (124 loc) · 4.28 KB
/
read_write.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "common.h"
/* Read from the keyboard and write to the server
Read from the server and write to the keyboard
we use select() to multiplex
*/
void read_write(SSL *ssl,int sock)
{
int width;
int r,c2sl=0,c2s_offset=0;
int read_blocked_on_write=0,write_blocked_on_read=0,read_blocked=0;
fd_set readfds,writefds;
int shutdown_wait=0;
char c2s[BUFSIZZ],s2c[BUFSIZZ];
int ofcmode;
/*First we make the socket nonblocking*/
ofcmode=fcntl(sock,F_GETFL,0);
ofcmode|=O_NDELAY;
if(fcntl(sock,F_SETFL,ofcmode))
err_exit("Couldn't make socket nonblocking");
width=sock+1;
while(1){
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(sock,&readfds);
/* If we're waiting for a read on the socket don't
try to write to the server */
if(!write_blocked_on_read){
/* If we have data in the write queue don't try to
read from stdin */
if(c2sl || read_blocked_on_write)
FD_SET(sock,&writefds);
else
FD_SET(fileno(stdin),&readfds);
}
r=select(width,&readfds,&writefds,0,0);
if(r==0)
continue;
/* Now check if there's data to read */
if((FD_ISSET(sock,&readfds) && !write_blocked_on_read) ||
(read_blocked_on_write && FD_ISSET(sock,&writefds))){
do {
read_blocked_on_write=0;
read_blocked=0;
r=SSL_read(ssl,s2c,BUFSIZZ);
switch(SSL_get_error(ssl,r)){
case SSL_ERROR_NONE:
/* Note: this call could block, which blocks the
entire application. It's arguable this is the
right behavior since this is essentially a terminal
client. However, in some other applications you
would have to prevent this condition */
fwrite(s2c,1,r,stdout);
break;
case SSL_ERROR_ZERO_RETURN:
/* End of data */
if(!shutdown_wait)
SSL_shutdown(ssl);
goto end;
break;
case SSL_ERROR_WANT_READ:
read_blocked=1;
break;
/* We get a WANT_WRITE if we're
trying to rehandshake and we block on
a write during that rehandshake.
We need to wait on the socket to be
writeable but reinitiate the read
when it is */
case SSL_ERROR_WANT_WRITE:
read_blocked_on_write=1;
break;
default:
berr_exit("SSL read problem");
}
/* We need a check for read_blocked here because
SSL_pending() doesn't work properly during the
handshake. This check prevents a busy-wait
loop around SSL_read() */
} while (SSL_pending(ssl) && !read_blocked);
}
/* Check for input on the console*/
if(FD_ISSET(fileno(stdin),&readfds)){
c2sl=read(fileno(stdin),c2s,BUFSIZZ);
if(c2sl==0){
shutdown_wait=1;
if(SSL_shutdown(ssl))
return;
}
c2s_offset=0;
}
/* If the socket is writeable... */
if((FD_ISSET(sock,&writefds) && c2sl) ||
(write_blocked_on_read && FD_ISSET(sock,&readfds))) {
write_blocked_on_read=0;
/* Try to write */
r=SSL_write(ssl,c2s+c2s_offset,c2sl);
switch(SSL_get_error(ssl,r)){
/* We wrote something*/
case SSL_ERROR_NONE:
c2sl-=r;
c2s_offset+=r;
break;
/* We would have blocked */
case SSL_ERROR_WANT_WRITE:
break;
/* We get a WANT_READ if we're
trying to rehandshake and we block on
write during the current connection.
We need to wait on the socket to be readable
but reinitiate our write when it is */
case SSL_ERROR_WANT_READ:
write_blocked_on_read=1;
break;
/* Some other error */
default:
berr_exit("SSL write problem");
}
}
}
end:
SSL_free(ssl);
close(sock);
return;
}