diff options
Diffstat (limited to 'base/tps/src/cms')
-rw-r--r-- | base/tps/src/cms/CertEnroll.cpp | 725 | ||||
-rw-r--r-- | base/tps/src/cms/ConnectionInfo.cpp | 78 | ||||
-rw-r--r-- | base/tps/src/cms/HttpConnection.cpp | 245 |
3 files changed, 1048 insertions, 0 deletions
diff --git a/base/tps/src/cms/CertEnroll.cpp b/base/tps/src/cms/CertEnroll.cpp new file mode 100644 index 000000000..89990d021 --- /dev/null +++ b/base/tps/src/cms/CertEnroll.cpp @@ -0,0 +1,725 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// Copyright (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +#include <string.h> + +#include "main/RA_Session.h" +#include "main/RA_Msg.h" +#include "main/Buffer.h" +#include "main/Util.h" +#include "engine/RA.h" +#include "cms/HttpConnection.h" +#include "cms/CertEnroll.h" + +// for public key processing +#include "pk11func.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "base64.h" +#include "nssb64.h" +#include "prlock.h" + +#include "main/Memory.h" + +Buffer * parseResponse(char * /*response*/); +ReturnStatus verifyProof(SECKEYPublicKey* , SECItem* , + unsigned short , unsigned char* , + unsigned char* ); + +#ifdef XP_WIN32 +#define TOKENDB_PUBLIC __declspec(dllexport) +#else /* !XP_WIN32 */ +#define TOKENDB_PUBLIC +#endif /* !XP_WIN32 */ + +/** + * Constructs handle for Certificate Enrollment + */ +TOKENDB_PUBLIC CertEnroll::CertEnroll() +{ +} + +/** + * Destructs handle for Certificate Enrollment + */ +TOKENDB_PUBLIC CertEnroll::~CertEnroll() +{ +} + +/** + * Revokes a certificate in the CA + * reason: + * 0 = Unspecified + * 1 = Key compromised + * 2 = CA key compromised + * 3 = Affiliation changed + * 4 = Certificate superseded + * 5 = Cessation of operation + * 6 = Certificate is on hold + * serialno: serial number in decimal + */ +TOKENDB_PUBLIC int CertEnroll::RevokeCertificate(const char *reason, const char *serialno, const char *connid, char *&o_status) +{ + char parameters[5000]; + char configname[5000]; + int num; + + PR_snprintf((char *)parameters, 5000, "op=revoke&revocationReason=%s&revokeAll=(certRecordId%%3D%s)&totalRecordCount=1", reason, serialno); + + PR_snprintf((char *)configname, 256, "conn.%s.servlet.revoke", connid); + char *servletID = (char*)RA::GetConfigStore()->GetConfigAsString(configname); + + PSHttpResponse *resp = sendReqToCA(servletID, parameters, connid); + + if (resp != NULL) { + char *content = resp->getContent(); + char *p = strstr(content, "status="); + num = *(p+7) - '0'; + RA::Debug("CertEnroll::RevokeCertificate", "serialno=%s reason=%s connid=%s status=%d", serialno, reason, connid, num); + if (num != 0) { + char *q = strstr(p, "error="); + q = q+6; + o_status = PL_strdup(q); + RA::Debug("CertEnroll::RevokeCertificate", "status string=%s", q); + } + if (content != NULL) { + resp->freeContent(); + content = NULL; + } + delete resp; + resp = NULL; + } else { + RA::Debug("CertEnroll::RevokeCertificate", "serialno=%s reason=%s connid=%s failed: resp is NULL"); + o_status = PL_strdup("resp from sendReqToCA is NULL"); + num = 1; //non-zero + } + return num; +} + +TOKENDB_PUBLIC int CertEnroll::UnrevokeCertificate(const char *serialno, const char *connid, + char *&o_status) +{ + char parameters[5000]; + char configname[5000]; + int num; + + PR_snprintf((char *)parameters, 5000, "serialNumber=%s",serialno); + + PR_snprintf((char *)configname, 256, "conn.%s.servlet.unrevoke", connid); + char *servletID = (char*)RA::GetConfigStore()->GetConfigAsString(configname); + + PSHttpResponse *resp = sendReqToCA(servletID, parameters, connid); + if (resp != NULL) { + // XXX - need to parse response + char *content = resp->getContent(); + char *p = strstr(content, "status="); + num = *(p+7) - '0'; + RA::Debug("CertEnroll::UnrevokeCertificate", "status=%d", num); + + if (num != 0) { + char *q = strstr(p, "error="); + q = q+6; + o_status = PL_strdup(q); + RA::Debug("CertEnroll::UnrevokeCertificate", "status string=%s", q); + } + + if (content != NULL) { + resp->freeContent(); + content = NULL; + } + delete resp; + resp = NULL; + } else { + RA::Debug("CertEnroll::UnrevokeCertificate", "serialno=%s reason=%s connid=%s failed: resp is NULL"); + o_status = PL_strdup("resp from sendReqToCA is NULL"); + num = 1; //non-zero + } + + return num; +} + +TOKENDB_PUBLIC Buffer *CertEnroll::RenewCertificate(PRUint64 serialno, const char *connid, const char *profileId, char *error_msg) +{ + char parameters[5000]; + char configname[5000]; + + RA::Debug("CertEnroll::RenewCertificate", "begins. profileId=%s",profileId); + // on CA, renewal expects parameter "serial_num" if renew by serial number + // ahh. need to allow larger serialno...later + PR_snprintf((char *)parameters, 5000, "serial_num=%u&profileId=%s&renewal=true", + (int)serialno, profileId); + RA::Debug("CertEnroll::RenewCertificate", "got parameters =%s", parameters); + //e.g. conn.ca1.servlet.renewal=/ca/ee/ca/profileSubmitSSLClient + PR_snprintf((char *)configname, 256, "conn.%s.servlet.renewal", connid); + const char *servlet = RA::GetConfigStore()->GetConfigAsString(configname); + if (servlet == NULL) { + RA::Debug("CertEnroll::RenewCertificate", + "Missing the configuration parameter for %s", configname); + PR_snprintf(error_msg, 512, "Missing the configuration parameter for %s", configname); + return NULL; + } + + // on CA, same profile servlet processes the renewal as well as enrollment + PSHttpResponse *resp = sendReqToCA(servlet, parameters, connid); + // XXX - need to parse response + Buffer * certificate = NULL; + if (resp != NULL) { + RA::Debug(LL_PER_PDU, "CertEnroll::RenewCertificate", + "sendReqToCA done"); + + certificate = parseResponse(resp); + RA::Debug(LL_PER_PDU, "CertEnroll::RenewCertificate", + "parseResponse done"); + + if( resp != NULL ) { + delete resp; + resp = NULL; + } + } else { + RA::Error("CertEnroll::RenewCertificate", + "sendReqToCA failure"); + PR_snprintf(error_msg, 512, "sendReqToCA failure"); + return NULL; + } + + return certificate; +} + + +/** + * Sends certificate request to CA for enrollment. + */ +Buffer * CertEnroll::EnrollCertificate( + SECKEYPublicKey *pk_parsed, + const char *profileId, + const char *uid, + const char *cuid /*token id*/, + const char *connid, + char *error_msg, + SECItem** encodedPublicKeyInfo) +{ + char parameters[5000]; + + SECItem* si = SECKEY_EncodeDERSubjectPublicKeyInfo(pk_parsed); + if (si == NULL) { + + RA::Error("CertEnroll::EnrollCertificate", + "SECKEY_EncodeDERSubjectPublicKeyInfo returns error"); + PR_snprintf(error_msg, 512, "SECKEY_EncodeDERSubjectPublicKeyInfo returns error"); + return NULL; + } + + // b64 encode it + char* pk_b64 = BTOA_ConvertItemToAscii(si); + + if(encodedPublicKeyInfo == NULL) + { + if( si != NULL ) { + SECITEM_FreeItem( si, PR_TRUE ); + si = NULL; + } + } + else + { + + *encodedPublicKeyInfo = si; + + } + + if (pk_b64 == NULL) { + RA::Error(LL_PER_PDU, "CertEnroll::EnrollCertificate", + "BTOA_ConvertItemToAscii returns error"); + + PR_snprintf(error_msg, 512, "BTOA_ConvertItemToAscii returns error"); + return NULL; + } + RA::Debug(LL_PER_PDU, "CertEnroll::EnrollCertificate", + "after BTOA_ConvertItemToAscii pk_b64=%s",pk_b64); + + char *url_pk = Util::URLEncode(pk_b64); + char *url_uid = Util::URLEncode(uid); + char *url_cuid = Util::URLEncode(cuid); + const char *servlet; + char configname[256]; + + PR_snprintf((char *)configname, 256, "conn.%s.servlet.enrollment", connid); + servlet = RA::GetConfigStore()->GetConfigAsString(configname); + + PR_snprintf((char *)parameters, 5000, "profileId=%s&tokencuid=%s&screenname=%s&publickey=%s", profileId, url_cuid, url_uid, url_pk); + + PSHttpResponse *resp = sendReqToCA(servlet, parameters, connid); + Buffer * certificate = NULL; + if (resp != NULL) { + RA::Debug(LL_PER_PDU, "CertEnroll::EnrollCertificate", + "sendReqToCA done"); + + certificate = parseResponse(resp); + RA::Debug(LL_PER_PDU, "CertEnroll::EnrollCertificate", + "parseResponse done"); + + if( resp != NULL ) { + delete resp; + resp = NULL; + } + } else { + RA::Error("CertEnroll::EnrollCertificate", + "sendReqToCA failure"); + PR_snprintf(error_msg, 512, "sendReqToCA failure"); + return NULL; + } + + if( pk_b64 != NULL ) { + PR_Free( pk_b64 ); + pk_b64 = NULL; + } + if( url_pk != NULL ) { + PR_Free( url_pk ); + url_pk = NULL; + } + if( url_uid != NULL ) { + PR_Free( url_uid ); + url_uid = NULL; + } + if( url_cuid != NULL ) { + PR_Free( url_cuid ); + url_cuid = NULL; + } + + return certificate; +} + +/** + * Extracts information from the public key blob and verify proof. + * + * Muscle Key Blob Format (RSA Public Key) + * --------------------------------------- + * + * The key generation operation places the newly generated key into + * the output buffer encoding in the standard Muscle key blob format. + * For an RSA key the data is as follows: + * + * Byte Encoding (0 for plaintext) + * + * Byte Key Type (1 for RSA public) + * + * Short Key Length (1024 û high byte first) + * + * Short Modulus Length + * + * Byte[] Modulus + * + * Short Exponent Length + * + * Byte[] Exponent + * + * + * Signature Format (Proof) + * --------------------------------------- + * + * The key generation operation creates a proof-of-location for the + * newly generated key. This proof is a signature computed with the + * new private key using the RSA-with-MD5 signature algorithm. The + * signature is computed over the Muscle Key Blob representation of + * the new public key and the challenge sent in the key generation + * request. These two data fields are concatenated together to form + * the input to the signature, without any other data or length fields. + * + * Byte[] Key Blob Data + * + * Byte[] Challenge + * + * + * Key Generation Result + * --------------------------------------- + * + * The key generation command puts the key blob and the signature (proof) + * into the output buffer using the following format: + * + * Short Length of the Key Blob + * + * Byte[] Key Blob Data + * + * Short Length of the Proof + * + * Byte[] Proof (Signature) Data + * + * @param blob the publickey blob to be parsed + * @param challenge the challenge generated by RA + * @return + * rc is 1 if success, -1 if failure + * pk is the public key resulted from parsing the blob. + * + ******/ + +SECKEYPublicKey *CertEnroll::ParsePublicKeyBlob(unsigned char *blob, + Buffer *challenge) +{ + char configname[5000]; + SECKEYPublicKey *pk = NULL; + + ReturnStatus rs; + rs.status = PR_FAILURE; + rs.statusNum = ::MSG_INVALID; + + if ((blob == NULL) || (challenge == NULL)) { + RA::Error(LL_PER_PDU, "CertEnroll::ParsePublicKeyBlob", "invalid input"); + return NULL; + } + + /* + * decode blob into structures + */ + + // offset to the beginning of the public key length. should be 0 + unsigned short pkeyb_len_offset = 0; + + unsigned short pkeyb_len = 0; + unsigned char* pkeyb; + unsigned short proofb_len = 0; + unsigned char* proofb; + + /* + * now, convert lengths + */ + // 1st, keyblob length + unsigned char len0 = blob[pkeyb_len_offset]; + unsigned char len1 = blob[pkeyb_len_offset +1]; + pkeyb_len = (unsigned short) ((len0 << 8) | (len1 & 0xFF)); + + RA::Debug(LL_PER_PDU, "CertEnroll::ParsePublicKeyBlob", + "pkeyb_len =%d",pkeyb_len); + + if (pkeyb_len <= 0) { + RA::Error("CertEnroll::ParsePublicKeyBlob", "public key blob length = %d", pkeyb_len); + return NULL; + } + // 2nd, proofblob length + unsigned short proofb_len_offset = pkeyb_len_offset + 2 + pkeyb_len; + len0 = blob[proofb_len_offset]; + len1 = blob[proofb_len_offset +1]; + proofb_len = (unsigned short) (len0 << 8 | len1 & 0xFF); + RA::Debug(LL_PER_PDU, "CertEnroll::ParsePublicKeyBlob", + "proofb_len =%d", proofb_len); + + // public key blob + pkeyb = &blob[pkeyb_len_offset + 2]; + + // proof blob + proofb = &blob[proofb_len_offset + 2]; + + SECItem siProof; + siProof.type = (SECItemType) 0; + siProof.data = (unsigned char *)proofb; + siProof.len = proofb_len; + + // convert pkeyb to pkey + // 1 byte encoding, 1 byte key type, 2 bytes key length, then the key + unsigned short pkey_offset = 4; + // now, convert lengths for modulus and exponent + len0 = pkeyb[pkey_offset]; + len1 = pkeyb[pkey_offset + 1]; + unsigned short mod_len = (len0 << 8 | len1); + + len0 = pkeyb[pkey_offset + 2 + mod_len]; + len1 = pkeyb[pkey_offset + 2 + mod_len + 1]; + unsigned short exp_len = (len0 << 8 | len1); + + + // public key mod blob + unsigned char * modb = &pkeyb[pkey_offset + 2]; + + // public key exp blob + unsigned char * expb = &pkeyb[pkey_offset + 2 + mod_len + 2]; + + // construct SECItem + SECItem siMod; + siMod.type = (SECItemType) 0; + siMod.data = (unsigned char *) modb; + siMod.len = mod_len; + + SECItem siExp; + siExp.type = (SECItemType) 0; + siExp.data = (unsigned char *)expb; + siExp.len = exp_len; + + // construct SECKEYRSAPublicKeyStr + SECKEYRSAPublicKeyStr rsa_pks; + rsa_pks.modulus = siMod; + rsa_pks.publicExponent = siExp; + + // construct SECKEYPublicKey + // this is to be returned + pk = (SECKEYPublicKey *) malloc(sizeof(SECKEYPublicKey)); + pk->keyType = rsaKey; + pk->pkcs11Slot = NULL; + pk->pkcs11ID = CK_INVALID_HANDLE; + pk->u.rsa = rsa_pks; + + PR_snprintf((char *)configname, 256, "general.verifyProof"); + int verifyProofEnable = RA::GetConfigStore()->GetConfigAsInt(configname, 0x1); + if (verifyProofEnable) { + rs = verifyProof(pk, &siProof, pkeyb_len, pkeyb, challenge); + if (rs.status == PR_FAILURE) { + RA::Error("CertEnroll::ParsePublicKeyBlob", + "verify proof failed"); + free(pk); + pk = NULL; + } + } + + return pk; +} + + +/** + * verify the proof. + * @param pk the public key from the input blob + * @param siProof the proof from the input blob + * @param pkeyb_len the length of the publickey blob + * @param pkeyb the public key blob + * @param challenge the challenge generated by RA + * + * @return + * returns success indication in case of success + * returns error message number as defined in ReturnStatus in Base.h + */ +ReturnStatus CertEnroll::verifyProof(SECKEYPublicKey* pk, SECItem* siProof, + unsigned short pkeyb_len, unsigned char* pkeyb, + Buffer* challenge) { + + ReturnStatus rs; + VFYContext * vc = NULL; + rs.statusNum = ::VRFY_SUCCESS; + rs.status = PR_SUCCESS; + + // verify proof (signature) + RA::Debug(LL_PER_PDU, "CertEnroll::verifyProof", + "verify proof begins"); + + vc = VFY_CreateContext(pk, siProof, SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE, NULL); + + if (vc == NULL) { + RA::Error("CertEnroll::verifyProof", + "VFY_CreateContext() failed"); + rs.status = PR_FAILURE; + rs.statusNum = ::VFY_BEGIN_FAILURE; + return rs; + } else { + RA::Debug(LL_PER_PDU, "CertEnroll::verifyProof", + "VFY_CreateContext() succeeded"); + } + + unsigned char proof[1024]; + int i =0; + for (i = 0; i<pkeyb_len; i++) { + proof[i] = pkeyb[i]; + } + // RA::DebugBuffer("CertEnroll::VerifyProof","VerifyProof:: challenge =", challenge); + unsigned char* chal = (unsigned char *)(BYTE *) (*challenge); + unsigned int j = 0; + for (j=0; j < challenge->size(); i++, j++) { + proof[i] = chal[j]; + // RA::Debug(LL_PER_PDU, "CertEnroll::VerifyProof","proof[%d]= %x", + // i, proof[i]); + } + + SECStatus vs = VFY_Begin(vc); + if (vs == SECSuccess) { + vs = VFY_Update(vc, (unsigned char *)proof , pkeyb_len + challenge->size()); + if (vs == SECSuccess) { + vs = VFY_End(vc); + if (vs == SECFailure) { + RA::Error("CertEnroll::verifyProof", + "VFY_End() failed pkeyb_len=%d challenge_size=%d", pkeyb_len, challenge->size()); + rs.statusNum = ::VFY_UPDATE_FAILURE; + rs.status = PR_FAILURE; + } + } else { + RA::Error("CertEnroll::verifyProof", + "VFY_Update() failed"); + rs.statusNum = ::VFY_UPDATE_FAILURE; + rs.status = PR_FAILURE; + } + } else { + RA::Error("CertEnroll::verifyProof", + "VFY_Begin() failed"); + + rs.statusNum = ::VFY_BEGIN_FAILURE; + rs.status = PR_FAILURE; + } + + if( vc != NULL ) { + VFY_DestroyContext( vc, PR_TRUE ); + vc = NULL; + } + RA::Debug(LL_PER_PDU, "CertEnroll::verifyProof", + " VFY_End() returned %d",vs); + + return rs; + +} + +/** + * sendReqToCA sends cert enrollment request via HTTPS to the CA + * @param pk normalized public key + * @param uid uid/screenname + * @param cuid cud number of the client token + * @param timeout timeout value for connection + * @return + * PSHttpResponse if success + * NULL if failure + */ +PSHttpResponse * CertEnroll::sendReqToCA(const char *servlet, const char *parameters, const char *connid) +{ + // compose http uri + + RA::Debug(LL_PER_PDU, "CertEnroll::sendReqToCA", + "begins"); + + HttpConnection *caConn = RA::GetCAConn(connid); + if (caConn == NULL) { + RA::Debug(LL_PER_PDU, "CertEnroll::sendReqToCA", "Failed to get CA Connection %s", connid); + RA::Error(LL_PER_PDU, "CertEnroll::sendReqToCA", "Failed to get CA Connection %s", connid); + return NULL; + } + // PRLock *ca_lock = RA::GetCALock(); + int ca_curr = RA::GetCurrentIndex(caConn); + int maxRetries = caConn->GetNumOfRetries(); + ConnectionInfo *connInfo = caConn->GetFailoverList(); + char **hostport = connInfo->GetHostPortList(); + int currRetries = 0; + + RA::Debug(LL_PER_PDU, "Before calling getResponse, caHostPort is %s", hostport[ca_curr]); + + PSHttpResponse * response = caConn->getResponse(ca_curr, servlet, parameters); + while (response == NULL) { + RA::Failover(caConn, connInfo->GetHostPortListLen()); + ca_curr = RA::GetCurrentIndex(caConn); + + if (++currRetries >= maxRetries) { + RA::Debug(LL_PER_PDU, "Used up all the retries. Response is NULL",""); + RA::Error("CertEnroll::sendReqToCA", "Failed connecting to CA after %d retries", currRetries); + if (caConn != NULL) { + RA::ReturnCAConn(caConn); + } + return NULL; + } + response = caConn->getResponse(ca_curr, servlet, parameters); + } + + if (caConn != NULL) { + RA::ReturnCAConn(caConn); + } + return response; +} + +/** + * parse the http response and retrieve the certificate. + * @param resp the response returned from http request + * @return + * The certificate in Buffer if success + * NULL if failure + */ +Buffer * CertEnroll::parseResponse(PSHttpResponse * resp) +{ + unsigned int i; + unsigned char blob[8192]; /* cert returned */ + int blob_len; /* cert length */ + char *certB64 = NULL; + char *certB64End = NULL; + unsigned int certB64Len = 0; + Buffer *cert = NULL; + char * response = NULL; + SECItem * outItemOpt = NULL; + + if (resp == NULL) { + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "no response found"); + return NULL; + } + response = resp->getContent(); + if (response == NULL) { + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "no content found"); + return NULL; + } + + // process result + // first look for errorCode="" to look for success clue + // and errorReason="..." to extract error reason + char pattern[20] = "errorCode=\"0\""; + char * err = strstr((char *)response, (char *)pattern); + + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "begin parsing err: %s", err); + + if (err == NULL) { + RA::Error("CertEnroll::parseResponse", + "can't find pattern for cert request response"); + goto endParseResp; + } + + // if success, look for "outputList.outputVal=" to extract + // the cert + certB64 = strstr((char *)response, "outputVal="); + certB64 = &certB64[11]; // point pass open " + + certB64End = strstr(certB64, "\";"); + *certB64End = '\0'; + + certB64Len = strlen(certB64); + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "certB64 len = %d", certB64Len); + + for (i=0; i<certB64Len-1 ; i++) { + if (certB64[i] == '\\') { certB64[i] = ' '; certB64[i+1] = ' '; } + } + + // b64 decode and put back in blob + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "b64 decode received cert"); + + outItemOpt = NSSBase64_DecodeBuffer(NULL, NULL, certB64, certB64Len); + if (outItemOpt == NULL) { + RA::Error("CertEnroll::parseResponse", + "b64 decode failed"); + + goto endParseResp; + } + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "b64 decode len =%d",outItemOpt->len); + + memcpy((char*)blob, (const char*)(outItemOpt->data), outItemOpt->len); + blob_len = outItemOpt->len; + + cert = new Buffer((BYTE *) blob, blob_len); + if( outItemOpt != NULL ) { + SECITEM_FreeItem( outItemOpt, PR_TRUE ); + outItemOpt = NULL; + } + + RA::Debug(LL_PER_PDU, "CertEnroll::parseResponse", + "finished"); + + endParseResp: + resp->freeContent(); + return cert; +} + diff --git a/base/tps/src/cms/ConnectionInfo.cpp b/base/tps/src/cms/ConnectionInfo.cpp new file mode 100644 index 000000000..3ab503c5d --- /dev/null +++ b/base/tps/src/cms/ConnectionInfo.cpp @@ -0,0 +1,78 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// Copyright (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "plstr.h" +#include "cms/ConnectionInfo.h" +#include "engine/RA.h" +#include "httpClient/httpc/engine.h" +#include "main/Util.h" +#include "main/Memory.h" + +#ifdef XP_WIN32 +#define TPS_PUBLIC __declspec(dllexport) +#else /* !XP_WIN32 */ +#define TPS_PUBLIC +#endif /* !XP_WIN32 */ + +/** + * Constructs a base processor. + */ +TPS_PUBLIC ConnectionInfo::ConnectionInfo () +{ + for( int i = 0; i < HOST_PORT_MEMBERS; i++ ) { + m_hostPortList[i] = NULL; + } +} + +/** + * Destructs processor. + */ +TPS_PUBLIC ConnectionInfo::~ConnectionInfo() +{ + for (int i=0; i<m_len; i++) { + if( m_hostPortList[i] != NULL ) { + PL_strfree( m_hostPortList[i] ); + m_hostPortList[i] = NULL; + } + } +} + +TPS_PUBLIC void ConnectionInfo::BuildFailoverList(const char *str) { + char *lasts = NULL; + char *tok = PL_strtok_r((char *)str, " ", &lasts); + m_len = 0; + while (tok != NULL) { + m_hostPortList[m_len] = PL_strdup(tok); + tok = PL_strtok_r(NULL, " ", &lasts); + m_len++; + } +} + +TPS_PUBLIC int ConnectionInfo::GetHostPortListLen() { + return m_len; +} + +TPS_PUBLIC char **ConnectionInfo::GetHostPortList() { + return m_hostPortList; +} + diff --git a/base/tps/src/cms/HttpConnection.cpp b/base/tps/src/cms/HttpConnection.cpp new file mode 100644 index 000000000..89f773557 --- /dev/null +++ b/base/tps/src/cms/HttpConnection.cpp @@ -0,0 +1,245 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// Copyright (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "cms/HttpConnection.h" +#include "main/Memory.h" +#include "main/NameValueSet.h" +#include "engine/RA.h" + +#ifdef XP_WIN32 +#define TPS_PUBLIC __declspec(dllexport) +#else /* !XP_WIN32 */ +#define TPS_PUBLIC +#endif /* !XP_WIN32 */ + +/** + * Constructs a base class for HttpConnection + */ +TPS_PUBLIC HttpConnection::HttpConnection(const char *id, ConnectionInfo *cinfo, int retries, int timeout, + bool isSSL, const char *nickname, bool keepAlive, NameValueSet *headers) +{ + m_failoverList = cinfo; + m_retries = retries; + m_timeout = timeout; + m_Id = PL_strdup(id); + m_isSSL = isSSL; + m_clientnickname = PL_strdup(nickname); + m_keepAlive = keepAlive; + m_headers = headers; + m_curr = 0; + m_lock = PR_NewLock(); +} + +/** + * Destructs processor. + */ +TPS_PUBLIC HttpConnection::~HttpConnection () +{ + if( m_clientnickname != NULL ) { + PL_strfree( m_clientnickname ); + m_clientnickname = NULL; + } + if( m_Id != NULL ) { + PL_strfree( m_Id ); + m_Id = NULL; + } + if( m_failoverList != NULL ) { + delete m_failoverList; + m_failoverList = NULL; + } + if( m_headers != NULL ) { + delete m_headers; + m_headers = NULL; + } + if( m_lock != NULL ) { + PR_DestroyLock( m_lock ); + m_lock = NULL; + } +} + +TPS_PUBLIC int HttpConnection::GetNumOfRetries() { + return m_retries; +} + +int HttpConnection::GetTimeout() { + return m_timeout; +} + +TPS_PUBLIC ConnectionInfo *HttpConnection::GetFailoverList() { + return m_failoverList; +} + +TPS_PUBLIC char *HttpConnection::GetId() { + return m_Id; +} + +TPS_PUBLIC bool HttpConnection::IsSSL() { + return m_isSSL; +} + +TPS_PUBLIC char * HttpConnection::GetClientNickname() { + return m_clientnickname; +} + +TPS_PUBLIC bool HttpConnection::IsKeepAlive() { + return m_keepAlive; +} + +TPS_PUBLIC PSHttpResponse *HttpConnection::getResponse(int index, const char *servlet, const char *body) { + char *host_port; + char uri[800]; + char *nickname; + const char *httpprotocol; + + ConnectionInfo *failoverList = GetFailoverList(); + int len = failoverList->ConnectionInfo::GetHostPortListLen(); + if (index >= len) { + index = len - 1; // use the last one + } + host_port= (failoverList->GetHostPortList())[index]; + + if (IsSSL()) { + httpprotocol = "https"; + } else { + httpprotocol = "http"; + } + + PR_snprintf((char *)uri, 800, + "%s://%s/%s", + httpprotocol, host_port, servlet); + + RA::Debug("HttpConnection::getResponse", "Send request to host %s servlet %s", host_port, servlet); + + RA::Debug(LL_PER_PDU, "HttpConnection::getResponse", "uri=%s", uri); + RA::Debug(LL_PER_PDU, "HttpConnection::getResponse", "host_port=%s", host_port); + + char *pPort = NULL; + char *pPortActual = NULL; + + + char hostName[512]; + + /* + * Isolate the host name, account for IPV6 numeric addresses. + * + */ + + if(host_port) + strncpy(hostName,host_port,512); + + pPort = hostName; + while(1) { + pPort = strchr(pPort, ':'); + if (pPort) { + pPortActual = pPort; + pPort++; + } else + break; + } + + if(pPortActual) + *pPortActual = '\0'; + + + /* + * Rifle through the values for the host + */ + + PRAddrInfo *ai; + void *iter; + PRNetAddr addr; + int family = PR_AF_INET; + + ai = PR_GetAddrInfoByName(hostName, PR_AF_UNSPEC, PR_AI_ADDRCONFIG); + if (ai) { + printf("%s\n", PR_GetCanonNameFromAddrInfo(ai)); + iter = NULL; + while ((iter = PR_EnumerateAddrInfo(iter, ai, 0, &addr)) != NULL) { + char buf[512]; + PR_NetAddrToString(&addr, buf, sizeof buf); + RA::Debug( LL_PER_PDU, + "HttpConnection::getResponse: ", + "Sending addr -- Msg='%s'\n", + buf ); + family = PR_NetAddrFamily(&addr); + RA::Debug( LL_PER_PDU, + "HttpConnection::getResponse: ", + "Sending family -- Msg='%d'\n", + family ); + break; + } + PR_FreeAddrInfo(ai); + + } + + PSHttpServer httpserver(host_port, family); + nickname = GetClientNickname(); + if (IsSSL()) + httpserver.setSSL(PR_TRUE); + else + httpserver.setSSL(PR_FALSE); + + PSHttpRequest httprequest(&httpserver, uri, HTTP11, 0); + if (IsSSL()) { + httprequest.setSSL(PR_TRUE); + if (nickname != NULL) { + httprequest.setCertNickName(nickname); + } else { + return NULL; + } + } else + httprequest.setSSL(PR_FALSE); + + httprequest.setMethod("POST"); + + if (body != NULL) { + httprequest.setBody( strlen(body), body); + } + + httprequest.addHeader( "Content-Type", "application/x-www-form-urlencoded" ); + if (m_headers != NULL) { + for (int i=0; i<m_headers->Size(); i++) { + char *name = m_headers->GetNameAt(i); + httprequest.addHeader(name, m_headers->GetValue(name)); + } + } + + if (IsKeepAlive()) + httprequest.addHeader( "Connection", "keep-alive" ); + + HttpEngine httpEngine; + return httpEngine.makeRequest(httprequest, httpserver, (PRIntervalTime)GetTimeout(), + PR_FALSE /*expectChunked*/); +} + +TPS_PUBLIC PRLock * HttpConnection::GetLock() { + return m_lock; +} + +TPS_PUBLIC int HttpConnection::GetCurrentIndex() { + return m_curr; +} + +TPS_PUBLIC void HttpConnection::SetCurrentIndex(int index) { + m_curr = index; +} |