21#if HAVE_LIBCRYPTO_EVP_PKEY_GET_DEFAULT_DIGEST_NAME
23 const auto pkey = key.get();
28 char defaultDigestName[80] =
"";
29 const auto nameGetterResult = EVP_PKEY_get_default_digest_name(pkey, defaultDigestName,
sizeof(defaultDigestName));
30 debugs(83, 3,
"nameGetterResult=" << nameGetterResult <<
" defaultDigestName=" << defaultDigestName);
31 if (nameGetterResult <= 0) {
41 if (nameGetterResult == 2 && strcmp(defaultDigestName,
"UNDEF") == 0)
60 const auto digestOrNil =
signWithDigest(key) ? &availableDigest :
nullptr;
61 return X509_sign(&cert, key.get(), digestOrNil);
67 if (ERR_peek_last_error()) {
70 while (ERR_get_error()) {}
84 unsigned int reported = 0;
85 while (
const auto errorToForget = ERR_get_error())
86 os <<
Debug::Extra <<
"OpenSSL-saved error #" << (++reported) <<
": 0x" <<
asHex(errorToForget);
90[[ noreturn ]]
static void
99static Security::PrivateKeyPointer
106 if (EVP_PKEY_keygen_init(rsa.get()) <= 0)
110 if (EVP_PKEY_CTX_set_rsa_keygen_bits(rsa.get(), num) <= 0)
114 EVP_PKEY *pkey =
nullptr;
115 if (EVP_PKEY_keygen(rsa.get(), &pkey) <= 0)
118 return Security::PrivateKeyPointer(pkey);
131 bn.reset(BN_dup(serial));
136 if (!BN_rand(bn.get(), 64, 0, 0))
140 if (ai && !BN_to_ASN1_INTEGER(bn.get(), ai))
147 bufferToWrite.clear();
154 if (!PEM_write_bio_X509 (bio.get(), cert.
get()))
157 if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(),
nullptr,
nullptr, 0,
nullptr,
nullptr))
161 long len = BIO_get_mem_data(bio.get(), &ptr);
165 bufferToWrite = std::string(ptr, len);
178 if (!PEM_write_bio_X509 (bio.get(), cert.
get()))
182 long len = BIO_get_mem_data(bio.get(), &ptr);
186 if (!bufferToWrite.empty())
187 bufferToWrite.append(
" ");
189 bufferToWrite.append(ptr, len);
196 BIO_puts(bio.get(), bufferToRead);
208 EVP_PKEY * pkeyPtr =
nullptr;
209 pkey.resetWithoutLocking(PEM_read_bio_PrivateKey(bio.get(), &pkeyPtr,
nullptr,
nullptr));
223 const auto castedBuffer =
const_cast<char*
>(bufferToRead);
224 if (
const auto bio = BIO_new_mem_buf(castedBuffer, -1))
226 const auto savedErrno = errno;
227 ThrowErrors(
"cannot allocate OpenSSL BIO structure", savedErrno,
Here());
237 std::string cn = rawCn;
244 pos = cn.find(
'.', pos + 1);
245 }
while (pos != std::string::npos && (cn.length() - pos + 2) >
MaxCnLen);
249 if (pos == std::string::npos || cn.find(
'.', pos + 1) == std::string::npos)
252 std::string fixedCn(1,
'*');
253 fixedCn.append(cn.c_str() + pos);
259 if (cn.length() > 2 && *cn.begin() ==
'[' && *cn.rbegin() ==
']')
260 cn = cn.substr(1, cn.size()-2);
262 X509_NAME *name = X509_get_subject_name(cert.
get());
266 int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
268 X509_NAME_ENTRY *tmp = X509_NAME_get_entry(name, loc);
269 X509_NAME_delete_entry(name, loc);
270 X509_NAME_ENTRY_free(tmp);
274 return X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
275 (
unsigned char *)(cn.c_str()), -1, -1, 0);
293 setValidAfter(false),
294 setValidBefore(false),
295 setCommonName(false),
304 if (sig && sig->data) {
305 const unsigned char *s = sig->data;
306 for (
int i = 0; i < sig->length; ++i) {
308 snprintf(hex,
sizeof(hex),
"%02x", s[i]);
317 static std::string certKey;
319 certKey.reserve(4096);
323 if (certKey.empty()) {
324 certKey.append(
"/CN=", 4);
329 certKey.append(
"+SetValidAfter=on", 17);
332 certKey.append(
"+SetValidBefore=on", 18);
335 certKey.append(
"+SetCommonName=", 15);
340 certKey.append(
"+Sign=", 6);
344 if (properties.
signHash !=
nullptr) {
345 certKey.append(
"+SignHash=", 10);
346 certKey.append(EVP_MD_name(properties.
signHash));
361 if (!mimicCert.
get() || !issuerCert.
get())
366 bool addKeyId =
false, addIssuer =
false;
368 addKeyId = (akid.get()->keyid !=
nullptr);
369 addIssuer = (akid.get()->issuer && akid.get()->serial);
372 if (!addKeyId && !addIssuer)
379 const int index = X509_get_ext_by_NID(issuerCert.
get(), NID_subject_key_identifier, -1);
380 if (index >= 0 && (ext = X509_get_ext(issuerCert.
get(), index))) {
381 issuerKeyId.reset((ASN1_OCTET_STRING *)X509V3_EXT_d2i(ext));
387 if (issuerKeyId.get() ==
nullptr || addIssuer) {
388 issuerName.reset(X509_NAME_dup(X509_get_issuer_name(issuerCert.
get())));
389 issuerSerial.reset(ASN1_INTEGER_dup(X509_get_serialNumber(issuerCert.
get())));
393 if (!theAuthKeyId.get())
395 theAuthKeyId.get()->keyid = issuerKeyId.release();
396 if (issuerName && issuerSerial) {
398 if (genNames.get()) {
399 if (GENERAL_NAME *aname = GENERAL_NAME_new()) {
400 sk_GENERAL_NAME_push(genNames.get(), aname);
401 aname->type = GEN_DIRNAME;
402 aname->d.dirn = issuerName.release();
403 theAuthKeyId.get()->issuer = genNames.release();
404 theAuthKeyId.get()->serial = issuerSerial.release();
411 if (!theAuthKeyId.get()->keyid && (!theAuthKeyId.get()->issuer || !theAuthKeyId.get()->serial))
414 const X509V3_EXT_METHOD *method = X509V3_EXT_get_nid(NID_authority_key_identifier);
418 unsigned char *ext_der =
nullptr;
419 int ext_len = ASN1_item_i2d((ASN1_VALUE *)theAuthKeyId.get(), &ext_der, ASN1_ITEM_ptr(method->it));
421 extOct.get()->data = ext_der;
422 extOct.get()->length = ext_len;
424 if (!extAuthKeyId.get())
427 if (!X509_add_ext(cert.
get(), extAuthKeyId.get(), -1))
441 static int extensions[]= {
444 NID_basic_constraints,
463 const Security::PrivateKeyPointer certKey(X509_get_pubkey(mimicCert.
get()));
464#if OPENSSL_VERSION_MAJOR < 3
467 const auto rsaPkey = EVP_PKEY_is_a(certKey.get(),
"RSA") == 1;
472 for (
int i = 0; (nid = extensions[i]) != 0; ++i) {
473 const int pos = X509_get_ext_by_NID(mimicCert.
get(), nid, -1);
474 if (X509_EXTENSION *ext = X509_get_ext(mimicCert.
get(), pos)) {
476 if (X509_add_ext(cert.
get(), ext, -1))
478 if (nid == NID_key_usage && !rsaPkey) {
484 const int p = X509_get_ext_by_NID(cert.
get(), NID_key_usage, -1);
485 if ((ext = X509_get_ext(cert.
get(), p)) !=
nullptr) {
486 ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING *)X509V3_EXT_d2i(ext);
487 ASN1_BIT_STRING_set_bit(keyusage, KeyEncipherment, 1);
490 const X509V3_EXT_METHOD *method = X509V3_EXT_get(ext);
491 assert(method && method->it);
492 unsigned char *ext_der =
nullptr;
493 int ext_len = ASN1_item_i2d((ASN1_VALUE *)keyusage,
495 (
const ASN1_ITEM *)ASN1_ITEM_ptr(method->it));
497 ASN1_OCTET_STRING *ext_oct = ASN1_OCTET_STRING_new();
498 ext_oct->data = ext_der;
499 ext_oct->length = ext_len;
500 X509_EXTENSION_set_data(ext, ext_oct);
502 ASN1_OCTET_STRING_free(ext_oct);
503 ASN1_BIT_STRING_free(keyusage);
521 return SBuf(
reinterpret_cast<const char *
>(buffer.data), buffer.length);
525static std::optional<SBuf>
528 unsigned char *utfBuffer =
nullptr;
529 const auto conversionResult = ASN1_STRING_to_UTF8(&utfBuffer, &asnBuffer);
530 if (conversionResult < 0) {
535 const auto utfChars =
reinterpret_cast<char *
>(utfBuffer);
536 const auto utfLength =
static_cast<size_t>(conversionResult);
538 return SBuf(utfChars, utfLength);
541std::optional<AnyP::Host>
549std::optional<AnyP::Host>
552 const auto cn = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(&name, cnIndex));
554 debugs(83, 7,
"no CN at " << cnIndex);
579 X509_NAME *name = X509_get_subject_name(cert.
get());
583 const int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
595 const auto altNamePrefix = cn->ip() ?
"IP:" :
"DNS:";
596 auto altName =
ToSBuf(altNamePrefix, *cn);
597 const auto ext = X509V3_EXT_conf_nid(
nullptr,
nullptr, NID_subject_alt_name, altName.c_str());
601 const bool result = X509_add_ext(cert.
get(), ext, -1);
603 X509_EXTENSION_free(ext);
613 if (X509_NAME *name = X509_get_subject_name(properties.
mimicCert.
get())) {
615 X509_set_subject_name(cert.
get(), name);
632 ASN1_TIME *aTime =
nullptr;
655 int addedExtensions = 0;
656 bool useCommonNameAsAltName =
true;
659 unsigned char *alStr;
661 alStr = X509_alias_get0(properties.
mimicCert.
get(), &alLen);
663 X509_alias_set1(cert.
get(), alStr, alLen);
669 int pos = X509_get_ext_by_NID(properties.
mimicCert.
get(), NID_subject_alt_name, -1);
670 X509_EXTENSION *ext=X509_get_ext(properties.
mimicCert.
get(), pos);
672 if (X509_add_ext(cert.
get(), ext, -1))
676 useCommonNameAsAltName =
false;
687 X509_set_version(cert.
get(), 2);
704 if (!X509_set_pubkey(cert.
get(), pkey.get()))
716 ret = X509_set_issuer_name(cert.
get(), X509_get_subject_name(properties.
signWithX509.
get()));
718 ret = X509_set_issuer_name(cert.
get(), X509_get_subject_name(cert.
get()));
733 certToStore = std::move(cert);
734 pkeyToStore = std::move(pkey);
743 BIGNUM *serial =
nullptr;
744 serial = BN_bin2bn(md, n,
nullptr);
747 if (BN_is_zero(serial) ==
true)
751 assert(BN_num_bits(serial) <= 160);
759 if (BN_is_bit_set(serial, 159))
760 BN_clear_bit(serial, 159);
770 unsigned char md[EVP_MAX_MD_SIZE];
772 if (!X509_digest(cert.
get(),EVP_sha1(),md,&n))
781 unsigned char md[EVP_MAX_MD_SIZE];
783 if (!X509_pubkey_digest(cert.
get(),EVP_sha1(),md,&n))
793 Security::PrivateKeyPointer fakePkey;
798 serial.reset(BN_new());
799 BN_zero(serial.get());
828 bio.reset(BIO_new(BIO_s_file()));
831 if (!BIO_read_filename(bio.get(), filename))
841 if (
const auto cert = PEM_read_bio_X509(bio.get(),
nullptr,
nullptr,
nullptr))
843 const auto savedErrno = errno;
850 if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
853 (void)ERR_get_error();
854 if (!ERR_peek_last_error())
858 ThrowErrors(
"cannot read a PEM-encoded certificate", savedErrno,
Here());
875 if (EVP_PKEY *akey = PEM_read_bio_PrivateKey(bio.get(),
nullptr, passwd_callback,
nullptr)) {
876 pkey.resetWithoutLocking(akey);
896 bio.reset(BIO_new(BIO_s_file()));
899 if (!BIO_write_filename(bio.get(),
const_cast<char *
>(filename)))
909 if (!PEM_write_bio_X509(bio.get(), cert.
get()))
919 if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(),
nullptr,
nullptr, 0,
nullptr,
nullptr))
935 tm.data = (
unsigned char *)date;
936 tm.length = strlen(date);
938 return (X509_cmp_current_time(&tm) > 0);
950 if ((aTime->length + 3) > bufLen)
954 if (aTime->type == V_ASN1_UTCTIME) {
955 if (aTime->data[0] >
'5') {
966 memcpy(str, aTime->data, aTime->length);
967 str[aTime->length] =
'\0';
973 char strTime1[64], strTime2[64];
979 return strcmp(strTime1, strTime2);
989 if (X509_check_issued(properties.
signWithX509.
get(), cert) != X509_V_OK)
999 X509_NAME *cert1_name = X509_get_subject_name(cert);
1000 X509_NAME *cert2_name = X509_get_subject_name(cert2);
1001 if (X509_NAME_cmp(cert1_name, cert2_name) != 0)
1028 alStr1 = (
char *)X509_alias_get0(cert, &alLen);
1029 char *alStr2 = (
char *)X509_alias_get0(cert2, &alLen);
1030 if ((!alStr1 && alStr2) || (alStr1 && !alStr2) ||
1031 (alStr1 && alStr2 && strcmp(alStr1, alStr2)) != 0)
1035 STACK_OF(GENERAL_NAME) * cert1_altnames;
1036 cert1_altnames = (
STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name,
nullptr,
nullptr);
1037 STACK_OF(GENERAL_NAME) * cert2_altnames;
1038 cert2_altnames = (
STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert2, NID_subject_alt_name,
nullptr,
nullptr);
1040 if (cert1_altnames) {
1041 int numalts = sk_GENERAL_NAME_num(cert1_altnames);
1042 for (
int i = 0; match && i < numalts; ++i) {
1043 GENERAL_NAME *aName = sk_GENERAL_NAME_value(cert1_altnames, i);
1044 match = sk_GENERAL_NAME_find(cert2_altnames, aName);
1046 }
else if (cert2_altnames)
1049 sk_GENERAL_NAME_pop_free(cert1_altnames, GENERAL_NAME_free);
1050 sk_GENERAL_NAME_pop_free(cert2_altnames, GENERAL_NAME_free);
1057 static char name[1024] =
"";
1063 const int nameLen = X509_NAME_get_text_by_NID(
1064 X509_get_subject_name(x509),
1065 nid, name,
sizeof(name));
1086 if (!cert1 || ! cert2)
1090 unsigned char *cert1Asn =
nullptr;
1091 cert1Len = ASN1_item_i2d((ASN1_VALUE *)cert1.
get(), &cert1Asn, ASN1_ITEM_rptr(X509));
1094 unsigned char *cert2Asn =
nullptr;
1095 cert2Len = ASN1_item_i2d((ASN1_VALUE *)cert2.
get(), &cert2Asn, ASN1_ITEM_rptr(X509));
1097 if (cert1Len != cert2Len)
1100 bool ret = (memcmp(cert1Asn, cert2Asn, cert1Len) == 0);
1102 OPENSSL_free(cert1Asn);
1103 OPENSSL_free(cert2Asn);
1108const ASN1_BIT_STRING *
#define Assure(condition)
#define Here()
source code location of the caller
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define SQUID_CONST_X509_GET0_SIGNATURE_ARGS
static std::optional< Host > ParseIp(const Ip::Address &)
converts an already parsed IP address to a Host object
static std::optional< Host > ParseSimpleDomainName(const SBuf &)
static std::ostream & Extra(std::ostream &)
static std::optional< Address > Parse(const char *)
a stream manipulator for printing a system call error (if any)
void reset()
Forget the raw pointer - unlock if any value was set. Become a nil pointer.
T * get() const
Returns raw and possibly nullptr pointer.
a source code location that is cheap to create, copy, and store
Security::PrivateKeyPointer signWithPkey
The key of the signing certificate.
Security::CertPointer signWithX509
Certificate to sign the generated request.
bool setCommonName
Replace the CN field of the mimicking subject with the given.
bool setValidAfter
Do not mimic "Not Valid After" field.
CertSignAlgorithm signAlgorithm
The signing algorithm to use.
bool setValidBefore
Do not mimic "Not Valid Before" field.
Security::CertPointer mimicCert
Certificate to mimic.
const EVP_MD * signHash
The signing hash to use.
std::string commonName
A CN to use for the generated certificate.
an std::runtime_error with thrower location info
#define debugs(SECTION, LEVEL, CONTENT)
const char * getOrganization(X509 *x509)
const char * CommonHostName(X509 *x509)
bool CertificatesCmp(const Security::CertPointer &cert1, const Security::CertPointer &cert2)
static bool setSerialNumber(ASN1_INTEGER *ai, BIGNUM const *serial)
void ReadPrivateKeyFromFile(char const *keyFilename, Security::PrivateKeyPointer &pkey, pem_password_cb *passwd_callback)
bool ReadPrivateKey(BIO_Pointer &bio, Security::PrivateKeyPointer &pkey, pem_password_cb *passwd_callback)
bool OpenCertsFileForWriting(BIO_Pointer &bio, const char *filename)
bool WritePrivateKey(BIO_Pointer &bio, const Security::PrivateKeyPointer &pkey)
bool appendCertToMemory(Security::CertPointer const &cert, std::string &bufferToWrite)
bool sslDateIsInTheFuture(char const *date)
bool OpenCertsFileForReading(BIO_Pointer &bio, const char *filename)
std::string & OnDiskCertificateDbKey(const CertificateProperties &)
bool generateSslCertificate(Security::CertPointer &cert, Security::PrivateKeyPointer &pkey, CertificateProperties const &properties)
bool writeCertAndPrivateKeyToMemory(Security::CertPointer const &cert, Security::PrivateKeyPointer const &pkey, std::string &bufferToWrite)
bool readCertAndPrivateKeyFromMemory(Security::CertPointer &cert, Security::PrivateKeyPointer &pkey, char const *bufferToRead)
bool certificateMatchesProperties(X509 *peer_cert, CertificateProperties const &properties)
bool WriteX509Certificate(BIO_Pointer &bio, const Security::CertPointer &cert)
const char * CertAdaptAlgorithmStr[]
const char * certSignAlgorithm(int sg)
const char * CertSignAlgorithmStr[]
Security::LockingPointer< X509, X509_free_cpp, HardFun< int, X509 *, X509_up_ref > > CertPointer
std::unique_ptr< EVP_PKEY_CTX, HardFun< void, EVP_PKEY_CTX *, &EVP_PKEY_CTX_free > > EVP_PKEY_CTX_Pointer
std::optional< AnyP::Host > ParseAsSimpleDomainNameOrIp(const SBuf &)
std::unique_ptr< BIO, HardFun< void, BIO *, &BIO_vfree > > BIO_Pointer
std::unique_ptr< BIGNUM, HardFun< void, BIGNUM *, &BN_free > > BIGNUM_Pointer
std::unique_ptr< ASN1_OCTET_STRING, HardFun< void, ASN1_OCTET_STRING *, &ASN1_OCTET_STRING_free > > ASN1_OCTET_STRING_Pointer
std::ostream & ReportAndForgetErrors(std::ostream &)
std::optional< AnyP::Host > ParseCommonNameAt(X509_NAME &, int)
interprets X.509 Subject or Issuer name entry (at the given position) as CN
Security::CertPointer ReadOptionalCertificate(const BIO_Pointer &)
const ASN1_BIT_STRING * X509_get_signature(const Security::CertPointer &)
UniqueCString OneLineSummary(X509_NAME &)
a RAII wrapper for the memory-allocating flavor of X509_NAME_oneline()
Security::CertPointer ReadCertificate(const BIO_Pointer &)
std::unique_ptr< STACK_OF(GENERAL_NAME), sk_GENERAL_NAME_free_wrapper > GENERAL_NAME_STACK_Pointer
BIO_Pointer ReadOnlyBioTiedTo(const char *)
SBuf AsnToSBuf(const ASN1_STRING &)
converts ASN1_STRING to SBuf
std::unique_ptr< X509_NAME, HardFun< void, X509_NAME *, &X509_NAME_free > > X509_NAME_Pointer
std::unique_ptr< ASN1_INTEGER, HardFun< void, ASN1_INTEGER *, &ASN1_INTEGER_free > > ASN1_INT_Pointer
std::unique_ptr< X509_EXTENSION, HardFun< void, X509_EXTENSION *, &X509_EXTENSION_free > > X509_EXTENSION_Pointer
std::unique_ptr< char, HardFun< void, char *, &OPENSSL_free_for_c_strings > > UniqueCString
void ForgetErrors()
Clear any errors accumulated by OpenSSL in its global storage.
std::unique_ptr< AUTHORITY_KEYID, HardFun< void, AUTHORITY_KEYID *, &AUTHORITY_KEYID_free > > AUTHORITY_KEYID_Pointer
#define X509_getm_notAfter
#define X509_getm_notBefore
#define X509_set1_notBefore
void X509_get0_signature(ASN1_BIT_STRING **psig, X509_ALGOR **palg, const X509 *x)
STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx)
RSA * EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
#define X509_set1_notAfter
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
static bool signWithDigest(const Security::PrivateKeyPointer &key)
whether to supply a digest algorithm name when calling X509_sign() with the given key
static bool replaceCommonName(Security::CertPointer &cert, std::string const &rawCn)
static std::optional< SBuf > ParseAsUtf8(const ASN1_STRING &asnBuffer)
OpenSSL ASN1_STRING_to_UTF8() wrapper.
static auto Sign(Security::Certificate &cert, const Security::PrivateKeyPointer &key, const EVP_MD &availableDigest)
OpenSSL X509_sign() wrapper.
static bool buildCertificate(Security::CertPointer &cert, Ssl::CertificateProperties const &properties)
static void printX509Signature(const Security::CertPointer &cert, std::string &out)
static BIGNUM * x509Digest(Security::CertPointer const &cert)
static BIGNUM * createCertSerial(unsigned char *md, unsigned int n)
static Security::PrivateKeyPointer CreateRsaPrivateKey()
static BIGNUM * x509Pubkeydigest(Security::CertPointer const &cert)
static int mimicExtensions(Security::CertPointer &cert, Security::CertPointer const &mimicCert, Security::CertPointer const &issuerCert)
static const size_t MaxCnLen
static bool createSerial(Ssl::BIGNUM_Pointer &serial, Ssl::CertificateProperties const &properties)
static bool mimicAuthorityKeyId(Security::CertPointer &cert, Security::CertPointer const &mimicCert, Security::CertPointer const &issuerCert)
static const char * getSubjectEntry(X509 *x509, int nid)
static bool generateFakeSslCertificate(Security::CertPointer &certToStore, Security::PrivateKeyPointer &pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial)
static bool asn1timeToGeneralizedTimeStr(ASN1_TIME *aTime, char *buf, int bufLen)
Print the time represented by a ASN1_TIME struct to a string using GeneralizedTime format.
static bool addAltNameWithSubjectCn(Security::CertPointer &cert)
static int asn1time_cmp(ASN1_TIME *asnTime1, ASN1_TIME *asnTime2)
static void ThrowErrors(const char *const problem, const int savedErrno, const SourceLocation &where)
#define SQUID_SSL_SIGN_HASH_IF_NONE
SBuf text("GET http://resource.com/path HTTP/1.1\r\n" "Host: resource.com\r\n" "Cookie: laijkpk3422r j1noin \r\n" "\r\n")