WIP: uniproc
[hband-tools.git] / preload / autossl / autossl.c
blobe673076378b992c1e33d0b5479a147ad4f00538d
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 #include <dlfcn.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <bsd/unistd.h>
13 void _autossl_ip_parse_error(const char* s, const size_t len)
15 fprintf(stderr, "autossl: failed to parse ip address '%.*s'\n", len, s);
18 int connect(int sockfd, const struct sockaddr_in *orig_sockaddr, socklen_t addrlen)
20 char *upgrade_ip_str;
21 struct in_addr upgrade_ip;
22 char *upgrade_port_str;
23 in_port_t upgrade_port = 0;
25 char *tls_cmd;
26 #define IP_STR_LEN 39+1
27 #define PORT_STR_LEN 5+1
28 char connect_ip[IP_STR_LEN];
29 char connect_port[PORT_STR_LEN];
31 char *next_separator;
32 char *autossl_errno_str;
34 struct sockaddr_in to_sockaddr;
35 int (*orig_connect)(int, const struct sockaddr_in *, socklen_t) = dlsym(RTLD_NEXT, "connect");
39 if(orig_sockaddr->sin_family != AF_INET) goto stdlib;
41 upgrade_port_str = getenv("AUTOSSL_UPGRADE_PORTS");
42 if(upgrade_port_str == NULL) goto stdlib;
45 int upgrade_port_matched = 0;
46 next_separator = NULL;
48 do{
49 next_separator = strchrnul(upgrade_port_str, ' ');
50 upgrade_port = atoi(upgrade_port_str);
51 if(upgrade_port == 0) { fprintf(stderr, "autossl: failed to parse port number(s): %s\n", upgrade_port_str); goto error_case; }
52 if(upgrade_port == ntohs(orig_sockaddr->sin_port)) upgrade_port_matched = 1;
53 upgrade_port_str = (char*)(next_separator + 1);
55 while(!upgrade_port_matched && *next_separator != '\0');
57 if(!upgrade_port_matched) goto stdlib;
59 upgrade_ip_str = getenv("AUTOSSL_UPGRADE_IPS");
60 if(upgrade_ip_str != NULL)
62 int upgrade_ip_matched = 0;
63 char *next_separator = NULL;
65 do{
66 next_separator = strchrnul(upgrade_ip_str, ' ');
67 if(inet_aton(upgrade_ip_str, &upgrade_ip) == 0)
69 _autossl_ip_parse_error(upgrade_ip_str, next_separator - upgrade_ip_str);
70 goto error_case;
72 if(ntohl(orig_sockaddr->sin_addr.s_addr) == ntohl(upgrade_ip.s_addr)) {
73 upgrade_ip_matched = 1;
75 upgrade_ip_str = (char*)(next_separator + 1);
77 while(!upgrade_ip_matched && *next_separator != '\0');
79 if(!upgrade_ip_matched) goto stdlib;
82 tls_cmd = getenv("AUTOSSL_TLS_CMD");
83 if(tls_cmd == NULL) goto stdlib;
86 int sockpair[2];
87 if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == -1)
89 perror("autossl: sockpair");
90 goto error_case;
93 pid_t childpid = fork();
94 if(childpid < 0)
96 perror("autossl: fork");
97 goto error_case;
99 if(childpid == 0)
101 /* save the ip and port we wanted to connect to as strings */
102 snprintf(connect_ip, IP_STR_LEN, "%s", inet_ntoa(orig_sockaddr->sin_addr));
103 snprintf(connect_port, PORT_STR_LEN, "%d", ntohs(orig_sockaddr->sin_port));
104 /* wire STDIO to the newly created socket */
105 dup2(sockpair[1], 0);
106 dup2(sockpair[1], 1);
107 /* leave stderr open */
108 /* close dangling files */
109 closefrom(3);
110 execlp(tls_cmd, tls_cmd, connect_ip, connect_port, NULL);
111 _exit(127);
114 close(sockpair[1]);
115 if(dup2(sockpair[0], sockfd) == -1)
117 perror("autossl: dup2");
118 close(sockpair[0]);
119 goto error_case;
122 if(!getenv("AUTOSSL_SILENT"))
123 fprintf(stderr, "autossl: redirecting %s:%d -> fd#%d\n", inet_ntoa(orig_sockaddr->sin_addr), ntohs(orig_sockaddr->sin_port), sockpair[0]);
125 /* the caller closes sockfd only, not sockpair[0], so unused open
126 files may pile up eventually in long running programs. */
128 /* childpid process won't be reaped, so don't be scared on the
129 zombie processes, they will be disappear as the main program exits. */
131 return 0;
133 error_case:
134 autossl_errno_str = getenv("AUTOSSL_ERRNO");
135 if(autossl_errno_str)
137 errno = atoi(autossl_errno_str);
138 return -1;
141 stdlib:
142 return orig_connect(sockfd, orig_sockaddr, addrlen);