Removed threading
[middleman.git] / src / socket.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include "proto.h"
25
26 SOCKET *sock_new(int fd)
27 {
28         SOCKET *ret;
29
30         ret = xmalloc(sizeof(SOCKET));
31
32         ret->fd = fd;
33         ret->frozen = FALSE;
34         ret->inbuf_len = ret->outbuf_len = 0;
35         ret->inbuf_reallen = ret->outbuf_reallen = 0;
36         ret->inbuf = ret->outbuf = NULL;
37
38         return ret;
39 }
40
41 void sock_close(SOCKET * sock)
42 {
43         sock_flush(sock);
44
45         close(sock->fd);
46
47         FREE_AND_NULL(sock->inbuf);
48         FREE_AND_NULL(sock->outbuf);
49
50         xfree(sock);
51 }
52
53 int sock_read(SOCKET * sock, void *buf, int len)
54 {
55         int pos = 0, x;
56         char b[BLOCKSIZE];
57
58         if (sock->inbuf_len != 0 && len != -1) {
59                 memcpy(buf, sock->inbuf, (sock->inbuf_len < len) ? sock->inbuf_len : len);
60                 if (sock->inbuf_len <= len) {
61                         /* all buffered data used up, clear it */
62                         pos = sock->inbuf_len;
63
64                         sock_clear(sock, IN);
65
66                         /* buffer is exact size of requested read size, no need to do any network IO */
67                         if (pos == len)
68                                 return len;
69                 } else {
70                         /* buffer larger than requested read size, no need to do any network IO */
71                         /* chop off used part of buffer */
72                         memcpy(sock->inbuf, sock->inbuf + len, sock->inbuf_len - len);
73                         sock_resize(sock, sock->inbuf_len - len, IN);
74
75                         return len;
76                 }
77         }
78
79         x = read(sock->fd, b, BLOCKSIZE);
80         if (x > 0) {
81                 if (len == -1) {
82                         /* just buffer */
83                         sock_resize(sock, sock->inbuf_len + x, IN);
84                         memcpy(sock->inbuf + sock->inbuf_len - x, b, x);
85                 } else {
86                         memcpy(buf + pos, b, (x > len - pos) ? len - pos : x);
87                         if (x > len - pos) {
88                                 /* buffer the rest */
89                                 sock_resize(sock, x - (len - pos), IN);
90                                 memcpy(sock->inbuf, &b[len - pos], x - (len - pos));
91                         }
92
93                         pos += (x > len - pos) ? len - pos : x;
94                 }
95         }
96
97         return (pos != 0) ? pos : x;
98 }
99
100 int sock_write(SOCKET * sock, void *buf, int len)
101 {
102         int x = 0, tmp;
103
104         if (sock->outbuf_len != 0 && sock->frozen == FALSE) {
105                 x = write(sock->fd, sock->outbuf, sock->outbuf_len);
106                 if (x > 0) {
107                         if (x == sock->outbuf_len) {
108                                 /* successfully wrote everything */
109                                 sock_clear(sock, OUT);
110                         } else {
111                                 /* partial write */
112                                 memcpy(sock->outbuf, sock->outbuf + x, sock->outbuf_len - x);
113                                 sock_resize(sock, sock->outbuf_len - x, OUT);
114                         }
115                 }
116         }
117
118         if (len != -1) {
119                 x = 0;
120
121                 if (sock->outbuf_len == 0 && sock->frozen == FALSE)
122                         x = write(sock->fd, buf, len);
123                 if (x != len) {
124                         /* buffer data that couldn't be written immediately */
125                         tmp = sock->outbuf_len;
126                         sock_resize(sock, sock->outbuf_len + len - ((x != -1) ? x : 0), OUT);
127                         memcpy(sock->outbuf + tmp, buf + ((x != -1) ? x : 0), len - ((x != -1) ? x : 0));
128                 }
129         }
130
131         return x;
132 }
133
134 int sock_getline(SOCKET * sock, char *buf, int len)
135 {
136         int x = 0, pos = 0;
137         void *ptr = NULL;
138
139         do {
140                 if (sock->inbuf_len != 0 && ((ptr = memchr(sock->inbuf + pos, '\n', sock->inbuf_len - pos)))) {
141                         /* if len is -1, just discard the line */
142                         if (len != -1) {
143                                 x = ((ptr - sock->inbuf) + 1 < len) ? (ptr - sock->inbuf) + 1 : len;
144
145                                 memcpy(buf, sock->inbuf, x);
146                                 buf[(x != len) ? x : x - 1] = '\0';
147                         }
148
149                         if (ptr - sock->inbuf == sock->inbuf_len || ptr - sock->inbuf + 1 == sock->inbuf_len) {
150                                 /* used up all buffered data */
151                                 sock_clear(sock, IN);
152                         } else {
153                                 /* remove entire line from buffer, even if it's larger than requested read size */
154                                 memcpy(sock->inbuf, ptr + 1, sock->inbuf_len - (ptr - sock->inbuf) - 1);
155                                 sock_resize(sock, sock->inbuf_len - (ptr - sock->inbuf) - 1, IN);
156                         }
157
158                         break;
159                 } else
160                         pos = sock->inbuf_len;
161         } while ((x = sock_read(sock, NULL, -1)) > 0);
162
163         return x;
164 }
165
166 void sock_flush(SOCKET * sock)
167 {
168         sock->frozen = FALSE;
169
170         while (sock->outbuf_len > 0 && sock_write(sock, NULL, -1) != -1);
171
172         sock_resize(sock, 0, OUT);
173         sock_resize(sock, 0, IN);
174 }
175
176 void sock_resize(SOCKET * sock, int newsize, int which)
177 {
178         if (which == IN) {
179                 if (sock->inbuf != NULL && sock->inbuf_reallen >= newsize && sock->inbuf_reallen < newsize + FILEBUF_ALIGNMENT)
180                         sock->inbuf_len = newsize;
181                 else if (newsize == 0)
182                         sock_clear(sock, IN);
183                 else {
184                         sock->inbuf_reallen = ALIGN2(newsize, FILEBUF_ALIGNMENT);
185                         sock->inbuf = xrealloc(sock->inbuf, sock->inbuf_reallen);
186                         sock->inbuf_len = newsize;
187                 }
188         } else {
189                 if (sock->outbuf != NULL && sock->outbuf_reallen >= newsize && sock->outbuf_reallen < newsize + FILEBUF_ALIGNMENT)
190                         sock->outbuf_len = newsize;
191                 else if (newsize == 0)
192                         sock_clear(sock, OUT);
193                 else {
194                         sock->outbuf_reallen = ALIGN2(newsize, FILEBUF_ALIGNMENT);
195                         sock->outbuf = xrealloc(sock->outbuf, sock->outbuf_reallen);
196                         sock->outbuf_len = newsize;
197                 }
198         }
199 }
200
201 void sock_clear(SOCKET * sock, int which)
202 {
203         if (which == IN) {
204                 if (sock->inbuf_reallen > FILEBUF_ALIGNMENT) {
205                         FREE_AND_NULL(sock->inbuf);
206                         sock->inbuf_reallen = 0;
207                 }
208
209                 sock->inbuf_len = 0;
210         } else {
211                 if (sock->outbuf_reallen > FILEBUF_ALIGNMENT) {
212                         FREE_AND_NULL(sock->outbuf);
213                         sock->outbuf_reallen = 0;
214                 }
215
216                 sock->outbuf_len = 0;
217         }
218 }