1 #include "burp.h"
2 #include "alloc.h"
3 #include "asfd.h"
4 #include "async.h"
5 #include "handy.h"
6 #include "iobuf.h"
7 #include "log.h"
8
async_free(struct async ** as)9 void async_free(struct async **as)
10 {
11 if(!as || !*as) return;
12 free_v((void **)as);
13 }
14
async_settimers(struct async * as,int sec,int usec)15 static void async_settimers(struct async *as, int sec, int usec)
16 {
17 as->setsec=sec;
18 as->setusec=usec;
19 }
20
21 // The normal server and client processes will just exit on error within
22 // async_io, but the champ chooser server needs to manage client fds and
23 // remove them from its list if one of them had a problem.
asfd_problem(struct asfd * asfd)24 static int asfd_problem(struct asfd *asfd)
25 {
26 asfd->want_to_remove++;
27 asfd->as->last_time=asfd->as->now;
28 return -1;
29 }
30
31 #ifdef HAVE_WIN32
32 // Jesus H. Christ. How does anybody ever get anything done with windows?
33 // The normal async stuff does not work for the client monitor, because the
34 // windows select() only supports sockets, and windows stdin/stdout are not
35 // sockets.
36 // It seems to be impossible to do non-blocking i/o on windows stdin.
37 // If you have a windows console, you can use PeekConsoleInput to look at
38 // events in order to look ahead. This is not looking at stdin, which means
39 // that this does not work for ssh via cygwin.
windows_stupidity_hacks(struct asfd * asfd,fd_set * fsr,fd_set * fsw)40 static int windows_stupidity_hacks(struct asfd *asfd,
41 fd_set *fsr, fd_set *fsw)
42 {
43 if(asfd->do_read && asfd->fd==fileno(stdin))
44 {
45 DWORD len=0;
46 INPUT_RECORD irec;
47 HANDLE han=GetStdHandle(STD_INPUT_HANDLE);
48
49 switch(WaitForSingleObject(han, 0))
50 {
51 case WAIT_OBJECT_0:
52 if(!PeekConsoleInput(han, &irec, 1, &len))
53 break;
54 if(irec.EventType==KEY_EVENT
55 && irec.Event.KeyEvent.bKeyDown)
56 {
57 // This will block until the user hits
58 // the return key.
59 if(asfd->do_read(asfd)
60 || asfd->parse_readbuf(asfd))
61 return asfd_problem(asfd);
62 }
63 else
64 {
65 // Purge event we are not interested in.
66 ReadConsoleInput(han, &irec, 1, &len);
67 }
68 break;
69 default:
70 break;
71 }
72 }
73 if(asfd->do_write && asfd->fd==fileno(stdout))
74 {
75 // This is saying that we think that stdout is always OK to
76 // write to. Maybe this will not work all the time.
77 FD_SET((unsigned int)asfd->fd, fsw);
78 }
79 return 0;
80 }
81 #endif
82
async_io(struct async * as,int doread)83 static int async_io(struct async *as, int doread)
84 {
85 int mfd=-1;
86 fd_set fsr;
87 fd_set fsw;
88 fd_set fse;
89 int dosomething=0;
90 struct timeval tval;
91 struct asfd *asfd;
92 static int s=0;
93
94 as->now=time(NULL);
95 if(!as->last_time) as->last_time=as->now;
96
97 if(as->doing_estimate) goto end;
98
99 FD_ZERO(&fsr);
100 FD_ZERO(&fsw);
101 FD_ZERO(&fse);
102
103 tval.tv_sec=as->setsec;
104 tval.tv_usec=as->setusec;
105
106 for(asfd=as->asfd; asfd; asfd=asfd->next)
107 {
108 if(asfd->attempt_reads)
109 asfd->doread=doread;
110 else
111 asfd->doread=0;
112
113 asfd->dowrite=0;
114
115 if(doread)
116 {
117 if(asfd->parse_readbuf(asfd))
118 return asfd_problem(asfd);
119 if(asfd->rbuf->buf || asfd->read_blocked_on_write)
120 asfd->doread=0;
121 }
122
123 if(asfd->writebuflen && !asfd->write_blocked_on_read)
124 asfd->dowrite++; // The write buffer is not yet empty.
125
126 if(!asfd->doread && !asfd->dowrite) continue;
127
128 #ifdef HAVE_WIN32
129 if(asfd->fd==fileno(stdin)
130 || asfd->fd==fileno(stdout))
131 {
132 dosomething++;
133 continue;
134 }
135 #endif
136
137 add_fd_to_sets(asfd->fd, asfd->doread?&fsr:NULL,
138 asfd->dowrite?&fsw:NULL, &fse, &mfd);
139
140 dosomething++;
141 }
142 if(!dosomething) goto end;
143 /*
144 for(asfd=as->asfd; asfd; asfd=asfd->next)
145 {
146 printf("%s: %d %d %d %d\n", asfd->desc,
147 asfd->doread, asfd->dowrite,
148 asfd->readbuflen, asfd->writebuflen);
149 }
150 */
151
152 if(mfd>0)
153 {
154 errno=0;
155 s=select(mfd+1, &fsr, &fsw, &fse, &tval);
156 if(errno==EAGAIN || errno==EINTR) goto end;
157
158 if(s<0)
159 {
160 logp("select error in %s: %s\n", __func__,
161 strerror(errno));
162 as->last_time=as->now;
163 return -1;
164 }
165 }
166
167 for(asfd=as->asfd; asfd; asfd=asfd->next)
168 {
169 #ifdef HAVE_WIN32
170 if(windows_stupidity_hacks(asfd, &fsr, &fsw))
171 return -1;
172 #endif
173 if(FD_ISSET(asfd->fd, &fse))
174 {
175 switch(asfd->fdtype)
176 {
177 case ASFD_FD_SERVER_LISTEN_MAIN:
178 case ASFD_FD_SERVER_LISTEN_STATUS:
179 as->last_time=as->now;
180 return -1;
181 default:
182 logp("%s: had an exception\n",
183 asfd->desc);
184 return asfd_problem(asfd);
185 }
186 }
187
188 if(asfd->doread && FD_ISSET(asfd->fd, &fsr)) // Able to read.
189 {
190 asfd->network_timeout=asfd->max_network_timeout;
191 switch(asfd->fdtype)
192 {
193 case ASFD_FD_SERVER_LISTEN_MAIN:
194 case ASFD_FD_SERVER_LISTEN_STATUS:
195 // Indicate to the caller that we have
196 // a new incoming client.
197 asfd->new_client++;
198 break;
199 default:
200 if(asfd->do_read(asfd)
201 || asfd->parse_readbuf(asfd))
202 return asfd_problem(asfd);
203 break;
204 }
205 }
206
207 if(asfd->dowrite && FD_ISSET(asfd->fd, &fsw)) // Able to write.
208 {
209 asfd->network_timeout=asfd->max_network_timeout;
210 if(asfd->do_write(asfd))
211 return asfd_problem(asfd);
212 }
213
214 if((!asfd->doread || !FD_ISSET(asfd->fd, &fsr))
215 && (!asfd->dowrite || !FD_ISSET(asfd->fd, &fsw)))
216 {
217 // Be careful to avoid 'read quick' mode.
218 if((as->setsec || as->setusec)
219 && asfd->max_network_timeout>0
220 && as->now-as->last_time>0
221 && asfd->network_timeout--<=0)
222 {
223 logp("%s: no activity for %d seconds.\n",
224 asfd->desc, asfd->max_network_timeout);
225 return asfd_problem(asfd);
226 }
227 }
228 }
229
230 end:
231 as->last_time=as->now;
232 return 0;
233 }
234
async_read_write(struct async * as)235 static int async_read_write(struct async *as)
236 {
237 return async_io(as, 1 /* Read too. */);
238 }
239
async_write(struct async * as)240 static int async_write(struct async *as)
241 {
242 return async_io(as, 0 /* No read. */);
243 }
244
async_read_quick(struct async * as)245 static int async_read_quick(struct async *as)
246 {
247 int r;
248 int savesec=as->setsec;
249 int saveusec=as->setusec;
250 as->setsec=0;
251 as->setusec=0;
252 r=as->read_write(as); // Maybe make an as->read(as) function some time.
253 as->setsec=savesec;
254 as->setusec=saveusec;
255 return r;
256 }
257
async_asfd_add(struct async * as,struct asfd * asfd)258 static void async_asfd_add(struct async *as, struct asfd *asfd)
259 {
260 struct asfd *x;
261 if(!as->asfd)
262 {
263 as->asfd=asfd;
264 return;
265 }
266 // Add to the end;
267 for(x=as->asfd; x->next; x=x->next) { }
268 x->next=asfd;
269 }
270
async_asfd_remove(struct async * as,struct asfd * asfd)271 static void async_asfd_remove(struct async *as, struct asfd *asfd)
272 {
273 struct asfd *l;
274 if(!asfd) return;
275 if(as->asfd==asfd)
276 {
277 as->asfd=as->asfd->next;
278 return;
279 }
280 for(l=as->asfd; l; l=l->next)
281 {
282 if(l->next!=asfd) continue;
283 l->next=asfd->next;
284 return;
285 }
286 }
287
async_asfd_free_all(struct async ** as)288 void async_asfd_free_all(struct async **as)
289 {
290 struct asfd *a=NULL;
291 struct asfd *asfd=NULL;
292 if(!as || !*as) return;
293 for(asfd=(*as)->asfd; asfd; asfd=a)
294 {
295 a=asfd->next;
296 asfd_free(&asfd);
297 }
298 async_free(as);
299 }
300
async_init(struct async * as,int estimate)301 static int async_init(struct async *as, int estimate)
302 {
303 as->setsec=1;
304 as->setusec=0;
305 as->last_time=0;
306 as->doing_estimate=estimate;
307
308 as->read_write=async_read_write;
309 as->write=async_write;
310 as->read_quick=async_read_quick;
311
312 as->settimers=async_settimers;
313 as->asfd_add=async_asfd_add;
314 as->asfd_remove=async_asfd_remove;
315
316 return 0;
317 }
318
async_alloc(void)319 struct async *async_alloc(void)
320 {
321 struct async *as;
322 if(!(as=(struct async *)calloc_w(1, sizeof(struct async), __func__)))
323 return NULL;
324 as->init=async_init;
325 return as;
326 }
327