2 // Name : internet_address
3 // Author : Chris Koeritz
5 // Copyright (c) 1995-$now By Author. This program is free software; you can
6 // redistribute it and/or modify it under the terms of the GNU General Public
7 // License as published by the Free Software Foundation:
8 // http://www.gnu.org/licenses/gpl.html
9 // or under the terms of the GNU Library license:
10 // http://www.gnu.org/licenses/lgpl.html
11 // at your preference. Those licenses describe your legal rights to this
12 // software, and no other rights or warranties apply.
13 // Please send updates for this code to: fred@gruntose.com -- Thanks, fred.
16 #include "internet_address.h"
17 #include "machine_uid.h"
19 #include <basis/byte_array.h>
20 #include <basis/functions.h>
21 #include <basis/astring.h>
22 #include <basis/mutex.h>
23 #include <configuration/configurator.h>
24 #include <configuration/variable_tokenizer.h>
25 #include <loggers/program_wide_logger.h>
26 #include <structures/static_memory_gremlin.h>
27 #include <structures/string_table.h>
28 #include <textual/parser_bits.h>
33 using namespace basis;
34 using namespace configuration;
35 using namespace loggers;
36 using namespace structures;
37 using namespace textual;
41 //#define DEBUG_ADDRESS
42 // uncomment if you want a debugging version of address.
47 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
52 //hmmm: consider moving the functions for storage out to a helper file.
54 internet_address internet_address::load(configurator &config,
55 const astring §ion, const astring &name, const internet_address &def)
58 if (!config.get(section, name, token_list)) {
59 // no entry stored means no address. set the address to the default.
60 config.put(section, name, def.tokenize());
63 internet_address to_return;
64 // get the rest of the work done by detokenize.
65 if (to_return.detokenize(token_list)) return to_return;
66 // didn't work, dang it. Note that we don't reject it here and store the
67 // default. that's because this indicates an error in formatting of the
68 // address, and we want the user to have a chance to correct that and try
70 return internet_address();
73 bool internet_address::store(configurator &config, const astring §ion,
74 const astring &name, const internet_address &to_store)
76 astring text = to_store.tokenize();
77 return config.put(section, name, text);
83 // provides an easy way to cast to the proper type and provide a non-pointer
85 #define CAST_UP(type) \
86 const type *temp = dynamic_cast<const type *>(&compare_in); \
87 if (!temp) return false; /* shouldn't happen but it means bad things. */ \
88 const type &to_compare = *temp;
92 internet_address::internet_address() { fill(byte_array(), "", 0); }
94 internet_address::internet_address(const byte_array &ip,
95 const astring &host, int port_in)
96 { fill(ip, host, port_in); }
98 //hmmm: for ipv6, we will need a new object, called ipv6_address perhaps.
99 // it will copy everything here but will have a longer address array.
100 // we will need a new object for ipv6_machine_uid also.
102 machine_uid internet_address::convert() const
103 { return internet_machine_uid(hostname, byte_array(ADDRESS_SIZE, ip_address)); }
105 bool internet_address::ip_appropriate_number(const astring &to_check, int indy,
108 FUNCDEF("ip_appropriate_number");
110 for (int i = indy; (i < indy + 3) && (i < to_check.length()); i++) {
111 // make sure it looks like a good number.
112 if (!parser_bits::is_numeric(to_check[i]) || (to_check[i] == '-') ) {
113 // this one doesn't look good right here, but if we already got a digit,
115 if (i == indy) return false; // hadn't gotten any digits at all yet.
116 else break; // got one, so let's check our progress below.
118 accum += to_check[i]; // snag the current digit.
120 if (!accum.length()) return false; // how would that happen?
121 int convert = accum.convert(-1);
122 return (convert >= 0) && (convert <= 255);
125 // needs to see 1-3 numbers, period, 1-3 numbers, period etc...
126 // for a total of three periods and 4 number sets.
127 bool internet_address::has_ip_address(const astring &to_check,
130 FUNCDEF("has_ip_address");
133 for (int i = 0; i < to_check.length(); i++) {
136 // if (!!ip_found) LOG(astring("current ip found=") + ip_found);
137 if (!ip_appropriate_number(to_check, i, num_found)) {
138 // LOG(a_sprintf("no ip approp %d", i));
141 // we're seeing the number we want here.
142 // LOG(astring("num found = ") + num_found);
144 if (nums_seen >= 4) {
145 ip_found += num_found; // get our last part.
146 return true; // hey, that's a match.
148 // look for a period now.
149 int period_indy = to_check.find('.', i);
150 if (negative(period_indy) || (period_indy > i + 3) ) hosed = true;
152 for (int x = i; x < period_indy; x++) {
153 if (!parser_bits::is_numeric(to_check[x]) || (to_check[x] == '-')) {
154 // LOG(a_sprintf("hosed by bad char at %d -> %c", x, to_check[x]));
155 hosed = true; // wrong character seen in between.
159 ip_found += to_check.substring(i, period_indy);
160 i = period_indy; // skip to where our next number should be.
172 const abyte localhosts_bytes[] = { 127, 0, 0, 1 };
173 SAFE_STATIC_CONST(byte_array, internet_address::localhost,
174 (ADDRESS_SIZE, localhosts_bytes))
176 bool internet_address::is_localhost() const
178 // check whether the host is "local" or "localhost".
179 astring host = hostname;
181 if ( (host.equal_to("local")) || (host.equal_to("localhost")) )
184 // check whether the address is local even if the hostname wasn't.
185 for (int i = 0; i < ADDRESS_SIZE; i++) {
186 if (ip_address[i] != localhost().get(i))
190 return true; // the address matched.
193 astring internet_address::normalize_host() const
195 // try the hostname first.
196 astring remote = hostname;
197 if (remote.t()) return remote;
198 // there was no host we can use; try the IP address instead.
199 byte_array ip_form(ADDRESS_SIZE, ip_address);
200 remote = ip_address_text_form(ip_form);
201 return remote; // they get whatever we had as the address.
204 int internet_address::packed_size() const
206 return sizeof(port) +
207 + sizeof(int) + ADDRESS_SIZE
208 + sizeof(int) + MAXIMUM_HOSTNAME_LENGTH;
211 void internet_address::pack(byte_array &packed_form) const
213 attach(packed_form, port);
214 packed_form += byte_array(ADDRESS_SIZE, ip_address);
215 packed_form += byte_array(MAXIMUM_HOSTNAME_LENGTH, (abyte *)hostname);
218 bool internet_address::unpack(byte_array &packed_form)
220 // check for minimum expected length.
221 if (packed_form.length() < int(sizeof(port)) + ADDRESS_SIZE
222 + MAXIMUM_HOSTNAME_LENGTH)
224 if (!detach(packed_form, port)) return false;
225 packed_form.stuff(ADDRESS_SIZE, ip_address);
226 packed_form.zap(0, ADDRESS_SIZE - 1);
227 packed_form.stuff(MAXIMUM_HOSTNAME_LENGTH, (abyte *)hostname);
228 packed_form.zap(0, MAXIMUM_HOSTNAME_LENGTH - 1);
232 void internet_address::fill(const byte_array &ip, const astring &host,
236 int mini = minimum(int(ADDRESS_SIZE), ip.length());
237 for (int i = 0; i < mini; i++) ip_address[i] = ip[i];
238 for (int j = mini; j < ADDRESS_SIZE; j++) ip_address[j] = 0;
240 host.stuff(hostname, MAXIMUM_HOSTNAME_LENGTH - 1);
243 base_address *internet_address::create_copy() const
245 return new internet_address(byte_array(ADDRESS_SIZE, ip_address),
249 astring internet_address::text_form() const
251 astring to_print("[");
252 if (astring(hostname).t()) {
254 to_print += hostname;
257 ///// bool print_ip = false;
258 ///// for (int i = 0; i < ADDRESS_SIZE; i++) if (ip_address[i]) print_ip = true;
259 ///// if (print_ip) {
260 to_print += "ip_addr=";
261 for (int i = 0; i < ADDRESS_SIZE; i++) {
262 to_print += a_sprintf("%d", int(ip_address[i]));
263 if (i != ADDRESS_SIZE - 1) to_print += ".";
267 to_print += a_sprintf("port=%u]", port);
271 bool internet_address::is_nil_address(const address_array &ip_address)
273 for (int i = 0; i < ADDRESS_SIZE; i++) if (ip_address[i]) return false;
277 const abyte nil_address_bytes[] = { 0, 0, 0, 0 };
278 SAFE_STATIC_CONST(byte_array, internet_address::nil_address,
279 (ADDRESS_SIZE, nil_address_bytes))
281 bool internet_address::is_nil_address() const
282 { return is_nil_address(ip_address); }
284 bool internet_address::same_host(const base_address &compare_in) const
286 CAST_UP(internet_address);
288 // they can't be the same if one is a valid address and the other's not, but
289 // they are the same if both addresses are empty.
290 // even so, they're not the same if either's name was non-empty but they're
292 if ( (is_nil_address(ip_address) && is_nil_address(to_compare.ip_address))
293 && !astring(hostname) && !astring(to_compare.hostname) )
295 if ( (is_nil_address(ip_address) && is_nil_address(to_compare.ip_address))
296 && (astring(hostname).iequals(to_compare.hostname)) )
298 if (is_nil_address(ip_address)) return false;
299 if (is_nil_address(to_compare.ip_address)) return false;
301 // check that the bytes don't differ for the IP address.
302 for (int i = 0; i < ADDRESS_SIZE; i++)
303 if (ip_address[i] != to_compare.ip_address[i])
304 return false; // we have a loser.
306 // they could still be different addresses if the hostnames differ...
308 if (astring(hostname).t() && astring(to_compare.hostname).t()) {
310 if (astring(hostname).lower() != astring(to_compare.hostname).lower())
315 // all bytes and host were identical, so it's the same address.
318 bool internet_address::same_port(const base_address &compare_in) const
320 CAST_UP(internet_address);
321 return port == to_compare.port;
324 bool internet_address::shareable(const base_address &) const
327 bool internet_address::detokenize(const astring &info)
330 FUNCDEF("detokenize");
333 // either IP address or host must be specified.
335 FIND("address", addr);
337 LOG(astring("info is ") + info + astring('.'));
338 LOG(astring("addr is ") + addr);
342 // this bit rips off successive bytes from the string until the internet
343 // address is filled out. ignores any erroneous strings.
344 for (int i = 0; i < ADDRESS_SIZE; i++) {
346 LOG(astring("ip curr: ") + addr);
348 int current_byte = addr.convert(int(0));
349 ip_found += abyte(current_byte);
351 LOG(a_sprintf("%d: %02x ", i, current_byte));
353 int indy = addr.find('.');
354 addr.zap(0, indy); // no error checking, but whatever.
359 GRAB("port", port_t); // this one's definitely needed.
360 int port = port_t.convert(0);
361 fill(ip_found, host, port);
363 LOG(astring("tcp/ip address found::: ") + text_form());
369 astring internet_address::tokenize() const
376 LOG(a_sprintf("host eval is %d for %s", astring(hostname).t(), hostname));
378 if (astring(hostname).t()) ADD("host", hostname);
379 bool print_ip = false;
380 for (int i = 0; i < ADDRESS_SIZE; i++) {
381 if (ip_address[i]) print_ip = true;
385 for (int i = 0; i < ADDRESS_SIZE; i++)
386 ip_addr += a_sprintf("%d", int(ip_address[i])) + astring(".");
387 ip_addr.zap(ip_addr.end(), ip_addr.end()); // remove last period.
388 ADD("address", ip_addr);
390 ADD("port", a_sprintf("%d", int(port)));
392 LOG(astring("your toke's currently ") + fred.text_form());
397 bool internet_address::appropriate_for_ip(const astring &to_check)
399 for (int i = 0; i < to_check.length(); i++) {
400 char curr = to_check[i];
401 if (curr == '.') continue;
402 if ( (curr >= '0') && (curr <= '9') ) continue;
403 // an unsavory character was seen, so fail out.
410 // fixes by CAK 6/26/2002.
411 // and more by CAK in november 2010.
412 bool internet_address::is_valid_internet_address(const astring &to_check,
413 byte_array &ip_form, bool &all_zeros)
415 astring tmpstr = to_check; // temporary copy of the given address string.
416 all_zeros = true; // default to true until proven otherwise.
417 ip_form.reset(); // empty the address.
419 // if it's got anything besides dots and numbers, bail out.
420 if (!appropriate_for_ip(to_check)) return false;
421 // catch a case the below logic does not--where the string starts or
423 if ( (to_check[0] == '.') || (to_check[to_check.end()] == '.') )
425 // catch another case that was missed before, where there are multiple dots
426 // in a row but enough numbers to give 4 components.
427 if (to_check.contains("..")) return false;
429 // get the first part of the address.
430 char *p = strtok(tmpstr.s(), ".");
433 while (p && (index < ADDRESS_SIZE)) {
434 int nTemp = astring(p).convert(-1);
436 // is this part of the string a valid number between 0 & 255?
437 if ( (nTemp < 0) || (nTemp > 255) ) return false; // no.
439 // yes, assign the number to the next address byte.
440 ip_form += (abyte)nTemp;
442 // get the next part of the string
443 p = strtok(NULL_POINTER, ".");
446 // if p is non-null, there was extra stuff at the end, so return false.
449 for (int i = 0; i < ip_form.length(); i++)
450 if (ip_form[i]) { all_zeros = false; break; }
452 return ip_form.length() == ADDRESS_SIZE;
455 bool internet_address::valid_address(const astring &to_check)
459 return is_valid_internet_address(to_check, addr, all_zeros);
462 astring internet_address::ip_address_text_form(const byte_array &ip_address)
464 return a_sprintf("%d.%d.%d.%d", int(ip_address[0]),
465 int(ip_address[1]), int(ip_address[2]), int(ip_address[3]));