tcpip_stack.cpp

Go to the documentation of this file.
00001 #ifndef TCPIP_STACK_IMPLEMENTATION_FILE
00002 #define TCPIP_STACK_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : tcpip_stack                                                       *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *  Purpose:                                                                   *
00010 *                                                                             *
00011 *     Useful functions for dealing with WinSock.                              *
00012 *                                                                             *
00013 *******************************************************************************
00014 * Copyright (c) 1991-$now By Author.  This program is free software; you can  *
00015 * redistribute it and/or modify it under the terms of the GNU General Public  *
00016 * License as published by the Free Software Foundation; either version 2 of   *
00017 * the License or (at your option) any later version.  This is online at:      *
00018 *     http://www.fsf.org/copyleft/gpl.html                                    *
00019 * Please send any updates to: fred@gruntose.com                               *
00020 \*****************************************************************************/
00021 
00022 #include "address.h"
00023 #include "imp_sockets.h"
00024 #include "machine_uid.h"
00025 #include "tcpip_definitions.h"
00026 #include "tcpip_stack.h"
00027 
00028 #include <basis/function.h>
00029 #include <basis/portable.h>
00030 #include <basis/string_array.h>
00031 
00032 #ifdef __UNIX__
00033   #include <arpa/inet.h>
00034   #include <errno.h>
00035   #include <memory.h>
00036   #include <netdb.h>
00037   #include <sys/ioctl.h>
00038   #include <sys/socket.h>
00039   #include <sys/time.h>
00040   #include <sys/types.h>
00041   #include <termios.h>
00042   #include <unistd.h>
00043 #endif
00044 
00045 //#define DEBUG_TCPIP_STACK
00046   // turn on for clamor.
00047 
00048 #undef LOG
00049 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
00050 
00052 
00053 #ifdef __WIN32__
00054   const WORD WINSOCK_VERSION_REQUIRED = 0x0101;
00055     // 1.1 version is used by this version of tcp/ip transport.
00056 #endif
00057 
00059 
00060 tcpip_stack::tcpip_stack()
00061 : _healthy(initialize_tcpip())
00062 {}
00063 
00064 tcpip_stack::~tcpip_stack()
00065 {
00066   deinitialize_tcpip();
00067   _healthy = false;
00068 }
00069 
00070 bool tcpip_stack::initialize_tcpip()
00071 {
00072   #undef static_class_name
00073   #define static_class_name() "tcpip_stack"
00074   FUNCDEF("initialize_tcpip");
00075 #ifdef __WIN32__
00076   // make sure we have the right version of WinSock available.
00077   WORD desired_winsock = WINSOCK_VERSION_REQUIRED;
00078   WSADATA startup_data;
00079   int error = WSAStartup(desired_winsock, &startup_data);
00080   if (error) {
00081     LOG(istring("startup error: ") + tcpip_error_name(portable::system_error()));
00082     return false;
00083   }
00084 #endif
00085   return true;
00086   #undef static_class_name
00087 }
00088 
00089 void tcpip_stack::deinitialize_tcpip()
00090 {
00091 #ifdef __WIN32__
00092   WSACleanup();
00093 #endif
00094 }
00095 
00096 istring tcpip_stack::hostname() const
00097 {
00098   FUNCDEF("hostname");
00099   char hostname[256];
00100   hostname[0] = '\0';
00101   // gethostname() finds the name for our tcp/ip host.
00102   if (negative(gethostname(hostname, 255))) {
00103     LOG(istring(istring::SPRINTF, "gethostname error %s.",
00104         tcpip_error_name(portable::system_error()).s()));
00105     return hostname;
00106   }
00107   return hostname;
00108 }
00109 
00110 istring tcpip_stack::tcpip_error_name(int error_value)
00111 {
00112   switch (error_value) {
00113     // winsock errors:
00114     case SOCK_EINTR: return "EINTR";
00115     case SOCK_EBADF: return "EBADF";
00116     case SOCK_EACCES: return "EACCES";
00117     case SOCK_EFAULT: return "EFAULT";
00118     case SOCK_EINVAL: return "EINVAL";
00119     case SOCK_EMFILE: return "EMFILE";
00120     case SOCK_EWOULDBLOCK: return "EWOULDBLOCK";
00121     case SOCK_EINPROGRESS: return "EINPROGRESS";
00122     case SOCK_EALREADY: return "EALREADY";
00123     case SOCK_ENOTSOCK: return "ENOTSOCK";
00124     case SOCK_EDESTADDRREQ: return "EDESTADDRREQ";
00125     case SOCK_EMSGSIZE: return "EMSGSIZE";
00126     case SOCK_EPROTOTYPE: return "EPROTOTYPE";
00127     case SOCK_ENOPROTOOPT: return "ENOPROTOOPT";
00128     case SOCK_EPROTONOSUPPORT: return "EPROTONOSUPPORT";
00129     case SOCK_ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
00130     case SOCK_EOPNOTSUPP: return "EOPNOTSUPP";
00131     case SOCK_EPFNOSUPPORT: return "EPFNOSUPPORT";
00132     case SOCK_EAFNOSUPPORT: return "EAFNOSUPPORT";
00133     case SOCK_EADDRINUSE: return "EADDRINUSE";
00134     case SOCK_EADDRNOTAVAIL: return "EADDRNOTAVAIL";
00135     case SOCK_ENETDOWN: return "ENETDOWN";
00136     case SOCK_ENETUNREACH: return "ENETUNREACH";
00137     case SOCK_ENETRESET: return "ENETRESET";
00138     case SOCK_ECONNABORTED: return "ECONNABORTED";
00139     case SOCK_ECONNRESET: return "ECONNRESET";
00140     case SOCK_ENOBUFS: return "ENOBUFS";
00141     case SOCK_EISCONN: return "EISCONN";
00142     case SOCK_ENOTCONN: return "ENOTCONN";
00143     case SOCK_ESHUTDOWN: return "ESHUTDOWN";
00144     case SOCK_ETOOMANYREFS: return "ETOOMANYREFS";
00145     case SOCK_ETIMEDOUT: return "ETIMEDOUT";
00146     case SOCK_ECONNREFUSED: return "ECONNREFUSED";
00147     case SOCK_ELOOP: return "ELOOP";
00148     case SOCK_ENAMETOOLONG: return "ENAMETOOLONG";
00149     case SOCK_EHOSTDOWN: return "EHOSTDOWN";
00150     case SOCK_EHOSTUNREACH: return "EHOSTUNREACH";
00151     case SOCK_ENOTEMPTY: return "ENOTEMPTY";
00152     case SOCK_EUSERS: return "EUSERS";
00153     case SOCK_EDQUOT: return "EDQUOT";
00154     case SOCK_ESTALE: return "ESTALE";
00155     case SOCK_EREMOTE: return "EREMOTE";
00156 #ifdef __WIN32__
00157     case SOCK_EPROCLIM: return "EPROCLIM";
00158     case SOCK_SYSNOTREADY: return "SYSNOTREADY";
00159     case SOCK_VERNOTSUPPORTED: return "VERNOTSUPPORTED";
00160     case SOCK_HOST_NOT_FOUND: return "HOST_NOT_FOUND";
00161     case SOCK_TRY_AGAIN: return "TRY_AGAIN";
00162     case SOCK_NO_RECOVERY: return "NO_RECOVERY";
00163     case SOCK_NO_DATA: return "NO_DATA";  // or NO_ADDRESS.
00164     case SOCK_NOTINITIALISED: return "NOTINITIALISED";
00165 #endif
00166   }
00167 
00168   // return a standard OS error...
00169   return portable::system_error_text(error_value);
00170 }
00171 
00173 
00174 bool tcpip_stack::enumerate_adapters(string_array &ip_addresses,
00175     bool add_local) const
00176 {
00177   FUNCDEF("enumerate_adapters");
00178   ip_addresses.reset();
00179   // see if they want to put the local adapter in there.
00180   if (add_local)
00181     ip_addresses += "127.0.0.1";
00183 
00184 
00186   istring this_host = hostname();
00187 #ifdef DEBUG_TCPIP_STACK
00188   LOG(istring("hostname is \"") + this_host + istring("\"."));
00189 #endif
00190   if (!this_host) {
00191 #ifdef DEBUG_TCPIP_STACK
00192     LOG("failed to get the hostname for this machine!");
00193 #endif
00194     return false;
00195   }
00196 
00197   hostent *host_entry = gethostbyname(this_host.s());
00198   if (!host_entry) {
00199 #ifdef DEBUG_TCPIP_STACK
00200     LOG(istring("failed to get host entry for \"") + this_host + "\".");
00201 #endif
00202     return false;
00203   } 
00204   for (int adapter_num = 0; /* check is inside loop */; adapter_num++) {
00205     in_addr *current_entry = (in_addr *)host_entry->h_addr_list[adapter_num];
00206     if (!current_entry) break;
00207     char *ip_address = inet_ntoa(*current_entry);
00208 #ifdef DEBUG_TCPIP_STACK
00209     LOG(istring("current is: ") + istring(ip_address));
00210 #endif
00211     ip_addresses += ip_address;
00212   }
00214 
00215 #ifdef DEBUG_TCPIP_STACK
00216   LOG(istring("read addresses:") + log_base::platform_ending()
00217       + ip_addresses.text_form());
00218 #endif
00219 
00220   return !!ip_addresses.length();
00221 }
00222 
00223 bool tcpip_stack::enumerate_adapters(machine_uid_array &ip_addresses,
00224     bool add_local) const
00225 {
00226   ip_addresses.reset();
00227   string_array text_list;
00228   if (!enumerate_adapters(text_list, add_local))
00229     return false;
00230   for (int i = 0; i < text_list.length(); i++) {
00231     bool worked;
00232     internet_address addr_form = fill_and_resolve(text_list[i], 0, worked);
00233     if (worked) {
00234       hostname().stuff(addr_form.hostname, addr_form.MAXIMUM_HOSTNAME_LENGTH);
00235       ip_addresses += addr_form.convert();
00236     }
00237   }
00238   return !!ip_addresses.elements();
00239 }
00240 
00241 sockaddr tcpip_stack::convert(const internet_address &make_from)
00242 {
00243   FUNCDEF("convert [to sockaddr]");
00244   sockaddr_in new_socket;  // our socket.
00245   memset(&new_socket, 0, sizeof(new_socket));  // clear it out.
00246   new_socket.sin_family = AF_INET;
00247   byte_array ip(internet_address::ADDRESS_SIZE, make_from.ip_address);
00248   ip.stuff(internet_address::ADDRESS_SIZE, (byte *)&new_socket.sin_addr);
00249   new_socket.sin_port = htons(u_short(make_from.port));
00250     // possibly unportable conversion to short above.
00251   // now we need to return the more generic form of socket address.
00252   sockaddr to_return;
00253   memset(&to_return, 0, sizeof(to_return));
00254   memory_assign(&to_return, (sockaddr *)&new_socket, sizeof(new_socket));
00255     // sockaddr_in guaranteed to be smaller or equal to sockaddr.
00256   return to_return;
00257 }
00258 
00259 internet_address tcpip_stack::convert(const sockaddr &make_from_o)
00260 {
00261   const sockaddr_in *make_from = (const sockaddr_in *)&make_from_o;
00262   byte_array ip(internet_address::ADDRESS_SIZE, (byte *)&make_from->sin_addr);
00263   internet_address to_return;
00264   to_return.fill(ip, "", ntohs(make_from->sin_port));
00265   return to_return;
00266 }
00267 
00268 byte_array tcpip_stack::full_resolve(const istring &hostname,
00269     istring &full_hostname) const
00270 {
00271   FUNCDEF("full_resolve");
00272   if (!hostname) return byte_array();  // blank hostnames go nowhere.
00273   full_hostname.reset();
00274   // check first for local host equivalents.
00275   if ( hostname.iequals("local") || hostname.iequals("localhost") ) {
00276     byte_array to_return = internet_address::localhost();
00277     full_hostname = "localhost";
00278     return to_return;
00279   } else if (hostname.iequals("inaddr_any")
00280         || hostname.iequals("any-address")) {
00281     byte_array to_return = internet_address::nil_address();
00282     full_hostname = "inaddr_any";
00283     return to_return;
00284   }
00285   // gethostbyname() fills in details about the host, such as its IP address
00286   // and full hostname.
00287   hostent *machine = gethostbyname(hostname.observe());
00288   if (!machine) {
00289     LOG(istring(istring::SPRINTF, "gethostbyname error %s.",
00290         tcpip_error_name(portable::system_error()).s()));
00291     return byte_array();
00292   }
00293   full_hostname = istring(machine->h_name);
00294   return byte_array(machine->h_length, (byte *)machine->h_addr);
00295 }
00296 
00297 bool tcpip_stack::resolve_any(const istring &hostname,
00298     internet_address &to_return) const
00299 {
00300   FUNCDEF("resolve_any");
00301   to_return = internet_address();
00302   if (!hostname) return false;  // blank hostnames go nowhere.
00303   istring full_host;
00304   byte_array ip = full_resolve(hostname, full_host);
00305   if (!ip.length()) return false;
00306   // success then.  fill out the address object.
00307   full_host.stuff(to_return.hostname,
00308       internet_address::MAXIMUM_HOSTNAME_LENGTH);
00309   // copy the ip address into our structure.
00310   ip.stuff(internet_address::ADDRESS_SIZE, (byte *)&to_return.ip_address);
00311   return true;
00312 }
00313 
00314 istring tcpip_stack::dns_resolve(const istring &hostname) const
00315 {
00316   FUNCDEF("dns_resolve");
00317   if (!hostname) return "";  // blank hostnames go nowhere.
00318   if (hostname.iequals("local") || hostname.iequals("localhost")) {
00319     return "127.0.0.1";
00320   } else if (hostname.iequals("inaddr_any")
00321       || hostname.iequals("any-address")) {
00322     return "0.0.0.0";
00323   }
00324   // gethostbyname() fills out details about the host, such as its IP address
00325   // and full hostname.
00326   hostent *machine = gethostbyname(hostname.observe());
00327   if (!machine) {
00328     return "";
00329   }
00330   byte_array ip(machine->h_length, (byte *)machine->h_addr);
00331     // copy the ip address into an array for easier manipulation.
00332   istring to_return;
00333   for (int i = 0; i < ip.length(); i++) {
00334     to_return += istring(istring::SPRINTF, "%d", int(ip[i]));
00335     if (i != ip.length() - 1) to_return += ".";
00336   }
00337   return to_return;
00338 }
00339 
00340 /*
00341 
00342 //decide if this should be kept or not.
00343 
00344 internet_address tcpip_stack::deprecated_lookup
00345     (const byte_array &ip_address)
00346 {
00347   FUNCDEF("deprecated_lookup");
00348   // lookup the client's hostname through DNS.
00349   hostent *entry = gethostbyaddr((char *)ip_address.observe(),
00350       ip_address.length(), PF_INET);
00351   istring hostname = "unknown_host";
00352   if (entry) hostname = entry->h_name;
00353 
00354   internet_address to_return;
00355   to_return.fill(ip_address, hostname, 0);
00356     // the zero is above because we don't know the port here.
00357   return to_return;
00358 }
00359 */
00360 
00361 machine_uid tcpip_stack::this_host(int location_type) const
00362 {
00363   switch (location_type) {
00364     case machine_uid::TCPIP_LOCATION: {
00365       istring host = hostname();
00366       istring full_host;
00367       byte_array ip = full_resolve(host, full_host);
00368       if (!ip.length()) return machine_uid();  // failure.
00369       return internet_machine_uid(full_host, ip);
00370     }
00371     case machine_uid::IPX_LOCATION: {
00373 return machine_uid();  // return junk.
00374       break;
00375     }
00376     case machine_uid::NETBIOS_LOCATION: {
00378 return machine_uid();  // return junk.
00379       break;
00380     }
00381     default:
00382 //complain.
00383       return machine_uid();  // return junk.
00384   }
00385 }
00386 
00387 internet_address tcpip_stack::fill_and_resolve(const istring &machine_in,
00388     int port, bool &worked) const
00389 {
00390   internet_address to_return;
00391   istring machine = machine_in;
00392   machine.to_lower();
00393   if (!machine) machine = "local";  // assume they mean this machine.
00394   bool resolved = false;  // true if we know the name.
00395   byte_array ip_addr;  // the ip address we're guessing/decoding/looking up.
00396   bool is_ip = false;  // set to true if we have the IP address in ip_addr.
00397   if (machine.iequals("local") || machine.iequals("localhost")) {
00398     // handle our special flag and the normal localhost machine type.
00399     machine = "localhost";
00400     ip_addr = internet_address::localhost();
00401     is_ip = true;  // we have the address already.
00402     resolved = true;  // we know the name also.
00403   } else if (machine.iequals("inaddr_any")
00404       || machine.iequals("any-address")) {
00405     // handle the any address case.
00406     machine = "inaddr_any";
00407     ip_addr = internet_address::nil_address();
00408     is_ip = true;  // we have the address already.
00409     resolved = true;  // we know the name also.
00410   } else {
00411     // well, we now are just going to guess about it being an IP address.
00412     bool all_zeros;
00413     is_ip = internet_address::is_valid_internet_address(machine, ip_addr,
00414         all_zeros);
00415   }
00416   if (is_ip) {
00417     // we try to fill in the hostname if given ip only.
00418 //hmmm: use reverse dns to get the machine name for real!
00419 //      for cases where we really had an ip address, using the machine there
00420 //      would be a mistake.
00421     if (resolved) to_return.fill(ip_addr, machine, port);
00422     else to_return.fill(ip_addr, istring::empty_string(), port);
00423     resolved = true;  // claim we are happy with it now.
00424   } else {
00425     // we try to fill in the ip address for the host.
00426     istring full_host;
00427     ip_addr = full_resolve(machine, full_host);
00428     if (ip_addr.length()) {
00429       to_return.fill(ip_addr, machine, port);
00430       resolved = true;
00431     }
00432   }
00433   worked = resolved;
00434   return to_return;
00435 }
00436 
00437 
00438 #endif //TCPIP_STACK_IMPLEMENTATION_FILE
00439 

Generated on Fri Nov 28 04:28:58 2008 for HOOPLE Libraries by  doxygen 1.5.1