teed.c - teed - A multiplex relay tee(1) daemon. HTML git clone git://bitreich.org/teed git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/teed DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- teed.c (5223B) --- 1 /* 2 * See LICENSE file for license information. 3 * 4 * Copy me if you can. 5 * by 20h 6 */ 7 8 #include <unistd.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/socket.h> 13 #include <sys/un.h> 14 #include <signal.h> 15 #include <errno.h> 16 #include <sys/stat.h> 17 #include <sys/select.h> 18 19 volatile sig_atomic_t isrunning = 1; 20 21 void * 22 memdup(void *p, int l) 23 { 24 char *ret; 25 26 ret = calloc(1, l); 27 if (ret == NULL) { 28 perror("calloc"); 29 exit(1); 30 } 31 memcpy(ret, p, l); 32 33 return (void *)ret; 34 } 35 36 typedef struct llist llist; 37 struct llist { 38 llist *prev; 39 llist *next; 40 int dlen; 41 void *data; 42 }; 43 44 #define forllist(list, elem) for (elem = list;\ 45 elem; elem = elem->next) 46 47 void 48 closeallfds(llist *fds) 49 { 50 llist *e; 51 52 forllist(fds, e) 53 close(*(int *)e->data); 54 } 55 56 void 57 llist_free1(llist *l) 58 { 59 if (l == NULL) 60 return; 61 free(l->data); 62 l->data = NULL; 63 l->next = NULL; 64 l->prev = NULL; 65 free(l); 66 } 67 68 void 69 llist_free(llist *l) 70 { 71 llist *tl; 72 73 while (l != NULL) { 74 tl = l->next; 75 llist_free1(l); 76 l = tl; 77 } 78 } 79 80 llist * 81 llist_new(llist *prev, llist *next, void *data, int len) 82 { 83 llist *ret; 84 85 ret = calloc(1, sizeof(llist)); 86 if (ret == NULL) { 87 perror("calloc"); 88 exit(1); 89 } 90 ret->prev = prev; 91 ret->next = next; 92 if (data != NULL) { 93 ret->data = memdup(data, len); 94 ret->dlen = len; 95 } else { 96 ret->data = NULL; 97 ret->dlen = 0; 98 } 99 100 return ret; 101 } 102 103 int 104 llist_len(llist *l) 105 { 106 int llistl = 0; 107 for (;l != NULL; l = l->next) 108 llistl++; 109 return llistl; 110 } 111 112 llist * 113 llist_put(llist *l, void *data, int len) 114 { 115 llist *ol; 116 117 if (l == NULL) 118 return llist_new(NULL, NULL, data, len); 119 120 ol = l; 121 for (; l->next; l = l->next); 122 l->next = llist_new(l, NULL, data, len); 123 124 return ol; 125 } 126 127 llist * 128 llist_del(llist **l, llist *e) 129 { 130 llist *enext, *eprev; 131 132 enext = e->next; 133 eprev = e->prev; 134 if (enext != NULL) 135 enext->prev = eprev; 136 if (eprev != NULL) 137 eprev->next = enext; 138 llist_free1(e); 139 140 if (*l == e) 141 *l = enext; 142 143 if (eprev != NULL) 144 return eprev; 145 if (enext != NULL) 146 return enext; 147 return NULL; 148 } 149 150 void 151 sighandler(int signo) 152 { 153 switch (signo) { 154 case SIGINT: 155 case SIGTERM: 156 isrunning = 0; 157 break; 158 } 159 } 160 161 void 162 initsignals(void) 163 { 164 signal(SIGPIPE, SIG_IGN); 165 signal(SIGINT, sighandler); 166 signal(SIGTERM, sighandler); 167 } 168 169 int 170 main(int argc, char *argv[]) 171 { 172 int on, i, maxsfd, afd, rval, recvl, 173 sendl, sent, lfd; 174 struct sockaddr_un saddrs[2], clt; 175 char *bindpaths[2] = {"in", "out"}, 176 recvbuf[4*1024], 177 *sendbufi; 178 llist *wfds, *rfds, *lfds, *e, *qe; 179 socklen_t saddrlen, cltlen; 180 fd_set fdset; 181 struct timespec timeout; 182 183 initsignals(); 184 185 rval = 1; 186 rfds = NULL; 187 wfds = NULL; 188 lfds = NULL; 189 190 on = 1; 191 for (i = 0; i < 2; i++) { 192 lfd = socket(AF_UNIX, SOCK_STREAM, 0); 193 if (lfd < 0) { 194 perror("socket"); 195 goto stop_serving; 196 } 197 if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, 198 &on, sizeof(on)) < 0) { 199 perror("setsockopt"); 200 goto stop_serving; 201 } 202 203 if (access(bindpaths[i], F_OK) == 0) { 204 if (remove(bindpaths[i]) < 0) { 205 perror("remove"); 206 goto stop_serving; 207 } 208 } 209 210 saddrs[i].sun_family = AF_UNIX; 211 strncpy(saddrs[i].sun_path, bindpaths[i], 212 sizeof(bindpaths[i])-1); 213 saddrlen = sizeof(saddrs[i]); 214 215 if (bind(lfd, (struct sockaddr *)&saddrs[i], saddrlen) < 0) { 216 perror("bind"); 217 goto stop_serving; 218 } 219 220 if (chmod(bindpaths[i], 0775) < 0) { 221 perror("chmod"); 222 goto stop_serving; 223 } 224 225 if (listen(lfd, 255) < 0) { 226 perror("listen"); 227 goto stop_serving; 228 } 229 230 lfds = llist_put(lfds, &lfd, sizeof(lfd)); 231 } 232 233 timeout.tv_sec = 1; 234 timeout.tv_nsec = 0; 235 for (;isrunning;) { 236 maxsfd = 0; 237 FD_ZERO(&fdset); 238 forllist(lfds, e) { 239 FD_SET(*(int *)e->data, &fdset); 240 if (*(int *)e->data > maxsfd) 241 maxsfd = *(int *)e->data; 242 } 243 forllist(rfds, e) { 244 FD_SET(*(int *)e->data, &fdset); 245 if (*(int *)e->data > maxsfd) 246 maxsfd = *(int *)e->data; 247 } 248 249 if (pselect(maxsfd+1, &fdset, NULL, NULL, &timeout, NULL) < 0) { 250 if (errno == EINTR) 251 continue; 252 perror("pselect"); 253 goto stop_serving; 254 } 255 256 i = -1; 257 forllist(lfds, e) { 258 i++; 259 if (FD_ISSET(*(int *)e->data, &fdset)) { 260 cltlen = sizeof(clt); 261 afd = accept(*(int *)e->data, 262 (struct sockaddr *)&clt, 263 &cltlen); 264 if (afd < 0 && errno != ECONNABORTED 265 && errno != EINTR) { 266 perror("accept"); 267 goto stop_serving; 268 } 269 270 if (i == 0) { 271 rfds = llist_put(rfds, &afd, 272 sizeof(afd)); 273 } else { 274 wfds = llist_put(wfds, &afd, 275 sizeof(afd)); 276 } 277 } 278 } 279 280 forllist(rfds, e) { 281 if (FD_ISSET(*(int *)e->data, &fdset)) { 282 recvl = read(*(int *)e->data, recvbuf, 283 sizeof(recvbuf)); 284 if (recvl <= 0) { 285 close(*(int *)e->data); 286 e = llist_del(&rfds, e); 287 if (e == NULL) 288 break; 289 } 290 291 forllist(wfds, qe) { 292 sendbufi = recvbuf; 293 sendl = recvl; 294 while (sendl > 0) { 295 if ((sent = write(*(int *)qe->data, sendbufi, sendl)) < 0) { 296 close(*(int *)qe->data); 297 qe = llist_del(&wfds, qe); 298 break; 299 } 300 sendl -= sent; 301 sendbufi += sent; 302 } 303 if (qe == NULL) 304 break; 305 } 306 } 307 } 308 309 } 310 311 rval = 0; 312 stop_serving: 313 closeallfds(lfds); 314 llist_free(lfds); 315 closeallfds(rfds); 316 llist_free(rfds); 317 closeallfds(wfds); 318 llist_free(wfds); 319 320 return rval; 321 } 322