📄 tcpproxy.c
字号:
/* File: tcpproxy/tcpproxy.c Copyright (C) 1999 Wolfgang Zekoll <wzk@quietsche-entchen.de> This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Version 1.1.4 - 11NOV99wzk * * - shutdown(sfd, 1) wenn close() auf der Client Seite erkannt wird. * * -w Option * * * Version 1.1.2 - 11SEP99wzk * * - Die Optionen -c und -z sind neu. * * * Version 1.1.0 - 01SEP99wzk * * Aenderungen gegenueber den Vorgaengerversionen: * * - Unterstuetzung von Server-Programmen. * - Der netcat-Modus wird nicht mehr unterstuetzt. * - Erweiterung des Kommandozeilenoptionen. * */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <signal.h>#include <wait.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/fcntl.h>#include <sys/socket.h>#include <netdb.h>#include <netinet/in.h>#include <syslog.h>#include <sys/time.h>#include "ip-lib.h"#include "lib.h"#define PIDFILE "/var/run/tcpproxy.pid"#define ERROR_LOG "/tmp/tcpproxy-error.log"#define MAX_PORT 80char *program = "";char progname[80] = "";char logname[80] = "";int debug = 0;char configfile[300] = "";int use_errorlog = 0;char errorlog[200] = ERROR_LOG;int standalone = 0;int uid = -1;int gid = -2;char pidfile[200] = "";int listports = 0;typedef struct _port { unsigned int port; int count; char ip[40]; int sock; } port_t;int binds = 0;port_t *bindport;#define PROXY_DEFAULT 0#define PROXY_SERVER 1#define PROXY_PROGRAM 2#define MAX_ARGS 32typedef struct _config { char interface[300]; unsigned int port; int timeout; int cleanenv; char varname[80]; char acp[200]; char writefile[200]; int proxytype; union { struct { char name[300]; unsigned int port; } server; struct { int exec; char command[300]; char *argv[MAX_ARGS + 2]; } program; } u; struct { char ip[300]; unsigned int port; } incoming; char client_ip[300]; char client[300]; int local_client; long bytes_send; long bytes_received; struct _config *next; } config_t;config_t *first = NULL;config_t *last = NULL;int config_count = 0;/* void *allocate(int size); */config_t *new_configuration(config_t *def){ config_t *x; x = allocate(sizeof(config_t)); if (def != NULL) memmove(x, def, sizeof(config_t)); if (first == NULL) { first = x; last = x; } else { last->next = x; last = x; } config_count++; return (x);}config_t *find_configuration(char *interface, unsigned int port){ config_t *x, *global; global = NULL; x = first; while (x != NULL) { if (x->port == port) { if (*x->interface == 0 || strcmp(x->interface, "0.0.0.0") == 0) global = x; else if (strcmp(x->interface, interface) == 0) break; } x = x->next; } if (x == NULL && global != NULL) x = global; return (x);}int _check_interface(config_t *x, int lineno){ if (*x->interface == 0) { fprintf (stderr, "%s: line %d: interface not set\n", program, lineno); exit (1); } return (0);}int _check_proxytype(config_t *x, int lineno){ if (x->proxytype != 0) { fprintf (stderr, "%s: line %d: service redefinition\n", program, lineno); exit (1); } return (0);}int read_configuration(char *filename, config_t *def){ int lineno; unsigned int port; char *p, word[80], line[300]; FILE *fp; config_t *x; /* * Konfiguration lesen */ if ((fp = fopen(filename, "r")) == NULL) { fprintf (stderr, "%s: can't read config file: %s\n", program, filename); exit (-1); } /* * Einstellungen vor dem ersten bind gehen in die * default-Konfiguration. */ x = def; lineno = 0; port = 0; while (fgets(line, sizeof(line), fp) != NULL) { lineno++; p = skip_ws(noctrl(line)); if (*p == 0 || *p == '#') continue; get_word(&p, word, sizeof(word)); strlwr(word); p = skip_ws(p); /* * globale Optionen */ if (strcmp(word, "uid") == 0) uid = atoi(p); else if (strcmp(word, "gid") == 0) gid = atoi(p); else if (strcmp(word, "pidfile") == 0) { copy_string(pidfile, p, sizeof(pidfile)); if (*pidfile == 0) strcpy(pidfile, PIDFILE); } else if (strcmp(word, "standalone") == 0) { get_word(&p, word, sizeof(word)); strlwr(word); if (strcmp(word, "yes") == 0 || strcmp(word, "on") == 0) standalone = 1; else if (strcmp(word, "no") == 0 || strcmp(word, "off") == 0) standalone = 0; } else if (strcmp(word, "logname") == 0) { if (*(p = skip_ws(p)) == 0) { fprintf (stderr, "%s: line %d: missing logname\n", program, lineno); exit (-1); } copy_string(logname, p, sizeof(logname)); } /* * Globale Vorgaben, bzw. Serviceparameter */ else if (strcmp(word, "timeout") == 0) { x->timeout = atoi(p); if (x->timeout < 1) x->timeout = 60; } else if (strcmp(word, "setenv") == 0) { get_word(&p, x->varname, sizeof(x->varname)); if (*x->varname == 0) strcpy(x->varname, "PROXY_"); } else if (strcmp(word, "acp") == 0) { copy_string(x->acp, p, sizeof(x->acp)); } /* * Server-Konfiguration */ else if (strcmp(word, "port") == 0 || strcmp(word, "bind") == 0) { if (binds >= MAX_PORT) { fprintf (stderr, "%s: line %d: too many ports\n", program, lineno); exit (-1); } port = getportnum(p); bindport[binds].port = port; bindport[binds].count = 0; binds++; } else if (strcmp(word, "interface") == 0) { if (binds == 0 || port == 0) { fprintf (stderr, "%s: line %d: no port specification\n", program, lineno); exit (-1); } else if (*(p = skip_ws(p)) == 0) { fprintf (stderr, "%s: line %d: no interface\n", program, lineno); exit (-1); } x = new_configuration(def); x->port = port; get_word(&p, x->interface, sizeof(x->interface)); if (strcmp(x->interface, "*") == 0 || strcmp(x->interface, "any") == 0) strcpy(x->interface, "0.0.0.0"); copy_string(bindport[binds-1].ip, x->interface, sizeof(bindport[binds-1].ip)); bindport[binds-1].count++; get_word(&p, word, sizeof(word)); strlwr(word); if (*word == 0) /* nichts */ ; else if (strcmp(word, "server") == 0) { _check_proxytype(x, lineno); x->proxytype = PROXY_SERVER; copy_string(x->u.server.name, p, sizeof(x->u.server.name)); x->u.server.port = get_port(x->u.server.name, x->port); } else if (strcmp(word, "exec") == 0) { _check_proxytype(x, lineno); x->proxytype = PROXY_PROGRAM; x->u.program.exec = 1; copy_string(x->u.program.command, p, sizeof(x->u.program.command)); } else { fprintf (stderr, "%s: line %d: syntax error: %s\n", program, lineno, word); exit (-1); } } else if (strcmp(word, "server") == 0) { _check_interface(x, lineno); _check_proxytype(x, lineno); copy_string(x->u.server.name, p, sizeof(x->u.server.name)); x->u.server.port = get_port(x->u.server.name, x->port); } else if (strcmp(word, "exec") == 0) { int argc; _check_interface(x, lineno); _check_proxytype(x, lineno); x->proxytype = PROXY_PROGRAM; x->u.program.exec = 1; copy_string(x->u.program.command, p, sizeof(x->u.program.command)); argc = split(x->u.program.command, x->u.program.argv, ' ', MAX_ARGS); if (argc >= MAX_ARGS) { fprintf (stderr, "%s: line %d: too much command line parameters\n", program, lineno); exit (1); } x->u.program.argv[argc] = NULL; } else if (strcmp(word, "writefile") == 0 || strcmp(word, "debug") == 0) { _check_interface(x, lineno); get_word(&p, x->writefile, sizeof(x->writefile)); } else { fprintf (stderr, "%s: line %d: unkown configuration directive %s\n", program, lineno, word); exit (-1); } } fclose (fp); return (0);}int cleanenv(config_t *x, char **envp){ int k, n; char *p, **env; env = envp; n = 0; while (env[n] != NULL) n++; env = envp; k = 0; while (k < n && *env != NULL) { char var[800]; p = *env; get_quoted(&p, '=', var, sizeof(var)); /* * Ist nicht ungefaehrlich. Wenn der folgende Aufruf * fehlschlaegt muessten wir unseren env-Pointer * inkrementieren, um nicht in eine Endlosschleife, * die immer auf derselben Variablen stehenbleibt, * zu landen. Das waere falsch, wenn wir die Variable * erfolgreich loeschen. Ungluecklicherweise liefert * unsetenv() keinen return-code zurueck. */ unsetenv(var); k++; } if (*env != NULL || k != n) syslog(LOG_NOTICE, "cleanenv error"); return (0);}int set_variables(config_t *x){ char var[200], val[200]; snprintf (var, sizeof(var) - 2, "%sINTERFACE", x->varname); setenv(var, x->incoming.ip, 1); snprintf (val, sizeof(val) - 2, "%u", x->port); snprintf (var, sizeof(var) - 2, "%sPORT", x->varname); setenv(var, val, 1); snprintf (var, sizeof(var) - 2, "%sCLIENT", x->varname); setenv(var, x->client_ip, 1); snprintf (var, sizeof(var) - 2, "%sCLIENTNAME", x->varname); setenv(var, x->client, 1); return (0);}int run_acp(config_t *x){ int rc, pid, pfd[2]; char line[300]; if (*x->acp == 0) return (0); rc = 0; if (pipe(pfd) != 0) { syslog(LOG_NOTICE, "can't pipe: %m"); exit (1); } else if ((pid = fork()) < 0) { syslog(LOG_NOTICE, "can't fork acp: %m"); exit (1); } else if (pid == 0) { int argc; char *argv[32]; close(0); /* Das acp kann nicht vom client lesen. */ dup2(pfd[1], 2); /* stderr wird vom parent gelesen. */ close(pfd[0]); set_variables(x); copy_string(line, x->acp, sizeof(line)); argc = split(line, argv, ' ', 30); argv[argc] = NULL; execvp(argv[0], argv); syslog(LOG_NOTICE, "can't exec acp %s: %m", argv[0]); exit (1); } else { int len; char message[300]; close(pfd[1]); *message = 0; if ((len = read(pfd[0], message, sizeof(message) - 2)) < 0) len = 0; message[len] = 0; noctrl(message); if (waitpid(pid, &rc, 0) < 0) { syslog(LOG_NOTICE, "error while waiting for acp: %m"); exit (1); } rc = WIFEXITED(rc) != 0? WEXITSTATUS(rc): 1; if (*message == 0) copy_string(message, rc == 0? "access granted": "access denied", sizeof(message)); if (*message != 0) syslog(LOG_NOTICE, "%s (rc= %d)", message, rc); } return (rc);}int multiaccept_loop(config_t *start){ int i, max; fd_set bound, available; FD_ZERO(&bound); max = 0; for (i=0; i<binds; i++) { if (bindport[i].sock < 0) { syslog(LOG_NOTICE, "invalid socket number found: %d", bindport[i].sock); exit (-1); } FD_SET(bindport[i].sock, &bound); if (bindport[i].sock > max) max = bindport[i].sock; } while (1) { memmove(&available, &bound, sizeof(fd_set)); if (select(max + 1, &available, NULL, NULL, NULL) < 0) { if (errno == EINTR) { syslog(LOG_NOTICE, "interrupted select call"); continue; } syslog(LOG_NOTICE, "select error: %m"); exit (-1); } for (i=0; i<binds; i++) { if (FD_ISSET(bindport[i].sock, &available)) { int connect, pid, len; struct sockaddr_in client; /* * hier kommt ein accept an */ len = sizeof(client); if ((connect = accept(bindport[i].sock, (struct sockaddr *) &client, &len)) < 0) { if (errno == EINTR || errno == ECONNABORTED)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -