updates to get builds working on certain platforms...
[feisty_meow.git] / octopi / library / sockets / tcpip_stack.cpp
1 /**
2 *  Name   : tcpip_stack
3 *  Author : Chris Koeritz
4 * Copyright (c) 1991-$now By Author.  This program is free software; you can  *
5 * redistribute it and/or modify it under the terms of the GNU General Public  *
6 * License as published by the Free Software Foundation; either version 2 of   *
7 * the License or (at your option) any later version.  This is online at:      *
8 *     http://www.fsf.org/copyleft/gpl.html                                    *
9 * Please send any updates to: fred@gruntose.com                               *
10 \*****************************************************************************/
11
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>
18 #include <loggers/critical_events.h>
19 #include <loggers/program_wide_logger.h>
20 #include <structures/string_array.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
48 //////////////
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
57 //////////////
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
78 //////////////
79
80 tcpip_stack::tcpip_stack()
81 : _healthy(initialize_tcpip())
82 {}
83
84 tcpip_stack::~tcpip_stack()
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
115 astring tcpip_stack::hostname() const
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
129 astring tcpip_stack::tcpip_error_name(int error_value)
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
192 //////////////
193
194 bool tcpip_stack::enumerate_adapters(string_array &ip_addresses,
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";
204   astring this_host = hostname();
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
240 bool tcpip_stack::enumerate_adapters(machine_uid_array &ip_addresses,
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;
264   byte_array ip(internet_address::ADDRESS_SIZE, make_from.ip_address);
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
285 byte_array tcpip_stack::full_resolve(const astring &hostname,
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") ) {
293     byte_array to_return = internet_address::localhost();
294     full_hostname = "localhost";
295     return to_return;
296   } else if (hostname.iequals("inaddr_any")
297         || hostname.iequals("any-address")) {
298     byte_array to_return = internet_address::nil_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,
325       internet_address::MAXIMUM_HOSTNAME_LENGTH);
326   // copy the ip address into our structure.
327   ip.stuff(internet_address::ADDRESS_SIZE, (abyte *)&to_return.ip_address);
328   return true;
329 }
330
331 astring tcpip_stack::dns_resolve(const astring &hostname) const
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) {
381     case machine_uid::TCPIP_LOCATION: {
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     }
388     case machine_uid::IPX_LOCATION: {
389 ///uhhh...
390 return machine_uid();  // return junk.
391       break;
392     }
393     case machine_uid::NETBIOS_LOCATION: {
394 ///uhhh...
395 return machine_uid();  // return junk.
396       break;
397     }
398     default:
399 //complain.
400       return machine_uid();  // return junk.
401   }
402 }
403
404 internet_address tcpip_stack::fill_and_resolve(const astring &machine_in,
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