This is a discussion on Re: ftp-proxy diff, please test within the mailing.openbsd.tech forums, part of the OpenBSD category; --> Anyone testing this one? I'd like at least two reports. Thanks. On Fri, 8 Dec 2006, Camiel Dobbelaar wrote: ...
| |||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| Anyone testing this one? I'd like at least two reports. Thanks. On Fri, 8 Dec 2006, Camiel Dobbelaar wrote: > This diff makes ftp-proxy use the readline function from libevent, so the > custom getline() function and the two static buffers per session can all > be done away with. > > Keep an eye on the ftp-proxy process to see if it does not grow slowly > (libevent readline is malloc/free based). > > Thanks. > > > Index: ftp-proxy.c > ================================================== ================= > RCS file: /cvs/src/usr.sbin/ftp-proxy/ftp-proxy.c,v > retrieving revision 1.10 > diff -u -p -r1.10 ftp-proxy.c > --- ftp-proxy.c 15 Oct 2006 18:23:44 -0000 1.10 > +++ ftp-proxy.c 7 Dec 2006 12:16:41 -0000 > @@ -51,6 +51,7 @@ > #define NTOP_BUFS 3 > #define TCP_BACKLOG 10 > > +#define ANON_ONLY_MSG "500 Only anonymous FTP allowed" > #define CHROOT_DIR "/var/empty" > #define NOPRIV_USER "proxy" > > @@ -72,10 +73,6 @@ struct session { > struct bufferevent *server_bufev; > int client_fd; > int server_fd; > - char cbuf[MAX_LINE]; > - size_t cbuf_valid; > - char sbuf[MAX_LINE]; > - size_t sbuf_valid; > int cmd; > u_int16_t port; > u_int16_t proxy_port; > @@ -85,32 +82,30 @@ struct session { > LIST_HEAD(, session) sessions = LIST_HEAD_INITIALIZER(sessions); > > void client_error(struct bufferevent *, short, void *); > -int client_parse(struct session *s); > -int client_parse_anon(struct session *s); > -int client_parse_cmd(struct session *s); > +int client_parse(struct session *s, char **); > +int client_parse_anon(struct session *s, char **); > +int client_parse_cmd(struct session *s, char **); > void client_read(struct bufferevent *, void *); > +int client_send(struct session *, char *); > void client_write(struct bufferevent *, void *); > int drop_privs(void); > void end_session(struct session *); > int exit_daemon(void); > -int getline(char *, size_t *); > void handle_connection(const int, short, void *); > void handle_signal(int, short, void *); > struct session * init_session(void); > void logmsg(int, const char *, ...); > -u_int16_t parse_port(int); > +u_int16_t parse_port(int, char *); > u_int16_t pick_proxy_port(void); > -void proxy_reply(int, struct sockaddr *, u_int16_t); > +char *proxy_reply(int, struct sockaddr *, u_int16_t); > void server_error(struct bufferevent *, short, void *); > -int server_parse(struct session *s); > +int server_parse(struct session *s, char **); > void server_read(struct bufferevent *, void *); > +int server_send(struct session *, char *); > void server_write(struct bufferevent *, void *); > const char *sock_ntop(struct sockaddr *); > void usage(void); > > -char linebuf[MAX_LINE + 1]; > -size_t linelen; > - > char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN]; > > struct sockaddr_storage fixed_server_ss, fixed_proxy_ss; > @@ -140,57 +135,48 @@ client_error(struct bufferevent *bufev, > } > > int > -client_parse(struct session *s) > +client_parse(struct session *s, char **line) > { > /* Reset any previous command. */ > s->cmd = CMD_NONE; > s->port = 0; > > - /* Commands we are looking for are at least 4 chars long. */ > - if (linelen < 4) > - return (1); > - > - if (linebuf[0] == 'P' || linebuf[0] == 'p' || > - linebuf[0] == 'E' || linebuf[0] == 'e') > - return (client_parse_cmd(s)); > + if (*line[0] == 'P' || *line[0] == 'p' || > + *line[0] == 'E' || *line[0] == 'e') > + return (client_parse_cmd(s, line)); > > - if (anonymous_only && (linebuf[0] == 'U' || linebuf[0] == 'u')) > - return (client_parse_anon(s)); > + if (anonymous_only && (*line[0] == 'U' || *line[0] == 'u')) > + return (client_parse_anon(s, line)); > > return (1); > } > > int > -client_parse_anon(struct session *s) > +client_parse_anon(struct session *s, char **line) > { > - if (strcasecmp("USER ftp\r\n", linebuf) != 0 && > - strcasecmp("USER anonymous\r\n", linebuf) != 0) { > - snprintf(linebuf, sizeof linebuf, > - "500 Only anonymous FTP allowed\r\n"); > - logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); > + if (strcasecmp("USER ftp", *line) != 0 && > + strcasecmp("USER anonymous", *line) != 0) { > + /* Clear line so it's not sent to the server. */ > + free(*line); > + *line = NULL; > > /* Talk back to the client ourself. */ > - linelen = strlen(linebuf); > - bufferevent_write(s->client_bufev, linebuf, linelen); > - > - /* Clear buffer so it's not sent to the server. */ > - linebuf[0] = '\0'; > - linelen = 0; > + logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, ANON_ONLY_MSG); > + return (client_send(s, ANON_ONLY_MSG "\r\n")); > } > - > return (1); > } > > int > -client_parse_cmd(struct session *s) > +client_parse_cmd(struct session *s, char **line) > { > - if (strncasecmp("PASV", linebuf, 4) == 0) > + if (strncasecmp("PASV", *line, 4) == 0) > s->cmd = CMD_PASV; > - else if (strncasecmp("PORT ", linebuf, 5) == 0) > + else if (strncasecmp("PORT ", *line, 5) == 0) > s->cmd = CMD_PORT; > - else if (strncasecmp("EPSV", linebuf, 4) == 0) > + else if (strncasecmp("EPSV", *line, 4) == 0) > s->cmd = CMD_EPSV; > - else if (strncasecmp("EPRT ", linebuf, 5) == 0) > + else if (strncasecmp("EPRT ", *line, 5) == 0) > s->cmd = CMD_EPRT; > else > return (1); > @@ -201,15 +187,22 @@ client_parse_cmd(struct session *s) > } > > if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) { > - s->port = parse_port(s->cmd); > + s->port = parse_port(s->cmd, *line); > if (s->port < MIN_PORT) { > - logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, > - linebuf); > + logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, *line); > return (0); > } > s->proxy_port = pick_proxy_port(); > - proxy_reply(s->cmd, sstosa(&s->proxy_ss), s->proxy_port); > - logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); > + > + /* Rewrite the client command. */ > + free(*line); > + *line = proxy_reply(s->cmd, sstosa(&s->proxy_ss), > + s->proxy_port); > + if (*line == NULL) { > + logmsg(LOG_ERR, "#%d client proxy_reply failed", s->id); > + return (0); > + } > + logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, *line); > } > > return (1); > @@ -219,31 +212,45 @@ void > client_read(struct bufferevent *bufev, void *arg) > { > struct session *s = arg; > - size_t buf_avail, read; > - int n; > + size_t linelen; > + char *line; > > - do { > - buf_avail = sizeof s->cbuf - s->cbuf_valid; > - read = bufferevent_read(bufev, s->cbuf + s->cbuf_valid, > - buf_avail); > - s->cbuf_valid += read; > - > - while ((n = getline(s->cbuf, &s->cbuf_valid)) > 0) { > - logmsg(LOG_DEBUG, "#%d client: %s", s->id, linebuf); > - if (!client_parse(s)) { > - end_session(s); > - return; > - } > - bufferevent_write(s->server_bufev, linebuf, linelen); > + while ((line = evbuffer_readline(bufev->input))) { > + linelen = strlen(line); > + if (linelen > MAX_LINE) { > + logmsg(LOG_ERR, "#%d client command too long", s->id); > + goto fail; > } > + logmsg(LOG_DEBUG, "#%d client: %s", s->id, line); > > - if (n == -1) { > - logmsg(LOG_ERR, "#%d client command too long or not" > - " clean", s->id); > - end_session(s); > - return; > - } > - } while (read == buf_avail); > + if (linelen > 0 && !client_parse(s, &line)) > + goto fail; > + > + /* Send line to server; client_parse() may have changed it. */ > + if (!server_send(s, line)) > + goto fail; > + > + free(line); > + } > + return; > + fail: > + free(line); > + end_session(s); > +} > + > +int > +client_send(struct session *s, char *line) > +{ > + if (evbuffer_add_printf(s->client_bufev->output, "%s\r\n", line) < 0) { > + logmsg(LOG_ERR, "#%d client_send printf failed", s->id); > + return (0); > + } > + if (event_add(&s->client_bufev->ev_write, NULL) < 0) { > + logmsg(LOG_ERR, " #%d client_send event_add failed: %s", > + s->id, strerror(errno)); > + return (0); > + } > + return (1); > } > > void > @@ -324,43 +331,6 @@ exit_daemon(void) > return (-1); > } > > -int > -getline(char *buf, size_t *valid) > -{ > - size_t i; > - > - if (*valid > MAX_LINE) > - return (-1); > - > - /* Copy to linebuf while searching for a newline. */ > - for (i = 0; i < *valid; i++) { > - linebuf[i] = buf[i]; > - if (buf[i] == '\0') > - return (-1); > - if (buf[i] == '\n') > - break; > - } > - > - if (i == *valid) { > - /* No newline found. */ > - linebuf[0] = '\0'; > - linelen = 0; > - if (i < MAX_LINE) > - return (0); > - return (-1); > - } > - > - linelen = i + 1; > - linebuf[linelen] = '\0'; > - *valid -= linelen; > - > - /* Move leftovers to the start. */ > - if (*valid != 0) > - bcopy(buf + linelen, buf, *valid); > - > - return ((int)linelen); > -} > - > void > handle_connection(const int listen_fd, short event, void *ev) > { > @@ -533,10 +503,6 @@ init_session(void) > s->id = id_count++; > s->client_fd = -1; > s->server_fd = -1; > - s->cbuf[0] = '\0'; > - s->cbuf_valid = 0; > - s->sbuf[0] = '\0'; > - s->sbuf_valid = 0; > s->client_bufev = NULL; > s->server_bufev = NULL; > s->cmd = CMD_NONE; > @@ -767,17 +733,17 @@ main(int argc, char *argv[]) > } > > u_int16_t > -parse_port(int mode) > +parse_port(int mode, char *line) > { > unsigned int port, v[6]; > int n; > char *p; > > /* Find the last space or left-parenthesis. */ > - for (p = linebuf + linelen; p > linebuf; p--) > + for (p = line + strlen(line); p > line; p--) > if (*p == ' ' || *p == '(') > break; > - if (p == linebuf) > + if (p == line) > return (0); > > switch (mode) { > @@ -825,50 +791,46 @@ pick_proxy_port(void) > (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO))); > } > > -void > +char * > proxy_reply(int cmd, struct sockaddr *sa, u_int16_t port) > { > int i, r; > + char *reply; > > switch (cmd) { > case CMD_PORT: > - r = snprintf(linebuf, sizeof linebuf, > - "PORT %s,%u,%u\r\n", sock_ntop(sa), port / 256, > - port % 256); > + r = asprintf(&reply, "PORT %s,%u,%u", sock_ntop(sa), > + port / 256, port % 256); > break; > case CMD_PASV: > - r = snprintf(linebuf, sizeof linebuf, > - "227 Entering Passive Mode (%s,%u,%u)\r\n", sock_ntop(sa), > - port / 256, port % 256); > + r = asprintf(&reply, "227 Entering Passive Mode (%s,%u,%u)", > + sock_ntop(sa), port / 256, port % 256); > break; > case CMD_EPRT: > if (sa->sa_family == AF_INET) > - r = snprintf(linebuf, sizeof linebuf, > - "EPRT |1|%s|%u|\r\n", sock_ntop(sa), port); > + r = asprintf(&reply, "EPRT |1|%s|%u|", sock_ntop(sa), > + port); > else if (sa->sa_family == AF_INET6) > - r = snprintf(linebuf, sizeof linebuf, > - "EPRT |2|%s|%u|\r\n", sock_ntop(sa), port); > + r = asprintf(&reply, "EPRT |2|%s|%u|", sock_ntop(sa), > + port); > break; > case CMD_EPSV: > - r = snprintf(linebuf, sizeof linebuf, > - "229 Entering Extended Passive Mode (|||%u|)\r\n", port); > + r = asprintf(&reply, > + "229 Entering Extended Passive Mode (|||%u|)", port); > break; > } > > - if (r < 0 || r >= sizeof linebuf) { > - logmsg(LOG_ERR, "proxy_reply failed: %d", r); > - linebuf[0] = '\0'; > - linelen = 0; > - return; > - } > - linelen = (size_t)r; > + if (r < 0) > + return (NULL); > > if (cmd == CMD_PORT || cmd == CMD_PASV) { > /* Replace dots in IP address with commas. */ > - for (i = 0; i < linelen; i++) > - if (linebuf[i] == '.') > - linebuf[i] = ','; > + for (i = 0; i < r; i++) > + if (reply[i] == '.') > + reply[i] = ','; > } > + > + return (reply); > } > > void > @@ -891,12 +853,12 @@ server_error(struct bufferevent *bufev, > } > > int > -server_parse(struct session *s) > +server_parse(struct session *s, char **line) > { > struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa; > int prepared = 0; > > - if (s->cmd == CMD_NONE || linelen < 4 || linebuf[0] != '2') > + if (s->cmd == CMD_NONE || *line[0] != '2') > goto out; > > /* > @@ -923,25 +885,30 @@ server_parse(struct session *s) > orig_sa = sstosa(&s->server_ss); > > /* Passive modes. */ > - if ((s->cmd == CMD_PASV && strncmp("227 ", linebuf, 4) == 0) || > - (s->cmd == CMD_EPSV && strncmp("229 ", linebuf, 4) == 0)) { > - s->port = parse_port(s->cmd); > + if ((s->cmd == CMD_PASV && strncmp("227 ", *line, 4) == 0) || > + (s->cmd == CMD_EPSV && strncmp("229 ", *line, 4) == 0)) { > + s->port = parse_port(s->cmd, *line); > if (s->port < MIN_PORT) { > - logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, > - linebuf); > + logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, *line); > return (0); > } > s->proxy_port = pick_proxy_port(); > logmsg(LOG_INFO, "#%d passive: client to server port %d" > " via port %d", s->id, s->port, s->proxy_port); > > + /* Rewrite the server reply. */ > + free(*line); > + *line = proxy_reply(s->cmd, orig_sa, s->proxy_port); > + if (*line == NULL) { > + logmsg(LOG_ERR, "#%d server proxy_reply failed", s->id); > + return (0); > + } > + logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, *line); > + > if (prepare_commit(s->id) == -1) > goto fail; > prepared = 1; > > - proxy_reply(s->cmd, orig_sa, s->proxy_port); > - logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); > - > /* rdr from $client to $orig_server port $proxy_port -> $server > port $port */ > if (add_rdr(s->id, client_sa, orig_sa, s->proxy_port, > @@ -966,7 +933,7 @@ server_parse(struct session *s) > > /* Active modes. */ > if ((s->cmd == CMD_PORT || s->cmd == CMD_EPRT) && > - strncmp("200 ", linebuf, 4) == 0) { > + strncmp("200 ", *line, 4) == 0) { > logmsg(LOG_INFO, "#%d active: server to client port %d" > " via port %d", s->id, s->port, s->proxy_port); > > @@ -1033,33 +1000,47 @@ void > server_read(struct bufferevent *bufev, void *arg) > { > struct session *s = arg; > - size_t buf_avail, read; > - int n; > + size_t linelen; > + char *line; > > bufferevent_settimeout(bufev, timeout, 0); > > - do { > - buf_avail = sizeof s->sbuf - s->sbuf_valid; > - read = bufferevent_read(bufev, s->sbuf + s->sbuf_valid, > - buf_avail); > - s->sbuf_valid += read; > - > - while ((n = getline(s->sbuf, &s->sbuf_valid)) > 0) { > - logmsg(LOG_DEBUG, "#%d server: %s", s->id, linebuf); > - if (!server_parse(s)) { > - end_session(s); > - return; > - } > - bufferevent_write(s->client_bufev, linebuf, linelen); > + while ((line = evbuffer_readline(bufev->input))) { > + linelen = strlen(line); > + if (linelen > MAX_LINE) { > + logmsg(LOG_ERR, "#%d server reply too long", s->id); > + goto fail; > } > + logmsg(LOG_DEBUG, "#%d server: %s", s->id, line); > > - if (n == -1) { > - logmsg(LOG_ERR, "#%d server reply too long or not" > - " clean", s->id); > - end_session(s); > - return; > - } > - } while (read == buf_avail); > + if (linelen > 0 && !server_parse(s, &line)) > + goto fail; > + > + /* Send line to client; server_parse() may have changed it. */ > + if (!client_send(s, line)) > + goto fail; > + > + free(line); > + } > + return; > + fail: > + free(line); > + end_session(s); > +} > + > +int > +server_send(struct session *s, char *line) > +{ > + if (evbuffer_add_printf(s->server_bufev->output, "%s\r\n", line) < 0) { > + logmsg(LOG_ERR, "#%d server_send printf failed", s->id); > + return (0); > + } > + if (event_add(&s->server_bufev->ev_write, NULL) < 0) { > + logmsg(LOG_ERR, " #%d server_send event_add failed: %s", > + s->id, strerror(errno)); > + return (0); > + } > + return (1); > } > > void |