Merge branch 'master' of feistymeow.org:feisty_meow
[feisty_meow.git] / octopi / library / sockets / internet_address.cpp
1 //////////////
2 // Name   : internet_address
3 // Author : Chris Koeritz
4 //////////////
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.
14 //////////////
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>
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>
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
44 //////////////
45
46 #undef LOG
47 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
48
49 //////////////
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
81 //////////////
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
90 //////////////
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
102 machine_uid internet_address::convert() const
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
204 int internet_address::packed_size() const
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   }
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 += ".";
264     }
265     to_print += ", ";
266 /////  }
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