/* * simple http redirect server */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char version[] = "one"; char *host; int ontty = 1; int verbose = 0; int debug = 0; char *pgm = "redirect"; static void complain(char *fmt, ...) { static int size = 1024; static char *bfr = 0; va_list ptr; int ret; int ofs; char *myerror = strerror(errno); char *expandfmt; int count = 0; int isusage; char *p; static int logopen = 0; if ( !(ontty||logopen) ) { openlog(pgm, 0, LOG_DAEMON); logopen = 1; } if (bfr == 0 && (bfr = malloc(size)) == 0 ) { if (ontty) perror("initial bfr alloc"); else syslog(LOG_INFO, "initial buffer alloc: %m"); exit(EX_OSERR); } for (p = fmt; p = strstr(p, "%m"); count++, p++) ; if (count > 0) { if ( expandfmt = malloc(1 + strlen(fmt) + (count*strlen(myerror))) ) { char *q; for (p=fmt, q=expandfmt; *p; ) { if ( (p[0] == '%') && (p[1] == 'm') ) { strcat(q, myerror); q += strlen(myerror); p += 2; } else *q++ = *p++; } *q = 0; } } else expandfmt = strdup(fmt); if (expandfmt == 0) { if (ontty) perror("building format string"); else syslog(LOG_INFO, "building format string: %m"); exit(EX_OSERR); } if (strncmp(expandfmt, "%USAGE ", 7) == 0) { expandfmt += 7; sprintf(bfr, "usage: %s", pgm); } else if (ontty) sprintf(bfr, "%s: ", pgm); else bfr[0] = 0; ofs = strlen(bfr); while (1) { if (bfr == 0) { if (ontty) perror("buffer alloc"); else syslog(LOG_INFO, "buffer alloc: %m"); exit(EX_OSERR); } va_start(ptr, fmt); ret = vsnprintf(bfr+ofs, (size-ofs)-1, expandfmt, ptr); va_end(ptr); if ( (ret < 0) || (ret >= (size-ofs)) ) { size *= 2; bfr = realloc(bfr, size); } else break; } free(expandfmt); if (ontty) { strcat(bfr, "\n"); fputs(bfr, stderr); fflush(stderr); } else syslog(LOG_INFO, bfr); } void daemonize() { pid_t daemon; int i, nul; for (i=0; i < 2; i++) { if ( (daemon = fork()) == -1) { complain("during daemonization: %m"); exit(EX_OSERR); } else if (daemon > 0) exit(EX_OK); } close(0); close(1); close(2); if ( (nul = open("/dev/null", O_RDWR)) != -1 && dup2(nul,0) != -1 && dup2(nul,1) != -1 && dup2(nul,2) != -1) close(nul); else { complain("Cannot attach to /dev/null: %m"); exit(EX_OSERR); } setsid(); ontty = 0; } static int attach(int port) { struct sockaddr service; struct sockaddr_in *af_inet = (struct sockaddr_in*)&service; int size; int ret; int on = 1; if ( (ret = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if (verbose || ontty) complain("socket failed: %m");; return -1; } setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on); setsockopt(ret, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on); memset(&service, 0, sizeof service); af_inet->sin_family = AF_INET; af_inet->sin_port = port; if (bind(ret, &service, sizeof service) < 0) { if (verbose || ontty) complain("bind failed: %m"); close(ret); return -1; } if (listen(ret, 5) == -1) { if (verbose || ontty) complain("listen failed: %m"); close(ret); return -1; } return ret; } static int connection(int client) { char line[1024]; char *p, *q; FILE *in, *out; if ( ! ((in = fdopen(client, "r")) && (out=fdopen(client,"w"))) ) { if (verbose || ontty) complain("fdopens failed: %m"); close(client); return; } if (fgets(line, sizeof line, in) == 0) { oops: fclose(in); fclose(out); close(client); return 0; } if (verbose) { strtok(line, "\r\n"); complain("[%s]", line); } p = line; while (*p && !isspace(*p)) ++p; /* skip command */ while (*p && isspace(*p)) ++p; /* skip space */ if (*p == 0) goto oops; q = p; while (*q && !isspace(*q)) ++q; /* skip pathname */ if (*q) *q++ = 0; while (*q && isspace(*q)) ++q; if (*q) { char timestr[80]; time_t now; struct tm *tm; now = time(0); tm = gmtime(&now); strftime(timestr, sizeof timestr, "%a, %d %b %Y %H:%M:%S GMT", tm); fprintf(out,"HTTP/1.1 302 Found\n" "Date: %s\n" "Server: Simple Redirect Daemon v %s\n" "Location: %s%s\n" "Connection: close\n" "Content-Type: text/html; charset=iso-8859-1\n" "\n", timestr, version, host, p); } fprintf(out,"\n" "\n" "302 Found\n" "\n" "

Found

\n" "The document has moved to %s%s.

\n" "


\n" "\n", host, p, host, p); fflush(out); fclose(in); fclose(out); close(client); return 1; } void main(argc,argv) int argc; char **argv; { int port; int sock, client; int opt; pgm = basename(argv[0]); ontty = isatty(fileno(stdin)); opterr = 0; while ( (opt = getopt(argc, argv, "vd")) != EOF ) { switch (opt) { case 'v': verbose = 1; break; case 'd': debug = 1; break; default: complain("%USAGE [-dv] to-website [listen-port]"); } } argc -= optind; argv += optind; if (argc < 1) { complain("%USAGE [-dv] to-website [listen-port]"); exit(EX_USAGE); } host = argv[0]; if (strstr(host, "://") == 0) { complain("malformed to-website"); exit(EX_USAGE); } if (argc > 1) { int rpn = atoi(argv[1]); if (rpn == 0) { complain("unrecognisable port number (%s)", argv[1]); exit(EX_USAGE); } port = htons(rpn); } else { struct servent *proto = getservbyname("www", "tcp"); port = proto ? proto->s_port : htons(80); } if ( (sock = attach(port)) == -1) { if ( !(ontty || verbose) ) complain("could not attach to port %d", ntohs(port)); exit(EX_OSERR); } if (!debug) daemonize(); while (1) { struct sockaddr j; int js = sizeof j; if ( (client = accept(sock, &j, &js)) != -1 ) connection(client); else if (errno != EINTR) { if (verbose || ontty) complain("accept failed: %m"); close(sock); while ( (sock = attach(port)) == -1 ) { complain("reattach failed; sleeping and retrying"); sleep(20); } } } }