feisty meow concerns codebase  2.140
tcpip_stack.cpp
Go to the documentation of this file.
1 
12 #include "internet_address.h"
13 #include "machine_uid.h"
14 #include "raw_socket.h"
15 #include "tcpip_stack.h"
16 
17 #include <basis/functions.h>
21 
22 //#ifdef __UNIX__
23  #include <arpa/inet.h>
24  #include <errno.h>
25  #include <memory.h>
26  #include <netdb.h>
27  #include <sys/ioctl.h>
28  #include <sys/socket.h>
29  #include <sys/time.h>
30  #include <sys/types.h>
31  #include <termios.h>
32  #include <unistd.h>
33 //#endif
34 
35 using namespace basis;
36 using namespace loggers;
37 using namespace structures;
38 using namespace textual;
39 
40 namespace sockets {
41 
42 //#define DEBUG_TCPIP_STACK
43  // turn on for clamor.
44 
45 #undef LOG
46 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
47 
49 
50  /*
51 #ifdef __WIN32__
52  const WORD WINSOCK_VERSION_REQUIRED = 0x0101;
53  // 1.1 version is used by this version of tcp/ip transport.
54 #endif
55 */
56 
58 
59 const char *communication_commons::outcome_name(const outcome &to_name)
60 {
61  switch (to_name.value()) {
62  case NO_CONNECTION: return "NO_CONNECTION";
63  case NO_SERVER: return "NO_SERVER";
64  case NO_ANSWER: return "NO_ANSWER";
65  case SHUTDOWN: return "SHUTDOWN";
66  case ALREADY_SETUP: return "ALREADY_SETUP";
67  case MEDIUM_ERROR: return "MEDIUM_ERROR";
68  case BAD_MODE: return "BAD_MODE";
69  case ALREADY_CONNECTED: return "ALREADY_CONNECTED";
70  case WRONG_ENTITY: return "WRONG_ENTITY";
71  case IPC_ERROR: return "IPC_ERROR";
72  case TOO_NOISY: return "TOO_NOISY";
73  case COMM_ERROR: return "COMM_ERROR";
74  default: return common::outcome_name(to_name);
75  }
76 }
77 
79 
80 tcpip_stack::tcpip_stack()
81 : _healthy(initialize_tcpip())
82 {}
83 
85 {
86  deinitialize_tcpip();
87  _healthy = false;
88 }
89 
90 bool tcpip_stack::initialize_tcpip()
91 {
92  /*
93 #ifdef __WIN32__
94  FUNCDEF("initialize_tcpip");
95  // make sure we have the right version of WinSock available.
96  WORD desired_winsock = WINSOCK_VERSION_REQUIRED;
97  WSADATA startup_data;
98  int error = WSAStartup(desired_winsock, &startup_data);
99  if (error) {
100  LOG(astring("startup error: ") + tcpip_error_name(critical_events::system_error()));
101  return false;
102  }
103 #endif
104 */
105  return true;
106 }
107 
108 void tcpip_stack::deinitialize_tcpip()
109 {
110 /*#ifdef __WIN32__
111  WSACleanup();
112 #endif*/
113 }
114 
116 {
117  FUNCDEF("hostname");
118  char hostname[256];
119  hostname[0] = '\0';
120  // gethostname() finds the name for our tcp/ip host.
121  if (negative(gethostname(hostname, 255))) {
122  LOG(astring(astring::SPRINTF, "gethostname error %s.",
123  tcpip_error_name(critical_events::system_error()).s()));
124  return hostname;
125  }
126  return hostname;
127 }
128 
130 {
131  switch (error_value) {
132  // winsock errors:
133  case SOCK_EINTR: return "EINTR";
134  case SOCK_EBADF: return "EBADF";
135  case SOCK_EACCES: return "EACCES";
136  case SOCK_EFAULT: return "EFAULT";
137  case SOCK_EINVAL: return "EINVAL";
138  case SOCK_EMFILE: return "EMFILE";
139  case SOCK_EWOULDBLOCK: return "EWOULDBLOCK";
140  case SOCK_EINPROGRESS: return "EINPROGRESS";
141  case SOCK_EALREADY: return "EALREADY";
142  case SOCK_ENOTSOCK: return "ENOTSOCK";
143  case SOCK_EDESTADDRREQ: return "EDESTADDRREQ";
144  case SOCK_EMSGSIZE: return "EMSGSIZE";
145  case SOCK_EPROTOTYPE: return "EPROTOTYPE";
146  case SOCK_ENOPROTOOPT: return "ENOPROTOOPT";
147  case SOCK_EPROTONOSUPPORT: return "EPROTONOSUPPORT";
148  case SOCK_ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
149  case SOCK_EOPNOTSUPP: return "EOPNOTSUPP";
150  case SOCK_EPFNOSUPPORT: return "EPFNOSUPPORT";
151  case SOCK_EAFNOSUPPORT: return "EAFNOSUPPORT";
152  case SOCK_EADDRINUSE: return "EADDRINUSE";
153  case SOCK_EADDRNOTAVAIL: return "EADDRNOTAVAIL";
154  case SOCK_ENETDOWN: return "ENETDOWN";
155  case SOCK_ENETUNREACH: return "ENETUNREACH";
156  case SOCK_ENETRESET: return "ENETRESET";
157  case SOCK_ECONNABORTED: return "ECONNABORTED";
158  case SOCK_ECONNRESET: return "ECONNRESET";
159  case SOCK_ENOBUFS: return "ENOBUFS";
160  case SOCK_EISCONN: return "EISCONN";
161  case SOCK_ENOTCONN: return "ENOTCONN";
162  case SOCK_ESHUTDOWN: return "ESHUTDOWN";
163  case SOCK_ETOOMANYREFS: return "ETOOMANYREFS";
164  case SOCK_ETIMEDOUT: return "ETIMEDOUT";
165  case SOCK_ECONNREFUSED: return "ECONNREFUSED";
166  case SOCK_ELOOP: return "ELOOP";
167  case SOCK_ENAMETOOLONG: return "ENAMETOOLONG";
168  case SOCK_EHOSTDOWN: return "EHOSTDOWN";
169  case SOCK_EHOSTUNREACH: return "EHOSTUNREACH";
170  case SOCK_ENOTEMPTY: return "ENOTEMPTY";
171  case SOCK_EUSERS: return "EUSERS";
172  case SOCK_EDQUOT: return "EDQUOT";
173  case SOCK_ESTALE: return "ESTALE";
174  case SOCK_EREMOTE: return "EREMOTE";
175 /* #ifdef __WIN32__
176  case SOCK_EPROCLIM: return "EPROCLIM";
177  case SOCK_SYSNOTREADY: return "SYSNOTREADY";
178  case SOCK_VERNOTSUPPORTED: return "VERNOTSUPPORTED";
179  case SOCK_HOST_NOT_FOUND: return "HOST_NOT_FOUND";
180  case SOCK_TRY_AGAIN: return "TRY_AGAIN";
181  case SOCK_NO_RECOVERY: return "NO_RECOVERY";
182  case SOCK_NO_DATA: return "NO_DATA"; // or NO_ADDRESS.
183  case SOCK_NOTINITIALISED: return "NOTINITIALISED";
184 #endif
185 */
186  }
187 
188  // return a standard OS error...
189  return critical_events::system_error_text(error_value);
190 }
191 
193 
195  bool add_local) const
196 {
197 #ifdef DEBUG_TCPIP_STACK
198  FUNCDEF("enumerate_adapters");
199 #endif
200  ip_addresses.reset();
201  // see if they want to put the local adapter in there.
202  if (add_local)
203  ip_addresses += "127.0.0.1";
205 #ifdef DEBUG_TCPIP_STACK
206  LOG(astring("hostname is \"") + this_host + astring("\"."));
207 #endif
208  if (!this_host) {
209 #ifdef DEBUG_TCPIP_STACK
210  LOG("failed to get the hostname for this machine!");
211 #endif
212  return false;
213  }
214 
215  hostent *host_entry = gethostbyname(this_host.s());
216  if (!host_entry) {
217 #ifdef DEBUG_TCPIP_STACK
218  LOG(astring("failed to get host entry for \"") + this_host + "\".");
219 #endif
220  return false;
221  }
222  for (int adapter_num = 0; /* check is inside loop */; adapter_num++) {
223  in_addr *current_entry = (in_addr *)host_entry->h_addr_list[adapter_num];
224  if (!current_entry) break;
225  char *ip_address = inet_ntoa(*current_entry);
226 #ifdef DEBUG_TCPIP_STACK
227  LOG(astring("current is: ") + astring(ip_address));
228 #endif
229  ip_addresses += ip_address;
230  }
231 
232 #ifdef DEBUG_TCPIP_STACK
233  LOG(astring("read addresses:") + parser_bits::platform_eol_to_chars()
234  + ip_addresses.text_form());
235 #endif
236 
237  return !!ip_addresses.length();
238 }
239 
241  bool add_local) const
242 {
243  ip_addresses.reset();
244  string_array text_list;
245  if (!enumerate_adapters(text_list, add_local))
246  return false;
247  for (int i = 0; i < text_list.length(); i++) {
248  bool worked;
249  internet_address addr_form = fill_and_resolve(text_list[i], 0, worked);
250  if (worked) {
251  hostname().stuff(addr_form.hostname, addr_form.MAXIMUM_HOSTNAME_LENGTH);
252  ip_addresses += addr_form.convert();
253  }
254  }
255  return !!ip_addresses.elements();
256 }
257 
258 sockaddr tcpip_stack::convert(const internet_address &make_from)
259 {
260  FUNCDEF("convert [to sockaddr]");
261  sockaddr_in new_socket; // our socket.
262  memset(&new_socket, 0, sizeof(new_socket)); // clear it out.
263  new_socket.sin_family = AF_INET;
265  ip.stuff(internet_address::ADDRESS_SIZE, (abyte *)&new_socket.sin_addr);
266  new_socket.sin_port = htons(basis::un_short(make_from.port));
267  // possibly unportable conversion to short above.
268  // now we need to return the more generic form of socket address.
269  sockaddr to_return;
270  memset(&to_return, 0, sizeof(to_return));
271  memcpy(&to_return, (sockaddr *)&new_socket, sizeof(new_socket));
272  // sockaddr_in guaranteed to be smaller or equal to sockaddr.
273  return to_return;
274 }
275 
276 internet_address tcpip_stack::convert(const sockaddr &make_from_o)
277 {
278  const sockaddr_in *make_from = (const sockaddr_in *)&make_from_o;
279  byte_array ip(internet_address::ADDRESS_SIZE, (abyte *)&make_from->sin_addr);
280  internet_address to_return;
281  to_return.fill(ip, "", ntohs(make_from->sin_port));
282  return to_return;
283 }
284 
286  astring &full_hostname) const
287 {
288  FUNCDEF("full_resolve");
289  if (!hostname) return byte_array(); // blank hostnames go nowhere.
290  full_hostname.reset();
291  // check first for local host equivalents.
292  if ( hostname.iequals("local") || hostname.iequals("localhost") ) {
294  full_hostname = "localhost";
295  return to_return;
296  } else if (hostname.iequals("inaddr_any")
297  || hostname.iequals("any-address")) {
299  full_hostname = "inaddr_any";
300  return to_return;
301  }
302  // gethostbyname() fills in details about the host, such as its IP address
303  // and full hostname.
304  hostent *machine = gethostbyname(hostname.observe());
305  if (!machine) {
306  LOG(astring(astring::SPRINTF, "gethostbyname error %s.",
307  tcpip_error_name(critical_events::system_error()).s()));
308  return byte_array();
309  }
310  full_hostname = astring(machine->h_name);
311  return byte_array(machine->h_length, (abyte *)machine->h_addr);
312 }
313 
314 bool tcpip_stack::resolve_any(const astring &hostname,
315  internet_address &to_return) const
316 {
317  FUNCDEF("resolve_any");
318  to_return = internet_address();
319  if (!hostname) return false; // blank hostnames go nowhere.
320  astring full_host;
321  byte_array ip = full_resolve(hostname, full_host);
322  if (!ip.length()) return false;
323  // success then. fill out the address object.
324  full_host.stuff(to_return.hostname,
326  // copy the ip address into our structure.
328  return true;
329 }
330 
332 {
333  FUNCDEF("dns_resolve");
334  if (!hostname) return ""; // blank hostnames go nowhere.
335  if (hostname.iequals("local") || hostname.iequals("localhost")) {
336  return "127.0.0.1";
337  } else if (hostname.iequals("inaddr_any")
338  || hostname.iequals("any-address")) {
339  return "0.0.0.0";
340  }
341  // gethostbyname() fills out details about the host, such as its IP address
342  // and full hostname.
343  hostent *machine = gethostbyname(hostname.observe());
344  if (!machine) {
345  return "";
346  }
347  byte_array ip(machine->h_length, (abyte *)machine->h_addr);
348  // copy the ip address into an array for easier manipulation.
349  astring to_return;
350  for (int i = 0; i < ip.length(); i++) {
351  to_return += astring(astring::SPRINTF, "%d", int(ip[i]));
352  if (i != ip.length() - 1) to_return += ".";
353  }
354  return to_return;
355 }
356 
357 /*
358 
359 //decide if this should be kept or not.
360 
361 internet_address tcpip_stack::deprecated_lookup
362  (const byte_array &ip_address)
363 {
364  FUNCDEF("deprecated_lookup");
365  // lookup the client's hostname through DNS.
366  hostent *entry = gethostbyaddr((char *)ip_address.observe(),
367  ip_address.length(), PF_INET);
368  astring hostname = "unknown_host";
369  if (entry) hostname = entry->h_name;
370 
371  internet_address to_return;
372  to_return.fill(ip_address, hostname, 0);
373  // the zero is above because we don't know the port here.
374  return to_return;
375 }
376 */
377 
378 machine_uid tcpip_stack::this_host(int location_type) const
379 {
380  switch (location_type) {
382  astring host = hostname();
383  astring full_host;
384  byte_array ip = full_resolve(host, full_host);
385  if (!ip.length()) return machine_uid(); // failure.
386  return internet_machine_uid(full_host, ip);
387  }
390 return machine_uid(); // return junk.
391  break;
392  }
395 return machine_uid(); // return junk.
396  break;
397  }
398  default:
399 //complain.
400  return machine_uid(); // return junk.
401  }
402 }
403 
405  int port, bool &worked) const
406 {
407  internet_address to_return;
408  astring machine = machine_in;
409  machine.to_lower();
410  if (!machine) machine = "local"; // assume they mean this machine.
411  bool resolved = false; // true if we know the name.
412  byte_array ip_addr; // the ip address we're guessing/decoding/looking up.
413  bool is_ip = false; // set to true if we have the IP address in ip_addr.
414  if (machine.iequals("local") || machine.iequals("localhost")) {
415  // handle our special flag and the normal localhost machine type.
416  machine = "localhost";
417  ip_addr = internet_address::localhost();
418  is_ip = true; // we have the address already.
419  resolved = true; // we know the name also.
420  } else if (machine.iequals("inaddr_any")
421  || machine.iequals("any-address")) {
422  // handle the any address case.
423  machine = "inaddr_any";
424  ip_addr = internet_address::nil_address();
425  is_ip = true; // we have the address already.
426  resolved = true; // we know the name also.
427  } else {
428  // well, we now are just going to guess about it being an IP address.
429  bool all_zeros;
430  is_ip = internet_address::is_valid_internet_address(machine, ip_addr,
431  all_zeros);
432  }
433  if (is_ip) {
434  // we try to fill in the hostname if given ip only.
435 //hmmm: use reverse dns to get the machine name for real!
436 // for cases where we really had an ip address, using the machine there
437 // would be a mistake.
438  if (resolved) to_return.fill(ip_addr, machine, port);
439  else to_return.fill(ip_addr, astring::empty_string(), port);
440  resolved = true; // claim we are happy with it now.
441  } else {
442  // we try to fill in the ip address for the host.
443  astring full_host;
444  ip_addr = full_resolve(machine, full_host);
445  if (ip_addr.length()) {
446  to_return.fill(ip_addr, machine, port);
447  resolved = true;
448  }
449  }
450  worked = resolved;
451  return to_return;
452 }
453 
454 } //namespace.
455 
456 
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
Definition: array.h:349
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
outcome stuff(int length, contents *to_stuff) const
Copies at most "length" elements from this into the array "to_stuff".
Definition: array.h:476
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
bool iequals(const astring &that) const
returns true if this is case-insensitively equal to "that".
Definition: astring.cpp:565
void stuff(char *to_stuff, int count) const
a synonym for copy().
Definition: astring.h:216
void reset()
clears out the contents string.
Definition: astring.h:202
virtual const char * observe() const
observes the underlying pointer to the zero-terminated string.
Definition: astring.cpp:140
void to_lower()
to_lower modifies "this" by replacing capitals with lower-case.
Definition: astring.cpp:528
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
static const char * outcome_name(const outcome &to_name)
Returns a string representation of the outcome "to_name".
Outcomes describe the state of completion for an operation.
Definition: outcome.h:31
int value() const
Definition: outcome.h:51
this type of address describes a destination out on the internet.
machine_uid convert() const
static const basis::byte_array & localhost()
void fill(const basis::byte_array &ip_address, const basis::astring &host, int port)
static const basis::byte_array & nil_address()
char hostname[MAXIMUM_HOSTNAME_LENGTH]
static bool is_valid_internet_address(const basis::astring &to_check, basis::byte_array &ip_form, bool &all_zeros)
static basis::astring tcpip_error_name(int error_value)
internet_address fill_and_resolve(const basis::astring &machine, int port, bool &worked) const
basis::byte_array full_resolve(const basis::astring &hostname, basis::astring &full_host) const
basis::astring hostname() const
machine_uid this_host(int location_type) const
bool resolve_any(const basis::astring &name, internet_address &resolved) const
basis::astring dns_resolve(const basis::astring &hostname) const
bool enumerate_adapters(structures::string_array &ip_addresses, bool add_local=false) const
static sockaddr convert(const internet_address &to_convert)
An array of strings with some additional helpful methods.
Definition: string_array.h:32
basis::astring text_form() const
A synonym for the text_format() method.
Definition: string_array.h:71
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
unsigned char abyte
A fairly important unit which is seldom defined...
Definition: definitions.h:51
unsigned short un_short
Abbreviated name for unsigned short integers.
Definition: definitions.h:64
bool negative(const type &a)
negative returns true if "a" is less than zero.
Definition: functions.h:43
A logger that sends to the console screen using the standard output device.
Provides access to the operating system's socket methods.
Definition: base_address.h:26
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#define SOCK_EDESTADDRREQ
#define SOCK_ENOBUFS
#define SOCK_EDQUOT
#define SOCK_EPFNOSUPPORT
#define SOCK_EOPNOTSUPP
#define SOCK_ETOOMANYREFS
#define SOCK_ESHUTDOWN
#define SOCK_ENAMETOOLONG
#define SOCK_EALREADY
#define SOCK_ENETUNREACH
#define SOCK_ESOCKTNOSUPPORT
#define SOCK_EMSGSIZE
#define SOCK_EAFNOSUPPORT
#define SOCK_ECONNRESET
#define SOCK_EADDRNOTAVAIL
#define SOCK_EHOSTUNREACH
#define SOCK_ESTALE
#define SOCK_EACCES
#define SOCK_EINVAL
#define SOCK_ECONNABORTED
#define SOCK_ETIMEDOUT
#define SOCK_EPROTONOSUPPORT
#define SOCK_ENETDOWN
#define SOCK_EINPROGRESS
#define SOCK_EFAULT
#define SOCK_EPROTOTYPE
#define SOCK_ECONNREFUSED
#define SOCK_EMFILE
#define SOCK_EBADF
#define SOCK_ELOOP
#define SOCK_EWOULDBLOCK
#define SOCK_EHOSTDOWN
#define SOCK_EADDRINUSE
#define SOCK_ENETRESET
#define SOCK_ENOTSOCK
#define SOCK_EUSERS
#define SOCK_ENOTCONN
#define SOCK_EISCONN
#define SOCK_ENOTEMPTY
#define SOCK_EREMOTE
#define SOCK_EINTR
#define SOCK_ENOPROTOOPT
#define LOG(to_print)
Definition: tcpip_stack.cpp:46