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