repo: tlswrap
action: commit
revision: 
path_from: 
revision_from: f5f8ee4c04fbcfef45a8a9e0bb87035d32064101:
path_to: 
revision_to: 
git.thebackupbox.net
tlswrap
git clone git://git.thebackupbox.net/tlswrap
commit f5f8ee4c04fbcfef45a8a9e0bb87035d32064101
Author: epoch 
Date:   Wed Dec 20 15:07:03 2023 +0000

    tlswrap got pid grouping so it can kill off all children. also made a version that does not actually do any TLS to do /just/ the pid grouping and killing.

diff --git a/Makefile b/Makefile
index 4fb4e683e5d3f0ff26c39ff632c12da550e70ffa..
index ..644124183540456837f2916efe4f7c74d5ee98e1 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ PREFIX:=/usr/local

 .PHONY: install all

-all: tlswrap-ja3 tlswrap
+all: tlswrap-ja3 tlswrap nontlswrap

 tlswrap: CFLAGS=-pedantic -Wall
 tlswrap: LDLIBS=-lssl -lcrypto
@@ -12,6 +12,10 @@ tlswrap-ja3: CFLAGS=-DJA3 -pedantic -Wall
 tlswrap-ja3: LDLIBS=-lssl -lcrypto
 tlswrap-ja3: tlswrap-ja3.c

+nontlswrap: CFLAGS=-pedantic -Wall
+nontlswrap: nontlswrap.c
+
 install: all
 	install -Dt $(PREFIX)/bin tlswrap
 	install -Dt $(PREFIX)/bin tlswrap-ja3
+	install -Dt $(PREFIX)/bin nontlswrap
diff --git a/nontlswrap.c b/nontlswrap.c
new file mode 100644
index 0000000000000000000000000000000000000000..d9454045ec2e4ce940b8981f43b3577182fdfae9
--- /dev/null
+++ b/nontlswrap.c
@@ -0,0 +1,209 @@
+#include  // snprintf
+#include  // setenv
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include  // waitpid
+#include 
+#include 
+
+#include 
+
+int main(int argc,char *argv[]) {
+  setpgid(0,0);
+  setvbuf(stdin, NULL, _IONBF, 0);
+  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stderr, NULL, _IONBF, 0);
+  syslog(LOG_DAEMON|LOG_DEBUG,"started");
+  struct sockaddr_in6 sa6;
+  char ra[NI_MAXHOST],rp[NI_MAXSERV];
+  char sa[NI_MAXHOST],sp[NI_MAXSERV];
+  char ru[6+3+NI_MAXHOST+NI_MAXSERV+1];//6 for "tcp://", 3 just in case [ and ] are needed and the :, +1 for null
+  char su[6+3+NI_MAXHOST+NI_MAXSERV+1];
+  unsigned int sl=sizeof(sa6);
+
+  argv++;//skip argv[0]
+  argc--;
+
+  if(argc == 0
+    || !strcmp(argv[0],"--help")
+    || !strcmp(argv[0],"-h")) {
+    fprintf(argc?stdout:stderr,"usage: nontlswrap [--help|-h]  [] [] [...]\n");
+    return 1;
+  }
+  if(argc == 0) {
+    fprintf(stderr,"missing argument. need the absolute path of an executable and arguments to execv into at the end.\n");
+    return 1;
+  }
+
+  if(getsockname(0,(struct sockaddr *)&sa6,&sl) != -1) {
+    if(getnameinfo((struct sockaddr *)&sa6,sl,sa,sizeof(sa),sp,sizeof(sp),NI_NUMERICHOST|NI_NUMERICSERV) == 0) {
+      setenv("SERVER_ADDR",sa,1);
+      setenv("SERVER_PORT",sp,1);
+      if(sa6.sin6_family == AF_INET6) {
+        snprintf(su,sizeof(su)-1,"tcp://[%s]:%s",sa,sp);
+      } else {
+        snprintf(su,sizeof(su)-1,"tcp://%s:%s",sa,sp);
+      }
+      setenv("SERVER_URL",su,1);
+    }
+  }
+  if(getpeername(0,(struct sockaddr *)&sa6,&sl) != -1) {
+    if(getnameinfo((struct sockaddr *)&sa6,sl,ra,sizeof(ra),rp,sizeof(rp),NI_NUMERICHOST|NI_NUMERICSERV) == 0) {
+      setenv("REMOTE_ADDR",ra,1);
+      setenv("REMOTE_PORT",rp,1);
+      if(sa6.sin6_family == AF_INET6) {
+        snprintf(ru,sizeof(ru)-1,"tcp://[%s]:%s",ra,rp);
+      } else {
+        snprintf(ru,sizeof(ru)-1,"tcp://%s:%s",ra,rp);
+      }
+      setenv("REMOTE_URL",ru,1);
+    }
+  }
+  int x;
+
+  int a[2]; //a is subprocess's stdin, so need to read decrypted data from stdin and write to a[1]
+  int b[2]; //b is subprocees's stdout, so need to read it, and give it to SSL to encrypt and push out.
+  int c[2]; //c is subprocess's stderr, so need to read it, and write lines to syslog.
+  pipe(a);
+  pipe(b);
+  pipe(c);
+
+  syslog(LOG_DAEMON|LOG_DEBUG,"accepted a connection!");
+  char buffer[65535];//fuck it. let's make it big.
+
+  int child=fork();
+  if(child == 0) {
+    x=dup(0);
+    dup2(a[0],0);
+    dup2(b[1],1);
+    dup2(c[1],2);
+    close(a[0]);
+    close(b[1]);
+    close(a[1]);
+    close(b[0]);
+    close(c[0]);
+    close(c[1]);
+    dup2(x,3);//we're passing this to the child ONLY so it can do getpeername and stuff. this can probably be closed.
+    execv(argv[0],argv);
+    printf("[!!!] server-side tlswrap configuration error.\n");
+    printf("[!!!] failed to execute subprocess.\n");
+    for(;*argv;argv++) {
+      printf("[!!!] argv: %s\n",*argv);
+    }
+    return 0;
+  }
+  if(child == -1) {
+    syslog(LOG_DAEMON|LOG_WARNING,"failed to fork");
+    return 1;
+  }
+  //fprintf(stderr,"made it here\n");
+  syslog(LOG_DAEMON|LOG_ERR,"our pid: %d child pid: %d",getpid(),child);
+  int j;
+  int r1;
+  int r2;
+  int r3;
+  int fdmax=0;
+  fd_set master;
+  fd_set readfs;
+  fd_set errorfs;
+  FD_ZERO(&master);
+  FD_ZERO(&readfs);
+  FD_ZERO(&errorfs);
+  FD_SET(0,&master);//SSL is ready to be read from
+  FD_SET(b[0],&master);//subprocess's stdout is ready to be read from
+  FD_SET(c[0],&master);//subprocess's stderr
+  fdmax=b[0]>c[0]?b[0]:c[0];
+  struct timeval orig_timeout;
+  struct timeval timeout;
+  orig_timeout.tv_sec=0;
+  orig_timeout.tv_usec=10000;// 1/100th of a second. (10ms) sound good?
+  close(a[0]);
+  close(b[1]);
+  close(c[1]);
+  //unsigned int error_code;
+  //unsigned int error_code_size = sizeof(error_code);
+  //syslog(LOG_DAEMON|LOG_DEBUG,"entering select loop");
+  //fprintf(stderr,"made it here\n");
+  for(;FD_ISSET(b[0],&master) || FD_ISSET(c[0],&master);) { //a select() brick that reads from ssl and writes to subprocess and reads from subprocess and writes to ssl
+    readfs=master;
+    errorfs=master;
+    timeout=orig_timeout;
+    if((j=select(fdmax+1,&readfs,0,&errorfs,&timeout)) == -1 ) {
+      //syslog(LOG_DAEMON|LOG_ERR,"giving up. error'd in select");
+      break;
+    }
+
+    if(recv(0,NULL,1, MSG_PEEK | MSG_DONTWAIT) == 0) { //make sure the TLS is still connected. :D
+      syslog(LOG_DAEMON|LOG_ERR,"non-TLS connection seems to have dropped unexpectedly.\n");
+      break;
+    }
+
+    if(FD_ISSET(0,&errorfs)) syslog(LOG_DAEMON|LOG_ERR,"select: stdin error");
+    if(FD_ISSET(b[0],&errorfs)) syslog(LOG_DAEMON|LOG_ERR,"select: b[0] error");
+    if(FD_ISSET(c[0],&errorfs)) syslog(LOG_DAEMON|LOG_ERR,"select: c[0] error");
+    if(FD_ISSET(0,&readfs)) {
+      if((r1=read(0,buffer,sizeof(buffer))) <= 0) {
+        syslog(LOG_DAEMON|LOG_DEBUG,"EOF or error on stdin %d msg: %s",r1,strerror(errno));
+        FD_CLR(0,&master);
+	close(a[1]);
+      } else {
+        syslog(LOG_DAEMON|LOG_DEBUG,"SSL read? %d msg: %s",r1,strerror(errno));
+        syslog(LOG_DAEMON|LOG_DEBUG,"read %d bytes from ssl!",r1);
+        if((r3=write(a[1],buffer,r1) < 0)) {
+          syslog(LOG_DAEMON|LOG_ERR,"a write failed. -_- %d",r3);
+        }
+      }
+    }
+    if(FD_ISSET(b[0],&readfs)) {
+      if((r2=read(b[0],buffer,sizeof(buffer))) <= 0) {
+        syslog(LOG_DAEMON|LOG_DEBUG,"subprocess stdout done.");
+        FD_CLR(b[0],&master);
+	close(b[0]);
+      } else {
+        syslog(LOG_DAEMON|LOG_DEBUG,"read %d bytes from subprocess!",r2);
+        if((r3=write(1,buffer,r2)) <= 0) {
+          syslog(LOG_DAEMON|LOG_ERR,"write had an error: %d %s",r3,strerror(errno));
+        }
+      }
+    }
+    if(FD_ISSET(c[0],&readfs)) {
+      if((r2=read(c[0],buffer,sizeof(buffer)-1)) <= 0) {
+        syslog(LOG_DAEMON|LOG_DEBUG,"subprocess stderr done.");
+        FD_CLR(c[0],&master);
+      } else {
+        //write(2,buffer,r2);
+        buffer[r2]=0;//gotta null this off sice we're passing to something that expects a string.
+        //fprintf(stderr,"%s",buffer);
+        syslog(LOG_DAEMON|LOG_WARNING,"%s -> %s stderr of [%s] : %s",ru,su,argv[0],buffer);
+      }
+    }
+  }
+
+  int status;
+  if(killpg(getpid(),SIGCONT)) { //if the process hasn't exited already, let's tell them they've been hungup on.
+    syslog(LOG_DAEMON|LOG_WARNING,"killpg(%d,SIGCONT): %s",getpid(),strerror(errno));
+  }
+  sleep(5);
+  if(waitpid(child,&status,WNOHANG) == child) {
+    syslog(LOG_DAEMON|LOG_ERR,"child process exited after a SIGCONT and a 5s wait. gonna send a HUP to everything else anyway.");
+  }
+  if(killpg(getpid(),SIGHUP)) {
+    syslog(LOG_DAEMON|LOG_ERR,"killpg: %s",strerror(errno));
+  }
+  sleep(5);
+  if(killpg(getpid(),SIGTERM)) {
+    syslog(LOG_DAEMON|LOG_ERR,"killpg: %s",strerror(errno));
+  }
+  sleep(5);
+  if(killpg(getpid(),SIGKILL)) {
+    syslog(LOG_DAEMON|LOG_ERR,"killpg: %s",strerror(errno));
+  }
+  syslog(LOG_DAEMON|LOG_ERR,"sent signals to all processes in our process group. they should be DEAD");
+  return 0;
+}
+
+
diff --git a/tlswrap.c b/tlswrap.c
index 41b7ddb563af2f4ea49980294f2e47f5b3c7da3e..
index ..65e29782af24a1d3d133a6a4bc93a9c483fef6d1 100644
--- a/tlswrap.c
+++ b/tlswrap.c
@@ -398,6 +398,7 @@ int tlswrap_SSL_early_cb_fn(SSL *s, int *al, void *arg) {
 #endif

 int main(int argc,char *argv[]) {
+  setpgid(0,0);
   setvbuf(stdin, NULL, _IONBF, 0);
   setvbuf(stdout, NULL, _IONBF, 0);
   setvbuf(stderr, NULL, _IONBF, 0);
@@ -539,9 +540,9 @@ int main(int argc,char *argv[]) {
     }
     //now, let's try harder on these error messages.
     err_err = ERR_get_error(); //???
-    if(errno == 0) {//good nuff?y
-      return 1;
-    }
+    //if(errno == 0) {//good nuff?y
+    //  return 1;
+    //}

     syslog(LOG_DAEMON|LOG_ERR,"%s -> %s SSL_accept() failed. %d / %d / %d / %s / %s / %d / %s",
       ru,
@@ -686,22 +687,27 @@ int main(int argc,char *argv[]) {
   SSL_shutdown(ssl);
   SSL_free(ssl);
   EVP_cleanup();
+
   int status;
+  char *url=getenv("REMOTE_URL");
+  if(killpg(getpid(),SIGCONT)) { //if the process hasn't exited already, let's tell them they've been hungup on.
+    syslog(LOG_DAEMON|LOG_WARNING,"%s: killpg(%d,SIGCONT): %s",url,getpid(),strerror(errno));
+  }
+  sleep(5);
   if(waitpid(child,&status,WNOHANG) == child) {
-    syslog(LOG_DAEMON|LOG_DEBUG,"child process exited as it should :)");
-    return 0;
+    syslog(LOG_DAEMON|LOG_ERR,"%s: child process exited after a SIGCONT and a 5s wait. gonna send a HUP to everything else anyway.",url);
   }
-  syslog(LOG_DAEMON|LOG_WARNING,"waitpid: %s",strerror(errno));
-  if(kill(child,SIGTERM)) { //if the process hasn't exited already, let's try to kill it.
-    syslog(LOG_DAEMON|LOG_WARNING,"kill: %s",strerror(errno));
+  if(killpg(getpid(),SIGHUP)) {
+    syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno));
   }
   sleep(5);
-  if(waitpid(child,&status,WNOHANG) == child) {
-    syslog(LOG_DAEMON|LOG_WARNING,"child process exited after a SIGTERM and a 5s wait.");
-    return 0;
+  if(killpg(getpid(),SIGTERM)) {
+    syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno));
+  }
+  sleep(5);
+  if(killpg(getpid(),SIGKILL)) {
+    syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno));
   }
-  syslog(LOG_DAEMON|LOG_CRIT,"waitpid: %s",strerror(errno));
+  syslog(LOG_DAEMON|LOG_ERR,"%s: sent signals to all processes in our process group. they should be DEAD",url);
   return 0;
 }
-
-

-----END OF PAGE-----