IBR-DTNSuite  0.10
iostreamBIO.cpp
Go to the documentation of this file.
1 /*
2  * iostreamBIO.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Stephen Roettger <roettger@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
23 
24 #include "ibrcommon/Logger.h"
25 
26 #include <openssl/err.h>
27 
28 namespace ibrcommon
29 {
30 
31 static const int ERR_BUF_SIZE = 256;
32 
33 const char * const iostreamBIO::name = "iostreamBIO";
34 
35 /* callback functions for openssl */
36 static int bwrite(BIO *bio, const char *buf, int len);
37 static int bread(BIO *bio, char *buf, int len);
38 //static int bputs(BIO *bio, const char *);
39 //static int bgets(BIO *bio, char *, int);
40 static long ctrl(BIO *bio, int cmd, long num, void *ptr);
41 static int create(BIO *bio);
42 //static int destroy(BIO *bio);
43 //static long (*callback_ctrl)(BIO *, int, bio_info_cb *);
44 
45 
46 static BIO_METHOD iostream_method =
47 {
50  bwrite,
51  bread,
52  NULL,//bputs
53  NULL,//bgets
54  ctrl,
55  create,
56  NULL,//destroy,
57  NULL//callback_ctrl
58 };
59 
60 iostreamBIO::iostreamBIO(iostream *stream)
61  : _stream(stream)
62 {
63  /* create BIO */
64  _bio = BIO_new(&iostream_method);
65  if(!_bio){
66  /* creation failed, throw exception */
67  char err_buf[ERR_BUF_SIZE];
68  ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
69  err_buf[ERR_BUF_SIZE - 1] = '\0';
70  IBRCOMMON_LOGGER_TAG("iostreamBIO", critical) << "BIO creation failed: " << err_buf << IBRCOMMON_LOGGER_ENDL;
71  throw BIOException(err_buf);
72  }
73 
74  /* save the iostream in the bio object */
75  _bio->ptr = stream;
76 }
77 
79  return _bio;
80 }
81 
82 static int create(BIO *bio)
83 {
84  bio->ptr = NULL;
85  /* (from openssl memory bio) */
86  bio->shutdown=1;
87  bio->init=1;
88  /* from bss_mem.c (openssl):
89  * bio->num is used to hold the value to return on 'empty', if it is
90  * 0, should_retry is not set
91  *
92  * => -1 means the caller can retry, 0: retry is useless
93  * it is set to 0 since the underlying stream is blocking
94  */
95  bio->num= 0;
96 
97  return 1;
98 }
99 
100 
101 
102 static long ctrl(BIO *bio, int cmd, long num, void *)
103 {
104  long ret;
105  iostream *stream = reinterpret_cast<iostream*>(bio->ptr);
106 
107  IBRCOMMON_LOGGER_DEBUG_TAG("iostreamBIO", 90) << "ctrl called, cmd: " << cmd << ", num: " << num << "." << IBRCOMMON_LOGGER_ENDL;
108 
109  switch(cmd){
110  case BIO_CTRL_PUSH:
111  case BIO_CTRL_POP:
112  ret = 0;
113  break;
114 // case BIO_CTRL_GET_CLOSE:
115 // ret = bio->shutdown;
116 // break;
117 // case BIO_CTRL_SET_CLOSE:
118 // bio->shutdown = (int)num;
119 // ret = 1;
120 // break;
121  case BIO_CTRL_FLUSH:
122  /* try to flush the underlying stream */
123  ret = 1;
124  try{
125  stream->flush();
126  } catch(ios_base::failure &ex){
127  /* ignore, the badbit is checked instead */
128  }
129 // catch(ConnectionClosedException &ex){
130 // throw;
131 // }
132  if(stream->bad()){
133  IBRCOMMON_LOGGER_DEBUG_TAG("iostreamBIO", 20) << "underlying Stream went bad while flushing." << IBRCOMMON_LOGGER_ENDL;
134  ret = 0;
135  }
136  break;
137  default:
138  IBRCOMMON_LOGGER_TAG("iostreamBIO", warning) << "ctrl called with unhandled cmd: " << cmd << "." << IBRCOMMON_LOGGER_ENDL;
139  ret = 0;
140  break;
141  }
142 
143  return ret;
144 }
145 
146 
147 
148 static int bread(BIO *bio, char *buf, int len)
149 {
150  iostream *stream = reinterpret_cast<iostream*>(bio->ptr);
151  int num_bytes = bio->num;
152 
153  try{
154  /* make sure to read at least 1 byte and then read as much as we can */
155  num_bytes = static_cast<int>( stream->read(buf, 1).readsome(buf+1, len-1) + 1 );
156  } catch(ios_base::failure &ex){
157  /* ignore, bio->num will be returned and indicate the error */
158  }
159 // catch(ConnectionClosedException &ex){
160 // throw; //this exception will be catched at higher layers
161 // }
162 
163  return num_bytes;
164 }
165 
166 
167 
168 static int bwrite(BIO *bio, const char *buf, int len)
169 {
170  if(len == 0){
171  return 0;
172  }
173  iostream *stream = reinterpret_cast<iostream*>(bio->ptr);
174 
175  /* write the data */
176  try{
177  stream->write(buf, len);
178  } catch(ios_base::failure &ex){
179  /* ignore, the badbit is checked instead */
180  }
181 // catch(ConnectionClosedException &ex){
182 // throw;
183 // }
184  if(stream->bad()){
185  IBRCOMMON_LOGGER_DEBUG_TAG("iostreamBIO", 20) << "underlying Stream went bad while writing." << IBRCOMMON_LOGGER_ENDL;
186  return 0;
187  }
188 
189  /* flush the underlying stream */
190  try{
191  stream->flush();
192  } catch(ios_base::failure &ex){
193  /* ignore, the badbit is checked instead */
194  }
195 // catch(ConnectionClosedException &ex){
196 // throw;
197 // }
198  if(stream->bad()){
199  IBRCOMMON_LOGGER_DEBUG_TAG("iostreamBIO", 20) << "underlying Stream went bad while flushing (bwrite)." << IBRCOMMON_LOGGER_ENDL;
200  return 0;
201  }
202 
203  return len;
204 }
205 
206 }