00001 #ifndef CROMP_CLIENT_IMPLEMENTATION_FILE
00002 #define CROMP_CLIENT_IMPLEMENTATION_FILE
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "cromp_client.h"
00019 #include "cromp_common.h"
00020 #include "cromp_transaction.h"
00021
00022 #include <basis/chaos.h>
00023 #include <basis/function.h>
00024 #include <basis/istring.h>
00025 #include <basis/log_base.h>
00026 #include <basis/mutex.h>
00027 #include <basis/portable.h>
00028 #include <data_struct/static_memory_gremlin.h>
00029 #include <mechanisms/ithread.h>
00030 #include <mechanisms/roller.cpp>
00031 #include <mechanisms/time_stamp.h>
00032 #include <octopus/entity_defs.h>
00033 #include <octopus/identity_infoton.h>
00034 #include <octopus/unhandled_request.h>
00035 #include <sockets/address.h>
00036 #include <sockets/machine_uid.h>
00037 #include <sockets/spocket.h>
00038 #include <sockets/tcpip_stack.h>
00039 #include <tentacles/encryption_tentacle.h>
00040 #include <tentacles/encryption_wrapper.h>
00041 #include <tentacles/entity_registry.h>
00042 #include <tentacles/key_repository.h>
00043 #include <tentacles/login_tentacle.h>
00044 #include <tentacles/security_infoton.h>
00045
00046 #ifndef OMIT_CRYPTO_SUPPORT
00047 #include <crypto/rsa_crypto.h>
00048 #endif
00049
00050
00051
00052
00053 #undef LOG
00054 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
00055 #undef FILT_LOG
00056 #define FILT_LOG(filter, s) CLASS_FILTER_LOG(program_wide_logger(), s, filter)
00057
00058 const int MAX_CONN_ATTEMPTS = 3;
00059
00060
00061 const int INTERCONNECTION_SNOOZE = 200;
00062
00063
00064
00065
00066 #define AUTO_LOCK \
00067 auto_synchronizer l(*_lock)
00068
00069
00070 #define CHECK_LOCKOUT \
00071 if (_disallowed) { \
00072 \
00073 return NO_CONNECTION; \
00074 }
00075
00076
00077 #define CAST_REPLY(type, varname, newvar, retval) \
00078 type *newvar = dynamic_cast<type *>(varname); \
00079 if (!newvar) { \
00080 LOG("failed to cast " #varname " to appropriate type, " #type "."); \
00081 WHACK(varname); \
00082 return retval; \
00083 }
00084
00086
00087 class asynch_connection_thread : public ithread
00088 {
00089 public:
00090 asynch_connection_thread(cromp_client &parent)
00091 : ithread(), _parent(parent) {}
00092 ~asynch_connection_thread() { stop(); }
00093 void perform_activity(void *formal(ptr)) {
00094 FUNCDEF("perform_activity");
00095 while (!should_stop()) {
00096 if (_parent.connected()) {
00097 FILT_LOG(common::NETWORK_LOGGING, _parent.instance_name()
00098 + " got connected.");
00099 break;
00100 }
00101
00102
00103
00104
00105 FILT_LOG(common::NETWORK_LOGGING, _parent.instance_name() + " still "
00106 "unconnected; trying connect now.");
00107 _parent.locked_connect();
00108 FILT_LOG(common::NETWORK_LOGGING, _parent.instance_name()
00109 + " done calling connect.");
00110 }
00111
00112 _parent._disallowed = false;
00113 }
00114
00115 private:
00116 cromp_client &_parent;
00117 };
00118
00120
00121 cromp_client::cromp_client(const internet_address &addr, int connection_wait,
00122 int max_per_ent)
00123 : cromp_common(cromp_common::chew_hostname(addr), max_per_ent),
00124 _encrypting(false),
00125 _connection_wait(connection_wait),
00126 _lock(new mutex),
00127 _ent(new octopus_entity(randomize_entity())),
00128 _req_id(new int_roller(1, MAXINT - 20)),
00129 _identified(false),
00130 _authorized(false),
00131 _disallowed(false),
00132 _asynch_connector(NIL),
00133 #ifndef OMIT_CRYPTO_SUPPORT
00134 _channel_secured(false),
00135 _crypto(new blowfish_crypto(encryption_infoton::BLOWFISH_KEY_SIZE)),
00136 _encrypt_arm(NIL),
00137 #endif
00138 _guardian(new blank_entity_registry)
00139 {
00140 FUNCDEF("constructor");
00141 #ifdef DEBUG_CROMP_CLIENT
00142 LOG(istring("initial entity=") + _ent->mangled_form());
00143 #endif
00144 open_common(addr);
00145
00146
00147 add_tentacle(new login_tentacle(*_guardian));
00148
00149
00150 }
00151
00152 cromp_client::~cromp_client()
00153 {
00154 FUNCDEF("destructor");
00155 disconnect();
00156 close_common();
00157 _identified = false;
00158 _authorized = false;
00159 WHACK(_ent);
00160 WHACK(_req_id);
00161 #ifndef OMIT_CRYPTO_SUPPORT
00162 _channel_secured = false;
00163 WHACK(_crypto);
00164 #endif
00165 WHACK(_guardian);
00166 WHACK(_lock);
00167 }
00168
00169 bool cromp_client::connected() const { return spock()->connected(); }
00170
00171 void cromp_client::enable_encryption()
00172 {
00173 FUNCDEF("enable_encryption");
00174 #ifndef OMIT_CRYPTO_SUPPORT
00175 AUTO_LOCK;
00176
00177 #ifdef DEBUG_CROMP_CLIENT
00178 LOG(istring("enabling encryption for ") + class_name() + " on "
00179 + other_side().text_form());
00180 #endif
00181 _encrypting = true;
00182
00183
00184 if (other_side().is_localhost()) {
00185
00186 byte_array temp_priv_key;
00187 localhost_only_key().private_key(temp_priv_key);
00188 _encrypt_arm = new encryption_tentacle(temp_priv_key);
00189
00190
00191
00192
00193 } else
00194 _encrypt_arm = new encryption_tentacle(encryption_infoton::RSA_KEY_SIZE);
00195 add_tentacle(_encrypt_arm, true);
00196 add_tentacle(new unwrapping_tentacle, false);
00197 #endif
00198 }
00199
00200 void cromp_client::stop_asynch_thread()
00201 {
00202 FUNCDEF("stop_asynch_thread");
00203 if (_asynch_connector) {
00204 #ifdef DEBUG_CROMP_CLIENT
00205 LOG(instance_name() + " stopping thread.");
00206 #endif
00207 _asynch_connector->cancel();
00208 AUTO_LOCK;
00209 _asynch_connector->stop();
00210 WHACK(_asynch_connector);
00211 }
00212 _disallowed = false;
00213 }
00214
00215 void cromp_client::reset(const internet_address &addr, int connection_wait,
00216 int max_per_ent)
00217 {
00218 FUNCDEF("reset");
00219 stop_asynch_thread();
00220 AUTO_LOCK;
00221 close_common();
00222 max_bytes_per_entity(max_per_ent);
00223 *_ent = randomize_entity();
00224 _req_id->set_current(1);
00225 _identified = false;
00226 _authorized = false;
00227 #ifndef OMIT_CRYPTO_SUPPORT
00228 _channel_secured = false;
00229 #endif
00230 _connection_wait = connection_wait;
00231 _disallowed = false;
00232 #ifdef DEBUG_CROMP_CLIENT
00233 LOG(istring("resetting entity=") + _ent->mangled_form());
00234 #endif
00235 open_common(addr);
00236 }
00237
00238 const octopus_entity &cromp_client::entity() const
00239 {
00240 AUTO_LOCK;
00241 return *_ent;
00242 }
00243
00244 SAFE_STATIC(tcpip_stack, _hidden_stack, )
00245
00246 octopus_entity cromp_client::randomize_entity() const
00247 {
00248 istring host = cromp_common::chew_hostname(internet_address
00249 (byte_array::empty_array(), _hidden_stack().hostname(), 0), NIL);
00250 chaos randomizer;
00251 return octopus_entity(host, portable::process_id(),
00252 randomizer.inclusive(0, MAXINT / 3),
00253 randomizer.inclusive(0, MAXINT / 3));
00254 }
00255
00256 octopus_request_id cromp_client::next_id()
00257 {
00258 AUTO_LOCK;
00259 return octopus_request_id(*_ent, _req_id->next_id());
00260 }
00261
00262 outcome cromp_client::synchronous_request(const infoton &to_send,
00263 infoton * & received, octopus_request_id &item_id,
00264 int timeout)
00265 {
00266 FUNCDEF("synchronous_request");
00267 received = NIL;
00268 outcome ret = submit(to_send, item_id);
00269 if (ret != OKAY) {
00270 FILT_LOG(common::NETWORK_LOGGING, istring("failed to submit request: ")
00271 + outcome_name(ret) + " on " + to_send.text_form());
00272 return ret;
00273 }
00274 ret = acquire(received, item_id, timeout);
00275 if (ret != OKAY) {
00276 FILT_LOG(common::NETWORK_LOGGING, istring("failed to acquire response: ")
00277 + outcome_name(ret) + " for " + to_send.text_form());
00278 return ret;
00279 }
00280 return OKAY;
00281 }
00282
00283 outcome cromp_client::login()
00284 {
00285 FUNCDEF("login");
00286 CHECK_LOCKOUT;
00287 if (!_identified) {
00288 #ifndef OMIT_CRYPTO_SUPPORT
00289 _channel_secured = false;
00290 #endif
00291
00292 identity_infoton identity;
00293 octopus_request_id item_id = octopus_request_id::randomized_id();
00294 infoton *response;
00295 outcome ret = synchronous_request(identity, response, item_id);
00296 if (ret != OKAY) return ret;
00297
00298 CAST_REPLY(identity_infoton, response, ide_reply, NO_SERVER);
00299 if (!ide_reply->_new_name.blank()) {
00300 #ifdef DEBUG_CROMP_CLIENT
00301 LOG(istring("setting new entity to: ")
00302 + ide_reply->_new_name.mangled_form());
00303 #endif
00304 AUTO_LOCK;
00305 *_ent = ide_reply->_new_name;
00306 _identified = true;
00307 } else {
00308 #ifdef DEBUG_CROMP_CLIENT
00309 LOG("identity request failed: got blank name.");
00310 #endif
00311 }
00312 WHACK(ide_reply);
00313 }
00314
00315 #ifndef OMIT_CRYPTO_SUPPORT
00316 if (_encrypting && !_channel_secured) {
00317
00318
00319 if (!_encrypt_arm)
00320 LOG("there's no encryption arm!!!!");
00321
00322 encryption_infoton encro;
00323 {
00324 AUTO_LOCK;
00325 encro.prepare_public_key(_encrypt_arm->private_key());
00326 }
00327
00328 infoton *response;
00329 octopus_request_id item_id;
00330 outcome ret = synchronous_request(encro, response, item_id);
00331 if (ret != OKAY) return ret;
00332
00333 CAST_REPLY(encryption_infoton, response, enc_reply, ENCRYPTION_MISMATCH);
00334
00335
00336
00337
00338
00339 byte_array transformed;
00340 ret = _encrypt_arm->consume(*enc_reply, item_id, transformed);
00341 if (ret != OKAY) {
00342 LOG(istring("failed to process encryption infoton for ")
00343 + item_id.text_form());
00344 WHACK(enc_reply);
00345 return ret;
00346 }
00347 WHACK(enc_reply);
00348
00349 octenc_key_record *reco = _encrypt_arm->keys().lock(item_id._entity);
00350 if (!reco) {
00351 LOG(istring("failed to locate key for ") + item_id._entity.text_form());
00352 return NOT_FOUND;
00353 }
00354 _crypto->set_key(reco->_key.get_key(),
00355 encryption_infoton::BLOWFISH_KEY_SIZE);
00356 _encrypt_arm->keys().unlock(reco);
00357 _channel_secured = true;
00358 }
00359 #endif
00360
00361 if (!_authorized) {
00362
00363 security_infoton::login_modes login_type = security_infoton::LI_LOGIN;
00364 security_infoton securinfo(login_type, OKAY, byte_array());
00365 octopus_request_id item_id;
00366 infoton *response;
00367 outcome ret = synchronous_request(securinfo, response, item_id);
00368 unhandled_request *temp_unh = dynamic_cast<unhandled_request *>(response);
00369 if (temp_unh) {
00370 #ifdef DEBUG_CROMP_CLIENT
00371 LOG(istring("got an unhandled request with reason: ")
00372 + common::outcome_name(temp_unh->_reason));
00373 #endif
00374 return temp_unh->_reason;
00375 }
00376 CAST_REPLY(security_infoton, response, sec_reply, NO_SERVER);
00377 outcome success = sec_reply->_success;
00378 if (success == tentacle::OKAY) {
00379 AUTO_LOCK;
00380 _authorized = true;
00381 } else {
00382 #ifdef DEBUG_CROMP_CLIENT
00383 LOG(istring("login request failed."));
00384 #endif
00385 }
00386 WHACK(sec_reply);
00387 }
00388
00389 return OKAY;
00390 }
00391
00392 outcome cromp_client::connect()
00393 {
00394 FUNCDEF("connect");
00395 stop_asynch_thread();
00396 AUTO_LOCK;
00397 return locked_connect();
00398 }
00399
00400 outcome cromp_client::asynch_connect()
00401 {
00402 FUNCDEF("asynch_connect");
00403 if (connected()) return OKAY;
00404 if (_asynch_connector) return NO_CONNECTION;
00405
00406 LOG(instance_name() + " entry.");
00407
00408 {
00409 AUTO_LOCK;
00410
00411 if (connected()) return OKAY;
00412 if (_asynch_connector) {
00413 LOG("logic error: asynchronous connector already exists.");
00414 return NO_CONNECTION;
00415 }
00416 _disallowed = true;
00417 _asynch_connector = new asynch_connection_thread(*this);
00418 }
00419 _asynch_connector->start(NIL);
00420
00421 LOG(instance_name() + " exit.");
00422
00423 return NO_CONNECTION;
00424 }
00425
00426 outcome cromp_client::locked_connect()
00427 {
00428 FUNCDEF("locked_connect");
00429 if (!spock()) return BAD_INPUT;
00430 if (connected()) return OKAY;
00431
00432 locked_disconnect();
00433 *_ent = randomize_entity();
00434
00435 int attempts = 0;
00436 while (attempts++ < MAX_CONN_ATTEMPTS) {
00437 #ifdef DEBUG_CROMP_CLIENT
00438 LOG(instance_name() + " calling spocket connect.");
00439 #endif
00440 outcome ret = spock()->connect(_connection_wait);
00441 #ifdef DEBUG_CROMP_CLIENT
00442 LOG(instance_name() + " done calling spocket connect.");
00443 #endif
00444 if (ret == spocket::OKAY) {
00445 #ifdef DEBUG_CROMP_CLIENT
00446 LOG("finished connection... now affirming identity.");
00447 #endif
00448 return login();
00449 }
00450 if (ret == spocket::TIMED_OUT) return TIMED_OUT;
00451 if ( (ret == spocket::NO_ANSWER) || (ret == spocket::ACCESS_DENIED) ) {
00452
00453 locked_disconnect();
00454 return NO_SERVER;
00455 }
00456 #ifdef DEBUG_CROMP_CLIENT
00457 LOG(isprintf("error gotten=%s", spocket::outcome_name(ret)));
00458 #endif
00459
00460 if (attempts < MAX_CONN_ATTEMPTS - 1)
00461 portable::sleep_ms(INTERCONNECTION_SNOOZE);
00462 }
00463 FILT_LOG(common::NETWORK_LOGGING, instance_name() + " failed to connect.");
00464 locked_disconnect();
00465 return NO_CONNECTION;
00466 }
00467
00468 outcome cromp_client::disconnect()
00469 {
00470 stop_asynch_thread();
00471 AUTO_LOCK;
00472 return locked_disconnect();
00473 }
00474
00475 void cromp_client::keep_alive_pause(int duration, int interval)
00476 {
00477 if (duration < 0) duration = 0;
00478 if (interval < 0) interval = 40;
00479 if (interval > duration) interval = duration;
00480
00481
00482 time_stamp leave_at(duration);
00483 while (time_stamp() < leave_at) {
00484 push_outgoing(1);
00485 grab_anything(false);
00486
00487 if (duration)
00488 portable::sleep_ms(interval);
00489 }
00490 }
00491
00492 outcome cromp_client::locked_disconnect()
00493 {
00494 if (!spock()) return BAD_INPUT;
00495 outcome ret = spock()->disconnect();
00496 _identified = false;
00497 _authorized = false;
00498 #ifndef OMIT_CRYPTO_SUPPORT
00499 _channel_secured = false;
00500 #endif
00501 *_ent = octopus_entity();
00502 if (ret != spocket::OKAY) {
00503
00504 return OKAY;
00505 }
00506 return OKAY;
00507 }
00508
00509 #ifndef OMIT_CRYPTO_SUPPORT
00510 bool cromp_client::wrap_infoton(const infoton &request,
00511 encryption_wrapper &wrapped)
00512 {
00513 FUNCDEF("wrap_infoton");
00514 if (!_channel_secured) return false;
00515
00516
00517
00518
00519 bool is_ident = !!dynamic_cast<const identity_infoton *>(&request);
00520 bool is_encrypt = !!dynamic_cast<const encryption_infoton *>(&request);
00521 bool is_wrapper = !!dynamic_cast<const encryption_wrapper *>(&request);
00522 if (!is_ident && !is_encrypt && !is_wrapper) {
00523
00524
00525 if (!_channel_secured) {
00526 #ifdef DEBUG_CROMP_CLIENT
00527 LOG("the channel has not been secured yet.");
00528 #endif
00529 return false;
00530 }
00531 #ifdef DEBUG_CROMP_CLIENT
00532 LOG(istring("encrypting ") + request.text_form());
00533 #endif
00534 byte_array packed_request;
00535 infoton::fast_pack(packed_request, request);
00536 _crypto->encrypt(packed_request, wrapped._wrapped);
00537 return true;
00538 } else return false;
00539 }
00540 #endif
00541
00542 outcome cromp_client::submit(const infoton &request,
00543 octopus_request_id &item_id, int max_tries)
00544 {
00545 FUNCDEF("submit");
00546 CHECK_LOCKOUT;
00547 item_id = next_id();
00548 bool is_ident = !!dynamic_cast<const identity_infoton *>(&request);
00549 if (!_identified && !is_ident) return BAD_INPUT;
00550
00551 #ifndef OMIT_CRYPTO_SUPPORT
00552 if (_encrypting && _channel_secured) {
00553
00554
00555
00556
00557
00558
00559
00560 encryption_wrapper real_request;
00561 bool wrapped_okay = wrap_infoton(request, real_request);
00562 if (wrapped_okay) {
00563 outcome to_return = cromp_common::pack_and_ship(real_request, item_id,
00564 max_tries);
00565 return to_return;
00566 }
00567
00568
00569
00570 } else {
00571 #ifdef DEBUG_CROMP_CLIENT
00572 LOG("the channel has not been secured yet.");
00573 #endif
00574 }
00575 #endif
00576
00577 outcome to_return = cromp_common::pack_and_ship(request, item_id, max_tries);
00578 return to_return;
00579 }
00580
00581 outcome cromp_client::acquire(infoton * &response,
00582 const octopus_request_id &cmd_id, int timeout)
00583 {
00584 FUNCDEF("acquire");
00585 CHECK_LOCKOUT;
00586 outcome to_return = cromp_common::retrieve_and_restore(response, cmd_id,
00587 timeout);
00588
00589 unhandled_request *intermed = dynamic_cast<unhandled_request *>(response);
00590 if (intermed) {
00591
00592 to_return = intermed->_reason;
00593 }
00594
00595 decrypt_package_as_needed(to_return, response, cmd_id);
00596
00597 return to_return;
00598 }
00599
00600 void cromp_client::decrypt_package_as_needed(outcome &to_return,
00601 infoton * &response, const octopus_request_id &cmd_id)
00602 {
00603 FUNCDEF("decrypt_package_as_needed");
00604 #ifndef OMIT_CRYPTO_SUPPORT
00605 if (dynamic_cast<encryption_wrapper *>(response)) {
00606 if (!_encrypt_arm) {
00607 LOG(istring("received an encryption_wrapper but we are not "
00608 "encrypting, on ") + cmd_id.text_form());
00609 to_return = ENCRYPTION_MISMATCH;
00610 return;
00611 }
00612 byte_array transformed;
00613 outcome ret = _encrypt_arm->consume(*response, cmd_id, transformed);
00614 if ( (ret != OKAY) && (ret != PARTIAL) ) {
00615 LOG(istring("failed to decrypt wrapper for ") + cmd_id.text_form());
00616 to_return = ret;
00617 return;
00618 }
00619
00620 string_array classif;
00621 byte_array decro;
00622 bool worked = infoton::fast_unpack(transformed, classif, decro);
00623 if (!worked) {
00624 LOG("failed to fast_unpack the transformed data.");
00625 to_return = ENCRYPTION_MISMATCH;
00626 } else {
00627 infoton *new_req = NIL;
00628 outcome rest_ret = octo()->restore(classif, decro, new_req);
00629 if (rest_ret == tentacle::OKAY) {
00630
00631 WHACK(response);
00632 response = new_req;
00633 } else {
00634 LOG("failed to restore transformed infoton.");
00635 to_return = ENCRYPTION_MISMATCH;
00636 }
00637 }
00638 }
00639 #else
00640
00641 if (cmd_id._request_num || response) {}
00642 to_return = OKAY;
00643 #endif
00644 }
00645
00646
00647 #endif //CROMP_CLIENT_IMPLEMENTATION_FILE
00648