5748382 [rkeene@sledge /home/rkeene/devel/old/rutil_tcpcgi-0.1.17]$ cat -n tcpcgid.c
  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 }
5748383 [rkeene@sledge /home/rkeene/devel/old/rutil_tcpcgi-0.1.17]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 2004-01-01 16:22:42