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