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