/* MiddleMan filtering proxy server Copyright (C) 2002 Jason McLaughlin This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "proto.h" SOCKET *sock_new(int fd) { SOCKET *ret; ret = xmalloc(sizeof(SOCKET)); ret->fd = fd; ret->frozen = FALSE; ret->inbuf_len = ret->outbuf_len = 0; ret->inbuf_reallen = ret->outbuf_reallen = 0; ret->inbuf = ret->outbuf = NULL; return ret; } void sock_close(SOCKET * sock) { sock_flush(sock); close(sock->fd); FREE_AND_NULL(sock->inbuf); FREE_AND_NULL(sock->outbuf); xfree(sock); } int sock_read(SOCKET * sock, void *buf, int len) { int pos = 0, x; char b[BLOCKSIZE]; if (sock->inbuf_len != 0 && len != -1) { memcpy(buf, sock->inbuf, (sock->inbuf_len < len) ? sock->inbuf_len : len); if (sock->inbuf_len <= len) { /* all buffered data used up, clear it */ pos = sock->inbuf_len; sock_clear(sock, IN); /* buffer is exact size of requested read size, no need to do any network IO */ if (pos == len) return len; } else { /* buffer larger than requested read size, no need to do any network IO */ /* chop off used part of buffer */ memcpy(sock->inbuf, sock->inbuf + len, sock->inbuf_len - len); sock_resize(sock, sock->inbuf_len - len, IN); return len; } } x = read(sock->fd, b, BLOCKSIZE); if (x > 0) { if (len == -1) { /* just buffer */ sock_resize(sock, sock->inbuf_len + x, IN); memcpy(sock->inbuf + sock->inbuf_len - x, b, x); } else { memcpy(buf + pos, b, (x > len - pos) ? len - pos : x); if (x > len - pos) { /* buffer the rest */ sock_resize(sock, x - (len - pos), IN); memcpy(sock->inbuf, &b[len - pos], x - (len - pos)); } pos += (x > len - pos) ? len - pos : x; } } return (pos != 0) ? pos : x; } int sock_write(SOCKET * sock, void *buf, int len) { int x = 0, tmp; if (sock->outbuf_len != 0 && sock->frozen == FALSE) { x = write(sock->fd, sock->outbuf, sock->outbuf_len); if (x > 0) { if (x == sock->outbuf_len) { /* successfully wrote everything */ sock_clear(sock, OUT); } else { /* partial write */ memcpy(sock->outbuf, sock->outbuf + x, sock->outbuf_len - x); sock_resize(sock, sock->outbuf_len - x, OUT); } } } if (len != -1) { x = 0; if (sock->outbuf_len == 0 && sock->frozen == FALSE) x = write(sock->fd, buf, len); if (x != len) { /* buffer data that couldn't be written immediately */ tmp = sock->outbuf_len; sock_resize(sock, sock->outbuf_len + len - ((x != -1) ? x : 0), OUT); memcpy(sock->outbuf + tmp, buf + ((x != -1) ? x : 0), len - ((x != -1) ? x : 0)); } } return x; } int sock_getline(SOCKET * sock, char *buf, int len) { int x = 0, pos = 0; void *ptr = NULL; do { if (sock->inbuf_len != 0 && ((ptr = memchr(sock->inbuf + pos, '\n', sock->inbuf_len - pos)))) { /* if len is -1, just discard the line */ if (len != -1) { x = ((ptr - sock->inbuf) + 1 < len) ? (ptr - sock->inbuf) + 1 : len; memcpy(buf, sock->inbuf, x); buf[(x != len) ? x : x - 1] = '\0'; } if (ptr - sock->inbuf == sock->inbuf_len || ptr - sock->inbuf + 1 == sock->inbuf_len) { /* used up all buffered data */ sock_clear(sock, IN); } else { /* remove entire line from buffer, even if it's larger than requested read size */ memcpy(sock->inbuf, ptr + 1, sock->inbuf_len - (ptr - sock->inbuf) - 1); sock_resize(sock, sock->inbuf_len - (ptr - sock->inbuf) - 1, IN); } break; } else pos = sock->inbuf_len; } while ((x = sock_read(sock, NULL, -1)) > 0); return x; } void sock_flush(SOCKET * sock) { sock->frozen = FALSE; while (sock->outbuf_len > 0 && sock_write(sock, NULL, -1) != -1); sock_resize(sock, 0, OUT); sock_resize(sock, 0, IN); } void sock_resize(SOCKET * sock, int newsize, int which) { if (which == IN) { if (sock->inbuf != NULL && sock->inbuf_reallen >= newsize && sock->inbuf_reallen < newsize + FILEBUF_ALIGNMENT) sock->inbuf_len = newsize; else if (newsize == 0) sock_clear(sock, IN); else { sock->inbuf_reallen = ALIGN2(newsize, FILEBUF_ALIGNMENT); sock->inbuf = xrealloc(sock->inbuf, sock->inbuf_reallen); sock->inbuf_len = newsize; } } else { if (sock->outbuf != NULL && sock->outbuf_reallen >= newsize && sock->outbuf_reallen < newsize + FILEBUF_ALIGNMENT) sock->outbuf_len = newsize; else if (newsize == 0) sock_clear(sock, OUT); else { sock->outbuf_reallen = ALIGN2(newsize, FILEBUF_ALIGNMENT); sock->outbuf = xrealloc(sock->outbuf, sock->outbuf_reallen); sock->outbuf_len = newsize; } } } void sock_clear(SOCKET * sock, int which) { if (which == IN) { if (sock->inbuf_reallen > FILEBUF_ALIGNMENT) { FREE_AND_NULL(sock->inbuf); sock->inbuf_reallen = 0; } sock->inbuf_len = 0; } else { if (sock->outbuf_reallen > FILEBUF_ALIGNMENT) { FREE_AND_NULL(sock->outbuf); sock->outbuf_reallen = 0; } sock->outbuf_len = 0; } }