/* * Copyright (c) 1997-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* nntp-gw.c */ /* Status reporting and minor workarounds by ArkanoiD, 2001 */ /* Copyright 1997-2000 by Eberhard Mattes Copyright 1999-2000 by Giles Heron Donated to the public domain. No warranty. */ #include #include #include #include #include #include #include #include #include #include #include #include "firewall.h" #include "libemfw.h" #include "emio.h" #include "firewall2.h" #include "ci_milter.h" /* Reference: RFC 977 */ #define SERVER_PORT 119 /* Default port for NNTP */ #define LF 10 #define CR 13 #define WHITE(c) ((c) == 32 || (c) == 9) /* Flags in command table. */ #define CMD_SIZE 1 /* Log list size (with "log list-size") */ #define CMD_NOAUTH 2 /* No authorization required */ #if defined(linux) || defined (SOLARIS) extern void initsetproctitle(int,char**); #endif #define QTEMPLATE "/tmp/nntp-gw.XXXXXXXXXX" #define MAXLINE 1024 extern int milter_inspect_mail(char*,char*,char*,char*,char*,char*,char***); static const char *user; static char input[512]; static char cmd[32]; static size_t input_length; static size_t cmd_length; static int server_fd; static int server_contacted; static EMI_FILE *c_in, *s_in; static EMO_FILE *c_out, *s_out; static char group_name[512]; static unsigned long group_bytes; static unsigned long group_articles; static int log_auth; static int log_command; static int log_response; static int log_list_size; static int log_post; static int log_article_summary; static int cdscp = 0; static int sdscp = 0; static int transparent = 0; static int extendperm = 0; static char **validests = (char **)0; static char **validusers = (char **)0; static char separator[2] = "@"; static fwparm options[] = { { FWPARM_BOOL, "-tranparent", (char*) &transparent }, { FWPARM_BOOL, "-extnd", (char*) &extendperm }, { FWPARM_CHAR, "-separator", separator }, { FWPARM_LIST, "-dest", (char*) &validests }, { FWPARM_LIST, "-user", (char*) &validusers }, { FWPARM_LIST, "-plug-to", proxy_stats.dst }, { FWPARM_PORT, "-port", (char*)&proxy_stats.port }, { FWPARM_DSCP, "-client-dscp", (char*) &cdscp }, { FWPARM_DSCP, "-server-dscp", (char*) &sdscp }, { 0, 0, 0 }}; /* firewall.h lacks some declarations. */ int conn_server (const char *, int, int, char *); int str_to_port (char *); int do_daemon (int); int peername (int, char *, char *, int); int hostmatch (const char *, const char *); /* Prototypes. */ static void quit (int) ATTR_NORETURN; static void read_error (EMI_FILE *) ATTR_NORETURN; static void write_error (EMO_FILE *) ATTR_NORETURN; /* Keep byte count in sync */ void em_update_status() { proxy_stats.inbytes = emi_amount(s_in,0); proxy_stats.outbytes = emo_amount(s_out,0); proxy_update_status(); } static void log_group (void) { if (log_article_summary && group_name[0] != 0 && group_articles != 0) syslog (LLEV, "group %.256s: articles=%lu bytes=%lu", group_name, group_articles, group_bytes); } static void quit (int rc) { log_group (); em_update_status(); proxy_exit(); } static void puts_qf(fd,line) int fd; char *line; { if (sosay(fd,line)) { close(fd); syslog(LLEV,"fwtksyserr: error writing quarantine file, %s", strerror(errno)); quit(1); } } /* * Parse rfc2822 address simple way and get address itself. * NOTE: this function works for reference information only and * may provide incaccurate data, keeps inline comments, ingores * quoting etc etc. It is not important for nntp gateway but * use of this function should be restricted for such purpose only. * That's why we are static. */ static char* parse_address(address) char* address; { char *p,*p1; static char mybuf[MAXLINE]; memset(mybuf,0,sizeof(mybuf)); if (!address || !(*address)) return(NULL); strncpy(mybuf,address,sizeof(mybuf)-1); if (!(p=strchr(mybuf,'<'))) return(mybuf); if (!(*(++p)) || !(p1=strrchr(p,'>'))) return(NULL); *(p1) = '\0'; return(p); } /* Return "client" or "server", depending on whether the file F is used for the client or server, respectively. */ static const char *cs (void *f) { return (f == c_in || f == c_out) ? "client" : "server"; } static void read_error (EMI_FILE *f) { syslog (LLEV, "%s: read(): %s", cs (f), strerror(errno)); quit (1); } static void write_error (EMO_FILE *f) { syslog (LLEV, "%s: write(): %s", cs (f), strerror(errno)); quit (1); } static size_t read_line (EMI_FILE *f, char *buf, size_t size) { size_t i = 0; int c; while ((c = emi_getc (f)) != EMI_EOF && c != LF) { if (i + 1 >= size) { syslog (LLEV, "securityalert: %s: line too long", cs (f)); quit (1); } if (c == 0) { syslog (LLEV, "securityalert: %s: line contains invalid character", cs (f)); quit (1); } buf[i++] = (char)c; } if (c == LF) { if (i != 0 && buf[i-1] == CR) --i; buf[i] = 0; return i; } if (emi_error (f)) read_error (f); else { syslog (LLEV, "%s: unterminated line", cs (f)); quit (1); } em_update_status(); return 0; /* unreached */ } static void command (void) { if (log_command) syslog (LLEV, "command: %.512s", input); emo_write (s_out, input, input_length); emo_write (s_out, "\r\n", 2); if (emo_flush (s_out) != 0) write_error (s_out); em_update_status(); } static void respond (const char *s) { emo_puts (c_out, s); emo_write (c_out, "\r\n", 2); if (emo_flush (c_out) != 0) write_error (c_out); } static int has_body (int code) { return (code == 100 || code == 215 || code == 220 || code == 221 || code == 222 || code == 224 || code == 230 || code == 231); } static int get_response (void) { int code, i; input_length = read_line (s_in, input, sizeof (input)); if (log_response) syslog (LLEV, "response: %.512s", input); code = 0; for (i = 0; i < 3; ++i) { if (input[i] >= '0' && input[i] <= '9') code = code * 10 + input[i] - '0'; else { code = -1; break; } } return code; } static void put_response (void) { emo_write (c_out, input, input_length); emo_write (c_out, "\r\n", 2); if (emo_flush (c_out) != 0) write_error (c_out); em_update_status(); } static int copy_response (void) { int code = get_response (); put_response (); return code; } static void reject_article(EMO_FILE *dst) { emo_puts (dst, "Path: localhost\r\n"); emo_puts (dst, "From: \"Firewall content inspection service\" \r\n"); emo_puts (dst, "Subject: Article was rejected by content inspection service\r\n"); emo_puts (dst, "Distribution: local\r\n"); emo_puts (dst, "Lines: 1\r\n"); emo_puts (dst, "\r\n"); emo_puts (dst, "The article does not meet filtering criteria and cannot be displayed\r\n"); } static unsigned long copy_body (EMO_FILE *dst, EMI_FILE *src) { int c; enum { STATE_CR, /* CR */ STATE_CR_LF, /* CR LF */ STATE_DOT, /* CR LF . */ STATE_DOT_CR, /* CR LF . CR */ STATE_DOT_CR_LF, /* CR LF . CR LF */ STATE_OTHER /* everything else */ } state; unsigned long n0; n0 = emi_amount (src, 0); state = STATE_CR_LF; for (;;) { /* We're in state STATE_CR or STATE_CR_LF now. */ do { c = emi_getc (src); if (c == EMI_EOF) break; emo_putc (dst, c); switch (state) { case STATE_CR: if (c == LF) state = STATE_CR_LF; else if (c == CR) state = STATE_CR; else state = STATE_OTHER; break; case STATE_CR_LF: if (c == '.') state = STATE_DOT; else if (c == LF) state = STATE_CR_LF; else if (c == CR) state = STATE_CR; else state = STATE_OTHER; break; case STATE_DOT: if (c == LF) state = STATE_DOT_CR_LF; else if (c == CR) state = STATE_DOT_CR; else state = STATE_OTHER; break; case STATE_DOT_CR: if (c == LF) state = STATE_DOT_CR_LF; else if (c == CR) state = STATE_CR; else state = STATE_OTHER; break; default: syslog (LLEV, "fwtksyserr: internal error"); quit (1); } } while (state != STATE_DOT_CR_LF && state != STATE_OTHER); if (c == EMI_EOF || state == STATE_DOT_CR_LF) break; do { c = emi_getc (src); if (c == EMI_EOF) break; emo_putc (dst, c); } while (c != LF && c != CR); if (c == LF) state = STATE_CR_LF; else state = STATE_CR; } if (c == EMI_EOF) { syslog (LLEV, "%s: unterminated line", cs (src)); quit (1); } if (emo_flush (dst) != 0) write_error (dst); em_update_status(); return emi_amount (src, 0) - n0; } static long copy_inspect_body (EMO_FILE *dst, EMI_FILE *src) { unsigned long n0; char buf[1024]; char *qf; char **rcpt; int tfd; FILE *tf; Cfg* cf; char *from = NULL; n0 = emi_amount (src, 0); qf = xstrdup(QTEMPLATE); if ((tfd = mkstemp(qf)) == -1) { syslog(LLEV,"fwtksyserr: cannot create temporary file"); quit(1); } /* * Parse header */ while(1) { read_line (src,buf,sizeof(buf)); if (!strcmp(buf,".")) goto skipbody; if (!(*buf)) break; if (!from) { if (!strncmp(buf,"From: ",6)) { if (!(from = parse_address(buf+6))) syslog(LLEV,"Malformed From: %s", buf+6); } } puts_qf(tfd,buf); } /* * Continue with body */ while(1) { read_line (src,buf,sizeof(buf)); if (!strcmp(buf,".")) break; puts_qf(tfd,buf); } skipbody: close(tfd); if (!from) from = "unknown"; snprintf(buf,sizeof(buf)-1,"nntp@%.80s",proxy_stats.rladdr); rcpt = xmalloc(sizeof(char*)*2); rcpt[0] = xstrdup(buf); rcpt[1] = NULL; cf=cfg_get("milter", proxy_confp); while (cf != (Cfg *)0) { int mfstate; if((cf->argc < 1) || (cf->argv[0][0] == '-')) { syslog(LLEV,"fwtkcfgerr: missing parameter, line %d",cf->ln); } if (((mfstate = milter_inspect_mail("nntp-gw", proxy_stats.rladdr,proxy_stats.riaddr, cf->argv[0],qf,from,&rcpt)) & MS_ER)) { syslog(LLEV,"fwtksyserr: milter %s failed",cf->argv[0]); quit(1); } if (mfstate & MS_RJ) { if (src == s_in) { reject_article(dst); goto done; } else goto postcleanup; } cf=cfg_get("milter",(Cfg *)0); } if (src == c_in) cf=cfg_get("post-milter", proxy_confp); else if (src == s_in) cf=cfg_get("read-milter", proxy_confp); else { syslog(LLEV,"fwtksyserr: internal logic error: milter called for operation which is not post nor read"); quit(1); } while (cf != (Cfg *)0) { int mfstate; if((cf->argc < 1) || (cf->argv[0][0] == '-')) { syslog(LLEV,"fwtkcfgerr: missing parameter, line %d",cf->ln); } if (((mfstate = milter_inspect_mail("nntp-gw", proxy_stats.rladdr,proxy_stats.riaddr, cf->argv[0],qf,from,&rcpt)) & MS_ER)) { syslog(LLEV,"fwtksyserr: milter %s failed",cf->argv[0]); quit(1); } if (mfstate & MS_RJ) { if (src == s_in) { reject_article(dst); goto done; } else goto postcleanup; } cf=cfg_get("milter",(Cfg *)0); if (src == c_in) cf=cfg_get("post-milter", (Cfg *)0); else cf=cfg_get("read-milter", (Cfg *)0); } if (!(tf = fopen(qf,"r"))) { syslog(LLEV,"fwtksyserr: cannot open quarantine file"); quit(1); } while(fgets(buf,sizeof(buf),tf)) { emo_puts (dst, buf); } fclose(tf); unlink(qf); done: emo_write (dst, ".\r\n", 3); if (emo_flush (dst) != 0) write_error (dst); em_update_status(); free(rcpt[0]); free(rcpt); free(qf); return emi_amount (src, 0) - n0; postcleanup: /* * Posting failed, release data structures */ unlink(qf); free(rcpt[0]); free(rcpt); free(qf); return -1; } static int check_group (const char *group, const char **msg, int *delay) { Cfg *cf = cfg_get ("newsgroups", proxy_confp); int i; size_t len; len = strlen (group); while (cf != NULL) { for (i = 0; i < cf->argc && cf->argv[i][0] != '-'; ++i) { if (lower_match (cf->argv[i], strlen (cf->argv[i]), group, len)) { if (cf->flags & PERM_DENY) goto deny; return 0; } } cf = cfg_get ("newsgroups", (Cfg *)0); } /* permit by default */ return 0; deny: syslog (LLEV, "deny host=%.100s/%.64s newsgroup=%.512s", proxy_stats.rladdr, proxy_stats.riaddr, group); *msg = "411 access denied"; *delay = 0; while (++i < cf->argc) { if (i + 1 < cf->argc && strcmp (cf->argv[i], "-message") == 0) *msg = cf->argv[++i]; else if (i + 1 < cf->argc && strcmp (cf->argv[i], "-delay") == 0) *delay = atoi (cf->argv[++i]); else if (strcmp (cf->argv[i], "-quit") == 0) *delay = -1; } return -1; } /* Get banner from remote server and pass back. */ static void display_banner (void) { int code; code = copy_response (); if (code != 200 && code != 201) quit (1); } /* Get banner from remote server and discard. */ static void discard_banner (void) { char banner[512]; read_line (s_in, banner, sizeof (banner)); if (log_response) syslog (LLEV, "discarded banner: %.512s", input); } /* Open a connection to the server. */ static void open_server (void) { proxy_update_operation("CONNECTING"); if ((server_fd = conn_server (proxy_stats.dst, proxy_stats.port, 0, (char *)0)) < 0) { syslog (LLEV, "cannot connect to server %.512s/%d: %s", proxy_stats.dst, proxy_stats.port, strerror(errno)); respond ("400 cannot connect to server"); exit (1); } if (sdscp) proxy_set_dscp(server_fd,sdscp); s_in = emi_fdopen (server_fd, 4096); s_out = emo_fdopen (server_fd, 4096); if (s_in == NULL || s_out == NULL) { syslog (LLEV, "fdopen() failed: %s", strerror(errno)); exit (1); } emi_timeout (s_in, PROXY_TIMEOUT); emo_timeout (s_out, PROXY_TIMEOUT); /* Scary side-effect time! */ server_contacted = 1; } static int do_default (int flags) { unsigned long n; command (); if (has_body (copy_response ())) { n = copy_body (c_out, s_in); if (log_list_size && (flags & CMD_SIZE)) syslog (LLEV, "%s: bytes=%lu", cmd, n); } return 0; } /* AUTHINFO USER - called from do_auth. */ static int do_auth_user (int flags) { static char server_buf[512]; static char user_buf[512]; int code; size_t sep, start, i, n; /* Check if connected to server already. */ if (server_contacted) { do_default (flags); return 0; } /* Move onto the start of the user name. */ i = cmd_length; while (input[i] != 0 && WHITE (input[i])) ++i; start = i; sep = i - 1; while (input[i] != 0 && !WHITE (input[i])) { if (input[i] == *separator) sep = i; ++i; } /* Get the server name. */ n = i - sep - 1; if (n >= sizeof (server_buf)) { respond ("501 server name too long"); return 0; } memcpy (server_buf, input + sep + 1, n); server_buf[n] = 0; strncpy(proxy_stats.dst,server_buf,sizeof(proxy_stats.dst) - 1); proxy_update_status(); /* Get the user name. */ if (sep > start) { n = sep - start; if (n >= sizeof (user_buf)) { respond ("501 user name too long"); return 0; } memcpy (user_buf, input + start, n); user_buf[n] = 0; user = user_buf; } /* Check allowed to connect to server. */ if (proxy_check_dest(validests,extendperm) || searchlist((char*) user, validusers)) { /* Not allowed to connect to server. */ /* Ask for more auth - will fail on USER (as no server length). */ respond ("381 More authentication information required"); return 0; } /* Do the initial connection to the server. */ open_server (); if (user) { /* Auth - discard the banner from the far end. */ discard_banner (); /* Want to ignore everything from the @ sign. */ input[sep] = 0; input_length = sep; /* Now pass the AUTH command through. */ command (); /* And get the response */ code = copy_response (); if (log_auth) syslog (LLEV, "AUTHINFO USER: code=%d", code); } else { /* No auth - pass the returned banner to the user. */ display_banner (); } return 0; } /* AUTHINFO PASS - called from do_auth. */ static int do_auth_pass (int flags) { int code; if (!server_contacted) /* First check we're connected to the server */ respond ("482 Authentication rejected"); else if (user == NULL) /* Check if need to pass this through */ respond ("281 Authentication accepted"); else { /* Pass the auth command through to the server. */ command (); code = copy_response (); if (log_auth) syslog (LLEV, "AUTHINFO PASS: code=%d", code); } return 0; } /* Hash table of AUTH subcommands. This include file is generated from auth.tab by maketable. */ #include "authinfo.h" /* AUTHINFO */ static int do_auth (int flags) { const char *s = input + cmd_length; const char *e; size_t len; const struct hash_entry *he; int (*handler)(int); while (*s != 0 && WHITE (*s)) ++s; e = s; while (*e != 0 && !WHITE (*e)) ++e; len = e - s; if (len == 0 || len >= sizeof (cmd)) respond ("482 Authentication rejected"); else { upper_copy (cmd, s, len); cmd[len] = 0; cmd_length = e - input; he = find_hash_entry2 (&authinfo_descr, cmd, len); if (he == NULL) respond ("482 Authentication rejected"); else { handler = he->handler; ALWAYS_ASSERT (AUTHINFO_HANDLER (handler)); handler (he->flags); } } return 0; } static void do_null (void) { syslog (LLEV, "empty command"); } static void do_unknown (void) { syslog (LLEV, "unknown command: %.512s", input); respond ("500 command not recognized"); } /* ARTICLE, BODY, and HEAD. */ /* XXX - we need special handling for BODY and HEAD */ static int do_article (int flags) { command (); if (has_body (copy_response ())) { long n; if ((n = copy_inspect_body (c_out, s_in)) == -1L) { syslog(LLEV,"fwtksyserr: internal logic error: invalid exit code on copy_inspect_body()"); quit(1); } group_bytes += n; ++group_articles; } return 0; } /* GROUP */ static int do_group (int flags) { const char *s = input + cmd_length; const char *e, *msg; size_t len; int delay; while (*s != 0 && WHITE (*s)) ++s; e = s; while (*e != 0 && !WHITE (*e)) ++e; len = e - s; if (*e != 0 || len >= sizeof (group_name)) respond ("501 command syntax error"); else { log_group (); memcpy (group_name, s, len); group_name[len] = 0; group_bytes = 0; group_articles = 0; if (check_group (group_name, &msg, &delay) != 0) { respond (msg); if (delay > 0) sleep (delay); else if (delay < 0) quit (1); } else do_default (flags); } return 0; } /* Hash table of NNTP extensions (for LIST EXTENSIONS). This include file is generated from ext.tab by maketable. */ #include "ext.h" /* LIST EXTENSIONS */ static int do_list_extensions (int flags) { const struct hash_entry *he; const char *s; size_t len; int code; command (); code = copy_response (); if (code == 202 || code == 215) { for (;;) { input_length = read_line (s_in, input, sizeof (input)); if (strcmp (input, ".") == 0) break; s = input; len = input_length; if (*s == ' ') { ++s; --len; } if (len >= sizeof (cmd)) he = NULL; else { upper_copy (cmd, s, len); cmd[len] = 0; he = find_hash_entry2 (&ext_descr, cmd, len); } if (he == NULL) syslog (LLEV, "unknown extension: %.512s", input); else if (he->flags != 0) { emo_write (c_out, input, input_length); emo_write (c_out, "\r\n", 2); } } respond ("."); } return 0; } /* Hash table of LIST subcommands. This include file is generated from list.tab by maketable. */ #include "list.h" /* LIST */ static int do_list (int flags) { const char *s = input + cmd_length; const char *e; size_t len; const struct hash_entry *he; int (*handler)(int); while (*s != 0 && WHITE (*s)) ++s; e = s; while (*e != 0 && !WHITE (*e)) ++e; len = e - s; if (len == 0) do_default (flags); /* LIST ACTIVE */ else { if (len >= sizeof (cmd)) do_unknown (); else { upper_copy (cmd, s, len); cmd[len] = 0; cmd_length = e - input; he = find_hash_entry2 (&list_descr, cmd, len); if (he == NULL) do_unknown (); else { handler = he->handler; ALWAYS_ASSERT (LIST_HANDLER (handler)); handler (he->flags); } } } return 0; } /* LISTGROUP */ static int do_listgroup (int flags) { int code; unsigned long n; command (); code = copy_response (); if (code == 211 || has_body (code)) { n = copy_body (c_out, s_in); if (log_list_size) syslog (LLEV, "LISTGROUP: bytes=%lu", n); } return 0; } /* POST and IHAVE */ static int do_post (int flags) { long n; int code; int is_ihave; is_ihave = !strcasecmp(input,"ihave"); command (); code = copy_response (); if (code == 335 || code == 340) { if ((n = copy_inspect_body (s_out, c_in)) == -1L) { if (is_ihave) strlcpy(input,"437 article rejected",sizeof(input)); else strlcpy(input,"441 posting failed",sizeof(input)); input_length = strlen(input); put_response(); return 0; } code = copy_response (); if (log_post) { if (code == 235 || code == 240) syslog (LLEV, "%s: bytes=%lu", cmd, n); else syslog (LLEV, "%s: error=%d bytes=%lu", cmd, code, n); } } else if (log_post) syslog (LLEV, "%s: error=%d", cmd, code); return 0; } /* QUIT */ static int do_quit (int flags) { if (server_contacted) do_default (flags); quit (0); return 0; } /* Specific configuration for nntp-gw. */ static void config_nntp_gw () { Cfg *cf; int i; if ((cf = cfg_get ("server", proxy_confp)) != NULL) { if (cf->argc != 1 && cf->argc != 2) { syslog (LLEV, "fwtkcfgerr: server must have one or two parameters, " "line %d", cf->ln); exit (1); } strlcpy(proxy_stats.dst,cf->argv[0],sizeof(proxy_stats.dst) - 1); proxy_update_status(); if (cf->argc == 2) proxy_stats.port = str_to_port (cf->argv[1]); } if ((cf = cfg_get ("log", proxy_confp)) != NULL) for (i = 0; i < cf->argc; ++i) { if (strcmp (cf->argv[i], "command") == 0) log_command = 1; else if (strcmp (cf->argv[i], "response") == 0) log_response = 1; else if (strcmp (cf->argv[i], "list-size") == 0) log_list_size = 1; else if (strcmp (cf->argv[i], "article-summary") == 0) log_article_summary = 1; else if (strcmp (cf->argv[i], "post") == 0) log_post = 1; else if (strcmp (cf->argv[i], "auth") == 0) log_auth = 1; else { syslog (LLEV, "fwtkcfgerr: invalid parameter for log, line %d", cf->ln); exit (1); } } } /* Catch SIGPIPE. SIGPIPE is generated when writing to a closed pipe or socket. */ static void catch_sigpipe (int signo) { syslog (LLEV, "attempt to write to closed connection"); quit (1); } /* Hash table of NNTP commands. This include file is generated from command.tab by maketable. */ #include "command.h" /* Read commands from the client and pass them to the server. */ static void cmd_loop (void) { size_t len; const struct hash_entry *he; int (*handler)(int); for (;;) { proxy_update_operation("IDLE"); input_length = read_line (c_in, input, sizeof (input)); len = 0; while (input[len] != 0 && !WHITE (input[len])) ++len; if (len >= sizeof (cmd)) do_unknown (); else if (len == 0) do_null (); else { upper_copy (cmd, input, len); cmd[len] = 0; cmd_length = len; proxy_update_operation(cmd); he = find_hash_entry2 (&command_descr, cmd, len); if (he == NULL) do_unknown (); else if (!server_contacted && !(he->flags & CMD_NOAUTH)) respond ("450 Authorization required for this command"); else { handler = he->handler; ALWAYS_ASSERT (COMMAND_HANDLER (handler)); handler (he->flags); } } } } /* This is where nntp-gw starts. */ int main (int argc, char *argv[]) { Cfg* cf; if (argc >= 2 && strcmp (argv[1], "-check") == 0) { check_snprintf (0); exit (0); } proxy_stats.port = SERVER_PORT; proxy_init(argc,argv); proxy_chroot_setugid(); config_nntp_gw (); if ((cf = proxy_conf_hosts (proxy_confp, proxy_stats.rladdr, proxy_stats.riaddr)) == NULL) exit (1); else proxy_parse_options(cf,options); if (cdscp) proxy_set_dscp(0,cdscp); if (transparent) proxy_get_transparent_dst(proxy_stats.dst,&proxy_stats.port); c_in = emi_fdopen (0, 4096); c_out = emo_fdopen (0, 4096); if (c_in == NULL || c_out == NULL) { syslog (LLEV, "fdopen() failed: %s", strerror(errno)); exit (1); } emi_timeout (c_in, PROXY_TIMEOUT); emo_timeout (c_out, PROXY_TIMEOUT); /* Install signal handler. */ signal (SIGPIPE, catch_sigpipe); if (*proxy_stats.dst) { /* Config has "server" parameter - so we want to connect now. */ open_server (); display_banner (); } else { /* Will connect when the user sends AUTHINFO. */ respond ("200 Hello you can post or not"); } /* Now sit there processing commands. */ cmd_loop (); return 0; }