#include #include #include #include #include #include "trlitool.h" #include #include #include #include #include #include #include #include using namespace std; static void json_ssl_init() { static bool ssl_init = false; if (!ssl_init){ ssl_init = true; SSL_load_error_strings(); #if OPENSSL_VERSION_MAJOR < 3 ERR_load_BIO_strings(); #endif OpenSSL_add_all_algorithms(); } } static DEBUG_KEY DEBUG_JSON ("json","JSON protocol"); static DEBUG_KEY D_SSL ("ssl","SSL connections"); void REQUEST_JSON::append (const char car) { if (valid){ if (offset + 1 < REQ_BUF_SIZE){ buf[offset++] = car; }else{ valid = false; } } } void REQUEST_JSON::append_comma() { if (!first) append (','); first = false; } void REQUEST_JSON::open_array() { append ('['); } void REQUEST_JSON::close_array() { append (']'); first = false; // Normally append_comma() set first to false // If the array is empty, append_comma() is not called // so we force first to false here as well. } void REQUEST_JSON::append (const char *s, bool put_quote) { if (valid){ if (offset==0) append ('{'); int nbquote = 0; const char *pt = s; while (*pt != '\0'){ if (*pt == '"' || *pt == '\\' || *pt == '\n' || *pt == '\r') nbquote++; pt++; } int len = (pt-s)+nbquote; if (put_quote) len += 2; if (offset + len < REQ_BUF_SIZE){ char *dst = buf+offset; if (put_quote) *dst++ = '"'; pt = s; while (*pt != '\0'){ char car = *pt++; if (car == '\n'){ *dst++ = '\\'; *dst++ = 'n'; }else if (car == '\r'){ *dst++ = '\\'; *dst++ = 'r'; }else{ if (car == '"' || car == '\\'){ *dst++ = '\\'; } *dst++ = car; } } if (put_quote) *dst++ = '"'; offset += len; }else{ valid = false; } } } void REQUEST_JSON::append (const char *s) { append (s,true); } namespace { struct BIOFreeAll { void operator()(BIO* p) { BIO_free_all(p); } }; } static string Base64Encode(unsigned char *sig, size_t sig_len) { std::unique_ptr b64(BIO_new(BIO_f_base64())); BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); BIO* sink = BIO_new(BIO_s_mem()); BIO_push(b64.get(), sink); BIO_write(b64.get(), sig,sig_len); BIO_flush(b64.get()); const char* encoded; const long size = BIO_get_mem_data(sink, &encoded); string result; for (long off=0; off size) len = size - off; result += string(encoded+off,len) + "\n"; } return result; } // Assumes no newlines or extra characters in encoded string static vector Base64Decode(const char* encoded) { std::unique_ptr b64(BIO_new(BIO_f_base64())); BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); BIO* source = BIO_new_mem_buf(encoded, -1); // read-only source BIO_push(b64.get(), source); const int maxlen = strlen(encoded) / 4 * 3 + 1; std::vector decoded(maxlen); const int len = BIO_read(b64.get(), decoded.data(), maxlen); decoded.resize(len); return decoded; } void REQUEST_JSON::append_bob (const void *s, size_t size) { if (valid){ string enc = Base64Encode((unsigned char*)s,size); size_t size = enc.size()+2; if (offset + size < REQ_BUF_SIZE){ buf[offset] = '"'; offset++; memcpy (buf+offset,enc.c_str(),enc.size()); offset += enc.size(); buf[offset] = '"'; offset++; }else{ valid = false; } } } void REQUEST_JSON::append_name (const char *name) { append_comma(); if (name != NULL){ append (name); append (':'); } } /* Add one value */ void REQUEST_JSON::append_val (PARAM_STRING val) { append (val.ptr); } void REQUEST_JSON::append_val (PARAM_STRING val, bool put_quote) { append (val.ptr,put_quote); } /* Add one value part of many */ void REQUEST_JSON::append_vals (PARAM_STRING val){ append (val.ptr); } void REQUEST_JSON::reset() { offset = 0; offset_read = 0; buf[0] = '\0'; valid = true; first = true; } REQUEST_JSON::REQUEST_JSON() { reset(); } void REQUEST_JSON::add (const char *name, int val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%d",val); append_name (name); append_val (tmp); } void REQUEST_JSON::add (const char *name, long val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%ld",val); append_name (name); append_val (tmp); } void REQUEST_JSON::add (const char *name, unsigned val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%u",val); append_name (name); append_val (tmp); } void REQUEST_JSON::add (const char *name, bool val) { append_name (name); append_val (val ? "true" : "false",false); } void REQUEST_JSON::add (const char *name, char *val) { append_name (name); append_val (val); } void REQUEST_JSON::add (const char *name, const char *val) { append_name (name); append_val (val); } void REQUEST_JSON::add (const char *name, PARAM_STRING val) { append_name (name); append_val (val.ptr); } void REQUEST_JSON::add (const char *name, const string &val) { append_name (name); append_val (val.c_str()); } void REQUEST_JSON::add (const char *name, const BOB_TYPE &val) { append_name (name); append_bob (val.getbuffer(),val.getsize()); } void REQUEST_JSON::add (const char *name, const vector> &vals) { append_name (name); open_array(); first = true; for (auto &x:vals){ append_comma(); open_array(); first = true; for (auto &xx:x){ append_comma(); append_vals(xx.c_str()); } close_array(); } close_array(); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); open_array(); first = true; for (auto &x:vals){ append_comma(); append_vals(x.c_str()); } close_array(); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); open_array(); first = true; for (auto x:vals){ append_comma(); append_vals(x); } close_array(); } void REQUEST_JSON::add (const char *name, const PARAM_VECTOR_STRING vals) { append_name (name); open_array(); first = true; for (auto x:vals){ append_comma(); append_vals(x); } close_array(); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); open_array(); first = true; for (auto x:vals){ append_comma(); append_vals(x ? "1" : "0"); } close_array(); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); open_array(); first = true; for (auto x:vals){ append_comma(); char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%d",x); append_vals(tmp); } close_array(); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); open_array(); first = true; for (auto x:vals){ append_comma(); char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%u",x); append_vals(tmp); } close_array(); } void REQUEST_JSON::add (const char *name, const vector &vals) { append_name (name); open_array(); first = true; for (auto &x:vals){ append_comma(); append_bob(x.getbuffer(),x.getsize()); } close_array(); } void REQUEST_JSON::add_timestamp() { char tmp[100]; long long now = fdpass_getnow(); snprintf (tmp,sizeof(tmp)-1,"%Ld",now); add ("_time_",tmp); } bool REQUEST_JSON::is_valid() const { return valid; } // Put the last tag to end the REQUEST void REQUEST_JSON::complete() { if (offset==0) append ('{'); append ('}'); buf[offset] = '\0'; } void REQUEST_JSON::dump() { FILE *fout = fopen ("/tmp/json.log","a"); if (fout != NULL){ fprintf (fout,"buf : %s\n",buf); fprintf (fout,"read: %s\n",buf+offset_read); fclose (fout); } } int REQUEST_JSON::send (BIO *bio) { int ret = -1; if (valid){ ret = BIO_write (bio,buf,offset); }else{ // We send an empty answer anyway REQUEST_JSON req; req.complete(); BIO_write (bio,req.buf,req.offset); } return ret; } int REQUEST_JSON::send (_F_TCPSERVER_V1 *c) { complete(); if (valid){ printf ("%s\r\n",buf); }else{ // We send an empty answer anyway REQUEST_JSON req; req.complete(); printf ("%s\r\n",req.buf); } return 0; } // Add a part of a request (received on the socket) // Return -1 if something is wrong // Return 0 if the request is still incomplete // Return 1 if the request is complete, ready to process int REQUEST_JSON::addpart (const char *line, int len) { int ret = -1; if (!valid){ ret = -1; }else{ if (len + offset > REQ_BUF_SIZE){ valid = false; }else{ memcpy(buf+offset,line,len); offset += len; buf[offset] = '\0'; ret = 0; const char *pt = buf; if (*pt != '{'){ ret = -1; }else{ enum LASTOPEN {LAST_BRACE, LAST_BRACK}; vector lasts; bool err = false; while (!err && *pt != '\0'){ const char car = *pt++; if (car == '{'){ lasts.push_back(LAST_BRACE); }else if (car == '['){ lasts.push_back(LAST_BRACK); }else if (car == ']'){ if (lasts.size() == 0 || lasts.back() != LAST_BRACK){ ret = -1; break; } lasts.pop_back(); }else if (car == '}'){ if (lasts.size() == 0 || lasts.back() != LAST_BRACE){ ret = -1; break; } lasts.pop_back(); }else if (car == '"'){ // We have to skip over string as they can contain special characters while (*pt != '\0' && *pt != '"'){ if (*pt == '\\'){ pt++; if (*pt == 'n' || *pt == 'r' || *pt == '\\' || *pt == '"'){ pt++; }else{ // Invalid err = true; break; } }else{ pt++; } } if (*pt == '"'){ pt++; }else{ err = true; break; } } } if (lasts.size()==0){ offset_read=1; // We skip the first { immediatly ret = 1; } } } } return ret; } int REQUEST_JSON::addpart (const char *line) { return addpart (line,strlen(line)); } unsigned REQUEST_JSON::getlength() const { return offset; } const char *REQUEST_JSON::getbuf() const { return buf; } /* Check that the current field in the buffer has this name */ int REQUEST_JSON::checkname (const char *name, const char *&retpt) { retpt = NULL; int ret = -1; char *pt = buf+offset_read; debug_printf (DEBUG_JSON,"checkname1 name=%s pt=%-100.100s\n",name,pt); if (name == NULL){ // Trick to simply unserialise code for USER objects // This code does not need to validate the name of each field received retpt = pt; ret = 0; }else if (*pt == '"'){ pt++; int len = strlen(name); if (strncmp(pt,name,len)==0 && pt[len] == '"'){ pt = str_skip(pt+len+1); if (*pt == ':'){ pt++; offset_read = pt - buf; ret = 0; retpt = pt; } } } debug_printf (DEBUG_JSON,"checkname2 ret=%d pt=%-100.100s\n retpt=%-100.100s\n",ret,pt,retpt); return ret; } /* Check that the parsing (getarg()) reached the end of the buffer */ bool REQUEST_JSON::is_all_read() { bool ret = false; const char *pt = buf+offset_read; pt = str_skip(pt); if (*pt == '}'){ unsigned off = pt - buf; // tlmp_warning ("is_all off %u offset %u\n",off,offset); ret = off == offset; if (!ret){ pt = str_skip(pt+1); off = pt - buf; ret = off == offset; } } return ret; } /* This is the base function. It checks for a name and return a pointer to the value of the variable It checks this is the expected property name. If valid, it check the value. Value are normally quoted. val always points at the start of the value. If the value is quoted, val points after the first quote. The last quote will be overwritten by a '\0', ending the string. lennoquote is set to 0. If the value is not quoted, val points at the start and lennoquote tells the size. The value is not ended with a '\0'. Only boolean and number values are this way, so the content of the JSON is not used directly. */ int REQUEST_JSON::getarg (const char *name, const char *&val, int &lennoquote) { int ret = -1; val = ""; lennoquote = 0; const char *pt; if (checkname (name,pt) != -1){ pt = str_skip(pt); if (*pt == '"'){ pt++; const char *start = pt; char *dst = (char*)pt; const char *end = buf+offset; while (pt < end){ if (*pt == '\\'){ pt++; if (*pt == 'n'){ *dst = '\n'; }else if (*pt == 'r'){ *dst = '\r'; }else if (*pt != '"' && *pt != '\\'){ break; }else{ *dst = *pt; } }else if (*pt == '"'){ ret = 0; pt = str_skip(pt+1); if (*pt == ',') pt++; offset_read = pt - buf; val = start; break; }else if (pt != dst){ *dst = *pt; } dst++; pt++; } // We terminate the string and break the buffer! *dst = '\0'; }else{ // Used for numbers or true/false const char *start = pt; const char *end = buf+offset; // tlmp_warning ("autre :%s:\n",start); while (pt < end){ if (isalnum(*pt) || *pt == '-'){ pt++; }else{ break; } } lennoquote = pt - start; // tlmp_warning ("autre2 :%s:\n",pt); // We have to help is_all_read() // Normally, when the content is quoted, we replace the end qquote with '\0' // to terminate the string and we move the pointer just after the comma if // there is one. But is_all_read expect to see the last } char last = *pt; if (last == ',' || last == '}'){ if (last == ',') pt++; // Comma are discared ret = 0; val = start; } offset_read = pt - buf; } } return ret; } // Like the function above, but the caller does not handle a value without quotes int REQUEST_JSON::getarg (const char *name, const char *&val) { int lennoquote; int ret = getarg(name,val,lennoquote); if (ret != -1 && lennoquote > 0){ ret = -1; } return ret; } int REQUEST_JSON::getarg (const char *name, string &val) { const char *ptval; int ret = getarg (name,ptval); if (ret != -1) val = ptval; return ret; } template int gen_getarg (REQUEST_JSON *req, const char *name, T &val, T cnv(const char *, int len)) { int ret = -1; const char *valstr; int len; if (req->getarg(name,valstr,len)==0){ const char *pt = valstr; ret = 0; if constexpr (std::is_same_v || std::is_same_v){ if (pt[0] == '-'){ pt++; if (len > 0) len--; } }else if constexpr (std::is_same_v || std::is_same_v){ if (pt[0] == '-'){ ret = -1; } }else{ static_assert (false,"gen_getarg: type not supported"); } if (ret == 0 && fdpass_is_all_digit(pt,len)){ val = cnv(valstr,len); }else{ ret = -1; } } return ret; } // Those function trust that atoi and friends stop at the end of a digit sequence. // So len is not used. static int atoi_len (const char *pt, int len) { return atoi(pt); } static unsigned atou_len (const char *pt, int len) { return atoi(pt); } static long atol_len (const char *pt, int len) { return atol(pt); } static unsigned long long atoll_len (const char *pt, int len) { return atoll(pt); } int REQUEST_JSON::getarg (const char *name, int &val) { return gen_getarg(this,name,val,atoi_len); } int REQUEST_JSON::getarg (const char *name, long &val) { return gen_getarg(this,name,val,atol_len); } int REQUEST_JSON::getarg (const char *name, unsigned long long &val) { return gen_getarg(this,name,val,atoll_len); } int REQUEST_JSON::getarg (const char *name, unsigned &val) { return gen_getarg(this,name,val,atou_len); } int REQUEST_JSON::getarg (const char *name, bool &val) { int ret = -1; const char *valstr; int lennoquote; if (getarg(name,valstr,lennoquote)==0){ if (lennoquote > 0){ if ((lennoquote == 1 && *valstr == '1') || (lennoquote == 4 && memcmp(valstr,"true",4)==0)){ val = true; ret = 0; }else if ((lennoquote == 1 && *valstr == '0') || (lennoquote == 5 && memcmp(valstr,"false",5)==0)){ val = false; ret = 0; } }else{ if (strcmp(valstr,"1")==0 || strcmp(valstr,"true")==0){ val = true; ret = 0; }else if (strcmp(valstr,"0")==0 || strcmp(valstr,"false")==0){ val = false; ret = 0; } } } return ret; } int REQUEST_JSON::getarg (const char *name, BOB_TYPE &val) { int ret = -1; const char *pt; if (checkname(name,pt)!=-1){ const char *end = buf + offset; if (pt + 2 < end){ if (*pt == '"'){ pt++; const char *start = pt; char *dst = (char*)pt; while (pt < end && *pt != '"'){ char car = *pt++; if (car > ' ') *dst++ = car; } if (*pt == '"'){ *dst = '\0'; vector v = Base64Decode(start); val.setbuffer((const void*)v.data(),v.size(),true); pt++; if (*pt == ',') pt++; offset_read = pt - buf; ret = 0; } } } } return ret; } bool REQUEST_JSON::expect (const char car) { bool ret = false; if (offset_read < offset){ const char *pt = buf + offset_read; pt = str_skip(pt); debug_printf (DEBUG_JSON,"expect1 car=%c pt=%-50.50s\n",car,pt); if (*pt == car){ pt++; ret = true; debug_printf (DEBUG_JSON,"expect2 pt=%-50.50s\n",pt); offset_read = pt - buf; } } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; const char *pt; if (checkname(name,pt) != -1 && expect('[')){ pt = buf + offset_read; const char *end = buf + offset; while (pt < end){ offset_read = pt - buf; if (*pt == ']'){ pt = str_skip(pt+1); if (*pt == ',') pt++; ret = 0; offset_read = pt - buf; break; }else if (getarg(NULL,pt)==-1){ break; }else{ vals.push_back(pt); pt = buf + offset_read; } } } if (ret != 0){ vals.clear(); } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vals.clear(); vector valss; if (getarg (name,valss)!=-1){ for (auto x:valss) vals.push_back(x); ret = 0; } return ret; } int REQUEST_JSON::getarg (const char *name, vector > &vals) { const char *pt; int ret = checkname(name,pt); if (ret != -1){ pt = str_skip(pt); if (*pt == '['){ pt++; offset_read = pt-buf; const char *end = buf + offset; while (pt < end){ if (*pt == ']'){ pt = str_skip(pt+1); if (*pt == ',') pt++; ret = 0; offset_read = pt - buf; break; }else{ vector valss; if (getarg(NULL,valss)==-1){ break; }else{ vals.push_back(valss); pt = buf + offset_read; } } } } } if (ret != 0){ vals.clear(); } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vector valss; if (getarg (name,valss)!=-1){ ret = 0; for (auto x:valss){ if (strcmp(x,"0")==0){ vals.push_back(false); }else if (strcmp(x,"1")==0){ vals.push_back(true); }else{ ret = -1; break; } } } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vector valss; if (getarg (name,valss)!=-1){ ret = 0; for (auto x:valss){ const char *pt = x; if (isdigit(*pt) || (pt[0] == '-' && isdigit(pt[1]))){ pt++; while (isdigit(*pt)) pt++; if (*pt == '\0'){ vals.push_back(atoi(x)); }else{ ret = -1; break; } } } } return ret; } int REQUEST_JSON::getarg (const char *name, vector &vals) { int ret = -1; vector valss; if (getarg (name,valss)!=-1){ ret = 0; for (auto x:valss){ const char *pt = x; if (isdigit(*pt)){ while (isdigit(*pt)) pt++; if (*pt == '\0'){ vals.push_back(atoi(x)); }else{ ret = -1; break; } } } } return ret; } int REQUEST_JSON_INFO::addpart (const char *line, int len) { return req.addpart(line,len); } int REQUEST_JSON_INFO::addpart (const char *line) { return addpart (line,strlen(line)); } CONNECT_HTTP_INFO::~CONNECT_HTTP_INFO() { close(); } static SSL_CTX * ctx = NULL; CONNECT_HTTP_INFO::CONNECT_HTTP_INFO() { strictmode = true; bio = NULL; use_ssl = false; ssl = NULL; pageapi = "webapi"; if (ctx == NULL){ ctx = SSL_CTX_new(SSLv23_client_method()); static const char *pemfile = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"; if(! SSL_CTX_load_verify_locations(ctx, pemfile,NULL)){ tlmp_error ("Can't load openssl certificates in %s\n",pemfile); } } } void CONNECT_HTTP_INFO::copy (const CONNECT_HTTP_INFO &c) { bio = NULL; ssl = NULL; use_ssl = c.use_ssl; strictmode = c.strictmode; host = c.host; port = c.port; bind = c.bind; pageapi = c.pageapi; } void CONNECT_HTTP_INFO::swap (CONNECT_HTTP_INFO &&c) { std::swap(bio,c.bio); std::swap(ssl,c.ssl); std::swap(use_ssl,c.use_ssl); std::swap(strictmode,c.strictmode); std::swap(host,c.host); std::swap(port,c.port); std::swap(bind,c.bind); std::swap(pageapi,c.pageapi); } CONNECT_HTTP_INFO::CONNECT_HTTP_INFO(const CONNECT_HTTP_INFO &c) { copy (c); } CONNECT_HTTP_INFO::CONNECT_HTTP_INFO(CONNECT_HTTP_INFO &&c) { bio = nullptr; ssl = nullptr; strictmode = true; use_ssl = false; swap (std::forward(c)); } CONNECT_HTTP_INFO &CONNECT_HTTP_INFO::operator =(const CONNECT_HTTP_INFO &c) { close(); ssl = NULL; copy (c); return *this; } CONNECT_HTTP_INFO &CONNECT_HTTP_INFO::operator =(CONNECT_HTTP_INFO &&c) { swap (std::forward(c)); return *this; } static int json_splithttp (const char *line, string &host, string &port, const char *default_port) { int ret = -1; // Line starts after the // const char *pt = strchr(line,':'); if (pt != NULL){ ret = 0; host = string(line,pt-line); port = (pt+1); }else{ ret = 0; host = line; port = default_port; } return ret; } int CONNECT_HTTP_INFO::init (PARAM_STRING serverurl) { int ret = -1; if (strncasecmp(serverurl.ptr,"http://",7)==0){ if (json_splithttp(serverurl.ptr+7,host,port,"80")!=-1){ use_ssl = false; ret = 0; } }else if (strncasecmp(serverurl.ptr,"https://",8)==0){ if (json_splithttp(serverurl.ptr+8,host,port,"443")!=-1){ use_ssl = true; ret = 0; } } return ret; } void CONNECT_HTTP_INFO::close() { if (bio != NULL){ BIO_free_all(bio); bio = NULL; } } /* When called, it will make connection to self sign certificate and won't validate that the hostname in the certificate match the target hostname (in the URL). This is useful for development only. */ void CONNECT_HTTP_INFO::setnonstrictmode() { strictmode = false; } void CONNECT_HTTP_INFO::reconnect() { json_ssl_init(); close(); { const char *default_port = use_ssl ? "443" : "80"; const char *portstr = port.size()==0 ? default_port : port.c_str(); string tmp = string_f("%s:%s",host.c_str(),portstr); if (use_ssl){ bio = BIO_new_ssl_connect(ctx); BIO_get_ssl(bio, & ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); BIO_set_conn_hostname(bio, tmp.c_str()); SSL_set_tlsext_host_name(ssl, host.c_str()); if (strictmode){ X509_VERIFY_PARAM *param = SSL_get0_param(ssl); /* Enable automatic hostname checks */ X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); if (!X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size())) { tlmp_error ("VERIFY PARAM error\n"); } SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL); //SSL_CTX_set_verify_depth(ctx, 2 + 1); } }else{ bio = BIO_new_connect (tmp.c_str()); } if(BIO_do_connect(bio) <= 0){ tlmp_error ("JSON: Failed connection to %s\n",tmp.c_str()); ERR_print_errors_fp(stderr); close(); }else{ if (use_ssl){ int code = SSL_get_verify_result (ssl); debug_printf (D_SSL,"SSL_get_verify_result=%d\n",code); if (code == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN && !strictmode ) code = X509_V_OK; if (code != X509_V_OK){ tlmp_error ("Invalid SSL connection for site %s: code=%d\n",host.c_str(),code); close(); }else if (debug_ison()){ // Debugging X509 *x = SSL_get_peer_certificate(ssl); tlmp_error ("x=%p\n",x); X509_NAME *name = X509_get_subject_name(x); char *buf = X509_NAME_oneline (name,0,0); debug_printf (D_SSL,"SubjectName=%s\n",buf); OPENSSL_free (buf); if (X509_check_host(x, host.c_str(),0,0,NULL)){ debug_printf (D_SSL,"X509 match\n"); }else{ debug_printf (D_SSL,"X509 mismatch\n"); } X509_free(x); } } } } } void CONNECT_HTTP_INFO::setpageapi(PARAM_STRING page) { pageapi = page.ptr; } void CONNECT_HTTP_INFO::reset(REQUEST_JSON &req) { req.reset(); } int CONNECT_HTTP_INFO::send (REQUEST_JSON &req) { int ret = 0; req.complete(); string tmp = string_f("POST /%s.hc HTTP/1.1\r\n" "host: %s\r\n" "Content-Length: %d\r\n" "Content-Type: Text/plain\r\n" "\r\n" "%s\r\n",pageapi.c_str(),host.c_str(),req.getlength()+2,req.getbuf()); int size = tmp.size(); debug_printf (DEBUG_JSON,"write1 bio=%p\n%s\n",bio,tmp.c_str()); if (bio == NULL || BIO_write(bio,tmp.c_str(),size) != size){ reconnect(); //printf ("write2 fd=%d\n",fd); if (bio == NULL || BIO_write(bio,tmp.c_str(),size) != size){ ret = -1; } } if (ret != -1) req.reset(); return ret; } int CONNECT_HTTP_INFO::write (const void *buf, size_t size) { int ret = size; if (bio == NULL || BIO_write(bio,buf,size) != ret){ if (errno == EINTR){ ret = -1; }else{ reconnect(); //printf ("write2 fd=%d\n",fd); if (bio == NULL || BIO_write(bio,buf,size) != ret){ ret = -1; } } } return ret; } int CONNECT_HTTP_INFO::send (PARAM_STRING s) { int ret = 0; int size = strlen(s.ptr); if (write(s.ptr,size) != size) ret = -1; return ret; } static const char *chunked = "Transfer-Encoding: chunked"; static unsigned len_chunked = strlen(chunked); static const char *conclose = "Connection: close"; static unsigned len_conclose = strlen(conclose); static unsigned xtoi (const char *pt) { unsigned ret = 0; while (isxdigit(*pt)){ char car = *pt++; if (isdigit(car)){ ret = (ret<<4) + car - '0'; }else{ ret = (ret<<4) + toupper(car)+10-'A'; } } return ret; } /* Set timeout for receive operation. if seconds == -1, disable timeout. timeout is disabled by default. */ void CONNECT_HTTP_INFO::set_timeout (int seconds) { timeout = seconds; } bool CONNECT_HTTP_INFO::was_timeout () const { return timeout_seen; } int CONNECT_HTTP_INFO::read (void *data, size_t len) { int ret = -1; timeout_seen = false; if (timeout == -1){ ret = BIO_read(bio,data,len); }else{ int fdSocket; if (BIO_get_fd(bio, &fdSocket) < 0){ // failed to get fd. }else{ fd_set fds; struct timeval time_out; FD_ZERO(&fds); FD_SET(fdSocket, &fds); time_out.tv_usec = 0; time_out.tv_sec = timeout; int ok = select(fdSocket + 1, &fds, NULL, NULL, &time_out); if (ok == 0){ // timeout has occurred. timeout_seen = true; }else{ ret = BIO_read(bio,data,len); } } } return ret; } int CONNECT_HTTP_INFO::receive(REQUEST_JSON &req) { glocal REQUEST_JSON *req = &req; glocal bool must_close = false; glocal bool chunk_mode = false; glocal bool header_seen = false; glocal int chunk_size = 0; glocal bool end = false; glocal int ok = 0; req.reset(); STREAMP_BUF buf; int len=-2; char line[REQ_BUF_SIZE+1]; while (!glocal.end && (len=read(line,sizeof(line)-1))>0){ line[len] = '\0'; debug_printf (DEBUG_JSON,"receive2 len=%d\n%s\n",len,line); (buf,line,len); int ret = 0; const char *start = (const char*)buf; const char *pt = start; const char *endpt = pt + len; // printf ("process %d header_seen=%d\n",len,glocal.header_seen); if (!glocal.header_seen){ while (pt < endpt){ if (pt[0] == '\r' && pt[1] == '\n'){ ret = (pt - start) + 2; if (pt == start){ glocal.header_seen = true; }else if (strncasecmp(start,conclose,len_conclose)==0){ glocal.must_close = true; }else if (strncasecmp(start,chunked,len_chunked)==0){ glocal.chunk_mode = true; } break; } pt++; } }else{ if (glocal.chunk_mode){ if (glocal.chunk_size == 0){ while (pt < endpt && isxdigit(*pt)) pt++; if (pt + 2 <= endpt && pt[0] == '\r' && pt[1] == '\n'){ pt += 2; ret = pt-start; if (ret > 2){ glocal.chunk_size = xtoi(start); if (glocal.chunk_size == 0){ glocal.end = true; end = true; } } } }else if (len >= glocal.chunk_size){ ret = glocal.chunk_size; glocal.ok = glocal.req->addpart(pt,ret); glocal.chunk_size = 0; } }else{ ret = len; glocal.ok = glocal.req->addpart(pt,len); glocal.end = glocal.ok != 0; } } //printf ("Use %d: %*.*s",ret,ret,ret,start); return ret; } debug_printf (DEBUG_JSON,"receive3 end=%d len=%d\n",glocal.end,len); //printf ("must_close =%d ok=%d\n",glocal.must_close,glocal.ok); if (glocal.must_close){ BIO_free_all(bio); bio = NULL; } return glocal.ok == 1 ? 0 : -1; } int CONNECT_HTTP_INFO::receive(void *buf, unsigned size) { int ret = -1; int len; if ((len=BIO_read(bio,buf,size))>0){ ret = len; } return ret; } #ifdef TEST #include #include #include #include #include using namespace std; static int req_init (REQUEST_JSON &req, const char *line) { int ok = req.addpart(line,strlen(line)); if (ok == -1){ tlmp_error ("Invalid: %s\n",line); }else if (ok == 0){ tlmp_error ("Incomplete line %s\n",line); } return ok; } static void test_json_nums (const char *line) { REQUEST_JSON req; int ok = req_init (req,line); if (ok == 1){ int ival; long lval; unsigned uval; unsigned long long ullval; if (req.getarg("ival",ival)!=-1 && req.getarg("lval",lval)!=-1 && req.getarg("uval",uval)!=-1 && req.getarg("ullval",ullval)!=-1 && req.is_all_read() ){ printf ("all ok: %s\n",line); printf ("\tival\t%d\n",ival); printf ("\tlval\t%ld\n",lval); printf ("\tuval\t%u\n",uval); printf ("\tullval\t%Lu\n",ullval); }else{ printf ("Invalid format: %s\n",line); } } } static void test_json (const char *line) { REQUEST_JSON req; int ok = req_init (req,line); if (ok == 1){ const char *func; const char *sessionid; const char *owner; vector fulltext; unsigned offset,nb; const char *firstseen; bool format; if (req.getarg("_func_",func)!=-1 && req.getarg("sessionid",sessionid)!=-1 && req.getarg("owner",owner)!=-1 && req.getarg("fulltext",fulltext)!=-1 && req.getarg("offset",offset)!=-1 && req.getarg("nb",nb)!=-1 && req.getarg("firstseen",firstseen)!=-1 && req.getarg("format",format)!=-1 && req.is_all_read() ){ printf ("all ok: %s\n",line); printf ("\tfunc\t\t%s\n",func); printf ("\tsessionid\t%s\n",sessionid); printf ("\towner\t%s\n",owner); printf ("\tfulltext\t"); for (auto t:fulltext) printf ("\"%s\" ",t); printf ("\n"); printf ("\toffset\t\t%u\n",offset); printf ("\tnb\t\t%u\n",nb); printf ("\tfirstseen\t%s\n",firstseen); printf ("\tformat\t\t%d\n",format); }else{ printf ("Invalid format: %s\n",line); } } } int main (int argc, char *argv[]) { glocal int ret = -1; glocal.ret = (argc,argv); setproginfo ("","0.0","..."); int ret = -1; const char *line1 = "{\"_func_\":\"interest_check\",\"sessionid\":\"YiFGj4BfAtaOp4hpD2IEA\",\"owner\":\"\",\"fulltext\":[],\"offset\":0,\"nb\":10,\"firstseen\":\"\",\"format\":true}"; const char *line2 = "{\"_func_\":\"interest_check\",\"sessionid\":\"qf_Tj9xuYB3zqohp9aEBA\",\"owner\":\"\",\"fulltext\":[],\"offset\":\"0\",\"nb\":\"20\",\"firstseen\":\"\",\"format\":true}"; const char *line3 = "{\"_func_\":\"interest_check\",\"sessionid\":\"qf_Tj9xuYB3zqohp9aEBA\",\"owner\":\"\",\"fulltext\":[\"a\",\"b\",\"c\"],\"offset\":\"0\",\"nb\":\"20\",\"firstseen\":\"\",\"format\":true}"; test_json (line1); test_json (line2); test_json (line3); const char *ivld1 = "{\"_func_\":\"interest_check\",\"sessionid\":\"YiFGj4BfAtaOp4hpD2IEA\",\"owner\":\"\",\"fulltext\":[],\"offset\":0,\"nb\":-10,\"firstseen\":\"\",\"format\":true}"; const char *ivld2 = "{\"_func_\":\"interest_check\",\"sessionid\":\"qf_Tj9xuYB3zqohp9aEBA\",\"owner\":\"\",\"fulltext\":[],\"offset\":\"0\",\"nb\":\"-20\",\"firstseen\":\"\",\"format\":true}"; test_json (ivld1); test_json (ivld2); // Numbers const char *num1 = "{\"ival\":-42,\"lval\":-123456789123456789,\"uval\":42,\"ullval\":123456789123456789}"; const char *num2 = "{\"ival\":\"-42\",\"lval\":\"-123456789123456789\",\"uval\":\"42\",\"ullval\":\"123456789123456789\"}"; test_json_nums (num1); test_json_nums (num2); return ret; return glocal.ret; } #endif