1 /* 2 tcpcgi.c -- Client portion of rutil_tcpcgi. 3 Copyright (C) 2003 Roy Keene 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19 email: tcpcgi@rkeene.org 20 21 */ 22 23 /* 24 * tcpcgi.c -- Local listener part of reverse-utils::TCP-over-HTTP/CGI: 25 * This needs to do quite a bit of work in the background. 26 * 27 * It will create a listening socket, upon connection to that socket 28 * it will send the proper HTTP request to create the socket on the 29 * other end, and wait for an ackowledgement. 30 * 31 * Every second it will query the remote machine for all the sessions 32 * that exist and pass the data to the client socket. 33 * 34 * When data arrives, it needs to hex-encode it and put it on the socket. 35 * -- Roy Keene [300820031505] <tcpcgi@rkeene.org> 36 */ 37 38 #include <string.h> 39 #define _XOPEN_SOURCE_EXTENDED 40 #include <netinet/in.h> 41 #include <sys/socket.h> 42 #include <sys/types.h> 43 #include <arpa/inet.h> 44 #include <sys/time.h> 45 #include <sys/poll.h> 46 #include <sys/wait.h> 47 #include <unistd.h> 48 #include <signal.h> 49 #include <stdlib.h> 50 #include <stdio.h> 51 #include <ctype.h> 52 #include <fcntl.h> 53 #include <netdb.h> 54 #include <errno.h> 55 #include <time.h> 56 57 #include "tcpcgi.h" 58 #include "tcpnet.h" 59 60 extern char *optarg; 61 extern int optind, opterr, optopt; 62 63 64 struct sockinfo { 65 char *session; 66 char *http_host; 67 char *http_path; 68 int http_port; 69 signed int fd; 70 }; 71 72 int use_post=1; 73 int local_only=0; 74 int poll_time_min=TCPCGI_CLIENT_POLLTIME_MIN; 75 int poll_time_max=TCPCGI_CLIENT_POLLTIME_MAX; 76 char *userpass=NULL; 77 char *proxyhost=NULL; 78 struct sockinfo socks_info[128]; /* Needs to be num of elements as socks_poll in main() */ 79 80 81 char *mimecode(void *databuf, int datalen) { 82 unsigned char buffer[3]; 83 unsigned char *data=databuf; 84 char mimeabet[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 85 char *ret; 86 int outlen, retlen; 87 int i,x=0; 88 89 outlen=(int) ((((double) datalen)*1.3333333)+0.9); 90 retlen=((int) ((((double) datalen)/3.00)+0.9))*4; 91 ret=malloc(retlen+1); 92 93 for (i=0; i<datalen; i+=3) { 94 buffer[0]=data[i]; 95 buffer[1]=data[i+1]; 96 buffer[2]=data[i+2]; 97 ret[x++]=mimeabet[((buffer[0]&0xfc)>>2)%sizeof(mimeabet)]; 98 if (x>=outlen) break; 99 ret[x++]=mimeabet[(((buffer[0]&0x3)<<4)|((buffer[1]&0xf0)>>4))%sizeof(mimeabet)]; 100 if (x>=outlen) break; 101 ret[x++]=mimeabet[(((buffer[1]&0xf)<<2)|((buffer[2]&0xc0)>>6))%sizeof(mimeabet)]; 102 if (x>=outlen) break; 103 ret[x++]=mimeabet[(buffer[2]&0x3f)%sizeof(mimeabet)]; 104 if (x>=outlen) break; 105 } 106 while (x!=retlen) ret[x++]='='; 107 ret[x]='\0'; 108 return(ret); 109 } 110 111 char *hexcode(void *data, int datalen) { 112 char hexabet[]="0123456789abcdef"; 113 unsigned char *data_num=data; 114 char *ret; 115 int i, destpos=0; 116 117 if (data==NULL || datalen==0) return(strdup("")); 118 119 ret=malloc(datalen*3+1); 120 121 for (i=0; i<datalen; i++) { 122 if (data_num[i]==' ') { 123 ret[destpos++]='+'; 124 continue; 125 } 126 #if 1 127 /* if it's a nonprintable charectar, or [%?@&=+: ], convert it */ 128 if (!isgraph(data_num[i]) || data_num[i]==' ' || \ 129 data_num[i]=='&' || data_num[i]=='=' || \ 130 data_num[i]==':' || data_num[i]=='@' || \ 131 data_num[i]=='?' || data_num[i]=='%' || \ 132 data_num[i]=='+' || data_num[i]=='#') { 133 #else 134 /* If the charectar is not alphanumeric or and not [+./-], convert it to hex. */ 135 if (!isalnum(data_num[i]) && data_num[i]!='/' && data_num[i]!='.' && data_num[i]!='~' && data_num[i]!='+') { 136 #endif 137 ret[destpos++]='#'; 138 ret[destpos++]=hexabet[(data_num[i]>>4)&0xf]; 139 ret[destpos++]=hexabet[data_num[i]&0xf]; 140 continue; 141 } 142 /* Otherwise, we just copy it over. */ 143 ret[destpos++]=data_num[i]; 144 } 145 ret[destpos]='\0'; 146 147 #ifdef PARANOID 148 ret=realloc(ret, strlen(ret)+1); 149 #endif 150 return(ret); 151 } 152 153 void *tcpcgi_getdata(char *http_host, int http_port, char *http_path, char *http_userpass, char *cmd, char *session, char *dest, void *data, int *datalen, signed int *status, int proto_type) { 154 FILE *fp; 155 char *hex_cmd=NULL, *hex_session=NULL, *hex_data=NULL, *hex_path=NULL, *hex_host=NULL; 156 char *host=NULL, *port_str=NULL, *hostport; 157 char *http_auth=NULL; 158 char http_realhost[1024], *http_realport_tmp; 159 char buf[8192], *retbuf=NULL; 160 int fd; 161 int sink=0; 162 int datalen_val; 163 int http_authlen=0; 164 int http_realport=http_port; 165 unsigned int datalen_loc; 166 167 PRINTERR("Entering tcpcgi_getdata()"); 168 if (http_host==NULL || http_path==NULL || cmd==NULL || session==NULL) { 169 PRINTERR("Invalid parameters passed."); 170 if (status) *status=-1; 171 return(NULL); 172 } 173 174 datalen_val=(datalen)?(*datalen):(0); 175 /* Need to free the hex_* variables past this point. */ 176 hex_cmd=hexcode(cmd, strlen(cmd)); /* Guarenteed not NULL */ 177 hex_session=hexcode(session, strlen(session)); /* Guarenteed not NULL */ 178 hex_path=hexcode(http_path, strlen(http_path)); /* Guarenteed not NULL */ 179 hex_data=hexcode(data, datalen_val); 180 if (dest) { 181 hostport=strdup(dest); 182 /* Parse the host:port value into two seperate variables */ 183 port_str=strchr(hostport, ':'); 184 if (!port_str) { 185 PRINTERR("Unable to parse destination."); 186 if (status) *status=-1; 187 if (hex_cmd) free(hex_cmd); 188 if (hex_session) free(hex_session); 189 if (hex_data) free(hex_data); 190 if (hex_path) free(hex_path); 191 return(NULL); 192 } 193 *port_str=0; 194 port_str++; 195 host=hostport; 196 hex_host=hexcode(host, strlen(host)); 197 } 198 199 if (proxyhost) { 200 /* This will cause a slow, steady memory leak ... */ 201 strcpy(http_realhost, proxyhost); 202 http_realport_tmp=strchr(http_realhost, ':'); 203 if (http_realport_tmp) { 204 *http_realport_tmp='\0'; 205 http_realport=atoi(http_realport_tmp+1); 206 } else { 207 http_realport=http_port; 208 } 209 } else { 210 strcpy(http_realhost, http_host); 211 http_realport=http_port; 212 } 213 PRINTERR("Creating connection to \"%s\" on port %i", http_realhost, http_realport); 214 215 fd=createconnection_tcp(http_realhost, http_realport); 216 217 if (fd<0) { 218 PERROR("connect"); 219 PRINTERR("Couldn't connect!"); 220 if (status) *status=-1; 221 if (hex_cmd) free(hex_cmd); 222 if (hex_session) free(hex_session); 223 if (hex_data) free(hex_data); 224 if (hex_host) free(hex_host); 225 if (hex_path) free(hex_path); 226 return(NULL); 227 } 228 fp=fdopen(fd, "r+"); 229 if (!fp) { 230 PERROR("fdopen"); 231 if (status) *status=-1; 232 if (http_auth[0]!='\0') free(http_auth); 233 if (hex_cmd) free(hex_cmd); 234 if (hex_session) free(hex_session); 235 if (hex_data) free(hex_data); 236 if (hex_host) free(hex_host); 237 if (hex_path) free(hex_path); 238 return(NULL); 239 } 240 241 if (http_userpass) { 242 http_authlen=128+strlen(http_userpass); 243 http_auth=malloc(http_authlen); 244 snprintf(http_auth, http_authlen, "Authorization: Basic %s\r\n", http_userpass); 245 } else { 246 http_auth=""; 247 } 248 249 snprintf(buf, sizeof(buf), "action=%s&id=%s&host=%s&port=%s&data=%s&datalen=%i&ts=%i&proto=%s", hex_cmd, hex_session, hex_host, port_str, hex_data, datalen_val, (unsigned int) time(NULL), (proto_type==SOCK_DGRAM)?"udp":"tcp"); 250 251 252 buf[sizeof(buf)-1]='\0'; 253 if (use_post) { 254 fprintf(fp, "POST %s HTTP/1.0\r\nHost: %s:%i\r\n%sContent-length: %i\r\n\r\n%s\r\n", hex_path, http_host, http_port, http_auth, (int) strlen(buf), buf); 255 PRINTERR("POST http://%s:%i%s?[%i]%s HTTP/1.0\n", http_host, http_port, hex_path, (int) strlen(buf), buf); 256 } else { 257 fprintf(fp, "GET %s:%i?%s HTTP/1.0\r\nHost: %s:%i\r\n%s\r\n", hex_path, http_port, buf, http_host, http_port, http_auth); 258 PRINTERR("GET http://%s:%i%s?[%i]%s HTTP/1.0\n", http_host, http_port, hex_path, (int) strlen(buf), buf); 259 } 260 fflush(fp); 261 262 if (status) *status=0; 263 if (datalen) *datalen=0; 264 PRINTERR("Scheduling panic alarm."); 265 alarm(120); /* In case of emergency, please panic. */ 266 if (fgets(buf, sizeof(buf), fp)==NULL) { 267 if (http_auth[0]!='\0') free(http_auth); 268 if (hex_cmd) free(hex_cmd); 269 if (hex_session) free(hex_session); 270 if (hex_data) free(hex_data); 271 if (hex_host) free(hex_host); 272 if (hex_path) free(hex_path); 273 fclose(fp); 274 return(NULL); 275 } 276 PRINTERR("Unscheduling panic alarm."); 277 alarm(0); /* Panic no more. */ 278 if (strstr(buf," 40")) { 279 PRINTERR("File not available error, %s", buf); 280 if (http_auth[0]!='\0') free(http_auth); 281 if (hex_cmd) free(hex_cmd); 282 if (hex_session) free(hex_session); 283 if (hex_data) free(hex_data); 284 if (hex_host) free(hex_host); 285 if (hex_path) free(hex_path); 286 fclose(fp); 287 return(NULL); 288 } 289 if (!strstr(buf, " 200 ")) { 290 PRINTERR("Invalid response, %s", buf); 291 if (http_auth[0]!='\0') free(http_auth); 292 if (hex_cmd) free(hex_cmd); 293 if (hex_session) free(hex_session); 294 if (hex_data) free(hex_data); 295 if (hex_host) free(hex_host); 296 if (hex_path) free(hex_path); 297 sleep(1); /* Primitive backoff */ 298 fclose(fp); 299 return(tcpcgi_getdata(http_host, http_port, http_path, http_userpass, cmd, session, dest, data, datalen, status, SOCK_STREAM)); 300 } 301 PRINTERR("Scheduling panic alarm."); 302 alarm(240); /* In case of emergency, please panic. */ 303 while (1) { 304 if (fgets(buf, sizeof(buf), fp)==NULL) break; 305 while (buf[strlen(buf)-1]<' ' && buf[0]!='\0') buf[strlen(buf)-1]='\0'; 306 /* On the first blank line, assume the HTTP headers have ended. */ 307 if (buf[0]=='\0' && !sink) { 308 sink=1; /* Only come here once. */ 309 if (fgets(buf, sizeof(buf), fp)==NULL) break; 310 PRINTERR("statusmsg=%s", buf); 311 if (strstr(buf, "stat: FAIL -")!=NULL) { 312 PRINTERR("Setting status to -ERR"); 313 if (status) *status=-1; 314 } 315 datalen_loc=(fgetc(fp))<<8; 316 datalen_loc|=fgetc(fp); 317 if (datalen_loc>16384) continue; 318 if (datalen_loc) { 319 PRINTERR("Now expecting %i bytes of data.", datalen_loc); 320 retbuf=malloc(datalen_loc); 321 datalen_loc=fread(retbuf, 1, datalen_loc, fp); 322 PRINTERR("[%i](omitted)", datalen_loc); 323 } 324 if (datalen) *datalen=datalen_loc; 325 PRINTERR("We got all that we want, let's get outta here."); 326 break; 327 } 328 PRINTERR("buf=%s", buf); 329 } 330 PRINTERR("Unscheduling panic alarm."); 331 alarm(0); 332 333 fclose(fp); 334 if (http_auth[0]!='\0') free(http_auth); 335 if (hex_cmd) free(hex_cmd); 336 if (hex_session) free(hex_session); 337 if (hex_data) free(hex_data); 338 if (hex_host) free(hex_host); 339 if (hex_path) free(hex_path); 340 return(retbuf); 341 } 342 343 char *tcpcgi_gensess(void) { 344 static char ret[12]={0,0,0,0,0,0,0,0,0,0,0,0}; 345 signed int i=0; 346 char alphabet[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 347 int j; 348 349 srand(time(NULL)+ret[0]+ret[1]); 350 for (i=0; i<sizeof(ret); i++) { 351 j=rand()%(sizeof(alphabet)-1); 352 if (alphabet[j]=='\0' && i==0) { i--; continue; } /* Don't generate an empty session. */ 353 ret[i]=alphabet[j]; 354 } 355 ret[sizeof(ret)-1]='\0'; 356 return(ret); 357 } 358 359 void tcpcgi_closesession(int fd, char *http_host, int http_port, char *http_path, char *session) { 360 if (!session || fd<0) return; 361 PRINTERR("Terminating session \"%s\".", session); 362 363 tcpcgi_getdata(http_host, http_port, http_path, userpass, "close", session, NULL, NULL, NULL, NULL, 0); 364 return; 365 } 366 367 /* 368 * Return values here: 369 * -1 (error) 370 * 0 (no error, but no data was recvd) 371 * 1 (no error, data recv) 372 */ 373 int tcpcgi_handledata(char *http_host, int http_port, char *http_path, char *session, int proto_type, char *dest, int fd, void *buf, int datalen) { 374 unsigned char *ret; 375 unsigned char *lbuf=buf; 376 char *cmd; 377 int status=0; 378 int dataretlen; 379 int rv; 380 381 while (datalen>1024) { 382 if ((rv=tcpcgi_handledata(http_host, http_port, http_path, session, proto_type, dest, fd, lbuf, 1024))<0) { 383 return(rv); 384 } 385 datalen-=1024; 386 lbuf+=1024; 387 } 388 389 if (lbuf && datalen!=0) { 390 cmd="write"; 391 dataretlen=datalen; 392 } else { 393 cmd="read"; 394 dataretlen=0; 395 } 396 397 ret=tcpcgi_getdata(http_host, http_port, http_path, userpass, cmd, session, dest, lbuf, &dataretlen, &status, proto_type); 398 PRINTERR("[%i](omitted, again)", dataretlen); 399 if (dataretlen>0 && ret && status>=0) { 400 write(fd, ret, dataretlen); 401 free(ret); 402 status=1; 403 } 404 405 PRINTERR("Returning %i", status); 406 return(status); 407 } 408 409 410 411 int handle_background(void) { 412 int i; 413 char *http_host, *http_path, *session; 414 int fd, http_port; 415 int ret=0; 416 int rv; 417 418 for (i=0; i<(sizeof(socks_info)/sizeof(struct sockinfo)); i++) { 419 if (socks_info[i].fd<0) continue; 420 http_host=socks_info[i].http_host; 421 http_port=socks_info[i].http_port; 422 http_path=socks_info[i].http_path; 423 session=socks_info[i].session; 424 fd=socks_info[i].fd; 425 if ((rv=tcpcgi_handledata(http_host, http_port, http_path, session, SOCK_STREAM, NULL, fd, NULL, 0))<0) { 426 PRINTERR("[fd=%i] We got an error in the background and we don't know how to handle it!", fd); 427 close(fd); 428 continue; 429 } 430 if (rv>0) ret++; 431 } 432 return(ret); 433 } 434 435 void sighandler(int sig) { 436 if (sig==SIGALRM) { 437 PRINTERR("Recieved SIGALRM!"); 438 return; 439 } 440 PRINTERR("Recieved weird signal %i", sig); 441 } 442 443 int print_help(char *msg) { 444 fprintf(stderr, "Usage: tcpcgi -H host -f file -d dest [-P port] [-p port] [-m time]\n"); 445 fprintf(stderr, " [-x time] [-t time] [-b amt] [-V proxy] [-U user] [-rigluvh]\n"); 446 fprintf(stderr, " Options:\n"); 447 fprintf(stderr, " -H host HTTP Server which has tcpcgi.cgi.\n"); 448 fprintf(stderr, " -f file Full URL-relative on HTTP server to tcpcgi.cgi,\n"); 449 fprintf(stderr, " including tcpcgi.cgi.\n"); 450 fprintf(stderr, " -d dest Destination to for HTTP server to connect to and\n"); 451 fprintf(stderr, " proxy to (must be in the form of host:port).\n"); 452 fprintf(stderr, " -P port Port to connect to HTTP server on.\n"); 453 fprintf(stderr, " -p port Port to listen for connections on locally (%i is\n", TCPCGI_CLIENT_PORT); 454 fprintf(stderr, " the default).\n"); 455 fprintf(stderr, " -m time Lowest amount of time (in milliseconds) to check for\n"); 456 fprintf(stderr, " data from HTTP server (%i is the default).\n", TCPCGI_CLIENT_POLLTIME_MIN); 457 fprintf(stderr, " -x time Greatest amount of time (in milliseconds) to check\n"); 458 fprintf(stderr, " for data from HTTP server (%i is the default).\n", TCPCGI_CLIENT_POLLTIME_MAX); 459 fprintf(stderr, " -t time Specify that the amount of time for polling be fixed\n"); 460 fprintf(stderr, " at this value (in ms).\n"); 461 fprintf(stderr, " -b amt Backoff amount, if less than 5, specifies backoff\n"); 462 fprintf(stderr, " should multiply the backoff time by this number,\n"); 463 fprintf(stderr, " otherwise it's incremented by this amount (2 is the\n"); 464 fprintf(stderr, " default).\n"); 465 fprintf(stderr, " -V proxy Specify a host to send all HTTP traffic through.\n"); 466 fprintf(stderr, " -U user Username and password to use for http authentication\n"); 467 fprintf(stderr, " (must be in the form of user:pass).\n"); 468 fprintf(stderr, " -r Specify that the backoff algorithm reset when remote\n"); 469 fprintf(stderr, " activity occurs, instead of decrementing.\n"); 470 fprintf(stderr, " -i Specify that the backoff algorithm should respond to\n"); 471 fprintf(stderr, " local traffic as well as remote.\n"); 472 fprintf(stderr, " -g Toggle between HTTP get and HTTP post (%s is\n", use_post?"POST":"GET"); 473 fprintf(stderr, " the default).\n"); 474 fprintf(stderr, " -l Bind to %s only for listening (%s is the\n", local_only?"ALL":"LOCALHOST", local_only?"LOCAL":"ALL"); 475 fprintf(stderr, " default).\n"); 476 fprintf(stderr, " -u Use UDP instead of TCP.\n"); 477 fprintf(stderr, " -v Print version (%s) and exit.\n", TCPCGI_VERSION); 478 fprintf(stderr, " -h This help information.\n"); 479 if (msg) { 480 fprintf(stderr, "\ntcpcgi: %s\n", msg); 481 } 482 return(1); 483 } 484 485 #ifdef CLOSE_POLL 486 #undef CLOSE_POLL 487 #endif 488 #define CLOSE_POLL(idx) { \ 489 if (idx!=0) { \ 490 tcpcgi_closesession(socks_poll[idx].fd, http_host, http_port, http_path, socks_info[idx].session); \ 491 close(socks_poll[idx].fd); \ 492 socks_poll[idx].fd=-1; \ 493 socks_info[idx].fd=-1; \ 494 pollcnt--; \ 495 pollpos=idx; \ 496 } \ 497 } 498 int main(int argc, char **argv) { 499 struct pollfd socks_poll[sizeof(socks_info)/sizeof(struct sockinfo)]; 500 struct sockaddr_in *addr; 501 struct sigaction sac; 502 signed int pollpos=0; 503 signed int status=0; 504 signed int ch; 505 char buf[8192]; 506 char *http_host=NULL, *http_path=NULL; 507 char *hostport=NULL; 508 char udp_sess_wrk[128], *udp_sess, *socks_sess; 509 socklen_t addrlen; 510 sigset_t s; 511 int masterfd, fd, clientfd; 512 int pollcnt=0, pollmax; 513 int num_events; 514 int errcnt=0; 515 int i,x, rv; 516 int listen_port=TCPCGI_CLIENT_PORT; 517 int poll_time=200; /* Start out with a reasonably fast poll_time. */ 518 int poll_backoff=2; 519 int poll_backoff_reset=0, poll_backoff_ireset=0; 520 int http_port=80; 521 int proto_type=SOCK_STREAM; 522 #ifndef DEBUG 523 pid_t chpid; 524 #endif 525 526 while ((ch=getopt(argc, argv, "H:f:P:p:d:m:x:t:b:V:U:vgriulh"))!=-1) { 527 switch (ch) { 528 case 'H': 529 http_host=strdup(optarg); 530 break; 531 case 'f': 532 if (optarg[0]!='/') return(print_help("File should begin with a `/\'")); 533 http_path=strdup(optarg); 534 break; 535 case 'P': 536 http_port=atoi(optarg); 537 if (http_port<=0) return(print_help("Port cannot be <= 0.")); 538 break; 539 case 'p': 540 listen_port=atoi(optarg); 541 if (listen_port<=0) return(print_help("Port cannot be <= 0.")); 542 break; 543 case 'd': 544 hostport=strdup(optarg); 545 if (strchr(hostport,':')==NULL) return(print_help("Destination must be in the form of host:port")); 546 break; 547 case 'm': 548 poll_time_min=atoi(optarg); 549 if (poll_time_min<=0) return(print_help("Polltime min cannot be <= 0.")); 550 if (poll_time<poll_time_min) poll_time=poll_time_min; 551 break; 552 case 'x': 553 poll_time_max=atoi(optarg); 554 if (poll_time_max<=0) return(print_help("Polltime max cannot be <= 0.")); 555 if (poll_time>poll_time_max) poll_time=poll_time_max; 556 break; 557 case 't': 558 poll_time=poll_time_max=poll_time_min=atoi(optarg); 559 if (poll_time_max<=0) return(print_help("Polltime cannot be <= 0.")); 560 break; 561 case 'b': 562 poll_backoff=atoi(optarg); 563 if (poll_backoff<=0) return(print_help("Poll backoff cannot be <= 0.")); 564 break; 565 case 'r': 566 poll_backoff_reset=!poll_backoff_reset; 567 break; 568 case 'i': 569 poll_backoff_ireset=!poll_backoff_ireset; 570 break; 571 case 'g': 572 use_post=!use_post; 573 break; 574 case 'v': 575 printf("Reverse utilities::TCP-over-HTTP/CGI v%s\n", TCPCGI_VERSION); 576 return(0); 577 break; 578 case 'l': 579 local_only=!local_only; 580 break; 581 case 'u': 582 proto_type=(proto_type==SOCK_STREAM)?SOCK_DGRAM:SOCK_STREAM; 583 break; 584 case 'U': 585 userpass=mimecode(optarg, strlen(optarg)); 586 break; 587 case 'V': 588 proxyhost=strdup(optarg); 589 break; 590 case ':': 591 case '?': 592 case 'h': 593 return(print_help(NULL)); 594 break; 595 } 596 } 597 598 /* Basic sanity checks for supplied args */ 599 if (http_host==NULL || http_path==NULL) { 600 return(print_help("You must specify the HOST (-H) and FILE (-f) options.")); 601 } 602 if (http_path[0]!='/') { 603 return(print_help("File should begin with a `/\'")); 604 } 605 if (hostport==NULL) { 606 return(print_help("You must specify the DEST (-d) option.")); 607 } 608 if (poll_time_max<poll_time_min) { 609 return(print_help("Polltime max cannot be less than polltime min.")); 610 } 611 612 sigemptyset(&s); 613 sigaddset(&s, SIGALRM); 614 sac.sa_handler=sighandler; 615 sac.sa_mask=s; 616 sac.sa_flags=0; 617 sigaction(SIGALRM, &sac, NULL); 618 619 signal(SIGPIPE, SIG_IGN); 620 masterfd=createlisten(listen_port, local_only, proto_type); 621 if (masterfd<0) { 622 PERROR("createlisten()"); 623 return(1); 624 } 625 626 #ifndef DEBUG 627 /* We're all out of complaints at this point, and should be ready to go in the background. */ 628 if ((chpid=fork())<0) { PERROR("fork"); return(1); } 629 if (chpid!=0) { 630 /* Parent */ 631 wait(NULL); 632 return(0); 633 } 634 /* Child */ 635 if ((chpid=fork())<0) { return(1); } 636 if (chpid!=0) return(0); 637 /* Grand-child */ 638 setsid(); 639 #endif 640 641 for (i=0; i<(sizeof(socks_poll)/sizeof(struct pollfd)); i++) socks_poll[i].fd=-1; 642 for (i=0; i<(sizeof(socks_info)/sizeof(struct sockinfo)); i++) { 643 socks_info[i].session=NULL; 644 socks_info[i].fd=-1; 645 } 646 socks_poll[pollpos].fd=masterfd; 647 socks_poll[pollpos].events=POLLIN; 648 socks_poll[pollpos].revents=0; 649 pollpos++; 650 pollcnt++; 651 652 addrlen=sizeof(struct sockaddr_in); 653 addr=malloc(addrlen); 654 655 while (1) { 656 for (i=0; i<(sizeof(socks_poll)/sizeof(struct pollfd)); i++) { 657 if (socks_poll[i].fd!=-1) pollmax=(i+1); 658 } 659 660 num_events=poll(socks_poll, pollmax, (pollcnt>1)?(poll_time):(-1)); 661 if (num_events<0) { 662 if (errcnt==TCPCGI_DAEMON_MAXERR) { 663 fprintf(stderr, "Maximum error count (%i) reached, aborting...\n", TCPCGI_DAEMON_MAXERR); 664 break; 665 } 666 errcnt++; 667 PERROR("poll()"); 668 continue; 669 } 670 errcnt=0; 671 if (num_events==0) { 672 /* If we just have the listening socket in our poll group, don't adjust polltime */ 673 if (pollcnt==1) continue; 674 675 /* Check background activity, and adjust polltime if needed. */ 676 if (handle_background()==0) { 677 /* If nothing is happening in the background, slow down. */ 678 if (poll_backoff<5) { poll_time*=poll_backoff; } else { poll_time+=poll_backoff; } 679 PRINTERR("Lengthening polltime [poll_time=%i], nothing is happening.", poll_time); 680 } else { 681 /* If we're active in the background, speed up! */ 682 if (poll_backoff<5) { poll_time/=poll_backoff; } else { poll_time-=poll_backoff; } 683 if (poll_backoff_reset) poll_time=poll_time_min; 684 PRINTERR("Shortening polltime [poll_time=%i], stuff is happening.", poll_time); 685 } 686 if (poll_time>poll_time_max) poll_time=poll_time_max; 687 if (poll_time<poll_time_min) poll_time=poll_time_min; 688 continue; 689 } 690 for (i=0; i<pollmax; i++) { 691 if (socks_poll[i].revents&(POLLERR|POLLHUP|POLLNVAL)) { 692 /* Socket is broke, close it. */ 693 CLOSE_POLL(i); 694 PRINTERR("Closing socket [idx=%i, fd=%i], due to errors.", i, fd); 695 num_events--; 696 if (num_events==0) break; 697 continue; 698 } 699 if ((socks_poll[i].revents&POLLIN)!=POLLIN) continue; 700 num_events--; 701 fd=socks_poll[i].fd; 702 socks_poll[i].events=POLLIN; 703 socks_poll[i].revents=0; 704 /* 705 This is special for UDP, since 706 it will never have a connection 707 we have just guess at this by 708 the existance (or lack of...) 709 a session with a generated name 710 */ 711 if (proto_type==SOCK_DGRAM) { 712 PRINTERR("Incoming UDP ..."); 713 if (fd!=masterfd) { 714 PRINTERR("... not from master?! IGNORING!"); 715 continue; 716 } 717 addrlen=sizeof(struct sockaddr_in); 718 recvfrom(fd, buf, 5, MSG_PEEK, (struct sockaddr *)addr, &addrlen); 719 memcpy(udp_sess_wrk, &addr->sin_port, 2); 720 memcpy(udp_sess_wrk+2, &addr->sin_addr.s_addr, sizeof(addr->sin_addr.s_addr)); 721 udp_sess=hexcode(udp_sess_wrk, 2+sizeof(addr->sin_addr.s_addr)); 722 PRINTERR("Session name is .. %s", udp_sess); 723 } 724 if (fd==masterfd && proto_type!=SOCK_DGRAM) { 725 /* Handle incoming connection. */ 726 PRINTERR("New connection..."); 727 addrlen=sizeof(struct sockaddr_in); 728 clientfd=accept(fd, (struct sockaddr *)addr, &addrlen); 729 if (clientfd<0) { 730 PERROR("accept"); 731 continue; 732 } 733 if (pollpos==-1) { 734 for (x=0; x<pollmax; x++) { 735 if (socks_poll[x].fd==-1) { 736 pollpos=x; 737 break; 738 } 739 } 740 } 741 if (pollpos==-1) pollpos=pollmax; 742 if (pollpos>=(sizeof(socks_poll)/sizeof(struct pollfd))) { 743 PRINTERR("Ran out of available socks_poll[] entries."); 744 close(fd); 745 continue; 746 } 747 socks_poll[pollpos].fd=clientfd; 748 socks_poll[pollpos].events=POLLIN|POLLERR|POLLHUP|POLLNVAL; 749 socks_poll[pollpos].revents=0; 750 pollcnt++; 751 752 /* Create session */ 753 socks_info[pollpos].session=strdup(tcpcgi_gensess()); 754 socks_info[pollpos].http_port=http_port; 755 socks_info[pollpos].http_host=http_host; 756 socks_info[pollpos].http_path=http_path; 757 tcpcgi_getdata(http_host, http_port, http_path, userpass, "open", socks_info[pollpos].session, hostport, NULL, NULL, &status, proto_type); 758 if (status<0) { 759 PRINTERR("Error creating session on remote end."); 760 CLOSE_POLL(pollpos); 761 continue; 762 } 763 764 /* This needs to be the last step, it actually registers the session for updates. */ 765 socks_info[pollpos].fd=clientfd; 766 767 768 PRINTERR("... from [idx=%i, fd=%i], pollcnt=%i, pollmax=%i: session=%s", pollpos, clientfd, pollcnt, pollmax, socks_info[pollpos].session); 769 pollpos=-1; 770 if (num_events==0) break; 771 continue; 772 } 773 /* Handle data from an existing connection. */ 774 PRINTERR("Data waiting on [idx=%i, fd=%i]...", i, fd); 775 x=read(fd, buf, sizeof(buf)); 776 PRINTERR(".. not anymore, we read %i bytes.", x); 777 if (x<0) PERROR("read"); 778 if (x==0) { 779 PRINTERR("EOF from [idx=%i, fd=%i].", i, fd); 780 CLOSE_POLL(i); 781 continue; 782 } 783 if (proto_type==SOCK_DGRAM) { 784 socks_sess=udp_sess; 785 } else { 786 socks_sess=socks_info[i].session; 787 } 788 789 /* Do the actual transmission */ 790 if ((rv=tcpcgi_handledata(http_host, http_port, http_path, socks_sess, proto_type, hostport, fd, buf, x))<0) { 791 PRINTERR("Unable to put data."); 792 CLOSE_POLL(i); 793 continue; 794 } 795 if (poll_backoff_ireset) { 796 /* Check background activity, and adjust polltime if needed. */ 797 if (handle_background()==0) { 798 /* If nothing is happening in the background, slow down. */ 799 if (poll_backoff<5) { poll_time*=poll_backoff; } else { poll_time+=poll_backoff; } 800 PRINTERR("Lengthening polltime [poll_time=%i], nothing is happening.", poll_time); 801 } else { 802 /* If we're active in the background, speed up! */ 803 if (poll_backoff<5) { poll_time/=poll_backoff; } else { poll_time-=poll_backoff; } 804 if (poll_backoff_reset) poll_time=poll_time_min; 805 PRINTERR("Shortening polltime [poll_time=%i], stuff is happening.", poll_time); 806 } 807 if (poll_time>poll_time_max) poll_time=poll_time_max; 808 if (poll_time<poll_time_min) poll_time=poll_time_min; 809 } 810 if (num_events==0) break; 811 } 812 } 813 return(errcnt); 814 } |