IBR-DTNSuite  0.8
ibrcommon/ibrcommon/net/iostreamBIO.cpp
Go to the documentation of this file.
00001 /*
00002  * iostreamBIO.cpp
00003  *
00004  *  Created on: Mar 25, 2011
00005  *      Author: roettger
00006  */
00007 
00008 #include "iostreamBIO.h"
00009 
00010 #include "ibrcommon/Logger.h"
00011 
00012 #include <openssl/err.h>
00013 
00014 namespace ibrcommon
00015 {
00016 
00017 static const int ERR_BUF_SIZE = 256;
00018 
00019 const char * const iostreamBIO::name = "iostreamBIO";
00020 
00021 /* callback functions for openssl */
00022 static int bwrite(BIO *bio, const char *buf, int len);
00023 static int bread(BIO *bio, char *buf, int len);
00024 //static int bputs(BIO *bio, const char *);
00025 //static int bgets(BIO *bio, char *, int);
00026 static long ctrl(BIO *bio, int cmd, long num, void *ptr);
00027 static int create(BIO *bio);
00028 //static int destroy(BIO *bio);
00029 //static long (*callback_ctrl)(BIO *, int, bio_info_cb *);
00030 
00031 
00032 static BIO_METHOD iostream_method =
00033 {
00034                 iostreamBIO::type,
00035                 iostreamBIO::name,
00036                 bwrite,
00037                 bread,
00038                 NULL,//bputs
00039                 NULL,//bgets
00040                 ctrl,
00041                 create,
00042                 NULL,//destroy,
00043                 NULL//callback_ctrl
00044 };
00045 
00046 iostreamBIO::iostreamBIO(iostream *stream)
00047         :       _stream(stream)
00048 {
00049         /* create BIO */
00050         _bio = BIO_new(&iostream_method);
00051         if(!_bio){
00052                 /* creation failed, throw exception */
00053                 char err_buf[ERR_BUF_SIZE];
00054         ERR_error_string_n(ERR_get_error(), err_buf, ERR_BUF_SIZE);
00055         err_buf[ERR_BUF_SIZE - 1] = '\0';
00056         IBRCOMMON_LOGGER(critical) << "iostreamBIO: BIO creation failed: " << err_buf << IBRCOMMON_LOGGER_ENDL;
00057                 throw BIOException(err_buf);
00058         }
00059 
00060         /* save the iostream in the bio object */
00061         _bio->ptr = stream;
00062 }
00063 
00064 BIO *
00065 iostreamBIO::getBIO(){
00066         return _bio;
00067 }
00068 
00069 static int create(BIO *bio)
00070 {
00071         bio->ptr = NULL;
00072         /* (from openssl memory bio) */
00073         bio->shutdown=1;
00074         bio->init=1;
00075         /* from bss_mem.c (openssl):
00076          * bio->num is used to hold the value to return on 'empty', if it is
00077          * 0, should_retry is not set
00078          *
00079          * => -1 means the caller can retry, 0: retry is useless
00080          * it is set to 0 since the underlying stream is blocking
00081          */
00082         bio->num= 0;
00083 
00084         return 1;
00085 }
00086 
00087 
00088 
00089 static long ctrl(BIO *bio, int cmd, long  num, void *ptr)
00090 {
00091         long ret;
00092         iostream *stream = reinterpret_cast<iostream*>(bio->ptr);
00093 
00094         IBRCOMMON_LOGGER_DEBUG(90) << "iostreamBIO: ctrl called, cmd: " << cmd << ", num: " << num << "." << IBRCOMMON_LOGGER_ENDL;
00095 
00096         switch(cmd){
00097         case BIO_CTRL_PUSH:
00098         case BIO_CTRL_POP:
00099                 ret = 0;
00100                 break;
00101 //      case BIO_CTRL_GET_CLOSE:
00102 //              ret = bio->shutdown;
00103 //              break;
00104 //      case BIO_CTRL_SET_CLOSE:
00105 //              bio->shutdown = (int)num;
00106 //              ret = 1;
00107 //              break;
00108         case BIO_CTRL_FLUSH:
00109                 /* try to flush the underlying stream */
00110                 ret = 1;
00111                 try{
00112                         stream->flush();
00113                 } catch(ios_base::failure &ex){
00114                         /* ignore, the badbit is checked instead */
00115                 }
00116 //              catch(ConnectionClosedException &ex){
00117 //                      throw;
00118 //              }
00119                 if(stream->bad()){
00120                         IBRCOMMON_LOGGER_DEBUG(20) << "iostreamBIO: underlying Stream went bad while flushing." << IBRCOMMON_LOGGER_ENDL;
00121                         ret = 0;
00122                 }
00123                 break;
00124         default:
00125                 IBRCOMMON_LOGGER(warning) << "iostreamBIO: ctrl called with unhandled cmd: " << cmd << "." << IBRCOMMON_LOGGER_ENDL;
00126                 ret = 0;
00127                 break;
00128         }
00129 
00130         return ret;
00131 }
00132 
00133 
00134 
00135 static int bread(BIO *bio, char *buf, int len)
00136 {
00137         iostream *stream = reinterpret_cast<iostream*>(bio->ptr);
00138         streamsize num_bytes = bio->num;
00139 
00140         try{
00141                 /* make sure to read at least 1 byte and then read as much as we can */
00142                 num_bytes = stream->read(buf, 1).readsome(buf+1, len-1) + 1;
00143         } catch(ios_base::failure &ex){
00144                 /* ignore, bio->num will be returned and indicate the error */
00145         }
00146 //      catch(ConnectionClosedException &ex){
00147 //              throw; //this exception will be catched at higher layers
00148 //      }
00149 
00150         return num_bytes;
00151 }
00152 
00153 
00154 
00155 static int bwrite(BIO *bio, const char *buf, int len)
00156 {
00157         if(len == 0){
00158                 return 0;
00159         }
00160         iostream *stream = reinterpret_cast<iostream*>(bio->ptr);
00161 
00162         /* write the data */
00163         try{
00164                 stream->write(buf, len);
00165         } catch(ios_base::failure &ex){
00166                 /* ignore, the badbit is checked instead */
00167         }
00168 //      catch(ConnectionClosedException &ex){
00169 //              throw;
00170 //      }
00171         if(stream->bad()){
00172                 IBRCOMMON_LOGGER_DEBUG(20) << "iostreamBIO: underlying Stream went bad while writing." << IBRCOMMON_LOGGER_ENDL;
00173                 return 0;
00174         }
00175 
00176         /* flush the underlying stream */
00177         try{
00178                 stream->flush();
00179         } catch(ios_base::failure &ex){
00180                 /* ignore, the badbit is checked instead */
00181         }
00182 //      catch(ConnectionClosedException &ex){
00183 //              throw;
00184 //      }
00185         if(stream->bad()){
00186                 IBRCOMMON_LOGGER_DEBUG(20) << "iostreamBIO: underlying Stream went bad while flushing (bwrite)." << IBRCOMMON_LOGGER_ENDL;
00187                 return 0;
00188         }
00189 
00190         return len;
00191 }
00192 
00193 }