include/wipal/pcap/descriptor.hxx

00001 /*
00002  * WiPal - A library and a set of tools to manipulate wireless traces.
00003  * Copyright (C) 2007  Universite Pierre et Marie Curie - Paris 6
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00018  * MA  02110-1301  USA
00019  *
00020  * Author: Thomas Claveirole <thomas.claveirole@lip6.fr>
00021  */
00022 #ifndef PCAP_DESCRIPTOR_HXX_
00023 # define PCAP_DESCRIPTOR_HXX_
00024 
00025 # include <stdexcept>
00026 # include <fstream>
00027 
00028 # include <boost/lexical_cast.hpp>
00029 
00030 # include "descriptor.hh"
00031 
00032 # include <wipal/tool/endianness.hh>
00033 # include <wipal/tool/exceptions.hh>
00034 
00035 namespace pcapxx
00036 {
00037 
00038   namespace internals
00039   {
00040 
00042     struct DummySetup
00043     {
00044       inline
00045       void
00046       operator () (std::streampos, std::streampos)
00047       {
00048       }
00049     };
00050 
00052     struct DummyUpdate
00053     {
00054       inline
00055       const std::string*
00056       operator () (std::streampos)
00057       {
00058         return 0;
00059       }
00060     };
00061 
00062   } // End of namespace pcapxx::internals.
00063 
00064   template <class Bottom>
00065   descriptor<Bottom>::descriptor(const std::string&     filename,
00066                                  bool                   build_index):
00067     super_type ()
00068   {
00069     internals::DummySetup       pi_setup;
00070     internals::DummyUpdate      pi_update;
00071 
00072     setup(filename, build_index, pi_setup, pi_update);
00073   }
00074 
00075   template <class Bottom>
00076   descriptor<Bottom>::descriptor(const std::string&     filename,
00077                                  const index_type&      marks,
00078                                  unsigned               pkt_count):
00079     super_type (),
00080     marks_ (marks),
00081     pkt_count_ (pkt_count)
00082   {
00083     internals::DummySetup       pi_setup;
00084     internals::DummyUpdate      pi_update;
00085 
00086     setup(filename, false, pi_setup, pi_update);
00087   }
00088 
00089   template <class Bottom>
00090   template <typename ProgressInfoSetupType,
00091             typename ProgressInfoUpdateType>
00092   descriptor<Bottom>::descriptor(const std::string&             filename,
00093                                  ProgressInfoSetupType&         pi_setup,
00094                                  ProgressInfoUpdateType&        pi_update):
00095     super_type ()
00096   {
00097     setup(filename, true, pi_setup, pi_update);
00098   }
00099 
00100   template <class Bottom>
00101   typename descriptor<Bottom>::iterator
00102   descriptor<Bottom>::operator [] (size_t i)
00103   {
00104     assert(marks_.size() and "Cannot use operator [] without index.");
00105 
00106     const size_t        near_mark = i / mark_step;
00107     const size_t        extra_steps = i % mark_step;
00108 
00109     assert(i < pkt_count_);
00110     assert(near_mark < marks_.size());
00111 
00112     iterator            r (*this, marks_[near_mark], i - extra_steps);
00113 
00114     for (unsigned j = 0; j < extra_steps; ++j)
00115       {
00116         ++r;
00117         assert(r != this->end());
00118       }
00119     return r;
00120   }
00121 
00122   template <class Bottom>
00123   size_t
00124   descriptor<Bottom>::size() const
00125   {
00126     assert(marks_.size() and "Cannot use size() without index.");
00127 
00128     return pkt_count_;
00129   }
00130 
00131   template <class Bottom>
00132   bool
00133   descriptor<Bottom>::swapped() const
00134   {
00135     return swap_;
00136   }
00137 
00138   template <class Bottom>
00139   int32_t
00140   descriptor<Bottom>::zone() const
00141   {
00142     return zone_;
00143   }
00144 
00145   template <class Bottom>
00146   int32_t
00147   descriptor<Bottom>::snaplen() const
00148   {
00149     return snaplen_;
00150   }
00151 
00152   template <class Bottom>
00153   typename descriptor<Bottom>::link_type
00154   descriptor<Bottom>::linktype() const
00155   {
00156     return type_;
00157   }
00158 
00159   template <class Bottom>
00160   void
00161   descriptor<Bottom>::expect(link_type l) const
00162   {
00163     using boost::lexical_cast;
00164 
00165     if (linktype() != l)
00166       throw std::invalid_argument (file_name() + ": Expected link type " +
00167                                    lexical_cast<std::string>(l) +
00168                                    ", got " +
00169                                    lexical_cast<std::string> (linktype()));
00170   }
00171 
00172   template <class Bottom>
00173   std::streampos
00174   descriptor<Bottom>::file_size() const
00175   {
00176     return file_size_;
00177   }
00178 
00179   template <class Bottom>
00180   const std::string&
00181   descriptor<Bottom>::file_name() const
00182   {
00183     return file_name_;
00184   }
00185 
00186   template <class Bottom>
00187   dumper
00188   descriptor<Bottom>::dumper(const std::string& filename) const
00189   {
00190     return pcapxx::dumper (filename, int32_t (linktype()), snaplen());
00191   }
00192 
00193   template <class Bottom>
00194   template <typename ProgressInfoSetupType,
00195             typename ProgressInfoUpdateType>
00196   void
00197   descriptor<Bottom>::setup(const std::string&          filename,
00198                             bool                        build_index,
00199                             ProgressInfoSetupType&      pi_setup,
00200                             ProgressInfoUpdateType&     pi_update)
00201   {
00202     internals::file_header      h;
00203     std::ifstream               f (filename.c_str(), (std::ios::in |
00204                                                       std::ios::binary));
00205 
00206     if (not f)
00207       throw tool::file_error ("Open failed");
00208     file_name_ = filename;
00209     if (not f.seekg(0, std::ios::end))
00210       throw tool::seek_error ("Seek operation failed");
00211     file_size_ = f.tellg();
00212     if (not f.seekg(0, std::ios::beg))
00213       throw tool::seek_error ("Seek operation failed");
00214 
00215     if (not f.read(reinterpret_cast<char*> (&h), sizeof (h)) or
00216         f.gcount() != sizeof (h))
00217       throw tool::read_error ("Unreadable PCAP file header");
00218     switch (h.magic)
00219       {
00220       case 0xa1b2c3d4:
00221         swap_ = false;
00222         break;
00223       case 0xd4c3b2a1:
00224         swap_ = true;
00225         break;
00226       default:
00227         throw std::invalid_argument ("Not a PCAP trace");
00228       }
00229 
00230     if (tool::extract_short(h.version_major, swap_) != internals::version_major)
00231       throw std::invalid_argument ("Invalid PCAP major version");
00232     if (tool::extract_short(h.version_minor, swap_) != internals::version_minor)
00233       throw std::invalid_argument ("Invalid PCAP minor version");
00234 
00235     zone_       = tool::extract_long_s(h.thiszone, swap_);
00236     snaplen_    = tool::extract_long_u(h.snaplen, swap_);
00237     type_       = link_type (tool::extract_long_u(h.linktype, swap_));
00238 
00239     pkt_count_ = 0;
00240     if (build_index)
00241       setup_marks(f, pi_setup, pi_update);
00242   }
00243 
00244   template <class Bottom>
00245   void
00246   descriptor<Bottom>::warn(const std::streampos& p, const std::string& msg)
00247   {
00248     std::cerr << file_name_ <<  ':' << p << ": WARNING: " << msg << '.'
00249               << std::endl;
00250   }
00251 
00252   template <class Bottom>
00253   void
00254   descriptor<Bottom>::handle_truncation(const std::string& msg)
00255   {
00256     warn(file_size_, "corrupted file (" + msg + ')');
00257     if (not pkt_count_)
00258       throw std::invalid_argument ("Empty PCAP file");
00259     --pkt_count_;
00260   }
00261 
00262   template <class Bottom>
00263   template <typename ProgressInfoSetupType,
00264             typename ProgressInfoUpdateType>
00265   void
00266   descriptor<Bottom>::setup_marks(std::istream&                 f,
00267                                   ProgressInfoSetupType&        pi_setup,
00268                                   ProgressInfoUpdateType&       pi_update)
00269   {
00270 # ifdef TIME_FILE_LOADING
00271     using namespace boost::posix_time;
00272 
00273     const ptime start = microsec_clock::universal_time();
00274 # endif // TIME_FILE_LOADING
00275 
00276     {
00277       const std::streampos      first_packet_offset = f.tellg();
00278 
00279       marks_.push_back(first_packet_offset);
00280       pi_setup(first_packet_offset, file_size_);
00281     }
00282 
00283     internals::file_frame_header h;
00284 
00285     while (f.read(reinterpret_cast<char*> (&h), sizeof (h)))
00286       {
00287         if (f.gcount() != sizeof (h))
00288           {
00289             handle_truncation("truncated packet header");
00290             break;
00291           }
00292 
00293         char                    junk[junk_len];
00294 
00295         h.caplen = tool::extract_long_u(h.caplen, swap_);
00296         while(h.caplen) // Skip packet data.
00297           {
00298             size_t              to_read = std::min(sizeof (junk),
00299                                                    size_t (h.caplen));
00300 
00301             if (not f.read(junk, to_read))
00302               {
00303                 handle_truncation("truncated packet data");
00304                 break;
00305               }
00306             h.caplen -= to_read;
00307           }
00308 
00309         if (++pkt_count_ % mark_step == 0)
00310           {
00311             const std::streampos        current_offset = f.tellg();
00312 
00313             marks_.push_back(current_offset);
00314             if (const std::string* e = pi_update(current_offset))
00315               throw tool::user_interruption (*e);
00316           }
00317       }
00318     pi_update(file_size_);
00319     if (f.bad())
00320       throw tool::file_error ("Unrecoverable corruption in PCAP file");
00321     f.clear();
00322 
00323 # ifdef TIME_FILE_LOADING
00324     std::cerr << (microsec_clock::universal_time() - start) << std::endl;
00325 # endif // TIME_FILE_LOADING
00326   }
00327 
00328   inline
00329   void
00330   dumper::operator () (const pkthdr* h, const void* f)
00331   {
00332     // PCAP header.
00333     {
00334       internals::file_frame_header h_;
00335 
00336       h_.ts     = h->ts;
00337       h_.caplen = h->caplen;
00338       h_.len    = h->len;
00339 
00340       if (not output_->write(reinterpret_cast<char*> (&h_), sizeof (h_)))
00341         throw tool::write_error("Could not write PCAP packet header");
00342     }
00343 
00344     // Packet.
00345     if (not output_->write(static_cast<const char*> (f), h->caplen))
00346       throw tool::write_error("Could not write packet data");
00347 
00348     if (++pkt_count_ % pcapxx::descriptor<tool::bottom>::mark_step == 0)
00349       marks_.push_back(output_->tellp());
00350   }
00351 
00352   inline
00353   descriptor<tool::bottom>
00354   dumper::descriptor() const
00355   {
00356     return pcapxx::descriptor<tool::bottom> (file_name_, marks_, pkt_count_);
00357   }
00358 
00359 
00360 } // End of namespace pcapxx.
00361 
00362 #endif // ! PCAP_DESCRIPTOR_HXX_

Generated on Tue Jan 15 19:32:31 2008 for wipal by  doxygen 1.5.4