Panda3D

bioStreamBuf.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file bioStreamBuf.cxx
10 * @author drose
11 * @date 2002-09-25
12 */
13
14 #include "bioStreamBuf.h"
15 #include "config_downloader.h"
16 #include "openSSLWrapper.h"
17 #include <errno.h>
18
19 #ifdef HAVE_OPENSSL
20
21 #if defined(WIN32_VC) || defined(WIN64_VC)
22 #include <winsock2.h>
23 #include <windows.h> // for WSAGetLastError()
24 #undef X509_NAME
25 #endif // WIN32_VC
26
27 /**
28 *
29 */
30 BioStreamBuf::
31 BioStreamBuf() {
32 _read_open = false;
33 _write_open = false;
34
35 #ifdef PHAVE_IOSTREAM
36 _buffer = (char *)PANDA_MALLOC_ARRAY(8192);
37 char *ebuf = _buffer + 8192;
38 char *mbuf = _buffer + 4096;
39 setg(_buffer, mbuf, mbuf);
40 setp(mbuf, ebuf);
41
42 #else
43 allocate();
44 // Chop the buffer in half. The bottom half goes to the get buffer; the top
45 // half goes to the put buffer.
46 char *b = base();
47 char *t = ebuf();
48 char *m = b + (t - b) / 2;
49 setg(b, m, m);
50 setp(b, m);
51 #endif
52 }
53
54 /**
55 *
56 */
57 BioStreamBuf::
58 ~BioStreamBuf() {
59 close();
60 #ifdef PHAVE_IOSTREAM
61 PANDA_FREE_ARRAY(_buffer);
62 #endif
63 }
64
65 /**
66 *
67 */
68 void BioStreamBuf::
69 open(BioPtr *source) {
70 _source = source;
71 _read_open = true;
72 _write_open = true;
73 }
74
75 /**
76 *
77 */
78 void BioStreamBuf::
79 close() {
80 sync();
81 _source.clear();
82 _read_open = false;
83 _write_open = false;
84 }
85
86 /**
87 * Called by the system ostream implementation when its internal buffer is
88 * filled, plus one character.
89 */
90 int BioStreamBuf::
91 overflow(int ch) {
92 size_t n = pptr() - pbase();
93 if (downloader_cat.is_spam()) {
94 downloader_cat.spam()
95 << "BioStreamBuf::overflow, " << n << " bytes\n";
96 }
97 if (n != 0) {
98 size_t num_wrote = write_chars(pbase(), n);
99 pbump(-(int)n);
100 if (num_wrote != n) {
101 return EOF;
102 }
103 }
104
105 if (ch != EOF) {
106 // Store the next character back in the buffer.
107 *pptr() = ch;
108 pbump(1);
109 }
110
111 return 0;
112 }
113
114 /**
115 * Called by the system iostream implementation to implement a flush
116 * operation.
117 */
118 int BioStreamBuf::
119 sync() {
120 /*
121 size_t n = egptr() - gptr();
122 gbump(n);
123 */
124
125 size_t n = pptr() - pbase();
126
127 if (downloader_cat.is_spam() && n != 0) {
128 downloader_cat.spam()
129 << "BioStreamBuf::sync, " << n << " bytes\n";
130 }
131
132 size_t num_wrote = write_chars(pbase(), n);
133 pbump(-(int)n);
134
135 if (num_wrote != n) {
136 return EOF;
137 }
138
139 return 0;
140 }
141
142 /**
143 * Called by the system istream implementation when its internal buffer needs
144 * more characters.
145 */
146 int BioStreamBuf::
147 underflow() {
148 // Sometimes underflow() is called even if the buffer is not empty.
149 if (gptr() >= egptr()) {
150 size_t buffer_size = egptr() - eback();
151 gbump(-(int)buffer_size);
152
153 size_t num_bytes = buffer_size;
154
155 // BIO_read might return -1 or -2 on eof or error, so we have to allow for
156 // negative numbers.
157 int read_count = BIO_read(*_source, gptr(), buffer_size);
158 thread_consider_yield();
159
160 if (read_count != (int)num_bytes) {
161 // Oops, we didn't read what we thought we would.
162 if (read_count <= 0) {
163 // Immediately save the os error in case we screw up and do something
164 // that will change its value before we can output it.
165 #if defined(WIN32_VC) || defined(WIN64_VC)
166 int os_error = WSAGetLastError();
167 #else
168 int os_error = errno;
169 #endif // WIN32_VC
170
171 // Though BIO_eof() is tempting, it appears there are cases in which
172 // that never returns true, if the socket is closed by the server.
173 // But BIO_should_retry() *appears* to be reliable.
174 _read_open = (BIO_should_retry(*_source) != 0);
175
176 #ifdef IS_OSX
177 // occassionally we get -1 on read_open on the mac the os_error is 35
178 // which means "Resource temporarily unavailable".
179 if (!_read_open && os_error == 35) {
180 downloader_cat.warning() << "forcing retry to true again and _read_open to true\n";
181 BIO_set_retry_read(*_source);
182 _read_open = true;
183 }
184 #endif
185 if (!_read_open) {
186 downloader_cat.info()
187 << "Lost connection to "
188 << _source->get_server_name() << ":"
189 << _source->get_port() << " (" << read_count << ").\n";
190 OpenSSLWrapper::get_global_ptr()->notify_ssl_errors();
191
192 SSL *ssl = nullptr;
193 BIO_get_ssl(*_source, &ssl);
194 if (ssl != nullptr) {
195 downloader_cat.warning()
196 << "OpenSSL error code: " << SSL_get_error(ssl, read_count)
197 << "\n";
198 }
199
200 #if defined(WIN32_VC) || defined(WIN64_VC)
201 downloader_cat.warning()
202 << "Windows error code: " << os_error << "\n";
203 #else
204 downloader_cat.warning()
205 << "Unix error code: " << os_error << "\n";
206 #endif // WIN32_VC
207 }
208 gbump(num_bytes);
209 return EOF;
210 }
211
212 // Slide what we did read to the top of the buffer.
213 nassertr(read_count < (int)num_bytes, EOF);
214 size_t delta = (int)num_bytes - read_count;
215 memmove(gptr() + delta, gptr(), read_count);
216 gbump(delta);
217 }
218
219 if (downloader_cat.is_spam()) {
220 downloader_cat.spam()
221 << "read " << read_count << " bytes from " << _source << "\n";
222 }
223 }
224
225 return (unsigned char)*gptr();
226 }
227
228 /**
229 * Sends some characters to the dest stream. Does not return until all
230 * characters are sent or the socket is closed, even if the underlying BIO is
231 * non-blocking.
232 */
233 size_t BioStreamBuf::
234 write_chars(const char *start, size_t length) {
235 if (length != 0) {
236 size_t wrote_so_far = 0;
237
238 int write_count = BIO_write(*_source, start, length);
239 thread_consider_yield();
240 while (write_count != (int)(length - wrote_so_far)) {
241 if (write_count <= 0) {
242 /*
243 * http:www.openssl.orgdocscryptoBIO_s_bio.html "Calls to BIO_write() will
244 * place data in the buffer or request a retry if the buffer is full." when
245 * the server is terminated, this seems to be the best way of detecting that
246 * case on the client: a BIO write error without a retry request _write_open =
247 * BIO_should_retry(*_source); _write_open = !BIO_eof(*_source);
248 */
249 _write_open = (BIO_should_write(*_source) != 0 || BIO_should_retry(*_source) != 0);
250 if (!_write_open) {
251 return wrote_so_far;
252 }
253
254 // Block on the underlying socket before we try to write some more.
255 int fd = -1;
256 BIO_get_fd(*_source, &fd);
257 if (fd < 0) {
258 downloader_cat.warning()
259 << "socket BIO has no file descriptor.\n";
260 } else {
261 if (downloader_cat.is_spam()) {
262 downloader_cat.spam()
263 << "waiting to write to BIO.\n";
264 }
265 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
266 // In SIMPLE_THREADS mode, instead of blocking, simply yield the
267 // thread.
268 thread_yield();
269 #else
270 // In any other threading mode, we actually want to block.
271 fd_set wset;
272 FD_ZERO(&wset);
273 FD_SET(fd, &wset);
274 select(fd + 1, nullptr, &wset, nullptr, nullptr);
275 #endif // SIMPLE_THREADS
276 }
277
278 } else {
279 // wrote some characters.
280 wrote_so_far += write_count;
281 if (downloader_cat.is_spam()) {
282 downloader_cat.spam()
283 << "wrote " << write_count << " bytes to " << _source << "\n";
284 }
285 }
286
287 // Try to write some more.
288 write_count = BIO_write(*_source, start + wrote_so_far, length - wrote_so_far);
289 if (downloader_cat.is_spam()) {
290 downloader_cat.spam()
291 << "continued, wrote " << write_count << " bytes.\n";
292 }
293 thread_consider_yield();
294 }
295 if (downloader_cat.is_spam()) {
296 downloader_cat.spam()
297 << "wrote " << write_count << " bytes to " << _source << "\n";
298 }
299 }
300
301 return length;
302 }
303
304 #endif // HAVE_OPENSSL
bioStreamBuf.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_downloader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
openSSLWrapper.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.