1 /* 2 tcpcgid.c -- Server/daemon 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 #include <string.h> 23 #define _XOPEN_SOURCE_EXTENDED 24 #include <sys/socket.h> 25 #include <sys/types.h> 26 #include <sys/time.h> 27 #include <sys/poll.h> 28 #include <sys/wait.h> 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <fcntl.h> 34 35 #include "tcpcgid.h" 36 #include "tcpcgi.h" 37 #include "tcpnet.h" 38 #define CACHE_DEBUG 39 #include "cache.h" 40 41 #define CLOSE_POLL(idx) { \ 42 close(socks_poll[idx].fd); \ 43 tcpcgi_handledata(socks_poll[idx].fd, NULL, 0); \ 44 socks_poll[idx].fd=-1; \ 45 pollcnt--; \ 46 pollpos=idx; \ 47 } 48 49 50 static cache_t *sess=NULL; 51 52 struct conninfo { 53 cache_t *parms; 54 int state; 55 }; 56 57 struct sessioninfo { 58 char password[32]; 59 int sockfd; 60 }; 61 62 int tcpcgi_errmsg(int fd, char *msg) { 63 PRINTERR("[fd=%i]: FAIL: %s", fd, msg); 64 write(fd, "FAIL - ", strlen("FAIL - ")); 65 write(fd, msg, strlen(msg)); 66 write(fd, "\n", 1); 67 return(-1); 68 } 69 70 int tcpcgi_write(int fd, char *msg) { 71 PRINTERR("[fd=%i]: SUCCESS: %s", fd, msg); 72 write(fd, "SUCCESS: ", strlen("SUCCESS: ")); 73 write(fd, msg, strlen(msg)); 74 write(fd, "\n", 1); 75 return(0); 76 } 77 78 int tcpcgi_write_data(int fd, void *data, int datalen) { 79 unsigned char datalen_chr[2]; 80 if (data==NULL) { 81 write(fd, "\0\0", 2); 82 return(0); 83 } 84 datalen_chr[0]=(datalen>>8)&0xff; 85 datalen_chr[1]=(datalen)&0xff; 86 write(fd, datalen_chr, 1); 87 write(fd, datalen_chr+1, 1); 88 write(fd, data, datalen); 89 return(0); 90 } 91 92 void tcpcgi_free_cacheparms(void *data, void *key) { 93 free(data); 94 free(key); 95 } 96 97 void tcpcgi_free_conninfo(void *data, void *key) { 98 struct conninfo *ci=data; 99 100 if (ci) cache_destroy(ci->parms); 101 free(ci); 102 } 103 104 void tcpcgi_free_session(void *data, void *key) { 105 struct sessioninfo *si=data; 106 107 if (si) { 108 if (si->sockfd>=0) close(si->sockfd); 109 } 110 free(key); 111 free(si); 112 } 113 114 115 /* Create a session and connect to a host. */ 116 int tcpcgi_cmd_open(int fd, cache_t *parms) { 117 struct sessioninfo *si; 118 char *host, *port_str; 119 char *session; 120 int port; 121 int sockfd; 122 123 PRINTERR("Starting tcpcgi_cmd_open"); 124 host=cache_find(parms, "host", strlen("host"), NULL); 125 port_str=cache_find(parms, "port", strlen("port"), NULL); 126 session=cache_find(parms, "id", strlen("id"), NULL); 127 if (!host) return(tcpcgi_errmsg(fd, "Need parameter: host")); 128 if (!port_str) return(tcpcgi_errmsg(fd, "Need parameter: port")); 129 if (!session) return(tcpcgi_errmsg(fd, "Need parameter: id")); 130 si=cache_find(sess, session, strlen(session), NULL); 131 if (si) return(tcpcgi_errmsg(fd, "Improper parameter: id; Session exists")); 132 133 port=atoi(port_str); 134 135 /* Connect to host:port */ 136 PRINTERR("[%s] Connecting to \"%s\", port %i", session, host, port); 137 sockfd=createconnection_tcp(host, port); 138 if (sockfd<0) return(tcpcgi_errmsg(fd, "Unable to connect")); 139 140 /* Let 'em know we did something. */ 141 tcpcgi_write(fd, "Connected."); 142 143 /* Create the session info */ 144 si=malloc(sizeof(struct sessioninfo)); 145 si->sockfd=sockfd; 146 si->password[0]='\0'; 147 148 /* Register the session */ 149 cache_add(sess, strdup(session), strlen(session), si, sizeof(struct sessioninfo), 0, tcpcgi_free_session); 150 151 /* No data to report, so write an empty one. */ 152 tcpcgi_write_data(fd, NULL, 0); 153 154 return(0); 155 } 156 157 /* Destroy a session, and close connection to destination. */ 158 int tcpcgi_cmd_close(int fd, cache_t *parms) { 159 struct sessioninfo *si; 160 char *session; 161 162 PRINTERR("Starting tcpcgi_cmd_close"); 163 session=cache_find(parms, "id", strlen("id"), NULL); 164 if (!session) return(tcpcgi_errmsg(fd, "Need parameter: id")); 165 166 si=cache_find(sess, session, strlen(session), NULL); 167 if (!si) return(tcpcgi_errmsg(fd, "Improper parameter: id; Session does not exists")); 168 169 /* Delete the session (note: this also closes the socket). */ 170 cache_delete(sess, session, strlen(session), NULL, 1); 171 tcpcgi_write(fd, "Session closed."); 172 173 /* No data to report, so write an empty one. */ 174 tcpcgi_write_data(fd, NULL, 0); 175 176 return(0); 177 } 178 179 int tcpcgi_cmd_get(int fd, cache_t *parms) { 180 struct sessioninfo *si; 181 char *session; 182 char buf[8192]; 183 int buflen; 184 int sockfd; 185 int sockflags, oldsockflags; 186 187 PRINTERR("Starting tcpcgi_cmd_get"); 188 session=cache_find(parms, "id", strlen("id"), NULL); 189 if (!session) return(tcpcgi_errmsg(fd, "Need parameter: id")); 190 191 si=cache_find(sess, session, strlen(session), NULL); 192 if (!si) return(tcpcgi_errmsg(fd, "Improper parameter: id; Session does not exists")); 193 sockfd=si->sockfd; 194 195 /* Put the socket into non-blocking mode. */ 196 oldsockflags=fcntl(sockfd, F_GETFL); 197 sockflags=oldsockflags|O_NONBLOCK; 198 fcntl(sockfd, F_SETFL, sockflags); 199 200 /* Read the data*/ 201 buflen=read(sockfd, buf, sizeof(buf)); 202 if (buflen<0) { 203 /* No data available, this may not be an error. */ 204 tcpcgi_write(fd, "No data"); 205 tcpcgi_write_data(fd, NULL, 0); 206 return(0); 207 } 208 209 if (buflen==0) { 210 /* We lost the peer, destroy the session. */ 211 cache_delete(sess, session, strlen(session), NULL, 1); 212 tcpcgi_write(fd, "EOF reached?"); 213 tcpcgi_write_data(fd, NULL, 0); 214 return(0); 215 } 216 217 /* Restore the sock flags */ 218 fcntl(sockfd, F_SETFL, oldsockflags); 219 220 /* Let the user know we're sending good data. */ 221 tcpcgi_write(fd, "Data follows"); 222 223 /* No data to report, so write an empty one. */ 224 tcpcgi_write_data(fd, buf, buflen); 225 226 return(0); 227 } 228 229 int tcpcgi_cmd_put(int fd, cache_t *parms) { 230 struct sessioninfo *si; 231 char *session, *data, *proto, *dest, *dest_addr; 232 int dest_port; 233 int proto_type; 234 int datalen=0; 235 int x; 236 237 PRINTERR("Starting tcpcgi_cmd_put"); 238 proto=cache_find(parms, "proto", strlen("proto"), NULL); 239 session=cache_find(parms, "id", strlen("id"), NULL); 240 data=cache_find(parms, "data", strlen("data"), &datalen); 241 if (!session) return(tcpcgi_errmsg(fd, "Need parameter: id")); 242 if (!data) return(tcpcgi_errmsg(fd, "Need parameter: data")); 243 if (!proto) proto="tcp"; 244 proto_type=SOCK_STREAM; 245 if (strcmp(proto,"udp")==0) proto_type=SOCK_DGRAM; 246 247 if (proto_type!=SOCK_DGRAM) { 248 si=cache_find(sess, session, strlen(session), NULL); 249 if (!si) return(tcpcgi_errmsg(fd, "Improper parameter: id; Session does not exists")); 250 while ((x=write(si->sockfd, data, datalen))>0) { 251 datalen-=x; 252 if (datalen==0) break; 253 } 254 } else { 255 dest=cache_find(parms, "dest", strlen("dest"), NULL); 256 if (!dest) return(tcpcgi_errmsg(fd, "Need parameter: dest")); 257 if (strchr(dest,':')==NULL) return(tcpcgi_errmsg(fd, "Improper parameter: dest")); 258 dest_addr=strdup(dest); 259 dest_port=atoi(strchr(dest,':')+1); 260 *(strchr(dest_addr,':'))='\0'; 261 PRINTERR("dest=%s, dest_port=%i, dest_addr=%s", dest, dest_port, dest_addr); 262 /* XXX: WORK ON UDP SUPPORT WAS TEMPORARILY SUSPENDED HERE ON 4Sep03 */ 263 } 264 265 266 if (x<0) { 267 /* We destroy the session at this point ... */ 268 cache_delete(sess, session, strlen(session), NULL, 1); 269 PERROR("write"); 270 return(tcpcgi_errmsg(fd, "Could not write to socket.")); 271 } 272 273 /* If we made it this far, also check for data. */ 274 return(tcpcgi_cmd_get(fd, parms)); 275 } 276 277 278 void *tcpcgi_cmd_list[][2]={ {"open", tcpcgi_cmd_open}, 279 {"close", tcpcgi_cmd_close}, 280 {"write", tcpcgi_cmd_put}, 281 {"read", tcpcgi_cmd_get}, 282 }; 283 284 int tcpcgi_handledata(int fd, unsigned char *data, unsigned int datalen) { 285 static cache_t *c=NULL; 286 cache_t *parms; 287 struct conninfo *ci; 288 unsigned char *msg_name, *msg_val; 289 unsigned char *cmd; 290 int (*func)(int, cache_t *, cache_t **); 291 int cmd_len; 292 int msg_val_len, msg_name_len; 293 int i; 294 295 if (!c) c=cache_create(11); 296 if (!sess) sess=cache_create(11); 297 if (!parms) parms=cache_create(5); 298 299 if (datalen==0 && data==NULL) { 300 PRINTERR("Cleaning up cache entries for [fd=%i].", fd); 301 cache_delete(c, &fd, sizeof(fd), NULL, 1); 302 return(0); 303 } 304 PRINTERR("Got data from [fd=%i], datalen=%i", fd, datalen); 305 306 ci=cache_find(c, &fd, sizeof(fd), NULL); 307 if (!ci) { 308 ci=malloc(sizeof(struct conninfo)); 309 ci->parms=cache_create(5); 310 ci->state=0; /* XXX */ 311 } 312 cache_add(c, &fd, sizeof(fd), ci, sizeof(struct conninfo), 0, tcpcgi_free_conninfo); 313 PRINTERR("Reading in data name+value pairs..."); 314 /* Read the pairs into ci->parms */ 315 for (i=0; i<datalen; ) { 316 msg_name_len=data[i]; 317 msg_val_len=(data[i+1]<<8)|(data[i+2]); 318 msg_name=malloc(msg_name_len+1); 319 msg_val=malloc(msg_val_len+1); 320 memcpy(msg_name, data+i+3, msg_name_len); 321 memcpy(msg_val, data+i+3+msg_name_len, msg_val_len); 322 /* 323 * These two lines allow us to treat the parameters as 324 * null-terminated strings if we wish. 325 */ 326 msg_name[msg_name_len]='\0'; 327 msg_val[msg_val_len]='\0'; 328 PRINTERR("%s = [%i]\"%s\"", msg_name, msg_val_len, msg_val); 329 cache_add(ci->parms, msg_name, msg_name_len, msg_val, msg_val_len, 0, tcpcgi_free_cacheparms); 330 331 i+=(msg_name_len+msg_val_len+3); 332 } 333 334 /* 335 * Find the `cmd' keyword and determine what function should 336 * handle it, then call that function with 3 arguments: 337 * fd, ci->parms, &sess 338 */ 339 cmd=cache_find(ci->parms, "action", strlen("action"), &cmd_len); 340 for (i=0; i<(sizeof(tcpcgi_cmd_list)/sizeof(void *))/2; i++) { 341 if (memcmp(cmd, tcpcgi_cmd_list[i][0], cmd_len)==0) { 342 func=tcpcgi_cmd_list[i][1]; 343 return(func(fd, ci->parms, &sess)); 344 } 345 } 346 /* Invalid/unknown command */ 347 return(tcpcgi_errmsg(fd, "Invalid command")); 348 349 } 350 351 int MAIN_FUNC(void) { 352 struct pollfd socks_poll[128]; 353 struct sockaddr addr; 354 char buf[8192]; 355 socklen_t addrlen; 356 int masterfd, fd, clientfd; 357 int pollcnt=0, pollpos=0, pollmax; 358 int num_events; 359 int errcnt=0; 360 int i,x; 361 #if !defined(TCPCGID_STANDALONE) || !defined(DEBUG) 362 pid_t chpid; 363 #endif 364 365 #ifdef TCPCGID_STANDALONE 366 /* If we're in stand-alone mode, do this before we fork, in case of failure */ 367 /* Note, this code is duplicated below with the OPPOSITE condition */ 368 signal(SIGPIPE, SIG_IGN); 369 370 masterfd=createlisten(TCPCGI_DAEMON_PORT, 1, SOCK_STREAM); 371 if (masterfd<0) { 372 PERROR("createlisten()"); 373 return(-1); 374 } 375 #endif 376 377 #if defined(TCPCGID_STANDALONE) && defined(DEBUG) 378 PRINTERR("Not forking."); 379 #else 380 /* We're all out of complaints at this point, and should be ready to go in the background. */ 381 if ((chpid=fork())<0) { PERROR("fork"); return(1); } 382 if (chpid!=0) { 383 /* Parent [old process] */ 384 wait(NULL); 385 return(0); 386 } 387 /* Child [new process] */ 388 /* Beyond here, we need to exit() instead of return (unless standalone mode) */ 389 if ((chpid=fork())<0) { RET_FROM_MAIN(1); } 390 if (chpid!=0) RET_FROM_MAIN(0); 391 /* Grand-child */ 392 setsid(); 393 #endif 394 395 #ifndef TCPCGID_STANDALONE 396 /* If we're not in stand-alone mode, do this now (since we didn't do it above). */ 397 signal(SIGPIPE, SIG_IGN); 398 399 masterfd=createlisten(TCPCGI_DAEMON_PORT, 1, SOCK_STREAM); 400 if (masterfd<0) { 401 PERROR("createlisten()"); 402 RET_FROM_MAIN(-1); 403 } 404 #endif 405 406 for (i=0; i<(sizeof(socks_poll)/sizeof(struct pollfd)); i++) socks_poll[i].fd=-1; 407 socks_poll[pollpos].fd=masterfd; 408 socks_poll[pollpos].events=POLLIN; 409 socks_poll[pollpos].revents=0; 410 pollpos++; 411 pollcnt++; 412 while (1) { 413 for (i=0; i<(sizeof(socks_poll)/sizeof(struct pollfd)); i++) { 414 if (socks_poll[i].fd!=-1) pollmax=(i+1); 415 } 416 num_events=poll(socks_poll, pollmax, -1); 417 if (num_events<0) { 418 if (errcnt==TCPCGI_DAEMON_MAXERR) { 419 fprintf(stderr, "Maximum error count (%i) reached, aborting...\n", TCPCGI_DAEMON_MAXERR); 420 break; 421 } 422 errcnt++; 423 PERROR("poll()"); 424 continue; 425 } 426 errcnt=0; 427 if (num_events==0) continue; 428 for (i=0; i<pollmax; i++) { 429 if (socks_poll[i].revents&(POLLERR|POLLHUP|POLLNVAL)) { 430 /* Socket is broke, close it. */ 431 CLOSE_POLL(i); 432 PRINTERR("Closing socket [idx=%i, fd=%i], due to errors.", i, fd); 433 num_events--; 434 if (num_events==0) break; 435 continue; 436 } 437 if ((socks_poll[i].revents&POLLIN)!=POLLIN) continue; 438 num_events--; 439 fd=socks_poll[i].fd; 440 socks_poll[i].events=POLLIN; 441 socks_poll[i].revents=0; 442 if (fd==masterfd) { 443 /* Handle incoming connection. */ 444 PRINTERR("New connection..."); 445 addrlen=sizeof(addr); 446 clientfd=accept(fd, &addr, &addrlen); 447 if (clientfd<0) { 448 PERROR("accept"); 449 continue; 450 } 451 if (pollpos==-1) { 452 for (x=0; x<pollmax; x++) { 453 if (socks_poll[x].fd==-1) { 454 pollpos=x; 455 break; 456 } 457 } 458 } 459 if (pollpos==-1) pollpos=pollmax; 460 if (pollpos>=(sizeof(socks_poll)/sizeof(struct pollfd))) { 461 PRINTERR("Ran out of available socks_poll[] entries."); 462 close(fd); 463 continue; 464 } 465 socks_poll[pollpos].fd=clientfd; 466 socks_poll[pollpos].events=POLLIN|POLLERR|POLLHUP|POLLNVAL; 467 socks_poll[pollpos].revents=0; 468 pollcnt++; 469 PRINTERR("... from [idx=%i, fd=%i], pollcnt=%i, pollmax=%i", pollpos, clientfd, pollcnt, pollmax); 470 pollpos=-1; 471 } else { 472 /* Handle data from an existing connection. */ 473 PRINTERR("Data waiting on [idx=%i, fd=%i]...", i, fd); 474 x=read(fd, buf, sizeof(buf)); 475 if (x<0) PERROR("read"); 476 if (x==0) { 477 PRINTERR("EOF from [idx=%i, fd=%i].", i, fd); 478 CLOSE_POLL(i); 479 continue; 480 } 481 if (tcpcgi_handledata(fd, buf, x)<0) { 482 /* If we got an error, report no data. */ 483 tcpcgi_write_data(fd, NULL, 0); 484 continue; 485 } 486 } 487 if (num_events==0) break; 488 } 489 } 490 RET_FROM_MAIN(errcnt); 491 } |