feisty meow concerns codebase  2.140
internet_address.cpp
Go to the documentation of this file.
1 // 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.
15 
16 #include "internet_address.h"
17 #include "machine_uid.h"
18 
19 #include <basis/byte_array.h>
20 #include <basis/functions.h>
21 #include <basis/astring.h>
22 #include <basis/mutex.h>
28 #include <textual/parser_bits.h>
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 using namespace basis;
34 using namespace configuration;
35 using namespace loggers;
36 using namespace structures;
37 using namespace textual;
38 
39 namespace sockets {
40 
41 //#define DEBUG_ADDRESS
42  // uncomment if you want a debugging version of address.
43 
45 
46 #undef LOG
47 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
48 
50 
51 /*
52 //hmmm: consider moving the functions for storage out to a helper file.
53 
54 internet_address internet_address::load(configurator &config,
55  const astring &section, const astring &name, const internet_address &def)
56 {
57  astring token_list;
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());
61  return def;
62  }
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
69  // again.
70  return internet_address();
71 }
72 
73 bool internet_address::store(configurator &config, const astring &section,
74  const astring &name, const internet_address &to_store)
75 {
76  astring text = to_store.tokenize();
77  return config.put(section, name, text);
78 }
79 */
80 
82 
83 // provides an easy way to cast to the proper type and provide a non-pointer
84 // to work with.
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;
89 
91 
92 internet_address::internet_address() { fill(byte_array(), "", 0); }
93 
94 internet_address::internet_address(const byte_array &ip,
95  const astring &host, int port_in)
96 { fill(ip, host, port_in); }
97 
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.
101 
103 { return internet_machine_uid(hostname, byte_array(ADDRESS_SIZE, ip_address)); }
104 
105 bool internet_address::ip_appropriate_number(const astring &to_check, int indy,
106  astring &accum)
107 {
108  FUNCDEF("ip_appropriate_number");
109  accum.reset();
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,
114  // we're okay.
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.
117  }
118  accum += to_check[i]; // snag the current digit.
119  }
120  if (!accum.length()) return false; // how would that happen?
121  int convert = accum.convert(-1);
122  return (convert >= 0) && (convert <= 255);
123 }
124 
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,
128  astring &ip_found)
129 {
130  FUNCDEF("has_ip_address");
131  int nums_seen = 0;
132  ip_found.reset();
133  for (int i = 0; i < to_check.length(); i++) {
134  bool hosed = false;
135  astring num_found;
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));
139  hosed = true;
140  } else {
141  // we're seeing the number we want here.
142 // LOG(astring("num found = ") + num_found);
143  nums_seen++;
144  if (nums_seen >= 4) {
145  ip_found += num_found; // get our last part.
146  return true; // hey, that's a match.
147  }
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;
151  else {
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.
156  }
157  }
158  if (!hosed) {
159  ip_found += to_check.substring(i, period_indy);
160  i = period_indy; // skip to where our next number should be.
161  }
162  }
163  }
164  if (hosed) {
165  nums_seen = 0;
166  ip_found.reset();
167  }
168  }
169  return false;
170 }
171 
172 const abyte localhosts_bytes[] = { 127, 0, 0, 1 };
173 SAFE_STATIC_CONST(byte_array, internet_address::localhost,
174  (ADDRESS_SIZE, localhosts_bytes))
175 
176 bool internet_address::is_localhost() const
177 {
178  // check whether the host is "local" or "localhost".
179  astring host = hostname;
180  host.to_lower();
181  if ( (host.equal_to("local")) || (host.equal_to("localhost")) )
182  return true;
183 
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))
187  return false;
188  }
189 
190  return true; // the address matched.
191 }
192 
193 astring internet_address::normalize_host() const
194 {
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.
202 }
203 
205 {
206  return sizeof(port) +
207  + sizeof(int) + ADDRESS_SIZE
208  + sizeof(int) + MAXIMUM_HOSTNAME_LENGTH;
209 }
210 
211 void internet_address::pack(byte_array &packed_form) const
212 {
213  attach(packed_form, port);
214  packed_form += byte_array(ADDRESS_SIZE, ip_address);
215  packed_form += byte_array(MAXIMUM_HOSTNAME_LENGTH, (abyte *)hostname);
216 }
217 
218 bool internet_address::unpack(byte_array &packed_form)
219 {
220  // check for minimum expected length.
221  if (packed_form.length() < int(sizeof(port)) + ADDRESS_SIZE
222  + MAXIMUM_HOSTNAME_LENGTH)
223  return false;
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);
229  return true;
230 }
231 
232 void internet_address::fill(const byte_array &ip, const astring &host,
233  int port_in)
234 {
235  port = port_in;
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;
239  hostname[0] = '\0';
240  host.stuff(hostname, MAXIMUM_HOSTNAME_LENGTH - 1);
241 }
242 
243 base_address *internet_address::create_copy() const
244 {
245  return new internet_address(byte_array(ADDRESS_SIZE, ip_address),
246  hostname, port);
247 }
248 
249 astring internet_address::text_form() const
250 {
251  astring to_print("[");
252  if (astring(hostname).t()) {
253  to_print += "host=";
254  to_print += hostname;
255  to_print += ", ";
256  }
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 += ".";
264  }
265  to_print += ", ";
267  to_print += a_sprintf("port=%u]", port);
268  return to_print;
269 }
270 
271 bool internet_address::is_nil_address(const address_array &ip_address)
272 {
273  for (int i = 0; i < ADDRESS_SIZE; i++) if (ip_address[i]) return false;
274  return true;
275 }
276 
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))
280 
281 bool internet_address::is_nil_address() const
282 { return is_nil_address(ip_address); }
283 
284 bool internet_address::same_host(const base_address &compare_in) const
285 {
286  CAST_UP(internet_address);
287 
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
291  // both nil.
292  if ( (is_nil_address(ip_address) && is_nil_address(to_compare.ip_address))
293  && !astring(hostname) && !astring(to_compare.hostname) )
294  return true;
295  if ( (is_nil_address(ip_address) && is_nil_address(to_compare.ip_address))
296  && (astring(hostname).iequals(to_compare.hostname)) )
297  return true;
298  if (is_nil_address(ip_address)) return false;
299  if (is_nil_address(to_compare.ip_address)) return false;
300 
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.
305 
306  // they could still be different addresses if the hostnames differ...
307 
308  if (astring(hostname).t() && astring(to_compare.hostname).t()) {
309  // name comparison.
310  if (astring(hostname).lower() != astring(to_compare.hostname).lower())
311  return false;
312  }
313 
314  return true;
315  // all bytes and host were identical, so it's the same address.
316 }
317 
318 bool internet_address::same_port(const base_address &compare_in) const
319 {
320  CAST_UP(internet_address);
321  return port == to_compare.port;
322 }
323 
324 bool internet_address::shareable(const base_address &) const
325 { return true; }
326 
327 bool internet_address::detokenize(const astring &info)
328 {
329 #ifdef DEBUG_ADDRESS
330  FUNCDEF("detokenize");
331 #endif
332  LOADER_ENTRY;
333  // either IP address or host must be specified.
334 
335  FIND("address", addr);
336 #ifdef DEBUG_ADDRESS
337  LOG(astring("info is ") + info + astring('.'));
338  LOG(astring("addr is ") + addr);
339 #endif
340  byte_array ip_found;
341  if (addr.t()) {
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++) {
345 #ifdef DEBUG_ADDRESS
346  LOG(astring("ip curr: ") + addr);
347 #endif
348  int current_byte = addr.convert(int(0));
349  ip_found += abyte(current_byte);
350 #ifdef DEBUG_ADDRESS
351  LOG(a_sprintf("%d: %02x ", i, current_byte));
352 #endif
353  int indy = addr.find('.');
354  addr.zap(0, indy); // no error checking, but whatever.
355  }
356  }
357 
358  FIND("host", host);
359  GRAB("port", port_t); // this one's definitely needed.
360  int port = port_t.convert(0);
361  fill(ip_found, host, port);
362 #ifdef DEBUG_ADDRESS
363  LOG(astring("tcp/ip address found::: ") + text_form());
364 #endif
365  LOADER_EXIT;
366  return true;
367 }
368 
369 astring internet_address::tokenize() const
370 {
371 #ifdef DEBUG_ADDRESS
372  FUNCDEF("tokenize");
373 #endif
374  STORER_ENTRY;
375 #ifdef DEBUG_ADDRESS
376  LOG(a_sprintf("host eval is %d for %s", astring(hostname).t(), hostname));
377 #endif
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;
382  }
383  if (print_ip) {
384  astring ip_addr;
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);
389  }
390  ADD("port", a_sprintf("%d", int(port)));
391 #ifdef DEBUG_ADDRESS
392  LOG(astring("your toke's currently ") + fred.text_form());
393 #endif
394  DUMP_EXIT;
395 }
396 
397 bool internet_address::appropriate_for_ip(const astring &to_check)
398 {
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.
404  return false;
405  }
406  return true;
407 }
408 
409 // by Gary Hardley.
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)
414 {
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.
418 
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
422  // ends with a dot.
423  if ( (to_check[0] == '.') || (to_check[to_check.end()] == '.') )
424  return false;
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;
428 
429  // get the first part of the address.
430  char *p = strtok(tmpstr.s(), ".");
431 
432  int index = 0;
433  while (p && (index < ADDRESS_SIZE)) {
434  int nTemp = astring(p).convert(-1);
435 
436  // is this part of the string a valid number between 0 & 255?
437  if ( (nTemp < 0) || (nTemp > 255) ) return false; // no.
438 
439  // yes, assign the number to the next address byte.
440  ip_form += (abyte)nTemp;
441 
442  // get the next part of the string
443  p = strtok(NULL_POINTER, ".");
444  }
445 
446  // if p is non-null, there was extra stuff at the end, so return false.
447  if (p) return false;
448 
449  for (int i = 0; i < ip_form.length(); i++)
450  if (ip_form[i]) { all_zeros = false; break; }
451 
452  return ip_form.length() == ADDRESS_SIZE;
453 }
454 
455 bool internet_address::valid_address(const astring &to_check)
456 {
457  byte_array addr;
458  bool all_zeros;
459  return is_valid_internet_address(to_check, addr, all_zeros);
460 }
461 
462 astring internet_address::ip_address_text_form(const byte_array &ip_address)
463 {
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]));
466 }
467 
468 } //namespace.
469 
470 
#define STORER_ENTRY
Definition: base_address.h:87
#define DUMP_EXIT
Definition: base_address.h:97
#define GRAB(name, value)
Definition: base_address.h:83
#define LOADER_ENTRY
Definition: base_address.h:72
#define ADD(name, value)
Definition: base_address.h:94
#define LOADER_EXIT
Definition: base_address.h:76
#define FIND(name, value)
Definition: base_address.h:80
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
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 zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Definition: array.h:769
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
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
astring lower() const
like to_lower(), but returns a new string rather than modifying this.
Definition: astring.cpp:542
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition: astring.h:97
int convert(int default_value) const
Converts the string into a corresponding integer.
Definition: astring.cpp:757
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition: astring.cpp:521
bool substring(astring &target, int start, int end) const
a version that stores the substring in an existing "target" string.
Definition: astring.cpp:865
virtual void text_form(base_string &state_fill) const
Provides a text view of all the important info owned by this object.
Definition: astring.cpp:130
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
bool equal_to(const char *that) const
returns true if "that" is equal to this.
Definition: astring.cpp:159
int end() const
returns the index of the last (non-null) character in the string.
Definition: astring.h:86
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
int find(char to_find, int position=0, bool reverse=false) const
Locates "to_find" in "this".
Definition: astring.cpp:574
bool contains(const astring &to_find) const
Returns true if "to_find" is contained in this string or false if not.
Definition: astring.cpp:162
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
this type of address describes a destination out on the internet.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
#define CAST_UP(type)
#define LOG(to_print)
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
void attach(byte_array &packed_form, const char *to_attach)
Packs a character string "to_attach" into "packed_form".
Definition: astring.cpp:1015
bool detach(byte_array &packed_form, astring &to_detach)
Unpacks a character string "to_attach" from "packed_form".
Definition: astring.cpp:1023
type minimum(type a, type b)
maximum returns the greater of two values.
Definition: functions.h:29
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
const abyte localhosts_bytes[]
const abyte nil_address_bytes[]
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
bool unpack(basis::byte_array &packed_form, set< contents > &to_unpack)
provides a way to unpack any set that stores packable objects.
Definition: set.h:139
void pack(basis::byte_array &packed_form, const set< contents > &to_pack)
provides a way to pack any set that stores packable objects.
Definition: set.h:131
int packed_size(const byte_array &packed_form)
Reports the size required to pack a byte array into a byte array.
time_locus convert(time_number seconds, time_number useconds, const tm &cal_values)
Definition: earth_time.cpp:326
#define SAFE_STATIC_CONST(type, func_name, parms)
this version returns a constant object instead.