1 /*
2  * tlink.c: runs as a CGI program and passes request to Interchange
3  *          server via TCP/IP
4  *
5  * $Id: tlink.c,v 2.6 2007-08-09 13:40:52 pajamian Exp $
6  *
7  * Copyright (C) 2005-2007 Interchange Development Group,
8  * http://www.icdevgroup.org/
9  * Copyright (C) 1996-2002 Red Hat, Inc.
10  * Copyright (C) 1995 by Andrew M. Wilcox <amw@wilcoxsolutions.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public
23  * License along with this program; if not, write to the Free
24  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
25  * MA  02110-1301  USA.
26  */
27 
28 #include "config.h"
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <setjmp.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/time.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <arpa/inet.h>
41 #include <arpa/telnet.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 #include <sys/un.h>
45 #include <unistd.h>
46 
47 #ifndef ENVIRON_DECLARED
48 extern char** environ;
49 #endif
50 
51 /* For GCC on Solaris systems */
52 #ifndef INADDR_NONE
53 #define INADDR_NONE -1
54 #endif
55 
56 struct sockaddr_in ServAddr;
57 
58 /* CGI output to the server is on stdout, fd 1.
59  */
60 #define CGIOUT 1
61 
62 #ifdef HAVE_STRERROR
63 #define ERRMSG strerror
64 #else
65 #define ERRMSG perror
66 #endif
67 
68 
69 
70 /* Return this message to the browser when the server is not running.
71  */
server_not_running()72 void server_not_running()
73 {
74   printf("Content-type: text/html\r\n\r\n");
75   printf("<HTML><HEAD><TITLE>No response</TITLE></HEAD><BODY BGCOLOR=\"#FFFFFF\">");
76   printf("<H3>We're sorry, the Interchange server is unavailable...</H3>\r\n");
77   printf("We are out of service or may be experiencing high system\r\n");
78   printf("demand. Please try again soon.</BODY></HTML>\r\n");
79   exit(1);
80 }
81 
get_the_host(char * host,struct in_addr * ip_address)82 struct hostent *get_the_host(char *host, struct in_addr *ip_address)
83 {
84     struct in_addr ip;
85     struct hostent *hp;
86 
87     ip.s_addr = inet_addr(host);
88     if (ip.s_addr != INADDR_NONE) {
89         hp = gethostbyaddr((char *) &ip, (int) sizeof(ip), AF_INET);
90     } else {
91         /* No IP address, so it must be a hostname, like ftp.wustl.edu. */
92         hp = gethostbyname(host);
93         if (hp != NULL)
94             ip = * (struct in_addr *) hp->h_addr_list;
95     }
96     if (ip_address != NULL)
97         *ip_address = ip;
98     return (hp);
99 }
100 
101 
102 /* Return this message to the browser when a system error occurs.
103  * Should we log to a file?  Email to admin?
104  */
die(e,msg)105 static void die(e, msg)
106      int e;
107      char* msg;
108 {
109   printf("Content-type: text/plain\r\n\r\n");
110   printf("We are sorry, but the Interchange server is unavailable due to a\r\n");
111   printf("system error.\r\n\r\n");
112   printf("%s: %s (%d)\r\n", msg, ERRMSG(e), e);
113   exit(1);
114 }
115 
116 
117 /* Read the entity from stdin if present.
118  */
119 static int entity_len = 0;
120 static char* entity_buf = 0;
121 
122 static void
get_entity()123 get_entity()
124 {
125   int len;
126   char* cl;
127   int nr;
128 
129   entity_len = 0;
130   cl = getenv("CONTENT_LENGTH");
131   if (cl != 0)
132     entity_len = atoi(cl);
133 
134   if (entity_len == 0) {
135     entity_buf = 0;
136     return;
137   }
138 
139   entity_buf = malloc(entity_len);
140   if (entity_buf == 0)
141     die(0, "malloc");
142 
143   nr = fread(entity_buf, 1, entity_len, stdin);
144   if (nr == 0) {
145     free(entity_buf);
146     entity_len = 0;
147     entity_buf = 0;
148   }
149 }
150 
151 
152 static char ibuf[1024];		/* input buffer */
153 static jmp_buf reopen_socket;	/* bailout when server shuts down */
154 #define buf_size 1024		/* output buffer size */
155 static char buf[buf_size];	/* output buffer */
156 static char* bufp;		/* current position in output buffer */
157 static int buf_left;		/* space left in output buffer */
158 static int sock;		/* socket fd */
159 
160 /* Open the unix file socket and make a connection to the server.  If
161  * the server isn't listening on the socket, retry for LINK_TIMEOUT
162  * seconds.
163  */
open_socket()164 static void open_socket()
165 {
166   int size;
167   int s;
168   struct in_addr ip_address;
169   struct hostent *hp;
170   int i;
171   int e;
172   int r;
173   char* lhost;
174   char* lpstring;
175   int lport;
176   unsigned int p; /* port */
177   char *machine = LINK_HOST;  //static and global string;
178   uid_t euid;
179   gid_t egid;
180 
181 
182   lhost = getenv("MINIVEND_HOST");
183   if(lhost == NULL) {
184   	lhost = machine;
185   }
186 
187   lpstring = getenv("MINIVEND_PORT");
188   if(lpstring != 0) {
189   	lport = atoi(lpstring);
190   }
191   else {
192   	lport = LINK_PORT;
193   }
194 
195   p = (unsigned int) htons((unsigned short) lport);
196 
197   ServAddr.sin_port = p;
198 
199   hp = get_the_host(lhost, &ip_address);
200 
201  	if(hp == NULL) {
202 	    if (ip_address.s_addr == INADDR_NONE) {
203             die("Unknown host.\n");
204         }
205         ServAddr.sin_family = AF_INET;
206         ServAddr.sin_addr.s_addr = ip_address.s_addr;
207     }
208     else {
209 		ServAddr.sin_addr = *((struct in_addr *)hp->h_addr);
210         ServAddr.sin_family = hp->h_addrtype;
211         /* We'll fill in the rest of the structure below. */
212     }
213 
214 
215   for (i = 0;  i < LINK_TIMEOUT;  ++i) {
216     sock = socket(ServAddr.sin_family, SOCK_STREAM, 0);
217     e = errno;
218     if (sock < 0)
219       die(e, "Could not open socket");
220 
221     do {
222       s = connect(sock, (struct sockaddr*) &ServAddr, (int) sizeof (ServAddr));
223       e = errno;
224     } while (s == -1 && e == EINTR);
225 
226     if (s == 0)
227       break;
228     close(sock);
229     sleep(1);
230   }
231   if (s < 0) {
232     server_not_running();
233     exit(1);
234   }
235 }
236 
237 /* Close the socket connection.
238  */
close_socket()239 static void close_socket()
240 {
241   if (close(sock) < 0)
242     die(errno, "close");
243 }
244 
245 /* Write out the output buffer to the socket.  If the cgi-bin server
246  * has 'listen'ed on the socket but closes it before 'accept'ing our
247  * connection, we'll get a EPIPE here and retry the connection over again.
248  */
write_out()249 static void write_out()
250 {
251   char* p = buf;
252   int len = bufp - buf;
253   int w;
254 
255   while (len > 0) {
256     do {
257       w = write(sock, p, len);
258     } while (w < 0 && errno == EINTR); /* retry on interrupted system call */
259     if (w < 0 && errno == EPIPE) /* server closed */
260       longjmp(reopen_socket, 1); /* try to reopen the connection */
261     if (w < 0)
262       die(errno, "write");
263     p += w;			/* write the rest out if short write */
264     len -= w;
265   }
266 
267   bufp = buf;			/* reset output buffer */
268   buf_left = buf_size;
269 }
270 
271 /* Write out LEN characters from STR to the cgi-bin server.
272  */
out(len,str)273 static void out(len, str)
274      int len;
275      char* str;
276 {
277   char* strp = str;
278   int str_left = len;
279 
280   while (str_left > 0) {
281     if (str_left < buf_left) {	       /* all fits in buffer */
282       memcpy(bufp, strp, str_left);
283       bufp += str_left;
284       buf_left -= str_left;
285       str_left = 0;
286     } else {			       /* only part fits */
287       memcpy(bufp, strp, buf_left);    /* copy in as much as fits */
288       str_left -= buf_left;
289       strp += buf_left;
290       bufp += buf_left;
291       write_out();		       /* write out buffer */
292     }
293   }
294 }
295 
296 /* Writes the null-terminated STR to the cgi-bin server.
297  */
outs(str)298 static void outs(str)
299      char* str;
300 {
301   out(strlen(str), str);
302 }
303 
304 /* Returns I as an ascii string.  Don't some systems define itoa for you?
305  */
itoa(i)306 static char* itoa(i)
307      int i;
308 {
309   static char buf[32];
310   sprintf(buf, "%d", i);
311   return buf;
312 }
313 
314 /* Sends the null-terminated value STR to the cgi-bin server.  First
315  * writes the length, then a space, then the value, and finally an
316  * aesthetic newline.
317  */
outv(str)318 static void outv(str)
319      char* str;
320 {
321   int len = strlen(str);
322 
323   outs(itoa(len));
324   out(1, " ");
325   out(len, str);
326   out(1, "\n");
327 }
328 
329 /* Send the program arguments (but not the program name argv[0])
330  * to the server.
331  */
send_arguments(argc,argv)332 static void send_arguments(argc, argv)
333      int argc;
334      char** argv;
335 {
336   int i;
337 
338   outs("arg ");
339   outs(itoa(argc - 1));		       /* number of arguments */
340   outs("\n");
341   for (i = 1;  i < argc;  ++i) {
342     outv(argv[i]);
343   }
344 }
345 
346 /* Send the environment to the server.
347  */
send_environment()348 static void send_environment()
349 {
350   int n;
351   char** e;
352 
353   /* count number of env variables */
354   for (e = environ, n = 0;  *e != 0;  ++e, ++n)
355     ;
356 
357   outs("env ");
358   outs(itoa(n));		       /* number of vars */
359   outs("\n");
360   for (e = environ;  *e != 0;  ++e) {
361     outv(*e);
362   }
363 }
364 
365 /* Send entity if we have one.
366  */
367 static void
send_entity()368 send_entity()
369 {
370   char* cl;
371   int len;
372   int left;
373   int tr;
374 
375   if (entity_len > 0) {
376     outs("entity\n");
377     outs(itoa(entity_len));
378     out(1, " ");
379     out(entity_len, entity_buf);
380     out(1, "\n");
381   }
382 }
383 
384 #define BUFSIZE 16384
385 
386 struct buffer {
387   int len;
388   int written;
389   struct buffer* nextbuf;
390   char buf[BUFSIZE];
391 };
392 
new_buffer()393 static struct buffer* new_buffer()
394 {
395   struct buffer* buf = (struct buffer*) malloc(sizeof(struct buffer));
396   if (buf == 0)
397     die(0, "malloc");
398   buf->len = 0;
399   buf->written = 0;
400   buf->nextbuf = 0;
401   return buf;
402 }
403 
read_from_server(bp)404 static int read_from_server(bp)
405      struct buffer* bp;
406 {
407   int b;
408   int n;
409   char* a;
410 
411   b = BUFSIZE - bp->len;
412   a = (bp->buf) + bp->len;
413   do {
414     n = read(sock, a, b);
415   } while (n < 0 && errno == EINTR);
416   if (n < 0)
417     die(errno, "read");
418   if (n == 0) {
419     return 0;
420   }
421   bp->len += n;
422   return 1;
423 }
424 
write_to_client(bp)425 static int write_to_client(bp)
426      struct buffer* bp;
427 {
428   int b = bp->len - bp->written;
429   int n;
430 
431   do {
432     n = write(CGIOUT, bp->buf + bp->written, b);
433   } while (n < 0 && errno == EINTR);
434   if (n < 0 && errno == EAGAIN)
435     return 0;
436   if (n < 0)
437     die(errno, "write");
438   bp->written += n;
439   return (bp->written == bp->len);
440 }
441 
return_response()442 static void return_response()
443 {
444   int reading;
445   int writing;
446   fd_set readfds;
447   fd_set writefds;
448   int maxfd;
449   int r;
450   struct buffer* readbuf;
451   struct buffer* writebuf;
452   struct buffer* newbuf;
453 
454   int f;
455   if (fcntl(CGIOUT, F_SETFL, O_NONBLOCK) < 0)
456     die(errno, "fcntl");
457   f = fcntl(CGIOUT, F_GETFL);
458 
459   reading = 1;
460   readbuf = writebuf = new_buffer();
461 
462   for (;;) {
463     if (writebuf->written == BUFSIZE && writebuf->nextbuf != 0) {
464       newbuf = writebuf->nextbuf;
465       free(writebuf);
466       writebuf = newbuf;
467     }
468 
469     writing = (writebuf->written < writebuf->len);
470 
471     if (!reading && !writing)
472       break;
473 
474     FD_ZERO(&readfds);
475     FD_ZERO(&writefds);
476     maxfd = 0;
477     if (reading) {
478       FD_SET(sock, &readfds);
479       maxfd = sock;
480     }
481     if (writing) {
482       FD_SET(CGIOUT, &writefds);
483       if (maxfd < CGIOUT)
484         maxfd = CGIOUT;
485     }
486 
487     r = select(maxfd + 1, &readfds, &writefds, 0, 0);
488     if (r < 0)
489       die(errno, "select");
490 
491     if (reading && FD_ISSET(sock, &readfds)) {
492       if (readbuf->len == BUFSIZE) {
493         newbuf = new_buffer();
494         readbuf->nextbuf = newbuf;
495         readbuf = newbuf;
496       }
497       r = read_from_server(readbuf);
498       if (r == 0)
499         reading = 0;
500     }
501 
502     if (writing && FD_ISSET(CGIOUT, &writefds)) {
503       r = write_to_client(writebuf);
504     }
505   }
506 }
507 
508 
509 #if 0
510 /* Now read the response from the cgi-bin server and return it to our
511  * caller (httpd).  We assume the server just closes the socket at the
512  * end of the response.
513  */
514 static void read_sock()
515 {
516   int nr;
517   char* p;
518   int w;
519 
520   for (;;) {
521     do {
522       nr = read(sock, ibuf, sizeof(ibuf));
523     } while (nr < 0 && errno == EINTR);	/* interrupted system call */
524     if (nr < 0)
525       die(errno, "read");
526     if (nr == 0)		       /* that's it, all done */
527       break;
528 
529     p = ibuf;			       /* write it to our stdout */
530     while (nr > 0) {
531       do {
532 	w = write(CGIOUT, p, nr);
533       } while (w < 0 && errno == EINTR);
534       if (w < 0)
535 	die(errno, "write");
536       p += w;			       /* and write again if short write */
537       nr -= w;
538     }
539   }
540 }
541 #endif
542 
main(argc,argv)543 int main(argc, argv)
544      int argc;
545      char** argv;
546 {
547 
548   /* Give us an EPIPE error instead of a SIGPIPE signal if the server
549    * closes the socket on us.
550    */
551   if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
552     die(errno, "signal");
553 
554   get_entity();
555 
556   /* If the server does close the socket, jump back here to reopen. */
557   if (setjmp(reopen_socket)) {
558     close_socket();		       /* close our end of old socket */
559   }
560 
561   bufp = buf;			       /* init output buf */
562   buf_left = buf_size;
563   open_socket();		       /* open our connection */
564   send_arguments(argc, argv);
565   send_environment();
566   send_entity();
567   outs("end\n");
568   write_out();			       /* flush output buffer */
569 
570   return_response();
571   close_socket();
572   return 0;
573 }
574