raw_socket.cpp

Go to the documentation of this file.
00001 #ifndef RAW_SOCKET_IMPLEMENTATION_FILE
00002 #define RAW_SOCKET_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : raw_socket                                                        *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 1991-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "imp_sockets.h"
00019 #include "raw_socket.h"
00020 #include "tcpip_definitions.h"
00021 #include "tcpip_stack.h"
00022 
00023 #include <basis/function.h>
00024 #include <basis/portable.h>
00025 #include <loggers/console_logger.h>
00026 
00027 #ifdef __UNIX__
00028   #include <arpa/inet.h>
00029   #include <errno.h>
00030   #include <netinet/tcp.h>
00031   #include <sys/ioctl.h>
00032   #include <sys/socket.h>
00033   #include <unistd.h>
00034   #define OPTYPE (void *)
00035 #endif
00036 #ifdef __WIN32__
00037   #define OPTYPE (char *)
00038 #endif
00039 
00040 //#define DEBUG_RAW_SOCKET
00041   // uncomment for noisy diagnostics.
00042 
00043 #undef LOG
00044 #define LOG(to_print) CLASS_EMERGENCY_LOG(console_logger(), to_print)
00045 
00046 const int MULTIPLE_DISCONNECT_CHECKS = 28;
00047   // we will make certain that select really says there's data and ioctl
00048   // really says there's no data waiting before we believe there's been
00049   // a disconnect.  we'll check that state the number of times specified.
00050 
00051 class fd_set_wrapper : public fd_set {};
00052 
00054 
00055 const u_int NON_BLOCKING = FIONBIO;
00056 const u_int IOCTL_READ = FIONREAD;
00057 
00058 #ifdef __WIN32__
00059 // defined by winsock header but not present in the winsock dll.
00060 int PASCAL FAR __WSAFDIsSet(SOCKET fd, fd_set FAR *the_set)
00061 {
00062   int i = the_set->fd_count;
00063   while (i--)
00064     if (the_set->fd_array[i] == fd)
00065       return true;
00066   return false;
00067 }
00068 #endif
00069 
00071 
00072 raw_socket::raw_socket()
00073 : _stack(new tcpip_stack())
00074 {}
00075 
00076 raw_socket::~raw_socket()
00077 {
00078   WHACK(_stack);
00079 }
00080 
00081 int raw_socket::close(u_int &socket)
00082 {
00083   int to_return = 0;
00084 #ifdef __WIN32__
00085   to_return = closesocket(socket);
00086 #endif
00087 #ifdef __UNIX__
00088   to_return = ::close(socket);
00089 #endif
00090   socket = 0;
00091   return to_return;
00092 }
00093 
00094 //move this into parser bits as a OR combiner or something.
00095 inline void combine(istring &existing, const istring &addition)
00096 {
00097   if (addition.t()) {
00098     if (existing.t()) existing += " | ";
00099     existing += addition;
00100   }
00101 }
00102 
00103 istring raw_socket::interest_name(int interest)
00104 {
00105   istring to_return;
00106   if (interest & SI_CONNECTED) combine(to_return, "CONNECTED");
00107   if (interest & SI_DISCONNECTED) combine(to_return, "DISCONNECTED");
00108   if (interest & SI_WRITABLE) combine(to_return, "WRITABLE");
00109   if (interest & SI_READABLE) combine(to_return, "READABLE");
00110   if (interest & SI_ERRONEOUS) combine(to_return, "ERRONEOUS");
00111   if (!interest) combine(to_return, "NORMAL");
00112   return to_return;
00113 }
00114 
00115 int raw_socket::ioctl(u_int socket, int request, void *argp) const
00116 {
00117 #ifdef __UNIX__
00118   return ::ioctl(socket, request, argp);
00119 #endif
00120 #ifdef __WIN32__
00121   return ioctlsocket(socket, request, (u_long *)argp);
00122 #endif
00123 }
00124 
00125 bool raw_socket::set_non_blocking(u_int socket, bool non_blocking)
00126 {
00127   FUNCDEF("set_non_blocking");
00128   int arg = int(non_blocking);
00129   if (negative(ioctl(socket, FIONBIO, &arg))) {
00130     LOG(isprintf("Could not set FIONBIO option on raw_socket %u.", socket));
00131     return false;
00132   }
00133   return true;
00134 }
00135 
00136 bool raw_socket::set_nagle_algorithm(u_int socket, bool use_nagle)
00137 {
00138   FUNCDEF("set_nagle_algorithm");
00139   int arg = int(!use_nagle);  // opposite of the flag, since we set no-delay.
00140   if (negative(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, OPTYPE &arg,
00141       sizeof(arg)))) {
00142     LOG(isprintf("Could not change nagle coalescing mode on %u.", socket));
00143     return false;
00144   }
00145   return true;
00146 }
00147 
00148 bool raw_socket::set_broadcast(u_int socket, bool broadcasting)
00149 {
00150   FUNCDEF("set_broadcast");
00151   int arg = int(broadcasting);
00152   if (negative(setsockopt(socket, SOL_SOCKET, SO_BROADCAST, OPTYPE &arg,
00153       sizeof(arg)))) {
00154     LOG(isprintf("Could not change broadcast mode on %u.", socket));
00155     return false;
00156   }
00157   return true;
00158 }
00159 
00160 bool raw_socket::set_reuse_address(u_int socket, bool reuse)
00161 {
00162   FUNCDEF("set_reuse_address");
00163   int arg = int(reuse);
00164   if (negative(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, OPTYPE &arg,
00165       sizeof(arg)))) {
00166     LOG(isprintf("Could not set reuse address mode on %u.", socket));
00167     return false;
00168   }
00169   return true;
00170 }
00171 
00172 bool raw_socket::set_keep_alive(u_int socket, bool keep_alive)
00173 {
00174   FUNCDEF("set_keep_alive");
00175   int arg = int(keep_alive);
00176   if (negative(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, OPTYPE &arg,
00177       sizeof(arg)))) {
00178     LOG(isprintf("Could not set keep alive mode on %u.", socket));
00179     return false;
00180   }
00181   return true;
00182 }
00183 
00184 int raw_socket::select(u_int socket, int mode, int timeout) const
00185 {
00186   FUNCDEF("select [single]");
00187   if (!socket) return SI_ERRONEOUS;
00188   fd_set_wrapper read_list, write_list, exceps;
00189   int ret = inner_select(socket, mode, timeout, read_list, write_list, exceps);
00190   if (!ret) return 0;  // nothing is happening.
00191   if (ret == SI_ERRONEOUS) return SI_ERRONEOUS;  // something bad happened.
00192   // otherwise we should be at base-line status.
00193   return analyze_select_result(socket, mode, read_list, write_list, exceps);
00194 }
00195 
00196 int raw_socket::inner_select(u_int socket, int mode, int timeout,
00197     fd_set_wrapper &read_list, fd_set_wrapper &write_list,
00198     fd_set_wrapper &exceptions) const
00199 {
00200   FUNCDEF("inner_select");
00201   // setup the file descriptor sets for the select.  we check readability,
00202   // writability and exception status.
00203   FD_ZERO(&read_list); FD_SET(socket, &read_list);
00204   FD_ZERO(&write_list); FD_SET(socket, &write_list);
00205   FD_ZERO(&exceptions); FD_SET(socket, &exceptions);
00206 
00207   timeval time_out = portable::fill_timeval_ms(timeout);
00208     // timeval has tv_sec=seconds, tv_usec=microseconds.
00209 
00210   // select will tell us about the socket.
00211   int ret = ::select(socket + 1,
00212       (mode & SELECTING_JUST_WRITE)? NIL : &read_list,
00213       (mode & SELECTING_JUST_READ)? NIL : &write_list,
00214       &exceptions, &time_out);
00215   int error = portable::system_error();
00216 
00217   if (!ret) return 0;  // nothing to report.
00218 
00219   if (ret == SOCKET_ERROR) {
00220     switch (error) {
00221       // all real errors fall out to the error handling stuff.
00222       case SOCK_EFAULT:  // intentional fall-through.
00223       case SOCK_ENETDOWN:  // intentional fall-through.
00224       case SOCK_EINVAL:  // intentional fall-through.
00225       case SOCK_EINTR:  // intentional fall-through.
00226 #ifdef __WIN32__
00227       case SOCK_NOTINITIALISED:  // intentional fall-through.
00228 #endif
00229       case SOCK_ENOTSOCK:
00230         break;
00231 
00232       // hopefully all these others are bogus errors...
00233       case SOCK_EINPROGRESS:  // intentional fall-through.
00234       case 0:  // intentional fall-through.
00235       default:
00236 #ifdef DEBUG_RAW_SOCKET
00237         LOG("got to weird case, in progress or zero.");
00238 #endif
00239         return 0;  // not really an error.
00240     }
00241 #ifdef DEBUG_RAW_SOCKET
00242     LOG(isprintf("socket %u had error %d in select: %s.",
00243         socket, error, _stack->tcpip_error_name(error).s()));
00244 #endif
00245     return SI_ERRONEOUS;
00246   }
00247 
00248   // if we got to here, then there are some things to report...
00249   return SI_BASELINE;
00250 }
00251 
00252 int raw_socket::test_readability(u_int socket) const
00253 {
00254   FUNCDEF("test_readability");
00255   u_int len;
00256   if (negative(ioctl(socket, IOCTL_READ, &len))) {
00257     LOG(istring(istring::SPRINTF, "socket %u had ioctl error: %s.",
00258         socket, _stack->tcpip_error_name(portable::system_error()).s()));
00259     return SI_ERRONEOUS;
00260   } else {
00261     if (positive(len)) return SI_READABLE;
00262     else return SI_DISCONNECTED;
00263   }
00264 }
00265 
00266 int raw_socket::analyze_select_result(u_int socket, int mode,
00267     fd_set_wrapper &read_list, fd_set_wrapper &write_list,
00268     fd_set_wrapper &exceptions) const
00269 {
00270   FUNCDEF("analyze_select_result");
00271   int to_return = 0;
00272 
00273   // in case of an exception, we return an error.
00274   if (FD_ISSET(socket, &exceptions)) {
00275 #ifdef DEBUG_RAW_SOCKET
00276     LOG(istring(istring::SPRINTF, "exception seen for socket %u!", socket));
00277 #endif
00278   }
00279 
00280   // check to see if there are bytes to read.
00281   if ( ! (mode & SELECTING_JUST_WRITE) && FD_ISSET(socket, &read_list)) {
00282     // make sure we have data.  if no data is available, it means a
00283     // disconnect occurred.
00284 
00285     int readable = test_readability(socket);
00286     if (readable == SI_ERRONEOUS)
00287       to_return |= SI_ERRONEOUS;
00288     else if (readable == SI_READABLE)
00289       to_return |= SI_READABLE;
00290     else if (readable == SI_DISCONNECTED) {
00291       // we need to check multiple times to be sure the OS really means this.
00292       // either windoze seems to report an erroneous disconnect every few
00293       // days or there's a bad synchronization issue as yet uncovered.
00294       bool really_disconnected = true;
00295       for (int i = 0; i < MULTIPLE_DISCONNECT_CHECKS; i++) {
00296         fd_set_wrapper read_list, write_list, exceps;
00297         int temp_ret = inner_select(socket, SELECTING_JUST_READ, 0, read_list,
00298             write_list, exceps);
00299         // check the return value first...
00300         if (!temp_ret) {
00301           // nothing happening (a zero return) means the socket's no longer
00302           // claiming to have a readable state; our disconnect condition is
00303           // thus violated and we can leave.
00304           really_disconnected = false;
00305           break;
00306         }
00307         if (temp_ret == SI_ERRONEOUS) {
00308           // this, on the other hand, sounds really bad.  the socket doesn't
00309           // seem to exist any more or something else horrid happened.
00310           really_disconnected = true;
00311           break;
00312         }
00313         // if the select worked, we can check the fd_set now for readability.
00314         if (!FD_ISSET(socket, &read_list)) {
00315           // we are not in a disconnected state without being told we're
00316           // readable.  supposedly.
00317           really_disconnected = false;
00318           break;
00319         }
00320         // now we really test the socket for readability by making sure there
00321         // really is data pending on the socket.  if it's readable but there's
00322         // no data, then either a disconnection has occurred or is in progress.
00323         readable = test_readability(socket);
00324         if (readable != SI_DISCONNECTED) {
00325           // we are not disconnected if there's really data waiting.
00326           really_disconnected = false;
00327           break;
00328         }
00329       }
00330       if (really_disconnected) {
00331 #ifdef DEBUG_RAW_SOCKET
00332         LOG(isprintf("connection closed on socket %u.", socket));
00333 #endif
00334         to_return |= SI_DISCONNECTED;
00335       }
00336     }
00337   }
00338 
00339   // check writability state.
00340   if (! (mode & SELECTING_JUST_READ) && FD_ISSET(socket, &write_list)) {
00341     to_return |= SI_WRITABLE;
00342   }
00343 
00344   return to_return;
00345 }
00346 
00347 int raw_socket::select(int_array &read_sox, int_array &write_sox,
00348     int timeout) const
00349 {
00350   FUNCDEF("select [multiple]");
00351   if (!read_sox.length() && !write_sox.length())
00352    return 0;  // nothing happened to nothing.
00353 
00354   int to_return = 0;  // will get bits slammed into it to report results.
00355 
00356   // setup the file descriptor sets for the select.  we check readability,
00357   // writability and exception status.
00358   fd_set_wrapper read_list; FD_ZERO(&read_list);
00359   fd_set_wrapper write_list; FD_ZERO(&write_list);
00360   fd_set_wrapper exceptions; FD_ZERO(&exceptions);
00361   // set up the lists with the sets we were handed.
00362   u_int highest = 0;
00363   int i = 0;
00364   for (i = 0; i < read_sox.length(); i++) {
00365     u_int sock = (u_int)read_sox[i];
00366     if (sock > highest) highest = sock;
00367     FD_SET(sock, &read_list);
00368   }
00369   for (i = 0; i < write_sox.length(); i++) {
00370     u_int sock = (u_int)write_sox[i];
00371     if (sock > highest) highest = sock;
00372     FD_SET(sock, &write_list);
00373   }
00374 
00375   timeval time_out = portable::fill_timeval_ms(timeout);
00376     // timeval has tv_sec=seconds, tv_usec=microseconds.
00377 
00378   // select will tell us about the socket.
00379   int ret = ::select(highest + 1,
00380       (read_sox.length())? &read_list : NIL,
00381       (write_sox.length())? &write_list : NIL,
00382       &exceptions, &time_out);
00383   int error = portable::system_error();
00384 
00385   if (ret == SOCKET_ERROR) {
00386     switch (error) {
00387       // all real errors fall out to the error handling stuff.
00388       case SOCK_EFAULT:  // intentional fall-through.
00389       case SOCK_ENETDOWN:  // intentional fall-through.
00390       case SOCK_EINVAL:  // intentional fall-through.
00391       case SOCK_EINTR:  // intentional fall-through.
00392 #ifdef __WIN32__
00393       case SOCK_NOTINITIALISED:  // intentional fall-through.
00394 #endif
00395       case SOCK_ENOTSOCK:
00396         break;
00397 
00398       // hopefully all these others are bogus errors...
00399       case SOCK_EINPROGRESS:  // intentional fall-through.
00400       case 0:  // intentional fall-through.
00401       default:
00402 #ifdef DEBUG_RAW_SOCKET
00403         LOG("got to weird case, in progress or zero.");
00404 #endif
00405 
00406 //hmmm: fix retd sox?  what's this outcome mean for the list?
00407 
00408         return 0;  // not really an error.
00409     }
00410 #ifdef DEBUG_RAW_SOCKET
00411     LOG(isprintf("sockets had error %d in select: %s.",
00412         error, _stack->tcpip_error_name(error).s()));
00413 #endif
00414 
00415 //hmmm: fix retd sox?  what's this outcome mean for the list?
00416 
00417     return SI_ERRONEOUS;
00418   } else if (!ret) {
00419     // we know of nothing exciting for any of these.
00420     read_sox.reset();
00421     write_sox.reset();
00422     return 0;  // nothing to report.
00423   }
00424 
00425   // if we got to here, then there are some things to report...
00426   // iterate through the lists and check results.
00427   for (int k = 0; k < read_sox.length(); k++) {
00428     u_int socket = read_sox[k];
00429     int interim = analyze_select_result(socket, SELECTING_JUST_READ, read_list,
00430         write_list, exceptions);
00431     if (!interim) {
00432       // nothing happened on that guy.
00433       read_sox.zap(k, k);
00434       k--;  // skip back to before the whack.
00435       continue;
00436     }
00437     to_return |= interim;  // join the results with overall result.
00438   }
00439   for (int p = 0; p < write_sox.length(); p++) {
00440     u_int socket = write_sox[p];
00441     int interim = analyze_select_result(socket, SELECTING_JUST_WRITE, read_list,
00442         write_list, exceptions);
00443     if (!interim) {
00444       // nothing happened on that guy.
00445       write_sox.zap(p, p);
00446       p--;  // skip back to before the whack.
00447       continue;
00448     }
00449     to_return |= interim;  // join the results with overall result.
00450   }
00451 
00452   return to_return;
00453 }
00454 
00455 
00456 
00457 #endif //RAW_SOCKET_IMPLEMENTATION_FILE
00458 

Generated on Fri Nov 21 04:29:15 2008 for HOOPLE Libraries by  doxygen 1.5.1