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 \*****************************************************************************/
12 #include "internet_address.h"
13 #include "machine_uid.h"
14 #include "raw_socket.h"
15 #include "tcpip_stack.h"
17 #include <basis/functions.h>
18 #include <loggers/critical_events.h>
19 #include <loggers/program_wide_logger.h>
20 #include <structures/string_array.h>
23 #include <arpa/inet.h>
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
30 #include <sys/types.h>
35 using namespace basis;
36 using namespace loggers;
37 using namespace structures;
38 using namespace textual;
42 //#define DEBUG_TCPIP_STACK
43 // turn on for clamor.
46 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
51 const WORD WINSOCK_VERSION_REQUIRED = 0x0101;
52 // 1.1 version is used by this version of tcp/ip transport.
57 const char *communication_commons::outcome_name(const outcome &to_name)
59 switch (to_name.value()) {
60 case NO_CONNECTION: return "NO_CONNECTION";
61 case NO_SERVER: return "NO_SERVER";
62 case NO_ANSWER: return "NO_ANSWER";
63 case SHUTDOWN: return "SHUTDOWN";
64 case ALREADY_SETUP: return "ALREADY_SETUP";
65 case MEDIUM_ERROR: return "MEDIUM_ERROR";
66 case BAD_MODE: return "BAD_MODE";
67 case ALREADY_CONNECTED: return "ALREADY_CONNECTED";
68 case WRONG_ENTITY: return "WRONG_ENTITY";
69 case IPC_ERROR: return "IPC_ERROR";
70 case TOO_NOISY: return "TOO_NOISY";
71 case COMM_ERROR: return "COMM_ERROR";
72 default: return common::outcome_name(to_name);
78 tcpip_stack::tcpip_stack()
79 : _healthy(initialize_tcpip())
82 tcpip_stack::~tcpip_stack()
88 bool tcpip_stack::initialize_tcpip()
91 FUNCDEF("initialize_tcpip");
92 // make sure we have the right version of WinSock available.
93 WORD desired_winsock = WINSOCK_VERSION_REQUIRED;
95 int error = WSAStartup(desired_winsock, &startup_data);
97 LOG(astring("startup error: ") + tcpip_error_name(critical_events::system_error()));
104 void tcpip_stack::deinitialize_tcpip()
111 astring tcpip_stack::hostname() const
116 // gethostname() finds the name for our tcp/ip host.
117 if (negative(gethostname(hostname, 255))) {
118 LOG(astring(astring::SPRINTF, "gethostname error %s.",
119 tcpip_error_name(critical_events::system_error()).s()));
125 astring tcpip_stack::tcpip_error_name(int error_value)
127 switch (error_value) {
129 case SOCK_EINTR: return "EINTR";
130 case SOCK_EBADF: return "EBADF";
131 case SOCK_EACCES: return "EACCES";
132 case SOCK_EFAULT: return "EFAULT";
133 case SOCK_EINVAL: return "EINVAL";
134 case SOCK_EMFILE: return "EMFILE";
135 case SOCK_EWOULDBLOCK: return "EWOULDBLOCK";
136 case SOCK_EINPROGRESS: return "EINPROGRESS";
137 case SOCK_EALREADY: return "EALREADY";
138 case SOCK_ENOTSOCK: return "ENOTSOCK";
139 case SOCK_EDESTADDRREQ: return "EDESTADDRREQ";
140 case SOCK_EMSGSIZE: return "EMSGSIZE";
141 case SOCK_EPROTOTYPE: return "EPROTOTYPE";
142 case SOCK_ENOPROTOOPT: return "ENOPROTOOPT";
143 case SOCK_EPROTONOSUPPORT: return "EPROTONOSUPPORT";
144 case SOCK_ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
145 case SOCK_EOPNOTSUPP: return "EOPNOTSUPP";
146 case SOCK_EPFNOSUPPORT: return "EPFNOSUPPORT";
147 case SOCK_EAFNOSUPPORT: return "EAFNOSUPPORT";
148 case SOCK_EADDRINUSE: return "EADDRINUSE";
149 case SOCK_EADDRNOTAVAIL: return "EADDRNOTAVAIL";
150 case SOCK_ENETDOWN: return "ENETDOWN";
151 case SOCK_ENETUNREACH: return "ENETUNREACH";
152 case SOCK_ENETRESET: return "ENETRESET";
153 case SOCK_ECONNABORTED: return "ECONNABORTED";
154 case SOCK_ECONNRESET: return "ECONNRESET";
155 case SOCK_ENOBUFS: return "ENOBUFS";
156 case SOCK_EISCONN: return "EISCONN";
157 case SOCK_ENOTCONN: return "ENOTCONN";
158 case SOCK_ESHUTDOWN: return "ESHUTDOWN";
159 case SOCK_ETOOMANYREFS: return "ETOOMANYREFS";
160 case SOCK_ETIMEDOUT: return "ETIMEDOUT";
161 case SOCK_ECONNREFUSED: return "ECONNREFUSED";
162 case SOCK_ELOOP: return "ELOOP";
163 case SOCK_ENAMETOOLONG: return "ENAMETOOLONG";
164 case SOCK_EHOSTDOWN: return "EHOSTDOWN";
165 case SOCK_EHOSTUNREACH: return "EHOSTUNREACH";
166 case SOCK_ENOTEMPTY: return "ENOTEMPTY";
167 case SOCK_EUSERS: return "EUSERS";
168 case SOCK_EDQUOT: return "EDQUOT";
169 case SOCK_ESTALE: return "ESTALE";
170 case SOCK_EREMOTE: return "EREMOTE";
172 case SOCK_EPROCLIM: return "EPROCLIM";
173 case SOCK_SYSNOTREADY: return "SYSNOTREADY";
174 case SOCK_VERNOTSUPPORTED: return "VERNOTSUPPORTED";
175 case SOCK_HOST_NOT_FOUND: return "HOST_NOT_FOUND";
176 case SOCK_TRY_AGAIN: return "TRY_AGAIN";
177 case SOCK_NO_RECOVERY: return "NO_RECOVERY";
178 case SOCK_NO_DATA: return "NO_DATA"; // or NO_ADDRESS.
179 case SOCK_NOTINITIALISED: return "NOTINITIALISED";
183 // return a standard OS error...
184 return critical_events::system_error_text(error_value);
189 bool tcpip_stack::enumerate_adapters(string_array &ip_addresses,
190 bool add_local) const
192 #ifdef DEBUG_TCPIP_STACK
193 FUNCDEF("enumerate_adapters");
195 ip_addresses.reset();
196 // see if they want to put the local adapter in there.
198 ip_addresses += "127.0.0.1";
199 astring this_host = hostname();
200 #ifdef DEBUG_TCPIP_STACK
201 LOG(astring("hostname is \"") + this_host + astring("\"."));
204 #ifdef DEBUG_TCPIP_STACK
205 LOG("failed to get the hostname for this machine!");
210 hostent *host_entry = gethostbyname(this_host.s());
212 #ifdef DEBUG_TCPIP_STACK
213 LOG(astring("failed to get host entry for \"") + this_host + "\".");
217 for (int adapter_num = 0; /* check is inside loop */; adapter_num++) {
218 in_addr *current_entry = (in_addr *)host_entry->h_addr_list[adapter_num];
219 if (!current_entry) break;
220 char *ip_address = inet_ntoa(*current_entry);
221 #ifdef DEBUG_TCPIP_STACK
222 LOG(astring("current is: ") + astring(ip_address));
224 ip_addresses += ip_address;
227 #ifdef DEBUG_TCPIP_STACK
228 LOG(astring("read addresses:") + parser_bits::platform_eol_to_chars()
229 + ip_addresses.text_form());
232 return !!ip_addresses.length();
235 bool tcpip_stack::enumerate_adapters(machine_uid_array &ip_addresses,
236 bool add_local) const
238 ip_addresses.reset();
239 string_array text_list;
240 if (!enumerate_adapters(text_list, add_local))
242 for (int i = 0; i < text_list.length(); i++) {
244 internet_address addr_form = fill_and_resolve(text_list[i], 0, worked);
246 hostname().stuff(addr_form.hostname, addr_form.MAXIMUM_HOSTNAME_LENGTH);
247 ip_addresses += addr_form.convert();
250 return !!ip_addresses.elements();
253 sockaddr tcpip_stack::convert(const internet_address &make_from)
255 FUNCDEF("convert [to sockaddr]");
256 sockaddr_in new_socket; // our socket.
257 memset(&new_socket, 0, sizeof(new_socket)); // clear it out.
258 new_socket.sin_family = AF_INET;
259 byte_array ip(internet_address::ADDRESS_SIZE, make_from.ip_address);
260 ip.stuff(internet_address::ADDRESS_SIZE, (abyte *)&new_socket.sin_addr);
261 new_socket.sin_port = htons(basis::un_short(make_from.port));
262 // possibly unportable conversion to short above.
263 // now we need to return the more generic form of socket address.
265 memset(&to_return, 0, sizeof(to_return));
266 memcpy(&to_return, (sockaddr *)&new_socket, sizeof(new_socket));
267 // sockaddr_in guaranteed to be smaller or equal to sockaddr.
271 internet_address tcpip_stack::convert(const sockaddr &make_from_o)
273 const sockaddr_in *make_from = (const sockaddr_in *)&make_from_o;
274 byte_array ip(internet_address::ADDRESS_SIZE, (abyte *)&make_from->sin_addr);
275 internet_address to_return;
276 to_return.fill(ip, "", ntohs(make_from->sin_port));
280 byte_array tcpip_stack::full_resolve(const astring &hostname,
281 astring &full_hostname) const
283 FUNCDEF("full_resolve");
284 if (!hostname) return byte_array(); // blank hostnames go nowhere.
285 full_hostname.reset();
286 // check first for local host equivalents.
287 if ( hostname.iequals("local") || hostname.iequals("localhost") ) {
288 byte_array to_return = internet_address::localhost();
289 full_hostname = "localhost";
291 } else if (hostname.iequals("inaddr_any")
292 || hostname.iequals("any-address")) {
293 byte_array to_return = internet_address::nil_address();
294 full_hostname = "inaddr_any";
297 // gethostbyname() fills in details about the host, such as its IP address
298 // and full hostname.
299 hostent *machine = gethostbyname(hostname.observe());
301 LOG(astring(astring::SPRINTF, "gethostbyname error %s.",
302 tcpip_error_name(critical_events::system_error()).s()));
305 full_hostname = astring(machine->h_name);
306 return byte_array(machine->h_length, (abyte *)machine->h_addr);
309 bool tcpip_stack::resolve_any(const astring &hostname,
310 internet_address &to_return) const
312 FUNCDEF("resolve_any");
313 to_return = internet_address();
314 if (!hostname) return false; // blank hostnames go nowhere.
316 byte_array ip = full_resolve(hostname, full_host);
317 if (!ip.length()) return false;
318 // success then. fill out the address object.
319 full_host.stuff(to_return.hostname,
320 internet_address::MAXIMUM_HOSTNAME_LENGTH);
321 // copy the ip address into our structure.
322 ip.stuff(internet_address::ADDRESS_SIZE, (abyte *)&to_return.ip_address);
326 astring tcpip_stack::dns_resolve(const astring &hostname) const
328 FUNCDEF("dns_resolve");
329 if (!hostname) return ""; // blank hostnames go nowhere.
330 if (hostname.iequals("local") || hostname.iequals("localhost")) {
332 } else if (hostname.iequals("inaddr_any")
333 || hostname.iequals("any-address")) {
336 // gethostbyname() fills out details about the host, such as its IP address
337 // and full hostname.
338 hostent *machine = gethostbyname(hostname.observe());
342 byte_array ip(machine->h_length, (abyte *)machine->h_addr);
343 // copy the ip address into an array for easier manipulation.
345 for (int i = 0; i < ip.length(); i++) {
346 to_return += astring(astring::SPRINTF, "%d", int(ip[i]));
347 if (i != ip.length() - 1) to_return += ".";
354 //decide if this should be kept or not.
356 internet_address tcpip_stack::deprecated_lookup
357 (const byte_array &ip_address)
359 FUNCDEF("deprecated_lookup");
360 // lookup the client's hostname through DNS.
361 hostent *entry = gethostbyaddr((char *)ip_address.observe(),
362 ip_address.length(), PF_INET);
363 astring hostname = "unknown_host";
364 if (entry) hostname = entry->h_name;
366 internet_address to_return;
367 to_return.fill(ip_address, hostname, 0);
368 // the zero is above because we don't know the port here.
373 machine_uid tcpip_stack::this_host(int location_type) const
375 switch (location_type) {
376 case machine_uid::TCPIP_LOCATION: {
377 astring host = hostname();
379 byte_array ip = full_resolve(host, full_host);
380 if (!ip.length()) return machine_uid(); // failure.
381 return internet_machine_uid(full_host, ip);
383 case machine_uid::IPX_LOCATION: {
385 return machine_uid(); // return junk.
388 case machine_uid::NETBIOS_LOCATION: {
390 return machine_uid(); // return junk.
395 return machine_uid(); // return junk.
399 internet_address tcpip_stack::fill_and_resolve(const astring &machine_in,
400 int port, bool &worked) const
402 internet_address to_return;
403 astring machine = machine_in;
405 if (!machine) machine = "local"; // assume they mean this machine.
406 bool resolved = false; // true if we know the name.
407 byte_array ip_addr; // the ip address we're guessing/decoding/looking up.
408 bool is_ip = false; // set to true if we have the IP address in ip_addr.
409 if (machine.iequals("local") || machine.iequals("localhost")) {
410 // handle our special flag and the normal localhost machine type.
411 machine = "localhost";
412 ip_addr = internet_address::localhost();
413 is_ip = true; // we have the address already.
414 resolved = true; // we know the name also.
415 } else if (machine.iequals("inaddr_any")
416 || machine.iequals("any-address")) {
417 // handle the any address case.
418 machine = "inaddr_any";
419 ip_addr = internet_address::nil_address();
420 is_ip = true; // we have the address already.
421 resolved = true; // we know the name also.
423 // well, we now are just going to guess about it being an IP address.
425 is_ip = internet_address::is_valid_internet_address(machine, ip_addr,
429 // we try to fill in the hostname if given ip only.
430 //hmmm: use reverse dns to get the machine name for real!
431 // for cases where we really had an ip address, using the machine there
432 // would be a mistake.
433 if (resolved) to_return.fill(ip_addr, machine, port);
434 else to_return.fill(ip_addr, astring::empty_string(), port);
435 resolved = true; // claim we are happy with it now.
437 // we try to fill in the ip address for the host.
439 ip_addr = full_resolve(machine, full_host);
440 if (ip_addr.length()) {
441 to_return.fill(ip_addr, machine, port);