#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trlitool.h" #include #include #include using namespace std; static DEBUG_KEY D_SIGN ("sign","Protocol secret/signature validation"); void _assign (std::vector &d, const std::vector &s) { d.clear(); for (auto x:s) d.push_back(x); } void _assign (std::vector> &d, const std::vector> &s) { d.clear(); for (auto x:s){ vector t; for (auto xx:x) t.push_back(xx); d.push_back(t); } } BOB_TYPE::BOB_TYPE (size_t _size) { owner = true; buffer = malloc(_size); if (buffer == nullptr){ size = 0; }else{ size = _size; } } void BOB_TYPE::setbuffer (const void *_buffer, size_t _size, bool copy) { if (copy){ owner = true; buffer = malloc(_size); if (buffer != nullptr){ memcpy (buffer,_buffer,_size);; size = _size; }else{ size = 0; } }else{ owner = false; buffer = (void *)_buffer; size = _size; } } int fdpass_validstr(const char *s, std::string &v) { v = s; return 0; } int fdpass_validbool(const char *s, bool &v) { int ret = -1; if (s!=NULL && (strcmp(s,"0")==0 || strcmp(s,"1")==0)){ v = atoi(s); ret = 0; } return ret; } int fdpass_validint(const char *s, int &v) { int ret = 0; if (s != NULL){ const char *pt = s; if (*pt == '-') pt++; if (fdpass_is_all_digit(pt)){ v = atoi(s); ret = 0; } } return ret; } int fdpass_validuns(const char *s, unsigned &v) { int ret = 0; if (s != NULL){ const char *pt = s; if (fdpass_is_all_digit(pt)){ v = atoi(s); ret = 0; } } return ret; } long long fdpass_getnow() { struct timeval tv; if (gettimeofday(&tv,NULL)==-1){ tlmp_error ("Can't get timeofday, ending (%s)\n",strerror(errno)); exit (-1); } return tv.tv_sec*1000000+tv.tv_usec; } /* Convert a time_t into a string localtime */ void fdpass_asctime (time_t t, DATEASC &dst) { if (t == (time_t)0){ strcpy (dst.buf,"----/--/--_--:--:--"); }else{ struct tm *tt = localtime (&t); snprintf (dst.buf,sizeof(dst.buf),"%04d/%02d/%02d-%02d:%02d:%02d" ,tt->tm_year+1900,tt->tm_mon+1,tt->tm_mday ,tt->tm_hour,tt->tm_min,tt->tm_sec); } } int fdpass_tcpconnect (const char *bind, bool transparent_mode, const char *host, const char *port) { int fd = cmdsock_connect (bind,transparent_mode,host,port,5,1); if (fd != -1){ int opt = 1; setsockopt (fd,SOL_TCP,TCP_NODELAY,&opt,sizeof(opt)); }else{ tlmp_error ("Can't connect to host %s, port %s (bind=%s transparent_mode=%d) (%s)\n" ,host,port,bind,transparent_mode,strerror(errno)); } return fd; } int fdpass_tcpconnect (const char *host, const char *port) { return fdpass_tcpconnect (NULL,false,host,port); } /* Opens a control unix socket for the service */ int fdpass_setcontrol (TCPSERVER_V1 &o, const char *control, const char *user) { int ret = 0; SSTRING tmp; tmp.setfromf ("unix:%s",control); if (o.listen("",tmp.c_str())==-1){ tlmp_error ("Can't setup the control socket %s (%s)\n" ,control,strerror(errno)); ret = -1; }else if (user != NULL){ struct passwd *u = getpwnam(user) ; if (u == NULL){ tlmp_error ("Error retrieving %s uid/gid\n",user) ; ret = -1; }else{ chown(control, u->pw_uid, u->pw_gid) ; chmod(control, 0770) ; } } return ret; } void fdpass_shasum (const char *line, char out[SHA256_DIGEST_LENGTH*2+1]) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx,EVP_sha256(),nullptr); EVP_DigestUpdate(ctx,line,strlen(line)); unsigned char bufout[BUFSIZ]; unsigned int size=0; EVP_DigestFinal_ex(ctx,bufout,&size); EVP_MD_CTX_free(ctx); char *ptout = out; for (unsigned j = 0; j < size; j++){ sprintf(ptout,"%02x", bufout[j]); ptout += 2; } } void fdpass_shasum (const char *buf1, int lenbuf1, const char *buf2, int lenbuf2, char out[SHA256_DIGEST_LENGTH*2+1]) { EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx,EVP_sha256(),nullptr); EVP_DigestUpdate(ctx,buf1,lenbuf1); EVP_DigestUpdate(ctx,buf2,lenbuf2); unsigned char bufout[BUFSIZ]; unsigned int size=0; EVP_DigestFinal_ex(ctx,bufout,&size); EVP_MD_CTX_free(ctx); char *ptout = out; for (unsigned j = 0; j < SHA256_DIGEST_LENGTH; j++){ sprintf(ptout,"%02x", bufout[j]); ptout += 2; } } /* Compute the sha256 sum of a line and checks if it match the signature */ int fdpass_checksign (const char *line, const char *signature) { char out[SHA256_DIGEST_LENGTH*2+1]; fdpass_shasum (line,out); return strcmp(signature,out); } /* Protocol lines are built this way command arg1 arg2 ... salt signature The salt is a string built using random characters concatenated with the timeofday (microseconds). This produces an unpredictable salt. The signature is an sha256 sum of the line up to the signature + the secret */ int fdpass_valid_secret ( const string &secret, const char *line, // The original protocol line vector &words, // Each words in the line int &n) // Number of words in the line // If there is salt and signature, 2 will be substract // The caller does not know about salt and signature { int ret = -1; if (secret.size() == 0){ // Secret mode not active ret = 0; }else{ if (n > 2){ const char *signature = words[n-1].c_str(); string line0 = string(line,strlen(line)-strlen(signature)) + secret; if (fdpass_checksign(line0.c_str(),signature)==0){ ret = 0; } debug_printf (D_SIGN,"REC: %s\nTST: %s\nSIG: %s ret=%d\n",line,line0.c_str(),signature,ret); } n-=2; } return ret; } int fdpass_valid_secret ( const map &secrets, const char *host, const char *line, // The original protocol line vector &words, // Each words in the line int &n) // Number of words in the line // If there is salt and signature, 2 will be substract // The caller does not know about salt and signature { int ret = -1; if (secrets.size()==0){ // Secret mode not active ret = 0; }else{ map::const_iterator it = secrets.find(host); if (it == secrets.end()){ tlmp_error ("No secret for host %s\n",host); }else{ ret = fdpass_valid_secret (it->second,line,words,n); } } return ret; } void fdpass_makesalt (char out[32+1]) { static int fd = -1; if (fd == -1){ fd = open ("/dev/urandom",O_RDONLY,0); if (fd == -1){ tlmp_error ("Can't open /dev/uramdom (%s), can't continue\n",strerror(errno)); exit (-1); } } unsigned char buf[8]; int n = read (fd,buf,8); if (n != 8){ tlmp_error ("can't read /dev/uandom (%s), can't continue",strerror(errno)); exit (-1); } sprintf (out,"%02x%02x%02x%02x%02x%02x%02x%02x",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); struct timeval tv; if (gettimeofday(&tv,NULL)==-1){ tlmp_error ("Can't get timeofday, ending (%s)\n",strerror(errno)); exit (-1); } sprintf (out+16,"%08lx%08lx",(unsigned long)tv.tv_sec,(unsigned long)tv.tv_usec); } /* Send a message to a blackhole component, adding optionnally a signature */ void fdpass_send (_F_TCPSERVER_V1 *c, const string &secret, const char *line) { if (secret.size()==0){ c->send (line); c->send ("\n"); }else{ char salt[33]; fdpass_makesalt(salt); string buf = string(line) + " " + salt + " "; c->send (buf.c_str()); buf += secret; char out[SHA256_DIGEST_LENGTH*2+1]; fdpass_shasum (buf.c_str(),out); c->send (out); c->send ("\n"); } } void fdpass_send (_F_TCPSERVER_V1 *c, const string &host, const map &secrets, const char *line) { if (secrets.size() == 0){ static string empty; fdpass_send (c,empty,line); }else{ map::const_iterator it=secrets.find(host); if (it == secrets.end()){ tlmp_error ("Can't send message to host %s, no secret defined: %s\n",host.c_str(),line); }else{ fdpass_send (c,it->second,line); } } } void fdpass_sendf (_F_TCPSERVER_V1 *c, const string &secret, const char *ctl, ...) { char line[1000]; va_list list; va_start (list,ctl); vsnprintf (line,sizeof(line)-1,ctl,list); va_end (list); fdpass_send (c,secret,line); } void fdpass_sendf (_F_TCPSERVER_V1 *c, const string &host, const map &secrets, const char *ctl, ...) { char line[1000]; va_list list; va_start (list,ctl); vsnprintf (line,sizeof(line)-1,ctl,list); va_end (list); if (secrets.size() == 0){ static string empty; fdpass_send (c,empty,line); }else{ map::const_iterator it=secrets.find(host); if (it == secrets.end()){ tlmp_error ("Can't send message to host %s, no secret defined: %s\n",host.c_str(),line); }else{ fdpass_send (c,it->second,line); } } } int fdpass_sendto (int fd, const string &secret, const char *line) { int ret = -1; if (secret.size()==0){ int len = strlen(line); if (write (fd,line,len)==len && write (fd,"\n",1)==1){ ret = 0; } }else{ char salt[33]; fdpass_makesalt(salt); string buf = string(line) + " " + salt + " "; if (write (fd,buf.c_str(),buf.size())==(int)buf.size()){ buf += secret; char out[SHA256_DIGEST_LENGTH*2+1]; fdpass_shasum (buf.c_str(),out); if (write (fd,out,SHA256_DIGEST_LENGTH*2)==SHA256_DIGEST_LENGTH*2 && write (fd,"\n",1)==1){ ret = 0; } } } return ret; } int fdpass_sendto (int fd, const string &host, const map &secrets, const char *line) { int ret = -1; if (secrets.size() == 0){ static string empty; fdpass_sendto (fd,empty,line); }else{ map::const_iterator it=secrets.find(host); if (it == secrets.end()){ tlmp_error ("Can't send message to host %s, no secret defined: %s\n",host.c_str(),line); }else{ ret = fdpass_sendto (fd,it->second,line); } } return ret; } int fdpass_sendtof (int fd, const string &secret, const char *ctl, ...) { char line[1000]; va_list list; va_start (list,ctl); vsnprintf (line,sizeof(line)-1,ctl,list); va_end (list); return fdpass_sendto (fd,secret,line); } int fdpass_sendtof (int fd, const string &host, const map &secrets, const char *ctl, ...) { char line[1000]; va_list list; va_start (list,ctl); vsnprintf (line,sizeof(line)-1,ctl,list); va_end (list); return fdpass_sendto (fd,host,secrets,line); } static const char *badchar = "\"'`();*?"; /* Check if a protocol line do not contain bad character (dangourous for scripting) */ bool fdpass_protocheck (const char *line, string &newline) { bool ret = true; if (strlen(line)>250){ newline = string(line,250); ret = false; }else{ while (*badchar != '\0'){ if (strchr(line,*badchar)!=NULL){ newline = line; ret = false; break; } badchar++; } } return ret; } /* Check if this char is a member of the badchar set */ bool fdpass_isbadchar (const char car) { return strchr(badchar,car)!=NULL; } /* Format the intruder time */ void fdpass_format_intruder (time_t intrudetime, char intruder[100]) { intruder[0] = '\0'; if (intrudetime != 0){ DATEASC date; fdpass_asctime (intrudetime,date); snprintf (intruder,100-1," *** Intruder since %s",date.buf); } } /* Read one line in a text file */ void fdpass_readsecret ( const char *secretfile, string &mysecret) { mysecret = ""; if (secretfile != NULL){ glocal string *mysecret = &mysecret; (secretfile,true); line = str_skip(line); if (*line != '\0') *glocal.mysecret = line; return 0; } } void fdpass_readsecrets( const char *secretfile, map &secrets) { secrets.clear(); if (secretfile != NULL){ glocal map *secrets = &secrets; (secretfile,true); const char *pt = str_skip(line); if (*pt != '\0' && *pt != '#'){ string name,secret; pt = str_copyword (name,line); pt = str_copyword(secret,pt); pt = str_skip(pt); if (secret.size() == 0 || *pt != '\0'){ tlmp_error ("File %s, line %d, invalid line\n",info.filename,noline); }else{ (*glocal.secrets)[name] = secret; } } return 0; } } /* Find one secret in a map. If the secret is not there, return an empty string */ string fdpass_findsecret (const map &secrets, const string &host) { string ret; if (secrets.size() > 0){ map::const_iterator it = secrets.find(host); if (it != secrets.end()){ ret = it->second; } } return ret; } static const char REQ_ENDREQUEST=1; static const char REQ_ENDNAME=2; static const char REQ_ENDVALUE=3; static const char REQ_ENDVVALUE=4; static const char REQ_EOL=5; static const char REQ_ENDVVEC=6; static const char REQ_BOBSTART=7; static const char REQ_LASTMARK=7; void REQUEST::append (const char *s, const char marker) { if (valid){ const char *pt = s; while (*pt != '\0'){ if (*pt <= REQ_LASTMARK){ valid = false; break; } pt++; } if (valid){ int len = pt-s; if (offset + len + 1 < REQ_BUF_SIZE){ strcpy (buf+offset,s); offset += len; buf[offset++] = marker; }else{ valid = false; } } } } void REQUEST::append_bob (const void *s, size_t size) { if (valid){ if (offset + size +2 + 1 < REQ_BUF_SIZE){ buf[offset] = REQ_BOBSTART; offset++; buf[offset] = size & 0xff; offset++; buf[offset] = size >> 8; offset++; memcpy (buf+offset,s,size); offset += size; }else{ valid = false; } } } void REQUEST::append_name (const char *name) { if (name != NULL) append (name,REQ_ENDNAME); } /* Add one value */ void REQUEST::append_val (PARAM_STRING val) { append (val.ptr,REQ_ENDVALUE); } /* Add one value part of many */ void REQUEST::append_vals (PARAM_STRING val){ append (val.ptr,REQ_ENDVVALUE); } void REQUEST::append_endvalue () { append ("",REQ_ENDVALUE); } void REQUEST::append_endvvalue () { append ("",REQ_ENDVVALUE); } bool REQUEST::is_endvvalue (bool &err) { bool ret = false; err = false; const char *pt = buf+offset_read; const char *end = buf+offset; if (pt < end){ if (*pt == REQ_ENDVVALUE){ ret = true; offset_read++; } }else{ err = true;; } return ret; } void REQUEST::reset() { offset = 0; offset_read = 0; buf[0] = '\0'; valid = true; addsign_was_called = false; } REQUEST::REQUEST() { reset(); } void REQUEST::addsign() { if (offset != 0){ valid = false; tlmp_error ("REQUEST::addsign must be called before all adds\n"); }else{ addsign_was_called = true; offset += 32 + SHA256_DIGEST_LENGTH*2; } } void REQUEST::add (const char *name, int val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%d",val); append_name (name); append_val (tmp); } void REQUEST::add (const char *name, long val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%ld",val); append_name (name); append_val (tmp); } void REQUEST::add (const char *name, unsigned val) { char tmp[100]; snprintf (tmp,sizeof(tmp),"%u",val); append_name (name); append_val (tmp); } void REQUEST::add (const char *name, bool val) { append_name (name); append_val (val ? "1" : "0"); } void REQUEST::add (const char *name, char *val) { append_name (name); append_val (val); } void REQUEST::add (const char *name, const char *val) { append_name (name); append_val (val); } void REQUEST::add (const char *name, PARAM_STRING val) { append_name (name); append_val (val.ptr); } void REQUEST::add (const char *name, const string &val) { append_name (name); append_val (val.c_str()); } void REQUEST::add (const char *name, const BOB_TYPE &val) { append_name (name); append_bob (val.getbuffer(),val.getsize()); } void REQUEST::add (const char *name, const vector> &vals) { append_name (name); for (auto &x:vals){ for (auto &xx:x){ append_vals(xx.c_str()); } append ("",REQ_ENDVALUE); } append ("",REQ_ENDVVEC); } void REQUEST::add (const char *name, const vector &vals) { append_name (name); for (auto &x:vals){ append_vals(x.c_str()); } append_endvalue(); } void REQUEST::add (const char *name, const vector &vals) { append_name (name); for (auto x:vals){ append_vals(x); } append_endvalue(); } void REQUEST::add (const char *name, const PARAM_VECTOR_STRING vals) { append_name (name); for (auto x:vals){ append_vals(x); } append_endvalue(); } void REQUEST::add (const char *name, const vector &vals) { append_name (name); for (auto x:vals){ append_vals(x ? "1" : "0"); } append_endvalue(); } void REQUEST::add (const char *name, const vector &vals) { append_name (name); for (auto x:vals){ char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%d",x); append_vals(tmp); } append_endvalue(); } void REQUEST::add (const char *name, const vector &vals) { append_name (name); for (auto x:vals){ char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%u",x); append_vals(tmp); } append_endvalue(); } void REQUEST::add (const char *name, const vector &vals) { append_name (name); for (auto x:vals){ append_bob(x.getbuffer(),x.getsize()); } append_endvalue(); } void REQUEST::add_timestamp() { char tmp[100]; long long now = fdpass_getnow(); snprintf (tmp,sizeof(tmp)-1,"%Ld",now); add ("_time_",tmp); } bool REQUEST::is_valid() const { return valid; } // Put the last tag to end the REQUEST void REQUEST::complete() { append ("",REQ_ENDREQUEST); buf[offset] = '\0'; } // Checks that the getargs call reached the end of the buffer bool REQUEST::is_all_read() const { bool ret = false; if (offset_read == offset-1 && buf[offset_read] == REQ_ENDREQUEST) ret = true; return ret; } void REQUEST::sign(const char *secret) { if (!addsign_was_called){ tlmp_error ("Invalid REQUEST sequence: sign called without addsign\n"); valid = false; }else{ char *salt = buf + signlen; char tmpsalt[32+1]; // memset (tmpsalt,0,sizeof(tmpsalt)); fdpass_makesalt(tmpsalt); // for (unsigned i=0; i REQ_LASTMARK) pt++; if (*pt == REQ_ENDNAME){ int len = pt-start; param = string(start,len); pt++; }else if (*pt == REQ_BOBSTART){ pt++; unsigned len = pt[0] + ((unsigned char)pt[1]<<8); pt += 2+len; tlmp_warning ("param=%s bob=[%u]",param.c_str(),len); param.clear(); }else if (*pt == REQ_ENDVALUE){ int len = pt-start; tlmp_warning ("param=%s val=%*.*s",param.c_str(),len,len,start); param.clear(); pt++; }else if (*pt == REQ_ENDVVALUE){ int len = pt-start; if (param.size()>0){ tlmp_warning ("param=%s %*.*s, ",param.c_str(),len,len,start); param.clear(); }else{ tlmp_warning ("%*.*s, ",len,len,start); } pt++; if (*pt == REQ_ENDVALUE) pt++; }else if (*pt == REQ_ENDREQUEST){ break; } } // tlmp_warning ("\n"); } int request_splitline(char *buf, unsigned offset, std::function f) { int ret = 0; // We split the bloc in lines for (unsigned i=0; i 80) len = 80; unsigned last = i+len; for (unsigned j=i; jsend (buf,offset); }else{ // We send an empty answer anyway REQUEST req; req.complete(); c->send (req.buf,req.offset); } return ret; } // 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::addpart (const char *line, int len, EXTRABYTES &extra) { int ret = -1; if (!valid){ ret = -1; }else{ if (len + offset > REQ_BUF_SIZE){ valid = false; }else{ memcpy(buf+offset,line,len); offset += len; ret = 0; // We have to figure out if we have received everything. We can't just check if there is REQ_ENDREQUEST // at the end of the buffer since we support binary data (BOB_TYPE). // So we have to walk the buffer const char *pt = buf; const char *end = buf + offset; while (pt < end){ while (pt < end && *pt > REQ_LASTMARK) pt++; if (pt < end){ if (*pt == REQ_ENDREQUEST){ int nb = (end - pt)-1; if (nb > 0){ //tlmp_warning ("addpart save nb=%d\n",nb); pt++; extra.save(pt,nb); offset = (pt-buf); } ret = 1; break; }else if (*pt == REQ_BOBSTART){ pt++; if (pt+2 <= end){ unsigned len = pt[0] + ((unsigned char)pt[1]<<8); pt += 2+len; if (pt > end) break; }else{ break; } }else{ // Skip the mark pt++; } } } //if (buf[offset-1] == REQ_ENDREQUEST) ret = 1; } } return ret; } int REQUEST::addpart (const char *line, int len) { EXTRABYTES extra; return addpart (line,len,extra); } int REQUEST::addpart (const char *line) { return addpart (line,strlen(line)); } /* This is like addpart, except the line read came from REQUEST::write. which output the buffer in 80 characters lines and encode the \n as REQ_EOL */ int REQUEST::addpartfile (const char *line) { string tmp(line); for (unsigned i=0; i REQ_LASTMARK) pt++; if (*pt == REQ_ENDNAME){ *pt++ = '\0'; // We end the string there. Clearly, we can walk the buffer once if (strcmp(start,name)==0){ offset_read = pt-buf; retpt = pt; ret = 0; } } } return ret; } int REQUEST::getarg (const char *name, const char *&val) { int ret = -1; val = ""; char *pt; if (checkname (name,pt) != -1){ val = pt; const char *end = buf+offset; while (pt < end && *pt > REQ_LASTMARK){ pt++; } if (*pt == REQ_ENDVALUE){ *pt++ = '\0'; offset_read = pt-buf; ret = 0; }else{ val = ""; } } return ret; } int REQUEST::getarg (const char *name, string &val) { const char *ptval; int ret = getarg (name,ptval); if (ret != -1) val = ptval; return ret; } int REQUEST::getarg (const char *name, int &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (pt[0] == '-') pt++; if (fdpass_is_all_digit(pt)){ val = atoi(valstr); ret = 0; } } return ret; } int REQUEST::getarg (const char *name, long &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (pt[0] == '-') pt++; if (fdpass_is_all_digit(pt)){ val = atoi(valstr); ret = 0; } } return ret; } int REQUEST::getarg (const char *name, unsigned long long &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (fdpass_is_all_digit(pt)){ val = atoll(valstr); ret = 0; } } return ret; } int REQUEST::getarg (const char *name, unsigned &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ const char *pt = valstr; if (fdpass_is_all_digit(pt)){ val = atoi(valstr); ret = 0; } } return ret; } int REQUEST::getarg (const char *name, bool &val) { int ret = -1; const char *valstr; if (getarg(name,valstr)==0){ if (strcmp(valstr,"1")==0){ val = true; ret = 0; }else if (strcmp(valstr,"0")==0){ val = false; ret = 0; } } return ret; } int REQUEST::getarg (const char *name, BOB_TYPE &val) { int ret = -1; char *pt; if (checkname(name,pt)!=-1){ const char *end = buf+offset; if (pt < end && *pt == REQ_BOBSTART){ pt++; if (pt+2 <= end){ unsigned len = pt[0] + ((unsigned char)pt[1]<<8); pt += 2; if (pt + len <= end){ val.setbuffer(pt,len,false); pt += len; offset_read = pt-buf; ret = 0; } } } } return ret; } int REQUEST::getarg (const char *name, vector &vals) { char *pt; int ret = checkname(name,pt); if (ret != -1){ const char *end = buf+offset; while (true){ const char *val = pt; while (pt < end && *pt > REQ_LASTMARK) pt++; if (*pt == REQ_ENDVVALUE){ *pt++ = '\0'; vals.push_back(val); }else if (*pt == REQ_ENDVALUE && pt == val){ pt++; ret = 0; break; }else{ break; } } offset_read = pt-buf; } if (ret != 0){ name = ""; vals.clear(); } return ret; } int REQUEST::getarg (const char *name, vector &vals) { char *pt; int ret = checkname(name,pt); if (ret != -1){ const char *end = buf+offset; while (true){ const char *val = pt; while (pt < end && *pt > REQ_LASTMARK) pt++; if (*pt == REQ_ENDVVALUE){ *pt++ = '\0'; vals.push_back(val); }else if (*pt == REQ_ENDVALUE && pt == val){ pt++; ret = 0; break; }else{ break; } } offset_read = pt-buf; } if (ret != 0){ name = ""; vals.clear(); } return ret; } int REQUEST::getarg (const char *name, vector > &vals) { char *pt; int ret = checkname(name,pt); if (ret != -1){ const char *end = buf+offset; vector vvals; while (pt < end && *pt != REQ_ENDVVEC){ const char *val = pt; while (pt < end && *pt > REQ_LASTMARK) pt++; if (*pt == REQ_ENDVVALUE){ *pt++ = '\0'; vvals.push_back(val); }else if (*pt == REQ_ENDVALUE && pt == val){ pt++; vals.push_back(vvals); vvals.clear(); }else{ break; } } if (*pt == REQ_ENDVVEC){ ret = 0; pt++; } offset_read = pt-buf; } if (ret != 0){ name = ""; vals.clear(); } return ret; } int REQUEST::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::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::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_INFO::addpart (const char *line, int len) { int ret = -1; if (intruder == (time_t)0){ ret = req.addpart(line,len); if(ret == 1){ if (secret.size() > 0){ if (req.checksign(secret.c_str())==-1){ intruder = time(NULL); ret = -1; } } } } return ret; } int REQUEST_INFO::addpart (const char *line) { return addpart (line,strlen(line)); } CONNECT_INFO::~CONNECT_INFO() { close (); } CONNECT_INFO::CONNECT_INFO() { fd = -1; } CONNECT_INFO::CONNECT_INFO(int _fd) { fd = _fd; } void CONNECT_INFO::copy (const CONNECT_INFO &c) { host = c.host; port = c.port; bind = c.bind; secret = c.secret; fd = -1; values.clear(); timeout = c.timeout; timeout_seen = false; } void CONNECT_INFO::swap (CONNECT_INFO &&c) { std::swap(host,c.host); std::swap(port,c.port); std::swap(bind,c.bind); std::swap(secret,c.secret); std::swap(fd,c.fd); std::swap(values,c.values); std::swap(timeout_seen,c.timeout_seen); std::swap(timeout,c.timeout); } CONNECT_INFO::CONNECT_INFO(const CONNECT_INFO &c) { copy (c); } CONNECT_INFO::CONNECT_INFO(CONNECT_INFO &&c) { fd = -1; swap (std::forward(c)); } CONNECT_INFO & CONNECT_INFO::operator=(const CONNECT_INFO &c) { close (); copy (c); return *this; } CONNECT_INFO & CONNECT_INFO::operator=(CONNECT_INFO &&c) { close (); swap (std::forward(c)); return *this; } void CONNECT_INFO::close() { ::close (fd); fd = -1; } /* Set timeout for receive operation. if seconds == -1, disable timeout. timeout is disabled by default. */ void CONNECT_INFO::set_timeout (int seconds) { timeout = seconds; } bool CONNECT_INFO::was_timeout () const { return timeout_seen; } void CONNECT_INFO::reconnect() { close (); if (host.empty()){ fd = fdpass_tcpconnect (NULL,false,"unix:",port.c_str()); }else{ const char *bindstr=NULL; if (bind.size() > 0) bindstr=bind.c_str(); fd = fdpass_tcpconnect (bindstr,false,host.c_str(),port.c_str()); } } void CONNECT_INFO::reset(REQUEST &req) { req.reset(); if (secret.size() > 0) req.addsign(); } int CONNECT_INFO::send (const char *command, vector &lines) { int ret = 0; lines.clear(); string tmp; for (auto it:values) tmp += string(" ") + it; values.clear(); if (fdpass_sendtof (fd,secret,"%s %s",command,tmp.c_str())==-1){ reconnect(); if (fdpass_sendtof (fd,secret,"%s %s",command,tmp.c_str())==-1){ ret = -1; } } if (ret == 0){ glocal vector *lines = &lines; (fd,10); if (strcmp(line,"Ok")==0){ end = true; }else{ glocal.lines->push_back(line); } } return ret; } int CONNECT_INFO::send (REQUEST &req) { int ret = 0; req.complete(); if (secret.size() > 0) req.sign(secret.c_str()); if (req.sendfd(fd)==-1){ reconnect(); if (req.sendfd(fd)==-1){ ret = -1; } } if (ret != -1) req.reset(); return ret; } int CONNECT_INFO::read_select (void *data, size_t len) { int ret = -1; timeout_seen = false; if (timeout == -1){ ret = read(fd,data,len); }else{ fd_set fds; struct timeval time_out; FD_ZERO(&fds); FD_SET(fd, &fds); time_out.tv_usec = 0; time_out.tv_sec = timeout; int ok = select(fd + 1, &fds, NULL, NULL, &time_out); if (ok == 0){ // timeout has occurred. timeout_seen = true; }else{ ret = read(fd,data,len); } } return ret; } int CONNECT_INFO::receive(REQUEST &req) { req.reset(); int ok = 0; char line[REQ_BUF_SIZE+1]; int len; if (extra.filled()){ len = extra.get(line); //tlmp_warning ("CON::receive extra len=%d",len); ok = req.addpart (line,len,extra); } if (ok == 0){ while ((len=read_select(line,sizeof(line)-1))>0){ line[len] = '\0'; ok = req.addpart(line,len,extra); if (ok != 0) break; } } return ok == 1 ? 0 : -1; } // Return true if there are unprocessed bytes // This happens when using asynchronous protocols // (where the server may issue several replies in a row for a single query) bool CONNECT_INFO::has_more() const { return extra.filled(); } static string force_addr; void fdpass_set_force_addr (PARAM_STRING addr) { force_addr = addr.ptr; } const char *fdpass_get_force_addr() { return force_addr.c_str(); } int fdpass_sendmail ( PARAM_STRING mailserv, PARAM_STRING mailport, PARAM_STRING from, PARAM_STRING addr, // Email address PARAM_STRING subject, PARAM_STRING body) { glocal const char *from = from.ptr; glocal const char *to_addr = addr.ptr; glocal const char *body_addr = addr.ptr; glocal const char *subject = subject.ptr; glocal const char *body = body.ptr; glocal int success = 0; glocal int noline = 0; if (force_addr.size() > 0) glocal.to_addr = force_addr.c_str(); (mailserv,mailport,1); debug_printf ("sendmail: %s\n",line); if (glocal.noline==0){ sendf ("Helo writed\n"); }else if (glocal.noline == 1){ sendf ("mail from: %s\r\n",glocal.from); }else if (glocal.noline == 2){ sendf ("rcpt to: %s\r\n",glocal.to_addr); }else if (glocal.noline == 3){ sendf ("data\r\n"); }else if (glocal.noline == 4){ time_t now = time(NULL); struct tm *t = localtime(&now); int hour = t->tm_hour; const char *ampm = "AM"; if (hour > 12){ hour -= 12; ampm = "PM"; } static const char *tbday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; static const char *tbmonth[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; string date = string_f ("%s %s %d %04d %d:%02d%s",tbday[t->tm_wday],tbmonth[t->tm_mon],t->tm_mday,t->tm_year+1900 ,hour,t->tm_min,ampm); // Convert the message. Newlines are converted to crlf. // crlf are kept as is. string crlfbody = copystring(glocal.body,[](auto &c, auto pt){ if (pt[0] == '\r' && pt[1] == '\n'){ c.keep(2); }else if (pt[0] == '\n'){ c.replace(1,"\r\n"); } }); sendf ("from: %s\r\n" "to: %s\r\n" "date: %s\r\n" "subject: %s\r\n" "\r\n%s\r\n.\r\n" ,glocal.from ,glocal.body_addr ,date.c_str() ,glocal.subject ,crlfbody.c_str()); }else{ glocal.success = 1; send ("quit\r\n"); end = true; } glocal.noline++; return glocal.success; } #ifdef TEST static void fdpass_shasum_old (const char *line, char out[SHA256_DIGEST_LENGTH*2+1]) { SHA256_CTX ctx; SHA256_Init(&ctx); SHA256_Update(&ctx, line,strlen(line)); unsigned char bufout[BUFSIZ]; SHA256_Final(bufout, &ctx); char *ptout = out; for (unsigned j = 0; j < SHA256_DIGEST_LENGTH; j++){ sprintf(ptout,"%02x", bufout[j]); ptout += 2; } } static void fdpass_shasum_old (const char *buf1, int lenbuf1, const char *buf2, int lenbuf2, char out[SHA256_DIGEST_LENGTH*2+1]) { SHA256_CTX ctx; SHA256_Init(&ctx); SHA256_Update(&ctx, buf1,lenbuf1); SHA256_Update(&ctx, buf2,lenbuf2); unsigned char bufout[BUFSIZ]; SHA256_Final(bufout, &ctx); char *ptout = out; for (unsigned j = 0; j < SHA256_DIGEST_LENGTH; j++){ sprintf(ptout,"%02x", bufout[j]); ptout += 2; } } int main (int argc, char *argv[]) { for (int i=1; i