int main (int argc, char **argv) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n" "(transport build: OTMessage -> TCP -> SSL)\n" "NOTE: IF YOU PREFER TO USE XmlRpc with Http transport, then rebuild using:\n" "\"make -f Makefile.rpc\" (but make sure the client is built the same way.)\n\n", OTLog::Version()); // ----------------------------------------------------------------------- // This object handles all the actual transaction notarization details. // (This file you are reading is a wrapper for OTServer, which adds the transport layer.) OTServer theServer; // ----------------------------------------------------------------------- #ifdef _WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD( 2, 2 ); int nWSA = WSAStartup( wVersionRequested, &wsaData ); OT_ASSERT_MSG(0 != nWSA, "Error calling WSAStartup.\n"); #endif OTLog::vOutput(0, "\n\nWelcome to Open Transactions, version %s.\n\n", OTLog::Version()); // ----------------------------------------------------------------------- OTString strCAFile, strDHFile, strKeyFile, strSSLPassword; if (argc < 2) { OTLog::vOutput(0, "Usage: transaction <SSL-password> <data_folder path>\n\n" "(Password defaults to '%s' if left blank on the command line.)\n" "(Folder defaults to '%s' if left blank.)\n", KEY_PASSWORD, SERVER_PATH_DEFAULT); strSSLPassword.Set(KEY_PASSWORD); OTLog::SetMainPath(SERVER_PATH_DEFAULT); } else if (argc < 3) { OTLog::vOutput(0, "Usage: transaction <SSL-password> <data_folder path>\n\n" "(Folder defaults to '%s' if left blank.)\n", SERVER_PATH_DEFAULT); strSSLPassword.Set(argv[1]); OTLog::SetMainPath(SERVER_PATH_DEFAULT); } else { strSSLPassword.Set(argv[1]); OTLog::SetMainPath(argv[2]); } strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE); strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ----------------------------------------------------------------------- // Init loads up server's nym so it can decrypt messages sent in envelopes. // It also does various other initialization work. // // (Envelopes prove that ONLY someone who actually had the server contract, // and had loaded it into his wallet, could ever connect to the server or // communicate with it. And if that person is following the contract, there // is only one server he can connect to, and one key he can use to talk to it.) OTLog::Output(0, "\n\nNow loading the server nym, which will also ask you for a password, to unlock\n" "its private key. (Default password is \"test\".)\n"); // Initialize SSL -- This MUST occur before any Private Keys are loaded! SFSocketGlobalInit(); // Any app that uses OT has to initialize SSL first. theServer.Init(); // Keys, etc are loaded here. // ----------------------------------------------------------------------- // We're going to listen on the same port that is listed in our server contract. // // OTString strHostname; // The hostname of this server, according to its own contract. int nPort=0; // The port of this server, according to its own contract. OT_ASSERT_MSG(theServer.GetConnectInfo(strHostname, nPort), "Unable to find my own connect info (which is in my server contract BTW.)\n"); const int nServerPort = nPort; // ----------------------------------------------------------------------- SFSocket *socket = NULL; listOfConnections theConnections; // Alloc Socket socket = SFSocketAlloc(); OT_ASSERT_MSG(NULL != socket, "SFSocketAlloc Failed\n"); // Initialize SSL Socket int nSFSocketInit = SFSocketInit(socket, strCAFile.Get(), strDHFile.Get(), strKeyFile.Get(), strSSLPassword.Get(), NULL); OT_ASSERT_MSG(nSFSocketInit >= 0, "SFSocketInit Context Failed\n"); // Listen on Address/Port int nSFSocketListen = SFSocketListen(socket, INADDR_ANY, nServerPort); OT_ASSERT_MSG(nSFSocketListen >= 0, "nSFSocketListen Failed\n"); theServer.ActivateCron(); do { SFSocket * clientSocket = NULL; // Accept Client Connection if (NULL != (clientSocket = SFSocketAccept(socket))) { OTClientConnection * pClient = new OTClientConnection(*clientSocket, theServer); theConnections.push_back(pClient); OTLog::Output(0, "Accepting new connection.\n"); } // READ THROUGH ALL CLIENTS HERE, LOOP A LIST // AND process in-buffer onto our list of messages. OTClientConnection * pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { // Here we read the bytes from the pipe into the client's buffer // As necessary this function also processes those bytes into OTMessages // and adds them to the Input List for that client. pConnection->ProcessBuffer(); } } // Now loop through them all again, and process their messages onto the reply list. pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextInputMessage()) { OTMessage * pReply = new OTMessage; OT_ASSERT(NULL != pReply); if (theServer.ProcessUserCommand(*pMsg, *pReply, pConnection)) { OTLog::vOutput(0, "Successfully processed user command: %s.\n", pMsg->m_strCommand.Get()); pConnection->AddToOutputList(*pReply); } else { OTLog::Output(0, "Unable to process user command in XML, or missing payload, in main.\n"); delete pReply; pReply = NULL; } // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // Now loop through them all again, and process their replies down the pipe pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextOutputMessage()) { pConnection->ProcessReply(*pMsg); // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // The Server now processes certain things on a regular basis. // This method call is what gives it the opportunity to do that. theServer.ProcessCron(); // Now go to sleep for a tenth of a second. // (Main loop processes ten times per second, currently.) OTLog::SleepMilliseconds(100); // 100 ms == (1 second / 10) } while (1); // Close and Release Socket Resources SFSocketRelease(socket); #ifdef _WIN32 WSACleanup(); #endif return(0); }
int main (int argc, char **argv) { OTLog::vOutput(0, "\n\nWelcome to Open Transactions... Test Server -- version %s\n" "(transport build: OTMessage -> TCP -> SSL)\n" "IF YOU PREFER TO USE XmlRpc with HTTP, then rebuild from main folder like this:\n\n" "cd ..; make clean; make rpc\n\n", OTLog::Version()); // ----------------------------------------------------------------------- // This object handles all the actual transaction notarization details. // (This file you are reading is a wrapper for OTServer, which adds the transport layer.) OTServer theServer; // ----------------------------------------------------------------------- #ifdef _WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD( 2, 2 ); int err = WSAStartup( wVersionRequested, &wsaData ); /* Tell the user that we could not find a usable */ /* Winsock DLL. */ OT_ASSERT_MSG((err == 0), "WSAStartup failed!\n"); /* Confirm that the WinSock DLL supports 2.2. */ /* Note that if the DLL supports versions greater */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ bool bWinsock = (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2); /* Tell the user that we could not find a usable */ /* WinSock DLL. */ if (!bWinsock) WSACleanup(); // do cleanup. OT_ASSERT_MSG((!bWinsock), "Could not find a usable version of Winsock.dll\n"); /* The Winsock DLL is acceptable. Proceed to use it. */ /* Add network programming using Winsock here */ /* then call WSACleanup when done using the Winsock dll */ OTLog::vOutput(0,"The Winsock 2.2 dll was found okay\n"); #endif OTLog::vOutput(0, "\n\nWelcome to Open Transactions, version %s.\n\n", OTLog::Version()); // ----------------------------------------------------------------------- // The beginnings of an INI file!! #ifndef _WIN32 // if UNIX (NOT windows) wordexp_t exp_result; wordexp(OT_INI_FILE_DEFAULT, &exp_result, 0); const OTString strIniFileDefault(exp_result.we_wordv[0]); wordfree(&exp_result); #else const OTString strIniFileDefault(OT_INI_FILE_DEFAULT); #endif OTString strPath(SERVER_PATH_DEFAULT); { CSimpleIniA ini; SI_Error rc = ini.LoadFile(strIniFileDefault.Get()); if (rc >=0) { const char * pVal = ini.GetValue("paths", "server_path", SERVER_PATH_DEFAULT); // todo stop hardcoding. if (NULL != pVal) { strPath.Set(pVal); OTLog::vOutput(0, "Reading ini file (%s). \n Found Server data_folder path: %s \n", strIniFileDefault.Get(), strPath.Get()); } else { strPath.Set(SERVER_PATH_DEFAULT); OTLog::vOutput(0, "Reading ini file (%s): \n Failed reading Server data_folder path. Using: %s \n", strIniFileDefault.Get(), strPath.Get()); } } else { strPath.Set(SERVER_PATH_DEFAULT); OTLog::vOutput(0, "Unable to load ini file (%s) to find data_folder path\n Will assume that server data_folder is at path: %s \n", strIniFileDefault.Get(), strPath.Get()); } } // ----------------------------------------------------------------------- OTString strCAFile, strDHFile, strKeyFile; //, strSSLPassword; if (argc < 1) { OTLog::vOutput(0, "\n==> USAGE: %s [absolute_path_to_data_folder]\n\n" // OTLog::vOutput(0, "\n==> USAGE: %s <SSL-password> [absolute_path_to_data_folder]\n\n" #if defined (FELLOW_TRAVELER) // "(Password defaults to '%s' if left blank.)\n" "(Folder defaults to '%s' if left blank.)\n" #else "The test password is always 'test'.\n" "OT will try to read the data_folder path from your ini file. If you prefer\n" "to specify it at the command line, and you want to see the exact path, type:\n" " cd data_folder && pwd && cd ..\n" #endif "\n\n", argv[0] #if defined (FELLOW_TRAVELER) // , KEY_PASSWORD, , strPath.Get() #endif ); #if defined (FELLOW_TRAVELER) // strSSLPassword.Set(KEY_PASSWORD); OTLog::SetMainPath(strPath.Get()); #else exit(1); #endif } else if (argc < 2) { // strSSLPassword.Set(argv[1]); OTLog::SetMainPath(strPath.Get()); } else { // strSSLPassword.Set(argv[1]); OTLog::SetMainPath(argv[1]); // formerly [2] } OTLog::vOutput(0, "Using as path to data folder: %s\n", OTLog::Path()); strCAFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), CA_FILE); strDHFile. Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), DH_FILE); strKeyFile.Format("%s%s%s", OTLog::Path(), OTLog::PathSeparator(), KEY_FILE); // ----------------------------------------------------------------------- // Init loads up server's nym so it can decrypt messages sent in envelopes. // It also does various other initialization work. // // (Envelopes prove that ONLY someone who actually had the server contract, // and had loaded it into his wallet, could ever connect to the server or // communicate with it. And if that person is following the contract, there // is only one server he can connect to, and one key he can use to talk to it.) OTLog::Output(0, "\n\nNow loading the server nym, which will also ask you for a password, to unlock\n" "its private key. (Default password is \"test\".)\n"); // Initialize SSL -- This MUST occur before any Private Keys are loaded! SFSocketGlobalInit(); // Any app that uses OT has to initialize SSL first. theServer.Init(); // Keys, etc are loaded here. // ----------------------------------------------------------------------- // We're going to listen on the same port that is listed in our server contract. // // OTString strHostname; // The hostname of this server, according to its own contract. int nPort=0; // The port of this server, according to its own contract. OT_ASSERT_MSG(theServer.GetConnectInfo(strHostname, nPort), "Unable to find my own connect info (which is in my server contract BTW.)\n"); const int nServerPort = nPort; // ----------------------------------------------------------------------- SFSocket *socket = NULL; listOfConnections theConnections; // Alloc Socket socket = SFSocketAlloc(); OT_ASSERT_MSG(NULL != socket, "SFSocketAlloc Failed\n"); // Initialize SSL Socket int nSFSocketInit = SFSocketInit(socket, strCAFile.Get(), strDHFile.Get(), strKeyFile.Get(), strSSLPassword.Get(), NULL); OT_ASSERT_MSG(nSFSocketInit >= 0, "SFSocketInit Context Failed\n"); // Listen on Address/Port int nSFSocketListen = SFSocketListen(socket, INADDR_ANY, nServerPort); OT_ASSERT_MSG(nSFSocketListen >= 0, "nSFSocketListen Failed\n"); theServer.ActivateCron(); do { SFSocket * clientSocket = NULL; // Accept Client Connection if (NULL != (clientSocket = SFSocketAccept(socket))) { OTClientConnection * pClient = new OTClientConnection(*clientSocket, theServer); theConnections.push_back(pClient); OTLog::Output(0, "Accepting new connection.\n"); } // READ THROUGH ALL CLIENTS HERE, LOOP A LIST // AND process in-buffer onto our list of messages. OTClientConnection * pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { // Here we read the bytes from the pipe into the client's buffer // As necessary this function also processes those bytes into OTMessages // and adds them to the Input List for that client. pConnection->ProcessBuffer(); } } // Now loop through them all again, and process their messages onto the reply list. pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextInputMessage()) { OTMessage * pReply = new OTMessage; OT_ASSERT(NULL != pReply); if (theServer.ProcessUserCommand(*pMsg, *pReply, pConnection)) { OTLog::vOutput(0, "Successfully processed user command: %s.\n", pMsg->m_strCommand.Get()); pConnection->AddToOutputList(*pReply); } else { OTLog::Output(0, "Unable to process user command in XML, or missing payload, in main.\n"); delete pReply; pReply = NULL; } // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // Now loop through them all again, and process their replies down the pipe pConnection = NULL; for (listOfConnections::iterator ii = theConnections.begin(); ii != theConnections.end(); ++ii) { if (pConnection = *ii) { OTMessage * pMsg = NULL; while (pMsg = pConnection->GetNextOutputMessage()) { pConnection->ProcessReply(*pMsg); // I am responsible to delete this here. delete pMsg; pMsg = NULL; } } } // The Server now processes certain things on a regular basis. // This method call is what gives it the opportunity to do that. theServer.ProcessCron(); // Now go to sleep for a tenth of a second. // (Main loop processes ten times per second, currently.) OTLog::SleepMilliseconds(100); // 100 ms == (1 second / 10) } while (1); // Close and Release Socket Resources SFSocketRelease(socket); #ifdef _WIN32 WSACleanup(); #endif return(0); }
// true == YES, DISCONNECT m_pSocket, something must have gone wrong. // false == NO, do NOT disconnect m_pSocket, everything went wonderfully! // bool ProcessMessage_ZMQ(OTServer & theServer, const std::string & str_Message, std::string & str_Reply) { if (str_Message.size() < 1) return false; const char * szFunc = "ProcessMessage_ZMQ"; // -------------------- // return value. std::string resultString = ""; // Whatever we put in this string is what will get returned. // First we grab the client's message OTASCIIArmor ascMessage; ascMessage.MemSet(str_Message.data(), str_Message.size()); // ------------------ // // OTPayload thePayload; // thePayload.SetPayloadSize(str_Message.size()); // memcpy((void*)thePayload.GetPayloadPointer(), str_Message.data(), str_Message.size()); // ---------------------------------------------------------------------- // OTLog::vError("Envelope: \n%s\n Size: %ld\n", ascMessage.Get(), ascMessage.GetLength()); bool bReturnVal = false; // "false" == no, do NOT disconnect. No errors. ("True" means YES, DISCONNECT!) OTMessage theMsg, theReply; // we'll need these in a sec... // OTEnvelope theEnvelope(ascMessage); OTEnvelope theEnvelope; if (false == theEnvelope.SetAsciiArmoredData(ascMessage)) { OTLog::vError("%s: Error retrieving envelope.\n", szFunc); bReturnVal = true; // disconnect the socket! } else { // Now the base64 is decoded and the envelope is in binary form again. OTLog::vOutput(2, "%s: Successfully retrieved envelope from ZMQ message...\n", szFunc); OTString strEnvelopeContents; // OTString strPubkeyPath("TESTPUBKEY.txt"); // theServer.GetServerNym().SavePublicKey(strPubkeyPath); // Decrypt the Envelope. if (false == theEnvelope.Open(theServer.GetServerNym(), strEnvelopeContents)) // now strEnvelopeContents contains the decoded message. { OTLog::vError("%s: Unable to open envelope.\n", szFunc); bReturnVal = true; // disconnect the socket! } else { // All decrypted--now let's load the results into an OTMessage. // No need to call theMsg.ParseRawFile() after, since // LoadContractFromString handles it. // if (strEnvelopeContents.Exists() && theMsg.LoadContractFromString(strEnvelopeContents)) { theReply.m_strCommand.Format("@%s", theMsg.m_strCommand.Get()); theReply.m_strNymID = theMsg.m_strNymID; // UserID theReply.m_strServerID = theMsg.m_strServerID; // ServerID, a hash of the server contract. theReply.m_bSuccess = false; // The default reply. In fact this is probably superfluous. // In case you want to see all the incoming messages... // OTLog::vOutput(0, "%s\n\n", strEnvelopeContents.Get()); // By constructing this without a socket, I put it in ZMQ mode, instead of tcp/ssl. OTClientConnection theClient(theServer); // By optionally passing in &theClient, the client Nym's public key will be // set on it whenever verification is complete. (So for the reply, I'll // have the key and thus I'll be able to encrypt reply to the recipient.) if (false == theServer.ProcessUserCommand(theMsg, theReply, &theClient)) { const OTString s1(theMsg), s2(theReply); OTLog::vOutput(0, "%s: Unable to process user command.\n\n ********** " "REQUEST:\n\n%s\n\n ********** RESPONSE:\n\n%s\n\n", szFunc, s1.Get(), s2.Get()); // NOTE: normally you would even HAVE a true or false if we're in this block. ProcessUserCommand() // is what tries to process a command and then sets false if/when it fails. Until that point, you // wouldn't get any server reply. I'm now changing this slightly, so you still get a reply (defaulted // to success==false.) That way if a client needs to re-sync his request number, he will get the false // and therefore know to resync the # as his next move, vs being stuck with no server reply (and thus // stuck with a bad socket.) // We sign the reply here, but not in the else block, since it's already signed in cases where // ProcessUserCommand() is a success, by the time that call returns. theReply.m_bSuccess = false; // Since the process call definitely failed, I'm making sure this here is definitely set to false (even though it probably was already.) theReply.SignContract(theServer.GetServerNym()); theReply.SaveContract(); } else // At this point the reply is ready to go, and theClient has the public key of the recipient... OTLog::vOutput(1, "%s: Successfully processed user command: %s.\n", szFunc, theMsg.m_strCommand.Get()); // ------------------------------------------------- // The transaction is now processed (or not), and the server's reply message is in theReply. // Let's seal it up to the recipient's nym (in an envelope) and send back to the user... // OTEnvelope theRecipientEnvelope; bool bSealed = theClient.SealMessageForRecipient(theReply, theRecipientEnvelope); if (false == bSealed) { OTLog::vOutput(0, "%s: Unable to seal envelope. (No reply will be sent.)\n", szFunc); bReturnVal = true; // disconnect the socket! } else { // OTPayload theReplyPayload; // theReplyPayload.SetEnvelope(theRecipientEnvelope); // resultString = ascReply.Get(); // resultString.assign(theReplyPayload.GetPayloadPointer(), theReplyPayload.GetPayloadSize()); OTASCIIArmor ascReply; if (theRecipientEnvelope.GetAsciiArmoredData(ascReply)) resultString.assign(ascReply.Get(), ascReply.GetLength()); } } else { OTLog::vError("%s: Error loading message from envelope contents:\n\n%s\n\n", szFunc, strEnvelopeContents.Get()); bReturnVal = true; // disconnect the socket! } } } // ---------------------------------------------------------------------- str_Reply = resultString; return bReturnVal; } // ProcessMessage_ZMQ